---
title: "Deploying New Languages on `/learn`"
---

> 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.

# Deploying New Languages on `/learn`

To enable a new language on `/learn` (curriculum), you need to complete the following steps:

- Coordinate translation syncing with the i18n team so the new language's curriculum and client translations are available in the i18n-curriculum repository.
- Open a PR to add the build configuration:
  - Update `packages/shared/src/config/i18n.ts`.
  - Update `packages/shared/src/config/curriculum.ts`.
  - Update `client/src/utils/algolia-locale-setup.ts`.
  - Add the language to the test matrix in `.github/workflows/curriculum-i18n-submodule.yml`.
  - Add `client/i18n/locales/<language>/links.json`.
- Open a PR to add `build/universal/trending/<language>.yaml` in the CDN repository if the file does not exist.
- Coordinate the hosting setup with the infrastructure team and add the language to `.github/workflows/deploy-client.yml`.
- Once the VM is ready, open a separate PR to show the new language in the navigation menu.

We will explain each step in the following sections.

## Preparing Translations and CI

Coordinate with the i18n team to make the new language's translations available in the [i18n-curriculum repository](https://github.com/freeCodeCamp/i18n-curriculum). Curriculum challenges belong in `curriculum/challenges/<language>/blocks/`, and client translations belong in `client/<language>/` within that repository. For example, [the Arabic translations PR](https://github.com/freeCodeCamp/i18n-curriculum/pull/332) added curriculum content and client translations.

Ensure the main repository's `curriculum/i18n-curriculum` submodule points to a commit containing the new language's files before enabling its build.

Add the language to `jobs.test-curriculum.strategy.matrix.locale` in [`.github/workflows/curriculum-i18n-submodule.yml`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/.github/workflows/curriculum-i18n-submodule.yml), unless it is already tested in the full CI suite. This workflow builds the localized client and tests its curriculum when the submodule update branch changes. [The Arabic configuration PR](https://github.com/freeCodeCamp/freeCodeCamp/pull/68603) added this matrix entry alongside the language configuration, search configuration, and `links.json`.

## Enabling a Language

:::note
Make the translation files available before enabling the language in the build configuration. Setup fails when the required client translation files are missing from the submodule.
:::

There are a few steps to take in order to allow the codebase to build in your desired language.

First, visit the [`packages/shared/src/config/i18n.ts`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/packages/shared/src/config/i18n.ts) file to add the language to the list of available languages and configure the values. There are several objects here.

- `Languages`: Add the new language to `Languages` enum, similar to the others. The string value here will be used in the `.env` file to set a build language later.
- `availableLangs`: Add the new property from the `Languages` enum to both the `client` and `curriculum` arrays.
- `i18nextCodes`: These are the ISO language codes for each language. You will need to add the appropriate ISO code for the language you are enabling. These do need to be unique for each language.
- `LangNames`: These are the display names for the language selector in the navigation menu.
- `LangCodes`: These are the language codes used for formatting dates and numbers. These should be Unicode CLDR codes instead of ISO codes.
- `hiddenLangs`: These languages will not be displayed in the navigation menu. This is used for languages that are not yet ready for release. Include your language in this array in the first PR and ask staff team to prepare the VM instance for your language. When the VM is ready, make another PR to remove it from the array.
- `rtlLangs`: These are languages that read from right to left.

As an example, if you wanted to enable Dothraki as a language, your `i18n.ts` objects should look like this:

```ts
export enum Languages {
  English = 'english',
  Espanol = 'espanol',
  Chinese = 'chinese',
  ChineseTraditional = 'chinese-traditional',
  Dothraki = 'dothraki'
}

export const availableLangs = {
  client: [
    Languages.English,
    Languages.Espanol,
    Languages.Chinese,
    Languages.ChineseTraditional,
    Languages.Dothraki
  ],
  curriculum: [
    Languages.English,
    Languages.Espanol,
    Languages.Chinese,
    Languages.ChineseTraditional,
    Languages.Dothraki
  ]
};

export const i18nextCodes = {
  [Languages.English]: 'en',
  [Languages.Espanol]: 'es',
  [Languages.Chinese]: 'zh',
  [Languages.ChineseTraditional]: 'zh-Hant',
  [Languages.Dothraki]: 'mis'
};

export const LangNames: { [key: string]: string } = {
  [Languages.English]: 'English',
  [Languages.Espanol]: 'Español',
  [Languages.Chinese]: '中文（简体字）',
  [Languages.ChineseTraditional]: '中文（繁體字）',
  [Languages.Dothraki]: 'Dothraki'
};

export const LangCodes = {
  [Languages.English]: 'en-US',
  [Languages.Espanol]: 'es-419',
  [Languages.Chinese]: 'zh',
  [Languages.ChineseTraditional]: 'zh-Hant',
  [Languages.Dothraki]: 'mis'
};

export const hiddenLangs: Languages[] = [Languages.Dothraki];

export const rtlLangs = [''];
```

:::note
When a language has been set up in the deployment pipeline AND has a public `/learn` instance live, it can be removed from the `hiddenLangs` array and be made available to the public.
:::

### Set Translated SuperBlocks

In the [packages/shared/src/config/curriculum.ts](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/packages/shared/src/config/curriculum.ts) file, add the new language to the `notAuditedSuperBlocks` object. This lists all the superblocks which are not fully translated. Add an array of superblocks that have not been fully translated to it. For example:

```js
export const notAuditedSuperBlocks: NotAuditedSuperBlocks = {
  ...
  [Languages.Dothraki]: [
    SuperBlocks.DataVis,
    SuperBlocks.RelationalDb,
    SuperBlocks.BackEndDevApis,
    SuperBlocks.QualityAssurance,
    SuperBlocks.SciCompPy,
    SuperBlocks.DataAnalysisPy,
    SuperBlocks.InfoSec,
    SuperBlocks.MachineLearningPy,
    SuperBlocks.CollegeAlgebraPy,
    SuperBlocks.FoundationalCSharp,
    SuperBlocks.CodingInterviewPrep,
    SuperBlocks.ProjectEuler,
    SuperBlocks.JsAlgoDataStructNew,
    SuperBlocks.TheOdinProject
  ]
}
```

Be sure to only add the superblocks that are **not** fully translated and approved. The translated superblocks will be calculated from this object. When a new superblock is finished being fully translated, remove it from the array for that language.

See the `SuperBlocks` enum at the beginning of the same file for the full list of superblocks.

### Configure Search

Next, open the [`client/src/utils/algolia-locale-setup.ts`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/src/utils/algolia-locale-setup.ts) file. This data is used for the search bar that loads `/news` articles. While it is unlikely that you are going to test this functionality, missing the data for your language can lead to errors when attempting to build the codebase locally.

Add an object for your language to the `algoliaIndices` object. You should use the same values as the `english` object for local testing, replacing the `english` key with your language's `availableLangs` value.

:::note
If we have already deployed an instance of news in your target language, you can update the values to reflect the live instance. Otherwise, use the English values.
:::

If you were to add Dothraki:

```js
const algoliaIndices = {
  english: {
    name: 'news',
    searchPage: 'https://www.freecodecamp.org/news/search/'
  },
  espanol: {
    name: 'news-es',
    searchPage: 'https://www.freecodecamp.org/espanol/news/search/'
  },
  chinese: {
    name: 'news-zh',
    searchPage: 'https://www.freecodecamp.org/chinese/news/search/'
  },
  'chinese-traditional': {
    name: 'news-zh',
    searchPage: 'https://www.freecodecamp.org/chinese/news/search/'
  },
  dothraki: {
    name: 'news',
    searchPage: 'https://www.freecodecamp.org/news/search/'
  }

  // If we already have /news in the target language up and running, you can update the values like this:
  // dothraki: {
  //   name: 'news-mis',
  //   searchPage: 'https://www.freecodecamp.org/dothraki/news/search/'
  // }
};
```

### Client UI

Copy the English `links.json` from [`client/i18n/locales/english`](https://github.com/freeCodeCamp/freeCodeCamp/tree/main/client/i18n/locales/english) to `client/i18n/locales/<language>/links.json` and update URLs where localized resources exist.

See [the localized client guide's file structure](/how-to-work-on-localized-client-webapp/#file-structure) for file ownership and translation details.

Production builds also require a `build/universal/trending/<language>.yaml` file in the [CDN repository](https://github.com/freeCodeCamp/cdn/tree/main/build/universal/trending). If one does not exist, copy [the English trending file](https://github.com/freeCodeCamp/cdn/blob/main/build/universal/trending/english.yaml) as a starting point.

#### `links.json`

You can replace any URLs that you have corresponding pages ready in your language.

For example, if you have a publication in your language, you can replace the URL for `"news"`.

### Enabling Localized Videos

This section is applicable only if you have localized videos in the challenges. Otherwise, you can skip this section.

For the video challenges, you need to change a few things. First, add the new locale to the GraphQL query in the [`client/src/templates/Challenges/generic/show.tsx`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/src/templates/Challenges/generic/show.tsx) file. For example, adding Dothraki to the query:

```tsx
  query GenericChallenge($id: String!) {
    challengeNode(id: { eq: $id }) {
      challenge {
        videoId
        videoLocaleIds {
          espanol
          italian
          portuguese
          dothraki
        }
      }
    }
  }
```

For each challenge with a localized video, add its video ID under the new language in `videoLocaleIds`. For example:

```yml
videoLocaleIds:
  espanol: 3muQV-Im3Z0
  italian: hiRTRAqNlpE
  portuguese: AelGAcoMXbI
  dothraki: new-id-here
dashedName: introduction-why-program
---
```

Update the `VideoLocaleIds` interface in `client/src/redux/prop-types.ts` to include the new language.

```ts
export interface VideoLocaleIds {
  espanol?: string;
  italian?: string;
  portuguese?: string;
  dothraki?: string;
}
```

Update the `ChallengeNodeChallengeVideoLocaleIds` type in [`client/schema.gql`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/schema.gql) to include the new language. Gatsby loads this schema snapshot with inference disabled for `ChallengeNode`, so the new query field must be declared here.

```graphql
type ChallengeNodeChallengeVideoLocaleIds {
  espanol: String
  italian: String
  portuguese: String
  dothraki: String
}
```

Finally, update the challenge schema in `curriculum/schema/challenge-schema.js`.

```js
videoLocaleIds: Joi.when('challengeType', {
  is: challengeTypes.video,
  then: Joi.object().keys({
    espanol: Joi.string(),
    italian: Joi.string(),
    portuguese: Joi.string(),
    dothraki: Joi.string()
  })
}),
```

## Testing Translations Locally

Follow [Testing a New Language in the localized client guide](/how-to-work-on-localized-client-webapp/#testing-a-new-language) to prepare unpublished client translations and run the app locally. You can test the client UI with `CURRICULUM_LOCALE=english` before enabling a translated curriculum.

For curriculum testing, translated challenges belong in `curriculum/i18n-curriculum/curriculum/challenges/<language>/blocks/`. See [the curriculum file structure](/curriculum-file-structure/#internationalization-structure). Set `CURRICULUM_LOCALE` to the configured language and run `pnpm run clean-and-develop` after preparing the translation files.

For help obtaining translations that are not yet available in the submodule, contact the translation team.

:::tip
If you build the client in one language and then want to build it in a different language, you will need to use the command `pnpm run clean-and-develop` after changing the `.env` file, as Gatsby will cache the first language.
:::

## Configure Deployment

Coordinate with the infrastructure team to prepare the hosts for the new language. In [`.github/workflows/deploy-client.yml`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/.github/workflows/deploy-client.yml), add the language to both:

- The `target_language` workflow input's `options` list.
- The `languageMap` object in the `setup-matrix` job, using the short code agreed with the infrastructure team. The workflow uses this code to identify the deployment hosts.

[The Arabic deployment PR](https://github.com/freeCodeCamp/freeCodeCamp/pull/69829) added `arabic` to the options and mapped it to `ara`. The deployment workflow is started manually and supports deploying a single language.

## Show the language in the navigation menu

When your prior PR is merged and the VM for your language is ready, make another PR to show your language in the navigation menu.

In [`packages/shared/src/config/i18n.ts`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/packages/shared/src/config/i18n.ts) file, you have included your language in `hiddenLangs` array in the prior PR. Remove it from the array now.

```ts
export const hiddenLangs: Languages[] = []; // Remove your language from the array
```

When this PR is merged and deployed, the curriculum in your language will be live.

Source: https://contribute.freecodecamp.org/how-to-enable-new-languages/index.mdx
