aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/mozilla/add-ons/webextensions/internationalization/index.html
blob: 88971977ae33742938192b4988a992f63e8d3c1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
---
title: 国际化
slug: Mozilla/Add-ons/WebExtensions/Internationalization
translation_of: Mozilla/Add-ons/WebExtensions/Internationalization
---
<div>{{AddonSidebar}}</div>

<p><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions">WebExtensions</a> API 有一个相当方便的模块可用于附加组件的国际化(<a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n">i18n</a>)。我们将在本文中探讨其功能,并为它的运作方式提供一个实例。WebExtensions 的 i18n 系统类似常见的 i18n 用途 JavaScript 库,例如 <a href="http://i18njs.com/">i18n.js</a></p>

<div class="note">
<p>本文中的 WebExtension 实例 <a href="https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n">notify-link-clicks-i18n</a> 可在 GitHub 上查阅。在您阅读下列章节时,可参照它的代码。</p>
</div>

<h2 id="剖析一个国际化的_WebExtension">剖析一个国际化的 WebExtension</h2>

<p>一个国际化的 WebExtension 与其他 WebExtension 一样可以包含各类功能,如<a href="/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Background_scripts">后台脚本</a><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts">内容脚本</a>等,但它也有些额外的部分,从而允许它适应不同的语言区域。目录树大致如下:</p>

<ul class="directory-tree">
 <li>webextension-根目录/
  <ul>
   <li>_locales
    <ul>
     <li>en
      <ul>
       <li>messages.json
        <ul>
         <li>英语消息(字符串)</li>
        </ul>
       </li>
      </ul>
     </li>
     <li>de
      <ul>
       <li>messages.json
        <ul>
         <li>德语消息(字符串)</li>
        </ul>
       </li>
      </ul>
     </li>
     <li>等其他语言</li>
    </ul>
   </li>
   <li>manifest.json
    <ul>
     <li>含按语言区域而定的元数据</li>
    </ul>
   </li>
   <li>myJavascript.js
    <ul>
     <li>含用于检索浏览器语言区域、特定语言环境的消息等的 JavaScript。</li>
    </ul>
   </li>
   <li>myStyles.css
    <ul>
     <li>含按语言区域而定的 CSS</li>
    </ul>
   </li>
  </ul>
 </li>
</ul>

<p>让我们逐项探讨这些新特性,因为下列每个章节都是你在国际化 WebExtension 时所要遵循的步骤。</p>

<h2 id="在__locales_中提供本地化的字符串">在 _locales 中提供本地化的字符串</h2>

<div class="pull-aside">
<div class="moreinfo">你可以使用<a href="http://r12a.github.io/apps/subtags/">Language subtag lookup page</a>上的查找工具查询语言子标签。请注意,你需要搜索语言的英语名称。</div>
</div>

<p>每个 i18n 系统都需要你提供想支持的各语言区域的已翻译字符串。 在 WebExtensions 中,这些字符串放在一个名为 <code>_locales</code> 的目录中,<code>_locales</code> 目录则位于扩展的根目录。每个语言区域都有一个名叫 <code>messages.json</code> 的文件,其中包含相应的字符串(在 WebExtension 中称作“消息”),这个文件放在 <code>_locales</code> 的子目录下,子目录以对应语言区域的语言子标签来命名。</p>

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

<p>因此<a href="https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n/_locales" title="1">在我们这个示例应用</a>中,我们有如下几个目录:"en"(英语)、"de"(德语)、"nl"(荷兰语)以及 "ja"(日语)。每个目录都包含一个 <code>messages.json</code> 文件。</p>

<p>现在我们来看其中一个文件(<a href="https://github.com/mdn/webextensions-examples/blob/master/notify-link-clicks-i18n/_locales/en/messages.json">_locales/en/messages.json</a>)的结构:</p>

<pre class="brush: 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"
      }
    }
  }
}</pre>

<p>这个文件是一个标准的 JSON — 其中每个成员都是一个带有名称的对象,里面包含一个 <code>message</code>(消息)和一个 <code>description</code>(描述)。这些项目都是字符串。<code>$URL$</code> 是一个占位符,在 WebExtension 调用 <code>notificationContent</code> 成员时将被一个子字符串替换。你将在接下来的{{anch("从 JavaScript 检索消息字符串")}}章节中了解如何使用。</p>

<div class="note">
<p><strong>注意:</strong>你可以在 <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/Locale-Specific_Message_reference">Locale-Specific Message reference</a> 里找到更多有关  <code>messages.json</code> 文件中内容的信息。</p>
</div>

<h2 id="国际化_manifest.json">国际化 manifest.json</h2>

<p>要国际化你的 manifest.json,有几项任务要完成。</p>

<h3 id="在_manifests_中检索本地化的字符串">在 manifests 中检索本地化的字符串</h3>

<p>你的 <a href="https://github.com/mdn/webextensions-examples/blob/master/notify-link-clicks-i18n/manifest.json">manifest.json</a> 包含显示给用户的字符串,例如附加组件的名称和描述。如果你将这些字符串国际化,并将合适的翻译放到 messages.json 中,则用户将根据当前语言区域看到适当的字符串翻译。</p>

<p>要将这些字符串国际化,按如下格式指定:</p>

<pre class="brush: json">"name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__",</pre>

<p>我们在在这里检索到消息字符串依赖于浏览器的语言区域,而不仅仅是一个静态字符串。</p>

<p>要这样调用消息字符串,你需要用的方式为:</p>

<ol>
 <li>两个下划线,接着是</li>
 <li>字符串 "MSG",接着是</li>
 <li>一个下划线,接着是</li>
 <li>你想调用的在 <code>messages.json</code> 中定义的消息名称,接着是</li>
 <li>两个下划线</li>
</ol>

<pre><strong>__MSG_</strong> + <em>messageName</em> + <strong>__</strong></pre>

<h3 id="指定默认语言区域">指定默认语言区域</h3>

<p>你还必须在你的 manifest.json 中指定另一个字段,它就是 <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/default_locale">default_locale</a></p>

<pre class="brush: json">"default_locale": "en"</pre>

<p>如果扩展没有包含浏览器当前语言区域的本地化字符串,则会使用该字段所指定的默认语言区域。任何浏览器语言区域下不可用的消息字符串都将用默认语言区域的字符串替换。有关浏览器如何选择字符串,还有一些细节需要注意 — 详见{{anch("选择本地化的字符串")}}</p>

<h2 id="依赖语言区域的_CSS">依赖语言区域的 CSS</h2>

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

<pre class="brush: css">header {
  background-image: url(../images/__MSG_extensionName__/header.png);
}</pre>

<p>这很有用,不过在这种情况下使用{{anch("预定义消息")}}来处理或许会更好。</p>

<h2 id="从_JavaScript_检索消息字符串">从 JavaScript 检索消息字符串</h2>

<p>所以,您应该已经建立起您的消息字符串和 manifest。现在只需开始从 JavaScript 调用这些消息字符串,以便你的扩展尽可能多地表述适合的语言。实际上 <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n">i18n API</a> 相当简单,只需包含以下四个主要的方法(method):</p>

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

<p>在我们的 <a href="https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n">notify-link-clicks-i18n</a> 示例中,<a href="https://github.com/mdn/webextensions-examples/blob/master/notify-link-clicks-i18n/background-script.js">后台脚本</a>包含下列代码:</p>

<pre class="brush: js">var title = browser.i18n.getMessage("notificationTitle");
var content = browser.i18n.getMessage("notificationContent", message.url);</pre>

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

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

<p><code>"placeholders"</code> 这个成员定义了所有的占位符,以及它们所检索的来源。<code>"url"</code> 这个占位符指定了其内容取自 $1,它就是<code> getMessage() 第二个参数里的第一个值。由于占位符就叫做</code> <code>"url",我们就在实际的消息字符串中用</code> <code>$URL$</code> 调用它(<code>"name"</code><code> $NAME$</code> 调用也是一样的,以此类推)。对于多个占位符,你可以将其置于数组内,并作为第二个参数传递到 {{WebExtAPIRef("i18n.getMessage()")}}<code>[a, b, c]</code> 可替换为 <code>$1</code>, <code>$2</code>, and <code>$3,以此类推,并置于</code> <code>messages.json</code> 内。</p>

<p>接下来我们看一个例子:在 <code>en/messages.json 文件中</code>原始的 <code>notificationContent</code> 消息字符串如下: in the <code>en/messages.json</code> file is</p>

<pre>您点击了 $URL$。</pre>

<p>我们可以看到链接点击后会打开 <code>https://developer.mozilla.org。</code>{{WebExtAPIRef("i18n.getMessage()")}} 调用后,第二个参数的内容就变成了 messages.json 里的 <code>$1</code>,并替换定义在 <code>"url" 占位符里的</code> <code>$URL$</code> 这个占位符。所以最后的消息字符串就变成了:</p>

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

<h3 id="直接占位符的使用">直接占位符的使用</h3>

<p>你可以直接将变量<code>($1</code>, <code>$2</code>, <code>$3</code> 等)插入消息字符串,例如我们可以将上述的<code> "notificationContent"</code> 成员重写为:</p>

<pre class="brush: json">"notificationContent": {
  "message": "你点击了 $1。",
  "description": "告诉用户点击了哪个链接。"
}</pre>

<p>这么做看起来似乎更快捷,也没那么复杂,但另一种方法(使用 <code>"placeholders")更切实际,因为提供一个占位符名</code>(例如 <code>"url"</code>)和 example 可帮你记住占位符是什么 — 在你代码写了一个礼拜后,你很可能忘了 <code>$1</code><code>$8</code> 表示什么,但你或许记得起来占位符名表示什么。</p>

<h3 id="替换硬编码">替换硬编码</h3>

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

<pre class="brush: json">"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/"
    }
  }
}</pre>

<p>在本例中我们只是硬编码了占位符的内容,而不是从 <code>$1 这样的变量值中获取它。有时候你会遇到消息文件非常复杂,或者如果你想将文件里的不同值分离开来以便字符串可读性更好,</code>这种情况下它会很有用,这些值可通过编程来访问。</p>

<p>此外,你可使用这样的替代方式指定不想被翻译的一部分字符串,例如人名或公司名。</p>

<h2 id="选择本地化的字符串">选择本地化的字符串</h2>

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

<ol>
 <li>如果有精确匹配当前语言区域的 <code>messages.json</code> 文件,并且它包含该字符串,则返回它。</li>
 <li>否则,如果当前语言区域有合格区域(例如 <code>en_US</code>)并且有一个无区域限定的 <code>messages.json</code> 文件(例如 <code>en</code>)且包含该字符串,则返回它。</li>
 <li>否则,如果 <code>manifest.json 里包含</code> <code>default_locale</code> 所对应的 <code>messages.json</code> 文件且包含该字符串,则返回它。</li>
 <li>否则,返回一个空字符串。</li>
</ol>

<p>参见下列示例:</p>

<ul class="directory-tree">
 <li>webextension-root-directory/
  <ul>
   <li>_locales
    <ul>
     <li>en_GB
      <ul>
       <li>messages.json
        <ul>
         <li><code>{ "colorLocalised": { "message": "colour", "description": "Color." }, ... }</code></li>
        </ul>
       </li>
      </ul>
      en

      <ul>
       <li>messages.json
        <ul>
         <li><code>{ "colorLocalised": { "message": "color", "description": "Color." }, ... }</code></li>
        </ul>
       </li>
      </ul>
     </li>
     <li>fr
      <ul>
       <li>messages.json
        <ul>
         <li><code>{ "colorLocalised": { "message": "<span lang="fr">couleur</span>", "description": "Color." }, ...}</code></li>
        </ul>
       </li>
      </ul>
     </li>
    </ul>
   </li>
  </ul>
 </li>
</ul>

<p>假设 <code>default_locale</code> 是设为 <code>fr</code>,而浏览器的语言环境为 <code>en_GB</code></p>

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

<h2 id="预定义消息">预定义消息</h2>

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

<pre>__MSG_extensionName__</pre>

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

<pre>__MSG_@@ui_locale__</pre>

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

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">消息名</th>
   <th scope="col">描述</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>@@extension_id</code></td>
   <td>扩展或应用的 ID;你可以使用该字符串构建 URL,用于扩展内部的资源。甚至未本地化的扩展也可以使用该消息。<br>
    <strong>注意:</strong>该消息无法在 manifest 文件中使用。</td>
  </tr>
  <tr>
   <td><code>@@ui_locale</code></td>
   <td>当前的语言区域;你可以使用该字符串构建语言区域特有的 URL。</td>
  </tr>
  <tr>
   <td><code>@@bidi_dir</code></td>
   <td>当前语言区域对应的文本书写方向,可以是 "ltr",例如英语这样的从左到右书写的语言,或"rtl",例如阿拉伯语这样的从右到左书写的语言。</td>
  </tr>
  <tr>
   <td><code>@@bidi_reversed_dir</code></td>
   <td>如果 <code>@@bidi_dir</code> 是 "ltr",则是 "rtl",否则是 "ltr"。</td>
  </tr>
  <tr>
   <td><code>@@bidi_start_edge</code></td>
   <td>如果 <code>@@bidi_dir</code> 是 "ltr",则是 "left",否则是 "right"。</td>
  </tr>
  <tr>
   <td><code>@@bidi_end_edge</code></td>
   <td>如果<code> @@bidi_dir</code> 是 "ltr",则是 "right",否则是 "left"。</td>
  </tr>
 </tbody>
</table>

<p>回到我们之前的例子,像下面这样写更有意义:</p>

<pre class="brush: css">header {
  background-image: url(../images/__MSG_@@ui_locale__/header.png);
}</pre>

<p>现在我们可以保存本地指定的图片,目录则可以根据所支持的不同语言区域进行匹配,例如 en、de 等 — 这样显得更有意义多了。</p>

<p>让我们看看一个在 CSS 文件中使用 <code>@@bidi_*</code> 消息的例子:</p>

<pre class="brush: css">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;
}</pre>

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

<pre class="brush: css">direction: ltr;
padding-left: 0;
padding-right: 1.5em;
</pre>

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

<pre class="brush: css">direction: rtl;
padding-right: 0;
padding-left: 1.5em;</pre>

<h2 id="测试你的_WebExtension">测试你的 WebExtension</h2>

<p>从 Firefox 45 开始,你可以临时安装磁盘上的 WebExtensions  — 另见<a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Packaging_and_installation#Loading_from_disk">从磁盘加载。</a>按上述步骤操作,然后尝试我们的 <a href="https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n">notify-link-clicks-i18n</a> WebExtension。访问你喜欢的任何网站,然后点一下链接,查看是否有通知出现来显示所点击的链接网址。</p>

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

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

<div class="note">
<p><strong>Note</strong>: 这个方法可以用来修改浏览器的语言区域,即使你未安装过该语言区域对应的<a href="https://addons.mozilla.org/en-US/firefox/language-tools/">语言包</a>。这样的话你就可以将浏览器用户界面调整显示为你的默认语言。</p>
</div>

<ol>
</ol>

<p>再次从磁盘临时加载该扩展,然后测试你的新语言区域:</p>

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

<p>{{EmbedYouTube("R7--fp5pPGg")}}</p>