c# - resx 回退系统中的额外级别

标签 c# .net resx

在我当前的项目中,我们使用 resx 文件来翻译我们的应用程序。
现在,为我们的网站打上品牌的请求需要一些字符串的翻译略有不同。
我正在考虑将 Culture 扩展到 BrandedCulture,然后编写一个使用以下回退策略的 ResourceManager:

  • 请求文化和请求品牌的 Resx 文件
  • 请求文化的 Resx 文件
  • 回退文化的 Resx 文件

  • 因此,基本上在现有检查的基础上添加一项额外检查。

    我想创建一个自定义的 IResourceProvider,它基本上包装了默认的 ResourceProvider,并从第 2 步开始使用它(正常行为),但是我在实例化/继承默认资源提供程序时遇到了困难,因为它是内部的。

    有什么建议?我走在正确的道路上吗?

    可能看起来重复 this ,但不是

    最佳答案

    TL;博士 使用自定义资源提供程序来自定义资源访问。根据您的 View 引擎,使用正确的工具请求资源,以便资源提供者工厂正常工作。

    自定义资源提供者工厂

    既然你问的是一个web项目,你可以自己实现 ResourceProviderFactory 并在您的 web.config 文件中定义它:

    <system.web>
        <globalization resourceProviderFactoryType="..." />
    </system.web>
    

    上面的代码片段应该是自我描述的。本厂可能会退回您的 custom resource provider implementation .您还可以为默认行为激活标准资源提供程序 using reflection .
    public override IResourceProvider CreateGlobalResourceProvider(string classKey)
    {
        var factory = (ResourceProviderFactory)Activator.CreateInstance(Type.GetType("System.Web.Compilation.ResXResourceProviderFactory, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"));
        var provider = factory.CreateGlobalResourceProvider(classKey);
    
        return new CustomResourceProvider(provider);
    }
    
    CustomResourceProvider持有标准资源提供者的引用以实现默认行为。可以找到编写自定义提供程序的详细示例 here .

    但是,我认为为一小部分本地化字符串创建新文化并不是一个好主意。文化描述了用于格式化货币、日期、数字等的特定语言环境和规则。可用的语言环境已根据各种 ISO 标准(639、3166 和 15924)进行标准化。您的解决方案定义了新的文化以对现有文化的字符串子集进行建模。这不是文化的定义。

    例如,您必须更改的字符串之一是英文的。在“品牌化”之后,它仍然是英文的。因此,实际的文化应该保持不变。

    你可以用不同的方式来挑战这个问题。使用自定义资源提供程序确实是一个有效的解决方案!通过覆盖它是 GetObject -method 并检查定义的前缀,例如:
    public override object GetObject(string resourceKey, CultureInfo culture)
    {
        if (m_brandedMode)
            return m_provider("Branded" + resourceKey, culture) ?? m_provider(resourceKey, culture);
        else
            return m_provider(resourceKey, culture);
    }
    

    这里m_brandedMode是一个简单的 bool 开关,可以从上面的资源提供者工厂设置。如果启用了自定义资源提供程序查询带有“品牌”前缀的字符串。如果没有字符串匹配,则使用默认实现。

    在您的英语资源文件中,您将有两个版本字符串,它们因品牌而异:MyString 和 BrandedMyString。它们都具有相同的文化,但资源提供者会根据应用程序设置对它们进行查询。

    当然这只是一个例子。您还可以为有品牌和无品牌的字符串实现不同的数据源,即使这可能需要做更多的工作。但是,我建议不要触及文化,也不要触及默认行为。 .NET 中的资源管理有时有点棘手。

    使用资源(正确的方式)

    根据您的 View 引擎,您必须更改访问本地化字符串的方式,以便环境选择您的自定义工厂。请注意,我仅针对使用 ASPX 和 Razor View 引擎的 Web View 进行了测试,但我认为这可以类似地用于使用 WinForms 和 WPF 的离线应用程序。

    通常,如果您将默认资源添加到您的项目,设计人员会生成围绕它的包装器。如果您的默认资源文件名为 Resources,则设计器会在 Resources.Designer.cs 文件中生成一个 Resources 类。这非常方便但具有误导性,因为它显然不打算用作本地化源,而是用于符号、图标和所有其他想要嵌入到程序集中的数据。因此,第一步是决定您希望如何本地化您的项目。这通常可以通过 View 或全局来完成。

    按 View 本地化意味着在 View 文件所在的目录中为每个 View 放置一个资源文件(名称相同,类似于 Index.cshtml.resx)。全局局部化意味着一种文化的所有字符串都进入一个资源文件,该文件通常位于 内。 App_GlobalResources 目录。然而,这不是强制性的。事实上,资源文件可以嵌入到您的程序集、另一个程序集或位于其他地方。

    本地化 ASPX View

    我从 ASPX 的本地化字符串开始,因为它实际上更容易(这让我感到惊讶)。在 ASPX 中,您使用的是 <%$访问资源的表达式构建器语法:
    <asp:Label runat="server" Text="<%$ Resources:MyResource, Test %>" />
    

    这告诉编译器使用来自 MyResource 资源容器(默认为资源文件)的资源 Test。该表达式导致调用 CreateGlobalResourceProvider("MyResource") .返回的提供者获取对 GetObject("Test") 的调用.

    我没有深入研究这一点,但显然不能直接在本地本地化 ASPX View 。然而,这不应该是自言自语。 (由此产生的对工厂的调用转到 GetLocalResourceProvider 而不是 GetGlobalResourceProvider )

    本地化 Razor View

    棘手的是,您通常使用自动生成的 Resource类像这样:
    @MyResource.Test
    

    这通过使用标准资源管理器绕过资源提供程序生成。访问本地化字符串的正确方法是使用 HttpContext :
    @HttpContext.GetGlobalResourceObject("MyResource", "Test")
    

    知道了!这叫 GetGlobalResourceProvider("MyResource")在自定义资源提供程序工厂上。其余的就像之前描述的一样。以类似的方式,您可以使用 @HttpContext.GetLocalResourceObject("~/Views/Home/Index.cshtml", "Test")为了请求本地资源。

    我不知道为什么这不是标准扩展,但您可以将其包装在花哨的辅助方法中:
    public static string Resource(this HtmlHelper helper, string resourceName, string resourceKey)
    {
        return helper.ViewContext.HttpContext.GetGlobalResourceObject(resourceName, resourceKey).ToString();
    }
    
    public static string Resource(this WebViewPage page, string resourceKey)
    {
        return page.ViewContext.HttpContext.GetLocalResourceObject(page.VirtualPath, resourceKey).ToString();
    }
    

    以下命令将请求全局或本地字符串:
    @Html.Resource("MyResources", "Test");    // Global resource "Test" from "MyResources" container.
    @this.Resource("Test");                   // Local resource "Test".
    

    棘手,但它应该可以完成工作!

    关于c# - resx 回退系统中的额外级别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21909823/

    相关文章:

    c# - 从资源文件动态生成可用的本地化

    c# - 使用 resx 文件时,可以用常量值替换变量吗

    c# - Devexpress Winforms 数据 GridView : Deactivate "Drag Column Header here to group by that column"-Option

    c# - IdentityServer3 的 ASP.NET 身份插件 UI (IdentityManager) 在部署到 Azure (WebApp) 后不起作用

    c# - 如何查看一个对象是否在 4 个点内

    asp.net - 如何在 Visual Studio 中写入输出

    .net - 如何嵌套资源标识符?

    c# - 公开 MVVM 中内置的用户控件的属性

    c# - 在客户端检查 WCF 调用的 HttpWebRequest?

    c# - 没有从 DBNull 到可空类型的隐式转换有技术原因吗?