@intlayer/core 7.4.0 → 7.5.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/cjs/localization/getLocale.cjs +3 -4
  2. package/dist/cjs/localization/getLocale.cjs.map +1 -1
  3. package/dist/cjs/localization/getLocalizedUrl.cjs +2 -1
  4. package/dist/cjs/localization/getLocalizedUrl.cjs.map +1 -1
  5. package/dist/cjs/messageFormat/ICU.cjs +309 -0
  6. package/dist/cjs/messageFormat/ICU.cjs.map +1 -0
  7. package/dist/cjs/messageFormat/i18next.cjs +320 -0
  8. package/dist/cjs/messageFormat/i18next.cjs.map +1 -0
  9. package/dist/cjs/messageFormat/index.cjs +10 -0
  10. package/dist/cjs/messageFormat/vue-i18n.cjs +166 -0
  11. package/dist/cjs/messageFormat/vue-i18n.cjs.map +1 -0
  12. package/dist/esm/localization/getLocale.mjs +1 -2
  13. package/dist/esm/localization/getLocale.mjs.map +1 -1
  14. package/dist/esm/localization/getLocalizedUrl.mjs +2 -1
  15. package/dist/esm/localization/getLocalizedUrl.mjs.map +1 -1
  16. package/dist/esm/messageFormat/ICU.mjs +307 -0
  17. package/dist/esm/messageFormat/ICU.mjs.map +1 -0
  18. package/dist/esm/messageFormat/i18next.mjs +318 -0
  19. package/dist/esm/messageFormat/i18next.mjs.map +1 -0
  20. package/dist/esm/messageFormat/index.mjs +5 -0
  21. package/dist/esm/messageFormat/vue-i18n.mjs +164 -0
  22. package/dist/esm/messageFormat/vue-i18n.mjs.map +1 -0
  23. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts +8 -7
  24. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts.map +1 -1
  25. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts +8 -7
  26. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts.map +1 -1
  27. package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts +8 -7
  28. package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts.map +1 -1
  29. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts +2 -2
  30. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts.map +1 -1
  31. package/dist/types/interpreter/getContent/plugins.d.ts.map +1 -1
  32. package/dist/types/messageFormat/ICU.d.ts +11 -0
  33. package/dist/types/messageFormat/ICU.d.ts.map +1 -0
  34. package/dist/types/messageFormat/i18next.d.ts +9 -0
  35. package/dist/types/messageFormat/i18next.d.ts.map +1 -0
  36. package/dist/types/messageFormat/index.d.ts +4 -0
  37. package/dist/types/messageFormat/vue-i18n.d.ts +9 -0
  38. package/dist/types/messageFormat/vue-i18n.d.ts.map +1 -0
  39. package/dist/types/transpiler/translation/translation.d.ts +1 -1
  40. package/dist/types/transpiler/translation/translation.d.ts.map +1 -1
  41. package/package.json +11 -6
  42. package/dist/cjs/dist/esm/getStorageAttributes.cjs +0 -134
  43. package/dist/cjs/dist/esm/getStorageAttributes.cjs.map +0 -1
  44. package/dist/cjs/dist/esm/localization/localeResolver.cjs +0 -28
  45. package/dist/cjs/dist/esm/localization/localeResolver.cjs.map +0 -1
  46. package/dist/cjs/dist/esm/utils/getCookie.cjs +0 -32
  47. package/dist/cjs/dist/esm/utils/getCookie.cjs.map +0 -1
  48. package/dist/cjs/dist/esm/utils/localeStorage.cjs +0 -61
  49. package/dist/cjs/dist/esm/utils/localeStorage.cjs.map +0 -1
  50. package/dist/cjs/localization/isValidLocale.cjs +0 -54
  51. package/dist/cjs/localization/isValidLocale.cjs.map +0 -1
  52. package/dist/esm/dist/esm/getStorageAttributes.mjs +0 -133
  53. package/dist/esm/dist/esm/getStorageAttributes.mjs.map +0 -1
  54. package/dist/esm/dist/esm/localization/localeResolver.mjs +0 -26
  55. package/dist/esm/dist/esm/localization/localeResolver.mjs.map +0 -1
  56. package/dist/esm/dist/esm/utils/getCookie.mjs +0 -31
  57. package/dist/esm/dist/esm/utils/getCookie.mjs.map +0 -1
  58. package/dist/esm/dist/esm/utils/localeStorage.mjs +0 -59
  59. package/dist/esm/dist/esm/utils/localeStorage.mjs.map +0 -1
  60. package/dist/esm/localization/isValidLocale.mjs +0 -52
  61. package/dist/esm/localization/isValidLocale.mjs.map +0 -1
  62. package/dist/types/localization/isValidLocale.d.ts +0 -35
  63. package/dist/types/localization/isValidLocale.d.ts.map +0 -1
@@ -1,23 +1,22 @@
1
1
  const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
- const require_localeResolver = require('../dist/esm/localization/localeResolver.cjs');
3
- const require_localeStorage = require('../dist/esm/utils/localeStorage.cjs');
4
2
  const require_localization_localeDetector = require('./localeDetector.cjs');
5
3
  let __intlayer_config_client = require("@intlayer/config/client");
6
4
  let __intlayer_config_built = require("@intlayer/config/built");
7
5
  __intlayer_config_built = require_rolldown_runtime.__toESM(__intlayer_config_built);
6
+ let __intlayer_core = require("@intlayer/core");
8
7
 
9
8
  //#region src/localization/getLocale.ts
10
9
  const getLocale = async (ctx = {}) => {
11
10
  const defaultLocale = __intlayer_config_built.default?.internationalization?.defaultLocale ?? __intlayer_config_client.DefaultValues.Internationalization.DEFAULT_LOCALE;
12
11
  const availableLocales = __intlayer_config_built.default?.internationalization?.locales ?? __intlayer_config_client.DefaultValues.Internationalization.LOCALES;
13
- const storedLocale = require_localeStorage.getLocaleFromStorage({
12
+ const storedLocale = (0, __intlayer_core.getLocaleFromStorage)({
14
13
  getCookie: ctx.getCookie,
15
14
  getHeader: ctx.getHeader
16
15
  });
17
16
  if (storedLocale) return storedLocale;
18
17
  const acceptLanguageHeader = ctx.getHeader?.("accept-language");
19
18
  if (!acceptLanguageHeader) return defaultLocale;
20
- const userFallbackLocale = require_localeResolver.localeResolver(require_localization_localeDetector.getPreferredLanguages(acceptLanguageHeader, availableLocales), availableLocales, defaultLocale);
19
+ const userFallbackLocale = (0, __intlayer_core.localeResolver)(require_localization_localeDetector.getPreferredLanguages(acceptLanguageHeader, availableLocales), availableLocales, defaultLocale);
21
20
  if (userFallbackLocale) return userFallbackLocale;
22
21
  return defaultLocale;
23
22
  };
@@ -1 +1 @@
1
- {"version":3,"file":"getLocale.cjs","names":["configuration","DefaultValues","getLocaleFromStorage","localeResolver","getPreferredLanguages"],"sources":["../../../src/localization/getLocale.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport { DefaultValues } from '@intlayer/config/client';\nimport { getLocaleFromStorage, localeResolver } from '@intlayer/core';\nimport type { Locale } from '@intlayer/types';\nimport { getPreferredLanguages } from './localeDetector';\n\nexport type RequestContext = {\n getHeader?: (name: string) => string | null | undefined;\n getCookie?: (name: string) => string | null | undefined;\n};\n\nexport const getLocale = async (ctx: RequestContext = {}): Promise<Locale> => {\n const defaultLocale =\n configuration?.internationalization?.defaultLocale ??\n DefaultValues.Internationalization.DEFAULT_LOCALE;\n const availableLocales =\n configuration?.internationalization?.locales ??\n DefaultValues.Internationalization.LOCALES;\n\n // Try locale from storage (cookie or header)\n const storedLocale = getLocaleFromStorage({\n getCookie: ctx.getCookie,\n getHeader: ctx.getHeader,\n });\n\n if (storedLocale) {\n return storedLocale;\n }\n\n // Fallback to Accept-Language negotiation\n const acceptLanguageHeader = ctx.getHeader?.('accept-language');\n\n if (!acceptLanguageHeader) {\n return defaultLocale;\n }\n\n const preferredLocaleStrings = getPreferredLanguages(\n acceptLanguageHeader,\n availableLocales\n );\n\n const userFallbackLocale = localeResolver(\n preferredLocaleStrings,\n availableLocales,\n defaultLocale\n );\n\n if (userFallbackLocale) {\n return userFallbackLocale;\n }\n\n // Default locale\n return defaultLocale;\n};\n"],"mappings":";;;;;;;;;AAWA,MAAa,YAAY,OAAO,MAAsB,EAAE,KAAsB;CAC5E,MAAM,gBACJA,iCAAe,sBAAsB,iBACrCC,uCAAc,qBAAqB;CACrC,MAAM,mBACJD,iCAAe,sBAAsB,WACrCC,uCAAc,qBAAqB;CAGrC,MAAM,eAAeC,2CAAqB;EACxC,WAAW,IAAI;EACf,WAAW,IAAI;EAChB,CAAC;AAEF,KAAI,aACF,QAAO;CAIT,MAAM,uBAAuB,IAAI,YAAY,kBAAkB;AAE/D,KAAI,CAAC,qBACH,QAAO;CAQT,MAAM,qBAAqBC,sCALIC,0DAC7B,sBACA,iBACD,EAIC,kBACA,cACD;AAED,KAAI,mBACF,QAAO;AAIT,QAAO"}
1
+ {"version":3,"file":"getLocale.cjs","names":["configuration","DefaultValues","getPreferredLanguages"],"sources":["../../../src/localization/getLocale.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport { DefaultValues } from '@intlayer/config/client';\nimport { getLocaleFromStorage, localeResolver } from '@intlayer/core';\nimport type { Locale } from '@intlayer/types';\nimport { getPreferredLanguages } from './localeDetector';\n\nexport type RequestContext = {\n getHeader?: (name: string) => string | null | undefined;\n getCookie?: (name: string) => string | null | undefined;\n};\n\nexport const getLocale = async (ctx: RequestContext = {}): Promise<Locale> => {\n const defaultLocale =\n configuration?.internationalization?.defaultLocale ??\n DefaultValues.Internationalization.DEFAULT_LOCALE;\n const availableLocales =\n configuration?.internationalization?.locales ??\n DefaultValues.Internationalization.LOCALES;\n\n // Try locale from storage (cookie or header)\n const storedLocale = getLocaleFromStorage({\n getCookie: ctx.getCookie,\n getHeader: ctx.getHeader,\n });\n\n if (storedLocale) {\n return storedLocale;\n }\n\n // Fallback to Accept-Language negotiation\n const acceptLanguageHeader = ctx.getHeader?.('accept-language');\n\n if (!acceptLanguageHeader) {\n return defaultLocale;\n }\n\n const preferredLocaleStrings = getPreferredLanguages(\n acceptLanguageHeader,\n availableLocales\n );\n\n const userFallbackLocale = localeResolver(\n preferredLocaleStrings,\n availableLocales,\n defaultLocale\n );\n\n if (userFallbackLocale) {\n return userFallbackLocale;\n }\n\n // Default locale\n return defaultLocale;\n};\n"],"mappings":";;;;;;;;AAWA,MAAa,YAAY,OAAO,MAAsB,EAAE,KAAsB;CAC5E,MAAM,gBACJA,iCAAe,sBAAsB,iBACrCC,uCAAc,qBAAqB;CACrC,MAAM,mBACJD,iCAAe,sBAAsB,WACrCC,uCAAc,qBAAqB;CAGrC,MAAM,yDAAoC;EACxC,WAAW,IAAI;EACf,WAAW,IAAI;EAChB,CAAC;AAEF,KAAI,aACF,QAAO;CAIT,MAAM,uBAAuB,IAAI,YAAY,kBAAkB;AAE/D,KAAI,CAAC,qBACH,QAAO;CAQT,MAAM,yDALyBC,0DAC7B,sBACA,iBACD,EAIC,kBACA,cACD;AAED,KAAI,mBACF,QAAO;AAIT,QAAO"}
@@ -65,7 +65,8 @@ const getLocalizedUrl = (url, currentLocale, options = {}) => {
65
65
  }
66
66
  const { prefix } = require_localization_getPrefix.getPrefix(currentLocale, {
67
67
  defaultLocale,
68
- mode
68
+ mode,
69
+ locales
69
70
  });
70
71
  let localizedPath = `/${prefix}${parsedUrl.pathname}`;
71
72
  localizedPath = localizedPath.replaceAll(/\/+/g, "/");
@@ -1 +1 @@
1
- {"version":3,"file":"getLocalizedUrl.cjs","names":["configuration","DefaultValues","getPathWithoutLocale","checkIsURLAbsolute","getPrefix"],"sources":["../../../src/localization/getLocalizedUrl.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport { DefaultValues } from '@intlayer/config/client';\nimport type { LocalesValues } from '@intlayer/types';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\nimport { getPrefix } from './getPrefix';\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 * @returns The localized URL for the current locale.\n */\nexport const getLocalizedUrl = (\n url: string,\n currentLocale: LocalesValues,\n options: {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: 'prefix-no-default' | 'prefix-all' | 'no-prefix' | 'search-params';\n } = {}\n): string => {\n const { defaultLocale, mode, locales } = {\n defaultLocale:\n configuration?.internationalization?.defaultLocale ??\n DefaultValues.Internationalization.DEFAULT_LOCALE,\n mode: configuration?.routing?.mode ?? DefaultValues.Routing.ROUTING_MODE,\n locales:\n configuration?.internationalization?.locales ??\n DefaultValues.Internationalization.LOCALES,\n ...options,\n };\n\n // Remove any existing locale segment from the URL\n const urlWithoutLocale = getPathWithoutLocale(url, locales);\n\n if (mode === 'no-prefix') {\n // No locale prefixing\n return urlWithoutLocale;\n }\n\n // Determine if the original URL is absolute (includes protocol)\n const isAbsoluteUrl = checkIsURLAbsolute(urlWithoutLocale);\n\n // Initialize a URL object if the URL is absolute\n // For relative URLs, use a dummy base to leverage the URL API\n const parsedUrl = isAbsoluteUrl\n ? new URL(urlWithoutLocale)\n : new URL(urlWithoutLocale, 'http://example.com');\n\n // Prepare the base URL (protocol + host) if it's absolute\n const baseUrl = isAbsoluteUrl\n ? `${parsedUrl.protocol}//${parsedUrl.host}`\n : '';\n\n if (mode === 'search-params') {\n // Use search parameters for locale handling\n const searchParams = new URLSearchParams(parsedUrl.search);\n searchParams.set('locale', currentLocale.toString());\n\n const queryString = searchParams.toString();\n const pathWithQuery = queryString\n ? `${parsedUrl.pathname}?${queryString}`\n : parsedUrl.pathname;\n\n if (isAbsoluteUrl) {\n return `${baseUrl}${pathWithQuery}${parsedUrl.hash}`;\n }\n\n return `${pathWithQuery}${parsedUrl.hash}`;\n }\n\n const { prefix } = getPrefix(currentLocale, {\n defaultLocale,\n mode,\n });\n\n // Construct the new pathname with or without the locale prefix\n let localizedPath = `/${prefix}${parsedUrl.pathname}`;\n\n // Remove double slashes\n localizedPath = localizedPath.replaceAll(/\\/+/g, '/');\n\n // Remove trailing slash for non-root paths\n if (localizedPath.length > 1 && localizedPath.endsWith('/')) {\n localizedPath = localizedPath.slice(0, -1);\n }\n\n // Combine with the base URL if the original URL was absolute\n if (isAbsoluteUrl) {\n return `${baseUrl}${localizedPath}${parsedUrl.search}${parsedUrl.hash}`;\n }\n\n return `${localizedPath}${parsedUrl.search}${parsedUrl.hash}`;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,mBACX,KACA,eACA,UAII,EAAE,KACK;CACX,MAAM,EAAE,eAAe,MAAM,YAAY;EACvC,eACEA,iCAAe,sBAAsB,iBACrCC,uCAAc,qBAAqB;EACrC,MAAMD,iCAAe,SAAS,QAAQC,uCAAc,QAAQ;EAC5D,SACED,iCAAe,sBAAsB,WACrCC,uCAAc,qBAAqB;EACrC,GAAG;EACJ;CAGD,MAAM,mBAAmBC,+DAAqB,KAAK,QAAQ;AAE3D,KAAI,SAAS,YAEX,QAAO;CAIT,MAAM,gBAAgBC,oDAAmB,iBAAiB;CAI1D,MAAM,YAAY,gBACd,IAAI,IAAI,iBAAiB,GACzB,IAAI,IAAI,kBAAkB,qBAAqB;CAGnD,MAAM,UAAU,gBACZ,GAAG,UAAU,SAAS,IAAI,UAAU,SACpC;AAEJ,KAAI,SAAS,iBAAiB;EAE5B,MAAM,eAAe,IAAI,gBAAgB,UAAU,OAAO;AAC1D,eAAa,IAAI,UAAU,cAAc,UAAU,CAAC;EAEpD,MAAM,cAAc,aAAa,UAAU;EAC3C,MAAM,gBAAgB,cAClB,GAAG,UAAU,SAAS,GAAG,gBACzB,UAAU;AAEd,MAAI,cACF,QAAO,GAAG,UAAU,gBAAgB,UAAU;AAGhD,SAAO,GAAG,gBAAgB,UAAU;;CAGtC,MAAM,EAAE,WAAWC,yCAAU,eAAe;EAC1C;EACA;EACD,CAAC;CAGF,IAAI,gBAAgB,IAAI,SAAS,UAAU;AAG3C,iBAAgB,cAAc,WAAW,QAAQ,IAAI;AAGrD,KAAI,cAAc,SAAS,KAAK,cAAc,SAAS,IAAI,CACzD,iBAAgB,cAAc,MAAM,GAAG,GAAG;AAI5C,KAAI,cACF,QAAO,GAAG,UAAU,gBAAgB,UAAU,SAAS,UAAU;AAGnE,QAAO,GAAG,gBAAgB,UAAU,SAAS,UAAU"}
1
+ {"version":3,"file":"getLocalizedUrl.cjs","names":["configuration","DefaultValues","getPathWithoutLocale","checkIsURLAbsolute","getPrefix"],"sources":["../../../src/localization/getLocalizedUrl.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport { DefaultValues } from '@intlayer/config/client';\nimport type { LocalesValues } from '@intlayer/types';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\nimport { getPrefix } from './getPrefix';\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 * @returns The localized URL for the current locale.\n */\nexport const getLocalizedUrl = (\n url: string,\n currentLocale: LocalesValues,\n options: {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: 'prefix-no-default' | 'prefix-all' | 'no-prefix' | 'search-params';\n } = {}\n): string => {\n const { defaultLocale, mode, locales } = {\n defaultLocale:\n configuration?.internationalization?.defaultLocale ??\n DefaultValues.Internationalization.DEFAULT_LOCALE,\n mode: configuration?.routing?.mode ?? DefaultValues.Routing.ROUTING_MODE,\n locales:\n configuration?.internationalization?.locales ??\n DefaultValues.Internationalization.LOCALES,\n ...options,\n };\n\n // Remove any existing locale segment from the URL\n const urlWithoutLocale = getPathWithoutLocale(url, locales);\n\n if (mode === 'no-prefix') {\n // No locale prefixing\n return urlWithoutLocale;\n }\n\n // Determine if the original URL is absolute (includes protocol)\n const isAbsoluteUrl = checkIsURLAbsolute(urlWithoutLocale);\n\n // Initialize a URL object if the URL is absolute\n // For relative URLs, use a dummy base to leverage the URL API\n const parsedUrl = isAbsoluteUrl\n ? new URL(urlWithoutLocale)\n : new URL(urlWithoutLocale, 'http://example.com');\n\n // Prepare the base URL (protocol + host) if it's absolute\n const baseUrl = isAbsoluteUrl\n ? `${parsedUrl.protocol}//${parsedUrl.host}`\n : '';\n\n if (mode === 'search-params') {\n // Use search parameters for locale handling\n const searchParams = new URLSearchParams(parsedUrl.search);\n searchParams.set('locale', currentLocale.toString());\n\n const queryString = searchParams.toString();\n const pathWithQuery = queryString\n ? `${parsedUrl.pathname}?${queryString}`\n : parsedUrl.pathname;\n\n if (isAbsoluteUrl) {\n return `${baseUrl}${pathWithQuery}${parsedUrl.hash}`;\n }\n\n return `${pathWithQuery}${parsedUrl.hash}`;\n }\n\n const { prefix } = getPrefix(currentLocale, {\n defaultLocale,\n mode,\n locales,\n });\n\n // Construct the new pathname with or without the locale prefix\n let localizedPath = `/${prefix}${parsedUrl.pathname}`;\n\n // Remove double slashes\n localizedPath = localizedPath.replaceAll(/\\/+/g, '/');\n\n // Remove trailing slash for non-root paths\n if (localizedPath.length > 1 && localizedPath.endsWith('/')) {\n localizedPath = localizedPath.slice(0, -1);\n }\n\n // Combine with the base URL if the original URL was absolute\n if (isAbsoluteUrl) {\n return `${baseUrl}${localizedPath}${parsedUrl.search}${parsedUrl.hash}`;\n }\n\n return `${localizedPath}${parsedUrl.search}${parsedUrl.hash}`;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,mBACX,KACA,eACA,UAII,EAAE,KACK;CACX,MAAM,EAAE,eAAe,MAAM,YAAY;EACvC,eACEA,iCAAe,sBAAsB,iBACrCC,uCAAc,qBAAqB;EACrC,MAAMD,iCAAe,SAAS,QAAQC,uCAAc,QAAQ;EAC5D,SACED,iCAAe,sBAAsB,WACrCC,uCAAc,qBAAqB;EACrC,GAAG;EACJ;CAGD,MAAM,mBAAmBC,+DAAqB,KAAK,QAAQ;AAE3D,KAAI,SAAS,YAEX,QAAO;CAIT,MAAM,gBAAgBC,oDAAmB,iBAAiB;CAI1D,MAAM,YAAY,gBACd,IAAI,IAAI,iBAAiB,GACzB,IAAI,IAAI,kBAAkB,qBAAqB;CAGnD,MAAM,UAAU,gBACZ,GAAG,UAAU,SAAS,IAAI,UAAU,SACpC;AAEJ,KAAI,SAAS,iBAAiB;EAE5B,MAAM,eAAe,IAAI,gBAAgB,UAAU,OAAO;AAC1D,eAAa,IAAI,UAAU,cAAc,UAAU,CAAC;EAEpD,MAAM,cAAc,aAAa,UAAU;EAC3C,MAAM,gBAAgB,cAClB,GAAG,UAAU,SAAS,GAAG,gBACzB,UAAU;AAEd,MAAI,cACF,QAAO,GAAG,UAAU,gBAAgB,UAAU;AAGhD,SAAO,GAAG,gBAAgB,UAAU;;CAGtC,MAAM,EAAE,WAAWC,yCAAU,eAAe;EAC1C;EACA;EACA;EACD,CAAC;CAGF,IAAI,gBAAgB,IAAI,SAAS,UAAU;AAG3C,iBAAgB,cAAc,WAAW,QAAQ,IAAI;AAGrD,KAAI,cAAc,SAAS,KAAK,cAAc,SAAS,IAAI,CACzD,iBAAgB,cAAc,MAAM,GAAG,GAAG;AAI5C,KAAI,cACF,QAAO,GAAG,UAAU,gBAAgB,UAAU,SAAS,UAAU;AAGnE,QAAO,GAAG,gBAAgB,UAAU,SAAS,UAAU"}
@@ -0,0 +1,309 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_interpreter_getContent_deepTransform = require('../interpreter/getContent/deepTransform.cjs');
3
+ const require_transpiler_enumeration_enumeration = require('../transpiler/enumeration/enumeration.cjs');
4
+ const require_transpiler_gender_gender = require('../transpiler/gender/gender.cjs');
5
+ const require_transpiler_insertion_insertion = require('../transpiler/insertion/insertion.cjs');
6
+ let __intlayer_types = require("@intlayer/types");
7
+
8
+ //#region src/messageFormat/ICU.ts
9
+ const parseICU = (text) => {
10
+ let index = 0;
11
+ const parseNodes = () => {
12
+ const nodes = [];
13
+ let currentText = "";
14
+ while (index < text.length) {
15
+ const char = text[index];
16
+ if (char === "{") {
17
+ if (currentText) {
18
+ nodes.push(currentText);
19
+ currentText = "";
20
+ }
21
+ index++;
22
+ nodes.push(parseArgument());
23
+ } else if (char === "}") break;
24
+ else if (char === "'") if (index + 1 < text.length && text[index + 1] === "'") {
25
+ currentText += "'";
26
+ index += 2;
27
+ } else {
28
+ const nextQuote = text.indexOf("'", index + 1);
29
+ if (nextQuote !== -1) {
30
+ currentText += text.substring(index + 1, nextQuote);
31
+ index = nextQuote + 1;
32
+ } else {
33
+ currentText += "'";
34
+ index++;
35
+ }
36
+ }
37
+ else {
38
+ currentText += char;
39
+ index++;
40
+ }
41
+ }
42
+ if (currentText) nodes.push(currentText);
43
+ return nodes;
44
+ };
45
+ const parseArgument = () => {
46
+ let name = "";
47
+ while (index < text.length && /[^,}]/.test(text[index])) {
48
+ name += text[index];
49
+ index++;
50
+ }
51
+ name = name.trim();
52
+ if (index >= text.length) throw new Error("Unclosed argument");
53
+ if (text[index] === "}") {
54
+ index++;
55
+ return {
56
+ type: "argument",
57
+ name
58
+ };
59
+ }
60
+ if (text[index] === ",") {
61
+ index++;
62
+ let type = "";
63
+ while (index < text.length && /[^,}]/.test(text[index])) {
64
+ type += text[index];
65
+ index++;
66
+ }
67
+ type = type.trim();
68
+ if (index >= text.length) throw new Error("Unclosed argument");
69
+ if (text[index] === "}") {
70
+ index++;
71
+ return {
72
+ type: "argument",
73
+ name,
74
+ format: { type }
75
+ };
76
+ }
77
+ if (text[index] === ",") {
78
+ index++;
79
+ if (type === "plural" || type === "select") {
80
+ const options = {};
81
+ while (index < text.length && text[index] !== "}") {
82
+ while (index < text.length && /\s/.test(text[index])) index++;
83
+ let key = "";
84
+ while (index < text.length && /[^{\s]/.test(text[index])) {
85
+ key += text[index];
86
+ index++;
87
+ }
88
+ while (index < text.length && /\s/.test(text[index])) index++;
89
+ if (text[index] !== "{") throw new Error("Expected { after option key");
90
+ index++;
91
+ const value = parseNodes();
92
+ if (text[index] !== "}") throw new Error("Expected } after option value");
93
+ index++;
94
+ options[key] = value;
95
+ while (index < text.length && /\s/.test(text[index])) index++;
96
+ }
97
+ index++;
98
+ if (type === "plural") return {
99
+ type: "plural",
100
+ name,
101
+ options
102
+ };
103
+ else if (type === "select") return {
104
+ type: "select",
105
+ name,
106
+ options
107
+ };
108
+ } else {
109
+ let style = "";
110
+ while (index < text.length && text[index] !== "}") {
111
+ style += text[index];
112
+ index++;
113
+ }
114
+ if (index >= text.length) throw new Error("Unclosed argument");
115
+ style = style.trim();
116
+ index++;
117
+ return {
118
+ type: "argument",
119
+ name,
120
+ format: {
121
+ type,
122
+ style
123
+ }
124
+ };
125
+ }
126
+ }
127
+ }
128
+ throw new Error("Malformed argument");
129
+ };
130
+ return parseNodes();
131
+ };
132
+ const icuNodesToIntlayer = (nodes) => {
133
+ if (nodes.length === 0) return "";
134
+ if (nodes.length === 1 && typeof nodes[0] === "string") return nodes[0];
135
+ if (nodes.every((node) => typeof node === "string" || node.type === "argument")) {
136
+ let str = "";
137
+ for (const node of nodes) if (typeof node === "string") str += node;
138
+ else if (typeof node !== "string" && node.type === "argument") if (node.format) str += `{${node.name}, ${node.format.type}${node.format.style ? `, ${node.format.style}` : ""}}`;
139
+ else str += `{{${node.name}}}`;
140
+ return require_transpiler_insertion_insertion.insert(str);
141
+ }
142
+ if (nodes.length === 1) {
143
+ const node = nodes[0];
144
+ if (typeof node === "string") return node;
145
+ if (node.type === "argument") {
146
+ if (node.format) return require_transpiler_insertion_insertion.insert(`{${node.name}, ${node.format.type}${node.format.style ? `, ${node.format.style}` : ""}}`);
147
+ return require_transpiler_insertion_insertion.insert(`{{${node.name}}}`);
148
+ }
149
+ if (node.type === "plural") {
150
+ const options = {};
151
+ for (const [key, val] of Object.entries(node.options)) {
152
+ let newKey = key;
153
+ if (key.startsWith("=")) newKey = key.substring(1);
154
+ else if (key === "one") newKey = "1";
155
+ else if (key === "two") newKey = "2";
156
+ else if (key === "few") newKey = "<=3";
157
+ else if (key === "many") newKey = ">=4";
158
+ else if (key === "other") newKey = "fallback";
159
+ options[newKey] = icuNodesToIntlayer(val.map((v) => {
160
+ if (typeof v === "string") return v.replace(/#/g, `{{${node.name}}}`);
161
+ return v;
162
+ }));
163
+ }
164
+ options.__intlayer_icu_var = node.name;
165
+ return require_transpiler_enumeration_enumeration.enu(options);
166
+ }
167
+ if (node.type === "select") {
168
+ const options = {};
169
+ for (const [key, val] of Object.entries(node.options)) options[key] = icuNodesToIntlayer(val);
170
+ const optionKeys = Object.keys(options);
171
+ if ((options.male || options.female) && optionKeys.every((k) => [
172
+ "male",
173
+ "female",
174
+ "other",
175
+ "fallback"
176
+ ].includes(k))) return require_transpiler_gender_gender.gender({
177
+ fallback: options.other,
178
+ male: options.male,
179
+ female: options.female
180
+ });
181
+ options.__intlayer_icu_var = node.name;
182
+ return require_transpiler_enumeration_enumeration.enu(options);
183
+ }
184
+ }
185
+ return nodes.map((node) => icuNodesToIntlayer([node]));
186
+ };
187
+ const icuToIntlayerPlugin = {
188
+ canHandle: (node) => typeof node === "string" && (node.includes("{") || node.includes("}")),
189
+ transform: (node) => {
190
+ try {
191
+ return icuNodesToIntlayer(parseICU(node));
192
+ } catch {
193
+ return node;
194
+ }
195
+ }
196
+ };
197
+ const intlayerToIcuPlugin = {
198
+ canHandle: (node) => typeof node === "string" && (node.includes("{") || node.includes("}")) || node && typeof node === "object" && (node.nodeType === __intlayer_types.NodeType.Insertion || node.nodeType === __intlayer_types.NodeType.Enumeration || node.nodeType === __intlayer_types.NodeType.Gender || node.nodeType === "composite") || Array.isArray(node),
199
+ transform: (node, props, next) => {
200
+ if (typeof node === "string") return node.replace(/\{\{([^}]+)\}\}/g, "{$1}");
201
+ if (node.nodeType === __intlayer_types.NodeType.Insertion) return node.insertion.replace(/\{\{([^}]+)\}\}/g, "{$1}");
202
+ if (node.nodeType === __intlayer_types.NodeType.Enumeration) {
203
+ const options = node.enumeration;
204
+ const transformedOptions = {};
205
+ for (const [key, val] of Object.entries(options)) {
206
+ if (key === "__intlayer_icu_var") continue;
207
+ const childVal = next(val, props);
208
+ transformedOptions[key] = typeof childVal === "string" ? childVal : JSON.stringify(childVal);
209
+ }
210
+ let varName = options.__intlayer_icu_var || "n";
211
+ if (!options.__intlayer_icu_var) {
212
+ const match = (transformedOptions.fallback || transformedOptions.other || Object.values(transformedOptions)[0]).match(/\{([a-zA-Z0-9_]+)\}(?!,)/);
213
+ if (match) varName = match[1];
214
+ }
215
+ const keys = Object.keys(transformedOptions);
216
+ const pluralKeys = [
217
+ "1",
218
+ "2",
219
+ "<=3",
220
+ ">=4",
221
+ "fallback",
222
+ "other",
223
+ "zero",
224
+ "one",
225
+ "two",
226
+ "few",
227
+ "many"
228
+ ];
229
+ const isPlural = keys.every((k) => pluralKeys.includes(k) || /^[<>=]?\d+(\.\d+)?$/.test(k));
230
+ const parts = [];
231
+ if (isPlural) {
232
+ for (const [key, val] of Object.entries(transformedOptions)) {
233
+ let icuKey = key;
234
+ if (key === "fallback") icuKey = "other";
235
+ else if (key === "<=3") icuKey = "few";
236
+ else if (key === ">=4") icuKey = "many";
237
+ else if (/^\d+$/.test(key)) icuKey = `=${key}`;
238
+ else if ([
239
+ "zero",
240
+ "few",
241
+ "many"
242
+ ].includes(key)) icuKey = key;
243
+ else icuKey = "other";
244
+ let strVal = val;
245
+ strVal = strVal.replace(new RegExp(`\\{${varName}\\}`, "g"), "#");
246
+ parts.push(`${icuKey} {${strVal}}`);
247
+ }
248
+ return `{${varName}, plural, ${parts.join(" ")}}`;
249
+ } else {
250
+ const entries = Object.entries(transformedOptions).sort(([keyA], [keyB]) => {
251
+ if (keyA === "fallback" || keyA === "other") return 1;
252
+ if (keyB === "fallback" || keyB === "other") return -1;
253
+ return 0;
254
+ });
255
+ for (const [key, val] of entries) {
256
+ let icuKey = key;
257
+ if (key === "fallback") icuKey = "other";
258
+ parts.push(`${icuKey} {${val}}`);
259
+ }
260
+ return `{${varName}, select, ${parts.join(" ")}}`;
261
+ }
262
+ }
263
+ if (node.nodeType === __intlayer_types.NodeType.Gender) {
264
+ const options = node.gender;
265
+ const varName = "gender";
266
+ const parts = [];
267
+ const entries = Object.entries(options).sort(([keyA], [keyB]) => {
268
+ if (keyA === "fallback") return 1;
269
+ if (keyB === "fallback") return -1;
270
+ return 0;
271
+ });
272
+ for (const [key, val] of entries) {
273
+ let icuKey = key;
274
+ if (key === "fallback") icuKey = "other";
275
+ const childVal = next(val, props);
276
+ const strVal = typeof childVal === "string" ? childVal : JSON.stringify(childVal);
277
+ parts.push(`${icuKey} {${strVal}}`);
278
+ }
279
+ return `{${varName}, select, ${parts.join(" ")}}`;
280
+ }
281
+ if (Array.isArray(node) || node.nodeType === "composite" && Array.isArray(node.composite)) return (Array.isArray(node) ? node : node.composite).map((item) => next(item, props)).join("");
282
+ return next(node, props);
283
+ }
284
+ };
285
+ const intlayerToICUFormatter = (message) => {
286
+ return require_interpreter_getContent_deepTransform.deepTransformNode(message, {
287
+ dictionaryKey: "icu",
288
+ keyPath: [],
289
+ plugins: [{
290
+ id: "icu",
291
+ ...intlayerToIcuPlugin
292
+ }]
293
+ });
294
+ };
295
+ const icuToIntlayerFormatter = (message) => {
296
+ return require_interpreter_getContent_deepTransform.deepTransformNode(message, {
297
+ dictionaryKey: "icu",
298
+ keyPath: [],
299
+ plugins: [{
300
+ id: "icu",
301
+ ...icuToIntlayerPlugin
302
+ }]
303
+ });
304
+ };
305
+
306
+ //#endregion
307
+ exports.icuToIntlayerFormatter = icuToIntlayerFormatter;
308
+ exports.intlayerToICUFormatter = intlayerToICUFormatter;
309
+ //# sourceMappingURL=ICU.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ICU.cjs","names":["nodes: ICUNode[]","options: Record<string, ICUNode[]>","insert","options: Record<string, any>","enu","gender","NodeType","transformedOptions: Record<string, string>","deepTransformNode"],"sources":["../../../src/messageFormat/ICU.ts"],"sourcesContent":["import { type Dictionary, NodeType } from '@intlayer/types';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, gender, insert } from '../transpiler';\n\n// Types for our AST\ntype ICUNode =\n | string\n | {\n type: 'argument';\n name: string;\n format?: { type: string; style?: string };\n }\n | { type: 'plural'; name: string; options: Record<string, ICUNode[]> }\n | { type: 'select'; name: string; options: Record<string, ICUNode[]> };\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nconst parseICU = (text: string): ICUNode[] => {\n let index = 0;\n\n const parseNodes = (): ICUNode[] => {\n const nodes: ICUNode[] = [];\n let currentText = '';\n\n while (index < text.length) {\n const char = text[index];\n\n if (char === '{') {\n if (currentText) {\n nodes.push(currentText);\n currentText = '';\n }\n index++; // skip {\n nodes.push(parseArgument());\n } else if (char === '}') {\n // End of current block\n break;\n } else if (char === \"'\") {\n // Escaping\n if (index + 1 < text.length && text[index + 1] === \"'\") {\n currentText += \"'\";\n index += 2;\n } else {\n // Find next quote\n const nextQuote = text.indexOf(\"'\", index + 1);\n if (nextQuote !== -1) {\n // Determine if this is escaping syntax characters\n // For simplicity, we'll treat content between single quotes as literal\n // provided it contains syntax chars.\n // Standard ICU: ' quoted string '\n // If it is just an apostrophe, it should be doubled.\n // But simplified: take content between quotes literally.\n currentText += text.substring(index + 1, nextQuote);\n index = nextQuote + 1;\n } else {\n currentText += \"'\";\n index++;\n }\n }\n } else {\n currentText += char;\n index++;\n }\n }\n\n if (currentText) {\n nodes.push(currentText);\n }\n return nodes;\n };\n\n const parseArgument = (): ICUNode => {\n // We are past '{'\n // Parse name\n let name = '';\n while (index < text.length && /[^,}]/.test(text[index])) {\n name += text[index];\n index++;\n }\n name = name.trim();\n\n if (index >= text.length) throw new Error('Unclosed argument');\n\n if (text[index] === '}') {\n index++;\n return { type: 'argument', name };\n }\n\n // Must be comma\n if (text[index] === ',') {\n index++;\n // Parse type\n let type = '';\n while (index < text.length && /[^,}]/.test(text[index])) {\n type += text[index];\n index++;\n }\n type = type.trim();\n\n if (index >= text.length) throw new Error('Unclosed argument');\n\n if (text[index] === '}') {\n index++;\n return { type: 'argument', name, format: { type } };\n }\n\n if (text[index] === ',') {\n index++;\n\n // If plural or select, parse options\n if (type === 'plural' || type === 'select') {\n // Parse options\n const options: Record<string, ICUNode[]> = {};\n\n while (index < text.length && text[index] !== '}') {\n // skip whitespace\n while (index < text.length && /\\s/.test(text[index])) index++;\n\n // parse key\n let key = '';\n while (index < text.length && /[^{\\s]/.test(text[index])) {\n key += text[index];\n index++;\n }\n\n while (index < text.length && /\\s/.test(text[index])) index++;\n\n if (text[index] !== '{')\n throw new Error('Expected { after option key');\n index++; // skip {\n\n const value = parseNodes();\n\n if (text[index] !== '}')\n throw new Error('Expected } after option value');\n index++; // skip }\n\n options[key] = value;\n\n while (index < text.length && /\\s/.test(text[index])) index++;\n }\n\n index++; // skip closing argument }\n\n if (type === 'plural') {\n return { type: 'plural', name, options };\n } else if (type === 'select') {\n return { type: 'select', name, options };\n }\n } else {\n // Parse style for number/date/time\n let style = '';\n while (index < text.length && text[index] !== '}') {\n style += text[index];\n index++;\n }\n if (index >= text.length) throw new Error('Unclosed argument');\n\n style = style.trim();\n index++; // skip }\n\n return { type: 'argument', name, format: { type, style } };\n }\n }\n }\n\n throw new Error('Malformed argument');\n };\n\n return parseNodes();\n};\n\nconst icuNodesToIntlayer = (nodes: ICUNode[]): any => {\n if (nodes.length === 0) return '';\n if (nodes.length === 1 && typeof nodes[0] === 'string') return nodes[0];\n\n // Check if we can flatten to a single string (insert)\n const canFlatten = nodes.every(\n (node) => typeof node === 'string' || node.type === 'argument'\n );\n if (canFlatten) {\n let str = '';\n for (const node of nodes) {\n if (typeof node === 'string') {\n str += node;\n } else if (typeof node !== 'string' && node.type === 'argument') {\n if (node.format) {\n str += `{${node.name}, ${node.format.type}${\n node.format.style ? `, ${node.format.style}` : ''\n }}`;\n } else {\n str += `{{${node.name}}}`;\n }\n }\n }\n return insert(str);\n }\n\n // Mix of string and complex types.\n // If we have just one complex type and it covers everything?\n if (nodes.length === 1) {\n const node = nodes[0];\n\n if (typeof node === 'string') return node; // already handled\n if (node.type === 'argument') {\n if (node.format) {\n return insert(\n `{${node.name}, ${node.format.type}${\n node.format.style ? `, ${node.format.style}` : ''\n }}`\n );\n }\n return insert(`{{${node.name}}}`);\n }\n if (node.type === 'plural') {\n const options: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(node.options)) {\n // Map ICU keys to Intlayer keys\n let newKey = key;\n if (key.startsWith('=')) {\n newKey = key.substring(1); // =0 -> 0\n } else if (key === 'one') {\n newKey = '1';\n } else if (key === 'two') {\n newKey = '2';\n } else if (key === 'few') {\n newKey = '<=3';\n } else if (key === 'many') {\n newKey = '>=4';\n } else if (key === 'other') {\n newKey = 'fallback';\n }\n // Handle # in plural value\n // For plural, we need to pass the variable name down or replace #\n // Intlayer uses {{n}} (or whatever var name)\n // We should replace # with {{n}} in the string parts of val\n const replacedVal = val.map((v) => {\n if (typeof v === 'string') {\n return v.replace(/#/g, `{{${node.name}}}`);\n }\n return v;\n });\n\n options[newKey] = icuNodesToIntlayer(replacedVal);\n }\n\n // Preserve variable name\n options.__intlayer_icu_var = node.name;\n\n return enu(options);\n }\n if (node.type === 'select') {\n const options: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(node.options)) {\n options[key] = icuNodesToIntlayer(val);\n }\n\n // Check if it looks like gender\n const optionKeys = Object.keys(options);\n // It is gender if it has 'male' OR 'female' AND only contains gender keys (male, female, other)\n const isGender =\n (options.male || options.female) &&\n optionKeys.every((k) =>\n ['male', 'female', 'other', 'fallback'].includes(k)\n );\n\n if (isGender) {\n return gender({\n fallback: options.other,\n male: options.male,\n female: options.female,\n });\n }\n\n // Preserve variable name\n options.__intlayer_icu_var = node.name;\n\n return enu(options);\n }\n }\n\n // If multiple nodes, return array\n return nodes.map((node) => icuNodesToIntlayer([node]));\n};\n\nconst icuToIntlayerPlugin = {\n canHandle: (node: any) =>\n typeof node === 'string' && (node.includes('{') || node.includes('}')),\n transform: (node: any) => {\n try {\n const ast = parseICU(node);\n return icuNodesToIntlayer(ast);\n } catch {\n // If parsing fails, return original string\n return node;\n }\n },\n};\n\nconst intlayerToIcuPlugin = {\n canHandle: (node: any) =>\n (typeof node === 'string' && (node.includes('{') || node.includes('}'))) ||\n (node &&\n typeof node === 'object' &&\n (node.nodeType === NodeType.Insertion ||\n node.nodeType === NodeType.Enumeration ||\n node.nodeType === NodeType.Gender ||\n node.nodeType === 'composite')) ||\n Array.isArray(node),\n transform: (node: any, props: any, next: any) => {\n if (typeof node === 'string') {\n return node.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeType.Insertion) {\n // insert(\"Hello {{name}}\") -> \"Hello {name}\"\n return node.insertion.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeType.Enumeration) {\n const options = node.enumeration;\n\n // Transform all values first\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n if (key === '__intlayer_icu_var') continue;\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n // Infer variable name\n let varName = options.__intlayer_icu_var || 'n';\n\n if (!options.__intlayer_icu_var) {\n const fallbackVal =\n transformedOptions.fallback ||\n transformedOptions.other ||\n Object.values(transformedOptions)[0];\n // Match {variable} but avoid {variable, ...} (which are nested ICUs)\n // Actually nested ICU starts with {var, ...\n // Simple variable is {var}\n // We look for {var} that is NOT followed by ,\n const match = fallbackVal.match(/\\{([a-zA-Z0-9_]+)\\}(?!,)/);\n if (match) {\n varName = match[1];\n }\n }\n\n const keys = Object.keys(transformedOptions);\n const pluralKeys = [\n '1',\n '2',\n '<=3',\n '>=4',\n 'fallback',\n 'other',\n 'zero',\n 'one',\n 'two',\n 'few',\n 'many',\n ];\n // Also check for numbers\n const isPlural = keys.every(\n (k) => pluralKeys.includes(k) || /^[<>=]?\\d+(\\.\\d+)?$/.test(k)\n );\n\n const parts = [];\n\n if (isPlural) {\n for (const [key, val] of Object.entries(transformedOptions)) {\n let icuKey = key;\n\n if (key === 'fallback') icuKey = 'other';\n else if (key === '<=3') icuKey = 'few';\n else if (key === '>=4') icuKey = 'many';\n else if (/^\\d+$/.test(key)) icuKey = `=${key}`;\n else if (['zero', 'few', 'many'].includes(key)) icuKey = key;\n else icuKey = 'other';\n\n let strVal = val;\n\n // Replace {varName} with #\n // Note: next() has already converted {{var}} -> {var}\n strVal = strVal.replace(new RegExp(`\\\\{${varName}\\\\}`, 'g'), '#');\n\n parts.push(`${icuKey} {${strVal}}`);\n }\n\n return `{${varName}, plural, ${parts.join(' ')}}`;\n } else {\n // Select\n const entries = Object.entries(transformedOptions).sort(\n ([keyA], [keyB]) => {\n if (keyA === 'fallback' || keyA === 'other') return 1;\n if (keyB === 'fallback' || keyB === 'other') return -1;\n return 0;\n }\n );\n\n for (const [key, val] of entries) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n // Do NOT map other keys to 'other'. Keep 'active', 'inactive', etc.\n\n parts.push(`${icuKey} {${val}}`);\n }\n return `{${varName}, select, ${parts.join(' ')}}`;\n }\n }\n\n if (node.nodeType === NodeType.Gender) {\n const options = node.gender;\n const varName = 'gender';\n const parts = [];\n\n const entries = Object.entries(options).sort(([keyA], [keyB]) => {\n if (keyA === 'fallback') return 1;\n if (keyB === 'fallback') return -1;\n return 0;\n });\n\n for (const [key, val] of entries) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n\n const childVal = next(val, props);\n const strVal =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n\n parts.push(`${icuKey} {${strVal}}`);\n }\n return `{${varName}, select, ${parts.join(' ')}}`;\n }\n\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n // handle array/composite\n const arr = Array.isArray(node) ? node : node.composite;\n const items = arr.map((item: any) => next(item, props));\n return items.join('');\n }\n\n return next(node, props);\n },\n};\n\nexport const intlayerToICUFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'icu',\n keyPath: [],\n plugins: [{ id: 'icu', ...intlayerToIcuPlugin }],\n });\n};\n\nexport const icuToIntlayerFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'icu',\n keyPath: [],\n plugins: [{ id: 'icu', ...icuToIntlayerPlugin }],\n });\n};\n"],"mappings":"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"}