resx后备系统的额外级别

时间:2014-02-20 13:51:17

标签: c# .net resx

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

  1. 请求文化和请求品牌的Resx文件
  2. 请求文化的Resx文件
  3. 后备文化的Resx文件
  4. 所以基本上在现有的一个上面添加一个额外的支票。

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

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

    可能看起来是this的副本,但不是

1 个答案:

答案 0 :(得分:11)

TL; DR 使用自定义资源提供程序自定义资源访问权限。使用正确的工具根据您的视图引擎请求资源,以便资源提供程序工厂正常工作。

自定义资源提供程序工厂

由于您询问的是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)的标准化。您的解决方案定义了新的文化,以便为现有文化建模字符串子集。那不是什么文化定义的。

例如,您必须更改的一个字符串是英文的。在它&#34;品牌推广&#34;它仍然是英文的。因此,实际的文化应该保持不变。

你可以用不同的方式挑战这个问题。使用自定义资源提供程序确实是一个有效的解决方案!通过覆盖它的GetObject - 方法并检查已定义的前缀,例如:

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是一个简单的布尔开关,可以从上面的资源提供者工厂设置。如果已启用,则自定义资源提供程序将使用&#34; Branded&#34; -prefix查询字符串。如果没有字符串匹配,则使用默认实现。

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

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

使用资源(正确的方式)

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

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

按视图进行本地化只不过是在视图文件所在的目录中为每个视图(具有相同名称,如 Index.cshtml.resx )放置资源文件。全局升级意味着一种文化的所有字符串都会进入一个资源文件,该文件通常位于 App_GlobalResources 目录中。但这不是强制性的。实际上,资源文件可以嵌入到程序集,另一个程序集中或位于其他位置。

本地化ASPX视图

我开始使用ASPX的本地化字符串,因为它实际上更容易(让我感到惊讶)。在ASPX中,您使用<%$表达式构建器语法来访问资源:

<asp:Label runat="server" Text="<%$ Resources:MyResource, Test %>" />

这告诉编译器使用 MyResource 资源容器中的资源 Test (默认情况下是资源文件)。该表达式导致对CreateGlobalResourceProvider("MyResource")的调用。返回的提供商可以拨打GetObject("Test")

我没有深入研究这个问题,但显然本地化ASPX视图并不是直接可行的。但是,这不应该是努力和自言自语。 (对工厂的调用转到GetLocalResourceProvider而不是GetGlobalResourceProvider

本地化Razor视图

棘手的是,您通常使用自动生成的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".

整蛊,但它应该做的工作!