跳转到: 导航, 搜索

I18n/TranslatableStrings

Horizon 可翻译字符串规则

这仍在讨论中。请将此页视为指导而非规则。

评论和建议:https://etherpad.openstack.org/p/i18n_translatable_strings

尽可能使用注释为译者提供帮助

为译者提供关于可翻译字符串的提示。为此,在字符串前一行添加一个以 Translators: 关键字为前缀的注释,例如:

def my_view(request):
    # Translators: This is a famous Quote from Arthur C. Clark
    output = ugettext("Any sufficiently advanced technology is indistinguishable from magic.")

如果希望在 po 文件中看到注释,则需要将注释放置在第一个可翻译字符串之前。

some_message = ugettext(
    # Translators: This line is just before the first string.
    "This is a huge sentence, it is long and needs to be split on several"
    "lines."
)

您并非总是可以使用注释(参见 此 django bug),因此您可能需要使用上下文标记。


对于短字符串,强烈建议使用“译者注释”和“上下文标记”(如下所示)。


使用上下文标记来避免短字符串的歧义

字符串在英文中可能相同,但在其他语言中可能不同。例如,英语没有语法性别,有时单词的名词和动词形式相同。为了能够正确地本地化这些内容,我们可以添加“上下文”(在 gettext 中称为 msgctxt)来区分两个否则相同的字符串)。Django 提供了一个 pgettext() 函数来实现这一点。在英文中,“None”只有一种形式,但在其他语言中,您可能有 2、3、6 等不同的书写“None”的方式,具体取决于它所指的单词/对象

ugettext("None")  # Never do this.
pgettext("Quantity of Images", u"None")  # Do this instead

示例,英文中的同一句话有 2 种含义,需要上下文标记,因为在另一种语言中它们会以不同的方式书写

ugettext(u"Shut Off Instance") # Never do this.
# Do this:
pgettext("Action to perform (the instance is currently running)",
    u"Shut Off Instance")
pgettext("Past action (this is the status of the instance)",
    u"Shut Off Instance")

请记住,您需要思考: “相同的单词(或句子)在其他情况下是否可能意味着其他含义?” 如果答案是“是”,则需要上下文标记。

注意: 由于 pgettext_lazy django bug,该 bug 在 django 1.6 中已修复,但仍然存在于旧版本中(我们支持版本 1.4),因此养成始终使用 Unicode 字符串作为可翻译字符串的良好习惯,如示例所示。

始终尝试引用与代码中其他地方使用的相同上下文字符串(“图像数量”),以便使用相同的翻译。否则,如果上下文消息不同,译者将不得不为相似的上下文多次翻译相同的单词。

上下文标记也可以在 django 模板中使用

{% trans "May" context "month name" %}
{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}

另一个非常常见的需要上下文标记的例子是单词“Free”(免费)

英语:“Free Software” -> 法语:“Logiciel libre”(Free -> Libre)
英语:“Free Beer” -> 法语:“Bière gratuite”(Free -> Gratuite)
英语:“Free Trip” -> 法语:“Voyage gratuit”(Free -> Gratuit)

因此,如果您的代码中某个地方单独写了单词“Free”,则上下文标记是强制性的。如您所见,即使使用上下文标记,单词“Free”也不能用于使用字符串格式化变量的句子中,以指代将要免费的东西,因为翻译永远无法正常工作(“Free”需要适应性别)。这就是为什么大多数时候最好写完整的句子并避免动态组合的原因,因为动态组合可能会导致翻译完全失败。如果您无法避免动态组合,则必须使用字符串格式化变量,如稍后所述。


使用字符串格式化变量,切勿执行字符串连接

译者看不到连接过程,只会看到带有尾随空格的字符串。如果您需要一个可变部分,请始终使用变量进行编码

_("Image details: ") + variable + "." # Never do this.
_("Image details: %(variable)s.") % variable  # Do this instead.
# Or the following if the variable's content needs a translation.
# It Works only if the content of the variable is already present somewhere in the code as a translatable string.
# see ugettext_noop or ugettext_lazy documentation, it is a way to mark strings for translation in variables.
_("Image details: %(variable)s.") % _(variable)

在 django 模板中,当您需要变量时,请使用 blocktrans 代替 trans

{% trans "Image details:  " %}{{variable }}. /* Never do this */
{% blocktrans %}Image details:  {{ variable }}.{% endblocktrans %} /* Do this instead */

请注意,{{ variable }} 变量需要在模板上下文中存在。如果您需要评估模板表达式,例如过滤器或访问对象属性,由于您无法在 {% blocktrans %} 块内执行此操作,因此您需要首先将表达式绑定到局部变量

{% blocktrans with revision.created_date|timesince as timesince %}
{{ revision }} {{ timesince }} ago
{% endblocktrans %}

{% blocktrans with project.name as name %}Delete {{ name }}?{% endblocktrans %}

此外,某些语言需要将变量放在字符串的末尾以外的其他位置,这在使用字符串连接时是不可能的。

示例:"The %(name)s image is too large for this volume." 可以翻译为 "L'image %(name)s est trop volumineuse pour ce volume.",其中变量根据语言发生了变化。如果字符串为“The ” + image_name + “ image is too large”,则变量无法改变位置,并且在许多语言中无法正确翻译。但是,尽可能避免这种情况,因为即使使用字符串格式化变量,动态组合也可能在诸如“Here we serve free %(product_name)s”之类的句子中失败。许多语言确实需要根据“%(product_name)s”的性别来调整“free”。

使用 ungettext 进行复数形式处理

切勿通过确定 1 是单数而 0 或 >1 是复数来自己执行复数形式处理,因为这将在英语以外的所有其他语言中都失败。事实上,0 在英语中是复数形式,而在法语中是单数形式。有些语言有 2、3、4、5 或 6 种复数形式(例如,爱尔兰语 (ga) 有 5 种复数形式,阿拉伯语 (ar) 有 6 种复数形式)。请参阅 "Plural Forms" 和 "Translating plural forms" 以获取更多信息。

当需要复数形式处理时,始终使用:

ungettext(
    'Singular sentence',
    'Plural sentence',
    number
)

不要通过在代码中的其他地方定义 variable_singular_stringvariable_plural_string 来使用 ungettext(variable_singular_string, variable_plural_string, number),因为变量字符串不会出现在 pot 文件中用于复数形式处理,并且每个字符串都会被标记为需要翻译的独立字符串。当这些变量字符串被独立标记为翻译并随后在 ungettext 中使用时,pot 文件中只有 2 种复数形式,许多语言将无法处理正确的复数形式,因为它们需要更多的形式。

注意: 当使用延迟翻译和复数形式时,将键名而不是整数作为数字参数传递仅在 Django 1.6 中有效:https://docs.django.ac.cn/en/1.6/topics/i18n/translation/#lazy-translations-and-plural 由于 Horizon 支持 django 1.4,因此在使用延迟翻译时,您仍然需要使用变量数字参数形式。

尽可能重用现有字符串

不要修改所有现有的可翻译字符串,因为我们将失去译者已经完成的工作。因此,如果您的代码将可翻译字符串移动到其他位置,请尽可能重用它。此页上的规则适用于新字符串或需要新翻译的修改字符串。有一种快速的方法可以检查您的字符串是否已经翻译

选择一个或两个尽可能接近 100% 覆盖的语言(英语除外):https://www.transifex.com/projects/p/horizon/ 语言与英语越不同,越好(即使您不理解它),因为您将在测试中看到翻译是否存在。从您的 horizon 目录

. .venv/bin/activate  # the first call to ./run_tests.sh (when you run unittests) installed this virtual environment.
python manage.py shell
>>> from django.utils.translation import activate, ugettext
>>> activate('zh_CN')
>>> print ugettext("None")
wu
>>> print ugettext("Instance")
云主机
>>> activate('fr')
>>> print ugettext("None")
Aucun
>>> print ugettext("Instance")
Instance
>>>

如果返回的单词不同,则表示存在翻译。如您所见,“Instance”已被翻译,即使它被翻译成法语。您可能没有注意到它已被翻译,因为翻译看起来相同。因此,最好使用 2 种不同的语言进行检查。您还可以使用 pgettext(上下文标记)和 ungettext(复数形式)执行相同的测试。