import type { DeepKeys } from '../utils/deep-keys.js'; /** * Single source of truth for the locales the library declares support for. * * "Declared, data optional": `en`/`de` ship translation data; `fr`/`es`/`it`/`nl` * are valid target locales a consumer can register its own bundles for. The list * lives here exactly once — both the `Locale` union and the runtime * `isLocaleSupported` guard derive from it, so the type and the runtime check can * never drift apart (previously the same six codes were hardcoded twice). */ export declare const SUPPORTED_LOCALES: readonly ["en", "de", "fr", "es", "it", "nl"]; export type Locale = (typeof SUPPORTED_LOCALES)[number]; /** * Runtime guard derived from {@link SUPPORTED_LOCALES} — the single source of * truth. Used by both the registry and the request-scoped locale state, so the * type and the runtime check can never drift apart. */ export declare function isLocaleSupported(locale: string): locale is Locale; export type Translations = { [key: string]: string | Translations; }; export type PackageTranslations = Partial>; /** * Base translation function signature */ export type TranslationFunction = (key: string, params?: Record) => string; /** * Translation store interface */ export interface TranslationStore { locale: Locale; translations: PackageTranslations; t: TranslationFunction; } export type TranslationParams = Record string)>; type ExtractParams = T extends `${string}{{${infer Param}}}${infer Rest}` ? Param | ExtractParams : never; export type TranslationParametersFor> = PathValue extends string ? ExtractParams> extends never ? Record : Record>, string | number | boolean> : TranslationParams; export type TypedTranslationFunction = >(key: K, ...args: TranslationParametersFor extends Record ? [params?: TranslationParams, options?: TranslationOptions] : [params: TranslationParametersFor, options?: TranslationOptions]) => string; export interface I18nStore { locale: Locale; translations: Partial>; fallbackLocale: Locale; packageTranslations: Map>>; loadedLocales: Set; loadingLocales: Set; } export interface I18nConfig { defaultLocale?: Locale; fallbackLocale?: Locale; translations?: Partial>; detectBrowserLocale?: boolean; lazyLoad?: boolean; /** * Optional error handler. Invoked when a registered loader rejects * (loadLocale failure) or when an unsupported locale is requested * (setLocale failure). When omitted, the service falls back to * `console.warn` so consumers still see something during development. */ onError?: (error: I18nError) => void; } export type I18nError = { type: 'load-failed'; locale: Locale; cause: unknown; } | { type: 'unsupported-locale'; locale: string; } | { type: 'load-failed-no-fallback'; locale: Locale; }; /** * Surfaced through {@link I18nConfigureOptions.onMissingKey} when `translate` * resolves a key *nowhere* — neither the active nor the fallback locale, in any * package or the global bundle — and falls back to returning the key string * itself. The loud signal for "this will render as its raw key in production". */ export interface I18nMissingKey { /** The unresolved key, exactly as passed to `t` / `translate`. */ key: string; /** Active locale at the time of the miss. */ locale: Locale; /** Fallback locale that was also tried and also missed. */ fallbackLocale: Locale; /** Package scope, when the call was package-scoped (`useTranslate` / `packageName`). */ packageName?: string; /** Always `no-translation` today; a discriminant reserved for future miss reasons. */ reason: 'no-translation'; } type WidenStringLiteralsDeep = T extends string ? string : T extends Array ? Array> : T extends object ? { [K in keyof T]: WidenStringLiteralsDeep; } : T; export type TranslationSchema = WidenStringLiteralsDeep; export interface TranslationOptions { packageName?: string; fallbackToGlobal?: boolean; interpolate?: boolean; /** * @internal Whether an unresolved key reports through `onMissingKey`. Defaults * to `true`. Set `false` for internal probes that expect a miss — the * `pluralize` lookup of an *optional* `_plural` object, which legitimately * resolves nowhere and must not masquerade as a missing translation. */ reportMissing?: boolean; } export type TranslationLoader = (locale: Locale) => Promise; export type PackageTranslationLoader = (packageName: string, locale: Locale) => Promise; type PathValue = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue : never : P extends keyof T ? T[P] : never; export interface PluralRules { zero?: string; one?: string; two?: string; few?: string; many?: string; other: string; } export interface PluralParams extends TranslationParams { count: number; } export type ValidateTemplate = T extends `${string}{{${string}}}${string}` ? T : never; export type CreatePackageTypes = { keys: DeepKeys; params: { [K in DeepKeys]: PathValue extends string ? ExtractParams> extends never ? Record : Record>, string | number | boolean> : Record; }; }; export interface PackageI18n { /** * Context-scoped translation **hook**. Call during component initialisation to * get a typed `t` bound to the nearest ``'s locale (or the base * locale when none is mounted). This is the SSR-correct, reactive accessor — * re-exported by consumers as `useI18n`. */ useTranslate: () => TypedTranslationFunction; /** * Typed `t` for non-component use (tests, server-side utilities) where no * component context is available. Not bound to a ``. In * components always prefer {@link useTranslate}, which is provider-scoped and * reactive. */ t: TypedTranslationFunction; exists: (key: string) => boolean; getLocales: () => Locale[]; register: () => void; /** * Eagerly and **additively** register one locale's already-imported bundle for * this package — the SSR escape hatch for a locale declared as a lazy * `options.loaders` entry. Call once at server/app start (with e.g. * `import de from '@urbicon-ui/blocks/i18n/de'`) so the first server render * already resolves that locale instead of rendering the fallback until the * client-only on-mount chunk load lands. Merges — it does not drop the eager * base bundle. Throws on an unsupported locale or a non-object bundle. */ registerLocale: (locale: Locale, bundle: Translations) => void; types: CreatePackageTypes; } export interface I18nComponentProps { locale?: Locale; fallbackToGlobal?: boolean; useI18n?: boolean; } export {};