ASP.NET核心:以编程方式设置本地化视图的资源文件
ASP.NET Core: programatically set the resource file for localized view
我有一个用 ASP.NET Core v2.1 编写的网络应用程序。该应用程序使用通过 LocalizationOptions.ResourcesPath = "Resources"
配置的本地化视图,并通过在 cshtml 文件中注入 IViewLocalizer
来访问本地化字符串。
在某些情况下,我想使用与位于 Resources
文件夹中的默认资源文件不同的资源文件来渲染视图。另一个资源文件具有与默认资源文件相同的键(无需更改视图),因此只会呈现不同的文本。
例如在控制器中考虑这样的操作方法并查看评论我想解决的问题:
public async Task<IActionResult> ShowSomething([FromQuery] bool useDifferentResource)
{
if (useDifferentResource)
{
// how to render the view which will use different resource file
// then the default found in Resources folder?
// return View("MyView");
}
// this renders the view with the default resource file found in Resources folder
return View("MyView");
}
首先我不太确定你的要求的必要性。但是通过深入研究源代码,我发现这是可能的,并在这里提出了解决方案。
实际上 IViewLocalizer
是由注册为单例的 IHtmlLocalizerFactory
实例化的。这取决于 IStringLocalizerFactory
也被注册为单例。这里我们使用资源文件(由 ResourceManager
管理)进行本地化,因此实现 class 是 ResourceManagerStringLocalizerFactory
。
该工厂使用选项 LocalizationOptions
来获取配置的 ResourcesPath
用于创建 IStringLocalizer
的实例,该实例由 HtmlLocalizer
包装并最终包装在 ViewLocalizer
中。这里的要点是结果由缓存键缓存,缓存键取决于 view/page 路径和程序集的名称(资源嵌入其中)。所以在第一次创建 ViewLocalizer
的实例(通过 DI 可用)之后,它将被缓存,你没有机会更改配置的 ResourcesPath
或拦截以某种方式更改它。
这意味着我们需要一个自定义的ResourceManagerStringLocalizerFactory
来覆盖Create
方法(实际上它不是虚拟的,但我们可以重新实现它)。我们需要在缓存键中再包含一个因素(运行时资源路径),这样缓存才能正常工作。 ResourceManagerStringLocalizerFactory
中还有一个虚拟方法可以被覆盖以提供您的运行时资源路径:GetResourceLocationAttribute
。为了最小化自定义 ResourceManagerStringLocalizerFactory
的实现代码,我选择了要覆盖的方法。通过阅读源代码可以看出,提供自己的运行时资源路径并不是唯一的拦截点,但它似乎是最简单的。
这是核心原则。然而,当涉及到完整解决方案的实施时,事情就没那么简单了。这是完整的代码:
/// <summary>
/// A ViewLocalizer that can be aware of the request feature IActiveViewLocalizerFeature to use instead of
/// basing on the default implementation of ViewLocalizer
/// </summary>
public class ActiveLocalizerAwareViewLocalizer : ViewLocalizer
{
readonly IHttpContextAccessor _httpContextAccessor;
public ActiveLocalizerAwareViewLocalizer(IHtmlLocalizerFactory localizerFactory, IHostingEnvironment hostingEnvironment,
IHttpContextAccessor httpContextAccessor) : base(localizerFactory, hostingEnvironment)
{
_httpContextAccessor = httpContextAccessor;
}
public override LocalizedHtmlString this[string key, params object[] arguments]
{
get
{
var localizer = _getActiveLocalizer();
return localizer == null ? base[key, arguments] : localizer[key, arguments];
}
}
public override LocalizedHtmlString this[string key]
{
get
{
var localizer = _getActiveLocalizer();
return localizer == null ? base[key] : localizer[key];
}
}
IHtmlLocalizer _getActiveLocalizer()
{
return _httpContextAccessor.HttpContext.Features.Get<IActiveViewLocalizerFeature>()?.ViewLocalizer;
}
}
public static class HtmlLocalizerFactoryWithRuntimeResourcesPathExtensions
{
public static T WithResourcesPath<T>(this T factory, string resourcesPath) where T : IHtmlLocalizerFactory
{
if (factory is IRuntimeResourcesPath overridableFactory)
{
overridableFactory.SetRuntimeResourcesPath(resourcesPath);
}
return factory;
}
}
public interface IActiveViewLocalizerFeature
{
IHtmlLocalizer ViewLocalizer { get; }
}
public class ActiveViewLocalizerFeature : IActiveViewLocalizerFeature
{
public ActiveViewLocalizerFeature(IHtmlLocalizer viewLocalizer)
{
ViewLocalizer = viewLocalizer;
}
public IHtmlLocalizer ViewLocalizer { get; }
}
public interface IRuntimeResourcesPath
{
string ResourcesPath { get; }
void SetRuntimeResourcesPath(string resourcesPath);
}
public class RuntimeResourcesPathHtmlLocalizerFactory : HtmlLocalizerFactory, IRuntimeResourcesPath
{
readonly IStringLocalizerFactory _stringLocalizerFactory;
public RuntimeResourcesPathHtmlLocalizerFactory(IStringLocalizerFactory localizerFactory) : base(localizerFactory)
{
_stringLocalizerFactory = localizerFactory;
}
//NOTE: the factory is registered as a singleton, so we need this to manage different resource paths used on different tasks
readonly AsyncLocal<string> _asyncResourcePath = new AsyncLocal<string>();
public string ResourcesPath => _asyncResourcePath.Value;
void IRuntimeResourcesPath.SetRuntimeResourcesPath(string resourcesPath)
{
_asyncResourcePath.Value = resourcesPath;
}
public override IHtmlLocalizer Create(string baseName, string location)
{
if (_stringLocalizerFactory is IRuntimeResourcesPath overridableFactory)
{
overridableFactory.SetRuntimeResourcesPath(ResourcesPath);
}
return base.Create(baseName, location);
}
}
public static class RuntimeResourcesPathHtmlLocalizerFactoryExtensions
{
/// <summary>
/// Creates an IHtmlLocalizer with a runtime resources path (instead of using the configured ResourcesPath)
/// </summary>
public static IHtmlLocalizer CreateWithResourcesPath(this IHtmlLocalizerFactory factory, string resourcesPath, string baseName, string location = null)
{
location = location ?? Assembly.GetEntryAssembly().GetName().Name;
var result = factory.WithResourcesPath(resourcesPath).Create(baseName, location);
factory.WithResourcesPath(null);
return result;
}
}
public static class RuntimeResourcesPathLocalizationExtensions
{
static IHtmlLocalizer _useLocalizer(ActionContext actionContext, string resourcesPath, string viewPath)
{
var factory = actionContext.HttpContext.RequestServices.GetRequiredService<IHtmlLocalizerFactory>();
viewPath = viewPath.Substring(0, viewPath.Length - Path.GetExtension(viewPath).Length).TrimStart('/', '\')
.Replace("/", ".").Replace("\", ".");
var location = Assembly.GetEntryAssembly().GetName().Name;
var localizer = factory.CreateWithResourcesPath(resourcesPath, viewPath, location);
actionContext.HttpContext.Features.Set<IActiveViewLocalizerFeature>(new ActiveViewLocalizerFeature(localizer));
return localizer;
}
/// <summary>
/// Can be used inside Controller
/// </summary>
public static IHtmlLocalizer UseLocalizer(this ActionContext actionContext, string resourcesPath, string viewOrPageName = null)
{
//find the view before getting the path
var razorViewEngine = actionContext.HttpContext.RequestServices.GetRequiredService<IRazorViewEngine>();
if (actionContext is ControllerContext cc)
{
viewOrPageName = viewOrPageName ?? cc.ActionDescriptor.ActionName;
var viewResult = razorViewEngine.FindView(actionContext, viewOrPageName, false);
return _useLocalizer(actionContext, resourcesPath, viewResult.View.Path);
}
var pageResult = razorViewEngine.FindPage(actionContext, viewOrPageName);
//NOTE: here we have pageResult.Page is an IRazorPage but we don't use that to call UseLocalizer
//because that IRazorPage instance has very less info (lacking ViewContext, PageContext ...)
//The only precious info we have here is the Page.Path
return _useLocalizer(actionContext, resourcesPath, pageResult.Page.Path);
}
/// <summary>
/// Can be used inside Razor View or Razor Page
/// </summary>
public static IHtmlLocalizer UseLocalizer(this IRazorPage razorPage, string resourcesPath)
{
var path = razorPage.ViewContext.ExecutingFilePath;
if (string.IsNullOrEmpty(path))
{
path = razorPage.ViewContext.View.Path;
}
if (path == null) return null;
return _useLocalizer(razorPage.ViewContext, resourcesPath, path);
}
/// <summary>
/// Can be used inside PageModel
/// </summary>
public static IHtmlLocalizer UseLocalizer(this PageModel pageModel, string resourcesPath)
{
return pageModel.PageContext.UseLocalizer(resourcesPath, pageModel.RouteData.Values["page"]?.ToString()?.TrimStart('/'));
}
}
开头提到的习俗ResourceManagerStringLocalizerFactory
:
public class RuntimeResourcesPathResourceManagerStringLocalizerFactory
: ResourceManagerStringLocalizerFactory, IRuntimeResourcesPath, IStringLocalizerFactory
{
readonly AsyncLocal<string> _asyncResourcePath = new AsyncLocal<string>();
public string ResourcesPath => _asyncResourcePath.Value;
private readonly ConcurrentDictionary<string, ResourceManagerStringLocalizer> _localizerCache =
new ConcurrentDictionary<string, ResourceManagerStringLocalizer>();
public RuntimeResourcesPathResourceManagerStringLocalizerFactory(IOptions<LocalizationOptions> localizationOptions, ILoggerFactory loggerFactory) : base(localizationOptions, loggerFactory)
{
}
protected override ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly)
{
//we is where we override the configured ResourcesPath and use the runtime ResourcesPath.
return ResourcesPath == null ? base.GetResourceLocationAttribute(assembly) : new ResourceLocationAttribute(ResourcesPath);
}
public void SetRuntimeResourcesPath(string resourcesPath)
{
_asyncResourcePath.Value = resourcesPath;
}
/// <summary>
/// Almost cloned from the source code of ResourceManagerStringLocalizerFactory
/// We need to re-implement this because the framework code caches the result of Create using a cache key depending on only baseName & location.
/// But here we introduce one more parameter of (runtime) ResourcesPath, so we need to include that in the cache key as well for
/// it to work properly (otherwise each time changing the runtime ResourcesPath, the same cached result will be returned, which is wrong).
/// </summary>
IStringLocalizer IStringLocalizerFactory.Create(string baseName, string location)
{
if (baseName == null)
{
throw new ArgumentNullException(nameof(baseName));
}
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
return _localizerCache.GetOrAdd($"B={baseName},L={location},R={ResourcesPath}", _ =>
{
var assemblyName = new AssemblyName(location);
var assembly = Assembly.Load(assemblyName);
baseName = GetResourcePrefix(baseName, location);
return CreateResourceManagerStringLocalizer(assembly, baseName);
});
}
}
另一个扩展 class 以帮助方便地注册自定义服务:
public static class RuntimeResourcesPathLocalizationServiceCollectionExtensions
{
public static IServiceCollection AddRuntimeResourcesPathForLocalization(this IServiceCollection services)
{
services.AddSingleton<IStringLocalizerFactory, RuntimeResourcesPathResourceManagerStringLocalizerFactory>();
services.AddSingleton<IHtmlLocalizerFactory, RuntimeResourcesPathHtmlLocalizerFactory>();
return services.AddSingleton<IViewLocalizer, ActiveLocalizerAwareViewLocalizer>();
}
}
我们还实现了自定义 IViewLocalizer
,以便它可以在您的代码中无缝使用。它的工作只是检查是否有通过 HttpContext
共享的 IHtmlLocalizer
的任何活动实例(作为称为 IActiveViewLocalizerFeature
的功能)。每个不同的运行时资源路径将创建不同的 IHtmlLocalizer
将作为活动定位器共享。通常在一个请求范围内(以及在视图上下文中),我们通常只需要使用一个运行时资源路径(在渲染视图之前指定的一开始)。
要注册自定义服务:
services.AddRuntimeResourcesPathForLocalization();
要将本地化程序与运行时资源路径一起使用:
public async Task<IActionResult> ShowSomething([FromQuery] bool useDifferentResource)
{
if (useDifferentResource)
{
this.UseLocalizer("resources path of your choice");
}
return View("MyView");
}
注意:Controller 或 PageModel 范围内的 UseLocalizer
不是很有效,因为需要额外的逻辑来查找 view/page(使用 IRazorViewEngine
正如你在代码中看到的那样)。因此,如果可能,您应该将 UseLocalizer
移至 RazorPage
或 View
。切换条件可以通过视图模型或任何其他方式(查看数据,查看包,...)传递。
我有一个用 ASP.NET Core v2.1 编写的网络应用程序。该应用程序使用通过 LocalizationOptions.ResourcesPath = "Resources"
配置的本地化视图,并通过在 cshtml 文件中注入 IViewLocalizer
来访问本地化字符串。
在某些情况下,我想使用与位于 Resources
文件夹中的默认资源文件不同的资源文件来渲染视图。另一个资源文件具有与默认资源文件相同的键(无需更改视图),因此只会呈现不同的文本。
例如在控制器中考虑这样的操作方法并查看评论我想解决的问题:
public async Task<IActionResult> ShowSomething([FromQuery] bool useDifferentResource)
{
if (useDifferentResource)
{
// how to render the view which will use different resource file
// then the default found in Resources folder?
// return View("MyView");
}
// this renders the view with the default resource file found in Resources folder
return View("MyView");
}
首先我不太确定你的要求的必要性。但是通过深入研究源代码,我发现这是可能的,并在这里提出了解决方案。
实际上 IViewLocalizer
是由注册为单例的 IHtmlLocalizerFactory
实例化的。这取决于 IStringLocalizerFactory
也被注册为单例。这里我们使用资源文件(由 ResourceManager
管理)进行本地化,因此实现 class 是 ResourceManagerStringLocalizerFactory
。
该工厂使用选项 LocalizationOptions
来获取配置的 ResourcesPath
用于创建 IStringLocalizer
的实例,该实例由 HtmlLocalizer
包装并最终包装在 ViewLocalizer
中。这里的要点是结果由缓存键缓存,缓存键取决于 view/page 路径和程序集的名称(资源嵌入其中)。所以在第一次创建 ViewLocalizer
的实例(通过 DI 可用)之后,它将被缓存,你没有机会更改配置的 ResourcesPath
或拦截以某种方式更改它。
这意味着我们需要一个自定义的ResourceManagerStringLocalizerFactory
来覆盖Create
方法(实际上它不是虚拟的,但我们可以重新实现它)。我们需要在缓存键中再包含一个因素(运行时资源路径),这样缓存才能正常工作。 ResourceManagerStringLocalizerFactory
中还有一个虚拟方法可以被覆盖以提供您的运行时资源路径:GetResourceLocationAttribute
。为了最小化自定义 ResourceManagerStringLocalizerFactory
的实现代码,我选择了要覆盖的方法。通过阅读源代码可以看出,提供自己的运行时资源路径并不是唯一的拦截点,但它似乎是最简单的。
这是核心原则。然而,当涉及到完整解决方案的实施时,事情就没那么简单了。这是完整的代码:
/// <summary>
/// A ViewLocalizer that can be aware of the request feature IActiveViewLocalizerFeature to use instead of
/// basing on the default implementation of ViewLocalizer
/// </summary>
public class ActiveLocalizerAwareViewLocalizer : ViewLocalizer
{
readonly IHttpContextAccessor _httpContextAccessor;
public ActiveLocalizerAwareViewLocalizer(IHtmlLocalizerFactory localizerFactory, IHostingEnvironment hostingEnvironment,
IHttpContextAccessor httpContextAccessor) : base(localizerFactory, hostingEnvironment)
{
_httpContextAccessor = httpContextAccessor;
}
public override LocalizedHtmlString this[string key, params object[] arguments]
{
get
{
var localizer = _getActiveLocalizer();
return localizer == null ? base[key, arguments] : localizer[key, arguments];
}
}
public override LocalizedHtmlString this[string key]
{
get
{
var localizer = _getActiveLocalizer();
return localizer == null ? base[key] : localizer[key];
}
}
IHtmlLocalizer _getActiveLocalizer()
{
return _httpContextAccessor.HttpContext.Features.Get<IActiveViewLocalizerFeature>()?.ViewLocalizer;
}
}
public static class HtmlLocalizerFactoryWithRuntimeResourcesPathExtensions
{
public static T WithResourcesPath<T>(this T factory, string resourcesPath) where T : IHtmlLocalizerFactory
{
if (factory is IRuntimeResourcesPath overridableFactory)
{
overridableFactory.SetRuntimeResourcesPath(resourcesPath);
}
return factory;
}
}
public interface IActiveViewLocalizerFeature
{
IHtmlLocalizer ViewLocalizer { get; }
}
public class ActiveViewLocalizerFeature : IActiveViewLocalizerFeature
{
public ActiveViewLocalizerFeature(IHtmlLocalizer viewLocalizer)
{
ViewLocalizer = viewLocalizer;
}
public IHtmlLocalizer ViewLocalizer { get; }
}
public interface IRuntimeResourcesPath
{
string ResourcesPath { get; }
void SetRuntimeResourcesPath(string resourcesPath);
}
public class RuntimeResourcesPathHtmlLocalizerFactory : HtmlLocalizerFactory, IRuntimeResourcesPath
{
readonly IStringLocalizerFactory _stringLocalizerFactory;
public RuntimeResourcesPathHtmlLocalizerFactory(IStringLocalizerFactory localizerFactory) : base(localizerFactory)
{
_stringLocalizerFactory = localizerFactory;
}
//NOTE: the factory is registered as a singleton, so we need this to manage different resource paths used on different tasks
readonly AsyncLocal<string> _asyncResourcePath = new AsyncLocal<string>();
public string ResourcesPath => _asyncResourcePath.Value;
void IRuntimeResourcesPath.SetRuntimeResourcesPath(string resourcesPath)
{
_asyncResourcePath.Value = resourcesPath;
}
public override IHtmlLocalizer Create(string baseName, string location)
{
if (_stringLocalizerFactory is IRuntimeResourcesPath overridableFactory)
{
overridableFactory.SetRuntimeResourcesPath(ResourcesPath);
}
return base.Create(baseName, location);
}
}
public static class RuntimeResourcesPathHtmlLocalizerFactoryExtensions
{
/// <summary>
/// Creates an IHtmlLocalizer with a runtime resources path (instead of using the configured ResourcesPath)
/// </summary>
public static IHtmlLocalizer CreateWithResourcesPath(this IHtmlLocalizerFactory factory, string resourcesPath, string baseName, string location = null)
{
location = location ?? Assembly.GetEntryAssembly().GetName().Name;
var result = factory.WithResourcesPath(resourcesPath).Create(baseName, location);
factory.WithResourcesPath(null);
return result;
}
}
public static class RuntimeResourcesPathLocalizationExtensions
{
static IHtmlLocalizer _useLocalizer(ActionContext actionContext, string resourcesPath, string viewPath)
{
var factory = actionContext.HttpContext.RequestServices.GetRequiredService<IHtmlLocalizerFactory>();
viewPath = viewPath.Substring(0, viewPath.Length - Path.GetExtension(viewPath).Length).TrimStart('/', '\')
.Replace("/", ".").Replace("\", ".");
var location = Assembly.GetEntryAssembly().GetName().Name;
var localizer = factory.CreateWithResourcesPath(resourcesPath, viewPath, location);
actionContext.HttpContext.Features.Set<IActiveViewLocalizerFeature>(new ActiveViewLocalizerFeature(localizer));
return localizer;
}
/// <summary>
/// Can be used inside Controller
/// </summary>
public static IHtmlLocalizer UseLocalizer(this ActionContext actionContext, string resourcesPath, string viewOrPageName = null)
{
//find the view before getting the path
var razorViewEngine = actionContext.HttpContext.RequestServices.GetRequiredService<IRazorViewEngine>();
if (actionContext is ControllerContext cc)
{
viewOrPageName = viewOrPageName ?? cc.ActionDescriptor.ActionName;
var viewResult = razorViewEngine.FindView(actionContext, viewOrPageName, false);
return _useLocalizer(actionContext, resourcesPath, viewResult.View.Path);
}
var pageResult = razorViewEngine.FindPage(actionContext, viewOrPageName);
//NOTE: here we have pageResult.Page is an IRazorPage but we don't use that to call UseLocalizer
//because that IRazorPage instance has very less info (lacking ViewContext, PageContext ...)
//The only precious info we have here is the Page.Path
return _useLocalizer(actionContext, resourcesPath, pageResult.Page.Path);
}
/// <summary>
/// Can be used inside Razor View or Razor Page
/// </summary>
public static IHtmlLocalizer UseLocalizer(this IRazorPage razorPage, string resourcesPath)
{
var path = razorPage.ViewContext.ExecutingFilePath;
if (string.IsNullOrEmpty(path))
{
path = razorPage.ViewContext.View.Path;
}
if (path == null) return null;
return _useLocalizer(razorPage.ViewContext, resourcesPath, path);
}
/// <summary>
/// Can be used inside PageModel
/// </summary>
public static IHtmlLocalizer UseLocalizer(this PageModel pageModel, string resourcesPath)
{
return pageModel.PageContext.UseLocalizer(resourcesPath, pageModel.RouteData.Values["page"]?.ToString()?.TrimStart('/'));
}
}
开头提到的习俗ResourceManagerStringLocalizerFactory
:
public class RuntimeResourcesPathResourceManagerStringLocalizerFactory
: ResourceManagerStringLocalizerFactory, IRuntimeResourcesPath, IStringLocalizerFactory
{
readonly AsyncLocal<string> _asyncResourcePath = new AsyncLocal<string>();
public string ResourcesPath => _asyncResourcePath.Value;
private readonly ConcurrentDictionary<string, ResourceManagerStringLocalizer> _localizerCache =
new ConcurrentDictionary<string, ResourceManagerStringLocalizer>();
public RuntimeResourcesPathResourceManagerStringLocalizerFactory(IOptions<LocalizationOptions> localizationOptions, ILoggerFactory loggerFactory) : base(localizationOptions, loggerFactory)
{
}
protected override ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly)
{
//we is where we override the configured ResourcesPath and use the runtime ResourcesPath.
return ResourcesPath == null ? base.GetResourceLocationAttribute(assembly) : new ResourceLocationAttribute(ResourcesPath);
}
public void SetRuntimeResourcesPath(string resourcesPath)
{
_asyncResourcePath.Value = resourcesPath;
}
/// <summary>
/// Almost cloned from the source code of ResourceManagerStringLocalizerFactory
/// We need to re-implement this because the framework code caches the result of Create using a cache key depending on only baseName & location.
/// But here we introduce one more parameter of (runtime) ResourcesPath, so we need to include that in the cache key as well for
/// it to work properly (otherwise each time changing the runtime ResourcesPath, the same cached result will be returned, which is wrong).
/// </summary>
IStringLocalizer IStringLocalizerFactory.Create(string baseName, string location)
{
if (baseName == null)
{
throw new ArgumentNullException(nameof(baseName));
}
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
return _localizerCache.GetOrAdd($"B={baseName},L={location},R={ResourcesPath}", _ =>
{
var assemblyName = new AssemblyName(location);
var assembly = Assembly.Load(assemblyName);
baseName = GetResourcePrefix(baseName, location);
return CreateResourceManagerStringLocalizer(assembly, baseName);
});
}
}
另一个扩展 class 以帮助方便地注册自定义服务:
public static class RuntimeResourcesPathLocalizationServiceCollectionExtensions
{
public static IServiceCollection AddRuntimeResourcesPathForLocalization(this IServiceCollection services)
{
services.AddSingleton<IStringLocalizerFactory, RuntimeResourcesPathResourceManagerStringLocalizerFactory>();
services.AddSingleton<IHtmlLocalizerFactory, RuntimeResourcesPathHtmlLocalizerFactory>();
return services.AddSingleton<IViewLocalizer, ActiveLocalizerAwareViewLocalizer>();
}
}
我们还实现了自定义 IViewLocalizer
,以便它可以在您的代码中无缝使用。它的工作只是检查是否有通过 HttpContext
共享的 IHtmlLocalizer
的任何活动实例(作为称为 IActiveViewLocalizerFeature
的功能)。每个不同的运行时资源路径将创建不同的 IHtmlLocalizer
将作为活动定位器共享。通常在一个请求范围内(以及在视图上下文中),我们通常只需要使用一个运行时资源路径(在渲染视图之前指定的一开始)。
要注册自定义服务:
services.AddRuntimeResourcesPathForLocalization();
要将本地化程序与运行时资源路径一起使用:
public async Task<IActionResult> ShowSomething([FromQuery] bool useDifferentResource)
{
if (useDifferentResource)
{
this.UseLocalizer("resources path of your choice");
}
return View("MyView");
}
注意:Controller 或 PageModel 范围内的 UseLocalizer
不是很有效,因为需要额外的逻辑来查找 view/page(使用 IRazorViewEngine
正如你在代码中看到的那样)。因此,如果可能,您应该将 UseLocalizer
移至 RazorPage
或 View
。切换条件可以通过视图模型或任何其他方式(查看数据,查看包,...)传递。