Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions docs/i18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,127 @@ the JSON to the repo.
# build the JSON.
NODE_APP_INSTANCE=[MY_APP] bin/build-locales
```

## Setting up translations

To set up a component to be translated there are two pieces of code to know
about.

### Jed

We use [Jed](https://slexaxton.github.io/Jed/) as the API for providing
`gettext` functions inside React components. An initialized `Jed` instance
has all the `gettext` related functionality exposed as methods. There is a
fancy chained API but we've stuck to a more traditional approach.

Before we get into how to make use of these functions let's take a look at
how the Jed instance is exposed to our components.

### The Translation Provider

The translation provider is used to pass down a Jed instance via context to
components lower down in the component hierarchy. This part is already done
for you in addons-frontend. So you should only need to worry
about wrapping your components as detailed in the next section.

## The translate component wrapper.

The translate Higher Order Component is a helper that wraps any component
and takes the Jed `i18n` instance from context and makes it available in
the wrapped component's props.

Here's an example of a basic component setup for translation:


```javascript
import React, { PropTypes } from 'react';
import translate from 'core/i18n/translate';


export class MyTranslatedComponent extends React.Component {
static propTypes = {
i18n: PropTypes.object.isRequired,
}

render() {
const { i18n } = this.props;
return (
<div>
<p>{i18n.gettext('Something translated')}</p>
</div>
);
}
}

export default translate()(MyTranslatedComponent);
```

That's pretty much all there is to it.

## Using the Jed API

Once you have `i18n` available to your component you can then use
any of the Jed methods exposed on the `i18n` object.

```javascript
gettext = function ( key )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so handy to have right in the translation docs, thanks! 👍

dgettext = function ( domain, key )
dcgettext = function ( domain, key, category )
ngettext = function ( singular_key, plural_key, value )
dngettext = function ( domain, singular_ley, plural_key, value )
dcngettext = function ( domain, singular_key, plural_key, value, category )
pgettext = function ( context, key )
dpgettext = function ( domain, context, key )
npgettext = function ( context, singular_key, plural_key, value )
dnpgettext = function ( domain, context, singular_key, plural_key, value )
dcnpgettext = function ( domain, context, singular_key, plural_key, value, category )
sprintf = function ( string, substitutions)
```

### Using `sprintf`

As you can see a sprintf function is also available. You can use this to
provide substitutions in gettext wrapped strings.

There are two flavours to this, numbered placeholders or named ones.

Here's the numbered approach:

```javascript
i18n.sprintf(i18n.gettext('I like your %1$s %2$s.'), 'red', 'shirt'));
```

and here's the named arg approach:

```javascript
i18n.sprintf(i18n.gettext('I like your %(colour)s %(garment)s.'), { colour: 'red', garment: 'shirt' }));
```

Both of these approaches allow for translators to re-order the substitution
vars.

### Guidance on HTML in translations

Generally we're looking to avoid having HTML in the middle of translation
strings as much as possible.

If you need HTML it's better to use substitutions to add the HTML than
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you include an example of this? It's not how we did it in Django if I recall correctly, and while it sounds safer it also sounds a lot more complicated in my head.

leave HTML in the translation. Take the following string as an example:

```javascript
i18n.gettext('Take a look at the <a href="#">documentation</a>');
```

Using `sprintf` we can provide use start and end substitutions. This way
there's no HTML in the extracted string.

```javascript
i18n.sprintf(i18n.gettext(
'Take a look at the %(start_link)sdocumentation%(end_link)s'),
{ start_link: '<a href="#">', end_link: '</a>' });
```

You can also use DOMPurify to sanitize strings that may contain HTML
following substitutions so that anything not explicitly allowed is removed.
DOMpurify will also help protect against malformed HTML in case opening
and closing tag substitutions vars get swapped around inadvertently.