国际化

这篇翻译不完整。请帮忙从英语翻译这篇文章

WebExtensions API有一个相当方便的模块可用于附加组件的国际化 — i18n。在本文中,我们将探讨其功能,并提供一个它如何运作的实例。WebExtensions i18n 系统类似常见的 i18n 用的 JavaScript 库,例如 i18n.js

本文中的 WebExtension 实例 — notify-link-clicks-i18n — 在 GitHub 上可用。在您阅读下列章节时,请参照它的代码。

国际化 WebExtension 的剖析

一个国际化的 WebExtension 与其他 WebExtension 相关,可以包含各类功能,如后台脚本内容脚本等 — 但它也有些额外的部分,从而允许它根据不同的语言区域而切换。目录树的剖析如下:

  • webextension-root-directory/
    • _locales
      • en
        • messages.json
          • 英语消息(字符串)
      • de
        • messages.json
          • 德语消息(字符串)
      • 等等语言。
    • manifest.json
      • 依赖语言区域的元数据
    • myJavascript.js
      • JavaScript 检索浏览器语言区域、特定语言环境的消息等。
    • myStyles.css
      • 依赖语言区域的 CSS

让我们逐步探索各个新特性 — 下列每个章节都是您在国际化 WebExtension 时要考虑遵循的步骤。

在 _locales 中提供本地化字符串

您可以使用Language subtag lookup page上的查找工具查询语言子标签。请注意,您需要搜索语言的英语名称。

Every i18n system requires the provision of strings translated into all the different locales you want to support. In WebExtensions, these are contained within a directory called _locales, placed inside the extension root. Each individual locale has its strings (called messages in WebExtensions) contained within a file called messages.json, which is placed inside a subdirectory of _locales, named using the language subtag for that locale's language.

Note that if the subtag includes a basic language plus a regional variant, then the language and variant are conventionally separated using a hyphen: for example, "en-US". However, in the directories under _locales, the separator must be an underscore: "en_US".

So for example, in our sample app we have directories for "en" (English), "de" (German), "nl" (Dutch), and "ja" (Japanese). Each one of these has a messages.json file inside it.

现在我们来看一下这些文件(_locales/en/messages.json)的结构:

{
  "extensionName": {
    "message": "Notify link clicks i18n",
    "description": "Name of the extension."
  },

  "extensionDescription": {
    "message": "Shows a notification when the user clicks on links.",
    "description": "Description of the extension."
  },

  "notificationTitle": {
    "message": "Click notification",
    "description": "Title of the click notification."
  },

  "notificationContent": {
    "message": "You clicked $URL$.",
    "description": "Tells the user which link they clicked.",
    "placeholders": {
      "url" : {
        "content" : "$1",
        "example" : "https://developer.mozilla.org"
      }
    }
  }
}

文件是标准的 JSON — 其中每个成员都是一个带有名称的对象,其中包含一个 message(消息)和一个 description(描述)。这些项目都是字符串。$URL$ 是一个占位符,在 WebExtension 调用 notificationContent 成员时将被一个子字符串替换。你将在接下来的 Retrieving message strings from JavaScript 章节中了解如何操作。

Note: You can find much more information about the contents of messages.json files in our Locale-Specific Message reference.

国际化 manifest.json

要国际化你的 manifest.json 有几项任务要完成。

在 manifests 中检索本地化的字符串

你的 manifest.json 包含显示给用户的字符串,例如附加组件的名称和描述。如果您将这些字符串国际化,并将它们的适当翻译放在 messages.json 中,那么则将根据当前语言环境向用户显示正确的字符串翻译。

要使字符串国际化,请按如下格式指定:

"name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__",

在这里,我们检索的消息字符串依赖于浏览器的语言环境,而不仅仅是包含静态字符串。

要调用这样的消息字符串,您需要这样指定:

  1. 两个下划线,接着
  2. 字符串 "MSG",接着
  3. 一个下划线,接着
  4. 您想调用的在 messages.json 中定义的消息名称,接着
  5. 两个下划线
__MSG_ + messageName + __

指定默认语言区域

你应该在你的 manifest.json 中指定的另一个字段是 default_locale

"default_locale": "en"

如果扩展不包含浏览器当前语言环境的本地化字符串,则指定的默认语言环境被使用。浏览器语言环境中不可用的任何消息字符串都将用默认语言环境替换。有关浏览器如何选择字符串,还有一些细节需要注意 — 详见 Localized string selection

依赖区域的 CSS

在此之外,您还可以在扩展中的 CSS 文件检索本地化的字符串。例如,您可能想构建一个依赖于语言区域的 CSS 规则,如下所示:

header {
  background-image: url(../images/__MSG_extensionName__/header.png);
}

This is useful, although you might be better off handling such a situation using Predefined messages.

从 JavaScript 检索消息字符串

So, you've got your message strings set up, and your manifest. Now you just need to start calling your message strings from JavaScript so your extension can talk the right language as much as possible. The actual i18n API is pretty simple, containing just four main methods:

  • You'll probably use i18n.getMessage() most often — this is the method you use to retrieve a specific language string, as mentioned above. We'll see specific usage examples of this below.
  • The i18n.getAcceptLanguages() and i18n.getUILanguage() methods could be used if you needed to customize the UI depending on the locale — perhaps you might want to show preferences specific to the users' preferred languages higher up in a prefs list, or display cultural information relevant only to a certain language, or format displayed dates appropriately according to the browser locale.
  • The i18n.detectLanguage() method could be used to detect the language of user-submitted content, and format it appropriately.

在我们的 notify-link-clicks-i18n 示例中,后台脚本包含下列代码:

var title = browser.i18n.getMessage("notificationTitle");
var content = browser.i18n.getMessage("notificationContent", message.url);

第一行是从可用的检索 notificationTitle 消息 field from the available messages.json file most appropriate for the browser's current locale. The second one is similar, but it is being passed a URL as a second parameter. What gives? This is how you specify the content to replace the $URL$ placeholder we see in the notificationContent message field:

"notificationContent": {
  "message": "您点击了 $URL$。",
  "description": "告诉用户点击了哪个链接。",
  "placeholders": {
    "url" : {
      "content" : "$1",
      "example" : "https://developer.mozilla.org"
    }
  }
}

The "placeholders" member defines all the placeholders, and where they are retrieved from. The "url" placeholder specifies that its content is taken from $1, which is the first value given inside the second parameter of getMessage(). Since the placeholder is called "url", we use $URL$ to call it inside the actual message string (so for "name" you'd use $NAME$, etc.) If you have multiple placeholders, you can provide them inside an array that is given to i18n.getMessage() as the second parameter — [a, b, c] will be available as $1, $2, and $3, and so on, inside messages.json.

Let's run through an example: the original notificationContent message string in the en/messages.json file is

您点击了 $URL$。

Let's say the link clicked on points to https://developer.mozilla.org. After the i18n.getMessage() call, the contents of the second parameter are made available in messages.json as $1, which replaces the $URL$ placeholder as defined in the "url" placeholder. So the final message string is

您点击了 https://developer.mozilla.org。

直接占位符的使用

It is possible to insert your variables ($1, $2, $3, etc.) directly into the message strings, for example we could rewrite the above "notificationContent" member like this:

"notificationContent": {
  "message": "您点击了 $1。",
  "description": "告诉用户点击了哪个链接。"
}

This may seem quicker and less complex, but the other way (using "placeholders") is seen as best practice. This is because having the placeholder name (e.g. "url") and example helps you to remember what the placeholder is for — a week after you write your code, you'll probably forget what $1$8 refer to, but you'll be more likely to know what your placeholder names refer to.

替换硬编码

也可以在占位符中包含硬编码的字符串,从而每次都使用相同的值,而不是从代码中的变量获取值。例如:

"mdn_banner": {
  "message": "For more information on web technologies, go to $MDN$.",
  "description": "Tell the user about MDN",
  "placeholders": {
    "mdn": {
      "content": "https://developer.mozilla.org/"
    }
  }
}

In this case we are just hardcoding the placeholder content, rather than getting it from a variable value like $1. This can sometimes be useful when your message file is very complex, and you want to split up different values to make the strings more readable in the file, plus then these values could be accessed programmatically.

In addition, you can use such substitutions to specify parts of the string that you don't want to be translated, such as person or business names.

本地化字符串的选择

语言环境可以仅使用语言代码指定,例如 fren,也可以进一步限定区域代码,例如 en_USen_GB,其描述了使用相同基础语言的区域变体。当您向 i18n 系统询问一个字符串时,它将使用以下算法选择一个字符串:

  1. 如果有精确匹配当前语言环境的 messages.json 文件,并且它包含该字符串,则返回它。
  2. 否则,如果当前语言环境有合格区域(例如 en_US)并且有一个无区域限定的 messages.json 文件(例如 en)且包含该字符串,则返回它。
  3. 否则,if there is a messages.json file for the default_locale defined in the manifest.json, and it contains the string, return it.
  4. 否则,返回一个空字符串。

参见下列示例:

  • webextension-root-directory/
    • _locales
      • en_GB
        • messages.json
          • { "colorLocalised": { "message": "colour", "description": "Color." }, ... }
        en
        • messages.json
          • { "colorLocalised": { "message": "color", "description": "Color." }, ... }
      • fr
        • messages.json
          • { "colorLocalised": { "message": "couleur", "description": "Color." }, ...}

假设 default_locale 是设为 fr,而浏览器的语言环境为 en_GB

  • 如果该附加组件调用s getMessage("colorLocalised"),它将返回 "colour"。
  • 如果 "colorLocalised" 没有在 en_GB 中提供,那么 getMessage("colorLocalised") 将返回 "color" 而不是 "couleur".

预定义消息

i18n 模块为我们提供了一些预定义的消息,我们可以用之前在 Calling message strings from manifests and extension CSS 中看到的相同的方式调用。例如:

__MSG_extensionName__

预定义的消息使用完全相同的语法,例如在消息名称之前使用 @@

__MSG_@@ui_locale__

下表显示了各个可用的预定义消息。

Message name Description
@@extension_id The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even unlocalized extensions can use this message.
Note: You can't use this message in a manifest file.
@@ui_locale The current locale; you might use this string to construct locale-specific URLs.
@@bidi_dir The text direction for the current locale, either "ltr" for left-to-right languages such as English or "rtl" for right-to-left languages such as Arabic.
@@bidi_reversed_dir If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".
@@bidi_start_edge If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".
@@bidi_end_edge If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".

Going back to our earlier example, it would make more sense to write it like this:

header {
  background-image: url(../images/__MSG_@@ui_locale__/header.png);
}

Now we can just store our local specific images in directories that match the different locales we are supporting — en, de, etc. — which makes a lot more sense.

Let's look at an example of using @@bidi_* messages in a CSS file:

body {
  direction: __MSG_@@bidi_dir__;
}
      
div#header {
  margin-bottom: 1.05em;
  overflow: hidden;
  padding-bottom: 1.5em;
  padding-__MSG_@@bidi_start_edge__: 0;
  padding-__MSG_@@bidi_end_edge__: 1.5em;
  position: relative;
}

对于从左到右语言(例如英语、中文),该 CSS 声明调用在上面预定义的消息,最终转换为下列代码:

direction: ltr;
padding-left: 0;
padding-right: 1.5em;

而对于从右到左语言(如阿拉伯语),则将得到:

direction: rtl;
padding-right: 0;
padding-left: 1.5em;

测试你的 WebExtension

从 Firefox 45 开始,你可以临时安装磁盘上的 WebExtensions  — 另见从磁盘加载。按上述步骤操作,然后尝试我们的 notify-link-clicks-i18n WebExtension。访问你喜欢的任何网站,然后点一下链接,查看是否有通知出现来显示所点击的链接网址。

接下来,将 Firefox 的语言区域更改为你想测试的扩展支持的某个语言区域。

  1. 在 Firefox 中打开 "about:config",找到 general.useragent.locale 首选项。
  2. 双击该首选项(或按回车)以选择它,输入你想测试的语言环境的语言代码,然后点击“确定”(或按回车)。我们的示例扩展支持“en”(英语)、“de”德语()、“nl”(荷兰语)和“ja”日语。
  3. 重启你的浏览器以完成更改。

Note: This works to change the browser's locale, even if you haven't got the language pack installed for that language. You'll just the browser UI in your default language if this is the case.

再次从磁盘临时加载该扩展,然后测试你的新语言环境:

  • 再次访问 "about:addons" — 你现在应该看到该附加组件已列出其图标,以及相应语言的名称和描述。
  • 再次测试你的 WebExtension。在我们的例子中,你应转到另一个网站并点击一个链接,以查看该通知现在是否以相应语言显示。

文档标签和贡献者

 此页面的贡献者: yfdyh000
 最后编辑者: yfdyh000,