Raypx

Adding a New Locale

Step-by-step guide to adding support for a new language.

This guide walks through adding a new locale to Raypx. The process involves updating the app's i18n system and the docs i18n system separately.

Prerequisites

Before starting, choose a locale code that follows the BCP 47 format (e.g., fr-FR, ja-JP, ko-KR).

Steps

Update the routing configuration in packages/i18n/src/routing.ts:

export type SupportedLocale = "en-US" | "zh-CN" | "ja-JP"

export const routing = {
  locales: ["en-US", "zh-CN", "ja-JP"] as const,
  defaultLocale: "en-US" as SupportedLocale,
  localeCookie: "raypx-locale",
  localePrefix: "always" as const,
  ignoredPathPrefixes: ["/api", "/__", "/llms"],
}

export const LOCALE_META: Record<SupportedLocale, LocaleMetadata> = {
  "en-US": { code: "en-US", label: "English", nativeLabel: "English" },
  "zh-CN": { code: "zh-CN", label: "Chinese (Simplified)", nativeLabel: "Simplified Chinese" },
  "ja-JP": { code: "ja-JP", label: "Japanese", nativeLabel: "Japanese" },
}

Also update LOCALE_TIMEZONE_MAP in packages/i18n/src/request.ts:

const LOCALE_TIMEZONE_MAP: Record<SupportedLocale, string> = {
  "en-US": "America/New_York",
  "zh-CN": "Asia/Shanghai",
  "ja-JP": "Asia/Tokyo",
}

Create the message file at packages/i18n/src/messages/ja-JP.ts by copying the English file and translating the values:

import type { AppMessages } from "./index"

const jaJP: AppMessages = {
  WelcomePage: {
    title: "Raypxへようこそ",
    subtitle: "アカウントの設定を始めましょう",
  },
  Auth: {
    signIn: "Sign In", // Translate as needed
    signOut: "Sign Out",
    resetPassword: "Reset Password",
  },
  // ... all keys from en-US.ts
} satisfies AppMessages

export default jaJP

Then register it in packages/i18n/src/messages/index.ts:

import jaJP from "./ja-JP"

export const messages: Record<SupportedLocale, AppMessages> = {
  "en-US": enUS,
  "zh-CN": zhCN,
  "ja-JP": jaJP,
}

Update the Fumadocs i18n configuration in content/docs/source.config.ts (or equivalent Fumadocs config). Add the new locale to the i18n.locales array and provide navigation translations:

i18n: {
  locales: ["en-US", "zh-CN", "ja-JP"],
  defaultLocale: "en-US",
}

Also add the navigation label for the new locale in the Fumadocs i18n dictionaries.

Create the docs directory for the new locale by copying the English docs:

mkdir -p content/docs/ja-JP
cp -r content/docs/* content/docs/ja-JP/

Then translate the MDX files in content/docs/ja-JP/.

Test the new locale by navigating to /{new-locale}/ in the browser and verifying: the page renders with the translated messages, language switching works correctly, the raypx-locale cookie is set properly, and docs at /docs/{new-locale}/ render with translated content.

Run the type checker to verify all message keys are present:

pnpm run check-types

TypeScript will catch any missing keys in the new locale file since AppMessages is inferred from en-US.ts.

Completion Checklist

  • SupportedLocale type updated in packages/i18n/src/routing.ts
  • routing.locales array updated
  • LOCALE_META entry added with label and nativeLabel
  • LOCALE_TIMEZONE_MAP entry added in packages/i18n/src/request.ts
  • Message file created at packages/i18n/src/messages/{locale}.ts
  • Message file registered in packages/i18n/src/messages/index.ts
  • Fumadocs i18n.locales updated
  • Fumadocs navigation translations added
  • Docs directory created at content/docs/{locale}/
  • All MDX files translated
  • pnpm run check-types passes
  • Manual testing in browser succeeds

On this page