asp.net core multilingual internationalization custom resource file, core resource file
Let's talk about asp.net core's default multilingual and internationalization. Official document
1: Basic usage
First install the package Microsoft.AspNetCore.Mvc.Localization (depending on Microsoft.Extensions.Localization), and then use the resource file to save data corresponding to different languages.
1. Inject IViewLocalizer in the view page, and then use it where needed. such as:
1 @inject IViewLocalizer Localizer
2
3 <h2> @Localizer ["hello"] </ h2>
The characters in the brackets are the names in the resource file. After running, the output is the resource value set under the resource file corresponding to the current language.
So there is a question, how to set the resource file? 1. By default, it will look for the folder corresponding to the set LocalizationOptions.ResourcesPath value. If it is not set, it will look under the root directory.
Set ResourcesPath in Startup.
services.AddLocalization (options => options.ResourcesPath = "Resources");
2. Find the resource file with the same name as the current view file. There are two ways to search by dot. And path by default. Of course, you can also specify one of them. For example, the current view path is views / account / login.cshtml, then the resource files to look up are views / account / login. {CultureName} .resx file and views.account.login. {CultureName} .resx file
services.AddMvc ()
.AddViewLocalization ()
//.AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.SubFolder)
.AddDataAnnotationsLocalization ();
3. If it is a model class, the search path becomes the full namespace of the typeof (model) .FullName namespace corresponding to the model class. Examples are the ViewModels / account / login. {CultureName} .resx file and the ViewModels.account.login. {CultureName} .resx file. Similarly, if it is in the controller, the resource file is Controllers.HomeController. {CultureName} .resx or Controllers / HomeController. {CultureName} .resx
2: Analysis
So how does this work? If I want to use a database or a json file to store these resource files.
The IViewLocalizer interface is injected into the attempt file, and the corresponding implementation is ViewLocalizer. ViewLocalizer implements the definitions of IViewLocalizer and IHtmlLocalizer, and IViewLocalizer inherits from IHtmlLocalizer. ViewLocalizer will inject an IHtmlLocalizerFactory, and then use IHtmlLocalizerFactory to create an instance of IHtmlLocalizer. When creating, two parameters will be brought in, one is the path currently being tried, and one is the current application name.
IHtmlLocalizer is defined as follows:
So in the instance of IHtmlLocalizer, you can easily get the corresponding value.
Because ViewLocalizer will inject an instance of IHtmlLocalizerFactory. The default instance is HtmlLocalizerFactory. An instance of IStringLocalizerFactory (located in Microsoft.Extensions.Localization.Abstractions) is injected into the constructor of HtmlLocalizerFactory.
Is defined as
The definition of IHtmlLocalizerFactory is
It can be said that HtmlLocalizerFactory is a wrapper for HtmlLocalizerFactory.
Check the code to know that the default implementation of IStringLocalizerFactory is ResourceManagerStringLocalizerFactory, and reading resource files is the operation of this implementation.
Back to the question at the beginning, suppose I want to use a json file instead of a resx file. How to achieve it. ? There are 2 ways
1) Just implement the corresponding IStringLocalizerFactory and replace the default ResourceManagerStringLocalizerFactory.
2) Override ResourceManagerStringLocalizerFactory.
1) 1. Define a JsonStringLocalizerFactory and implement 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)
asp.net core multilingual internationalization custom resource file, core resource file
Let's talk about asp.net core's default multilingual and internationalization. Official document
1: Basic usage
First install the package Microsoft.AspNetCore.Mvc.Localization (depending on Microsoft.Extensions.Localization), and then use the resource file to save data corresponding to different languages.
1. Inject IViewLocalizer in the view page, and then use it where needed. such as:
1 @inject IViewLocalizer Localizer
2
3 <h2> @Localizer ["hello"] </ h2>
The characters in the brackets are the names in the resource file. After running, the output is the resource value set under the resource file corresponding to the current language.
So there is a question, how to set the resource file? 1. By default, it will look for the folder corresponding to the set LocalizationOptions.ResourcesPath value. If it is not set, it will look under the root directory.
Set ResourcesPath in Startup.
services.AddLocalization (options => options.ResourcesPath = "Resources");
2. Find the resource file with the same name as the current view file. There are two ways to search by dot. And path by default. Of course, you can also specify one of them. For example, the current view path is views / account / login.cshtml, then the resource files to look up are views / account / login. {CultureName} .resx file and views.account.login. {CultureName} .resx file
services.AddMvc ()
.AddViewLocalization ()
//.AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.SubFolder)
.AddDataAnnotationsLocalization ();
3. If it is a model class, the search path becomes the full namespace of the typeof (model) .FullName namespace corresponding to the model class. Examples are the ViewModels / account / login. {CultureName} .resx file and the ViewModels.account.login. {CultureName} .resx file. Similarly, if it is in the controller, the resource file is Controllers.HomeController. {CultureName} .resx or Controllers / HomeController. {CultureName} .resx
2: Analysis
So how does this work? If I want to use a database or a json file to store these resource files.
The IViewLocalizer interface is injected into the attempt file, and the corresponding implementation is ViewLocalizer. ViewLocalizer implements the definitions of IViewLocalizer and IHtmlLocalizer, and IViewLocalizer inherits from IHtmlLocalizer. ViewLocalizer will inject an IHtmlLocalizerFactory, and then use IHtmlLocalizerFactory to create an instance of IHtmlLocalizer. When creating, two parameters will be brought in, one is the path currently being tried, and one is the current application name.
IHtmlLocalizer is defined as follows:
So in the instance of IHtmlLocalizer, you can easily get the corresponding value.
Because ViewLocalizer will inject an instance of IHtmlLocalizerFactory. The default instance is HtmlLocalizerFactory. An instance of IStringLocalizerFactory (located in Microsoft.Extensions.Localization.Abstractions) is injected into the constructor of HtmlLocalizerFactory.
Is defined as
The definition of IHtmlLocalizerFactory is
It can be said that HtmlLocalizerFactory is a wrapper for HtmlLocalizerFactory.
Check the code to know that the default implementation of IStringLocalizerFactory is ResourceManagerStringLocalizerFactory, and reading resource files is the operation of this implementation.
Back to the question at the beginning, suppose I want to use a json file instead of a resx file. How to achieve it. ? There are 2 ways
1) Just implement the corresponding IStringLocalizerFactory and replace the default ResourceManagerStringLocalizerFactory.
2) Override ResourceManagerStringLocalizerFactory.
1) 1. Define a JsonStringLocalizerFactory and implement 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)
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. Add injection
services.AddSingleton <IStringLocalizerFactory, JsonStringLocalizerFactory> ();
4, json file
The above code simply implements a json file using dots (.) As a separator as a resource file. (Actually there is a small problem after the above code runs)
The code has been placed on Github
2). To be realized ~~~
Link: http://blog.wuliping.cn/post/aspnet-core-localization-and-custom-resource-service-with-file