让多语言开发变得简单点

实现方式大概两种:

1、key
使用方式:

在资源文件内定义key,如果原语言是中文,key通常是一个带含义的英文或者拼音;变态一点的就是xxxx1111,xxxx1112(很荣幸,我们的项目很变态 /(ㄒoㄒ)/~~),然后在代码中通过 Resource.lang.xxxx1111 或者 lang.xxxx1111(js文件内) 方式引用;

问题:
  1. key名难定义,就算定义得好也不见得大家都看得懂;
  2. 修改不方便(打开代码,看到的都是lang.xxxx,我要修改的内容在哪,马勒戈壁)。如果开发直接修改资源文件,那么每个语言的都要修改,可是日语只会 “亚麻得”;
  3. 如果项目是先按某个语言开发好功能,后期补翻译的,那就更悲催了,研发需要重新回来替换一次;

2、gettext
先了解三种文件类型:
  • .po文件

PO是Portable Object(可移植对象)的缩写形式,它是面向翻译人员的、提取于源代码的一种资源文件;

  • .pot文件

POT是Portable Object Template(可移植对象模板)的缩写形式,是一种模板文件,其实质与.po文件一样,其中包含了从源代码中提取所有的翻译字符串的列表,主要提供给翻译人员使用,可以通过.pot文件生成.po文件;

  • .mo文件

MO是Machine Object(机器对象)的缩写形式,它是面向计算机的、由.po文件通过GNU gettext工具包编译而成的二进制文件,应用程序通过读取.mo文件使自身的界面转换成用户使用的语言;


使用方式:

在代码文件内写成:_(“明道”),通过命令提取到pot文件中,通过poedit等工具根据pot文件创建其他语言对于的po文件

1
2
3
#: src/test.cs:36
msgid "明道"
msgstr ""
问题:

gettext的方式的主要问题是因为key的唯一性会导致多义词、单复数难处理,但对于我们目前来说还是可以接受的,所以最终决定换成gettext方式


如果是基于.Net Framework 的项目:

在需要支持翻译的项目nuget安装i18N:i18N详细介绍

Install-Package i18N

在使用文案的地方都按照以下方式来写:
1
2
3
4
5
1. [[[明道]]]   // 没有参数

2. [[[我在 %0 上班 ||| 明道 ]]] // 带参数

3. string.Format("[[[welcome %1, today is %0|||{0}|||{1}]]]", day, name) //string.Format 方式
web.config 配置:
1
2
3
4
5
6
7
8
9
<appSettings>
<!-- 定义资源文件名称(.po)-->
<add key="i18n.LocaleFilename" value="mdTranslation" />
</appSettings>
<system.webServer>
<modules>
<add name="i18n.LocalizingModule" type="i18n.LocalizingModule, i18n" />
</modules>
</system.webServer>
Application_Start 配置:
1
2
3
4
5
6
7
8
9
10
11
void Application_Start(object sender, EventArgs e)
{
// 默认语言(简体中文)
i18n.LocalizedApplication.Current.DefaultLanguage = "zh-Hans";
// url里面是否带语言参数 http://www.xxx.com/en, Void 表示不带语言参数(可按项目的实际要求,非Voide方式可能存在文件引用路径问题)
i18n.UrlLocalizer.UrlLocalizationScheme = i18n.UrlLocalizationScheme.Void;
// 哪些ContentType文件需求支持多语言解析
i18n.LocalizedApplication.Current.ContentTypesToLocalize = new Regex(@"^(?:(?:(?:text|application)/(?:plain|html|xml|json|x-json))(?:\s*;.*)?)$");
//如果开发的项目没有考虑时区问题,需要设置成null,不然dateTime类型会按照时区重新计算
i18n.LocalizedApplication.Current.SetPrincipalAppLanguageForRequestHandlers = null;
}
执行命令生成pot和po文件:
1
"$(TargetDir)i18n.PostBuild.exe" "$(ProjectDir)\web.config"
得到pot和po文件之后,可以使用以下方式来完成多语言的翻译:
1. poedit // 本地翻译软件,可以根据pot模板创建各语言的po文件,也可以直接对现有的po文件进行翻译

2. transifex // 在线翻译平台,可以导入pot模板,自动创建多个语言来在线翻译。也可以将现有的po文件导入

3. crowdin // 在线翻译平台(类似transifex)
我们使用的nuget安装的i18N在多语言实现上并没有用到mo文件,所以可以不生成mo文件



对于所有访问资源都在所部署的IIS服务器的情况,以上配置基本就可以了


但是对于引用CDN的文件来说,以上的方式就不合适了,所以我们弃用i18N自带的pot模板生成工具i18n.PostBuild,自己实现。

大概思路:

  1. 如果资源文件在所部署的IIS服务器上,依然使用以上的试试;

  2. 如果资源文件在CDN上(对于我们来说要翻译部分基本是 js、tpl、css、image),所以只需要处理js、tpl里面的多语言即可;

  3. 类似[[[xxxx]]]这样的语法,我们定义一个js里面的语法,_l(‘xxxx’);

    1
    2
    3
    _l("明道")

    _l("我在 %0 上班","明道")
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    window._l = function () {
    var args = arguments;
    if (args) {
    var key = args[0];
    var content = key;
    if (typeof mdTranslation != 'undefined' && mdTranslation[key])
    content = mdTranslation[key];
    // 含0% 1% 的内容替换
    if (args.length > 1) {
    for (var i = 1; i < args.length; i++) {
    content = content.replace(new RegExp('%' + (i - 1), 'g'), args[i]);
    }
    }
    return content;
    }
    return '';
    };
  4. 根据[[[xxxx]]] 和 _l(‘xxxx’) 语法我们通过脚本来提取所有的key,生成pot文件,大家可以注意文件内有一行 #: Disabled references:1,这里其实是有讲究的,我的方式是出于自动生成方便,大家有兴趣可以深入了解一下 gettext;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    msgid ""
    msgstr ""
    "Project-Id-Version: \n"
    "POT-Creation-Date: 2017-05-17 15:12:04+08:00\n"
    "MIME-Version: 1.0\n"
    "Content-Type: text/plain; charset=utf-8\n"
    "Content-Transfer-Encoding: 8bit\n"
    "X-Generator: i18n.POTGenerator\n"

    #: Disabled references:1
    msgid "明道"
    msgstr ""

    #: Disabled references:1
    msgid "我在 %0 上班"
    msgstr ""
  5. 将pot模板导入到 transifex/crowdin 中,指定这个模板需要翻译出哪些语言,在线翻译完成后下载po文件;

    crowdin.png

    po.png

  6. 根据po文件生成js文件,把js引入到项目下就可以通过 _l(‘xxxx’) 的方式使用了;

    local.png

    注意:如果使用第三方翻译平台,基本上都是按词数或者条数来收费的。为了减少一些成本,对于已经翻译好的内容,其实可以不用存储在翻译平台,翻译好后下载po文件后就可以把翻译平台上的内容清空。之后再导入新的pot文件,所以对pot的生成就要做一些处理,我们希望是增量式的。

    1. 提取站点中所有有效的key

    2. 清理pot和po文件中无效的key

    3. 对比提取出来的key与现有翻译文件(已经清理过)中的key

    4. 生成增量key的pot文件

    5. 导入只含增量key的pot文件,完成翻译

    6. 从翻译平台下载po文件,修改本地的pot和po文件(增量部分copy追加到文件中)

    7. 重新更新po文件生成js文件

      以上的实现我们是通过gulp来完成的,具体实现方式可以根据情况而定。

参考链接:

如果对你有帮助就好