---
title: "How to Work on Localized Client Webapp"
---

> Documentation Index
> Fetch the complete documentation index at: https://contribute.freecodecamp.org/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Work on Localized Client Webapp

:::tip
You'll need this guide if you're working on translating or localizing the freeCodeCamp platform into different languages. If you're working on curriculum, documentation, or other areas, you can skip this guide.
:::

The React-based client web app that powers our learning platform is built using Gatsby. It is translated into various world languages using [react-i18next](https://react.i18next.com/) and [i18next](https://www.i18next.com/).

You can learn more about setting up the client application locally for development by following [our local setup guide here](/how-to-setup-freecodecamp-locally/). By default, the application is available only in English.

Once you have set up the project locally you should be able to follow this documentation to run the client in the language of your choice from the list of available languages.

This could be helpful when you are working on a feature that specifically targets something that involves localization, and requires you to validate for instance a button's label in a different language.

Let's understand how the i18n frameworks and tooling work.

## File Structure

English source files and each language's `links.json` are maintained in [`client/i18n/locales`](https://github.com/freeCodeCamp/freeCodeCamp/tree/main/client/i18n/locales). Non-English versions of `intro.json`, `translations.json`, `meta-tags.json`, and `motivation.json` are maintained in the [i18n-curriculum repository](https://github.com/freeCodeCamp/i18n-curriculum/tree/main/client), which is included as a submodule at `curriculum/i18n-curriculum`.

The tracked files are organized as follows, relative to the repository root:

- client
  - i18n
    - config-for-tests.ts
    - config.js
    - locales
      - arabic
        - links.json
      - english
        - intro.json
        - links.json
        - meta-tags.json
        - motivation.json
        - translations.json
      - .../
    - locales.test.ts
    - schema-validation.ts
- curriculum
  - i18n-curriculum
    - client
      - arabic
        - intro.json
        - meta-tags.json
        - motivation.json
        - translations.json
      - .../
- packages
  - shared
    - src
      - config
        - i18n.ts

During setup, `client/tools/create-i18n.ts` copies the four non-English JSON files for `CLIENT_LOCALE` from the submodule into `client/i18n/locales/<language>/`. These copies are Git-ignored and overwritten when setup runs again. Setup also generates `trending.json` and `search-bar.json`.

Non-English content for the following four files comes through the translation pipeline via the i18n-curriculum submodule. Their English source files remain in the main repository.

**Non-English files supplied through the submodule:**

- The `translations.json` file contains the majority of the text that appears on the user interface elements. The keys are used in the codebase to get the correct text for whatever language is set. English is the fallback when a translation is missing.

- The `intro.json` file contains the key-value pairs for the introduction text on the certification pages.

- The `motivation.json` files are not required to have the same quotes, compliments, or array length. Just the same JSON structure.

- The `meta-tags.json` file contains the information for our website's meta tag information.

**Files maintained in the main repository for every language:**

- The `links.json` file contains URLs used by the client, including links to localized resources.

Update localized links in `client/i18n/locales/<language>/links.json`. For help with content supplied through the submodule, ask in the [contributors chat room](https://discord.gg/PRyKn3Vbay).

## Testing the Client App in a World Language

You can test the client app in any language available in the [list of `availableLangs` here](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/packages/shared/src/config/i18n.ts).

```js {3-14}
export const availableLangs = {
  client: [
    Languages.English,
    Languages.Espanol,
    Languages.Chinese,
    Languages.ChineseTraditional,
    Languages.Italian,
    Languages.Portuguese,
    Languages.Ukrainian,
    Languages.Japanese,
    Languages.German,
    Languages.Swahili,
    Languages.Korean,
    Languages.Arabic
  ],
  curriculum: [
    ...
  ]
};
```

From the repository root, initialize the translation submodule. This is required for a non-English client even if you keep the curriculum in English:

```bash
git submodule update --init curriculum/i18n-curriculum
```

Set `CLIENT_LOCALE` in your `.env` file to the language's value in the `Languages` enum, for example `CLIENT_LOCALE=arabic`. `CURRICULUM_LOCALE` controls the challenge content separately and can remain `english` when you are testing only the UI.

Run `pnpm run clean-and-develop` to apply the locale setting and copy the translation files.

### Testing a New Language

For a language that is not yet configured, prepare these additional files before starting the client:

1. Add the language to the `Languages` enum and `availableLangs.client` in [`packages/shared/src/config/i18n.ts`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/packages/shared/src/config/i18n.ts). Follow the comments to update the language codes and display name. Add right-to-left languages to `rtlLangs`; use `hiddenLangs` to keep a language out of the selector until launch.
2. Copy the English `links.json` to `client/i18n/locales/<language>/links.json` and update URLs where localized resources exist.
3. Ensure `curriculum/i18n-curriculum/client/<language>/` contains `intro.json`, `translations.json`, `meta-tags.json`, and `motivation.json`. For local testing before translations exist, copy those four English files there. Changes intended for publication in that directory belong in the separate i18n-curriculum repository.
4. Add an entry to `algoliaIndices` in [`client/src/utils/algolia-locale-setup.ts`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/src/utils/algolia-locale-setup.ts). Use the English entry's values if the language has no news publication. Setup reads this entry when generating the search placeholder.
5. If the language does not yet have a trending file on the CDN, copy the generated `client/i18n/locales/english/trending.json` from your English setup to `client/i18n/locales/<language>/trending.json`. The download script uses this local file as a fallback in development. Production requires a `build/universal/trending/<language>.yaml` file in the [CDN repository](https://github.com/freeCodeCamp/cdn/tree/main/build/universal/trending).

Then set `CLIENT_LOCALE` and run `pnpm run clean-and-develop` as described above. Enabling production deployments and publishing a localized curriculum require additional work beyond testing the client UI locally.

## How to Structure Components

If you are working on a feature or a bug for the client web app, say for example adding new UI items on the settings page, you should follow the guidelines below. They will help you prepare the components for localization into all the supported world languages.

### Functional Component

```js
import { useTranslation } from 'react-i18next';

// in the render method:
const { t } = useTranslation();

// call the "t" function with a key from the JSON file:
<p>{t('key')}</p>; // more details below
```

### Class Component

```js
import { withTranslation } from 'react-i18next';

// withTranslation adds the "t" function to props:
const { t } = this.props;

// call the "t" function with a key from the JSON file:
<h1>{t('key')}</h1> // more details below

// export without redux:
export default withTranslation()(Component);

// or with redux:
export default connect(...)(withTranslation()(Component));
```

## Translate Using the "t" Function

### Basic Translation

```js
// in the component:
<p>{t('p1')}</p>

// in the JSON file:
{
  "p1": "My paragraph"
}

// output:
<p>My paragraph</p>
```

### With Dynamic Data

```js
// in the component:
const username = 'moT';

<p>{t('welcome', { username: username })}</p>

// in the JSON file:
{
  "welcome": "Welcome {{username}}"
}

// output:
<p>Welcome moT</p>
```

The above example passes an object to the `t` function with a `username` variable. The variable will be used in the JSON value where `{{username}}` is.

## Translate with the `Trans` Component

The general rule is to use the "t" function when you can. But there's a `Trans` component for when that isn't enough, usually when you have elements embedded in the text. You can use the `Trans` component with any type of react component.

### Basic Elements Nested

```js
// in the component:
import { Trans } from 'react-i18next'

<p>
  <Trans>fcc.greeting</Trans>
</p>

// in the JSON file:
{
  "fcc": {
    "greeting": "Welcome to <strong>freeCodeCamp</strong>"
  }
}

// output:
<p>Welcome to <strong>freeCodeCamp</strong></p>
```

You can place the key inside the component tags like in the above example if the text contains "simple" tags with no attributes. `br`, `strong`, `i`, and `p` are the default, but that list can be expanded in the i18n config.

### Complex Elements Nested

Other times, you will want to have certain text inside another element, an anchor tag is a good example:

```js
// in the component:
<p>
  <Trans i18nKey='check-forum'>
    <a href='https://forum.freecodecamp.org/'>placeholder</a>
  </Trans>
</p>

// in the JSON file:
{
  "check-forum": "Check out <0>our forum</0>."
}

// output:
<p>Check out <a href='https://forum.freecodecamp.org/'>our forum</a></p>
```

In the above example, the key is set in the attributes of the `Trans` component. The `<0>` and `</0>` in the JSON represent the first child of the component, in this case, the anchor element. If there were more children, they would just count up from there using the same syntax. You can find the children of a component in the react dev tools by inspecting it. `placeholder` is simply there because the linter complains about empty `<a>` elements.

### With a Variable

```js
// in the component:
const email = 'team@freecodecamp.org';

<p>
  <Trans i18nKey='fcc.email'>
    <a href={`mailto:${email}`}>
      {{ email }}
    </a>
  </Trans>
</p>

// in the JSON file:
{
  "fcc": {
    "email": "Send us an email at: <0>{{email}}</0>"
  }
}

// output:
<p>Send us an email at: <a href='mailto:team@freecodecamp.org'>team@freecodecamp.org</a></p>
```

The `i18nKey` prop selects the translation. `Trans` reads the nested `{{ email }}` object to supply the value for `{{email}}` in the JSON string. The `<0>` tags place the translated text inside the anchor. A separate `email` prop on `Trans` is not needed.

## Changing Text

To change English client text, open the relevant `.json` file in `client/i18n/locales/english/`, find the key used in the React component, and update its value. Search the codebase for that key to check that the change makes sense everywhere it is used. For translations, use the workflow described in [File Structure](#file-structure); do not edit the generated non-English copies, since setup overwrites them.

Run `pnpm run clean-and-develop` to apply the change.

## Adding Text

If the text you want to add to the client exists in the relevant `.json` file, use the existing key. Otherwise, create a new key.

The English file is the "source of truth" for all of the `.json` files sharing the same name. Add new UI text to `client/i18n/locales/english/translations.json`, or to the appropriate English file for another translation namespace.

> **Note**
>
> For files managed through the translation pipeline, change only the English source in your pull request. Translation updates are synced separately. Do not add the key manually to the generated non-English copies.

Keep related keys together in the English source file. Put punctuation, spacing, and quotes in the JSON strings so translators can adjust the complete text.

> **Note**
>
> The underscore (`_`) is a reserved character for keys in the client-side files. See [the documentation](https://www.i18next.com/translation-function/plurals) for how they are used.

Run `pnpm run clean-and-develop` to apply the change.

## Proposing a Pull Request (PR)

After you've committed your changes, check here for [how to open a Pull Request](/how-to-open-a-pull-request/).

## Helpful Documentation

- [react-i18next docs](https://react.i18next.com/latest/usetranslation-hook)
- [i18next docs](https://www.i18next.com/translation-function/essentials)

Source: https://contribute.freecodecamp.org/how-to-work-on-localized-client-webapp/index.mdx
