Webnovel 國際化實踐

本文做者:張卓html

原創聲明:本文爲閱文前端團隊 YFE 成員出品,請尊重原創,轉載請聯繫公衆號 ( id: yuewen_YFE ) 獲取受權,並註明做者、出處和連接。前端

前言

Webnovel(起點海外項目)在今年開始了國際化的腳步,在剛剛上線的版本當中加入了對印尼、馬來西亞和菲律賓語言及內容的支持。在作國際化的過程當中,咱們遇到了很多問題,這篇文章就重點分享一下這些問題以及它們的解決方案。react

國際化和本地化

在開始以前,咱們先明確兩個概念: 國際化和本地化。國際化(i18n) 是一個設計和準備應用程序的過程,使其能用於不一樣的語言。 而 本地化(l10n) 是一個把國際化的應用針對部分區域翻譯成特定語言的過程。這篇文章的標題是「國際化」實踐,因此重點講的也是如何準備應用程序讓其可以進行本地化。git

須要解決的問題

將一個網站進行多語言化看似是一件簡單的事情,在大多數狀況下的確是的,只是將一個字符串映射到另外一個字符串的過程,可是起點海外做爲一款有追求的產品,咱們固然不會採用這麼簡單的方式。要把國際化這件事情作好,那就會遇到許多問題,例如單複數、富文本等。下面的部分會介紹一些常見的多語言問題以及一些通用的解決方案:github

單複數問題

在咱們中文當中,沒有單複數的概念,「一小時」和「兩小時」中的「小時」是同樣的,但在其它許多語言當中,不一樣數量的形式,有着不一樣的規則,英文中有單數和複數兩種規則,如「1 hour」 和 」2 hours」,而有的語言可能有更多。在一些語言中,基數和序數的規則可能也是不一樣的,如英文的「1st」,「2nd」,「3rd」,「4th」。web

Unicode 標準已經將世界上絕大部分語言的單複數規則進行了歸類,總結下來最多隻有 6 種規則,分別是:瀏覽器

  • zero
  • one
  • two
  • few
  • many (若是有一個單獨的分類的話,也用於分數)
  • other (必須,若是語言只有一個單一形式也使用)

阿拉伯語基數的單複數規則

如英文中,基數只有 one (1 hour)和 other (0 hours,2.5 hours)兩種規則,序數則有 one(1st,11st…),two(2nd),few(3rd),和 other(4th)四種規則。bash

規則是有了,咱們如何在實際的應用中使用呢?一種比較通用的方式是使用 ICU MessageFormat。ICU MessageFormat 是一種語法格式,經過 {key} 的形式來定義變量;經過一些關鍵詞來幫助咱們更方便的處理不一樣語言中的一些複雜狀況,例如使用 {key, plural, matches}來處理單複數規則:前端框架

You have {itemCount, plural,
    =0 {no items}
    one {1 item}
    other {{itemCount} items}
}.
複製代碼

ICU MessageFormat 不只能夠方便單複數狀況的使用,一樣能夠用於日期、性別以及其它複雜狀況,而且已經擁有了很是普遍的使用,不只絕大部分 JavaScript 的多語言庫使用了它,在其餘語言例如 Java 和 PHP 中也一樣內置了這套規則。框架

日期、數字以及貨幣 不一樣語言、國家和地區在表示日期和數字時也會有一些差別,如美國習慣「月/日/年」的形式來表示日期,而一樣使用英語的英國卻更習慣「日/月/年」。而貨幣就更不用說了,符號首先不一樣,如人民幣和日元的 「¥」 以及歐元的 「€」,而且它們放置的位置可能也不相同,日元習慣將貨幣符號放在數字前面,而歐元偏偏相反。

做爲開發者,咱們幾乎不可能去一一瞭解這些差別,好在 ECMAScript Internationalization API 提供了4個方法幫助咱們解決上面的問題:

  • Intl.Collator
  • Intl.DateTimeFormat
  • Intl.NumberFormat
  • Intl.PluralRules

Intl.Collator 並不經常使用,主要是用於語言敏感字符串比較的;Intl.DateTimeFormat 能夠幫助咱們根據不一樣地區語言格式化時間和日期;Intl.NumberFormat 則用來格式化數字和貨幣;而Intl.PluralRules 用於判斷單複數,能夠告訴咱們指定數量在某種語言下的分類(如 「one」,「other」)。其中前三個 API 已經相對穩定,瀏覽器也有較好的支持,DateTimeFormatNumberFormat 也有 polyfill 來讓咱們有更普遍的使用範圍,如 Node 和 React Native 環境中;PluralRules 還處在草案階段,瀏覽器支持性也比較差,不建議直接使用。

另外,咱們看到瀏覽器尤爲是 Chrome 對國際化的支持正在逐漸加大,除了上面已經進入標準的 4 個 API 外,Chrome 分別在 71 和 72 版本支持了 Intl.RelativeTimeFormatIntl.ListFormat 兩個 API,其中 Intl.RelativeTimeFormat 用來格式化相對時間,相似 Moment.js 中的功能,例如:

const rtf = new Intl.RelativeTimeFormat('en');
    
rtf.format(3.14, 'second');
// → 'in 3.14 seconds'
    
rtf.format(-15, 'minute');
// → '15 minutes ago'
複製代碼

Intl.ListFormat 用來格式化列表,例如:

const lf = new Intl.ListFormat('zh');
    
lf.format(['永鋒', '新宇']);
// → '永鋒和新宇'
複製代碼

這些 API 都遠比上面展現的例子強大,具體的用法能夠參考 MDN 和 Google Developers 官網,也相信從此在 Web 上進行國際化會愈來愈容易。

含義和語境

咱們知道,不管是中文仍是其它語言,一個字/詞語在不一樣場景下可能會有不一樣的含義,「About」 若是看成一個頁面的標題,表達的多是「關於/簡介」的含義,但放在一句話中就多是「大約」的意思了。

對於這個問題,咱們能夠經過提供給譯者更多的信息來解決這個問題。可經過文字描述幫助譯者來了解語境,發送截圖等方式來確保譯者可以準確的翻譯。

誰來翻譯

誰來翻譯,看起來彷佛不是一個問題,但它決定着咱們整個翻譯的流程,咱們須要在進行多語言時儘早肯定。通常來講,多是由專業翻譯或者用戶/志願者來翻譯,這兩種方式各有優劣:

  • 由專業翻譯進行翻譯。專業翻譯基本能夠保證較高的質量和效率,主要問題是成本較高。
  • 由用戶/志願者進行翻譯。不少開源項目都採用來這種方式。Twitter 也採用了這種方式,專門搭建了一個翻譯平臺來讓用戶更好的進行翻譯,在短短一年的時間內有超過 40 萬的志願者幫助其進行了翻譯,上線了 21 種語言。這種方式成本低,而且因爲可能參與的人數衆多,經過多人 review 等方式能夠保證翻譯質量;惟一須要擔憂的是翻譯時間不可控。而咱們通常也不須要本身搭建一套平臺,能夠選擇已有的成熟的商業平臺如 crowdin.com 或者開源的平臺如 Mozilla 的 pontoon。

除了上面提到的多語言問題,咱們在國際化的過程當中可能還要面臨多方合做、分國際/地區運營等其它類型的諸多問題,這裏篇幅有限,就不一一討論了。

解決方案

在作多語言的 Web 應用時,一種比較通用的方式是:將不一樣語言的字符放在不一樣的 JSON 或其它形式的文件當中,而後獲取用戶傾向的語言,加載對應語言的字符文件,而後在應用中展現便可。

在這種方式下,須要解決的最大問題是一些比較複雜的狀況,例如上文提到的單複數。在上文種咱們也提到了能夠經過 ICU MessageFormat 來解決這個問題,具體的作法是將 ICU MessageFormat 解析成 AST 而後轉化爲函數,在應用中傳遞對應參數到對應函數便可。

上面的方式還有一些細節值得討論,篇幅緣由這裏就不講了,接下來咱們看一下相對比較成熟的基於主流框架的 i18n 解決方案。

React Intl

React Intl 是雅虎開源的基於 React 的國際化解決方案。遵循 BCP 47 和 Unicode CLDR 標準,支持 ICU Message Format,並支持日期、時間和數字等的國際化。

React Intl 經過組件的形式實現多語言:

<FormattedMessage
  id="welcome"
  defaultMessage={`Hello {name}, you have {unreadCount, number} {unreadCount, plural,
    one {message}
    other {messages}
  }`}
  values={{name: <b>{name}</b>, unreadCount}}
/>
複製代碼

更具體的使用方式能夠參考它的 Github:github.com/yahoo/react…

Angular 的方案

Angular 應該是目前主流前端框架中惟一自帶 i18n 解決方案的框架,它一樣遵循 BCP 47 和 Unicode CLDR 標準,支持 ICU Message Format。

與通常的 i18n 方案不一樣,Angular 不須要提早準備一份 JSON 或其它形式的多語言映射表,只需使用 i18n 屬性來標記須要進行多語言的文本便可,例如:

<h1 i18n>Hello, webnovel</h1>
複製代碼

經過執行 ng xi18n 命令,Angular 會自動提取全部含義 i18n 屬性的字符,並生成一份 xlf 文件(xlf 是一種基於XML的交換格式,旨在標準化本地化過程當中在工具之間傳遞可本地化數據的方式),咱們能夠直接將 xlf 文件發送給譯者,譯者經過一些專門的軟件進行翻譯而後將這份文件返回給咱們。最後,咱們經過預編譯或者即時編譯的方式將多語言內容注入到應用當中便可完成所有工做。

Angular 的方案很是完善,對咱們可能不太注意的一些地方也作了支持,如咱們想對 img 標籤的 title 屬性進行多語言的話, 只需再加上 i18n-title 的屬性便可,例如: <img [src]="logo" i18n-title title="Webnovel logo" />

Webnovel 的方案

Webnovel 的多語言版本

對比了上述的幾種方案,咱們認爲目前 Angular 的方案是最爲理想。它相對完善;沒有很強的入侵性;得益於它能自動提取所需的字符串並生成 ID,使用它的便利程度也優於其餘框架;它的多語言支持預編譯和即時編譯,在性能和靈活度上都有了保證。

但比較遺憾的是 Webnovel 目前沒有使用 Angular,咱們的移動站點和 App 分別基於 React 和 React Native 構建,因爲已有的基於 react 的多語言庫 react-intl 不能很好的知足咱們的須要,咱們決定本身構建 i18n 的基礎庫,它須要作到:

  • 可以在經常使用 JavaScript 環境中使用,包括瀏覽器、 Node(服務端渲染)和 React Native
  • 遵循國際標準,支持 ICU MessageFormat
  • 支持字符模版的預編譯和即時編譯
  • 高性能,支持動態加載語言
  • 支持語言/字符串的 fallback
  • 使用成本低
  • 插件化

在有了基本目標之後,咱們開發了新的多語言庫 react-i18n:

@react-i18n/core

核心庫,經過 React 的 Context API,提供 withI18n 的高階組件以及 Message 組件來幫助應用進行多語言。其中 withI18n 將 i18n 信息傳遞給組件的 props,而 Message 組件相似於 React Intl 中的 FormattedMessage 組件,經過傳遞對應字符串的 id 和參數來直接渲染出字符。

@react-i18n/cli

命令行工具,主要提供預編譯字符模版和一些輔助功能,如

  • Excel 處理:將 Excel 轉化爲 JSON。咱們和翻譯是經過在線的 Excel 來進行信息同步。
  • 自動生成 ID:如 「hello world」 將生成 HELLO_WORLD_6f5902ac237024bdd0c176cb93063dc4 的 ID,既保證了在使用編輯器是可以經過自動補全方便輸入,也保證了惟一性。
  • 機器翻譯:經過使用 Microsoft Translator Text API 和 Google Translation API 將機器翻譯的結果直接填入表格,幫助譯者更快的進行翻譯。

react-i18n 庫已經知足了咱們的基本要求,而且已經在 Webnovel 的移動站點和 App 中運行了一段時間。因爲時間倉促, react-i18n 庫還不夠健全,咱們暫時還不能將其開源。接下來,咱們會將這個庫進行完善,儘早回饋給開源社區。

其餘須要注意的問題

遵循已有標準

遵循已有標準對於國際化來講很是重要。咱們在寫一個應用時,幾乎沒法避免與第三方合做,例如支付。在合做的過程當中,咱們若是使用相同的標準,那溝通、調試的成本將大大下降。關於國際化的標準很是多,有些也比較複雜,須要咱們耐心認真的閱讀。值得注意的是,標準並非一直不變的,例如上文中提到的 BCP 47 標準當中的地區標示,舊版中印尼的代碼是 in ,而新版中改成了 id,通常咱們應該遵循更新的標準。

更早、更多的瞭解成熟方案

對於國際化這樣已經存在很是多年的問題,必定是有成熟方案能夠借鑑的,在開始以前,去儘量多的瞭解已有方案,會讓咱們少走許多彎路。

結語

本文重點講述了基於 Web 技術的一些國際化方案以及在 Webnovel 中的實際應用。國際化一直以來都是一個很是困難的問題,因此更須要咱們長遠的去思考;國際化也不只僅是多語言,從排版佈局到文化差別,都是咱們須要考慮的,Webnovel 的國際化纔剛剛開始。

相關連接

查看更多分享,請關注閱文集團前端團隊公衆號:

相關文章
相關標籤/搜索