在Asp中使用文化和UI文化进行地址域定位.asp.net MVC 3

本文关键字:定位 行地址 asp net MVC 文化进 UI Asp 文化 | 更新日期: 2023-09-27 18:01:21

我有一个地址模型类型,它包含以下字段:

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"

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

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

使用CurrentCulture本地化的字符串查找字段的资源键-例如ResourceName_Display_Address_PostZip。当en-GB时返回DisplayName_GB_PostCode,当en-US时返回DisplayName_US_ZipCode

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

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

一个好的解决方案?还是我错过了什么显而易见的东西?

在Asp中使用文化和UI文化进行地址域定位.asp.net MVC 3

这只是伪代码。我现在不适合测试和调试。但是,如果您的自定义版本的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文件如

Name=PostZip | Value=邮政编码

Name=PostZip | Value=邮政编码[for 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此链接

这就是我所做的。

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

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

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

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

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文化如何,英文网站总是会显示'县'或'邮政编码';但是,如果用户界面文化愿意的话,可以通过简单地定义_GB后缀资源字符串的本地化版本,为这两个短语提供自己的本地翻译。

我也意识到我可能需要为整个页面做同样的事情,所以我正在考虑扩展Razor和Aspx视图引擎,以支持使用子文件夹自动地基于文化和UI文化的版本。唯一的问题是如何解释文件夹/en-GB/ -是有关后端或前端文化!