Files
WRNexusJSDoc/app/pages/packages/i18n.wrn
T

244 lines
20 KiB
Plaintext

page wrnexusi18n {
seo {
title = "@wrnexus/i18n"
description = "Translation loading, locale resolution, and Intl formatting."
}
view {
<div class="docs-shell">
<a href="#main" class="skip-link">Skip to content</a>
<header class="topbar">
<a class="brand" href="/"><span>W</span> WRNexusJS</a>
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.8.7</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
</header>
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
<main id="main" class="portal-main docs-layout docs-layout--navigation"><aside class="docs-section-menu" aria-label="Documentation sections"><nav>
<section><strong>Get started</strong><a href="/getting-started">Quick start</a><a href="/tutorial">Tutorial</a><a href="/guides/full-stack-auth-demo">Auth dashboard demo</a><a href="/guides/configuration-and-profiles">Configuration</a></section>
<section><strong>Build</strong><a href="/guides/pages-and-components">Pages and components</a><a href="/guides/routing">Routing</a><a href="/guides/server-data">Server data</a><a href="/guides/forms-and-validation">Forms and validation</a><a href="/guides/database">Database</a></section>
<section><strong>Auth and security</strong><a href="/guides/authentication">Authentication</a><a href="/guides/authorization">Authorization</a><a href="/guides/security">Application security</a><a href="/security">Security policy</a></section>
<section><strong>Reference</strong><a href="/packages">Packages overview</a><a href="/packages/cli">CLI</a><a href="/language">.wrn language</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/examples">Examples</a></section>
<section class="package-menu"><strong>Packages</strong><a href="/packages/ai">@wrnexus/ai</a><a href="/packages/auth">@wrnexus/auth</a><a href="/packages/authz">@wrnexus/authz</a><a href="/packages/benchmark">@wrnexus/benchmark</a><a href="/packages/cache">@wrnexus/cache</a><a href="/packages/captcha">@wrnexus/captcha</a><a href="/packages/cli">@wrnexus/cli</a><a href="/packages/compiler">@wrnexus/compiler</a><a href="/packages/content">@wrnexus/content</a><a href="/packages/core">@wrnexus/core</a><a href="/packages/csr">@wrnexus/csr</a><a href="/packages/db">@wrnexus/db</a><a href="/packages/dev-server">@wrnexus/dev-server</a><a href="/packages/dev-toolbar">@wrnexus/dev-toolbar</a><a href="/packages/encryption">@wrnexus/encryption</a><a href="/packages/graphql">@wrnexus/graphql</a><a href="/packages/helpers">@wrnexus/helpers</a><a href="/packages/i18n">@wrnexus/i18n</a><a href="/packages/identity">@wrnexus/identity</a><a href="/packages/image">@wrnexus/image</a><a href="/packages/jwt">@wrnexus/jwt</a><a href="/packages/language-server">@wrnexus/language-server</a><a href="/packages/mcp">@wrnexus/mcp</a><a href="/packages/mobile">@wrnexus/mobile</a><a href="/packages/native">@wrnexus/native</a><a href="/packages/oauth">@wrnexus/oauth</a><a href="/packages/observability">@wrnexus/observability</a><a href="/packages/playground">@wrnexus/playground</a><a href="/packages/plugin">@wrnexus/plugin</a><a href="/packages/pubsub">@wrnexus/pubsub</a><a href="/packages/pwa">@wrnexus/pwa</a><a href="/packages/queue">@wrnexus/queue</a><a href="/packages/reactive">@wrnexus/reactive</a><a href="/packages/realtime">@wrnexus/realtime</a><a href="/packages/router">@wrnexus/router</a><a href="/packages/security">@wrnexus/security</a><a href="/packages/ssr">@wrnexus/ssr</a><a href="/packages/store">@wrnexus/store</a><a href="/packages/styles">@wrnexus/styles</a><a href="/packages/syntax">@wrnexus/syntax</a><a href="/packages/test">@wrnexus/test</a><a href="/packages/tracking">@wrnexus/tracking</a><a href="/packages/typecheck">@wrnexus/typecheck</a><a href="/packages/ui">@wrnexus/ui</a><a href="/packages/uploader">@wrnexus/uploader</a><a href="/packages/validation">@wrnexus/validation</a></section>
</nav></aside><div class="docs-reading-column">
<article class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/i18n</span></nav><section class="doc-intro"><span class="eyebrow">Frontend · Package reference</span><h1>@wrnexus/i18n</h1><p>Translation loading, locale resolution, and Intl formatting.</p><div class="doc-meta"><span>v0.8.7</span><span>Private registry</span><span>Frontend</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the release-aligned package:</p><pre><code>bun add @wrnexus/i18n@0.8.7</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide"><p>Recursive locale loading, fallback resolution, SSR/browser translations, locale formatting, and language UI blocks for WRNexusJS.</p>
<h3 id="locale-files">Locale files</h3>
<p>Both layouts can be used together:</p>
<pre data-language="text"><code>app/locales/en.json
app/locales/en/common.json
app/locales/en/auth.json
app/locales/mr/common.json</code></pre>
<p>Namespaced files become keys such as <code>common.save</code> and <code>auth.signIn</code>.</p>
<pre data-language="ts"><code>import &#123; loadLocales, makeT, resolveI18n, resolveLang &#125; from &quot;@wrnexus/i18n&quot;;
const i18n = resolveI18n(loadLocales(&quot;app/locales&quot;, &#123; strict: true &#125;), &#123;
default: &quot;en&quot;,
locales: [&quot;en&quot;, &quot;mr&quot;, &quot;hi&quot;],
fallbacks: &#123; &quot;mr-IN&quot;: [&quot;mr&quot;, &quot;en&quot;] &#125;,
cookie: &#123; name: &quot;wire-lang&quot;, sameSite: &quot;Lax&quot;, secure: true &#125;,
&#125;);
const lang = resolveLang(i18n, cookieValue, request.headers.get(&quot;accept-language&quot;));
const t = makeT(i18n, lang);
t(&quot;common.hello&quot;, &#123; name: &quot;Ajay&quot; &#125;);</code></pre>
<h3 id="resolution-behavior">Resolution behavior</h3>
<ul>
<li>normalized BCP-47-style locale names</li>
<li>cookie preference</li>
<li>weighted <code>Accept-Language</code></li>
<li>wildcard language ranges</li>
<li>regional base fallback</li>
<li>explicit fallback chains</li>
<li>configured default language</li>
<li>automatic RTL for Arabic, Hebrew, Persian, Urdu, and related languages</li>
</ul>
<p>Locale JSON is size-limited and rejects prototype-pollution keys. Recursive namespace collisions are resolved safely.</p>
<h3 id="views-and-runtime">Views and runtime</h3>
<pre data-language="html"><code>&lt;h1 data-t=&quot;dashboard.title&quot;&gt;Dashboard&lt;/h1&gt;
&lt;input t:placeholder=&quot;search.placeholder&quot; /&gt;</code></pre>
<p>Text and translated attributes are resolved during SSR. Active/fallback messages are serialized safely for the language runtime, which rebinds <code>data-t</code> markers after client navigation.</p>
<p>Enable <code>i18nPlugin()</code> to use:</p>
<ul>
<li><code>&lt;LanguageSwitcher /&gt;</code></li>
<li><code>&lt;LocaleStatus /&gt;</code></li>
</ul>
<p><code>LanguageSwitcher</code> renders a native <code>select[data-wire-lang]</code>. The packaged runtime validates the selection against the configured locales, writes the configured language cookie, updates the document <code>lang</code>/<code>dir</code> attributes, emits <code>wrnexus:language-change</code>, and reloads so the next SSR request uses the same cookie. No application-owned browser script is required.</p>
<h3 id="formatting">Formatting</h3>
<ul>
<li><code>formatNumber</code></li>
<li><code>formatCurrency</code></li>
<li><code>formatDate</code></li>
<li><code>formatRelativeTime</code></li>
<li><code>plural</code></li>
<li><code>createLocaleFormatter</code></li>
<li><code>translationCoverage</code></li>
<p>Localization tooling can extract statically discoverable <code>t(&quot;key&quot;)</code>, <code>i18n.t(&quot;key&quot;)</code>, and <code>data-i18n=&quot;key&quot;</code> usage, compare every locale with a reference, and create layout-stressing pseudo-locales:</p>
</ul>
<pre data-language="ts"><code>import &#123;
auditLocaleKeys,
createPseudoLocale,
extractTranslationKeysFromFiles,
&#125; from &quot;@wrnexus/i18n&quot;;
const used = extractTranslationKeysFromFiles(sourceFiles);
const coverage = auditLocaleKeys(messages, &quot;en&quot;);
const enXA = createPseudoLocale(messages.en);
const arXB = createPseudoLocale(messages.en, &#123; rtl: true &#125;);</code></pre>
<p>Pseudo-localization preserves interpolation placeholders and markup tags. RTL pseudo output uses Unicode direction controls, while runtime direction detection continues to derive <code>rtl</code> from Arabic and other RTL language subtags.</p></section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>import &#123; TFunction &#125; from '@wrnexus/core';
export &#123; I18nPluginOptions, i18nComponentsDir, default as i18nPlugin &#125; from './plugin.js';
import '@wrnexus/plugin';
/**
* Locale-aware formatting helpers (Intl-based) + pluralization. Pair with the
* request language (`ctx.lang`) so numbers, dates, and currencies render right
* for each user.
*/
/** Format a number for a locale (e.g. 1234.5 → &quot;1,234.5&quot;). */
declare function formatNumber(value: number, lang: string, options?: Intl.NumberFormatOptions): string;
/** Format a currency amount (e.g. 9.99, &quot;USD&quot; → &quot;$9.99&quot;). */
declare function formatCurrency(value: number, currency: string, lang: string): string;
/** Format a date/timestamp for a locale. */
declare function formatDate(value: Date | number | string, lang: string, options?: Intl.DateTimeFormatOptions): string;
/** Relative time, e.g. -3 days → &quot;3 days ago&quot; (localized). */
declare function formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, lang: string): string;
/**
* Pick a plural form for `count` in `lang` using CLDR rules, e.g.
* `plural(n, &#123; one: &quot;1 item&quot;, other: &quot;# items&quot; &#125;, lang)` — &quot;#&quot; is replaced by n.
*/
declare function plural(count: number, forms: Partial&lt;Record&lt;Intl.LDMLPluralRule, string&gt;&gt;, lang: string): string;
interface ExtractedTranslationKey &#123;
key: string;
file?: string;
offset: number;
&#125;
declare function extractTranslationKeys(source: string, file?: string): ExtractedTranslationKey[];
declare function extractTranslationKeysFromFiles(files: Iterable&lt;string&gt;): ExtractedTranslationKey[];
declare function flattenMessageKeys(messages: Messages, prefix?: string): string[];
declare function auditLocaleKeys(messages: Record&lt;string, Messages&gt;, referenceLocale: string): Record&lt;string, &#123;
missing: string[];
extra: string[];
&#125;&gt;;
declare function pseudoLocalize(value: string, options?: &#123;
rtl?: boolean;
&#125;): string;
declare function createPseudoLocale(messages: Messages, options?: &#123;
rtl?: boolean;
&#125;): Messages;
declare function flattenMessages(messages: Messages, prefix?: string, output?: Record&lt;string, string&gt;): Record&lt;string, string&gt;;
declare function localeFallbacks(locale: string, fallback?: string): string[];
declare function translationCoverage(i18n: ResolvedI18n): Record&lt;string, &#123;
translated: number;
total: number;
percentage: number;
missing: string[];
extra: string[];
&#125;&gt;;
interface LocaleFormatter &#123;
number(value: number, options?: Intl.NumberFormatOptions): string;
currency(value: number, currency: string, options?: Omit&lt;Intl.NumberFormatOptions, &quot;style&quot; | &quot;currency&quot;&gt;): string;
date(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;
list(values: string[], options?: Intl.ListFormatOptions): string;
&#125;
declare function createLocaleFormatter(locale: string, timeZone?: string, calendar?: string): LocaleFormatter;
/** ICU-style plural/select templates with exact values and recursive interpolation. */
declare function formatMessage(template: string, params: Record&lt;string, string | number&gt;, locale: string): string;
/**
* @wrnexus/i18n — deterministic locale loading, fallback resolution, SSR
* translation markers, browser translation helpers, and UI language controls.
*/
type Messages = Record&lt;string, unknown&gt;;
interface LocaleLoadOptions &#123;
/** Throw on invalid JSON instead of warning and continuing. */
strict?: boolean;
/** Maximum JSON file size. Default 1 MiB. */
maxFileBytes?: number;
&#125;
interface I18nCookieConfig &#123;
name?: string;
maxAge?: number;
path?: string;
sameSite?: &quot;Strict&quot; | &quot;Lax&quot; | &quot;None&quot;;
secure?: boolean;
&#125;
interface I18nConfig &#123;
default?: string;
locales?: string[];
/** Human-readable locale names used by package language controls. */
labels?: Record&lt;string, string&gt;;
/** Per-locale fallback override. Example: `&#123; &quot;fr-CA&quot;: [&quot;fr&quot;, &quot;en&quot;] &#125;`. */
fallbacks?: Record&lt;string, string[]&gt;;
/** Locale direction overrides. Arabic/Hebrew/Persian/Urdu are RTL automatically. */
direction?: Record&lt;string, &quot;ltr&quot; | &quot;rtl&quot;&gt;;
cookie?: I18nCookieConfig;
strict?: boolean;
&#125;
interface ResolvedI18n &#123;
default: string;
langs: string[];
messages: Record&lt;string, Messages&gt;;
fallbacks: Record&lt;string, string[]&gt;;
direction: Record&lt;string, &quot;ltr&quot; | &quot;rtl&quot;&gt;;
labels: Record&lt;string, string&gt;;
cookie: Required&lt;I18nCookieConfig&gt;;
&#125;
declare const LANG_COOKIE = &quot;wire-lang&quot;;
declare const I18N_JS_HREF = &quot;/__wrnexus/i18n.js&quot;;
declare function normalizeLocale(locale: string): string;
/**
* Load both supported layouts:
* - `locales/en.json`
* - `locales/en/common.json`, `locales/en/auth.json`
*
* Namespaced files become `messages.en.common` and `messages.en.auth`.
*/
declare function loadLocales(dir: string, options?: LocaleLoadOptions): Record&lt;string, Messages&gt;;
declare function localeDirection(locale: string, overrides?: Record&lt;string, &quot;ltr&quot; | &quot;rtl&quot;&gt;): &quot;ltr&quot; | &quot;rtl&quot;;
declare function resolveI18n(messages: Record&lt;string, Messages&gt;, config?: I18nConfig): ResolvedI18n;
declare function lookupMessage(messages: Messages | undefined, key: string): string | undefined;
declare function interpolate(message: string, params?: Record&lt;string, string | number&gt;): string;
declare function translationChain(i18n: ResolvedI18n, locale: string): string[];
declare function makeT(i18n: ResolvedI18n, lang: string): TFunction;
/** Deeply apply tenant-specific translations without mutating the shared locale bundle. */
declare function withTenantMessages(i18n: ResolvedI18n, overrides: Record&lt;string, Messages&gt;): ResolvedI18n;
/** Load only common and route-specific messages for one locale. */
declare function loadRouteMessages(directory: string, locale: string, route: string): Messages;
declare function parseAcceptLanguage(value: string | null): string[];
declare function resolveLang(i18n: ResolvedI18n, cookieValue: string | undefined, acceptLanguage: string | null): string;
declare function translateHtml(html: string, t: TFunction): string;
declare function renderI18nData(i18n: ResolvedI18n, lang: string): string;
declare const I18N_RUNTIME: string;
export &#123; type ExtractedTranslationKey, I18N_JS_HREF, I18N_RUNTIME, type I18nConfig, type I18nCookieConfig, LANG_COOKIE, type LocaleFormatter, type LocaleLoadOptions, type Messages, type ResolvedI18n, auditLocaleKeys, createLocaleFormatter, createPseudoLocale, extractTranslationKeys, extractTranslationKeysFromFiles, flattenMessageKeys, flattenMessages, formatCurrency, formatDate, formatMessage, formatNumber, formatRelativeTime, interpolate, loadLocales, loadRouteMessages, localeDirection, localeFallbacks, lookupMessage, makeT, normalizeLocale, parseAcceptLanguage, plural, pseudoLocalize, renderI18nData, resolveI18n, resolveLang, translateHtml, translationChain, translationCoverage, withTenantMessages &#125;;
</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid"><article class="example-card"><h3>Namespaced files become keys such as common.save and auth.signIn.</h3><pre data-language="ts"><code>import &#123; loadLocales, makeT, resolveI18n, resolveLang &#125; from &quot;@wrnexus/i18n&quot;;
const i18n = resolveI18n(loadLocales(&quot;app/locales&quot;, &#123; strict: true &#125;), &#123;
default: &quot;en&quot;,
locales: [&quot;en&quot;, &quot;mr&quot;, &quot;hi&quot;],
fallbacks: &#123; &quot;mr-IN&quot;: [&quot;mr&quot;, &quot;en&quot;] &#125;,
cookie: &#123; name: &quot;wire-lang&quot;, sameSite: &quot;Lax&quot;, secure: true &#125;,
&#125;);
const lang = resolveLang(i18n, cookieValue, request.headers.get(&quot;accept-language&quot;));
const t = makeT(i18n, lang);
t(&quot;common.hello&quot;, &#123; name: &quot;Ajay&quot; &#125;);</code></pre></article><article class="example-card"><h3>## Views and runtime</h3><pre data-language="html"><code>&lt;h1 data-t=&quot;dashboard.title&quot;&gt;Dashboard&lt;/h1&gt;
&lt;input t:placeholder=&quot;search.placeholder&quot; /&gt;</code></pre></article><article class="example-card"><h3>reference, and create layout-stressing pseudo-locales</h3><pre data-language="ts"><code>import &#123;
auditLocaleKeys,
createPseudoLocale,
extractTranslationKeysFromFiles,
&#125; from &quot;@wrnexus/i18n&quot;;
const used = extractTranslationKeysFromFiles(sourceFiles);
const coverage = auditLocaleKeys(messages, &quot;en&quot;);
const enXA = createPseudoLocale(messages.en);
const arXB = createPseudoLocale(messages.en, &#123; rtl: true &#125;);</code></pre></article></div></section></article></div>
<aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#locale-files">Locale files</a><a class="toc-level-3" href="#resolution-behavior">Resolution behavior</a><a class="toc-level-3" href="#views-and-runtime">Views and runtime</a><a class="toc-level-3" href="#formatting">Formatting</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
</main>
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS 0.8.7</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
</div>
}
}