React (JSX/TSX) internationalization

Important! Always configure both React and plain JavaScript sources together.

<p>Hello world!</p><p>{t('helloWorld')}</p>
<!-- locales/en.js: helloWorld: 'Hello world!' -->

<p>Hello, {user}!</p><p>{t('hello', {user: user})}</p>
<!-- locales/en.js: helloWorld: 'Hello, {{user}}!' -->

Features supported

Configure hardcoded strings extraction from React (JSX/TSX) templates

The plugin should automatically configure itself for project with dependencies on react-i18next, but adjustments could be needed for custom setup.

Ract Source Code Preferences screenshot

Scope

i18n Ally is applying inspections for files that have .js/.jsx/.tsx extension and are included into a PhpStorm’s scope.

Create a new scope or adjust existing by clicking on button and handpicking only the meaningful directories and files.

Select Project files to include all .js/.jsx/.tsx files in your project.

Inline tags

Extraction of strings with a special treatment of inline tags is not currently supported.

Translatable attribute names

Translatable attributes are also checked for the translatable text:

<img src="…"
     alt="Checked by default"
     title="Checked by default"
     data-content="Requires configuration" />

You can add custom attributes, like data-content, by appending a new attribute to the comma-separated list.

Replacement template

The “Replacement template” reflects the result of the hardcoded string extraction.function name and arguments template.

Recommended value for react-i18next packages: t('%namespace%:%key%', %map%).

%key%

Short key or a natural language string that defines a translation.

%namespace%

Namespace (called ‘domain’ in Symfony) usually means a part of language file path from where translations would be searched for. The default namespace is usually messages, but could be changed by putting a namespace in first position in “Namespaces” field.

%map%

If there are no variables in the string, then nothing would be added.

Map will be replaced with an object if there are any placeholders detected: t('namespace:key', {foo: fooVariable, bar: barVariable}).

Placeholder names will be determined automatically based on a respective variable, function or method name.

In language files placeholder syntax will be determined based on the Placeholder format setting of the language file.

%list%

If there are no variables in the string, then nothing would be added.

List will be replaced with an array if there are any placeholders detected: t('namespace:key', [fooVariable, barVariable]).

In language files the ordered placeholder syntax {0}, {1} will be enforced.

%varargs%

If there are no variables in the string, then nothing would be added.

Varargs will be replaced with placeholder passed directly to the translation function if there are any placeholders detected: t('namespace:key', fooVariable, barVariable).

In language files the ordered placeholder syntax {0}, {1} will be enforced.

Supported language constructs

All strings inside tags and translatable attributes are checked.

What strings are skipped

  • All attributes except ones listed in “Translatable attribute names” preference.
  • Strings that looks like code: without letters, multiple words without spaces or camelCased ones.

Best practice: dealing with branching in messages

It’s common to have small and simple branching for presentation purposes:

return <>Webhook {isSuccess ? 'succeeded' : 'failed'}.</>;

The best practice it to separate this message into two different ones so translators would have a full context and would be able to adjust word order according the target language grammar.

1st step: manually extract the condition out of the message to get two messages without condition

return isSuccess
  ? return <>Webhook succeeded.</>
  : return <>Webhook failed.</>;

2nd step: replace simple messages with i18n Ally

return isSuccess
  ? return <>{t('webhookSucceeded')}</>
  : return <>{t('webhookFailed')}</>;