國際化
WebExtensions API 有一個非常方便的模組可用於擴充套件國際化——i18n。本文將探討其功能並提供一個實際的工作示例。
注意:本文中展示的示例擴充套件——notify-link-clicks-i18n——可在 GitHub 上獲取。在閱讀以下部分時,請參閱原始碼。
國際化擴充套件的組成
國際化擴充套件可以包含與任何其他擴充套件相同的功能——背景指令碼、內容指令碼等——但它也有一些額外的部分,允許它在不同的語言環境之間切換。這些總結在以下目錄樹中:
- 擴充套件根目錄/
-
_locales
-
en
- messages.json
- 英語訊息(字串)
- messages.json
-
de
- messages.json
- 德語訊息(字串)
- messages.json
-
etc.
-
-
manifest.json
- 依賴於語言環境的元資料
-
myJavascript.js
- 用於檢索瀏覽器語言環境、特定於語言環境的訊息等的 JavaScript。
-
myStyles.css
- 依賴於語言環境的 CSS
-
讓我們依次探索每個新功能——以下每個部分都代表國際化擴充套件時要遵循的一個步驟。
在 _locales 中提供本地化字串
注意:您可以使用語言子標籤查詢頁面上的查詢工具查詢語言子標籤。請注意,您需要搜尋語言的英文名稱。
每個 i18n 系統都需要提供翻譯成您想要支援的所有不同語言環境的字串。在擴充套件中,這些字串包含在一個名為 _locales 的目錄中,放置在擴充套件根目錄內。每個單獨的語言環境都將其字串(稱為訊息)包含在一個名為 messages.json 的檔案中,該檔案放置在 _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://mdn.club.tw"
}
}
}
}
此檔案是標準 JSON——它的每個成員都是一個帶名稱的物件,其中包含一個 message 和一個 description。所有這些項都是字串;$URL$ 是一個佔位符,當擴充套件呼叫 notificationContent 成員時,它會被子字串替換。您將在從 JavaScript 中檢索訊息字串部分了解如何執行此操作。
注意:您可以在我們的特定於語言環境的訊息參考中找到有關 messages.json 檔案內容的更多資訊。
國際化 manifest.json
國際化 manifest.json 有幾個不同的任務要執行。
在清單中檢索本地化字串
您的manifest.json 包含顯示給使用者的字串,例如擴充套件的名稱和描述。如果您將這些字串國際化,並將它們的適當翻譯放入 messages.json 中,那麼將根據當前語言環境向用戶顯示字串的正確翻譯,如下所示。
要國際化字串,請像這樣指定它們
"name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__",
在這裡,我們正在檢索依賴於瀏覽器語言環境的訊息字串,而不僅僅是包含靜態字串。
要像這樣呼叫訊息字串,您需要像這樣指定它
- 兩個下劃線,後面跟著
- 字串“MSG”,後面跟著
- 一個下劃線,後面跟著
- 您想呼叫的訊息名稱,如
messages.json中定義的,後面跟著 - 兩個下劃線
__MSG_ + messageName + __
指定預設語言環境
您應該在 manifest.json 中指定的另一個欄位是 default_locale
"default_locale": "en"
這指定了當擴充套件不包含瀏覽器當前語言環境的本地化字串時要使用的預設語言環境。任何在瀏覽器語言環境中不可用的訊息字串都將從預設語言環境中獲取。瀏覽器如何選擇字串還有一些需要注意的細節——請參閱本地化字串選擇。
依賴於語言環境的 CSS
請注意,您還可以從擴充套件中的 CSS 檔案中檢索本地化字串。例如,您可能希望構造一個依賴於語言環境的 CSS 規則,如下所示:
header {
background-image: url("../images/__MSG_extensionName__/header.png");
}
這很有用,儘管您最好使用預定義訊息來處理這種情況。
從 JavaScript 中檢索訊息字串
所以,您已經設定了訊息字串和清單。現在您只需開始從 JavaScript 呼叫您的訊息字串,以便您的擴充套件儘可能地使用正確的語言。i18n API 本身非常簡單,只包含四個主要方法:
- 您可能最常使用
i18n.getMessage()— 這是您用於檢索特定語言字串的方法,如上所述。我們將在下面看到它的具體使用示例。 i18n.getAcceptLanguages()和i18n.getUILanguage()方法可以在您需要根據語言環境自定義 UI 時使用——也許您想在偏好設定列表中顯示特定於使用者首選語言的偏好設定,或者顯示僅與某種語言相關的文化資訊,或者根據瀏覽器語言環境適當地格式化顯示的日期。i18n.detectLanguage()方法可用於檢測使用者提交內容的語言並適當地格式化。
在我們的 notify-link-clicks-i18n 示例中,背景指令碼包含以下幾行:
let title = browser.i18n.getMessage("notificationTitle");
let content = browser.i18n.getMessage("notificationContent", message.url);
第一個只是從最適合瀏覽器當前語言環境的可用 messages.json 檔案中檢索 notificationTitle message 欄位。第二個類似,但它將 URL 作為第二個引數傳入。這是怎麼回事?這就是您如何指定內容以替換我們在 notificationContent message 欄位中看到的 $URL$ 佔位符:
"notificationContent": {
"message": "You clicked $URL$.",
"description": "Tells the user which link they clicked.",
"placeholders": {
"url" : {
"content" : "$1",
"example" : "https://mdn.club.tw"
}
}
}
"placeholders" 成員定義了所有佔位符,以及它們從何處檢索。"url" 佔位符指定其內容取自 $1,這是作為 getMessage() 的第二個引數給出的第一個值。由於佔位符名為 "url",我們在實際訊息字串中使用 $URL$ 來呼叫它(因此對於 "name",您將使用 $NAME$,等等)。如果您有多個佔位符,您可以將它們作為第二個引數傳遞給 i18n.getMessage() 的陣列中——[a, b, c] 將在 messages.json 中作為 $1、$2 和 $3 等可用。
讓我們來看一個例子:en/messages.json 檔案中的原始 notificationContent 訊息字串是:
You clicked $URL$.
假設點選的連結指向 https://mdn.club.tw。在呼叫 i18n.getMessage() 之後,第二個引數的內容在 messages.json 中作為 $1 可用,它替換了 "url" 佔位符中定義的 $URL$ 佔位符。因此最終的訊息字串是:
You clicked https://mdn.club.tw.
直接佔位符使用
可以直接將變數($1、$2、$3 等)插入到訊息字串中,例如我們可以將上面的 "notificationContent" 成員改寫成這樣:
"notificationContent": {
"message": "You clicked $1.",
"description": "Tells the user which link they clicked."
}
這看起來可能更快、更簡單,但另一種方式(使用 "placeholders")被認為是最佳實踐。這是因為有佔位符名稱(例如,"url")和示例有助於您記住佔位符的用途——在您編寫程式碼一週後,您可能會忘記 $1 – $8 指的是什麼,但您更有可能知道您的佔位符名稱指的是什麼。
硬編碼替換
還可以在佔位符中包含硬編碼字串,以便每次都使用相同的值,而不是從程式碼中的變數獲取值。例如:
"mdn_banner": {
"message": "For more information on web technologies, go to $MDN$.",
"description": "Tell the user about MDN",
"placeholders": {
"mdn": {
"content": "https://mdn.club.tw/"
}
}
}
在這種情況下,我們只是硬編碼佔位符內容,而不是從 $1 這樣的變數值中獲取。當您的訊息檔案非常複雜時,這有時會很有用,並且您希望拆分不同的值以使檔案中的字串更具可讀性,此外,這些值還可以透過程式設計方式訪問。
此外,您可以使用此類替換來指定字串中您不希望翻譯的部分,例如人名或公司名稱。
本地化字串選擇
語言環境可以使用語言程式碼(例如 fr 或 en)或帶有指令碼和區域程式碼(例如 en-US 或 zh-Hans-CN)來指定。當您的擴充套件向 i18n 系統請求字串時,它使用以下演算法選擇字串:
- 如果使用者的瀏覽器語言環境設定的
messages.json檔案中包含該字串,則返回該字串。例如,如果使用者已將其瀏覽器設定為en-US並且擴充套件提供了_locales/en_US/messages.json檔案。 - 否則,如果瀏覽器語言環境帶有指令碼或區域(例如,
en-US或zh-Hans-CN),並且存在無區域版本以及指令碼版本(如果前者不可用)的messages.json檔案,並且該檔案包含該字串,則返回該字串。例如,如果使用者已將其瀏覽器設定為zh-Hans-CN(並且沒有_locales/zh_Hans_CN/messages.json檔案),則 i18n 系統會在zh-Hans中查詢字串,如果該字串不可用,則在zh中查詢。 - 否則,如果
manifest.json中定義的default_locale存在messages.json檔案,並且它包含該字串,則返回該字串。 - 否則返回一個空字串。
以這個例子為例
- 擴充套件根目錄/
- _locales
-
en_GB
- messages.json
{ "colorLocalized": { "message": "colour", "description": "Color." }, /* … */ }
en
- messages.json
{ "colorLocalized": { "message": "color", "description": "Color." }, /* … */ }
- messages.json
-
fr
- messages.json
{ "colorLocalized": { "message": "couleur", "description": "Color." }, /* … */}
- messages.json
-
- _locales
假設 default_locale 設定為 fr。
- 如果瀏覽器語言環境是
en-GB,當擴充套件呼叫getMessage("colorLocalized")時,它將返回 "colour",因為_locales/en_GB/messages.json包含colorLocalized訊息。 - 如果瀏覽器語言環境是
en-US,當擴充套件呼叫getMessage("colorLocalized")時,它將返回 "color",因為它會回退到_locales/en/messages.json中存在的訊息。 - 如果瀏覽器語言環境是
zh-Hans-CN,當擴充套件呼叫getMessage("colorLocalized")時,它將返回 "couleur",因為沒有與zh-Hans-CN語言環境匹配的語言、指令碼或區域。
預定義訊息
i18n 模組為我們提供了一些預定義訊息,我們可以像我們之前在在清單中檢索本地化字串和依賴於語言環境的 CSS中看到的那樣呼叫它們。例如:
__MSG_extensionName__
預定義訊息使用完全相同的語法,只是在訊息名稱前加上 @@,例如
__MSG_@@ui_locale__
下表顯示了可用的不同預定義訊息:
| 訊息名稱 | 描述 |
|---|---|
@@extension_id |
擴充套件內部生成的 UUID。您可以使用此字串來構建擴充套件內部資源的 URL。即使是未本地化的擴充套件也可以使用此訊息。 您不能在清單檔案中使用此訊息。 另請注意,此 ID **不是** |
@@ui_locale |
當前語言環境;您可以使用此字串來構建特定於語言環境的 URL。 |
@@bidi_dir |
當前語言環境的文字方向,對於從左到右的語言(如英語)為“ltr”,對於從右到左的語言(如阿拉伯語)為“rtl”。 |
@@bidi_reversed_dir |
如果 @@bidi_dir 是“ltr”,則為“rtl”;否則為“ltr”。 |
@@bidi_start_edge |
如果 @@bidi_dir 是“ltr”,則為“left”;否則為“right”。 |
@@bidi_end_edge |
如果 @@bidi_dir 是“ltr”,則為“right”;否則為“left”。 |
回到我們之前的例子,這樣寫會更有意義:
header {
background-image: url("../images/__MSG_@@ui_locale__/header.png");
}
現在我們可以將本地特定的圖片儲存在與我們支援的不同語言環境(en、de 等)匹配的目錄中,這樣更有意義。
讓我們看一個在 CSS 檔案中使用 @@bidi_* 訊息的示例:
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;
測試您的擴充套件
要測試您的擴充套件程式的本地化,您可以使用 Firefox 或 Firefox Beta,這些 Firefox 版本可以安裝語言包。
然後,對於您想要測試的擴充套件程式支援的每個語言環境,請按照使用另一種語言的 Firefox 中的說明切換 Firefox UI 語言。(如果您熟悉“設定”中的“語言”,請使用“設定替代項”。)
當 Firefox 執行在您的測試語言中時,從 about:debugging,臨時安裝擴充套件程式,如果已安裝則重新載入。安裝或重新載入擴充套件程式後,如果您正確設定了擴充套件程式,您將看到擴充套件程式以選定的語言顯示其圖示、名稱和描述。您還可以在 about:addons 中檢視本地化的擴充套件程式詳細資訊。現在,使用擴充套件程式的功能以確保翻譯到位。
如果您想嘗試此過程,可以使用 notify-link-clicks-i18n 擴充套件。將 Firefox 設定為顯示此示例支援的語言之一(德語、荷蘭語或日語)。載入擴充套件並訪問一個網站。單擊一個連結以檢視報告連結 URL 的通知的翻譯版本。