# How the freeCodeCamp i18n System Works Across Frontend and Backend

> Discover how freeCodeCamp's i18n system leverages i18next and a filesystem backend to share translations seamlessly between its React frontend and Node.js backend for universal localization.

- Repository: [freeCodeCamp.org/freeCodeCamp](https://github.com/freeCodeCamp/freeCodeCamp)
- Tags: internals
- Published: 2026-02-23

---

**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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/packages/shared/src/config/i18n.ts) file exports enumerations and mappings that standardize language codes across the entire application:

```typescript
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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/translations.json) – UI strings for buttons, headings, and messages
- [`intro.json`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/intro.json) – Introduction text for curriculum sections
- [`meta-tags.json`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/meta-tags.json) – SEO and social media metadata
- [`links.json`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/links.json) – URL slugs and external links

Example excerpt from [`client/i18n/locales/english/translations.json`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/i18n/locales/english/translations.json):

```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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/i18n/config.js) file initializes the i18next instance:

```typescript
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:

```tsx
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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/curriculum/src/get-challenges.ts) validates requested locales against `availableLangs` from the shared configuration:

```typescript
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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/__mocks__/react-i18next.js) file provides a simple implementation:

```javascript
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:

1. **Edit the translation file** at [`client/i18n/locales/english/translations.json`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/i18n/locales/english/translations.json):

```json
{
  "...": "...",
  "newFeature": {
    "title": "Exciting New Feature",
    "description": "This feature does amazing things."
  }
}

```

2. **Use in a React component**:

```tsx
import i18next from 'i18next';

export const NewFeatureBanner = () => (
  <section>
    <h2>{i18next.t('newFeature.title')}</h2>
    <p>{i18next.t('newFeature.description')}</p>
  </section>
);

```

3. **Run tests** – the mock automatically handles the new keys.

### Switching Languages at Runtime

Use the `useTranslation` hook to change languages dynamically:

```tsx
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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/i18n/config.js)
- **Shared configuration** in [`packages/shared/src/config/i18n.ts`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/packages/shared/src/config/i18n.ts) defines 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 `availableLangs` configuration to ensure curriculum content matches supported locales
- **Test mocking** via [`client/__mocks__/react-i18next.js`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/__mocks__/react-i18next.js) eliminates 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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/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`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/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.