Internationalization (i18n)
Relevant source files
The following files were used as context for generating this wiki page:
The Internationalization (i18n) system in Farm.js enables developers to build multi-language applications through locale-aware routing, typed translation catalogs, and automated detection signals. It integrates with both the server-side rendering (SSR) pipeline and the client-side runtime to ensure consistent language delivery across the full stack.
The system uses industry-standard ICU message formats and provides automated type generation for message keys. This ensures that translation keys and variables remain synchronized with the application's source code during development and build processes.
Sources: skills/farmjs/SKILL.md, packages/farm/src/tests/i18n.test.ts:182-195
Configuration
You configure internationalization directly in the farm.config.ts file using the i18n property. The configuration defines supported locales, the default language, and how the framework handles locale-based routing.
Configuration Options
| Option | Type | Description |
|---|---|---|
enabled | boolean | Activates the i18n system. Defaults to true if locales are provided. |
locales | string[] | A list of supported BCP 47 language tags (e.g., ["en", "fr", "ar"]). |
defaultLocale | string | The fallback locale used when no other locale is detected. |
routing | string | Strategies: prefix-except-default, prefix-always, or none. |
messages | string | The path to JSON translation catalogs. Defaults to src/messages. |
strict | boolean | If true, validates that all locales have matching keys. |
Sources: packages/farm/src/i18n/config.ts, skills/farmjs/SKILL.md
Routing Strategies
The framework manages URL structure based on the routing setting:
prefix-except-default: Omits the locale prefix for the default language but requires it for others (e.g.,/for English,/fr/for French).prefix-always: Requires a locale prefix for every request (e.g.,/en/,/fr/).none: Uses detection signals (cookies, headers) without changing the URL path.
Sources: packages/farm/src/tests/i18n.test.ts:69-90, skills/farmjs/SKILL.md
Architecture and Data Flow
The i18n system operates through a specialized runtime that intercepts requests to resolve the active locale. It prioritizes explicit URL signals over persistent cookies and browser Accept-Language headers.
The diagram shows how the framework determines the active language for a specific request.
Sources: packages/farm/src/tests/i18n.test.ts:114-140, packages/farm/src/nitro/universal-build.ts:1450-1470
Server-Side Implementation
Server modules access internationalization features through the @farm.js/core/i18n/server entry point. The framework provides isolated request contexts so that concurrent users can receive different languages without cross-contamination.
Key Server Functions
getLocale(): Returns the active BCP 47 string for the current request.t(key, variables?): Translates a key using the active locale and optional ICU variables.format: Provides locale-aware formatting for numbers, dates, and currencies.createTranslator(locale): Generates a translation function for a specific, fixed locale.
import { getLocale, t, format } from "@farm.js/core/i18n/server";
export default async function ServerComponent() {
const locale = getLocale();
const welcome = t("home.welcome", { name: "User" });
const price = format.number(1200, { style: "currency", currency: "USD" });
return <div>{welcome} - {price}</div>;
}
Sources: packages/farm/src/i18n/server.ts, skills/farmjs/SKILL.md, packages/farm/src/tests/i18n.test.ts:316-335
Client-Side Implementation
In Client Components (marked with "use client"), internationalization relies on React hooks. The client runtime hydrates the translations provided by the server during the initial SSR pass.
Client Hooks
useLocale(): Provides the currentlocale, the list of alllocales, and asetLocalefunction to change languages.useTranslations(): Returns thetfunction for the active client-side context.
Sources: skills/farmjs/SKILL.md, packages/farm/src/nitro/universal-build.ts:1140-1160
Translation Catalogs and Type Safety
Farm.js expects translation files as nested JSON objects located in the messages directory (usually src/messages/<locale>.json). During the build process, the framework crawls these files and generates TypeScript definitions in src/farm.d.ts.
Strict Validation
When strict: true is enabled, the build process:
- Ensures every key present in the
defaultLocalecatalog exists in all other catalogs. - Validates that ICU variable names (e.g.,
{count}) match across all languages for a given key. - Rejects catalog files that contain duplicate or malformed JSON.
Sources: packages/farm/src/tests/i18n.test.ts:210-230, packages/farm/src/i18n/config.ts
Message Resolution Logic
The sequence demonstrates how the runtime resolves nested keys and formats pluralization rules.
Sources: packages/farm/src/tests/i18n.test.ts:182-200
Metadata and Document Handling
The i18n system automatically modifies the HTML document structure based on the active locale. It injects the correct lang and dir (direction) attributes into the <html> tag. For Right-to-Left (RTL) languages like Arabic (ar) or Hebrew (he), the framework sets dir="rtl".
Additionally, the framework renders link rel="alternate" tags in the <head> to inform search engines about localized versions of the current page (SEO optimization).
Sources: packages/farm/src/nitro/universal-build.ts:1670-1700, skills/farmjs/SKILL.md
Summary
Internationalization in Farm.js provides a unified approach to multi-language support by combining automated routing, server-request isolation, and client-side hooks. Its primary strength lies in the deep integration with the TypeScript build process, which eliminates common errors related to missing translation keys or mismatched variables. By handling document attributes and SEO metadata automatically, the framework allows developers to focus on content while ensuring the application meets global accessibility and search standards.