From 368b900a6e1140e0ef6807dd185c4ffa8da77528 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 20 Oct 2022 13:16:18 -0400 Subject: [PATCH] fix #2464 - translation install/upgrade experience --- .../Modules/Admin/Languages/Add.razor | 360 ++++-------------- .../Modules/Admin/Languages/Index.razor | 222 ++++++++--- .../Admin/ModuleDefinitions/Edit.razor | 92 ++--- Oqtane.Client/Modules/Admin/Site/Index.razor | 2 +- Oqtane.Client/Program.cs | 9 +- .../Modules/Admin/Languages/Add.resx | 27 +- .../Interfaces/ILocalizationService.cs | 2 +- Oqtane.Client/Services/LocalizationService.cs | 2 +- .../Controllers/LanguageController.cs | 4 +- .../Controllers/LocalizationController.cs | 13 +- 10 files changed, 324 insertions(+), 409 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Languages/Add.razor b/Oqtane.Client/Modules/Admin/Languages/Add.razor index 0f371f29..14a7990e 100644 --- a/Oqtane.Client/Modules/Admin/Languages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Languages/Add.razor @@ -9,7 +9,7 @@ @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer -@if (_supportedCultures == null) +@if (_cultures == null) {

@SharedLocalizer["Loading"]

} @@ -17,104 +17,42 @@ else { - @if (_availableCultures.Count() == 0) - { - - @SharedLocalizer["Cancel"] - } - else - { -
-
-
- -
- -
-
-
- -
- -
+ +
+
+ +
+ +
+
+
+ +
+
- - @SharedLocalizer["Cancel"] - - } - - -
-
-
- - - - +
+ +
+ +
-
- - @if (_packages != null) - { - @if (_packages.Count > 0) - { - - - -

@context.Name

  by:  @context.Owner
- @(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)
- @(String.Format("{0:n0}", context.Downloads)) @SharedLocalizer["Search.Downloads"]  |   - @SharedLocalizer["Search.Released"]: @context.ReleaseDate.ToString("MMM dd, yyyy")  |   - @SharedLocalizer["Search.Version"]: @context.Version - @((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? "  |  " + SharedLocalizer["Search.Source"] + ": " + new Uri(context.PackageUrl).Host + "" : "")) - @((MarkupString)(context.TrialPeriod > 0 ? "  |  " + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "" : "")) - - - @if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl)) - { - - } - - - @if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl)) - { - @context.Price.Value.ToString("$#,##0.00") - } - else - { - - } - -
-
- } - else - { -
-
- @Localizer["Search.NoResults"] -
- } - - @SharedLocalizer["Cancel"] - -
-
- - } + + @SharedLocalizer["Cancel"] +
@@ -125,216 +63,86 @@ else
- + @SharedLocalizer["Cancel"] } -@if (_productname != "") -{ -
- -
-} - @code { private ElementReference form; private bool validated = false; - private string _code = string.Empty; - private string _isDefault = "False"; - private string _message; - private IEnumerable _supportedCultures; - private IEnumerable _availableCultures; - private List _packages; - private string _price = "free"; - private string _search = ""; - private string _productname = ""; - private string _license = ""; - private string _packageid = ""; - private string _version = ""; + private string _translated = "True"; + private string _code = "-"; + private string _default = "False"; + private List _languages; + private IEnumerable _cultures; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnParametersSetAsync() { var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId); - var languagesCodes = languages.Select(l => l.Code).ToList(); + _languages = languages.Select(l => l.Code).ToList(); - _supportedCultures = await LocalizationService.GetCulturesAsync(); - _availableCultures = _supportedCultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name)); - await LoadTranslations(); + await LoadCultures(); + } - if (_supportedCultures.Count() == 1) - { - _message = Localizer["OnlyEnglish"]; - } - else if (_availableCultures.Count() == 0) - { - _message = Localizer["AllLanguages"]; - } - } + private async Task LoadCultures() + { + _cultures = await LocalizationService.GetCulturesAsync(bool.Parse(_translated)); + _cultures = _cultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !_languages.Contains(c.Name)); + _code = "-"; + } - private async Task LoadTranslations() - { - _packages = await PackageService.GetPackagesAsync("translation", _search, _price, ""); - } + private async void TranslatedChanged(ChangeEventArgs e) + { + _translated = (string)e.Value; + await LoadCultures(); + StateHasChanged(); + } - private async void PriceChanged(ChangeEventArgs e) - { - try - { - _price = (string)e.Value; - _search = ""; - await LoadTranslations(); - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error On PriceChanged"); - } - } + private async Task SaveLanguage() + { + validated = true; + var interop = new Interop(JSRuntime); + if (await interop.FormValid(form) && _code != "-") + { + var language = new Language + { + SiteId = PageState.Page.SiteId, + Name = CultureInfo.GetCultureInfo(_code).DisplayName, + Code = _code, + IsDefault = (_default == null ? false : Boolean.Parse(_default)) + }; - private async Task Search() - { - try - { - await LoadTranslations(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error On Search"); - } - } + try + { + language = await LanguageService.AddLanguageAsync(language); - private async Task Reset() - { - try - { - _search = ""; - await LoadTranslations(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error On Reset"); - } - } + if (language.IsDefault) + { + await SetCultureAsync(language.Code); + } - private async Task SaveLanguage() - { - validated = true; - var interop = new Interop(JSRuntime); - if (await interop.FormValid(form)) - { - var language = new Language - { - SiteId = PageState.Page.SiteId, - Name = CultureInfo.GetCultureInfo(_code).DisplayName, - Code = _code, - IsDefault = (_isDefault == null ? false : Boolean.Parse(_isDefault)) - }; + await logger.LogInformation("Language Added {Language}", language); - try - { - language = await LanguageService.AddLanguageAsync(language); - - if (language.IsDefault) - { - await SetCultureAsync(language.Code); - } - - await logger.LogInformation("Language Added {Language}", language); - - NavigationManager.NavigateTo(NavigateUrl()); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message); - AddModuleMessage(Localizer["Error.Language.Add"], MessageType.Error); - } - } + NavigationManager.NavigateTo(NavigateUrl()); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message); + AddModuleMessage(Localizer["Error.Language.Add"], MessageType.Error); + } + } else { AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning); } } - - private void HideModal() - { - _productname = ""; - _license = ""; - StateHasChanged(); - } - - private async Task GetPackage(string packageid, string version) - { - try - { - var package = await PackageService.GetPackageAsync(packageid, version); - if (package != null) - { - _productname = package.Name; - if (!string.IsNullOrEmpty(package.License)) - { - _license = package.License.Replace("\n", "
"); - } - _packageid = package.PackageId; - _version = package.Version; - } - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version); - AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error); - } - } - - private async Task DownloadPackage() - { - try - { - await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder); - await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _packageid, _version); - AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success); - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _packageid, _version); - AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error); - } - } - - private async Task InstallLanguages() + private async Task InstallTranslations() { try { diff --git a/Oqtane.Client/Modules/Admin/Languages/Index.razor b/Oqtane.Client/Modules/Admin/Languages/Index.razor index bafe7e9b..29e1c88d 100644 --- a/Oqtane.Client/Modules/Admin/Languages/Index.razor +++ b/Oqtane.Client/Modules/Admin/Languages/Index.razor @@ -19,29 +19,86 @@ else   @SharedLocalizer["Name"] @Localizer["Code"] - @Localizer["Translation"] @Localizer["Default"] -   + @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + { + @Localizer["Translation"] +   + } @context.Name @context.Code - @context.Version - - @if (UpgradeAvailable(context.Code, context.Version)) - { - - } - + @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + { + @((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version) + + @switch (TranslationAvailable(context.Code, context.Version)) + { + case "install": + + break; + case "upgrade": + + break; + } + + } + @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && _install) + { + + } +} + +@if (_package != null) +{ +
+ +
} @code { private List _languages; private List _packages; + private Package _package; + private bool _install = false; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; @@ -49,64 +106,109 @@ else { _languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId); - var cultures = await LocalizationService.GetCulturesAsync(); - var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture)); + var cultures = await LocalizationService.GetCulturesAsync(false); + var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture)); - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) - { - _packages = await PackageService.GetPackagesAsync("translation"); - } - } + if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + { + _packages = await PackageService.GetPackagesAsync("translation"); + } + } - private async Task DeleteLanguage(Language language) - { - try - { - await LanguageService.DeleteLanguageAsync(language.LanguageId); - await logger.LogInformation("Language Deleted {Language}", language); + private async Task DeleteLanguage(Language language) + { + try + { + await LanguageService.DeleteLanguageAsync(language.LanguageId); + await logger.LogInformation("Language Deleted {Language}", language); - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Deleting Language {Language} {Error}", language, ex.Message); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting Language {Language} {Error}", language, ex.Message); - AddModuleMessage(Localizer["Error.Language.Delete"], MessageType.Error); - } - } + AddModuleMessage(Localizer["Error.Language.Delete"], MessageType.Error); + } + } - private bool UpgradeAvailable(string code, string version) - { - var upgradeavailable = false; - if (_packages != null && version != null) - { + private string TranslationAvailable(string code, string version) + { + if (_packages != null) + { var package = _packages.Where(item => item.PackageId == (Constants.PackageId + "." + code)).FirstOrDefault(); if (package != null) - { - upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0) && - (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0); - } + { + // package version needs to match current framework version + if (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0) + { + if (string.IsNullOrEmpty(version)) + { + return "install"; + } + else + { + if (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0) + { + return "upgrade"; + } + } + } + } + } + return ""; + } - } - return upgradeavailable; - } + private async Task GetPackage(string code) + { + try + { + _package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, Constants.Version); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", Constants.PackageId + "." + code, Constants.Version); + AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error); + } + } - private async Task DownloadLanguage(string code) - { - try - { - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) - { - await PackageService.DownloadPackageAsync(Constants.PackageId + "." + code, Constants.Version, Constants.PackagesFolder); - await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version); - await PackageService.InstallPackagesAsync(); - AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Downloading Translation {Code} {Version} {Error}", code, Constants.Version, ex.Message); - AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error); - } - } + private async Task DownloadPackage() + { + try + { + await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder); + await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _package.PackageId, _package.Version); + AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success); + _package = null; + _install = true; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _package.PackageId, _package.Version); + AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error); + } + } + + private void HideModal() + { + _package = null; + StateHasChanged(); + } + + private async Task InstallTranslations() + { + try + { + await PackageService.InstallPackagesAsync(); + AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success); + _install = false; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Installing Translations"); + } + } } diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index b425b9bb..21d0cf44 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -108,29 +108,30 @@
@SharedLocalizer["Name"] @Localizer["Code"] - @Localizer["Version"] + @Localizer["Version"]  
@context.Name @context.Code - @context.Version + @((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version) - @if (context.IsDefault) + @switch (TranslationAvailable(_packagename + "." + context.Code, context.Version)) { - - } - else - { - if (UpgradeAvailable(_packagename + "." + context.Code, context.Version)) - { - - } + case "install": + + break; + case "upgrade": + + break; } - + @if (_install) + { + + } } else { @@ -143,7 +144,7 @@ -@if (_productname != "") +@if (_package != null) {
@@ -203,9 +211,8 @@ private List _packages; private List _languages; - private string _productname = ""; - private string _packagelicense = ""; - private string _packageid = ""; + private Package _package; + private bool _install = false; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; @@ -297,24 +304,31 @@ private void HideModal() { - _productname = ""; - _packagelicense = ""; + _package = null; StateHasChanged(); } - private bool UpgradeAvailable(string packagename, string version) + private string TranslationAvailable(string packagename, string version) { - var upgradeavailable = false; if (_packages != null) { var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault(); if (package != null) { - upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0); + if (string.IsNullOrEmpty(version)) + { + return "install"; + } + else + { + if (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0) + { + return "upgrade"; + } + } } - } - return upgradeavailable; + return ""; } private async Task GetPackage(string packagename) @@ -322,16 +336,7 @@ var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version; try { - var package = await PackageService.GetPackageAsync(packagename, version); - if (package != null) - { - _productname = package.Name; - if (!string.IsNullOrEmpty(package.License)) - { - _packagelicense = package.License.Replace("\n", "
"); - } - _packageid = package.PackageId; - } + _package = await PackageService.GetPackageAsync(packagename, version); StateHasChanged(); } catch (Exception ex) @@ -341,16 +346,15 @@ } } - private async Task DownloadPackage(string packagename) + private async Task DownloadPackage() { try { - var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version; - await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder); - await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", packagename, version); + await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder); + await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _package.PackageId, _package.Version); AddModuleMessage(Localizer["Success.Translation.Download"], MessageType.Success); - _productname = ""; - _packagelicense = ""; + _package = null; + _install = true; StateHasChanged(); } catch (Exception ex) @@ -366,6 +370,8 @@ { await PackageService.InstallPackagesAsync(); AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success); + _install = false; + StateHasChanged(); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index 1aae6b9a..367d31de 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -444,7 +444,7 @@ } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); + await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message); AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error); } } diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 31219ace..cd9290a5 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -16,6 +16,7 @@ using Microsoft.JSInterop; using Oqtane.Documentation; using Oqtane.Modules; using Oqtane.Services; +using Oqtane.Shared; using Oqtane.UI; namespace Oqtane.Client @@ -56,7 +57,11 @@ namespace Oqtane.Client RegisterClientStartups(assembly, builder.Services); } - await builder.Build().RunAsync(); + var host = builder.Build(); + + await SetCultureFromLocalizationCookie(host.Services); + + await host.RunAsync(); } private static async Task LoadClientAssemblies(HttpClient http, IServiceProvider serviceProvider) @@ -220,7 +225,7 @@ namespace Oqtane.Client var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName); var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value; var localizationService = serviceProvider.GetRequiredService(); - var cultures = await localizationService.GetCulturesAsync(); + var cultures = await localizationService.GetCulturesAsync(false); if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase))) { diff --git a/Oqtane.Client/Resources/Modules/Admin/Languages/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Languages/Add.resx index b4184c1f..f046e0c2 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Languages/Add.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Languages/Add.resx @@ -117,45 +117,30 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - The Only Supported Culture That Has Been Defined Is English - Error Adding Language + + Specify If You Wish To Select Languages That Have Translations Installed + Name Of The Langauage Indicates Whether Or Not This Language Is The Default For The Site + + Translated? + Name: Default? - - All The Installed Languages Have Been Added. - - - Error Downloading Translation - - - The Only Installed Language Is English - - - Translation Downloaded Successfully. Click Install To Complete Installation. - Translations Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes. - - No Translations Match The Criteria Provided Or Package Service Is Disabled - - - Translations - Upload one or more translation packages. Once they are uploaded click Install to complete the installation. diff --git a/Oqtane.Client/Services/Interfaces/ILocalizationService.cs b/Oqtane.Client/Services/Interfaces/ILocalizationService.cs index 04069213..543f8eba 100644 --- a/Oqtane.Client/Services/Interfaces/ILocalizationService.cs +++ b/Oqtane.Client/Services/Interfaces/ILocalizationService.cs @@ -13,6 +13,6 @@ namespace Oqtane.Services /// Returns a collection of supported cultures /// /// - Task> GetCulturesAsync(); + Task> GetCulturesAsync(bool installed); } } diff --git a/Oqtane.Client/Services/LocalizationService.cs b/Oqtane.Client/Services/LocalizationService.cs index 8b45a49d..880624b7 100644 --- a/Oqtane.Client/Services/LocalizationService.cs +++ b/Oqtane.Client/Services/LocalizationService.cs @@ -14,6 +14,6 @@ namespace Oqtane.Services private string Apiurl => CreateApiUrl("Localization"); - public async Task> GetCulturesAsync() => await GetJsonAsync>(Apiurl); + public async Task> GetCulturesAsync(bool installed) => await GetJsonAsync>($"{Apiurl}?installed={installed}"); } } diff --git a/Oqtane.Server/Controllers/LanguageController.cs b/Oqtane.Server/Controllers/LanguageController.cs index 678b3f6d..e4c6dbc7 100644 --- a/Oqtane.Server/Controllers/LanguageController.cs +++ b/Oqtane.Server/Controllers/LanguageController.cs @@ -47,7 +47,7 @@ namespace Oqtane.Controllers var code = Path.GetFileName(Path.GetDirectoryName(file)); if (!languages.Any(item => item.Code == code)) { - languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = FileVersionInfo.GetVersionInfo(file).FileVersion, IsDefault = false }); + languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = FileVersionInfo.GetVersionInfo(file).ProductVersion, IsDefault = false }); } } } @@ -62,7 +62,7 @@ namespace Oqtane.Controllers var code = Path.GetFileName(Path.GetDirectoryName(file)); if (languages.Any(item => item.Code == code)) { - languages.Single(item => item.Code == code).Version = FileVersionInfo.GetVersionInfo(file).FileVersion; + languages.Single(item => item.Code == code).Version = FileVersionInfo.GetVersionInfo(file).ProductVersion; } } } diff --git a/Oqtane.Server/Controllers/LocalizationController.cs b/Oqtane.Server/Controllers/LocalizationController.cs index 433d816e..5a572ba2 100644 --- a/Oqtane.Server/Controllers/LocalizationController.cs +++ b/Oqtane.Server/Controllers/LocalizationController.cs @@ -21,9 +21,18 @@ namespace Oqtane.Controllers // GET: api/localization [HttpGet()] - public IEnumerable Get() + public IEnumerable Get(bool installed) { - var cultures = _localizationManager.GetSupportedCultures().Select(c => new Culture + string[] culturecodes; + if (installed) + { + culturecodes = _localizationManager.GetInstalledCultures(); + } + else + { + culturecodes = _localizationManager.GetSupportedCultures(); + } + var cultures = culturecodes.Select(c => new Culture { Name = CultureInfo.GetCultureInfo(c).Name, DisplayName = CultureInfo.GetCultureInfo(c).DisplayName,