本地化工作流程

要求程式設計師以魔法鍵來指稱特定訊息並不可行,因此本地化系統應提供符合人體工學的方式,以人體工學的方式參照這些鍵 (請記住上述 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 套件的一項重要功能是這些套件本身並非封存,而是透過內容雜湊指向檔案的資訊清單。因此,多個程式可共用相同檔案,而高度相關的語言 (「en-US」, "en-GB") 可能會共用訊息磁碟空間。下圖為字串生命週期的精簡總覽。

上圖顯示本地化流程。由於 XML 檔案附有註解,並不直接適合進行機器翻譯,因此我們將這類檔案轉換為 JSON 檔案,以便重複使用可用的程式庫進行載入,以及透過金鑰與訊息字串建構對應。這些字串隨後可以做為 `MessageFormat` 中的格式字串。

strings.xml

我們會重複使用 Android 字串資源 XML 格式來代表可本地化的字串。由於我們不會在 string.xml 格式中新增任何內容,因此功能的完整討論會委派給字串資源頁面

雖然上圖中的所有 XML 看起來就像是直接跟 1990 年代相連的蠕蟲,但實際上 XML 其實非常適合描述註解文字)。string.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>

上圖:將 fenced-off 參數交錯的範例,以範例值加註,並受到不屬於字串資源資料結構定義的標記。_

如果有需要,我們也可以視需要自行新增資料結構定義的內容,並在現有結構定義中以公開透明方式交錯該資料結構定義。

上述檔案內容有一些必要的限制:

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

目前,在專案中擁有多個字串檔案不會提供任何佈建。

訊息 ID

訊息 ID (每則訊息的「神奇」數字常數) 是根據 strings.xml 檔案的內容產生。每個字串訊息都會取得專屬 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 到適當訊息的對應。