详解基于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 DIV撑大让滚动条滚到最底部代码
Jun 06 Javascript
JQuery中serialize()、serializeArray()和param()方法示例介绍
Jul 31 Javascript
AngularJs实现ng1.3+表单验证
Dec 10 Javascript
JavaScript中在光标处插入添加文本标签节点的详细方法
Mar 22 Javascript
node.js中使用Export和Import的方法
Sep 18 Javascript
jquery实现图片跟随鼠标的实例
Oct 17 jQuery
Vue中多元素过渡特效的解决方案
Feb 05 Javascript
JS正则表达式验证密码强度
Mar 18 Javascript
js校验开始时间和结束时间
May 26 Javascript
vue使用better-scroll实现滑动以及左右联动
Jun 30 Javascript
vue实现简单的登录弹出框
Oct 26 Javascript
node使用async_hooks模块进行请求追踪
Jan 28 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 UTF8中文字符截断函数代码
2012/09/11 PHP
PHP输出Excel PHPExcel的方法
2018/07/26 PHP
Yii框架Session与Cookie使用方法示例
2019/10/14 PHP
最常用的12种设计模式小结
2011/08/09 Javascript
分享20多个很棒的jQuery 文件上传插件或教程
2011/09/04 Javascript
jQuery结合PHP+MySQL实现二级联动下拉列表[实例]
2011/11/15 Javascript
js阻止默认浏览器行为与冒泡行为的实现代码
2016/05/15 Javascript
zTree实现节点修改的实时刷新功能
2017/03/20 Javascript
彻底搞懂JavaScript中的apply和call方法(必看)
2017/09/18 Javascript
template.js前端模板引擎使用详解
2017/10/10 Javascript
利用node实现一个批量重命名文件的函数
2017/12/21 Javascript
Vue props用法详解(小结)
2018/07/03 Javascript
微信小程序中使用wxss加载图片并实现动画效果
2018/08/13 Javascript
说说如何在Vue.js中实现数字输入组件的方法
2019/01/08 Javascript
vue项目打包上传github并制作预览链接(pages)
2019/04/19 Javascript
jQuery 隐藏/显示效果函数用法实例分析
2020/05/20 jQuery
Python实现的基数排序算法原理与用法实例分析
2017/11/23 Python
Python使用win32com模块实现数据库表结构自动生成word表格的方法
2018/07/17 Python
python实现将多个文件分配到多个文件夹的方法
2019/01/07 Python
python+ffmpeg批量去视频开头的方法
2019/01/09 Python
Pytorch 抽取vgg各层并进行定制化处理的方法
2019/08/20 Python
Django实现从数据库中获取到的数据转换为dict
2020/03/27 Python
Python接口测试数据库封装实现原理
2020/05/09 Python
python 基于Apscheduler实现定时任务
2020/12/15 Python
详解如何用canvas画一个微笑的表情
2019/03/14 HTML / CSS
解析浏览器的一些“滚动”行为鉴赏
2019/09/16 HTML / CSS
html5 canvas 实现光线沿不规则路径运动
2020/04/20 HTML / CSS
Ticketmaster德国票务网站:购买音乐会和体育等门票
2016/11/14 全球购物
沙特阿拉伯电子产品和家用电器购物网站:Black Box
2019/07/24 全球购物
htmlentities() 和 htmlspecialchars()有什么区别
2015/07/01 面试题
体育教育专业毕业生自荐信
2013/11/15 职场文书
工作决心书范文
2014/03/11 职场文书
操行评语大全
2014/04/30 职场文书
党的群众教育实践活动实施方案
2014/06/12 职场文书
党的群众路线教育实践活动对照检查材料(教师)
2014/09/24 职场文书
怎样写好演讲稿题目?
2019/08/21 职场文书