262 lines
8.8 KiB
C#
262 lines
8.8 KiB
C#
using Microsoft.Extensions.Caching.Memory;
|
|
using System.Globalization;
|
|
|
|
namespace OnlyOneAccessTemplate.Services
|
|
{
|
|
public class LanguageService : ILanguageService
|
|
{
|
|
private readonly IMemoryCache _cache;
|
|
private readonly ILogger<LanguageService> _logger;
|
|
|
|
private static readonly Dictionary<string, string> SupportedLanguages = new()
|
|
{
|
|
{ "pt", "Português" },
|
|
{ "en", "English" },
|
|
{ "es", "Español" }
|
|
};
|
|
|
|
private static readonly Dictionary<string, string> LanguageCultures = new()
|
|
{
|
|
{ "pt", "pt-BR" },
|
|
{ "en", "en-US" },
|
|
{ "es", "es-ES" }
|
|
};
|
|
|
|
public LanguageService(IMemoryCache cache, ILogger<LanguageService> logger)
|
|
{
|
|
_cache = cache;
|
|
_logger = logger;
|
|
}
|
|
|
|
public string GetCurrentLanguage(HttpContext context)
|
|
{
|
|
try
|
|
{
|
|
// 1. Verificar na URL
|
|
var pathLanguage = GetLanguageFromPath(context.Request.Path);
|
|
if (!string.IsNullOrEmpty(pathLanguage))
|
|
{
|
|
return pathLanguage;
|
|
}
|
|
|
|
// 2. Verificar cookie de preferência
|
|
if (context.Request.Cookies.TryGetValue("preferred_language", out var cookieLanguage) &&
|
|
SupportedLanguages.ContainsKey(cookieLanguage))
|
|
{
|
|
return cookieLanguage;
|
|
}
|
|
|
|
// 3. Verificar Accept-Language header
|
|
var browserLanguage = GetLanguageFromBrowser(context);
|
|
if (!string.IsNullOrEmpty(browserLanguage))
|
|
{
|
|
return browserLanguage;
|
|
}
|
|
|
|
// 4. Retornar idioma padrão
|
|
return GetDefaultLanguage();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao determinar idioma atual");
|
|
return GetDefaultLanguage();
|
|
}
|
|
}
|
|
|
|
public string GetLanguageFromPath(string path)
|
|
{
|
|
if (string.IsNullOrEmpty(path) || path == "/")
|
|
return string.Empty;
|
|
|
|
var segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
|
if (segments.Length == 0)
|
|
return string.Empty;
|
|
|
|
var firstSegment = segments[0].ToLowerInvariant();
|
|
|
|
return SupportedLanguages.ContainsKey(firstSegment) ? firstSegment : string.Empty;
|
|
}
|
|
|
|
public List<string> GetSupportedLanguages()
|
|
{
|
|
return SupportedLanguages.Keys.ToList();
|
|
}
|
|
|
|
public string GetDefaultLanguage()
|
|
{
|
|
return "pt";
|
|
}
|
|
|
|
public string GetLanguageDisplayName(string languageCode)
|
|
{
|
|
return SupportedLanguages.TryGetValue(languageCode, out var displayName)
|
|
? displayName
|
|
: languageCode;
|
|
}
|
|
|
|
public Dictionary<string, string> GetHreflangUrls(string currentUrl, string currentLanguage)
|
|
{
|
|
var cacheKey = $"hreflang_{currentUrl}_{currentLanguage}";
|
|
|
|
if (_cache.TryGetValue(cacheKey, out Dictionary<string, string>? cachedUrls) && cachedUrls != null)
|
|
{
|
|
return cachedUrls;
|
|
}
|
|
|
|
var hreflangUrls = new Dictionary<string, string>();
|
|
|
|
try
|
|
{
|
|
var baseUrl = RemoveLanguageFromUrl(currentUrl, currentLanguage);
|
|
|
|
foreach (var language in SupportedLanguages.Keys)
|
|
{
|
|
var url = language == GetDefaultLanguage()
|
|
? baseUrl
|
|
: $"{baseUrl.TrimEnd('/')}/{language}";
|
|
|
|
hreflangUrls[language] = url;
|
|
}
|
|
|
|
// Adicionar x-default para o idioma padrão
|
|
hreflangUrls["x-default"] = hreflangUrls[GetDefaultLanguage()];
|
|
|
|
_cache.Set(cacheKey, hreflangUrls, TimeSpan.FromMinutes(15));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao gerar URLs hreflang para {CurrentUrl}", currentUrl);
|
|
}
|
|
|
|
return hreflangUrls;
|
|
}
|
|
|
|
public string GetCultureInfo(string languageCode)
|
|
{
|
|
return LanguageCultures.TryGetValue(languageCode, out var culture) ? culture : "pt-BR";
|
|
}
|
|
|
|
public void SetLanguageCookie(HttpContext context, string languageCode)
|
|
{
|
|
if (!SupportedLanguages.ContainsKey(languageCode))
|
|
return;
|
|
|
|
var cookieOptions = new CookieOptions
|
|
{
|
|
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
|
HttpOnly = false,
|
|
Secure = context.Request.IsHttps,
|
|
SameSite = SameSiteMode.Lax
|
|
};
|
|
|
|
context.Response.Cookies.Append("preferred_language", languageCode, cookieOptions);
|
|
}
|
|
|
|
public string GenerateLanguageSwitcher(HttpContext context, string currentLanguage)
|
|
{
|
|
var currentUrl = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";
|
|
var hreflangUrls = GetHreflangUrls(currentUrl, currentLanguage);
|
|
|
|
var switcher = new List<string>();
|
|
|
|
foreach (var language in SupportedLanguages)
|
|
{
|
|
var isActive = language.Key == currentLanguage;
|
|
var cssClass = isActive ? "active" : "";
|
|
var url = hreflangUrls.TryGetValue(language.Key, out var langUrl) ? langUrl : "/";
|
|
|
|
switcher.Add($"<a href=\"{url}\" class=\"language-link {cssClass}\" hreflang=\"{language.Key}\">{language.Value}</a>");
|
|
}
|
|
|
|
return string.Join("", switcher);
|
|
}
|
|
|
|
public bool IsRtlLanguage(string languageCode)
|
|
{
|
|
// Lista de idiomas RTL (Right-to-Left)
|
|
var rtlLanguages = new[] { "ar", "he", "fa", "ur" };
|
|
return rtlLanguages.Contains(languageCode);
|
|
}
|
|
|
|
public string GetLanguageDirection(string languageCode)
|
|
{
|
|
return IsRtlLanguage(languageCode) ? "rtl" : "ltr";
|
|
}
|
|
|
|
public bool IsValidLanguage(string languageCode)
|
|
{
|
|
return !string.IsNullOrEmpty(languageCode) && SupportedLanguages.ContainsKey(languageCode.ToLowerInvariant());
|
|
}
|
|
|
|
public string NormalizeLanguageCode(string languageCode)
|
|
{
|
|
if (string.IsNullOrEmpty(languageCode))
|
|
return GetDefaultLanguage();
|
|
|
|
var normalized = languageCode.ToLowerInvariant().Trim();
|
|
|
|
// Handle common variations
|
|
normalized = normalized switch
|
|
{
|
|
"pt-br" or "pt_br" or "portuguese" => "pt",
|
|
"en-us" or "en_us" or "english" => "en",
|
|
"es-es" or "es_es" or "spanish" or "español" => "es",
|
|
_ => normalized
|
|
};
|
|
|
|
return IsValidLanguage(normalized) ? normalized : GetDefaultLanguage();
|
|
}
|
|
|
|
private string GetLanguageFromBrowser(HttpContext context)
|
|
{
|
|
try
|
|
{
|
|
var acceptLanguage = context.Request.Headers.AcceptLanguage.ToString();
|
|
if (string.IsNullOrEmpty(acceptLanguage))
|
|
return string.Empty;
|
|
|
|
// Parse Accept-Language header
|
|
var languages = acceptLanguage
|
|
.Split(',')
|
|
.Select(lang => lang.Split(';')[0].Trim())
|
|
.Select(lang => lang.Length >= 2 ? lang.Substring(0, 2).ToLowerInvariant() : lang)
|
|
.Where(lang => SupportedLanguages.ContainsKey(lang));
|
|
|
|
return languages.FirstOrDefault() ?? string.Empty;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao analisar Accept-Language header");
|
|
return string.Empty;
|
|
}
|
|
}
|
|
|
|
private string RemoveLanguageFromUrl(string url, string currentLanguage)
|
|
{
|
|
try
|
|
{
|
|
var uri = new Uri(url);
|
|
var path = uri.AbsolutePath;
|
|
|
|
if (currentLanguage != GetDefaultLanguage())
|
|
{
|
|
var languagePrefix = $"/{currentLanguage}";
|
|
if (path.StartsWith(languagePrefix))
|
|
{
|
|
path = path.Substring(languagePrefix.Length);
|
|
if (string.IsNullOrEmpty(path))
|
|
path = "/";
|
|
}
|
|
}
|
|
|
|
return $"{uri.Scheme}://{uri.Host}{(uri.Port != 80 && uri.Port != 443 ? $":{uri.Port}" : "")}{path}";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao remover idioma da URL {Url}", url);
|
|
return url;
|
|
}
|
|
}
|
|
}
|
|
}
|