攻城獅
Not a programmer 不工程的攻城獅

Not a programmer 不工程的攻城獅

知識閱讀 - Meta 如何處理 APP 本地化語言

攻城獅's photo
攻城獅
·May 14, 2022·

Subscribe to my newsletter and never miss my upcoming articles

Facebook 約有 57% Android 和 49% iOS 的使用者使用非英語之外的語言,意味著有相當多不同的語言的語法、名詞、複數等語句需要處理。使用語言包的方式,讓 iOS APP 減少了約 16.6MB 的大小。

Why do we need language packs?

Android 和 iOS 原生平台提供的本地化框架存在兩個問題

翻譯的精確性

兩個原生平台只能提供簡單的文本翻譯,很難再沒有樣板程式碼的情況下建立符合語意的翻譯。為此開發了自己的 API 和框架-- FBT,此框架目前支援 PHP、Hack、JavaScript 和 React Native。

FBT API

fbt(
   'Write on ' +
     fbt.pronoun('possessive', gender) +
     ' timeline...',
   'Placeholder text for inline composer',
 )

Android

 <string name="title_male" description="Placeholder text for inline composer">Write on his timeline...</string>
 <string name="title_female" description="Placeholder text for inline composer">Write on her timeline...</string>
 <string name="title_other" description="Placeholder text for inline composer">Write on their timeline...</string>

iOS

if (gender == Male) {
  FBLocalizedString("Write on his timeline...", "Placeholder text for inline composer");
} else if (gender == Female) {
  FBLocalizedString("Write on her timeline...", "Placeholder text for inline composer");
} else {
  FBLocalizedString("Write on their timeline...", "Placeholder text for inline composer");
}

語言支援和應用程式大小

在送審應用程式到平台商店前,所有字串都應翻譯成適當的格式並內建在應用程式中。但是隨著支援的語言越多其容量也越大,這對於許多不是待在有足夠網路服務的地方,會造成人們不願意升級版本。而從應用程式中剔除掉這些翻譯文件,發現到可以節省 16.6 MB 的大小。所以最初在 Android 上改換成下載語言包的方式。

End-to-end flow

語言包框架有兩大階段:分別發生在 mobile build 發布之前和之後。

在構建應用程式二進製檔案時,為構建中使用的 FBT 字串構建語言包。要採用新的字串和最近的翻譯更新,每個發布版本必須在提交到應用商店之前與我們雲端儲存空間中的語言包相關聯。當發布版本發佈在應用商店中時,語言包將從雲存儲下載或在本地化初始化期間從磁盤載入,作為應用啟動的一部分。客戶端將訪問語言包並將翻譯資料下載到記憶體中。之後,每當調用字串 API 時,都會從解析的資料中找尋它的翻譯。

LangPack_1.webp

建立語言包

每天都會在每個 Meta 應用程式的程式碼庫中建立或修改許多本機字串。我們構建了一個自動化系統來提取字串、從資料庫中收集翻譯並將它們捆綁在一起。對於每個發布的版本,都有一個構建步驟,用於根據最新版本中的字串為所有受支援的語言環境構建語言包,並將語言包上傳到雲端儲存空間。

LangPack_2.webp

建立 FBT 字串

FBT 是一個開源本地化框架,它提供了一種更有效的方式來定義內容以實現靈活和高品質的本地化。這是一個簡單的函數包裝器,原本是英文文本。該框架旨在便於工程師使用。

Example: simple text

fbt('Hello, World', 'a simple example', {project:"foo"})

// Translation: "Hello, World"

Example: complex string

fbt("View " +
     fbt.name('user', shortName, gender) +
       "'s Timeline - " +
       fbt.plural('follower', count, {many: 'followers', showCount: 'yes'}),
     'In user composer, gives details about the person to who the post is directed.',
   )

// Translation: "View Lu's timeline - 1 follower"
// Translation: "View Lu's timeline - 5 follower"

Extraction 提取

搭配以下兩個:

  • id:由 text + description + relevant metadata 組成的雜湊鍵
  • text_or_table:多層級查詢表,每個級別都根據 FBT 定義查找值 callsite

Example: extracted object of complex string in above section

[
  {
    "description": "In user composer, gives details about the person to who the post is directed.",
    "id": "4sIjkwerw",
    "text_or_table": {
      "UNKNOWN": {
        "ONE": "View {user}'s Timeline - 1 follower",
        "OTHER": "View {user}'s Timeline - {number} followers"
      }
    },
    "variations": [
      {
        "type": "GENDER",
        "token": "user"
      },
      {
        "type": "NUMBER",
        "token": "number"
      }
    ],
    "tokens": {
      "name": "%1$@",
      "number": "%2$ld"
    }
  }
]

後續的改進

對於無法下載語言包或網路不穩定的人,會進行兩項改進

  • 在新版本安裝完成之前,在現有的 Client 端中提取已下載的語言包
  • 因為大多數的翻譯不會常常需要變動,在本地初始化中,如果目標的語言包不能用,會載入一個較舊的版本。

Reference

Did you find this article valuable?

Support 攻城獅 by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this

Impressum

As smiple as possible