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

后端 未结 7 2259
予麋鹿
予麋鹿 2021-01-30 00:30

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 internationalisat

7条回答
  •  轮回少年
    2021-01-30 00:57

    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.

    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.

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

    With the helper registered above, we can add translations using just client-side 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 ".".

    
    

    And finally the template can be rendered as normal with 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/roddeh-i18n@1.2.0/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);
        }
      );
    
    
    
    
    

    i18n with Handlebars



提交回复
热议问题