在大型.NET项目中实现多语言/全球化的最佳方法
我将很快在一个大型的C#项目上工作,并希望从一开始就构build多语言支持。 我已经玩了一段时间,可以使用每种语言的单独资源文件来工作,然后使用资源pipe理器加载string。
还有其他的好办法,我可以看看?
我已经看到使用许多不同方法实施的项目,每个都有其优点和缺点。
- 一个在configuration文件(不是我最喜欢的)
- 一个是使用数据库 – 这个工作很好,但在你知道要维护什么是一个痛苦。
- 一个使用过的资源按照你所build议的方式存档,我必须说这是我最喜欢的方法。
- 最基本的是使用包含string的包含文件 – 很难看。
我会说你select的资源方法很有意义。 看到其他人的答案也是很有趣的,因为我经常想知道是否有更好的方式来做这样的事情。 我已经看到许多资源都指向使用资源的方法,包括一个在这里 。
与资源一起使用单独的项目
我可以从经验中得知这一点,拥有包括API,MVC,项目库(核心function)和WPF在内的12个项目的最新解决scheme。 我还没有用便携库来testing这个,因为我没有为语言库创build一个可移植的库。
所以,让我们去。
Pro的
- 几乎到处都是强打字。
- 在WPF中,您不必处理
ResourceDirectories
。 - 根据我的testing,支持ASP.NET,Class Libraries和WPF。
- 没有额外的第三方库需要。
- 支持文化后备:en-US – > en。
- 不仅后端,在XAML中为WPF,在.cshtml中为MVC。
- 通过更改
Thread.CurrentThread.CurrentCulture
轻松操作语言 - search引擎可以使用不同的语言进行爬网,用户可以发送或保存特定于语言的url。
反对的
- WPF XAML有时是bug,新添加的string不直接显示。 重build是临时修复(vs2015)。
- 告诉我。
build立
在你的解决scheme中创build语言项目,给它一个像MyProject.Language这样的名字。 为其添加一个名为Resources的文件夹,并在该文件夹中创build两个资源文件(.resx)。 一个叫Resources.resx ,另一个叫Resources.en.resx (或特定的.en-GB.resx)。 在我的实现中,我将NL(荷兰语)语言作为默认语言,所以这将在我的第一个文件,而英语在我的第二个文件。
安装应该看起来像这样:
Resources.resx的属性必须是:
确保将自定义工具名称空间设置为您的项目名称空间。 原因是在WPF中,你不能引用XAML内部的Resources
。
并在资源文件中,将访问修饰符设置为Public:
在另一个项目中使用
引用您的项目:右键单击引用 – >添加引用 – > Prjects \ Solutions。
在文件中使用命名空间: using MyProject.Language;
像这样在后端使用它: string someText = Resources.orderGeneralError;
如果还有别的东西叫Resources,那么就把整个名字空间放进去。
在MVC中使用
在MVC中,你可以做,但是你喜欢设置语言,但我使用参数化的url,可以这样设置:
RouteConfig.cs在其他映射下面
routes.MapRoute( name: "Locolized", url: "{lang}/{controller}/{action}/{id}", constraints: new { lang = @"(\w{2})|(\w{2}-\w{2})" }, // en or en-US defaults: new { controller = "shop", action = "index", id = UrlParameter.Optional } );
FilterConfig.cs (可能需要添加,如果是的话,添加FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
到Global.asax
的Application_start()
方法
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new ErrorHandler.AiHandleErrorAttribute()); //filters.Add(new HandleErrorAttribute()); filters.Add(new LocalizationAttribute("nl-NL"), 0); } }
LocalizationAttribute
public class LocalizationAttribute : ActionFilterAttribute { private string _DefaultLanguage = "nl-NL"; private string[] allowedLanguages = { "nl", "en" }; public LocalizationAttribute(string defaultLanguage) { _DefaultLanguage = defaultLanguage; } public override void OnActionExecuting(ActionExecutingContext filterContext) { string lang = (string) filterContext.RouteData.Values["lang"] ?? _DefaultLanguage; LanguageHelper.SetLanguage(lang); } }
LanguageHelper只是设置文化信息。
//fixed number and date format for now, this can be improved. public static void SetLanguage(LanguageEnum language) { string lang = ""; switch (language) { case LanguageEnum.NL: lang = "nl-NL"; break; case LanguageEnum.EN: lang = "en-GB"; break; case LanguageEnum.DE: lang = "de-DE"; break; } try { NumberFormatInfo numberInfo = CultureInfo.CreateSpecificCulture("nl-NL").NumberFormat; CultureInfo info = new CultureInfo(lang); info.NumberFormat = numberInfo; //later, we will if-else the language here info.DateTimeFormat.DateSeparator = "/"; info.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy"; Thread.CurrentThread.CurrentUICulture = info; Thread.CurrentThread.CurrentCulture = info; } catch (Exception) { } }
.cshtml中的用法
@using MyProject.Language; <h3>@Resources.w_home_header</h3>
或者如果您不想定义使用,那么只需填写整个名称空间,或者您可以在/Views/web.config下定义名称空间:
<system.web.webPages.razor> <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <pages pageBaseType="System.Web.Mvc.WebViewPage"> <namespaces> ... <add namespace="MyProject.Language" /> </namespaces> </pages> </system.web.webPages.razor>
这个mvc实现源码教程: 真棒教程博客
在类库中使用模型
后端使用是相同的,但只是在属性中使用的一个例子
using MyProject.Language; namespace MyProject.Core.Models { public class RegisterViewModel { [Required(ErrorMessageResourceName = "accountEmailRequired", ErrorMessageResourceType = typeof(Resources))] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } } }
在WPF中使用。
Ofcourse添加一个对MyProject.Language命名空间的引用,我们知道如何在后端使用它。
在XAML中,在Window或UserControl的头文件中,添加名为lang
的名称空间引用,如下所示:
<UserControl x:Class="Babywatcher.App.Windows.Views.LoginView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:MyProject.App.Windows.Views" xmlns:lang="clr-namespace:MyProject.Language;assembly=MyProject.Language" <!--this one--> mc:Ignorable="d" d:DesignHeight="210" d:DesignWidth="300">
然后,在一个标签里面:
<Label x:Name="lblHeader" Content="{x:Static lang:Resources.w_home_header}" TextBlock.FontSize="20" HorizontalAlignment="Center"/>
由于它是强types的,你确定资源string存在。 有时您可能需要在安装过程中重新编译项目,WPF有时候会有新的命名空间。
对于WPF还有一件事,在App.xaml.cs
设置语言。 你可以做你自己的实现(安装时select)或让系统决定。
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); SetLanguageDictionary(); } private void SetLanguageDictionary() { switch (Thread.CurrentThread.CurrentCulture.ToString()) { case "nl-NL": MyProject.Language.Resources.Culture = new System.Globalization.CultureInfo("nl-NL"); break; case "en-GB": MyProject.Language.Resources.Culture = new System.Globalization.CultureInfo("en-GB"); break; default://default english because there can be so many different system language, we rather fallback on english in this case. MyProject.Language.Resources.Culture = new System.Globalization.CultureInfo("en-GB"); break; } } }
在那里,你现在可以为你的项目使用一个单一的资源文件。 这使得将所有内容都导出到excl文档并让其他人翻译它变得非常容易。
+1数据库
如果对数据库进行更正,应用程序中的表单甚至可以即时重新转换。
我们使用了一个系统,其中所有控件都映射到一个XML文件(每个表单一个)到语言资源ID,但所有的ID都在数据库中。
基本上,不是每个控件都拥有这个ID(实现一个接口,或者使用VB6中的标签属性),我们使用了.NET中的控件树,通过reflection很容易发现。 当表单加载的过程中,如果XML文件丢失,就会生成这个XML文件。 XML文件会将控件映射到其资源ID,因此只需填写并映射到数据库即可。 这意味着如果某些东西没有被标记,或者需要将其拆分到另一个ID,则不需要更改已编译的二进制文件(英文中的某些词可能被用作名词和动词,可能需要翻译成两个不同的词在字典中,不要重复使用,但在初始分配ID时可能不会发现这一点)。 但事实是,整个翻译过程完全独立于您的二进制文件(每一种forms都必须从知道如何翻译自身及其所有控制的基本formsinheritance)。
应用程序涉及更多的唯一的方法是使用插入点的阶段。
数据库翻译软件是您的基本CRUD维护屏幕,具有各种工作stream程选项以方便翻阅缺失的翻译等。
我不认为有一个“最好的办法”。 这确实取决于您正在构build的应用程序的技术和types。
Webapps可以像其他海报所build议的那样将信息存储在数据库中,但我build议使用单独的资源文件。 这是资源文件从你的来源分开 。 单独的资源文件减less了对相同文件的争用,随着项目的增长,您可能会发现本地化将独立于业务逻辑。 (程序员和翻译员)。
Microsoft WinForm和WPF大师build议使用为每个语言环境定制的独立资源程序集。
WPF将UI元素设置为内容的能力降低了所需的布局工作,例如:(日语单词比英语短得多)。
如果你正在考虑WPF: 我build议阅读这个MSDN文章为了说实话,我find了WPF本地化工具:msbuild,locbaml,(也许是一个Excel电子表格)繁琐的使用,但它确实工作。
有些东西只是略有相关:我面对的一个常见问题是集成发送错误消息(通常是英文)的遗留系统,而不是错误代码。 这会强制更改遗留系统,或者将后端string映射到我自己的错误代码,然后映射到本地化的string…等等。 错误代码是本地化的朋友
我会去与多个资源文件。 这不应该很难configuration。 事实上,我最近回答了一个类似的问题,就是设置一个基于全球语言的资源文件和表单语言资源文件。
在Visual Studio 2008中进行本地化
至less对于WinForm开发,我会认为是最好的方法。
你可以使用像Sisulizer这样的商业工具。 它将为每种语言创build卫星汇编。 只有你应该注意的是不要混淆forms类名(如果你使用混淆器)。
我一直在寻找,我发现这个:
如果您使用WPF或Silverlight您的aproach可以使用WPF LocalizationExtension有很多原因。
IT的开放源代码它是免费的(并且将保持免费)处于真正的稳定状态
在Windows应用程序中,你可以这样做:
public partial class App : Application { public App() { } protected override void OnStartup(StartupEventArgs e) { Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("de-DE"); ; Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("de-DE"); ; FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata( XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); base.OnStartup(e); } }
而且我想在一个Wep页面上,可能会是一样的。
祝你好运!
大多数开源项目都使用GetText来达到这个目的。 我不知道以前是如何使用.Net项目的。
我们使用自定义提供程序来支持多语言,并将所有文本放在数据库表中。 除了更新数据库中的文本而不更新Web应用程序时,我们有时会遇到caching问题。
标准资源文件更容易。 但是,如果您有任何与语言相关的数据(如查找表),那么您将不得不pipe理两个资源集。
我没有这样做,但在我的下一个项目中,我将实现一个数据库资源提供程序。 我发现如何在MSDN上做到这一点:
http://msdn.microsoft.com/en-us/library/aa905797.aspx
我也发现这个实现:
DBResource提供程序