MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

翻译正在进行中。

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上的查找工具查询语言子标签。请注意,您需要搜索语言的英语名称。

所有 i18n 系统都要求提供需翻译为您想支持的不同语言区域的字符串。 在 WebExtensions 里,这些字符串都被包含在一个名为 _locales 的目录下,_locales 位于扩展的根目录。每个语言区域都带有一个名叫 messages.json 的文件,其中包含相应的字符串(在 WebExtension 里叫做消息),这个文件放在 _locales 的子目录下,子目录是用对应语言区域的语言子标签来命名的。

注意如果子标签包含一个基本的语言和一个区域变量,那么语言和变量之间通常会用连字号隔开,例如 "en-US"。但作为 _locales 的子目录,它必须使用下划线来隔开,例如 "en_US"。

所以例如我们这个示例应用,我们有如下几个目录:"en"(英语)、"de"(德语)、"nl"(荷兰语)以及 "ja"(日本语)。每个目录都包含一个 messages.json 文件。

现在我们来看一下其中一个文件(_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 成员时将被一个子字符串替换。您将在接下来的从 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"

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

依赖语言区域的 CSS

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

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

这很有用,不过在这种情况下使用预定义消息来处理或许会更好。

从 JavaScript 检索消息字符串

看来您已经建立起您的消息字符串和 manifest。现在您只需要开始从 JavaScript 调用这些消息字符串,以便您的扩展尽可能多地和正确的语言进行交互。实际的 i18n API 相当简单,只需包含以下四个主要的方法(method):

  • 很可能您最常使用的是 i18n.getMessage() — 您可以使用此方法检索一个指定的语言字符串,如上所述。下面我会看到特定的用法示例。
  • i18n.getAcceptLanguages()i18n.getUILanguage() 这两个方法可以在您需要根据语言区域自定义用户界面时使用 — 或许您希望根据用户想要的语言在首选项列表更高层显示首选项,或只显示和特定语言有关的文化信息,又或是按浏览器语言显示格式化过的日期。
  • i18n.detectLanguage() 这个方法可以用来检测用户提交内容的语言,并将其正确格式化。

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

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

第一行是从最适合当前语言区域的可用 messages.json 文件中检索 notificationTitle message 字段。第二行与第一行类似,但它是被传递了一个 URL 作为第二个参数。怎么一回事?它就是你指定内容替代 notificationContent message 字段里的 $URL$ 占位符的方式:

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

"placeholders" 这个成员定义了所有的占位符,以及他们所检索的来源。"url" 这个占位符指定了其内容取自 $1,它就是 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。在我们的例子中,你应转到另一个网站并点击一个链接,以查看该通知现在是否以相应语言显示。

文档标签和贡献者

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