0

I would to load dynamically the page's translation when its route change. I done it using onEnter for each route.

Is there a better way to implement this? Can I avoid to use onEnter callback function for each route?

var  Root = React.createClass({       
    baseTranslations : {},

    getInitialState: function(){    
        return {
            lang: 'it',
            translations: null,
        };    
    },    

    componentWillMount: function() {    
        // load the common translation for all routes            
        this.setState({
            lang: this.getLocale().lang
        }); 
        this.loadTranslation("base");
    },

    onChangeRoute: function(nextState, replace){
        // load the specific route's translation
        this.loadTranslation(nextState.location.pathname);
    },

    getLocale: function(what){
        // return lang and user region (ex: it-IT)    
    },

    loadTranslation: function(route){    
        var lcl = route;    
        var mapping = {
            '/'     : 'home',
            'auth'  : 'dashboard'
        };

        if(route in mapping){
            lcl = mapping[route];
        }

        $.ajax({
            url: './locales/' + this.state.lang +'/' + lcl + '.json',
            dataType: "text",
        }).done(function(res) {

            if(lcl === "base"){
                this.baseTranslations = JSON.parse(res);
            }

            this.setState({
                translations: $.extend(true, this.baseTranslations, JSON.parse(res))
            });

        }.bind(this));

    },

    render: function() {
        var children;

        if (this.state.translations) {
            children = (
                <IntlProvider locale={this.state.lang} messages={this.state.translations}>
                    <Router history={browserHistory}>
                        <Route path="/" component={App}>
                            <IndexRoute component={Home}/>

                            <Route path="signup"  onEnter={this.onChangeRoute} getComponents={(location, cb) => {

                                 require.ensure([], (require) => {
                                        cb(null, require('./components/Signup.jsx'))
                                 }, "Signup")

                            }} />     

                            <Route path="*" component={NoMatch}/>
                        </Route>
                    </Router>
                </IntlProvider>
            );
        }

        return <div>{children}</div>;
    }
});


ReactDOM.render(
    <Root/>,
    document.getElementById('rct-cont')
);
Webman
  • 1,534
  • 3
  • 26
  • 48
  • How do you manage the state of your application? One option would be to push it to your state management solution and query i18n on demand. – Juho Vepsäläinen Apr 10 '16 at 10:48
  • @bebraw I'm going to manage with Flux – Webman Apr 10 '16 at 10:50
  • How have you structured your translations? Based on the code it seems to be per route. If there isn't a lot to translate, maybe you could load whole i18n data in one query. You can consider pushing i18n state to a Flux store if you want to clean up your current solution. – Juho Vepsäläinen Apr 10 '16 at 11:18
  • @bebraw I have one json file for each route and for some routes the translation could have a lot of text. Do you have any link about how implement it with Flux and React-intl? – Webman Apr 10 '16 at 13:22
  • Not really. You would stash your i18n at a flux store and have actions for manipulating it (loading language per path, selecting language). You can perform basic caching there and even store your i18n data to `localStorage` if you want. This probably makes more sense as you learn more about Flux. – Juho Vepsäläinen Apr 10 '16 at 14:05
  • @bebraw I've been using recently React: 1. How could I modify IntlProvider's messages with Flux? 2. If the i18n data change, how I could update correctly the localStorage? – Webman Apr 10 '16 at 20:22

1 Answers1

0

As it's hard to say which Flux implementation you are going to use, I'll give you pseudocode and the key ideas here instead:

i18nActions

  • loadLanguage(language, route) - If a language for the given route hasn't been loaded, this triggers a query and then delegates the result to the store.
  • You can implement a caching layer here (localStorage) if you want. You need to be careful about invalidation, though. You should have some way of telling that data has become stale and needs to be refreshed.

i18nStore

  • Maintains i18n state visible to the user. You would consume this state from your UI level components.

Root

  • Triggers loadLanguage at onEnter to update the store.
  • Consumes the store and passes the data to IntlProvider. Other components can dig the i18n data from that context.

There are additional questions like what to do while the translations are loading. What should you show then? You can either defer displaying the UI to the user or you can use placeholders that will get replaced once the data has been received.

Juho Vepsäläinen
  • 26,573
  • 12
  • 79
  • 105