{"version":3,"file":"getLocalizedUrl.cjs","names":["internationalization","resolveRoutingConfig","getPathWithoutLocale","getRewriteRules","getLocalizedPath","getCanonicalPath","checkIsURLAbsolute","getPrefix"],"sources":["../../../src/localization/getLocalizedUrl.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\n\n// ── Tree-shake constants ──────────────────────────────────────────────────────\n// When these env vars are injected at build time, bundlers eliminate the\n// branches guarded by these constants.\n\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n  LocalesValues,\n  LocalizedUrl,\n  ResolvedDefaultLocale,\n} from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\nimport {\n  getPrefix,\n  type RoutingOptions,\n  resolveRoutingConfig,\n} from './getPrefix';\nimport {\n  getCanonicalPath,\n  getLocalizedPath,\n  getRewriteRules,\n} from './rewriteUtils';\n\nexport type { RoutingOptions };\n\n/** Strips the protocol and returns the bare hostname of a domain string. */\nconst extractHostname = (domain: string): string => {\n  try {\n    return /^https?:\\/\\//.test(domain) ? new URL(domain).hostname : domain;\n  } catch {\n    return domain;\n  }\n};\n\n/**\n * Generate URL by prefixing the given URL with the referenced locale or adding search parameters\n * based on the routing mode. Handles both absolute and relative URLs appropriately.\n *\n * This function gets the locales, default locale, and routing mode from the configuration if not provided.\n *\n * Example:\n *\n * ```ts\n *  // prefix-no-default mode\n *  getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'prefix-no-default' });\n *  // Returns '/fr/about' for the French locale\n *  // Returns '/about' for the English locale (default)\n *\n *  // prefix-all mode\n *  getLocalizedUrl('/about', 'en', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'prefix-all' });\n *  // Returns '/en/about' for the English locale\n *  // Returns '/fr/about' for the French locale\n *\n *  // search-params mode\n *  getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'search-params' });\n *  // Returns '/about?locale=fr' for the French locale\n *\n *  // no-prefix mode\n *  getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'no-prefix' });\n *  // Returns '/about' for any locale\n * ```\n *\n * @param url - The original URL string to be processed.\n * @param currentLocale - The current locale.\n * @param options - Configuration options\n * @param options.locales - Optional array of supported locales. Defaults to configured locales.\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @param options.currentDomain - Hostname of the page being rendered. Used to decide\n *   whether to emit a relative URL (same domain) or an absolute URL (cross-domain).\n *   Auto-detected from the input URL or `window.location` when omitted.\n * @returns The localized URL for the current locale.\n */\n/**\n * The return type is narrowed to a template-literal type when both `url` and\n * `currentLocale` are string literals and the routing mode / defaultLocale are\n * not overridden via `options`.\n */\nexport const getLocalizedUrl = <\n  const T extends string,\n  const L extends LocalesValues = ResolvedDefaultLocale,\n>(\n  url: T,\n  currentLocale: L = internationalization?.defaultLocale as L,\n  options: RoutingOptions = {}\n): LocalizedUrl<T, L> => {\n  const { defaultLocale, mode, locales, rewrite, domains, currentDomain } =\n    resolveRoutingConfig(options);\n\n  const urlWithoutLocale = getPathWithoutLocale(url, locales);\n  const rewriteRules = getRewriteRules(rewrite, 'url');\n\n  if (\n    !(\n      process.env['INTLAYER_ROUTING_MODE'] &&\n      process.env['INTLAYER_ROUTING_MODE'] !== 'no-prefix'\n    ) &&\n    mode === 'no-prefix'\n  ) {\n    return getLocalizedPath(\n      getCanonicalPath(urlWithoutLocale, undefined, rewriteRules),\n      currentLocale as Locale,\n      rewriteRules\n    ).path as LocalizedUrl<T, L>;\n  }\n\n  const isAbsoluteUrl = checkIsURLAbsolute(urlWithoutLocale);\n  const parsedUrl = isAbsoluteUrl\n    ? new URL(urlWithoutLocale)\n    : new URL(urlWithoutLocale, 'http://example.com');\n\n  const translatedPathname = getLocalizedPath(\n    getCanonicalPath(parsedUrl.pathname, undefined, rewriteRules),\n    currentLocale as Locale,\n    rewriteRules\n  ).path;\n\n  // ── Domain routing ────────────────────────────────────────────────────────\n  // Resolve the \"current\" hostname so we can choose between a relative URL\n  // (same domain) and an absolute URL (cross-domain).\n  //\n  // Detection priority:\n  //   1. Explicit `currentDomain` option passed by the caller.\n  //   2. Hostname extracted from an absolute input URL.\n  //   3. `window.location.hostname` in browser environments.\n  // When none of these is available we fall back to always generating an\n  // absolute URL (the previous behaviour, safe for SSR/static generation).\n  const detectedCurrentHostname =\n    process.env['INTLAYER_ROUTING_DOMAINS'] !== 'false'\n      ? extractHostname(\n          currentDomain ??\n            (isAbsoluteUrl ? parsedUrl.hostname : undefined) ??\n            (typeof window !== 'undefined'\n              ? window?.location?.hostname\n              : undefined) ??\n            ''\n        ) || null\n      : null;\n\n  const localeDomain =\n    process.env['INTLAYER_ROUTING_DOMAINS'] !== 'false'\n      ? domains?.[currentLocale as LocalesValues]\n      : undefined;\n\n  const localeDomainHostname = localeDomain\n    ? extractHostname(localeDomain)\n    : null;\n\n  // Only prepend the locale's domain when it differs from the current hostname.\n  const isCrossDomain =\n    localeDomainHostname !== null &&\n    detectedCurrentHostname !== null &&\n    localeDomainHostname !== detectedCurrentHostname;\n\n  const normalizedDomain =\n    isCrossDomain && localeDomain\n      ? /^https?:\\/\\//.test(localeDomain)\n        ? localeDomain\n        : `https://${localeDomain}`\n      : null;\n\n  const baseUrl = normalizedDomain\n    ? normalizedDomain\n    : isAbsoluteUrl\n      ? `${parsedUrl.protocol}//${parsedUrl.host}`\n      : '';\n  // ─────────────────────────────────────────────────────────────────────────\n\n  if (\n    !(\n      process.env['INTLAYER_ROUTING_MODE'] &&\n      process.env['INTLAYER_ROUTING_MODE'] !== 'search-params'\n    ) &&\n    mode === 'search-params'\n  ) {\n    const searchParams = new URLSearchParams(parsedUrl.search);\n\n    searchParams.set('locale', currentLocale.toString());\n\n    const queryParams = searchParams.toString();\n    const path = queryParams\n      ? `${translatedPathname}?${queryParams}`\n      : translatedPathname;\n\n    return `${baseUrl}${path}${parsedUrl.hash}` as LocalizedUrl<T, L>;\n  }\n\n  const { prefix } = getPrefix(currentLocale, {\n    defaultLocale,\n    mode,\n    locales,\n    domains,\n  });\n\n  let localizedPath = `/${prefix}${translatedPathname}`.replace(/\\/+/g, '/');\n\n  if (localizedPath.length > 1 && localizedPath.endsWith('/')) {\n    localizedPath = localizedPath.slice(0, -1);\n  }\n\n  return `${baseUrl}${localizedPath}${parsedUrl.search}${parsedUrl.hash}` as LocalizedUrl<\n    T,\n    L\n  >;\n};\n"],"mappings":";;;;;;;;;;AA4BA,MAAM,mBAAmB,WAA2B;AAClD,KAAI;AACF,SAAO,eAAe,KAAK,OAAO,GAAG,IAAI,IAAI,OAAO,CAAC,WAAW;SAC1D;AACN,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDX,MAAa,mBAIX,KACA,gBAAmBA,6CAAsB,eACzC,UAA0B,EAAE,KACL;CACvB,MAAM,EAAE,eAAe,MAAM,SAAS,SAAS,SAAS,kBACtDC,oDAAqB,QAAQ;CAE/B,MAAM,mBAAmBC,+DAAqB,KAAK,QAAQ;CAC3D,MAAM,eAAeC,kDAAgB,SAAS,MAAM;AAEpD,KACE,EACE,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B,gBAE3C,SAAS,YAET,QAAOC,mDACLC,mDAAiB,kBAAkB,QAAW,aAAa,EAC3D,eACA,aACD,CAAC;CAGJ,MAAM,gBAAgBC,oDAAmB,iBAAiB;CAC1D,MAAM,YAAY,gBACd,IAAI,IAAI,iBAAiB,GACzB,IAAI,IAAI,kBAAkB,qBAAqB;CAEnD,MAAM,qBAAqBF,mDACzBC,mDAAiB,UAAU,UAAU,QAAW,aAAa,EAC7D,eACA,aACD,CAAC;CAYF,MAAM,0BACJ,QAAQ,IAAI,gCAAgC,UACxC,gBACE,kBACG,gBAAgB,UAAU,WAAW,YACrC,OAAO,WAAW,cACf,QAAQ,UAAU,WAClB,WACJ,GACH,IAAI,OACL;CAEN,MAAM,eACJ,QAAQ,IAAI,gCAAgC,UACxC,UAAU,iBACV;CAEN,MAAM,uBAAuB,eACzB,gBAAgB,aAAa,GAC7B;CAQJ,MAAM,mBAJJ,yBAAyB,QACzB,4BAA4B,QAC5B,yBAAyB,2BAGR,eACb,eAAe,KAAK,aAAa,GAC/B,eACA,WAAW,iBACb;CAEN,MAAM,UAAU,mBACZ,mBACA,gBACE,GAAG,UAAU,SAAS,IAAI,UAAU,SACpC;AAGN,KACE,EACE,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B,oBAE3C,SAAS,iBACT;EACA,MAAM,eAAe,IAAI,gBAAgB,UAAU,OAAO;AAE1D,eAAa,IAAI,UAAU,cAAc,UAAU,CAAC;EAEpD,MAAM,cAAc,aAAa,UAAU;AAK3C,SAAO,GAAG,UAJG,cACT,GAAG,mBAAmB,GAAG,gBACzB,qBAEuB,UAAU;;CAGvC,MAAM,EAAE,WAAWE,yCAAU,eAAe;EAC1C;EACA;EACA;EACA;EACD,CAAC;CAEF,IAAI,gBAAgB,IAAI,SAAS,qBAAqB,QAAQ,QAAQ,IAAI;AAE1E,KAAI,cAAc,SAAS,KAAK,cAAc,SAAS,IAAI,CACzD,iBAAgB,cAAc,MAAM,GAAG,GAAG;AAG5C,QAAO,GAAG,UAAU,gBAAgB,UAAU,SAAS,UAAU"}