I18N libraries and tools for your react application.
JavaScript Shell
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
bin
doc
samples/app
scripts
src
tests
.babelrc
.eslintignore
.eslintrc
.flowconfig
.gitignore
.travis.yml
CHANGELOG.md
CONTRIBUTING.md
LICENSE
README.md
package.json

README.md

@gandi/react-translate

I18N libraries and tools for your react application.

build status npm version npm downloads #gandi on freenode

Features:

  • String localization with counterpart;
  • Messages extraction;
  • Catalogs management (similar to gettext);
  • Date & Time formats with momentjs;
  • Prices and number format with intljs;
  • Several components & functions.

Installation

npm install --save @gandi/react-translate

Add the following script to your package.json, it's a simple shortcut for extraction scripts:

// ...
"scripts": {
  // ...
  "i18n": "react-translate-scripts"
}

Extraction script requires babel-cli, babel-gettext-plugin and po2json in your dependencies (it's up to you to select the version saved in your project):

npm install --save-dev babel-cli babel-gettext-plugin po2json

Usage

1. init: provideTranslate

Provide some helpers functions in the React context.

It's up to you to load translations from the json generated by scripts (cf. [How to create language files]). So you will be able to promise a json object or preload translations directly in the DOM (e.g. via server side rendering).

import { provideTranslate, createTranslator } from '@gandi/react-translate';

const translatorParams = {
  translations: {       // catalog
    'This is a test %(username)s!': 'C\'est un test %(username)s!',
  },
  locale: 'fr',         // user's locale
  utcOffset: 0,         // user's zone offset
  defaultLocale: 'en',  // default application locale
  logMissing: false,    // display warnings when translations are missing (except on production)
  localeData: {         // IntlPolyfill localeData configuration
    locale: 'fr',
    number: {
      currencies: {
        USD: '$US',
        EUR: '',
      },
    },
  },
};

const translator = createTranslator(translatorParams);

@provideTranslate(translator)
class App {
  render() {
    ...
  }
}

2. Implementation: withTranslator(options)

Inject helpers into the components props from context variables.

import React, { Component, PropTypes } from 'react';
import { withTranslator } from '@gandi/react-translate';

@withTranslator()
class MyComponent extends Component {
  static propTypes = {
    __: PropTypes.func.isRequired,
  };

  static preFetch({ __ }) {
    console.log(__('So i18n'));
  }

  render() {
    const { __ } = this.props;
    return (<div>{ __('So i18n') }</div>);
  }
}

In order to prevent the unknown props warning (and non-standard DOM attributes):

bundle.js:2009 Warning: Unknown props `formatDate`, `formatNumber`, `formatPrice`, `formatCurrency`, `localize`
on <button> tag. Remove these props from the element. For details, see https://fb.me/react-unknown-prop

you can use the propsNamespace option:

@withTranslator({ propsNamespace: 'translator' })
class Presenter extends React.Component {
  static propTypes = {
    translator: PropTypes.shape({ __: PropTypes.func.isRequired }),
  };

  render() {
    const { translator: { __ }, ...props } = this.props;

    return <Button {...props}>{__('foobar')}</Button>;
  }
}

3. Create language files

This library uses babel and the plugin babel-gettext-plugin to extract your internationalized strings.

In case you created your app with create-react-app you have to declare the babel preset you need to build your application, for example:

// file:.babelrc
{
  "presets": ["react"]
}

Message files

First create a template message file with all the translation strings in a json:

npm run i18n extract_messages [folder/to/extract] [namespace]

(All commands are launched from the root directory of your node project.)

Catalogs

A catalog file is a json file, representing a single language.

Before the first run you'll need to create one folder per needed locale, for instance:

mkdir locales/{fr,it,es}

After you created your message file – and each time you make changes to it – you’ll need to create or update the catalogs:

npm run i18n create_catalog && npm run i18n update_catalog

Sometimes you must clean the catalogs by running this command:

npm run i18n clean_catalog

Tips: In case you use a tool like weblate, you may not clean every time you are extracting messages or updating catalogs to avoid losing old translations that could be used by the tool to suggest translations.

Documentation

Tests

More in testing documentation.

You have various ways to stub your translator.

import { createTranslateContext, createTranslateContextTypes }
  from '@gandi/react-translate/dist/test';

describe('...', () => {
  it('...', () => {
    const wrapper =  mount(<Component />, {
      context: createTranslateContext(),
      childContextTypes: createTranslateContextTypes(),
    });
  });
});
import { stubProvideTranslate } from '@gandi/react-translate/dist/test';

describe('LocalLoader component', () => {
  it('should render a spinner', () => {
    const LocalLoader_ = stubProvideTranslate({ locale: 'fr' })(LocalLoader);
    // ...
  });
});

Components examples

@gandi/react-translate comes bundled wit some components:

<div>
  <DateTime>1982-03-28 12:00:00 UTC</DateTime>
  <FromNow>1982-03-28 12:00:00 UTC</FromNow>
</div>

To use localization programmatically, just do:

import React, { Component, PropTypes } from 'react';
import { withTranslator } from '@gandi/react-translate';

@withTranslator()
class MyComponent extends Component {
  static propTypes = {
    localize: PropTypes.func.isRequired,
  };

  static preFetch({ localize }) {
    console.log(localize(new Date()).format('LLLL'));
  }

  render() {
    const { localize } = this.props;
    return (<div>{ localize(new Date()).format('LLLL') }</div>);
  }
}

App samples

Check the samples/app directory to see a "real" application in action.

cd samples/app
npm i
npm run start

Change Log

All notable changes to this project will be documented in this section.

This project adheres to Semantic Versioning and Keep A Changelog.

Patrons

The work on this project was possible thanks to Gandi.net (#no_bullshit).

Found a bug or contribute?

Please open an issue. If it's clear and well labelized, it's quicker to fix!

Else you can start with CONTRIBUTING.md.

TODO

  • Document Pricing features
  • Add formatCurrency component
  • Improve doc on how to load translations (at least give an example)
  • Link translator options to related libs
  • Allow user to init a logger for missing translations and use warning as fallback
  • Fix or change build process in bin/merge_catalogs which looks for legacy gandi's catalogs