先說說 asp.net core 預設的多語言和國際化。 官方文檔 一:基本使用方法 先要安裝 包 Microsoft.AspNetCore.Mvc.Localization (依賴 Microsoft.Extensions.Localization) 然後使用 資源文件保存不同的語言對應的數據。 ...
先說說 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