【问题标题】:How to implement resource files localization ASP-NET.core?ASP-NET.core如何实现资源文件本地化?
【发布时间】:2020-09-21 03:10:10
【问题描述】:

我被要求在net.core 解决方案中实现本地化,如下所示:

  • 资源文件必须位于单独的项目(如“MyProject.Common”)中,才能在解决方案中的所有其他项目中使用,

  • 资源必须按“部分”划分,例如,我们在 Web 项目中有 3 个区域,如下所示:

  • Users,

  • Content,

  • Administration,

所以我被要求有类似的东西:

  • UsersResources.fr-CA.resx,
  • UsersResources.en-CA.resx,
  • ContentResources.fr-Ca.resx,
  • ...

我开始阅读 Localization is AP-NET core 的文档,但我对它的工作原理有点困惑。 似乎我被告知要做的事情是不可能的。

问题是,我可能需要使用业务、视图和控制器中的资源,因此我正在寻找一种方法来实现它,以便团队可以通过调用 ContentResources.MyCustomResource 来使用旧方法。

有没有办法接近它?

我发现有人提到 https://www.nuget.org/packages/ResXResourceReader.NetStandard 的帖子。

但我不知道它是否符合我的需要......

#编辑: 所以,尝试实现 Laz 的共享资源解决方案。

到目前为止,在启动​​时我有这个: 在ConfigureServices

services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
            .AddDataAnnotationsLocalization(options => {
                            options.DataAnnotationLocalizerProvider = (type, factory) =>
                            factory.Create(typeof(SharedResources));
                        
services.AddLocalization();

services.Configure<RequestLocalizationOptions>(
            opts =>
            {
                    /* your configurations*/
                    var supportedCultures = new List<CultureInfo>
                    {
                        new CultureInfo("en"),
                        new CultureInfo("fr")
                    };
                opts.DefaultRequestCulture = new RequestCulture("fr", "fr");
                opts.SupportedCultures = supportedCultures;
                opts.SupportedUICultures = supportedCultures;
            }
        );
  

Configure

app.UseRequestLocalization();
// used to force culture to fr but doen't seem to work
var cultureInfo = new CultureInfo("fr");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;

在 MyProject.Common 中,我有这样的结构:

MyProject.Common
-Resources
 --SharedResources.cs
  ---SharedResources.fr.resx
  ---SharedResources.en.resx
 --UsersResources.cs
  ---UsersResources.fr.resx
  ---UsersResources.en.resx

假设我想使用SharedResources

SharedResources.en.resx我添加了资源:

SharedResources.fr.resx我添加了资源:

现在在我的UserServiceBusiness 层中,我这样做了:

 private readonly IStringLocalizer Localizer;

    public UserService(IStringLocalizerFactory factory)
    {
        var type = typeof(SharedResources);
        var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
        _localizer = factory.Create(type);
    }       

    public void Test()
    {
        var test = Localizer["Test"]; //using the key of resources file i want
    }

但我在test 变量中得到的结果是“Test”,它是资源的key,而不是value

【问题讨论】:

  • 你可以参考这个讨论类似需求的SO线程:stackoverflow.com/a/52775064/6751634
  • @FeiHan 感谢您的宝贵时间。还尝试了您链接的解决方案,但结果与以下 Laz Ziya 的解决方案相同:我得到的只是资源的key

标签: asp.net-core localization resources


【解决方案1】:

.net core 的默认本地化设置可以基于one shared resource filebased on the class name

在您的情况下,您可以使用共享资源方法,但您必须通过为工厂提供所需的资源类型来在每个控制器/类中创建自定义本地化器。

首先创建一个包含所需资源的类库,为您想要的每种资源类型创建一个公共虚拟类,因此类库结构可以如下所示:

// SharedResourcesLibrary.csproj

- UsersResources.cs
 - UsersResources.fr-ca.resx
 - UsersResources.en-ca.resx

- ContentResources.cs
  - ContentResources.fr-ca.resx
  - ContentResources.en-ca.resx

...

虚拟类是空的,它们只是用作调用相关 resx 文件的类型。

// Dummy users resources class
public class UsersResources { }

那么在将ResourcesLibrary项目引用到其他项目之后,就可以通过调用相关的资源类型(虚拟类)来使用资源了。:

using SharedResourcesLibrary;

public class UsersController : Controller
{
    private readonly IStringLocalizer _localizer;

    public UsersController(IStringLocalizerFactory factory)
    {
        var type = typeof(UsersResources);
        _localizer = factory.Create(type);
    }       

    public IActionResult About()
    {
         ViewData["Message"] = _localizer["Welcome."];
    }
}

要使用其他资源,只需使用相关资源类型创建本地化程序。


另一种方法可以通过根据您的区域创建自定义多个IStringLocalizers,然后将它们注入控制器来完成。

// Create one localizer for each area
public class UsersLocalizer : IStringLocalizer
{
    private readonly IStringLocalizer _localizer;

    public UsersLocalizer(IStringLocalizerFactory factory)
    {
        var type = typeof(UsersResources);
        var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
        _localizer = factory.Create(type);
    }

    public LocalizedString this[string name] => _localizer[name];

    public LocalizedString this[string name, params object[] arguments] => _localizer[name, arguments];

    // ...
}

同样,您可以为其他区域创建定位器...然后在启动时注册:

services.AddTransient<IStringLocalizer, UsersLocalizer>();
services.AddTransient<IStringLocalizer, AdminsLocalizer>();
services.AddTransient<IStringLocalizer, ContentLocalizer>();
// ...

这样所有的定位器都会被注册,如果你只是注入IStringLocalizer,它会得到最后一个注册的,因为所有的定位器都实现了相同的IStringLocalizer接口。

所以您必须进行类型选择以注入正确的定位器:

public UsersController : Controller
{
    private readonly IStringLocalizer _localizer;

    public UsersController(IEnumerable<IStringLocalizer> localizers)
    {
        _localizer = localizers.FirstOrDefault(x => x.GetType() == typeof(UsersLocalizer));
    }

    public IActionResult About()
    {
        ViewData["Message"] = _localizer["Welcome."];
    }
}

Registering multiple implementation with the same interface in Asp.Net Core的不同方式可以参考这篇文章

【讨论】:

  • 感谢您的详细解答。目前正在尝试第一种方法(factory.Create(type) 一个),结果当我在业务层中使用Localizer["Test"] 时,我所拥有的只是关键Test 本身,而不是值Here is a test。知道我在这里错过了什么吗?
  • 资源文件在同一个项目中的结果是一样的吗?
  • 你如何改变文化?在任何视图中查看@CultureInfo.CurrentCulture.Name 的输出,以确保您处于正确的文化中
  • 我刚刚使用第一种方法和您的启动设置创建了一个测试样本,一切正常如果您愿意,可以分享:) 所以,我认为您的问题与文化有关设置和当前文化。
  • @j0w 很高兴知道它有效,对于枚举,这将是另一个问题,但this 可能会有所帮助:)
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 2015-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 2013-05-01
相关资源
最近更新 更多