繁体   English   中英

在 Asp.Net MVC 3 中按文化和 UI 文化进行地址字段本地化

[英]Address field localization by both culture and UI culture in Asp.Net MVC 3

我有一个包含以下字段的地址 model 类型:

public string CountyState { get; set; }
public string PostZip { get; set; }

我正在使用DisplayNameAttribute的自定义版本(在 .Net 4 DisplayAttribute给我们本地化之前编写),它可以本地化这两个字段的显示名称。

因此,这些的本地en-GB翻译自然是"County""Post Code ",而对于en-US ,它们当然是"State""Zip Code"

但是,我的 MVC 网站正在显示不同位置的不同企业的支付页面,这些页面也必须能够翻译成其他语言。 因此,UI 文化可能是en-GB ,但如果为美国企业付款(后端文化en-US ),地址字段仍应显示"State""Zip Code"

因此,对这些显示字段进行单个资源类型/键本地化查找似乎是不够的。 不想根据国家/地区使用不同的 model 类型,不想编写不同的 Html - 随着我的网站的增长,这两种解决方案都将变得难以管理,因为企业等将通过数据库添加。

我的网站已经在使用CurrentCulture来表示企业的默认文化(当然用于格式化货币),并且我正在使用CurrentUICulture来表示用户的文化; 所以我考虑了我认为有点古怪的解决方案:

使用由CurrentCulture本地化的字符串来查找该字段的资源键 - 例如ResourceName_Display_Address_PostZip en-GB时,这将返回DisplayName_GB_PostCode ,但当en-US时,它将返回DisplayName_US_ZipCode

现在使用CurrentUICulture来查找它的本地化版本。

然后我必须编写一个可以包装这个逻辑的属性,以便它与 MVC 兼容。

一个好的解决方案? 还是我错过了一些非常明显的东西?

这只是伪代码。 我现在不在测试和调试的地方。 但是,如果您的DisplayNameAttribute自定义版本具有关联的CurrentCulture ,您应该能够编写和扩展类似的方法:

    public static string GetLocalizedDisplayName<TProperty>(Expression<Func<TProperty>> e)
    {
        var modelType = ((MemberExpression)e.Body).Member.ReflectedType.GetType();
        var classProperties = TypeDescriptor.GetProperties(modelType).OfType<CustomAttribute>;
        var prop = from property in classProperties
                   where property.CultureInfo == CultureInfo.CurrentCulture
                   select property;

        return prop != null && !string.IsNullOrEmpty(prop.DisplayName) ? prop.DisplayName : member.Member.Name;
    }
[Required(ErrorMessageResourceType=typeof(Languages.Resource),ErrorMessageResourceName="PostZip")]
[Display(ResourceType=typeof(Languages.Resource),Name="PostZip")]
public string PostZip { get; set; }

现在为不同的本地人创建不同的.resx 文件,例如

名称=邮编 | 值 = Zip 代码 [适用于 en-US]

名称=邮编 | 值 = 邮政编码 [适用于 en-GB]

---内部控制器-----

 //whatever logic you are looking for, now  set culture manually

CultureInfo ci = new CultureInfo("hi");
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);

----------内部视图--------------

 @Html.LabelFor(model => model.PostZip)

也参考http://haacked.com/archive/2009/12/12/localizing-aspnetmvc-validation.aspx这个链接。

所以这就是我所做的。

我不得不使此代码对我的特定项目更加本地化,因为它依赖于传入的资源 class 具有类型为System.Resources.ResourceManager的公共 static 属性ResourceManager 这对于 VS 自己的强类型资源访问器类(从 .resx 文件生成)生成的 class 来说是正确的,所以我认为它可能有资格被重用。 它使用它,因此它可以指定在查找字符串时要使用的文化。

它首先对要用于 model 属性文本的资源名称进行资源查找(使用CurrentCulture ); 然后它将该字符串泵回另一个资源查找(使用CurrentUICulture )以获取要使用的实际显示名称。

我已经用 TODO 替换了所有错误检查以保持简洁。

最后,我直接从DisplayNameAttribute派生,因为这个新模式与我用来为我自己现有的自定义属性获取基于 UICulture 的资源字符串的代码(从RequiredAttribute中提取)不能很好地搭配。 我不喜欢 class 名称,但现在想不出更好的名称。

public class CultureDrivenDisplayNameAttribute : DisplayNameAttribute
{
  private readonly string _displayName = null;
  public CultureDrivenDisplayNameAttribute(Type resourceType,
    string resourceNameLookup)
  {
    //TODO: check for null args

    //get the ResourceManager member
    var prop = resourceType.GetProperty(
      "ResourceManager", 
      System.Reflection.BindingFlags.Public 
        | System.Reflection.BindingFlags.Static);

    //TODO: check for null or unexpected property type.

    System.Resources.ResourceManager resourceManager = 
      prop.GetValue(null, null) as System.Resources.ResourceManager;

    //TODO: check for null

    //ResourceManager is used as it lets us specify the culture - we need this
    //to be able to lookup first on Culture, then by UICulture.
    string finalResourceName = 
      resourceManager.GetString(resourceNameLookup, 
        Thread.CurrentThread.CurrentCulture);

    //TODO: check for null/whitespace resource value

    _displayName = resourceManager.GetString(finalResourceName, 
      Thread.CurrentThread.CurrentUICulture);
    //TODO: check for null display name
  }

  public override string DisplayName
  {
    get
    {
      return _displayName;
    }
  }
}

所以,现在我将我的地址模型的 PostZip/CountyState 属性装饰如下:

[CultureDrivenDisplayName(typeof(StringResources), 
  "ResourceName_AddressDetails_CountyState")]
public string CountyState { get; set; }

[CultureDrivenDisplayName(typeof(StringResources), 
  "ResourceName_AddressDetails_PostZip")]
public string PostZip { get; set; }

然后,在我的中性资源文件中:

ResourceName_AddressDetails_CountyState: "DisplayName_AddressDetails_CountyState"
ResourceName_AddressDetails_PostZip: "DisplayName_AddressDetails_PostZip"
DisplayName_AddressDetails_CountyState: "County/State"
DisplayName_AddressDetails_PostZip: "Post/Zip Code"

因此,如果我没有为给定的文化设置资源文件,地址将显示合理的文本。

但在我的 en-GB 资源文件中:

ResourceName_AddressDetails_CountyState: "DisplayName_AddressDetails_CountyState_GB"
ResourceName_AddressDetails_PostZip: "DisplayName_AddressDetails_PostZip_GB"
DisplayName_AddressDetails_CountyState_GB: "County"
DisplayName_AddressDetails_PostZip_GB: "Post Code"

所以现在,不管 UI 文化如何,英文网站总是会显示“县”或“邮政编码”; 但是如果 UI 文化愿意,只需定义_GB后缀资源字符串的本地化版本,它就能够提供它自己的两个短语的本地翻译。

我还意识到我可能需要对整个页面执行相同的操作,因此我正在考虑扩展 Razor 和 Aspx 视图引擎,以使用子文件夹自动支持基于文化和 UI 文化的版本。 那么唯一的问题是如何解释文件夹/en-GB/ - 与后端或前端文化有关!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM