问题描述
在 node.js
web项目中实现i18n存在一个相当常见的问题。如果您想要问题,问题似乎更糟:
There is a rather usual problem to implement i18n in node.js
web project. The problem seems even worse if you want to:
- 使用网络组件(例如)
- 对服务器端和客户端文件使用单个翻译文件
- 翻译一些programmaticaly项目(如动态创建的字符串)
感谢全新的由Mozilla团队开发,这个问题可以很容易地解决。
Thanks to brand-new L20n library developed by Mozilla team this problem can be solved rather easily.
推荐答案
项目结构
首先我创建了一个项目结构,它将我的文件分开保存,按目的分组:
Project structure
First I created a project structure, that would keep my files separatedly, grouped by their purpose:
.
+-- app.js
+-- piblic
| +-- locales
| +-- app.ru.l20n
| +-- app.en.l20n
|
+-- node_models
| +-- l20n
|
+-- bower_components
| +-- Polymer libraries
|
+-- app_modules
| +-- app-l20n-node
| +-- index.js
|
+-- app_components
+-- app-l20n
+-- app-l20n.html
+-- app-custom-component
+-- app-custom-component.html
这个想法很简单: app-l20n- node
用作本地化所有服务器端作业的模块, app-l20n
是用户界面l10n的Polymer组件。
The idea is simple: app-l20n-node
is used as module to localize all server-side jobs, app-l20n
is a Polymer component for user interface l10n.
运行 npm install l20n --save
当前版本是3.5.1,它有一个小bug。 l20n的主文件是 ./ dist / compat / node / l20n.js
并且它有两个必需的变量,这些变量在代码中的任何地方都没有使用,但可以粉碎你的应用程序启动,因为它们仅在图书馆的Devdependencies中提及。为了避免它,我只是将它们直接注释到库代码中:
Run npm install l20n --save
Current version is 3.5.1 and it has a small bug. Main file of l20n is ./dist/compat/node/l20n.js
and it has two required variables, that are not used anywhere in code, but can crush your app on launch, as they are mentioned only in Devdependencies of the library. To avoid it I just commented them right into the library code:
//var string_prototype_startswith = require('string.prototype.startswith');
//var string_prototype_endswith = require('string.prototype.endswith');
翻译文件
我创建了翻译文件在我的 / public / locales /
文件夹中,名称如 app.ru.l20n
和 app .en.l20n
。根据L20n规则,文件的内容如下所示:
Translation files
I created translation files in my /public/locales/
folder, named like app.ru.l20n
and app.en.l20n
. According to L20n rules, the contents of files look like:
<foo "Foo translation">
<bar "Bar translation">
<register[$variant] {
infinitive: "Register now!"
}>
Node.js + L20n
现在是时候为L20n创建一个节点模块了。
在我的例子中, app_modules\app-l20n-node\index.js
的代码如下所示:
'use strict';
const L20n = require('l20n');
var path = require('path');
module.exports = function(keys, lang){
const env = new L20n.Env(L20n.fetchResource);
// Don't forget nice debug feature of L20n library
env.addEventListener('*', e => console.log(e));
// I suppose that I'll always provide locale code for translation,
// but if it would not happen, module should use preset
var langs = [];
if(!lang) {
// you should define locales here
langs = [{code: 'ru'}, {code: 'en'}]
} else {
langs = [{code: lang}]
}
// set context, using path to locale files
const ctx = env.createContext(langs,
[path.join(__dirname, '../../public/locales/app.{locale}.l20n')]);
const fv = ctx.formatValues;
return fv.apply(ctx, keys);
};
现在我们可以在我们的node.js代码中使用此模块并获取按键和区域设置请求的翻译。我使用 express-sessions
而不是硬编码的语言环境,并将用户定义的语言环境存储为会话属性 req.session.locale
。但这一切都取决于项目。
Now we can use this module in our node.js code and get translation, requested by keys and locale. Instead of hard-coded locale I use express-sessions
and store user-defined locale as session attribute req.session.locale
. But it all depends on project.
var l20n = require('../../app_modules/app-l20n-node');
l20n(['foo','bar'], 'en')
.then((t)=>{
console.log(t); // ["Foo translation", "Bar translation"]
});
Polymer + L20n
现在我们应该为L20n创建一个Polymer组件。
首先,添加库并链接到翻译文件到你的html < head>
:
<script src="/node_modules/l20n/dist/compat/web/l20n.js"></script>
<link rel="localization" href="/locales/app.{locale}.l20n">
现在是时候创建一个聚合物组件 app-l20n.html
带有新举动。
Now it's time to create a Polymer component app-l20n.html
with a new behavior.
<script>
/**
* Create namespace for custom behavior or use existing one.
*/
window.MB = window.MB || {};
MB.i18n = {
/**
* Use l20n.js to translate certain strings
* @param component A Polymer component, usually "this.translate(this, props);"
* @param props An array of keys to translate: strings or arrays.
*/
translate: function(component, props) {
var view = document.l10n;
var promise = view.formatValues.apply(view, props);
promise.then(function(args){
for (var i in args){
var prop = props[i];
// strings with parameters represented by arrays:
// ["string", {param: value}]
if (Array.isArray(prop)) {
// get property name - usually the same, as translation key
// so the object would have properties obj.Foo and obj.Bar
var propName = prop[0];
// if it is needed to create multiple translations of the same
// string in one component, but with different parameters,
// the best way is to use suffix:
// ["string", {param: value}, "_suffix"]
if (prop.length == 3) propName = propName + prop[2];
component.set(propName, args[i]);
}
// common strings
else component.set(prop, args[i]);
}
});
}
};
</script>
不,由于新行为准备就绪,我们可以在我们的自定义Polymer组件中实现它。您可以通过Polymer Behavior以编程方式获得翻译或使用L20n自定义标签属性功能。
No, as the new behavior is ready, we can implement it in our custom Polymer components. You can get translation programmatically by Polymer Behavior or using L20n custom tag attributes feature.
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/gold-email-input/gold-email-input.html">
<link rel="import" href="/bower_components/paper-button/paper-button.html">
<link rel="import" href="/app_components/app-l20n/app-l20n.html">
<dom-module id="app-custom-component">
<template>
<gold-email-input
auto-validate
required
name="email"
value="{{Foo}}"
label="{{Bar}}">
</gold-email-input>
<paper-button onclick="regFormSubmit(event)">
<iron-icon icon="perm-identity"></iron-icon>
<span data-l10n-id="Register" data-l10n-args='{"variant": "infinitive"}'></span>
</paper-button>
</template>
<script>
function regFormSubmit(event){}
Polymer({
is: 'app-custom-component',
behaviors: [
MB.i18n
],
ready: function(){
this.$.passwordValidator.validate = this._validatePasswords.bind(this);
// add your component properties to array. They can be simple like in this example, or
// more complex, with parameters: ["Some_key", {param: "xxx"}].
// you can even translate the same string in different properties, using custom suffix:
// ["Some_key", {param: "yyy"}, "_suffix"] and place it in template with shortcut: {{Some_key_suffix}}
var translateProps = ["Foo", "Bar"];
// now translate, using behavior
this.translate(this, translateProps);
}
});
</script>
</dom-module>
希望这个小教程会有所帮助。
Hope this little tutorial would be helpful.
这篇关于教程:基于L20n库的node.js / Polymer i18n解决方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!