cyclomarc
cyclomarc

Reputation: 2012

Ember: dynamic switch to chosen language (using i18n library)

I am using the ember-i18n library for translation of static strings used throughout my application. As the language files are rather big, I do not want to load at application start all possible language dictionaries. I thus want to load the dictionary dynamically when the user chooses to change language. I have made a first implementation that works rather well.

See http://jsfiddle.net/cyclomarc/RYbNG/7/

When starting the app, it is rendered in english. You can now select one of the views (About or Info) and these are also rendered in english. When you click on 'Dutch', the dutch language dictionary is loaded and the application is redirected to the index route in the correct language.

It seems that the new language strings are only used when you transition to a dummy route and then back to the route you want (in my sample this is always 'index').

updateLanguage: function (lang) {
    var _self = this;
    //Load correct dictionary and transition to index route
    $.getScript("http://libraries.azurewebsites.net/locales/dictionary_" + lang + ".js", function () {
      CLDR.defaultLanguage = lang;
      _self.transitionToRoute('I18redirect');
    });
  }

App.I18redirectRoute = Ember.Route.extend({
  activate: function () {
    this.transitionTo('index');
  }
});

My questions:

  1. Is this the best way to reload a view.template (transition to dummy route and then on activate transition to index) ?

  2. Is there a way to transition back to the route where you requested the language change (would need to use something with get(path) or so ) ?

  3. I would also like to translate the strings 'outside' the red div (the application outlet). I transition back to index, but in that case, the application template is not redrawn ... What could be the reason ?

  4. Is it the expected behaviour that when you move away from a template and then re-enter the template, the template itself is rebuild with all the language strings, or is this only when in the meantime the language is changed ? How could such a rebuild of template with new strings be traced in console log ?

Any other ideas to make this a robust switching solution ?

Upvotes: 15

Views: 3313

Answers (3)

Sam
Sam

Reputation: 1132

I tried all the ways listed here and had various issues with all of them. I found a much more elegant hack to make this work.

CAVEAT: This only works if you are serving Ember using Rails (but could probably be adapted to work with any server-side framework).

In Rails, the i18n-js gem integrates with Rails default locales. Dynamic switching in Ember is a huge pain because you must manually redraw all views or reset the app to get the language to update when I18n.locale is changed.

The best way I have found to do it is to specify locale using parameters set on Rails, before Ember even loads.

RAILS configuration:

Make your ember#index controller action look like this:

def index
  if params[:locale]
    I18n.locale = params[:locale]
  end
end

Then in your html template, add this single line of javascript:

<html>
  <head>
    <%= stylesheet_link_tag :application, :media => :all %>
    <%= stylesheet_link_tag    "application", 'http://fonts.googleapis.com/css?family=Roboto:400,300,300italic,100,700' %>
    <%= stylesheet_link_tag    "application", 'http://fonts.googleapis.com/css?family=Roboto+Condensed:400,300,300italic' %>
    <%= javascript_include_tag :application %>
    <title>Review Blaster 9000</title>

<!-- SET LOCALE USING RAILS PARAMETER PARSING -->
    <script>I18n.locale = '<%=I18n.locale%>';</script>
<!-- END SET LOCALE -->

  </head>
  <body>

<!-- Start Ember.js -->
      <%= yield %>
<!-- End Ember.js -->

  </body>
</html>

Then in your template, specify the language switchers like this:

<a href='/?locale=en'>English</a>
<a href='/?locale=es'>Spanish</a>
...etc...

Clicking on those links will result in a complete refresh of the app. Rails will parse the parameter, set I18n and then Ember will start with the correct value already set beforehand, so everything will draw correctly.

Upvotes: 0

Sviatoslav Zalishchuk
Sviatoslav Zalishchuk

Reputation: 739

You could utilize rerender() method of the view, this way you won't have to switch to a dummy url:

updateLanguage: function(lang) {

    //Send xhr to get a dictionary for corresponding language
    this.getDictionary(lang).then(successCb, failureCb);

    //If dictionary was retrieved, set translations property and 
    function successCb(response) {
        Ember.I18n.translations = response;
        $.each(Ember.View.views, function(index, view) {
          view.rerender();
        });
    };

    function failureCb(response) {
        console.error('Dictionary for this language is not available');
    };
}

Upvotes: 0

jaaksarv
jaaksarv

Reputation: 1480

I have not done translations in my app yet, but I plan to do so. So I'm also interested to know what is the best solution. I will describe my current idea how I would do it with my current knowledge.

1) I would define language as a top level route.

App.Router.map(function() {
    this.resource('language', {path:'/language/:lang_code'}, function() {
        // All other routes
    });
});

Then changing the language would be a link to Language route with corresponding language code. I would also define Language model with attributes code, name, flag etc. for this. This would be also handy when showing list of language select buttons or dropdowns.

Also when loading subroute model you already know what the langugage is and you can fetch the models in current language (for example think about blog post that is different in every language). Use this.modelFor('language') to get current language in any subroute.

2) This is tricky. AFAIK there is no public API to get current URL with all the dynamic segments. There is currentPath property in application controller, but it doesn't include dynamic route variables. There are also some Ember internal variables, but I wouldn't use them as they might change. You can also investigate current router url: this.get('router.url'). Also you can bypass ember and use window.location.href to read the URL directly. But I would not spend too much time on that. Users change their language rarely (usually on first visit only) and it's not a big problem to redirect to index route on language change. I would store it in a cookie or local storage and then there is no need to change the language later. If Ember comes up with nice and easy way to do it then you can easily add this functionality later.

3) I would not put anything into application template as it is not translated. Use language template instead of application template and then everything should re-rendered as you are switching the language.

4) I think that the texts are not automatically updating because they are not currently two-way binded. But it is fine, because it is not a very good idea to have two way bindings for all texts as it can make system slow. So just put the language as top level route and everything should work fine:)

Upvotes: 0

Related Questions