@koine/i18n 2.0.0-beta.76 → 2.0.0-beta.78

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 (260) hide show
  1. package/README.md +1 -0
  2. package/api.cjs.js +2203 -0
  3. package/api.esm.js +2179 -0
  4. package/compiler-sync.cjs.d.ts +1 -0
  5. package/compiler-sync.cjs.default.js +1 -0
  6. package/compiler-sync.cjs.js +20 -0
  7. package/compiler-sync.cjs.mjs +2 -0
  8. package/compiler-sync.esm.js +16 -0
  9. package/compiler-worker.cjs.d.ts +1 -0
  10. package/compiler-worker.cjs.default.js +1 -0
  11. package/compiler-worker.cjs.js +18 -0
  12. package/compiler-worker.cjs.mjs +2 -0
  13. package/compiler-worker.esm.js +16 -0
  14. package/compiler.cjs.d.ts +1 -0
  15. package/compiler.cjs.default.js +1 -0
  16. package/compiler.cjs.js +21 -0
  17. package/compiler.cjs.mjs +2 -0
  18. package/compiler.esm.js +13 -0
  19. package/formatRoutePathname.cjs.js +12 -0
  20. package/formatRoutePathname.esm.js +10 -0
  21. package/index.cjs.d.ts +1 -0
  22. package/index.cjs.default.js +1 -0
  23. package/index.cjs.js +47 -0
  24. package/index.cjs.mjs +2 -0
  25. package/index.esm.js +42 -0
  26. package/next.cjs.d.ts +1 -0
  27. package/next.cjs.default.js +1 -0
  28. package/next.cjs.js +377 -0
  29. package/next.cjs.mjs +2 -0
  30. package/next.esm.js +353 -0
  31. package/package.json +22 -305
  32. package/actions/helpers-git.js +0 -45
  33. package/actions/helpers-i18n.js +0 -27
  34. package/actions/i18n.js +0 -17
  35. package/adapter-js/code/config.cjs.js +0 -14
  36. package/adapter-js/code/config.js +0 -14
  37. package/adapter-js/code/defaultLocale.js +0 -7
  38. package/adapter-js/code/deriveLocalisedPathnames.js +0 -78
  39. package/adapter-js/code/index.js +0 -123
  40. package/adapter-js/code/isLocale.js +0 -9
  41. package/adapter-js/code/locales.js +0 -8
  42. package/adapter-js/code/pathnameToRouteId.js +0 -20
  43. package/adapter-js/code/routes.js +0 -11
  44. package/adapter-js/code/routesSlim.js +0 -11
  45. package/adapter-js/code/routesSpa.js +0 -11
  46. package/adapter-js/code/tFns.js +0 -34
  47. package/adapter-js/code/tInterpolateParams.js +0 -18
  48. package/adapter-js/code/tPluralise.js +0 -10
  49. package/adapter-js/code/to.js +0 -32
  50. package/adapter-js/code/toFns.js +0 -25
  51. package/adapter-js/code/toFormat.js +0 -56
  52. package/adapter-js/code/toSpa.js +0 -42
  53. package/adapter-js/code/types.js +0 -327
  54. package/adapter-next/code/index.js +0 -54
  55. package/adapter-next/code/next-redirects.js +0 -5
  56. package/adapter-next/code/next-rewrites.js +0 -5
  57. package/adapter-next/code/useCurrentLocalisedPathnames.js +0 -20
  58. package/adapter-next/code/useLocale.js +0 -8
  59. package/adapter-next/code/useRouteId.js +0 -10
  60. package/adapter-next/code/useTo.js +0 -26
  61. package/adapter-next/code/useToSpa.js +0 -34
  62. package/adapter-next/plugin-async.js +0 -11
  63. package/adapter-next/plugin-legacy.js +0 -187
  64. package/adapter-next/plugin-shared.js +0 -39
  65. package/adapter-next/plugin.js +0 -13
  66. package/adapter-next/redirects.js +0 -51
  67. package/adapter-next/rewrites.js +0 -50
  68. package/adapter-next/transformPathname.js +0 -3
  69. package/adapter-next/webpackPluginI18n.js +0 -11
  70. package/adapter-next-translate/code/DynamicNamespaces.js +0 -9
  71. package/adapter-next-translate/code/T.js +0 -31
  72. package/adapter-next-translate/code/TransText.js +0 -9
  73. package/adapter-next-translate/code/getT.js +0 -15
  74. package/adapter-next-translate/code/index.js +0 -49
  75. package/adapter-next-translate/code/nextTranslateI18n.js +0 -19
  76. package/adapter-next-translate/code/useT.js +0 -43
  77. package/cjs/adapter-js/code/config.cjs.d.ts +0 -3
  78. package/cjs/adapter-js/code/config.d.ts +0 -3
  79. package/cjs/adapter-js/code/defaultLocale.d.ts +0 -3
  80. package/cjs/adapter-js/code/deriveLocalisedPathnames.d.ts +0 -2
  81. package/cjs/adapter-js/code/index.d.ts +0 -3
  82. package/cjs/adapter-js/code/isLocale.d.ts +0 -2
  83. package/cjs/adapter-js/code/locales.d.ts +0 -3
  84. package/cjs/adapter-js/code/pathnameToRouteId.d.ts +0 -3
  85. package/cjs/adapter-js/code/routes.d.ts +0 -3
  86. package/cjs/adapter-js/code/routesSlim.d.ts +0 -3
  87. package/cjs/adapter-js/code/routesSpa.d.ts +0 -3
  88. package/cjs/adapter-js/code/tFns.d.ts +0 -3
  89. package/cjs/adapter-js/code/tInterpolateParams.d.ts +0 -3
  90. package/cjs/adapter-js/code/tPluralise.d.ts +0 -2
  91. package/cjs/adapter-js/code/to.d.ts +0 -3
  92. package/cjs/adapter-js/code/toFns.d.ts +0 -3
  93. package/cjs/adapter-js/code/toFormat.d.ts +0 -3
  94. package/cjs/adapter-js/code/toSpa.d.ts +0 -3
  95. package/cjs/adapter-js/code/types.d.ts +0 -3
  96. package/cjs/adapter-next/code/index.d.ts +0 -3
  97. package/cjs/adapter-next/code/next-redirects.d.ts +0 -3
  98. package/cjs/adapter-next/code/next-rewrites.d.ts +0 -3
  99. package/cjs/adapter-next/code/useCurrentLocalisedPathnames.d.ts +0 -2
  100. package/cjs/adapter-next/code/useLocale.d.ts +0 -3
  101. package/cjs/adapter-next/code/useRouteId.d.ts +0 -2
  102. package/cjs/adapter-next/code/useTo.d.ts +0 -2
  103. package/cjs/adapter-next/code/useToSpa.d.ts +0 -2
  104. package/cjs/adapter-next/plugin-async.d.ts +0 -9
  105. package/cjs/adapter-next/plugin-legacy.d.ts +0 -22
  106. package/cjs/adapter-next/plugin-shared.d.ts +0 -13
  107. package/cjs/adapter-next/plugin.d.ts +0 -7
  108. package/cjs/adapter-next/redirects.d.ts +0 -4
  109. package/cjs/adapter-next/rewrites.d.ts +0 -4
  110. package/cjs/adapter-next/transformPathname.d.ts +0 -1
  111. package/cjs/adapter-next/webpackPluginI18n.d.ts +0 -7
  112. package/cjs/adapter-next-translate/code/DynamicNamespaces.d.ts +0 -2
  113. package/cjs/adapter-next-translate/code/T.d.ts +0 -2
  114. package/cjs/adapter-next-translate/code/TransText.d.ts +0 -2
  115. package/cjs/adapter-next-translate/code/getT.d.ts +0 -2
  116. package/cjs/adapter-next-translate/code/index.d.ts +0 -3
  117. package/cjs/adapter-next-translate/code/nextTranslateI18n.d.ts +0 -3
  118. package/cjs/adapter-next-translate/code/useT.d.ts +0 -2
  119. package/cjs/client/formatRoutePathname.d.ts +0 -4
  120. package/cjs/client/index.d.ts +0 -3
  121. package/cjs/client/interpolateTo.d.ts +0 -4
  122. package/cjs/client/routeHasDynamicPortion.d.ts +0 -2
  123. package/cjs/compiler/api.d.ts +0 -30
  124. package/cjs/compiler/code/data-routes.d.ts +0 -19
  125. package/cjs/compiler/code/data-translations.d.ts +0 -13
  126. package/cjs/compiler/code/data.d.ts +0 -33
  127. package/cjs/compiler/code/generate.d.ts +0 -14
  128. package/cjs/compiler/code/index.d.ts +0 -3
  129. package/cjs/compiler/code/tsCompile.d.ts +0 -2
  130. package/cjs/compiler/code/write.d.ts +0 -11
  131. package/cjs/compiler/config.d.ts +0 -12
  132. package/cjs/compiler/helpers.d.ts +0 -2
  133. package/cjs/compiler/input/data-local.d.ts +0 -4
  134. package/cjs/compiler/input/data-remote.d.ts +0 -7
  135. package/cjs/compiler/input/data.d.ts +0 -4
  136. package/cjs/compiler/input/index.d.ts +0 -4
  137. package/cjs/compiler/input/types.d.ts +0 -14
  138. package/cjs/compiler/input/write.d.ts +0 -8
  139. package/cjs/compiler/pluralisation.d.ts +0 -37
  140. package/cjs/compiler/summary/data.d.ts +0 -6
  141. package/cjs/compiler/summary/generate.d.ts +0 -4
  142. package/cjs/compiler/summary/index.d.ts +0 -3
  143. package/cjs/compiler/summary/write.d.ts +0 -10
  144. package/cjs/compiler/types.d.ts +0 -82
  145. package/cjs/compiler-sync.d.ts +0 -4
  146. package/cjs/compiler-worker.d.ts +0 -1
  147. package/cjs/compiler.d.ts +0 -2
  148. package/cjs/i18n/actions/helpers-git.js +0 -45
  149. package/cjs/i18n/actions/helpers-i18n.js +0 -27
  150. package/cjs/i18n/actions/i18n.js +0 -17
  151. package/cjs/i18n/adapter-js/code/config.cjs.js +0 -14
  152. package/cjs/i18n/adapter-js/code/config.js +0 -14
  153. package/cjs/i18n/adapter-js/code/defaultLocale.js +0 -7
  154. package/cjs/i18n/adapter-js/code/deriveLocalisedPathnames.js +0 -78
  155. package/cjs/i18n/adapter-js/code/index.js +0 -123
  156. package/cjs/i18n/adapter-js/code/isLocale.js +0 -9
  157. package/cjs/i18n/adapter-js/code/locales.js +0 -8
  158. package/cjs/i18n/adapter-js/code/pathnameToRouteId.js +0 -20
  159. package/cjs/i18n/adapter-js/code/routes.js +0 -11
  160. package/cjs/i18n/adapter-js/code/routesSlim.js +0 -11
  161. package/cjs/i18n/adapter-js/code/routesSpa.js +0 -11
  162. package/cjs/i18n/adapter-js/code/tFns.js +0 -34
  163. package/cjs/i18n/adapter-js/code/tInterpolateParams.js +0 -18
  164. package/cjs/i18n/adapter-js/code/tPluralise.js +0 -10
  165. package/cjs/i18n/adapter-js/code/to.js +0 -32
  166. package/cjs/i18n/adapter-js/code/toFns.js +0 -25
  167. package/cjs/i18n/adapter-js/code/toFormat.js +0 -56
  168. package/cjs/i18n/adapter-js/code/toSpa.js +0 -42
  169. package/cjs/i18n/adapter-js/code/types.js +0 -327
  170. package/cjs/i18n/adapter-next/code/index.js +0 -54
  171. package/cjs/i18n/adapter-next/code/next-redirects.js +0 -5
  172. package/cjs/i18n/adapter-next/code/next-rewrites.js +0 -5
  173. package/cjs/i18n/adapter-next/code/useCurrentLocalisedPathnames.js +0 -20
  174. package/cjs/i18n/adapter-next/code/useLocale.js +0 -8
  175. package/cjs/i18n/adapter-next/code/useRouteId.js +0 -10
  176. package/cjs/i18n/adapter-next/code/useTo.js +0 -26
  177. package/cjs/i18n/adapter-next/code/useToSpa.js +0 -34
  178. package/cjs/i18n/adapter-next/plugin-async.js +0 -11
  179. package/cjs/i18n/adapter-next/plugin-legacy.js +0 -187
  180. package/cjs/i18n/adapter-next/plugin-shared.js +0 -39
  181. package/cjs/i18n/adapter-next/plugin.js +0 -13
  182. package/cjs/i18n/adapter-next/redirects.js +0 -51
  183. package/cjs/i18n/adapter-next/rewrites.js +0 -50
  184. package/cjs/i18n/adapter-next/transformPathname.js +0 -3
  185. package/cjs/i18n/adapter-next/webpackPluginI18n.js +0 -11
  186. package/cjs/i18n/adapter-next-translate/code/DynamicNamespaces.js +0 -9
  187. package/cjs/i18n/adapter-next-translate/code/T.js +0 -31
  188. package/cjs/i18n/adapter-next-translate/code/TransText.js +0 -9
  189. package/cjs/i18n/adapter-next-translate/code/getT.js +0 -15
  190. package/cjs/i18n/adapter-next-translate/code/index.js +0 -49
  191. package/cjs/i18n/adapter-next-translate/code/nextTranslateI18n.js +0 -19
  192. package/cjs/i18n/adapter-next-translate/code/useT.js +0 -43
  193. package/cjs/i18n/client/formatRoutePathname.js +0 -5
  194. package/cjs/i18n/client/index.js +0 -3
  195. package/cjs/i18n/client/interpolateTo.js +0 -14
  196. package/cjs/i18n/client/routeHasDynamicPortion.js +0 -2
  197. package/cjs/i18n/compiler/api.js +0 -18
  198. package/cjs/i18n/compiler/code/data-routes.js +0 -99
  199. package/cjs/i18n/compiler/code/data-translations.js +0 -86
  200. package/cjs/i18n/compiler/code/data.js +0 -20
  201. package/cjs/i18n/compiler/code/generate.js +0 -46
  202. package/cjs/i18n/compiler/code/index.js +0 -2
  203. package/cjs/i18n/compiler/code/tsCompile.js +0 -27
  204. package/cjs/i18n/compiler/code/write.js +0 -39
  205. package/cjs/i18n/compiler/config.js +0 -13
  206. package/cjs/i18n/compiler/helpers.js +0 -14
  207. package/cjs/i18n/compiler/input/data-local.js +0 -60
  208. package/cjs/i18n/compiler/input/data-remote.js +0 -41
  209. package/cjs/i18n/compiler/input/data.js +0 -11
  210. package/cjs/i18n/compiler/input/index.js +0 -3
  211. package/cjs/i18n/compiler/input/types.js +0 -1
  212. package/cjs/i18n/compiler/input/write.js +0 -15
  213. package/cjs/i18n/compiler/pluralisation.js +0 -59
  214. package/cjs/i18n/compiler/summary/data.js +0 -28
  215. package/cjs/i18n/compiler/summary/generate.js +0 -27
  216. package/cjs/i18n/compiler/summary/index.js +0 -2
  217. package/cjs/i18n/compiler/summary/write.js +0 -18
  218. package/cjs/i18n/compiler/types.js +0 -1
  219. package/cjs/i18n/compiler-sync.js +0 -2
  220. package/cjs/i18n/compiler-worker.js +0 -3
  221. package/cjs/i18n/compiler.js +0 -1
  222. package/cjs/i18n/index.js +0 -2
  223. package/cjs/i18n/next.js +0 -3
  224. package/cjs/i18n/types.js +0 -1
  225. package/cjs/index.d.ts +0 -2
  226. package/cjs/next.d.ts +0 -3
  227. package/cjs/package.json +0 -34
  228. package/cjs/types.d.ts +0 -48
  229. package/client/formatRoutePathname.js +0 -5
  230. package/client/index.js +0 -3
  231. package/client/interpolateTo.js +0 -14
  232. package/client/routeHasDynamicPortion.js +0 -2
  233. package/compiler/api.js +0 -18
  234. package/compiler/code/data-routes.js +0 -99
  235. package/compiler/code/data-translations.js +0 -86
  236. package/compiler/code/data.js +0 -20
  237. package/compiler/code/generate.js +0 -46
  238. package/compiler/code/index.js +0 -2
  239. package/compiler/code/tsCompile.js +0 -27
  240. package/compiler/code/write.js +0 -39
  241. package/compiler/config.js +0 -13
  242. package/compiler/helpers.js +0 -14
  243. package/compiler/input/data-local.js +0 -60
  244. package/compiler/input/data-remote.js +0 -41
  245. package/compiler/input/data.js +0 -11
  246. package/compiler/input/index.js +0 -3
  247. package/compiler/input/types.js +0 -1
  248. package/compiler/input/write.js +0 -15
  249. package/compiler/pluralisation.js +0 -59
  250. package/compiler/summary/data.js +0 -28
  251. package/compiler/summary/generate.js +0 -27
  252. package/compiler/summary/index.js +0 -2
  253. package/compiler/summary/write.js +0 -18
  254. package/compiler/types.js +0 -1
  255. package/compiler-sync.js +0 -2
  256. package/compiler-worker.js +0 -3
  257. package/compiler.js +0 -1
  258. package/index.js +0 -2
  259. package/next.js +0 -3
  260. package/types.js +0 -1
package/api.cjs.js ADDED
@@ -0,0 +1,2203 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@koine/utils');
4
+ var node_fs = require('node:fs');
5
+ var node_path = require('node:path');
6
+ var node = require('@koine/node');
7
+ var formatRoutePathname = require('./formatRoutePathname.cjs.js');
8
+ var ts = require('typescript');
9
+ var minimatch = require('minimatch');
10
+ var utils$1 = require('next/dist/shared/lib/utils');
11
+ var promises = require('node:fs/promises');
12
+ var glob = require('glob');
13
+ require('node:child_process');
14
+ var node_https = require('node:https');
15
+
16
+ function _interopNamespace(e) {
17
+ if (e && e.__esModule) return e;
18
+ var n = Object.create(null);
19
+ if (e) {
20
+ Object.keys(e).forEach(function (k) {
21
+ if (k !== 'default') {
22
+ var d = Object.getOwnPropertyDescriptor(e, k);
23
+ Object.defineProperty(n, k, d.get ? d : {
24
+ enumerable: true,
25
+ get: function () { return e[k]; }
26
+ });
27
+ }
28
+ });
29
+ }
30
+ n["default"] = e;
31
+ return Object.freeze(n);
32
+ }
33
+
34
+ var ts__namespace = /*#__PURE__*/_interopNamespace(ts);
35
+
36
+ var config = ({ config }) => `
37
+ import { locales } from "./locales";
38
+ import { defaultLocale } from "./defaultLocale";
39
+
40
+ /**
41
+ */
42
+ export const config = {
43
+ locales,
44
+ defaultLocale,
45
+ hideDefaultLocaleInUrl: ${config.hideDefaultLocaleInUrl},
46
+ }
47
+
48
+ export default config;
49
+ `;
50
+
51
+ var configCjs = ({ config }) => `
52
+ const { locales } = require("./locales");
53
+ const { defaultLocale } = require("./defaultLocale");
54
+
55
+ const config = {
56
+ locales,
57
+ defaultLocale,
58
+ hideDefaultLocaleInUrl: ${config.hideDefaultLocaleInUrl},
59
+ };
60
+
61
+ exports.config = config;
62
+
63
+ module.exports = config;
64
+ `;
65
+
66
+ var defaultLocale = ({ config }) => `
67
+ import type { I18n } from "./types";
68
+
69
+ export const defaultLocale: I18n.Locale = "${config.defaultLocale}";
70
+
71
+ export default defaultLocale;
72
+ `;
73
+
74
+ var deriveLocalisedPathnames = () => `
75
+ import { locales } from "./locales";
76
+ import { to } from "./to";
77
+ import type { I18n } from "./types";
78
+
79
+ function getPathnameDynamicPortionName(pathname: string) {
80
+ const res = pathname.match(/\\[(.+)\\]/);
81
+ return res ? res[1] : false;
82
+ }
83
+
84
+ function isStaticPathname(pathname: string) {
85
+ return !/\\[/.test(pathname);
86
+ }
87
+
88
+ // e.g. ['my', 'path', 'id', 'view'] or ['nl' 'my', 'path', 'id', 'view']
89
+ // where the first might be the locale (depending on hideDefaultLocaleInUrl)
90
+ function getPathnameParts(pathname: string) {
91
+ return pathname
92
+ .split("/")
93
+ .slice(1)
94
+ .filter((p, i) => (i === 0 ? !locales.includes(p as I18n.Locale) : true));
95
+ }
96
+
97
+ function localisePathname(
98
+ locale: I18n.Locale,
99
+ // e.g. "my.path.[id].view"
100
+ routeId: I18n.RouteIdDynamic | I18n.RouteIdStatic,
101
+ locationLike: LocationLike
102
+ ) {
103
+ const toPathname = to(routeId as I18n.RouteIdStatic, locale);
104
+
105
+ if (isStaticPathname(toPathname)) {
106
+ return toPathname;
107
+ }
108
+ // e.g. ['my', 'path', '[id]', 'view']
109
+ const toPathnameParts = getPathnameParts(toPathname);
110
+ // e.g. "my.path.[id].view"
111
+ const routeIdDynamic = routeId as I18n.RouteIdDynamic;
112
+ // e.g. /my/path/123/view
113
+ const currentPathname = locationLike.pathname;
114
+ // e.g. ['my', 'path', '123', 'view']
115
+ const currentPathnameParts = getPathnameParts(currentPathname);
116
+ // e.g. { id: "123" }
117
+ const params: Record<string, string> = {};
118
+
119
+ for (let i = 0; i < toPathnameParts.length; i++) {
120
+ // e.g. "my" or "[id]"
121
+ const part = toPathnameParts[i];
122
+ // e.g. "id"
123
+ const name = getPathnameDynamicPortionName(part);
124
+ if (name) {
125
+ // e.g. "123"
126
+ const value = currentPathnameParts[i];
127
+ if (value) {
128
+ params[name] = value;
129
+ }
130
+ }
131
+ }
132
+
133
+ return (
134
+ to(routeIdDynamic, params as never, locale) +
135
+ location?.search || "" +
136
+ location?.hash || ""
137
+ );
138
+ }
139
+
140
+ type LocationLike = { pathname: string; search?: string; hash?: string; };
141
+
142
+ export type LocalisedPathnames = Record<I18n.Locale, string>;
143
+
144
+ export const deriveLocalisedPathnames = (routeId: I18n.RouteId, locationLike: LocationLike) =>
145
+ locales.reduce((pathnames, locale) => {
146
+ pathnames[locale] = localisePathname(locale, routeId, locationLike);
147
+ return pathnames;
148
+ }, {} as LocalisedPathnames);
149
+
150
+ export default deriveLocalisedPathnames;
151
+ `;
152
+
153
+ var isLocale = () => `
154
+ import { locales } from "./locales";
155
+ import type { I18n } from "./types";
156
+
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ export const isLocale = (payload: any): payload is I18n.Locale => locales.includes(payload);
159
+
160
+ export default isLocale;
161
+ `;
162
+
163
+ var locales = ({ config }) => {
164
+ const value = `[${config.locales.map((l) => `"${l}"`).join(", ")}]`;
165
+ return `
166
+ export const locales = ${value} as const;
167
+
168
+ export default locales;
169
+ `;
170
+ };
171
+
172
+ var pathnameToRouteId = ({ options }) => {
173
+ const { idDelimiter, optionalCatchAll, catchAll } = options.routes.tokens;
174
+ return `
175
+ /**
176
+ * Convert a URL like pathname to a "named route"
177
+ * E.g. it transforms:
178
+ * - \`/dashboard/user/[id]\` into \`dashboard.user.[id]\`
179
+ */
180
+ export const pathnameToRouteId = (pathname: string) =>
181
+ pathname
182
+ .replace(/^\\//g, "")
183
+ .replace(/\\//g, "${idDelimiter}")
184
+ .replace(/${utils.escapeRegExp(idDelimiter)}${utils.escapeRegExp(optionalCatchAll.start)}.+$/, "")
185
+ .replace(/${utils.escapeRegExp(idDelimiter)}${utils.escapeRegExp(catchAll.start)}.+$/, "")
186
+ .replace(/\\/index$/, "");
187
+
188
+ export default pathnameToRouteId;
189
+ `;
190
+ };
191
+
192
+ var routes = ({ routes }) => {
193
+ const value = JSON.stringify(Object.fromEntries(Object.entries(routes.byId)
194
+ .map(([routeId, { pathnames }]) => [routeId, pathnames])
195
+ .sort()), null, 2);
196
+ return `
197
+ export const routes = ${value} as const;
198
+
199
+ export default routes;
200
+ `;
201
+ };
202
+
203
+ var routesSlim = ({ routes }) => {
204
+ const value = JSON.stringify(Object.fromEntries(Object.entries(routes.byId)
205
+ .map(([routeId, { pathnamesSlim, pathnames }]) => [
206
+ routeId,
207
+ pathnamesSlim || pathnames,
208
+ ])
209
+ .sort()), null, 2);
210
+ return `
211
+ export const routesSlim = ${value} as const;
212
+
213
+ export default routesSlim;
214
+ `;
215
+ };
216
+
217
+ var routesSpa = ({ routes }) => {
218
+ const value = JSON.stringify(Object.fromEntries(Object.entries(routes.byId)
219
+ .filter(([, { pathnamesSpa }]) => !!pathnamesSpa)
220
+ .map(([routeId, { pathnamesSpa }]) => [routeId, pathnamesSpa])
221
+ .sort()), null, 2);
222
+ return `
223
+ export const routesSpa = ${value} as const;
224
+
225
+ export default routesSpa;
226
+ `;
227
+ };
228
+
229
+ let dataParamsToTsInterfaceBody = (params) => Object.keys(params)
230
+ .reduce((pairs, paramName) => {
231
+ const value = params[paramName];
232
+ let type = "";
233
+ switch (value) {
234
+ case "number":
235
+ type = "number";
236
+ break;
237
+ case "string":
238
+ type = "string";
239
+ break;
240
+ default:
241
+ type = "string | number";
242
+ break;
243
+ }
244
+ pairs.push(`${paramName}: ${type};`);
245
+ return pairs;
246
+ }, [])
247
+ .join(" ");
248
+
249
+ const getTranslationValueOutput = (value) => {
250
+ if (utils.isString(value) || utils.isNumber(value)) {
251
+ return `"${value}"`;
252
+ }
253
+ else if (utils.isBoolean(value)) {
254
+ return `${value}`;
255
+ }
256
+ else if (utils.isArray(value)) {
257
+ return JSON.stringify(value);
258
+ }
259
+ return `(${JSON.stringify(value)})`;
260
+ };
261
+ const areEqualTranslationsValues = (a, b) => utils.areEqual(a, b);
262
+ const getFunctionBodyWithLocales$1 = (config, perLocaleValues) => {
263
+ const { defaultLocale } = config;
264
+ let output = "";
265
+ for (const locale in perLocaleValues) {
266
+ const value = perLocaleValues[locale];
267
+ if (locale !== defaultLocale &&
268
+ !areEqualTranslationsValues(value, perLocaleValues[defaultLocale])) {
269
+ output += `locale === "${locale}" ? ${getTranslationValueOutput(value)} : `;
270
+ }
271
+ }
272
+ output += getTranslationValueOutput(perLocaleValues[defaultLocale]);
273
+ return output;
274
+ };
275
+ var tFns = ({ config, options, translations }) => {
276
+ let output = `
277
+ /* eslint-disable @typescript-eslint/no-unused-vars */
278
+ /* eslint-disable prefer-const */
279
+ import type { I18n } from "./types";
280
+ import { tInterpolateParams } from "./tInterpolateParams";
281
+ import { tPluralise } from "./tPluralise";
282
+
283
+ `;
284
+ for (const translationId in translations) {
285
+ let { values, params, plural } = translations[translationId];
286
+ const name = `${options.translations.fnsPrefix}${translationId}`;
287
+ if (plural) {
288
+ if (params) {
289
+ params["count"] = "number";
290
+ }
291
+ else {
292
+ params = { count: "number" };
293
+ }
294
+ }
295
+ const argParam = params
296
+ ? `params: { ${dataParamsToTsInterfaceBody(params)} }`
297
+ : "";
298
+ const argLocale = "locale?: I18n.Locale";
299
+ const args = [argParam, argLocale].filter(Boolean).join(", ");
300
+ output += `export let ${name} = (${args}) => `;
301
+ let outputFnReturn = "";
302
+ if (utils.isPrimitive(values)) {
303
+ outputFnReturn += getTranslationValueOutput(values);
304
+ }
305
+ else {
306
+ outputFnReturn += getFunctionBodyWithLocales$1(config, values);
307
+ }
308
+ if (plural) {
309
+ outputFnReturn = `tPluralise(${outputFnReturn}, params.count)`;
310
+ }
311
+ if (params) {
312
+ outputFnReturn = `tInterpolateParams(${outputFnReturn}, params);`;
313
+ }
314
+ else {
315
+ outputFnReturn = `${outputFnReturn};`;
316
+ }
317
+ output += outputFnReturn;
318
+ output += `\n`;
319
+ }
320
+ return output;
321
+ };
322
+
323
+ const escapeEachChar = (input) => input
324
+ .split("")
325
+ .map((v) => `\\${v}`)
326
+ .join("");
327
+ var tInterpolateParams = ({ options }) => {
328
+ const { start, end } = options.translations.dynamicDelimiters;
329
+ return `
330
+ /* eslint-disable prefer-const */
331
+ export let tInterpolateParams = (
332
+ value: string,
333
+ params?: object,
334
+ ) =>
335
+ params ? value.replace(
336
+ /${escapeEachChar(start)}(.*?)${escapeEachChar(end)}/g,
337
+ (_, key) =>
338
+ params[key.trim() as keyof typeof params] + "",
339
+ ) : value;
340
+
341
+ export default tInterpolateParams;
342
+ `;
343
+ };
344
+
345
+ var tPluralise = () => {
346
+ return `
347
+ /* eslint-disable prefer-const */
348
+ let pluralRules = new Intl.PluralRules();
349
+
350
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
351
+ export let tPluralise = (values: any, count: number) =>
352
+ values[count] || values[pluralRules.select(count)] || (count === 0 ? values.zero : values["other"]);
353
+
354
+ export default tPluralise;
355
+ `;
356
+ };
357
+
358
+ var to = ({ config }) => `
359
+ import { isLocale } from "./isLocale";
360
+ import { toFormat } from "./toFormat";
361
+ import { routesSlim } from "./routesSlim";
362
+ import type { I18n } from "./types";
363
+
364
+ /**
365
+ * *To* route utility
366
+ *
367
+ * @returns A localised relative URL based on your i18nCompiler configuration
368
+ */
369
+ export function to<Id extends I18n.RouteId>(
370
+ id: Id,
371
+ ...args: Id extends I18n.RouteIdDynamic
372
+ ?
373
+ | [I18n.RouteParams[Id]]
374
+ | [I18n.RouteParams[Id], I18n.Locale]
375
+ : [] | [I18n.Locale]
376
+ ) {
377
+ const locale = (isLocale(args[0]) ? args[0] : args[1]) || "${config.defaultLocale}";
378
+
379
+ return toFormat(
380
+ locale,
381
+ (routesSlim[id] as Record<string, string>)[locale] ??
382
+ (routesSlim[id] as Record<string, string>)["${config.defaultLocale}"] ??
383
+ routesSlim[id],
384
+ isLocale(args[0]) ? undefined : args[0]
385
+ ) as I18n.RoutePathnames[Id];
386
+ }
387
+
388
+ export default to;
389
+ `;
390
+
391
+ const getFunctionBodyWithLocales = (config, perLocaleValues) => {
392
+ const { defaultLocale } = config;
393
+ let output = "";
394
+ for (const locale in perLocaleValues) {
395
+ const value = perLocaleValues[locale];
396
+ if (locale !== defaultLocale && value !== perLocaleValues[defaultLocale]) {
397
+ output += `locale === "${locale}" ? "${value}" : `;
398
+ }
399
+ }
400
+ output += '"' + perLocaleValues[defaultLocale] + '"';
401
+ return output;
402
+ };
403
+ var toFns = ({ config, routes, options }) => {
404
+ const hasOneLocale = config.locales.length === 1;
405
+ let output = `
406
+ /* eslint-disable prefer-const */
407
+ import { toFormat } from "./toFormat";
408
+ import type { I18n } from "./types";
409
+
410
+ `;
411
+ for (const routeId in routes.byId) {
412
+ const { pathnames, params } = routes.byId[routeId];
413
+ const name = `${options.routes.fnsPrefix}${utils.changeCaseSnake(routeId)}`;
414
+ const paramsType = `I18n.RouteParams["${routeId}"]`;
415
+ const argParam = params ? `params: ${paramsType}` : "";
416
+ const argLocale = hasOneLocale ? "" : "locale?: I18n.Locale";
417
+ const args = [argParam, argLocale].filter(Boolean).join(", ");
418
+ const formatArgLocale = hasOneLocale ? `""` : "locale";
419
+ const formatArgParams = params ? ", params" : "";
420
+ output += `export let ${name} = (${args}) => `;
421
+ if (utils.isString(pathnames)) {
422
+ output += `toFormat(${formatArgLocale}, "${pathnames}"${formatArgParams});`;
423
+ }
424
+ else {
425
+ output += `toFormat(${formatArgLocale}, ${getFunctionBodyWithLocales(config, pathnames)}${formatArgParams});`;
426
+ }
427
+ output += `\n`;
428
+ }
429
+ return output;
430
+ };
431
+
432
+ var toFormat = ({ config }) => `
433
+ export function toFormat(
434
+ locale: string | undefined,
435
+ pathname: string,
436
+ params?: object,
437
+ ) {
438
+ locale = locale || "${config.defaultLocale}";
439
+ if (process.env["NODE_ENV"] === "development") {
440
+ if (params) {
441
+ pathname.replace(/\\[(.*?)\\]/g, (_, dynamicKey) => {
442
+ const key = dynamicKey as Extract<keyof typeof params, string>;
443
+
444
+ if (!(key in params)) {
445
+ console.warn(
446
+ "[@koine/i18n]::interpolateTo, using '" +
447
+ pathname +
448
+ "' without param '" +
449
+ key +
450
+ "'",
451
+ { params }
452
+ );
453
+ }
454
+
455
+ if (!["string", "number"].includes(typeof params[key])) {
456
+ console.warn(
457
+ "[@koine/i18n]::toFormat, using '" +
458
+ pathname +
459
+ "' with unserializable param '" +
460
+ key +
461
+ "' (type '" +
462
+ Object.prototype.toString.call((params[key])).slice(8, -1) +
463
+ "')",
464
+ );
465
+ }
466
+ return "";
467
+ });
468
+ }
469
+ }
470
+
471
+ if (params) {
472
+ pathname = pathname.replace(
473
+ /\\[(.*?)\\]/g,
474
+ (_, key) =>
475
+ params[key as keyof typeof params] + "",
476
+ )
477
+ }
478
+ ${config.hideDefaultLocaleInUrl
479
+ ? `
480
+ if (locale !== "${config.defaultLocale}") {
481
+ return "/" + locale + pathname;
482
+ }
483
+ `
484
+ : ``}
485
+ return pathname;
486
+ }
487
+
488
+ export default toFormat;
489
+ `;
490
+
491
+ var toSpa = ({ config, options }) => {
492
+ const { idDelimiter } = options.routes.tokens;
493
+ return `
494
+ import { isLocale } from "./isLocale";
495
+ import { routesSpa } from "./routesSpa";
496
+ import { toFormat } from "./toFormat";
497
+ import type { I18n } from "./types";
498
+
499
+ /**
500
+ * *To spa* route utility
501
+ *
502
+ * @returns A localised relative URL based on your i18nCompiler configuration
503
+ */
504
+ export function toSpa<
505
+ Root extends keyof I18n.RouteSpa,
506
+ Path extends Extract<keyof I18n.RouteSpa[Root], string>,
507
+ >(
508
+ rootId: Root,
509
+ pathId: Path,
510
+ ...args: I18n.RouteJoinedId<Root, Path> extends I18n.RouteIdDynamic
511
+ ?
512
+ | [I18n.RouteParams[I18n.RouteJoinedId<Root, Path>]]
513
+ | [I18n.RouteParams[I18n.RouteJoinedId<Root, Path>], I18n.Locale]
514
+ : [] | [I18n.Locale]
515
+ ) {
516
+ const locale = (isLocale(args[0]) ? args[0] : args[1]) || "${config.defaultLocale}";
517
+ const fullId = \`\${rootId}${idDelimiter}\${pathId}\` as I18n.RouteJoinedId<Root, Path>;
518
+ return toFormat(
519
+ // FIXME: actually the locale will be prepended if hideDefaultLocaleInUrl will be false
520
+ "", // do not pass the locale so that won't be prepended
521
+ (routesSpa[fullId] as Record<string, string>)[locale],
522
+ args.length === 2
523
+ ? args[0]
524
+ : args[0] && !isLocale(args[0])
525
+ ? args[0]
526
+ : void 0,
527
+ ) as I18n.RouteSpa[Root][Path];
528
+ }
529
+
530
+ export default toSpa;
531
+ `;
532
+ };
533
+
534
+ const pluralSuffixes = ["zero", "one", "two", "few", "many", "other"];
535
+ const requiredPluralSuffix = "other";
536
+ let isPluralSuffix = (suffix) => pluralSuffixes.includes(suffix) ||
537
+ utils.isNumericLiteral(suffix);
538
+ let removePluralSuffix = (key) => {
539
+ const [suffix] = utils.splitReverse(key, "_");
540
+ return suffix ? key.replace(`_${suffix}`, "") : key;
541
+ };
542
+ let getPluralSuffix = (key) => utils.splitReverse(key, "_")[0];
543
+ let isPluralKey = (key) => {
544
+ const [suffix] = utils.splitReverse(key, "_");
545
+ return isPluralSuffix(suffix);
546
+ };
547
+ const groupPluralsKeysByRoot = (pluralKeys) => {
548
+ const map = {};
549
+ pluralKeys.forEach((key) => {
550
+ const [root] = utils.split(key, "_");
551
+ map[root] = map[root] || [];
552
+ map[root].push(key);
553
+ });
554
+ return map;
555
+ };
556
+ let transformKeysForPlurals = (keys) => {
557
+ if (keys.some(hasRequiredPluralSuffix) ||
558
+ keys.includes(requiredPluralSuffix)) {
559
+ const pluralKeys = keys.filter(isPluralKey);
560
+ if (pluralKeys.length) {
561
+ let transformedKeys = [...keys];
562
+ const groupedPlurals = groupPluralsKeysByRoot(pluralKeys);
563
+ utils.forin(groupedPlurals, (pluralRoot, pluralKeys) => {
564
+ if (!keys.includes(pluralRoot)) {
565
+ transformedKeys.push(pluralRoot);
566
+ }
567
+ pluralKeys.forEach((pluralKey) => {
568
+ if (keys.includes(pluralKey)) {
569
+ transformedKeys = transformedKeys.filter((k) => k !== pluralKey);
570
+ }
571
+ });
572
+ });
573
+ return transformedKeys;
574
+ }
575
+ }
576
+ return keys;
577
+ };
578
+ let hasRequiredPluralSuffix = (key) => isPluralKey(key) && getPluralSuffix(key) === requiredPluralSuffix;
579
+ let hasPlurals = (value) => Object.keys(value).some(hasRequiredPluralSuffix) ||
580
+ Object.keys(value).includes(requiredPluralSuffix);
581
+ let hasOnlyPluralKeys = (value) => hasPlurals(value) ? pickNonPluralKeys(value).length === 0 : false;
582
+ let pickNonPluralKeys = (value) => Object.keys(value).filter((k) => !isPluralSuffix(k));
583
+ let pickNonPluralValue = (value) => (hasPlurals(value) ? utils.objectPick(value, pickNonPluralKeys(value)) : value);
584
+
585
+ const buildTypeForObjectValue = (key, value) => {
586
+ if (!utils.isArray(value) && utils.isObject(value)) {
587
+ if (hasPlurals(value)) {
588
+ return hasOnlyPluralKeys(value)
589
+ ? `"${key}": string;`
590
+ : `"${key}": ${buildTypeForValue(pickNonPluralValue(value))}`;
591
+ }
592
+ }
593
+ return `"${key}": ${buildTypeForValue(value)}`;
594
+ };
595
+ const buildTypeForValue = (value) => {
596
+ let out = "";
597
+ let primitiveType = "";
598
+ if (utils.isBoolean(value)) {
599
+ primitiveType = "boolean";
600
+ }
601
+ else if (utils.isString(value)) {
602
+ primitiveType = "string";
603
+ }
604
+ if (primitiveType) {
605
+ out += primitiveType + ";";
606
+ }
607
+ else if (!value) {
608
+ out += "";
609
+ }
610
+ else if (utils.isArray(value)) {
611
+ const firstValue = value[0];
612
+ out += `${buildTypeForValue(firstValue)}[];`;
613
+ }
614
+ else if (utils.isObject(value)) {
615
+ out += "{";
616
+ const keys = transformKeysForPlurals(Object.keys(value));
617
+ for (let i = 0; i < keys.length; i++) {
618
+ const key = keys[i];
619
+ const single = value[key] || "";
620
+ out += buildTypeForObjectValue(key, single);
621
+ }
622
+ out += "};";
623
+ }
624
+ out = out.replace(/;\[\];/g, "[];");
625
+ out = out.replace(/;+/g, ";");
626
+ return out;
627
+ };
628
+ const buildTranslationsDictionary = (config, dataInput) => {
629
+ const { translationFiles } = dataInput;
630
+ const { defaultLocale } = config;
631
+ const defaultLocaleFiles = translationFiles.filter((f) => f.locale === defaultLocale);
632
+ const out = [];
633
+ for (let i = 0; i < defaultLocaleFiles.length; i++) {
634
+ const { path, data } = defaultLocaleFiles[i];
635
+ const namespace = path.replace(".json", "");
636
+ out.push(`"${namespace}": ${buildTypeForValue(data)}`);
637
+ }
638
+ return out.sort();
639
+ };
640
+ const buildRouteParams = (routes) => {
641
+ const out = [];
642
+ utils.forin(routes.byId, (routeId, { params }) => {
643
+ if (params) {
644
+ out.push(`"${routeId}": { ${dataParamsToTsInterfaceBody(params)} };`);
645
+ }
646
+ });
647
+ return out;
648
+ };
649
+ const buildUnionType = (values) => values
650
+ .sort()
651
+ .map((routeId) => `"${routeId}"`)
652
+ .join(" | ");
653
+ const buildRoutesUnion = (routes, filterFn) => buildUnionType(Object.keys(routes.byId).filter((routeId) => filterFn(routeId, routes.byId[routeId])));
654
+ const groupRoutesSpa = (routes) => Object.keys(routes.byId).reduce((map, routeId) => {
655
+ const route = routes.byId[routeId];
656
+ if (route.inWildcard) {
657
+ for (let I = 0; I < routes.wildcardIds.length; I++) {
658
+ const spaRootRouteId = routes.wildcardIds[I];
659
+ if (routeId.startsWith(spaRootRouteId)) {
660
+ map[spaRootRouteId] = map[spaRootRouteId] || [];
661
+ map[spaRootRouteId].push(routeId);
662
+ }
663
+ }
664
+ }
665
+ return map;
666
+ }, {});
667
+ const buildRoutesSpa = (config, routes, options) => {
668
+ const map = groupRoutesSpa(routes);
669
+ const output = [];
670
+ for (const rootRouteId in map) {
671
+ const pathRoutesIds = map[rootRouteId].map((fullRouteId) => fullRouteId.split(rootRouteId)[1].slice(1));
672
+ const rootPathname = routes.byId[rootRouteId].pathnames[config.defaultLocale];
673
+ const pathnames = [];
674
+ for (let i = 0; i < pathRoutesIds.length; i++) {
675
+ const subRouteId = pathRoutesIds[i];
676
+ const fullRouteId = `${rootRouteId}${options.routes.tokens.idDelimiter}${subRouteId}`;
677
+ const fullPathname = routes.byId[fullRouteId].pathnames[config.defaultLocale];
678
+ const pathPathname = fullPathname.split(rootPathname)[1];
679
+ pathnames.push(`"${subRouteId}": "${pathPathname}";`);
680
+ }
681
+ output.push(`"${rootRouteId}": { ${pathnames.join(" ")} }`);
682
+ }
683
+ return output;
684
+ };
685
+ const buildRoutesPathnames = (config, routes) => {
686
+ const output = [];
687
+ for (const routeId in routes.byId) {
688
+ const route = routes.byId[routeId];
689
+ output.push(`"${route.id}": "${route.pathnames[config.defaultLocale]}";`);
690
+ }
691
+ return output;
692
+ };
693
+ var types = ({ config, input, routes, options, }) => {
694
+ const routeIdStatic = buildRoutesUnion(routes, (_, { params }) => !params);
695
+ const routeIdDynamic = buildRoutesUnion(routes, (_, { params }) => !!params);
696
+ const { idDelimiter } = options.routes.tokens;
697
+ return `
698
+ /* eslint-disable @typescript-eslint/no-namespace */
699
+ /* eslint-disable @typescript-eslint/no-explicit-any */
700
+ /* eslint-disable @typescript-eslint/ban-types */
701
+ import type { Split } from "@koine/utils";
702
+ import type { I18nUtils } from "@koine/i18n";
703
+
704
+ export namespace I18n {
705
+ /**
706
+ * Any of the available locale code
707
+ */
708
+ export type Locale = ${config.locales.map((l) => `"${l}"`).join(" | ")};
709
+
710
+ /**
711
+ * Utility to map values by all available locales
712
+ *
713
+ * @usecase I need to map zendesk URLs to my project's locales
714
+ */
715
+ export type LocalesMap<T = any> = Record<Locale, T>;
716
+
717
+ /**
718
+ * Any of the available route id
719
+ */
720
+ export type RouteId = RouteIdStatic | RouteIdDynamic;
721
+
722
+ /**
723
+ * The static routes available ids
724
+ */
725
+ export type RouteIdStatic = ${routeIdStatic};
726
+
727
+ /**
728
+ * The dynamic routes available ids
729
+ */
730
+ export type RouteIdDynamic = ${routeIdDynamic};
731
+
732
+ /**
733
+ * SPA routes ids map
734
+ */
735
+ /**
736
+ * Map every SPA path divided by their roots to their actual pathname value for the default locale
737
+ */
738
+ export type RouteSpa = {
739
+ ${buildRoutesSpa(config, routes, options).join("\n ")}
740
+ }
741
+
742
+ /**
743
+ * Map every route id to its actual pathanem value for the default locale
744
+ */
745
+ export type RoutePathnames = {
746
+ ${buildRoutesPathnames(config, routes).join("\n ")}
747
+ }
748
+
749
+ /**
750
+ * Route dynamic params dictionary for each dynamic route id
751
+ */
752
+ export type RouteParams = {
753
+ ${buildRouteParams(routes).join("\n ")}
754
+ }
755
+
756
+ /**
757
+ * Utility to join two route ids
758
+ */
759
+ export type RouteJoinedId<Root extends string, Tail extends string> = \`\${Root}${idDelimiter}\${Tail}\` extends RouteId ? \`\${Root}${idDelimiter}\${Tail}\` : never;
760
+
761
+ /**
762
+ * Extract all children routes that starts with the given string
763
+ *
764
+ * This is useful to get the subroutes of an application area, e.g. all subroutes
765
+ * of a dashboard, using it with: \` type DashboardRoutes = RoutesChildrenOf<"dashboard">;\`
766
+ */
767
+ export type RoutesChildrenOf<
768
+ TStarts extends string,
769
+ T extends string = RouteId,
770
+ > = T extends \`\${TStarts}.\${infer First}\` ? \`\${TStarts}.\${First}\` : never;
771
+
772
+ /**
773
+ * The types extracted from the translations JSON files, this is a little
774
+ * more sophisticated than the type result of \`typeof "./en/messages.json"\`
775
+ */
776
+ export type TranslationsDictionary = {
777
+ ${buildTranslationsDictionary(config, input).join("\n ")}
778
+ }
779
+
780
+ /**
781
+ * Any of the available translations namespaces
782
+ */
783
+ export type TranslateNamespace = Extract<keyof TranslationsDictionary, string>;
784
+
785
+ /**
786
+ * Translation **value** found at a specific _path_ in the given _namespace_
787
+ *
788
+ * \`TPath\` can be any of all possible paths:
789
+ * - \`myKey\` sub dictionaries within a namespace
790
+ * - \`myKey.nested\` whatever nested level of nesting within a namespace
791
+ */
792
+ export type TranslationAtPathFromNamespace<
793
+ TNamespace extends TranslateNamespace,
794
+ TPath extends TranslationsPaths<TranslationsDictionary[TNamespace]>,
795
+ > = TNamespace extends TranslateNamespace
796
+ ? TPath extends string // TranslationsPaths<TranslationsDictionary[TNamespace]>
797
+ ? I18nUtils.Get<TranslationsDictionary[TNamespace], TPath>
798
+ : TranslationsDictionary[TNamespace]
799
+ : never;
800
+
801
+ /**
802
+ * The generic type passed and to use with {@link TranslationAtPath} when you
803
+ * want to build a type extending that
804
+ */
805
+ export type TranslationAtPathGeneric =
806
+ | TranslateNamespace
807
+ | TranslationsAllPaths;
808
+
809
+ /**
810
+ * Translation **value** found at a _path_
811
+ *
812
+ * \`TPath\` can be any of all possible paths begininng with a namespace:
813
+ * - \`namespace\` only a namespace
814
+ * - \`namespace:myKey\` sub dictionaries within a namespace
815
+ * - \`namespace:myKey.nested\` whatever nested level of nesting
816
+ */
817
+ export type TranslationAtPath<TPath extends TranslationAtPathGeneric> =
818
+ TPath extends TranslateNamespace
819
+ ? TranslationsDictionary[TPath]
820
+ : TPath extends \`\${infer Namespace}:\${infer Path}\`
821
+ ? Namespace extends TranslateNamespace
822
+ ? I18nUtils.Get<TranslationsDictionary[Namespace], Path>
823
+ : never
824
+ : never;
825
+
826
+ /**
827
+ * All translations paths from the given _path_
828
+ *
829
+ * \`TPath\` can be any of all possible paths begininng with a namespace:
830
+ * - \`namespace\` only a namespace
831
+ * - \`namespace:myKey\` sub dictionaries within a namespace
832
+ * - \`namespace:myKey.nested\` whatever nested level of nesting
833
+ */
834
+ export type TranslationsPathsFrom<TPath extends TranslationAtPathGeneric> =
835
+ TPath extends TranslateNamespace
836
+ ? TranslationsPaths<TranslationsDictionary[TPath]>
837
+ : TPath extends \`\${infer Namespace}:\${infer Path}\`
838
+ ? Namespace extends TranslateNamespace
839
+ ? I18nUtils.Get<TranslationsDictionary[Namespace], Path> extends object
840
+ ? TranslationsPaths<I18nUtils.Get<TranslationsDictionary[Namespace], Path>>
841
+ : TranslationsPaths<TranslationsDictionary[Namespace]>
842
+ : never
843
+ : never;
844
+
845
+ /**
846
+ * All translations _paths_ of the given one, e.g. if \`TPath\` would be
847
+ * \`"dashboard.users.[id].edit"\` the generated type would be the union
848
+ * \`"dashboard.users.[id]" | "dashboard.users" | "dashboard"\`.
849
+ */
850
+ export type TranslationsPathsAncestors<
851
+ TPath extends string,
852
+ TSeparator extends string = ".",
853
+ > = I18nUtils.BuildRecursiveJoin<Split<TPath, TSeparator>, TSeparator>;
854
+
855
+ /**
856
+ * Recursive mapped type to extract all usable string paths from a translation
857
+ * definition object (usually from a JSON file).
858
+ */
859
+ export type TranslationsPaths<T, TAsObj extends boolean = true> = I18nUtils.Paths<T, TAsObj>;
860
+
861
+ /**
862
+ * Recursive mapped type of all usable string paths from the whole translations
863
+ * dictionary.
864
+ */
865
+ export type TranslationsAllPaths = I18nUtils.AllPaths<TranslationsDictionary>;
866
+
867
+ /**
868
+ * Unlike in \`next-translate\` we allow passing some predefined arguments as
869
+ * shortcuts for common use cases:
870
+ * - \`"obj"\` as a shortcut for \`{ returnObjects: true }\`
871
+ * - \`""\` as a shortcut for \`{ fallback: "" }\`
872
+ *
873
+ */
874
+ export type TranslationShortcut = "obj" | "";
875
+
876
+ /**
877
+ * Query object to populate the returned translated string interpolating data
878
+ * or a TranslationShortcut.
879
+ *
880
+ * NOTE: when using a shortcut passing TranslationOptions to \`t()\` is not supported
881
+ * TODO: type safe this behaviour of the third argument (options).
882
+ */
883
+ export type TranslationQuery =
884
+ | undefined
885
+ | null
886
+ | TranslationShortcut
887
+ | {
888
+ [key: string]: string | number | boolean;
889
+ };
890
+
891
+ /**
892
+ * Opions of the translate function or a TranslationShortcut.
893
+ *
894
+ * NOTE: when using a shortcut passing TranslationOptions to \`t()\` is not supported
895
+ * TODO: type safe this behaviour of the third argument (options).
896
+ */
897
+ export type TranslationOptions =
898
+ | undefined
899
+ | TranslationShortcut
900
+ | {
901
+ returnObjects?: boolean;
902
+ fallback?: string | string[];
903
+ default?: string;
904
+ };
905
+
906
+ /**
907
+ * Translate function which optionally accept a namespace as first argument
908
+ */
909
+ export type Translate<
910
+ TNamespace extends TranslateNamespace | undefined = TranslateNamespace,
911
+ > = TNamespace extends TranslateNamespace
912
+ ? TranslateNamespaced<TNamespace>
913
+ : TranslateDefault;
914
+
915
+ /**
916
+ * Translate function **without** namespace, it allows to select any of the all
917
+ * available strings in _all_ namespaces.
918
+ */
919
+ export type TranslateDefault = <
920
+ TPath extends TranslationsAllPaths,
921
+ TReturn = TranslationAtPath<TPath>,
922
+ >(
923
+ s: TPath,
924
+ q?: TranslationQuery,
925
+ o?: TranslationOptions,
926
+ ) => TReturn;
927
+
928
+ /**
929
+ * Translate function **with** namespace, it allows to select all available
930
+ * strings _only_ in the given namespace.
931
+ */
932
+ export type TranslateNamespaced<TNamespace extends TranslateNamespace> = <
933
+ TPath extends TranslationsPaths<TranslationsDictionary[TNamespace]>,
934
+ TReturn = TranslationAtPathFromNamespace<TNamespace, TPath>,
935
+ >(
936
+ s: TPath,
937
+ q?: TranslationQuery,
938
+ o?: TranslationOptions,
939
+ ) => TReturn;
940
+
941
+ /**
942
+ * Translate function _loose_ type, to use only in implementations that uses
943
+ * the \`t\` function indirectly without needng knowledge of the string it needs
944
+ * to output.
945
+ */
946
+ export type TranslateLoose<TReturn = string> = (
947
+ s?: any,
948
+ q?: TranslationQuery,
949
+ o?: TranslationOptions,
950
+ ) => TReturn;
951
+
952
+ /**
953
+ * Translate function _loosest_ type it allows to return string or object or array
954
+ * or whatever basically.
955
+ */
956
+ export type TranslateLoosest<TReturn = any> = (
957
+ s?: any,
958
+ q?: TranslationQuery,
959
+ o?: TranslationOptions,
960
+ ) => TReturn;
961
+ }
962
+ `;
963
+ };
964
+
965
+ const adapter$2 = () => {
966
+ return {
967
+ files: [
968
+ { name: "config.cjs", fn: configCjs, ext: "js" },
969
+ { name: "config", fn: config, ext: "ts", index: true },
970
+ { name: "defaultLocale", fn: defaultLocale, ext: "ts", index: true },
971
+ {
972
+ name: "deriveLocalisedPathnames",
973
+ fn: deriveLocalisedPathnames,
974
+ ext: "ts",
975
+ index: true,
976
+ },
977
+ { name: "isLocale", fn: isLocale, ext: "ts", index: true },
978
+ { name: "locales", fn: locales, ext: "ts", index: true },
979
+ {
980
+ name: "pathnameToRouteId",
981
+ fn: pathnameToRouteId,
982
+ ext: "ts",
983
+ index: true,
984
+ },
985
+ { name: "routes", fn: routes, ext: "ts" },
986
+ { name: "routesSlim", fn: routesSlim, ext: "ts" },
987
+ { name: "routesSpa", fn: routesSpa, ext: "ts" },
988
+ { name: "tFns", fn: tFns, ext: "ts" },
989
+ {
990
+ name: "tInterpolateParams",
991
+ fn: tInterpolateParams,
992
+ ext: "ts",
993
+ },
994
+ { name: "to", fn: to, ext: "ts", index: true },
995
+ { name: "toFns", fn: toFns, ext: "ts", index: true },
996
+ { name: "toFormat", fn: toFormat, ext: "ts", index: true },
997
+ { name: "toSpa", fn: toSpa, ext: "ts", index: true },
998
+ { name: "tPluralise", fn: tPluralise, ext: "ts" },
999
+ { name: "types", fn: types, ext: "ts", index: true },
1000
+ ],
1001
+ };
1002
+ };
1003
+
1004
+ var DynamicNamespaces = () => `
1005
+ "use client";
1006
+
1007
+ import _DynamicNamespaces from "next-translate/DynamicNamespaces";
1008
+
1009
+ export const DynamicNamespaces = _DynamicNamespaces;
1010
+
1011
+ export default DynamicNamespaces;
1012
+ `;
1013
+
1014
+ var T = () => `
1015
+ "use client";
1016
+
1017
+ import type { TransProps } from "next-translate";
1018
+ import Trans from "next-translate/Trans";
1019
+ import type { I18n } from "./types";
1020
+
1021
+ export type TProps<
1022
+ TNamespace extends I18n.TranslateNamespace | undefined = undefined,
1023
+ > =
1024
+ | (Omit<TransProps, "i18nKey" | "ns"> & {
1025
+ i18nKey: I18n.TranslationsAllPaths;
1026
+ })
1027
+ | (Omit<TransProps, "i18nKey" | "ns"> & {
1028
+ ns: TNamespace;
1029
+ i18nKey: I18n.TranslationsPaths<TNamespace>;
1030
+ });
1031
+
1032
+ const TypedT = <
1033
+ TNamespace extends I18n.TranslateNamespace | undefined = undefined,
1034
+ >(
1035
+ props: TProps<TNamespace>,
1036
+ ) =>
1037
+ (<Trans {...(props as TransProps)} />) as React.ReactElement<
1038
+ TProps<TNamespace>
1039
+ >;
1040
+
1041
+ export const T = Trans as typeof TypedT;
1042
+
1043
+ export default T;
1044
+ `;
1045
+
1046
+ var TransText = () => `
1047
+ "use client";
1048
+
1049
+ import _TransText from "next-translate/TransText";
1050
+
1051
+ export const TransText = _TransText;
1052
+
1053
+ export default TransText;
1054
+ `;
1055
+
1056
+ var getT = () => `
1057
+ import _getT from "next-translate/getT";
1058
+ import type { I18n } from "./types";
1059
+
1060
+ export type GetT = <
1061
+ TNamespace extends I18n.TranslateNamespace | undefined = undefined,
1062
+ >(
1063
+ locale?: I18n.Locale,
1064
+ namespace?: TNamespace,
1065
+ ) => Promise<I18n.Translate<TNamespace>>;
1066
+
1067
+ export const getT = _getT as GetT;
1068
+
1069
+ export default getT;
1070
+ `;
1071
+
1072
+ var nextTranslateI18n = ({ config }) => `
1073
+ /**
1074
+ * Get 'next-translate' configuration
1075
+ *
1076
+ * @see https://github.com/vinissimus/next-translate#how-are-translations-loaded
1077
+ *
1078
+ * @param {Omit<Partial<import("next-translate").I18nConfig>, "pages"> & { pages: Record<string, import("./types").I18n.TranslateNamespace[]> }} config
1079
+ */
1080
+ module.exports = (config = { pages: {} }) => {
1081
+ return {
1082
+ locales: [${config.locales.map((l) => `"${l}"`).join(", ")}],
1083
+ defaultLocale: "${config.defaultLocale}",
1084
+ logBuild: false,
1085
+ // logger: () => void 0,
1086
+ loadLocaleFrom: (locale, namespace) => import(\`./translations/\${locale}/\${namespace}.json\`).then((m) => m.default),
1087
+ ...config,
1088
+ };
1089
+ }
1090
+ `;
1091
+
1092
+ var useT = () => `
1093
+ "use client";
1094
+
1095
+ import { useMemo } from "react";
1096
+ import useTranslation from "next-translate/useTranslation";
1097
+ import type { I18n } from "./types";
1098
+
1099
+ /**
1100
+ * Wrap next-translate useTranslations for type safety and adds TranslationShortcut
1101
+ * as second/thir argument.
1102
+ *
1103
+ * @see https://github.com/vinissimus/next-translate/issues/513#issuecomment-779826418
1104
+ *
1105
+ * About the typescript support for translation strings see:
1106
+ * - https://github.com/vinissimus/next-translate/issues/721
1107
+ */
1108
+ export const useT = <TNamespace extends I18n.TranslateNamespace>(namespace: TNamespace) => {
1109
+ const t = useTranslation().t;
1110
+ const tMemoized = useMemo(
1111
+ () =>
1112
+ function <
1113
+ TPath extends I18n.TranslationsPaths<I18n.TranslationsDictionary[TNamespace]>,
1114
+ TReturn = I18n.TranslationAtPathFromNamespace<TNamespace, TPath>,
1115
+ >(s: TPath, q?: I18n.TranslationQuery, o?: I18n.TranslationOptions): TReturn {
1116
+ return t(
1117
+ (namespace ? namespace + ":" + s : s) as string,
1118
+ q === "obj" || q === "" ? null : q,
1119
+ q === "obj" || o === "obj"
1120
+ ? { returnObjects: true }
1121
+ : q === "" || o === ""
1122
+ ? { fallback: "" }
1123
+ : o,
1124
+ ) as TReturn;
1125
+ // ) as TReturn extends (undefined | never | unknown) ? TranslateReturn<I18n.TranslationQuery, I18n.TranslationOptions> : TReturn;
1126
+ // );
1127
+ },
1128
+ [t, namespace],
1129
+ );
1130
+ return tMemoized;
1131
+ };
1132
+
1133
+ export default useT;
1134
+ `;
1135
+
1136
+ const adapter$1 = () => {
1137
+ return {
1138
+ dependsOn: ["next"],
1139
+ needsTranslationsFiles: true,
1140
+ files: [
1141
+ {
1142
+ name: "DynamicNamespaces",
1143
+ fn: DynamicNamespaces,
1144
+ ext: "tsx",
1145
+ index: true,
1146
+ },
1147
+ { name: "getT", fn: getT, ext: "ts", index: true },
1148
+ { name: "nextTranslateI18n", fn: nextTranslateI18n, ext: "js" },
1149
+ { name: "T", fn: T, ext: "tsx", index: true },
1150
+ { name: "TransText", fn: TransText, ext: "tsx", index: true },
1151
+ { name: "useT", fn: useT, ext: "ts", index: true },
1152
+ ],
1153
+ };
1154
+ };
1155
+
1156
+ function transformPathname(rawPathnameOrTemplate, wildcard) {
1157
+ return ("/" +
1158
+ rawPathnameOrTemplate
1159
+ .split("/")
1160
+ .filter(Boolean)
1161
+ .map((part) => {
1162
+ if (part.startsWith("[[...")) {
1163
+ return `:${encodeURIComponent(part.slice(5, -2))}`;
1164
+ }
1165
+ if (part.startsWith("[[")) {
1166
+ return `:${encodeURIComponent(part.slice(2, -2))}`;
1167
+ }
1168
+ if (part.startsWith("[")) {
1169
+ return `:${encodeURIComponent(part.slice(1, -1))}`;
1170
+ }
1171
+ return `${encodeURIComponent(part)}`;
1172
+ })
1173
+ .join("/") +
1174
+ (wildcard ? "/:wildcard*" : ""));
1175
+ }
1176
+
1177
+ function generatePathRedirect(arg) {
1178
+ const { localeSource, localeDestination, template, pathname, permanent } = arg;
1179
+ const sourcePrefix = localeSource ? `${localeSource}/` : "";
1180
+ const source = formatRoutePathname.formatRoutePathname(sourcePrefix + template);
1181
+ const destinationPrefix = localeDestination ? `${localeDestination}/` : "";
1182
+ const destination = formatRoutePathname.formatRoutePathname(destinationPrefix + pathname);
1183
+ if (source === destination)
1184
+ return;
1185
+ const redirect = {
1186
+ source,
1187
+ destination,
1188
+ permanent: Boolean(permanent),
1189
+ };
1190
+ return redirect;
1191
+ }
1192
+ let generateRedirects = (config, routes, options, localeParam = "", permanent = false) => {
1193
+ const { defaultLocale, hideDefaultLocaleInUrl } = config;
1194
+ const regexIdDelimiter = new RegExp(utils.escapeRegExp(options.tokens.idDelimiter), "g");
1195
+ const redirects = [];
1196
+ for (const routeId in routes) {
1197
+ const route = routes[routeId];
1198
+ const pathnamesByLocale = routes[routeId].pathnames;
1199
+ for (const locale in pathnamesByLocale) {
1200
+ const localisedPathname = pathnamesByLocale[locale];
1201
+ const template = transformPathname(routeId.replace(regexIdDelimiter, "/"), route.wildcard);
1202
+ const pathname = transformPathname(localisedPathname, route.wildcard);
1203
+ if (route.inWildcard)
1204
+ break;
1205
+ const isDefaultLocale = locale === defaultLocale;
1206
+ const isVisibleDefaultLocale = isDefaultLocale && !hideDefaultLocaleInUrl;
1207
+ const isHiddenDefaultLocale = isDefaultLocale && hideDefaultLocaleInUrl;
1208
+ const arg = { template, pathname, permanent };
1209
+ if (localeParam) {
1210
+ if (isVisibleDefaultLocale) {
1211
+ redirects.push(generatePathRedirect({ ...arg, localeDestination: locale }));
1212
+ }
1213
+ else if (isHiddenDefaultLocale) {
1214
+ redirects.push(generatePathRedirect({ ...arg, localeSource: locale }));
1215
+ }
1216
+ else if (!isDefaultLocale) {
1217
+ redirects.push(generatePathRedirect({
1218
+ ...arg,
1219
+ localeSource: locale,
1220
+ localeDestination: locale,
1221
+ }));
1222
+ }
1223
+ else {
1224
+ redirects.push(generatePathRedirect(arg));
1225
+ }
1226
+ }
1227
+ else {
1228
+ if (pathname !== template) {
1229
+ if (isVisibleDefaultLocale) {
1230
+ redirects.push(generatePathRedirect({ ...arg, localeDestination: locale }));
1231
+ }
1232
+ else if (!isDefaultLocale) {
1233
+ redirects.push(generatePathRedirect({
1234
+ ...arg,
1235
+ localeSource: locale,
1236
+ localeDestination: locale,
1237
+ }));
1238
+ }
1239
+ else {
1240
+ redirects.push(generatePathRedirect(arg));
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ const cleaned = utils.arrayUniqueByProperties(redirects.filter(Boolean), ["source", "destination"])
1247
+ .sort((a, b) => a.source.localeCompare(b.source))
1248
+ .map((rewrite) => (localeParam ? rewrite : { ...rewrite, locale: false }));
1249
+ return cleaned;
1250
+ };
1251
+
1252
+ var nextRedirects = ({ config, routes, options }) => {
1253
+ const value = JSON.stringify(generateRedirects(config, routes.byId, options.routes), null, 2);
1254
+ return `module.exports = ${value}`;
1255
+ };
1256
+
1257
+ function generatePathRewrite(arg) {
1258
+ const { localeSource, localeDestination, template, pathname } = arg;
1259
+ let sourcePrefix = "";
1260
+ if (localeSource)
1261
+ sourcePrefix = `/${localeSource}`;
1262
+ const source = formatRoutePathname.formatRoutePathname(sourcePrefix + pathname);
1263
+ let destinationPrefix = "";
1264
+ if (localeDestination)
1265
+ destinationPrefix = `/${localeDestination}`;
1266
+ const destination = formatRoutePathname.formatRoutePathname(destinationPrefix + template);
1267
+ if (source === destination)
1268
+ return;
1269
+ const rewrite = { source, destination };
1270
+ return rewrite;
1271
+ }
1272
+ const generateRewriteForPathname = (config, localeParam = "", locale, template, pathname, rewrites) => {
1273
+ const { defaultLocale, hideDefaultLocaleInUrl } = config;
1274
+ const isDefaultLocale = locale === defaultLocale;
1275
+ const isHiddenDefaultLocale = isDefaultLocale && hideDefaultLocaleInUrl;
1276
+ const isHiddenLocale = isHiddenDefaultLocale;
1277
+ const arg = { config, template, pathname };
1278
+ if (localeParam) {
1279
+ if (isHiddenLocale) {
1280
+ rewrites.push(generatePathRewrite({ ...arg, localeDestination: locale }));
1281
+ }
1282
+ else {
1283
+ rewrites.push(generatePathRewrite({ ...arg, localeSource: locale, localeDestination: locale }));
1284
+ }
1285
+ }
1286
+ else {
1287
+ if (pathname !== template) {
1288
+ if (isHiddenLocale) {
1289
+ rewrites.push(generatePathRewrite(arg));
1290
+ }
1291
+ else {
1292
+ rewrites.push({
1293
+ ...generatePathRewrite({ ...arg, localeSource: locale }),
1294
+ locale: false,
1295
+ });
1296
+ }
1297
+ }
1298
+ }
1299
+ };
1300
+ let generateRewrites = (config, routes, options, localeParam = "") => {
1301
+ const regexIdDelimiter = new RegExp(utils.escapeRegExp(options.tokens.idDelimiter), "g");
1302
+ const rewrites = [];
1303
+ for (const routeId in routes) {
1304
+ const route = routes[routeId];
1305
+ const pathnamesByLocale = routes[routeId].pathnames;
1306
+ for (const locale in pathnamesByLocale) {
1307
+ const localisedPathname = pathnamesByLocale[locale];
1308
+ const routeIdAsTemplate = routeId.replace(regexIdDelimiter, "/");
1309
+ if (route.inWildcard)
1310
+ break;
1311
+ generateRewriteForPathname(config, localeParam, locale, transformPathname(routeIdAsTemplate), transformPathname(localisedPathname), rewrites);
1312
+ if (route.wildcard) {
1313
+ generateRewriteForPathname(config, localeParam, locale, transformPathname(routeIdAsTemplate, route.wildcard), transformPathname(localisedPathname, route.wildcard), rewrites);
1314
+ }
1315
+ }
1316
+ }
1317
+ const cleaned = utils.arrayUniqueByProperties(rewrites.filter(Boolean), ["source", "destination"]).sort((a, b) => {
1318
+ return a.source.localeCompare(b.source);
1319
+ });
1320
+ return cleaned;
1321
+ };
1322
+
1323
+ var nextRewrites = ({ config, routes, options }) => {
1324
+ const value = JSON.stringify(generateRewrites(config, routes.byId, options.routes), null, 2);
1325
+ return `module.exports = ${value}`;
1326
+ };
1327
+
1328
+ var useCurrentLocalisedPathnames = () => `
1329
+ "use client";
1330
+
1331
+ import { useEffect, useState } from "react";
1332
+ import { deriveLocalisedPathnames, type LocalisedPathnames } from "./deriveLocalisedPathnames";
1333
+ import { useRouteId } from "./useRouteId";
1334
+
1335
+ export function useCurrentLocalisedPathnames() {
1336
+ const routeId = useRouteId();
1337
+ const [urls, setUrls] = useState<LocalisedPathnames>({} as LocalisedPathnames);
1338
+
1339
+ useEffect(() => {
1340
+ setUrls(deriveLocalisedPathnames(routeId, location));
1341
+ }, [routeId]);
1342
+
1343
+ return urls;
1344
+ }
1345
+
1346
+ export default useCurrentLocalisedPathnames;
1347
+ `;
1348
+
1349
+ var useLocale = ({ config }) => `
1350
+ import { useRouter } from "next/router";
1351
+ import type { I18n } from "./types";
1352
+
1353
+ export const useLocale = () => (useRouter().locale as I18n.Locale) || "${config.defaultLocale}";
1354
+
1355
+ export default useLocale;
1356
+ `;
1357
+
1358
+ var useRouteId = () => `
1359
+ import { useRouter } from "next/router";
1360
+ import type { I18n } from "./types";
1361
+ import { pathnameToRouteId } from "./pathnameToRouteId";
1362
+
1363
+ export const useRouteId = () =>
1364
+ pathnameToRouteId(useRouter().pathname) as I18n.RouteId;
1365
+
1366
+ export default useRouteId;
1367
+ `;
1368
+
1369
+ var useTo = () => `
1370
+ "use client";
1371
+
1372
+ import { to } from "./to";
1373
+ import type { I18n } from "./types";
1374
+ import { useLocale } from "./useLocale";
1375
+
1376
+ export type UseToReturn = ReturnType<typeof useTo>;
1377
+
1378
+ export const useTo = () => {
1379
+ const locale = useLocale();
1380
+ return <Id extends I18n.RouteId>(
1381
+ routeId: Id,
1382
+ ...args: Id extends I18n.RouteIdDynamic
1383
+ ? [params: I18n.RouteParams[Id]]
1384
+ : []
1385
+ ) =>
1386
+ args[0]
1387
+ ? // @ts-expect-error nevermind
1388
+ (to(routeId, args[0], locale) as I18n.RoutePathnames[Id])
1389
+ : // @ts-expect-error nevermind
1390
+ (to(routeId, locale) as I18n.RoutePathnames[Id]);
1391
+ };
1392
+
1393
+ export default useTo;
1394
+ `;
1395
+
1396
+ var useToSpa = () => `
1397
+ "use client";
1398
+
1399
+ import { toSpa } from "./toSpa";
1400
+ import type { I18n } from "./types";
1401
+ import { useLocale } from "./useLocale";
1402
+
1403
+ export type UseToSpaReturn = ReturnType<typeof useToSpa>;
1404
+
1405
+ export const useToSpa = () => {
1406
+ const locale = useLocale();
1407
+ return <
1408
+ Root extends keyof I18n.RouteSpa,
1409
+ Path extends Extract<keyof I18n.RouteSpa[Root], string>,
1410
+ >(
1411
+ root: Root,
1412
+ path: Path,
1413
+ ...args: I18n.RouteJoinedId<Root, Path> extends I18n.RouteIdDynamic
1414
+ ? [params: I18n.RouteParams[I18n.RouteJoinedId<Root, Path>]]
1415
+ : I18n.RouteJoinedId<Root, Path> extends I18n.RouteIdStatic
1416
+ ? []
1417
+ : never
1418
+ ) => {
1419
+ const [params] = args;
1420
+ return (
1421
+ // prettier-ignore
1422
+ // @ts-expect-error FIXME: types
1423
+ (params ? toSpa(root, path, params, locale) : toSpa(root, path, locale)) as I18n.RouteSpa[Root][Path]
1424
+ );
1425
+ };
1426
+ };
1427
+
1428
+ export default useToSpa;
1429
+ `;
1430
+
1431
+ const adapter = () => {
1432
+ return {
1433
+ dependsOn: ["js"],
1434
+ files: [
1435
+ { name: "next-redirects", fn: nextRedirects, ext: "js" },
1436
+ { name: "next-rewrites", fn: nextRewrites, ext: "js" },
1437
+ {
1438
+ name: "useCurrentLocalisedPathnames",
1439
+ fn: useCurrentLocalisedPathnames,
1440
+ ext: "ts",
1441
+ index: true,
1442
+ },
1443
+ { name: "useLocale", fn: useLocale, ext: "ts", index: true },
1444
+ { name: "useRouteId", fn: useRouteId, ext: "ts", index: true },
1445
+ { name: "useTo", fn: useTo, ext: "ts", index: true },
1446
+ { name: "useToSpa", fn: useToSpa, ext: "ts", index: true },
1447
+ ],
1448
+ };
1449
+ };
1450
+
1451
+ const adaptersMap = {
1452
+ js: adapter$2,
1453
+ next: adapter,
1454
+ "next-translate": adapter$1,
1455
+ };
1456
+ const getIndexFile = (generatedFiles) => {
1457
+ let output = "";
1458
+ generatedFiles
1459
+ .filter((generatedFile) => generatedFile.index)
1460
+ .sort((a, b) => a.name.localeCompare(b.name))
1461
+ .forEach((generatedFile) => {
1462
+ output += `export * from "./${generatedFile.name}";\n`;
1463
+ });
1464
+ return output;
1465
+ };
1466
+ const getAdapters = async (adapterArg, adapterName, adapters = []) => {
1467
+ const adapterCreator = adaptersMap[adapterName];
1468
+ const adapterToResolve = adapterCreator(adapterArg);
1469
+ const adapter = utils.isPromise(adapterToResolve)
1470
+ ? await adapterToResolve
1471
+ : adapterToResolve;
1472
+ adapters = adapters.concat(adapter);
1473
+ if (adapter.dependsOn) {
1474
+ await Promise.all(adapter.dependsOn.map(async (adapaterName) => {
1475
+ adapters = adapters.concat(await getAdapters(adapterArg, adapaterName));
1476
+ }));
1477
+ }
1478
+ return adapters;
1479
+ };
1480
+ const generateCodeFromAdapters = (data, options, adapters) => {
1481
+ const { outputFiles } = options;
1482
+ const files = adapters.reduce((allFiles, adapter) => [...allFiles, ...adapter.files], []);
1483
+ const generatedFiles = files.map((file) => {
1484
+ const { fn, ...rest } = file;
1485
+ const name = outputFiles?.[rest.name] || rest.name;
1486
+ return { ...rest, name, content: fn(data) };
1487
+ });
1488
+ const indexFileContent = getIndexFile(generatedFiles);
1489
+ if (indexFileContent) {
1490
+ generatedFiles.push({
1491
+ name: "index",
1492
+ ext: "ts",
1493
+ content: getIndexFile(generatedFiles),
1494
+ });
1495
+ }
1496
+ return {
1497
+ files: generatedFiles,
1498
+ needsTranslationsFiles: adapters.some((adapter) => adapter.needsTranslationsFiles),
1499
+ };
1500
+ };
1501
+ let generateCode = async (data, options) => generateCodeFromAdapters(data, options, await getAdapters(data, options.adapter));
1502
+
1503
+ let tsCompile = (cwd, output, relativePaths, tsOptions) => {
1504
+ const rootNames = Array.from(relativePaths)
1505
+ .filter((p) => p.endsWith(".ts") || p.endsWith(".tsx"))
1506
+ .map((relativePath) => node_path.join(cwd, output, relativePath));
1507
+ const compilerOptions = {
1508
+ noEmitOnError: true,
1509
+ noImplicitAny: true,
1510
+ declaration: true,
1511
+ target: ts__namespace.ScriptTarget.ESNext,
1512
+ module: ts__namespace.ModuleKind.ESNext,
1513
+ moduleResolution: ts__namespace.ModuleResolutionKind.Bundler,
1514
+ resolveJsonModule: true,
1515
+ allowJs: false,
1516
+ esModuleInterop: true,
1517
+ jsx: ts__namespace.JsxEmit.ReactJSX,
1518
+ outDir: node_path.join(cwd, output),
1519
+ skipLibCheck: true,
1520
+ noEmitHelpers: true,
1521
+ importHelpers: true,
1522
+ ...(tsOptions || {}),
1523
+ };
1524
+ const program = ts__namespace.createProgram(rootNames, compilerOptions);
1525
+ const emitResult = program.emit();
1526
+ const allDiagnostics = ts__namespace
1527
+ .getPreEmitDiagnostics(program)
1528
+ .concat(emitResult.diagnostics);
1529
+ allDiagnostics.forEach((diagnostic) => {
1530
+ if (diagnostic.file) {
1531
+ const { line, character } = ts__namespace.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
1532
+ const message = ts__namespace.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
1533
+ console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
1534
+ }
1535
+ else {
1536
+ console.log(ts__namespace.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
1537
+ }
1538
+ });
1539
+ return emitResult;
1540
+ };
1541
+
1542
+ const writeCodeFiles = async (cwd, output, codeGenerated, files) => Promise.all(codeGenerated.files.map(async ({ name, ext, content }) => {
1543
+ const relativePath = `${name}.${ext}`;
1544
+ const filepath = node_path.join(cwd, output, relativePath);
1545
+ await node.fsWrite(filepath, content);
1546
+ files.add(relativePath);
1547
+ }));
1548
+ const writeCompiledTypescriptFiles = (cwd, output, files) => {
1549
+ const tsFiles = Array.from(files).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
1550
+ tsCompile(cwd, output, tsFiles);
1551
+ tsFiles.forEach((relativePath) => {
1552
+ files.add(relativePath.replace(/\.tsx?$/, ".js"));
1553
+ files.add(relativePath.replace(/\.tsx?$/, ".d.ts"));
1554
+ files.delete(relativePath);
1555
+ node_fs.rmSync(node_path.join(cwd, output, relativePath), { force: true });
1556
+ });
1557
+ };
1558
+ const writeTranslationsFiles = async (cwd, output, { translationFiles }, folders) => Promise.all(translationFiles.map(async ({ data, locale, path }) => {
1559
+ const relativePath = node_path.join("translations", locale);
1560
+ await node.fsWrite(node_path.join(cwd, output, relativePath, path), JSON.stringify(data));
1561
+ folders.add(relativePath);
1562
+ }));
1563
+ const getWriteGitgnoreArgs = (cwd, output, folders, files) => [
1564
+ node_path.join(cwd, output, ".gitignore"),
1565
+ Array.from(new Set([...folders, ...files]))
1566
+ .sort()
1567
+ .map((relativePath) => `/${relativePath}`)
1568
+ .join(`\n`),
1569
+ ];
1570
+ let writeCode = async (options, data) => {
1571
+ const { cwd = process.cwd(), output, skipTsCompile, skipGitignore, skipTranslations, ...generateOptions } = options;
1572
+ const code = await generateCode(data, generateOptions);
1573
+ const files = new Set();
1574
+ const folders = new Set();
1575
+ await writeCodeFiles(cwd, output, code, files);
1576
+ if (!skipTsCompile) {
1577
+ writeCompiledTypescriptFiles(cwd, output, files);
1578
+ }
1579
+ if (code.needsTranslationsFiles && !skipTranslations) {
1580
+ await writeTranslationsFiles(cwd, output, data.input, folders);
1581
+ }
1582
+ if (!skipGitignore) {
1583
+ await node.fsWrite(...getWriteGitgnoreArgs(cwd, output, folders, files));
1584
+ }
1585
+ return;
1586
+ };
1587
+
1588
+ const codeDataRoutesOptions = {
1589
+ translationJsonFileName: "~.json",
1590
+ fnsPrefix: "",
1591
+ tokens: {
1592
+ parentReference: "^",
1593
+ idDelimiter: ".",
1594
+ catchAll: {
1595
+ start: "[...",
1596
+ end: "]",
1597
+ },
1598
+ optionalCatchAll: {
1599
+ start: "[[...",
1600
+ end: "]]",
1601
+ },
1602
+ },
1603
+ };
1604
+ const getCodeDataRoutesUtils = (config, options) => {
1605
+ const { idDelimiter, parentReference, catchAll, optionalCatchAll } = options.tokens;
1606
+ return {
1607
+ ...config,
1608
+ ...options,
1609
+ reg: {
1610
+ trailingDelimiter: new RegExp(`${utils.escapeRegExp(idDelimiter)}+$`),
1611
+ indexEnd: new RegExp(`${utils.escapeRegExp(idDelimiter)}index$`),
1612
+ parentReference: new RegExp(`^/${utils.escapeRegExp(parentReference)}`),
1613
+ catchAll: new RegExp(`${utils.escapeRegExp(catchAll.start)}(.+)${utils.escapeRegExp(catchAll.end)}$`),
1614
+ optionalCatchAll: new RegExp(`${utils.escapeRegExp(optionalCatchAll.start)}(.+)${utils.escapeRegExp(optionalCatchAll.end)}$`),
1615
+ },
1616
+ };
1617
+ };
1618
+ const parseUserDefinedRouteId = (userRouteId, { reg }) => {
1619
+ let routeId = userRouteId.replace(reg.indexEnd, "");
1620
+ const isOptionalCatchAll = reg.optionalCatchAll.test(userRouteId);
1621
+ const isCatchAll = reg.catchAll.test(userRouteId);
1622
+ if (isOptionalCatchAll)
1623
+ routeId = routeId.replace(reg.optionalCatchAll, "");
1624
+ if (isCatchAll)
1625
+ routeId = routeId.replace(reg.catchAll, "");
1626
+ routeId = routeId.replace(reg.trailingDelimiter, "");
1627
+ return {
1628
+ routeId,
1629
+ isCatchAll,
1630
+ isOptionalCatchAll,
1631
+ };
1632
+ };
1633
+ const normaliseUserDefinedRoutePathname = (routePathname) => formatRoutePathname.formatRoutePathname(routePathname
1634
+ .replace(/\*/g, "")
1635
+ .replace(/[[{]{1,2}(.*?)[\]}]{1,2}/g, (_search, replaceValue) => `[${replaceValue.trim()}]`));
1636
+ const extractRouteParamsFromRouteId = (routeId) => {
1637
+ const matches = routeId.match(/\[.*?\]/g);
1638
+ if (matches) {
1639
+ return matches
1640
+ .map((match) => match.slice(1, -1).trim())
1641
+ .reduce((map, paramName) => {
1642
+ map[paramName] = "stringOrNumber";
1643
+ return map;
1644
+ }, {});
1645
+ }
1646
+ return;
1647
+ };
1648
+ const replaceRouteParentTokens = (dataRoutes, utils, locale, id, continueWhen) => {
1649
+ const { tokens: { parentReference, idDelimiter }, reg, } = utils;
1650
+ const route = dataRoutes.byId[id];
1651
+ let pathname = route.pathnames[locale];
1652
+ if (pathname.startsWith(`/${parentReference}`)) {
1653
+ pathname = pathname.replace(reg.parentReference, "");
1654
+ const parentId = id.split(idDelimiter).slice(0, -1).join(idDelimiter);
1655
+ if (!continueWhen || (continueWhen && continueWhen(route, parentId))) {
1656
+ if (parentId) {
1657
+ pathname =
1658
+ replaceRouteParentTokens(dataRoutes, utils, locale, parentId, continueWhen) + pathname;
1659
+ }
1660
+ else {
1661
+ throw Error("Used a parent route token reference without a matching parent route");
1662
+ }
1663
+ }
1664
+ }
1665
+ return pathname;
1666
+ };
1667
+ const replaceRoutesPathnamesParentTokens = (dataRoutes, utils) => {
1668
+ for (const routeId in dataRoutes.byId) {
1669
+ for (const locale in dataRoutes.byId[routeId].pathnames) {
1670
+ dataRoutes.byId[routeId].pathnames[locale] = replaceRouteParentTokens(dataRoutes, utils, locale, routeId);
1671
+ }
1672
+ }
1673
+ };
1674
+ const manageRoutesSpaPathnames = (dataRoutes, utils) => {
1675
+ const continueWhen = (currentRoute, parentRouteId) => currentRoute.inWildcard && !dataRoutes.wildcardIds.includes(parentRouteId);
1676
+ for (const routeId in dataRoutes.byId) {
1677
+ const { inWildcard } = dataRoutes.byId[routeId];
1678
+ if (inWildcard && dataRoutes.byId[routeId].pathnamesSpa) {
1679
+ for (const locale in dataRoutes.byId[routeId].pathnamesSpa) {
1680
+ dataRoutes.byId[routeId].pathnamesSpa[locale] =
1681
+ replaceRouteParentTokens(dataRoutes, utils, locale, routeId, continueWhen);
1682
+ }
1683
+ }
1684
+ else {
1685
+ delete dataRoutes.byId[routeId].pathnamesSpa;
1686
+ }
1687
+ }
1688
+ };
1689
+ const addRoutesSlimPathnames = (dataRoutes, utils$1) => {
1690
+ const { defaultLocale, locales } = utils$1;
1691
+ for (const routeId in dataRoutes.byId) {
1692
+ const pathnamesPerLocale = dataRoutes.byId[routeId].pathnames;
1693
+ const defaultLocalePathname = pathnamesPerLocale[defaultLocale];
1694
+ const pathnamesSlim = {};
1695
+ for (const locale in pathnamesPerLocale) {
1696
+ const url = pathnamesPerLocale[locale];
1697
+ if (url !== defaultLocalePathname) {
1698
+ pathnamesSlim[locale] = url;
1699
+ }
1700
+ }
1701
+ if (Object.keys(pathnamesSlim).length === locales.length - 1) ;
1702
+ else if (Object.keys(pathnamesSlim).length >= 1) {
1703
+ pathnamesSlim[defaultLocale] = defaultLocalePathname;
1704
+ dataRoutes.byId[routeId].pathnamesSlim = utils.objectSortByKeysMatching(pathnamesSlim, defaultLocale);
1705
+ }
1706
+ else {
1707
+ dataRoutes.byId[routeId].pathnamesSlim = defaultLocalePathname;
1708
+ }
1709
+ }
1710
+ };
1711
+ const addInWildcardFlags = (dataRoutes) => {
1712
+ if (dataRoutes.wildcardIds.length) {
1713
+ for (const routeId in dataRoutes.byId) {
1714
+ const inWildcard = dataRoutes.wildcardIds.some((wildcardRouteId) => routeId.startsWith(wildcardRouteId) && wildcardRouteId !== routeId);
1715
+ if (inWildcard)
1716
+ dataRoutes.byId[routeId].inWildcard = true;
1717
+ }
1718
+ }
1719
+ };
1720
+ const buildDataRoutesFromJsonData = (json, locale, utils$1, data) => {
1721
+ const routes = utils.objectFlat(json, utils$1.tokens.idDelimiter);
1722
+ for (const _key in routes) {
1723
+ const key = _key;
1724
+ const routePathname = routes[key];
1725
+ const { routeId, isCatchAll, isOptionalCatchAll } = parseUserDefinedRouteId(key, utils$1);
1726
+ if (!data.byId[routeId]) {
1727
+ data.byId[routeId] = data.byId[routeId] || {};
1728
+ const params = extractRouteParamsFromRouteId(routeId);
1729
+ data.byId[routeId].id = routeId;
1730
+ if (params)
1731
+ data.byId[routeId].params = params;
1732
+ if (isCatchAll || isOptionalCatchAll) {
1733
+ data.byId[routeId].wildcard = true;
1734
+ data.wildcardIds.push(routeId);
1735
+ }
1736
+ }
1737
+ data.byId[routeId].pathnames = data.byId[routeId].pathnames || {};
1738
+ data.byId[routeId].pathnames[locale] = normaliseUserDefinedRoutePathname(routePathname);
1739
+ data.byId[routeId].pathnames = utils.objectSortByKeysMatching(data.byId[routeId].pathnames, utils$1.defaultLocale);
1740
+ data.byId[routeId].pathnamesSpa = { ...data.byId[routeId].pathnames };
1741
+ }
1742
+ data.byId = utils.objectSort(data.byId);
1743
+ };
1744
+ let getCodeDataRoutes = (config, options, { translationFiles }) => {
1745
+ const dataRoutes = { byId: {}, wildcardIds: [] };
1746
+ const utils = getCodeDataRoutesUtils(config, options);
1747
+ for (let i = 0; i < translationFiles.length; i++) {
1748
+ const { path, locale, data } = translationFiles[i];
1749
+ if (path === options.translationJsonFileName) {
1750
+ buildDataRoutesFromJsonData(data, locale, utils, dataRoutes);
1751
+ }
1752
+ }
1753
+ addInWildcardFlags(dataRoutes);
1754
+ manageRoutesSpaPathnames(dataRoutes, utils);
1755
+ replaceRoutesPathnamesParentTokens(dataRoutes, utils);
1756
+ addRoutesSlimPathnames(dataRoutes, utils);
1757
+ return dataRoutes;
1758
+ };
1759
+
1760
+ const codeDataTranslationsOptions = {
1761
+ ignorePaths: [],
1762
+ dynamicDelimiters: {
1763
+ start: "{{",
1764
+ end: "}}",
1765
+ },
1766
+ fnsAsDataCodes: true,
1767
+ fnsPrefix: "",
1768
+ createArrayIndexBasedFns: false,
1769
+ };
1770
+ const slashRegex = new RegExp(node_path.sep, "g");
1771
+ const normaliseTranslationKey = (key) => {
1772
+ const replaced = key
1773
+ .replace(/~/g, "$")
1774
+ .replace(/-/g, "_")
1775
+ .replace(slashRegex, "_")
1776
+ .replace(/_+/g, "_")
1777
+ .replace(/[^a-zA-Z0-9_$]/gi, "");
1778
+ return /^[0-9]/.test(replaced) ? "$" + replaced : replaced;
1779
+ };
1780
+ const extractTranslationParamsFromPrimitive = (options, value) => {
1781
+ if (utils.isString(value)) {
1782
+ const { start, end } = options.dynamicDelimiters;
1783
+ const regex = new RegExp(`${start}(.*?)${end}`, "gm");
1784
+ const matches = value.match(regex);
1785
+ if (matches) {
1786
+ return matches
1787
+ .map((match) => match.replace(start, "").replace(end, "").trim())
1788
+ .reduce((map, paramName) => {
1789
+ map[paramName] = "stringOrNumber";
1790
+ return map;
1791
+ }, {});
1792
+ }
1793
+ }
1794
+ return;
1795
+ };
1796
+ const extractTranslationParamsFromValue = (options, value, params = {}) => {
1797
+ if (utils.isPrimitive(value)) {
1798
+ const extracted = extractTranslationParamsFromPrimitive(options, value);
1799
+ if (extracted)
1800
+ params = { ...params, ...extracted };
1801
+ return params;
1802
+ }
1803
+ else if (utils.isArray(value)) {
1804
+ for (let i = 0; i < value.length; i++) {
1805
+ const extracted = extractTranslationParamsFromPrimitive(options, value[i]);
1806
+ if (extracted)
1807
+ params = { ...params, ...extracted };
1808
+ }
1809
+ }
1810
+ else {
1811
+ for (const key in value) {
1812
+ const extracted = extractTranslationParamsFromValue(options, value[key], params);
1813
+ if (extracted)
1814
+ params = { ...params, ...extracted };
1815
+ }
1816
+ }
1817
+ return {};
1818
+ };
1819
+ const manageDataTranslationsPlurals = (options, dataTranslations) => {
1820
+ const pluralTranslationIds = Object.keys(dataTranslations).filter(isPluralKey);
1821
+ pluralTranslationIds.forEach((translationIdPluralised) => {
1822
+ const id = removePluralSuffix(translationIdPluralised);
1823
+ const pluralSuffix = getPluralSuffix(translationIdPluralised);
1824
+ dataTranslations[id] = dataTranslations[id] || {};
1825
+ if (dataTranslations[translationIdPluralised]) {
1826
+ const values = dataTranslations[id].values || {};
1827
+ utils.forin(dataTranslations[translationIdPluralised].values, (locale, value) => {
1828
+ values[locale] = utils.isObject(values[locale]) ? values[locale] : {};
1829
+ values[locale][pluralSuffix] = value;
1830
+ const params = extractTranslationParamsFromValue(options, value);
1831
+ if (params) {
1832
+ dataTranslations[id].params = {
1833
+ ...(dataTranslations[id].params || {}),
1834
+ ...params,
1835
+ };
1836
+ }
1837
+ });
1838
+ if (Object.keys(values).length) {
1839
+ dataTranslations[id].values = values;
1840
+ dataTranslations[id].plural = true;
1841
+ }
1842
+ delete dataTranslations[translationIdPluralised];
1843
+ }
1844
+ });
1845
+ return dataTranslations;
1846
+ };
1847
+ const addDataTranslationEntry = (options, id, locale, value, dataTranslations) => {
1848
+ if (utils.isPrimitive(value)) {
1849
+ dataTranslations[id] = dataTranslations[id] || {};
1850
+ dataTranslations[id].values = dataTranslations[id].values || {};
1851
+ dataTranslations[id].values[locale] = value;
1852
+ dataTranslations[id].typeValue = "Primitive";
1853
+ const params = extractTranslationParamsFromPrimitive(options, value);
1854
+ if (params)
1855
+ dataTranslations[id].params = params;
1856
+ }
1857
+ else {
1858
+ if (options.fnsAsDataCodes) {
1859
+ const typeValue = utils.isArray(value) ? "Array" : "Object";
1860
+ dataTranslations[id] = dataTranslations[id] || {};
1861
+ dataTranslations[id].values = dataTranslations[id].values || {};
1862
+ dataTranslations[id].values[locale] = value;
1863
+ dataTranslations[id].typeValue = typeValue;
1864
+ }
1865
+ if (utils.isArray(value)) {
1866
+ if (options.createArrayIndexBasedFns) {
1867
+ for (let i = 0; i < value.length; i++) {
1868
+ addDataTranslationEntry(options, id + "_" + i, locale, value[i], dataTranslations);
1869
+ }
1870
+ }
1871
+ }
1872
+ else {
1873
+ for (const tKey in value) {
1874
+ addDataTranslationEntry(options, id + "_" + normaliseTranslationKey(tKey), locale, value[tKey], dataTranslations);
1875
+ }
1876
+ }
1877
+ }
1878
+ return dataTranslations;
1879
+ };
1880
+ const getCodeDataTranslationsFromFile = (options, file, dataTranslations) => {
1881
+ const { locale, path } = file;
1882
+ const filename = node_path.join(node_path.dirname(path), node_path.basename(path, node_path.extname(path)));
1883
+ for (const tKey in file.data) {
1884
+ const tValue = file.data[tKey];
1885
+ const id = normaliseTranslationKey(filename + (tKey ? "_" + tKey : ""));
1886
+ addDataTranslationEntry(options, id, locale, tValue, dataTranslations);
1887
+ }
1888
+ return dataTranslations;
1889
+ };
1890
+ let getCodeDataTranslations = (_config, options, { translationFiles }) => {
1891
+ const { ignorePaths } = options;
1892
+ let dataTranslations = {};
1893
+ for (let i = 0; i < translationFiles.length; i++) {
1894
+ if (!ignorePaths ||
1895
+ (ignorePaths &&
1896
+ ignorePaths.every((glob) => !minimatch.minimatch(translationFiles[i].path, glob)))) {
1897
+ getCodeDataTranslationsFromFile(options, translationFiles[i], dataTranslations);
1898
+ }
1899
+ }
1900
+ dataTranslations = manageDataTranslationsPlurals(options, dataTranslations);
1901
+ dataTranslations = utils.objectSort(dataTranslations);
1902
+ return dataTranslations;
1903
+ };
1904
+
1905
+ const codeDataOptions = {
1906
+ routes: codeDataRoutesOptions,
1907
+ translations: codeDataTranslationsOptions,
1908
+ };
1909
+ let getCodeData = (config, options, input) => {
1910
+ const optionsSafe = utils.objectMergeWithDefaults(codeDataOptions, options);
1911
+ optionsSafe.translations.ignorePaths.push(optionsSafe.routes.translationJsonFileName);
1912
+ input = {
1913
+ ...input,
1914
+ localesFolders: input.localesFolders.sort((a, b) => config.defaultLocale ? -1 : a.localeCompare(b)),
1915
+ };
1916
+ return {
1917
+ config,
1918
+ options: optionsSafe,
1919
+ input,
1920
+ routes: getCodeDataRoutes(config, optionsSafe.routes, input),
1921
+ translations: getCodeDataTranslations(config, optionsSafe.translations, input),
1922
+ };
1923
+ };
1924
+
1925
+ const configDefaults = {
1926
+ locales: ["en"],
1927
+ defaultLocale: "en",
1928
+ hideDefaultLocaleInUrl: true,
1929
+ };
1930
+ let getConfig = (dataInput, options = {}) => {
1931
+ options.locales = options.locales || dataInput.localesFolders;
1932
+ options.defaultLocale = options.defaultLocale || options.locales?.[0];
1933
+ options.hideDefaultLocaleInUrl = !!options.hideDefaultLocaleInUrl;
1934
+ const merged = utils.objectMergeWithDefaults(configDefaults, options);
1935
+ merged.locales = merged.locales.sort((a, b) => merged.defaultLocale ? -1 : a.localeCompare(b));
1936
+ return merged;
1937
+ };
1938
+
1939
+ const getWriteInputArgs = (options, data) => {
1940
+ const { cwd = process.cwd(), output, pretty } = options;
1941
+ return [
1942
+ node_path.join(cwd, output),
1943
+ pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data),
1944
+ ];
1945
+ };
1946
+ let writeInput = async (options, data) => {
1947
+ await node.fsWrite(...getWriteInputArgs(options, data));
1948
+ };
1949
+
1950
+ const getLocalesFolders = (options) => {
1951
+ const { cwd, ignore } = options;
1952
+ const folders = glob.glob
1953
+ .sync("*", {
1954
+ cwd,
1955
+ withFileTypes: true,
1956
+ ignore: [...ignore, "node_modules/**"],
1957
+ })
1958
+ .filter((folder) => folder.isDirectory())
1959
+ .map((path) => path.relative());
1960
+ return folders.sort((a, b) => a.localeCompare(b));
1961
+ };
1962
+ let getInputDataLocal = async (options) => {
1963
+ const { cwd = process.cwd(), ignore = [], source } = options;
1964
+ const path = node_path.join(cwd, source);
1965
+ const localesFolders = getLocalesFolders({ cwd: path, ignore });
1966
+ const translationFiles = [];
1967
+ await Promise.all(localesFolders.map(async (locale) => {
1968
+ const jsonFiles = await glob.glob("**/*.json", {
1969
+ cwd: node_path.join(path, locale),
1970
+ ignore: options.ignore,
1971
+ });
1972
+ await Promise.all(jsonFiles.map(async (relativePath) => {
1973
+ const fullPath = node_path.join(path, locale, relativePath);
1974
+ const rawContent = await promises.readFile(fullPath, "utf8");
1975
+ if (rawContent) {
1976
+ translationFiles.push({
1977
+ path: relativePath,
1978
+ data: JSON.parse(rawContent),
1979
+ locale: locale,
1980
+ });
1981
+ }
1982
+ }));
1983
+ }));
1984
+ return {
1985
+ localesFolders,
1986
+ translationFiles,
1987
+ };
1988
+ };
1989
+
1990
+ const GITHUB_RAW_URL = "https://raw.githubusercontent.com";
1991
+ let getInputDataRemote = async (options) => new Promise((resolve, reject) => {
1992
+ const { ignore = [], source } = options;
1993
+ const isGithubUrl = source.startsWith(GITHUB_RAW_URL);
1994
+ let result = "";
1995
+ const req = node_https.request(source, isGithubUrl
1996
+ ? {}
1997
+ : {
1998
+ headers: {
1999
+ Accept: "application/json",
2000
+ },
2001
+ }, (res) => {
2002
+ res.setEncoding("utf8");
2003
+ res.on("data", (chunk) => {
2004
+ result += chunk;
2005
+ });
2006
+ res.on("end", () => {
2007
+ try {
2008
+ const dataInput = (isGithubUrl ? JSON.parse(result) : result);
2009
+ resolve({
2010
+ ...dataInput,
2011
+ localesFolders: ignore.length
2012
+ ? dataInput.localesFolders.filter((folder) => ignore.every((glob) => !minimatch.minimatch(folder, glob)))
2013
+ : dataInput.localesFolders,
2014
+ translationFiles: ignore.length
2015
+ ? dataInput.translationFiles.filter((file) => ignore.every((glob) => !minimatch.minimatch(file.path, glob)))
2016
+ : dataInput.translationFiles,
2017
+ });
2018
+ }
2019
+ catch (e) {
2020
+ throw Error(`Failed to parse JSON from ${source}`);
2021
+ }
2022
+ });
2023
+ });
2024
+ req.on("error", (e) => {
2025
+ console.error(e);
2026
+ reject("");
2027
+ });
2028
+ req.end();
2029
+ });
2030
+
2031
+ let getInputData = async (options) => {
2032
+ const { source } = options;
2033
+ if (utils$1.isAbsoluteUrl(source)) {
2034
+ return await getInputDataRemote(options);
2035
+ }
2036
+ return await getInputDataLocal(options);
2037
+ };
2038
+
2039
+ const getWords = (value, options = {}) => {
2040
+ let out = "";
2041
+ if (value && typeof value === "string") {
2042
+ out += " " + value.trim();
2043
+ }
2044
+ else if (Array.isArray(value)) {
2045
+ for (let i = 0; i < value.length; i++) {
2046
+ out += getWords(value[i], options);
2047
+ }
2048
+ }
2049
+ else if (typeof value === "object") {
2050
+ for (const _key in value) {
2051
+ const key = _key;
2052
+ const single = value[key];
2053
+ out += getWords(single, options);
2054
+ }
2055
+ }
2056
+ return out;
2057
+ };
2058
+ const getSummaryDataEntry = (sourceUrl, file) => {
2059
+ const { locale, path } = file;
2060
+ const url = `${sourceUrl}/${locale}/${path}`;
2061
+ const words = getWords(file.data);
2062
+ const wordsCount = words.split(" ").filter(Boolean).length;
2063
+ const characters = words.split(" ").filter(Boolean).join("").length;
2064
+ return {
2065
+ characters,
2066
+ locale: locale,
2067
+ path,
2068
+ url,
2069
+ words: wordsCount,
2070
+ };
2071
+ };
2072
+ const summaryDataOptions = {};
2073
+ let getSummaryData = (config, options, { translationFiles }) => {
2074
+ const { defaultLocale } = config;
2075
+ let dataSummary = {};
2076
+ for (let i = 0; i < translationFiles.length; i++) {
2077
+ const file = translationFiles[i];
2078
+ const { locale } = file;
2079
+ const entry = getSummaryDataEntry(options.sourceUrl, file);
2080
+ dataSummary[locale] = dataSummary[locale] || {};
2081
+ dataSummary[locale].files = dataSummary[locale].files || [];
2082
+ dataSummary[locale].files.push(entry);
2083
+ }
2084
+ dataSummary = utils.objectSortByKeysMatching(dataSummary, defaultLocale);
2085
+ utils.forin(dataSummary, (locale, dataPerLocale) => {
2086
+ dataSummary[locale].characters = utils.arraySum(dataPerLocale.files.map((f) => f.characters));
2087
+ dataSummary[locale].files = dataSummary[locale].files.sort((a, b) => a.path.localeCompare(b.path));
2088
+ dataSummary[locale].words = utils.arraySum(dataPerLocale.files.map((f) => f.words));
2089
+ dataSummary[locale] = utils.objectSort(dataSummary[locale]);
2090
+ });
2091
+ return dataSummary;
2092
+ };
2093
+
2094
+ const getSummaryDataByPath = (data) => {
2095
+ let out = {};
2096
+ utils.forin(data, (locale, dataPerLocale) => {
2097
+ const { files } = dataPerLocale;
2098
+ for (let i = 0; i < files.length; i++) {
2099
+ const file = files[i];
2100
+ const { path } = file;
2101
+ out[path] = out[path] || {};
2102
+ out[path][locale] = file;
2103
+ }
2104
+ });
2105
+ out = utils.objectSort(out);
2106
+ return out;
2107
+ };
2108
+ const generateSummaryMarkdownByPath = (data) => {
2109
+ const dataByPath = getSummaryDataByPath(data);
2110
+ let output = "";
2111
+ let body = "";
2112
+ const locales = [];
2113
+ const styleBorder = `style="border-right:1px solid grey"`;
2114
+ utils.forin(dataByPath, (path, dataPerPath) => {
2115
+ body += `<tr>`;
2116
+ body += `<td ${styleBorder}>${path}</td>`;
2117
+ utils.forin(dataPerPath, (locale, file) => {
2118
+ const { characters, words, url } = file;
2119
+ if (!locales.includes(locale))
2120
+ locales.push(locale);
2121
+ body += `<td><a href="${url}">${locale}</a></td>`;
2122
+ body += `<td>${words}</td>`;
2123
+ body += `<td ${styleBorder}>${characters}</td>`;
2124
+ });
2125
+ body += `</tr>`;
2126
+ });
2127
+ output += `<table><thead><tr>`;
2128
+ output += `<th ${styleBorder}>file path</th>`;
2129
+ output += locales
2130
+ .map(() => `<th>lang</th><th>words</th><th ${styleBorder}>chars</th>`)
2131
+ .join("");
2132
+ output += `</tr></thead><tbody>${body}</tbody></table>\n`;
2133
+ return output;
2134
+ };
2135
+ const generateSummaryMarkdownByLocale = (data, options) => {
2136
+ let output = "";
2137
+ let body = "";
2138
+ utils.forin(data, (locale, dataPerLocale) => {
2139
+ const { files, characters, words } = dataPerLocale;
2140
+ const url = `${options.sourceUrl}/${locale}`;
2141
+ body += `<tr>`;
2142
+ body += `<th><a href="${url}">${locale}</a></th>`;
2143
+ body += `<td>${files.length}</td>`;
2144
+ body += `<td>${words}</td>`;
2145
+ body += `<td>${characters}</td>`;
2146
+ body += `</tr>`;
2147
+ });
2148
+ output += `<table><thead><tr>`;
2149
+ output += `<th>locale</th><th>files</th><th>words</th><th>chars</th>`;
2150
+ output += `</tr></thead><tbody>${body}</tbody></table>\n`;
2151
+ return output;
2152
+ };
2153
+ const generateSummaryMarkdown = (data, options) => {
2154
+ let output = "# Summary\n";
2155
+ output += "\n### By locale\n\n";
2156
+ output += generateSummaryMarkdownByLocale(data, options);
2157
+ output += "\n### By file path\n\n";
2158
+ output += generateSummaryMarkdownByPath(data);
2159
+ return output;
2160
+ };
2161
+ let generateSummary = (data, options) => {
2162
+ const markdown = generateSummaryMarkdown(data, utils.objectMergeWithDefaults(summaryDataOptions, options));
2163
+ return markdown;
2164
+ };
2165
+
2166
+ const getWriteSummaryJsonArgs = (options, data, cwd, outputJson) => [
2167
+ node_path.join(cwd, outputJson),
2168
+ options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data),
2169
+ ];
2170
+ const getWriteSummaryMdArgs = (options, data, cwd, outputMarkdown) => [node_path.join(cwd, outputMarkdown), generateSummary(data, options)];
2171
+ let writeSummary = async (options, data) => {
2172
+ const { cwd = process.cwd(), outputJson, outputMarkdown, ...rest } = options;
2173
+ if (outputJson) {
2174
+ await node.fsWrite(...getWriteSummaryJsonArgs(options, data, cwd, outputJson));
2175
+ }
2176
+ if (outputMarkdown) {
2177
+ await node.fsWrite(...getWriteSummaryMdArgs(rest, data, cwd, outputMarkdown));
2178
+ }
2179
+ return data;
2180
+ };
2181
+
2182
+ let i18nCompiler = async (options) => {
2183
+ const { input: optsInput, code: optsCode, summary: optsSummary, ...configOptions } = options;
2184
+ const writables = [];
2185
+ const input = await getInputData(optsInput);
2186
+ const config = getConfig(input, configOptions);
2187
+ const code = getCodeData(config, optsCode, input);
2188
+ if (optsInput?.write) {
2189
+ writables.push(writeInput(optsInput.write, input));
2190
+ }
2191
+ if (optsCode?.write) {
2192
+ writables.push(writeCode({ ...optsCode, ...optsCode.write }, code));
2193
+ }
2194
+ if (optsSummary?.write) {
2195
+ writables.push(writeSummary({ ...optsSummary, ...optsSummary.write }, getSummaryData(config, optsSummary, input)));
2196
+ }
2197
+ await Promise.all(writables);
2198
+ return { config, input, code };
2199
+ };
2200
+
2201
+ exports.generateRedirects = generateRedirects;
2202
+ exports.generateRewrites = generateRewrites;
2203
+ exports.i18nCompiler = i18nCompiler;