-
Notifications
You must be signed in to change notification settings - Fork 400
Add docs re: component translation #775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
muffinresearch
merged 2 commits into
mozilla:master
from
muffinresearch:add-docs-for-translations
Jul 18, 2016
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 ) | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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! 👍