- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我很快就会从事一个大型 c# 项目,并希望从一开始就构建多语言支持。我已经玩过了,可以使用每种语言的单独资源文件让它工作,然后使用资源管理器加载字符串。
还有其他好的方法可以研究吗?
最佳答案
使用带有资源的单独项目
我可以从经验中看出这一点,目前的解决方案有 12 个 24 个项目,其中包括 API、MVC、项目库(核心功能)、WPF、UWP 和 Xamarin。这篇长文值得一读,因为我认为这是最好的方法。借助 VS 工具,可轻松导出和导入以发送给翻译机构或由其他人审阅。
EDIT 02/2018: 仍然很强大,将其转换为 .NET Standard 库,甚至可以跨 .NET Framework 和 NET Core 使用它。我添加了一个额外的部分来将其转换为 JSON,因此例如 angular 可以使用它。
EDIT 2019: 继续使用 Xamarin,这仍然适用于所有平台。例如。 Xamarin.Forms 建议也使用 resx 文件。 (我还没有在 Xamarin.Forms 中开发应用程序,但是文档,这是刚开始的详细方式,涵盖了它: Xamarin.Forms Documentation )。就像将其转换为 JSON 一样,我们也可以将其转换为 Xamarin.Android 的 .xml 文件。
EDIT 2019 (2): 在从 WPF 升级到 UWP 时,我遇到在 UWP 中他们更喜欢使用另一种文件类型 .resw
,这在内容方面相同但用法不同。我找到了一种不同的方法,在我看来,它比 the default solution 效果更好。
2020 年编辑: 更新了一些可能需要多语言项目的大型(模块化)项目的建议。
所以,让我们开始吧。
专业人士的
ResourceDirectories
。 Thread.CurrentThread.CurrentCulture
Resources
。
using MyProject.Language;
在后端像这样使用它:
string someText = Resources.orderGeneralError;
如果还有其他叫做资源的东西,那么只需放入整个命名空间。
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);
到
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);
}
}
本地化属性
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>
或者,如果您不想定义 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>
本mvc实现源码教程:
Awesome tutorial blog
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; }
}
}
如果你有 reshaper,它会自动检查给定的资源名称是否存在。如果你更喜欢类型安全,你可以使用
T4 templates to generate an enum
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"/>
由于它是强类型的,因此您确定资源字符串存在。您有时可能需要在安装过程中重新编译项目,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;
}
}
}
在 UWP 中使用
x:Uid
设置为资源中的键。在您的资源中,您必须执行
Example.Text
来填充
TextBlock
的文本。我根本不喜欢那个解决方案,因为我想重新使用我的资源文件。最终我想出了以下解决方案。我今天(2019 年 9 月 26 日)才发现这一点,所以如果结果证明这不能按预期工作,我可能会带回其他东西。
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);
}
}
将此添加到构造函数中的
App.xaml.cs
:
CustomXamlResourceLoader.Current = new MyXamlResourceLoader();
无论您想在应用程序中的哪个位置,使用它来更改语言:
ApplicationLanguages.PrimaryLanguageOverride = "nl";
Frame.Navigate(this.GetType());
需要最后一行来刷新 UI。当我仍在从事这个项目时,我注意到我需要这样做 2 次。我可能会在用户第一次启动时选择语言。但由于这将通过 Windows 应用商店分发,因此语言通常等于系统语言。
<TextBlock Text="{CustomResource ExampleResourceKey}"></TextBlock>
在 Angular 中使用它(转换为 JSON)
<#@ 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/CreateLocalizationEN.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);
}
#>
如果您有一个 modulair 应用程序并且您按照我的建议创建了多个语言项目,那么您必须为每个项目创建一个 T4 文件。确保 json 文件是逻辑定义的,它不必是
en.json
,也可以是
example-en.json
。要组合多个 json 文件以与
ngx-translate 一起使用,请按照说明
here
#@ 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 使用
values-xx
文件夹,所以上面是
values-en
文件夹中的英文。但是您还必须生成一个进入
values
文件夹的默认值。只需复制上面的 T4 模板并更改上面代码中的文件夹。
resx
文件一起工作很棒。考虑
donating 给他的出色工作(我与此无关,我只是喜欢扩展名)。
关于c# - 在大型 .NET 项目中实现多语言/全局化的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/373388/
创建使用.NET框架的asp.net页面时,访问该页面的客户端是否需要在其计算机上安装.NET框架? IE。用户访问www.fakesite.com/default.aspx,如果他们没有安装框架,他
我阅读了很多不同的博客和 StackOverflow 问题,试图找到我的问题的答案,但最后我找不到任何东西,所以我想自己问这个问题。 我正在构建一个应用程序,其中有一个长时间运行的工作线程,它执行一些
已锁定。这个问题及其答案是locked因为这个问题是题外话,但却具有历史意义。目前不接受新的答案或互动。 我一直想知道为什么微软为这样一个伟大的平台选择了一个如此奇怪的、对搜索引擎不友好的名称。他们就
.Net Framework .Net .NET Standard的区别 1、.NET Framework 在未来.NET Framework或许成为过去时,目前还是有很多地方在使用的。这一套
如果有选择的话,您会走哪条路? ASP.NET Webforms + ASP.NET AJAX 或 ASP.NET MVC + JavaScript Framework of your Choice
我有一个 Web 服务,它通过专用连接通过 https 使用第三方 Web 服务,我应用了 ServicePointManager.ServerCertificateValidationCallbac
为什么我应该选择ASP.NET Web Application (.NET Framework)而不是ASP.NET Core Web Application (.NET Framework)? 我在
我在网络上没有找到任何关于包含 .NET Standard、.NET Core 和 .NET Framework 项目的 .NET 解决方案的公认命名约定。 就我而言,我们在 .NET 框架项目中有以
.NET Compact 是 .NET 的完美子集吗? 假设我考虑了屏幕大小和其他限制并避免了 .NET Compact 不支持的类和方法,或者 .NET Compact 是一个不同且不兼容的 GUI
我已经阅读了所有我能找到的关于 connectionManagement 中的 maxconnection 设置的文章:即 http://support.microsoft.com/kb/821268
我现在正在使用asp.net mvc,想知道使用内置的Json或 Json.Net哪个是更好的选择,但我不确定一个人是否比另一个人有优势。 另外,如果我确实选择沿用Json.Net的路线,那么我应该选
在 Visual Studio 中,您至少可以创建三种不同类型的类库: 类库(.NET Framework) 类库(.NET 标准) 类库(.NET Core) 虽然第一个是我们多年来一直使用的,但我
.NET 和 ASP.NET 之间有什么区别?它们有什么关系? 最佳答案 ASP.Net 基于 .Net 框架构建,提供有关 Web 开发的附加功能。 你可以去看看wikipedia article
在安装更高版本(3.0)之前,我需要安装.net框架1.1和2.0吗?或者单独安装 3.0 框架就足够了,并为在早期框架版本上编写的软件提供支持?谢谢 ,丽然 最佳答案 不,您不必安装以前的框架。 我
我正在开发一个项目,人们可以“更新”类别,例如更改类别的名称。我收到以下消息 This is called after clicking update 按钮 with the SQL statemen
.NET 类 System.Net.CookieContainer 线程安全吗? --更新:交 key 答复-- 是否有任何方法可以确保异步请求期间修改的变量(即 HttpWebRequest.Coo
我正在使用 JScript.NET 在我编写的 C# WinForms 应用程序中编写脚本。它工作得很好,但我只是尝试在脚本中放置一些异常处理,但我无法弄清楚如何判断我的 C# 代码抛出了哪种类型的异
我需要你的帮助, 比如我有一个小数类型的变量,我想这样取整。 例如 3.0 = 3 3.1 = 4 3.2 = 4 3.3 = 4 3.4 = 4 3.5 = 4 3.6 = 4 3.7 = 4 3.
我使用过这样的代码:http://msdn.microsoft.com/en-us/library/dw70f090.aspx在 ASP.NET 中工作之前访问数据库(2-3 年前)。我没有意识到我正
自 ConfigurationManager .NET Standard 中不存在,检索正在执行的程序集的应用程序设置的最佳方法是什么,无论是 web.config或 appSettings.{env
我是一名优秀的程序员,十分优秀!