2023-04-20 02:44:24 +02:00
|
|
|
#nullable enable
|
2020-12-10 15:34:50 +01:00
|
|
|
using System;
|
2020-06-29 04:44:35 +02:00
|
|
|
using System.Collections.Generic;
|
2021-07-27 08:17:56 +02:00
|
|
|
using System.Globalization;
|
2018-11-09 08:48:38 +01:00
|
|
|
using System.IO;
|
2020-12-10 15:34:50 +01:00
|
|
|
using System.Linq;
|
2021-07-08 07:34:10 +02:00
|
|
|
using BTCPayServer.Client.Models;
|
2020-06-28 10:55:27 +02:00
|
|
|
using Microsoft.AspNetCore.Hosting;
|
2021-07-27 08:17:56 +02:00
|
|
|
using Microsoft.AspNetCore.Http;
|
2021-10-15 07:27:52 +02:00
|
|
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
2022-07-21 11:09:51 +02:00
|
|
|
using Microsoft.Extensions.Logging;
|
2018-11-09 08:48:38 +01:00
|
|
|
using Newtonsoft.Json;
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
using Newtonsoft.Json.Serialization;
|
2018-03-23 09:27:48 +01:00
|
|
|
|
|
|
|
namespace BTCPayServer.Services
|
|
|
|
{
|
2021-07-27 08:17:56 +02:00
|
|
|
public class Language
|
|
|
|
{
|
|
|
|
public Language(string code, string displayName)
|
|
|
|
{
|
|
|
|
DisplayName = displayName;
|
|
|
|
Code = code;
|
|
|
|
}
|
|
|
|
|
|
|
|
[JsonProperty("code")] public string Code { get; set; }
|
|
|
|
[JsonProperty("currentLanguage")] public string DisplayName { get; set; }
|
|
|
|
}
|
|
|
|
|
2018-03-23 09:27:48 +01:00
|
|
|
public class LanguageService
|
|
|
|
{
|
2022-07-21 11:09:51 +02:00
|
|
|
private readonly ILogger<LanguageService> _logger;
|
2018-11-09 08:48:38 +01:00
|
|
|
private readonly Language[] _languages;
|
|
|
|
|
2022-07-21 11:09:51 +02:00
|
|
|
public LanguageService(IWebHostEnvironment environment, ILogger<LanguageService> logger)
|
2018-03-23 09:27:48 +01:00
|
|
|
{
|
2022-07-21 11:09:51 +02:00
|
|
|
_logger = logger;
|
2019-10-03 10:37:10 +02:00
|
|
|
var path = environment.WebRootPath;
|
2024-04-05 09:23:04 +02:00
|
|
|
path = Path.Combine(path, "locales", "checkout");
|
2018-11-09 08:48:38 +01:00
|
|
|
var files = Directory.GetFiles(path, "*.json");
|
|
|
|
var result = new List<Language>();
|
|
|
|
foreach (var file in files)
|
|
|
|
{
|
2022-07-21 11:09:51 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
|
|
|
|
using var stream = new StreamReader(file);
|
|
|
|
var json = stream.ReadToEnd();
|
2023-04-20 02:44:24 +02:00
|
|
|
result.Add(JObject.Parse(json).ToObject<Language>()!);
|
2022-07-21 11:09:51 +02:00
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
_logger.LogError(e, $"Could not parse language file {file}");
|
|
|
|
}
|
2018-11-09 08:48:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_languages = result.ToArray();
|
|
|
|
}
|
2021-07-27 08:17:56 +02:00
|
|
|
|
2018-11-09 08:48:38 +01:00
|
|
|
public Language[] GetLanguages()
|
|
|
|
{
|
|
|
|
return _languages;
|
2018-03-23 09:27:48 +01:00
|
|
|
}
|
2020-12-10 15:34:50 +01:00
|
|
|
|
2021-10-15 07:27:52 +02:00
|
|
|
public IEnumerable<SelectListItem> GetLanguageSelectListItems()
|
|
|
|
{
|
2021-12-31 08:59:02 +01:00
|
|
|
IEnumerable<SelectListItem> items = GetLanguages().Select(l => new SelectListItem
|
|
|
|
{
|
|
|
|
Value = l.Code,
|
|
|
|
Text = l.DisplayName,
|
|
|
|
Disabled = false
|
|
|
|
}).OrderBy(lang => lang.Text);
|
|
|
|
|
2021-10-15 07:27:52 +02:00
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
2023-04-20 02:44:24 +02:00
|
|
|
public Language? FindLanguageInAcceptLanguageHeader(string? acceptLanguageHeader)
|
2021-07-27 08:17:56 +02:00
|
|
|
{
|
2021-08-31 09:01:16 +02:00
|
|
|
if (acceptLanguageHeader is null)
|
|
|
|
return null;
|
2021-07-27 08:17:56 +02:00
|
|
|
IDictionary<string, float> acceptedLocales = new Dictionary<string, float>();
|
|
|
|
var locales = acceptLanguageHeader.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
for (int i = 0; i < locales.Length; i++)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var oneLocale = locales[i];
|
|
|
|
var parts = oneLocale.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
var locale = parts[0];
|
|
|
|
var qualityScore = 1.0f;
|
|
|
|
if (parts.Length == 2)
|
|
|
|
{
|
|
|
|
var qualityScorePart = parts[1];
|
|
|
|
if (qualityScorePart.StartsWith("q=", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
|
|
|
qualityScorePart = qualityScorePart.Substring(2);
|
|
|
|
qualityScore = float.Parse(qualityScorePart, CultureInfo.InvariantCulture);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Invalid format, continue with next
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!locale.Equals("*", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
|
|
|
acceptedLocales[locale] = qualityScore;
|
|
|
|
}
|
|
|
|
}
|
2021-07-29 13:29:34 +02:00
|
|
|
catch (System.FormatException)
|
2021-07-27 08:17:56 +02:00
|
|
|
{
|
|
|
|
// Can't use this piece, moving on...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var sortedAcceptedLocales = from entry in acceptedLocales orderby entry.Value descending select entry;
|
|
|
|
foreach (var pair in sortedAcceptedLocales)
|
|
|
|
{
|
|
|
|
var lang = FindLanguage(pair.Key);
|
|
|
|
if (lang != null)
|
|
|
|
{
|
|
|
|
return lang;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Look for a supported language that matches the given locale (can be in different notations like "nl" or "nl-NL").
|
|
|
|
* Example: "nl" is not supported, but we do have "nl-NL"
|
|
|
|
*/
|
2023-04-20 02:44:24 +02:00
|
|
|
public Language? FindLanguage(string locale)
|
2020-12-10 15:34:50 +01:00
|
|
|
{
|
2021-07-27 08:17:56 +02:00
|
|
|
var supportedLangs = GetLanguages();
|
|
|
|
var split = locale.Split('-', StringSplitOptions.RemoveEmptyEntries);
|
2020-12-10 15:34:50 +01:00
|
|
|
var lang = split[0];
|
|
|
|
var country = split.Length == 2 ? split[1] : split[0].ToUpperInvariant();
|
|
|
|
|
|
|
|
var langStart = lang + "-";
|
2021-07-27 08:17:56 +02:00
|
|
|
var langMatches = supportedLangs
|
2020-12-10 15:34:50 +01:00
|
|
|
.Where(l => l.Code.Equals(lang, StringComparison.OrdinalIgnoreCase) ||
|
2021-07-27 08:17:56 +02:00
|
|
|
l.Code.StartsWith(langStart, StringComparison.OrdinalIgnoreCase))
|
|
|
|
.ToList();
|
2020-12-10 15:34:50 +01:00
|
|
|
|
|
|
|
var countryMatches = langMatches;
|
|
|
|
var countryEnd = "-" + country;
|
2021-07-27 08:17:56 +02:00
|
|
|
countryMatches = countryMatches.Where(l =>
|
|
|
|
l.Code.EndsWith(countryEnd, StringComparison.OrdinalIgnoreCase)).ToList();
|
2020-12-10 15:34:50 +01:00
|
|
|
return countryMatches.FirstOrDefault() ?? langMatches.FirstOrDefault();
|
|
|
|
}
|
2021-07-27 08:17:56 +02:00
|
|
|
|
2023-04-20 02:44:24 +02:00
|
|
|
public Language? AutoDetectLanguageUsingHeader(IHeaderDictionary headerDictionary, string? defaultLang)
|
2021-07-27 08:17:56 +02:00
|
|
|
{
|
|
|
|
if (headerDictionary?.TryGetValue("Accept-Language",
|
|
|
|
out var acceptLanguage) is true && !string.IsNullOrEmpty(acceptLanguage))
|
|
|
|
{
|
|
|
|
return FindLanguageInAcceptLanguageHeader(acceptLanguage.ToString()) ?? FindLanguageInAcceptLanguageHeader(defaultLang);
|
|
|
|
}
|
|
|
|
return FindLanguageInAcceptLanguageHeader(defaultLang);
|
|
|
|
}
|
2018-03-23 09:27:48 +01:00
|
|
|
}
|
|
|
|
}
|