我正在为.NET MVC应用程序开发UI,在不久的将来它将要求所有内容的国际本地化。我对.NET总体上非常熟悉,但是从来没有一个项目需要如此高度地关注国际可访问性。
该项目最初是用英语完成的。在这一点上,我应该采取什么措施来简化将来的本地化工作?
我正在为.NET MVC应用程序开发UI,在不久的将来它将要求所有内容的国际本地化。我对.NET总体上非常熟悉,但是从来没有一个项目需要如此高度地关注国际可访问性。
该项目最初是用英语完成的。在这一点上,我应该采取什么措施来简化将来的本地化工作?
Answers:
您正在开发ASP.Net MVC应用程序,对吗?其他答案似乎特定于桌面应用程序。让我捕捉一下常见的事情:
语言环境检测
应用程序正确检测用户的语言环境非常重要。在桌面应用程序中,CultureInfo.CurrentCulture拥有首选的格式设置区域(应用于格式化数字,日期,货币等),而CultureInfo.CurrentUICulture拥有首选的用户界面区域设置(应用于显示本地化消息的区域)。 。对于Web应用程序,应该将两种区域性都设置为auto(以自动从AcceptLanguage标头中检测区域设置),除非您想要实现一些高级的区域设置检测工作流(即,希望支持按需更改语言)。
外化字符串
所有字符串都应来自资源,即Resx文件。在Winforms App中,可以通过将窗体Localizable属性设置为true来轻松实现。您还需要手动(不幸的是)外部化来自模型的字符串。这也相对简单。在Asp.Net中,您需要手动外部化所有内容...
版面
您绝对需要允许字符串扩展。在Winforms世界中,可以通过TableLayoutPanel实现此功能,应使用TableLayoutPanel确保布局将自动调整以容纳更长的文本。在网络世界中,您有点不走运。您可能需要实现CSS本地化机制-一种修改(覆盖)CSS定义的方法。这将使本地化人员可以按需修改样式问题。确保呈现的页面中的每个HTML元素都有唯一的ID-它将可以精确定位。
文化特定问题
避免使用可能特定于西方文化的图形,颜色和声音。如果您确实需要它,请提供本地化方法。避免使用对方向敏感的图形(因为当您尝试本地化为阿拉伯语或希伯来语时会出现问题)。另外,不要假设整个世界都使用相同的数字(即阿拉伯语不是这样)。
ToString()和Parse()
除非不支持,否则请确保在调用ToString()时始终传递CultureInfo。这样,您就可以评论自己的意图。例如:如果您在内部使用某个数字,并且由于某种原因需要将其转换为字符串,请使用:
int i = 42;
var s = i.ToString(CultureInfo.InvariantCulture);
对于将要显示给用户使用的数字:
var s = i.ToString(CultureInfo.CurrentCulture); // formatting culture used
同样适用于Parse(),TryParse()甚至ParseExact()-如果不正确使用CultureInfo,可能会引入一些讨厌的错误。那是因为Microsoft中一些可怜的人,满怀良好的意愿,决定将CultureInfo.CurrentCulture视作默认值是一个好主意(如果您不传递任何内容,将使用它)-毕竟有人使用ToString( )他/她想将其显示给用户,对吗?事实并非总是如此-例如,尝试将应用程序版本号存储在数据库中,然后将其转换为Version类的实例。祝好运。
日期和时区
确保始终在UTC中存储和实例化DateTime(使用DateTime.UtcNow代替DateTime.Now)。在显示时将其转换为本地格式的本地时间
DateTime now = DateTime.UtcNow;
var s = now.ToLocalTime().ToString(CultureInfo.CurrentCulture);
如果您需要在正文中发送带有时间参考的电子邮件,请确保包含时区信息-包括UTC偏移量和城市列表:
DateTime someDate; // i.e. from database
var formattedDate = String.Format("{0} {1}",
someDate.ToLocaleTime().ToString(CultureInfo.CurrentCulture),
TimeZoneInfo.Local.DisplayName);
复合消息
您已经被警告不要连接字符串。相反,您可能会使用如上所示的String.Format()。但是,我必须指出,您应尽量减少使用复合消息。那只是因为目标语法规则通常是非常不同的,所以翻译人员可能不仅需要重新排列句子(可以通过使用占位符和String.Format()解决),还可以根据什么将被替代。让我给你举一些例子:
// Multiple plural forms
English: 4 viruses found.
Polish: Znaleziono 4 wirusy. **OR** Znaleziono 5 wirusów.
// Conjugation
English: Program encountered incorrect character | Application encountered incorrect character.
Polish: Program napotkał nieznaną literę | Aplikacja napotkała nieznaną literę.
其他串联问题
串联不限于字符串。避免将控件放在一起,例如:
在[带数字的文本框]天中再次提醒我。
应该将其重新设计为:在此天数内再次提醒我:[文本框]。
字符编码和字体
始终保存,传输Unicode中的任何文本(即UTF-8)。请勿对字体进行硬编码-本地化可能需要对其进行修改,这将关闭默认的字体后备机制(对于Winforms)。请记住,在大多数字段(即用户名)中允许使用“奇怪”字符。
测试
您可能需要实现所谓的伪翻译,即为德国文化创建资源并复制带有前缀和后缀的英文字符串。您还可以包装占位符以轻松检测复合字符串。伪翻译的目的是检测可本地化的问题,例如硬编码的字符串,布局问题和复合消息的过度使用。
String.Format
,使其可以支持这种很酷的语法:"There {0:was|were} {0} {0:virus|viruses} found."
每种语言都可以加载自己的规则,因此您可以做到"Znaleziono {0} {0:wirusy|wirusów}."
这一点。源代码在GitHub上:github.com/scottrippey/SmartFormat/wiki
"{0} {0:plik|pliki|plików}"
。格式化程序具有波兰语规则,该规则确定要使用的三种形式中的哪一种,并正确确定特殊情况。我目前正在努力添加更多规则,因此,gettext
本文将非常有用,谢谢。
您应该考虑一些基本事项:
外部化所有字符串资源
您的所有资源都应包含在可以进行本地化的外部文件中。如果您也想将错误消息本地化,请不要忘记错误消息。
留出足够的空间来扩展字符串
例如,某些语言中的字符串通常最多长30%(例如希腊文),因此请确保以某种方式设计UI,以便在必要时可以扩展字符串。这是法语的一个极端示例:
确定->接受者(法语-400%扩展)
我建议您以某种伪翻译作为起点(http://en.wikipedia.org/wiki/Pseudolocalization)。或者,您可以通过Google翻译或Bing翻译资源。这样可以很好地指示实际翻译的外观。
注意图像中的文字
如果您在应用程序中使用任何图像-确保它们不包含任何文本-这显然无法翻译。
永远不要硬编码Windows文件夹的任何路径
很明显,但是我过去已经看过。例如,C:\Program Files
在某些国际版本的Windows(例如,C:\Programme
在德语OS)上进行了翻译。
避免使用特定于语言环境的术语
例如,如果您以表格的形式向某人要求其“高中”,则在西欧几乎没有意义。
避免通过字符串连接创建字符串
例如,这看起来无害:
strWelcome = ReadExternalString("Welcome");
strMessage = strWelcome + ", " + UserName;
但是,例如日语单词的顺序会有所不同,因此最终可能没有任何意义。
时间/日期设置
始终确保从操作系统获取时间/日期格式。
除了这里已经给出的所有出色答案之外,还有些亚洲语言的注意事项:
中文和韩文文本往往比等效的英文文本短得多(因为您通常需要更少的块状字符来写相同的东西),因此页面实际上可能看上去是空白的,但是却被德语完全塞住了……您需要这样做这里有些动态大小看起来不错。
但是,就字符数而言,日语文本通常倾向于更长,甚至比等效的英语文本更长。
亚洲字符通常布置在基线上,不包括后裔(即y,g,q,j等的下部)。在格式化屏幕元素(通常是按钮)时,其内部带有文本,如果如果文字仅是亚洲语言(即没有西方字母),则文字看起来像是向上移动。
处理数字格式不同。不同的亚洲国家/地区使用不同的数字格式方式。与货币相同。例如,在东亚,10,000(wan)是一个常见单位。在印度,通常有100,000(十万)。
一些国家的货币有很多零,没有小数点(例如日本,印度尼西亚,意大利),而其他国家的小数点后最多有两位数。
单词顺序可能并不总是相同。如果您的字符串来自不同数据的组合,则最好以字符串格式使用{0},{1}等,而不是硬编码单词顺序。
每种语言和语言环境的排序是不同的-您应始终依赖于O / S特定于语言环境的排序。
注意“全角”和“半角”字符之间的差异。括号,标点符号等可以具有不同于标准ASCII的“全角”版本。如果您要根据这些字母进行搜索或字符串分割,则需要先将所有全角符号转换为等价的半角。
当心数据输入的陷阱-例如,在中文中,句点不是点“。”。逗号是全角,而不是“,”。如果进行数据输入的用户可能会意外打开亚洲语言IME,请不要尝试搜索西方标点符号。
不要以电话号码格式假设任何内容。并不总是有区号等,并且可以采用不同的格式。通常,每个国家/地区都有一个格式字符串。
不要以为人们只有一个手机号码或一个传真号码等。在亚洲,情况并非如此。
对于地址,不要假设任何东西。不一定总是有邮政编码。邮政编码可能并不总是数字。一个国家可能没有省/州。一个国家可能只是一个大城市(例如新加坡)。对于某些亚洲国家/地区,房屋的最小单位可能是“ X房Y单元Z区Z层A楼B组C房地产D”。通常,在字段数和地址中允许的字符数上要非常宽松。
称呼不仅限于先生,太太等。尽管您可以安全地使用“ M”和“ F”进行性爱,但我们还不那么奇怪...
一些基本步骤是确保屏幕上显示的任何字符串都不是代码中的文字。如果您正在执行Winforms,则每个表单都将具有UI资源。对于对话框,报告等,请确保您使用项目资源文件。
因此,您的代码中可能会出现诸如Resources.UploadFailed之类的内容,而不是代码中的“ Upload failed”(上载失败)
这样,您可以为您使用的每种语言创建一个新的资源文件(.Net将对此提供帮助。)并在每个文件中包含本地化的字符串。
编辑 我忘记了在执行UI时要提及的内容,请确保您不只是在其中添加内容。根据您要本地化的语言,房地产可能是个问题。我从事的项目中,德语和葡萄牙语是字符串增长的两个最大罪犯。如果我们不小心,英语,法语和意大利语的字符串会很好用德语。
我建议您在程序集上运行FXCop或Visual Studio Code Analysis(它们完全相同)。
它们擅长检测未使用适当的面向区域性重载的.NET代码,例如:CA1305:指定IFormatProvider。
我还必须补充一点,这些工具也令人沮丧,因为它们通常会在您的代码中检测到成千上万的问题,但是,即使您不遵循每个规则,也应该学到很多东西。
您需要考虑:
多语言路由
将所有硬编码字符串移动到资源文件
属性的示例:
模型:
[Display(Name = <Resource for display name>.<field for this property>)]
[Required(ErrorMessage = <Resource for error message>.<field for this validate message>)]
public string TestProperty { get; set; }
视图:
@Html.LabelFor(m=>m.TestProperty)
@Html.EditorFor(m => m.TestProperty)
@Html.ValidationMessageFor(m => m.TestProperty)
这是其余答案中未提及的内容。
根据您的应用程序及其本地化的复杂性,我强烈建议您实施替代资源提供程序,并将本地化的资源保留在数据库中。使用默认的ASP.NET本地化方案,所有资源都保存在RESX文件中,该文件:
作为一个可能的用例,请考虑为您的应用程序提供语言包以及通过UI导入和导出语言的功能。RESX文件在这里无济于事。
在这种情况下,替代资源提供者将非常有帮助。有关如何实现的更多信息,请参见此处。当然,这种情况很少见,在企业应用程序中更常见,但仍然有效。
最重要的是用各种语言管理内容。我自己开发了一些websistes,以各种语言管理内容是最大的挑战。
我正在使用数据库存储资源/内容。它使我可以灵活地添加所需的任何语言支持。如果未找到特定语言的资源,我已经实现了退回到英语的逻辑。
您以后可以使用翻译器将英语值转换为任何语言。
国际化中要考虑的事项摘要:
所有信息应国际化。考虑到图形可能包含我们要国际化的信息。
字段或字符串的大小,取决于语言,因为这可能会给我们带来问题。
单词的顺序取决于我们所使用的语言,因此一种语言的顺序将与另一种语言相同。
我们必须考虑到日期格式将从一种语言更改为另一种语言