【问题标题】:Best way to design for localization of strings设计字符串本地化的最佳方法
【发布时间】:2010-09-16 03:56:32
【问题描述】:

这是一个笼统的问题,欢迎发表意见。我一直在尝试想出一种好方法来设计用于 Windows MFC 应用程序和相关实用程序的字符串资源本地化。我的愿望清单是:

  • 必须在代码中保留字符串文字(而不是用宏 #define 资源 ID 替换),以便消息仍然可以内联阅读
  • 必须允许本地化字符串资源 (duh)
  • 不得施加额外的运行时环境限制(例如:对 .NET 的依赖等)
  • 应尽量减少对现有代码的干扰(修改越少越好)
  • 应该是可调试的
  • 应生成可用常用工具编辑的资源文件(即:常用格式)
  • 不应使用复制/粘贴注释块来保留代码中的文字字符串或任何其他可能导致去同步的内容
  • 如果允许静态(编译时)检查每个“注释”字符串是否在资源文件中会很好
  • 如果允许跨语言资源字符串池(适用于各种语言的组件,例如:本机 C++ 和 .NET)会很好

除了静态检查之外,我有一种方法可以在某种程度上满足我的所有愿望,但我必须开发一些自定义代码来实现它(并且它有局限性)。我想知道是否有人以特别好的方式解决了这个问题。

编辑: 我目前的解决方案是这样的:

ShowMessage( RESTRING( _T("Some string") ) );
ShowMessage( RESTRING( _T("Some string with variable %1"), sNonTranslatedStringVariable ) );

然后,我有一个自定义实用程序来解析“RESTRING”块中的字符串并将它们放入一个 .resx 文件中以进行本地化,并使用一个单独的 C# COM 对象从本地化资源文件中加载它们并返回。如果 C# 对象不可用(或无法加载),我会回退到代码中的字符串。该宏扩展为一个模板类,该类调用 COM 对象并进行格式化等。

无论如何,我认为添加我现在的参考资料会很有用。

【问题讨论】:

    标签: c++ windows mfc localization


    【解决方案1】:

    我们使用英文字符串作为ID。

    如果从国际资源对象(从安装的 I18N dll 加载)查找失败,那么我们默认使用 ID 字符串。

    代码如下:

    doAction(I18N.get("Press OK to continue"));
    

    作为构建过程的一部分,我们有一个 perl 脚本来解析所有源代码中的字符串常量。它构建应用程序中所有字符串的临时文件,然后将它们与每个本地的资源字符串进行比较,以查看它们是否存在。任何缺失的字符串都会向相应的翻译团队发送电子邮件。

    我们可以为每个本地设置多个 dll。 dll 的名称基于 RFC 3066
    语言[_territory][.codeset][@modifier]

    我们尝试从机器中提取语言环境,并在加载 I18N dll 时尽可能具体,但如果不存在更具体的版本,则回退到不太具体的本地变体。

    例子:

    在英国:如果本地是 en_GB.UTF-8
    (我在特定的 Windows 意义上松散地使用术语 dll)。

    首先查找 I18N.en_GB.UTF-8 dll。如果此 dll 不存在,则回退到 I18N.en_GB。如果此 dll 不存在则回退到 I18N.en 如果此 dll 不存在则回退到 I18N.default

    此规则的唯一例外是: 简体中文 (zh_CN),其中备用是美国英语 (en_US)。如果机器不支持简体中文,则不太可能支持全中文。

    【讨论】:

    • 听起来与我正在做的类似,但是如何提取所有字符串来创建资源 dll?还是手工完成?
    • 好的,所以您正在做的事情与我正在做的事情非常相似(除了我选择了 resx 并且您使用了本机资源库)。很酷的交易,让我对我正在采取的方法感觉好多了。我会为更多的 cmets 保留它,但这似乎是一个好方法。
    • GNU gettext 已经在做这一切,所以只需重新发明(劣质版本的)轮子。
    • 我们发现英语字符串有时不够具体,无法匹配所有其他语言中的适当单个字符串。当英文字符串应该是 2 个或更多不同的字符串时,您将如何处理,具体取决于它所使用的应用程序部分?
    • @Greg Domjan:英文版有超过 2 个字符串。 “按下按钮 V1”和“按下按钮 V2”。在英语资源文件中,两者都映射到字符串“Press Button”,而在其他语言中,它们映射到特定的变体。然后你需要添加代码来决定使用哪个字符串。
    【解决方案2】:

    简单的方法是在代码中只使用字符串 ID - 不使用文字字符串。 然后,您可以为每种语言生成不同版本的 .rc 文件,并创建仅资源 DLL 或仅创建不同的语言版本。

    有一些共享软件工具可以帮助本地化 rc 文件,这些工具可以处理针对较长单词的语言调整对话框元素的大小并警告缺少翻译。

    一个更复杂的问题是词序,如果你在 printf 中有多个数字,对于不同语言的语法,它们的顺序必须不同。 codeproject 上有一些扩展的 printf 类,可让您指定诸如 printf("word %1s and %2s",var1,var2) 之类的内容,以便您可以在必要时切换 %1s 和 %2s。

    【讨论】:

    • 我在一个项目中采用了这个解决方案。但是,如果我添加一个新的字符串,那么就会生成一个新的定义,并且需要重新编译整个项目,这是一个很大的时间损失
    【解决方案3】:

    我不太了解这通常在 Windows 上是如何完成的,但在 Apple 的 Cocoa framework 中处理本地化字符串的方式非常有效。它们有一个非常基本的文本格式文件,您可以将其发送给翻译人员,以及一些用于从文件中检索值的预处理器宏。

    在您的代码中,您会看到以您的母语显示的字符串,而不是不透明的 ID。

    【讨论】:

    • 这基本上是我正在寻找的东西,但它适用于普通 C++(没有框架)。
    • 是的,我想它可能就像您正在寻找的那样。不幸的是,我不知道这种设计的任何纯 C++ 版本,但(在我看来)编写一个似乎并不难。关键的简化是有一个“genstrings”工具来扫描源代码并创建文件。
    【解决方案4】:

    既然是公开征求意见,我就是这样做的。

    我的本​​地化文本文件是一个简单的制表符分隔的文本文件,可以在 Excel 中加载和编辑。 第一列是定义,右边的每一列是后续语言,例如:

    ID              ENGLISH      FRENCH    GERMAN
    STRING_YES      YES          OUI       YA
    STRING_NO       NO           NON       NEIN
    

    然后在我的 makefile 中是一个自定义构建步骤,它生成一个 strings.h 文件和一个 strings.dat。在我的例子中,它为字符串 ids 构建一个枚举列表,然后为文本构建一个带有偏移量的二进制文件。因为在我的应用程序中,用户可以随时更改语言,我将它们全部保存在内存中,但如果需要,您可以轻松地让预处理器为每种语言生成不同的输出文件。

    我喜欢这个设计的一点是,如果缺少任何字符串,那么我会得到一个编译错误,而如果在运行时查找字符串,那么您可能不知道代码中很少使用的部分缺少字符串直到以后。

    【讨论】:

      【解决方案5】:

      您的解决方案与 Unix/Linux "gettext" 解决方案非常相似。事实上,您不需要编写提取例程。

      我不确定您为什么希望 _RESTRING 宏处理多个参数。我的代码(使用 wxWidgets 对 gettext 的支持)如下所示:MyString.Format(_("Some string with variable %ls"), _("variable"));。也就是说,String::Format(...) 得到两个单独翻译的参数。事后看来,Boost::Format 会更好,但它也允许boost::format(_("Some string with variable %1")) % _("variable");

      (为简洁起见,我们使用_() 宏)

      【讨论】:

      • 我希望宏能够处理内联可变参数格式,主要是为了方便。否则我必须在它周围加上其他东西,可能还有另一个字符串变量声明,这是浪费的代码。
      • 我们在多字节项目中使用gettext和多语言资源。问题是我们不能使用 gettext 的 GPL 许可证,我们必须自己编写。另一个问题是关于格式化参数的字符串同步: printf(_("I want this: %.20s and this: %f%.3s"));外部工具应该进行同步。
      【解决方案6】:

      在我已本地化为 10 多种语言的一个项目中,我将要本地化的所有内容都放入了一个纯资源 dll。在安装时,用户选择了与他们的应用程序一起安装的 dll。

      我只需要将英文 dll 交付给本地化团队。他们为我在构建中包含的每种语言返回了一个本地化的 dll。

      我知道这并不完美,但它确实有效。

      【讨论】:

      • 您是否找到了一种无需将代码中的字符串替换为资源 ID 的方法?你所描述的听起来像口语化的方法,当然可以,但不是我真正想要的。
      • 不,我们不会将字符串硬编码到我们的应用程序中。我们看到的是一个与资源 ID 相关的描述性常量。
      【解决方案7】:

      您想要一个我一直想编写但一直没有时间编写的高级实用程序。 如果您没有找到这样的工具,您可能想要回退到我的 CMsg() 和 CFMsg() 包装类,它们允许非常轻松地从资源表中提取字符串。 (CFMsg 甚至提供了一个 FormatMessage 单行包装器。 是的,在没有您正在寻找的工具的情况下,将字符串的副本保留在注释中是一个很好的解决方案。关于评论的去同步,请记住字符串文字很少更改。

      http://www.codeproject.com/KB/string/stringtable.aspx

      顺便说一句,本机 Win32 程序和 .NET 程序具有完全不同的资源存储管理。您将很难为两者找到共同的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-09
        • 1970-01-01
        • 1970-01-01
        • 2019-09-29
        • 2016-12-11
        相关资源
        最近更新 更多