diff --git a/BTCPayServer/Controllers/UIServerController.Translations.cs b/BTCPayServer/Controllers/UIServerController.Translations.cs index 29ac5c355..d01720861 100644 --- a/BTCPayServer/Controllers/UIServerController.Translations.cs +++ b/BTCPayServer/Controllers/UIServerController.Translations.cs @@ -1,4 +1,6 @@ +using System; using System.Data.Common; +using System.Net.Http; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Models.ServerViewModels; @@ -122,5 +124,53 @@ namespace BTCPayServer.Controllers TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Dictionary {0} deleted", dictionary].Value; return RedirectToAction(nameof(ListDictionaries)); } + + [HttpPost("server/dictionaries/download")] + public async Task DownloadLanguagePack(string language) + { + if (string.IsNullOrEmpty(language)) + { + TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Please select a language"].Value; + return RedirectToAction(nameof(ListDictionaries)); + } + + string translationsJson; + try + { + translationsJson = await FetchLanguagePackFromRepository(language); + } + catch (HttpRequestException ex) + { + TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Failed to download language pack: {0}", ex.Message].Value; + return RedirectToAction(nameof(ListDictionaries)); + } + + var translations = Translations.CreateFromJson(translationsJson); + var existingDictionary = await _localizer.GetDictionary(language); + if (existingDictionary is null) + { + existingDictionary = await _localizer.CreateDictionary(language, Translations.DefaultLanguage, "Custom"); + TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Language pack '{0}' downloaded successfully", language].Value; + } + else + { + TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Language pack '{0}' updated successfully", language].Value; + } + + await _localizer.Save(existingDictionary, translations); + return RedirectToAction(nameof(ListDictionaries)); + } + + private async Task FetchLanguagePackFromRepository(string language) + { + var fileName = language.ToLowerInvariant(); + var url = $"https://raw.githubusercontent.com/btcpayserver/btcpayserver-translator/main/translations/{fileName}.json"; + + var httpClient = HttpClientFactory.CreateClient(); + using var response = await httpClient.GetAsync(url); + response.EnsureSuccessStatusCode(); + + return await response.Content.ReadAsStringAsync(); + } } } diff --git a/BTCPayServer/Services/LocalizerService.cs b/BTCPayServer/Services/LocalizerService.cs index 6e4ef62f1..e2eed7a30 100644 --- a/BTCPayServer/Services/LocalizerService.cs +++ b/BTCPayServer/Services/LocalizerService.cs @@ -177,7 +177,7 @@ namespace BTCPayServer.Services { await using var ctx = _ContextFactory.CreateContext(); var db = ctx.Database.GetDbConnection(); - var r = await db.QueryFirstAsync("SELECT * FROM lang_dictionaries WHERE dict_id=@dict_id", new { dict_id = name }); + var r = await db.QueryFirstOrDefaultAsync("SELECT * FROM lang_dictionaries WHERE dict_id=@dict_id", new { dict_id = name }); if (r is null) return null; return new Dictionary(r.dict_id, r.fallback, r.source ?? "", JObject.Parse(r.metadata ?? "{}")); diff --git a/BTCPayServer/Views/UIServer/ListDictionaries.cshtml b/BTCPayServer/Views/UIServer/ListDictionaries.cshtml index 334499394..ecdd79aba 100644 --- a/BTCPayServer/Views/UIServer/ListDictionaries.cshtml +++ b/BTCPayServer/Views/UIServer/ListDictionaries.cshtml @@ -6,9 +6,14 @@ @@ -65,3 +70,50 @@ + +@* Download Language Pack Modal *@ +