详解基于webpack&gettext的前端多语言方案


Posted in Javascript onJanuary 29, 2019

gettext 是GNU 提供的一套 国际化与本地化 处理的相关函数库。大多数语言都有对应的gettext实现。本文主要使用jed 来实现gettext 一系列方法对应的功能。

pot/po文件

  • pot文件 是po文件的模板文件,一般是通过 xgettext 程序生成出来的。
  • po文件 是根据pot文件通过msginit程序,设置对应的国家语言生成用于填写实际翻译内容的文件。

xgettext/msginit/msgmerge

  • xgettext 程序可以扫描指定的代码文件,取出其中gettext部分的内容生成对应的pot文件。
  • msginit 根据对应的pot文件生成对应语言版本用于实际翻译的po文件。
  • msgmerge 如果对应语言版本的po文件存在的话,则需要使用msgmerge方式把pot文件中新加入的一些msgid合并到po文件当中。

多语言支持流程

安装gettext

brew install gettext
brew link gettext

langs-loader 加载语言文件

  • 安装
npm install git@github.com:ezbuy/langs-loader.git --save-dev
  • 配置

需要修改webpack.config.js文件在module->rules(webpack 2.0)下面添加loader规则:

{
  test: /\.pot$/i,
  use: [
    "json",
    {
      loader: 'langs',
      options: {isLoadAll: isDev,format:"jed1.x", "fallback-to-msgid":true, code:langCode}
    }
  ]
}
  • isLoadAll

isLoadAll表示会把所有语言版本的文件通过po2json转换成json,然后组合成一个json作为数据传给引用的地方。

  • code

code选项一般需要在代码发布时指定,不同语言版本打包时通过传入不同的code区分不同语言版本的代码打包。

代码中使用gettext系列函数

各端开发时使用各自实现的gettext方法去包装需要实现多语言的文案。使用gettext函数包装后, langs-util 就能知道代码中有需要多语言翻译的地方,并可以通过 langs-util 生成相关的pot及po文件。gettext方法底层我们使用jed 来实现其对应功能。

import toi18n from "common/i18n";

const _ = toi18n(require("langs/index.pot"));

const hiStr = _.gettext("Hi!");

const num = Math.ceil(Math.random() * 2);

const numStr = _.ngettext("item", "items", num);

// I like your red shirt.
console.log(_.sprintf( "I like your %1$s %2$s.", 'red', 'shirt'));
  • gettext

一般使用 gettext 方法包装一个需要翻译的字符串。

  • ngettext

可以用于单复数处理,第一个参数传入单数情况下的翻译,第二个参数传入复数情况下的翻译,第三个传入实际需要执行判断的数据。

  • sprintf

Jed内置提供了sprintf的支持,它是使用javascript-sprintf的sprintf函数的实现。

通过gettext 一系列函数的配合,使用 langs-util 进行生成处理后就能生成对应的pot及po文件。

langs-util gen -i src/
# langs/index.pot

msgid "Hi!"
msgstr ""

msgid "item"
msgid_plural "items"
msgstr[0] ""
msgstr[1] ""
# langs/index.th.po

msgid "Hi"
msgstr ""

msgid "item"
msgid_plural "items"
msgstr[0] ""
msgstr[1] ""

把生成的文件交由对应的翻译人员完成翻译就可以重新放回到文件夹当中替换目标po文件。

打包&发布

各端打包方式都会有相应的差异,最终目的无非就是使线上代码可以按照不同语言的版本加载不同的语言文件。对于 后台 等系统则可以使用isLoadAll的方式把所有语言版本的文件都加载进来,通过i18n的包装函数去从数据源中拿出不同国家的多语言数据文件。

cross-env LANG_CODE=th

移动端发布使用环境变量 LANG_CODE 来区分要编译成的语言版本。

const langCode = process.env.LANG_CODE

{
  test: /\.pot$/i,
  use: [
    "json",
    {
      loader: 'langs',
      options: {isLoadAll: isDev,format:"jed1.x", "fallback-to-msgid":true, code:langCode}
    }
  ]
}

生成完所有语言语言版本的静态文件后,需要让浏览器能够运行不同语言版本的js文件。一种方式可以通过后台程序读出语言版本,再把正确语言版本的js路径输出到html当中。另外一种方式可以新建一个多语言loader的文件,通过前端js代码动态插入正确语言版本的js代码(文件打包的时候需要把各语言版本的js文件路径输出到页面之中)。

import {languageCode} from "common/constant";

const loadScript = (path: string) => {
  const script = window.document.createElement("script");
  script.setAttribute("src", path);
  window.document.body.appendChild(script);
  return new Promise((resolve, reject) => {
    script.onload = resolve;
    script.onerror = reject;
  });
};

const {mainFilePath} = window;

if (typeof mainFilePath === "string") {
  loadScript(mainFilePath);
}else if (typeof mainFilePath !== "string") {
  loadScript(mainFilePath[languageCode] );
}

langs-loader 实现

在loader代码中拿到require文件的实际路径,如果存在code选项的话则根据pot文件找到对应code的po文件,然后使用po2json转换为对应格式的json文件,最后再返回给引用的地方。如果存在isLoadAll选项并且isLoadAll选项为true的话,则会load 同名pot文件的所有对应语言版本的po文件,然后再通过po2json转换组合成一个json文件,再返回出去。

langs-util 实现

langs-util主要整合了xgettext/msginit/msgmerge等方法。传入所需要解析的文件或文件夹,生成对应的po及pot文件。

const genPoFiles = (inputFilePaths: string | string[], potFilePath: string, langs: string[]) => {
  const potDirName = dirname(potFilePath);
  const filename = basename(potFilePath, extname(potFilePath));
  const filePaths = typeof inputFilePaths === "string" ? inputFilePaths : inputFilePaths.join(" ");
  execOnlyErrorOutput(`xgettext --language=JavaScript --add-comments --sort-output --from-code=UTF-8 --no-location --msgid-bugs-address=wanglin@ezbuy.com -o ${potFilePath} ${filePaths}`);

  checkFileExists(potFilePath).then((ifPotFileExists) => {
    if (ifPotFileExists) {
      console.log(green(potFilePath));
      writeFileSync(potFilePath, readFileSync(potFilePath).toString().replace("charset=CHARSET", "charset=UTF-8"));
      langs.forEach((lang) => {
        const poFilePath = join(potDirName, `${filename}${lang === "" ? "" : `.${lang}` }.po`);
        checkFileExists(poFilePath).then((ifExists) => {
          if (ifExists) {
            execOnlyErrorOutput(`msgmerge --output-file=${poFilePath} ${poFilePath} ${potFilePath}`);
          }else {
            execOnlyErrorOutput(`msginit --no-translator --input=${potFilePath} --locale=${lang} --output=${poFilePath}`);
          }
        });
      });
    }
  });
};

生成po文件时,需要传入需要给xgettext解析的文件列表以及目标pot文件路径还有生成的多语言版本种类。xgettext会根据传入的解析文件生成新的pot文件,如果生成成功则开始生成对应语言版本的po文件,如果对应的po文件存在则使用msgmerge更新po文件,如果不存在则使用msginit创建新的po文件。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
解决jquery的datepicker的本地化以及Today问题
May 23 Javascript
jquery索引在使用中的一些困惑
Oct 24 Javascript
下拉列表select 由左边框移动到右边示例
Dec 04 Javascript
用jQuery实现的智能隐藏、滑动效果的返回顶部代码
Mar 18 Javascript
移除AngularJS下URL中的#字符的方法
Jun 19 Javascript
JS代码随机生成姓名、手机号、身份证号、银行卡号
Apr 27 Javascript
vue.js加载新的内容(实例代码)
Jun 01 Javascript
Three.js基础学习之场景对象
Sep 27 Javascript
微信小程序methods中定义的方法互相调用的实例代码
Aug 07 Javascript
JavaScript错误处理操作实例详解
Jan 04 Javascript
记录一次开发微信网页分享的步骤
May 07 Javascript
JS前端轻量fabric.js系列物体基类
Aug 05 Javascript
vue实现父子组件之间的通信以及兄弟组件的通信功能示例
Jan 29 #Javascript
vue实现的下拉框功能示例
Jan 29 #Javascript
angular4中引入echarts的方法示例
Jan 29 #Javascript
微信小程序实现炫酷的弹出式菜单特效
Jan 28 #Javascript
微信小程序实现页面浮动导航
Jan 28 #Javascript
微信小程序顶部导航栏滑动tab效果
Jan 28 #Javascript
详解基于node.js的脚手架工具开发经历
Jan 28 #Javascript
You might like
php连接数据库代码应用分析
2011/05/29 PHP
PHP版网站缓存加快打开速度的方法分享
2012/06/03 PHP
Session 失效的原因汇总及解决丢失办法
2015/09/30 PHP
深入讲解PHP的Yii框架中的属性(Property)
2016/03/18 PHP
TP5框架实现一次选择多张图片并预览的方法示例
2020/04/04 PHP
JS文本框追加多个下拉框的值的简单实例
2013/07/12 Javascript
超精准的javascript验证身份证号的具体实现方法
2015/11/18 Javascript
JQuery移动页面开发之屏幕方向改变与滚屏的实现
2015/12/03 Javascript
Javascript+CSS3实现进度条效果
2016/10/28 Javascript
通过sails和阿里大于实现短信验证
2017/01/04 Javascript
JS使用插件cryptojs进行加密解密数据实例
2017/05/11 Javascript
jQuery制作input提示内容(兼容IE8以上)
2017/07/05 jQuery
vue.js todolist实现代码
2017/10/29 Javascript
vue2.0 实现页面导航提示引导的方法
2018/03/13 Javascript
Angular动态绑定样式及改变UI框架样式的方法小结
2018/09/03 Javascript
Vue 处理表单input单行文本框的实例代码
2019/05/09 Javascript
关于angular浏览器兼容性问题的解决方案
2020/07/26 Javascript
微信小程序input抖动问题的修复方法
2021/03/03 Javascript
[01:15:45]DOTA2上海特级锦标赛B组小组赛#1 Alliance VS Spirit第一局
2016/02/26 DOTA
[49:08]FNATIC vs Infamous 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
删除目录下相同文件的python代码(逐级优化)
2012/05/25 Python
仅用50行Python代码实现一个简单的代理服务器
2015/04/08 Python
Python的Flask框架应用调用Redis队列数据的方法
2016/06/06 Python
给你选择Python语言实现机器学习算法的三大理由
2017/11/15 Python
如何在python开发工具PyCharm中搭建QtPy环境(教程详解)
2020/02/04 Python
解决Python中导入自己写的类,被划红线,但不影响执行的问题
2020/07/13 Python
美国汽车性能部件和赛车零件网站:Vivid Racing
2018/03/27 全球购物
New delete 与malloc free 的联系与区别
2013/02/04 面试题
幼儿园五一活动方案
2014/02/07 职场文书
员工拓展培训方案
2014/02/15 职场文书
软环境建设心得体会
2014/09/09 职场文书
2015年中个人总结范文
2015/03/10 职场文书
2015年大学团支部工作总结
2015/05/13 职场文书
JS 4个超级实用的小技巧 提升开发效率
2021/10/05 Javascript
node快速搭建后台的实现步骤
2022/02/18 NodeJs
MongoDB使用场景总结
2022/02/24 MongoDB