How the freeCodeCamp i18n System Works Across Frontend and Backend
The freeCodeCamp i18n system uses i18next with a filesystem backend to share JSON translation bundles between the React frontend and Node.js backend, ensuring consistent localization across Gatsby server-side rendering and client-side runtime.
The internationalization (i18n) architecture in the freeCodeCamp repository synchronizes translated content across the React frontend and Node.js backend using a unified configuration. By leveraging i18next with shared JSON locale files located in client/i18n/locales/, the system ensures that both Gatsby's build-time rendering and client-side hydration display identical translated strings.
Language Configuration and Enumeration
The system defines supported languages in a shared configuration package that both frontend and backend consume.
Shared Language Definitions
The packages/shared/src/config/i18n.ts file exports enumerations and mappings that standardize language codes across the entire application:
export enum Languages {
English = 'english',
Espanol = 'espanol',
// …other supported languages
}
export const availableLangs = {
client: [Languages.English, Languages.Espanol, /* … */],
curriculum: [Languages.English, Languages.Espanol, /* … */]
};
export const i18nextCodes = {
[Languages.English]: 'en',
[Languages.Espanol]: 'es',
// 2‑letter ISO‑639‑1 codes used by i18next
};
These definitions are imported wherever a language list is needed, including the language selector in the footer, the curriculum loader, and the build scripts.
Translation File Structure
Each supported language has a dedicated folder under client/i18n/locales/ containing JSON translation files:
translations.json– UI strings for buttons, headings, and messagesintro.json– Introduction text for curriculum sectionsmeta-tags.json– SEO and social media metadatalinks.json– URL slugs and external links
Example excerpt from client/i18n/locales/english/translations.json:
{
"buttons": {
"sign-in": "Sign in",
"submit": "Submit"
},
"learn": {
"heading": "Welcome to freeCodeCamp's curriculum.",
"start-at-beginning": "If you are new to coding, we recommend you <0>start at the beginning</0>."
}
}
Frontend Implementation
The React frontend initializes i18next with a filesystem backend to support both Gatsby's server-side rendering and client-side hydration.
i18next Configuration
The client/i18n/config.js file initializes the i18next instance:
import i18n from 'i18next';
import backend from 'i18next-fs-backend';
import { initReactI18next } from 'react-i18next';
import { i18nextCodes } from '@freecodecamp/shared/config/i18n';
const clientLocale = process.env.CLIENT_LOCALE || 'english';
const i18nextCode = i18nextCodes[clientLocale as keyof typeof i18nextCodes];
i18n
.use(backend) // reads JSON files from the filesystem
.use(initReactI18next) // integrates with React components
.init({
lng: i18nextCode,
fallbackLng: 'en',
ns: ['translations', 'intro', 'meta-tags', 'links'],
defaultNS: 'translations',
backend: {
loadPath: `${__dirname}/locales/{{lng}}/{{ns}}.json`,
},
interpolation: { escapeValue: false }
});
When Gatsby renders pages on the server, i18next-fs-backend loads the appropriate JSON bundles, ensuring the generated HTML contains translated strings.
React Component Integration
Components access translations using the useTranslation hook or i18next.t method:
import i18next from 'i18next';
function Example() {
return (
<button>{i18next.t('buttons.sign-in')}</button>
);
}
This approach works identically during server-side rendering and client-side hydration because both environments use the same i18next configuration with the filesystem backend.
Backend and Build-Time Integration
While the backend does not render UI components, it uses the same language configuration and locale files to serve curriculum content and support build processes.
Server-Side Rendering Support
Build scripts such as client/tools/generate-search-placeholder.ts import the shared i18n configuration to generate language-specific assets during the Gatsby build process. These scripts use the same CLIENT_LOCALE environment variable and i18nextCodes mapping to ensure consistency with the runtime application.
Curriculum Loading
The curriculum loader in curriculum/src/get-challenges.ts validates requested locales against availableLangs from the shared configuration:
import { availableLangs } from '@freecodecamp/shared/config/i18n';
// Throws if an unsupported locale is requested
if (!availableLangs.curriculum.includes(requestedLocale)) {
throw new Error(`Unsupported curriculum locale: ${requestedLocale}`);
}
This ensures that the backend only attempts to load curriculum files for languages that have been properly configured in the shared i18n module.
Testing and Mocking
The test suite replaces the i18next integration with a lightweight mock to avoid filesystem dependencies during unit testing.
The client/__mocks__/react-i18next.js file provides a simple implementation:
module.exports = {
useTranslation: () => ({
t: (key) => key,
i18n: { changeLanguage: () => new Promise(() => {}) }
})
};
This mock returns the translation key as the translated string, allowing tests to verify that components call the correct keys without requiring actual translation files.
Practical Implementation Examples
Adding a New Translation Key
To add a new UI string, modify the JSON files and reference the key in components:
- Edit the translation file at
client/i18n/locales/english/translations.json:
{
"...": "...",
"newFeature": {
"title": "Exciting New Feature",
"description": "This feature does amazing things."
}
}
- Use in a React component:
import i18next from 'i18next';
export const NewFeatureBanner = () => (
<section>
<h2>{i18next.t('newFeature.title')}</h2>
<p>{i18next.t('newFeature.description')}</p>
</section>
);
- Run tests – the mock automatically handles the new keys.
Switching Languages at Runtime
Use the useTranslation hook to change languages dynamically:
import { useTranslation } from 'react-i18next';
export const LanguageSwitcher = () => {
const { i18n } = useTranslation();
const changeToSpanish = () => i18n.changeLanguage('es');
return <button onClick={changeToSpanish}>Español</button>;
};
The changeLanguage call loads the Spanish JSON bundle via the filesystem backend in development, or from pre-bundled assets in production.
Summary
- i18next with filesystem backend powers both Gatsby server-side rendering and client-side hydration in
client/i18n/config.js - Shared configuration in
packages/shared/src/config/i18n.tsdefines supported languages, ISO codes, and available locales for both frontend and backend - JSON translation bundles stored in
client/i18n/locales/<lang>/provide the actual strings, organized by namespace (translations, intro, meta-tags, links) - Backend validation uses the same
availableLangsconfiguration to ensure curriculum content matches supported locales - Test mocking via
client/__mocks__/react-i18next.jseliminates filesystem dependencies during unit testing by returning translation keys as strings
Frequently Asked Questions
What technology does freeCodeCamp use for internationalization?
freeCodeCamp uses i18next with the i18next-fs-backend plugin as its core internationalization engine. This setup allows the application to load JSON translation files from the filesystem during both Gatsby's server-side rendering and the React client-side runtime, ensuring that the same translation bundles power both the build process and the browser experience.
How does freeCodeCamp ensure consistent translations between frontend and backend?
The system ensures consistency by using a shared configuration package (packages/shared/src/config/i18n.ts) that defines supported languages, ISO-639-1 codes, and available locales. Both the React frontend and Node.js backend import these definitions, ensuring they reference the same language lists and translation file locations. The backend validates curriculum requests against availableLangs.curriculum to prevent loading content for unsupported locales.
How are translations handled during the Gatsby build process?
During the Gatsby build, client/i18n/config.js initializes i18next with the filesystem backend configured to read from client/i18n/locales/{{lng}}/{{ns}}.json. This allows Gatsby's server-side rendering to access actual translated strings when generating static HTML, ensuring that pages are pre-rendered with the correct language content rather than raw translation keys, which provides proper SEO and eliminates hydration mismatches.
How does freeCodeCamp test components that use internationalization?
The test suite uses a mock implementation located at client/__mocks__/react-i18next.js that replaces the actual i18next integration. The mock returns the translation key itself as the translated string (e.g., t: (key) => key), allowing unit tests to verify that components invoke the correct translation keys without requiring actual JSON files or filesystem access.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →