aboutsummaryrefslogtreecommitdiff
path: root/files/ru/mozilla/add-ons/webextensions/internationalization/index.html
blob: 4c70617131d4ae42362858afb1603350b36d70a6 (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
395
396
397
398
399
400
401
402
403
404
405
406
---
title: Интернационализация
slug: Mozilla/Add-ons/WebExtensions/Internationalization
translation_of: Mozilla/Add-ons/WebExtensions/Internationalization
original_slug: Mozilla/Add-ons/WebExtensions/Интернационализация
---
<div>{{AddonSidebar}}</div>

<p>API <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions">WebExtensions</a> предоставляет полезный модуль для интернационализации расширений — <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n">i18n</a>. В этой статье мы рассмотрим его особенности и пример его работы. Система для расширений, построенных с помощью API WebExtension, i18n похожа на библиотеки JavaScript для i18n, такие как <a href="http://i18njs.com/">i18n.js</a>.</p>

<div class="note">
<p>Расширение, используемое в этой статье в качестве примера, — <a href="https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n">notify-link-clicks-i18n</a> — доступно на GitHub. Читая последующие секции этой статьи, вы можете исследовать его исходный код.</p>
</div>

<h2 id="Структура_интернализированного_расширения">Структура интернализированного расширения</h2>

<p>Интернационализированное расширение может содержать такие же элементы, как и любое другое расширение — <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>корневая-директория-расширения/
  <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>Давайте отдельно рассмотрим каждый элемент — последующие секции представляют собой шаги, которым стоит следовать во время интернационализации вашего расширения.</p>

<h2 id="Добавление_локализованных_строк_в__locales">Добавление локализованных строк в _locales</h2>

<div class="pull-aside">
<div class="moreinfo">Вы можете определить тэг языка при помощи инструмента <em>Find</em>  на <a href="https://r12a.github.io/app-subtags/">странице определения языковых тегов</a>. Обратите внимание на то, что при поиске нужно использовать английское название языка</div>
</div>

<p>Каждая система i18n требует предоставить строки во всех локализациях, которые вы хотите поддерживать. В расширениях они хранятся в директории  <code>_locales</code>, размещённой внутри корневой директории. Строки каждой локализации (также называемые сообщениями) хранятся в файле <code>messages.json</code>, находящемся в поддиректории <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">в нашем примере</a> существую директории "en" (английский), "de" (немецкий), "nl" (голландский), and "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> — это заполнитель, который заменяется подстрокой, когда элемент <code>notificationContent</code> вызывается расширением. Вы научитесь это делать в секции {{anch("Получение сообщений из JavaScript")}}.</p>

<div class="note">
<p><strong>Примечание</strong>: вы можете найти больше информации о структуре <code>messages.json</code> <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/Locale-Specific_Message_reference">здесь</a>.</p>
</div>

<h2 id="Интернационализация_manifest.json">Интернационализация manifest.json</h2>

<p>Для интернационализации файла manifest.json нужно предпринять несколько шагов.</p>

<h3 id="Получение_локализованных_строк_в_манифестах">Получение локализованных строк в манифестах</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>Этот параметр устанавливает локализацию по умолчанию, используемую, если расширение не поддерживает локализацию браузера пользователя. Любые сообщения, недоступные в текущей локализации, будут браться из той локализации, которая установлена по умолчанию. There are some more details to be aware of in terms of how the browser selects strings — see {{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.json. Чтобы ваше расширение начало использовать правильные языки, соответствующие сообщения следует вызывать при помощи JavaScript. <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n">API i18n</a> достаточно прост и содержит всего 4 основных метода:</p>

<ul>
 <li>Скорее всего, наиболее часто вы будете использовать {{WebExtAPIRef("i18n.getMessage()")}} — этот метод используется для получения конкретного сообщения. Примеры его использования можно увидеть ниже.</li>
 <li>Методы {{WebExtAPIRef("i18n.getAcceptLanguages()")}} и {{WebExtAPIRef("i18n.getUILanguage()")}} используются, если UI надо менять в зависимости от локализации — например, если вы хотите, чтобы предпочтения, свойственные носителям какого-либо языка, находились выше в списке, или чтобы формат дат соответствовал локализации браузера.</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>notificationTitle message</code> из доступного файла <code>messages.json</code>, соответствующее наиболее подходящей локализации . Вторая строка похожа на первую, но в ней метод принимает URL в качестве второго параметра. Зачем? С помощью этого параметра мы указываем, на что нужно заменить заполнитель <code>$URL$</code> в поле <code>notificationContent message</code>:</p>

<pre class="brush: json">"notificationContent": {
  "message": "You clicked $URL$.",
  "description": "Tells the user which link they clicked.",
  "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> и <code>$3</code>, и т. д. внутрь <code>messages.json</code>.</p>

<p>Давайте посмотрим на пример: изначально сообщение <code>notificationContent</code> в файле <code>en/messages.json</code> такое:</p>

<pre>You clicked $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>You clicked 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": "You clicked $1.",
  "description": "Tells the user which link they clicked."
}</pre>

<p>Этот метод может показаться более быстрым и простым, но другой способ (использование <code>"placeholders"</code>) считается лучшей практикой. Это вызвано тем, что имена заполнителей (например <code>"url"</code>) и примеры помогают понять, что означают заполнители — через неделю после написания кода Вы, наверное, забудете, что обозначают заполнители <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>messages.json</code> для <code>default_locale</code>, указанной в <code>manifest.json</code>, и этот файл содержит нужную строку, возвращается она.</li>
 <li>В противном случае возвращается пустая строка.</li>
</ol>

<p>Рассмотрим следующий пример:</p>

<ul class="directory-tree">
 <li>корневая-директория-расширения/
  <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>Если бы в <code>en_GB</code> не было "colorLocalized", то вызов <code>getMessage("colorLocalised")</code>, вернул бы "color", а не "couleur".</li>
</ul>

<h2 id="Заранее_определённые_сообщения">Заранее определённые сообщения</h2>

<p>Модуль i18n module предоставляет заранее определённые сообщения, которые можно вызвать таким же образом, как мы это делали в разделе {{anch("Интернационализация manifest.json")}}. Например:</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">Message name</th>
   <th scope="col">Description</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>@@extension_id</code></td>
   <td>
    <p>Внутренний UUID расширения. Эту строку можно использовать для создания URL ресурсов внутри расширения.Даже нелокализованные расширения могут использовать это сообщения.</p>

    <p>Это сообщения нельзя использовать в manifest.json.</p>

    <p>Также стоит заметить, что этот ID — <em>не</em> ID аддона, которое возвращает {{WebExtAPIRef("runtime.id")}}, и которое может быть установлено с помощью ключа <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/applications">applications</a> в manifest.json. Это сгенерированный UUID, содержащийся в URL аддона. Это означает, что данную величину нельзя использовать в качестве параметра <code>extensionId</code>  метода {{WebExtAPIRef("runtime.sendMessage()")}}, или для проверки поля <code>id</code> объекта {{WebExtAPIRef("runtime.MessageSender")}}.</p>
   </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>Давайте рассмотрим пример использования сообщений <code>@@bidi_*</code> в файле CSS:</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="Тестирование_расширения">Тестирование расширения</h2>

<p>Начиная с Firefox 45, расширения могут быть временно установлены с диска — подробнее об этом написано в статье <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Packaging_and_installation#Loading_from_disk">Loading from disk</a>. Сделайте это и попробуйте протестировать наше расширение <a href="https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n">notify-link-clicks-i18n</a>. Перейдите на одну из ваших любимых страниц и нажмите на ссылку, чтобы проверить, появляется ли сообщения, содержащее URL нажатой ссылки.</p>

<p>Затем измените локализацию Firefox на какую-либо поддерживающуюся расширением, которое вы хотите протестировать.</p>

<ol>
 <li>Откройте "about:config" в Firefox, и найдите параметр <code>intl.locale.requested</code> (обратите внимание на версию Firefox: в версиях до Firefox 59 этот параметр называется <code>general.useragent.locale</code>).</li>
 <li>Если параметр существует, нажмите на него дважды (или нажмите Return/Enter), чтобы выбрать его, введите языковой код локализации, которую вы хотите протестировать и нажмите "OK" (или Return/Enter). Например, в нашем примере расширение поддерживает "en" (английский), "de" (немецкий), "nl" (голландский), and "ja" (японский). Вы также можете указать пустую строку (<code>""</code>) в качестве значения. В этом случае браузер выберет язык вашей ОС по умолчанию.</li>
 <li>Если параметр <code>intl.locale.requested</code> не существует, нажмите правой кнопкой мыши на список параметров (или откройте контекстное меню при помощи клавиатуры), и выберите "New", а затем "String". Введите <code>intl.locale.requested</code> как имя настройки и, "de", "nl", и т. д. как значение, как это описано в шаге 2.</li>
 <li>Найдите <code>intl.locale.matchOS</code> и, если параметр существует и равен <code>true</code>, дважды нажмите на него и установите на <code>false</code>.</li>
 <li>Перезапустите браузер, чтобы изменения вступили в силу.</li>
</ol>

<div class="note">
<p><strong>Примечание</strong>: Этот метод работает, даже если у вас не установлен <a href="https://addons.mozilla.org/en-US/firefox/language-tools/">языковой пакет</a> для выбранного языка. В этом случае UI браузера просто будет использовать ваш язык по умолчанию.</p>
</div>

<ol>
</ol>

<div class="note">
<p><strong>Примечание:</strong> Чтобы изменить результат <code>getUILanguage</code> требуется языковой пакет, поскольку он отражает язык UI браузера, а не язык сообщений расширения.</p>
</div>

<p>Ещё раз загрузите расширение с диска и протестируйте локализацию:</p>

<ul>
 <li>Ещё раз откройте "about:addons" — теперь вы должны увидеть ваше расширение, его иконку, имя и описание на выбранном языке.</li>
 <li>Ещё раз протестируйте расширение. Для нашего примера, вам следовало бы посетить другую страницу и, нажав на ссылку, проверить, появляется ли сообщение на нужном языке.</li>
</ul>

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