@koine/i18n 2.0.0-beta.72 → 2.0.0-beta.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/adapter-js/code/config.cjs.js +14 -0
  2. package/adapter-js/code/config.js +14 -0
  3. package/adapter-js/code/defaultLocale.js +7 -0
  4. package/adapter-js/code/deriveLocalisedPathnames.js +78 -0
  5. package/adapter-js/code/index.js +57 -0
  6. package/adapter-js/code/isLocale.js +9 -0
  7. package/adapter-js/code/locales.js +8 -0
  8. package/adapter-js/code/pathnameToRouteId.js +20 -0
  9. package/adapter-js/code/routes.js +10 -0
  10. package/adapter-js/code/routesSlim.js +13 -0
  11. package/adapter-js/code/routesSpa.js +11 -0
  12. package/adapter-js/code/tFns.js +75 -0
  13. package/adapter-js/code/tInterpolateParams.js +21 -0
  14. package/adapter-js/code/tPluralise.js +12 -0
  15. package/adapter-js/code/to.js +32 -0
  16. package/adapter-js/code/toFns.js +41 -0
  17. package/adapter-js/code/toFormat.js +58 -0
  18. package/adapter-js/code/toSpa.js +42 -0
  19. package/adapter-js/code/types.js +382 -0
  20. package/adapter-next/code/index.js +27 -0
  21. package/adapter-next/code/next-redirects.js +5 -0
  22. package/adapter-next/code/next-rewrites.js +5 -0
  23. package/adapter-next/code/useCurrentLocalisedPathnames.js +20 -0
  24. package/adapter-next/code/useLocale.js +8 -0
  25. package/adapter-next/code/useRouteId.js +10 -0
  26. package/adapter-next/code/useTo.js +26 -0
  27. package/adapter-next/code/useToSpa.js +34 -0
  28. package/adapter-next/plugin-async.js +21 -0
  29. package/adapter-next/plugin-legacy.js +246 -0
  30. package/adapter-next/plugin-shared.js +49 -0
  31. package/adapter-next/plugin.js +24 -0
  32. package/adapter-next/redirects.js +77 -0
  33. package/adapter-next/rewrites.js +68 -0
  34. package/adapter-next/transformPathname.js +20 -0
  35. package/adapter-next/webpackPluginI18n.js +19 -0
  36. package/adapter-next-translate/code/DynamicNamespaces.js +9 -0
  37. package/adapter-next-translate/code/T.js +31 -0
  38. package/adapter-next-translate/code/TransText.js +9 -0
  39. package/adapter-next-translate/code/getT.js +15 -0
  40. package/adapter-next-translate/code/index.js +26 -0
  41. package/adapter-next-translate/code/nextTranslateI18n.js +19 -0
  42. package/adapter-next-translate/code/useT.js +43 -0
  43. package/client/formatRoutePathname.d.ts +2 -2
  44. package/client/formatRoutePathname.js +8 -0
  45. package/client/index.js +3 -0
  46. package/client/interpolateTo.js +36 -0
  47. package/client/routeHasDynamicPortion.js +1 -0
  48. package/compiler/api.js +23 -0
  49. package/compiler/code/data-routes.js +174 -0
  50. package/compiler/code/data-translations.js +148 -0
  51. package/compiler/code/data.js +22 -0
  52. package/compiler/code/generate.js +73 -0
  53. package/compiler/code/index.js +3 -0
  54. package/compiler/code/tsCompile.js +40 -0
  55. package/compiler/code/write.js +78 -0
  56. package/compiler/config.js +14 -0
  57. package/compiler/helpers.js +19 -0
  58. package/compiler/input/data-local.js +70 -0
  59. package/compiler/input/data-remote.js +65 -0
  60. package/compiler/input/data.js +17 -0
  61. package/compiler/input/index.js +4 -0
  62. package/compiler/input/types.js +1 -0
  63. package/compiler/input/write.js +15 -0
  64. package/compiler/pluralisation.js +67 -0
  65. package/compiler/summary/data.js +55 -0
  66. package/compiler/summary/generate.js +73 -0
  67. package/compiler/summary/index.js +2 -0
  68. package/compiler/summary/write.js +28 -0
  69. package/compiler/types.js +1 -0
  70. package/compiler-sync.js +3 -0
  71. package/compiler-worker.js +3 -0
  72. package/compiler.js +1 -0
  73. package/index.js +2 -0
  74. package/next.js +3 -0
  75. package/package.json +3 -3
  76. package/types.js +1 -0
@@ -0,0 +1,14 @@
1
+ export default ({ config }) => `
2
+ const { locales } = require("./locales");
3
+ const { defaultLocale } = require("./defaultLocale");
4
+
5
+ const config = {
6
+ locales,
7
+ defaultLocale,
8
+ hideDefaultLocaleInUrl: ${config.hideDefaultLocaleInUrl},
9
+ };
10
+
11
+ exports.config = config;
12
+
13
+ module.exports = config;
14
+ `;
@@ -0,0 +1,14 @@
1
+ export default ({ config }) => `
2
+ import { locales } from "./locales";
3
+ import { defaultLocale } from "./defaultLocale";
4
+
5
+ /**
6
+ */
7
+ export const config = {
8
+ locales,
9
+ defaultLocale,
10
+ hideDefaultLocaleInUrl: ${config.hideDefaultLocaleInUrl},
11
+ }
12
+
13
+ export default config;
14
+ `;
@@ -0,0 +1,7 @@
1
+ export default ({ config }) => `
2
+ import type { I18n } from "./types";
3
+
4
+ export const defaultLocale: I18n.Locale = "${config.defaultLocale}";
5
+
6
+ export default defaultLocale;
7
+ `;
@@ -0,0 +1,78 @@
1
+ export default () => `
2
+ import { locales } from "./locales";
3
+ import { to } from "./to";
4
+ import type { I18n } from "./types";
5
+
6
+ function getPathnameDynamicPortionName(pathname: string) {
7
+ const res = pathname.match(/\\[(.+)\\]/);
8
+ return res ? res[1] : false;
9
+ }
10
+
11
+ function isStaticPathname(pathname: string) {
12
+ return !/\\[/.test(pathname);
13
+ }
14
+
15
+ // e.g. ['my', 'path', 'id', 'view'] or ['nl' 'my', 'path', 'id', 'view']
16
+ // where the first might be the locale (depending on hideDefaultLocaleInUrl)
17
+ function getPathnameParts(pathname: string) {
18
+ return pathname
19
+ .split("/")
20
+ .slice(1)
21
+ .filter((p, i) => (i === 0 ? !locales.includes(p as I18n.Locale) : true));
22
+ }
23
+
24
+ function localisePathname(
25
+ locale: I18n.Locale,
26
+ // e.g. "my.path.[id].view"
27
+ routeId: I18n.RouteIdDynamic | I18n.RouteIdStatic,
28
+ locationLike: LocationLike
29
+ ) {
30
+ const toPathname = to(routeId as I18n.RouteIdStatic, locale);
31
+
32
+ if (isStaticPathname(toPathname)) {
33
+ return toPathname;
34
+ }
35
+ // e.g. ['my', 'path', '[id]', 'view']
36
+ const toPathnameParts = getPathnameParts(toPathname);
37
+ // e.g. "my.path.[id].view"
38
+ const routeIdDynamic = routeId as I18n.RouteIdDynamic;
39
+ // e.g. /my/path/123/view
40
+ const currentPathname = locationLike.pathname;
41
+ // e.g. ['my', 'path', '123', 'view']
42
+ const currentPathnameParts = getPathnameParts(currentPathname);
43
+ // e.g. { id: "123" }
44
+ const params: Record<string, string> = {};
45
+
46
+ for (let i = 0; i < toPathnameParts.length; i++) {
47
+ // e.g. "my" or "[id]"
48
+ const part = toPathnameParts[i];
49
+ // e.g. "id"
50
+ const name = getPathnameDynamicPortionName(part);
51
+ if (name) {
52
+ // e.g. "123"
53
+ const value = currentPathnameParts[i];
54
+ if (value) {
55
+ params[name] = value;
56
+ }
57
+ }
58
+ }
59
+
60
+ return (
61
+ to(routeIdDynamic, params as never, locale) +
62
+ location?.search || "" +
63
+ location?.hash || ""
64
+ );
65
+ }
66
+
67
+ type LocationLike = { pathname: string; search?: string; hash?: string; };
68
+
69
+ export type LocalisedPathnames = Record<I18n.Locale, string>;
70
+
71
+ export const deriveLocalisedPathnames = (routeId: I18n.RouteId, locationLike: LocationLike) =>
72
+ locales.reduce((pathnames, locale) => {
73
+ pathnames[locale] = localisePathname(locale, routeId, locationLike);
74
+ return pathnames;
75
+ }, {} as LocalisedPathnames);
76
+
77
+ export default deriveLocalisedPathnames;
78
+ `;
@@ -0,0 +1,57 @@
1
+ import config from "./config";
2
+ import configCjs from "./config.cjs";
3
+ import defaultLocale from "./defaultLocale";
4
+ import deriveLocalisedPathnames from "./deriveLocalisedPathnames";
5
+ import isLocale from "./isLocale";
6
+ import locales from "./locales";
7
+ import pathnameToRouteId from "./pathnameToRouteId";
8
+ import routes from "./routes";
9
+ import routesSlim from "./routesSlim";
10
+ import routesSpa from "./routesSpa";
11
+ import tFns from "./tFns";
12
+ import tInterpolateParams from "./tInterpolateParams";
13
+ import tPluralise from "./tPluralise";
14
+ import to from "./to";
15
+ import toFns from "./toFns";
16
+ import toFormat from "./toFormat";
17
+ import toSpa from "./toSpa";
18
+ import types from "./types";
19
+ const adapter = () => {
20
+ return {
21
+ files: [
22
+ { name: "config.cjs", fn: configCjs, ext: "js" },
23
+ { name: "config", fn: config, ext: "ts", index: true },
24
+ { name: "defaultLocale", fn: defaultLocale, ext: "ts", index: true },
25
+ {
26
+ name: "deriveLocalisedPathnames",
27
+ fn: deriveLocalisedPathnames,
28
+ ext: "ts",
29
+ index: true,
30
+ },
31
+ { name: "isLocale", fn: isLocale, ext: "ts", index: true },
32
+ { name: "locales", fn: locales, ext: "ts", index: true },
33
+ {
34
+ name: "pathnameToRouteId",
35
+ fn: pathnameToRouteId,
36
+ ext: "ts",
37
+ index: true,
38
+ },
39
+ { name: "routes", fn: routes, ext: "ts" },
40
+ { name: "routesSlim", fn: routesSlim, ext: "ts" },
41
+ { name: "routesSpa", fn: routesSpa, ext: "ts" },
42
+ { name: "tFns", fn: tFns, ext: "ts" },
43
+ {
44
+ name: "tInterpolateParams",
45
+ fn: tInterpolateParams,
46
+ ext: "ts",
47
+ },
48
+ { name: "to", fn: to, ext: "ts", index: true },
49
+ { name: "toFns", fn: toFns, ext: "ts", index: true },
50
+ { name: "toFormat", fn: toFormat, ext: "ts", index: true },
51
+ { name: "toSpa", fn: toSpa, ext: "ts", index: true },
52
+ { name: "tPluralise", fn: tPluralise, ext: "ts" },
53
+ { name: "types", fn: types, ext: "ts", index: true },
54
+ ],
55
+ };
56
+ };
57
+ export default adapter;
@@ -0,0 +1,9 @@
1
+ export default () => `
2
+ import { locales } from "./locales";
3
+ import type { I18n } from "./types";
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ export const isLocale = (payload: any): payload is I18n.Locale => locales.includes(payload);
7
+
8
+ export default isLocale;
9
+ `;
@@ -0,0 +1,8 @@
1
+ export default ({ config }) => {
2
+ const value = `[${config.locales.map((l) => `"${l}"`).join(", ")}]`;
3
+ return `
4
+ export const locales = ${value} as const;
5
+
6
+ export default locales;
7
+ `;
8
+ };
@@ -0,0 +1,20 @@
1
+ import { escapeRegExp } from "@koine/utils";
2
+ export default ({ options }) => {
3
+ const { idDelimiter, optionalCatchAll, catchAll } = options.routes.tokens;
4
+ return `
5
+ /**
6
+ * Convert a URL like pathname to a "named route"
7
+ * E.g. it transforms:
8
+ * - \`/dashboard/user/[id]\` into \`dashboard.user.[id]\`
9
+ */
10
+ export const pathnameToRouteId = (pathname: string) =>
11
+ pathname
12
+ .replace(/^\\//g, "")
13
+ .replace(/\\//g, "${idDelimiter}")
14
+ .replace(/${escapeRegExp(idDelimiter)}${escapeRegExp(optionalCatchAll.start)}.+$/, "")
15
+ .replace(/${escapeRegExp(idDelimiter)}${escapeRegExp(catchAll.start)}.+$/, "")
16
+ .replace(/\\/index$/, "");
17
+
18
+ export default pathnameToRouteId;
19
+ `;
20
+ };
@@ -0,0 +1,10 @@
1
+ export default ({ routes }) => {
2
+ const value = JSON.stringify(Object.fromEntries(Object.entries(routes.byId)
3
+ .map(([routeId, { pathnames }]) => [routeId, pathnames])
4
+ .sort()), null, 2);
5
+ return `
6
+ export const routes = ${value} as const;
7
+
8
+ export default routes;
9
+ `;
10
+ };
@@ -0,0 +1,13 @@
1
+ export default ({ routes }) => {
2
+ const value = JSON.stringify(Object.fromEntries(Object.entries(routes.byId)
3
+ .map(([routeId, { pathnamesSlim, pathnames }]) => [
4
+ routeId,
5
+ pathnamesSlim || pathnames,
6
+ ])
7
+ .sort()), null, 2);
8
+ return `
9
+ export const routesSlim = ${value} as const;
10
+
11
+ export default routesSlim;
12
+ `;
13
+ };
@@ -0,0 +1,11 @@
1
+ export default ({ routes }) => {
2
+ const value = JSON.stringify(Object.fromEntries(Object.entries(routes.byId)
3
+ .filter(([, { pathnamesSpa }]) => !!pathnamesSpa)
4
+ .map(([routeId, { pathnamesSpa }]) => [routeId, pathnamesSpa])
5
+ .sort()), null, 2);
6
+ return `
7
+ export const routesSpa = ${value} as const;
8
+
9
+ export default routesSpa;
10
+ `;
11
+ };
@@ -0,0 +1,75 @@
1
+ import { areEqual, isArray, isBoolean, isNumber, isPrimitive, isString, } from "@koine/utils";
2
+ import { dataParamsToTsInterfaceBody } from "../../compiler/helpers";
3
+ const getTranslationValueOutput = (value) => {
4
+ if (isString(value) || isNumber(value)) {
5
+ return `"${value}"`;
6
+ }
7
+ else if (isBoolean(value)) {
8
+ return `${value}`;
9
+ }
10
+ else if (isArray(value)) {
11
+ return JSON.stringify(value);
12
+ }
13
+ return `(${JSON.stringify(value)})`;
14
+ };
15
+ const areEqualTranslationsValues = (a, b) => areEqual(a, b);
16
+ const getFunctionBodyWithLocales = (config, perLocaleValues) => {
17
+ const { defaultLocale } = config;
18
+ let output = "";
19
+ for (const locale in perLocaleValues) {
20
+ const value = perLocaleValues[locale];
21
+ if (locale !== defaultLocale &&
22
+ !areEqualTranslationsValues(value, perLocaleValues[defaultLocale])) {
23
+ output += `locale === "${locale}" ? ${getTranslationValueOutput(value)} : `;
24
+ }
25
+ }
26
+ output += getTranslationValueOutput(perLocaleValues[defaultLocale]);
27
+ return output;
28
+ };
29
+ export default ({ config, options, translations }) => {
30
+ let output = `
31
+ /* eslint-disable @typescript-eslint/no-unused-vars */
32
+ /* eslint-disable prefer-const */
33
+ import type { I18n } from "./types";
34
+ import { tInterpolateParams } from "./tInterpolateParams";
35
+ import { tPluralise } from "./tPluralise";
36
+
37
+ `;
38
+ for (const translationId in translations) {
39
+ let { values, params, plural } = translations[translationId];
40
+ const name = `${options.translations.fnsPrefix}${translationId}`;
41
+ if (plural) {
42
+ if (params) {
43
+ params["count"] = "number";
44
+ }
45
+ else {
46
+ params = { count: "number" };
47
+ }
48
+ }
49
+ const argParam = params
50
+ ? `params: { ${dataParamsToTsInterfaceBody(params)} }`
51
+ : "";
52
+ const argLocale = "locale?: I18n.Locale";
53
+ const args = [argParam, argLocale].filter(Boolean).join(", ");
54
+ output += `export let ${name} = (${args}) => `;
55
+ let outputFnReturn = "";
56
+ if (isPrimitive(values)) {
57
+ outputFnReturn += getTranslationValueOutput(values);
58
+ }
59
+ else {
60
+ outputFnReturn += getFunctionBodyWithLocales(config, values);
61
+ }
62
+ if (plural) {
63
+ outputFnReturn = `tPluralise(${outputFnReturn}, params.count)`;
64
+ }
65
+ if (params) {
66
+ outputFnReturn = `tInterpolateParams(${outputFnReturn}, params);`;
67
+ }
68
+ else {
69
+ outputFnReturn = `${outputFnReturn};`;
70
+ }
71
+ output += outputFnReturn;
72
+ output += `\n`;
73
+ }
74
+ return output;
75
+ };
@@ -0,0 +1,21 @@
1
+ const escapeEachChar = (input) => input
2
+ .split("")
3
+ .map((v) => `\\${v}`)
4
+ .join("");
5
+ export default ({ options }) => {
6
+ const { start, end } = options.translations.dynamicDelimiters;
7
+ return `
8
+ /* eslint-disable prefer-const */
9
+ export let tInterpolateParams = (
10
+ value: string,
11
+ params?: object,
12
+ ) =>
13
+ params ? value.replace(
14
+ /${escapeEachChar(start)}(.*?)${escapeEachChar(end)}/g,
15
+ (_, key) =>
16
+ params[key.trim() as keyof typeof params] + "",
17
+ ) : value;
18
+
19
+ export default tInterpolateParams;
20
+ `;
21
+ };
@@ -0,0 +1,12 @@
1
+ export default () => {
2
+ return `
3
+ /* eslint-disable prefer-const */
4
+ let pluralRules = new Intl.PluralRules();
5
+
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ export let tPluralise = (values: any, count: number) =>
8
+ values[count] || values[pluralRules.select(count)] || (count === 0 ? values.zero : values["other"]);
9
+
10
+ export default tPluralise;
11
+ `;
12
+ };
@@ -0,0 +1,32 @@
1
+ export default ({ config }) => `
2
+ import { isLocale } from "./isLocale";
3
+ import { toFormat } from "./toFormat";
4
+ import { routesSlim } from "./routesSlim";
5
+ import type { I18n } from "./types";
6
+
7
+ /**
8
+ * *To* route utility
9
+ *
10
+ * @returns A localised relative URL based on your i18nCompiler configuration
11
+ */
12
+ export function to<Id extends I18n.RouteId>(
13
+ id: Id,
14
+ ...args: Id extends I18n.RouteIdDynamic
15
+ ?
16
+ | [I18n.RouteParams[Id]]
17
+ | [I18n.RouteParams[Id], I18n.Locale]
18
+ : [] | [I18n.Locale]
19
+ ) {
20
+ const locale = (isLocale(args[0]) ? args[0] : args[1]) || "${config.defaultLocale}";
21
+
22
+ return toFormat(
23
+ locale,
24
+ (routesSlim[id] as Record<string, string>)[locale] ??
25
+ (routesSlim[id] as Record<string, string>)["${config.defaultLocale}"] ??
26
+ routesSlim[id],
27
+ isLocale(args[0]) ? undefined : args[0]
28
+ ) as I18n.RoutePathnames[Id];
29
+ }
30
+
31
+ export default to;
32
+ `;
@@ -0,0 +1,41 @@
1
+ import { changeCaseSnake, isString } from "@koine/utils";
2
+ const getFunctionBodyWithLocales = (config, perLocaleValues) => {
3
+ const { defaultLocale } = config;
4
+ let output = "";
5
+ for (const locale in perLocaleValues) {
6
+ const value = perLocaleValues[locale];
7
+ if (locale !== defaultLocale && value !== perLocaleValues[defaultLocale]) {
8
+ output += `locale === "${locale}" ? "${value}" : `;
9
+ }
10
+ }
11
+ output += '"' + perLocaleValues[defaultLocale] + '"';
12
+ return output;
13
+ };
14
+ export default ({ config, routes, options }) => {
15
+ const hasOneLocale = config.locales.length === 1;
16
+ let output = `
17
+ /* eslint-disable prefer-const */
18
+ import { toFormat } from "./toFormat";
19
+ import type { I18n } from "./types";
20
+
21
+ `;
22
+ for (const routeId in routes.byId) {
23
+ const { pathnames, params } = routes.byId[routeId];
24
+ const name = `${options.routes.fnsPrefix}${changeCaseSnake(routeId)}`;
25
+ const paramsType = `I18n.RouteParams["${routeId}"]`;
26
+ const argParam = params ? `params: ${paramsType}` : "";
27
+ const argLocale = hasOneLocale ? "" : "locale?: I18n.Locale";
28
+ const args = [argParam, argLocale].filter(Boolean).join(", ");
29
+ const formatArgLocale = hasOneLocale ? `""` : "locale";
30
+ const formatArgParams = params ? ", params" : "";
31
+ output += `export let ${name} = (${args}) => `;
32
+ if (isString(pathnames)) {
33
+ output += `toFormat(${formatArgLocale}, "${pathnames}"${formatArgParams});`;
34
+ }
35
+ else {
36
+ output += `toFormat(${formatArgLocale}, ${getFunctionBodyWithLocales(config, pathnames)}${formatArgParams});`;
37
+ }
38
+ output += `\n`;
39
+ }
40
+ return output;
41
+ };
@@ -0,0 +1,58 @@
1
+ export default ({ config }) => `
2
+ export function toFormat(
3
+ locale: string | undefined,
4
+ pathname: string,
5
+ params?: object,
6
+ ) {
7
+ locale = locale || "${config.defaultLocale}";
8
+ if (process.env["NODE_ENV"] === "development") {
9
+ if (params) {
10
+ pathname.replace(/\\[(.*?)\\]/g, (_, dynamicKey) => {
11
+ const key = dynamicKey as Extract<keyof typeof params, string>;
12
+
13
+ if (!(key in params)) {
14
+ console.warn(
15
+ "[@koine/i18n]::interpolateTo, using '" +
16
+ pathname +
17
+ "' without param '" +
18
+ key +
19
+ "'",
20
+ { params }
21
+ );
22
+ }
23
+
24
+ if (!["string", "number"].includes(typeof params[key])) {
25
+ console.warn(
26
+ "[@koine/i18n]::toFormat, using '" +
27
+ pathname +
28
+ "' with unserializable param '" +
29
+ key +
30
+ "' (type '" +
31
+ Object.prototype.toString.call((params[key])).slice(8, -1) +
32
+ "')",
33
+ );
34
+ }
35
+ return "";
36
+ });
37
+ }
38
+ }
39
+
40
+ if (params) {
41
+ pathname = pathname.replace(
42
+ /\\[(.*?)\\]/g,
43
+ (_, key) =>
44
+ params[key as keyof typeof params] + "",
45
+ )
46
+ }
47
+ ${config.hideDefaultLocaleInUrl
48
+ ? `
49
+ if (locale !== "${config.defaultLocale}") {
50
+ return "/" + locale + pathname;
51
+ }
52
+ `
53
+ : ``}
54
+ return pathname;
55
+ }
56
+
57
+ export default toFormat;
58
+ `;
@@ -0,0 +1,42 @@
1
+ export default ({ config, options }) => {
2
+ const { idDelimiter } = options.routes.tokens;
3
+ return `
4
+ import { isLocale } from "./isLocale";
5
+ import { routesSpa } from "./routesSpa";
6
+ import { toFormat } from "./toFormat";
7
+ import type { I18n } from "./types";
8
+
9
+ /**
10
+ * *To spa* route utility
11
+ *
12
+ * @returns A localised relative URL based on your i18nCompiler configuration
13
+ */
14
+ export function toSpa<
15
+ Root extends keyof I18n.RouteSpa,
16
+ Path extends Extract<keyof I18n.RouteSpa[Root], string>,
17
+ >(
18
+ rootId: Root,
19
+ pathId: Path,
20
+ ...args: I18n.RouteJoinedId<Root, Path> extends I18n.RouteIdDynamic
21
+ ?
22
+ | [I18n.RouteParams[I18n.RouteJoinedId<Root, Path>]]
23
+ | [I18n.RouteParams[I18n.RouteJoinedId<Root, Path>], I18n.Locale]
24
+ : [] | [I18n.Locale]
25
+ ) {
26
+ const locale = (isLocale(args[0]) ? args[0] : args[1]) || "${config.defaultLocale}";
27
+ const fullId = \`\${rootId}${idDelimiter}\${pathId}\` as I18n.RouteJoinedId<Root, Path>;
28
+ return toFormat(
29
+ // FIXME: actually the locale will be prepended if hideDefaultLocaleInUrl will be false
30
+ "", // do not pass the locale so that won't be prepended
31
+ (routesSpa[fullId] as Record<string, string>)[locale],
32
+ args.length === 2
33
+ ? args[0]
34
+ : args[0] && !isLocale(args[0])
35
+ ? args[0]
36
+ : void 0,
37
+ ) as I18n.RouteSpa[Root][Path];
38
+ }
39
+
40
+ export default toSpa;
41
+ `;
42
+ };