本地化工作流程

要求程式設計師熟記用於特定訊息的魔法鍵並不切合實際,因此,本地化系統應提供符合人體工學的方式,以符號的方式參照這些鍵 (請參閱上述 MSG_Hello_World 的抽象範例)。

遵循 18n 和 l10n 的最佳做法,來源字串會儲存在 XML 檔案中 (在此為 strings.xml),如下所述。以下是 strings.xml 檔案的範例。這個檔案的目標是宣告程式使用的所有外部字串,並為這些字串提供本機專屬的 name。字串會做為翻譯的依據,而 name 會做為符號 xml <!-- comment --> <?xml version="1.0" encoding="utf-8"?> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- comment --> <string name="STRING_NAME" >text_string</string> <string name="STRING_NAME_2" >text_string_2</string> <string name="STRING_NAME_3" >string with an intervening newline</string> </resources> 的依據

檔案 strings.xml 會經過一系列轉換作業,其中翻譯人員會產生同一個檔案的特定語言變化版本。翻譯程序的輸入/輸出行為如下:strings.xml 檔案會輸入字串,這些字串以某種原始 (人類) 語言編寫,並輸出多種 strings.xml,每種都以特定單一語言翻譯。整個翻譯程序可能相當複雜,在大型機構中,可能會將工作外包給世界各地的翻譯人員,並使用大量專屬的翻譯工具。不過,只要維持輸入/輸出行為,我們就不需要太在意箱子中的確切運作機制,因為我們通常都知道翻譯作業可能需要一段時間。產生的檔案會轉換為機器可讀取的格式,並與 Fuchsia 程式一併在同一個位置中提供。 Fuchsia 套件的重要功能是,它們本質上並非封存檔,而是根據內容雜湊指向檔案的資訊清單。因此,多個程式可以共用相同的檔案,且密切相關的語言 (例如「en-US」、「en-GB」) 可能會共用訊息磁碟空間。下圖簡要說明字串的生命週期。

上圖顯示本地化流程。由於 XML 檔案已加上註解,因此無法直接用於機器翻譯,因此我們會轉換為 JSON 檔案,以便重複使用可用的程式庫來載入這些檔案,並建構從鍵到訊息字串的對應項目。這些字串可用於 `MessageFormat` 中的格式字串。

strings.xml

我們會重複使用 Android 字串資源 XML 格式,以表示可本地化的字串。由於我們不會在 strings.xml 格式中新增任何內容,因此功能的完整討論內容會轉交至字串資源頁面

雖然上述圖表中的所有 XML 都讓這個討論看起來像是從某個直接連結至 1990 年代的蟲洞中冒出來,但 XML 其實非常適合用來描述註解文字。strings.xml 是經過時間考驗的 Android 格式,因此我們知道它很合適,而且開發人員也熟悉這個格式。

舉例來說,您可以使用註解宣告字串資源,並將註解交錯插入來源文字。

<!-- … -->
<string name="title"
   >Best practices for <annotation font="title_emphasis">text</annotation> look like so</string>
<!-- … -->

上圖:翻譯文字和註解交錯的示例。_

您可以交錯不應翻譯的文字,例如

<string name="countdown">
  <xliff:g id="time" example="5 days"
    >{1}</xliff:g> until holiday</string>

上圖:範例說明如何交錯使用區隔的參數,並以註解提供示例值,以及使用不屬於字串資源資料結構定義的標記進行保護。_

我們也可以視需要在資料結構定義中加入自訂項目,並在現有結構定義中透明地交錯該資料結構定義。

上述檔案內容必須符合以下限制:

  • 檔案中的每個 name 屬性都必須不重複。
  • 名稱 ID 可包含大小寫 ASCII 字母、數字和底線,但開頭不得為數字。舉例來說,_H_e_L_L_o_wo_1_rld 可以使用,但 0cool 不行。
  • 檔案中不得有重複的 name-message 組合。

目前,系統不支援在專案中使用多個字串檔案。

訊息 ID

系統會根據 strings.xml 檔案的內容產生訊息 ID (每則訊息的「神奇」數字常數)。每個字串訊息都會取得專屬 ID,這項 ID 是根據 name 上的單向雜湊和訊息本身的內容計算而得。這項 ID 指派作業可確保兩則不同訊息不會意外產生相同的 ID。

這些訊息的產生作業會由 Fuchsia 中的 GN 建構規則自動執行,但最終是由名為 strings_to_fidl 的程式執行。這個程式會為訊息 ID 產生 FIDL 中繼表示法,而一般 FIDL 工具鍊則用於產生該資訊的語言專屬版本。舉例來說,C++ 風格會是包含以下內容的標頭檔案:

namespace fuchsia {
namespace intl {

namespace l10n {
enum class MessageIds : uint64_t {
  STRING_NAME = 42u,
  STRING_NAME_2 = 43u,
  STRING_NAME_3 = 44u,
};

}  // namespace l10n
}  // namespace intl
}  // namespace fuchsia

上述範例中,為每個特定列舉值指派的精確值並不相關。由於所有 ID 都是在編譯時產生,因此目前產生方法也無關緊要,也不會出現版本偏差。我們目前可以安全地假設相同的名稱-內容組合會一律指派相同的郵件 ID。

將產生的檔案納入 C++ 程式相當容易。以下提供最簡單的範例,但請參閱完整的範例,瞭解連線的確切細節。程式庫參數 fuchsia.intl.l10n 是由作者直接提供,做為 strings_to_fidl 的標記;如果使用適當的 GN 範本,則做為 GN 範本的參數。

#include <iostream>

// This header file has been generated from the strings library fuchsia.intl.l10n.
#include "fuchsia/intl/l10n/cpp/fidl.h"

// Each library name segment between dots gets its own nested namespace in
// the generated C++ code.
using fuchsia::intl::l10n::MessageIds;

int main() {
  std::cout << "Constant: " << static_cast<uint64_t>(MessageIds::STRING_NAME) << std::endl;
  return 0;
}

*.json

FIDL 和 C++ 程式碼產生功能可讓程式作者使用訊息 ID。在包裝方面,我們也必須為支援的每種語言提供本地化素材資源。目前這項資訊的編碼為 JSON。這項決定是為了加快速度,但我們可以針對這項決定做出許多改善,以提升效能和安全性。

產生這項資訊的任務會委派給名為 strings_to_json 的程式,該程式會將原始 strings.xml 與特定語言的檔案合併 (例如,法文翻譯會位於 strings_fr.xml 中)。同樣地,對於由 GN 驅動的建構作業,strings_to_json 的叫用會封裝在建構規則中。

以下是產生 JSON 檔案的內容範例。

{
  "locale_id": "fr",
  "source_locale_id": "en-US",
  "num_messages": 3,
  "messages": {
    "42": "le string",
    "43": "le string 2",
    "44": "le string\nwith intervening newline"
  }
}

JSON 格式目前定義了下列欄位。如果下方表格已過時,JSON 結構的真相來源為 字串模型

欄位 類型 說明
locale_id 語言代碼 ID (字串) 訊息翻譯的語言代碼。
source_locale_id 語言代碼 ID (字串) 來源訊息檔案的語言代碼。
num_messages 正整數 原始 strings.xml 中訊息的數量。這樣一來,我們就能比較訊息數量與 JSON 檔案中的訊息數量,快速估算翻譯品質。
messages 在地圖上顯示[u64->string] 將郵件 ID 對應至適當的郵件。