Javascript 如何使用 Handlebars.js(小胡子模板)制作 i18n?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7760332/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 03:30:33  来源:igfitidea点击:

How to make i18n with Handlebars.js (mustache templates)?

javascriptjqueryinternationalizationhandlebars.jsmustache

提问by mdcarter

I'm currently using Handlebars.js (associated with Backbone and jQuery) to make a web app almost totally client side rendered, and I'm having issues with the internationalisation of this app.

我目前正在使用 Handlebars.js(与 Backbone 和 jQuery 相关联)来制作几乎完全由客户端呈现的 Web 应用程序,并且我在此应用程序的国际化方面遇到了问题。

How can I make this work?

我怎样才能使这项工作?

Are there any plugins?

有插件吗?

回答by poweratom

I know this has been answered, but I'd like to share my simple solution. To build on Gazler's solution using I18n.js (which we use with our project at work), I just used a very simple Handlebars helper to facilitate the process to do the localization on the fly:

我知道这已经得到了回答,但我想分享我的简单解决方案。为了使用 I18n.js(我们在我们的项目中使用它)构建 Gazler 的解决方案,我只使用了一个非常简单的 Handlebars 助手来促进动态本地化的过程:

Handler

处理程序

Handlebars.registerHelper('I18n',
  function(str){
    return (I18n != undefined ? I18n.t(str) : str);
  }
);

Template

模板

<script id="my_template" type="x-handlebars-template">
    <div>{{I18n myVar}}</div>
</script>

The primary advantage of this is that there's no expensive pre/post processing on the entire json object. Not to mention if the incoming json has nested objects/arrays, the time spent looking for and parsing for them might get expensive if the object is huge.

这样做的主要优点是对整个 json 对象没有昂贵的前/后处理。更不用说传入的 json 是否有嵌套的对象/数组,如果对象很大,查找和解析它们所花费的时间可能会很昂贵。

Cheers!

干杯!

回答by Gazler

https://github.com/fnando/i18n-jsis a ruby gem that will create an internationalization file from your config/locales folder. However if you are not using rails, you can find the javascript used on its own here.

https://github.com/fnando/i18n-js是一个 ruby​​ gem,它将从您的 config/locales 文件夹创建一个国际化文件。但是,如果您不使用 rails,则可以在此处找到单独使用的 javascript 。

You then simply store the translations in a nested object..

然后,您只需将翻译存储在嵌套对象中。

I18n.translations = {"en":{"date":{"formats":{"default":"%Y-%m-%d","short":"%b %d","long":"%B %d, %Y"}}}};

Something that may also be of use to you that I use on my projects is a patch to mustache that automatically translates strings in the format @@translation_key@@

我在我的项目中使用的对您也可能有用的东西是胡子补丁,它会自动翻译@@translation_key@@ 格式的字符串

i18nize = function (result) {
    if (I18n) {
      var toBeTranslated = result.match(/@@([^@]*)@@/gm);
      if (!toBeTranslated) return result;
      for(var i = 0; i < toBeTranslated.length; i++) {
        result = result.replace(toBeTranslated[i], I18n.t(toBeTranslated[i].replace(/@/g, "")));
      }
    }
    return result;
};

You then call i18nize afterrender to allow you to put translations in your templates instead of passing them through.

然后渲染后调用 i18nize以允许您将翻译放入模板而不是通过它们。

Beware of patching mustache as you will not be able to port your templates to standard mustache implementations.However in my case, the benefits offered outweighed this issue.

请注意修补 mustache,因为您将无法将模板移植到标准 mustache 实现。然而,就我而言,所提供的好处超过了这个问题。

Hope this helps.

希望这可以帮助。

回答by jvenezia

Based on @poweratom 's answer :

基于@poweratom 的回答:

Only with ember.js, same with options passed to I18n.js.

仅使用ember.js,与传递给 I18n.js 的选项相同。

It will magically reload if computed properties are used.

如果使用计算属性,它将神奇地重新加载。

Ember.Handlebars.helper "t", (str, options) ->
  if I18n? then I18n.t(str, options.hash) else str

Template:

模板:

{{t 'sharings.index.title' count=length}}

Yml:

yml:

en:
  sharings:
    index:
      title: To listen (%{count})

回答by dam1

With NodeJs / Express:

使用NodeJs / Express

  • node-i18n( detect the Accept-Language header )

      app.use(i18n.init); 
    
  • Sample translation file

    {   
     "hello": "hello",   
     "home-page": {
       "home": "Home",
        "signup": "Sign Up"  
     } 
    }
    
  • In Express controller

    ...
    data.tr = req.__('home-page');
    var template = Handlebars.compile(source);
    var result = template(data);
    
  • Handlebars Template

        <li class="active"><a href="/">{{tr.home}}</a></li>
    
  • node-i18n(检测 Accept-Language 标头)

      app.use(i18n.init); 
    
  • 示例翻译文件

    {   
     "hello": "hello",   
     "home-page": {
       "home": "Home",
        "signup": "Sign Up"  
     } 
    }
    
  • 在 Express 控制器中

    ...
    data.tr = req.__('home-page');
    var template = Handlebars.compile(source);
    var result = template(data);
    
  • 车把模板

        <li class="active"><a href="/">{{tr.home}}</a></li>
    

回答by Sahib Khan

The question is answered but their may be a case where you do not want to depend on any i8n lib and use completely your own. I am using my own inspired from https://gist.github.com/tracend/3261055

问题已得到解答,但可能是您不想依赖任何 i8n 库并完全使用您自己的库的情况。我正在使用我自己的灵感来自https://gist.github.com/tracend/3261055

回答by James

As already established, using Handlebars for internationalization means you will have to register a custom helper to link to the i18n library of your choice. Most i18n libraries don't have this "glue" available out of the box, but it's pretty easy to add.

如前所述,使用 Handlebars 进行国际化意味着您必须注册一个自定义帮助程序以链接到您选择的 i18n 库。大多数 i18n 库都没有这种开箱即用的“胶水”,但添加起来非常容易。

Building on @poweratom's answer (which in turn builds on @Glazer's), one can register a helper which allows a pass-through of the Handlebars parameters.

基于@poweratom 的回答(反过来又基于@Glazer 的回答),可以注册一个允许传递 Handlebars 参数的帮助程序。

Handlebars.registerHelper('i18n',
  function(str){
    return new Handlebars.SafeString((typeof(i18n) !== "undefined" ? i18n.apply(null, arguments) : str));
  }
);

The existing answers use other libraries, but I prefer i18njs.com(npm/roddeh-i18n) because it has better support for the more important aspects of client-side internationalization such as grammar rules (and it doesn't create a dependency on YML and/or Ember, and doesn't require server-side rendering with nodejs).

现有答案使用其他库,但我更喜欢i18njs.com(npm/roddeh-i18n),因为它更好地支持客户端国际化的更重要方面,例如语法规则(并且它不会创建对 YML 的依赖)和/或 Ember,并且不需要使用 nodejs 进行服务器端渲染)。

With the helper registered above, we can add translations using just client-sideJSON/JavaScript:

使用上面注册的帮助程序,我们可以仅使用客户端JSON/JavaScript添加翻译:

i18n.translator.add({
  "values":{
    "Yes": "はい",
    "No": "いいえ",
    "It is %n": [[0,null,"%nです"]],
    "Do you want to continue?": "続けたいですか?",
    "Don't worry %{name}": "%{name}を心配しないでください",
    "%{name} uploaded %n photos to their %{album} album": "%{name}は彼の%{album}アルバムに写真%n枚をアップロードしました"
  },
  "contexts":[
    {
      "matches": { "gender": "male" },
      "values": { "%{name} uploaded %n photos to their %{album} album": [[0,null,"%{name}は彼の%{album}アルバムに写真%n枚をアップロードしました"]] }
    },
    {
      "matches": { "gender": "female" },
      "values": { "%{name} uploaded %n photos to their %{album} album": [[0,null,"%{name}は彼女の%{album}アルバムに写真%n枚をアップロードしました"]] }
    }
  ]
});

Now, any handlebars template we create can be internationalized by just passing-through parameters to the library. For example, formatting a number (i.e. "%n") requires the first parameter to be the path to the number. So to get the count from an object {"count":3} we could reference the path "./count" or just "count". Conditional matches require the last parameter to be the path to the object where the matches will be found; usually just the root object ".".

现在,我们创建的任何把手模板都可以通过将参数传递给库来国际化。例如,格式化数字(即“%n”)要求第一个参数是数字的路径。因此,要从对象 {"count":3} 获取计数,我们可以引用路径“./count”或仅引用“count”。条件匹配要求最后一个参数是找到匹配项的对象的路径;通常只是根对象“.”。

<script id="messagestemplate" type="text/x-handlebars-template">
  <p>
    {{i18n 'Do you want to continue?'}} {{i18n 'Yes'}}<br>
    {{i18n 'Don\'t worry %{name}' . }}<br>
    {{i18n 'It is %n' count}}<br>
    {{i18n '%{name} uploaded %n photos to their %{album} album' count . .}}
  </p>
</script>

And finally the template can be rendered as normal with Handlebars:

最后,模板可以使用 Handlebars 正常渲染:

var userData = {
  gender: "male",
  name: "Scott",
  album: "Precious Memories",
  count: 1
};

var templateSource = $("#messagestemplate").html();
var messagesTemplate = Handlebars.compile(templateSource);
var renderedMessages = messagesTemplate(userData);

$('#target-message').html(renderedMessages);

Here's a more complete example:

这是一个更完整的示例:

// Using http://i18njs.com (npm/roddeh-i18n)

// Includes:
//   cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
//   rawgit.com/components/handlebars.js/master/handlebars.js
//   cdn.jsdelivr.net/npm/[email protected]/dist/i18n.min.js


// REGISTER I18N HELPER   {{i18n 'Text to translate'}}

Handlebars.registerHelper('i18n',
  function(str){
    return new Handlebars.SafeString((typeof(i18n) !== "undefined" ? i18n.apply(null, arguments) : str));
  }
);


// REGISTER THE TEMPLATE

var templateSource = $("#atemplate").html();
var template = Handlebars.compile(templateSource);

function updateMessage(data) {
  $('#target-message').html(template(data));
}


// ADD TRANSLATIONS

function setLanguage(lang) {
  // Spanish
  if (lang == 'es') {
    i18n.translator.reset();
    i18n.translator.add({
      "values":{
        "Yes": "Si",
        "No": "No",
        "Do you want to continue?": "?Quieres continuar?",
        "Don't worry %{name}": "No te preocupes %{name}",
        "It is %n": [[0,null,"Es %n"]],
        "%{name} uploaded %n photos to their %{album} album":[
            [0, 0, "%{name} ha subido %n fotos a su album %{album}"],
            [1, 1, "%{name} ha subido %n foto a su album %{album}"],
            [2, null, "%{name} ha subido %n fotos a su album %{album}"]
         ]
      }
    });
  }

  // Japanese
  else if (lang == 'jp') {
    i18n.translator.reset();
    i18n.translator.add({
      "values":{
        "Yes": "はい",
        "No": "いいえ",
        "It is %n": [[0,null,"%nです"]],
        "Do you want to continue?": "続けたいですか?",
        "Don't worry %{name}": "%{name}を心配しないでください",
        "%{name} uploaded %n photos to their %{album} album": "%{name}は彼の%{album}アルバムに写真%n枚をアップロードしました"
      },
      "contexts":[
        {
          "matches":{ "gender":"male" },
          "values":{ "%{name} uploaded %n photos to their %{album} album": [[0,null,"%{name}は彼の%{album}アルバムに写真%n枚をアップロードしました"]] }
        },
        {
          "matches":{ "gender":"female" },
          "values":{ "%{name} uploaded %n photos to their %{album} album": [[0,null,"%{name}は彼女の%{album}アルバムに写真%n枚をアップロードしました"]] }
        }
      ]
    });
  }

  // Default Language (English)
  else {
    i18n.translator.reset();
    i18n.translator.add({
      "values":{
        "Yes": "Yes",
        "No": "No",
        "Do you want to continue?": "Do you want to continue?",
        "Don't worry %{name}": "Not to worry %{name}",
        "It is %n": [[0,null,"It's %n"]],
        "%{name} uploaded %n photos to their %{album} album":[
            [0, 0, "%{name} uploaded %n photos to their %{album} album"],
            [1, 1, "%{name} uploaded %n photo to their %{album} album"],
            [2, null, "%{name} uploaded %n photos to their %{album} album"]
         ]
      }
    });
  }
}


// SET DEFAULT LANGUAGE TO BROWSER/SYSTEM SETTINGS

var browserLanguage = (navigator.languages && navigator.languages[0] || navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage || 'en').split('-')[0];

setLanguage(browserLanguage);


// RENDER THE TEMPLATE WITH DATA

var userData = {
  gender: "female",
  name: "Scott",
  album: "Precious Memories",
  count: 1
};

updateMessage(userData);


// USER-TRIGGERED LANGUAGE SELECTION

// note: the array around browserLanguage is important when setting radio buttons!
$("input[name=lang]")
  .val([browserLanguage])
  .click(
    function() {
      var lang = $('input[name=lang]:checked').val();
      setLanguage(lang);
      updateMessage(userData);
    }
  );
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/i18n.min.js"></script>
<script src="https://rawgit.com/components/handlebars.js/master/handlebars.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<h1>i18n with Handlebars</h1>

<label><input type="radio" name="lang" value="en"> English</label><br>
<label><input type="radio" name="lang" value="es"> Espanol</label><br>
<label><input type="radio" name="lang" value="jp"> Japanese</label>

<div id="target-message"></div>

<!--
  NOTE: The helper {{i18n ...}} is just a passthrough for
    the i18n library. Parameters come from the single object
    passed into the handlebars template. Formatting a
    number (i.e. "%n") requires the first parameter to be
    the path to the number.  For example, count from the
    object {"count":3} could be referenced by the path
    "./count" or just "count".  Conditional matches require
    the last parameter to be the path to the object where
    the matches will be found; usually just the root object ".".

    see:
      handlebarsjs paths:   https://handlebarsjs.com/#paths
      i18n formatting:      http://i18njs.com/#formatting
-->

<script id="atemplate" type="text/x-handlebars-template">
  <p>
    {{i18n 'Do you want to continue?'}} {{i18n 'Yes'}}<br>
    {{i18n 'Don\'t worry %{name}' . }}<br>
    {{i18n 'It is %n' count}}<br>
    {{i18n '%{name} uploaded %n photos to their %{album} album' count . .}}
  </p>
</script>

回答by equivalent8

for those not using any JS framework http://i18next.comlooks promising too

对于那些不使用任何 JS 框架http://i18next.com看起来也很有希望

just create handlebars helper to call translations like here http://i18next.com/pages/doc_templates.html

只需创建把手助手来调用这里的翻译http://i18next.com/pages/doc_templates.html