教程:基于L20n库

时间:2016-02-28 00:22:31

标签: javascript node.js internationalization polymer l20n

node.js网络项目中实施i18n存在一个相当常见的问题。如果你想要问题,问题似乎更糟:

  1. 使用网络组件(例如Polymer
  2. 将单个翻译文件用于服务器端和客户端文件
  3. 以programmaticaly方式翻译某些项目(如动态创建的字符串)
  4. 感谢Mozilla团队开发的全新L20n library,这个问题可以很容易地解决。

1 个答案:

答案 0 :(得分:0)

项目结构

首先,我创建了一个项目结构,它将我的文件分开,按目的分组:

.
+-- 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组件。

安装

运行npm install l20n --save
当前版本是3.5.1,它有一个小bug。 l20n的主文件是./dist/compat/node/l20n.js,它有两个必需的变量,不会在代码中的任何地方使用,但可以在启动时粉碎你的应用程序,因为它们仅在库的Devdependencies中提到。为了避免它,我只是将它们直接注释到库代码中:

//var string_prototype_startswith = require('string.prototype.startswith');
//var string_prototype_endswith = require('string.prototype.endswith');

翻译文件

我在/public/locales/文件夹中创建了翻译文件,名称为app.ru.l20napp.en.l20n。根据L20n规则,文件的内容如下:

<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。但这一切都取决于项目。

var l20n =  require('../../app_modules/app-l20n-node');

l20n(['foo','bar'], 'en')
    .then((t)=>{
        console.log(t); // ["Foo translation", "Bar translation"]
    });

聚合物+ L20n

现在我们应该为L20n创建一个聚合物组件 首先,将库和链接到翻译文件添加到您的html <head>

<script src="/node_modules/l20n/dist/compat/web/l20n.js"></script>
<link rel="localization" href="/locales/app.{locale}.l20n">

现在是时候创建一个带有新行为的聚合物组件app-l20n.html

<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自定义标记属性功能以编程方式进行翻译。

<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>

希望这个小教程会有所帮助。