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.
- Update
- Open a PR to add
build/universal/trending/<language>.yamlin 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
Section titled “Preparing Translations and CI”Coordinate with the i18n team to make the new language’s translations available in the i18n-curriculum repository. Curriculum challenges belong in curriculum/challenges/<language>/blocks/, and client translations belong in client/<language>/ within that repository. For example, the Arabic translations PR 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, 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 added this matrix entry alongside the language configuration, search configuration, and links.json.
Enabling a Language
Section titled “Enabling a Language”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 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 toLanguagesenum, similar to the others. The string value here will be used in the.envfile to set a build language later.availableLangs: Add the new property from theLanguagesenum to both theclientandcurriculumarrays.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:
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 = [''];Set Translated SuperBlocks
Section titled “Set Translated SuperBlocks”In the 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:
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
Section titled “Configure Search”Next, open the 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.
If you were to add Dothraki:
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
Section titled “Client UI”Copy the English links.json from 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 for file ownership and translation details.
Production builds also require a build/universal/trending/<language>.yaml file in the CDN repository. If one does not exist, copy the English trending file as a starting point.
links.json
Section titled “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
Section titled “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 file. For example, adding Dothraki to the query:
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:
videoLocaleIds: espanol: 3muQV-Im3Z0 italian: hiRTRAqNlpE portuguese: AelGAcoMXbI dothraki: new-id-heredashedName: introduction-why-program---Update the VideoLocaleIds interface in client/src/redux/prop-types.ts to include the new language.
export interface VideoLocaleIds { espanol?: string; italian?: string; portuguese?: string; dothraki?: string;}Update the ChallengeNodeChallengeVideoLocaleIds type in 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.
type ChallengeNodeChallengeVideoLocaleIds { espanol: String italian: String portuguese: String dothraki: String}Finally, update the challenge schema in curriculum/schema/challenge-schema.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
Section titled “Testing Translations Locally”Follow Testing a New Language in the localized client guide 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. 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.
Configure Deployment
Section titled “Configure Deployment”Coordinate with the infrastructure team to prepare the hosts for the new language. In .github/workflows/deploy-client.yml, add the language to both:
- The
target_languageworkflow input’soptionslist. - The
languageMapobject in thesetup-matrixjob, using the short code agreed with the infrastructure team. The workflow uses this code to identify the deployment hosts.
The Arabic deployment PR 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
Section titled “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 file, you have included your language in hiddenLangs array in the prior PR. Remove it from the array now.
export const hiddenLangs: Languages[] = []; // Remove your language from the arrayWhen this PR is merged and deployed, the curriculum in your language will be live.