# Internationalization Grafana uses the [i18next](https://www.i18next.com/) framework for managing translating phrases in the Grafana frontend. ## tl;dr **Please note:** We do not currently accept contributions for translations. Please do not submit pull requests for grafana.json files - they will be rejected. - Use `Go to {{ pageTitle }}` in code to add a translatable phrase - Translations are stored in JSON files in `public/locales/{locale}/grafana.json` - If a particular phrase is not available in the a language then it will fall back to English - To update phrases in English, edit the default phrase in the component's source, then run `yarn i18n:extract`. Do not edit the `en-ES/grafana.json` or update the english phrase in Crowdin - To update phrases in any translated language, edit the phrase in Crowdin. Do not edit the `{locale}/grafana.json` ## How to add a new translation phrase ### JSX 1. For JSX children, use the `` component from `app/core/internationalization` with the `i18nKey`, ensuring it conforms to the guidelines below, with the default english translation. e.g. ```jsx import { Trans } from 'app/core/internationalization'; const SearchTitle = ({ term }) => ( Results for {{ term }} ); ``` Prefer using `` for JSX children, and `t()` for props and other javascript usage. When translating in grafana-ui, use a relative path to import `` and `t()` from `src/utils/i18n`. Note that our tooling must be able to statically analyse the code to extract the phrase, so the `i18nKey` can not be dynamic. e.g. the following will not work: ```jsx const ErrorMessage = ({ id, message }) => There was an error: {{ message }}; ``` 2. Upon reload, the default English phrase will appear on the page. 3. Before submitting your PR, run the `yarn i18n:extract` command to extract the messages you added into the `grafana.json` file and make them available for translation. ### Plain JS usage Sometimes you may need to translate a string cannot be represented in JSX, such as `placeholder` props. Use the `t` macro for this. ```jsx import { t } from "app/core/internationalization" const placeholder = t('form.username-placeholder','Username'); return ``` Interpolating phrases is a bit more verbose. Make sure the placeholders in the string match the values passed in the object - there's no type safety here! ```jsx const placeholder = t('page.greeting', 'Hello {{ username }}', { username }); ``` While the `t` function can technically be used outside of React functions (e.g, in actions/reducers), aim to keep all UI phrases within the React UI functions. ## How to add a new language 1. Add new locale in Crowdin and sync files to repo 1. Grafana OSS Crowdin project -> "dot dot dot" menu in top right -> Target languages 2. Grafana OSS Crowdin project -> Integrations -> Github -> Sync Now 3. If Crowdin's locale code is different from our IETF language tag, add a custom mapping in Project Settings -> Language mapping 2. Update `public/app/core/internationalization/constants.ts` (add new constant, and add to `LOCALES`) 3. Update `public/locales/i18next-parser.config.js` to add the new locale to `locales` 4. Run `yarn i18n:extract` and commit the result ## How translations work in Grafana Grafana uses the [i18next](https://www.i18next.com/) framework for managing translating phrases in the Grafana frontend. It: - Marks up phrases within our code for extraction - Extracts phrases into messages catalogues for translating in external systems - Manages the user's locale and putting the translated phrases in the UI English phrases remain in our Javascript bundle in the source components (as the `` or `t()` default phrase). At runtime, we don't need to load any messages for en-US. If the user's language preference is set to another language, Grafana will load that translations's messages JSON before the initial render. ### Phrase ID naming convention We set explicit IDs for phrases to make it easier to identify phrases out of context, and to track where they're used. IDs follow a naming scheme that includes _where_ the phrase is used. The exception is the rare case of single reoccuring words like "Cancel", but default to using a feature/phrase specific phrase. Message IDs are made of _up to_ three segments in the format `feature.area.phrase`. For example: - `dashboard.header.refresh-label` - `explore.toolbar.share-tooltip` For components used all over the site, use just two segments: - `footer.update` - `navigation.home` ### I18next context We rely on a global i18next singleton (that lives inside the i18next) for storing the i18next config/context. ## Examples See [i18next](https://www.i18next.com/) and [react-i18next](https://react.i18next.com/) documentation for more details. ### Basic usage For fixed phrases: ```jsx import { Trans } from 'app/core/internationalization'; Hello user!; ``` To interpolate variables, include it as an object child. It's weird syntax, but Trans will do it's magic to make it work: ```jsx import { Trans } from 'app/core/internationalization'; Hello {{ name: user.name }}!; const userName = user.name; Hello {{ userName }}!; ``` Variables must be strings (or, must support calling `.toString()`, which we almost never want). ```jsx import { Trans } from 'app/core/internationalization'; // This will not work const userName = user.name; Hello {{ userName }}!; // Instead, put the JSX inside the phrase directly const userName = user.name; Hello {{ userName }}! ; ``` ### React components and HTML tags Both HTML tags and React components can be included in a phase. The Trans function will handle interpolating it's children properly ```js import { Trans } from "app/core/internationalization" Click to learn more. // ↓ is in the grafana.json file like ↓ { "page": { "explainer": "Click <0>here to <1>learn more" } } ``` ### Plurals Plurals require special handling to make sure they can be translating according to the rules of each locale (which may be more complex that you think!). Use the `` component, with the `count` prop. ```js import { Trans } from 'app/core/internationalization'; You got {{ count: messages.length }} messages. ; ``` Once extracted with `yarn i18n:extract` you will need to manually fill in the grafana.json message catalogues with the additional plural forms. See the [react-i18next docs](https://react.i18next.com/latest/trans-component#plural) for more details. ## Documentation [Grafana's documentation](https://grafana.com/docs/grafana/latest/) is not yet open for translation and should be authored in American English only.