目录

介绍

本地化更长的字符串

设计理由

与ResourceManager结合使用

使用字符串插值进行本地化

源代码


介绍

几年来,我一直在构建一个主题为“应该构建到.NET框架中的东西,但不是”的库。但我一直推迟写关于应该内置的东西的文章,但是,你知道,不是。再也不是!它被称为Loyc.Essentials,您可以通过NuGet获取它(它以Loyc命名,但这并不重要。)

Loyc.Essentials有一个Localize类,它是一个全局钩子,可以在其中安装字符串映射本地化器。如果你还在使用Loyc.Essentials,你应该使用它。它准备将您的程序翻译成其他语言,几乎不费吹灰之力。

这个想法是通过让本地化变得非常容易来说服程序员支持它。默认情况下,它没有连接到任何翻译器(它只是通过字符串),所以只为一种语言市场编写程序的人可以轻松地使他们的代码“多语言准备”,而无需做任何额外的工作。

你所要做的就是调用.Localized()扩展方法,这实际上比编写传统string.Format()方法要短。(同时:using Loyc;)

编辑:一般来说,这不适用于C#6的插值字符串($"..."),因为C#6是如何设计的,但本文末尾描述了一种解决方法。

翻译系统本身独立于Localize,并通过委托连接Localized(),以便多语言翻译系统成为可能。此类应该适用于任何.NET程序,并且使用此实用程序的某些程序将希望使用不同的本地化程序。

像这样使用它:

string result = "Hello, {0}".Localized(userName);

或者,为了提高清晰度,请使用命名占位符:

string result = "Hello, {person's name}".Localized("person's name", userName);

无论安装何种本地化程序,都会在其数据库中查找文本并返回翻译。如果没有最终用户语言的翻译,则应返回适当的默认翻译:原始文本或某些默认语言的翻译,例如英语。

本地化程序需要一个外部翻译表,在概念上如下:

关键名称

语言

翻译文本

“Hello,{0}”

“ES”

“Hola,{0}”

“Hello,{0}”

“FR”

“Bonjour,{0}”

“Load

“ES”

“Cargar”

“Load

“FR”

“Charge

“Save

“ES”

“Guardar

“Save

“FR”

“Enregistrer”

许多开发人员使用resx文件来存储翻译。支持这一点,如下所述。

本地化更长的字符串

对于较长的消息,最好使用短名称来表示消息,以便在编辑英文文本时,不必更改其他语言的转换表。为此,请使用以下Symbol方法:

// The translation table will be searched for "ConfirmQuitWithoutSaving"
string result = Localize.Symbol("ConfirmQuitWithoutSaving","Are you sure you want to quit without saving '{filename}'?", "filename", fileName);// Enhanced C# syntax with symbol literal
string result = Localize.Symbol(@@ConfirmQuitWithoutSaving,"Are you sure you want to quit without saving '{filename}'?", "filename", fileName);

这对于长字符串或文本段落最有用,但我希望某些项目,作为策略,使用符号表示所有可本地化的文本。

同样,您可以在不设置任何转换表的情况下调用此方法。但是,允许实际的消息为null。在这种情况下,如果没有设置翻译器或没有可用的翻译,则Localize.Symbol返回符号本身(第一个参数)作为最后的手段。

如果变量参数列表不为空,则Localize.Formatter被调用以从格式字符串构建完成的字符串。可以单独进行格式化——例如:

Console.WriteLine("{0} is {0:X} in hexadecimal".Localized(), N);

在此示例中,WriteLine它本身执行格式化,而不是Localized。

如上所示,Localize默认格式化程序StringExt.FormatCore具有标准格式化程序不具备的额外功能:命名参数。这是一个例子:

...
string verb = (IsFileLoaded ? "parse" : "load").Localized();
MessageBox.Show("Not enough memory to {load/parse} '{filename}'.".Localized("load/parse", verb, "filename", FileName));

如您所见,通过指定参数名称{filename}而不是像{0}这样的数字,在格式字符串中提到了命名参数。变量参数列表包含相同的名称,后跟其值,例如"filename", FileName。此功能使您(开发人员)有机会告诉写作翻译人员特定参数的目的是什么。

译者不得改变任何论点:这个词{filename}不能被翻译。

在运行时,带有命名参数的格式字符串将转换为带有编号参数的“普通”格式字符串。上面的示例将变为“Could not {1} the file: {3}”,然后传递给string.Format。

设计理由

许多开发人员不想花时间编写国际化或本地化代码,并且很想编写仅适用于一种语言的代码。毫无疑问,因为与硬编码字符串相比,这是一个痛苦的问题。Microsoft建议代码携带一个ResourceManager对象并直接从中请求字符串:

private ResourceManager rm;rm = new ResourceManager("RootNamespace.Resources", this.GetType().Assembly);Console.Writeline(rm.GetString("StringIdentifier"));

这种方法有缺点:

  • 在可能包含可本地化字符串的所有类之间传递ResourceManager实例可能很麻烦; 全局工具更方便。
  • 程序员必须将所有翻译放在资源文件中; 因此,编写代码很麻烦,因为程序员必须切换到资源文件并将字符串添加到代码中。反过来,读取代码的人无法分辨出字符串的含义,并且必须加载资源文件才能找到答案。
  • 改变本地化管理并不容易; 例如,如果有人想将翻译存储在an.ini,.xml或.les文件中而不是在程序集内部,该怎么办?如果用户想要集中一组程序集的所有翻译,而不是在每个程序集中拥有单独的资源,该怎么办?
  • 如果找不到请求的标识符,则GetString返回null,可能导致空白输出或一个NullReferenceException异常 。

Microsoft确实通过提供Visual Studio内置的代码生成器来解决第一个缺点,它为每个字符串提供了一个全局属性; 看这里。

即便如此,您可能会发现此类提供了一种更方便的方法,因为您的本地语言字符串是在代码中编写的,并且如果所需的语言不可用,您可以保证在运行时获取字符串(非空)。

与ResourceManager结合使用

该类通过UseResourceManager帮助类方法支持ResourceManager 。例如,在调用Localize.UseResourceManager(resourceManager)之后,如果你编写

"Save As...".Localized()

然后resourceManager.GetString("Save As...") 被调用以获取已翻译的字符串,如果未找到翻译则调用原始字符串(是的,在您的resx文件中,您可以在左侧使用空格和标点符号)。您甚至可以添加“名称计算器”来编码resx文件的命名约定,例如删除空格和标点符号(有关详细信息,请查看UseResourceManager方法。)

在.NET程序中,通常有一个“主”resx文件,例如Resources.resx,它包含默认字符串,以及其他非英语翻译文件(例如,西班牙语的Resources.es.resx)。当使用Localized()时您可能会使用稍微不同的方法:您仍然为项目创建一个Resources.resx文件,但是您将字符串表保留为空(您仍然可以将其用于其他资源,例如图标)。这会导致Visual Studio生成一个具有ResourceManager属性的Resources类,以便您可以轻松获取所需的ResourceManager实例。

  • 程序启动时,调用Localize.UseResourceManager(Resources.ResourceManager)。
  • 使用Localized()扩展方法获取短字符串的翻译。
  • 对于长串,请使用Localize.Symbol("ShortAlias", "Long string", params...)。第一个参数是传递给ResourceManager.GetString()的字符串

使用字符串插值进行本地化

可以将本地化与C#6内插字符串结合起来,就像在  $"this string {...}"中一样(并感谢  Florian Rappl 让我注意到这一点。)

不幸的是,Localize() 不能与他们合作。

最初我认为它根本不可能,因为通常字符串插值被转换为  string.Format,其行为无法自定义。但是,与lambda方法有时成为表达式树的方式大致相同,如果目标方法接受System.FormattableString对象,编译器将切换  string.Format 到FormattableStringFactory.Create(.NET 4.6方法)。

问题是,如果可能的话,编译器会更喜欢调用string.Format,所以如果有一个接受FormattableString的Localized()重载  ,它就不能用于字符串插值,因为C#编译器会简单地忽略它(因为Localized()它已经可以接受一个字符串)。实际上,它比这更糟糕:编译器在调用扩展方法时也拒绝使用FormattableString 。

如果您使用非扩展方法,它可以工作。例如:

static class Loca
{public static string lize(this FormattableString message){ return message.Format.Localized(message.GetArguments()); }
}

然后你可以像这样使用它:

public class Program
{public static void Main(string[] args){Localize.UseResourceManager(Resources.ResourceManager);var name = "Dave";Console.WriteLine(Loca.lize($"Hello, {name}"));}
}

重要的是要意识到编译器将$"..."字符串转换为旧式格式字符串。所以在这个例子中,Loca.lize 实际上接收"Hello, {0}"的格式字符串而不是"Hello, {name}"。

不幸的是,有点令人困惑的是,与普通字符串相比,我们需要一种完全不同的本地化插值字符串方式,如果你忘了——如果你写了$"Hello, {name}".Localized()——你的代码将被破坏,因为格式化将在本地化之前发生,因此没有翻译将被找到。

为了避免这种混淆,我不打算扩展我的库以支持字符串插值,但如果您更喜欢在应用程序中使用字符串插值,您仍然可以通过添加类似Loca.lize 项目的帮助方法来对其进行本地化。

源代码

源代码在这里。不幸的是,它使用了一些特定于Loyc.Essentials的类型(Symbol ,ThreadLocalVariable<T>,SavedValue<T>和ScratchBuffer<T>),所以如果你想要在不使用Loyc.Essentials NuGet包的情况下使用Localize,你必须花费一些时间来转换到“plain-old “C#。

原文地址:https://www.codeproject.com/Articles/1165045/Prepare-all-your-apps-for-localization

准备好所有应用程序以进行本地化相关推荐

  1. 在ASP.NET AJAX中使用应用程序服务和本地化(3):用户个性化组件ProfileService

    本文来自<ASP.NET AJAX程序设计 第II卷:客户端Microsoft AJAX Library相关>的第五章<应用程序服务和本地化>. 在内建了身份认证应用程序服务之 ...

  2. 在ASP.NET AJAX中使用应用程序服务和本地化(4):示例程序:读取、修改并保存用户个性化信息...

    本文来自<ASP.NET AJAX程序设计 第II卷:客户端Microsoft AJAX Library相关>的第五章<应用程序服务和本地化>. 让我们通过编写一个完整的示例程 ...

  3. gettext 国际化_如何使用Gettext在Phoenix应用程序中执行本地化

    gettext 国际化 by Anastasia 由Anastasia 如何使用Gettext在Phoenix应用程序中执行本地化 (How to perform localization in Ph ...

  4. 在ASP.NET AJAX中使用应用程序服务和本地化(5):自定义应用程序服务的服务器端实现...

    本文来自<ASP.NET AJAX程序设计 第II卷:客户端Microsoft AJAX Library相关>的第五章<应用程序服务和本地化>. 身份认证与用户个性化等应用程序 ...

  5. IOS应用程序多语言本地化解决方案

    最近要对一款游戏进行多语言本地化,在网上找了一些方案,加上自己的一点点想法整理出一套方案和大家分享! 多语言在应用程序中一般有两种做法: 一.程序中提供给用户自己选择的机会: 二.根据当前用户当前移动 ...

  6. mfc 应用程序 语言进行本地化

    在软件国际化的今天,资源从代码中独立出来,使在不同语言操作系统下能运行不同语言版本的程序,是很有意义的事. MFC 7.0 及更高版本提供对附属 DLL 的增强支持,该功能有助于创建针对多种语言进行本 ...

  7. IOS应用程序自身的本地化

    为啥要说应用程序自身?因为普通的本地化是根据设备当前的设置来完成的.而实际中一些应用往往需要与设备设置无关的本地化.例如一款游戏在游戏中,玩家可以选择游戏的语言,但是并不会改变所用设备的语言. 近期工 ...

  8. oracle 国际化时间,Oracle Solaris中的应用程序国际化和本地化

    libc库中可用于代码转换的iconv()函数如下: iconv_open() 代码转换分配功能 代码转换功能 iconv_close() 代码转换取消分配功能 iconvctl() 控制和查询代码转 ...

  9. BlackBerry 应用程序开发者指南 第一卷:基础--第8章 本地化应用程序

    作者:Confach 发表于2006-04-28 21:43 pm 版权信息:可以任意转载, 转载时请务必以超链接形式标明文章原始出处 和作者信息. http://www.cnblogs.com/co ...

最新文章

  1. php连接基础方法怎么查询数据库,php基础之连接mysql数据库和查询数据
  2. 百度资源管理平台 站长工具 批量添加主站域名 子站域名 域名主动推送
  3. 一站式解决使用枚举的各种痛点
  4. ADT 压缩包 R23.0.0
  5. [转]static和final的区别
  6. Android学习---解决Android Graphical Layout 界面效果不显示
  7. 赵本山 政治敏锐_每天5分钟保持敏锐的7种方法
  8. canape数据导入matlab,CANape使用介绍
  9. 2022联想创新科技大会--智能为变革赋能
  10. 服务器安装找不到RAID卡驱动,DELL R430安装WINDOWS 2008 R2 RAID卡无驱动之无法识别sas硬盘的解决方法...
  11. PVE7.2 显示CPU温度和频率
  12. 计算机无法打开压缩包,压缩包损坏或压缩格式未知且无法打开
  13. android 短信接口收拦截,闪修侠科普 | 双11垃圾短信没停过,教你一键屏蔽~
  14. ctf-2020-12-07
  15. 仿淘宝的详情页图片切换
  16. Pulsar官方文档翻译-概念和架构-多租户(Multi Tenancy)
  17. mysql 查询dual报错_MYSQL基础02(查询)
  18. uni-app实现仿微信前端(二)
  19. bxl文件转换为AD可以用的原理图和PCB库文件
  20. PS如何快速使用对象选择工具抠图?

热门文章

  1. 日料美食海鲜精品海报PSD分层模板,美味势不可挡
  2. UI设计素材|卡券界面设计
  3. mysql keeplevied_mysql keepalived
  4. ise 时钟约束_ISE时序约束笔记2——Global Timing Constraints
  5. 遍历QListWidget的item
  6. 《深入浅出DPDK》读书笔记(十一):DPDK虚拟化技术篇(I/O虚拟化、CPU虚拟化、内存虚拟化、VT-d、I/O透传)
  7. Docker 教程、架构、Linux下的安装
  8. 华强北耳机芯片检测软件_关于华强北耳机air pods pro
  9. Python库:time库
  10. maven的依赖和聚合