{"version":3,"file":"initializeI18n.mjs","names":["i18n","I18nInstance","InitOptions","initReactI18next","LanguageDetector","AxiosResponse","apiMutate","ConfigService","readCachedBundle","writeCachedBundle","setLanguageLoader","LanguageLoader","flattenBundle","getStoredTenant","setI18nEnabled","isI18nStackEnabled","SYSTEM_NS","TranslationRow","label","value","scope","TranslationMap","Record","TranslationBundles","system","overrides","TranslationLoader","language","unityUserId","tenantId","Promise","InitializeI18nOptions","loadTranslations","loadLanguages","defaultBundles","i18nConfig","Partial","fallbackLanguage","supportedLanguages","enabled","isInitialized","LOCALE_LOADERS","default","fr-CA","es-US","SHIPPED_LOCALES","Object","keys","staticBundles","staticLoaded","Set","staticInflight","Map","lastApplied","resolveLocaleKey","find","locale","split","registerStaticBundles","entries","forEach","bundle","ensureStaticBundle","key","undefined","has","resolve","pending","get","load","then","module","add","previous","applyMergedBundle","catch","error","console","finally","delete","set","activeLoadTranslations","activeUnityUserId","activeTenantId","safeSessionUserId","sessionStorage","getItem","safeSessionTenantId","peekDetectedLanguage","fallback","fromQueryString","URLSearchParams","window","location","search","fromSessionStorage","fromCookie","document","cookie","entry","startsWith","slice","length","buildTranslationMap","languageData","reduce","acc","item","resolvedDefaultBaseUrl","createDefaultTranslationLoader","baseUrl","waitForConfig","productId","config","UNITY_PRODUCT_ID","urlFor","base","tenantV1ApiUrl","legacyParams","qs","toString","params","brand","toBundles","response","rows","Array","isArray","data","filter","row","attempt","Error","method","knownBaseUrl","productV1ApiUrl","fresh","inflight","removeResourceBundle","addResourceBundle","hydrateLanguage","cacheUserId","hasResourceBundle","ensureLanguage","bundles","initializeUnityI18n","options","use","init","lng","fallbackLng","debug","partialBundledLanguages","keySeparator","nsSeparator","returnEmptyString","interpolation","escapeValue","handleLanguageChanged","on","detection","order","caches","convertDetectedLanguage","includes","supportedLngs","react","bindI18nStore","refreshUserTranslations","PRELOAD_FETCH_TIMEOUT_MS","changeLanguage","race","setTimeout","refreshTranslations","languageCode","refetch","setActiveTranslationTenant","next","size","clear","getI18nInstance"],"sources":["../../../src/utilities/i18n/initializeI18n.ts"],"sourcesContent":["import i18n, { type i18n as I18nInstance, type InitOptions } from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport LanguageDetector from 'i18next-browser-languagedetector';\nimport type { AxiosResponse } from 'axios';\nimport { apiMutate } from '../useAxiosMutate';\nimport { ConfigService } from '../../configService';\nimport { readCachedBundle, writeCachedBundle } from './translationCache';\nimport { setLanguageLoader, type LanguageLoader } from './languageSource';\nimport { flattenBundle } from './flattenBundle';\nimport { getStoredTenant } from '../storage/UnitySessionStorage';\nimport { setI18nEnabled, isI18nStackEnabled } from './i18nEnabled';\n\n/**\n * Each language renders from ONE merged bundle in the 'translation' namespace:\n * the SYSTEM chain (Product/Brand/Tenant rows, flattened by tier precedence\n * server-side) with the signed-in user's override rows merged over it per key.\n * See applyMergedBundle for why the lookup deliberately does NOT use a separate\n * overrides namespace.\n *\n * Every fetch is authenticated (no anonymous translation calls, by policy), so\n * NO network happens before the user resolves: startup paints from the local\n * cache, and refreshUserTranslations kicks off ONE request post-auth that\n * returns the whole chain plus the caller's user rows.\n *\n * The system and user parts are cached SEPARATELY in localStorage\n * (stale-while-revalidate): the system entry is shared/pre-auth-safe, the user\n * entry is per-user and cleared at logout. Both hydrate synchronously (merged)\n * before first paint, and the network response replaces the merge in the\n * background - so repeat visits paint fully translated (overrides included)\n * with no flicker.\n */\nconst SYSTEM_NS = 'translation';\n\n/** A udp.Translation row as served by the chain GET. */\ninterface TranslationRow {\n  label?: string;\n  value?: string;\n  scope?: number;\n}\n\ntype TranslationMap = Record<string, string>;\n\n/** The two client-side buckets produced by one chain request. */\nexport interface TranslationBundles {\n  /** Flattened Product/Brand/Tenant chain (last-write-wins by precedence). */\n  system: TranslationMap;\n  /** The caller's own user-tier rows ({} when no unityUserId was supplied). */\n  overrides: TranslationMap;\n}\n\n/**\n * Fetches both translation bundles in ONE authenticated request. When\n * unityUserId is supplied the response also carries the caller's user-tier\n * rows (partitioned into bundles.overrides); tenantId scopes the Tenant/User\n * tiers to the active tenant. Resolve to null for \"not ready\" (nothing is\n * cached or applied); empty objects are legitimate results and ARE applied.\n */\nexport type TranslationLoader = (\n  language: string,\n  unityUserId?: string | null,\n  tenantId?: string | null\n) => Promise<TranslationBundles | null>;\n\nexport interface InitializeI18nOptions {\n  /**\n   * Overrides how translation bundles are fetched. Apps whose generated\n   * translation stack is not on the product API point the stock loader at\n   * their own backend, e.g.:\n   *   loadTranslations: (lng, userId, tenantId) =>\n   *     createDefaultTranslationLoader(ConfigService.tenantV2ApiUrl)(lng, userId, tenantId)\n   */\n  loadTranslations?: TranslationLoader;\n  /**\n   * Overrides how the shell's language switcher list is fetched (mirrors\n   * loadTranslations). Omit for the stock probe (product API first, legacy\n   * Unity list as fallback). Apps that override loadTranslations with their\n   * own backend usually point this at the same base, e.g.:\n   *   loadLanguages: createDefaultLanguageLoader(ConfigService.tenantV2ApiUrl)\n   */\n  loadLanguages?: LanguageLoader;\n  /**\n   * App-provided default translation bundles per language code, merged OVER\n   * the library's shipped locales and UNDER everything fetched from the\n   * translation stack (Product/Brand/Tenant rows, then user overrides).\n   * Nested or flat structure - bundles are flattened to literal dotted keys\n   * at registration (see flattenBundle), so existing apps with nested JSON\n   * files can pass them as-is.\n   */\n  defaultBundles?: Record<string, object>;\n  /**\n   * Extra i18next init options, spread LAST so they win over the defaults.\n   *\n   * NOTE: keySeparator/nsSeparator default to false here and should stay that\n   * way - every layer (shipped locales, app defaultBundles, and the fetched\n   * db tiers) stores literal dotted keys, so re-enabling a separator makes\n   * lookups miss and silently fall back to English defaultValues.\n   */\n  i18nConfig?: Partial<InitOptions>;\n  fallbackLanguage?: string;\n  /**\n   * Restrict i18next to these language codes (the fallback is added\n   * automatically). Omit to accept any code - the default, because\n   * udp.Language is the authoritative list and a tenant may define languages\n   * the library ships no static bundle for. Set it when an app knows its\n   * language set is fixed and wants a junk `?lng=` guarded out.\n   */\n  supportedLanguages?: string[];\n  /**\n   * Turn the whole translation stack off. Defaults to the app's .env flag\n   * (REACT_APP_I18N_DISABLED, itself false by default), so\n   * most apps set it there rather than here; pass it explicitly to override the\n   * environment for one surface or a test. Note the polarity: this option is\n   * positive (`enabled: false` turns i18n off), the env variable is negative\n   * (`REACT_APP_I18N_DISABLED=true` turns i18n off).\n   *\n   * When false: no locale chunks are imported, no udp.Translation request is\n   * made, no language is detected or persisted, and the shell hides the\n   * language switcher. i18next is still initialised at the fallback language so\n   * every t() renders its inline English defaultValue - the UI stays working\n   * English rather than showing raw keys.\n   */\n  enabled?: boolean;\n}\n\nlet isInitialized = false;\n\n// Static bundles shipped with the library: the BOTTOM layer of every merged\n// bundle (fetched Product/Brand/Tenant rows and user overrides spread over\n// them), so library UI strings render translated with zero db seeding while\n// admins can still re-word any of them through the translations page.\n//\n// LAZY on purpose: each locale is ~14 KB gzip and eagerly importing all of them\n// put ~42 KB into the entry chunk - about twice the repo's bundle-size\n// regression gate. Only the active language's chunk is fetched.\n//\n// en-US is deliberately ABSENT: every t() call in the library carries its\n// English text as an inline defaultValue, so the English layer needs no chunk\n// at all (verified: no dotted-key call site omits defaultValue).\nconst LOCALE_LOADERS: Record<string, () => Promise<{ default: object }>> = {\n  'fr-CA': () => import('./locales/fr-CA.json'),\n  'es-US': () => import('./locales/es-US.json')\n};\n\n// Locale codes the library has a static layer for, including English (whose\n// layer is the inline defaultValues). Used to normalize a bare detected code\n// ('fr' -> 'fr-CA'); NOT a whitelist - udp.Language is the authoritative\n// language list and may contain more.\nconst SHIPPED_LOCALES: string[] = ['en-US', ...Object.keys(LOCALE_LOADERS)];\n\n// Flattened static layer per language: library locales + app defaultBundles\n// (app entries win over library ones per key).\nconst staticBundles: Record<string, TranslationMap> = {};\nconst staticLoaded = new Set<string>();\nconst staticInflight = new Map<string, Promise<void>>();\n\n// The fetched layers last applied per language, so a locale chunk that resolves\n// AFTER a fetch can be re-spread UNDERNEATH them (a plain addResourceBundle\n// would let static values overwrite db ones).\nconst lastApplied: Record<\n  string,\n  { system?: TranslationMap | null; overrides?: TranslationMap | null }\n> = {};\n\n/** Resolve a language onto a shipped locale key ('fr' -> 'fr-CA'). */\nconst resolveLocaleKey = (language: string): string | undefined =>\n  LOCALE_LOADERS[language]\n    ? language\n    : Object.keys(LOCALE_LOADERS).find(\n        (locale) => locale.split('-')[0] === language.split('-')[0]\n      );\n\nconst registerStaticBundles = (\n  defaultBundles?: Record<string, object>\n): void => {\n  // App-provided bundles only; library locales arrive lazily per language.\n  Object.entries(defaultBundles ?? {}).forEach(([language, bundle]) => {\n    staticBundles[language] = {\n      ...(staticBundles[language] ?? {}),\n      ...flattenBundle(bundle)\n    };\n  });\n};\n\n/**\n * Load the library's static bundle for a language (once). Resolves immediately\n * for English and for anything we ship no locale for - the inline\n * defaultValues cover those. A failure is logged and swallowed: English still\n * renders.\n */\nconst ensureStaticBundle = (language: string): Promise<void> => {\n  const key = language ? resolveLocaleKey(language) : undefined;\n  if (!key || staticLoaded.has(key)) {\n    return Promise.resolve();\n  }\n  const pending = staticInflight.get(key);\n  if (pending !== undefined) {\n    return pending;\n  }\n\n  const load = LOCALE_LOADERS[key]()\n    .then((module) => {\n      staticLoaded.add(key);\n      staticBundles[key] = {\n        ...flattenBundle(module.default),\n        // App defaultBundles keep priority over the library's own strings.\n        ...(staticBundles[key] ?? {})\n      };\n      // Already applied under this language (e.g. a language switch resolved\n      // its cache before the chunk landed)? Re-spread so static sits beneath.\n      const previous = lastApplied[key];\n      if (previous) {\n        applyMergedBundle(key, previous.system, previous.overrides);\n      }\n    })\n    .catch((error) => {\n      console.error('Failed to load locale bundle', key, error);\n    })\n    .finally(() => {\n      staticInflight.delete(key);\n    });\n\n  staticInflight.set(key, load);\n  return load;\n};\n\n// The loader configured at init, and the signed-in user's id once known\n// (set by refreshUserTranslations, called from UserProvider). Language\n// switches made after login load overrides automatically.\nlet activeLoadTranslations: TranslationLoader | null = null;\nlet activeUnityUserId: string | null = null;\n// The active tenant (set by setActiveTranslationTenant, called from\n// TenantProvider). Tenant (scope 2) and User (scope 3) rows are tenant-scoped\n// server-side, so the chain fetch sends it and a tenant switch refetches.\nlet activeTenantId: string | null = null;\n\nconst safeSessionUserId = (): string | null => {\n  try {\n    return sessionStorage.getItem('user-id');\n  } catch {\n    return null;\n  }\n};\n\nconst safeSessionTenantId = (): string | null => {\n  try {\n    return getStoredTenant() || null;\n  } catch {\n    return null;\n  }\n};\n\n/**\n * Resolve the language the detector will pick, BEFORE i18next initialises, so\n * the right locale chunk can be awaited. Mirrors the configured detection order\n * (querystring -> sessionStorage -> cookie) using i18next-browser-\n * languagedetector's default lookup keys; keep the two in sync.\n */\nconst peekDetectedLanguage = (fallback: string): string => {\n  const fromQueryString = (): string | null => {\n    try {\n      return new URLSearchParams(window.location.search).get('lng');\n    } catch {\n      return null;\n    }\n  };\n  const fromSessionStorage = (): string | null => {\n    try {\n      return sessionStorage.getItem('i18nextLng');\n    } catch {\n      return null;\n    }\n  };\n  const fromCookie = (): string | null => {\n    try {\n      return (\n        document.cookie\n          .split('; ')\n          .find((entry) => entry.startsWith('i18next='))\n          ?.slice('i18next='.length) ?? null\n      );\n    } catch {\n      return null;\n    }\n  };\n  return fromQueryString() ?? fromSessionStorage() ?? fromCookie() ?? fallback;\n};\n\n/** Convert the API response into an i18next resource bundle. */\nconst buildTranslationMap = (\n  languageData: TranslationRow[] = []\n): TranslationMap => {\n  return languageData.reduce<TranslationMap>((acc, item) => {\n    if (item?.label) {\n      acc[item.label] = item?.value ?? '';\n    }\n    return acc;\n  }, {});\n};\n\n// The API that most recently answered a translation fetch successfully; the\n// default loader tries the product API first and falls back to the tenant API,\n// memoizing only on a 2xx so a race/outage never locks in the wrong host.\nlet resolvedDefaultBaseUrl: string | null = null;\n\n/**\n * Build the standard translation loader against the backend hosting the\n * generated translation stack. Exported so apps can point the stock loader at\n * their own API via options.loadTranslations.\n *\n * ONE authenticated request per language: the chain GET returns Product, Brand\n * (the caller's brand - 'Default' until brand resolution exists) and Tenant\n * rows, plus - when unityUserId is supplied - the caller's own user-tier rows.\n * The response is scope-tagged and pre-ordered by precedence, so partitioning\n * is trivial: user rows (Scope 3) become the overrides bundle, everything else\n * flattens last-write-wins into the system bundle.\n *\n * The runtime never invokes this pre-auth - it hydrates from the local cache\n * until the user resolves - so token acquisition here is always safe.\n * @param baseUrl\n *   Omit to auto-resolve: the product API is tried first, and any failure\n *   (including 404 - the stack not being generated there) falls back to the\n *   tenant API. The host that answers successfully is remembered for the\n *   session.\n */\nexport const createDefaultTranslationLoader =\n  (baseUrl?: string): TranslationLoader =>\n  async (language, unityUserId, tenantId) => {\n    await ConfigService.waitForConfig?.();\n\n    const productId = ConfigService.config.UNITY_PRODUCT_ID;\n\n    // Two path flavors: the GENERATED stack (UdpTranslation controller, tiered)\n    // everywhere except the tenant V1 API, which hosts the LEGACY hand-written\n    // TranslationController (Translation/language, no product/brand/scope - it\n    // merges the user's overrides server-side when unityUserId is passed).\n    const urlFor = (base: string): string => {\n      if (base === ConfigService.tenantV1ApiUrl) {\n        const legacyParams = new URLSearchParams();\n        if (unityUserId) legacyParams.set('unityUserId', unityUserId);\n        const qs = legacyParams.toString();\n        return `Translation/language/${language}${qs ? `?${qs}` : ''}`;\n      }\n      const params = new URLSearchParams({ productId, brand: 'Default' });\n      if (unityUserId) params.set('unityUserId', unityUserId);\n      // Tenant-scoped tiers (Tenant/User rows) are filtered server-side.\n      if (tenantId) params.set('tenantId', tenantId);\n      return `UdpTranslation/language/${language}?${params.toString()}`;\n    };\n\n    const toBundles = (\n      response: AxiosResponse<TranslationRow[]>\n    ): TranslationBundles => {\n      const rows = Array.isArray(response?.data) ? response.data : [];\n      return {\n        system: buildTranslationMap(rows.filter((row) => row?.scope !== 3)),\n        overrides: buildTranslationMap(rows.filter((row) => row?.scope === 3))\n      };\n    };\n\n    // A host attempt only counts as success when the response is actually the\n    // translations API (a JSON array). Guards against an empty/missing base URL\n    // turning into a RELATIVE request that the SPA dev server answers with\n    // index.html - a \"successful\" 200/304 that must not pin the host.\n    const attempt = async (\n      base?: string | null\n    ): Promise<AxiosResponse<TranslationRow[]>> => {\n      if (!base) {\n        throw new Error('Translations base URL is not configured');\n      }\n      const response = (await apiMutate(base, urlFor(base), {\n        method: 'get'\n      })) as AxiosResponse<TranslationRow[]>;\n      if (!Array.isArray(response?.data)) {\n        throw new Error('Unexpected translations response (not the API)');\n      }\n      return response;\n    };\n\n    // Explicit baseUrl or an already-proven host: single attempt.\n    const knownBaseUrl = baseUrl || resolvedDefaultBaseUrl;\n    if (knownBaseUrl) {\n      return toBundles(await attempt(knownBaseUrl));\n    }\n\n    // Auto-resolve by attempting the real endpoint: product API first, tenant\n    // API on ANY failure (a 404 can mean either \"stack not generated here\" or\n    // \"no rows for this language\" - falling back is correct or harmless in both\n    // cases). Only a VALIDATED successful response pins the host for the session.\n    try {\n      const response = await attempt(ConfigService.productV1ApiUrl);\n      resolvedDefaultBaseUrl = ConfigService.productV1ApiUrl;\n      return toBundles(response);\n    } catch {\n      const response = await attempt(ConfigService.tenantV1ApiUrl);\n      resolvedDefaultBaseUrl = ConfigService.tenantV1ApiUrl;\n      return toBundles(response);\n    }\n  };\n\n// Languages fetched from the network this session. A cache-hydrated bundle is\n// NOT fresh - the network revalidation still runs.\nconst fresh = new Set<string>();\nconst inflight = new Map<string, Promise<void>>();\n\n/**\n * Apply system + overrides for a language as ONE merged bundle in the single\n * 'translation' namespace, user values winning per key.\n *\n * Merged (not separate namespaces) because i18next resolves namespace-first,\n * language-second: with an 'overrides' namespace, a user's en-US override\n * would outrank the fr-CA system string after a language switch (the overrides\n * namespace is exhausted across the whole LANGUAGE fallback chain before the\n * system namespace is consulted). Merging per language keeps the language\n * fallback semantics correct; the system/user separation lives in the CACHE\n * (separate entries) rather than in the lookup.\n */\nconst applyMergedBundle = (\n  language: string,\n  system?: TranslationMap | null,\n  overrides?: TranslationMap | null\n): void => {\n  // Remember the fetched layers so a lazily-arriving locale chunk can re-spread\n  // itself underneath them (see ensureStaticBundle).\n  lastApplied[language] = { system, overrides };\n  i18n.removeResourceBundle(language, SYSTEM_NS);\n  // Static library/app defaults are re-spread on EVERY apply: the remove+add\n  // replace (needed so rows deleted server-side disappear) would otherwise\n  // wipe them on the first fetch.\n  i18n.addResourceBundle(\n    language,\n    SYSTEM_NS,\n    {\n      ...(staticBundles[language] ?? {}),\n      ...(system ?? {}),\n      ...(overrides ?? {})\n    },\n    true,\n    true\n  );\n};\n\n/**\n * Cache-only hydration (no network): paints last session's bundles before auth\n * completes. No-ops once the language has fresh network data.\n */\nconst hydrateLanguage = (\n  language: string,\n  cacheUserId?: string | null\n): void => {\n  if (fresh.has(language) || i18n.hasResourceBundle(language, SYSTEM_NS)) {\n    return;\n  }\n  const system = readCachedBundle(language, null) as TranslationMap | null;\n  const overrides = cacheUserId\n    ? (readCachedBundle(language, cacheUserId) as TranslationMap | null)\n    : null;\n  // Apply even with an empty cache when static defaults exist for the\n  // language - library strings should paint translated pre-auth on first visit.\n  if (system || overrides || staticBundles[language]) {\n    applyMergedBundle(language, system, overrides);\n  }\n};\n\n/**\n * Ensure a language with ONE authenticated request: hydrate synchronously from\n * cache (stale-while-revalidate), then fetch the full chain plus the user's\n * rows and REPLACE the merged bundle (remove + add, so entries deleted\n * server-side or belonging to a previous user disappear). Concurrent calls for\n * the same language share one in-flight promise. Only invoked post-auth (the\n * user id is always known here).\n */\nconst ensureLanguage = (\n  language: string,\n  unityUserId?: string | null\n): Promise<void> => {\n  if (fresh.has(language)) {\n    return Promise.resolve();\n  }\n  const pending = inflight.get(language);\n  if (pending !== undefined) {\n    return pending;\n  }\n\n  // Seed the tenant from the session hint (stored by TenantProvider last\n  // session / storeInitialTenant from the URL) when the provider hasn't\n  // resolved yet: the first fetch then already carries the right tenant, and\n  // the provider's later setActiveTranslationTenant call dedupes instead of\n  // invalidating and refetching. A DIFFERENT resolved tenant still refetches.\n  if (activeTenantId === null) {\n    activeTenantId = safeSessionTenantId();\n  }\n\n  hydrateLanguage(language, unityUserId);\n\n  const load = (async () => {\n    try {\n      if (!activeLoadTranslations) {\n        return;\n      }\n      const bundles = await activeLoadTranslations(\n        language,\n        unityUserId,\n        activeTenantId\n      );\n      if (bundles == null) {\n        return;\n      }\n      applyMergedBundle(language, bundles.system, bundles.overrides);\n      fresh.add(language);\n      writeCachedBundle(language, null, bundles.system ?? {});\n      writeCachedBundle(language, unityUserId, bundles.overrides ?? {});\n    } catch (error) {\n      console.error('Failed to load translations', error);\n    } finally {\n      inflight.delete(language);\n    }\n  })();\n\n  inflight.set(language, load);\n  return load;\n};\n\n/**\n * Initializes the shared i18n instance used across UDP React applications.\n * Safe to call multiple times – initialization will only occur once per runtime.\n */\nexport const initializeUnityI18n = (\n  options: InitializeI18nOptions = {}\n): I18nInstance => {\n  if (isInitialized) {\n    return i18n;\n  }\n  // Set BEFORE the await below so a second call (StrictMode double-effects)\n  // can't start a second init while the locale chunk is in flight.\n  isInitialized = true;\n\n  const fallbackLanguage = options.fallbackLanguage ?? 'en-US';\n\n  // Resolve the opt-out BEFORE anything else reads it (languageSource and the\n  // shell both consult isI18nStackEnabled).\n  setI18nEnabled(options.enabled);\n  if (!isI18nStackEnabled()) {\n    // Minimal init: no detector (nothing to detect - the language is fixed), no\n    // locale chunk, no loader registered, so nothing can start a fetch later.\n    // partialBundledLanguages mirrors the enabled path, where en-US likewise has\n    // no resource bundle and English comes from the inline defaultValues.\n    void i18n.use(initReactI18next).init({\n      lng: fallbackLanguage,\n      fallbackLng: fallbackLanguage,\n      debug: false,\n      partialBundledLanguages: true,\n      keySeparator: false,\n      nsSeparator: false,\n      returnEmptyString: false,\n      interpolation: { escapeValue: false },\n      ...(options.i18nConfig ?? {})\n    });\n    return i18n;\n  }\n\n  const loadTranslations =\n    options.loadTranslations ?? createDefaultTranslationLoader();\n  activeLoadTranslations = loadTranslations;\n  setLanguageLoader(options.loadLanguages);\n  registerStaticBundles(options.defaultBundles);\n\n  const handleLanguageChanged = (language: string): void => {\n    if (!language) {\n      return;\n    }\n    // Await the locale chunk BEFORE applying anything: on a language switch the\n    // cache/fetch would otherwise land first and paint English until the chunk\n    // arrived. Resolves immediately for English.\n    void ensureStaticBundle(language).then(() => {\n      // Every translation fetch is authenticated (no anonymous calls, by\n      // policy), so no network happens until the user resolves: pre-auth we\n      // paint from the local cache only (system + the session-hinted user's\n      // overrides), and refreshUserTranslations kicks off the fetch post-auth.\n      if (activeUnityUserId) {\n        void ensureLanguage(language, activeUnityUserId);\n      } else {\n        hydrateLanguage(language, safeSessionUserId());\n      }\n    });\n  };\n\n  i18n.on('languageChanged', handleLanguageChanged);\n\n  // Resolve the language the detector WILL pick (same sources/order as the\n  // detection config below) and await its locale chunk before init. i18next is\n  // then initialised with the static layer already in place, so the first\n  // translated paint is correct instead of flashing English. react-i18next's\n  // default useSuspense keeps consumers suspended - not rendering English -\n  // during this window, which init already occupied (it runs post-first-paint\n  // from UdpAppProvider's effect).\n  void ensureStaticBundle(peekDetectedLanguage(fallbackLanguage)).then(() => {\n    void i18n\n      .use(LanguageDetector)\n      .use(initReactI18next)\n      .init({\n        fallbackLng: fallbackLanguage,\n        debug: false,\n        // One merged bundle per language in the default 'translation' namespace\n        // (user overrides merged over the system chain at apply time). See\n        // applyMergedBundle for why lookup does NOT use a separate overrides\n        // namespace: i18next resolves namespace-first, language-second, which\n        // would let a fallback-language override outrank the active language's\n        // system string after a language switch.\n        partialBundledLanguages: true,\n        // REQUIRED for this stack, hence a library default rather than a\n        // per-app opt-in: every layer stores LITERAL dotted keys - the shipped\n        // locales (flattened at registration) and udp.Translation labels, which\n        // are user-authored and may contain incidental dots. With i18next's\n        // default '.' separator a key like 'common.cancel' is treated as a\n        // NESTED path, silently misses the flat bundle, and falls back to the\n        // inline defaultValue - i.e. English everywhere, with no error. Apps can\n        // still override both (options.i18nConfig is spread last) but should not.\n        keySeparator: false,\n        nsSeparator: false,\n        // Detector names/keys must match i18next-browser-languagedetector's API:\n        // the lookup is 'querystring' (all lowercase) and the write-back option\n        // is 'caches' - the previous 'queryString'/'cache' spelling was silently\n        // ignored, so selections never persisted. The detector now WRITES the\n        // selected language to sessionStorage on every changeLanguage and READS\n        // it back on startup (cookie kept as a read fallback for older sessions).\n        detection: {\n          order: ['querystring', 'sessionStorage', 'cookie'],\n          caches: ['sessionStorage'],\n          // Normalize a bare detected code onto a locale we actually ship\n          // ('fr' -> 'fr-CA'), derived from the shipped locales so it stays\n          // correct if that set changes. A detector option, not a top-level one.\n          // Only affects DETECTED codes; explicit changeLanguage() calls from the\n          // switcher already pass full udp.Language codes.\n          convertDetectedLanguage: (language: string): string =>\n            SHIPPED_LOCALES.includes(language)\n              ? language\n              : (SHIPPED_LOCALES.find(\n                  (locale) => locale.split('-')[0] === language.split('-')[0]\n                ) ?? language)\n        },\n        // Only load/look up the exact language, not its base ('fr-CA', never a\n        // bare 'fr' the stack has no bundle for).\n        load: 'currentOnly',\n        // A db Value saved as '' should fall through to the layer below (and\n        // ultimately the inline English defaultValue) rather than rendering a\n        // blank label. Note: deliberately NOT paired with\n        // nonExplicitSupportedLngs - that strips region codes, so 'fr-CA' would\n        // resolve against a nonexistent 'fr' bundle and silently render English.\n        returnEmptyString: false,\n        // Restricting languages is OPT-IN (options.supportedLanguages): the\n        // authoritative language list lives in udp.Language, which may hold more\n        // than the library ships, so a hardcoded supportedLngs would block a\n        // tenant's extra language outright.\n        ...(options.supportedLanguages\n          ? { supportedLngs: [...options.supportedLanguages, fallbackLanguage] }\n          : {}),\n        interpolation: {\n          escapeValue: false\n        },\n        react: {\n          // Re-render consumers when a resource bundle is added/replaced -\n          // that's how override arrival updates the screen (default react-i18next\n          // only re-renders on languageChanged).\n          bindI18nStore: 'added removed'\n        },\n        // NOTE: no `backend` option - this stack registers no backend plugin, so\n        // the previous `backend: { loadPath: '' }` was inert config.\n        ...(options.i18nConfig ?? {})\n      });\n  });\n\n  return i18n;\n};\n\n/**\n * Kick off the (authenticated) translation load once the signed-in user is\n * known - ONE request returning the system chain plus the user's overrides.\n * Called by UserProvider when the user resolves; safe to call repeatedly\n * (duplicate calls share the in-flight load). Fire-and-forget by design:\n * labels swap in when the fetch lands rather than blocking render (the cache\n * hydration at startup makes that swap a visual no-op on repeat visits).\n */\nexport const refreshUserTranslations = (unityUserId: string): Promise<void> => {\n  if (!unityUserId || !isI18nStackEnabled()) {\n    return Promise.resolve();\n  }\n  // Record the user id even if i18n hasn't initialized yet - the init-time\n  // languageChanged pass then fetches directly.\n  activeUnityUserId = unityUserId;\n  if (!isInitialized || !i18n.language || !activeLoadTranslations) {\n    return Promise.resolve();\n  }\n\n  return ensureLanguage(i18n.language, unityUserId);\n};\n\n// Cap on the pre-switch translation fetch (see changeLanguage). A slow or hung\n// backend must not stall the language switcher; the bundle still applies when\n// it eventually lands.\nconst PRELOAD_FETCH_TIMEOUT_MS = 1000;\n\n/**\n * Switch the UI language without a flash of English. Prefer this over calling\n * i18n.changeLanguage() directly from UI.\n *\n * i18next flips i18n.language and emits languageChanged SYNCHRONOUSLY, so a\n * bare changeLanguage() re-renders every consumer before the new language has\n * any resource bundle: each t() falls through to its inline English\n * defaultValue, then repaints translated once the locale chunk and the db fetch\n * land. Visible only on the FIRST switch to a given language - afterwards the\n * bundle is already registered, which is why the flicker looks intermittent.\n *\n * Loading the target language's layers BEFORE the flip makes it a single\n * correct paint. Bundles are per language, so this is invisible while the\n * current language is still active. The init path already does this via\n * peekDetectedLanguage; this is the same guarantee for an explicit switch.\n */\nexport const changeLanguage = async (language: string): Promise<void> => {\n  if (!language) {\n    return;\n  }\n  // Opted out: switching would import a locale chunk and move off the fixed\n  // fallback language, which is the whole thing the flag disables.\n  if (!isI18nStackEnabled()) {\n    return;\n  }\n  if (language === i18n.language) {\n    return;\n  }\n\n  // Locale chunk first - local, and the bulk of the library's own labels.\n  await ensureStaticBundle(language);\n\n  // Then the db layers (product/brand/tenant rows plus the user's overrides),\n  // which are all an app-migrated product's labels. Authenticated-only by\n  // policy, so pre-auth this paints from cache instead.\n  if (activeUnityUserId && activeLoadTranslations) {\n    await Promise.race([\n      ensureLanguage(language, activeUnityUserId),\n      new Promise<void>((resolve) => {\n        setTimeout(resolve, PRELOAD_FETCH_TIMEOUT_MS);\n      })\n    ]);\n  } else {\n    hydrateLanguage(language, safeSessionUserId());\n  }\n\n  // handleLanguageChanged still runs on the flip; both calls no-op now (the\n  // chunk is loaded and the language is fresh), so this costs no extra request.\n  await i18n.changeLanguage(language);\n};\n\n/**\n * Force-refetch a language's merged bundle after its rows changed server-side\n * (the translation management pages call this after a successful save/delete,\n * so the running app reflects the change without a reload).\n *\n * The language is evicted from the fresh set so the next ensureLanguage\n * revalidates instead of no-opping. When it is the CURRENT UI language the\n * refetch fires immediately - replacing the merged bundle and rewriting the\n * localStorage cache, so the change also survives a cold start. Any other\n * language is refetched on its next language switch. Fetches stay\n * authenticated-only: pre-auth (no active user) this only evicts.\n * @param languageCode Defaults to the current UI language.\n */\nexport const refreshTranslations = (languageCode?: string): Promise<void> => {\n  const language = languageCode || i18n.language;\n  if (!language || !isI18nStackEnabled()) {\n    return Promise.resolve();\n  }\n  fresh.delete(language);\n\n  const refetch = (): Promise<void> => {\n    fresh.delete(language);\n    if (\n      language !== i18n.language ||\n      !activeUnityUserId ||\n      !activeLoadTranslations\n    ) {\n      return Promise.resolve();\n    }\n    return ensureLanguage(language, activeUnityUserId);\n  };\n\n  // An in-flight initial load would be handed back as-is by ensureLanguage\n  // (and would be stale - it started before the mutation): wait it out, evict\n  // the freshness it recorded, then fetch the post-change state.\n  const pending = inflight.get(language);\n  return pending !== undefined ? pending.then(refetch, refetch) : refetch();\n};\n\n/**\n * Record the active tenant for translation fetches (called by TenantProvider\n * whenever the active tenant resolves or changes). Tenant and User tier rows\n * are tenant-scoped server-side, so a tenant CHANGE invalidates every fetched\n * language and refetches the current one; the first resolve before any fetch\n * just records the id for the upcoming load.\n */\nexport const setActiveTranslationTenant = (\n  tenantId?: string | null\n): Promise<void> => {\n  const next = tenantId ?? null;\n  if (next === activeTenantId || !isI18nStackEnabled()) {\n    return Promise.resolve();\n  }\n  activeTenantId = next;\n  if (fresh.size === 0 && inflight.size === 0) {\n    // Nothing fetched yet - the id just rides along on the upcoming load\n    // (refreshUserTranslations / languageChanged drives the initial fetch).\n    return Promise.resolve();\n  }\n  // Something already loaded under the previous tenant context (including a\n  // fetch that raced the tenant resolve): every fetched language is stale.\n  fresh.clear();\n  return refreshTranslations();\n};\n\n/** Convenience accessor for the shared i18n instance. */\nexport const getI18nInstance = (): I18nInstance => i18n;\n"],"mappings":"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"}