让多语言开发变得简单点

实现方式大概两种:

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 来完成的,具体实现方式可以根据情况而定。

参考链接:

如果对你有帮助就好