A component for React that utilizes the Counterpart module to provide multi-lingual/localized text content.

Overview

React Translate Component

Translate is a component for React that utilizes the Counterpart module and the Interpolate component to provide multi-lingual/localized text content. It allows switching locales without a page reload.

Installation

Install via npm:

% npm install react-translate-component

Usage

Here is a quick-start tutorial to get you up and running with Translate. It's a step-by-step guide on how to build a simple app that uses the Translate component from scratch. We assume you have recent versions of Node.js and npm installed.

First, let's create a new project:

$ mkdir translate-example
$ cd translate-example
$ touch client.js
$ npm init                   # accept all defaults here

Next, add the dependencies our little project requires:

$ npm install react counterpart react-interpolate-component react-translate-component --save

The react, counterpart and react-interpolate-component packages are peer dependencies of react-translate-component and need to be installed along-side of it.

We will put our application logic into client.js. Open the file in your favorite editor and add the following lines:

'use strict';

var counterpart = require('counterpart');
var React       = require('react');
var ReactDOM    = require('react-dom');
var Translate   = require('react-translate-component');

This loads the localization library, React and our Translate component.

Let's write our entry-point React component. Add the following code to the file:

class MyApp extends React.Component {
  render() {
    return (
      <html>
        <head>
          <meta charSet="utf-8" />
          <title>React Translate Quick-Start</title>
          <script src="/bundle.js" />
        </head>

        <body>
          --> body content will be added soon <--
        </body>
      </html>
    );
  }
}

if (typeof window !== 'undefined') {
  window.onload = function() {
    ReactDOM.render(<MyApp />, document);
  };
}

module.exports = MyApp;

Now we have the basic HTML chrome for our tiny little app.

Next, we will create a LocaleSwitcher component which will be used to, well, switch locales. Here is the code to append to client.js:

class LocaleSwitcher extends React.Component {
  handleChange(e) {
    counterpart.setLocale(e.target.value);
  }

  render() {
    return (
      <p>
        <span>Switch Locale:</span>

        <select defaultValue={counterpart.getLocale()} onChange={this.handleChange}>
          <option>en</option>
          <option>de</option>
        </select>
      </p>
    );
  }
}

For demonstration purposes, we don't bother and hard-code the available locales.

Whenever the user selects a different locale from the drop-down, we correspondingly set the new drop-down's value as locale in the Counterpart library, which in turn triggers an event that our (soon to be integrated) Translate component listens to. As initially active value for the select element we specify Counterpart's current locale ("en" by default).

Now add LocaleSwitcher as child of the empty body element of our MyApp component:

        <body>
          <LocaleSwitcher />
        </body>

Next, we create a Greeter component that is going to display a localized message which will greet you:

class Greeter extends React.Component {
  render() {
    return <Translate {...this.props} content="example.greeting" />;
  }
}

In the component's render function, we simply transfer all incoming props to Translate (the component this repo is all about). As content property we specify the string "example.greeting" which acts as the key into the translations dictionary of Counterpart.

Now add the new Greeter component to the body element, provide a with prop holding the interpolations (your first name in this case) and a component prop which is set to "h1":

        <body>
          <LocaleSwitcher />
          <Greeter with={{ name: "Martin" }} component="h1" />
        </body>

The value of the name key will be interpolated into the translation result. The component prop tells Translate which HTML tag to render as container element (a <span> by default).

All that's left to do is to add the actual translations. You do so by calling the registerTranslations function of Counterpart. Add this to client.js:

counterpart.registerTranslations('en', {
  example: {
    greeting: 'Hello %(name)s! How are you today?'
  }
});

counterpart.registerTranslations('de', {
  example: {
    greeting: 'Hallo, %(name)s! Wie geht\'s dir heute so?'
  }
});

In the translations above we defined placeholders (in sprintf's named arguments syntax) which will be interpolated with the value of the name key we gave to the Greeter component via the with prop.

That's it for the application logic. To eventually see this working in a browser, we need to create the server-side code that will be executed by Node.js.

First, let's install some required dependencies and create a server.js file:

$ npm install express connect-browserify reactify node-jsx --save
$ touch server.js

Now open up server.js and add the following lines:

'use strict';

var express     = require('express');
var browserify  = require('connect-browserify');
var reactify    = require('reactify');
var React       = require('react');

require('node-jsx').install();

var App = React.createFactory(require('./client'));

express()
  .use('/bundle.js', browserify.serve({
    entry: __dirname + '/client',
    debug: true, watch: true,
    transforms: [reactify]
  }))
  .get('/', function(req, res, next) {
    res.send(React.renderToString(App()));
  })
  .listen(3000, function() {
    console.log('Point your browser to http://localhost:3000');
  });

Note that you shouldn't use this code in production as the bundle.js file will be compiled on every request.

Last but not least, start the application:

$ node server.js

It should tell you to point your browser to http://localhost:3000. There you will find the page greeting you. Observe that when switching locales the greeting message adjusts its text to the new locale without ever reloading the page or doing any ajax magic.

Please take a look at this repo's spec.js file to see some more nice tricks like translating HTML element attributes (title, placeholder etc.). To become a master craftsman we encourage you to also read Counterpart's README.

Asynchronous Rendering on the Server-side

The above example for server.js will not work when you're calling ReactDOMServer.renderToString(...) within the callback of an async function and calling counterpart.setLocale(...) synchronously outside of that callback. This is because the Counterpart module is used as a singleton instance inside of the Translate component. See PR [#6] for details.

To fix this, create a wrapper component (or extend your root component) and pass an instance of Counterpart as React context. Here's an example:

var http = require('http');
var Translator = require('counterpart').Instance;
var React = require('react');
var ReactDOMServer = require('react-dom/server');
var Translate = require('react-translate-component');
var MyApp = require('./my/components/App');

var en = require('./my/locales/en');
var de = require('./my/locales/de');

class Wrapper extends React.Component {
  getChildContext() {
    return {
      translator: this.props.translator
    };
  }

  render() {
    return <MyApp data={this.props.data} />;
  }
}

Wrapper.childContextTypes = {
  translator: Translate.translatorType
};

http.createServer(function(req, res) {
  var queryData = url.parse(req.url, true).query;

  var translator = new Translator();
  translator.registerTranslations('en', en);
  translator.registerTranslations('de', de);
  translator.setLocale(req.locale || 'en');

  doAsyncStuffHere(function(err, data) {
    if (err) { return err; }

    var html = ReactDOMServer.renderToString(
      <Wrapper data={data} translator={translator} />
    );

    res.write(html);
  });
}).listen(3000);

An Advanced Example

The code for a more sophisticated example can be found in the repo's example directory. You can clone this repository and run make install example and point your web browser to http://localhost:3000. In case you are too lazy for that, we also have a live demo of the example app.

Contributing

Here's a quick guide:

  1. Fork the repo and make install.

  2. Run the tests. We only take pull requests with passing tests, and it's great to know that you have a clean slate: make test.

  3. Add a test for your change. Only refactoring and documentation changes require no new tests. If you are adding functionality or are fixing a bug, we need a test!

  4. Make the test pass.

  5. Push to your fork and submit a pull request.

Licence

Released under The MIT License.

Issues
  • Allow counterpart instances passed in via context

    Allow counterpart instances passed in via context

    When you use the component on the server side you have to create different counterpart instances to ensure consistent locale settings for every request.

    opened by nikuph 9
  • How to translate an attribute?

    How to translate an attribute?

    <input placeholder="what's up ?" />

    I only want to localize the "what's up ?" value.

    opened by AlSayedGamal 7
  • Possible EventEmitter memory leak detected

    Possible EventEmitter memory leak detected

    Hi, first of all thank you for this component. I appreciate your work.

    I'm trying it and it works. but I'm getting this in Chrome console.

    image

    In the image you can read this message:

    (node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit. 
    

    It´s a warning. But is anoying :(

    What i'm doing

    I've moved all my Ì18n` to a file this way:

    'use strict';
    
    var Translate = require('react-translate-component')
      , counterpart = require('counterpart')
      , I18nLib = {};
    
    /**
     * initialize translations
     */
    I18nLib.initialize = function () {
      this.registerTranslations('en', require('../../locales/en.js'));
      this.registerTranslations('en-US', require('../../locales/en'));
      this.registerTranslations('en-GB', require('../../locales/en'));
      this.registerTranslations('en-BB', require('../../locales/en'));
      this.registerTranslations('es', require('../../locales/es'));
      this.registerTranslations('es-ES', require('../../locales/es'));
      this.registerTranslations('es-AR', require('../../locales/es'));
      this.registerTranslations('ca', require('../../locales/ca'));
    };
    
    /**
     * this is a counterpart-style convenience function that
     * returns a React component
     *
     * @param {String} key
     * @return {Object}
     */
    I18nLib.t = function (key) {
      return Translate.translate(key);
    };
    
    /**
     * This generate a React Component for translations
     * Ex.: <Translate component={React.DOM.option}>this.is.a.key</Translate>
     *
     * @return {Object}
     */
    I18nLib.Translate = Translate;
    
    /**
     * Original translation library.
     * Helpful when you want a plain string and not a React component
     *
     * @return {string}
     */
    I18nLib.counterpart = counterpart;
    
    /**
     * Set current locale. It is a counterpart method
     *
     * @return {string}
     */
    I18nLib.getLocale = I18nLib.counterpart.getLocale;
    
    /**
     * Set current locale. It is a counterpart method
     *
     * @param {string} locale
     */
    I18nLib.setLocale = function (locale) {
      I18nLib.counterpart.setLocale(locale);
    };
    
    /**
     * Register translations.
     * Data is a file with translations
     *
     * @param {string} locale
     * @param {string} data
     */
    I18nLib.registerTranslations = function (locale, data) {
      I18nLib.counterpart.registerTranslations(locale, data);
    };
    
    module.exports = I18nLib;
    

    And then I use this way:

    <div className="col-lg-12">
      {<Translate>my.welcome.signin.description</Translate>}
    </div>
    ...
    <p>{I18n.t('my.welcome.services.s_3')}</p>
    

    Not a solution

    If I remove some of the translations I generate less than 10 event binding and the warning disappear. But that is not a real solution :)

    opened by andresgutgon 5
  • Default translation value

    Default translation value

    Hi,

    Consider a case where a translation is not available for a particular language. Is it possible to make it default to a particular language like english?

    Or at least is it possible to call a function if a translation is not available? So that for instance I can call my api to notify me if a translation has not been added yet.

    Thanks in advance, Jean

    opened by frg 5
  • translate returns an object instead of a string

    translate returns an object instead of a string

    when used outside the return statement of the React class render function, the react-translate-component's translate function returns an object where a string should be returned. code snippet within a React.creatClass: render:function() { var menusLns = this.getFooterMenu(); //getting a json file that describes my menus var i, j; var menus = []; var content;

        for (i = 0; i < menusLns.length; ++i) {
            var Menu = {};
            Menu.title = menusLns[i].id;
           Menu.items = "";
            for (j = 0; j < menusLns[i].menuitem.length; ++j) {
                var tagTitle = menusLns[i].menuitem[j].value;
                console.log("item tagTitle = " + tagTitle); // Ok
                var text = this._e("gui", tagTitle); // we are not within the return statement...
    

    // this._e comes from a mixin lib in which we have _e: require('react-translate-component').translate, // this syntax works nicely in another react component, but within its return statement console.log("item text = " + text); // KO: object Object var link = menusLns[i].menuitem[j].link; var item = '\u003cli\u003e\u003ca title="' + tagTitle + '" href="' + link + '"\u003e' + text + '\u003c/a\u003e\u003c/li\u003e\n'; console.log("menu item: " + item); Menu.items += item; } menus.push(Menu); }

        return (
            <  ....  >
        );
    }
    
    opened by ppet 5
  • Check for updated translate key on every render call

    Check for updated translate key on every render call

    Currently, if the key of an already mounted translate-component gets changed in-between render calls, the translate component sticks to the old translation key, because "this.key" has already been set. This change allows for updating translation keys of already mounted translate components.

    (This change is passing all the tests. Please tell me if you want me to write a new test for this.)

    opened by tobitos 5
  • Question: why not translate in the rendering engine

    Question: why not translate in the rendering engine

    Seems like one could apply translations in react's DOM patch before it hits the real DOM. A simple set of regular expressions would take most of the translations out of the code and move it into the external translation resources. This seems more consistent with modern localization software in my experience.

    Please comment... Thank you.

    opened by jcalfee 4
  • Translate tag spams warnings in react 15.2.0

    Translate tag spams warnings in react 15.2.0

    What did you do?

    use Translate component with the property content ex: <Translate className="label-content" content={ translateKey }/>

    What did you expect to happen?

    react 15.2.0 not to throw warnings

    What actually happened?

    spams Unknown Prop Warnings

    ex:

    warning.js:44 Warning: Unknown prop component on tag. Remove this prop from the element. For details, see https://fb.me/react-unknown-prop in span (created by Translate) in Translate (created by InputElement) ...

    What version of this module are you using?

    0.11.0

    opened by nitelite 4
  • How to avoid the default generated span tag

    How to avoid the default generated span tag

    Hello,

    First of all i would like to thank you for building this component, it is very much helpful!

    I am facing an issue, basically i wanted to display a country drop-down in my application, below is the code what i am using

    <select className="form-control" name="country" ref="country"  defaultValue={this.state.personData.countryCode} onChange={this.validateCountry} >
        <option value="empty-field">Country</option>
         {function () {
            var optionsTag = []
                    for(var key in countryData){
                        var langKey = 'digest.people.form.countryDropdown.'+ key;
                         optionsTag.push(<option key={key} value={key}><Translate {...this.props} content={langKey} /></option>);
                     }
                     return optionsTag;
                }()}
              </select>`
    

    The above code works but it creates an extra span tag inside every option.

    Can you suggest any best way to do this?

    Very much appreciated!

    Thanks, Dhiraj

    opened by powercoder23 4
  • Link to translate string

    Link to translate string

    Hello,

    I dont know how I can translate string with my component for example:

    module.exports = {
        i_agree_to_the_terms_of_user: "Souhlasím s <Link onClick={this.toUrl}>podmínkami užití</Link>"
    };
    

    Thanks you for your component!

    opened by knapeto 3
  • Add children to Translate

    Add children to Translate

    Hello,

    I'm trying to achieve the following HTML in React:

    <a className="collapsible-header">
    // the value on the next line is the text which needs translation
        language
        <i className="material-icons">arrow_drop_down</i>
    </a> 
    

    and I'm trying this:

    <Translate
         content="language"
         component="a"
         className="collapsible-header"
         children={
              <i className="material-icons">arrow_drop_down"</i>
          }
    />
    

    But the "i" tag isn't rendered.

    If the "children" isn't supported, is there another way for this ?

    opened by davidlubomirov 1
  • a default message when the translator is not defined

    a default message when the translator is not defined

    Hello, how can I set a default message instead of "missing translation" ?

    opened by AliMohammad93 1
  • Translation won't work if string contains `.`

    Translation won't work if string contains `.`

    Support I have string

    Explore and get a feel for the game before moving onto micro-level. You can play for 1 hour for just ₹50. And win lot more.

    If I apply translation for this string it is not working, My observation is beacuse string contains . and translation is not working after ., Any comments or suggestion on this.

    opened by erpardeepjain 2
  • Make Language Switcher work with

    Make Language Switcher work with "counterpart.translate"

    Hey there! I am currently working on my first real react project, and stumbled over a little problem. Once I implemented a language changer, it perfectly changes the translation of all Elements, but not of all elements that use counterpart.translate. I have some Elements in my Code, where I can't use the Element, so I had to use counterpart.translate. Here is an example:

    <Link to="/blog" className="footer-link">{counterpart.translate("footer.community.blog")}</Link>
    

    Does anybody haven an Idea on how I could refresh those as well?

    opened by OfficialCRUGG 1
  • Question: Where to put translations?

    Question: Where to put translations?

    Hey.

    First of all, nice work. I like how simple your solution to this problem is.

    I have created my app with create-react-app and I am using ES2015 features. Where would you propose to put the calls to registerTranslations if not in my index.js file. I would like to have separate files holding my translations, which are then loaded with the app. Also, I'm not too sure how to include these separate files?

    Also, do you suggest to load all translations when the app is loaded? Could these become a heavy task with large amounts of text?

    Thanks!

    opened by andersravn 2
  • Missing translation could output given string

    Missing translation could output given string

    In my code I have some cases where the string is pass to a function. I use Translate within this function to make it easier but some string are not keys of the translation file. Would be nice to have an option to tell the system to just output the string as is.

    opened by micky2be 10
  • Return translation as string

    Return translation as string

    Hi,

    I wanna use this package in combination with https://github.com/nfl/react-helmet to dynamically set localized page titles. The <Helmet /> component only accepts strings... What do you think of exposing a utility function which returns just a string getTranslationAsString? I could imagine that this might useful also for other uses cases.

    I can provide a pr, if you think this makes sense.

    opened by raoulus 11
  • Problem in select option

    Problem in select option

    Hello,

    From this issue : https://github.com/martinandert/react-translate-component/issues/20

    I'm trying to translate my option select.

    But with :

    <Translate {...this.props} component="option" key={0} value={0} content={"key_lang"} />

    It working well but i get a warning :

    warning.js:36 Warning: Unknown prop coverComponent on

    Thanks

    opened by briva 1
  • Improvement - JSX examples

    Improvement - JSX examples

    Great job! I have my translation up and running! I suggest making examples available in JSX too. Let me know if you are taking PRs, I could make an example/demo with jsx!

    Thank you!

    opened by okbel 3
  • How would you translate something that had a link?

    How would you translate something that had a link?

    Some of our localized strings include markup because the actual translation process results in words (or words that we would like to have marked up) that change position

    For example:

    In English:

    Use <a href="http://www.google.com/chrome" target="_blank">Chrome</a> or <a href="http://www.mozilla.org" target="_blank">Firefox</a> to stream your recordings directly.
    

    In German:

    Streamen Sie Ihre Aufzeichnungen direkt in <a href="http://www.google.com/chrome" target="_blank">Chrome</a> oder <a href="http://www.mozilla.org" target="_blank">Firefox</a>.
    

    But these tags get stripped when I use this library, and i understand its an issue with setting markup directly with react. Breaking the phrase up in to smaller parts does not solve the issue because the composition of the parts depends on the language.

    Any thoughts on how such a case could be solved?

    opened by mattotodd 4
Owner
Martin Andert
Martin Andert
The next generation state management library for React

The next generation state management library for React

Bytedance Inc. 150 Sep 18, 2021
HTML to React parser that works on both the server (Node.js) and the client (browser):

HTML to React parser that works on both the server (Node.js) and the client (browser):

Mark 1k Oct 19, 2021
React-Godfather aims to explore an alternative mental model for function components.

React-Godfather "Look ma, no Hooks!" React-Godfather aims to explore an alternative mental model for function components. It adds a thin layer between

John Kapolos 17 Sep 20, 2021
A lightweight react library that converts raw HTML to a React DOM structure.

A lightweight react library that converts raw HTML to a React DOM structure.

Arve Knudsen 652 Oct 17, 2021
iOS Today Widget in React Native

React Native Today Widget Experimental library investigating limits of implementation iOS App Extensions using React Native. Sample result from Comple

Matěj Kříž 345 Oct 21, 2021
Cookie cutter react portfolio suited perfectly for Github Pages

Cookie cutter react portfolio suited perfectly for Github Pages, get up and running in less then 5 minutes, just edit the data.json file and off you go!

null 37 Sep 9, 2021
🌊🐷 Utility for generalized composition of React components

???? Utility for generalized composition of React components

Enki 279 Oct 4, 2021
CSS media queries for React

react-media react-media is a CSS media query component for React. A <Media> component listens for matches to a CSS media query and renders stuff based

React Training 2.4k Oct 15, 2021
A toy virtual DOM diffing and reconciliation algorithm, with a simple event loop

A toy virtual DOM diffing and reconciliation algorithm, with a simple event loop

Mauro Cano 1 Oct 9, 2021
📏 A resizable component for React.

?? A resizable component for React.

bokuweb 1.5k Oct 17, 2021
Collection of Google fonts as typeface data for usage with three.js, react-three-fiber, and other tools.

Collection of Google fonts as typeface data for usage with three.js, react-three-fiber, and other tools.

Components AI 44 Oct 3, 2021
Easily create presentation board using React

React Speaker Board Easily create presentation board using React. Quick Usage It install react-speaker-board running this comand. $ yarn add react-spe

nappa 6 Oct 14, 2021
A ReactJS password recovery box component built using the FluentUI library

A ReactJS password recovery box component built using the FluentUI library

Boia Alexandru 2 Sep 20, 2021
@custom-elements-manifest/analyzer plugin to ✨ automatically ✨ create react wrappers for your custom elements

cem-plugin-reactify @custom-elements-manifest/analyzer plugin to automatically create React wrappers for your custom elements based on your custom ele

Pascal Schilp 7 Oct 6, 2021
Utility Components for determining whether elements are in the center of the screen.

@n1ru4l/react-in-center-of-screen Utility Components for determining whether elements are in the center of the screen. Codesandbox Quick Demo: https:/

Laurin Quast 12 May 29, 2021
🤖 React Native Android widgets bridged to JS, a proof of concept

React Native Android Widget Proof Of Concept ?? Using React Native and having Android widgets is possible. Create buttons in Java / Android XML to tri

Netbeast 186 Sep 21, 2021
Truncate a long string in the middle, instead of the end.

React Middle Ellipsis Check out the demo. Adding ellipses to the end of long text is cool. But not always! Sometimes the end of the text contains vita

bluepeter 41 Sep 8, 2021
🔌 "Plug and play" for RxJS Observables in React Apps!

"Plug and play" for RxJS Observables in React Apps! npm install @ngneat/react-rxjs useObservable Ever had an Observable holding data that you need to

ngneat 12 Oct 11, 2021
A Fetch Library Support React New Suspense SSR

use-suspense-fetch A data fetching library for React Suspense. inspired by use-asset Feature use LRU Cache support create custom cache support React 1

Snake 6 Jul 12, 2021