美文网首页architecture
前端国际化方案选择

前端国际化方案选择

作者: jsAllen | 来源:发表于2018-11-23 04:38 被阅读104次

国际化基础知识

  1. 国际化与本地化

国际化与本地化,或者说全球化,其目的是让你的站点支持多个国家和区域。其中国际化是指功能和代码设计能处理多种语言和文化习俗,能够在创建不同语言版本时,不需要重新设计源程序代码。国际化的英文单词是 Internationalization ,简称 I18N。 本地化是将站点按照特定国家、地区或语言市场的需要进行加工,使之满足特定用户对语言和文化的特殊要求。本地化的英文对应Localization,缩写为L10N。举例 Moment,其支持 setLocale 方法切换语言就是国际化,每一个 locale 的配置文件定义了具体区域时间格式就是本地化。

  1. CLDR

Unicode Common Locale Data Repository (CLDR) 是软件国际化的基石,它作为一个国际标准提供了构建国际化软件所需要的定义和数据。可参考其 json 形式

  1. ECMAScript Internationalization API

ECMA 提供的国际化 API(底层使用了 CLDR),包括

  • Intl.Collator, 排序
  • Intl.NumberFormat, 数字的格式化
  • Intl.DateTimeFormat, 日期的格式化

由于兼容性原因,你还需要 polyfill

  1. ICU

一套定义国际化语法的标准,仅仅是一种规范,不同语言有不同的语法,不同语法需要对应一种相应的解析方法,例如 javascript 里的一种 icu 语法,和其解析器:

https://github.com/messageformat/messageformat
https://github.com/messageformat/parser

  1. Format.js

FormatJS is a modular collection of JavaScript libraries for internationalization that are focused on formatting numbers, dates, and strings for displaying to people. It includes a set of core libraries that build on the JavaScript Intl built-ins and industry-wide i18n standards, plus a set of integrations for common template and component libraries.

format.js 是一套国际化的方案(实现了上面的 3 和 4 点),包括以下一些核心库:

核心库
  1. po、pot、mo 三种翻译文件格式
  • po(Portable Object)文件,是面向翻译人员的、提取于源代码的一种资源文件。当软件升级的时候,通过使用 gettext 软件包处理 po 文件,可以在一定程度上使翻译成果得以继承,减轻翻译人员的负担。文件格式示例如下:
#: ./src/js/components/PaymentManagement/OrderFilter/index.js:69
#: ./src/js/components/PaymentManagement/OrderFilter/index.js:67
msgid "请填写表 ID"
msgstr "Please fill in the table ID"

#: ./src/js/utils/helper.js:382
msgctxt "数字金额"
msgid "{count} 千万"
msgstr "{count}0 million"

#: ./src/js/components/Dashboard/StatisticsPanel/API/index.js:33
msgid "{count} 次"
msgid_plural "{count} 次"
msgstr[0] "{count} time"
msgstr[1] "{count} times"
  • pot 文件,性质和 po 一样,只不过它主要用作模板

  • mo( Machine Object)文件是面向计算机的、由 PO 文件通过 gettext 软件包编译而成的二进制文件。程序通过读取 MO 文件使自身的界面转换成用户使用的语言。|

  1. 获取翻译内容的两种方式

a. 通过 key(id)的方式

 locale.json
{   
  zh-ch: { 
     'app.helloWorld': '你好世界!' 
  }, 
  en: { 
    'app.helloWorld': 'Hello world!' 
  }
} 
 _('app.helloWorld')

b. 已默认语言的语句作为 id

// 如下以中文为默认语言
// locale.json
{
  zh-ch: {
    ‘你好世界!’: ‘你好世界!’
   },
 en: {
   ‘你好世界!’: ‘Hello world!’
  }
}
 _('你好世界!')

有时会遇到相同词语在不同语境下,有不同意思,如 “黄色”,一种表示颜色,一种表示色情,此时需要结合 context 来区分(可参考方案二的做法)

_c(‘表示颜色’, ‘黄色’)
_c(‘表示色情’,‘黄色’)

{
 zh-ch: {
   ‘表示黄色’:{
      ‘黄色’:‘黄色’
    },
   ‘表示色情’:{
      ‘黄色’:‘黄色’
    }
 },
 en: {
   ‘表示黄色’:{
      ‘黄色’:‘yellow’
    },
   ‘表示色情’:{
      ‘黄色’:‘pornographic’
    }
 }
}

国际化方案

方案一:react-intl

react-intl 是基于 react 整合了 format.js 的 一套 react 国际化方案。

This library provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.

  1. react-intl 提供 API 和组件两种形式方式使用:

  2. 加载 localeData

import {addLocaleData} from 'react-intl';
import frLocaleData from 'react-intl/locale-data/fr';
addLocaleData(frLocaleData);

The locale data added with this function supports plural and relative-time formatting features as described in Loading Locale Data.

3.Creating an I18n Context

ReactDOM.render( 
  <IntlProvider
    locale={usersLocale}
    messages={translationsForUsersLocale}
  >
    <App/>
  </IntlProvider>, 
  document.getElementById('container')
);

根据 locale 加载不同的 messages 文件,messages 格式如下:

{
  "greeting": "Hello, {name}"
}

4.格式化接口

日期:

  • formatDate
  • formatTime
  • formatRelative

数字:

  • formatNumber
  • formatPlural(This function should only be used in apps that only need to support one language. If your app supports multiple languages use formatMessage instead.)
  • formatNumber(1000); // "1,000" formatNumber(0.5, {style: 'percent'}); // "50%" - formatNumber(1000, {style: 'currency', currency: 'USD'}); // $1,000

字符串:

  • formatMessage
  • formatHTMLMessage
  • formatMessage(messages.greeting, {name: 'Eric'})

复数:

"Hello, {name}, you have {itemCount, plural, =0 {no items} one {# item} other {# items} }."

5.格式化组件

如果句子中有 html 元素,推荐使用 FormattedMessage 组件

<FormattedMessage 
  id='app.greeting' 
   description='Greeting to welcome the user to the app'  defaultMessage='Hello, {name}!'
   values={{  name: <b>Eric</b>  }}
/>

This allows messages to still be defined as a plain string without HTML — making it easier for it to be translated. At runtime, React will also optimize this by only re-rendering the variable parts of the message when they change. In the above example, if the user changed their name, React would only need to update the contents of the <b> element.

6.提供 babel-plugin-react-intl 插件,自动提取待翻译文本,前提需要先用 defineMessages 进行定义,同时生成的 json 无法和现有的翻译工具(需要 po 文件格式)整合(react-intl-gettext 工具可以将 json 转为 po 文件)

const messages = defineMessages({ 
   greeting: {
     id: 'app.home.greeting',  
     description: 'Message to greet the user.',  
     defaultMessage: 'Hello, {name}!', 
   }, 
});

formatMessage('app.home.greeting', {name: ‘xxx’})
  1. 总结
  • 提供了国际化前端转换的完整方案(即字符串、时间、日期的转换)
  • 使用麻烦,需要指定 id,而不是默认语言作为 id,比如使用 formatMessage(‘你好’)
  • 没有提供完整解决方案,即只解决了如何从翻译文件解析转换,而没有解决如何自动生成翻译文件(babel-plugin-react-intl 不完善),整合使用现有的翻译工具
  • 文档和示例不完整、不清晰

方案二:使用 node-gettext + narp 构建一套完整的翻译工具链

目前公司知晓云项目在实践的方案。

  1. 使用 node-gettext 做为转换工具
import  Gettext  from  'node-gettext' 
import swedishTranslations from  './translations/sv-SE.json'  
const gt = new  Gettext()  gt.addTranslations('sv-SE', 'messages', swedishTranslations)  
gt.setLocale('sv-SE') 
gt.gettext('The world is a funny place')

node-gettext 进提供了字符串的转换,没有提供日期、数字的转换:

GNU gettext features categories such as LC_MESSAGES, LC_NUMERIC and LC_MONETARY, but since there already is a plethora of great JavaScript libraries to deal with numbers, currencies, dates etc, node-gettext is simply targeted towards strings/phrases. You could say it just assumes the LC_MESSAGES category at all times.

另外,node-gettext 接受的是 json 文件

  1. 使用 narp 完成翻译文本自动提取,向 poeditor 或 transifex 上传待翻译文件和拉取翻译后的文件,和格式转换等功能
  • . narp push

Extraction of strings from source code (react-gettext-parser),
Merging upstream and local translations in order to support translations in branches (pot-merge)
Uploading POT resources to the translation service you use (Transifex or POEditor)

  • narp pull

Download PO translations from the translation service(丢弃掉旧的,全部用最新的拉取的)
Converting PO to JSON (gettext-parser)
Writing the JSON translations to where you want it

  1. The JSON translations are formatted for node-gettext.(使用 node gettext 转换)
  • 使用 react-gettext-parser 提取翻译字符串,生成 pot 文件
  • 调用 poeditor 或 transifex 平台 API 上传下载翻译文件
  • 使用 gettext-parser Parse and compile gettext po and mo files to/from json
  1. 总结:
  • node-gettext 仅提供字符串的转换,数字和时间等,需要自己使用第三方库来实现,这个特点见仁见智,对于想一步到位的开发者而言,需要自己找第三方库,同时每个库都要单独进行 locale 配置,稍显麻烦;而对于需要定制化,或对性能要求高的开发者,则提供了一个扩展性很强的环境。
  • node-gettext 不支持插值,需要借助第三方插件
  • node-gettext 的语法简单通用
  • narp 封装了 poeditor 和 transifex 两个平台的 API,通过命令行可以实现快速地上传和下载翻译文件,同时实现了文件自动合并功能,省去在多分支情况下,遇到冲突时要手动合并的麻烦
  • 目前 narp 会将不同的翻译文件合成一个 json 文件,但支持的语种和待翻译语句较多时,产生的文件会很大,严重影响性能。最好的做法还是,不同语言生成到不同文件中,初始加载默认语言,在根据当前区域,动态加载所需语言(这种情况下切换语言的话,需要刷新才能得到更新,这种需求也比较合理)。

该方案的操作实例,可以参考这里 https://github.com/YaoKaiLun/i18n-example

方案三:i18n-next

前面的方案二,已经基本满足了我们的业务需求,而且其相对简单,容易理解。但如果你有跟多的需求,或者你的国际化要求更高,那你可以尝试使用 i18n-next

I18next is an internationalization-framework written in and for JavaScript. But it's much more than that.
i18next goes beyond just providing the standard i18n features such as (plurals, context, interpolation, format). It provides you with a complete solution to localize your product from web to mobile and desktop.

它支持以下特性:

  • Splitting translations into multiple files. Only load translations needed.
  • There are plugins to detect languages for most environments (browser, native, server). This enables to set priority of where to detect and even enables to cache set languages over requests / visits.
  • There are endless plugins to load translation from server, filesystem, ... this backends also assert that loading gets retried on failure, or that a file does not get loaded twice and callbacks of success are only called once. Those backends can even provide an additional layer for local caching eg. in localStorage.
  • Options what to load and how to fallback depending on language.
  • Support for objects and arrays
  • Full control over management of the translations stored.
  • Rich system of events to react on changes important to your application.
  • Freedom of i18n formats - prefer ICU? Just use i18next-icu plugin.

同时,可以通过一些自动提取翻译语句和解析 PO 文件插件来实现方案二中 narp 的部分功能。

事实上,i18n-next 在 2011 的时候已经创建了。i18n-next 可以说提供了一个解决国际化问题的完整生态,除了基本的需求外,你还可以通过各种扩展,实现更多功能。而且相比 react-intl,他更活跃、更友好、更可靠一点。

其它需要考虑的国际化问题

待补充...

相关文章

  • 前端国际化方案选择

    国际化基础知识 国际化与本地化 国际化与本地化,或者说全球化,其目的是让你的站点支持多个国家和区域。其中国际化是指...

  • react项目国际化:实现自动装配方案

    该方案提供一个外挂式的前端项目国际化实现方案,可以支持由于某些原因在一开始没有支持国际化,后续在几乎不需要改造原有...

  • 【整理】前端国际化小结

    近期在做国际化的改造,做了相应的调研,简单做下项目前端国际化的小结 国际化可以分为前端国际化和后端国际化,也可以是...

  • 前端国际化优化方案

    国际化一般都是使用 i18n 这个库,在开发过程中需要对中英文两个文件内容中添加对应中英文文本,业务代码中使用可读...

  • 国际化(i18n) 地区对照语言码

    如果您是来找前端国际化解决方案的 那么我推荐一篇https://github.com/OneWayTech/i1...

  • 此构建版本无效解决方案.md

    1、错误的info.plist国际化方案 2、导致的结果 3、正确的国际化方案 command + N 创建 In...

  • vue3中使用element-plus的一些坑

    一、国际化问题 日期选择组件 el-date-picker无法显示中文问题 1、官网解决方案[https://el...

  • jQuery实现资源国际化

    jQuery实现资源国际化 1、jQuery之前端国际化jQuery.i18n.properties 1. jQu...

  • SpringBoot(三)动态数据源切换

      最近有一个项目国际化的需求,解决方案一般是这样的:WEB网站国际化的一种解决方案。  简单来说,国际化一方面需...

  • 一款基于vue的国籍地区选择器

    大多数地区选择器仅支持国内区域,因此封装了一套全球国家的地区选择器,方便大家在前端国际化时使用。 该插件地址:ht...

网友评论

    本文标题:前端国际化方案选择

    本文链接:https://www.haomeiwen.com/subject/qqfdqqtx.html