GithubHelp home page GithubHelp logo

martinandert / react-translate-component Goto Github PK

View Code? Open in Web Editor NEW
321.0 9.0 31.0 72 KB

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

License: MIT License

JavaScript 93.94% Makefile 3.63% Ruby 2.42%

react-translate-component's Introduction

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.

react-translate-component's People

Contributors

martinandert avatar raoulus avatar tobitos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-translate-component's Issues

Update to react 0.13

I'm using react 0.13 but the peerDependencies lock to react 1.20:

"peerDependencies": {
  "react": "^0.12.1",
  "counterpart": "^0.16.4",
  "react-interpolate-component": "^0.6.2"
},

Is it possible to upgrade to the last react version ?

Add support for html in fallback attribute

Is it possible for the fallback attribute to accept html content?

<Translate content="core.helloworld" fallback="<h1>Translation not found for "hello world".</h1>" />

Getting a nasty error when using `fallback`

It seems fallback is missing from the propTypes and React is trowing the following error:

Warning: Unknown prop `fallback` on <span> tag. Remove this prop from the element. For details, see https://fb.me/react-unknown-prop
    in span (created by Interpolate)
    in Interpolate (created by Translate)
    in Translate (created by FolderEntry)
    in div (created by Button)
    ...

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?

Dependencies are not installed automatically?

When following the instructions from the quick-start tutorial, I get the following error:

$ npm install react-translate-component --save
[email protected] /Users/Rijk/Code/Playground/React/translate
├── UNMET PEER DEPENDENCY counterpart@^0.17.0
├── UNMET PEER DEPENDENCY react@^15.0.0
├── UNMET PEER DEPENDENCY react-interpolate-component@^0.10.0
└─┬ [email protected] 
  └── [email protected] 

npm WARN [email protected] requires a peer of react@^15.0.0 but none was installed.
npm WARN [email protected] requires a peer of counterpart@^0.17.0 but none was installed.
npm WARN [email protected] requires a peer of react-interpolate-component@^0.10.0 but none was installed.

Is this correct? Do I have to install them manually (the guide does not mention this)?

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?

Support HTML content

At the moment it's not possible to set html content in the translation values.

If I use:

<div dangerouslySetInnerHTML={{__html: _t('my_translation')}} />

it just renders [Object object] instead of the translated string.

Problem when interpolating numbers

Finally in the correct repo. Here is my issue.
I have the following setup for a table footer with pagination info:

Translations:

app: {
  footer: 'Zeige Einträge %(start)s bis %(end)s von %(total)s'
}

I use the component like this:

<Translate content="app.footer"
  start={this.state.skip + 1}
  end={this.state.skip + this.state.top}
  total={this.state.total}
/>

The translation works perfectly on first load, but when I change the values for either top or skip the calculation fails, instead the numbers get concatenated like strings. See screenshot:
screenshot

Am I missing something? Is there a possibility to interpolate as a number, like %(start)n instead of
%(start)s?

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 ?

Expose onLocaleChange from counterpart

Hi,

in my app I need to have a listener when the locale changes (in order to switch custom monetary formats). Currently I have to do this directly with counterpart, but I think it would be useful to expose this functionality inside the react-translate-component as well.

module.exports.onLocaleChange = translator.onLocaleChange.bind(translator);

What do you think?

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.

Escape the % Character

Hi!,

When the JSON string has a "%" character the translator fails, raise the following error in console:

Uncaught SyntaxError: [sprintf] unexpected placeholder.

example JSON translate:

{
  "fee": "The fee is 4%"
}

Problem in select option

Hello,

From this issue : #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 tag. Remove this prop from the element. For details, see https://fb.me/react-unknown-prop

Thanks

How many languages can this support?

This component looks good for the 1-10 languages range, but is it intended to work/scale with 20-50 languages?

When some one does choose a new language, is that an http call or is the second language simply part of the minified javascript app?

cookies

Hi
tell me how to add cookies?
so that when switching, the language is not reset but remains selected

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

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!

How to translate placeholder attribute in input element

I have an input element like this one:
<input type="email" id="inputEmail" className="form-control" placeholder="Email address" required/>

I've tried to translate it in this way:
<Translate component="input" type="email" id="inputEmail" className="form-control" placeholder="login_page.email_address" required/>

In result I expected to have translated placeholder, but I've got:
<input type="email" id="inputEmail" class="form-control" placeholder="login_page.email_address" required="">

What am I doing wrong?

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

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.

Translate without <span> tag

Is there a way to translate without outputting the <span> tag wrapper?
Anyway to configure this globally? Thanks.

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

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.

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

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!

Can't translate from Array

I'm trying to translate from an Array
For instance:

en.js

module.exports = {
    foo: {
        bar: [
            {
                lorem: "ipsum",
                some: "text"
            }
        ]
    }
};

component.jsx

<Translate content="foo.bar[0].lorem" />

At this point, all I get is

missing translation: en.foo.bar[0].lorem

Is there a way to achieve this?

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 (
        <  ....  >
    );
}

Right to left text direction languages (Farsi, Arabic, Hebrew etc.)

Hi,
I'm using this component, works great for our app. Thanks so much for making it.
One issue I'm having is that I would like to support some languages with right-to-left text direction (Farsi at the moment, Arabic in the near future, possibly others). HTML "sees" strings as left-to-right by default, but you can set the text direction to right-to-left using CSS:
.myParagraph { direction: rtl; }
Otherwise, if HTML "sees" text as right to left, then it automatically puts full stops and question marks on the right side of paragraphs (what it "sees" as the end of the sentence) instead of the left.
So one way this could be done is to add some global state to our app that sets the text direction, and then the style of every single text-component in the app will respond to that app-level state. While that seems like a conceivable solution, it feels hacky and like a lot of work.
Is there any thing in react-translate-component that can make this easier and prettier?
Thanks!

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.

Can't make dynamic translation work using `with`

I have a simple label "Inbox": "Inbox [%(unread)]" showing the number of unread message.
I setting the props with like so { unread: nbUnreadMsg || 0 } but the display stay as Inbox [%(unread)]

This number can change dynamically of course and will trigger a rendering on this comonent.
Am I missing something here?

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!

Any plans to update peer deps?

We just updated our main project to react 16 and latest counterpart now react-translate-component is unhappy about unmatched peer deps. Are there plans to update?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.