在大型 .NET 项目中实现多语言/全球化的最佳方式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/373388/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to implement multi-language/globalization in large .NET project
提问by tjjjohnson
I'll soon be working on a large c# project and would like to build in multi-language support from the start. I've had a play around and can get it working using a separate resource file for each language, then use a resource manager to load up the strings.
我很快就会从事一个大型 c# 项目,并希望从一开始就构建多语言支持。我已经玩过了,可以使用每种语言的单独资源文件让它工作,然后使用资源管理器加载字符串。
Are there any other good approaches that I could look into?
还有其他好的方法可以研究吗?
回答by BenAlabaster
I've seen projects implemented using a number of different approaches, each have their merits and drawbacks.
我见过使用多种不同方法实施的项目,每种方法都有其优点和缺点。
- One did it in the config file (not my favourite)
- One did it using a database - this worked pretty well, but was a pain in the you know what to maintain.
- One used resource files the way you're suggesting and I have to say it was my favourite approach.
- The most basic one did it using an include file full of strings - ugly.
- 一个是在配置文件中完成的(不是我最喜欢的)
- 有人使用数据库来完成它 - 这工作得很好,但是你知道要维护什么是一种痛苦。
- 一个人按照您建议的方式使用资源文件,我不得不说这是我最喜欢的方法。
- 最基本的一个是使用一个充满字符串的包含文件 - 丑陋。
I'd say the resource method you've chosen makes a lot of sense. It would be interesting to see other people's answers too as I often wonder if there's a better way of doing things like this. I've seen numerous resources that all point to the using resources method, including one right here on SO.
我会说你选择的资源方法很有意义。看到其他人的答案也会很有趣,因为我经常想知道是否有更好的方法来做这样的事情。我已经看到许多资源都指向 using resources 方法,包括SO 上的一个。
回答by KMessenger
I'd go with the multiple resource files. It shouldn't be that hard to configure. In fact I recently answered a similar question on setting a global language based resource files in conjunction with form language resource files.
我会使用多个资源文件。配置起来应该不难。事实上,我最近回答了一个类似的问题,关于结合表单语言资源文件设置基于全局语言的资源文件。
Localization in Visual Studio 2008
I would consider that the best approach at least for WinForm development.
我认为至少对于 WinForm 开发来说这是最好的方法。
回答by Cade Roux
+1 Database
+1 数据库
Forms in your app can even re-translate themselves on the fly if corrections are made to the database.
如果对数据库进行更正,您的应用程序中的表单甚至可以即时重新翻译自己。
We used a system where all the controls were mapped in an XML file (one per form) to language resource IDs, but all the IDs were in the database.
我们使用了一个系统,其中所有控件都在 XML 文件中(每个表单一个)映射到语言资源 ID,但所有 ID 都在数据库中。
Basically, instead of having each control hold the ID (implementing an interface, or using the tag property in VB6), we used the fact that in .NET, the control tree was easily discoverable through reflection. A process when the form loaded would build the XML file if it was missing. The XML file would map the controls to their resource IDs, so this simply needed to be filled in and mapped to the database. This meant that there was no need to change the compiled binary if something was not tagged, or if it needed to be split to another ID (some words in English which might be used as both nouns and verbs might need to translate to two different words in the dictionary and not be re-used, but you might not discover this during initial assignment of IDs). But the fact is that the whole translation process becomes completely independent of your binary (every form has to inherit from a base form which knows how to translate itself and all its controls).
基本上,我们不是让每个控件都拥有 ID(实现一个接口,或在 VB6 中使用 tag 属性),而是利用了这样一个事实,即在 .NET 中,控件树很容易通过反射发现。如果缺少 XML 文件,则加载表单时的过程将构建它。XML 文件会将控件映射到它们的资源 ID,因此只需填写并映射到数据库即可。这意味着如果某些东西没有被标记,或者如果需要将其拆分为另一个 ID,则无需更改已编译的二进制文件(某些英语单词可能既用作名词又用作动词,可能需要翻译成两个不同的词)在字典中,不会被重复使用,但您可能不会在初始分配 ID 时发现这一点)。
The only ones where the app gets more involved is when a phase with insertion points is used.
应用程序唯一涉及更多的是使用带有插入点的阶段。
The database translation software was your basic CRUD maintenance screen with various workflow options to facilitate going through the missing translations, etc.
数据库翻译软件是您的基本 CRUD 维护屏幕,具有各种工作流程选项,以方便处理丢失的翻译等。
回答by Vasil
回答by MW_dev
I don't think there is a "best way". It really will depend on the technologies and type of application you are building.
我认为没有“最好的方法”。这实际上取决于您正在构建的应用程序的技术和类型。
Webapps can store the information in the database as other posters have suggested, but I recommend using seperate resource files. That is resource files seperate from your source. Seperate resource files reduces contention for the same files and as your project grows you may find localization will be done seperatly from business logic. (Programmers and Translators).
Webapps 可以按照其他海报的建议将信息存储在数据库中,但我建议使用单独的资源文件。那是与您的源分开的资源文件。单独的资源文件减少了对相同文件的争用,随着项目的增长,您可能会发现本地化将与业务逻辑分开完成。(程序员和翻译)。
Microsoft WinForm and WPF gurus recommend using seperate resource assemblies customized to each locale.
Microsoft WinForm 和 WPF 专家建议使用针对每个区域设置定制的单独资源程序集。
WPF's ability to size UI elements to content lowers the layout work required eg: (japanese words are much shorter than english).
WPF 根据内容调整 UI 元素大小的能力降低了所需的布局工作,例如:(日语单词比英语短得多)。
If you are considering WPF: I suggest reading this msdn articleTo be truthful I found the WPF localization tools: msbuild, locbaml, (and maybe an excel spreadsheet) tedious to use, but it does work.
如果您正在考虑 WPF:我建议您阅读这篇 msdn 文章老实说,我发现 WPF 本地化工具:msbuild、locbaml(可能还有一个 excel 电子表格)使用起来很乏味,但它确实有效。
Something only slightly related: A common problem I face is integrating legacy systems that send error messages (usually in english), not error codes. This forces either changes to legacy systems, or mapping backend strings to my own error codes and then to localized strings...yech. Error codes are localizations friend
一些稍微相关的事情:我面临的一个常见问题是集成发送错误消息(通常是英文)而不是错误代码的遗留系统。这会强制更改遗留系统,或者将后端字符串映射到我自己的错误代码,然后映射到本地化的字符串......是的。 错误代码是本地化的朋友
回答by Ben Dempsey
Standard resource files are easier. However, if you have any language dependent data such as lookup tables then you will have to manage two resource sets.
标准资源文件更容易。但是,如果您有任何语言相关数据,例如查找表,那么您将必须管理两个资源集。
I haven't done it, but in my next project I would implement a database resource provider. I found how to do it on MSDN:
我还没有这样做,但在我的下一个项目中,我将实现一个数据库资源提供程序。我在 MSDN 上找到了方法:
http://msdn.microsoft.com/en-us/library/aa905797.aspx
http://msdn.microsoft.com/en-us/library/aa905797.aspx
I also found this implementation:
我还发现了这个实现:
回答by Cossintan
We use a custom provider for multi language support and put all texts in a database table. It works well except we sometimes face caching problems when updating texts in the database without updating the web application.
我们使用自定义提供程序来支持多语言并将所有文本放入数据库表中。除了在更新数据库中的文本而不更新 Web 应用程序时有时会遇到缓存问题之外,它运行良好。
回答by Davorin
回答by JxXx
I′ve been searching and I′ve found this:
我一直在寻找,我发现了这个:
If your using WPF or Silverlight your aproach could be use WPF LocalizationExtensionfor many reasons.
如果您使用 WPF 或 Silverlight,出于多种原因,您的方法可能是使用WPF LocalizationExtension。
IT′s Open Source It′s FREE (and will stay free) is in a real stabel state
IT 的开源 It's FREE(并将保持免费)处于真正稳定的状态
In a Windows Application you could do someting like this:
在 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);
}
}
And I think on a Wep Page the aproach could be the same.
我认为在 Wep Page 上的方法可能是相同的。
Good Luck!
祝你好运!
回答by CularBytes
Use a separate project with Resources
使用带有资源的单独项目
I can tell this from out experience, having a current solution with 1224projects that includes API, MVC, Project Libraries (Core functionalities), WPF, UWP and Xamarin. It is worth reading this long post as I think it is the best way to do so. With the help of VS tools easily exportable and importable to sent to translation agencies or review by other people.
我可以从经验中看出这一点,目前有一个包含12 24个项目的解决方案,其中包括 API、MVC、项目库(核心功能)、WPF、UWP 和 Xamarin。这篇长文值得一读,因为我认为这是最好的方法。借助 VS 工具,可轻松导出和导入以发送给翻译机构或由其他人审阅。
EDIT 02/2018:Still going strong, converting it to a .NET Standard library makes it possible to even use it across .NET Framework and NET Core. I added an extra section for converting it to JSON so for example angular can use it.
编辑 02/2018:仍然很强大,将其转换为 .NET Standard 库甚至可以跨 .NET Framework 和 NET Core 使用它。我添加了一个额外的部分来将其转换为 JSON,因此例如 angular 可以使用它。
EDIT 2019:Going forward with Xamarin, this still works across all platforms. E.g. Xamarin.Forms advices to use resx files as well. (I did not develop an app in Xamarin.Forms yet, but the documentation, that is way to detailed to just get started, covers it: Xamarin.Forms Documentation). Just like converting it to JSON we can also convert it to a .xml file for Xamarin.Android.
编辑 2019:继续使用 Xamarin,这仍然适用于所有平台。例如,Xamarin.Forms 建议也使用 resx 文件。(我还没有在 Xamarin.Forms 中开发应用程序,但是文档,这是刚开始的详细方式,涵盖了它:Xamarin.Forms 文档)。就像将其转换为 JSON 一样,我们也可以将其转换为 Xamarin.Android 的 .xml 文件。
EDIT 2019 (2):While upgrading to UWP from WPF, I encountered that in UWP they prefer to use another filetype .resw
, which is is in terms of content identical but the usage is different. I found a different way of doing this which, in my opinion, works better then the default solution.
编辑 2019 (2):从 WPF 升级到 UWP 时,我遇到在 UWP 中他们更喜欢使用另一种文件类型.resw
,即内容相同但用法不同。我找到了一种不同的方法,在我看来,它比默认解决方案效果更好。
So, lets get to it.
所以,让我们开始吧。
Pro's
亲
- Strongly typed almost everywhere.
- In WPF you don't have to deal with
ResourceDirectories
. - Supported for ASP.NET, Class Libraries, WPF, Xamarin, .NET Core, .NET Standard as far as I have tested.
- No extra third-party libraries needed.
- Supports culture fallback: en-US -> en.
- Not only back-end, works also in XAML for WPF and Xamarin.Forms, in .cshtml for MVC.
- Easily manipulate the language by changing the
Thread.CurrentThread.CurrentCulture
- Search engines can Crawl in different languages and user can send or save language-specific urls.
- 几乎无处不在的强类型。
- 在 WPF 中,您不必处理
ResourceDirectories
. - 据我测试,支持 ASP.NET、类库、WPF、Xamarin、.NET Core、.NET Standard。
- 不需要额外的第三方库。
- 支持文化回退:en-US -> en。
- 不仅是后端,还可以在 XAML 中用于 WPF 和 Xamarin.Forms,在 .cshtml 中用于 MVC。
- 通过更改语言轻松操纵语言
Thread.CurrentThread.CurrentCulture
- 搜索引擎可以使用不同的语言进行爬网,用户可以发送或保存特定语言的 url。
Con's
骗子
- WPF XAML is sometimes buggy, newly added strings don't show up directly. Rebuild is the temp fix (vs2015).
- UWP XAML does not show intellisense suggestions and does not show the text while designing.
- Tell me.
- WPF XAML 有时会出错,新添加的字符串不会直接显示。重建是临时修复(vs2015)。
- UWP XAML 不显示智能感知建议,也不会在设计时显示文本。
- 告诉我。
Setup
设置
Create language project in your solution, give it a name like MyProject.Language. Add a folder to it called Resources, and in that folder, create two Resources files (.resx). One called Resources.resxand another called Resources.en.resx(or .en-GB.resx for specific). In my implementation, I have NL (Dutch) language as the default language, so that goes in my first file, and English goes in my second file.
在您的解决方案中创建语言项目,为其命名为MyProject.Language。向其中添加一个名为 Resources 的文件夹,并在该文件夹中创建两个资源文件 (.resx)。一个称为Resources.resx,另一个称为Resources.en.resx(或特定的 .en-GB.resx)。在我的实现中,我将 NL(荷兰语)语言作为默认语言,因此它在我的第一个文件中,而英语在我的第二个文件中。
Setup should look like this:
设置应如下所示:
The properties for Resources.resx must be:
Make sure that the custom tool namespace is set to your project namespace. Reason for this is that in WPF, you cannot reference to Resources
inside XAML.
确保自定义工具命名空间设置为您的项目命名空间。原因是在 WPF 中,您不能引用Resources
内部 XAML。
And inside the resource file, set the access modifier to Public:
在资源文件中,将访问修饰符设置为 Public:
Using in another project
在另一个项目中使用
Reference to your project: Right click on References -> Add Reference -> Prjects\Solutions.
引用您的项目:右键单击 References -> Add Reference -> Prjects\Solutions。
Use namespace in a file: using MyProject.Language;
在文件中使用命名空间: using MyProject.Language;
Use it like so in back-end:
string someText = Resources.orderGeneralError;
If there is something else called Resources, then just put in the entire namespace.
在后端像这样使用它:
string someText = Resources.orderGeneralError;
如果还有其他名为 Resources 的东西,那么只需放入整个命名空间。
Using in MVC
在MVC中使用
In MVC you can do however you like to set the language, but I used parameterized url's, which can be setup like so:
在 MVC 中,您可以根据自己的喜好设置语言,但我使用了参数化的 url,可以像这样设置:
RouteConfig.csBelow the other mappings
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(might need to be added, if so, add FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
to the Application_start()
method in Global.asax
FilterConfig.cs(可能需要添加,如果添加FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
到Application_start()
方法中Global.asax
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);
}
}
LanguageHelperjust sets the Culture info.
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)
{
}
}
Usage in .cshtml
.cshtml 中的用法
@using MyProject.Language;
<h3>@Resources.w_home_header</h3>
or if you don't want to define usings then just fill in the entire namespace OR you can define the namespace under /Views/web.config:
或者,如果您不想定义 using,则只需填写整个命名空间,或者您可以在 /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>
This mvc implementation source tutorial: Awesome tutorial blog
这个mvc实现源码教程:Awesome教程博客
Using in class libraries for models
在模型类库中使用
Back-end using is the same, but just an example for using in attributes
后端使用是一样的,只是在属性中使用的一个例子
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; }
}
}
If you have reshaper it will automatically check if the given resource name exists. If you prefer type safety you can use T4 templates to generate an enum
如果你有 reshaper,它会自动检查给定的资源名称是否存在。如果您更喜欢类型安全,则可以使用T4 模板生成枚举
Using in WPF.
在 WPF 中使用。
Ofcourse add a reference to your MyProject.Languagenamespace, we know how to use it in back-end.
当然添加对MyProject.Language命名空间的引用,我们知道如何在后端使用它。
In XAML, inside the header of a Window or UserControl, add a namespace reference called lang
like so:
在 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">
Then, inside a label:
然后,在标签内:
<Label x:Name="lblHeader" Content="{x:Static lang:Resources.w_home_header}" TextBlock.FontSize="20" HorizontalAlignment="Center"/>
Since it is strongly typed you are sure the resource string exists. You might need to recompile the project sometimes during setup, WPF is sometimes buggy with new namespaces.
由于它是强类型的,因此您确定资源字符串存在。您有时可能需要在安装过程中重新编译项目,WPF 有时会因新命名空间而出错。
One more thing for WPF, set the language inside the App.xaml.cs
. You can do your own implementation (choose during installation) or let the system decide.
WPF 的另一件事是在App.xaml.cs
. 您可以自己实施(在安装过程中选择)或让系统决定。
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;
}
}
}
Using in UWP
在 UWP 中使用
In UWP, Microsoft uses this solution, meaning you will need to create new resource files. Plus you can not re-use the text either because they want you to set the x:Uid
of your control in XAML to a key in your resources. And in your resources you have to do Example.Text
to fill a TextBlock
's text. I didn't like that solution at all because I want to re-use my resource files. Eventually I came up with the following solution. I just found this out today (2019-09-26) so I might come back with something else if it turns out this doesn't work as desired.
在 UWP 中,Microsoft 使用此解决方案,这意味着您将需要创建新的资源文件。另外,您也不能重复使用文本,因为他们希望您将x:Uid
XAML 中的控件设置为资源中的键。在您的资源中,您必须Example.Text
填写 aTextBlock
的文本。我根本不喜欢那个解决方案,因为我想重新使用我的资源文件。最终我想出了以下解决方案。我今天(2019 年 9 月 26 日)才发现这一点,所以如果结果证明这不能按预期工作,我可能会带回其他东西。
Add this to your project:
将此添加到您的项目中:
using Windows.UI.Xaml.Resources;
public class MyXamlResourceLoader : CustomXamlResourceLoader
{
protected override object GetResource(string resourceId, string objectType, string propertyName, string propertyType)
{
return MyProject.Language.Resources.ResourceManager.GetString(resourceId);
}
}
Add this to App.xaml.cs
in the constructor:
将此添加到App.xaml.cs
构造函数中:
CustomXamlResourceLoader.Current = new MyXamlResourceLoader();
Where ever you want to in your app, use this to change the language:
无论您想在应用程序中的哪个位置,使用它来更改语言:
ApplicationLanguages.PrimaryLanguageOverride = "nl";
Frame.Navigate(this.GetType());
The last line is needed to refresh the UI. While I am still working on this project I noticed that I needed to do this 2 times. I might end up with a language selection at the first time the user is starting. But since this will be distributed via Windows Store, the language is usually equal to the system language.
需要最后一行来刷新 UI。当我仍在从事这个项目时,我注意到我需要这样做 2 次。我可能会在用户第一次启动时选择语言。但由于这将通过 Windows 应用商店分发,因此语言通常等于系统语言。
Then use in XAML:
然后在 XAML 中使用:
<TextBlock Text="{CustomResource ExampleResourceKey}"></TextBlock>
Using it in Angular (convert to JSON)
在 Angular 中使用它(转换为 JSON)
Now days it is more common to have a framework like Angular in combination with components, so without cshtml. Translations are stored in json files, I am not going to cover how that works, but if you want to convert this to a JSON file, it is pretty easy, I use a T4 template script that converts the Resources file to a json file. I recommend installing T4 editorto read the syntax and use it correctly because you need to do some modifications.
现在,将像 Angular 这样的框架与组件结合使用更为常见,因此没有 cshtml。翻译存储在 json 文件中,我不打算介绍它的工作原理,但是如果您想将其转换为 JSON 文件,这很容易,我使用 T4 模板脚本将资源文件转换为 json 文件。我建议安装T4编辑器来阅读语法并正确使用它,因为您需要做一些修改。
Only 1 thing to note: It is not possible to generate the data, copy it, clean the data and generate it for another language. So you have to copy below code as many times as languages you have and change the entry before '//choose language here'. Currently no time to fix this but probably will update later (if interested).
只有一件事需要注意:不可能生成数据、复制数据、清理数据并为另一种语言生成数据。因此,您必须根据您拥有的语言多次复制以下代码,并更改“//在此处选择语言”之前的条目。目前没有时间解决这个问题,但可能会稍后更新(如果有兴趣)。
Path: MyProject.Language/T4/CreateWebshopLocalizationEN.tt
路径:MyProject.Language/T4/CreateWebshopLocalizationEN.tt
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.ComponentModel.Design" #>
<#@ output extension=".json" #>
<#
var fileNameNl = "../Resources/Resources.resx";
var fileNameEn = "../Resources/Resources.en.resx";
var fileNameDe = "../Resources/Resources.de.resx";
var fileNameTr = "../Resources/Resources.tr.resx";
var fileResultName = "../T4/CreateWebshopLocalizationEN.json";//choose language here
var fileResultPath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", fileResultName);
//var fileDestinationPath = "../../MyProject.Web/ClientApp/app/i18n/";
var fileNameDestNl = "nl.json";
var fileNameDestEn = "en.json";
var fileNameDestDe = "de.json";
var fileNameDestTr = "tr.json";
var pathBaseDestination = Directory.GetParent(Directory.GetParent(this.Host.ResolvePath("")).ToString()).ToString();
string[] fileNamesResx = new string[] {fileNameEn }; //choose language here
string[] fileNamesDest = new string[] {fileNameDestEn }; //choose language here
for(int x = 0; x < fileNamesResx.Length; x++)
{
var currentFileNameResx = fileNamesResx[x];
var currentFileNameDest = fileNamesDest[x];
var currentPathResx = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", currentFileNameResx);
var currentPathDest =pathBaseDestination + "/MyProject.Web/ClientApp/app/i18n/" + currentFileNameDest;
using(var reader = new ResXResourceReader(currentPathResx))
{
reader.UseResXDataNodes = true;
#>
{
<#
foreach(DictionaryEntry entry in reader)
{
var name = entry.Key;
var node = (ResXDataNode)entry.Value;
var value = node.GetValue((ITypeResolutionService) null);
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("\n", "");
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("\r", "");
#>
"<#=name#>": "<#=value#>",
<#
}
#>
"WEBSHOP_LASTELEMENT": "just ignore this, for testing purpose"
}
<#
}
File.Copy(fileResultPath, currentPathDest, true);
}
#>
Use in Xamarin.Android
在 Xamarin.Android 中使用
As explained above in the updates, I use the same method as I have done with Angular/JSON. But Android uses XML files, so I wrote a T4 file that generates those XML files.
正如上面在更新中所解释的,我使用的方法与我对 Angular/JSON 所做的相同。但是 Android 使用 XML 文件,所以我编写了一个 T4 文件来生成这些 XML 文件。
Path: MyProject.Language/T4/CreateAppLocalizationEN.tt
路径:MyProject.Language/T4/CreateAppLocalizationEN.tt
#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.ComponentModel.Design" #>
<#@ output extension=".xml" #>
<#
var fileName = "../Resources/Resources.en.resx";
var fileResultName = "../T4/CreateAppLocalizationEN.xml";
var fileResultRexPath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", fileName);
var fileResultPath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", fileResultName);
var fileNameDest = "strings.xml";
var pathBaseDestination = Directory.GetParent(Directory.GetParent(this.Host.ResolvePath("")).ToString()).ToString();
var currentPathDest =pathBaseDestination + "/MyProject.App.AndroidApp/Resources/values-en/" + fileNameDest;
using(var reader = new ResXResourceReader(fileResultRexPath))
{
reader.UseResXDataNodes = true;
#>
<resources>
<#
foreach(DictionaryEntry entry in reader)
{
var name = entry.Key;
//if(!name.ToString().Contains("WEBSHOP_") && !name.ToString().Contains("DASHBOARD_"))//only include keys with these prefixes, or the country ones.
//{
// if(name.ToString().Length != 2)
// {
// continue;
// }
//}
var node = (ResXDataNode)entry.Value;
var value = node.GetValue((ITypeResolutionService) null);
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("\n", "");
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("\r", "");
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("&", "&");
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("<<", "");
//if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("'", "\'");
#>
<string name="<#=name#>">"<#=value#>"</string>
<#
}
#>
<string name="WEBSHOP_LASTELEMENT">just ignore this</string>
<#
#>
</resources>
<#
File.Copy(fileResultPath, currentPathDest, true);
}
#>
Android works with values-xx
folders, so above is for English for in the values-en
folder. But you also have to generate a default which goes into the values
folder. Just copy above T4 template and change the folder in the above code.
Android 使用values-xx
文件夹,所以上面是文件夹中的英文values-en
。但是您还必须生成一个进入values
文件夹的默认值。只需复制上面的 T4 模板并更改上面代码中的文件夹。
There you go, you can now use one single resource file for all your projects. This makes it very easy exporting everything to an excl document and let someone translate it and import it again.
好了,您现在可以为所有项目使用一个资源文件。这使得将所有内容导出到 excl 文档并让某人翻译并再次导入变得非常容易。
Special thanks to this amazing VS extensionwhich works awesome with resx
files. Consider donatingto him for his awesome work (I have nothing to do with that, I just love the extension).
特别感谢这个惊人的 VS 扩展,它可以很好地处理resx
文件。考虑为他出色的工作捐赠给他(我与此无关,我只是喜欢扩展名)。