实现方式大概两种:
1、key
使用方式:
在资源文件内定义 key,如果原语言是中文,key 通常是一个带含义的英文或者拼音;变态一点的就是 xxxx1111,xxxx1112(很荣幸,我们的项目很变态 /(ㄒoㄒ)/~~),然后在代码中通过 Resource.lang.xxxx1111 或者 lang.xxxx1111(js文件内) 方式引用;
问题:
- key 名难定义,就算定义得好也不见得大家都看得懂;
- 修改不方便(打开代码,看到的都是 lang.xxxx,我要修改的内容在哪)。如果开发直接修改资源文件,那么每个语言的都要修改,可是日语只会 "亚麻得";
- 如果项目是先按某个语言开发好功能,后期补翻译的,那就更悲催了,研发需要重新回来替换一次;
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 文件
#: src/test.cs:36
msgid "明道"
msgstr ""
问题:
gettext 的方式的主要问题是因为 key 的唯一性会导致多义词、单复数难处理,但对于我们目前来说还是可以接受的,所以最终决定换成 gettext 方式
如果是基于 .NET Framework 的项目:
在需要支持翻译的项目 NuGet 安装 i18N:i18N详细介绍
Install-Package i18N
在使用文案的地方都按照以下方式来写:
1. [[[明道]]] // 没有参数
2. [[[我在 %0 上班 ||| 明道 ]]] // 带参数
3. string.Format("[[[welcome %1, today is %0|||{0}|||{1}]]]", day, name) //string.Format 方式
web.config 配置:
<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 配置:
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 文件:
"$(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 文件
</br>
对于所有访问资源都在所部署的IIS服务器的情况,以上配置基本就可以了
但是对于引用 CDN 的文件来说,以上的方式就不合适了,所以我们弃用 i18N 自带的 pot 模板生成工具 i18n.PostBuild,自己实现。
大概思路:
如果资源文件在所部署的 IIS 服务器上,依然使用以上的试试;
如果资源文件在 CDN 上(对于我们来说要翻译部分基本是 js、tpl、css、image),所以只需要处理 js、tpl 里面的多语言即可;
-
类似 [[[xxxx]]] 这样的语法,我们定义一个js里面的语法,_l('xxxx');
_l("明道") _l("我在 %0 上班","明道")
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 ''; };
-
根据 [[[xxxx]]] 和 _l('xxxx') 语法我们通过脚本来提取所有的 key,生成 pot 文件,大家可以注意文件内有一行 #: Disabled references:1,这里其实是有讲究的,我的方式是出于自动生成方便,大家有兴趣可以深入了解一下 gettext;
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 ""
-
将 pot 模板导入到 transifex/crowdin 中,指定这个模板需要翻译出哪些语言,在线翻译完成后下载 po 文件;
-
根据 po 文件生成js文件,把 js 引入到项目下就可以通过 _l('xxxx') 的方式使用了;
注意:如果使用第三方翻译平台,基本上都是按词数或者条数来收费的。为了减少一些成本,对于已经翻译好的内容,其实可以不用存储在翻译平台,翻译好后下载 po 文件后就可以把翻译平台上的内容清空。之后再导入新的 pot 文件,所以对 pot 的生成就要做一些处理,我们希望是增量式的。
提取站点中所有有效的 key
清理 pot 和 po 文件中无效的 key
对比提取出来的 key 与现有翻译文件(已经清理过)中的 key
生成增量 key 的 pot 文件
导入只含增量 key 的 pot 文件,完成翻译
从翻译平台下载 po 文件,修改本地的 pot 和 po 文件(增量部分 copy 追加到文件中)
重新更新 po 文件生成 js 文件
以上的实现我们是通过 gulp 来完成的,具体实现方式可以根据情况而定。