Unified Multi Language Architecture The monorepo implements a unified internationalization (i18n) strategy using Paraglide JS. This architecture ensures type-safe, performant, and developer-friendly multi-language support across all frontend applications (Panel, Cycle Planner, ClawUI, and Platform).

Overview

The i18n system provides support for English (default), Simplified Chinese, and Malay. It utilizes a build-time compilation strategy where translations are converted into type-safe JavaScript functions, enabling efficient tree-shaking and eliminating runtime JSON loading.

Key Capabilities

  • Type-Safe Messages: Translations are compiled into JS functions with full TypeScript inference.
  • Shared Dictionary: Common UI strings (buttons, forms, errors) are defined centrally in frontend/shared-i18n/.
  • Hybrid Persistence: Language preferences are stored in localStorage for instant access and synced to the database for cross-device persistence.
  • Framework Agnostic: Works seamlessly with both SolidStart (instant client-side switching) and Astro (page-reload switching).

Domain Glossary

Core Concepts

  • Locale: A language identifier (e.g., “en”, “zh”, “ms”) determining the active translation set and formatting rules.
  • Message: A translatable string defined in JSON, compiled by Paraglide into a JS function (e.g., m.button_save()).
  • Translation: The target-language version of a message.
  • Merge Strategy: The build process that combines shared translations with app-specific overrides: shared + app-specific → merge → Paraglide compile.

Process Concepts

  • Language Detection Chain: Ordered sequence: localStoragebrowser settingEnglish fallback.
  • Machine Translation: Automated pipeline using inlang CLI to generate initial translations for review.
  • Tree-Shaking: Automatic exclusion of unused message strings from the final production bundle.

Implementation Details

1. Technology Stack

  • Compiler/Runtime: @inlang/paraglide-js
  • Automation: @inlang/cli (Machine Translation)
  • Formatting: Native Intl APIs (DateTimeFormat, NumberFormat)
  • Orchestration: Turbo tasks (build:i18n)

2. Directory Structure

frontend/
├── shared-i18n/              # Shared dictionary (all apps)
│   ├── en.json
│   ├── zh.json
│   └── ms.json
├── solidstart/
│   ├── apps/panel/
│   │   ├── i18n/             # Panel-specific strings
│   │   └── src/
│   │       └── paraglide/    # Generated at build time
└── turbo.json                # Orchestrates build:i18n across all apps

3. Usage in Code

In SolidStart components, use the createReactiveTranslator utility to ensure translations update reactively when the language changes.
import { m } from "../paraglide/messages";
import { createReactiveTranslator } from "../lib/create-reactive-translator";

const Welcome = () => {
  const t = createReactiveTranslator();

  return (
    <div>
      {/* Static message */}
      <h1>{t(m.welcome_title)}</h1>

      {/* Message with parameters */}
      <p>{t(() => m.welcome_message({ name: "User" }))}</p>
    </div>
  );
};

4. Detection & Persistence

The useLanguage hook (SolidStart) or utility functions (Astro) manage the locale state. When a user logs in, the language_preference is fetched from the backend and synced to localStorage.

Decision Rationale

Why Paraglide?

  • Performance: Build-time compilation enables tree-shaking and avoids runtime I/O overhead.
  • Safety: TypeScript ensures developers don’t call missing keys or provide incorrect parameters.
  • Unified Strategy: A single tool that supports both our reactive (SolidJS) and static/SSR (Astro) frameworks.

Why Shared + App-Specific Structure?

  • DRY Principle: Avoid duplicating common strings like “Save” or “Cancel” across four different apps.
  • Isolation: Keep domain-specific terminology (e.g., “organization” in Panel) isolated to the relevant application.
  • Flexibility: Apps can override shared strings if they require a specific context-aware label.

Invariants & Limitations

  • Compile-Time Artifacts: Translations are bundled into the app; new languages require a rebuild.
  • LTR Only: All supported MVP languages (en, zh, ms) are Left-to-Right.
  • Independent Preferences: Switching language in one app (e.g., Panel) does not automatically switch it in another (e.g., Platform) until the next sync/login.
  • No HTML in Messages: Messages are plain text; UI components should wrap them for styling.
  • frontend/shared-i18n/ - Central translation sources.
  • docs/frontend/I18N_README.md - Technical implementation details and walkthroughs.
  • openspec/changes/i18n-paraglide-implementation/ - Original feature proposal and specs.