typesafe-i18n

The preset for typesafe-i18n. It reads LL.key() calls and the src/i18n/{locale}/index.ts locale files.

The typesafe-i18n preset supports typesafe-i18n.

Detection

The preset applies when the project depends on typesafe-i18n.

x18n.config.ts
import { defineConfig } from '@x18n/core'

export default defineConfig({
  presets: ['typesafe-i18n'],
})

Locale files

If your config has no resources, the preset uses src/i18n/{locale}/index.{ext} and, for namespaces, src/i18n/{locale}/{namespace}/index.{ext}. The keys of a namespace start with the name of the namespace: dashboard.title.

x18n reads a locale file without running it. The file must hold one static object, as the typesafe-i18n generator writes it:

src/i18n/en/index.ts
import type { BaseTranslation } from '../i18n-types'

const en = {
  HI: 'Hi {name:string}!',
  nav: { home: 'Home' },
} satisfies BaseTranslation

export default en

satisfies, as and a type on the variable (const de: Translation = { ... }) are fine. When x18n adds or removes a key, it rewrites only the object and keeps the rest of the file. A new locale file that x18n writes is a plain export default { ... }, without the type.

Supported syntax

A message is a function on the LL object. The path to the function is the key. In Svelte, the store is $LL. The preset reads .js, .ts, JSX, Vue and Svelte files.

src/routes/+page.svelte
<h1>{$LL.HI({ name: 'Ada' })}</h1>
<p>{$LL.dashboard.title()}</p>

Placeholders

Typed braces: {name}, {name:string}, {count:number|currency}, {gender|{male: his, *: their}} and {0}. The name is the part before : or |, so {name:string} in the base locale and {name} in a translation match. Plural parts such as {{apple|apples}} are not placeholders.

Extraction templates

KindsFilesTemplate
svelte-text{$LL.{key}({args})}
svelte-attribute{attr}={$LL.{key}({args})}
js-string, js-template**/*.svelte$LL.{key}({args})
vue-template-text{{ LL.{key}({args}) }}
vue-attribute:{attr}="LL.{key}({args})"
jsx-text{LL.{key}({args})}
jsx-attribute{attr}={LL.{key}({args})}
js-string, js-templateLL.{key}({args})

The templates add no import, because where LL comes from depends on the adapter (useI18nContext() in React, $i18n/i18n-svelte in Svelte).