using Microsoft.Extensions.Caching.Memory; using System.Globalization; namespace OnlyOneAccessTemplate.Services { public class LanguageService : ILanguageService { private readonly IMemoryCache _cache; private readonly ILogger _logger; private static readonly Dictionary SupportedLanguages = new() { { "pt", "Português" }, { "en", "English" }, { "es", "Español" } }; private static readonly Dictionary LanguageCultures = new() { { "pt", "pt-BR" }, { "en", "en-US" }, { "es", "es-ES" } }; public LanguageService(IMemoryCache cache, ILogger 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 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 GetHreflangUrls(string currentUrl, string currentLanguage) { var cacheKey = $"hreflang_{currentUrl}_{currentLanguage}"; if (_cache.TryGetValue(cacheKey, out Dictionary? cachedUrls) && cachedUrls != null) { return cachedUrls; } var hreflangUrls = new Dictionary(); 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(); 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($"{language.Value}"); } 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; } } } }