[英]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.