intor 2.3.4 → 2.3.6
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 +75 -6
- package/dist/{export → core/export}/index.js +1 -0
- package/dist/{export → core/export}/server/index.js +1 -1
- package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +2 -5
- package/dist/core/src/server/shared/logger/global-logger-pool.js +16 -0
- package/dist/core/src/server/shared/messages/global-messages-pool.js +27 -0
- package/dist/{src → core/src}/shared/utils/deep-merge.js +4 -9
- package/dist/{src → next/src}/adapters/next/navigation/link.js +6 -3
- package/dist/{src → next/src}/adapters/next/navigation/redirect.js +5 -5
- package/dist/{src → next/src}/adapters/next/navigation/use-pathname.js +1 -6
- package/dist/{src → next/src}/adapters/next/navigation/use-router.js +6 -3
- package/dist/next/src/client/react/contexts/translator/context.js +3 -0
- package/dist/next/src/client/react/contexts/translator-runtime/context.js +3 -0
- package/dist/{src → next/src}/client/react/navigation/use-navigation-strategy.js +1 -6
- package/dist/next/src/client/react/navigation/use-navigation-target.js +20 -0
- package/dist/next/src/config/constants/cache.constants.js +7 -0
- package/dist/next/src/server/messages/load-local-messages/load-local-messages.js +93 -0
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +78 -0
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +85 -0
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.js +12 -0
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.js +21 -0
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +31 -0
- package/dist/next/src/server/messages/load-messages.js +77 -0
- package/dist/next/src/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +55 -0
- package/dist/next/src/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +25 -0
- package/dist/next/src/server/messages/load-remote-messages/load-remote-messages.js +110 -0
- package/dist/next/src/server/messages/shared/utils/is-valid-messages.js +36 -0
- package/dist/next/src/server/shared/logger/get-logger.js +39 -0
- package/dist/next/src/server/translator/get-translator.js +35 -0
- package/dist/next/src/shared/constants/prefix-placeholder.js +4 -0
- package/dist/next/src/shared/utils/deep-merge.js +31 -0
- package/dist/next/src/shared/utils/normalizers/normalize-cache-key.js +45 -0
- package/dist/next/src/shared/utils/normalizers/normalize-pathname.js +43 -0
- package/dist/next/src/shared/utils/pathname/get-unprefixed-pathname.js +39 -0
- package/dist/next/src/shared/utils/pathname/locale-prefix-pathname.js +38 -0
- package/dist/next/src/shared/utils/pathname/localize-pathname.js +36 -0
- package/dist/next/src/shared/utils/pathname/standardize-pathname.js +30 -0
- package/dist/{export → react/export}/react/index.js +13 -5
- package/dist/react/src/client/react/contexts/messages/context.js +5 -0
- package/dist/{src → react/src}/client/react/contexts/messages/utils/use-refetch-messages.js +1 -1
- package/dist/react/src/client/react/contexts/translator/context.js +5 -0
- package/dist/{src → react/src}/client/react/contexts/translator/provider.js +11 -3
- package/dist/react/src/client/react/contexts/translator-runtime/context.js +5 -0
- package/dist/react/src/client/react/render/create-react-renderer.js +36 -0
- package/dist/react/src/client/react/render/render-rich-message-react.js +22 -0
- package/dist/react/src/client/react/translator/create-t-rich.js +23 -0
- package/dist/react/src/client/react/translator/t.js +15 -0
- package/dist/{src → react/src}/client/react/translator/use-translator.js +4 -2
- package/dist/react/src/client/shared/utils/build-cookie-string.js +30 -0
- package/dist/react/src/client/shared/utils/locale/set-locale-cookie-browser.js +18 -0
- package/dist/react/src/config/constants/cache.constants.js +7 -0
- package/dist/react/src/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +55 -0
- package/dist/react/src/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +25 -0
- package/dist/react/src/server/messages/load-remote-messages/load-remote-messages.js +110 -0
- package/dist/react/src/server/messages/shared/utils/is-valid-messages.js +36 -0
- package/dist/react/src/server/shared/logger/get-logger.js +39 -0
- package/dist/react/src/server/shared/logger/global-logger-pool.js +8 -0
- package/dist/react/src/server/shared/messages/global-messages-pool.js +10 -0
- package/dist/react/src/shared/utils/deep-merge.js +31 -0
- package/dist/react/src/shared/utils/normalizers/normalize-cache-key.js +45 -0
- package/dist/react/src/shared/utils/normalizers/normalize-locale.js +59 -0
- package/dist/{export → types/export}/index.d.ts +1 -1
- package/dist/types/export/internal/index.d.ts +1 -0
- package/dist/types/export/react/index.d.ts +1 -0
- package/dist/{export → types/export}/server/index.d.ts +1 -1
- package/dist/{src → types/src}/adapters/next/server/get-translator.d.ts +2 -5
- package/dist/types/src/client/react/contexts/index.d.ts +6 -0
- package/dist/types/src/client/react/index.d.ts +4 -0
- package/dist/{src → types/src}/client/react/navigation/use-navigation-target.d.ts +1 -1
- package/dist/types/src/client/react/render/create-react-renderer.d.ts +17 -0
- package/dist/types/src/client/react/render/index.d.ts +2 -0
- package/dist/types/src/client/react/render/render-rich-message-react.d.ts +13 -0
- package/dist/types/src/client/react/render/types.d.ts +17 -0
- package/dist/types/src/client/react/translator/create-t-rich.d.ts +15 -0
- package/dist/types/src/client/react/translator/index.d.ts +2 -0
- package/dist/types/src/client/react/translator/t.d.ts +27 -0
- package/dist/types/src/client/react/translator/translator-instance.d.ts +12 -0
- package/dist/types/src/client/react/translator/use-translator.d.ts +8 -0
- package/dist/types/src/client/shared/types/index.d.ts +1 -0
- package/dist/types/src/client/shared/types/translator-instance.d.ts +11 -0
- package/dist/{src → types/src}/server/index.d.ts +1 -1
- package/dist/{src → types/src}/server/shared/messages/global-messages-pool.d.ts +8 -1
- package/dist/{src → types/src}/server/translator/get-translator.d.ts +0 -4
- package/dist/{src → types/src}/shared/types/index.d.ts +1 -1
- package/dist/types/src/shared/types/translator-instance.d.ts +27 -0
- package/dist/{src → types/src}/shared/utils/deep-merge.d.ts +4 -1
- package/package.json +24 -22
- package/dist/export/internal/index.d.ts +0 -1
- package/dist/export/react/index.d.ts +0 -1
- package/dist/src/client/react/index.d.ts +0 -5
- package/dist/src/client/react/navigation/use-navigation-target.js +0 -27
- package/dist/src/client/react/translator/use-translator.d.ts +0 -12
- package/dist/src/shared/types/translator-instance.d.ts +0 -31
- /package/dist/{export → core/export}/config/index.js +0 -0
- /package/dist/{src → core/src}/config/constants/cache.constants.js +0 -0
- /package/dist/{src → core/src}/config/constants/cookie.constants.js +0 -0
- /package/dist/{src → core/src}/config/constants/routing.constants.js +0 -0
- /package/dist/{src → core/src}/config/define-intor-config.js +0 -0
- /package/dist/{src → core/src}/config/resolvers/resolve-cache-options.js +0 -0
- /package/dist/{src → core/src}/config/resolvers/resolve-cookie-options.js +0 -0
- /package/dist/{src → core/src}/config/resolvers/resolve-fallback-locales.js +0 -0
- /package/dist/{src → core/src}/config/resolvers/resolve-routing-options.js +0 -0
- /package/dist/{src → core/src}/config/validators/validate-default-locale.js +0 -0
- /package/dist/{src → core/src}/config/validators/validate-supported-locales.js +0 -0
- /package/dist/{src → core/src}/server/helpers/local-messages-from-url.js +0 -0
- /package/dist/{src → core/src}/server/intor/intor.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-local-messages/load-local-messages.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-messages.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -0
- /package/dist/{src → core/src}/server/messages/load-remote-messages/load-remote-messages.js +0 -0
- /package/dist/{src → core/src}/server/messages/shared/utils/is-valid-messages.js +0 -0
- /package/dist/{src → core/src}/server/shared/logger/get-logger.js +0 -0
- /package/dist/{src → core/src}/server/translator/get-translator.js +0 -0
- /package/dist/{src → core/src}/shared/constants/prefix-placeholder.js +0 -0
- /package/dist/{src → core/src}/shared/error/intor-error.js +0 -0
- /package/dist/{src → core/src}/shared/utils/normalizers/normalize-cache-key.js +0 -0
- /package/dist/{src → core/src}/shared/utils/normalizers/normalize-pathname.js +0 -0
- /package/dist/{src → core/src}/shared/utils/pathname/get-unprefixed-pathname.js +0 -0
- /package/dist/{src → core/src}/shared/utils/pathname/locale-prefix-pathname.js +0 -0
- /package/dist/{src → core/src}/shared/utils/pathname/localize-pathname.js +0 -0
- /package/dist/{src → core/src}/shared/utils/pathname/standardize-pathname.js +0 -0
- /package/dist/{export → next/export}/next/index.js +0 -0
- /package/dist/{export → next/export}/next/proxy/index.js +0 -0
- /package/dist/{export → next/export}/next/server/index.js +0 -0
- /package/dist/{src → next/src}/adapters/next/proxy/intor-proxy.js +0 -0
- /package/dist/{src → next/src}/adapters/next/proxy/utils/set-locale-cookie-edge.js +0 -0
- /package/dist/{src → next/src}/adapters/next/server/get-locale.js +0 -0
- /package/dist/{src → next/src}/adapters/next/server/get-translator.js +0 -0
- /package/dist/{src/client/react/contexts/messages → next/src/client/react/contexts/config}/context.js +0 -0
- /package/dist/{src/client/react/contexts/translator-runtime → next/src/client/react/contexts/locale}/context.js +0 -0
- /package/dist/{src/client/react/contexts/translator → next/src/client/react/contexts/messages}/context.js +0 -0
- /package/dist/{src → next/src}/client/shared/utils/build-cookie-string.js +0 -0
- /package/dist/{src → next/src}/client/shared/utils/locale/set-locale-cookie-browser.js +0 -0
- /package/dist/{src → next/src}/routing/locale/resolve-locale.js +0 -0
- /package/dist/{src → next/src}/routing/pathname/resolve-pathname.js +0 -0
- /package/dist/{src → next/src}/routing/pathname/strategies/all.js +0 -0
- /package/dist/{src → next/src}/routing/pathname/strategies/except-default.js +0 -0
- /package/dist/{src → next/src}/routing/pathname/strategies/none.js +0 -0
- /package/dist/{src → next/src}/routing/resolve-navigation-target.js +0 -0
- /package/dist/{src → next/src}/routing/resolve-routing.js +0 -0
- /package/dist/{src → next/src}/server/shared/logger/global-logger-pool.js +0 -0
- /package/dist/{src → next/src}/server/shared/messages/global-messages-pool.js +0 -0
- /package/dist/{src → next/src}/shared/utils/is-external-destination.js +0 -0
- /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-accept-language.js +0 -0
- /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-host.js +0 -0
- /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-pathname.js +0 -0
- /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-query.js +0 -0
- /package/dist/{src → next/src}/shared/utils/normalizers/normalize-locale.js +0 -0
- /package/dist/{src → react/src}/client/helpers/get-client-locale.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/config/context.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/config/hook.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/config/provider.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/intor-provider/intor-provider.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/locale/context.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/locale/hook.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/locale/provider.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/locale/utils/change-locale.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/messages/hook.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/messages/provider.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/translator/hook.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/translator-runtime/hook.js +0 -0
- /package/dist/{src → react/src}/client/react/contexts/translator-runtime/provider.js +0 -0
- /package/dist/{src → react/src}/client/shared/utils/locale/detect-browser-locale.js +0 -0
- /package/dist/{src → react/src}/client/shared/utils/locale/get-locale-cookie-browser.js +0 -0
- /package/dist/{src → react/src}/client/shared/utils/locale/set-document-locale.js +0 -0
- /package/dist/{export → types/export}/config/index.d.ts +0 -0
- /package/dist/{export → types/export}/next/index.d.ts +0 -0
- /package/dist/{export → types/export}/next/proxy/index.d.ts +0 -0
- /package/dist/{export → types/export}/next/server/index.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/navigation/index.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/navigation/link.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/navigation/redirect.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/navigation/use-pathname.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/navigation/use-router.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/proxy/index.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/proxy/intor-proxy.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/proxy/utils/set-locale-cookie-edge.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/server/get-locale.d.ts +0 -0
- /package/dist/{src → types/src}/adapters/next/server/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/helpers/get-client-locale.d.ts +0 -0
- /package/dist/{src → types/src}/client/helpers/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/config/context.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/config/hook.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/config/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/config/provider.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/config/types.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/intor-provider/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/intor-provider/intor-provider.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/intor-provider/types.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/locale/context.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/locale/hook.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/locale/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/locale/provider.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/locale/types.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/locale/utils/change-locale.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/messages/context.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/messages/hook.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/messages/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/messages/provider.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/messages/types.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/messages/utils/use-refetch-messages.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator/context.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator/hook.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator/provider.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator/types.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator-runtime/context.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator-runtime/hook.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator-runtime/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator-runtime/provider.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/contexts/translator-runtime/types.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/navigation/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/react/navigation/use-navigation-strategy.d.ts +0 -0
- /package/dist/{src → types/src}/client/shared/utils/build-cookie-string.d.ts +0 -0
- /package/dist/{src → types/src}/client/shared/utils/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/shared/utils/locale/detect-browser-locale.d.ts +0 -0
- /package/dist/{src → types/src}/client/shared/utils/locale/get-locale-cookie-browser.d.ts +0 -0
- /package/dist/{src → types/src}/client/shared/utils/locale/index.d.ts +0 -0
- /package/dist/{src → types/src}/client/shared/utils/locale/set-document-locale.d.ts +0 -0
- /package/dist/{src → types/src}/client/shared/utils/locale/set-locale-cookie-browser.d.ts +0 -0
- /package/dist/{src → types/src}/config/constants/cache.constants.d.ts +0 -0
- /package/dist/{src → types/src}/config/constants/cookie.constants.d.ts +0 -0
- /package/dist/{src → types/src}/config/constants/routing.constants.d.ts +0 -0
- /package/dist/{src → types/src}/config/define-intor-config.d.ts +0 -0
- /package/dist/{src → types/src}/config/index.d.ts +0 -0
- /package/dist/{src → types/src}/config/resolvers/resolve-cache-options.d.ts +0 -0
- /package/dist/{src → types/src}/config/resolvers/resolve-cookie-options.d.ts +0 -0
- /package/dist/{src → types/src}/config/resolvers/resolve-fallback-locales.d.ts +0 -0
- /package/dist/{src → types/src}/config/resolvers/resolve-routing-options.d.ts +0 -0
- /package/dist/{src → types/src}/config/types/cache.types.d.ts +0 -0
- /package/dist/{src → types/src}/config/types/cookie.types.d.ts +0 -0
- /package/dist/{src → types/src}/config/types/intor-config.types.d.ts +0 -0
- /package/dist/{src → types/src}/config/types/loader.types.d.ts +0 -0
- /package/dist/{src → types/src}/config/types/logger.types.d.ts +0 -0
- /package/dist/{src → types/src}/config/types/routing.types.d.ts +0 -0
- /package/dist/{src → types/src}/config/types/translator.types.d.ts +0 -0
- /package/dist/{src → types/src}/config/validators/validate-default-locale.d.ts +0 -0
- /package/dist/{src → types/src}/config/validators/validate-supported-locales.d.ts +0 -0
- /package/dist/{src → types/src}/routing/index.d.ts +0 -0
- /package/dist/{src → types/src}/routing/locale/index.d.ts +0 -0
- /package/dist/{src → types/src}/routing/locale/resolve-locale.d.ts +0 -0
- /package/dist/{src → types/src}/routing/locale/types.d.ts +0 -0
- /package/dist/{src → types/src}/routing/pathname/index.d.ts +0 -0
- /package/dist/{src → types/src}/routing/pathname/resolve-pathname.d.ts +0 -0
- /package/dist/{src → types/src}/routing/pathname/strategies/all.d.ts +0 -0
- /package/dist/{src → types/src}/routing/pathname/strategies/except-default.d.ts +0 -0
- /package/dist/{src → types/src}/routing/pathname/strategies/index.d.ts +0 -0
- /package/dist/{src → types/src}/routing/pathname/strategies/none.d.ts +0 -0
- /package/dist/{src → types/src}/routing/pathname/types.d.ts +0 -0
- /package/dist/{src → types/src}/routing/resolve-navigation-target.d.ts +0 -0
- /package/dist/{src → types/src}/routing/resolve-routing.d.ts +0 -0
- /package/dist/{src → types/src}/server/helpers/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/helpers/local-messages-from-url.d.ts +0 -0
- /package/dist/{src → types/src}/server/intor/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/intor/intor.d.ts +0 -0
- /package/dist/{src → types/src}/server/intor/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/load-local-messages.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/read-locale-messages.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-local-messages/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-messages.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-remote-messages/index.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-remote-messages/load-remote-messages.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/load-remote-messages/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/shared/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/shared/utils/is-valid-messages.d.ts +0 -0
- /package/dist/{src → types/src}/server/messages/types.d.ts +0 -0
- /package/dist/{src → types/src}/server/shared/logger/get-logger.d.ts +0 -0
- /package/dist/{src → types/src}/server/shared/logger/global-logger-pool.d.ts +0 -0
- /package/dist/{src → types/src}/server/translator/index.d.ts +0 -0
- /package/dist/{src → types/src}/shared/constants/index.d.ts +0 -0
- /package/dist/{src → types/src}/shared/constants/prefix-placeholder.d.ts +0 -0
- /package/dist/{src → types/src}/shared/error/index.d.ts +0 -0
- /package/dist/{src → types/src}/shared/error/intor-error.d.ts +0 -0
- /package/dist/{src → types/src}/shared/types/generated.d.ts +0 -0
- /package/dist/{src → types/src}/shared/types/routing.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/index.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/is-external-destination.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-accept-language.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-host.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-pathname.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-query.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/locale/index.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/normalizers/index.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/normalizers/normalize-cache-key.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/normalizers/normalize-locale.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/normalizers/normalize-pathname.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/pathname/get-unprefixed-pathname.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/pathname/index.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/pathname/locale-prefix-pathname.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/pathname/localize-pathname.d.ts +0 -0
- /package/dist/{src → types/src}/shared/utils/pathname/standardize-pathname.d.ts +0 -0
package/README.md
CHANGED
|
@@ -22,12 +22,81 @@ Fast to start, easy to extend, and free from the usual i18n heaviness.
|
|
|
22
22
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
---
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|-> load-local-messages -> read-locale-messages -> collect-file-entries & parse-file-entries
|
|
31
|
-
|-> load-remote-messages -> fetchLocaleMessages
|
|
27
|
+
Intor separates language translation from semantic rendering.
|
|
28
|
+
ICU operates at the text-translation layer, while semantic tags
|
|
29
|
+
are parsed and rendered in a dedicated AST-based rendering phase.
|
|
32
30
|
|
|
33
31
|
```
|
|
32
|
+
┌──────────────────────────────────────────────┐
|
|
33
|
+
│ Application │
|
|
34
|
+
│ │
|
|
35
|
+
│ t() / tRich() / <T /> │
|
|
36
|
+
└───────────────────────────┬──────────────────┘
|
|
37
|
+
│
|
|
38
|
+
▼
|
|
39
|
+
┌──────────────────────────────────────────────┐
|
|
40
|
+
│ Translator (Language / Semantic) │
|
|
41
|
+
│ intor-translator │
|
|
42
|
+
│ │
|
|
43
|
+
│ Responsibilities: │
|
|
44
|
+
│ - Resolve message key │
|
|
45
|
+
│ - Locale resolution & fallback │
|
|
46
|
+
│ - Message loading │
|
|
47
|
+
│ - Text-level interpolation │
|
|
48
|
+
│ • {name}, {count} │
|
|
49
|
+
│ • plural / select │
|
|
50
|
+
│ • ICU MessageFormat (optional) │
|
|
51
|
+
│ (ignoreTag: true) │
|
|
52
|
+
│ │
|
|
53
|
+
│ Output: │
|
|
54
|
+
│ - Translated string │
|
|
55
|
+
│ - May still contain <semantic tags> │
|
|
56
|
+
└───────────────────────────┬──────────────────┘
|
|
57
|
+
│
|
|
58
|
+
▼
|
|
59
|
+
┌──────────────────────────────────────────────┐
|
|
60
|
+
│ Semantic Parsing & AST Construction │
|
|
61
|
+
│ │
|
|
62
|
+
│ Responsibilities: │
|
|
63
|
+
│ - Tokenize semantic tags │
|
|
64
|
+
│ • <b>, <link>, <Component> │
|
|
65
|
+
│ - Build semantic AST │
|
|
66
|
+
│ • TextNode │
|
|
67
|
+
│ • TagNode │
|
|
68
|
+
│ │
|
|
69
|
+
│ Note: │
|
|
70
|
+
│ - Independent from ICU / formatter │
|
|
71
|
+
│ - Pure semantic structure │
|
|
72
|
+
└───────────────────────────┬──────────────────┘
|
|
73
|
+
│
|
|
74
|
+
▼
|
|
75
|
+
┌──────────────────────────────────────────────┐
|
|
76
|
+
│ Semantic Rendering (Renderer) │
|
|
77
|
+
│ │
|
|
78
|
+
│ Responsibilities: │
|
|
79
|
+
│ - Traverse semantic AST │
|
|
80
|
+
│ - Render TextNode │
|
|
81
|
+
│ - Render TagNode │
|
|
82
|
+
│ │
|
|
83
|
+
│ Renderer decides output type: │
|
|
84
|
+
│ - string │
|
|
85
|
+
│ - ReactNode │
|
|
86
|
+
│ - Vue / Svelte nodes │
|
|
87
|
+
│ - Markdown / CLI output │
|
|
88
|
+
│ │
|
|
89
|
+
│ Optional extensions: │
|
|
90
|
+
│ - Custom tag renderers │
|
|
91
|
+
│ - Rich components │
|
|
92
|
+
└───────────────────────────┬──────────────────┘
|
|
93
|
+
│
|
|
94
|
+
▼
|
|
95
|
+
┌──────────────────────────────────────────────┐
|
|
96
|
+
│ Final Output │
|
|
97
|
+
│ │
|
|
98
|
+
│ - Rendered rich content │
|
|
99
|
+
│ - Framework-specific result │
|
|
100
|
+
│ │
|
|
101
|
+
└──────────────────────────────────────────────┘
|
|
102
|
+
```
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { PREFIX_PLACEHOLDER } from '../src/shared/constants/prefix-placeholder.js';
|
|
2
|
+
export { deepMerge } from '../src/shared/utils/deep-merge.js';
|
|
2
3
|
export { localizePathname } from '../src/shared/utils/pathname/localize-pathname.js';
|
|
3
4
|
export { IntorError, IntorErrorCode } from '../src/shared/error/intor-error.js';
|
|
4
5
|
import 'intor-translator';
|
|
@@ -3,5 +3,5 @@ export { loadMessages } from '../../src/server/messages/load-messages.js';
|
|
|
3
3
|
export { isValidMessages } from '../../src/server/messages/shared/utils/is-valid-messages.js';
|
|
4
4
|
export { getTranslator } from '../../src/server/translator/get-translator.js';
|
|
5
5
|
export { clearLoggerPool } from '../../src/server/shared/logger/global-logger-pool.js';
|
|
6
|
-
export { clearMessagesPool } from '../../src/server/shared/messages/global-messages-pool.js';
|
|
6
|
+
export { clearMessagesPool, setGlobalMessagesPool } from '../../src/server/shared/messages/global-messages-pool.js';
|
|
7
7
|
export { loadLocalMessagesFromUrl } from '../../src/server/helpers/local-messages-from-url.js';
|
|
@@ -73,13 +73,10 @@ async function parseFileEntries({ fileEntries, limit, extraOptions: { messagesRe
|
|
|
73
73
|
for (const { namespace, messages } of parsedFileEntries) {
|
|
74
74
|
// Handle root-level namespace (i.e., [rootDir]/index.json)
|
|
75
75
|
if (namespace === "index") {
|
|
76
|
-
|
|
77
|
-
if (merged)
|
|
78
|
-
Object.assign(result, merged);
|
|
76
|
+
Object.assign(result, deepMerge(result, messages));
|
|
79
77
|
}
|
|
80
78
|
else {
|
|
81
|
-
result[namespace] =
|
|
82
|
-
deepMerge(result[namespace] ?? {}, messages) || {};
|
|
79
|
+
result[namespace] = deepMerge(result[namespace], messages);
|
|
83
80
|
}
|
|
84
81
|
}
|
|
85
82
|
return result;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function getGlobalLoggerPool() {
|
|
2
|
+
if (!globalThis.__INTOR_LOGGER_POOL__) {
|
|
3
|
+
globalThis.__INTOR_LOGGER_POOL__ = new Map();
|
|
4
|
+
}
|
|
5
|
+
return globalThis.__INTOR_LOGGER_POOL__;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Optional: clear all cache
|
|
9
|
+
* Useful in tests or dynamic reloads.
|
|
10
|
+
*/
|
|
11
|
+
function clearLoggerPool() {
|
|
12
|
+
const pool = getGlobalLoggerPool();
|
|
13
|
+
pool.clear();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { clearLoggerPool, getGlobalLoggerPool };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Keyv from 'keyv';
|
|
2
|
+
|
|
3
|
+
function getGlobalMessagesPool() {
|
|
4
|
+
if (!globalThis.__INTOR_MESSAGES_POOL__) {
|
|
5
|
+
globalThis.__INTOR_MESSAGES_POOL__ = new Keyv();
|
|
6
|
+
}
|
|
7
|
+
return globalThis.__INTOR_MESSAGES_POOL__;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Replace the global messages pool.
|
|
11
|
+
*
|
|
12
|
+
* - Intended for advanced usage (e.g. Redis, custom cache backends).
|
|
13
|
+
* - Must be called during application bootstrap.
|
|
14
|
+
*/
|
|
15
|
+
function setGlobalMessagesPool(pool) {
|
|
16
|
+
globalThis.__INTOR_MESSAGES_POOL__ = pool;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Optional: clear all cache
|
|
20
|
+
* - Useful in tests or dynamic reloads.
|
|
21
|
+
*/
|
|
22
|
+
function clearMessagesPool() {
|
|
23
|
+
const pool = getGlobalMessagesPool();
|
|
24
|
+
pool.clear();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { clearMessagesPool, getGlobalMessagesPool, setGlobalMessagesPool };
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Deeply merges two objects.
|
|
3
|
+
*
|
|
3
4
|
* - Nested objects → merged recursively
|
|
4
5
|
* - Array / primitive → b overwrites a
|
|
6
|
+
*
|
|
7
|
+
* This function always returns a plain object.
|
|
5
8
|
*/
|
|
6
|
-
const deepMerge = (a, b) => {
|
|
7
|
-
if (!a && !b)
|
|
8
|
-
return undefined;
|
|
9
|
-
if (!a)
|
|
10
|
-
return b;
|
|
11
|
-
if (!b)
|
|
12
|
-
return a;
|
|
9
|
+
const deepMerge = (a = {}, b = {}) => {
|
|
13
10
|
const result = { ...a };
|
|
14
11
|
for (const key in b) {
|
|
15
12
|
if (Object.prototype.hasOwnProperty.call(b, key)) {
|
|
@@ -21,11 +18,9 @@ const deepMerge = (a, b) => {
|
|
|
21
18
|
typeof bv === "object" &&
|
|
22
19
|
!Array.isArray(av) &&
|
|
23
20
|
!Array.isArray(bv)) {
|
|
24
|
-
// recursive merge
|
|
25
21
|
result[key] = deepMerge(av, bv);
|
|
26
22
|
}
|
|
27
23
|
else {
|
|
28
|
-
// overwrite with primitive or array
|
|
29
24
|
result[key] = bv;
|
|
30
25
|
}
|
|
31
26
|
}
|
|
@@ -3,11 +3,13 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import { formatUrl } from 'next/dist/shared/lib/router/utils/format-url';
|
|
4
4
|
import NextLink from 'next/link';
|
|
5
5
|
import 'react';
|
|
6
|
+
import { usePathname } from './use-pathname.js';
|
|
6
7
|
import '../../../client/react/contexts/config/context.js';
|
|
8
|
+
import 'intor/react';
|
|
7
9
|
import '../../../client/react/contexts/locale/context.js';
|
|
8
10
|
import '../../../config/constants/cache.constants.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
import 'logry';
|
|
12
|
+
import 'keyv';
|
|
11
13
|
import '../../../client/react/contexts/messages/context.js';
|
|
12
14
|
import 'intor-translator';
|
|
13
15
|
import '../../../client/react/contexts/translator-runtime/context.js';
|
|
@@ -27,7 +29,8 @@ import { useNavigationStrategy } from '../../../client/react/navigation/use-navi
|
|
|
27
29
|
* This component is responsible only for executing the resolved target.
|
|
28
30
|
*/
|
|
29
31
|
const Link = ({ href, locale, children, onClick, ...props }) => {
|
|
30
|
-
const {
|
|
32
|
+
const { localizedPathname } = usePathname();
|
|
33
|
+
const { resolveNavigation } = useNavigationTarget(localizedPathname);
|
|
31
34
|
const { decideNavigation } = useNavigationStrategy();
|
|
32
35
|
// Normalize Next.js href input into a string destination
|
|
33
36
|
const rawDestination = typeof href === "string" ? href : href ? formatUrl(href) : undefined;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { redirect as redirect$1 } from 'next/navigation';
|
|
2
2
|
import { getLocale } from '../server/get-locale.js';
|
|
3
3
|
import 'intor-translator';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import 'node:path';
|
|
5
|
+
import 'p-limit';
|
|
6
6
|
import '../../../config/constants/cache.constants.js';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import 'node:fs/promises';
|
|
8
|
+
import 'logry';
|
|
9
9
|
import { localizePathname } from '../../../shared/utils/pathname/localize-pathname.js';
|
|
10
|
-
|
|
10
|
+
import 'keyv';
|
|
11
11
|
import { isExternalDestination } from '../../../shared/utils/is-external-destination.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { usePathname as usePathname$1 } from 'next/navigation';
|
|
2
|
-
import 'react
|
|
3
|
-
import 'react';
|
|
4
|
-
import '../../../client/react/contexts/config/context.js';
|
|
5
|
-
import { useConfig } from '../../../client/react/contexts/config/hook.js';
|
|
6
|
-
import '../../../client/react/contexts/locale/context.js';
|
|
7
|
-
import { useLocale } from '../../../client/react/contexts/locale/hook.js';
|
|
2
|
+
import { useConfig, useLocale } from 'intor/react';
|
|
8
3
|
import { localizePathname } from '../../../shared/utils/pathname/localize-pathname.js';
|
|
9
4
|
|
|
10
5
|
/**
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { useRouter as useRouter$1 } from 'next/navigation';
|
|
2
|
+
import { usePathname } from './use-pathname.js';
|
|
2
3
|
import 'react/jsx-runtime';
|
|
3
4
|
import 'react';
|
|
4
5
|
import '../../../client/react/contexts/config/context.js';
|
|
6
|
+
import 'intor/react';
|
|
5
7
|
import '../../../client/react/contexts/locale/context.js';
|
|
6
8
|
import '../../../config/constants/cache.constants.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
import 'logry';
|
|
10
|
+
import 'keyv';
|
|
9
11
|
import '../../../client/react/contexts/messages/context.js';
|
|
10
12
|
import 'intor-translator';
|
|
11
13
|
import '../../../client/react/contexts/translator-runtime/context.js';
|
|
@@ -24,7 +26,8 @@ import { useNavigationStrategy } from '../../../client/react/navigation/use-navi
|
|
|
24
26
|
*/
|
|
25
27
|
const useRouter = () => {
|
|
26
28
|
const { push: nextRouterPush, replace: nextRouterReplace, prefetch: nextRouterPrefetch, ...rest } = useRouter$1();
|
|
27
|
-
const {
|
|
29
|
+
const { localizedPathname } = usePathname();
|
|
30
|
+
const { resolveNavigation } = useNavigationTarget(localizedPathname);
|
|
28
31
|
const { decideNavigation } = useNavigationStrategy();
|
|
29
32
|
const push = (href, options) => {
|
|
30
33
|
const { locale } = options || {};
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import 'react
|
|
2
|
-
import 'react';
|
|
3
|
-
import '../contexts/config/context.js';
|
|
4
|
-
import { useConfig } from '../contexts/config/hook.js';
|
|
1
|
+
import { useConfig, useLocale } from 'intor/react';
|
|
5
2
|
import { setLocaleCookieBrowser } from '../../shared/utils/locale/set-locale-cookie-browser.js';
|
|
6
|
-
import '../contexts/locale/context.js';
|
|
7
|
-
import { useLocale } from '../contexts/locale/hook.js';
|
|
8
3
|
|
|
9
4
|
/**
|
|
10
5
|
* Determines the navigation strategy for a resolved navigation target.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useConfig, useLocale } from 'intor/react';
|
|
2
|
+
import { resolveNavigationTarget } from '../../../routing/resolve-navigation-target.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook for resolving locale-aware navigation targets.
|
|
6
|
+
*
|
|
7
|
+
* This hook bridges Next.js navigation context with Intor's routing decision logic.
|
|
8
|
+
*/
|
|
9
|
+
const useNavigationTarget = (localizedPathname) => {
|
|
10
|
+
const { config } = useConfig();
|
|
11
|
+
const { locale: currentLocale } = useLocale();
|
|
12
|
+
const resolveNavigation = (input) => {
|
|
13
|
+
return resolveNavigationTarget(config, currentLocale, localizedPathname, {
|
|
14
|
+
...input,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
return { resolveNavigation };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { useNavigationTarget };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import pLimit from 'p-limit';
|
|
3
|
+
import { DEFAULT_CACHE_OPTIONS } from '../../../config/constants/cache.constants.js';
|
|
4
|
+
import { readLocaleMessages } from './read-locale-messages/read-locale-messages.js';
|
|
5
|
+
import { getLogger } from '../../shared/logger/get-logger.js';
|
|
6
|
+
import { getGlobalMessagesPool } from '../../shared/messages/global-messages-pool.js';
|
|
7
|
+
import { normalizeCacheKey } from '../../../shared/utils/normalizers/normalize-cache-key.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Load locale messages from the local file system.
|
|
11
|
+
*
|
|
12
|
+
* This function acts as the orchestration layer for local message loading.
|
|
13
|
+
* It is responsible for:
|
|
14
|
+
*
|
|
15
|
+
* - Resolving fallback locales in order
|
|
16
|
+
* - Coordinating cache read / write behavior
|
|
17
|
+
* - Limiting concurrent file reads for performance
|
|
18
|
+
*
|
|
19
|
+
* File system traversal, parsing, and message validation are delegated to lower-level utilities.
|
|
20
|
+
*/
|
|
21
|
+
const loadLocalMessages = async ({ pool = getGlobalMessagesPool(), rootDir = "messages", locale, fallbackLocales, namespaces, extraOptions: { concurrency = 10, cacheOptions = DEFAULT_CACHE_OPTIONS, loggerOptions = { id: "default" }, exts, messagesReader, } = {}, allowCacheWrite = false, }) => {
|
|
22
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
23
|
+
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
24
|
+
const start = performance.now();
|
|
25
|
+
logger.debug("Loading local messages.", {
|
|
26
|
+
rootDir,
|
|
27
|
+
resolvedRootDir: path.resolve(process.cwd(), rootDir),
|
|
28
|
+
});
|
|
29
|
+
// --- Cache key
|
|
30
|
+
const cacheKey = normalizeCacheKey([
|
|
31
|
+
loggerOptions.id,
|
|
32
|
+
"loaderType:local",
|
|
33
|
+
rootDir,
|
|
34
|
+
locale,
|
|
35
|
+
(fallbackLocales || []).toSorted().join(","),
|
|
36
|
+
(namespaces || []).toSorted().join(","),
|
|
37
|
+
]);
|
|
38
|
+
// --- Cache read --------------------------------------------------
|
|
39
|
+
if (cacheOptions.enabled && cacheKey) {
|
|
40
|
+
const cached = await pool?.get(cacheKey);
|
|
41
|
+
if (cached) {
|
|
42
|
+
logger.debug("Messages cache hit.", { key: cacheKey });
|
|
43
|
+
return cached;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const limit = pLimit(concurrency);
|
|
47
|
+
const candidateLocales = [locale, ...(fallbackLocales || [])];
|
|
48
|
+
let messages;
|
|
49
|
+
// Try each candidate locale in order and stop at the first successful result
|
|
50
|
+
for (let i = 0; i < candidateLocales.length; i++) {
|
|
51
|
+
const candidateLocale = candidateLocales[i];
|
|
52
|
+
const isLast = i === candidateLocales.length - 1;
|
|
53
|
+
try {
|
|
54
|
+
const readed = await readLocaleMessages({
|
|
55
|
+
limit,
|
|
56
|
+
rootDir,
|
|
57
|
+
locale: candidateLocale,
|
|
58
|
+
namespaces,
|
|
59
|
+
extraOptions: { loggerOptions, exts, messagesReader },
|
|
60
|
+
});
|
|
61
|
+
// Stop at the first locale that yields non-empty messages
|
|
62
|
+
if (Object.values(readed[candidateLocale] || {}).length > 0) {
|
|
63
|
+
messages = readed;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
if (isLast) {
|
|
69
|
+
logger.warn("Failed to load messages for all candidate locales.", {
|
|
70
|
+
locale,
|
|
71
|
+
fallbackLocales,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
logger.warn(`Failed to load locale messages for "${candidateLocale}", trying next fallback.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// --- Cache write --------------------------------------------------
|
|
80
|
+
if (allowCacheWrite && cacheOptions.enabled && cacheKey && messages) {
|
|
81
|
+
await pool?.set(cacheKey, messages, cacheOptions.ttl);
|
|
82
|
+
}
|
|
83
|
+
// Final success log with resolved locale and timing
|
|
84
|
+
if (messages) {
|
|
85
|
+
logger.trace("Finished loading local messages.", {
|
|
86
|
+
loadedLocale: Object.keys(messages)[0],
|
|
87
|
+
duration: `${Math.round(performance.now() - start)} ms`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return messages;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export { loadLocalMessages };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getLogger } from '../../../../shared/logger/get-logger.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Recursively collects all message files under a given root directory.
|
|
7
|
+
*
|
|
8
|
+
* - Supports filtering by allowed file extensions and optional namespaces.
|
|
9
|
+
* - Processes directories concurrently using the provided `limit` function.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* [{
|
|
14
|
+
* namespace: "auth", // If messages under locale root (no namespace) -> "index"
|
|
15
|
+
* fullPath: "/Users/john/my-app/messages/en-US/auth/login.json",
|
|
16
|
+
* relativePath: "auth/login.json",
|
|
17
|
+
* segments: ["auth", "login"],
|
|
18
|
+
* basename: "login",
|
|
19
|
+
* }, ... ];
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
async function collectFileEntries({ readdir = fs.readdir, limit, rootDir, namespaces, extraOptions: { exts = [".json"], loggerOptions } = {}, }) {
|
|
23
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
24
|
+
const logger = baseLogger.child({ scope: "collect-file-entries" });
|
|
25
|
+
const fileEntries = [];
|
|
26
|
+
const walk = async (currentDir) => {
|
|
27
|
+
// Read current directory entries
|
|
28
|
+
let entries = [];
|
|
29
|
+
try {
|
|
30
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
logger.debug("Locale directory not found, skipping locale.", {
|
|
34
|
+
localeDir: currentDir,
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Process each directory entry and collect valid files
|
|
39
|
+
const tasks = entries.map((entry) => limit(async () => {
|
|
40
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
41
|
+
// If entry is a directory, recurse into it
|
|
42
|
+
if (entry.isDirectory()) {
|
|
43
|
+
await walk(fullPath);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Only include files with extensions in exts[]
|
|
47
|
+
if (!exts.some((ext) => entry.name.endsWith(ext)))
|
|
48
|
+
return;
|
|
49
|
+
const relativePath = path.relative(rootDir, fullPath);
|
|
50
|
+
const ext = path.extname(relativePath);
|
|
51
|
+
const withoutExt = relativePath.slice(0, -ext.length);
|
|
52
|
+
const segments = withoutExt.split(path.sep).filter(Boolean);
|
|
53
|
+
const namespace = segments.at(0);
|
|
54
|
+
if (!namespace)
|
|
55
|
+
return;
|
|
56
|
+
// Filter namespaces if a list is provided (always include "index")
|
|
57
|
+
if (namespaces && namespace !== "index") {
|
|
58
|
+
if (!namespaces.includes(namespace))
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
fileEntries.push({
|
|
62
|
+
namespace,
|
|
63
|
+
fullPath,
|
|
64
|
+
relativePath,
|
|
65
|
+
segments,
|
|
66
|
+
basename: path.basename(entry.name, ext),
|
|
67
|
+
});
|
|
68
|
+
}));
|
|
69
|
+
await Promise.all(tasks);
|
|
70
|
+
};
|
|
71
|
+
await walk(rootDir);
|
|
72
|
+
if (fileEntries.length > 0) {
|
|
73
|
+
logger.trace(`Collected ${fileEntries.length} local message files for locale "${path.basename(rootDir)}".`);
|
|
74
|
+
}
|
|
75
|
+
return fileEntries;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { collectFileEntries };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { isValidMessages } from '../../../shared/utils/is-valid-messages.js';
|
|
3
|
+
import { getLogger } from '../../../../shared/logger/get-logger.js';
|
|
4
|
+
import { deepMerge } from '../../../../../shared/utils/deep-merge.js';
|
|
5
|
+
import { jsonReader } from './utils/json-reader.js';
|
|
6
|
+
import { nestObjectFromPath } from './utils/nest-object-from-path.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse locale message files (JSON or custom formats) into a unified LocaleMessages object.
|
|
10
|
+
*
|
|
11
|
+
* - Supports JSON and custom formats (via `customReader`)
|
|
12
|
+
* - Uses optional concurrency control (`limit`)
|
|
13
|
+
* - Builds nested objects based on file path segments
|
|
14
|
+
* - Deep-merges entries that belong to the same namespace
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```plain
|
|
18
|
+
* File paths:
|
|
19
|
+
* - en/index.json = { a: "A" }
|
|
20
|
+
* - en/ui.json = { b: "B" }
|
|
21
|
+
* - en/auth/index.json = { c: "C" }
|
|
22
|
+
* - en/auth/verify.json = { d: "D" }
|
|
23
|
+
*```
|
|
24
|
+
|
|
25
|
+
* The final return value is a LocaleMessages object:
|
|
26
|
+
* ```ts
|
|
27
|
+
* {
|
|
28
|
+
* en: {
|
|
29
|
+
* a: "A",
|
|
30
|
+
* ui: { b: "B" },
|
|
31
|
+
* auth: {
|
|
32
|
+
* c: "C",
|
|
33
|
+
* verify: { d: "D" },
|
|
34
|
+
* },
|
|
35
|
+
* },
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
async function parseFileEntries({ fileEntries, limit, extraOptions: { messagesReader, loggerOptions } = {}, }) {
|
|
40
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
41
|
+
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
42
|
+
// Read and parse all file entries
|
|
43
|
+
const parsedFileEntries = [];
|
|
44
|
+
const tasks = fileEntries.map(({ namespace, segments, basename, fullPath, relativePath }) => limit(async () => {
|
|
45
|
+
try {
|
|
46
|
+
const segsWithoutNs = segments.slice(1);
|
|
47
|
+
const ext = path.extname(fullPath);
|
|
48
|
+
// Use a custom reader if provided (e.g., for YAML)
|
|
49
|
+
const json = ext !== ".json" && messagesReader
|
|
50
|
+
? await messagesReader(fullPath)
|
|
51
|
+
: await jsonReader(fullPath);
|
|
52
|
+
// Validate messages structure
|
|
53
|
+
if (!isValidMessages(json)) {
|
|
54
|
+
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
55
|
+
}
|
|
56
|
+
const isIndex = basename === "index";
|
|
57
|
+
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
58
|
+
// Nest the parsed content based on the path segments
|
|
59
|
+
const nested = nestObjectFromPath(keyPath, json);
|
|
60
|
+
parsedFileEntries.push({ namespace, messages: nested });
|
|
61
|
+
logger.trace(`Parsed message file: ${relativePath}`);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
logger.error("Failed to read or parse file.", {
|
|
65
|
+
path: fullPath,
|
|
66
|
+
error,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
await Promise.all(tasks);
|
|
71
|
+
// Merge all entries belonging to the same namespace
|
|
72
|
+
const result = {};
|
|
73
|
+
for (const { namespace, messages } of parsedFileEntries) {
|
|
74
|
+
// Handle root-level namespace (i.e., [rootDir]/index.json)
|
|
75
|
+
if (namespace === "index") {
|
|
76
|
+
Object.assign(result, deepMerge(result, messages));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
result[namespace] = deepMerge(result[namespace], messages);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { parseFileEntries };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read & parse a JSON file
|
|
5
|
+
*/
|
|
6
|
+
async function jsonReader(filePath, readFile = fs.readFile) {
|
|
7
|
+
const raw = await readFile(filePath, "utf8");
|
|
8
|
+
const parsed = JSON.parse(raw);
|
|
9
|
+
return parsed;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { jsonReader };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a value inside nested objects according to a given path.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* const value = { a: "A" };
|
|
7
|
+
*
|
|
8
|
+
* nestObjectFromPath(["auth", "verify"], value); // → { auth: { verify: { a: "A" } } }
|
|
9
|
+
*
|
|
10
|
+
* nestObjectFromPath([], value); // → { a: "A" }
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
function nestObjectFromPath(path, value) {
|
|
14
|
+
let obj = value;
|
|
15
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
16
|
+
obj = { [path[i]]: obj };
|
|
17
|
+
}
|
|
18
|
+
return obj;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { nestObjectFromPath };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { collectFileEntries } from './collect-file-entries/collect-file-entries.js';
|
|
3
|
+
import { parseFileEntries } from './parse-file-entries/parse-file-entries.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Read messages for a specific locale from the file system.
|
|
7
|
+
*
|
|
8
|
+
* 1. Collects file entries under the specified locale directory.
|
|
9
|
+
* 2. Parses each file into a messages object.
|
|
10
|
+
* 3. Wraps the parsed messages under the locale key.
|
|
11
|
+
*/
|
|
12
|
+
const readLocaleMessages = async ({ limit, rootDir = "messages", locale, namespaces, extraOptions: { exts, messagesReader, loggerOptions } = {}, }) => {
|
|
13
|
+
// 1. Collect file entries
|
|
14
|
+
const fileEntries = await collectFileEntries({
|
|
15
|
+
rootDir: path.resolve(process.cwd(), rootDir, locale),
|
|
16
|
+
namespaces,
|
|
17
|
+
limit,
|
|
18
|
+
extraOptions: { exts, loggerOptions },
|
|
19
|
+
});
|
|
20
|
+
// 2. Parse file entries
|
|
21
|
+
const messages = await parseFileEntries({
|
|
22
|
+
fileEntries,
|
|
23
|
+
limit,
|
|
24
|
+
extraOptions: { messagesReader, loggerOptions },
|
|
25
|
+
});
|
|
26
|
+
// 3. Wrap the parsed messages under the locale key
|
|
27
|
+
const localeMessages = { [locale]: messages };
|
|
28
|
+
return localeMessages;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { readLocaleMessages };
|