@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.
- package/README.md +1 -0
- package/api.cjs.js +2203 -0
- package/api.esm.js +2179 -0
- package/compiler-sync.cjs.d.ts +1 -0
- package/compiler-sync.cjs.default.js +1 -0
- package/compiler-sync.cjs.js +20 -0
- package/compiler-sync.cjs.mjs +2 -0
- package/compiler-sync.esm.js +16 -0
- package/compiler-worker.cjs.d.ts +1 -0
- package/compiler-worker.cjs.default.js +1 -0
- package/compiler-worker.cjs.js +18 -0
- package/compiler-worker.cjs.mjs +2 -0
- package/compiler-worker.esm.js +16 -0
- package/compiler.cjs.d.ts +1 -0
- package/compiler.cjs.default.js +1 -0
- package/compiler.cjs.js +21 -0
- package/compiler.cjs.mjs +2 -0
- package/compiler.esm.js +13 -0
- package/formatRoutePathname.cjs.js +12 -0
- package/formatRoutePathname.esm.js +10 -0
- package/index.cjs.d.ts +1 -0
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +47 -0
- package/index.cjs.mjs +2 -0
- package/index.esm.js +42 -0
- package/next.cjs.d.ts +1 -0
- package/next.cjs.default.js +1 -0
- package/next.cjs.js +377 -0
- package/next.cjs.mjs +2 -0
- package/next.esm.js +353 -0
- package/package.json +22 -305
- package/actions/helpers-git.js +0 -45
- package/actions/helpers-i18n.js +0 -27
- package/actions/i18n.js +0 -17
- package/adapter-js/code/config.cjs.js +0 -14
- package/adapter-js/code/config.js +0 -14
- package/adapter-js/code/defaultLocale.js +0 -7
- package/adapter-js/code/deriveLocalisedPathnames.js +0 -78
- package/adapter-js/code/index.js +0 -123
- package/adapter-js/code/isLocale.js +0 -9
- package/adapter-js/code/locales.js +0 -8
- package/adapter-js/code/pathnameToRouteId.js +0 -20
- package/adapter-js/code/routes.js +0 -11
- package/adapter-js/code/routesSlim.js +0 -11
- package/adapter-js/code/routesSpa.js +0 -11
- package/adapter-js/code/tFns.js +0 -34
- package/adapter-js/code/tInterpolateParams.js +0 -18
- package/adapter-js/code/tPluralise.js +0 -10
- package/adapter-js/code/to.js +0 -32
- package/adapter-js/code/toFns.js +0 -25
- package/adapter-js/code/toFormat.js +0 -56
- package/adapter-js/code/toSpa.js +0 -42
- package/adapter-js/code/types.js +0 -327
- package/adapter-next/code/index.js +0 -54
- package/adapter-next/code/next-redirects.js +0 -5
- package/adapter-next/code/next-rewrites.js +0 -5
- package/adapter-next/code/useCurrentLocalisedPathnames.js +0 -20
- package/adapter-next/code/useLocale.js +0 -8
- package/adapter-next/code/useRouteId.js +0 -10
- package/adapter-next/code/useTo.js +0 -26
- package/adapter-next/code/useToSpa.js +0 -34
- package/adapter-next/plugin-async.js +0 -11
- package/adapter-next/plugin-legacy.js +0 -187
- package/adapter-next/plugin-shared.js +0 -39
- package/adapter-next/plugin.js +0 -13
- package/adapter-next/redirects.js +0 -51
- package/adapter-next/rewrites.js +0 -50
- package/adapter-next/transformPathname.js +0 -3
- package/adapter-next/webpackPluginI18n.js +0 -11
- package/adapter-next-translate/code/DynamicNamespaces.js +0 -9
- package/adapter-next-translate/code/T.js +0 -31
- package/adapter-next-translate/code/TransText.js +0 -9
- package/adapter-next-translate/code/getT.js +0 -15
- package/adapter-next-translate/code/index.js +0 -49
- package/adapter-next-translate/code/nextTranslateI18n.js +0 -19
- package/adapter-next-translate/code/useT.js +0 -43
- package/cjs/adapter-js/code/config.cjs.d.ts +0 -3
- package/cjs/adapter-js/code/config.d.ts +0 -3
- package/cjs/adapter-js/code/defaultLocale.d.ts +0 -3
- package/cjs/adapter-js/code/deriveLocalisedPathnames.d.ts +0 -2
- package/cjs/adapter-js/code/index.d.ts +0 -3
- package/cjs/adapter-js/code/isLocale.d.ts +0 -2
- package/cjs/adapter-js/code/locales.d.ts +0 -3
- package/cjs/adapter-js/code/pathnameToRouteId.d.ts +0 -3
- package/cjs/adapter-js/code/routes.d.ts +0 -3
- package/cjs/adapter-js/code/routesSlim.d.ts +0 -3
- package/cjs/adapter-js/code/routesSpa.d.ts +0 -3
- package/cjs/adapter-js/code/tFns.d.ts +0 -3
- package/cjs/adapter-js/code/tInterpolateParams.d.ts +0 -3
- package/cjs/adapter-js/code/tPluralise.d.ts +0 -2
- package/cjs/adapter-js/code/to.d.ts +0 -3
- package/cjs/adapter-js/code/toFns.d.ts +0 -3
- package/cjs/adapter-js/code/toFormat.d.ts +0 -3
- package/cjs/adapter-js/code/toSpa.d.ts +0 -3
- package/cjs/adapter-js/code/types.d.ts +0 -3
- package/cjs/adapter-next/code/index.d.ts +0 -3
- package/cjs/adapter-next/code/next-redirects.d.ts +0 -3
- package/cjs/adapter-next/code/next-rewrites.d.ts +0 -3
- package/cjs/adapter-next/code/useCurrentLocalisedPathnames.d.ts +0 -2
- package/cjs/adapter-next/code/useLocale.d.ts +0 -3
- package/cjs/adapter-next/code/useRouteId.d.ts +0 -2
- package/cjs/adapter-next/code/useTo.d.ts +0 -2
- package/cjs/adapter-next/code/useToSpa.d.ts +0 -2
- package/cjs/adapter-next/plugin-async.d.ts +0 -9
- package/cjs/adapter-next/plugin-legacy.d.ts +0 -22
- package/cjs/adapter-next/plugin-shared.d.ts +0 -13
- package/cjs/adapter-next/plugin.d.ts +0 -7
- package/cjs/adapter-next/redirects.d.ts +0 -4
- package/cjs/adapter-next/rewrites.d.ts +0 -4
- package/cjs/adapter-next/transformPathname.d.ts +0 -1
- package/cjs/adapter-next/webpackPluginI18n.d.ts +0 -7
- package/cjs/adapter-next-translate/code/DynamicNamespaces.d.ts +0 -2
- package/cjs/adapter-next-translate/code/T.d.ts +0 -2
- package/cjs/adapter-next-translate/code/TransText.d.ts +0 -2
- package/cjs/adapter-next-translate/code/getT.d.ts +0 -2
- package/cjs/adapter-next-translate/code/index.d.ts +0 -3
- package/cjs/adapter-next-translate/code/nextTranslateI18n.d.ts +0 -3
- package/cjs/adapter-next-translate/code/useT.d.ts +0 -2
- package/cjs/client/formatRoutePathname.d.ts +0 -4
- package/cjs/client/index.d.ts +0 -3
- package/cjs/client/interpolateTo.d.ts +0 -4
- package/cjs/client/routeHasDynamicPortion.d.ts +0 -2
- package/cjs/compiler/api.d.ts +0 -30
- package/cjs/compiler/code/data-routes.d.ts +0 -19
- package/cjs/compiler/code/data-translations.d.ts +0 -13
- package/cjs/compiler/code/data.d.ts +0 -33
- package/cjs/compiler/code/generate.d.ts +0 -14
- package/cjs/compiler/code/index.d.ts +0 -3
- package/cjs/compiler/code/tsCompile.d.ts +0 -2
- package/cjs/compiler/code/write.d.ts +0 -11
- package/cjs/compiler/config.d.ts +0 -12
- package/cjs/compiler/helpers.d.ts +0 -2
- package/cjs/compiler/input/data-local.d.ts +0 -4
- package/cjs/compiler/input/data-remote.d.ts +0 -7
- package/cjs/compiler/input/data.d.ts +0 -4
- package/cjs/compiler/input/index.d.ts +0 -4
- package/cjs/compiler/input/types.d.ts +0 -14
- package/cjs/compiler/input/write.d.ts +0 -8
- package/cjs/compiler/pluralisation.d.ts +0 -37
- package/cjs/compiler/summary/data.d.ts +0 -6
- package/cjs/compiler/summary/generate.d.ts +0 -4
- package/cjs/compiler/summary/index.d.ts +0 -3
- package/cjs/compiler/summary/write.d.ts +0 -10
- package/cjs/compiler/types.d.ts +0 -82
- package/cjs/compiler-sync.d.ts +0 -4
- package/cjs/compiler-worker.d.ts +0 -1
- package/cjs/compiler.d.ts +0 -2
- package/cjs/i18n/actions/helpers-git.js +0 -45
- package/cjs/i18n/actions/helpers-i18n.js +0 -27
- package/cjs/i18n/actions/i18n.js +0 -17
- package/cjs/i18n/adapter-js/code/config.cjs.js +0 -14
- package/cjs/i18n/adapter-js/code/config.js +0 -14
- package/cjs/i18n/adapter-js/code/defaultLocale.js +0 -7
- package/cjs/i18n/adapter-js/code/deriveLocalisedPathnames.js +0 -78
- package/cjs/i18n/adapter-js/code/index.js +0 -123
- package/cjs/i18n/adapter-js/code/isLocale.js +0 -9
- package/cjs/i18n/adapter-js/code/locales.js +0 -8
- package/cjs/i18n/adapter-js/code/pathnameToRouteId.js +0 -20
- package/cjs/i18n/adapter-js/code/routes.js +0 -11
- package/cjs/i18n/adapter-js/code/routesSlim.js +0 -11
- package/cjs/i18n/adapter-js/code/routesSpa.js +0 -11
- package/cjs/i18n/adapter-js/code/tFns.js +0 -34
- package/cjs/i18n/adapter-js/code/tInterpolateParams.js +0 -18
- package/cjs/i18n/adapter-js/code/tPluralise.js +0 -10
- package/cjs/i18n/adapter-js/code/to.js +0 -32
- package/cjs/i18n/adapter-js/code/toFns.js +0 -25
- package/cjs/i18n/adapter-js/code/toFormat.js +0 -56
- package/cjs/i18n/adapter-js/code/toSpa.js +0 -42
- package/cjs/i18n/adapter-js/code/types.js +0 -327
- package/cjs/i18n/adapter-next/code/index.js +0 -54
- package/cjs/i18n/adapter-next/code/next-redirects.js +0 -5
- package/cjs/i18n/adapter-next/code/next-rewrites.js +0 -5
- package/cjs/i18n/adapter-next/code/useCurrentLocalisedPathnames.js +0 -20
- package/cjs/i18n/adapter-next/code/useLocale.js +0 -8
- package/cjs/i18n/adapter-next/code/useRouteId.js +0 -10
- package/cjs/i18n/adapter-next/code/useTo.js +0 -26
- package/cjs/i18n/adapter-next/code/useToSpa.js +0 -34
- package/cjs/i18n/adapter-next/plugin-async.js +0 -11
- package/cjs/i18n/adapter-next/plugin-legacy.js +0 -187
- package/cjs/i18n/adapter-next/plugin-shared.js +0 -39
- package/cjs/i18n/adapter-next/plugin.js +0 -13
- package/cjs/i18n/adapter-next/redirects.js +0 -51
- package/cjs/i18n/adapter-next/rewrites.js +0 -50
- package/cjs/i18n/adapter-next/transformPathname.js +0 -3
- package/cjs/i18n/adapter-next/webpackPluginI18n.js +0 -11
- package/cjs/i18n/adapter-next-translate/code/DynamicNamespaces.js +0 -9
- package/cjs/i18n/adapter-next-translate/code/T.js +0 -31
- package/cjs/i18n/adapter-next-translate/code/TransText.js +0 -9
- package/cjs/i18n/adapter-next-translate/code/getT.js +0 -15
- package/cjs/i18n/adapter-next-translate/code/index.js +0 -49
- package/cjs/i18n/adapter-next-translate/code/nextTranslateI18n.js +0 -19
- package/cjs/i18n/adapter-next-translate/code/useT.js +0 -43
- package/cjs/i18n/client/formatRoutePathname.js +0 -5
- package/cjs/i18n/client/index.js +0 -3
- package/cjs/i18n/client/interpolateTo.js +0 -14
- package/cjs/i18n/client/routeHasDynamicPortion.js +0 -2
- package/cjs/i18n/compiler/api.js +0 -18
- package/cjs/i18n/compiler/code/data-routes.js +0 -99
- package/cjs/i18n/compiler/code/data-translations.js +0 -86
- package/cjs/i18n/compiler/code/data.js +0 -20
- package/cjs/i18n/compiler/code/generate.js +0 -46
- package/cjs/i18n/compiler/code/index.js +0 -2
- package/cjs/i18n/compiler/code/tsCompile.js +0 -27
- package/cjs/i18n/compiler/code/write.js +0 -39
- package/cjs/i18n/compiler/config.js +0 -13
- package/cjs/i18n/compiler/helpers.js +0 -14
- package/cjs/i18n/compiler/input/data-local.js +0 -60
- package/cjs/i18n/compiler/input/data-remote.js +0 -41
- package/cjs/i18n/compiler/input/data.js +0 -11
- package/cjs/i18n/compiler/input/index.js +0 -3
- package/cjs/i18n/compiler/input/types.js +0 -1
- package/cjs/i18n/compiler/input/write.js +0 -15
- package/cjs/i18n/compiler/pluralisation.js +0 -59
- package/cjs/i18n/compiler/summary/data.js +0 -28
- package/cjs/i18n/compiler/summary/generate.js +0 -27
- package/cjs/i18n/compiler/summary/index.js +0 -2
- package/cjs/i18n/compiler/summary/write.js +0 -18
- package/cjs/i18n/compiler/types.js +0 -1
- package/cjs/i18n/compiler-sync.js +0 -2
- package/cjs/i18n/compiler-worker.js +0 -3
- package/cjs/i18n/compiler.js +0 -1
- package/cjs/i18n/index.js +0 -2
- package/cjs/i18n/next.js +0 -3
- package/cjs/i18n/types.js +0 -1
- package/cjs/index.d.ts +0 -2
- package/cjs/next.d.ts +0 -3
- package/cjs/package.json +0 -34
- package/cjs/types.d.ts +0 -48
- package/client/formatRoutePathname.js +0 -5
- package/client/index.js +0 -3
- package/client/interpolateTo.js +0 -14
- package/client/routeHasDynamicPortion.js +0 -2
- package/compiler/api.js +0 -18
- package/compiler/code/data-routes.js +0 -99
- package/compiler/code/data-translations.js +0 -86
- package/compiler/code/data.js +0 -20
- package/compiler/code/generate.js +0 -46
- package/compiler/code/index.js +0 -2
- package/compiler/code/tsCompile.js +0 -27
- package/compiler/code/write.js +0 -39
- package/compiler/config.js +0 -13
- package/compiler/helpers.js +0 -14
- package/compiler/input/data-local.js +0 -60
- package/compiler/input/data-remote.js +0 -41
- package/compiler/input/data.js +0 -11
- package/compiler/input/index.js +0 -3
- package/compiler/input/types.js +0 -1
- package/compiler/input/write.js +0 -15
- package/compiler/pluralisation.js +0 -59
- package/compiler/summary/data.js +0 -28
- package/compiler/summary/generate.js +0 -27
- package/compiler/summary/index.js +0 -2
- package/compiler/summary/write.js +0 -18
- package/compiler/types.js +0 -1
- package/compiler-sync.js +0 -2
- package/compiler-worker.js +0 -3
- package/compiler.js +0 -1
- package/index.js +0 -2
- package/next.js +0 -3
- 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;
|