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.
Files changed (312) hide show
  1. package/README.md +75 -6
  2. package/dist/{export → core/export}/index.js +1 -0
  3. package/dist/{export → core/export}/server/index.js +1 -1
  4. package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +2 -5
  5. package/dist/core/src/server/shared/logger/global-logger-pool.js +16 -0
  6. package/dist/core/src/server/shared/messages/global-messages-pool.js +27 -0
  7. package/dist/{src → core/src}/shared/utils/deep-merge.js +4 -9
  8. package/dist/{src → next/src}/adapters/next/navigation/link.js +6 -3
  9. package/dist/{src → next/src}/adapters/next/navigation/redirect.js +5 -5
  10. package/dist/{src → next/src}/adapters/next/navigation/use-pathname.js +1 -6
  11. package/dist/{src → next/src}/adapters/next/navigation/use-router.js +6 -3
  12. package/dist/next/src/client/react/contexts/translator/context.js +3 -0
  13. package/dist/next/src/client/react/contexts/translator-runtime/context.js +3 -0
  14. package/dist/{src → next/src}/client/react/navigation/use-navigation-strategy.js +1 -6
  15. package/dist/next/src/client/react/navigation/use-navigation-target.js +20 -0
  16. package/dist/next/src/config/constants/cache.constants.js +7 -0
  17. package/dist/next/src/server/messages/load-local-messages/load-local-messages.js +93 -0
  18. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +78 -0
  19. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +85 -0
  20. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.js +12 -0
  21. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.js +21 -0
  22. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +31 -0
  23. package/dist/next/src/server/messages/load-messages.js +77 -0
  24. package/dist/next/src/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +55 -0
  25. package/dist/next/src/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +25 -0
  26. package/dist/next/src/server/messages/load-remote-messages/load-remote-messages.js +110 -0
  27. package/dist/next/src/server/messages/shared/utils/is-valid-messages.js +36 -0
  28. package/dist/next/src/server/shared/logger/get-logger.js +39 -0
  29. package/dist/next/src/server/translator/get-translator.js +35 -0
  30. package/dist/next/src/shared/constants/prefix-placeholder.js +4 -0
  31. package/dist/next/src/shared/utils/deep-merge.js +31 -0
  32. package/dist/next/src/shared/utils/normalizers/normalize-cache-key.js +45 -0
  33. package/dist/next/src/shared/utils/normalizers/normalize-pathname.js +43 -0
  34. package/dist/next/src/shared/utils/pathname/get-unprefixed-pathname.js +39 -0
  35. package/dist/next/src/shared/utils/pathname/locale-prefix-pathname.js +38 -0
  36. package/dist/next/src/shared/utils/pathname/localize-pathname.js +36 -0
  37. package/dist/next/src/shared/utils/pathname/standardize-pathname.js +30 -0
  38. package/dist/{export → react/export}/react/index.js +13 -5
  39. package/dist/react/src/client/react/contexts/messages/context.js +5 -0
  40. package/dist/{src → react/src}/client/react/contexts/messages/utils/use-refetch-messages.js +1 -1
  41. package/dist/react/src/client/react/contexts/translator/context.js +5 -0
  42. package/dist/{src → react/src}/client/react/contexts/translator/provider.js +11 -3
  43. package/dist/react/src/client/react/contexts/translator-runtime/context.js +5 -0
  44. package/dist/react/src/client/react/render/create-react-renderer.js +36 -0
  45. package/dist/react/src/client/react/render/render-rich-message-react.js +22 -0
  46. package/dist/react/src/client/react/translator/create-t-rich.js +23 -0
  47. package/dist/react/src/client/react/translator/t.js +15 -0
  48. package/dist/{src → react/src}/client/react/translator/use-translator.js +4 -2
  49. package/dist/react/src/client/shared/utils/build-cookie-string.js +30 -0
  50. package/dist/react/src/client/shared/utils/locale/set-locale-cookie-browser.js +18 -0
  51. package/dist/react/src/config/constants/cache.constants.js +7 -0
  52. package/dist/react/src/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +55 -0
  53. package/dist/react/src/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +25 -0
  54. package/dist/react/src/server/messages/load-remote-messages/load-remote-messages.js +110 -0
  55. package/dist/react/src/server/messages/shared/utils/is-valid-messages.js +36 -0
  56. package/dist/react/src/server/shared/logger/get-logger.js +39 -0
  57. package/dist/react/src/server/shared/logger/global-logger-pool.js +8 -0
  58. package/dist/react/src/server/shared/messages/global-messages-pool.js +10 -0
  59. package/dist/react/src/shared/utils/deep-merge.js +31 -0
  60. package/dist/react/src/shared/utils/normalizers/normalize-cache-key.js +45 -0
  61. package/dist/react/src/shared/utils/normalizers/normalize-locale.js +59 -0
  62. package/dist/{export → types/export}/index.d.ts +1 -1
  63. package/dist/types/export/internal/index.d.ts +1 -0
  64. package/dist/types/export/react/index.d.ts +1 -0
  65. package/dist/{export → types/export}/server/index.d.ts +1 -1
  66. package/dist/{src → types/src}/adapters/next/server/get-translator.d.ts +2 -5
  67. package/dist/types/src/client/react/contexts/index.d.ts +6 -0
  68. package/dist/types/src/client/react/index.d.ts +4 -0
  69. package/dist/{src → types/src}/client/react/navigation/use-navigation-target.d.ts +1 -1
  70. package/dist/types/src/client/react/render/create-react-renderer.d.ts +17 -0
  71. package/dist/types/src/client/react/render/index.d.ts +2 -0
  72. package/dist/types/src/client/react/render/render-rich-message-react.d.ts +13 -0
  73. package/dist/types/src/client/react/render/types.d.ts +17 -0
  74. package/dist/types/src/client/react/translator/create-t-rich.d.ts +15 -0
  75. package/dist/types/src/client/react/translator/index.d.ts +2 -0
  76. package/dist/types/src/client/react/translator/t.d.ts +27 -0
  77. package/dist/types/src/client/react/translator/translator-instance.d.ts +12 -0
  78. package/dist/types/src/client/react/translator/use-translator.d.ts +8 -0
  79. package/dist/types/src/client/shared/types/index.d.ts +1 -0
  80. package/dist/types/src/client/shared/types/translator-instance.d.ts +11 -0
  81. package/dist/{src → types/src}/server/index.d.ts +1 -1
  82. package/dist/{src → types/src}/server/shared/messages/global-messages-pool.d.ts +8 -1
  83. package/dist/{src → types/src}/server/translator/get-translator.d.ts +0 -4
  84. package/dist/{src → types/src}/shared/types/index.d.ts +1 -1
  85. package/dist/types/src/shared/types/translator-instance.d.ts +27 -0
  86. package/dist/{src → types/src}/shared/utils/deep-merge.d.ts +4 -1
  87. package/package.json +24 -22
  88. package/dist/export/internal/index.d.ts +0 -1
  89. package/dist/export/react/index.d.ts +0 -1
  90. package/dist/src/client/react/index.d.ts +0 -5
  91. package/dist/src/client/react/navigation/use-navigation-target.js +0 -27
  92. package/dist/src/client/react/translator/use-translator.d.ts +0 -12
  93. package/dist/src/shared/types/translator-instance.d.ts +0 -31
  94. /package/dist/{export → core/export}/config/index.js +0 -0
  95. /package/dist/{src → core/src}/config/constants/cache.constants.js +0 -0
  96. /package/dist/{src → core/src}/config/constants/cookie.constants.js +0 -0
  97. /package/dist/{src → core/src}/config/constants/routing.constants.js +0 -0
  98. /package/dist/{src → core/src}/config/define-intor-config.js +0 -0
  99. /package/dist/{src → core/src}/config/resolvers/resolve-cache-options.js +0 -0
  100. /package/dist/{src → core/src}/config/resolvers/resolve-cookie-options.js +0 -0
  101. /package/dist/{src → core/src}/config/resolvers/resolve-fallback-locales.js +0 -0
  102. /package/dist/{src → core/src}/config/resolvers/resolve-routing-options.js +0 -0
  103. /package/dist/{src → core/src}/config/validators/validate-default-locale.js +0 -0
  104. /package/dist/{src → core/src}/config/validators/validate-supported-locales.js +0 -0
  105. /package/dist/{src → core/src}/server/helpers/local-messages-from-url.js +0 -0
  106. /package/dist/{src → core/src}/server/intor/intor.js +0 -0
  107. /package/dist/{src → core/src}/server/messages/load-local-messages/load-local-messages.js +0 -0
  108. /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +0 -0
  109. /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.js +0 -0
  110. /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.js +0 -0
  111. /package/dist/{src → core/src}/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +0 -0
  112. /package/dist/{src → core/src}/server/messages/load-messages.js +0 -0
  113. /package/dist/{src → core/src}/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -0
  114. /package/dist/{src → core/src}/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -0
  115. /package/dist/{src → core/src}/server/messages/load-remote-messages/load-remote-messages.js +0 -0
  116. /package/dist/{src → core/src}/server/messages/shared/utils/is-valid-messages.js +0 -0
  117. /package/dist/{src → core/src}/server/shared/logger/get-logger.js +0 -0
  118. /package/dist/{src → core/src}/server/translator/get-translator.js +0 -0
  119. /package/dist/{src → core/src}/shared/constants/prefix-placeholder.js +0 -0
  120. /package/dist/{src → core/src}/shared/error/intor-error.js +0 -0
  121. /package/dist/{src → core/src}/shared/utils/normalizers/normalize-cache-key.js +0 -0
  122. /package/dist/{src → core/src}/shared/utils/normalizers/normalize-pathname.js +0 -0
  123. /package/dist/{src → core/src}/shared/utils/pathname/get-unprefixed-pathname.js +0 -0
  124. /package/dist/{src → core/src}/shared/utils/pathname/locale-prefix-pathname.js +0 -0
  125. /package/dist/{src → core/src}/shared/utils/pathname/localize-pathname.js +0 -0
  126. /package/dist/{src → core/src}/shared/utils/pathname/standardize-pathname.js +0 -0
  127. /package/dist/{export → next/export}/next/index.js +0 -0
  128. /package/dist/{export → next/export}/next/proxy/index.js +0 -0
  129. /package/dist/{export → next/export}/next/server/index.js +0 -0
  130. /package/dist/{src → next/src}/adapters/next/proxy/intor-proxy.js +0 -0
  131. /package/dist/{src → next/src}/adapters/next/proxy/utils/set-locale-cookie-edge.js +0 -0
  132. /package/dist/{src → next/src}/adapters/next/server/get-locale.js +0 -0
  133. /package/dist/{src → next/src}/adapters/next/server/get-translator.js +0 -0
  134. /package/dist/{src/client/react/contexts/messages → next/src/client/react/contexts/config}/context.js +0 -0
  135. /package/dist/{src/client/react/contexts/translator-runtime → next/src/client/react/contexts/locale}/context.js +0 -0
  136. /package/dist/{src/client/react/contexts/translator → next/src/client/react/contexts/messages}/context.js +0 -0
  137. /package/dist/{src → next/src}/client/shared/utils/build-cookie-string.js +0 -0
  138. /package/dist/{src → next/src}/client/shared/utils/locale/set-locale-cookie-browser.js +0 -0
  139. /package/dist/{src → next/src}/routing/locale/resolve-locale.js +0 -0
  140. /package/dist/{src → next/src}/routing/pathname/resolve-pathname.js +0 -0
  141. /package/dist/{src → next/src}/routing/pathname/strategies/all.js +0 -0
  142. /package/dist/{src → next/src}/routing/pathname/strategies/except-default.js +0 -0
  143. /package/dist/{src → next/src}/routing/pathname/strategies/none.js +0 -0
  144. /package/dist/{src → next/src}/routing/resolve-navigation-target.js +0 -0
  145. /package/dist/{src → next/src}/routing/resolve-routing.js +0 -0
  146. /package/dist/{src → next/src}/server/shared/logger/global-logger-pool.js +0 -0
  147. /package/dist/{src → next/src}/server/shared/messages/global-messages-pool.js +0 -0
  148. /package/dist/{src → next/src}/shared/utils/is-external-destination.js +0 -0
  149. /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-accept-language.js +0 -0
  150. /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-host.js +0 -0
  151. /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-pathname.js +0 -0
  152. /package/dist/{src → next/src}/shared/utils/locale/get-locale-from-query.js +0 -0
  153. /package/dist/{src → next/src}/shared/utils/normalizers/normalize-locale.js +0 -0
  154. /package/dist/{src → react/src}/client/helpers/get-client-locale.js +0 -0
  155. /package/dist/{src → react/src}/client/react/contexts/config/context.js +0 -0
  156. /package/dist/{src → react/src}/client/react/contexts/config/hook.js +0 -0
  157. /package/dist/{src → react/src}/client/react/contexts/config/provider.js +0 -0
  158. /package/dist/{src → react/src}/client/react/contexts/intor-provider/intor-provider.js +0 -0
  159. /package/dist/{src → react/src}/client/react/contexts/locale/context.js +0 -0
  160. /package/dist/{src → react/src}/client/react/contexts/locale/hook.js +0 -0
  161. /package/dist/{src → react/src}/client/react/contexts/locale/provider.js +0 -0
  162. /package/dist/{src → react/src}/client/react/contexts/locale/utils/change-locale.js +0 -0
  163. /package/dist/{src → react/src}/client/react/contexts/messages/hook.js +0 -0
  164. /package/dist/{src → react/src}/client/react/contexts/messages/provider.js +0 -0
  165. /package/dist/{src → react/src}/client/react/contexts/translator/hook.js +0 -0
  166. /package/dist/{src → react/src}/client/react/contexts/translator-runtime/hook.js +0 -0
  167. /package/dist/{src → react/src}/client/react/contexts/translator-runtime/provider.js +0 -0
  168. /package/dist/{src → react/src}/client/shared/utils/locale/detect-browser-locale.js +0 -0
  169. /package/dist/{src → react/src}/client/shared/utils/locale/get-locale-cookie-browser.js +0 -0
  170. /package/dist/{src → react/src}/client/shared/utils/locale/set-document-locale.js +0 -0
  171. /package/dist/{export → types/export}/config/index.d.ts +0 -0
  172. /package/dist/{export → types/export}/next/index.d.ts +0 -0
  173. /package/dist/{export → types/export}/next/proxy/index.d.ts +0 -0
  174. /package/dist/{export → types/export}/next/server/index.d.ts +0 -0
  175. /package/dist/{src → types/src}/adapters/next/navigation/index.d.ts +0 -0
  176. /package/dist/{src → types/src}/adapters/next/navigation/link.d.ts +0 -0
  177. /package/dist/{src → types/src}/adapters/next/navigation/redirect.d.ts +0 -0
  178. /package/dist/{src → types/src}/adapters/next/navigation/use-pathname.d.ts +0 -0
  179. /package/dist/{src → types/src}/adapters/next/navigation/use-router.d.ts +0 -0
  180. /package/dist/{src → types/src}/adapters/next/proxy/index.d.ts +0 -0
  181. /package/dist/{src → types/src}/adapters/next/proxy/intor-proxy.d.ts +0 -0
  182. /package/dist/{src → types/src}/adapters/next/proxy/utils/set-locale-cookie-edge.d.ts +0 -0
  183. /package/dist/{src → types/src}/adapters/next/server/get-locale.d.ts +0 -0
  184. /package/dist/{src → types/src}/adapters/next/server/index.d.ts +0 -0
  185. /package/dist/{src → types/src}/client/helpers/get-client-locale.d.ts +0 -0
  186. /package/dist/{src → types/src}/client/helpers/index.d.ts +0 -0
  187. /package/dist/{src → types/src}/client/react/contexts/config/context.d.ts +0 -0
  188. /package/dist/{src → types/src}/client/react/contexts/config/hook.d.ts +0 -0
  189. /package/dist/{src → types/src}/client/react/contexts/config/index.d.ts +0 -0
  190. /package/dist/{src → types/src}/client/react/contexts/config/provider.d.ts +0 -0
  191. /package/dist/{src → types/src}/client/react/contexts/config/types.d.ts +0 -0
  192. /package/dist/{src → types/src}/client/react/contexts/intor-provider/index.d.ts +0 -0
  193. /package/dist/{src → types/src}/client/react/contexts/intor-provider/intor-provider.d.ts +0 -0
  194. /package/dist/{src → types/src}/client/react/contexts/intor-provider/types.d.ts +0 -0
  195. /package/dist/{src → types/src}/client/react/contexts/locale/context.d.ts +0 -0
  196. /package/dist/{src → types/src}/client/react/contexts/locale/hook.d.ts +0 -0
  197. /package/dist/{src → types/src}/client/react/contexts/locale/index.d.ts +0 -0
  198. /package/dist/{src → types/src}/client/react/contexts/locale/provider.d.ts +0 -0
  199. /package/dist/{src → types/src}/client/react/contexts/locale/types.d.ts +0 -0
  200. /package/dist/{src → types/src}/client/react/contexts/locale/utils/change-locale.d.ts +0 -0
  201. /package/dist/{src → types/src}/client/react/contexts/messages/context.d.ts +0 -0
  202. /package/dist/{src → types/src}/client/react/contexts/messages/hook.d.ts +0 -0
  203. /package/dist/{src → types/src}/client/react/contexts/messages/index.d.ts +0 -0
  204. /package/dist/{src → types/src}/client/react/contexts/messages/provider.d.ts +0 -0
  205. /package/dist/{src → types/src}/client/react/contexts/messages/types.d.ts +0 -0
  206. /package/dist/{src → types/src}/client/react/contexts/messages/utils/use-refetch-messages.d.ts +0 -0
  207. /package/dist/{src → types/src}/client/react/contexts/translator/context.d.ts +0 -0
  208. /package/dist/{src → types/src}/client/react/contexts/translator/hook.d.ts +0 -0
  209. /package/dist/{src → types/src}/client/react/contexts/translator/index.d.ts +0 -0
  210. /package/dist/{src → types/src}/client/react/contexts/translator/provider.d.ts +0 -0
  211. /package/dist/{src → types/src}/client/react/contexts/translator/types.d.ts +0 -0
  212. /package/dist/{src → types/src}/client/react/contexts/translator-runtime/context.d.ts +0 -0
  213. /package/dist/{src → types/src}/client/react/contexts/translator-runtime/hook.d.ts +0 -0
  214. /package/dist/{src → types/src}/client/react/contexts/translator-runtime/index.d.ts +0 -0
  215. /package/dist/{src → types/src}/client/react/contexts/translator-runtime/provider.d.ts +0 -0
  216. /package/dist/{src → types/src}/client/react/contexts/translator-runtime/types.d.ts +0 -0
  217. /package/dist/{src → types/src}/client/react/navigation/index.d.ts +0 -0
  218. /package/dist/{src → types/src}/client/react/navigation/use-navigation-strategy.d.ts +0 -0
  219. /package/dist/{src → types/src}/client/shared/utils/build-cookie-string.d.ts +0 -0
  220. /package/dist/{src → types/src}/client/shared/utils/index.d.ts +0 -0
  221. /package/dist/{src → types/src}/client/shared/utils/locale/detect-browser-locale.d.ts +0 -0
  222. /package/dist/{src → types/src}/client/shared/utils/locale/get-locale-cookie-browser.d.ts +0 -0
  223. /package/dist/{src → types/src}/client/shared/utils/locale/index.d.ts +0 -0
  224. /package/dist/{src → types/src}/client/shared/utils/locale/set-document-locale.d.ts +0 -0
  225. /package/dist/{src → types/src}/client/shared/utils/locale/set-locale-cookie-browser.d.ts +0 -0
  226. /package/dist/{src → types/src}/config/constants/cache.constants.d.ts +0 -0
  227. /package/dist/{src → types/src}/config/constants/cookie.constants.d.ts +0 -0
  228. /package/dist/{src → types/src}/config/constants/routing.constants.d.ts +0 -0
  229. /package/dist/{src → types/src}/config/define-intor-config.d.ts +0 -0
  230. /package/dist/{src → types/src}/config/index.d.ts +0 -0
  231. /package/dist/{src → types/src}/config/resolvers/resolve-cache-options.d.ts +0 -0
  232. /package/dist/{src → types/src}/config/resolvers/resolve-cookie-options.d.ts +0 -0
  233. /package/dist/{src → types/src}/config/resolvers/resolve-fallback-locales.d.ts +0 -0
  234. /package/dist/{src → types/src}/config/resolvers/resolve-routing-options.d.ts +0 -0
  235. /package/dist/{src → types/src}/config/types/cache.types.d.ts +0 -0
  236. /package/dist/{src → types/src}/config/types/cookie.types.d.ts +0 -0
  237. /package/dist/{src → types/src}/config/types/intor-config.types.d.ts +0 -0
  238. /package/dist/{src → types/src}/config/types/loader.types.d.ts +0 -0
  239. /package/dist/{src → types/src}/config/types/logger.types.d.ts +0 -0
  240. /package/dist/{src → types/src}/config/types/routing.types.d.ts +0 -0
  241. /package/dist/{src → types/src}/config/types/translator.types.d.ts +0 -0
  242. /package/dist/{src → types/src}/config/validators/validate-default-locale.d.ts +0 -0
  243. /package/dist/{src → types/src}/config/validators/validate-supported-locales.d.ts +0 -0
  244. /package/dist/{src → types/src}/routing/index.d.ts +0 -0
  245. /package/dist/{src → types/src}/routing/locale/index.d.ts +0 -0
  246. /package/dist/{src → types/src}/routing/locale/resolve-locale.d.ts +0 -0
  247. /package/dist/{src → types/src}/routing/locale/types.d.ts +0 -0
  248. /package/dist/{src → types/src}/routing/pathname/index.d.ts +0 -0
  249. /package/dist/{src → types/src}/routing/pathname/resolve-pathname.d.ts +0 -0
  250. /package/dist/{src → types/src}/routing/pathname/strategies/all.d.ts +0 -0
  251. /package/dist/{src → types/src}/routing/pathname/strategies/except-default.d.ts +0 -0
  252. /package/dist/{src → types/src}/routing/pathname/strategies/index.d.ts +0 -0
  253. /package/dist/{src → types/src}/routing/pathname/strategies/none.d.ts +0 -0
  254. /package/dist/{src → types/src}/routing/pathname/types.d.ts +0 -0
  255. /package/dist/{src → types/src}/routing/resolve-navigation-target.d.ts +0 -0
  256. /package/dist/{src → types/src}/routing/resolve-routing.d.ts +0 -0
  257. /package/dist/{src → types/src}/server/helpers/index.d.ts +0 -0
  258. /package/dist/{src → types/src}/server/helpers/local-messages-from-url.d.ts +0 -0
  259. /package/dist/{src → types/src}/server/intor/index.d.ts +0 -0
  260. /package/dist/{src → types/src}/server/intor/intor.d.ts +0 -0
  261. /package/dist/{src → types/src}/server/intor/types.d.ts +0 -0
  262. /package/dist/{src → types/src}/server/messages/index.d.ts +0 -0
  263. /package/dist/{src → types/src}/server/messages/load-local-messages/index.d.ts +0 -0
  264. /package/dist/{src → types/src}/server/messages/load-local-messages/load-local-messages.d.ts +0 -0
  265. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.d.ts +0 -0
  266. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/index.d.ts +0 -0
  267. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/collect-file-entries/types.d.ts +0 -0
  268. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/index.d.ts +0 -0
  269. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/index.d.ts +0 -0
  270. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.d.ts +0 -0
  271. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/types.d.ts +0 -0
  272. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.d.ts +0 -0
  273. /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
  274. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/read-locale-messages.d.ts +0 -0
  275. /package/dist/{src → types/src}/server/messages/load-local-messages/read-locale-messages/types.d.ts +0 -0
  276. /package/dist/{src → types/src}/server/messages/load-local-messages/types.d.ts +0 -0
  277. /package/dist/{src → types/src}/server/messages/load-messages.d.ts +0 -0
  278. /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.d.ts +0 -0
  279. /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/index.d.ts +0 -0
  280. /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/types.d.ts +0 -0
  281. /package/dist/{src → types/src}/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.d.ts +0 -0
  282. /package/dist/{src → types/src}/server/messages/load-remote-messages/index.d.ts +0 -0
  283. /package/dist/{src → types/src}/server/messages/load-remote-messages/load-remote-messages.d.ts +0 -0
  284. /package/dist/{src → types/src}/server/messages/load-remote-messages/types.d.ts +0 -0
  285. /package/dist/{src → types/src}/server/messages/shared/types.d.ts +0 -0
  286. /package/dist/{src → types/src}/server/messages/shared/utils/is-valid-messages.d.ts +0 -0
  287. /package/dist/{src → types/src}/server/messages/types.d.ts +0 -0
  288. /package/dist/{src → types/src}/server/shared/logger/get-logger.d.ts +0 -0
  289. /package/dist/{src → types/src}/server/shared/logger/global-logger-pool.d.ts +0 -0
  290. /package/dist/{src → types/src}/server/translator/index.d.ts +0 -0
  291. /package/dist/{src → types/src}/shared/constants/index.d.ts +0 -0
  292. /package/dist/{src → types/src}/shared/constants/prefix-placeholder.d.ts +0 -0
  293. /package/dist/{src → types/src}/shared/error/index.d.ts +0 -0
  294. /package/dist/{src → types/src}/shared/error/intor-error.d.ts +0 -0
  295. /package/dist/{src → types/src}/shared/types/generated.d.ts +0 -0
  296. /package/dist/{src → types/src}/shared/types/routing.d.ts +0 -0
  297. /package/dist/{src → types/src}/shared/utils/index.d.ts +0 -0
  298. /package/dist/{src → types/src}/shared/utils/is-external-destination.d.ts +0 -0
  299. /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-accept-language.d.ts +0 -0
  300. /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-host.d.ts +0 -0
  301. /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-pathname.d.ts +0 -0
  302. /package/dist/{src → types/src}/shared/utils/locale/get-locale-from-query.d.ts +0 -0
  303. /package/dist/{src → types/src}/shared/utils/locale/index.d.ts +0 -0
  304. /package/dist/{src → types/src}/shared/utils/normalizers/index.d.ts +0 -0
  305. /package/dist/{src → types/src}/shared/utils/normalizers/normalize-cache-key.d.ts +0 -0
  306. /package/dist/{src → types/src}/shared/utils/normalizers/normalize-locale.d.ts +0 -0
  307. /package/dist/{src → types/src}/shared/utils/normalizers/normalize-pathname.d.ts +0 -0
  308. /package/dist/{src → types/src}/shared/utils/pathname/get-unprefixed-pathname.d.ts +0 -0
  309. /package/dist/{src → types/src}/shared/utils/pathname/index.d.ts +0 -0
  310. /package/dist/{src → types/src}/shared/utils/pathname/locale-prefix-pathname.d.ts +0 -0
  311. /package/dist/{src → types/src}/shared/utils/pathname/localize-pathname.d.ts +0 -0
  312. /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
- #### load-messages
25
+ ---
26
26
 
27
- ```plain
28
-
29
- [load-messages]
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
- const merged = deepMerge(result, messages);
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 { resolveNavigation } = useNavigationTarget();
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/jsx-runtime';
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 { resolveNavigation } = useNavigationTarget();
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 || {};
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+
3
+ React.createContext(undefined);
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+
3
+ React.createContext(undefined);
@@ -1,10 +1,5 @@
1
- import 'react/jsx-runtime';
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,7 @@
1
+ // Default cache options
2
+ const DEFAULT_CACHE_OPTIONS = {
3
+ enabled: process.env.NODE_ENV === "production",
4
+ ttl: 60 * 60 * 1000, // 1 hour
5
+ };
6
+
7
+ export { DEFAULT_CACHE_OPTIONS };
@@ -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 };