diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs index 8064ff99d2..5f53694177 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs @@ -16,10 +16,14 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations public string DefaultResourceName { get; set; } + public Dictionary> LanguagesMap { get; set; } + + public Dictionary> LanguageFilesMap { get; set; } + public ApplicationLocalizationConfigurationDto() { Values = new Dictionary>(); Languages = new List(); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs index 5d45f9c7e8..98fe3546c5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; +using Volo.Abp.Localization; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling { @@ -8,8 +11,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling { public List Files { get; } - public IServiceProvider ServiceProvider { get; } - public IFileProvider FileProvider { get; } public BundleConfigurationContext(IServiceProvider serviceProvider, IFileProvider fileProvider) @@ -18,5 +19,29 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling ServiceProvider = serviceProvider; FileProvider = fileProvider; } + + public IServiceProvider ServiceProvider { get; } + private readonly object _serviceProviderLock = new object(); + + private TRef LazyGetRequiredService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + lock (_serviceProviderLock) + { + if (reference == null) + { + reference = (TRef)ServiceProvider.GetRequiredService(serviceType); + } + } + } + + return reference; + } + + private IOptions _abpLocalizationOptions; + + public AbpLocalizationOptions LocalizationOptions => + LazyGetRequiredService(typeof(IOptions), ref _abpLocalizationOptions).Value; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/AbpAspNetCoreMvcUiPackagesModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/AbpAspNetCoreMvcUiPackagesModule.cs index ec82d4b3d6..a5ee47a753 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/AbpAspNetCoreMvcUiPackagesModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/AbpAspNetCoreMvcUiPackagesModule.cs @@ -1,4 +1,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker; +using Volo.Abp.Localization; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.Mvc.UI.Packages @@ -6,6 +8,18 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages [DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))] public class AbpAspNetCoreMvcUiPackagesModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.AddLanguagesMapOrUpdate(BootstrapDatepickerScriptContributor.PackageName, + new NameValue("zh-Hans", "zh-CN"), + new NameValue("zh-Hant", "zh-TW")); + options.AddLanguageFilesMapOrUpdate(BootstrapDatepickerScriptContributor.PackageName, + new NameValue("zh-Hans", "zh-CN"), + new NameValue("zh-Hant", "zh-TW")); + }); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs index fb29a8f2a5..ca852972d8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs @@ -1,7 +1,8 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; +using Volo.Abp.Localization; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker @@ -9,11 +10,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker [DependsOn(typeof(JQueryScriptContributor))] public class BootstrapDatepickerScriptContributor : BundleContributor { - public static readonly Dictionary CultureMap = new Dictionary - { - {"zh-Hans", "zh-CN"} - }; - + public const string PackageName = "bootstrap-datepicker"; + public override void ConfigureBundle(BundleConfigurationContext context) { context.Files.AddIfNotContains("/libs/bootstrap-datepicker/bootstrap-datepicker.min.js"); @@ -26,12 +24,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker ? "en" : CultureInfo.CurrentUICulture.Name; - TryAddCultureFile(context, MapCultureName(cultureName)); + TryAddCultureFile(context, cultureName); } protected virtual bool TryAddCultureFile(BundleConfigurationContext context, string cultureName) { - var filePath = $"/libs/bootstrap-datepicker/locales/bootstrap-datepicker.{cultureName}.min.js"; + var fileName = context.LocalizationOptions.GetLanguageFilesMap(PackageName, cultureName); + var filePath = $"/libs/bootstrap-datepicker/locales/bootstrap-datepicker.{fileName}.min.js"; if (!context.FileProvider.GetFileInfo(filePath).Exists) { @@ -41,11 +40,5 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker context.Files.AddIfNotContains(filePath); return true; } - - protected virtual string MapCultureName(string cultureName) - { - return CultureMap.GetOrDefault(cultureName) ?? - cultureName; - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/JQueryValidation/JQueryValidationScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/JQueryValidation/JQueryValidationScriptContributor.cs index b4acf05ec6..586d8864cd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/JQueryValidation/JQueryValidationScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/JQueryValidation/JQueryValidationScriptContributor.cs @@ -2,6 +2,7 @@ using System.Globalization; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; +using Volo.Abp.Localization; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryValidation @@ -11,6 +12,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryValidation { public const string DefaultLocalizationFolder = "/libs/jquery-validation/localization/"; + public const string PackageName = "jquery-validation"; + public override void ConfigureBundle(BundleConfigurationContext context) { context.Files.AddIfNotContains("/libs/jquery-validation/jquery.validate.js"); @@ -25,7 +28,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryValidation var cultureName = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName.Replace('-', '_'); - if (TryAddCultureFile(context, MapCultureName(cultureName))) + var fileName = context.LocalizationOptions.GetLanguageFilesMap(PackageName, cultureName); + if (TryAddCultureFile(context, fileName)) { return; } @@ -35,7 +39,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryValidation return; } - TryAddCultureFile(context, MapCultureName(cultureName.Substring(0, cultureName.IndexOf('_')))); + fileName = context.LocalizationOptions.GetLanguageFilesMap(PackageName, + cultureName.Substring(0, cultureName.IndexOf('_'))); + TryAddCultureFile(context, fileName); } protected virtual bool TryAddCultureFile(BundleConfigurationContext context, string cultureName) @@ -51,10 +57,5 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryValidation context.Files.AddIfNotContains(filePath); return true; } - - protected virtual string MapCultureName(string cultureName) - { - return cultureName; - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js index beef0d1f0c..56869350d5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js @@ -1,4 +1,4 @@ -(function ($) { +(function ($) { abp.dom = abp.dom || {}; @@ -73,25 +73,12 @@ abp.libs = abp.libs = abp.libs || {}; abp.libs.bootstrapDatepicker = { - languageMap: { - 'zh-Hans': 'zh-CN' - }, - mapLanguageName: function (name) { - return abp.libs.bootstrapDatepicker.languageMap[abp.localization.currentCulture.name] || name; - }, - isLanguageMapped: function (name) { - return abp.libs.bootstrapDatepicker.languageMap[abp.localization.currentCulture.name] !== undefined; - }, - getCurrentLanguageConfig: function () { - var mappedName = abp.libs.bootstrapDatepicker.mapLanguageName(abp.localization.currentCulture.name); - return $.fn.datepicker.dates[mappedName]; - }, + packageName: "bootstrap-datepicker", normalizeLanguageConfig: function () { - var languageConfig = abp.libs.bootstrapDatepicker.getCurrentLanguageConfig(); - if (languageConfig) { - if (!languageConfig.format || abp.libs.bootstrapDatepicker.isLanguageMapped(abp.localization.currentCulture.name)) { - languageConfig.format = abp.localization.currentCulture.dateTimeFormat.shortDatePattern.toLowerCase(); - } + var language = abp.localization.getLanguagesMap(this.packageName); + var languageConfig = $.fn.datepicker.dates[language]; + if (languageConfig && (!languageConfig.format || language !== abp.localization.currentCulture.name)) { + languageConfig.format = abp.localization.currentCulture.dateTimeFormat.shortDatePattern.toLowerCase(); } }, getFormattedValue: function (isoFormattedValue) { @@ -108,7 +95,7 @@ return { todayBtn: "linked", autoclose: true, - language: abp.libs.bootstrapDatepicker.mapLanguageName(abp.localization.currentCulture.cultureName) + language: abp.localization.getLanguagesMap(this.packageName) }; } }; @@ -132,7 +119,6 @@ abp.dom.initializers.initializeToolTips(args.$el.findWithSelf('[data-toggle="tooltip"]')); abp.dom.initializers.initializePopovers(args.$el.findWithSelf('[data-toggle="popover"]')); abp.dom.initializers.initializeTimeAgos(args.$el.findWithSelf('.timeago')); - abp.dom.initializers.initializeDatepickers(args.$el); abp.dom.initializers.initializeForms(args.$el.findWithSelf('form'), true); abp.dom.initializers.initializeScript(args.$el); }); @@ -156,4 +142,4 @@ $('[data-auto-focus="true"]').first().findWithSelf('input,select').focus(); }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index 6ad5b3276e..5aa8f07059 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -171,6 +171,9 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations ); } + localizationConfig.LanguagesMap = _localizationOptions.LanguagesMap; + localizationConfig.LanguageFilesMap = _localizationOptions.LanguageFilesMap; + return localizationConfig; } @@ -267,4 +270,4 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations }; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptions.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptions.cs index aba376d773..8974237f7d 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptions.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptions.cs @@ -17,11 +17,17 @@ namespace Volo.Abp.Localization public List Languages { get; } + public Dictionary> LanguagesMap { get; } + + public Dictionary> LanguageFilesMap { get; } + public AbpLocalizationOptions() { Resources = new LocalizationResourceDictionary(); GlobalContributors = new TypeList(); Languages = new List(); + LanguagesMap = new Dictionary>(); + LanguageFilesMap = new Dictionary>(); } } } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptionsExtensions.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptionsExtensions.cs new file mode 100644 index 0000000000..36355027db --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptionsExtensions.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.Localization +{ + public static class AbpLocalizationOptionsExtensions + { + public static AbpLocalizationOptions AddLanguagesMapOrUpdate(this AbpLocalizationOptions localizationOptions, + string packageName, params NameValue[] maps) + { + foreach (var map in maps) + { + AddOrUpdate(localizationOptions.LanguagesMap, packageName, map); + } + + return localizationOptions; + } + + public static string GetLanguagesMap(this AbpLocalizationOptions localizationOptions, string packageName, + string language) + { + return localizationOptions.LanguagesMap.TryGetValue(packageName, out var maps) + ? maps.FirstOrDefault(x => x.Name == language)?.Value ?? language + : language; + } + + public static AbpLocalizationOptions AddLanguageFilesMapOrUpdate(this AbpLocalizationOptions localizationOptions, + string packageName, params NameValue[] maps) + { + foreach (var map in maps) + { + AddOrUpdate(localizationOptions.LanguageFilesMap, packageName, map); + } + + return localizationOptions; + } + + public static string GetLanguageFilesMap(this AbpLocalizationOptions localizationOptions, string packageName, + string language) + { + return localizationOptions.LanguageFilesMap.TryGetValue(packageName, out var maps) + ? maps.FirstOrDefault(x => x.Name == language)?.Value ?? language + : language; + } + + private static void AddOrUpdate(IDictionary> maps, string packageName, NameValue value) + { + if (maps.TryGetValue(packageName, out var existMaps)) + { + existMaps.GetOrAdd(x => x.Name == value.Name, () => value).Value = value.Value; + } + else + { + maps.Add(packageName, new List {value}); + } + } + } +} diff --git a/npm/packs/core/src/abp.js b/npm/packs/core/src/abp.js index 190143da03..41837d2368 100644 --- a/npm/packs/core/src/abp.js +++ b/npm/packs/core/src/abp.js @@ -136,6 +136,38 @@ var abp = abp || {}; }; abp.localization.defaultResourceName = undefined; + abp.localization.currentCulture = { + cultureName: undefined + }; + + var getMapValue = function (packageMaps, packageName, language) { + language = language || abp.localization.currentCulture.name; + if (!packageMaps || !packageName || !language) { + return language; + } + + var packageMap = packageMaps[packageName]; + if (!packageMap) { + return language; + } + + for (var i = 0; i < packageMap.length; i++) { + var map = packageMap[i]; + if (map.name === language){ + return map.value; + } + } + + return language; + }; + + abp.localization.getLanguagesMap = function (packageName, language) { + return getMapValue(abp.localization.languagesMap, packageName, language); + }; + + abp.localization.getLanguageFilesMap = function (packageName, language) { + return getMapValue(abp.localization.languageFilesMap, packageName, language); + }; /* AUTHORIZATION **********************************************/ @@ -330,7 +362,7 @@ var abp = abp || {}; }; /* opts: { - * + * * } */ abp.ui.unblock = function (opts) { @@ -584,7 +616,7 @@ var abp = abp || {}; * This is a simple implementation created to be used by ABP. * Please use a complete cookie library if you need. * @param {string} key - * @param {string} value + * @param {string} value * @param {Date} expireDate (optional). If not specified the cookie will expire at the end of session. * @param {string} path (optional) */