# CloddsBot i18n System Architecture: How 10-Language Localization Works in TypeScript

> Discover the CloddsBot i18n system architecture. Learn how this TypeScript localization solution efficiently manages 10 languages using a Locale Registry, Translation Store, and Runtime API.

- Repository: [AL/CloddsBot](https://github.com/alsk1992/CloddsBot)
- Tags: architecture
- Published: 2026-09-11

---

**CloddsBot implements a lightweight, file-based internationalization (i18n) system written in TypeScript that supports 10 languages through a three-tier architecture comprising a Locale Registry, Translation Store, and Runtime API.**

The open-source **alsk1992/CloddsBot** repository delivers a modular i18n system architecture designed for multi-language scalability without external dependencies. This localization layer separates translation data from runtime logic, using pure TypeScript functions to handle language detection, dynamic locale switching, and string interpolation. Developers can extend support to additional languages by adding JSON translation files and updating the central registry.

## Core Components of the i18n Architecture

The system architecture consists of three tightly integrated components that manage the complete lifecycle of translation data.

**Locale Registry** – Defined by the `SUPPORTED_LOCALES` constant in [[`src/i18n/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/i18n/index.ts)](https://github.com/alsk1992/CloddsBot/blob/main/src/i18n/index.ts), this registry maintains metadata for each supported language including display names and native names.

**Translation Store** – Located under [`src/i18n/locales/`](https://github.com/alsk1992/CloddsBot/tree/main/src/i18n/locales), this directory contains JSON files (such as [`en.json`](https://github.com/alsk1992/CloddsBot/blob/main/en.json) and [`zh.json`](https://github.com/alsk1992/CloddsBot/blob/main/zh.json)) that hold the actual localized strings. The store implements lazy-loading with runtime caching to minimize memory footprint.

**Runtime API** – Exposed from [[`src/i18n/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/i18n/index.ts)](https://github.com/alsk1992/CloddsBot/blob/main/src/i18n/index.ts), this API surface provides the `t`, `setLocale`, `getLocale`, `initI18n`, `detectLocale`, `isLocaleSupported`, and `getSupportedLocales` functions that the rest of the codebase consumes.

## How the Localization System Works

### Initialization and Priority Resolution

On startup, the bot invokes `initI18n()` to establish the active locale. The function resolves the language code through a strict priority cascade: explicit configuration object (`config.locale`), the `CLODDS_LOCALE` environment variable, the system `LANG` variable (stripped of encoding suffixes), and finally the hardcoded fallback to English (`en`).

```typescript
export function initI18n(config?: { locale?: string }) {
  const locale = config?.locale
    || process.env.CLODDS_LOCALE
    || process.env.LANG?.split('.')[0]?.split('_')[0]
    || DEFAULT_LOCALE;
  setLocale(locale);
}

```

This initialization pattern ensures that containerized deployments, development environments, and user settings can each override the default behavior without code changes.

### Lazy Loading and Caching Strategy

When `loadTranslations(locale)` executes, it reads the corresponding JSON file from `src/i18n/locales/` and parses the content. The results are memoized in a `Map<Locale, Record<string, unknown>>`, ensuring that each translation file is loaded and parsed exactly once per process lifetime. Subsequent calls retrieve the cached object directly from memory, eliminating filesystem I/O overhead after the initial fetch.

### Translation Lookup and Fallback Chain

The `t(key, vars?)` function retrieves strings for the current locale using dot notation to access nested objects (e.g., `errors.notFound`). If the requested key does not exist in the active locale, the system falls back to the English translation file. If the key remains missing, the function returns the raw key string as a last resort, preventing runtime crashes while signaling a missing translation.

### String Interpolation Engine

Placeholders within translation strings use curly brace syntax (`{name}`, `{reason}`). The optional `vars` argument accepts an object mapping these placeholders to runtime values, enabling dynamic content injection without template literal evaluation.

### Runtime Locale Switching

The `setLocale(locale)` function validates the requested language against `SUPPORTED_LOCALES` before updating the in-memory `currentLocale` variable. This validation ensures that only registered languages become active, and the change takes immediate effect for all subsequent `t()` calls throughout the application.

### Multi-Source Locale Detection

For HTTP-based interactions, `detectLocale({header, query, cookie, userSetting})` examines multiple preference sources in parallel. It parses the `Accept-Language` header, query string parameters, cookies, and persisted user settings to identify the first supported candidate, then returns the matching locale code for use with `setLocale()`.

## Runtime API Reference

- **`initI18n(config?)`** – Initializes the system with optional configuration overrides.
- **`t(key, vars?)`** – Retrieves and interpolates translation strings for the active locale.
- **`setLocale(locale)`** – Validates and switches the active locale at runtime.
- **`getLocale()`** – Returns the currently active locale code.
- **`detectLocale(options)`** – Determines the best locale from HTTP headers, cookies, or user settings.
- **`isLocaleSupported(locale)`** – Boolean check against the `SUPPORTED_LOCALES` registry.
- **`getSupportedLocales()`** – Returns the full array of supported locale metadata for UI rendering.

## Practical Implementation Examples

### Basic Translation Usage

```typescript
import { t } from './i18n';

console.log(t('welcome.message'));                     // → "Welcome to Clodds"
console.log(t('welcome.greeting', { name: 'Alex' })); // → "Hello, Alex!"

```

### Dynamic Locale Switching

```typescript
import { setLocale, t } from './i18n';

setLocale('zh');                       // Switch to Chinese
console.log(t('welcome.message'));     // → "欢迎使用 Clodds"

```

### HTTP Request Locale Detection

```typescript
import { detectLocale, setLocale } from './i18n';

const locale = detectLocale({
  header: req.headers['accept-language'],
  query: req.query.lang,
  cookie: req.cookies.lang,
  userSetting: user.profile.locale,
});

setLocale(locale);

```

### Building Language Selectors

```typescript
import { getSupportedLocales } from './i18n';

const options = getSupportedLocales().map(l => ({
  value: l.code,
  label: `${l.name} (${l.nativeName})`,
}));

```

## Extending the System for New Languages

Adding support for an 11th language requires zero changes to the Runtime API. Create a new JSON file in `src/i18n/locales/` following the naming convention `[code].json`, populate it with the translation keys used in [`en.json`](https://github.com/alsk1992/CloddsBot/blob/main/en.json), and append the language metadata to the `SUPPORTED_LOCALES` array in [`src/i18n/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/i18n/index.ts). The lazy-loading mechanism automatically picks up the new file on the next `setLocale()` call targeting that language code.

## Summary

- **CloddsBot** uses a three-tier i18n architecture: Locale Registry, Translation Store, and Runtime API, all implemented in TypeScript within the `src/i18n/` directory.

- **Translation files** are stored as JSON in `src/i18n/locales/` and loaded on-demand into a cached Map to optimize memory usage.

- **Priority resolution** follows the chain: explicit config → `CLODDS_LOCALE` env → `LANG` env → English fallback.

- **The `t()` function** supports nested key lookup, automatic English fallback, and variable interpolation via the `vars` parameter.

- **Locale detection** can parse HTTP headers, query strings, cookies, and user settings to automatically determine the appropriate language.

## Frequently Asked Questions

### How does CloddsBot handle missing translation keys?

If a key is absent from the active locale, the system falls back to the English translation file. If the key remains missing there, `t()` returns the raw key string itself. This cascade prevents application crashes while making untranslated strings visible for debugging.

### Can I add a custom language without modifying the core i18n files?

No, adding a new language requires updating the `SUPPORTED_LOCALES` constant in [`src/i18n/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/i18n/index.ts) to register the metadata and ensure validation passes in `setLocale()`. However, you only need to modify that single registry entry and add the corresponding JSON file; no changes to the Runtime API functions are necessary.

### What is the performance impact of the i18n system?

The architecture minimizes overhead through lazy-loading and Map-based caching. Translation files are parsed only once per process, and subsequent lookups perform constant-time Map retrievals. The interpolation engine uses simple string replacement without heavy regex processing.

### How does the bot detect the correct locale for new users?

The `detectLocale()` function examines four sources in order of preference: the `Accept-Language` HTTP header, URL query parameters, cookies, and persisted user settings. It selects the first candidate that matches a code in `SUPPORTED_LOCALES`, defaulting to English if no match exists.