asp.net core 之多語言國際化自定義資源文件


先說說 asp.net core 默認的多語言和國際化。 官方文檔

一:基本使用方法

先要安裝 包 Microsoft.AspNetCore.Mvc.Localization (依賴 Microsoft.Extensions.Localization)  然后使用 資源文件保存不同的語言對應的數據。

1,在視圖頁面注入 IViewLocalizer ,然后在需要的地方使用即可。 比如:

1 @inject IViewLocalizer Localizer
2 
3 <h2>@Localizer["hello"]</h2>

 

其中 中括號中的字符 即是資源文件中的名稱, 運行后,輸出的即是 當前語言對應的資源文件下的設置的資源值。

那么有個問題來了,資源文件怎么設置?

1,默認情況下會去查找 設置的 LocalizationOptions.ResourcesPath  的值對應的文件夾,如果沒有設置,則去根目錄下查找。

在 Startup 中設置 ResourcesPath  。

services.AddLocalization(options => options.ResourcesPath = "Resources");

2,查找當前視圖文件對應的同名資源文件。 默認支持 使用 點 . 和路徑 path 查找兩種方式,當然也可以指定其中一個方式。 比如 當前視圖路徑是 views/account/login.cshtml ,那么 查找的資源文件是  views/account/login.{CultureName}.resx 文件和 views.account.login.{CultureName}.resx 文件 

services.AddMvc()
         .AddViewLocalization()
          //.AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.SubFolder)
          .AddDataAnnotationsLocalization();

3,如果是 model 類, 查找的路徑則變成了model 類對應的命名空間即typeof(model).FullName 全路徑。比如 ViewModels/account/login.{CultureName}.resx 文件和 ViewModels.account.login.{CultureName}.resx 文件 。同理 如果是在controller  那么,資源文件 則是  Controllers.HomeController.{CultureName}.resx 或者 Controllers/HomeController.{CultureName}.resx


二:解析

那么這個是如何實現的呢?如果我想使用 數據庫或者是 json 文件來存在這些資源文件。

在試圖文件中 注入的是 IViewLocalizer 接口,對應的實現是  ViewLocalizer 。ViewLocalizer 實現了IViewLocalizer 和IHtmlLocalizer 的定義,並且 IViewLocalizer 繼承自IHtmlLocalizer。  ViewLocalizer 會注入一個IHtmlLocalizerFactory,然后 用 IHtmlLocalizerFactory創建一個 IHtmlLocalizer 對應的實例。 在創建的時候 會帶入兩個參數 ,一個是 當前 試圖的路徑,一個是當前應用名稱。 

 

IHtmlLocalizer 定義如下:

 

所以在 IHtmlLocalizer的實例中, 既可以輕松的獲取對應的值。

因為 ViewLocalizer 會注入一個IHtmlLocalizerFactory 的實例。默認的實例 是  HtmlLocalizerFactory , 在 HtmlLocalizerFactory 的構造函數中會注入一個 IStringLocalizerFactory 的實例(位於Microsoft.Extensions.Localization.Abstractions)。

的定義是 

 

而  IHtmlLocalizerFactory 的定義是 

 

可以說  HtmlLocalizerFactory 是對 HtmlLocalizerFactory 的一個包裝。

 

查閱代碼知道 默認 IStringLocalizerFactory 實現是 ResourceManagerStringLocalizerFactory ,並且讀取資源文件均是這個實現來操作。

 

回到開頭的問題,假設我要使用 json 文件 代替 resx 文件。該如何實現呢,。?  有2種方法

1)只要實現對應的 IStringLocalizerFactory 並且代替默認的 ResourceManagerStringLocalizerFactory 。

2)重寫 ResourceManagerStringLocalizerFactory 。

1) 1,定義一個  JsonStringLocalizerFactory 並實現 IStringLocalizerFactory 。

public class JsonStringLocalizerFactory : IStringLocalizerFactory
    {
        private readonly string _applicationName;
        private readonly IHostingEnvironment _hostingEnvironment;
        private readonly LocalizationOptions _options;
        public JsonStringLocalizerFactory(IHostingEnvironment hostingEnvironment, IOptions<LocalizationOptions> localizationOptions)
        {
            if (localizationOptions == null)
            {
                throw new ArgumentNullException(nameof(localizationOptions));
            }
            this._hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
            this._options = localizationOptions.Value;
            this._applicationName = hostingEnvironment.ApplicationName;
        }

        public IStringLocalizer Create(Type resourceSource)
        {
            TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(resourceSource);
            //Assembly assembly = typeInfo.Assembly;
            //AssemblyName assemblyName = new AssemblyName(assembly.FullName);

            string baseResourceName = typeInfo.FullName;
            baseResourceName = TrimPrefix(baseResourceName, _applicationName + ".");

            return new JsonStringLocalizer(_hostingEnvironment, _options, baseResourceName, null);
        }

        public IStringLocalizer Create(string baseName, string location)
        {
            location = location ?? _applicationName;

            string baseResourceName = baseName;
            baseResourceName = TrimPrefix(baseName, location + ".");

            return new JsonStringLocalizer(_hostingEnvironment, _options, baseResourceName, null);
        }

        private static string TrimPrefix(string name, string prefix)
        {
            if (name.StartsWith(prefix, StringComparison.Ordinal))
            {
                return name.Substring(prefix.Length);
            }

            return name;
        }
    }

 

2, JsonStringLocalizer

public class JsonStringLocalizer : IStringLocalizer
    {
        private readonly ConcurrentDictionary<string, string> _all;

        private readonly IHostingEnvironment _hostingEnvironment;
        private readonly LocalizationOptions _options;

        private readonly string _baseResourceName;
        private readonly CultureInfo _cultureInfo;

        public LocalizedString this[string name] => Get(name);
        public LocalizedString this[string name, params object[] arguments] => Get(name, arguments);

        public JsonStringLocalizer(IHostingEnvironment hostingEnvironment, LocalizationOptions options, string baseResourceName, CultureInfo culture)
        {
            _options = options;
            _hostingEnvironment = hostingEnvironment;

            _cultureInfo = culture ?? CultureInfo.CurrentUICulture;
            _baseResourceName = baseResourceName + "." + _cultureInfo.Name;
            _all = GetAll();

        }

        public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
        {
            return _all.Select(t => new LocalizedString(t.Key, t.Value, true)).ToArray();
        }

        public IStringLocalizer WithCulture(CultureInfo culture)
        {
            if (culture == null)
                return this;

            CultureInfo.CurrentUICulture = culture;
            CultureInfo.DefaultThreadCurrentCulture = culture;

            return new JsonStringLocalizer(_hostingEnvironment, _options, _baseResourceName, culture);
        }

        private LocalizedString Get(string name, params object[] arguments)
        {
            if (_all.ContainsKey(name))
            {
                var current = _all[name];
                return new LocalizedString(name, string.Format(_all[name], arguments));
            }
            return new LocalizedString(name, name, true);
        }

        private ConcurrentDictionary<string, string> GetAll()
        {
            var file = Path.Combine(_hostingEnvironment.ContentRootPath, _baseResourceName + ".json");
            if (!string.IsNullOrEmpty(_options.ResourcesPath))
                file = Path.Combine(_hostingEnvironment.ContentRootPath, _options.ResourcesPath, _baseResourceName + ".json");

            Debug.WriteLineIf(!File.Exists(file), "Path not found! " + file);

            if (!File.Exists(file))
                return new ConcurrentDictionary<string, string>();

            try
            {
                var txt = File.ReadAllText(file);

                return JsonConvert.DeserializeObject<ConcurrentDictionary<string, string>>(txt);
            }
            catch (Exception)
            {
            }

            return new ConcurrentDictionary<string, string>();
        }
    }

 

3,添加注入 

services.AddSingleton<IStringLocalizerFactory, JsonStringLocalizerFactory>();

 

4,json 文件

 

上面的代碼只是簡單的實現了 使用 點(.) 作為分隔符的json 文件作為資源文件。(其實上面的代碼運行后有個小問題)

 

代碼已經放到 Github 

 

2)。待實現~~~ 

 

鏈接:http://blog.wuliping.cn/post/aspnet-core-localization-and-custom-resource-service-with-file


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM