实战本地化,3/3 – 节点.js 假期季,第 11 部分

这是 Mozilla 身份团队的“节点.JS 假期季系列”的第 11 集,共 12 集。这是关于本地化的最后一部分,希望让你对处理本地化工作感到信心满满!

使用我们的字符串

所以,我们首先将 i18n-abide 模块添加到我们的代码中,然后我们的 本地化 (L10n) 团队进行了一些字符串整理,现在我们有了多个语言环境,包含翻译后的字符串……

让我们准备好这些字符串,用于节点.js 并看看这个小家伙的运行情况!

下一步是,我们需要你的 PO 文件,通常在这样的文件系统中

locale
  en
    LC_MESSAGES
      messages.po
  de
    LC_MESSAGES
      messages.po
  es
    LC_MESSAGES
      messages.po

我们需要一种方法,在运行时将字符串从我们的 PO 文件获取到我们的应用程序中。您可以通过多种方法来实现

  1. 拥有 **服务器端字符串**,并且 i18n-abide 提供的 gettext 函数将发挥其魔力。
  2. 拥有 **客户端字符串**,你将在代码中包含一个 gettext.js 脚本。它与 i18n-abide 一起发布。

**这两种方法都需要** 将字符串以 **JSON** 文件格式存储。
服务器端翻译在应用程序启动时加载它们,而客户端翻译通过 HTTP 加载它们(或者您可以将它们放入构建并缩小的 JavaScript 中)。

由于此系统与 GNU Gettext 兼容,因此服务器端字符串的第三种选择是使用 node-gettext。它在进行服务器端翻译方面非常有效。

在本博文中,我们将使用第一个选项,因为它是最常见的 i18n-abide 使用方法。

compile-json

那么,我们如何将字符串从 PO 文件获取到 JSON 文件中呢?

我们的构建脚本称为 compile-json

假设我们的文件位于项目顶层目录 locale 中,并且我们希望 .json 文件放到 static/i18n 中,我们会这样做

示例

$ mkdir -p static/i18n
$ ./node_modules/.bin/compile-json locale static/i18n

我们获得的文件结构如下

static
  i18n
    en
      messages.json
      messages.js
    de
      messages.json
      messages.js
    es
      messages.json
      messages.js

compile-json 遍历每个 .po 文件并对其调用 po2json.js,生成 .json.js 文件。po2json.js 是 i18n-abide 提供的另一个程序。

如果我们从这些博文中获得迄今为止的西班牙语 messages.po,我们会看到

# Spanish translations for PACKAGE package.
# Copyright (C) 2013 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Austin King <ozten@localhost>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSIONn"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: 2012-06-24 09:50+0200n"
"PO-Revision-Date: 2013-04-24 16:42-0700n"
"Last-Translator: Austin King <ozten@nutria.localdomain>n"
"Language-Team: Spanishn"
"Language: esn"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=UTF-8n"
"Content-Transfer-Encoding: 8bitn"
"Plural-Forms: nplurals=2; plural=(n != 1);n"

#: /home/ozten/abide-demo/views/homepage.ejs:3
msgid "Mozilla Persona"
msgstr "Mozilla Personidada"

它将被转换为

messages": {
      "": {
         "Project-Id-Version": " PACKAGE VERSIONnReport-Msgid-Bugs-To: nPOT-Creation-Date: 2012-06-24 09:50+0200nPO-Revision-Date: 2013-04-24 16:42-0700nLast-Translator: Austin King <ozten@nutria.localdomain>nLanguage-Team: GermannLanguage: denMIME-Version: 1.0nContent-Type: text/plain; charset=UTF-8nContent-Transfer-Encoding: 8bitnPlural-Forms: nplurals=2; plural=(n != 1);n"
      },
      "Mozilla Persona": [
         null,
         "Mozilla Personidada"
      ]
   }
}

因此,我们可以从 Node 代码中的服务器端使用这些 .json 文件,或者通过 AJAX 请求它们,在客户端使用它们。

static 目录公开给网页流量,因此对 /i18n/es/messages.json 的请求将获得西班牙语 JSON 文件。

static 目录是 express 约定,你可以将这些文件存储在你希望的任何位置。你可以通过节点.js 或诸如 nginx 之类的网页服务器来提供静态文件。

注意: 你 **不需要** 将 .PO 文件 **部署到生产环境**,但将其一起发布并没有什么坏处。

配置

i18n-abide 需要一些配置,以确定支持哪些语言以及在哪里找到我们的 JSON 文件。

正如我们在 第一期中看到的那样,以下是我们的应用程序所需的配置

app.use(i18n.abide({
  supported_languages: ['en-US', 'de', 'es', 'zh-TW'],
  default_lang: 'en-US',
  translation_directory: 'static/i18n'
}));
  • supported_languages 告诉应用程序它支持英语、德语、西班牙语、中文(繁体)。
  • translation_directory 配置表示翻译后的 JSON 文件位于 static/i18n 下。
  • 请注意,translation_directory 仅适用于 **服务器端** gettext。

我们在第一篇文章中解释过,i18n-abide 将尽力提供合适的本地化字符串。

它将查看配置中的 supported_languages,以找到最佳语言匹配。

你只应将你准备好的语言环境 JSON 文件的语言添加到 supported_languages 中。

启动引擎

好了,现在配置到位,并且至少有一个语言环境已翻译,让我们启动它!

npm start

在你的网页浏览器中,将你的首选语言更改为你已本地化的语言。

现在加载应用程序的页面。你应该看到它现在已被翻译了。

对于真实世界的示例,以下是 **希腊语** 的 Mozilla Persona。太酷了!

乱码

如果你想 **测试** 你的 L10n 设置,**在你完成真正的翻译** 之前,我们已经构建了一个很棒的测试语言环境。它受 大卫·鲍伊的迷宫 启发。

要使用它,只需将 it-CH 或你目前未使用的其他语言环境添加到你的配置中的 **supported_languages** 和 **debug_lang** 设置下。

示例*部分*配置

app.use(i18n.abide({
  supported_languages: ['en-US', 'de', 'es', 'zh-TW', 'it-CH'],
  debug_lang: 'it-CH',
  ...

现在,如果你将浏览器的首选语言设置为意大利语/瑞士语 (it-CH),i18n-abide 将使用乱码来本地化内容。

这是一种方便的方法,可以确保你的视觉设计和散文适用于希伯来语等双向语言。你的网页设计师可以在你拥有资源创建实际的希伯来语字符串之前,测试他们的 RTL CSS。

深入探讨

我们只是触及了 i18n 和 l10n 的表面。如果你以多种语言环境发布基于节点.js 的服务,你将会遇到许多问题和有趣的细微差别。

以下是一些其他主题的提示。

字符串插值

i18n-abide 提供了一个 format 函数,它可以在客户端或服务器端的 JavaScript 代码中使用。

Format 接收一个格式化字符串,并在运行时将参数替换为实际值。此函数可以在两种参数替换风格中使用。

格式

  • %s – format 被调用时带有一个格式化字符串,然后是一个字符串数组。每个字符串将按顺序被替换。
  • %(named)s – format 被调用时带有一个格式化字符串,然后是一个对象,其中键与命名参数匹配。

你可以使用 format 将字符串中的 HTML 最小化。

考虑以下三个示例

<%= gettext('

Buy Blue Tickets Now!

') %>

<%= format(gettext('Buy Blue Tickets Now!'), ['/buy?prod=blue&tyep=ticket']) %>

<%= format(gettext('Buy Blue Tickets Now!'), {url: '/buy?prod=blue&tyep=ticket'}) %>

在 PO 文件中,它们生成以下字符串

Buy Blue Tickets Now!

" msgid "Buy Blue Tickets Now!" msgid "Buy Blue Tickets Now!"

第一个示例有一个段落标签,它出现在 PO 文件中。太糟糕了。如果你更改了标记……你可能需要在每个语言环境中更新它!

此外,看看那个难看的 URL。

使用 format 的原因

  • 避免让不熟悉 HTML 的本地化人员感到困惑,他们可能会意外地破坏你的代码
  • 避免维护问题

命名参数很不错,因为它们是自文档的。本地化人员知道该变量是一个 URL。字符串插值在软件本地化中很常见。

另一个示例是注入到字符串中的运行时数据。

<%= format(gettext('Welcome back, %(user_name)s'), {user_name: user.name}) %>

避免不灵活的设计

我们需要尽早戴上 L10n 帽子。从我们审核网站的初始图形设计时就开始。

  • 避免将文本放入图像中。使用 CSS 将单词作为纯文本定位在图像上。

  • 确保 CSS 是防弹的。德语中的一个英语单词可能会大很多倍,并破坏一个
    计划不周的设计。

  • 试试这个书签:Fauxgermanhausen das Pagen!

基于数据库的网站已经教会我们以系统的方式思考设计,但设计师可能不习惯允许可变长度的标签或按钮。

字符串冻结

还记得我们为本地化人员准备翻译文件而进行的构建步骤吗?在本博文中,我们了解了 po2json.js,用于在我们的应用程序中使用这些字符串…… 好吧,这意味着我们需要协调我们的软件发布与 L10n 社区。

连续部署在 L10n 中还没有解决。要么你必须在部署之前等待 100%
的字符串翻译完成,要么你必须接受在某些语言环境中使用部分翻译的应用程序。

L10n 团队可能需要 1、2 甚至 3 周时间来本地化你的应用程序,具体取决于字符串的数量。安排在 QA 周期内进行。

提供一个实时预览网站,以便本地化人员可以检查他们的工作。

总结

在这三篇博文中,我们了解了如何使用 i18n-abide 开发本地化应用程序,如何将 L10n 阶段添加到我们的发布构建中,最后是如何测试我们的工作。

本地化你的网站或应用程序将使你的网站对全球更多受众更有价值。

节点.js 黑客们,去让你们的服务为全世界所用!

系列中的前几篇文章

这是 关于节点.js 的共 12 篇帖子的系列的第十一部分。前面的文章是

关于 Austin King

西雅图非教条主义艺术家/程序员类型人类。应用程序工程团队的无赖网页开发者。拼写检查是针对一周的。

更多 Austin King 的文章……

关于 Robert Nyman [编辑名誉退休]

Mozilla Hacks 的技术布道者和编辑。他做演讲和写博客,主题是 HTML5、JavaScript 和开放网络。Robert 是 HTML5 和开放网络的坚定支持者,从 1999 年起就在瑞典和纽约市从事网络前端开发。他还定期在 http://robertnyman.com 上写博客,喜欢旅行和结识新朋友。

更多由 Robert Nyman [名誉编辑] 撰写的文章…


5 条评论

  1. Álvaro G. Vicario

    书签触发了这个

    错误:SyntaxError: 缺少 ) 作为参数列表的结束符
    athResult=document.evaluate(‘.//text()[normalize-space(.)!=”]’,document.body,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYP

    2013 年 4 月 30 日 下午 12:02

  2. Austin King

    抱歉,Alvaro,这是原始书签
    https://gist.github.com/ozten/5492240

    我会尝试修复这篇文章。

    2013 年 4 月 30 日 下午 2:55

  3. Edgar Orozco

    Austin,感谢这篇文章。我有一个 express 3、ejs、passport 应用程序,我想为 passport 中的 auth 本地化字符串,这些字符串超出了请求或模板的范围。如何在这种情况中实现本地化?

    我尝试使用带有变量的 gettext,但遇到了这个错误
    >> 9|
    无法读取未定义的属性“length”
    在 Object.gt [as gettext] (/home/workspace/sf2/example/node_modules/i18n-abide/lib/i18n.js:143:70)

    2013 年 5 月 11 日 上午 1:26

  4. Austin King

    Edgar - 我很想更好地了解你的用例。此外,这听起来像是 i18n-abide 中的错误。你能在 https://github.com/mozilla/i18n-abide/issues/new 中提供有关如何重现此问题的详细信息吗?

    如果你的代码是公开的,请在错误中提供指向它的指针。

    感谢您帮助 i18n-abide。

    2013 年 5 月 11 日 上午 7:19

    1. Edgar Orozco

      Austin,我已经填写了问题
      https://github.com/mozilla/i18n-abide/issues/32

      谢谢!

      2013 年 5 月 13 日 上午 11:12

本文的评论已关闭。