windows 设计字符串本地化的最佳方式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/185291/
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 design for localization of strings
提问by Nick
This is kinda a general question, open for opinions. I've been trying to come up with a good way to design for localization of string resources for a Windows MFC application and related utilities. My wishlist is:
这是一个普遍的问题,欢迎发表意见。我一直在努力想出一种很好的方法来设计 Windows MFC 应用程序和相关实用程序的字符串资源的本地化。我的愿望清单是:
- Must preserve string literals in code (as opposed to replacing with macro #define resource ID's), so that the messages are still readable inline
- Must allow localized string resources (duh)
- Must not impose additional run-time environment restrictions (eg: dependency on .NET, etc.)
- Should have minimal obtrusion into existing code (the less modification the better)
- Should be debuggable
- Should generate resource files which are editable by common tools (ie: common format)
- Should not use copy/paste comment blocks to preserve literal strings in code, or anything else which creates the potential for de-synchronization
- Would be nice to allow static (compile-time) checking that every "notated" string is in the resource file(s)
- Would be nice to allow cross-language resource string pooling (for components in various languages, eg: native C++ and .NET)
- 必须在代码中保留字符串文字(而不是用宏 #define 资源 ID 替换),以便消息仍然可读内联
- 必须允许本地化的字符串资源(废话)
- 不得强加额外的运行时环境限制(例如:对 .NET 的依赖等)
- 对现有代码的干扰最小(修改越少越好)
- 应该是可调试的
- 应生成可由常用工具编辑的资源文件(即:常用格式)
- 不应使用复制/粘贴注释块来保留代码中的文字字符串,或任何其他可能导致不同步的内容
- 允许静态(编译时)检查每个“带符号的”字符串是否在资源文件中会很好
- 允许跨语言资源字符串池会很好(对于各种语言的组件,例如:本机 C++ 和 .NET)
I have a way which fulfills all my wishlist to some extent except for static checking, but I have had to develop a bit of custom code to achieve it (and it has limitations). I'm wondering if anyone has solved this problem in a particularly good way.
除了静态检查之外,我有一种方法可以在某种程度上满足我的所有愿望,但是我不得不开发一些自定义代码来实现它(并且它有局限性)。我想知道是否有人以特别好的方式解决了这个问题。
Edit: The solution I currently have looks like this:
编辑:我目前拥有的解决方案如下所示:
ShowMessage( RESTRING( _T("Some string") ) );
ShowMessage( RESTRING( _T("Some string with variable %1"), sNonTranslatedStringVariable ) );
I then have a custom utility to parse out the strings from within the 'RESTRING' blocks and put them into a .resx file for localization, and a separate C# COM object to load them from localized resource files with fallback. If the C# object is not available (or cannot load), I fallback to the string in the code. The macro expands to a template class which calls the COM object and does the formatting, etc.
然后,我有一个自定义实用程序来解析“RESTRING”块中的字符串,并将它们放入一个 .resx 文件中以进行本地化,以及一个单独的 C# COM 对象,以使用回退从本地化资源文件中加载它们。如果 C# 对象不可用(或无法加载),我会回退到代码中的字符串。宏扩展为模板类,该类调用 COM 对象并进行格式设置等。
Anyway, I thought it would be useful to add what I have now for reference.
无论如何,我认为添加我现在拥有的内容以供参考会很有用。
回答by Martin York
We use the English string as the ID.
我们使用英文字符串作为 ID。
If it fails the look up from the international resource object (loaded from the I18N dll installed) then we default to the ID string.
如果从国际资源对象(从安装的 I18N dll 加载)查找失败,那么我们默认为 ID 字符串。
Code looks like:
代码如下:
doAction(I18N.get("Press OK to continue"));
As part of the build processes we have a perl script that parses all source for string constants. It builds a temp file of all strings in the application and then compares these against the resource strings in each local to see if they exists. Any missing strings generates an e-mail to the appropriate translation team.
作为构建过程的一部分,我们有一个 perl 脚本来解析字符串常量的所有源。它为应用程序中的所有字符串构建一个临时文件,然后将这些字符串与每个本地中的资源字符串进行比较,以查看它们是否存在。任何丢失的字符串都会生成一封电子邮件给相应的翻译团队。
We can have multiple dll for each local. The name of the dll is based on RFC 3066
language[_territory][.codeset][@modifier]
我们可以为每个本地拥有多个 dll。dll 的名称基于 RFC 3066
语言[_territory][.codeset][@modifier]
We try and extract the locale from the machine and be as specific as possible when loading the I18N dll but fallback to less specific local variations if the more specific version is not present.
我们尝试从机器中提取语言环境,并在加载 I18N dll 时尽可能具体,但如果更具体的版本不存在,则回退到不太具体的本地变化。
Example:
例子:
In the UK: If the local was en_GB.UTF-8
(I use the term dll loosely not in the specific windows sense).
在英国:如果本地是en_GB.UTF-8
(我松散地使用术语 dll,而不是在特定的 Windows 意义上)。
First look for the I18N.en_GB.UTF-8dll. If this dll does not exist fall back to I18N.en_GB. If this dll does not exist fall back to I18N.enIf this dll does not exist fall beck to I18N.default
首先查找I18N.en_GB.UTF-8dll。如果此 dll 不存在,则回退到 I18N.en_GB。如果这个 dll 不存在回退到 I18N.en如果这个 dll 不存在回退到 I18N.default
The only exception to this rule is: Simplified Chinese (zh_CN) where the fallback is US English (en_US). If the machine does not support simplified Chinese then it is unlikely to support full Chinese.
此规则的唯一例外是:简体中文 (zh_CN),其中后备为美国英语 (en_US)。如果机器不支持简体中文,则不太可能支持全中文。
回答by MSalters
Your solution is quite similar to the Unix/Linux "gettext
" solution. In fact, you would not need to write the extraction routines.
您的解决方案与 Unix/Linux " gettext
" 解决方案非常相似。事实上,您不需要编写提取例程。
I'm not sure why you want the _RESTRING macro to handle multiple arguments. My code (using wxWidgets' support for gettext) looks like this: MyString.Format(_("Some string with variable %ls"), _("variable"));
. That is to say, String::Format(...) gets two individually translated arguments. In hindsight, Boost::Format would have been better, but it too would allow boost::format(_("Some string with variable %1")) % _("variable");
我不确定为什么要 _RESTRING 宏来处理多个参数。我的代码(使用wxWidgets'的gettext的支持)看起来是这样的:MyString.Format(_("Some string with variable %ls"), _("variable"));
。也就是说, String::Format(...) 获得两个单独翻译的参数。事后看来,Boost::Format 会更好,但它也允许boost::format(_("Some string with variable %1")) % _("variable");
(We use the _()
macro for brevity)
(_()
为了简洁起见,我们使用宏)
回答by Martin Beckett
The simple way is to only use string IDs in your code - no literal strings. You can then produce different versions of the.rc file for each language and either create resource only DLLs or simply different language builds.
简单的方法是在您的代码中只使用字符串 ID - 没有文字字符串。然后,您可以为每种语言生成不同版本的 .rc 文件,并创建仅资源 DLL 或不同的语言版本。
There are a couple of shareware utilstohelp localising the rc file which handle resizing dialog elements for languages with longer words and warnign about missing translations.
有几个共享软件 utils 可以帮助本地化 rc 文件,这些软件可以处理具有较长单词的语言的对话框元素大小调整,并警告缺少翻译。
A more complicated problem is word order, if you have several numbers in a printf which must be in a different order for different language's grammar. There are some extended printf classes on codeproject that let you specify things like printf("word %1s and %2s",var1,var2) so you can switch %1s and %2s if necessary.
一个更复杂的问题是词序,如果 printf 中有多个数字,对于不同语言的语法,它们的顺序必须不同。codeproject 上有一些扩展的 printf 类,可让您指定 printf("word %1s and %2s",var1,var2) 之类的内容,以便您可以在必要时切换 %1s 和 %2s。
回答by Mark Bessey
I don't know much about how this is normally done on Windows, but the way localized strings are handled in Apple's Cocoa frameworkworks pretty well. They have a very basic text-format file that you can send to a translator, and some preprocessor macros to retrieve the values from the files.
我不太了解这在 Windows 上通常是如何完成的,但是在 Apple 的Cocoa 框架中处理本地化字符串的方式非常有效。它们有一个非常基本的文本格式文件,您可以将其发送给翻译器,还有一些预处理器宏可以从文件中检索值。
In your code, you'll see the strings in your native language, rather than as opaque IDs.
在您的代码中,您将看到以您的母语表示的字符串,而不是不透明的 ID。
回答by KPexEA
Since it is open for opinions, here is how I do it.
由于它对意见开放,这就是我的做法。
My localized text file is a simple tab delimited text file that can be loaded in Excel and edited. The first column is for the define and each column to the right is a subsequent language, for example:
我的本地化文本文件是一个简单的制表符分隔文本文件,可以在 Excel 中加载和编辑。第一列用于定义,右侧的每一列是后续语言,例如:
ID ENGLISH FRENCH GERMAN
STRING_YES YES OUI YA
STRING_NO NO NON NEIN
Then in my makefile is a cusom build step that generates a strings.h file and a strings.dat. In my case it builds an enum list for the string ids and then a binary file with offsets for the text. Since in my app the user can change the language at any time i have them all in memory but you could easily have your pre-processer generate a different output file for each language if necessary.
然后在我的 makefile 中是一个自定义构建步骤,它生成一个 strings.h 文件和一个 strings.dat。在我的例子中,它为字符串 id 构建了一个枚举列表,然后是一个带有文本偏移量的二进制文件。由于在我的应用程序中,用户可以随时更改语言,我将它们全部保存在内存中,但如果需要,您可以轻松地让预处理器为每种语言生成不同的输出文件。
The thing that I like about this design is that if any strings are missing then I would get a compile error whereas if strings were looked up at runtime then you might not know about a missing string in a seldom used part of the code until later.
我喜欢这个设计的一点是,如果缺少任何字符串,那么我会得到一个编译错误,而如果在运行时查找字符串,那么您可能直到稍后才知道代码中很少使用的部分中缺少字符串。
回答by Serge Wautier
You want an advanced utility that I've always wanted to write but never had the time to. If you don't find such a tool, you may want to fallback on my CMsg() and CFMsg() wrapper classes that allow to very easily pull strings from the resource table. (CFMsg even provide a FormatMessage one-liner wrapper. And yes, in the absence of that tool you're looking for, keeping a copy of the string in comment is a good solution. Regarding desynchronisation of the comment, remember that string literals are very rarely changed.
您想要一个我一直想编写但一直没有时间编写的高级实用程序。如果您没有找到这样的工具,您可能希望使用我的 CMsg() 和 CFMsg() 包装类,它们允许非常轻松地从资源表中提取字符串。(CFMsg 甚至提供了一个 FormatMessage 单行包装器。是的,如果没有您正在寻找的工具,在注释中保留字符串的副本是一个很好的解决方案。关于注释的去同步,请记住字符串文字是很少改变。
http://www.codeproject.com/KB/string/stringtable.aspx
http://www.codeproject.com/KB/string/stringtable.aspx
BTW, native Win32 programs and .NET programs have a totally different resource storage management. You'll have a hard time finding a common solution for both.
顺便说一句,原生的 Win32 程序和 .NET 程序具有完全不同的资源存储管理。您将很难为两者找到共同的解决方案。
回答by BoltBait
On one project I had localized into 10+ languages, I put everything that was to be localized into a single resource-only dll. At install time, the user selected which dll got installed with their application.
在我已本地化为 10 多种语言的一个项目中,我将所有要本地化的内容都放入了一个仅资源的 dll 中。在安装时,用户选择哪个 dll 与他们的应用程序一起安装。
I only had to deliver the English dll to the localization team. They returned a localized dll to me for each language which I included in the build.
我只需要将英文 dll 交付给本地化团队。他们为我包含在构建中的每种语言返回了一个本地化的 dll。
I know it's not perfect, but it worked.
我知道这并不完美,但它奏效了。