OSS-first docs
These docs teach the open system first: contracts, generated surfaces, runtimes, governance, and incremental adoption. Studio shows up as the operating layer on top, not as the source of truth.
Consumer i18n setup
Use ContractSpec i18n without losing ownership of translation contracts
Author translations as ContractSpec specs, resolve them through the framework-independent runtime, and project to adapters only when a host app needs them. This keeps web, server, mobile, CLI, and tests aligned on one source of truth.
Install the reusable packages
bun add @lssm-tech/lib.contracts-spec @lssm-tech/lib.translation-runtimeUse @lssm-tech/lib.contracts-spec/translations for catalogs and @lssm-tech/lib.translation-runtime for runtime resolution, formatting, diagnostics, snapshots, and optional adapters.
1. Translation contracts
Author TranslationSpec catalogs in or near the package that owns the copy. Keep meta.key stable, locale as BCP 47, placeholders documented, and owners explicit.
packages/libs/contracts-spec/src/translations@lssm-tech/lib.contracts-spec/translations
2. Runtime resolution
Use one request/runtime instance to negotiate locale, apply fallback chains, resolve tenant/user overrides, format ICU messages, and collect diagnostics.
packages/libs/translation-runtime/src/runtime.ts@lssm-tech/lib.translation-runtime
3. Optional adapters
Project a runtime snapshot to i18next only when a host app already uses i18next. Keep ContractSpec specs and snapshots canonical.
packages/libs/translation-runtime/src/adapters/i18next.ts@lssm-tech/lib.translation-runtime/i18next
Define the contract first
The spec layer owns keys, locales, versions, owners, placeholders, fallback intent, and translator context. Do not make framework JSON files or i18next resources the canonical source.
import { defineTranslation } from "@lssm-tech/lib.contracts-spec/translations";
export const checkoutEn = defineTranslation({
meta: {
key: "checkout.messages",
version: "1.0.0",
domain: "checkout",
owners: [{ team: "growth" }],
},
locale: "en-US",
fallback: "en-US",
messages: {
"checkout.pay": {
value: "Pay {amount, number, currency}",
description: "Primary checkout submit label.",
placeholders: [{ name: "amount", type: "currency" }],
},
},
});Resolve at runtime
Create a runtime with requested locales, fallback locales, specs, and diagnostics. Server hosts should create one runtime per request; mobile hosts should pass device/user locales explicitly.
import { createTranslationRuntime } from "@lssm-tech/lib.translation-runtime";
import { checkoutEn, checkoutFr } from "./translations/checkout";
const runtime = createTranslationRuntime({
defaultLocale: "en-US",
requestedLocales: [requestLocale, "fr-FR", "en-US"],
fallbackLocales: ["en-US"],
specs: [checkoutEn, checkoutFr],
onDiagnostic: (diagnostic) => reportI18nDiagnostic(diagnostic),
});
const label = runtime.t("checkout.pay", { amount: 42 });Package-level helper pattern
For package-local copy, use createI18nFactory to expose a small typed helper while still registering real TranslationSpec catalogs.
import { createI18nFactory } from "@lssm-tech/lib.contracts-spec/translations";
import { packageEn, packageFr } from "./catalogs";
export const packageI18n = createI18nFactory({
specKey: "my-package.messages",
catalogs: [packageEn, packageFr],
});
export const t = packageI18n.create("fr-FR").t;Production checklist
- Define TranslationSpec catalogs before wiring UI strings into components.
- Validate catalogs in tests so missing placeholders, invalid locales, and duplicate keys fail early.
- Create a translation runtime per server request when tenant, workspace, team, user, or request locale can differ.
- Serialize and hydrate the same runtime snapshot for SSR or streaming hosts; do not renegotiate locale during first client render.
- Let React Native hosts detect locale and load required Intl polyfills before formatting ICU messages.
- Use diagnostics as release evidence: missing keys, fallback usage, invalid snapshots, and adapter warnings should be visible in CI or observability.
Build a contract-driven form
Define schema-backed form data, fields, layout, policy, and submit actions as one reusable ContractSpec surface.
Set up Drizzle databases
Connect AuthOS, BillingOS, identity RBAC, migrations, and deterministic seeds to a host-owned Drizzle/PostgreSQL database boundary.
Why ContractSpec
Keep educational and comparison content reachable without letting it define the primary OSS learning path.