---
title: "Internationalization"
description: "en-US and id-ID message files, $t, and how to add a locale in Taman."
canonical_url: "https://vinicunca.dev/taman/guides/i18n"
---
# Internationalization

> en-US and id-ID message files, $t, and how to add a locale in Taman.

Taman ships **English** and **Bahasa Indonesia**. Language options live in `@taman/constants`:

```ts
export const SUPPORTED_LANGUAGES = [
  { label: 'English', value: 'en-US', icon: 'cif:gb' },
  { label: 'Bahasa Indonesia', value: 'id-ID', icon: 'cif:id' },
];
```

Default locale is `preferences.app.locale` → **en-US** (`taman-core` defaults).

## How messages load

`apps/web/src/locales/index.ts` glob-imports `./langs/**/*.json` and passes them to `@taman/locales` `loadLocalesMapFromDir`:

```ts
/\.\/langs\/([^/]+)\/(.*)\.json$/;
```

Each file becomes a **namespace** named after the filename (`todo.json` → `todo.*`). Route titles use `$t('todo.title')`.

App-specific files today:

| File | Typical keys |
| --- | --- |
| `page.json` | Auth page copy |
| `todo.json` | Todo feature |
| `form.json` | (`en-US` only in the source tree) |
| `examples.json` | Example gallery — **removed** on generate |

Shared UI strings (form rules, layout chrome) live in `@taman/locales` and `@taman/app-ui`, not only in the app folder.

## Using `$t`

```ts
import { $t } from '#/locales';

$t('todo.title');
$t('ui.formRules.required', [label]); // adapter/form.ts
```

`$t` is also valid in route `meta.title` because vue-i18n is installed before the router. `bootstrap.ts` watches the route and, when `preferences.app.dynamicTitle` is true, sets `document.title` to `$t(routeTitle) - preferences.app.name`.

`missingWarn` is on in development (`!import.meta.env.PROD`).

## Switching language

The header language widget (`widget.languageToggle`, default on) writes `preferences.app.locale`. Preferences persist under the app namespace in `localStorage`.

## Adding a locale

1. Add a value to `SUPPORTED_LANGUAGES` and the `LanguageOption['value']` union.
2. Create `apps/web/src/locales/langs/<locale>/*.json` mirroring `en-US`.
3. Translate `@taman/locales` (and email templates in `@taman/emails` — `en-US` and `id-ID` JSON plus Maizzle).
4. Keep taman-core `useSimpleLocale` in sync if you rely on overlay fallback strings (`en-US` / other).

## Email locales

Transactional mail uses `matchEmailLocale` / `emailLocale(request)` so invitation, verify, and reset-password emails follow the request locale. Templates: `packages/server/emails`.


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
