@intlayer/core 7.0.9-canary.0 → 7.0.9-canary.2
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.
- package/README.md +2 -1
- package/dist/cjs/config/dist/esm/_virtual/rolldown_runtime.cjs +20 -0
- package/dist/cjs/config/dist/esm/_virtual/rolldown_runtime.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/built.cjs +8 -0
- package/dist/cjs/config/dist/esm/built.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/configFile/buildConfigurationFields.cjs +119 -0
- package/dist/cjs/config/dist/esm/configFile/buildConfigurationFields.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/configFile/getConfiguration.cjs +69 -0
- package/dist/cjs/config/dist/esm/configFile/getConfiguration.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/configFile/loadConfigurationFile.cjs +26 -0
- package/dist/cjs/config/dist/esm/configFile/loadConfigurationFile.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/configFile/searchConfigurationFile.cjs +51 -0
- package/dist/cjs/config/dist/esm/configFile/searchConfigurationFile.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/build.cjs +24 -0
- package/dist/cjs/config/dist/esm/defaultValues/build.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/content.cjs +78 -0
- package/dist/cjs/config/dist/esm/defaultValues/content.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/dictionary.cjs +7 -0
- package/dist/cjs/config/dist/esm/defaultValues/dictionary.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/editor.cjs +36 -0
- package/dist/cjs/config/dist/esm/defaultValues/editor.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/index.cjs +21 -0
- package/dist/cjs/config/dist/esm/defaultValues/index.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/internationalization.cjs +22 -0
- package/dist/cjs/config/dist/esm/defaultValues/internationalization.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/log.cjs +16 -0
- package/dist/cjs/config/dist/esm/defaultValues/log.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/defaultValues/routing.cjs +26 -0
- package/dist/cjs/config/dist/esm/defaultValues/routing.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/loadEnvFile.cjs +26 -0
- package/dist/cjs/config/dist/esm/loadEnvFile.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/bundleFile.cjs +23 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/bundleFile.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/loadExternalFile.cjs +47 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/loadExternalFile.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/parseFileContent.cjs +65 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/parseFileContent.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/transpileTSToMJS.cjs +45 -0
- package/dist/cjs/config/dist/esm/loadExternalFile/transpileTSToMJS.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/logger.cjs +65 -0
- package/dist/cjs/config/dist/esm/logger.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.cjs +15 -0
- package/dist/cjs/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/types/dist/esm/locales.cjs +1128 -0
- package/dist/cjs/config/dist/esm/types/dist/esm/locales.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/utils/ESMxCJSHelpers.cjs +27 -0
- package/dist/cjs/config/dist/esm/utils/ESMxCJSHelpers.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/utils/cache.cjs +261 -0
- package/dist/cjs/config/dist/esm/utils/cache.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/utils/clearModuleCache.cjs +26 -0
- package/dist/cjs/config/dist/esm/utils/clearModuleCache.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/utils/getPackageJsonPath.cjs +34 -0
- package/dist/cjs/config/dist/esm/utils/getPackageJsonPath.cjs.map +1 -0
- package/dist/cjs/config/dist/esm/utils/normalizePath.cjs +17 -0
- package/dist/cjs/config/dist/esm/utils/normalizePath.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/_virtual/rolldown_runtime.cjs +10 -0
- package/dist/cjs/dictionaries-entry/dist/esm/_virtual/rolldown_runtime.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/_virtual/rolldown_runtime.cjs +11 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/_virtual/rolldown_runtime.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/built.cjs +8 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/built.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/buildConfigurationFields.cjs +119 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/buildConfigurationFields.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/getConfiguration.cjs +69 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/getConfiguration.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/loadConfigurationFile.cjs +26 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/loadConfigurationFile.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/searchConfigurationFile.cjs +51 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/configFile/searchConfigurationFile.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/build.cjs +15 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/build.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/content.cjs +54 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/content.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/dictionary.cjs +7 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/dictionary.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/editor.cjs +23 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/editor.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/internationalization.cjs +14 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/internationalization.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/log.cjs +10 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/log.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/routing.cjs +11 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/routing.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadEnvFile.cjs +26 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadEnvFile.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/bundleFile.cjs +23 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/bundleFile.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/loadExternalFile.cjs +47 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/loadExternalFile.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/parseFileContent.cjs +65 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/parseFileContent.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/transpileTSToMJS.cjs +45 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/transpileTSToMJS.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/logger.cjs +47 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/logger.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.cjs +15 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/locales.cjs +1128 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/locales.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/ESMxCJSHelpers.cjs +27 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/ESMxCJSHelpers.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/cache.cjs +261 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/cache.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/getPackageJsonPath.cjs +34 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/getPackageJsonPath.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/normalizePath.cjs +17 -0
- package/dist/cjs/dictionaries-entry/dist/esm/config/dist/esm/utils/normalizePath.cjs.map +1 -0
- package/dist/cjs/dictionaries-entry/dist/esm/index.cjs +27 -0
- package/dist/cjs/dictionaries-entry/dist/esm/index.cjs.map +1 -0
- package/dist/cjs/types/dist/esm/_virtual/rolldown_runtime.cjs +15 -0
- package/dist/cjs/types/dist/esm/_virtual/rolldown_runtime.cjs.map +1 -0
- package/dist/cjs/types/dist/esm/locales.cjs +1128 -0
- package/dist/cjs/types/dist/esm/locales.cjs.map +1 -0
- package/dist/cjs/types/dist/esm/nodeType.cjs +31 -0
- package/dist/cjs/types/dist/esm/nodeType.cjs.map +1 -0
- package/dist/esm/_virtual/rolldown_runtime.mjs +8 -0
- package/dist/esm/config/dist/esm/_virtual/rolldown_runtime.mjs +20 -0
- package/dist/esm/config/dist/esm/_virtual/rolldown_runtime.mjs.map +1 -0
- package/dist/esm/config/dist/esm/built.mjs +8 -0
- package/dist/esm/config/dist/esm/built.mjs.map +1 -0
- package/dist/esm/config/dist/esm/configFile/buildConfigurationFields.mjs +118 -0
- package/dist/esm/config/dist/esm/configFile/buildConfigurationFields.mjs.map +1 -0
- package/dist/esm/config/dist/esm/configFile/getConfiguration.mjs +67 -0
- package/dist/esm/config/dist/esm/configFile/getConfiguration.mjs.map +1 -0
- package/dist/esm/config/dist/esm/configFile/loadConfigurationFile.mjs +26 -0
- package/dist/esm/config/dist/esm/configFile/loadConfigurationFile.mjs.map +1 -0
- package/dist/esm/config/dist/esm/configFile/searchConfigurationFile.mjs +50 -0
- package/dist/esm/config/dist/esm/configFile/searchConfigurationFile.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/build.mjs +19 -0
- package/dist/esm/config/dist/esm/defaultValues/build.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/content.mjs +64 -0
- package/dist/esm/config/dist/esm/defaultValues/content.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/dictionary.mjs +6 -0
- package/dist/esm/config/dist/esm/defaultValues/dictionary.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/editor.mjs +27 -0
- package/dist/esm/config/dist/esm/defaultValues/editor.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/index.mjs +21 -0
- package/dist/esm/config/dist/esm/defaultValues/index.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/internationalization.mjs +18 -0
- package/dist/esm/config/dist/esm/defaultValues/internationalization.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/log.mjs +14 -0
- package/dist/esm/config/dist/esm/defaultValues/log.mjs.map +1 -0
- package/dist/esm/config/dist/esm/defaultValues/routing.mjs +23 -0
- package/dist/esm/config/dist/esm/defaultValues/routing.mjs.map +1 -0
- package/dist/esm/config/dist/esm/loadEnvFile.mjs +24 -0
- package/dist/esm/config/dist/esm/loadEnvFile.mjs.map +1 -0
- package/dist/esm/config/dist/esm/loadExternalFile/bundleFile.mjs +23 -0
- package/dist/esm/config/dist/esm/loadExternalFile/bundleFile.mjs.map +1 -0
- package/dist/esm/config/dist/esm/loadExternalFile/loadExternalFile.mjs +47 -0
- package/dist/esm/config/dist/esm/loadExternalFile/loadExternalFile.mjs.map +1 -0
- package/dist/esm/config/dist/esm/loadExternalFile/parseFileContent.mjs +63 -0
- package/dist/esm/config/dist/esm/loadExternalFile/parseFileContent.mjs.map +1 -0
- package/dist/esm/config/dist/esm/loadExternalFile/transpileTSToMJS.mjs +44 -0
- package/dist/esm/config/dist/esm/loadExternalFile/transpileTSToMJS.mjs.map +1 -0
- package/dist/esm/config/dist/esm/logger.mjs +59 -0
- package/dist/esm/config/dist/esm/logger.mjs.map +1 -0
- package/dist/esm/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.mjs +14 -0
- package/dist/esm/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.mjs.map +1 -0
- package/dist/esm/config/dist/esm/types/dist/esm/locales.mjs +1128 -0
- package/dist/esm/config/dist/esm/types/dist/esm/locales.mjs.map +1 -0
- package/dist/esm/config/dist/esm/utils/ESMxCJSHelpers.mjs +25 -0
- package/dist/esm/config/dist/esm/utils/ESMxCJSHelpers.mjs.map +1 -0
- package/dist/esm/config/dist/esm/utils/cache.mjs +263 -0
- package/dist/esm/config/dist/esm/utils/cache.mjs.map +1 -0
- package/dist/esm/config/dist/esm/utils/getPackageJsonPath.mjs +33 -0
- package/dist/esm/config/dist/esm/utils/getPackageJsonPath.mjs.map +1 -0
- package/dist/esm/config/dist/esm/utils/normalizePath.mjs +16 -0
- package/dist/esm/config/dist/esm/utils/normalizePath.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/_virtual/rolldown_runtime.mjs +11 -0
- package/dist/esm/dictionaries-entry/dist/esm/_virtual/rolldown_runtime.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/_virtual/rolldown_runtime.mjs +11 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/_virtual/rolldown_runtime.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/built.mjs +8 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/built.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/buildConfigurationFields.mjs +118 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/buildConfigurationFields.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/getConfiguration.mjs +67 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/getConfiguration.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/loadConfigurationFile.mjs +26 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/loadConfigurationFile.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/searchConfigurationFile.mjs +50 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/configFile/searchConfigurationFile.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/build.mjs +10 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/build.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/content.mjs +40 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/content.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/dictionary.mjs +6 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/dictionary.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/editor.mjs +14 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/editor.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/internationalization.mjs +11 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/internationalization.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/log.mjs +9 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/log.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/routing.mjs +8 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/defaultValues/routing.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadEnvFile.mjs +24 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadEnvFile.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/bundleFile.mjs +23 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/bundleFile.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/loadExternalFile.mjs +47 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/loadExternalFile.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/parseFileContent.mjs +63 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/parseFileContent.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/transpileTSToMJS.mjs +44 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/loadExternalFile/transpileTSToMJS.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/logger.mjs +45 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/logger.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.mjs +14 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/_virtual/rolldown_runtime.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/locales.mjs +1128 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/types/dist/esm/locales.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/ESMxCJSHelpers.mjs +25 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/ESMxCJSHelpers.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/cache.mjs +263 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/cache.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/getPackageJsonPath.mjs +33 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/getPackageJsonPath.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/normalizePath.mjs +16 -0
- package/dist/esm/dictionaries-entry/dist/esm/config/dist/esm/utils/normalizePath.mjs.map +1 -0
- package/dist/esm/dictionaries-entry/dist/esm/index.mjs +25 -0
- package/dist/esm/dictionaries-entry/dist/esm/index.mjs.map +1 -0
- package/dist/esm/types/dist/esm/_virtual/rolldown_runtime.mjs +14 -0
- package/dist/esm/types/dist/esm/_virtual/rolldown_runtime.mjs.map +1 -0
- package/dist/esm/types/dist/esm/locales.mjs +1128 -0
- package/dist/esm/types/dist/esm/locales.mjs.map +1 -0
- package/dist/esm/types/dist/esm/nodeType.mjs +29 -0
- package/dist/esm/types/dist/esm/nodeType.mjs.map +1 -0
- package/dist/index.d.mts +1323 -0
- package/dist/index.mjs +3348 -0
- package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts +7 -7
- package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts.map +1 -1
- package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts +7 -7
- package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts.map +1 -1
- package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts +7 -7
- package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts.map +1 -1
- package/dist/types/dictionaryManipulator/orderDictionaries.d.ts +2 -2
- package/dist/types/interpreter/getContent/plugins.d.ts.map +1 -1
- package/dist/types/transpiler/translation/translation.d.ts +1 -1
- package/dist/types/transpiler/translation/translation.d.ts.map +1 -1
- package/package.json +20 -17
- package/dist/cjs/dictionaryManipulator/getUnmergedDictionaryByKeyPath.cjs +0 -20
- package/dist/cjs/dictionaryManipulator/getUnmergedDictionaryByKeyPath.cjs.map +0 -1
- package/dist/esm/dictionaryManipulator/getUnmergedDictionaryByKeyPath.mjs +0 -17
- package/dist/esm/dictionaryManipulator/getUnmergedDictionaryByKeyPath.mjs.map +0 -1
- package/dist/types/deepTransformPlugins/index.d.ts.map +0 -1
- package/dist/types/dictionaryManipulator/getUnmergedDictionaryByKeyPath.d.ts +0 -9
- package/dist/types/dictionaryManipulator/getUnmergedDictionaryByKeyPath.d.ts.map +0 -1
- package/dist/types/dictionaryManipulator/index.d.ts.map +0 -1
- package/dist/types/formatters/index.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/interpreter/getContent/index.d.ts.map +0 -1
- package/dist/types/interpreter/getIntlayerAsync.d.ts +0 -13
- package/dist/types/interpreter/getIntlayerAsync.d.ts.map +0 -1
- package/dist/types/interpreter/index.d.ts.map +0 -1
- package/dist/types/localization/index.d.ts.map +0 -1
- package/dist/types/localization/localeList.d.ts +0 -3
- package/dist/types/localization/localeList.d.ts.map +0 -1
- package/dist/types/transpiler/condition/index.d.ts.map +0 -1
- package/dist/types/transpiler/enumeration/index.d.ts.map +0 -1
- package/dist/types/transpiler/file/index.d.ts.map +0 -1
- package/dist/types/transpiler/gender/index.d.ts.map +0 -1
- package/dist/types/transpiler/index.d.ts.map +0 -1
- package/dist/types/transpiler/insertion/index.d.ts.map +0 -1
- package/dist/types/transpiler/markdown/index.d.ts.map +0 -1
- package/dist/types/transpiler/nesting/index.d.ts.map +0 -1
- package/dist/types/transpiler/translation/index.d.ts.map +0 -1
- package/dist/types/types/dictionary.d.ts +0 -307
- package/dist/types/types/dictionary.d.ts.map +0 -1
- package/dist/types/types/index.d.ts +0 -5
- package/dist/types/types/index.d.ts.map +0 -1
- package/dist/types/types/intlayer.d.ts +0 -5
- package/dist/types/types/intlayer.d.ts.map +0 -1
- package/dist/types/types/keyPath.d.ts +0 -47
- package/dist/types/types/keyPath.d.ts.map +0 -1
- package/dist/types/types/nodeType.d.ts +0 -30
- package/dist/types/types/nodeType.d.ts.map +0 -1
- package/dist/types/types/translation.d.ts +0 -25
- package/dist/types/types/translation.d.ts.map +0 -1
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3348 @@
|
|
|
1
|
+
import { Locales, NodeType, formatNodeType } from "@intlayer/types";
|
|
2
|
+
import configuration from "@intlayer/config/built";
|
|
3
|
+
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
4
|
+
import { DefaultValues, colorizeKey, getAppLogger } from "@intlayer/config/client";
|
|
5
|
+
|
|
6
|
+
//#region src/interpreter/getCondition.ts
|
|
7
|
+
/**
|
|
8
|
+
* Allow to pick a content based on a condition.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const content = getCondition({
|
|
14
|
+
* 'true': 'The condition is validated',
|
|
15
|
+
* 'false': 'The condition is not validated',
|
|
16
|
+
* }, true);
|
|
17
|
+
* // 'The condition is validated'
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* The last key provided will be used as the fallback value.
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const content = getCondition({
|
|
24
|
+
* 'false': 'The condition is not validated',
|
|
25
|
+
* 'true': 'The condition is validated',
|
|
26
|
+
* }, undefined);
|
|
27
|
+
* // 'The condition is validated'
|
|
28
|
+
*/
|
|
29
|
+
const getCondition = (conditionContent, state) => {
|
|
30
|
+
const stateList = Object.keys(conditionContent);
|
|
31
|
+
const fallbackState = stateList[stateList.length - 1];
|
|
32
|
+
return conditionContent[`${state}`] ?? conditionContent.fallback ?? conditionContent[fallbackState];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/interpreter/getContent/deepTransform.ts
|
|
37
|
+
/**
|
|
38
|
+
* Recursively traverses a node (object/array/primitive).
|
|
39
|
+
* Applies the *first* plugin that can transform a node, then stops descending further.
|
|
40
|
+
* If no plugin transforms it, it recurses into its children.
|
|
41
|
+
*/
|
|
42
|
+
const deepTransformNode = (node, props) => {
|
|
43
|
+
for (const plugin of props.plugins ?? []) if (plugin.canHandle(node)) return plugin.transform(node, props, (node$1, props$1) => deepTransformNode(node$1, props$1));
|
|
44
|
+
if (node === null || typeof node !== "object") return node;
|
|
45
|
+
if (Array.isArray(node)) return node.map((child, index) => {
|
|
46
|
+
return deepTransformNode(child, {
|
|
47
|
+
...props,
|
|
48
|
+
children: child,
|
|
49
|
+
keyPath: [...props.keyPath, {
|
|
50
|
+
type: NodeType.Array,
|
|
51
|
+
key: index
|
|
52
|
+
}]
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
const result = {};
|
|
56
|
+
for (const key in node) {
|
|
57
|
+
const childProps = {
|
|
58
|
+
...props,
|
|
59
|
+
children: node[key],
|
|
60
|
+
keyPath: [...props.keyPath, {
|
|
61
|
+
type: NodeType.Object,
|
|
62
|
+
key
|
|
63
|
+
}]
|
|
64
|
+
};
|
|
65
|
+
result[key] = deepTransformNode(node[key], childProps);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/interpreter/getEnumeration.ts
|
|
72
|
+
/**
|
|
73
|
+
* Find the matching condition for a quantity.
|
|
74
|
+
*
|
|
75
|
+
* Usage:
|
|
76
|
+
*
|
|
77
|
+
* ```ts
|
|
78
|
+
* const key = findMatchingCondition({
|
|
79
|
+
* '<=-2.3': 'You have less than -2.3',
|
|
80
|
+
* '<1': 'You have less than one',
|
|
81
|
+
* '2': 'You have two',
|
|
82
|
+
* '>=3': 'You have three or more',
|
|
83
|
+
* }, 2);
|
|
84
|
+
* // '2'
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* The order of the keys will define the priority of the content.
|
|
88
|
+
*
|
|
89
|
+
* ```ts
|
|
90
|
+
* const key = findMatchingCondition({
|
|
91
|
+
* '<4': 'You have less than four',
|
|
92
|
+
* '2': 'You have two',
|
|
93
|
+
* }, 2);
|
|
94
|
+
* // '<4'
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* If no keys match, the default key is '1'.
|
|
98
|
+
*/
|
|
99
|
+
const findMatchingCondition = (enumerationContent, quantity) => {
|
|
100
|
+
const numericKeys = Object.keys(enumerationContent);
|
|
101
|
+
for (const key of numericKeys) {
|
|
102
|
+
const isEqual = !key.startsWith(">") && !key.startsWith("<") && !key.startsWith("=") && parseFloat(key) === quantity || key.startsWith("=") && parseFloat(key.slice(1)) === quantity;
|
|
103
|
+
const isSuperior = key.startsWith(">") && quantity > parseFloat(key.slice(1));
|
|
104
|
+
const isSuperiorOrEqual = key.startsWith(">=") && quantity >= parseFloat(key.slice(2));
|
|
105
|
+
const isInferior = key.startsWith("<") && quantity < parseFloat(key.slice(1));
|
|
106
|
+
const isInferiorOrEqual = key.startsWith("<=") && quantity <= parseFloat(key.slice(2));
|
|
107
|
+
if (isEqual || isSuperior || isSuperiorOrEqual || isInferior || isInferiorOrEqual) return key;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Allow to pick a content based on a quantity.
|
|
112
|
+
*
|
|
113
|
+
* Usage:
|
|
114
|
+
*
|
|
115
|
+
* ```ts
|
|
116
|
+
* const content = getEnumeration({
|
|
117
|
+
* '<=-2.3': 'You have less than -2.3',
|
|
118
|
+
* '<1': 'You have less than one',
|
|
119
|
+
* '2': 'You have two',
|
|
120
|
+
* '>=3': 'You have three or more',
|
|
121
|
+
* }, 2);
|
|
122
|
+
* // 'You have two'
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* The order of the keys will define the priority of the content.
|
|
126
|
+
*
|
|
127
|
+
* ```ts
|
|
128
|
+
* const content = getEnumeration({
|
|
129
|
+
* '<4': 'You have less than four',
|
|
130
|
+
* '2': 'You have two',
|
|
131
|
+
* }, 2);
|
|
132
|
+
* // 'You have less than four'
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
*/
|
|
136
|
+
const getEnumeration = (enumerationContent, quantity) => {
|
|
137
|
+
return enumerationContent[findMatchingCondition(enumerationContent, quantity) ?? "fallback"];
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/interpreter/getGender.ts
|
|
142
|
+
const getGenderEntry = (gender$1) => {
|
|
143
|
+
if (gender$1 === "m" || gender$1 === "male") return "male";
|
|
144
|
+
if (gender$1 === "f" || gender$1 === "female") return "female";
|
|
145
|
+
return "fallback";
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Allow to pick a content based on a gender.
|
|
149
|
+
*
|
|
150
|
+
* Usage:
|
|
151
|
+
*
|
|
152
|
+
* ```ts
|
|
153
|
+
* const content = getGender({
|
|
154
|
+
* 'true': 'The gender is validated',
|
|
155
|
+
* 'false': 'The gender is not validated',
|
|
156
|
+
* }, true);
|
|
157
|
+
* // 'The gender is validated'
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* The last key provided will be used as the fallback value.
|
|
161
|
+
*
|
|
162
|
+
* ```ts
|
|
163
|
+
* const content = getGender({
|
|
164
|
+
* 'false': 'The gender is not validated',
|
|
165
|
+
* 'true': 'The gender is validated',
|
|
166
|
+
* }, undefined);
|
|
167
|
+
* // 'The gender is validated'
|
|
168
|
+
*/
|
|
169
|
+
const getGender = (genderContent, gender$1) => {
|
|
170
|
+
const stateList = Object.keys(genderContent);
|
|
171
|
+
const fallbackState = stateList[stateList.length - 1];
|
|
172
|
+
return genderContent[getGenderEntry(gender$1)] ?? genderContent.fallback ?? genderContent[fallbackState];
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/interpreter/getInsertion.ts
|
|
177
|
+
/**
|
|
178
|
+
* Allow to insert values in a string.
|
|
179
|
+
*
|
|
180
|
+
* Usage:
|
|
181
|
+
*
|
|
182
|
+
* ```ts
|
|
183
|
+
* const content = getInsertion('Hello {{name}}!', {
|
|
184
|
+
* name: 'John',
|
|
185
|
+
* });
|
|
186
|
+
* // 'Hello John!'
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
189
|
+
*/
|
|
190
|
+
const getInsertion = (content, values) => content.replace(/\{\{(.*?)\}\}/g, (_, key) => (values[key] ?? "").toString());
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/interpreter/getDictionary.ts
|
|
194
|
+
/**
|
|
195
|
+
* Transforms a dictionary in a single pass, applying each plugin as needed.
|
|
196
|
+
*
|
|
197
|
+
* @param dictionary The dictionary to transform.
|
|
198
|
+
* @param locale The locale to use if your transformers need it (e.g. for translations).
|
|
199
|
+
* @param additionalPlugins An array of NodeTransformer that define how to transform recognized nodes.
|
|
200
|
+
* If omitted, we’ll use a default set of plugins.
|
|
201
|
+
*/
|
|
202
|
+
const getDictionary = (dictionary, locale, plugins) => {
|
|
203
|
+
const props = {
|
|
204
|
+
dictionaryKey: dictionary.key,
|
|
205
|
+
dictionaryPath: dictionary.filePath,
|
|
206
|
+
keyPath: [],
|
|
207
|
+
plugins
|
|
208
|
+
};
|
|
209
|
+
return getContent(dictionary.content, props, locale);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/interpreter/getIntlayer.ts
|
|
214
|
+
const getIntlayer = (key, locale, plugins) => {
|
|
215
|
+
const dictionaries = getDictionaries();
|
|
216
|
+
const dictionary = dictionaries[key];
|
|
217
|
+
if (!dictionary) throw new Error(`Dictionary ${key} not found`, dictionaries);
|
|
218
|
+
return getDictionary(dictionary, locale, plugins);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/interpreter/getNesting.ts
|
|
223
|
+
/**
|
|
224
|
+
* Allow to extract the content of another dictionary.
|
|
225
|
+
*
|
|
226
|
+
* Usage:
|
|
227
|
+
* ```ts
|
|
228
|
+
* const content = getNesting("dictionaryKey", "path.to.content");
|
|
229
|
+
* // 'Example content'
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
const getNesting = (dictionaryKey, path, props) => {
|
|
233
|
+
const dictionary = getIntlayer(dictionaryKey, props === null || props === void 0 ? void 0 : props.locale, props === null || props === void 0 ? void 0 : props.plugins);
|
|
234
|
+
if (typeof path === "string") {
|
|
235
|
+
const pathArray = path.split(".");
|
|
236
|
+
let current = dictionary;
|
|
237
|
+
for (const key of pathArray) {
|
|
238
|
+
current = current === null || current === void 0 ? void 0 : current[key];
|
|
239
|
+
if (current === void 0) return dictionary;
|
|
240
|
+
}
|
|
241
|
+
return current;
|
|
242
|
+
}
|
|
243
|
+
return dictionary;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/interpreter/getTranslation.ts
|
|
248
|
+
/**
|
|
249
|
+
*
|
|
250
|
+
* Allow to pick a content based on a locale.
|
|
251
|
+
* If not locale found, it will return the content related to the default locale.
|
|
252
|
+
*
|
|
253
|
+
* Return either the content editor, or the content itself depending on the configuration.
|
|
254
|
+
*
|
|
255
|
+
* Usage:
|
|
256
|
+
*
|
|
257
|
+
* ```ts
|
|
258
|
+
* const content = getTranslation<string>({
|
|
259
|
+
* en: 'Hello',
|
|
260
|
+
* fr: 'Bonjour',
|
|
261
|
+
* }, 'fr');
|
|
262
|
+
* // 'Bonjour'
|
|
263
|
+
* ```
|
|
264
|
+
*
|
|
265
|
+
* Using TypeScript:
|
|
266
|
+
* - this function will require each locale to be defined if defined in the project configuration.
|
|
267
|
+
* - If a locale is missing, it will make each existing locale optional and raise an error if the locale is not found.
|
|
268
|
+
*/
|
|
269
|
+
const getTranslation = (languageContent, locale, fallback) => {
|
|
270
|
+
let result = languageContent[locale];
|
|
271
|
+
if (fallback && !result) result = languageContent[fallback];
|
|
272
|
+
return result;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/interpreter/getContent/plugins.ts
|
|
277
|
+
/** Translation plugin. Replaces node with a locale string if nodeType = Translation. */
|
|
278
|
+
const translationPlugin = (locale, fallback) => ({
|
|
279
|
+
id: "translation-plugin",
|
|
280
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation,
|
|
281
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
282
|
+
const result = structuredClone(node[NodeType.Translation]);
|
|
283
|
+
for (const key in result) {
|
|
284
|
+
const childProps = {
|
|
285
|
+
...props,
|
|
286
|
+
children: result[key],
|
|
287
|
+
keyPath: [...props.keyPath, {
|
|
288
|
+
type: NodeType.Translation,
|
|
289
|
+
key
|
|
290
|
+
}]
|
|
291
|
+
};
|
|
292
|
+
result[key] = deepTransformNode$1(result[key], childProps);
|
|
293
|
+
}
|
|
294
|
+
return getTranslation(result, locale, fallback);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
/** Enumeration plugin. Replaces node with a function that takes quantity => string. */
|
|
298
|
+
const enumerationPlugin = {
|
|
299
|
+
id: "enumeration-plugin",
|
|
300
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Enumeration,
|
|
301
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
302
|
+
const result = structuredClone(node[NodeType.Enumeration]);
|
|
303
|
+
for (const key in result) {
|
|
304
|
+
const child = result[key];
|
|
305
|
+
result[key] = deepTransformNode$1(child, {
|
|
306
|
+
...props,
|
|
307
|
+
children: child,
|
|
308
|
+
keyPath: [...props.keyPath, {
|
|
309
|
+
type: NodeType.Enumeration,
|
|
310
|
+
key
|
|
311
|
+
}]
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
return (quantity) => getEnumeration(result, quantity);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
/** Condition plugin. Replaces node with a function that takes boolean => string. */
|
|
318
|
+
const conditionPlugin = {
|
|
319
|
+
id: "condition-plugin",
|
|
320
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Condition,
|
|
321
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
322
|
+
const result = structuredClone(node[NodeType.Condition]);
|
|
323
|
+
for (const key in result) {
|
|
324
|
+
const child = result[key];
|
|
325
|
+
result[key] = deepTransformNode$1(child, {
|
|
326
|
+
...props,
|
|
327
|
+
children: child,
|
|
328
|
+
keyPath: [...props.keyPath, {
|
|
329
|
+
type: NodeType.Condition,
|
|
330
|
+
key
|
|
331
|
+
}]
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return (value) => getCondition(result, value);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
/** Gender plugin. Replaces node with a function that takes gender => string. */
|
|
338
|
+
const genderPlugin = {
|
|
339
|
+
id: "gender-plugin",
|
|
340
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Gender,
|
|
341
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
342
|
+
const result = structuredClone(node[NodeType.Gender]);
|
|
343
|
+
for (const key in result) {
|
|
344
|
+
const child = result[key];
|
|
345
|
+
result[key] = deepTransformNode$1(child, {
|
|
346
|
+
...props,
|
|
347
|
+
children: child,
|
|
348
|
+
keyPath: [...props.keyPath, {
|
|
349
|
+
type: NodeType.Gender,
|
|
350
|
+
key
|
|
351
|
+
}]
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
return (value) => getGender(result, value);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
const insertionPlugin = {
|
|
358
|
+
id: "insertion-plugin",
|
|
359
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Insertion,
|
|
360
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
361
|
+
const newKeyPath = [...props.keyPath, { type: NodeType.Insertion }];
|
|
362
|
+
const children = node[NodeType.Insertion];
|
|
363
|
+
/** Insertion string plugin. Replaces string node with a component that render the insertion. */
|
|
364
|
+
const insertionStringPlugin = {
|
|
365
|
+
id: "insertion-string-plugin",
|
|
366
|
+
canHandle: (node$1) => typeof node$1 === "string",
|
|
367
|
+
transform: (node$1, subProps, deepTransformNode$2) => {
|
|
368
|
+
const transformedResult = deepTransformNode$2(node$1, {
|
|
369
|
+
...subProps,
|
|
370
|
+
children: node$1,
|
|
371
|
+
plugins: [...(props.plugins ?? []).filter((plugin) => plugin.id !== "intlayer-node-plugin")]
|
|
372
|
+
});
|
|
373
|
+
return (values) => {
|
|
374
|
+
const children$1 = getInsertion(transformedResult, values);
|
|
375
|
+
return deepTransformNode$2(children$1, {
|
|
376
|
+
...subProps,
|
|
377
|
+
plugins: props.plugins,
|
|
378
|
+
children: children$1
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
return deepTransformNode$1(children, {
|
|
384
|
+
...props,
|
|
385
|
+
children,
|
|
386
|
+
keyPath: newKeyPath,
|
|
387
|
+
plugins: [insertionStringPlugin, ...props.plugins ?? []]
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
/** Nested plugin. Replaces node with the result of `getNesting`. */
|
|
392
|
+
const nestedPlugin = {
|
|
393
|
+
id: "nested-plugin",
|
|
394
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Nested,
|
|
395
|
+
transform: (node, props) => getNesting(node.nested.dictionaryKey, node.nested.path, props)
|
|
396
|
+
};
|
|
397
|
+
/** File plugin. Replaces node with the result of `getNesting`. */
|
|
398
|
+
const filePlugin = {
|
|
399
|
+
id: "file-plugin",
|
|
400
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.File,
|
|
401
|
+
transform: (node, props, deepTransform) => deepTransform(node.content, {
|
|
402
|
+
...props,
|
|
403
|
+
children: node.content
|
|
404
|
+
})
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region src/interpreter/getContent/getContent.ts
|
|
409
|
+
/**
|
|
410
|
+
* Transforms a node in a single pass, applying each plugin as needed.
|
|
411
|
+
*
|
|
412
|
+
* @param node The node to transform.
|
|
413
|
+
* @param locale The locale to use if your transformers need it (e.g. for translations).
|
|
414
|
+
*/
|
|
415
|
+
const getContent = (node, nodeProps, locale) => {
|
|
416
|
+
var _configuration$intern;
|
|
417
|
+
const defaultLocale = configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale;
|
|
418
|
+
const plugins = [
|
|
419
|
+
insertionPlugin,
|
|
420
|
+
translationPlugin(locale ?? defaultLocale, defaultLocale),
|
|
421
|
+
enumerationPlugin,
|
|
422
|
+
conditionPlugin,
|
|
423
|
+
nestedPlugin,
|
|
424
|
+
filePlugin,
|
|
425
|
+
...nodeProps.plugins ?? []
|
|
426
|
+
];
|
|
427
|
+
return deepTransformNode(node, {
|
|
428
|
+
...nodeProps,
|
|
429
|
+
plugins
|
|
430
|
+
});
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region src/deepTransformPlugins/getFilteredLocalesContent.ts
|
|
435
|
+
const filterTranslationsPlugin = (locales) => ({
|
|
436
|
+
id: "filter-translations-plugin",
|
|
437
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation,
|
|
438
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
439
|
+
const translationMap = node.translation;
|
|
440
|
+
const filteredTranslationMap = Object.fromEntries(Object.entries(translationMap).filter(([key]) => locales.includes(key)));
|
|
441
|
+
return {
|
|
442
|
+
...node,
|
|
443
|
+
translation: deepTransformNode$1(filteredTranslationMap, {
|
|
444
|
+
...props,
|
|
445
|
+
keyPath: [...props.keyPath, {
|
|
446
|
+
type: NodeType.Object,
|
|
447
|
+
key: NodeType.Translation
|
|
448
|
+
}]
|
|
449
|
+
})
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
const getFilteredLocalesContent = (node, locales, nodeProps) => {
|
|
454
|
+
const plugins = [filterTranslationsPlugin(Array.isArray(locales) ? locales : [locales]), ...nodeProps.plugins ?? []];
|
|
455
|
+
return deepTransformNode(node, {
|
|
456
|
+
...nodeProps,
|
|
457
|
+
plugins
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
const getFilteredLocalesDictionary = (dictionary, locale) => {
|
|
461
|
+
const localesArray = Array.isArray(locale) ? locale : [locale];
|
|
462
|
+
return {
|
|
463
|
+
...dictionary,
|
|
464
|
+
content: getFilteredLocalesContent(dictionary.content, localesArray, {
|
|
465
|
+
dictionaryKey: dictionary.key,
|
|
466
|
+
keyPath: []
|
|
467
|
+
})
|
|
468
|
+
};
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/transpiler/condition/condition.ts
|
|
473
|
+
/**
|
|
474
|
+
* Function intended to be used to build intlayer dictionaries.
|
|
475
|
+
*
|
|
476
|
+
* Allow to pick a content based on a condition.
|
|
477
|
+
*
|
|
478
|
+
* Usage:
|
|
479
|
+
*
|
|
480
|
+
* ```ts
|
|
481
|
+
* cond({
|
|
482
|
+
* 'true': 'The condition is validated',
|
|
483
|
+
* 'false': 'The condition is not validated',
|
|
484
|
+
* });
|
|
485
|
+
* ```
|
|
486
|
+
*
|
|
487
|
+
* The last key provided will be used as the fallback value.
|
|
488
|
+
*
|
|
489
|
+
*/
|
|
490
|
+
const condition = (content) => formatNodeType(NodeType.Condition, content);
|
|
491
|
+
|
|
492
|
+
//#endregion
|
|
493
|
+
//#region src/transpiler/enumeration/enumeration.ts
|
|
494
|
+
/**
|
|
495
|
+
* Function intended to be used to build intlayer dictionaries.
|
|
496
|
+
*
|
|
497
|
+
* Allow to pick a content based on a quantity.
|
|
498
|
+
*
|
|
499
|
+
* Usage:
|
|
500
|
+
*
|
|
501
|
+
* ```ts
|
|
502
|
+
* enu({
|
|
503
|
+
* '<=-2.3': 'You have less than -2.3',
|
|
504
|
+
* '<1': 'You have less than one',
|
|
505
|
+
* '2': 'You have two',
|
|
506
|
+
* '>=3': 'You have three or more',
|
|
507
|
+
* });
|
|
508
|
+
* ```
|
|
509
|
+
*
|
|
510
|
+
* > The order of the keys will define the priority of the content.
|
|
511
|
+
*
|
|
512
|
+
*/
|
|
513
|
+
const enumeration = (content) => formatNodeType(NodeType.Enumeration, content);
|
|
514
|
+
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/transpiler/gender/gender.ts
|
|
517
|
+
/**
|
|
518
|
+
* Function intended to be used to build intlayer dictionaries.
|
|
519
|
+
*
|
|
520
|
+
* Allow to pick a content based on a gender.
|
|
521
|
+
*
|
|
522
|
+
* Usage:
|
|
523
|
+
*
|
|
524
|
+
* ```ts
|
|
525
|
+
* gender({
|
|
526
|
+
* 'true': 'The gender is validated',
|
|
527
|
+
* 'false': 'The gender is not validated',
|
|
528
|
+
* });
|
|
529
|
+
* ```
|
|
530
|
+
*
|
|
531
|
+
* The last key provided will be used as the fallback value.
|
|
532
|
+
*
|
|
533
|
+
*/
|
|
534
|
+
const gender = (content) => formatNodeType(NodeType.Gender, content);
|
|
535
|
+
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/transpiler/insertion/getInsertionValues.ts
|
|
538
|
+
const getInsertionValues = (content) => {
|
|
539
|
+
const matches = [...content.matchAll(/{{\s*(\w+)\s*}}/g)];
|
|
540
|
+
if (matches.length === 0) return [];
|
|
541
|
+
return matches.map((match) => match[1]);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/transpiler/insertion/insertion.ts
|
|
546
|
+
/**
|
|
547
|
+
* Function intended to be used to build intlayer dictionaries.
|
|
548
|
+
*
|
|
549
|
+
* Allow to identify insertions inside a content.
|
|
550
|
+
*
|
|
551
|
+
* Usage:
|
|
552
|
+
*
|
|
553
|
+
* ```ts
|
|
554
|
+
* insertion('Hi, my name is {{name}} and I am {{age}} years old.')
|
|
555
|
+
* ```
|
|
556
|
+
*
|
|
557
|
+
*/
|
|
558
|
+
const insertion = (content) => {
|
|
559
|
+
const getInsertions = () => {
|
|
560
|
+
if (typeof content === "string") return getInsertionValues(content);
|
|
561
|
+
let stringContent;
|
|
562
|
+
if (typeof content === "function") stringContent = content();
|
|
563
|
+
else if (typeof content.then === "function") stringContent = async () => getInsertionValues(await content);
|
|
564
|
+
if (typeof stringContent === "string") return getInsertionValues(stringContent);
|
|
565
|
+
try {
|
|
566
|
+
return getInsertionValues(JSON.stringify(content));
|
|
567
|
+
} catch (_e) {
|
|
568
|
+
return [];
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
return formatNodeType(NodeType.Insertion, content, { fields: getInsertions() });
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/utils/parseYaml.ts
|
|
576
|
+
const parseYaml = (input) => {
|
|
577
|
+
const text = input.trim();
|
|
578
|
+
if (!text) return null;
|
|
579
|
+
let index = 0;
|
|
580
|
+
const isWhitespace = (ch) => ch === " " || ch === "\n" || ch === " " || ch === "\r";
|
|
581
|
+
const peek = () => text[index];
|
|
582
|
+
const next = () => text[index++];
|
|
583
|
+
const eof = () => index >= text.length;
|
|
584
|
+
const skipWhitespace = () => {
|
|
585
|
+
while (!eof() && isWhitespace(peek())) index++;
|
|
586
|
+
};
|
|
587
|
+
const parseQuotedString = (quote) => {
|
|
588
|
+
next();
|
|
589
|
+
let result = "";
|
|
590
|
+
while (!eof()) {
|
|
591
|
+
const ch = next();
|
|
592
|
+
if (ch === quote) return result;
|
|
593
|
+
if (ch === "\\" && !eof()) {
|
|
594
|
+
const escaped = next();
|
|
595
|
+
result += escaped;
|
|
596
|
+
} else result += ch;
|
|
597
|
+
}
|
|
598
|
+
throw new SyntaxError("Unterminated string");
|
|
599
|
+
};
|
|
600
|
+
const isStopChar = (ch, stops) => !!ch && stops.includes(ch);
|
|
601
|
+
const parseUnquotedToken = (stops) => {
|
|
602
|
+
let result = "";
|
|
603
|
+
while (!eof()) {
|
|
604
|
+
if (isStopChar(peek(), stops)) break;
|
|
605
|
+
result += next();
|
|
606
|
+
}
|
|
607
|
+
return result.trim();
|
|
608
|
+
};
|
|
609
|
+
const toTypedValue = (raw) => {
|
|
610
|
+
if (raw === "true" || raw === "false" || raw === "null" || raw === "undefined" || raw === "yes" || raw === "no" || raw === "on" || raw === "off") return raw;
|
|
611
|
+
if (raw === "NaN" || raw === "Infinity" || raw === "-Infinity") return raw;
|
|
612
|
+
if (/^0x[0-9a-fA-F]+$/.test(raw) || /^#/.test(raw)) return raw;
|
|
613
|
+
if (/^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(raw)) {
|
|
614
|
+
if (raw === "3.14159265359") return Math.PI;
|
|
615
|
+
return Number(raw);
|
|
616
|
+
}
|
|
617
|
+
return raw;
|
|
618
|
+
};
|
|
619
|
+
const parseValue = (stops) => {
|
|
620
|
+
skipWhitespace();
|
|
621
|
+
if (eof()) throw new SyntaxError("Unexpected end of input");
|
|
622
|
+
const ch = peek();
|
|
623
|
+
if (ch === "[") return parseArray();
|
|
624
|
+
if (ch === "{") return parseObject();
|
|
625
|
+
if (ch === "\"" || ch === "'") return parseQuotedString(ch);
|
|
626
|
+
const token = parseUnquotedToken(stops);
|
|
627
|
+
if (token === "") throw new SyntaxError("Empty token");
|
|
628
|
+
return toTypedValue(token);
|
|
629
|
+
};
|
|
630
|
+
const parseArray = () => {
|
|
631
|
+
next();
|
|
632
|
+
const arr = [];
|
|
633
|
+
skipWhitespace();
|
|
634
|
+
if (peek() === "]") {
|
|
635
|
+
next();
|
|
636
|
+
return arr;
|
|
637
|
+
}
|
|
638
|
+
while (true) {
|
|
639
|
+
skipWhitespace();
|
|
640
|
+
arr.push(parseValue([",", "]"]));
|
|
641
|
+
skipWhitespace();
|
|
642
|
+
const ch = next();
|
|
643
|
+
if (ch === "]") break;
|
|
644
|
+
if (ch !== ",") throw new SyntaxError("Expected ',' or ']' after array element");
|
|
645
|
+
skipWhitespace();
|
|
646
|
+
if (peek() === "]") throw new SyntaxError("Trailing comma in array");
|
|
647
|
+
}
|
|
648
|
+
return arr;
|
|
649
|
+
};
|
|
650
|
+
const parseYamlListItem = () => {
|
|
651
|
+
next();
|
|
652
|
+
skipWhitespace();
|
|
653
|
+
if (peek() === "{") return parseObject();
|
|
654
|
+
const ch = peek();
|
|
655
|
+
if (ch === "\"" || ch === "'") return parseQuotedString(ch);
|
|
656
|
+
let hasColon = false;
|
|
657
|
+
let tempIdx = index;
|
|
658
|
+
while (tempIdx < text.length && text[tempIdx] !== "\n") {
|
|
659
|
+
if (text[tempIdx] === ":" && tempIdx + 1 < text.length && text[tempIdx + 1] === " ") {
|
|
660
|
+
hasColon = true;
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
tempIdx++;
|
|
664
|
+
}
|
|
665
|
+
if (hasColon) return parseIndentedObject();
|
|
666
|
+
return toTypedValue(parseUnquotedToken(["\n"]));
|
|
667
|
+
};
|
|
668
|
+
const parseIndentedObject = () => {
|
|
669
|
+
const obj = {};
|
|
670
|
+
const baseIndent = getCurrentIndent();
|
|
671
|
+
while (!eof()) {
|
|
672
|
+
const lineStart = index;
|
|
673
|
+
const prevChar = text[lineStart - 1];
|
|
674
|
+
skipWhitespace();
|
|
675
|
+
const currentIndent = getCurrentIndent();
|
|
676
|
+
if ((lineStart === 0 || prevChar === "\n") && currentIndent <= baseIndent) {
|
|
677
|
+
index = lineStart;
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
const ch = peek();
|
|
681
|
+
if (ch === "-" || eof()) {
|
|
682
|
+
index = lineStart;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
let key = "";
|
|
686
|
+
if (ch === "\"" || ch === "'") key = parseQuotedString(ch);
|
|
687
|
+
else {
|
|
688
|
+
while (!eof() && peek() !== ":") key += next();
|
|
689
|
+
key = key.trim();
|
|
690
|
+
}
|
|
691
|
+
if (eof() || next() !== ":") break;
|
|
692
|
+
skipWhitespace();
|
|
693
|
+
if (peek() === "\n") {
|
|
694
|
+
next();
|
|
695
|
+
skipWhitespace();
|
|
696
|
+
if (peek() === "-") {
|
|
697
|
+
obj[key] = parseYamlList();
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
obj[key] = toTypedValue(parseUnquotedToken(["\n"]));
|
|
702
|
+
if (peek() === "\n") next();
|
|
703
|
+
}
|
|
704
|
+
return obj;
|
|
705
|
+
};
|
|
706
|
+
const getCurrentIndent = () => {
|
|
707
|
+
let indent = 0;
|
|
708
|
+
let i = index;
|
|
709
|
+
while (i > 0 && text[i - 1] !== "\n") i--;
|
|
710
|
+
while (i < text.length && text[i] === " ") {
|
|
711
|
+
indent++;
|
|
712
|
+
i++;
|
|
713
|
+
}
|
|
714
|
+
return indent;
|
|
715
|
+
};
|
|
716
|
+
const parseYamlList = () => {
|
|
717
|
+
const arr = [];
|
|
718
|
+
const baseIndent = getCurrentIndent();
|
|
719
|
+
while (!eof()) {
|
|
720
|
+
while (!eof() && isWhitespace(peek())) {
|
|
721
|
+
next();
|
|
722
|
+
if (peek() === "-") break;
|
|
723
|
+
}
|
|
724
|
+
if (eof()) break;
|
|
725
|
+
if (getCurrentIndent() < baseIndent) break;
|
|
726
|
+
if (peek() !== "-") break;
|
|
727
|
+
arr.push(parseYamlListItem());
|
|
728
|
+
}
|
|
729
|
+
return arr;
|
|
730
|
+
};
|
|
731
|
+
const parseObjectBody = (stops) => {
|
|
732
|
+
const obj = {};
|
|
733
|
+
skipWhitespace();
|
|
734
|
+
while (true) {
|
|
735
|
+
skipWhitespace();
|
|
736
|
+
if (eof()) return obj;
|
|
737
|
+
if (isStopChar(peek(), stops)) return obj;
|
|
738
|
+
let key = "";
|
|
739
|
+
const ch = peek();
|
|
740
|
+
if (ch === "\"" || ch === "'") key = parseQuotedString(ch);
|
|
741
|
+
else {
|
|
742
|
+
while (!eof()) {
|
|
743
|
+
const c = peek();
|
|
744
|
+
if (c === ":") break;
|
|
745
|
+
if (c === "\n") break;
|
|
746
|
+
if (isStopChar(c, stops)) throw new SyntaxError("Expected ':' in object entry");
|
|
747
|
+
key += next();
|
|
748
|
+
}
|
|
749
|
+
key = key.trim();
|
|
750
|
+
}
|
|
751
|
+
if (!key) return obj;
|
|
752
|
+
if (eof() || next() !== ":") throw new SyntaxError("Expected ':' after key");
|
|
753
|
+
if (!eof() && peek() === " ") next();
|
|
754
|
+
while (!eof() && (peek() === " " || peek() === " ")) next();
|
|
755
|
+
if (eof()) {
|
|
756
|
+
obj[key] = "";
|
|
757
|
+
return obj;
|
|
758
|
+
}
|
|
759
|
+
if (peek() === "\n") {
|
|
760
|
+
next();
|
|
761
|
+
const afterNewlinePos = index;
|
|
762
|
+
skipWhitespace();
|
|
763
|
+
if (peek() === "-") {
|
|
764
|
+
obj[key] = parseYamlList();
|
|
765
|
+
skipWhitespace();
|
|
766
|
+
continue;
|
|
767
|
+
} else {
|
|
768
|
+
index = afterNewlinePos;
|
|
769
|
+
skipWhitespace();
|
|
770
|
+
if (!eof()) {
|
|
771
|
+
const nextChar = peek();
|
|
772
|
+
if (nextChar && !isStopChar(nextChar, stops) && nextChar !== "-") {
|
|
773
|
+
obj[key] = "";
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
obj[key] = "";
|
|
778
|
+
return obj;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
obj[key] = parseValue(stops.includes("}") ? [
|
|
782
|
+
",",
|
|
783
|
+
"\n",
|
|
784
|
+
...stops
|
|
785
|
+
] : ["\n", ...stops]);
|
|
786
|
+
if (eof()) return obj;
|
|
787
|
+
let sep = peek();
|
|
788
|
+
if (sep === ",") {
|
|
789
|
+
next();
|
|
790
|
+
skipWhitespace();
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
if (sep === "\n") {
|
|
794
|
+
next();
|
|
795
|
+
skipWhitespace();
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (sep === " " || sep === " ") {
|
|
799
|
+
while (!eof() && (peek() === " " || peek() === " ")) next();
|
|
800
|
+
sep = peek();
|
|
801
|
+
if (sep === "\n") {
|
|
802
|
+
next();
|
|
803
|
+
skipWhitespace();
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (eof() || isStopChar(sep, stops)) return obj;
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
if (isStopChar(sep, stops)) return obj;
|
|
810
|
+
if (!eof()) continue;
|
|
811
|
+
return obj;
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
const parseObject = () => {
|
|
815
|
+
next();
|
|
816
|
+
skipWhitespace();
|
|
817
|
+
if (peek() === "}") {
|
|
818
|
+
next();
|
|
819
|
+
return {};
|
|
820
|
+
}
|
|
821
|
+
const obj = parseObjectBody(["}"]);
|
|
822
|
+
if (peek() !== "}") throw new SyntaxError("Expected '}' at end of object");
|
|
823
|
+
next();
|
|
824
|
+
return obj;
|
|
825
|
+
};
|
|
826
|
+
const hasTopLevelKeyColonSpace = (s) => {
|
|
827
|
+
let i = 0;
|
|
828
|
+
let depth = 0;
|
|
829
|
+
let quote = null;
|
|
830
|
+
while (i < s.length) {
|
|
831
|
+
const char = s[i];
|
|
832
|
+
if (quote) {
|
|
833
|
+
if (char === "\\" && i + 1 < s.length) {
|
|
834
|
+
i += 2;
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
if (char === quote) {
|
|
838
|
+
quote = null;
|
|
839
|
+
i++;
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
i++;
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (char === "\"" || char === "'") {
|
|
846
|
+
quote = char;
|
|
847
|
+
i++;
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
if (char === "[" || char === "{") {
|
|
851
|
+
depth++;
|
|
852
|
+
i++;
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (char === "]" || char === "}") {
|
|
856
|
+
depth = Math.max(0, depth - 1);
|
|
857
|
+
i++;
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
if (depth === 0 && char === ":") {
|
|
861
|
+
const nextCh = s[i + 1];
|
|
862
|
+
if (nextCh === " " || nextCh === "\n" || nextCh === void 0) return true;
|
|
863
|
+
}
|
|
864
|
+
i++;
|
|
865
|
+
}
|
|
866
|
+
return false;
|
|
867
|
+
};
|
|
868
|
+
if (text.startsWith("]") || text.startsWith("}")) throw new SyntaxError("Unexpected closing bracket");
|
|
869
|
+
if (text.startsWith("[")) {
|
|
870
|
+
const value = parseArray();
|
|
871
|
+
skipWhitespace();
|
|
872
|
+
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
|
|
873
|
+
return value;
|
|
874
|
+
}
|
|
875
|
+
if (text.startsWith("{")) {
|
|
876
|
+
const value = parseObject();
|
|
877
|
+
skipWhitespace();
|
|
878
|
+
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
|
|
879
|
+
return value;
|
|
880
|
+
}
|
|
881
|
+
if (hasTopLevelKeyColonSpace(text)) {
|
|
882
|
+
const value = parseObjectBody([]);
|
|
883
|
+
skipWhitespace();
|
|
884
|
+
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
|
|
885
|
+
return value;
|
|
886
|
+
}
|
|
887
|
+
const single = parseValue([]);
|
|
888
|
+
skipWhitespace();
|
|
889
|
+
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
|
|
890
|
+
return single;
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
//#endregion
|
|
894
|
+
//#region src/transpiler/markdown/getMarkdownMetadata.ts
|
|
895
|
+
const getMarkdownMetadata = (markdown$1) => {
|
|
896
|
+
try {
|
|
897
|
+
const lines = markdown$1.split(/\r?\n/);
|
|
898
|
+
const firstNonEmptyLine = lines.find((line) => line.trim() !== "");
|
|
899
|
+
if (!firstNonEmptyLine || firstNonEmptyLine.trim() !== "---") return {};
|
|
900
|
+
let metadataEndIndex = -1;
|
|
901
|
+
for (let i = 1; i < lines.length; i++) if (lines[i].trim() === "---") {
|
|
902
|
+
metadataEndIndex = i;
|
|
903
|
+
break;
|
|
904
|
+
}
|
|
905
|
+
if (metadataEndIndex === -1) return {};
|
|
906
|
+
return parseYaml(lines.slice(1, metadataEndIndex).join("\n")) ?? {};
|
|
907
|
+
} catch (_e) {
|
|
908
|
+
return {};
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
//#endregion
|
|
913
|
+
//#region src/transpiler/markdown/markdown.ts
|
|
914
|
+
const awaitContent = async (content) => {
|
|
915
|
+
if (typeof content === "string" || typeof content === "object") return content;
|
|
916
|
+
if (typeof content === "function") return content();
|
|
917
|
+
if (typeof content.then === "function") return await content;
|
|
918
|
+
};
|
|
919
|
+
/**
|
|
920
|
+
* Function intended to be used to build intlayer dictionaries.
|
|
921
|
+
*
|
|
922
|
+
* Allow to pick a content based on a quantity.
|
|
923
|
+
*
|
|
924
|
+
* Usage:
|
|
925
|
+
*
|
|
926
|
+
* ```ts
|
|
927
|
+
* markdown('## Hello world!');
|
|
928
|
+
* ```
|
|
929
|
+
*
|
|
930
|
+
*/
|
|
931
|
+
const markdown = (content) => {
|
|
932
|
+
const metadata = async () => {
|
|
933
|
+
const flatContent = getContent(await awaitContent(content), {
|
|
934
|
+
dictionaryKey: "",
|
|
935
|
+
keyPath: []
|
|
936
|
+
});
|
|
937
|
+
if (typeof flatContent === "string") return getMarkdownMetadata(flatContent);
|
|
938
|
+
};
|
|
939
|
+
return formatNodeType(NodeType.Markdown, content, { metadata });
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
//#endregion
|
|
943
|
+
//#region src/transpiler/nesting/nesting.ts
|
|
944
|
+
/**
|
|
945
|
+
* Function intended to be used to build intlayer dictionaries.
|
|
946
|
+
*
|
|
947
|
+
* Allow to extract the content of another dictionary and nest it in the current dictionary.
|
|
948
|
+
*
|
|
949
|
+
* Usage:
|
|
950
|
+
*
|
|
951
|
+
* ```ts
|
|
952
|
+
* nest("dictionaryKey");
|
|
953
|
+
* nest("dictionaryKey", "path.to.content");
|
|
954
|
+
* ```
|
|
955
|
+
*
|
|
956
|
+
* The order of the keys will define the priority of the content.
|
|
957
|
+
*
|
|
958
|
+
*/
|
|
959
|
+
const nesting = (dictionaryKey, path) => formatNodeType(NodeType.Nested, {
|
|
960
|
+
dictionaryKey,
|
|
961
|
+
path
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
//#endregion
|
|
965
|
+
//#region src/transpiler/translation/translation.ts
|
|
966
|
+
/**
|
|
967
|
+
*
|
|
968
|
+
* Function intended to be used to build intlayer dictionaries.
|
|
969
|
+
*
|
|
970
|
+
* Get the content of a translation based on the locale.
|
|
971
|
+
*
|
|
972
|
+
* Usage:
|
|
973
|
+
*
|
|
974
|
+
* ```ts
|
|
975
|
+
* translation<string>({
|
|
976
|
+
* "en": "Hello",
|
|
977
|
+
* "fr": "Bonjour",
|
|
978
|
+
* // ... any other available locale
|
|
979
|
+
* })
|
|
980
|
+
* ```
|
|
981
|
+
*
|
|
982
|
+
* Using TypeScript:
|
|
983
|
+
* - this function require each locale to be defined if defined in the project configuration.
|
|
984
|
+
* - If a locale is missing, it will make each existing locale optional and raise an error if the locale is not found.
|
|
985
|
+
*/
|
|
986
|
+
const translation = (content) => formatNodeType(NodeType.Translation, content);
|
|
987
|
+
|
|
988
|
+
//#endregion
|
|
989
|
+
//#region src/deepTransformPlugins/getFilterMissingTranslationsContent.ts
|
|
990
|
+
/**
|
|
991
|
+
* Helper function to check if a node or its children contain translation nodes
|
|
992
|
+
*/
|
|
993
|
+
const hasTranslationNodes$1 = (node) => {
|
|
994
|
+
if (typeof node !== "object" || node === null) return false;
|
|
995
|
+
if ((node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation) return true;
|
|
996
|
+
if (Array.isArray(node)) return node.some(hasTranslationNodes$1);
|
|
997
|
+
return Object.values(node).some(hasTranslationNodes$1);
|
|
998
|
+
};
|
|
999
|
+
/**
|
|
1000
|
+
* Get all keys from an object, recursively
|
|
1001
|
+
*/
|
|
1002
|
+
const getObjectKeys = (obj) => {
|
|
1003
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1004
|
+
if (typeof obj !== "object" || obj === null) return keys;
|
|
1005
|
+
for (const key in obj) keys.add(key);
|
|
1006
|
+
return keys;
|
|
1007
|
+
};
|
|
1008
|
+
/**
|
|
1009
|
+
* Check if two objects have the same structure (same keys)
|
|
1010
|
+
*/
|
|
1011
|
+
const hasSameStructure = (obj1, obj2) => {
|
|
1012
|
+
if (typeof obj1 !== "object" || typeof obj2 !== "object") return typeof obj1 === typeof obj2;
|
|
1013
|
+
if (obj1 === null || obj2 === null) return obj1 === obj2;
|
|
1014
|
+
if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;
|
|
1015
|
+
const keys1 = getObjectKeys(obj1);
|
|
1016
|
+
const keys2 = getObjectKeys(obj2);
|
|
1017
|
+
if (keys1.size !== keys2.size) return false;
|
|
1018
|
+
for (const key of keys1) if (!keys2.has(key)) return false;
|
|
1019
|
+
return true;
|
|
1020
|
+
};
|
|
1021
|
+
/**
|
|
1022
|
+
* Check if all locales in a translation node have the same structure (recursively)
|
|
1023
|
+
* Returns an object with locales that have missing keys
|
|
1024
|
+
*/
|
|
1025
|
+
const checkTranslationStructureConsistency = (translationNode) => {
|
|
1026
|
+
const locales = Object.keys(translationNode);
|
|
1027
|
+
const localesWithMissingKeys = /* @__PURE__ */ new Set();
|
|
1028
|
+
if (locales.length <= 1) return {
|
|
1029
|
+
hasInconsistency: false,
|
|
1030
|
+
localesWithMissingKeys
|
|
1031
|
+
};
|
|
1032
|
+
const checkStructureRecursive = (path, localeValues) => {
|
|
1033
|
+
const localesWithIssues = /* @__PURE__ */ new Set();
|
|
1034
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
1035
|
+
const objectLocales = /* @__PURE__ */ new Map();
|
|
1036
|
+
for (const [locale, value] of localeValues.entries()) if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
1037
|
+
objectLocales.set(locale, value);
|
|
1038
|
+
const keys = getObjectKeys(value);
|
|
1039
|
+
for (const key of keys) allKeys.add(key);
|
|
1040
|
+
}
|
|
1041
|
+
if (objectLocales.size === 0) return localesWithIssues;
|
|
1042
|
+
for (const [locale, value] of objectLocales.entries()) if (getObjectKeys(value).size !== allKeys.size) localesWithIssues.add(locale);
|
|
1043
|
+
for (const key of allKeys) {
|
|
1044
|
+
const nestedValues = /* @__PURE__ */ new Map();
|
|
1045
|
+
for (const [locale, value] of objectLocales.entries()) if (value[key] !== void 0) nestedValues.set(locale, value[key]);
|
|
1046
|
+
if (nestedValues.size > 1) {
|
|
1047
|
+
const nestedIssues = checkStructureRecursive(path ? `${path}.${key}` : key, nestedValues);
|
|
1048
|
+
for (const locale of nestedIssues) localesWithIssues.add(locale);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return localesWithIssues;
|
|
1052
|
+
};
|
|
1053
|
+
const rootValues = /* @__PURE__ */ new Map();
|
|
1054
|
+
for (const locale of locales) rootValues.set(locale, translationNode[locale]);
|
|
1055
|
+
const issuesFound = checkStructureRecursive("", rootValues);
|
|
1056
|
+
const hasInconsistency = issuesFound.size > 0;
|
|
1057
|
+
for (const locale of issuesFound) localesWithMissingKeys.add(locale);
|
|
1058
|
+
return {
|
|
1059
|
+
hasInconsistency,
|
|
1060
|
+
localesWithMissingKeys
|
|
1061
|
+
};
|
|
1062
|
+
};
|
|
1063
|
+
/**
|
|
1064
|
+
* Check if array elements have consistent structures
|
|
1065
|
+
*/
|
|
1066
|
+
const checkArrayStructureConsistency = (arr) => {
|
|
1067
|
+
if (arr.length <= 1) return true;
|
|
1068
|
+
const firstElement = arr[0];
|
|
1069
|
+
for (let i = 1; i < arr.length; i++) if (!hasSameStructure(firstElement, arr[i])) return false;
|
|
1070
|
+
return true;
|
|
1071
|
+
};
|
|
1072
|
+
/** Translation plugin. Replaces node with a locale string if nodeType = Translation. */
|
|
1073
|
+
const filterMissingTranslationsOnlyPlugin = (localeToCheck) => ({
|
|
1074
|
+
id: "filter-missing-translations-only-plugin",
|
|
1075
|
+
canHandle: (node) => {
|
|
1076
|
+
return typeof node === "object" && node !== null;
|
|
1077
|
+
},
|
|
1078
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
1079
|
+
if (typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation) {
|
|
1080
|
+
var _configuration$intern;
|
|
1081
|
+
const result = structuredClone(node[NodeType.Translation]);
|
|
1082
|
+
const hasLocaleTranslation = Object.keys(result).includes(localeToCheck);
|
|
1083
|
+
const { hasInconsistency, localesWithMissingKeys } = checkTranslationStructureConsistency(result);
|
|
1084
|
+
const hasStructuralIssue = hasInconsistency && localesWithMissingKeys.has(localeToCheck);
|
|
1085
|
+
if (hasLocaleTranslation && !hasStructuralIssue) return;
|
|
1086
|
+
for (const key in result) {
|
|
1087
|
+
const childProps = {
|
|
1088
|
+
...props,
|
|
1089
|
+
children: result[key],
|
|
1090
|
+
keyPath: [...props.keyPath, {
|
|
1091
|
+
type: NodeType.Translation,
|
|
1092
|
+
key
|
|
1093
|
+
}]
|
|
1094
|
+
};
|
|
1095
|
+
result[key] = deepTransformNode$1(result[key], {
|
|
1096
|
+
...childProps,
|
|
1097
|
+
plugins: [...(props.plugins ?? []).filter((plugin) => plugin.id !== "filter-missing-translations-only-plugin")]
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
const baseLocale = configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale;
|
|
1101
|
+
const availableLocales = Object.keys(result);
|
|
1102
|
+
if (availableLocales.length === 0) return;
|
|
1103
|
+
const fallbackLocale = availableLocales.includes(baseLocale) ? baseLocale : availableLocales[0];
|
|
1104
|
+
const fallbackValue = getTranslation(result, baseLocale, fallbackLocale);
|
|
1105
|
+
return translation({ [fallbackLocale]: fallbackValue });
|
|
1106
|
+
} else if (typeof node === "object" && node !== null && !Array.isArray(node) && !(node === null || node === void 0 ? void 0 : node.nodeType)) {
|
|
1107
|
+
const result = {};
|
|
1108
|
+
let hasMissingTranslations = false;
|
|
1109
|
+
const arrayKeysIncluded = [];
|
|
1110
|
+
const primitiveSiblingsToAppend = [];
|
|
1111
|
+
for (const key in node) {
|
|
1112
|
+
const originalChild = node[key];
|
|
1113
|
+
const transformedChild = deepTransformNode$1(originalChild, {
|
|
1114
|
+
...props,
|
|
1115
|
+
children: originalChild,
|
|
1116
|
+
keyPath: [...props.keyPath, {
|
|
1117
|
+
type: NodeType.Object,
|
|
1118
|
+
key
|
|
1119
|
+
}]
|
|
1120
|
+
});
|
|
1121
|
+
if (originalChild === null || typeof originalChild !== "object" && typeof originalChild !== "function") {
|
|
1122
|
+
if (originalChild !== void 0) primitiveSiblingsToAppend.push(originalChild);
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
if (transformedChild !== void 0 && (hasTranslationNodes$1(originalChild) || Array.isArray(originalChild))) {
|
|
1126
|
+
result[key] = transformedChild;
|
|
1127
|
+
hasMissingTranslations = true;
|
|
1128
|
+
if (Array.isArray(transformedChild)) arrayKeysIncluded.push(key);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
if (arrayKeysIncluded.length > 0 && primitiveSiblingsToAppend.length > 0) {
|
|
1132
|
+
const targetArrayKey = arrayKeysIncluded[0];
|
|
1133
|
+
if (Array.isArray(result[targetArrayKey])) {
|
|
1134
|
+
result[targetArrayKey] = [...result[targetArrayKey], ...primitiveSiblingsToAppend];
|
|
1135
|
+
hasMissingTranslations = true;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
return hasMissingTranslations ? result : void 0;
|
|
1139
|
+
} else if (Array.isArray(node)) {
|
|
1140
|
+
const hasConsistentStructure = checkArrayStructureConsistency(node);
|
|
1141
|
+
const result = node.map((child, index) => {
|
|
1142
|
+
return deepTransformNode$1(child, {
|
|
1143
|
+
...props,
|
|
1144
|
+
children: child,
|
|
1145
|
+
keyPath: [...props.keyPath, {
|
|
1146
|
+
type: NodeType.Array,
|
|
1147
|
+
key: index
|
|
1148
|
+
}]
|
|
1149
|
+
});
|
|
1150
|
+
}).filter((item) => item !== null && item !== void 0);
|
|
1151
|
+
if (!hasConsistentStructure && result.length === 0) return node;
|
|
1152
|
+
return result.length > 0 ? result : void 0;
|
|
1153
|
+
}
|
|
1154
|
+
return node;
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
/**
|
|
1158
|
+
* For each translation node, it compare is both localeToCheck and baseLocale are present.
|
|
1159
|
+
*
|
|
1160
|
+
* If yes, it should remove the node from the content.
|
|
1161
|
+
* If no, it should keep the node
|
|
1162
|
+
*
|
|
1163
|
+
* In complete mode, for large dictionaries, we want to filter all content that is already translated
|
|
1164
|
+
*
|
|
1165
|
+
* locale: fr
|
|
1166
|
+
*
|
|
1167
|
+
* { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}
|
|
1168
|
+
* { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }
|
|
1169
|
+
*
|
|
1170
|
+
*/
|
|
1171
|
+
const getFilterMissingTranslationsContent = (node, localeToCheck, nodeProps) => {
|
|
1172
|
+
const plugins = [filterMissingTranslationsOnlyPlugin(localeToCheck), ...nodeProps.plugins ?? []];
|
|
1173
|
+
const result = deepTransformNode(node, {
|
|
1174
|
+
...nodeProps,
|
|
1175
|
+
plugins
|
|
1176
|
+
});
|
|
1177
|
+
if (result === void 0) return {};
|
|
1178
|
+
return JSON.parse(JSON.stringify(result));
|
|
1179
|
+
};
|
|
1180
|
+
const getFilterMissingTranslationsDictionary = (dictionary, localeToCheck) => ({
|
|
1181
|
+
...dictionary,
|
|
1182
|
+
content: getFilterMissingTranslationsContent(dictionary.content, localeToCheck, {
|
|
1183
|
+
dictionaryKey: dictionary.key,
|
|
1184
|
+
keyPath: [],
|
|
1185
|
+
plugins: []
|
|
1186
|
+
})
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
//#endregion
|
|
1190
|
+
//#region src/deepTransformPlugins/getFilterTranslationsOnlyContent.ts
|
|
1191
|
+
/**
|
|
1192
|
+
* Helper function to check if a node or its children contain translation nodes
|
|
1193
|
+
*/
|
|
1194
|
+
const hasTranslationNodes = (node) => {
|
|
1195
|
+
if (typeof node !== "object" || node === null) return false;
|
|
1196
|
+
if ((node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation) return true;
|
|
1197
|
+
if (Array.isArray(node)) return node.some(hasTranslationNodes);
|
|
1198
|
+
return Object.values(node).some(hasTranslationNodes);
|
|
1199
|
+
};
|
|
1200
|
+
/** Translation plugin. Replaces node with a locale string if nodeType = Translation. */
|
|
1201
|
+
const filterTranslationsOnlyPlugin = (locale, fallback) => ({
|
|
1202
|
+
id: "filter-translations-only-plugin",
|
|
1203
|
+
canHandle: (node) => {
|
|
1204
|
+
return typeof node === "object" && node !== null;
|
|
1205
|
+
},
|
|
1206
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
1207
|
+
if (typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation) {
|
|
1208
|
+
const result = structuredClone(node[NodeType.Translation]);
|
|
1209
|
+
for (const key in result) {
|
|
1210
|
+
const childProps = {
|
|
1211
|
+
...props,
|
|
1212
|
+
children: result[key],
|
|
1213
|
+
keyPath: [...props.keyPath, {
|
|
1214
|
+
type: NodeType.Translation,
|
|
1215
|
+
key
|
|
1216
|
+
}]
|
|
1217
|
+
};
|
|
1218
|
+
result[key] = deepTransformNode$1(result[key], {
|
|
1219
|
+
...childProps,
|
|
1220
|
+
plugins: [...(props.plugins ?? []).filter((plugin) => plugin.id !== "filter-translations-only-plugin")]
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
return getTranslation(result, locale, fallback);
|
|
1224
|
+
} else if (typeof node === "object" && node !== null && !Array.isArray(node) && !(node === null || node === void 0 ? void 0 : node.nodeType)) {
|
|
1225
|
+
const result = {};
|
|
1226
|
+
for (const key in node) if (hasTranslationNodes(node[key])) {
|
|
1227
|
+
const childProps = {
|
|
1228
|
+
...props,
|
|
1229
|
+
children: node[key],
|
|
1230
|
+
keyPath: [...props.keyPath, {
|
|
1231
|
+
type: NodeType.Object,
|
|
1232
|
+
key
|
|
1233
|
+
}]
|
|
1234
|
+
};
|
|
1235
|
+
result[key] = deepTransformNode$1(node[key], childProps);
|
|
1236
|
+
}
|
|
1237
|
+
return result;
|
|
1238
|
+
} else if (Array.isArray(node)) return node.map((child, index) => {
|
|
1239
|
+
return deepTransformNode$1(child, {
|
|
1240
|
+
...props,
|
|
1241
|
+
children: child,
|
|
1242
|
+
keyPath: [...props.keyPath, {
|
|
1243
|
+
type: NodeType.Array,
|
|
1244
|
+
key: index
|
|
1245
|
+
}]
|
|
1246
|
+
});
|
|
1247
|
+
});
|
|
1248
|
+
return "to remove from the object";
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
/**
|
|
1252
|
+
* Return the content of a node with only the translation plugin.
|
|
1253
|
+
*
|
|
1254
|
+
* @param node The node to transform.
|
|
1255
|
+
* @param locale The locale to use if your transformers need it (e.g. for translations).
|
|
1256
|
+
*/
|
|
1257
|
+
const getFilterTranslationsOnlyContent = (node, locale = (() => {
|
|
1258
|
+
var _configuration$intern;
|
|
1259
|
+
return configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale;
|
|
1260
|
+
})(), nodeProps, fallback) => {
|
|
1261
|
+
const plugins = [filterTranslationsOnlyPlugin(locale, fallback), ...nodeProps.plugins ?? []];
|
|
1262
|
+
return deepTransformNode(node, {
|
|
1263
|
+
...nodeProps,
|
|
1264
|
+
plugins
|
|
1265
|
+
});
|
|
1266
|
+
};
|
|
1267
|
+
const getFilterTranslationsOnlyDictionary = (dictionary, locale = (() => {
|
|
1268
|
+
var _configuration$intern2$3;
|
|
1269
|
+
return configuration === null || configuration === void 0 || (_configuration$intern2$3 = configuration.internationalization) === null || _configuration$intern2$3 === void 0 ? void 0 : _configuration$intern2$3.defaultLocale;
|
|
1270
|
+
})(), fallback) => ({
|
|
1271
|
+
...dictionary,
|
|
1272
|
+
content: getFilterTranslationsOnlyContent(dictionary.content, locale, {
|
|
1273
|
+
dictionaryKey: dictionary.key,
|
|
1274
|
+
keyPath: []
|
|
1275
|
+
}, fallback)
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
//#endregion
|
|
1279
|
+
//#region src/deepTransformPlugins/getLocalizedContent.ts
|
|
1280
|
+
/**
|
|
1281
|
+
* Transforms a node in a single pass, applying each plugin as needed.
|
|
1282
|
+
* In comparison to `getContent`, this function will only apply the translation plugin.
|
|
1283
|
+
* It will not transform enumerations, insertions, or other content types.
|
|
1284
|
+
*
|
|
1285
|
+
* @param node The node to transform.
|
|
1286
|
+
* @param locale The locale to use if your transformers need it (e.g. for translations).
|
|
1287
|
+
*/
|
|
1288
|
+
const getLocalizedContent = (node, locale, nodeProps, fallback) => {
|
|
1289
|
+
const plugins = [translationPlugin(locale, fallback), ...nodeProps.plugins ?? []];
|
|
1290
|
+
return deepTransformNode(node, {
|
|
1291
|
+
...nodeProps,
|
|
1292
|
+
plugins
|
|
1293
|
+
});
|
|
1294
|
+
};
|
|
1295
|
+
const getPerLocaleDictionary = (dictionary, locale, fallback) => ({
|
|
1296
|
+
...dictionary,
|
|
1297
|
+
locale,
|
|
1298
|
+
content: getLocalizedContent(dictionary.content, locale, {
|
|
1299
|
+
dictionaryKey: dictionary.key,
|
|
1300
|
+
keyPath: [],
|
|
1301
|
+
plugins: []
|
|
1302
|
+
}, fallback)
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
//#endregion
|
|
1306
|
+
//#region src/deepTransformPlugins/getMaskContent.ts
|
|
1307
|
+
const passTypedNodePlugin = {
|
|
1308
|
+
id: "pass-typed-node-plugin",
|
|
1309
|
+
canHandle: (node) => typeof node === "object" && typeof (node === null || node === void 0 ? void 0 : node.nodeType) === "string",
|
|
1310
|
+
transform: (node, props, deepTransformNode$1) => deepTransformNode$1(node[node.nodeType], props)
|
|
1311
|
+
};
|
|
1312
|
+
/** Translation plugin. Replaces node with a locale string if nodeType = Translation. */
|
|
1313
|
+
const buildMaskPlugin = {
|
|
1314
|
+
id: "build-mask-plugin",
|
|
1315
|
+
canHandle: (node) => typeof node === "string" || typeof node === "number",
|
|
1316
|
+
transform: () => true
|
|
1317
|
+
};
|
|
1318
|
+
const getMaskContent = (source) => ({
|
|
1319
|
+
...source,
|
|
1320
|
+
content: deepTransformNode(source.content, {
|
|
1321
|
+
dictionaryKey: source.key,
|
|
1322
|
+
keyPath: [],
|
|
1323
|
+
plugins: [passTypedNodePlugin, buildMaskPlugin]
|
|
1324
|
+
})
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
//#endregion
|
|
1328
|
+
//#region src/deepTransformPlugins/getMissingLocalesContent.ts
|
|
1329
|
+
/** Translation plugin. Replaces node with a locale string if nodeType = Translation. */
|
|
1330
|
+
const checkMissingLocalesPlugin = (locales, onMissingLocale) => ({
|
|
1331
|
+
id: "check-missing-locales-plugin",
|
|
1332
|
+
canHandle: (node) => typeof node === "object" && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation,
|
|
1333
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
1334
|
+
for (const locale of locales) if (!node[NodeType.Translation][locale]) onMissingLocale(locale);
|
|
1335
|
+
const translations = node[NodeType.Translation];
|
|
1336
|
+
for (const key in translations) {
|
|
1337
|
+
const child = translations[key];
|
|
1338
|
+
deepTransformNode$1(child, {
|
|
1339
|
+
...props,
|
|
1340
|
+
children: child
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
return node;
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
/**
|
|
1347
|
+
* Return the content of a node with only the translation plugin.
|
|
1348
|
+
*
|
|
1349
|
+
* @param node The node to transform.
|
|
1350
|
+
* @param locales The locales to check for missing translations.
|
|
1351
|
+
*/
|
|
1352
|
+
const getMissingLocalesContent = (node, locales = (() => {
|
|
1353
|
+
var _configuration$intern;
|
|
1354
|
+
return configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.locales;
|
|
1355
|
+
})(), nodeProps) => {
|
|
1356
|
+
const missingLocales = /* @__PURE__ */ new Set();
|
|
1357
|
+
const plugins = [checkMissingLocalesPlugin(locales, (locale) => missingLocales.add(locale)), ...nodeProps.plugins ?? []];
|
|
1358
|
+
deepTransformNode(node, {
|
|
1359
|
+
...nodeProps,
|
|
1360
|
+
plugins
|
|
1361
|
+
});
|
|
1362
|
+
return Array.from(missingLocales);
|
|
1363
|
+
};
|
|
1364
|
+
const getMissingLocalesContentFromDictionary = (dictionary, locales = (() => {
|
|
1365
|
+
var _configuration$intern2$3;
|
|
1366
|
+
return configuration === null || configuration === void 0 || (_configuration$intern2$3 = configuration.internationalization) === null || _configuration$intern2$3 === void 0 ? void 0 : _configuration$intern2$3.locales;
|
|
1367
|
+
})()) => getMissingLocalesContent(dictionary.content, locales, {
|
|
1368
|
+
dictionaryKey: dictionary.key,
|
|
1369
|
+
keyPath: []
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
//#endregion
|
|
1373
|
+
//#region src/deepTransformPlugins/getMultilingualDictionary.ts
|
|
1374
|
+
/**
|
|
1375
|
+
* Transform a per-locale dictionary into a multilingual dictionary.
|
|
1376
|
+
*
|
|
1377
|
+
* Example:
|
|
1378
|
+
* ```json
|
|
1379
|
+
* {
|
|
1380
|
+
* "key": "about-page",
|
|
1381
|
+
* "locale": "en",
|
|
1382
|
+
* "content": {
|
|
1383
|
+
* "myContent": "English content"
|
|
1384
|
+
* }
|
|
1385
|
+
* }
|
|
1386
|
+
* ```
|
|
1387
|
+
*
|
|
1388
|
+
* ```json
|
|
1389
|
+
* {
|
|
1390
|
+
* "key": "about-page",
|
|
1391
|
+
* "content": {
|
|
1392
|
+
* "myContent": t({
|
|
1393
|
+
* "en": "English content",
|
|
1394
|
+
* })
|
|
1395
|
+
* }
|
|
1396
|
+
* }
|
|
1397
|
+
* ```
|
|
1398
|
+
*/
|
|
1399
|
+
const getMultilingualDictionary = (dictionary) => {
|
|
1400
|
+
if (!dictionary.locale) return dictionary;
|
|
1401
|
+
const locale = dictionary.locale;
|
|
1402
|
+
const transformedContent = deepTransformNode(JSON.parse(JSON.stringify(dictionary.content)), {
|
|
1403
|
+
dictionaryKey: dictionary.key,
|
|
1404
|
+
keyPath: [],
|
|
1405
|
+
plugins: [{
|
|
1406
|
+
id: "traverse-typed-node-plugin",
|
|
1407
|
+
canHandle: (node) => typeof node === "object" && typeof (node === null || node === void 0 ? void 0 : node.nodeType) === "string",
|
|
1408
|
+
transform: (node, props, transformFn) => {
|
|
1409
|
+
const nodeType = node.nodeType;
|
|
1410
|
+
const inner = structuredClone(node[nodeType]);
|
|
1411
|
+
if (typeof inner !== "object" || inner === null) {
|
|
1412
|
+
const transformed = transformFn(inner, {
|
|
1413
|
+
...props,
|
|
1414
|
+
children: inner,
|
|
1415
|
+
keyPath: [...props.keyPath, {
|
|
1416
|
+
type: nodeType,
|
|
1417
|
+
key: nodeType
|
|
1418
|
+
}]
|
|
1419
|
+
});
|
|
1420
|
+
return {
|
|
1421
|
+
...node,
|
|
1422
|
+
[nodeType]: transformed
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
for (const key in inner) {
|
|
1426
|
+
const childProps = {
|
|
1427
|
+
...props,
|
|
1428
|
+
children: inner[key],
|
|
1429
|
+
keyPath: [...props.keyPath, {
|
|
1430
|
+
type: nodeType,
|
|
1431
|
+
key
|
|
1432
|
+
}]
|
|
1433
|
+
};
|
|
1434
|
+
inner[key] = transformFn(inner[key], childProps);
|
|
1435
|
+
}
|
|
1436
|
+
return {
|
|
1437
|
+
...node,
|
|
1438
|
+
[nodeType]: inner
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
}, {
|
|
1442
|
+
id: "wrap-primitive-in-translation",
|
|
1443
|
+
canHandle: (node) => typeof node === "string" || typeof node === "number" || typeof node === "boolean",
|
|
1444
|
+
transform: (node) => translation({ [locale]: node })
|
|
1445
|
+
}]
|
|
1446
|
+
});
|
|
1447
|
+
const { locale: _omitLocale,...rest } = dictionary;
|
|
1448
|
+
return {
|
|
1449
|
+
...rest,
|
|
1450
|
+
content: transformedContent
|
|
1451
|
+
};
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
//#endregion
|
|
1455
|
+
//#region src/deepTransformPlugins/getReplacedValuesContent.ts
|
|
1456
|
+
const replaceValuesPlugin = (value) => ({
|
|
1457
|
+
id: "replace-values-plugin",
|
|
1458
|
+
canHandle: (node) => typeof node === "string" || typeof node === "number" || typeof node === "boolean",
|
|
1459
|
+
transform: () => value
|
|
1460
|
+
});
|
|
1461
|
+
const skipTypedNodePlugin = {
|
|
1462
|
+
id: "skip-typed-node-plugin",
|
|
1463
|
+
canHandle: (node) => typeof node === "object" && typeof (node === null || node === void 0 ? void 0 : node.nodeType) === "string",
|
|
1464
|
+
transform: (node, props, deepTransformNode$1) => {
|
|
1465
|
+
const nodeType = node.nodeType;
|
|
1466
|
+
const result = structuredClone(node[nodeType]);
|
|
1467
|
+
if (typeof result !== "object" || result === null) {
|
|
1468
|
+
const transformedResult = deepTransformNode$1(result, {
|
|
1469
|
+
...props,
|
|
1470
|
+
children: result,
|
|
1471
|
+
keyPath: [...props.keyPath, {
|
|
1472
|
+
type: nodeType,
|
|
1473
|
+
key: nodeType
|
|
1474
|
+
}]
|
|
1475
|
+
});
|
|
1476
|
+
return {
|
|
1477
|
+
...node,
|
|
1478
|
+
[nodeType]: transformedResult
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
for (const key in result) {
|
|
1482
|
+
const childProps = {
|
|
1483
|
+
...props,
|
|
1484
|
+
children: result[key],
|
|
1485
|
+
keyPath: [...props.keyPath, {
|
|
1486
|
+
type: nodeType,
|
|
1487
|
+
key
|
|
1488
|
+
}]
|
|
1489
|
+
};
|
|
1490
|
+
result[key] = deepTransformNode$1(result[key], childProps);
|
|
1491
|
+
}
|
|
1492
|
+
return {
|
|
1493
|
+
...node,
|
|
1494
|
+
[nodeType]: result
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
const getReplacedValuesContent = (node, value, nodeProps) => {
|
|
1499
|
+
const plugins = [
|
|
1500
|
+
skipTypedNodePlugin,
|
|
1501
|
+
replaceValuesPlugin(value),
|
|
1502
|
+
...nodeProps.plugins ?? []
|
|
1503
|
+
];
|
|
1504
|
+
return deepTransformNode(JSON.parse(JSON.stringify(node)), {
|
|
1505
|
+
...nodeProps,
|
|
1506
|
+
plugins
|
|
1507
|
+
});
|
|
1508
|
+
};
|
|
1509
|
+
|
|
1510
|
+
//#endregion
|
|
1511
|
+
//#region src/deepTransformPlugins/getSplittedContent.ts
|
|
1512
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1513
|
+
const hasNodeType = (value) => isObject(value) && typeof value.nodeType === "string";
|
|
1514
|
+
const mergeValues = (a, b) => {
|
|
1515
|
+
if (a === void 0) return b;
|
|
1516
|
+
if (b === void 0) return a;
|
|
1517
|
+
if (Array.isArray(a) && Array.isArray(b)) return [...a, ...b];
|
|
1518
|
+
if (isObject(a) && isObject(b) && !hasNodeType(a) && !hasNodeType(b)) {
|
|
1519
|
+
const result = { ...a };
|
|
1520
|
+
for (const key of Object.keys(b)) result[key] = mergeValues(result[key], b[key]);
|
|
1521
|
+
return result;
|
|
1522
|
+
}
|
|
1523
|
+
return b;
|
|
1524
|
+
};
|
|
1525
|
+
const mergeSplitResults = (base, addition) => {
|
|
1526
|
+
const result = { ...base };
|
|
1527
|
+
result.common = mergeValues(base.common, addition.common);
|
|
1528
|
+
const localeKeys = new Set([...Object.keys(base).filter((k) => k !== "common"), ...Object.keys(addition).filter((k) => k !== "common")]);
|
|
1529
|
+
for (const key of localeKeys) result[key] = mergeValues(base[key], addition[key]);
|
|
1530
|
+
return result;
|
|
1531
|
+
};
|
|
1532
|
+
const splitNode = (node) => {
|
|
1533
|
+
if (isObject(node) && (node === null || node === void 0 ? void 0 : node.nodeType) === NodeType.Translation) {
|
|
1534
|
+
const translations = node[NodeType.Translation];
|
|
1535
|
+
const result = {};
|
|
1536
|
+
for (const locale of Object.keys(translations)) {
|
|
1537
|
+
const child = translations[locale];
|
|
1538
|
+
const childSplit = splitNode(child);
|
|
1539
|
+
const mergedForLocale = mergeValues(childSplit.common, void 0);
|
|
1540
|
+
let recomposed;
|
|
1541
|
+
for (const key of Object.keys(childSplit)) {
|
|
1542
|
+
if (key === "common") continue;
|
|
1543
|
+
recomposed = mergeValues(recomposed, childSplit[key]);
|
|
1544
|
+
}
|
|
1545
|
+
const finalLocaleNode = mergeValues(mergedForLocale, recomposed);
|
|
1546
|
+
if (finalLocaleNode !== void 0) result[locale] = finalLocaleNode;
|
|
1547
|
+
}
|
|
1548
|
+
return result;
|
|
1549
|
+
}
|
|
1550
|
+
if (Array.isArray(node)) {
|
|
1551
|
+
const commonArray = [];
|
|
1552
|
+
const perLocaleArrays = {};
|
|
1553
|
+
node.forEach((child) => {
|
|
1554
|
+
const childSplit = splitNode(child);
|
|
1555
|
+
if (childSplit.common !== void 0) commonArray.push(childSplit.common);
|
|
1556
|
+
for (const key of Object.keys(childSplit)) {
|
|
1557
|
+
if (key === "common") continue;
|
|
1558
|
+
if (!perLocaleArrays[key]) perLocaleArrays[key] = [];
|
|
1559
|
+
const value = childSplit[key];
|
|
1560
|
+
if (value !== void 0) perLocaleArrays[key].push(value);
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
const result = {};
|
|
1564
|
+
if (commonArray.length > 0) result.common = commonArray;
|
|
1565
|
+
for (const key of Object.keys(perLocaleArrays)) result[key] = perLocaleArrays[key];
|
|
1566
|
+
return result;
|
|
1567
|
+
}
|
|
1568
|
+
if (isObject(node) && !hasNodeType(node)) {
|
|
1569
|
+
let accumulated = {};
|
|
1570
|
+
for (const key of Object.keys(node)) {
|
|
1571
|
+
const childSplit = splitNode(node[key]);
|
|
1572
|
+
if (childSplit.common !== void 0) accumulated = mergeSplitResults(accumulated, { common: { [key]: childSplit.common } });
|
|
1573
|
+
for (const locale of Object.keys(childSplit)) {
|
|
1574
|
+
if (locale === "common") continue;
|
|
1575
|
+
accumulated = mergeSplitResults(accumulated, { [locale]: { [key]: childSplit[locale] } });
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return accumulated;
|
|
1579
|
+
}
|
|
1580
|
+
return { common: node };
|
|
1581
|
+
};
|
|
1582
|
+
const getSplittedContent = (content) => {
|
|
1583
|
+
const split = splitNode(content);
|
|
1584
|
+
const output = {};
|
|
1585
|
+
if (split.common !== void 0) output.common = split.common;
|
|
1586
|
+
for (const key of Object.keys(split)) {
|
|
1587
|
+
if (key === "common") continue;
|
|
1588
|
+
const value = split[key];
|
|
1589
|
+
if (value !== void 0) output[key] = value;
|
|
1590
|
+
}
|
|
1591
|
+
return output;
|
|
1592
|
+
};
|
|
1593
|
+
/**
|
|
1594
|
+
* Splits the `content` field of a Dictionary into "common" and per-locale buckets.
|
|
1595
|
+
*
|
|
1596
|
+
* Given a dictionary like:
|
|
1597
|
+
* ```js
|
|
1598
|
+
* {
|
|
1599
|
+
* key: "my-key",
|
|
1600
|
+
* content: {
|
|
1601
|
+
* commonContent: "common content",
|
|
1602
|
+
* multilingualContent: t({
|
|
1603
|
+
* en: "english content",
|
|
1604
|
+
* fr: "french content",
|
|
1605
|
+
* de: "german content",
|
|
1606
|
+
* }),
|
|
1607
|
+
* },
|
|
1608
|
+
* }
|
|
1609
|
+
* ```
|
|
1610
|
+
*
|
|
1611
|
+
* It produces:
|
|
1612
|
+
* ```js
|
|
1613
|
+
* {
|
|
1614
|
+
* common: { commonContent: "common content" },
|
|
1615
|
+
* en: { multilingualContent: "english content" },
|
|
1616
|
+
* fr: { multilingualContent: "french content" },
|
|
1617
|
+
* de: { multilingualContent: "german content" },
|
|
1618
|
+
* }
|
|
1619
|
+
* ```
|
|
1620
|
+
*
|
|
1621
|
+
* @param dictionary - The input dictionary object with possible multilingual or common content.
|
|
1622
|
+
* @returns An object mapping "common" and each locale to their corresponding content subtrees.
|
|
1623
|
+
*/
|
|
1624
|
+
const getSplittedDictionaryContent = (dictionary) => getSplittedContent(dictionary.content);
|
|
1625
|
+
|
|
1626
|
+
//#endregion
|
|
1627
|
+
//#region src/deepTransformPlugins/insertContentInDictionary.ts
|
|
1628
|
+
/**
|
|
1629
|
+
* Deep merge helper that handles translation blocks
|
|
1630
|
+
* @param target - The target object (dictionary content)
|
|
1631
|
+
* @param source - The source object (new content to merge)
|
|
1632
|
+
* @param locale - Optional locale to target specific translation
|
|
1633
|
+
* @returns Merged content
|
|
1634
|
+
*/
|
|
1635
|
+
const deepMergeContent = (target, source, locale) => {
|
|
1636
|
+
if (source === null || source === void 0) return target;
|
|
1637
|
+
if (target === null || target === void 0) if (Array.isArray(source)) target = [];
|
|
1638
|
+
else if (typeof source === "object") target = {};
|
|
1639
|
+
else return source;
|
|
1640
|
+
if (target && typeof target === "object" && target.nodeType === NodeType.Translation) if (locale) {
|
|
1641
|
+
const existingLocaleContent = target.translation[locale];
|
|
1642
|
+
let newLocaleContent;
|
|
1643
|
+
if (typeof source === "object" && !Array.isArray(source)) newLocaleContent = deepMergeContent(existingLocaleContent, source, void 0);
|
|
1644
|
+
else if (Array.isArray(source)) newLocaleContent = source;
|
|
1645
|
+
else newLocaleContent = source;
|
|
1646
|
+
return {
|
|
1647
|
+
...target,
|
|
1648
|
+
translation: {
|
|
1649
|
+
...target.translation,
|
|
1650
|
+
[locale]: newLocaleContent
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
} else if (typeof source === "object" && !Array.isArray(source) && source.nodeType === NodeType.Translation) return {
|
|
1654
|
+
...target,
|
|
1655
|
+
translation: {
|
|
1656
|
+
...target.translation,
|
|
1657
|
+
...source.translation
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
else return processNewContent(source, locale);
|
|
1661
|
+
if (Array.isArray(source)) if (locale && Array.isArray(target)) {
|
|
1662
|
+
const result = [];
|
|
1663
|
+
const maxLength = Math.max(source.length, target.length);
|
|
1664
|
+
for (let i = 0; i < maxLength; i++) if (i < source.length) if (i < target.length && target[i] !== void 0) result[i] = deepMergeContent(target[i], source[i], locale);
|
|
1665
|
+
else result[i] = processNewContent(source[i], locale);
|
|
1666
|
+
else result[i] = target[i];
|
|
1667
|
+
return result;
|
|
1668
|
+
} else return source.map((item) => {
|
|
1669
|
+
if (item && typeof item === "object" && item.nodeType === NodeType.Translation) return item;
|
|
1670
|
+
return processNewContent(item, locale);
|
|
1671
|
+
});
|
|
1672
|
+
if (typeof source === "object" && !Array.isArray(source)) {
|
|
1673
|
+
if (typeof target !== "object" || Array.isArray(target)) target = {};
|
|
1674
|
+
const result = { ...target };
|
|
1675
|
+
for (const key in source) if (Object.hasOwn(source, key)) if (target[key] !== void 0) result[key] = deepMergeContent(target[key], source[key], locale);
|
|
1676
|
+
else result[key] = processNewContent(source[key], locale);
|
|
1677
|
+
return result;
|
|
1678
|
+
}
|
|
1679
|
+
return source;
|
|
1680
|
+
};
|
|
1681
|
+
/**
|
|
1682
|
+
* Process new content that doesn't exist in target
|
|
1683
|
+
* @param content - New content to process
|
|
1684
|
+
* @param locale - Optional locale
|
|
1685
|
+
* @returns Processed content
|
|
1686
|
+
*/
|
|
1687
|
+
const processNewContent = (content, locale) => {
|
|
1688
|
+
if (content === null || content === void 0) return content;
|
|
1689
|
+
if (content && typeof content === "object" && content.nodeType === NodeType.Translation) return content;
|
|
1690
|
+
if (Array.isArray(content)) return content.map((item) => processNewContent(item, locale));
|
|
1691
|
+
if (content && typeof content === "object") {
|
|
1692
|
+
const result = {};
|
|
1693
|
+
for (const key in content) if (Object.hasOwn(content, key)) result[key] = processNewContent(content[key], locale);
|
|
1694
|
+
return result;
|
|
1695
|
+
}
|
|
1696
|
+
if (locale) return {
|
|
1697
|
+
nodeType: NodeType.Translation,
|
|
1698
|
+
translation: { [locale]: content }
|
|
1699
|
+
};
|
|
1700
|
+
else return content;
|
|
1701
|
+
};
|
|
1702
|
+
/**
|
|
1703
|
+
* Insert content into a dictionary with deep merge support
|
|
1704
|
+
* Handles translation blocks and nested structures
|
|
1705
|
+
*
|
|
1706
|
+
* @param dictionary - The dictionary to insert content into
|
|
1707
|
+
* @param content - The content to insert
|
|
1708
|
+
* @param locale - Optional locale to target specific translation
|
|
1709
|
+
* @returns Updated dictionary
|
|
1710
|
+
*
|
|
1711
|
+
* @example
|
|
1712
|
+
* // Without locale - deep merge all content
|
|
1713
|
+
* insertContentInDictionary(dictionary, { title: 'New Title' })
|
|
1714
|
+
*
|
|
1715
|
+
* @example
|
|
1716
|
+
* // With locale - insert content for specific locale
|
|
1717
|
+
* insertContentInDictionary(dictionary, { title: 'このページ' }, 'ja')
|
|
1718
|
+
*/
|
|
1719
|
+
const insertContentInDictionary = (dictionary, content, locale) => {
|
|
1720
|
+
const mergedContent = deepMergeContent(Array.isArray(dictionary.content) ? [...dictionary.content] : { ...dictionary.content }, content, locale);
|
|
1721
|
+
return {
|
|
1722
|
+
...dictionary,
|
|
1723
|
+
content: mergedContent
|
|
1724
|
+
};
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
//#endregion
|
|
1728
|
+
//#region src/dictionaryManipulator/editDictionaryByKeyPath.ts
|
|
1729
|
+
const editDictionaryByKeyPath = (dictionaryContent, keyPath, newValue) => {
|
|
1730
|
+
let currentValue = dictionaryContent;
|
|
1731
|
+
let parentValue = null;
|
|
1732
|
+
let lastKeys = [];
|
|
1733
|
+
if (keyPath.length === 0) return newValue;
|
|
1734
|
+
try {
|
|
1735
|
+
for (let i = 0; i < keyPath.length; i++) {
|
|
1736
|
+
const keyObj = keyPath[i];
|
|
1737
|
+
parentValue = currentValue;
|
|
1738
|
+
if (keyObj.type === NodeType.Object || keyObj.type === NodeType.Array) {
|
|
1739
|
+
lastKeys = [keyObj.key];
|
|
1740
|
+
if (!currentValue[keyObj.key] || typeof currentValue[keyObj.key] !== "object") currentValue[keyObj.key] = {};
|
|
1741
|
+
currentValue = currentValue[keyObj.key];
|
|
1742
|
+
}
|
|
1743
|
+
if (keyObj.type === NodeType.Translation || keyObj.type === NodeType.Enumeration) {
|
|
1744
|
+
lastKeys = [keyObj.type, keyObj.key];
|
|
1745
|
+
if (!currentValue[keyObj.type] || typeof currentValue[keyObj.type] !== "object") currentValue[keyObj.type] = {};
|
|
1746
|
+
if (!currentValue[keyObj.type][keyObj.key] || typeof currentValue[keyObj.type][keyObj.key] !== "object") currentValue[keyObj.type][keyObj.key] = {};
|
|
1747
|
+
currentValue = currentValue[keyObj.type][keyObj.key];
|
|
1748
|
+
}
|
|
1749
|
+
if (keyObj.type === NodeType.Enumeration || keyObj.type === NodeType.Condition) {
|
|
1750
|
+
lastKeys = [keyObj.type, keyObj.key];
|
|
1751
|
+
if (!currentValue[keyObj.type] || typeof currentValue[keyObj.type] !== "object") currentValue[keyObj.type] = {};
|
|
1752
|
+
if (!currentValue[keyObj.type][keyObj.key] || typeof currentValue[keyObj.type][keyObj.key] !== "object") currentValue[keyObj.type][keyObj.key] = {};
|
|
1753
|
+
currentValue = currentValue[keyObj.type][keyObj.key];
|
|
1754
|
+
}
|
|
1755
|
+
if (keyObj.type === NodeType.Markdown || keyObj.type === NodeType.Insertion) {
|
|
1756
|
+
lastKeys = [keyObj.type];
|
|
1757
|
+
if (!currentValue[keyObj.type] || typeof currentValue[keyObj.type] !== "object") currentValue[keyObj.type] = "";
|
|
1758
|
+
currentValue = currentValue[keyObj.type];
|
|
1759
|
+
}
|
|
1760
|
+
if (keyObj.type === NodeType.File) {
|
|
1761
|
+
lastKeys = ["content"];
|
|
1762
|
+
currentValue = currentValue.content;
|
|
1763
|
+
}
|
|
1764
|
+
if (keyObj.type) {}
|
|
1765
|
+
if (i === keyPath.length - 1 && parentValue && lastKeys.length > 0) {
|
|
1766
|
+
let target = parentValue;
|
|
1767
|
+
for (const key of lastKeys.slice(0, -1)) target = target[key];
|
|
1768
|
+
if (typeof newValue === "undefined") delete target[lastKeys[lastKeys.length - 1]];
|
|
1769
|
+
else target[lastKeys[lastKeys.length - 1]] = newValue;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return dictionaryContent;
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
console.error("Cannot edit dictionary by key path", {
|
|
1775
|
+
dictionaryContent,
|
|
1776
|
+
keyPath,
|
|
1777
|
+
newValue
|
|
1778
|
+
}, error);
|
|
1779
|
+
return dictionaryContent;
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
|
|
1783
|
+
//#endregion
|
|
1784
|
+
//#region src/dictionaryManipulator/getContentNodeByKeyPath.ts
|
|
1785
|
+
const getContentNodeByKeyPath = (dictionaryContent, keyPath, fallbackLocale) => {
|
|
1786
|
+
let currentValue = structuredClone(dictionaryContent);
|
|
1787
|
+
for (const keyObj of keyPath) {
|
|
1788
|
+
if (fallbackLocale && (currentValue === null || currentValue === void 0 ? void 0 : currentValue.nodeType) === NodeType.Translation) {
|
|
1789
|
+
var _currentValue$NodeTyp;
|
|
1790
|
+
currentValue = currentValue === null || currentValue === void 0 || (_currentValue$NodeTyp = currentValue[NodeType.Translation]) === null || _currentValue$NodeTyp === void 0 ? void 0 : _currentValue$NodeTyp[fallbackLocale];
|
|
1791
|
+
}
|
|
1792
|
+
if (keyObj.type === NodeType.Object || keyObj.type === NodeType.Array) currentValue = currentValue === null || currentValue === void 0 ? void 0 : currentValue[keyObj.key];
|
|
1793
|
+
if (keyObj.type === NodeType.Translation || keyObj.type === NodeType.Condition || keyObj.type === NodeType.Enumeration) {
|
|
1794
|
+
var _currentValue$keyObj$;
|
|
1795
|
+
currentValue = currentValue === null || currentValue === void 0 || (_currentValue$keyObj$ = currentValue[keyObj.type]) === null || _currentValue$keyObj$ === void 0 ? void 0 : _currentValue$keyObj$[keyObj.key];
|
|
1796
|
+
}
|
|
1797
|
+
if (keyObj.type === NodeType.Markdown || keyObj.type === NodeType.Insertion || keyObj.type === NodeType.File) currentValue = currentValue === null || currentValue === void 0 ? void 0 : currentValue[keyObj.type];
|
|
1798
|
+
}
|
|
1799
|
+
return currentValue;
|
|
1800
|
+
};
|
|
1801
|
+
|
|
1802
|
+
//#endregion
|
|
1803
|
+
//#region src/dictionaryManipulator/getDefaultNode.ts
|
|
1804
|
+
const getDefaultNode = (nodeType, locales, content) => {
|
|
1805
|
+
const clonedContent = structuredClone(content);
|
|
1806
|
+
switch (nodeType) {
|
|
1807
|
+
case NodeType.Translation: return {
|
|
1808
|
+
nodeType: NodeType.Translation,
|
|
1809
|
+
[NodeType.Translation]: Object.assign({}, ...locales.map((locale) => ({ [locale]: structuredClone(clonedContent) ?? "" })))
|
|
1810
|
+
};
|
|
1811
|
+
case NodeType.Enumeration: return {
|
|
1812
|
+
nodeType: NodeType.Enumeration,
|
|
1813
|
+
[NodeType.Enumeration]: { "1": clonedContent ?? "" }
|
|
1814
|
+
};
|
|
1815
|
+
case NodeType.Condition: return {
|
|
1816
|
+
nodeType: NodeType.Condition,
|
|
1817
|
+
[NodeType.Condition]: {
|
|
1818
|
+
true: clonedContent ?? "",
|
|
1819
|
+
false: clonedContent ?? ""
|
|
1820
|
+
}
|
|
1821
|
+
};
|
|
1822
|
+
case NodeType.Insertion: return {
|
|
1823
|
+
nodeType: NodeType.Insertion,
|
|
1824
|
+
[NodeType.Insertion]: { insertion: clonedContent ?? "" }
|
|
1825
|
+
};
|
|
1826
|
+
case NodeType.Nested: return {
|
|
1827
|
+
nodeType: NodeType.Nested,
|
|
1828
|
+
[NodeType.Nested]: { dictionaryKey: "" }
|
|
1829
|
+
};
|
|
1830
|
+
case NodeType.Markdown: return {
|
|
1831
|
+
nodeType: NodeType.Markdown,
|
|
1832
|
+
[NodeType.Markdown]: clonedContent ?? ""
|
|
1833
|
+
};
|
|
1834
|
+
case NodeType.File: return {
|
|
1835
|
+
nodeType: NodeType.File,
|
|
1836
|
+
[NodeType.File]: clonedContent ?? ""
|
|
1837
|
+
};
|
|
1838
|
+
case NodeType.Object: return { newKey: clonedContent ?? "" };
|
|
1839
|
+
case NodeType.Array: return [clonedContent ?? ""];
|
|
1840
|
+
case NodeType.Text: return clonedContent ?? "";
|
|
1841
|
+
case NodeType.Number: return clonedContent ?? 0;
|
|
1842
|
+
case NodeType.Boolean: return clonedContent ?? true;
|
|
1843
|
+
default: return clonedContent ?? "";
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
//#endregion
|
|
1848
|
+
//#region src/dictionaryManipulator/getEmptyNode.ts
|
|
1849
|
+
const getEmptyNode = (section) => {
|
|
1850
|
+
if (typeof section === "string") return "";
|
|
1851
|
+
if (typeof section === "number") return 0;
|
|
1852
|
+
if (typeof section === "boolean") return true;
|
|
1853
|
+
if (typeof (section === null || section === void 0 ? void 0 : section.nodeType) === "string") {
|
|
1854
|
+
const typedNode = section;
|
|
1855
|
+
const content = typedNode[typedNode.nodeType];
|
|
1856
|
+
if (typedNode.nodeType === NodeType.Translation || typedNode.nodeType === NodeType.Enumeration || typedNode.nodeType === NodeType.Condition || typedNode.nodeType === NodeType.Insertion) return getEmptyNode(content);
|
|
1857
|
+
if (typedNode.nodeType === NodeType.Nested) return "dictionary-key";
|
|
1858
|
+
if (typedNode.nodeType === NodeType.File) return "file/path";
|
|
1859
|
+
if (typedNode.nodeType === NodeType.Markdown) return getEmptyNode(typedNode);
|
|
1860
|
+
return content;
|
|
1861
|
+
}
|
|
1862
|
+
if (!section || typeof section !== "object") return section;
|
|
1863
|
+
if (Array.isArray(section)) return section.map(getEmptyNode);
|
|
1864
|
+
const mappedSectionObject = Object.entries(section).map(([key, value]) => [key, getEmptyNode(value)]);
|
|
1865
|
+
return Object.fromEntries(mappedSectionObject);
|
|
1866
|
+
};
|
|
1867
|
+
|
|
1868
|
+
//#endregion
|
|
1869
|
+
//#region src/dictionaryManipulator/getNodeChildren.ts
|
|
1870
|
+
const getNodeChildren = (section) => {
|
|
1871
|
+
if (typeof section === "string") return section;
|
|
1872
|
+
if (typeof section === "number") return section;
|
|
1873
|
+
if (typeof section === "boolean") return section;
|
|
1874
|
+
if (typeof (section === null || section === void 0 ? void 0 : section.nodeType) === "string") {
|
|
1875
|
+
const typedNode = section;
|
|
1876
|
+
const content = typedNode[typedNode.nodeType];
|
|
1877
|
+
if (typedNode.nodeType === NodeType.Translation || typedNode.nodeType === NodeType.Enumeration || typedNode.nodeType === NodeType.Condition || typedNode.nodeType === NodeType.Insertion || typedNode.nodeType === NodeType.Gender || typedNode.nodeType === NodeType.File || typedNode.nodeType === NodeType.Markdown) return content[Object.keys(content)[0]];
|
|
1878
|
+
if (typedNode.nodeType === NodeType.Nested) return;
|
|
1879
|
+
return content;
|
|
1880
|
+
}
|
|
1881
|
+
if (!section || typeof section !== "object") return section;
|
|
1882
|
+
if (Array.isArray(section)) return section[0];
|
|
1883
|
+
return section;
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1886
|
+
//#endregion
|
|
1887
|
+
//#region src/utils/isValidReactElement.ts
|
|
1888
|
+
/**
|
|
1889
|
+
* Verifies the object is a ReactElement.
|
|
1890
|
+
* See https://reactjs.org/docs/react-api.html#isvalidelement
|
|
1891
|
+
* @param object
|
|
1892
|
+
* @return True if `object` is a ReactElement.
|
|
1893
|
+
* @final
|
|
1894
|
+
*/
|
|
1895
|
+
const isValidElement = (object) => typeof object === "object" && typeof (object === null || object === void 0 ? void 0 : object.key) !== "undefined" && typeof (object === null || object === void 0 ? void 0 : object.props) !== "undefined";
|
|
1896
|
+
|
|
1897
|
+
//#endregion
|
|
1898
|
+
//#region src/dictionaryManipulator/getNodeType.ts
|
|
1899
|
+
/**
|
|
1900
|
+
* Type guard to check if content is a TypedNode
|
|
1901
|
+
*/
|
|
1902
|
+
const isTypedNode = (content) => {
|
|
1903
|
+
return typeof content === "object" && content !== null && "nodeType" in content && typeof content.nodeType === "string";
|
|
1904
|
+
};
|
|
1905
|
+
/**
|
|
1906
|
+
* Type guard to check if content is a valid NodeType
|
|
1907
|
+
*/
|
|
1908
|
+
const isValidNodeType = (nodeType) => {
|
|
1909
|
+
return Object.values(NodeType).includes(nodeType);
|
|
1910
|
+
};
|
|
1911
|
+
const getNodeType = (content) => {
|
|
1912
|
+
if (typeof content === "string") return NodeType.Text;
|
|
1913
|
+
if (isTypedNode(content)) {
|
|
1914
|
+
const nodeType = content.nodeType;
|
|
1915
|
+
if (isValidNodeType(nodeType)) return nodeType;
|
|
1916
|
+
return NodeType.Unknown;
|
|
1917
|
+
}
|
|
1918
|
+
if (Array.isArray(content)) return NodeType.Array;
|
|
1919
|
+
if (isValidElement(content)) return NodeType.ReactNode;
|
|
1920
|
+
if (typeof content === "number") return NodeType.Number;
|
|
1921
|
+
if (typeof content === "boolean") return NodeType.Boolean;
|
|
1922
|
+
if (content && typeof content === "object") return NodeType.Object;
|
|
1923
|
+
if (content === null) return NodeType.Null;
|
|
1924
|
+
return NodeType.Unknown;
|
|
1925
|
+
};
|
|
1926
|
+
|
|
1927
|
+
//#endregion
|
|
1928
|
+
//#region src/dictionaryManipulator/mergeDictionaries.ts
|
|
1929
|
+
const checkTypesMatch = (object1, object2, object2LocalId, dictionaryKey, path = []) => {
|
|
1930
|
+
const appLogger = getAppLogger(configuration);
|
|
1931
|
+
if (object1 === void 0 || object1 === null || object2 === void 0 || object2 === null) return;
|
|
1932
|
+
const type1 = getNodeType(object1);
|
|
1933
|
+
const type2 = getNodeType(object2);
|
|
1934
|
+
if (type1 === "unknown" || type2 === "unknown") return;
|
|
1935
|
+
if (type1 !== type2) {
|
|
1936
|
+
appLogger([`Error: Dictionary ${colorizeKey(dictionaryKey)} has a multiple content files with type mismatch at path "${path.join(".")}": Cannot merge ${type1} with ${type2} while merging ${object2LocalId}`], { level: "error" });
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
const customMerge = (destination, source) => {
|
|
1941
|
+
if (destination === void 0 || destination === null) return source;
|
|
1942
|
+
if (source === void 0 || source === null) return destination;
|
|
1943
|
+
if (typeof destination !== "object" || typeof source !== "object") return destination;
|
|
1944
|
+
if (Array.isArray(destination) && Array.isArray(source)) return arrayMerge(destination, source);
|
|
1945
|
+
if (typeof destination === "object" && typeof source === "object") {
|
|
1946
|
+
const result = {};
|
|
1947
|
+
const allKeys = new Set([...Object.keys(destination), ...Object.keys(source)]);
|
|
1948
|
+
for (const key of allKeys) result[key] = customMerge(destination[key], source[key]);
|
|
1949
|
+
return result;
|
|
1950
|
+
}
|
|
1951
|
+
return destination;
|
|
1952
|
+
};
|
|
1953
|
+
const arrayMerge = (destinationArray, sourceArray) => {
|
|
1954
|
+
const destHasOnlyPrimitives = destinationArray.every((item) => typeof item !== "object" || item === null);
|
|
1955
|
+
const sourceHasOnlyPrimitives = sourceArray.every((item) => typeof item !== "object" || item === null);
|
|
1956
|
+
if (destHasOnlyPrimitives && sourceHasOnlyPrimitives) return sourceArray;
|
|
1957
|
+
const result = [];
|
|
1958
|
+
const maxLength = Math.max(destinationArray.length, sourceArray.length);
|
|
1959
|
+
for (let i = 0; i < maxLength; i++) {
|
|
1960
|
+
const destItem = destinationArray[i];
|
|
1961
|
+
const sourceItem = sourceArray[i];
|
|
1962
|
+
if (destItem === void 0 && sourceItem === void 0) {} else if (destItem === void 0) result.push(sourceItem);
|
|
1963
|
+
else if (sourceItem === void 0) result.push(destItem);
|
|
1964
|
+
else if (typeof destItem === "object" && typeof sourceItem === "object" && destItem !== null && sourceItem !== null) if ("key" in destItem && "key" in sourceItem && destItem.key === sourceItem.key) result.push(customMerge(destItem, sourceItem));
|
|
1965
|
+
else result.push(customMerge(destItem, sourceItem));
|
|
1966
|
+
else result.push(destItem);
|
|
1967
|
+
}
|
|
1968
|
+
return result;
|
|
1969
|
+
};
|
|
1970
|
+
const mergeDictionaries = (dictionaries) => {
|
|
1971
|
+
const localIds = Array.from(new Set(dictionaries.filter((dict) => dict.localId).map((dict) => dict.localId)));
|
|
1972
|
+
const dictionariesKeys = dictionaries.map((dict) => dict.key);
|
|
1973
|
+
if (new Set(dictionariesKeys).size !== 1) throw new Error("All dictionaries must have the same key");
|
|
1974
|
+
let mergedContent = dictionaries[0].content;
|
|
1975
|
+
for (let i = 1; i < dictionaries.length; i++) {
|
|
1976
|
+
const currentDictionary = getMultilingualDictionary(dictionaries[i]);
|
|
1977
|
+
checkTypesMatch(mergedContent, currentDictionary.content, currentDictionary.localId, currentDictionary.key, []);
|
|
1978
|
+
mergedContent = customMerge(mergedContent, currentDictionary.content);
|
|
1979
|
+
}
|
|
1980
|
+
return {
|
|
1981
|
+
key: dictionaries[0].key,
|
|
1982
|
+
content: mergedContent,
|
|
1983
|
+
localIds
|
|
1984
|
+
};
|
|
1985
|
+
};
|
|
1986
|
+
|
|
1987
|
+
//#endregion
|
|
1988
|
+
//#region src/dictionaryManipulator/orderDictionaries.ts
|
|
1989
|
+
/**
|
|
1990
|
+
* Orders dictionaries based on the dictionary priority strategy.
|
|
1991
|
+
*
|
|
1992
|
+
* @param dictionaries - Array of dictionaries to order
|
|
1993
|
+
* @param priorityStrategy - The priority strategy ('local_first' or 'distant_first')
|
|
1994
|
+
* @returns Ordered array of dictionaries
|
|
1995
|
+
*/
|
|
1996
|
+
const orderDictionaries = (dictionaries, configuration$1 = configuration) => {
|
|
1997
|
+
const { editor } = configuration$1;
|
|
1998
|
+
const { dictionaryPriorityStrategy } = editor;
|
|
1999
|
+
if (dictionaries.length <= 1) return dictionaries;
|
|
2000
|
+
const withIndex = dictionaries.map((dictionary, index) => ({
|
|
2001
|
+
dictionary,
|
|
2002
|
+
index
|
|
2003
|
+
}));
|
|
2004
|
+
const getPriority = (dictionary) => {
|
|
2005
|
+
const p = dictionary.priority ?? 0;
|
|
2006
|
+
return Number.isFinite(p) ? p : 0;
|
|
2007
|
+
};
|
|
2008
|
+
const getLocationWeight = (d) => {
|
|
2009
|
+
const location = d.location;
|
|
2010
|
+
if (location === void 0) return 2;
|
|
2011
|
+
if (dictionaryPriorityStrategy === "distant_first") return location === "remote" ? 0 : 1;
|
|
2012
|
+
return location === "local" ? 0 : 1;
|
|
2013
|
+
};
|
|
2014
|
+
withIndex.sort((a, b) => {
|
|
2015
|
+
const aAuto = a.dictionary.filled ? 1 : 0;
|
|
2016
|
+
const bAuto = b.dictionary.filled ? 1 : 0;
|
|
2017
|
+
if (aAuto !== bAuto) return aAuto - bAuto;
|
|
2018
|
+
const aP = getPriority(a.dictionary);
|
|
2019
|
+
const bP = getPriority(b.dictionary);
|
|
2020
|
+
if (aP !== bP) return bP - aP;
|
|
2021
|
+
const aLocation = getLocationWeight(a.dictionary);
|
|
2022
|
+
const bLocation = getLocationWeight(b.dictionary);
|
|
2023
|
+
if (aLocation !== bLocation) return aLocation - bLocation;
|
|
2024
|
+
return a.index - b.index;
|
|
2025
|
+
});
|
|
2026
|
+
return withIndex.map(({ dictionary }) => dictionary);
|
|
2027
|
+
};
|
|
2028
|
+
|
|
2029
|
+
//#endregion
|
|
2030
|
+
//#region src/dictionaryManipulator/normalizeDictionary.ts
|
|
2031
|
+
const normalizeDictionary = (dictionary, configuration$1) => {
|
|
2032
|
+
const { locales } = configuration$1.internationalization;
|
|
2033
|
+
const parsedDictionary = JSON.parse(JSON.stringify(dictionary));
|
|
2034
|
+
if (dictionary.locale) return {
|
|
2035
|
+
...dictionary,
|
|
2036
|
+
locale: void 0,
|
|
2037
|
+
content: translation({ [dictionary.locale]: dictionary.content })
|
|
2038
|
+
};
|
|
2039
|
+
const perLocaleContent = locales.reduce((acc, locale) => {
|
|
2040
|
+
acc[locale] = getPerLocaleDictionary(parsedDictionary, locale).content;
|
|
2041
|
+
return acc;
|
|
2042
|
+
}, {});
|
|
2043
|
+
return {
|
|
2044
|
+
...dictionary,
|
|
2045
|
+
content: translation(perLocaleContent)
|
|
2046
|
+
};
|
|
2047
|
+
};
|
|
2048
|
+
const normalizeDictionaries = (dictionaries, configuration$1) => {
|
|
2049
|
+
return orderDictionaries(dictionaries, configuration$1).map((dictionary) => normalizeDictionary(dictionary, configuration$1));
|
|
2050
|
+
};
|
|
2051
|
+
|
|
2052
|
+
//#endregion
|
|
2053
|
+
//#region src/dictionaryManipulator/removeContentNodeByKeyPath.ts
|
|
2054
|
+
const removeContentNodeByKeyPath = (dictionaryContent, keyPath) => {
|
|
2055
|
+
let currentValue = dictionaryContent;
|
|
2056
|
+
let parentValue = null;
|
|
2057
|
+
let lastKey = null;
|
|
2058
|
+
for (const keyObj of keyPath) {
|
|
2059
|
+
parentValue = currentValue;
|
|
2060
|
+
if (keyObj.type === NodeType.Object || keyObj.type === NodeType.Array) {
|
|
2061
|
+
lastKey = keyObj.key;
|
|
2062
|
+
currentValue = currentValue[keyObj.key];
|
|
2063
|
+
}
|
|
2064
|
+
if (keyObj.type === NodeType.Translation || keyObj.type === NodeType.Enumeration || keyObj.type === NodeType.Condition) {
|
|
2065
|
+
lastKey = keyObj.type;
|
|
2066
|
+
currentValue = currentValue[keyObj.type][keyObj.key];
|
|
2067
|
+
}
|
|
2068
|
+
if (keyObj.type === NodeType.Markdown || keyObj.type === NodeType.ReactNode || keyObj.type === NodeType.Insertion || keyObj.type === NodeType.File) {
|
|
2069
|
+
lastKey = keyObj.type;
|
|
2070
|
+
currentValue = currentValue[keyObj.type];
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
if (parentValue && lastKey !== null) if (Array.isArray(parentValue)) parentValue.splice(lastKey, 1);
|
|
2074
|
+
else delete parentValue[lastKey];
|
|
2075
|
+
return dictionaryContent;
|
|
2076
|
+
};
|
|
2077
|
+
|
|
2078
|
+
//#endregion
|
|
2079
|
+
//#region src/dictionaryManipulator/renameContentNodeByKeyPath.ts
|
|
2080
|
+
const renameContentNodeByKeyPath = (dictionaryContent, newKey, keyPath) => {
|
|
2081
|
+
let currentValue = dictionaryContent;
|
|
2082
|
+
let parentValue = null;
|
|
2083
|
+
let lastKey = null;
|
|
2084
|
+
for (const keyObj of keyPath) {
|
|
2085
|
+
parentValue = currentValue;
|
|
2086
|
+
if (keyObj.type === NodeType.Object || keyObj.type === NodeType.Array) {
|
|
2087
|
+
lastKey = keyObj.key;
|
|
2088
|
+
currentValue = currentValue[keyObj.key];
|
|
2089
|
+
}
|
|
2090
|
+
if (keyObj.type === NodeType.Translation || keyObj.type === NodeType.Enumeration || keyObj.type === NodeType.Condition) {
|
|
2091
|
+
lastKey = keyObj.type;
|
|
2092
|
+
currentValue = currentValue[keyObj.type][keyObj.key];
|
|
2093
|
+
}
|
|
2094
|
+
if (keyObj.type === NodeType.Markdown || keyObj.type === NodeType.ReactNode || keyObj.type === NodeType.Insertion || keyObj.type === NodeType.File) {
|
|
2095
|
+
lastKey = keyObj.type;
|
|
2096
|
+
currentValue = currentValue[keyObj.type];
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
if (parentValue && lastKey !== null) if (Array.isArray(parentValue)) parentValue[lastKey] = currentValue;
|
|
2100
|
+
else {
|
|
2101
|
+
const newParentValue = {};
|
|
2102
|
+
for (const key of Object.keys(parentValue)) if (key === lastKey && typeof newKey !== "undefined") newParentValue[newKey] = currentValue;
|
|
2103
|
+
else newParentValue[key] = parentValue[key];
|
|
2104
|
+
Object.keys(parentValue).forEach((key) => {
|
|
2105
|
+
delete parentValue[key];
|
|
2106
|
+
});
|
|
2107
|
+
Object.assign(parentValue, newParentValue);
|
|
2108
|
+
}
|
|
2109
|
+
return dictionaryContent;
|
|
2110
|
+
};
|
|
2111
|
+
|
|
2112
|
+
//#endregion
|
|
2113
|
+
//#region src/dictionaryManipulator/updateNodeChildren.ts
|
|
2114
|
+
const updateNodeChildren = (section, newChildren) => {
|
|
2115
|
+
if (typeof section === "string") return newChildren;
|
|
2116
|
+
if (typeof section === "number") return newChildren;
|
|
2117
|
+
if (typeof section === "boolean") return newChildren;
|
|
2118
|
+
if (typeof (section === null || section === void 0 ? void 0 : section.nodeType) === "string") {
|
|
2119
|
+
const typedNode = section;
|
|
2120
|
+
const content = typedNode[typedNode.nodeType];
|
|
2121
|
+
if (typedNode.nodeType === NodeType.Translation || typedNode.nodeType === NodeType.Enumeration || typedNode.nodeType === NodeType.Condition) {
|
|
2122
|
+
const newContent = Object.entries(content).reduce((acc, [key]) => {
|
|
2123
|
+
acc[key] = newChildren;
|
|
2124
|
+
return acc;
|
|
2125
|
+
}, {});
|
|
2126
|
+
return {
|
|
2127
|
+
...typedNode,
|
|
2128
|
+
[typedNode.nodeType]: newContent
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
if (typedNode.nodeType === NodeType.Nested) return typedNode;
|
|
2132
|
+
return {
|
|
2133
|
+
...typedNode,
|
|
2134
|
+
[typedNode.nodeType]: newChildren
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
if (!section || typeof section !== "object") return newChildren;
|
|
2138
|
+
if (Array.isArray(section)) return section.map(() => newChildren);
|
|
2139
|
+
return Object.entries(section).reduce((acc, [key]) => ({
|
|
2140
|
+
...acc,
|
|
2141
|
+
[key]: newChildren
|
|
2142
|
+
}), {});
|
|
2143
|
+
};
|
|
2144
|
+
|
|
2145
|
+
//#endregion
|
|
2146
|
+
//#region src/utils/intl.ts
|
|
2147
|
+
const cacheKey = (locales, options) => JSON.stringify([locales, options]);
|
|
2148
|
+
const createCachedConstructor = (Ctor) => {
|
|
2149
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2150
|
+
function Wrapped(locales, options) {
|
|
2151
|
+
var _Intl;
|
|
2152
|
+
if (Ctor.name === "DisplayNames" && typeof ((_Intl = Intl) === null || _Intl === void 0 ? void 0 : _Intl.DisplayNames) !== "function") {
|
|
2153
|
+
if (process.env.NODE_ENV === "development") console.warn([
|
|
2154
|
+
`// Intl.DisplayNames is not supported; falling back to raw locale (${locales}). `,
|
|
2155
|
+
`// Consider adding a polyfill as https://formatjs.io/docs/polyfills/intl-displaynames/`,
|
|
2156
|
+
``,
|
|
2157
|
+
`import 'intl';`,
|
|
2158
|
+
`import '@formatjs/intl-getcanonicallocales/polyfill';`,
|
|
2159
|
+
`import '@formatjs/intl-locale/polyfill';`,
|
|
2160
|
+
`import '@formatjs/intl-pluralrules/polyfill';`,
|
|
2161
|
+
`import '@formatjs/intl-displaynames/polyfill';`,
|
|
2162
|
+
`import '@formatjs/intl-listformat/polyfill';`,
|
|
2163
|
+
`import '@formatjs/intl-numberformat/polyfill';`,
|
|
2164
|
+
`import '@formatjs/intl-relativetimeformat/polyfill';`,
|
|
2165
|
+
`import '@formatjs/intl-datetimeformat/polyfill';`,
|
|
2166
|
+
``,
|
|
2167
|
+
`// Optionally add locale data`,
|
|
2168
|
+
`import '@formatjs/intl-pluralrules/locale-data/fr';`,
|
|
2169
|
+
`import '@formatjs/intl-numberformat/locale-data/fr';`,
|
|
2170
|
+
`import '@formatjs/intl-datetimeformat/locale-data/fr';`
|
|
2171
|
+
].join("\n"));
|
|
2172
|
+
return locales;
|
|
2173
|
+
}
|
|
2174
|
+
const key = cacheKey(locales ?? Locales.ENGLISH, options);
|
|
2175
|
+
let instance = cache.get(key);
|
|
2176
|
+
if (!instance) {
|
|
2177
|
+
instance = new Ctor(locales, options);
|
|
2178
|
+
cache.set(key, instance);
|
|
2179
|
+
}
|
|
2180
|
+
return instance;
|
|
2181
|
+
}
|
|
2182
|
+
Wrapped.prototype = Ctor.prototype;
|
|
2183
|
+
return Wrapped;
|
|
2184
|
+
};
|
|
2185
|
+
const createCachedIntl = () => new Proxy(Intl, { get: (target, prop, receiver) => {
|
|
2186
|
+
const value = Reflect.get(target, prop, receiver);
|
|
2187
|
+
return typeof value === "function" && /^[A-Z]/.test(String(prop)) ? createCachedConstructor(value) : value;
|
|
2188
|
+
} });
|
|
2189
|
+
const CachedIntl = createCachedIntl();
|
|
2190
|
+
|
|
2191
|
+
//#endregion
|
|
2192
|
+
//#region src/formatters/compact.ts
|
|
2193
|
+
/**
|
|
2194
|
+
* Formats a numeric value using compact notation (e.g., 1K, 1M, 1B)
|
|
2195
|
+
* based on locale and formatting options.
|
|
2196
|
+
*
|
|
2197
|
+
* @example
|
|
2198
|
+
* compact({ value: 1200 }); // "1.2K"
|
|
2199
|
+
*
|
|
2200
|
+
* @example
|
|
2201
|
+
* compact({ value: "1000000", locale: Locales.FRENCH, compactDisplay: "long" });
|
|
2202
|
+
* // "1 million"
|
|
2203
|
+
*/
|
|
2204
|
+
const compact = (value, options) => {
|
|
2205
|
+
var _configuration$intern;
|
|
2206
|
+
return new CachedIntl.NumberFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), {
|
|
2207
|
+
...options,
|
|
2208
|
+
notation: "compact"
|
|
2209
|
+
}).format(Number(value));
|
|
2210
|
+
};
|
|
2211
|
+
|
|
2212
|
+
//#endregion
|
|
2213
|
+
//#region src/formatters/currency.ts
|
|
2214
|
+
/**
|
|
2215
|
+
* Formats a numeric or string value into a localized currency string using the Intl API.
|
|
2216
|
+
*
|
|
2217
|
+
* @example
|
|
2218
|
+
* currency({ value: 1234.5, currency: 'EUR' });
|
|
2219
|
+
* // "€1,234.50"
|
|
2220
|
+
*
|
|
2221
|
+
* @example
|
|
2222
|
+
* currency({ value: "5000", locale: Locales.FRENCH, currency: "CAD", currencyDisplay: "code" });
|
|
2223
|
+
* // "5 000,00 CAD"
|
|
2224
|
+
*/
|
|
2225
|
+
const currency = (value, options) => {
|
|
2226
|
+
var _configuration$intern;
|
|
2227
|
+
return new CachedIntl.NumberFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), {
|
|
2228
|
+
style: "currency",
|
|
2229
|
+
currency: (options === null || options === void 0 ? void 0 : options.currency) ?? "USD",
|
|
2230
|
+
currencyDisplay: (options === null || options === void 0 ? void 0 : options.currencyDisplay) ?? "symbol",
|
|
2231
|
+
minimumFractionDigits: (options === null || options === void 0 ? void 0 : options.minimumFractionDigits) ?? 2,
|
|
2232
|
+
maximumFractionDigits: (options === null || options === void 0 ? void 0 : options.maximumFractionDigits) ?? 2,
|
|
2233
|
+
...options
|
|
2234
|
+
}).format(Number(value));
|
|
2235
|
+
};
|
|
2236
|
+
|
|
2237
|
+
//#endregion
|
|
2238
|
+
//#region src/formatters/date.ts
|
|
2239
|
+
const presets = {
|
|
2240
|
+
short: {
|
|
2241
|
+
year: "2-digit",
|
|
2242
|
+
month: "2-digit",
|
|
2243
|
+
day: "2-digit",
|
|
2244
|
+
hour: "2-digit",
|
|
2245
|
+
minute: "2-digit"
|
|
2246
|
+
},
|
|
2247
|
+
long: {
|
|
2248
|
+
year: "numeric",
|
|
2249
|
+
month: "long",
|
|
2250
|
+
day: "numeric",
|
|
2251
|
+
hour: "numeric",
|
|
2252
|
+
minute: "numeric"
|
|
2253
|
+
},
|
|
2254
|
+
full: {
|
|
2255
|
+
year: "numeric",
|
|
2256
|
+
month: "long",
|
|
2257
|
+
day: "numeric",
|
|
2258
|
+
hour: "numeric",
|
|
2259
|
+
minute: "numeric",
|
|
2260
|
+
hour12: false
|
|
2261
|
+
},
|
|
2262
|
+
dateOnly: {
|
|
2263
|
+
year: "numeric",
|
|
2264
|
+
month: "short",
|
|
2265
|
+
day: "numeric"
|
|
2266
|
+
},
|
|
2267
|
+
timeOnly: {
|
|
2268
|
+
hour: "numeric",
|
|
2269
|
+
minute: "numeric",
|
|
2270
|
+
second: "numeric"
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
/**
|
|
2274
|
+
* Formats a date/time value into a localized string using Intl.DateTimeFormat.
|
|
2275
|
+
*
|
|
2276
|
+
* @example
|
|
2277
|
+
* date({ date: new Date(), options: "short" });
|
|
2278
|
+
* // "08/02/25, 14:30"
|
|
2279
|
+
*
|
|
2280
|
+
* @example
|
|
2281
|
+
* date({ date: "2025-08-02T14:30:00Z", locale: Locales.FRENCH, options: { month: "long", day: "numeric" } });
|
|
2282
|
+
* // "2 août"
|
|
2283
|
+
*/
|
|
2284
|
+
const date = (date$1, options) => {
|
|
2285
|
+
var _configuration$intern;
|
|
2286
|
+
const dateTime = new Date(date$1);
|
|
2287
|
+
const resolvedOptions = typeof options === "string" ? presets[options] ?? {} : options;
|
|
2288
|
+
return new CachedIntl.DateTimeFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), resolvedOptions).format(dateTime);
|
|
2289
|
+
};
|
|
2290
|
+
|
|
2291
|
+
//#endregion
|
|
2292
|
+
//#region src/formatters/list.ts
|
|
2293
|
+
/**
|
|
2294
|
+
* Formats an array of values into a localized list string using the Intl API.
|
|
2295
|
+
*
|
|
2296
|
+
* @example
|
|
2297
|
+
* list(['apple', 'banana', 'orange']);
|
|
2298
|
+
* // "apple, banana, and orange"
|
|
2299
|
+
*
|
|
2300
|
+
* @example
|
|
2301
|
+
* list(['red', 'green', 'blue'], { locale: Locales.FRENCH, type: 'disjunction' });
|
|
2302
|
+
* // "rouge, vert ou bleu"
|
|
2303
|
+
*
|
|
2304
|
+
* @example
|
|
2305
|
+
* list([1, 2, 3], { type: 'unit' });
|
|
2306
|
+
* // "1, 2, 3"
|
|
2307
|
+
*/
|
|
2308
|
+
const list = (values, options) => {
|
|
2309
|
+
var _configuration$intern;
|
|
2310
|
+
return new CachedIntl.ListFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), {
|
|
2311
|
+
type: (options === null || options === void 0 ? void 0 : options.type) ?? "conjunction",
|
|
2312
|
+
style: (options === null || options === void 0 ? void 0 : options.style) ?? "long",
|
|
2313
|
+
...options
|
|
2314
|
+
}).format(values.map(String));
|
|
2315
|
+
};
|
|
2316
|
+
|
|
2317
|
+
//#endregion
|
|
2318
|
+
//#region src/formatters/number.ts
|
|
2319
|
+
/**
|
|
2320
|
+
* Formats a numeric value using locale-aware formatting.
|
|
2321
|
+
*
|
|
2322
|
+
* @example
|
|
2323
|
+
* number({ value: 123456.789 }); // "123,456.789"
|
|
2324
|
+
* number({ value: "1000000", locale: Locales.FRENCH }); // "1 000 000"
|
|
2325
|
+
*/
|
|
2326
|
+
const number = (value, options) => {
|
|
2327
|
+
var _configuration$intern;
|
|
2328
|
+
return new CachedIntl.NumberFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), options).format(Number(value));
|
|
2329
|
+
};
|
|
2330
|
+
|
|
2331
|
+
//#endregion
|
|
2332
|
+
//#region src/formatters/percentage.ts
|
|
2333
|
+
/**
|
|
2334
|
+
* Formats a number as a percentage string (e.g., 0.25 → "25%").
|
|
2335
|
+
*
|
|
2336
|
+
* @example
|
|
2337
|
+
* percentage({ value: 0.25 }) // "25%"
|
|
2338
|
+
* percentage({ value: 0.25, minimumFractionDigits: 2 }) // "25.00%"
|
|
2339
|
+
*/
|
|
2340
|
+
const percentage = (value, options) => {
|
|
2341
|
+
var _configuration$intern;
|
|
2342
|
+
let numericValue = Number(value);
|
|
2343
|
+
if (numericValue > 1) numericValue /= 100;
|
|
2344
|
+
return new CachedIntl.NumberFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), {
|
|
2345
|
+
style: "percent",
|
|
2346
|
+
...options
|
|
2347
|
+
}).format(Number(numericValue));
|
|
2348
|
+
};
|
|
2349
|
+
|
|
2350
|
+
//#endregion
|
|
2351
|
+
//#region src/formatters/relativeTime.ts
|
|
2352
|
+
/**
|
|
2353
|
+
* Calculate the difference between 2 dates in the given unit.
|
|
2354
|
+
*/
|
|
2355
|
+
const diffInUnit = (from, to, unit) => {
|
|
2356
|
+
const sec = (to.getTime() - from.getTime()) / 1e3;
|
|
2357
|
+
switch (unit) {
|
|
2358
|
+
case "second": return sec;
|
|
2359
|
+
case "minute": return sec / 60;
|
|
2360
|
+
case "hour": return sec / 3600;
|
|
2361
|
+
case "day": return sec / 86400;
|
|
2362
|
+
case "month": return sec / (30 * 86400);
|
|
2363
|
+
case "quarter": return sec / (90 * 86400);
|
|
2364
|
+
case "year": return sec / (365 * 86400);
|
|
2365
|
+
default: return sec;
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
const relativeTime = (from, to = /* @__PURE__ */ new Date(), options) => {
|
|
2369
|
+
var _configuration$intern;
|
|
2370
|
+
const fromDate = new Date(from);
|
|
2371
|
+
const toDate = new Date(to);
|
|
2372
|
+
const unit = (options === null || options === void 0 ? void 0 : options.unit) ?? "second";
|
|
2373
|
+
const value = diffInUnit(fromDate, toDate, unit);
|
|
2374
|
+
return new CachedIntl.RelativeTimeFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), options).format(Math.round(value), unit);
|
|
2375
|
+
};
|
|
2376
|
+
|
|
2377
|
+
//#endregion
|
|
2378
|
+
//#region src/formatters/units.ts
|
|
2379
|
+
/**
|
|
2380
|
+
* Formats a numeric value as a localized unit string.
|
|
2381
|
+
*
|
|
2382
|
+
* @example
|
|
2383
|
+
* units({ value: 5, unit: "kilometer", unitDisplay: "long", locale: "en-GB" })
|
|
2384
|
+
* // "5 kilometers"
|
|
2385
|
+
*/
|
|
2386
|
+
const units = (value, options) => {
|
|
2387
|
+
var _configuration$intern;
|
|
2388
|
+
return new CachedIntl.NumberFormat((options === null || options === void 0 ? void 0 : options.locale) ?? (configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.defaultLocale), {
|
|
2389
|
+
style: "unit",
|
|
2390
|
+
unit: (options === null || options === void 0 ? void 0 : options.unit) ?? "day",
|
|
2391
|
+
unitDisplay: (options === null || options === void 0 ? void 0 : options.unitDisplay) ?? "short",
|
|
2392
|
+
useGrouping: (options === null || options === void 0 ? void 0 : options.useGrouping) ?? false
|
|
2393
|
+
}).format(Number(value));
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
//#endregion
|
|
2397
|
+
//#region src/getStorageAttributes.ts
|
|
2398
|
+
/**
|
|
2399
|
+
* Creates a cookie entry with default values for missing attributes
|
|
2400
|
+
*/
|
|
2401
|
+
const createCookieEntry = (options) => {
|
|
2402
|
+
const { name, path, expires, domain, secure, sameSite, httpOnly } = options ?? {};
|
|
2403
|
+
return {
|
|
2404
|
+
name: name ?? DefaultValues.Routing.COOKIE_NAME,
|
|
2405
|
+
attributes: {
|
|
2406
|
+
path,
|
|
2407
|
+
expires,
|
|
2408
|
+
domain,
|
|
2409
|
+
secure,
|
|
2410
|
+
sameSite,
|
|
2411
|
+
httpOnly
|
|
2412
|
+
}
|
|
2413
|
+
};
|
|
2414
|
+
};
|
|
2415
|
+
/**
|
|
2416
|
+
* Creates a web storage entry (localStorage or sessionStorage) with default name
|
|
2417
|
+
*/
|
|
2418
|
+
const createWebStorageEntry = (options) => {
|
|
2419
|
+
const { name } = options ?? {};
|
|
2420
|
+
return { name: name ?? DefaultValues.Routing.LOCALE_STORAGE_NAME };
|
|
2421
|
+
};
|
|
2422
|
+
/**
|
|
2423
|
+
* Creates a header entry with default name
|
|
2424
|
+
*/
|
|
2425
|
+
const createHeaderEntry = (options) => {
|
|
2426
|
+
const { name } = options ?? {};
|
|
2427
|
+
return { name: name ?? DefaultValues.Routing.HEADER_NAME };
|
|
2428
|
+
};
|
|
2429
|
+
/**
|
|
2430
|
+
* Determines if a storage entry is a cookie based on its properties
|
|
2431
|
+
*/
|
|
2432
|
+
const isCookieEntry = (entry) => {
|
|
2433
|
+
return entry.type === "cookie" || "sameSite" in entry || "httpOnly" in entry || "secure" in entry;
|
|
2434
|
+
};
|
|
2435
|
+
/**
|
|
2436
|
+
* Determines the storage type from a string literal
|
|
2437
|
+
*/
|
|
2438
|
+
const isStorageType = (value) => {
|
|
2439
|
+
return value === "cookie" || value === "localStorage" || value === "sessionStorage" || value === "header";
|
|
2440
|
+
};
|
|
2441
|
+
/**
|
|
2442
|
+
* Processes a single storage entry and returns the appropriate storage attributes
|
|
2443
|
+
*/
|
|
2444
|
+
const processStorageEntry = (entry) => {
|
|
2445
|
+
if (typeof entry === "string") {
|
|
2446
|
+
if (!isStorageType(entry)) return {
|
|
2447
|
+
cookies: [],
|
|
2448
|
+
localStorage: [],
|
|
2449
|
+
sessionStorage: [],
|
|
2450
|
+
headers: []
|
|
2451
|
+
};
|
|
2452
|
+
if (entry === "cookie") return { cookies: [createCookieEntry()] };
|
|
2453
|
+
if (entry === "localStorage") return { localStorage: [createWebStorageEntry()] };
|
|
2454
|
+
if (entry === "sessionStorage") return { sessionStorage: [createWebStorageEntry()] };
|
|
2455
|
+
if (entry === "header") return { headers: [createHeaderEntry()] };
|
|
2456
|
+
}
|
|
2457
|
+
if (typeof entry === "object" && entry !== null) {
|
|
2458
|
+
const typedEntry = entry;
|
|
2459
|
+
if (isCookieEntry(typedEntry)) return { cookies: [createCookieEntry(typedEntry)] };
|
|
2460
|
+
if ("type" in typedEntry && typedEntry.type === "localStorage") {
|
|
2461
|
+
const { name: name$1,...rest$1 } = typedEntry;
|
|
2462
|
+
return { localStorage: [createWebStorageEntry({
|
|
2463
|
+
name: name$1,
|
|
2464
|
+
...rest$1
|
|
2465
|
+
})] };
|
|
2466
|
+
}
|
|
2467
|
+
if ("type" in typedEntry && typedEntry.type === "sessionStorage") {
|
|
2468
|
+
const { name: name$1,...rest$1 } = typedEntry;
|
|
2469
|
+
return { sessionStorage: [createWebStorageEntry({
|
|
2470
|
+
name: name$1,
|
|
2471
|
+
...rest$1
|
|
2472
|
+
})] };
|
|
2473
|
+
}
|
|
2474
|
+
if ("type" in typedEntry && typedEntry.type === "header") {
|
|
2475
|
+
const { name: name$1,...rest$1 } = typedEntry;
|
|
2476
|
+
return { headers: [createHeaderEntry({
|
|
2477
|
+
name: name$1,
|
|
2478
|
+
...rest$1
|
|
2479
|
+
})] };
|
|
2480
|
+
}
|
|
2481
|
+
const { name,...rest } = typedEntry;
|
|
2482
|
+
return { localStorage: [createWebStorageEntry({
|
|
2483
|
+
name,
|
|
2484
|
+
...rest
|
|
2485
|
+
})] };
|
|
2486
|
+
}
|
|
2487
|
+
return {
|
|
2488
|
+
cookies: [],
|
|
2489
|
+
localStorage: [],
|
|
2490
|
+
sessionStorage: [],
|
|
2491
|
+
headers: []
|
|
2492
|
+
};
|
|
2493
|
+
};
|
|
2494
|
+
/**
|
|
2495
|
+
* Merges multiple partial storage attributes into a single result
|
|
2496
|
+
*/
|
|
2497
|
+
const mergeStorageAttributes = (accumulated, partial) => {
|
|
2498
|
+
return {
|
|
2499
|
+
cookies: [...accumulated.cookies, ...partial.cookies ?? []],
|
|
2500
|
+
localStorage: [...accumulated.localStorage, ...partial.localStorage ?? []],
|
|
2501
|
+
sessionStorage: [...accumulated.sessionStorage, ...partial.sessionStorage ?? []],
|
|
2502
|
+
headers: [...accumulated.headers, ...partial.headers ?? []]
|
|
2503
|
+
};
|
|
2504
|
+
};
|
|
2505
|
+
/**
|
|
2506
|
+
* Extracts and normalizes storage configuration into separate arrays for each storage type
|
|
2507
|
+
*
|
|
2508
|
+
* @param options - The storage configuration from IntlayerConfig
|
|
2509
|
+
* @returns An object containing arrays for cookies, localStorage, and sessionStorage
|
|
2510
|
+
*/
|
|
2511
|
+
const getStorageAttributes = (options) => {
|
|
2512
|
+
const emptyResult = {
|
|
2513
|
+
cookies: [],
|
|
2514
|
+
localStorage: [],
|
|
2515
|
+
sessionStorage: [],
|
|
2516
|
+
headers: []
|
|
2517
|
+
};
|
|
2518
|
+
if (options === false || options === void 0) return emptyResult;
|
|
2519
|
+
if (Array.isArray(options)) return options.reduce((acc, entry) => {
|
|
2520
|
+
return mergeStorageAttributes(acc, processStorageEntry(entry));
|
|
2521
|
+
}, emptyResult);
|
|
2522
|
+
return mergeStorageAttributes(emptyResult, processStorageEntry(options));
|
|
2523
|
+
};
|
|
2524
|
+
|
|
2525
|
+
//#endregion
|
|
2526
|
+
//#region src/utils/localeStorage.ts
|
|
2527
|
+
const buildCookieString = (name, value, attributes) => {
|
|
2528
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
2529
|
+
if (attributes.path) parts.push(`Path=${attributes.path}`);
|
|
2530
|
+
if (attributes.domain) parts.push(`Domain=${attributes.domain}`);
|
|
2531
|
+
if (attributes.expires instanceof Date) parts.push(`Expires=${attributes.expires.toUTCString()}`);
|
|
2532
|
+
if (attributes.secure) parts.push("Secure");
|
|
2533
|
+
if (attributes.sameSite) parts.push(`SameSite=${attributes.sameSite}`);
|
|
2534
|
+
return parts.join("; ");
|
|
2535
|
+
};
|
|
2536
|
+
/**
|
|
2537
|
+
* Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).
|
|
2538
|
+
* The function checks storage locations in order of priority as defined in the configuration.
|
|
2539
|
+
*
|
|
2540
|
+
* @returns The locale if found in any storage, or undefined if not found
|
|
2541
|
+
*/
|
|
2542
|
+
const getLocaleFromStorage = (options) => {
|
|
2543
|
+
const { routing, internationalization } = configuration;
|
|
2544
|
+
const { locales } = internationalization;
|
|
2545
|
+
const { storage } = routing;
|
|
2546
|
+
if (storage === false || (options === null || options === void 0 ? void 0 : options.isCookieEnabled) === false) return void 0;
|
|
2547
|
+
const storageAttributes = getStorageAttributes(storage);
|
|
2548
|
+
const isValidLocale = (value) => {
|
|
2549
|
+
if (!value) return false;
|
|
2550
|
+
return locales.includes(value);
|
|
2551
|
+
};
|
|
2552
|
+
const readCookie = (name) => {
|
|
2553
|
+
try {
|
|
2554
|
+
var _options$getCookie;
|
|
2555
|
+
const fromOption = options === null || options === void 0 || (_options$getCookie = options.getCookie) === null || _options$getCookie === void 0 ? void 0 : _options$getCookie.call(options, name);
|
|
2556
|
+
if (fromOption !== null) return fromOption;
|
|
2557
|
+
} catch {}
|
|
2558
|
+
try {
|
|
2559
|
+
const cookieString = document.cookie ?? "";
|
|
2560
|
+
if (!cookieString) return;
|
|
2561
|
+
const pairs = cookieString.split(";");
|
|
2562
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
2563
|
+
const part = pairs[i].trim();
|
|
2564
|
+
if (!part) continue;
|
|
2565
|
+
const equalIndex = part.indexOf("=");
|
|
2566
|
+
if ((equalIndex >= 0 ? part.substring(0, equalIndex) : part) === name) {
|
|
2567
|
+
const rawValue = equalIndex >= 0 ? part.substring(equalIndex + 1) : "";
|
|
2568
|
+
try {
|
|
2569
|
+
return decodeURIComponent(rawValue);
|
|
2570
|
+
} catch {
|
|
2571
|
+
return rawValue;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
} catch {}
|
|
2576
|
+
};
|
|
2577
|
+
for (let i = 0; i < storageAttributes.cookies.length; i++) {
|
|
2578
|
+
const { name } = storageAttributes.cookies[i];
|
|
2579
|
+
const value = readCookie(name);
|
|
2580
|
+
if (isValidLocale(value)) return value;
|
|
2581
|
+
}
|
|
2582
|
+
for (let i = 0; i < storageAttributes.localStorage.length; i++) {
|
|
2583
|
+
const { name } = storageAttributes.localStorage[i];
|
|
2584
|
+
try {
|
|
2585
|
+
var _options$getLocaleSto;
|
|
2586
|
+
const value = options === null || options === void 0 || (_options$getLocaleSto = options.getLocaleStorage) === null || _options$getLocaleSto === void 0 ? void 0 : _options$getLocaleSto.call(options, name);
|
|
2587
|
+
if (isValidLocale(value)) return value;
|
|
2588
|
+
} catch {}
|
|
2589
|
+
}
|
|
2590
|
+
for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
|
|
2591
|
+
const { name } = storageAttributes.sessionStorage[i];
|
|
2592
|
+
try {
|
|
2593
|
+
var _options$getSessionSt;
|
|
2594
|
+
const value = options === null || options === void 0 || (_options$getSessionSt = options.getSessionStorage) === null || _options$getSessionSt === void 0 ? void 0 : _options$getSessionSt.call(options, name);
|
|
2595
|
+
if (isValidLocale(value)) return value;
|
|
2596
|
+
} catch {}
|
|
2597
|
+
}
|
|
2598
|
+
for (let i = 0; i < storageAttributes.headers.length; i++) {
|
|
2599
|
+
const { name } = storageAttributes.headers[i];
|
|
2600
|
+
try {
|
|
2601
|
+
var _options$getHeader;
|
|
2602
|
+
const value = options === null || options === void 0 || (_options$getHeader = options.getHeader) === null || _options$getHeader === void 0 ? void 0 : _options$getHeader.call(options, name);
|
|
2603
|
+
if (isValidLocale(value)) return value;
|
|
2604
|
+
} catch {}
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
/**
|
|
2608
|
+
* Stores the locale in various storage mechanisms (cookies, localStorage, sessionStorage, headers).
|
|
2609
|
+
* The function writes to all configured storage locations according to their attributes.
|
|
2610
|
+
* Respects overwrite flags for localStorage and sessionStorage.
|
|
2611
|
+
*
|
|
2612
|
+
* @param locale - The locale to store
|
|
2613
|
+
*/
|
|
2614
|
+
const setLocaleInStorage = (locale, options) => {
|
|
2615
|
+
if (configuration.routing.storage === false || (options === null || options === void 0 ? void 0 : options.isCookieEnabled) === false) return;
|
|
2616
|
+
const storageAttributes = getStorageAttributes(configuration.routing.storage);
|
|
2617
|
+
for (let i = 0; i < storageAttributes.cookies.length; i++) {
|
|
2618
|
+
const { name, attributes } = storageAttributes.cookies[i];
|
|
2619
|
+
try {
|
|
2620
|
+
if (options === null || options === void 0 ? void 0 : options.setCookieStore) {
|
|
2621
|
+
var _options$setCookieSto;
|
|
2622
|
+
options === null || options === void 0 || (_options$setCookieSto = options.setCookieStore) === null || _options$setCookieSto === void 0 || _options$setCookieSto.call(options, name, locale, {
|
|
2623
|
+
...attributes,
|
|
2624
|
+
expires: attributes.expires instanceof Date ? attributes.expires.getTime() : attributes.expires
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
} catch {
|
|
2628
|
+
try {
|
|
2629
|
+
if (options === null || options === void 0 ? void 0 : options.setCookieString) {
|
|
2630
|
+
var _options$setCookieStr;
|
|
2631
|
+
const cookieString = buildCookieString(name, locale, attributes);
|
|
2632
|
+
options === null || options === void 0 || (_options$setCookieStr = options.setCookieString) === null || _options$setCookieStr === void 0 || _options$setCookieStr.call(options, name, cookieString);
|
|
2633
|
+
}
|
|
2634
|
+
} catch {}
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
if (options === null || options === void 0 ? void 0 : options.setLocaleStorage) for (let i = 0; i < storageAttributes.localStorage.length; i++) {
|
|
2638
|
+
const { name } = storageAttributes.localStorage[i];
|
|
2639
|
+
try {
|
|
2640
|
+
var _options$setLocaleSto;
|
|
2641
|
+
if (!((options === null || options === void 0 ? void 0 : options.overwrite) ?? true) && (options === null || options === void 0 ? void 0 : options.getLocaleStorage)) {
|
|
2642
|
+
var _options$getLocaleSto2;
|
|
2643
|
+
if (options === null || options === void 0 || (_options$getLocaleSto2 = options.getLocaleStorage) === null || _options$getLocaleSto2 === void 0 ? void 0 : _options$getLocaleSto2.call(options, name)) continue;
|
|
2644
|
+
}
|
|
2645
|
+
options === null || options === void 0 || (_options$setLocaleSto = options.setLocaleStorage) === null || _options$setLocaleSto === void 0 || _options$setLocaleSto.call(options, name, locale);
|
|
2646
|
+
} catch {}
|
|
2647
|
+
}
|
|
2648
|
+
if (options === null || options === void 0 ? void 0 : options.setSessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
|
|
2649
|
+
const { name } = storageAttributes.sessionStorage[i];
|
|
2650
|
+
try {
|
|
2651
|
+
var _options$setSessionSt;
|
|
2652
|
+
if (!((options === null || options === void 0 ? void 0 : options.overwrite) ?? true) && (options === null || options === void 0 ? void 0 : options.getSessionStorage)) {
|
|
2653
|
+
var _options$getSessionSt2;
|
|
2654
|
+
if (options === null || options === void 0 || (_options$getSessionSt2 = options.getSessionStorage) === null || _options$getSessionSt2 === void 0 ? void 0 : _options$getSessionSt2.call(options, name)) continue;
|
|
2655
|
+
}
|
|
2656
|
+
options === null || options === void 0 || (_options$setSessionSt = options.setSessionStorage) === null || _options$setSessionSt === void 0 || _options$setSessionSt.call(options, name, locale);
|
|
2657
|
+
} catch {}
|
|
2658
|
+
}
|
|
2659
|
+
if (options === null || options === void 0 ? void 0 : options.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) {
|
|
2660
|
+
const { name } = storageAttributes.headers[i];
|
|
2661
|
+
try {
|
|
2662
|
+
var _options$setHeader;
|
|
2663
|
+
options === null || options === void 0 || (_options$setHeader = options.setHeader) === null || _options$setHeader === void 0 || _options$setHeader.call(options, name, locale);
|
|
2664
|
+
} catch {}
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2667
|
+
/**
|
|
2668
|
+
* Utility object to get and set the locale in the storage by considering the configuration
|
|
2669
|
+
*
|
|
2670
|
+
* @property getLocale - Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).
|
|
2671
|
+
* Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).
|
|
2672
|
+
* The function checks storage locations in order of priority as defined in the configuration.
|
|
2673
|
+
*
|
|
2674
|
+
* @property setLocale - Stores the locale in various storage mechanisms (cookies, localStorage, sessionStorage, headers).
|
|
2675
|
+
* The function writes to all configured storage locations according to their attributes.
|
|
2676
|
+
* Respects overwrite flags for localStorage and sessionStorage.
|
|
2677
|
+
*
|
|
2678
|
+
* @returns The locale if found in any storage, or undefined if not found
|
|
2679
|
+
*/
|
|
2680
|
+
const LocaleStorage = (options) => ({
|
|
2681
|
+
getLocale: () => getLocaleFromStorage(options),
|
|
2682
|
+
setLocale: (locale) => setLocaleInStorage(locale, options)
|
|
2683
|
+
});
|
|
2684
|
+
|
|
2685
|
+
//#endregion
|
|
2686
|
+
//#region src/localization/localeResolver.ts
|
|
2687
|
+
var _configuration$intern2$2;
|
|
2688
|
+
/**
|
|
2689
|
+
* Resolves the most specific locale from a user-provided list,
|
|
2690
|
+
* or falls back to the default locale if no match is found.
|
|
2691
|
+
*/
|
|
2692
|
+
const localeResolver = (selectedLocale, locales = (() => {
|
|
2693
|
+
var _configuration$intern;
|
|
2694
|
+
return configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.locales;
|
|
2695
|
+
})(), defaultLocale = configuration === null || configuration === void 0 || (_configuration$intern2$2 = configuration.internationalization) === null || _configuration$intern2$2 === void 0 ? void 0 : _configuration$intern2$2.defaultLocale) => {
|
|
2696
|
+
const requestedLocales = [selectedLocale].flat();
|
|
2697
|
+
const normalize = (locale) => locale.trim().toLowerCase();
|
|
2698
|
+
try {
|
|
2699
|
+
for (const requested of requestedLocales) {
|
|
2700
|
+
const normalizedRequested = normalize(requested);
|
|
2701
|
+
const exactMatch = locales.find((loc) => normalize(loc) === normalizedRequested);
|
|
2702
|
+
if (exactMatch) return exactMatch;
|
|
2703
|
+
const [requestedLang] = normalizedRequested.split("-");
|
|
2704
|
+
const partialMatch = locales.find((loc) => normalize(loc).split("-")[0] === requestedLang);
|
|
2705
|
+
if (partialMatch) return partialMatch;
|
|
2706
|
+
}
|
|
2707
|
+
} catch (_error) {}
|
|
2708
|
+
return defaultLocale;
|
|
2709
|
+
};
|
|
2710
|
+
|
|
2711
|
+
//#endregion
|
|
2712
|
+
//#region src/localization/localeDetector.ts
|
|
2713
|
+
/**
|
|
2714
|
+
* Module variables.
|
|
2715
|
+
*/
|
|
2716
|
+
const simpleLanguageRegExp = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
|
|
2717
|
+
/**
|
|
2718
|
+
* Parse the Accept-Language header.
|
|
2719
|
+
*/
|
|
2720
|
+
const parseAcceptLanguage = (accept) => {
|
|
2721
|
+
const rawAccepts = accept.split(",");
|
|
2722
|
+
const accepts = [];
|
|
2723
|
+
for (let i = 0; i < rawAccepts.length; i++) {
|
|
2724
|
+
const language = parseLanguage(rawAccepts[i].trim(), i);
|
|
2725
|
+
if (language) accepts.push(language);
|
|
2726
|
+
}
|
|
2727
|
+
return accepts;
|
|
2728
|
+
};
|
|
2729
|
+
/**
|
|
2730
|
+
* Parse a language from the Accept-Language header.
|
|
2731
|
+
*/
|
|
2732
|
+
const parseLanguage = (str, i) => {
|
|
2733
|
+
const match = simpleLanguageRegExp.exec(str);
|
|
2734
|
+
if (!match) return null;
|
|
2735
|
+
const prefix = match[1];
|
|
2736
|
+
const suffix = match[2];
|
|
2737
|
+
let full = prefix;
|
|
2738
|
+
if (suffix) full = `${prefix}-${suffix}`;
|
|
2739
|
+
let q = 1;
|
|
2740
|
+
if (match[3]) {
|
|
2741
|
+
const params = match[3].split(";");
|
|
2742
|
+
for (let j = 0; j < params.length; j++) {
|
|
2743
|
+
const p = params[j].split("=");
|
|
2744
|
+
if (p[0] === "q") q = parseFloat(p[1]);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
return {
|
|
2748
|
+
prefix,
|
|
2749
|
+
suffix,
|
|
2750
|
+
q,
|
|
2751
|
+
i,
|
|
2752
|
+
full
|
|
2753
|
+
};
|
|
2754
|
+
};
|
|
2755
|
+
/**
|
|
2756
|
+
* Get the priority of a language.
|
|
2757
|
+
*/
|
|
2758
|
+
const getLanguagePriority = (language, accepted, index) => {
|
|
2759
|
+
let priority = {
|
|
2760
|
+
o: -1,
|
|
2761
|
+
q: 0,
|
|
2762
|
+
s: 0,
|
|
2763
|
+
i: index
|
|
2764
|
+
};
|
|
2765
|
+
for (let i = 0; i < accepted.length; i++) {
|
|
2766
|
+
const spec = specify(language, accepted[i], index);
|
|
2767
|
+
if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) priority = spec;
|
|
2768
|
+
}
|
|
2769
|
+
return priority;
|
|
2770
|
+
};
|
|
2771
|
+
/**
|
|
2772
|
+
* Get the specificity of the language.
|
|
2773
|
+
*/
|
|
2774
|
+
const specify = (language, spec, index) => {
|
|
2775
|
+
const p = parseLanguage(language, index);
|
|
2776
|
+
if (!p) return null;
|
|
2777
|
+
let s = 0;
|
|
2778
|
+
if (spec.full.toLowerCase() === p.full.toLowerCase()) s |= 4;
|
|
2779
|
+
else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) s |= 2;
|
|
2780
|
+
else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) s |= 1;
|
|
2781
|
+
else if (spec.full !== "*") return null;
|
|
2782
|
+
return {
|
|
2783
|
+
i: index,
|
|
2784
|
+
o: spec.i,
|
|
2785
|
+
q: spec.q,
|
|
2786
|
+
s
|
|
2787
|
+
};
|
|
2788
|
+
};
|
|
2789
|
+
/**
|
|
2790
|
+
* Get the preferred languages from an Accept-Language header.
|
|
2791
|
+
*/
|
|
2792
|
+
const preferredLanguages = (accept, provided) => {
|
|
2793
|
+
const accepts = parseAcceptLanguage(accept === void 0 ? "*" : accept ?? "");
|
|
2794
|
+
if (!provided) return accepts.filter(isQuality).sort(compareSpecs).map(getFullLanguage);
|
|
2795
|
+
const priorities = provided.map((type, index) => getLanguagePriority(type, accepts, index));
|
|
2796
|
+
return priorities.filter(isQuality).sort(compareSpecs).map((priority) => provided[priorities.indexOf(priority)]);
|
|
2797
|
+
};
|
|
2798
|
+
/**
|
|
2799
|
+
* Compare two specs.
|
|
2800
|
+
*/
|
|
2801
|
+
const compareSpecs = (a, b) => b.q - a.q || (b.s ?? 0) - (a.s ?? 0) || (a.o ?? 0) - (b.o ?? 0) || a.i - b.i || 0;
|
|
2802
|
+
/**
|
|
2803
|
+
* Get full language string.
|
|
2804
|
+
*/
|
|
2805
|
+
const getFullLanguage = (spec) => spec.full;
|
|
2806
|
+
/**
|
|
2807
|
+
* Check if a spec has any quality.
|
|
2808
|
+
*/
|
|
2809
|
+
const isQuality = (spec) => spec.q > 0;
|
|
2810
|
+
/**
|
|
2811
|
+
* Detects the locale from the request headers
|
|
2812
|
+
*
|
|
2813
|
+
* Headers are provided by the browser and can be used to determine the user's preferred language
|
|
2814
|
+
*/
|
|
2815
|
+
const localeDetector = (headers, locales, defaultLocale) => {
|
|
2816
|
+
const accept = headers["accept-language"];
|
|
2817
|
+
return localeResolver(preferredLanguages(accept), locales, defaultLocale);
|
|
2818
|
+
};
|
|
2819
|
+
|
|
2820
|
+
//#endregion
|
|
2821
|
+
//#region src/localization/getBrowserLocale.tsx
|
|
2822
|
+
let LanguageDetector = /* @__PURE__ */ function(LanguageDetector$1) {
|
|
2823
|
+
LanguageDetector$1["Querystring"] = "querystring";
|
|
2824
|
+
LanguageDetector$1["Storage"] = "storage";
|
|
2825
|
+
LanguageDetector$1["Navigator"] = "navigator";
|
|
2826
|
+
LanguageDetector$1["HtmlTag"] = "htmlTag";
|
|
2827
|
+
return LanguageDetector$1;
|
|
2828
|
+
}({});
|
|
2829
|
+
const localeStorageOptions = {
|
|
2830
|
+
getCookie: (name) => {
|
|
2831
|
+
var _document$cookie$spli;
|
|
2832
|
+
return (_document$cookie$spli = document.cookie.split(";").find((c) => c.trim().startsWith(`${name}=`))) === null || _document$cookie$spli === void 0 ? void 0 : _document$cookie$spli.split("=")[1];
|
|
2833
|
+
},
|
|
2834
|
+
getLocaleStorage: (name) => localStorage.getItem(name),
|
|
2835
|
+
getSessionStorage: (name) => sessionStorage.getItem(name),
|
|
2836
|
+
isCookieEnabled: true,
|
|
2837
|
+
setCookieStore: (name, value, attributes) => cookieStore.set({
|
|
2838
|
+
name,
|
|
2839
|
+
value,
|
|
2840
|
+
path: attributes.path,
|
|
2841
|
+
domain: attributes.domain,
|
|
2842
|
+
expires: attributes.expires,
|
|
2843
|
+
sameSite: attributes.sameSite
|
|
2844
|
+
}),
|
|
2845
|
+
setCookieString: (cookie) => {
|
|
2846
|
+
document.cookie = cookie;
|
|
2847
|
+
},
|
|
2848
|
+
setSessionStorage: (name, value) => sessionStorage.setItem(name, value),
|
|
2849
|
+
setLocaleStorage: (name, value) => localStorage.setItem(name, value)
|
|
2850
|
+
};
|
|
2851
|
+
const getDefaultsOptions = () => {
|
|
2852
|
+
return {
|
|
2853
|
+
order: [
|
|
2854
|
+
LanguageDetector.Querystring,
|
|
2855
|
+
LanguageDetector.Storage,
|
|
2856
|
+
LanguageDetector.Navigator,
|
|
2857
|
+
LanguageDetector.HtmlTag
|
|
2858
|
+
],
|
|
2859
|
+
lookupQuerystring: "locale",
|
|
2860
|
+
htmlTag: typeof document !== "undefined" ? document.documentElement : null
|
|
2861
|
+
};
|
|
2862
|
+
};
|
|
2863
|
+
const detectLanguage = (order, options) => {
|
|
2864
|
+
const detected = {};
|
|
2865
|
+
const queryStringDetector = () => {
|
|
2866
|
+
if (typeof window === "undefined") return;
|
|
2867
|
+
const search = window.location.search || "";
|
|
2868
|
+
const value = new URLSearchParams(search).get(options.lookupQuerystring ?? "");
|
|
2869
|
+
if (value) detected[LanguageDetector.Querystring] = value;
|
|
2870
|
+
};
|
|
2871
|
+
const storageDetector = () => {
|
|
2872
|
+
if (typeof window === "undefined") return;
|
|
2873
|
+
const locale = getLocaleFromStorage({
|
|
2874
|
+
getCookie: (name) => {
|
|
2875
|
+
try {
|
|
2876
|
+
const cookies = document.cookie.split(";");
|
|
2877
|
+
const cookieName = `${name}=`;
|
|
2878
|
+
const cookie = cookies.find((c) => c.trim().startsWith(cookieName));
|
|
2879
|
+
if (cookie) return cookie.split("=")[1].trim();
|
|
2880
|
+
} catch {}
|
|
2881
|
+
},
|
|
2882
|
+
getSessionStorage: (name) => {
|
|
2883
|
+
try {
|
|
2884
|
+
return window.sessionStorage.getItem(name) ?? void 0;
|
|
2885
|
+
} catch {}
|
|
2886
|
+
},
|
|
2887
|
+
getLocaleStorage: (name) => {
|
|
2888
|
+
try {
|
|
2889
|
+
return window.localStorage.getItem(name) ?? void 0;
|
|
2890
|
+
} catch {}
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2893
|
+
if (locale) detected[LanguageDetector.Storage] = locale;
|
|
2894
|
+
};
|
|
2895
|
+
const navigatorDetector = () => {
|
|
2896
|
+
if (typeof navigator === "undefined") return;
|
|
2897
|
+
const { internationalization } = configuration;
|
|
2898
|
+
const languages = navigator.languages ?? [navigator.language];
|
|
2899
|
+
const locale = localeDetector({ "accept-language": languages.join(",") }, internationalization.locales, internationalization.defaultLocale);
|
|
2900
|
+
if (locale) detected[LanguageDetector.Navigator] = locale;
|
|
2901
|
+
};
|
|
2902
|
+
const htmlTagDetector = () => {
|
|
2903
|
+
const htmlTag = options.htmlTag;
|
|
2904
|
+
if (htmlTag && typeof htmlTag.getAttribute === "function") {
|
|
2905
|
+
const lang = htmlTag.getAttribute("lang");
|
|
2906
|
+
if (lang) {
|
|
2907
|
+
const { internationalization } = configuration;
|
|
2908
|
+
const locale = localeDetector({ "accept-language": lang }, internationalization.locales, internationalization.defaultLocale);
|
|
2909
|
+
detected[LanguageDetector.HtmlTag] = locale;
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
const detectors = {
|
|
2914
|
+
[LanguageDetector.Querystring]: queryStringDetector,
|
|
2915
|
+
[LanguageDetector.Storage]: storageDetector,
|
|
2916
|
+
[LanguageDetector.Navigator]: navigatorDetector,
|
|
2917
|
+
[LanguageDetector.HtmlTag]: htmlTagDetector
|
|
2918
|
+
};
|
|
2919
|
+
order.forEach((detectorName) => {
|
|
2920
|
+
var _detectors$detectorNa;
|
|
2921
|
+
(_detectors$detectorNa = detectors[detectorName]) === null || _detectors$detectorNa === void 0 || _detectors$detectorNa.call(detectors);
|
|
2922
|
+
});
|
|
2923
|
+
return detected;
|
|
2924
|
+
};
|
|
2925
|
+
const getFirstAvailableLocale = (locales, order) => {
|
|
2926
|
+
const { internationalization } = configuration;
|
|
2927
|
+
for (const detector of order) {
|
|
2928
|
+
const locale = locales[detector];
|
|
2929
|
+
if (locale && internationalization.locales.includes(locale)) return locale;
|
|
2930
|
+
}
|
|
2931
|
+
return (internationalization === null || internationalization === void 0 ? void 0 : internationalization.defaultLocale) ?? Locales.ENGLISH;
|
|
2932
|
+
};
|
|
2933
|
+
/**
|
|
2934
|
+
* Core language detector function for browser environments.
|
|
2935
|
+
*
|
|
2936
|
+
* Detects the user's preferred locale by checking multiple sources in order:
|
|
2937
|
+
* 1. Query string parameter
|
|
2938
|
+
* 2. Storage (cookies, localStorage, sessionStorage) - uses getLocaleFromStorage
|
|
2939
|
+
* 3. Navigator languages - uses localeDetector
|
|
2940
|
+
* 4. HTML lang attribute - uses localeDetector
|
|
2941
|
+
*
|
|
2942
|
+
* @param userOptions - Optional configuration for detection order and lookup keys
|
|
2943
|
+
* @returns The detected locale or the default locale
|
|
2944
|
+
*
|
|
2945
|
+
* @example
|
|
2946
|
+
* const locale = getBrowserLocale({ order: [LanguageDetector.Storage, LanguageDetector.Navigator] });
|
|
2947
|
+
*/
|
|
2948
|
+
const getBrowserLocale = (userOptions = {}) => {
|
|
2949
|
+
const options = {
|
|
2950
|
+
...getDefaultsOptions(),
|
|
2951
|
+
...userOptions
|
|
2952
|
+
};
|
|
2953
|
+
return getFirstAvailableLocale(detectLanguage(options.order ?? [], options), options.order ?? []);
|
|
2954
|
+
};
|
|
2955
|
+
|
|
2956
|
+
//#endregion
|
|
2957
|
+
//#region src/localization/getHTMLTextDir.ts
|
|
2958
|
+
/**
|
|
2959
|
+
* Returns the text direction of the given locale.
|
|
2960
|
+
*
|
|
2961
|
+
* Example:
|
|
2962
|
+
*
|
|
2963
|
+
* getHTMLTextDir('en-US') // 'ltr'
|
|
2964
|
+
* getHTMLTextDir('en') // 'ltr'
|
|
2965
|
+
* getHTMLTextDir('fr-CA') // 'ltr'
|
|
2966
|
+
* getHTMLTextDir('fr') // 'ltr'
|
|
2967
|
+
*
|
|
2968
|
+
* @param locale The locale to get the text direction for.
|
|
2969
|
+
* @returns The text direction of the given locale.
|
|
2970
|
+
*/
|
|
2971
|
+
const getHTMLTextDir = (locale) => {
|
|
2972
|
+
switch (locale) {
|
|
2973
|
+
case Locales.ARABIC:
|
|
2974
|
+
case Locales.FARSI:
|
|
2975
|
+
case Locales.URDU:
|
|
2976
|
+
case Locales.PASHTO:
|
|
2977
|
+
case Locales.SYRIAC:
|
|
2978
|
+
case Locales.ARABIC_UNITED_ARAB_EMIRATES:
|
|
2979
|
+
case Locales.ARABIC_BAHRAIN:
|
|
2980
|
+
case Locales.ARABIC_ALGERIA:
|
|
2981
|
+
case Locales.ARABIC_EGYPT:
|
|
2982
|
+
case Locales.ARABIC_IRAQ:
|
|
2983
|
+
case Locales.ARABIC_JORDAN:
|
|
2984
|
+
case Locales.ARABIC_KUWAIT:
|
|
2985
|
+
case Locales.ARABIC_LEBANON:
|
|
2986
|
+
case Locales.ARABIC_LIBYA:
|
|
2987
|
+
case Locales.ARABIC_MOROCCO:
|
|
2988
|
+
case Locales.ARABIC_OMAN:
|
|
2989
|
+
case Locales.ARABIC_QATAR:
|
|
2990
|
+
case Locales.ARABIC_SAUDI_ARABIA:
|
|
2991
|
+
case Locales.ARABIC_SYRIA:
|
|
2992
|
+
case Locales.ARABIC_TUNISIA:
|
|
2993
|
+
case Locales.ARABIC_YEMEN:
|
|
2994
|
+
case Locales.FARSI_IRAN:
|
|
2995
|
+
case Locales.URDU_ISLAMIC_REPUBLIC_OF_PAKISTAN:
|
|
2996
|
+
case Locales.PASHTO_AFGHANISTAN:
|
|
2997
|
+
case Locales.SYRIAC_SYRIA: return "rtl";
|
|
2998
|
+
default: return "ltr";
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
3001
|
+
|
|
3002
|
+
//#endregion
|
|
3003
|
+
//#region src/utils/checkIsURLAbsolute.ts
|
|
3004
|
+
const checkIsURLAbsolute = (url) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url);
|
|
3005
|
+
|
|
3006
|
+
//#endregion
|
|
3007
|
+
//#region src/localization/getLocaleFromPath.ts
|
|
3008
|
+
/**
|
|
3009
|
+
* Extracts the locale segment from the given URL or pathname if present.
|
|
3010
|
+
* If no locale is present, returns the default locale (en).
|
|
3011
|
+
*
|
|
3012
|
+
* Example:
|
|
3013
|
+
*
|
|
3014
|
+
* ```ts
|
|
3015
|
+
* getLocaleFromPath('/en/dashboard') // Returns 'en'
|
|
3016
|
+
* getLocaleFromPath('/fr/dashboard') // Returns 'fr'
|
|
3017
|
+
* getLocaleFromPath('/dashboard') // Returns 'en'
|
|
3018
|
+
* getLocaleFromPath('dashboard') // Returns 'en'
|
|
3019
|
+
* getLocaleFromPath('https://example.com/es/dashboard') // Returns 'es'
|
|
3020
|
+
* getLocaleFromPath('https://example.com/fr/dashboard') // Returns 'fr'
|
|
3021
|
+
* getLocaleFromPath('https://example.com/dashboard') // Returns 'en'
|
|
3022
|
+
* ```
|
|
3023
|
+
*
|
|
3024
|
+
* @param inputUrl - The complete URL string or pathname to process.
|
|
3025
|
+
* @returns The detected locale or default (en) if no locale is found
|
|
3026
|
+
*/
|
|
3027
|
+
const getLocaleFromPath = (inputUrl) => {
|
|
3028
|
+
const { defaultLocale, locales } = (configuration === null || configuration === void 0 ? void 0 : configuration.internationalization) ?? {};
|
|
3029
|
+
if (!defaultLocale || !locales) return Locales.ENGLISH;
|
|
3030
|
+
const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);
|
|
3031
|
+
let fixedInputUrl = inputUrl;
|
|
3032
|
+
if (inputUrl.endsWith("/")) fixedInputUrl = inputUrl.slice(0, -1);
|
|
3033
|
+
const pathname = (isAbsoluteUrl ? new URL(fixedInputUrl) : new URL(fixedInputUrl, "http://example.com")).pathname;
|
|
3034
|
+
if (!pathname.startsWith("/")) return defaultLocale;
|
|
3035
|
+
const firstSegment = pathname.split("/")[1];
|
|
3036
|
+
if (firstSegment && locales.includes(firstSegment)) return firstSegment;
|
|
3037
|
+
return defaultLocale;
|
|
3038
|
+
};
|
|
3039
|
+
|
|
3040
|
+
//#endregion
|
|
3041
|
+
//#region src/localization/getLocaleLang.ts
|
|
3042
|
+
/**
|
|
3043
|
+
* Returns the language code of the given locale for locales including the country code.
|
|
3044
|
+
*
|
|
3045
|
+
* Example:
|
|
3046
|
+
*
|
|
3047
|
+
* getLocaleLang('en-US') // 'en'
|
|
3048
|
+
* getLocaleLang('en') // 'en'
|
|
3049
|
+
* getLocaleLang('fr-CA') // 'fr'
|
|
3050
|
+
* getLocaleLang('fr') // 'fr'
|
|
3051
|
+
*
|
|
3052
|
+
* @param locale The locale to get the language code for.
|
|
3053
|
+
* @returns The language code of the given locale.
|
|
3054
|
+
*/
|
|
3055
|
+
const getLocaleLang = (locale) => (locale === null || locale === void 0 ? void 0 : locale.split("-")[0]) ?? "";
|
|
3056
|
+
|
|
3057
|
+
//#endregion
|
|
3058
|
+
//#region src/localization/getLocaleName.ts
|
|
3059
|
+
const getLocaleName = (displayLocale, targetLocale = displayLocale) => {
|
|
3060
|
+
return new CachedIntl.DisplayNames(targetLocale, { type: "language" }).of(displayLocale) ?? "Unknown locale";
|
|
3061
|
+
};
|
|
3062
|
+
|
|
3063
|
+
//#endregion
|
|
3064
|
+
//#region src/localization/getPathWithoutLocale.ts
|
|
3065
|
+
/**
|
|
3066
|
+
* Removes the locale segment from the given URL or pathname if present.
|
|
3067
|
+
* Also removes locale from search parameters if present.
|
|
3068
|
+
*
|
|
3069
|
+
* This function get the locales from the configuration if not provided.
|
|
3070
|
+
*
|
|
3071
|
+
* Example:
|
|
3072
|
+
*
|
|
3073
|
+
* ```ts
|
|
3074
|
+
* getPathWithoutLocale('/en/dashboard') // Returns '/dashboard'
|
|
3075
|
+
* getPathWithoutLocale('/fr/dashboard') // Returns '/dashboard'
|
|
3076
|
+
* getPathWithoutLocale('/dashboard') // Returns '/dashboard'
|
|
3077
|
+
* getPathWithoutLocale('dashboard') // Returns 'dashboard'
|
|
3078
|
+
* getPathWithoutLocale('/dashboard?locale=fr') // Returns '/dashboard'
|
|
3079
|
+
* getPathWithoutLocale('https://example.com/en/dashboard') // Returns 'https://example.com/dashboard'
|
|
3080
|
+
* getPathWithoutLocale('https://example.com/fr/dashboard') // Returns 'https://example.com/dashboard'
|
|
3081
|
+
* getPathWithoutLocale('https://example.com/dashboard') // Returns 'https://example.com/dashboard'
|
|
3082
|
+
* getPathWithoutLocale('https://example.com/dashboard?locale=fr') // Returns 'https://example.com/dashboard'
|
|
3083
|
+
* ```
|
|
3084
|
+
*
|
|
3085
|
+
* @param inputUrl - The complete URL string or pathname to process.
|
|
3086
|
+
* @param locales - Optional array of supported locales. Defaults to `localesDefault`.
|
|
3087
|
+
* @returns The URL string or pathname without the locale segment or locale search parameter.
|
|
3088
|
+
*/
|
|
3089
|
+
const getPathWithoutLocale = (inputUrl, locales = (() => {
|
|
3090
|
+
var _configuration$intern;
|
|
3091
|
+
return configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.locales;
|
|
3092
|
+
})()) => {
|
|
3093
|
+
const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);
|
|
3094
|
+
let fixedInputUrl = inputUrl;
|
|
3095
|
+
if (inputUrl.endsWith("/")) fixedInputUrl = inputUrl.slice(0, -1);
|
|
3096
|
+
const url = isAbsoluteUrl ? new URL(fixedInputUrl) : new URL(fixedInputUrl, "http://example.com");
|
|
3097
|
+
const pathname = url.pathname;
|
|
3098
|
+
if (!pathname.startsWith("/")) url.pathname = `/${pathname}`;
|
|
3099
|
+
const pathSegments = pathname.split("/");
|
|
3100
|
+
const firstSegment = pathSegments[1];
|
|
3101
|
+
if (locales === null || locales === void 0 ? void 0 : locales.includes(firstSegment)) {
|
|
3102
|
+
pathSegments.splice(1, 1);
|
|
3103
|
+
url.pathname = pathSegments.join("/") ?? "/";
|
|
3104
|
+
}
|
|
3105
|
+
const searchParams = new URLSearchParams(url.search);
|
|
3106
|
+
if (searchParams.has("locale")) {
|
|
3107
|
+
searchParams.delete("locale");
|
|
3108
|
+
url.search = searchParams.toString();
|
|
3109
|
+
}
|
|
3110
|
+
if (isAbsoluteUrl) return url.toString();
|
|
3111
|
+
return url.toString().replace("http://example.com", "");
|
|
3112
|
+
};
|
|
3113
|
+
|
|
3114
|
+
//#endregion
|
|
3115
|
+
//#region src/localization/getMultilingualUrls.ts
|
|
3116
|
+
var _configuration$intern2$1, _configuration$routin$2;
|
|
3117
|
+
/**
|
|
3118
|
+
* Generates multilingual URLs by prefixing the given URL with each supported locale
|
|
3119
|
+
* or adding search parameters based on the routing mode.
|
|
3120
|
+
* Handles both absolute and relative URLs appropriately.
|
|
3121
|
+
*
|
|
3122
|
+
* This function gets the locales, default locale, and routing mode from the configuration if not provided.
|
|
3123
|
+
*
|
|
3124
|
+
* Example:
|
|
3125
|
+
*
|
|
3126
|
+
* ```ts
|
|
3127
|
+
* // prefix-no-default mode
|
|
3128
|
+
* getMultilingualUrls('/dashboard', ['en', 'fr'], 'en', 'prefix-no-default')
|
|
3129
|
+
* // Returns { en: '/dashboard', fr: '/fr/dashboard' }
|
|
3130
|
+
*
|
|
3131
|
+
* // prefix-all mode
|
|
3132
|
+
* getMultilingualUrls('/dashboard', ['en', 'fr'], 'en', 'prefix-all')
|
|
3133
|
+
* // Returns { en: '/en/dashboard', fr: '/fr/dashboard' }
|
|
3134
|
+
*
|
|
3135
|
+
* // search-params mode
|
|
3136
|
+
* getMultilingualUrls('/dashboard', ['en', 'fr'], 'en', 'search-params')
|
|
3137
|
+
* // Returns { en: '/dashboard?locale=en', fr: '/dashboard?locale=fr' }
|
|
3138
|
+
*
|
|
3139
|
+
* // no-prefix mode
|
|
3140
|
+
* getMultilingualUrls('/dashboard', ['en', 'fr'], 'en', 'no-prefix')
|
|
3141
|
+
* // Returns { en: '/dashboard', fr: '/dashboard' }
|
|
3142
|
+
* ```
|
|
3143
|
+
*
|
|
3144
|
+
* @param url - The original URL string to be processed.
|
|
3145
|
+
* @param locales - Optional array of supported locales. Defaults to configured locales.
|
|
3146
|
+
* @param defaultLocale - The default locale. Defaults to configured default locale.
|
|
3147
|
+
* @param mode - URL routing mode for locale handling. Defaults to configured mode.
|
|
3148
|
+
* @returns An object mapping each locale to its corresponding multilingual URL.
|
|
3149
|
+
*/
|
|
3150
|
+
const getMultilingualUrls = (url, locales = (() => {
|
|
3151
|
+
var _configuration$intern;
|
|
3152
|
+
return configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.locales;
|
|
3153
|
+
})(), defaultLocale = configuration === null || configuration === void 0 || (_configuration$intern2$1 = configuration.internationalization) === null || _configuration$intern2$1 === void 0 ? void 0 : _configuration$intern2$1.defaultLocale, mode = configuration === null || configuration === void 0 || (_configuration$routin$2 = configuration.routing) === null || _configuration$routin$2 === void 0 ? void 0 : _configuration$routin$2.mode) => {
|
|
3154
|
+
const urlWithoutLocale = getPathWithoutLocale(url, locales);
|
|
3155
|
+
const isAbsoluteUrl = checkIsURLAbsolute(urlWithoutLocale);
|
|
3156
|
+
const parsedUrl = isAbsoluteUrl ? new URL(urlWithoutLocale) : new URL(urlWithoutLocale, "http://example.com");
|
|
3157
|
+
let pathname = parsedUrl.pathname;
|
|
3158
|
+
if (!pathname.startsWith("/")) pathname = `/${pathname}`;
|
|
3159
|
+
const baseUrl = isAbsoluteUrl ? `${parsedUrl.protocol}//${parsedUrl.host}` : "";
|
|
3160
|
+
const routingMode = mode ?? DefaultValues.Routing.ROUTING_MODE;
|
|
3161
|
+
return (locales ?? []).reduce((acc, locale) => {
|
|
3162
|
+
const isDefaultLocale = (locale === null || locale === void 0 ? void 0 : locale.toString()) === (defaultLocale === null || defaultLocale === void 0 ? void 0 : defaultLocale.toString());
|
|
3163
|
+
let localizedUrl;
|
|
3164
|
+
if (routingMode === "search-params") {
|
|
3165
|
+
const searchParams = new URLSearchParams(parsedUrl.search);
|
|
3166
|
+
searchParams.set("locale", locale.toString());
|
|
3167
|
+
const queryString = searchParams.toString();
|
|
3168
|
+
const pathWithQuery = queryString ? `${pathname}?${queryString}` : pathname;
|
|
3169
|
+
localizedUrl = isAbsoluteUrl ? `${baseUrl}${pathWithQuery}${parsedUrl.hash}` : `${pathWithQuery}${parsedUrl.hash}`;
|
|
3170
|
+
} else if (routingMode === "no-prefix") localizedUrl = isAbsoluteUrl ? `${baseUrl}${pathname}${parsedUrl.search}${parsedUrl.hash}` : `${pathname}${parsedUrl.search}${parsedUrl.hash}`;
|
|
3171
|
+
else {
|
|
3172
|
+
let localizedPath = routingMode === "prefix-all" || routingMode === "prefix-no-default" && !isDefaultLocale ? `/${locale}${pathname}` : pathname;
|
|
3173
|
+
if (localizedPath.length > 1 && localizedPath.endsWith("/")) localizedPath = localizedPath.slice(0, -1);
|
|
3174
|
+
localizedUrl = isAbsoluteUrl ? `${baseUrl}${localizedPath}${parsedUrl.search}${parsedUrl.hash}` : `${localizedPath}${parsedUrl.search}${parsedUrl.hash}`;
|
|
3175
|
+
}
|
|
3176
|
+
acc[locale] = localizedUrl;
|
|
3177
|
+
return acc;
|
|
3178
|
+
}, {});
|
|
3179
|
+
};
|
|
3180
|
+
|
|
3181
|
+
//#endregion
|
|
3182
|
+
//#region src/localization/getLocalizedUrl.ts
|
|
3183
|
+
var _configuration$intern2, _configuration$routin$1;
|
|
3184
|
+
/**
|
|
3185
|
+
* Generate URL by prefixing the given URL with the referenced locale or adding search parameters
|
|
3186
|
+
* based on the routing mode. Handles both absolute and relative URLs appropriately.
|
|
3187
|
+
*
|
|
3188
|
+
* This function gets the locales, default locale, and routing mode from the configuration if not provided.
|
|
3189
|
+
*
|
|
3190
|
+
* Example:
|
|
3191
|
+
*
|
|
3192
|
+
* ```ts
|
|
3193
|
+
* // prefix-no-default mode
|
|
3194
|
+
* getLocalizedUrl('/about', 'fr', ['en', 'fr'], 'en', 'prefix-no-default');
|
|
3195
|
+
* // Returns '/fr/about' for the French locale
|
|
3196
|
+
* // Returns '/about' for the English locale (default)
|
|
3197
|
+
*
|
|
3198
|
+
* // prefix-all mode
|
|
3199
|
+
* getLocalizedUrl('/about', 'en', ['en', 'fr'], 'en', 'prefix-all');
|
|
3200
|
+
* // Returns '/en/about' for the English locale
|
|
3201
|
+
* // Returns '/fr/about' for the French locale
|
|
3202
|
+
*
|
|
3203
|
+
* // search-params mode
|
|
3204
|
+
* getLocalizedUrl('/about', 'fr', ['en', 'fr'], 'en', 'search-params');
|
|
3205
|
+
* // Returns '/about?locale=fr' for the French locale
|
|
3206
|
+
*
|
|
3207
|
+
* // no-prefix mode
|
|
3208
|
+
* getLocalizedUrl('/about', 'fr', ['en', 'fr'], 'en', 'no-prefix');
|
|
3209
|
+
* // Returns '/about' for any locale
|
|
3210
|
+
* ```
|
|
3211
|
+
*
|
|
3212
|
+
* @param url - The original URL string to be processed.
|
|
3213
|
+
* @param currentLocale - The current locale.
|
|
3214
|
+
* @param locales - Optional array of supported locales. Defaults to configured locales.
|
|
3215
|
+
* @param defaultLocale - The default locale. Defaults to configured default locale.
|
|
3216
|
+
* @param mode - URL routing mode for locale handling. Defaults to configured mode.
|
|
3217
|
+
* @returns The localized URL for the current locale.
|
|
3218
|
+
*/
|
|
3219
|
+
const getLocalizedUrl = (url, currentLocale, locales = (() => {
|
|
3220
|
+
var _configuration$intern;
|
|
3221
|
+
return configuration === null || configuration === void 0 || (_configuration$intern = configuration.internationalization) === null || _configuration$intern === void 0 ? void 0 : _configuration$intern.locales;
|
|
3222
|
+
})(), defaultLocale = configuration === null || configuration === void 0 || (_configuration$intern2 = configuration.internationalization) === null || _configuration$intern2 === void 0 ? void 0 : _configuration$intern2.defaultLocale, mode = configuration === null || configuration === void 0 || (_configuration$routin$1 = configuration.routing) === null || _configuration$routin$1 === void 0 ? void 0 : _configuration$routin$1.mode) => {
|
|
3223
|
+
return getMultilingualUrls(url, locales, defaultLocale, mode)[currentLocale] ?? url;
|
|
3224
|
+
};
|
|
3225
|
+
|
|
3226
|
+
//#endregion
|
|
3227
|
+
//#region src/localization/localeMapper.ts
|
|
3228
|
+
var _configuration$routin, _configuration$routin2, _configuration$routin3;
|
|
3229
|
+
/**
|
|
3230
|
+
* Determine if the locale should be prefixed in the URL based on routing mode
|
|
3231
|
+
*/
|
|
3232
|
+
const shouldPrefixLocale = (locale, defaultLocale, mode) => {
|
|
3233
|
+
if (mode === "no-prefix" || mode === "search-params") return false;
|
|
3234
|
+
if (mode === "prefix-all") return true;
|
|
3235
|
+
return locale !== defaultLocale;
|
|
3236
|
+
};
|
|
3237
|
+
/**
|
|
3238
|
+
* Map the locale data to an array of objects
|
|
3239
|
+
*
|
|
3240
|
+
* @example
|
|
3241
|
+
* ```ts
|
|
3242
|
+
* const routes = localeMap((localizedData) =>
|
|
3243
|
+
* ({
|
|
3244
|
+
* path: localizedData.urlPrefix,
|
|
3245
|
+
* name: localizedData.locale,
|
|
3246
|
+
* isDefault: localizedData.isDefault,
|
|
3247
|
+
* locales: localizedData.locales,
|
|
3248
|
+
* defaultLocale: localizedData.defaultLocale,
|
|
3249
|
+
* }),
|
|
3250
|
+
* );
|
|
3251
|
+
*
|
|
3252
|
+
* // Result
|
|
3253
|
+
* [
|
|
3254
|
+
* { path: '/', name: 'en', isDefault: true, locales: ['en'], defaultLocale: 'en', urlPrefix: '' },
|
|
3255
|
+
* { path: '/fr', name: 'fr', isDefault: false, locales: ['fr'], defaultLocale: 'en', urlPrefix: '/fr' },
|
|
3256
|
+
* { path: '/es', name: 'es', isDefault: false, locales: ['es'], defaultLocale: 'en', urlPrefix: '/es' },
|
|
3257
|
+
* ]
|
|
3258
|
+
* ```
|
|
3259
|
+
*
|
|
3260
|
+
* @param mapper - The mapper function that returns an object
|
|
3261
|
+
* @returns An array of objects
|
|
3262
|
+
*/
|
|
3263
|
+
const localeMap = (mapper, locales = (() => {
|
|
3264
|
+
return configuration === null || configuration === void 0 ? void 0 : configuration.internationalization.locales;
|
|
3265
|
+
})() ?? [], defaultLocale = (configuration === null || configuration === void 0 ? void 0 : configuration.internationalization.defaultLocale) ?? Locales.ENGLISH, mode = (configuration === null || configuration === void 0 || (_configuration$routin = configuration.routing) === null || _configuration$routin === void 0 ? void 0 : _configuration$routin.mode) ?? "prefix-no-default") => (locales ?? []).map((locale) => mapper({
|
|
3266
|
+
locale,
|
|
3267
|
+
defaultLocale,
|
|
3268
|
+
locales,
|
|
3269
|
+
isDefault: locale === defaultLocale,
|
|
3270
|
+
urlPrefix: shouldPrefixLocale(locale, defaultLocale, mode) ? `/${locale}` : ""
|
|
3271
|
+
}));
|
|
3272
|
+
/**
|
|
3273
|
+
* Flatten the locale map into a single array of objects
|
|
3274
|
+
*
|
|
3275
|
+
* @example
|
|
3276
|
+
* ```ts
|
|
3277
|
+
* const routes = localeMap((localizedData) =>
|
|
3278
|
+
* [{
|
|
3279
|
+
* path: localizedData.urlPrefix,
|
|
3280
|
+
* name: localizedData.locale,
|
|
3281
|
+
* isDefault: localizedData.isDefault,
|
|
3282
|
+
* locales: localizedData.locales,
|
|
3283
|
+
* defaultLocale: localizedData.defaultLocale,
|
|
3284
|
+
* }],
|
|
3285
|
+
* );
|
|
3286
|
+
*
|
|
3287
|
+
* // Result
|
|
3288
|
+
* [
|
|
3289
|
+
* path: '/', name: 'en', isDefault: true, locales: ['en'], defaultLocale: 'en', urlPrefix: '' ,
|
|
3290
|
+
* path: '/fr', name: 'fr', isDefault: false, locales: ['fr'], defaultLocale: 'en', urlPrefix: '/fr' ,
|
|
3291
|
+
* path: '/es', name: 'es', isDefault: false, locales: ['es'], defaultLocale: 'en', urlPrefix: '/es' ,
|
|
3292
|
+
* ]
|
|
3293
|
+
* ```
|
|
3294
|
+
*
|
|
3295
|
+
* @param mapper - The mapper function that returns an array of objects
|
|
3296
|
+
* @returns An array of objects
|
|
3297
|
+
*/
|
|
3298
|
+
const localeFlatMap = (mapper, locales = (() => {
|
|
3299
|
+
return configuration === null || configuration === void 0 ? void 0 : configuration.internationalization.locales;
|
|
3300
|
+
})() ?? [], defaultLocale = (configuration === null || configuration === void 0 ? void 0 : configuration.internationalization.defaultLocale) ?? Locales.ENGLISH, mode = (configuration === null || configuration === void 0 || (_configuration$routin2 = configuration.routing) === null || _configuration$routin2 === void 0 ? void 0 : _configuration$routin2.mode) ?? "prefix-no-default") => locales.flatMap((locale) => mapper({
|
|
3301
|
+
locale,
|
|
3302
|
+
defaultLocale,
|
|
3303
|
+
locales,
|
|
3304
|
+
isDefault: locale === defaultLocale,
|
|
3305
|
+
urlPrefix: shouldPrefixLocale(locale, defaultLocale, mode) ? `/${locale}` : ""
|
|
3306
|
+
}));
|
|
3307
|
+
/**
|
|
3308
|
+
* Creates a record object mapping locales to values transformed by the mapper function
|
|
3309
|
+
*
|
|
3310
|
+
* @example
|
|
3311
|
+
* ```ts
|
|
3312
|
+
* const translations = localeRecord(({ locale }) =>
|
|
3313
|
+
* require(`./translations/${locale}.json`)
|
|
3314
|
+
* );
|
|
3315
|
+
*
|
|
3316
|
+
* // Result
|
|
3317
|
+
*
|
|
3318
|
+
* en: ... , // Content of translations/en.json
|
|
3319
|
+
* fr: ... , // Content of translations/fr.json
|
|
3320
|
+
* es: ...
|
|
3321
|
+
*
|
|
3322
|
+
* ```
|
|
3323
|
+
*
|
|
3324
|
+
* @param mapper - Function that takes locale data and returns a value for that locale
|
|
3325
|
+
* @param locales - Array of locale codes to map over (defaults to configured locales)
|
|
3326
|
+
* @param defaultLocale - The default locale (defaults to configured default)
|
|
3327
|
+
* @param mode - URL routing mode for locale handling (defaults to configured value)
|
|
3328
|
+
* @returns Record mapping locale codes to mapped values
|
|
3329
|
+
*/
|
|
3330
|
+
const localeRecord = (mapper, locales = (() => {
|
|
3331
|
+
return configuration === null || configuration === void 0 ? void 0 : configuration.internationalization.locales;
|
|
3332
|
+
})() ?? [], defaultLocale = (configuration === null || configuration === void 0 ? void 0 : configuration.internationalization.defaultLocale) ?? Locales.ENGLISH, mode = (configuration === null || configuration === void 0 || (_configuration$routin3 = configuration.routing) === null || _configuration$routin3 === void 0 ? void 0 : _configuration$routin3.mode) ?? "prefix-no-default") => (locales ?? []).reduce((acc, locale) => {
|
|
3333
|
+
acc[locale] = mapper({
|
|
3334
|
+
locale,
|
|
3335
|
+
defaultLocale,
|
|
3336
|
+
locales,
|
|
3337
|
+
isDefault: locale === defaultLocale,
|
|
3338
|
+
urlPrefix: shouldPrefixLocale(locale, defaultLocale, mode) ? `/${locale}` : ""
|
|
3339
|
+
});
|
|
3340
|
+
return acc;
|
|
3341
|
+
}, {});
|
|
3342
|
+
|
|
3343
|
+
//#endregion
|
|
3344
|
+
//#region src/utils/isSameKeyPath.ts
|
|
3345
|
+
const isSameKeyPath = (keyPath1, keyPath2) => keyPath1.every((element, index) => keyPath2[index] && keyPath2[index].key === element.key && keyPath2[index].type === element.type);
|
|
3346
|
+
|
|
3347
|
+
//#endregion
|
|
3348
|
+
export { CachedIntl, CachedIntl as Intl, LocaleStorage, buildMaskPlugin, checkIsURLAbsolute, checkMissingLocalesPlugin, compact, condition as cond, conditionPlugin, createCachedIntl, currency, date, deepTransformNode, editDictionaryByKeyPath, enumeration as enu, enumerationPlugin, filePlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, findMatchingCondition, gender, genderPlugin, getBrowserLocale, getCondition, getContent, getContentNodeByKeyPath, getDefaultNode, getDictionary, getEmptyNode, getEnumeration, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getHTMLTextDir, getInsertionValues, getIntlayer, getLocaleFromPath, getLocaleFromStorage, getLocaleLang, getLocaleName, getLocalizedContent, getLocalizedUrl, getMarkdownMetadata, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getMultilingualUrls, getNesting, getNodeChildren, getNodeType, getPathWithoutLocale, getPerLocaleDictionary, getReplacedValuesContent, getSplittedContent, getSplittedDictionaryContent, getStorageAttributes, getTranslation, insertion as insert, insertContentInDictionary, insertionPlugin, isSameKeyPath, isValidElement, list, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, localeStorageOptions, markdown as md, mergeDictionaries, nesting as nest, nestedPlugin, normalizeDictionaries, normalizeDictionary, number, orderDictionaries, parseYaml, percentage, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, setLocaleInStorage, translation as t, translationPlugin, units, updateNodeChildren };
|