Getting started
Internationalization
Every string the components render comes from a catalog. English is the default, Brazilian Portuguese ships built in, and any phrase can be overridden.
Every visible string the package generates (aria-labels, placeholders, empty states, “Copy”, “On this page”, toast and dialog buttons) comes from a message catalog. The default language is English; Brazilian Portuguese ships built in and any other language can be registered. There are two ways to choose the language.
SSR apps: I18nProvider
On the server, wrap the tree in I18nProvider. The locale lives in Svelte context, so each request renders in its own language and concurrent requests never share state:
<!-- src/routes/+layout.svelte -->
<script>
import { I18nProvider } from "@codeatlasdev/ui";
let { data, children } = $props();
</script>
<I18nProvider locale={data.locale}>
{@render children()}
</I18nProvider>
locale is reactive: change the prop and every component inside re-renders. Pass messages to override phrases for that subtree (see below).
Client-only apps: setLocale
SPAs and Astro islands have no per-request concern, so one module-level locale shared by every island is enough:
<script>
import { setLocale } from "@codeatlasdev/ui";
setLocale("pt-BR");
</script>
On the server setLocale does nothing (and warns in dev), because module state would be shared by every concurrent request. Use I18nProvider in SSR layouts. An I18nProvider always wins over setLocale.
Add a language
Locales are any BCP 47 tag. Register a dictionary once, at module load. You only translate the keys you need; the rest fall back to English:
// src/lib/i18n.ts
import { registerLocale } from "@codeatlasdev/ui";
registerLocale("es", {
common: { close: "Cerrar", back: "Atrás" },
docs: { toc: "En esta página" },
});
Then use it with <I18nProvider locale="es"> or setLocale("es"). Region tags resolve to the base language (es-MX uses es), and unknown tags fall back to English. The exported Messages type describes the full shape and MessagesOverride the partial one.
Override phrases
I18nProvider’s messages prop and setLocale’s second argument are a deep partial of the catalog, merged over the chosen locale:
<I18nProvider locale="en" messages={{ docs: { toc: "In this article" }, common: { copy: "Copy link" } }}>
{@render children()}
</I18nProvider>
A later setLocale call replaces the previous overrides. Message functions (strings with runtime values) keep their signature:
setLocale("en", {
notificationBell: { label: (unread) => `Inbox, ${unread} new` },
});
Explicit props win
A text prop always beats the catalog. Components that take text (label, placeholder, confirmLabel, emptyText, …) fall back to the catalog only when the prop is omitted:
<TableOfContents {headings} label="Neste artigo" />
Reactive locale
With setLocale, the locale is shared module state, so every component and every island on the page re-renders when it changes. Read it with locale.value:
<script>
import { locale, setLocale } from "@codeatlasdev/ui";
</script>
<button onclick={() => setLocale(locale.value === "en" ? "pt-BR" : "en")}>
{locale.value}
</button>
Inside an I18nProvider, read the active locale and messages from context with useLocale() and useT(), called at the top level of a component <script>:
<script>
import { useLocale, useT } from "@codeatlasdev/ui";
const locale = useLocale();
const t = useT();
</script>
<p lang={locale()}>{t().common.close}</p>
Note
With Astro, each island is a separate Svelte app but they share the module, so one setLocale call in the first island that runs is enough. Call it before the islands that render text.
Catalog
The catalog is grouped by component, for example docs.search.placeholder, dialog.close, pagination.next, fileUpload, dataTable and toast. English (en.ts) is the source of truth; pt-BR.ts is typed as Messages, so a missing key is a type error. A test checks that both locales have identical keys.
Server errors
createErrorHandler never reads global locale state. Give it the language of the fallback message, fixed or per request:
// src/hooks.server.ts
import { createErrorHandler } from "@codeatlasdev/ui/kit";
export const handleError = createErrorHandler({ locale: (event) => event.locals.locale as string });
Dates, numbers and masks are separate
The UI language does not change date or number formatting. Pass locale to the components that format values, for example DatePicker, which defaults to pt-BR:
<DatePicker locale="en-US" label="Start date" />
Formatting helpers such as formatCurrency, formatDate and formatNumber take their own locale too.
In tests
The default is English. In browser tests, call setLocale("pt-BR") where a test asserts Portuguese text and reset with setLocale("en") in afterEach, so the module state does not leak between tests. In server-render tests, wrap the component in I18nProvider instead.