import { type i18n as I18nInstance, type InitOptions } from 'i18next'; import { type LanguageLoader } from './languageSource'; type TranslationMap = Record; /** The two client-side buckets produced by one chain request. */ export interface TranslationBundles { /** Flattened Product/Brand/Tenant chain (last-write-wins by precedence). */ system: TranslationMap; /** The caller's own user-tier rows ({} when no unityUserId was supplied). */ overrides: TranslationMap; } /** * Fetches both translation bundles in ONE authenticated request. When * unityUserId is supplied the response also carries the caller's user-tier * rows (partitioned into bundles.overrides); tenantId scopes the Tenant/User * tiers to the active tenant. Resolve to null for "not ready" (nothing is * cached or applied); empty objects are legitimate results and ARE applied. */ export type TranslationLoader = (language: string, unityUserId?: string | null, tenantId?: string | null) => Promise; export interface InitializeI18nOptions { /** * Overrides how translation bundles are fetched. Apps whose generated * translation stack is not on the product API point the stock loader at * their own backend, e.g.: * loadTranslations: (lng, userId, tenantId) => * createDefaultTranslationLoader(ConfigService.tenantV2ApiUrl)(lng, userId, tenantId) */ loadTranslations?: TranslationLoader; /** * Overrides how the shell's language switcher list is fetched (mirrors * loadTranslations). Omit for the stock probe (product API first, legacy * Unity list as fallback). Apps that override loadTranslations with their * own backend usually point this at the same base, e.g.: * loadLanguages: createDefaultLanguageLoader(ConfigService.tenantV2ApiUrl) */ loadLanguages?: LanguageLoader; /** * App-provided default translation bundles per language code, merged OVER * the library's shipped locales and UNDER everything fetched from the * translation stack (Product/Brand/Tenant rows, then user overrides). * Nested or flat structure - bundles are flattened to literal dotted keys * at registration (see flattenBundle), so existing apps with nested JSON * files can pass them as-is. */ defaultBundles?: Record; /** * Extra i18next init options, spread LAST so they win over the defaults. * * NOTE: keySeparator/nsSeparator default to false here and should stay that * way - every layer (shipped locales, app defaultBundles, and the fetched * db tiers) stores literal dotted keys, so re-enabling a separator makes * lookups miss and silently fall back to English defaultValues. */ i18nConfig?: Partial; fallbackLanguage?: string; /** * Restrict i18next to these language codes (the fallback is added * automatically). Omit to accept any code - the default, because * udp.Language is the authoritative list and a tenant may define languages * the library ships no static bundle for. Set it when an app knows its * language set is fixed and wants a junk `?lng=` guarded out. */ supportedLanguages?: string[]; /** * Turn the whole translation stack off. Defaults to the app's .env flag * (REACT_APP_I18N_DISABLED, itself false by default), so * most apps set it there rather than here; pass it explicitly to override the * environment for one surface or a test. Note the polarity: this option is * positive (`enabled: false` turns i18n off), the env variable is negative * (`REACT_APP_I18N_DISABLED=true` turns i18n off). * * When false: no locale chunks are imported, no udp.Translation request is * made, no language is detected or persisted, and the shell hides the * language switcher. i18next is still initialised at the fallback language so * every t() renders its inline English defaultValue - the UI stays working * English rather than showing raw keys. */ enabled?: boolean; } /** * Build the standard translation loader against the backend hosting the * generated translation stack. Exported so apps can point the stock loader at * their own API via options.loadTranslations. * * ONE authenticated request per language: the chain GET returns Product, Brand * (the caller's brand - 'Default' until brand resolution exists) and Tenant * rows, plus - when unityUserId is supplied - the caller's own user-tier rows. * The response is scope-tagged and pre-ordered by precedence, so partitioning * is trivial: user rows (Scope 3) become the overrides bundle, everything else * flattens last-write-wins into the system bundle. * * The runtime never invokes this pre-auth - it hydrates from the local cache * until the user resolves - so token acquisition here is always safe. * @param baseUrl * Omit to auto-resolve: the product API is tried first, and any failure * (including 404 - the stack not being generated there) falls back to the * tenant API. The host that answers successfully is remembered for the * session. */ export declare const createDefaultTranslationLoader: (baseUrl?: string) => TranslationLoader; /** * Initializes the shared i18n instance used across UDP React applications. * Safe to call multiple times – initialization will only occur once per runtime. */ export declare const initializeUnityI18n: (options?: InitializeI18nOptions) => I18nInstance; /** * Kick off the (authenticated) translation load once the signed-in user is * known - ONE request returning the system chain plus the user's overrides. * Called by UserProvider when the user resolves; safe to call repeatedly * (duplicate calls share the in-flight load). Fire-and-forget by design: * labels swap in when the fetch lands rather than blocking render (the cache * hydration at startup makes that swap a visual no-op on repeat visits). */ export declare const refreshUserTranslations: (unityUserId: string) => Promise; /** * Switch the UI language without a flash of English. Prefer this over calling * i18n.changeLanguage() directly from UI. * * i18next flips i18n.language and emits languageChanged SYNCHRONOUSLY, so a * bare changeLanguage() re-renders every consumer before the new language has * any resource bundle: each t() falls through to its inline English * defaultValue, then repaints translated once the locale chunk and the db fetch * land. Visible only on the FIRST switch to a given language - afterwards the * bundle is already registered, which is why the flicker looks intermittent. * * Loading the target language's layers BEFORE the flip makes it a single * correct paint. Bundles are per language, so this is invisible while the * current language is still active. The init path already does this via * peekDetectedLanguage; this is the same guarantee for an explicit switch. */ export declare const changeLanguage: (language: string) => Promise; /** * Force-refetch a language's merged bundle after its rows changed server-side * (the translation management pages call this after a successful save/delete, * so the running app reflects the change without a reload). * * The language is evicted from the fresh set so the next ensureLanguage * revalidates instead of no-opping. When it is the CURRENT UI language the * refetch fires immediately - replacing the merged bundle and rewriting the * localStorage cache, so the change also survives a cold start. Any other * language is refetched on its next language switch. Fetches stay * authenticated-only: pre-auth (no active user) this only evicts. * @param languageCode Defaults to the current UI language. */ export declare const refreshTranslations: (languageCode?: string) => Promise; /** * Record the active tenant for translation fetches (called by TenantProvider * whenever the active tenant resolves or changes). Tenant and User tier rows * are tenant-scoped server-side, so a tenant CHANGE invalidates every fetched * language and refetches the current one; the first resolve before any fetch * just records the id for the upcoming load. */ export declare const setActiveTranslationTenant: (tenantId?: string) => Promise; /** Convenience accessor for the shared i18n instance. */ export declare const getI18nInstance: () => I18nInstance; export {}; //# sourceMappingURL=initializeI18n.d.ts.map