我有一个包含以下字段的地址模型类型:
public string CountyState { get; set; }
public string PostZip { get; set; }
我正在使用DisplayNameAttribute
的自定义版本(在.Net 4 DisplayAttribute
为我们提供本地化之前编写),可以本地化这两个字段的显示名称。
当然,en-GB
和"County"
的本地"Post Code
翻译是en-US
,而"State"
当然是"Zip Code"
和en-GB
}。
但是,我的MVC网站正在显示不同地点的不同商家的付款页面,这些页面也必须能够翻译成其他语言。因此,UI文化可能是"State"
,但如果为美国企业付款,则地址字段仍应显示"Zip Code"
和en-US
({{1}的后端文化})。
因此,似乎这些显示字段的单个资源类型/键本地化查找是不够的。我不想要根据国家/地区使用不同的模型类型,并且不想要编写不同的Html - 随着我的网站增长,这两种解决方案将变得难以管理将通过数据库添加。
我的网站已使用CurrentCulture
来表示商家的默认文化(当然是用于格式化货币),而我正在使用CurrentUICulture
来表示用户的文化;所以我认为我认为这是一个有点古怪的解决方案:
使用CurrentCulture
本地化的字符串查找字段的资源键 - 例如ResourceName_Display_Address_PostZip
。当en-GB
这将返回DisplayName_GB_PostCode
时,但en-US
时会返回DisplayName_US_ZipCode
。
现在使用CurrentUICulture
查找其本地化版本。
然后我必须写一个可以包装这个逻辑的属性,以便它与MVC兼容。
一个好的解决方案?或者我错过了一些令人眼花缭乱的事情!?
答案 0 :(得分:1)
这只是伪代码。我现在不在测试和调试的地方。但是,如果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;
}
答案 1 :(得分:0)
[Required(ErrorMessageResourceType=typeof(Languages.Resource),ErrorMessageResourceName="PostZip")]
[Display(ResourceType=typeof(Languages.Resource),Name="PostZip")]
public string PostZip { get; set; }
现在为不同的本地人创建不同的.resx文件 例如
Name = PostZip |值=邮政编码[适用于en-US]
Name = PostZip |值=后置代码[适用于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此链接。
答案 2 :(得分:0)
所以这就是我所做的。
我必须使这个代码对我的特定项目更加本地化,因为它依赖于传递的资源类具有类型ResourceManager
的公共静态属性System.Resources.ResourceManager
。对于由VS'自己的强类型资源访问器类(从.resx文件生成)生成的类,这是正确的,因此我认为它可能符合可重用性。它使用它,因此它可以指定查找字符串时使用的文化。
首先对资源名称进行资源查找(使用CurrentCulture
),以用于模型属性的文本;然后它将该字符串泵回另一个资源查找(使用CurrentUICulture
)以获取要使用的实际显示名称。
我已经用TODO替换了所有错误检查以保持简洁。
最后我直接从DisplayNameAttribute
派生,因为这个新模式与我使用的代码(从RequiredAttribute
中删除)不能很好地为我自己现有的代码获取基于UICulture的资源字符串自定义属性。我不喜欢班级名称,但现在想不出更好的东西。
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/
- 与后端或前端文化有关!