wolmar-i18n 0.1.0

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/LICENSE +21 -0
  2. package/README.md +158 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  4. package/dist/client/create-client.cjs +87 -0
  5. package/dist/client/create-client.cjs.map +1 -0
  6. package/dist/client/create-client.d.cts +57 -0
  7. package/dist/client/create-client.d.ts +57 -0
  8. package/dist/client/create-client.js +85 -0
  9. package/dist/client/create-client.js.map +1 -0
  10. package/dist/client/index.cjs +4 -0
  11. package/dist/client/index.d.cts +2 -0
  12. package/dist/client/index.d.ts +2 -0
  13. package/dist/client/index.js +3 -0
  14. package/dist/index.cjs +7 -0
  15. package/dist/index.d.cts +5 -0
  16. package/dist/index.d.ts +5 -0
  17. package/dist/index.js +3 -0
  18. package/dist/proxy/create-proxy.cjs +42 -0
  19. package/dist/proxy/create-proxy.cjs.map +1 -0
  20. package/dist/proxy/create-proxy.d.cts +25 -0
  21. package/dist/proxy/create-proxy.d.ts +25 -0
  22. package/dist/proxy/create-proxy.js +41 -0
  23. package/dist/proxy/create-proxy.js.map +1 -0
  24. package/dist/proxy/index.cjs +6 -0
  25. package/dist/proxy/index.d.cts +3 -0
  26. package/dist/proxy/index.d.ts +3 -0
  27. package/dist/proxy/index.js +3 -0
  28. package/dist/proxy/negotiate.cjs +40 -0
  29. package/dist/proxy/negotiate.cjs.map +1 -0
  30. package/dist/proxy/negotiate.d.cts +20 -0
  31. package/dist/proxy/negotiate.d.ts +20 -0
  32. package/dist/proxy/negotiate.js +39 -0
  33. package/dist/proxy/negotiate.js.map +1 -0
  34. package/dist/server/create-server.cjs +35 -0
  35. package/dist/server/create-server.cjs.map +1 -0
  36. package/dist/server/create-server.d.cts +32 -0
  37. package/dist/server/create-server.d.ts +32 -0
  38. package/dist/server/create-server.js +35 -0
  39. package/dist/server/create-server.js.map +1 -0
  40. package/dist/server/index.cjs +3 -0
  41. package/dist/server/index.d.cts +2 -0
  42. package/dist/server/index.d.ts +2 -0
  43. package/dist/server/index.js +2 -0
  44. package/dist/types/config.d.cts +76 -0
  45. package/dist/types/config.d.ts +76 -0
  46. package/dist/types/messages.d.cts +63 -0
  47. package/dist/types/messages.d.ts +63 -0
  48. package/dist/utils/config.cjs +37 -0
  49. package/dist/utils/config.cjs.map +1 -0
  50. package/dist/utils/config.d.cts +24 -0
  51. package/dist/utils/config.d.ts +24 -0
  52. package/dist/utils/config.js +36 -0
  53. package/dist/utils/config.js.map +1 -0
  54. package/dist/utils/dictionary.cjs +20 -0
  55. package/dist/utils/dictionary.cjs.map +1 -0
  56. package/dist/utils/dictionary.js +20 -0
  57. package/dist/utils/dictionary.js.map +1 -0
  58. package/dist/utils/interpolate.cjs +20 -0
  59. package/dist/utils/interpolate.cjs.map +1 -0
  60. package/dist/utils/interpolate.js +20 -0
  61. package/dist/utils/interpolate.js.map +1 -0
  62. package/dist/utils/locale.cjs +14 -0
  63. package/dist/utils/locale.cjs.map +1 -0
  64. package/dist/utils/locale.js +14 -0
  65. package/dist/utils/locale.js.map +1 -0
  66. package/dist/utils/paths.cjs +40 -0
  67. package/dist/utils/paths.cjs.map +1 -0
  68. package/dist/utils/paths.d.cts +19 -0
  69. package/dist/utils/paths.d.ts +19 -0
  70. package/dist/utils/paths.js +38 -0
  71. package/dist/utils/paths.js.map +1 -0
  72. package/dist/utils/translator.cjs +54 -0
  73. package/dist/utils/translator.cjs.map +1 -0
  74. package/dist/utils/translator.js +54 -0
  75. package/dist/utils/translator.js.map +1 -0
  76. package/package.json +95 -0
@@ -0,0 +1,25 @@
1
+ import { I18nConfig, LocaleList } from "../types/config.js";
2
+ import { NextRequest, NextResponse } from "next/server.js";
3
+
4
+ //#region src/proxy/create-proxy.d.ts
5
+ /**
6
+ * A function to build the Next.js proxy that keeps every URL locale-prefixed.
7
+ *
8
+ * A saved locale outranks the link: someone whose cookie says `da` opening
9
+ * `/en/projects` is redirected to `/da/projects`. Choosing a locale is a
10
+ * decision; receiving a link is not.
11
+ *
12
+ * Resolution order: cookie, path prefix, `Accept-Language`, default locale.
13
+ *
14
+ * @param config The config returned by `defineI18nConfig`.
15
+ * @returns The proxy function to export from the app's `proxy.ts`.
16
+ * @example
17
+ * export const proxy = createI18nProxy(i18n);
18
+ * export const config = {
19
+ * matcher: ["/((?!api|_next|.*\\..*|favicon.ico|robots.txt).*)"],
20
+ * };
21
+ */
22
+ declare function createI18nProxy<TLocales extends LocaleList>(config: I18nConfig<TLocales>): (request: NextRequest) => NextResponse;
23
+ //#endregion
24
+ export { createI18nProxy };
25
+ //# sourceMappingURL=create-proxy.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { firstSegment } from "../utils/paths.js";
2
+ import { matchLocale } from "./negotiate.js";
3
+ import { NextResponse } from "next/server.js";
4
+ //#region src/proxy/create-proxy.ts
5
+ /**
6
+ * A function to build the Next.js proxy that keeps every URL locale-prefixed.
7
+ *
8
+ * A saved locale outranks the link: someone whose cookie says `da` opening
9
+ * `/en/projects` is redirected to `/da/projects`. Choosing a locale is a
10
+ * decision; receiving a link is not.
11
+ *
12
+ * Resolution order: cookie, path prefix, `Accept-Language`, default locale.
13
+ *
14
+ * @param config The config returned by `defineI18nConfig`.
15
+ * @returns The proxy function to export from the app's `proxy.ts`.
16
+ * @example
17
+ * export const proxy = createI18nProxy(i18n);
18
+ * export const config = {
19
+ * matcher: ["/((?!api|_next|.*\\..*|favicon.ico|robots.txt).*)"],
20
+ * };
21
+ */
22
+ function createI18nProxy(config) {
23
+ return function proxy(request) {
24
+ const { pathname } = request.nextUrl;
25
+ const prefix = firstSegment(pathname);
26
+ const isPrefixed = config.hasLocale(prefix);
27
+ const cookie = request.cookies.get(config.cookieName)?.value;
28
+ const locale = (config.hasLocale(cookie) ? cookie : void 0) ?? (isPrefixed ? prefix : void 0) ?? matchLocale(request.headers.get("accept-language"), config.locales) ?? config.defaultLocale;
29
+ if (isPrefixed && prefix === locale) return NextResponse.next();
30
+ const barePath = isPrefixed ? config.stripLocale(pathname) : pathname;
31
+ const url = request.nextUrl.clone();
32
+ url.pathname = config.localizePath(barePath, locale);
33
+ const response = NextResponse.redirect(url, 307);
34
+ response.headers.set("Vary", "Accept-Language, Cookie");
35
+ return response;
36
+ };
37
+ }
38
+ //#endregion
39
+ export { createI18nProxy };
40
+
41
+ //# sourceMappingURL=create-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-proxy.js","names":[],"sources":["../../src/proxy/create-proxy.ts"],"sourcesContent":["import { NextResponse } from \"next/server\";\nimport type { NextRequest } from \"next/server\";\nimport type { I18nConfig, LocaleList } from \"../types\";\nimport { firstSegment } from \"../utils/paths\";\nimport { matchLocale } from \"./negotiate\";\n\n/**\n * A function to build the Next.js proxy that keeps every URL locale-prefixed.\n *\n * A saved locale outranks the link: someone whose cookie says `da` opening\n * `/en/projects` is redirected to `/da/projects`. Choosing a locale is a\n * decision; receiving a link is not.\n *\n * Resolution order: cookie, path prefix, `Accept-Language`, default locale.\n *\n * @param config The config returned by `defineI18nConfig`.\n * @returns The proxy function to export from the app's `proxy.ts`.\n * @example\n * export const proxy = createI18nProxy(i18n);\n * export const config = {\n * matcher: [\"/((?!api|_next|.*\\\\..*|favicon.ico|robots.txt).*)\"],\n * };\n */\nexport function createI18nProxy<TLocales extends LocaleList>(\n config: I18nConfig<TLocales>,\n): (request: NextRequest) => NextResponse {\n return function proxy(request: NextRequest): NextResponse {\n const { pathname } = request.nextUrl;\n\n const prefix = firstSegment(pathname);\n const isPrefixed = config.hasLocale(prefix);\n\n const cookie = request.cookies.get(config.cookieName)?.value;\n const saved = config.hasLocale(cookie) ? cookie : undefined;\n\n const locale =\n saved ??\n (isPrefixed ? prefix : undefined) ??\n matchLocale(request.headers.get(\"accept-language\"), config.locales) ??\n config.defaultLocale;\n\n if (isPrefixed && prefix === locale) return NextResponse.next();\n\n const barePath = isPrefixed ? config.stripLocale(pathname) : pathname;\n\n const url = request.nextUrl.clone();\n url.pathname = config.localizePath(barePath, locale);\n\n const response = NextResponse.redirect(url, 307);\n\n response.headers.set(\"Vary\", \"Accept-Language, Cookie\");\n\n return response;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,gBACd,QACwC;AACxC,QAAO,SAAS,MAAM,SAAoC;EACxD,MAAM,EAAE,aAAa,QAAQ;EAE7B,MAAM,SAAS,aAAa,SAAS;EACrC,MAAM,aAAa,OAAO,UAAU,OAAO;EAE3C,MAAM,SAAS,QAAQ,QAAQ,IAAI,OAAO,WAAW,EAAE;EAGvD,MAAM,UAFQ,OAAO,UAAU,OAAO,GAAG,SAAS,KAAA,OAI/C,aAAa,SAAS,KAAA,MACvB,YAAY,QAAQ,QAAQ,IAAI,kBAAkB,EAAE,OAAO,QAAQ,IACnE,OAAO;AAET,MAAI,cAAc,WAAW,OAAQ,QAAO,aAAa,MAAM;EAE/D,MAAM,WAAW,aAAa,OAAO,YAAY,SAAS,GAAG;EAE7D,MAAM,MAAM,QAAQ,QAAQ,OAAO;AACnC,MAAI,WAAW,OAAO,aAAa,UAAU,OAAO;EAEpD,MAAM,WAAW,aAAa,SAAS,KAAK,IAAI;AAEhD,WAAS,QAAQ,IAAI,QAAQ,0BAA0B;AAEvD,SAAO"}
@@ -0,0 +1,6 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_negotiate = require("./negotiate.cjs");
3
+ const require_create_proxy = require("./create-proxy.cjs");
4
+ exports.createI18nProxy = require_create_proxy.createI18nProxy;
5
+ exports.matchLocale = require_negotiate.matchLocale;
6
+ exports.parseAcceptLanguage = require_negotiate.parseAcceptLanguage;
@@ -0,0 +1,3 @@
1
+ import { createI18nProxy } from "./create-proxy.cjs";
2
+ import { matchLocale, parseAcceptLanguage } from "./negotiate.cjs";
3
+ export { createI18nProxy, matchLocale, parseAcceptLanguage };
@@ -0,0 +1,3 @@
1
+ import { createI18nProxy } from "./create-proxy.js";
2
+ import { matchLocale, parseAcceptLanguage } from "./negotiate.js";
3
+ export { createI18nProxy, matchLocale, parseAcceptLanguage };
@@ -0,0 +1,3 @@
1
+ import { matchLocale, parseAcceptLanguage } from "./negotiate.js";
2
+ import { createI18nProxy } from "./create-proxy.js";
3
+ export { createI18nProxy, matchLocale, parseAcceptLanguage };
@@ -0,0 +1,40 @@
1
+ //#region src/proxy/negotiate.ts
2
+ /**
3
+ * A function to parse an `Accept-Language` header into locale tags.
4
+ * @param header The raw header value, or `null` when the request has none.
5
+ * @returns The tags ordered by q-value, highest first, with `*` and anything
6
+ * explicitly refused (`q=0`) removed.
7
+ */
8
+ function parseAcceptLanguage(header) {
9
+ if (!header) return [];
10
+ return header.split(",").map((part) => {
11
+ const [tag = "", ...params] = part.trim().split(";");
12
+ const weight = params.map((param) => param.trim()).find((param) => param.toLowerCase().startsWith("q="));
13
+ const quality = weight ? Number.parseFloat(weight.slice(2)) : 1;
14
+ return {
15
+ tag: tag.trim(),
16
+ quality: Number.isNaN(quality) ? 0 : quality
17
+ };
18
+ }).filter((entry) => entry.tag && entry.tag !== "*" && entry.quality > 0).sort((a, b) => b.quality - a.quality).map((entry) => entry.tag);
19
+ }
20
+ /**
21
+ * A function to pick the best supported locale for an `Accept-Language` header.
22
+ * @param header The raw header value, or `null` when the request has none.
23
+ * @param locales A list of supported locales to match against.
24
+ * @returns The best matching locale, or `undefined` when none match.
25
+ */
26
+ function matchLocale(header, locales) {
27
+ for (const tag of parseAcceptLanguage(header)) {
28
+ const wanted = tag.toLowerCase();
29
+ const exact = locales.find((locale) => locale.toLowerCase() === wanted);
30
+ if (exact) return exact;
31
+ const primary = wanted.split("-")[0];
32
+ const partial = locales.find((locale) => locale.toLowerCase() === primary);
33
+ if (partial) return partial;
34
+ }
35
+ }
36
+ //#endregion
37
+ exports.matchLocale = matchLocale;
38
+ exports.parseAcceptLanguage = parseAcceptLanguage;
39
+
40
+ //# sourceMappingURL=negotiate.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiate.cjs","names":[],"sources":["../../src/proxy/negotiate.ts"],"sourcesContent":["import type { LocaleList } from \"../types\";\n\n/**\n * A function to parse an `Accept-Language` header into locale tags.\n * @param header The raw header value, or `null` when the request has none.\n * @returns The tags ordered by q-value, highest first, with `*` and anything\n * explicitly refused (`q=0`) removed.\n */\nexport function parseAcceptLanguage(header: string | null): string[] {\n if (!header) return [];\n\n return header\n .split(\",\")\n .map((part) => {\n const [tag = \"\", ...params] = part.trim().split(\";\");\n\n const weight = params\n .map((param) => param.trim())\n .find((param) => param.toLowerCase().startsWith(\"q=\"));\n const quality = weight ? Number.parseFloat(weight.slice(2)) : 1;\n\n return {\n tag: tag.trim(),\n quality: Number.isNaN(quality) ? 0 : quality,\n };\n })\n .filter((entry) => entry.tag && entry.tag !== \"*\" && entry.quality > 0)\n .sort((a, b) => b.quality - a.quality)\n .map((entry) => entry.tag);\n}\n\n/**\n * A function to pick the best supported locale for an `Accept-Language` header.\n * @param header The raw header value, or `null` when the request has none.\n * @param locales A list of supported locales to match against.\n * @returns The best matching locale, or `undefined` when none match.\n */\nexport function matchLocale<TLocales extends LocaleList>(\n header: string | null,\n locales: TLocales,\n): TLocales[number] | undefined {\n for (const tag of parseAcceptLanguage(header)) {\n const wanted = tag.toLowerCase();\n\n // Each tag is tried exactly, then by its primary subtag, before moving to\n // the next one — so a higher-weighted `en-GB` beats a lower-weighted `da`.\n const exact = locales.find((locale) => locale.toLowerCase() === wanted);\n if (exact) return exact;\n\n const primary = wanted.split(\"-\")[0];\n const partial = locales.find((locale) => locale.toLowerCase() === primary);\n if (partial) return partial;\n }\n\n return undefined;\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,oBAAoB,QAAiC;AACnE,KAAI,CAAC,OAAQ,QAAO,EAAE;AAEtB,QAAO,OACJ,MAAM,IAAI,CACV,KAAK,SAAS;EACb,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,KAAK,MAAM,CAAC,MAAM,IAAI;EAEpD,MAAM,SAAS,OACZ,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,MAAM,UAAU,MAAM,aAAa,CAAC,WAAW,KAAK,CAAC;EACxD,MAAM,UAAU,SAAS,OAAO,WAAW,OAAO,MAAM,EAAE,CAAC,GAAG;AAE9D,SAAO;GACL,KAAK,IAAI,MAAM;GACf,SAAS,OAAO,MAAM,QAAQ,GAAG,IAAI;GACtC;GACD,CACD,QAAQ,UAAU,MAAM,OAAO,MAAM,QAAQ,OAAO,MAAM,UAAU,EAAE,CACtE,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,QAAQ,CACrC,KAAK,UAAU,MAAM,IAAI;;;;;;;;AAS9B,SAAgB,YACd,QACA,SAC8B;AAC9B,MAAK,MAAM,OAAO,oBAAoB,OAAO,EAAE;EAC7C,MAAM,SAAS,IAAI,aAAa;EAIhC,MAAM,QAAQ,QAAQ,MAAM,WAAW,OAAO,aAAa,KAAK,OAAO;AACvE,MAAI,MAAO,QAAO;EAElB,MAAM,UAAU,OAAO,MAAM,IAAI,CAAC;EAClC,MAAM,UAAU,QAAQ,MAAM,WAAW,OAAO,aAAa,KAAK,QAAQ;AAC1E,MAAI,QAAS,QAAO"}
@@ -0,0 +1,20 @@
1
+ import { LocaleList } from "../types/config.cjs";
2
+
3
+ //#region src/proxy/negotiate.d.ts
4
+ /**
5
+ * A function to parse an `Accept-Language` header into locale tags.
6
+ * @param header The raw header value, or `null` when the request has none.
7
+ * @returns The tags ordered by q-value, highest first, with `*` and anything
8
+ * explicitly refused (`q=0`) removed.
9
+ */
10
+ declare function parseAcceptLanguage(header: string | null): string[];
11
+ /**
12
+ * A function to pick the best supported locale for an `Accept-Language` header.
13
+ * @param header The raw header value, or `null` when the request has none.
14
+ * @param locales A list of supported locales to match against.
15
+ * @returns The best matching locale, or `undefined` when none match.
16
+ */
17
+ declare function matchLocale<TLocales extends LocaleList>(header: string | null, locales: TLocales): TLocales[number] | undefined;
18
+ //#endregion
19
+ export { matchLocale, parseAcceptLanguage };
20
+ //# sourceMappingURL=negotiate.d.cts.map
@@ -0,0 +1,20 @@
1
+ import { LocaleList } from "../types/config.js";
2
+
3
+ //#region src/proxy/negotiate.d.ts
4
+ /**
5
+ * A function to parse an `Accept-Language` header into locale tags.
6
+ * @param header The raw header value, or `null` when the request has none.
7
+ * @returns The tags ordered by q-value, highest first, with `*` and anything
8
+ * explicitly refused (`q=0`) removed.
9
+ */
10
+ declare function parseAcceptLanguage(header: string | null): string[];
11
+ /**
12
+ * A function to pick the best supported locale for an `Accept-Language` header.
13
+ * @param header The raw header value, or `null` when the request has none.
14
+ * @param locales A list of supported locales to match against.
15
+ * @returns The best matching locale, or `undefined` when none match.
16
+ */
17
+ declare function matchLocale<TLocales extends LocaleList>(header: string | null, locales: TLocales): TLocales[number] | undefined;
18
+ //#endregion
19
+ export { matchLocale, parseAcceptLanguage };
20
+ //# sourceMappingURL=negotiate.d.ts.map
@@ -0,0 +1,39 @@
1
+ //#region src/proxy/negotiate.ts
2
+ /**
3
+ * A function to parse an `Accept-Language` header into locale tags.
4
+ * @param header The raw header value, or `null` when the request has none.
5
+ * @returns The tags ordered by q-value, highest first, with `*` and anything
6
+ * explicitly refused (`q=0`) removed.
7
+ */
8
+ function parseAcceptLanguage(header) {
9
+ if (!header) return [];
10
+ return header.split(",").map((part) => {
11
+ const [tag = "", ...params] = part.trim().split(";");
12
+ const weight = params.map((param) => param.trim()).find((param) => param.toLowerCase().startsWith("q="));
13
+ const quality = weight ? Number.parseFloat(weight.slice(2)) : 1;
14
+ return {
15
+ tag: tag.trim(),
16
+ quality: Number.isNaN(quality) ? 0 : quality
17
+ };
18
+ }).filter((entry) => entry.tag && entry.tag !== "*" && entry.quality > 0).sort((a, b) => b.quality - a.quality).map((entry) => entry.tag);
19
+ }
20
+ /**
21
+ * A function to pick the best supported locale for an `Accept-Language` header.
22
+ * @param header The raw header value, or `null` when the request has none.
23
+ * @param locales A list of supported locales to match against.
24
+ * @returns The best matching locale, or `undefined` when none match.
25
+ */
26
+ function matchLocale(header, locales) {
27
+ for (const tag of parseAcceptLanguage(header)) {
28
+ const wanted = tag.toLowerCase();
29
+ const exact = locales.find((locale) => locale.toLowerCase() === wanted);
30
+ if (exact) return exact;
31
+ const primary = wanted.split("-")[0];
32
+ const partial = locales.find((locale) => locale.toLowerCase() === primary);
33
+ if (partial) return partial;
34
+ }
35
+ }
36
+ //#endregion
37
+ export { matchLocale, parseAcceptLanguage };
38
+
39
+ //# sourceMappingURL=negotiate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiate.js","names":[],"sources":["../../src/proxy/negotiate.ts"],"sourcesContent":["import type { LocaleList } from \"../types\";\n\n/**\n * A function to parse an `Accept-Language` header into locale tags.\n * @param header The raw header value, or `null` when the request has none.\n * @returns The tags ordered by q-value, highest first, with `*` and anything\n * explicitly refused (`q=0`) removed.\n */\nexport function parseAcceptLanguage(header: string | null): string[] {\n if (!header) return [];\n\n return header\n .split(\",\")\n .map((part) => {\n const [tag = \"\", ...params] = part.trim().split(\";\");\n\n const weight = params\n .map((param) => param.trim())\n .find((param) => param.toLowerCase().startsWith(\"q=\"));\n const quality = weight ? Number.parseFloat(weight.slice(2)) : 1;\n\n return {\n tag: tag.trim(),\n quality: Number.isNaN(quality) ? 0 : quality,\n };\n })\n .filter((entry) => entry.tag && entry.tag !== \"*\" && entry.quality > 0)\n .sort((a, b) => b.quality - a.quality)\n .map((entry) => entry.tag);\n}\n\n/**\n * A function to pick the best supported locale for an `Accept-Language` header.\n * @param header The raw header value, or `null` when the request has none.\n * @param locales A list of supported locales to match against.\n * @returns The best matching locale, or `undefined` when none match.\n */\nexport function matchLocale<TLocales extends LocaleList>(\n header: string | null,\n locales: TLocales,\n): TLocales[number] | undefined {\n for (const tag of parseAcceptLanguage(header)) {\n const wanted = tag.toLowerCase();\n\n // Each tag is tried exactly, then by its primary subtag, before moving to\n // the next one — so a higher-weighted `en-GB` beats a lower-weighted `da`.\n const exact = locales.find((locale) => locale.toLowerCase() === wanted);\n if (exact) return exact;\n\n const primary = wanted.split(\"-\")[0];\n const partial = locales.find((locale) => locale.toLowerCase() === primary);\n if (partial) return partial;\n }\n\n return undefined;\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,oBAAoB,QAAiC;AACnE,KAAI,CAAC,OAAQ,QAAO,EAAE;AAEtB,QAAO,OACJ,MAAM,IAAI,CACV,KAAK,SAAS;EACb,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,KAAK,MAAM,CAAC,MAAM,IAAI;EAEpD,MAAM,SAAS,OACZ,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,MAAM,UAAU,MAAM,aAAa,CAAC,WAAW,KAAK,CAAC;EACxD,MAAM,UAAU,SAAS,OAAO,WAAW,OAAO,MAAM,EAAE,CAAC,GAAG;AAE9D,SAAO;GACL,KAAK,IAAI,MAAM;GACf,SAAS,OAAO,MAAM,QAAQ,GAAG,IAAI;GACtC;GACD,CACD,QAAQ,UAAU,MAAM,OAAO,MAAM,QAAQ,OAAO,MAAM,UAAU,EAAE,CACtE,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,QAAQ,CACrC,KAAK,UAAU,MAAM,IAAI;;;;;;;;AAS9B,SAAgB,YACd,QACA,SAC8B;AAC9B,MAAK,MAAM,OAAO,oBAAoB,OAAO,EAAE;EAC7C,MAAM,SAAS,IAAI,aAAa;EAIhC,MAAM,QAAQ,QAAQ,MAAM,WAAW,OAAO,aAAa,KAAK,OAAO;AACvE,MAAI,MAAO,QAAO;EAElB,MAAM,UAAU,OAAO,MAAM,IAAI,CAAC;EAClC,MAAM,UAAU,QAAQ,MAAM,WAAW,OAAO,aAAa,KAAK,QAAQ;AAC1E,MAAI,QAAS,QAAO"}
@@ -0,0 +1,35 @@
1
+ const require_translator = require("../utils/translator.cjs");
2
+ //#region src/server/create-server.ts
3
+ /**
4
+ * A function to build the server-side translation helpers.
5
+ * @param config The config returned by `defineI18nConfig`.
6
+ * @param loaders One dynamic `import()` per locale, so only the requested
7
+ * locale's dictionary is ever loaded.
8
+ * @returns `getDictionary` for the raw object and `getTranslations` for a `t`
9
+ * function.
10
+ * @example
11
+ * import "server-only";
12
+ * export const { getDictionary, getTranslations } = createServerI18n(i18n, {
13
+ * da: async () => (await import("@/locales/da")).default,
14
+ * en: async () => (await import("@/locales/en")).default,
15
+ * });
16
+ */
17
+ function createServerI18n(config, loaders) {
18
+ async function getDictionary(locale) {
19
+ const resolved = config.hasLocale(locale) ? locale : config.defaultLocale;
20
+ const load = loaders[resolved];
21
+ if (!load) throw new Error(`createServerI18n: no dictionary loader for locale "${resolved}".`);
22
+ return load();
23
+ }
24
+ async function getTranslations(locale, namespace) {
25
+ return require_translator.createTranslator(await getDictionary(locale), namespace);
26
+ }
27
+ return {
28
+ getDictionary,
29
+ getTranslations
30
+ };
31
+ }
32
+ //#endregion
33
+ exports.createServerI18n = createServerI18n;
34
+
35
+ //# sourceMappingURL=create-server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-server.cjs","names":["createTranslator"],"sources":["../../src/server/create-server.ts"],"sourcesContent":["import { createTranslator } from \"../utils/translator\";\nimport type {\n DictionaryLoaders,\n I18nConfig,\n LocaleList,\n MessageSchema,\n NamespacePath,\n Scoped,\n Translator,\n} from \"../types\";\n\nexport interface ServerI18n<TLocales extends LocaleList, TDictionary> {\n /**\n * Loads the whole dictionary for a locale, for passing to `I18nProvider`.\n */\n getDictionary(locale: TLocales[number]): Promise<MessageSchema<TDictionary>>;\n /**\n * Loads a locale and returns its `t` function, optionally scoped to a namespace.\n */\n getTranslations<\n TNamespace extends NamespacePath<TDictionary> | undefined = undefined,\n >(\n locale: TLocales[number],\n namespace?: TNamespace,\n ): Promise<Translator<Scoped<TDictionary, TNamespace>>>;\n}\n\n/**\n * A function to build the server-side translation helpers.\n * @param config The config returned by `defineI18nConfig`.\n * @param loaders One dynamic `import()` per locale, so only the requested\n * locale's dictionary is ever loaded.\n * @returns `getDictionary` for the raw object and `getTranslations` for a `t`\n * function.\n * @example\n * import \"server-only\";\n * export const { getDictionary, getTranslations } = createServerI18n(i18n, {\n * da: async () => (await import(\"@/locales/da\")).default,\n * en: async () => (await import(\"@/locales/en\")).default,\n * });\n */\nexport function createServerI18n<TLocales extends LocaleList, TDictionary>(\n config: I18nConfig<TLocales>,\n loaders: DictionaryLoaders<TLocales, TDictionary>,\n): ServerI18n<TLocales, TDictionary> {\n async function getDictionary(\n locale: TLocales[number],\n ): Promise<MessageSchema<TDictionary>> {\n const resolved = config.hasLocale(locale) ? locale : config.defaultLocale;\n const load = loaders[resolved];\n\n if (!load) {\n throw new Error(\n `createServerI18n: no dictionary loader for locale \"${resolved}\".`,\n );\n }\n\n return load();\n }\n\n async function getTranslations<\n TNamespace extends NamespacePath<TDictionary> | undefined = undefined,\n >(\n locale: TLocales[number],\n namespace?: TNamespace,\n ): Promise<Translator<Scoped<TDictionary, TNamespace>>> {\n const dictionary = await getDictionary(locale);\n\n return createTranslator<Scoped<TDictionary, TNamespace>>(\n dictionary,\n namespace as string | undefined,\n );\n }\n\n return { getDictionary, getTranslations };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyCA,SAAgB,iBACd,QACA,SACmC;CACnC,eAAe,cACb,QACqC;EACrC,MAAM,WAAW,OAAO,UAAU,OAAO,GAAG,SAAS,OAAO;EAC5D,MAAM,OAAO,QAAQ;AAErB,MAAI,CAAC,KACH,OAAM,IAAI,MACR,sDAAsD,SAAS,IAChE;AAGH,SAAO,MAAM;;CAGf,eAAe,gBAGb,QACA,WACsD;AAGtD,SAAOA,mBAAAA,iBAFY,MAAM,cAAc,OAAO,EAI5C,UACD;;AAGH,QAAO;EAAE;EAAe;EAAiB"}
@@ -0,0 +1,32 @@
1
+ import { MessageSchema, NamespacePath, Scoped, Translator } from "../types/messages.cjs";
2
+ import { DictionaryLoaders, I18nConfig, LocaleList } from "../types/config.cjs";
3
+
4
+ //#region src/server/create-server.d.ts
5
+ interface ServerI18n<TLocales extends LocaleList, TDictionary> {
6
+ /**
7
+ * Loads the whole dictionary for a locale, for passing to `I18nProvider`.
8
+ */
9
+ getDictionary(locale: TLocales[number]): Promise<MessageSchema<TDictionary>>;
10
+ /**
11
+ * Loads a locale and returns its `t` function, optionally scoped to a namespace.
12
+ */
13
+ getTranslations<TNamespace extends NamespacePath<TDictionary> | undefined = undefined>(locale: TLocales[number], namespace?: TNamespace): Promise<Translator<Scoped<TDictionary, TNamespace>>>;
14
+ }
15
+ /**
16
+ * A function to build the server-side translation helpers.
17
+ * @param config The config returned by `defineI18nConfig`.
18
+ * @param loaders One dynamic `import()` per locale, so only the requested
19
+ * locale's dictionary is ever loaded.
20
+ * @returns `getDictionary` for the raw object and `getTranslations` for a `t`
21
+ * function.
22
+ * @example
23
+ * import "server-only";
24
+ * export const { getDictionary, getTranslations } = createServerI18n(i18n, {
25
+ * da: async () => (await import("@/locales/da")).default,
26
+ * en: async () => (await import("@/locales/en")).default,
27
+ * });
28
+ */
29
+ declare function createServerI18n<TLocales extends LocaleList, TDictionary>(config: I18nConfig<TLocales>, loaders: DictionaryLoaders<TLocales, TDictionary>): ServerI18n<TLocales, TDictionary>;
30
+ //#endregion
31
+ export { ServerI18n, createServerI18n };
32
+ //# sourceMappingURL=create-server.d.cts.map
@@ -0,0 +1,32 @@
1
+ import { MessageSchema, NamespacePath, Scoped, Translator } from "../types/messages.js";
2
+ import { DictionaryLoaders, I18nConfig, LocaleList } from "../types/config.js";
3
+
4
+ //#region src/server/create-server.d.ts
5
+ interface ServerI18n<TLocales extends LocaleList, TDictionary> {
6
+ /**
7
+ * Loads the whole dictionary for a locale, for passing to `I18nProvider`.
8
+ */
9
+ getDictionary(locale: TLocales[number]): Promise<MessageSchema<TDictionary>>;
10
+ /**
11
+ * Loads a locale and returns its `t` function, optionally scoped to a namespace.
12
+ */
13
+ getTranslations<TNamespace extends NamespacePath<TDictionary> | undefined = undefined>(locale: TLocales[number], namespace?: TNamespace): Promise<Translator<Scoped<TDictionary, TNamespace>>>;
14
+ }
15
+ /**
16
+ * A function to build the server-side translation helpers.
17
+ * @param config The config returned by `defineI18nConfig`.
18
+ * @param loaders One dynamic `import()` per locale, so only the requested
19
+ * locale's dictionary is ever loaded.
20
+ * @returns `getDictionary` for the raw object and `getTranslations` for a `t`
21
+ * function.
22
+ * @example
23
+ * import "server-only";
24
+ * export const { getDictionary, getTranslations } = createServerI18n(i18n, {
25
+ * da: async () => (await import("@/locales/da")).default,
26
+ * en: async () => (await import("@/locales/en")).default,
27
+ * });
28
+ */
29
+ declare function createServerI18n<TLocales extends LocaleList, TDictionary>(config: I18nConfig<TLocales>, loaders: DictionaryLoaders<TLocales, TDictionary>): ServerI18n<TLocales, TDictionary>;
30
+ //#endregion
31
+ export { ServerI18n, createServerI18n };
32
+ //# sourceMappingURL=create-server.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { createTranslator } from "../utils/translator.js";
2
+ //#region src/server/create-server.ts
3
+ /**
4
+ * A function to build the server-side translation helpers.
5
+ * @param config The config returned by `defineI18nConfig`.
6
+ * @param loaders One dynamic `import()` per locale, so only the requested
7
+ * locale's dictionary is ever loaded.
8
+ * @returns `getDictionary` for the raw object and `getTranslations` for a `t`
9
+ * function.
10
+ * @example
11
+ * import "server-only";
12
+ * export const { getDictionary, getTranslations } = createServerI18n(i18n, {
13
+ * da: async () => (await import("@/locales/da")).default,
14
+ * en: async () => (await import("@/locales/en")).default,
15
+ * });
16
+ */
17
+ function createServerI18n(config, loaders) {
18
+ async function getDictionary(locale) {
19
+ const resolved = config.hasLocale(locale) ? locale : config.defaultLocale;
20
+ const load = loaders[resolved];
21
+ if (!load) throw new Error(`createServerI18n: no dictionary loader for locale "${resolved}".`);
22
+ return load();
23
+ }
24
+ async function getTranslations(locale, namespace) {
25
+ return createTranslator(await getDictionary(locale), namespace);
26
+ }
27
+ return {
28
+ getDictionary,
29
+ getTranslations
30
+ };
31
+ }
32
+ //#endregion
33
+ export { createServerI18n };
34
+
35
+ //# sourceMappingURL=create-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-server.js","names":[],"sources":["../../src/server/create-server.ts"],"sourcesContent":["import { createTranslator } from \"../utils/translator\";\nimport type {\n DictionaryLoaders,\n I18nConfig,\n LocaleList,\n MessageSchema,\n NamespacePath,\n Scoped,\n Translator,\n} from \"../types\";\n\nexport interface ServerI18n<TLocales extends LocaleList, TDictionary> {\n /**\n * Loads the whole dictionary for a locale, for passing to `I18nProvider`.\n */\n getDictionary(locale: TLocales[number]): Promise<MessageSchema<TDictionary>>;\n /**\n * Loads a locale and returns its `t` function, optionally scoped to a namespace.\n */\n getTranslations<\n TNamespace extends NamespacePath<TDictionary> | undefined = undefined,\n >(\n locale: TLocales[number],\n namespace?: TNamespace,\n ): Promise<Translator<Scoped<TDictionary, TNamespace>>>;\n}\n\n/**\n * A function to build the server-side translation helpers.\n * @param config The config returned by `defineI18nConfig`.\n * @param loaders One dynamic `import()` per locale, so only the requested\n * locale's dictionary is ever loaded.\n * @returns `getDictionary` for the raw object and `getTranslations` for a `t`\n * function.\n * @example\n * import \"server-only\";\n * export const { getDictionary, getTranslations } = createServerI18n(i18n, {\n * da: async () => (await import(\"@/locales/da\")).default,\n * en: async () => (await import(\"@/locales/en\")).default,\n * });\n */\nexport function createServerI18n<TLocales extends LocaleList, TDictionary>(\n config: I18nConfig<TLocales>,\n loaders: DictionaryLoaders<TLocales, TDictionary>,\n): ServerI18n<TLocales, TDictionary> {\n async function getDictionary(\n locale: TLocales[number],\n ): Promise<MessageSchema<TDictionary>> {\n const resolved = config.hasLocale(locale) ? locale : config.defaultLocale;\n const load = loaders[resolved];\n\n if (!load) {\n throw new Error(\n `createServerI18n: no dictionary loader for locale \"${resolved}\".`,\n );\n }\n\n return load();\n }\n\n async function getTranslations<\n TNamespace extends NamespacePath<TDictionary> | undefined = undefined,\n >(\n locale: TLocales[number],\n namespace?: TNamespace,\n ): Promise<Translator<Scoped<TDictionary, TNamespace>>> {\n const dictionary = await getDictionary(locale);\n\n return createTranslator<Scoped<TDictionary, TNamespace>>(\n dictionary,\n namespace as string | undefined,\n );\n }\n\n return { getDictionary, getTranslations };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyCA,SAAgB,iBACd,QACA,SACmC;CACnC,eAAe,cACb,QACqC;EACrC,MAAM,WAAW,OAAO,UAAU,OAAO,GAAG,SAAS,OAAO;EAC5D,MAAM,OAAO,QAAQ;AAErB,MAAI,CAAC,KACH,OAAM,IAAI,MACR,sDAAsD,SAAS,IAChE;AAGH,SAAO,MAAM;;CAGf,eAAe,gBAGb,QACA,WACsD;AAGtD,SAAO,iBAFY,MAAM,cAAc,OAAO,EAI5C,UACD;;AAGH,QAAO;EAAE;EAAe;EAAiB"}
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_create_server = require("./create-server.cjs");
3
+ exports.createServerI18n = require_create_server.createServerI18n;
@@ -0,0 +1,2 @@
1
+ import { ServerI18n, createServerI18n } from "./create-server.cjs";
2
+ export { type ServerI18n, createServerI18n };
@@ -0,0 +1,2 @@
1
+ import { ServerI18n, createServerI18n } from "./create-server.js";
2
+ export { type ServerI18n, createServerI18n };
@@ -0,0 +1,2 @@
1
+ import { createServerI18n } from "./create-server.js";
2
+ export { createServerI18n };
@@ -0,0 +1,76 @@
1
+ import { MessageSchema } from "./messages.cjs";
2
+
3
+ //#region src/types/config.d.ts
4
+ /**
5
+ * The list of locale codes an app supports.
6
+ */
7
+ type LocaleList = readonly [string, ...string[]];
8
+ /**
9
+ * The options passed to `defineI18nConfig`.
10
+ */
11
+ interface I18nOptions<TLocales extends LocaleList> {
12
+ /**
13
+ * Every locale the app supports.
14
+ */
15
+ locales: TLocales;
16
+ /**
17
+ * The locale to use when none can be resolved from the cookie, path or header.
18
+ */
19
+ defaultLocale: TLocales[number];
20
+ /**
21
+ * The cookie the visitor's chosen locale is persisted in.
22
+ * @default "NEXT_LOCALE"
23
+ */
24
+ cookieName?: string;
25
+ }
26
+ /**
27
+ * The shared config returned by `defineI18nConfig`, which every other factory
28
+ * in the package takes as its first argument.
29
+ */
30
+ interface I18nConfig<TLocales extends LocaleList = LocaleList> {
31
+ /**
32
+ * Every locale the app supports.
33
+ */
34
+ readonly locales: TLocales;
35
+ /**
36
+ * The locale to fall back to when none can be resolved.
37
+ */
38
+ readonly defaultLocale: TLocales[number];
39
+ /**
40
+ * The cookie the visitor's chosen locale is persisted in.
41
+ */
42
+ readonly cookieName: string;
43
+ /**
44
+ * Checks whether an untrusted route param, cookie or header is a supported locale.
45
+ */
46
+ hasLocale(value: unknown): value is TLocales[number];
47
+ /**
48
+ * Prefixes a path with a locale, for building links and redirect targets.
49
+ */
50
+ localizePath(barePath: string, locale: TLocales[number]): string;
51
+ /**
52
+ * Removes the locale prefix from a path, for finding the same page in another locale.
53
+ */
54
+ stripLocale(pathname: string): string;
55
+ /**
56
+ * One route param per locale, for a `[locale]` segment's `generateStaticParams`.
57
+ */
58
+ staticParams(): {
59
+ locale: TLocales[number];
60
+ }[];
61
+ }
62
+ /**
63
+ * Reads the locale union out of a config, for typing your own helpers.
64
+ */
65
+ type LocaleOf<TConfig> = TConfig extends I18nConfig<infer TLocales> ? TLocales[number] : never;
66
+ /**
67
+ * The per-locale dictionary loaders passed to `createServerI18n`.
68
+ *
69
+ * Every loader returns the widened schema rather than `TDictionary` itself: the
70
+ * default locale's dictionary holds its own literal messages, which no other
71
+ * locale could ever be assignable to.
72
+ */
73
+ type DictionaryLoaders<TLocales extends LocaleList, TDictionary> = Record<TLocales[number], () => Promise<MessageSchema<TDictionary>>>;
74
+ //#endregion
75
+ export { DictionaryLoaders, I18nConfig, I18nOptions, LocaleList, LocaleOf };
76
+ //# sourceMappingURL=config.d.cts.map
@@ -0,0 +1,76 @@
1
+ import { MessageSchema } from "./messages.js";
2
+
3
+ //#region src/types/config.d.ts
4
+ /**
5
+ * The list of locale codes an app supports.
6
+ */
7
+ type LocaleList = readonly [string, ...string[]];
8
+ /**
9
+ * The options passed to `defineI18nConfig`.
10
+ */
11
+ interface I18nOptions<TLocales extends LocaleList> {
12
+ /**
13
+ * Every locale the app supports.
14
+ */
15
+ locales: TLocales;
16
+ /**
17
+ * The locale to use when none can be resolved from the cookie, path or header.
18
+ */
19
+ defaultLocale: TLocales[number];
20
+ /**
21
+ * The cookie the visitor's chosen locale is persisted in.
22
+ * @default "NEXT_LOCALE"
23
+ */
24
+ cookieName?: string;
25
+ }
26
+ /**
27
+ * The shared config returned by `defineI18nConfig`, which every other factory
28
+ * in the package takes as its first argument.
29
+ */
30
+ interface I18nConfig<TLocales extends LocaleList = LocaleList> {
31
+ /**
32
+ * Every locale the app supports.
33
+ */
34
+ readonly locales: TLocales;
35
+ /**
36
+ * The locale to fall back to when none can be resolved.
37
+ */
38
+ readonly defaultLocale: TLocales[number];
39
+ /**
40
+ * The cookie the visitor's chosen locale is persisted in.
41
+ */
42
+ readonly cookieName: string;
43
+ /**
44
+ * Checks whether an untrusted route param, cookie or header is a supported locale.
45
+ */
46
+ hasLocale(value: unknown): value is TLocales[number];
47
+ /**
48
+ * Prefixes a path with a locale, for building links and redirect targets.
49
+ */
50
+ localizePath(barePath: string, locale: TLocales[number]): string;
51
+ /**
52
+ * Removes the locale prefix from a path, for finding the same page in another locale.
53
+ */
54
+ stripLocale(pathname: string): string;
55
+ /**
56
+ * One route param per locale, for a `[locale]` segment's `generateStaticParams`.
57
+ */
58
+ staticParams(): {
59
+ locale: TLocales[number];
60
+ }[];
61
+ }
62
+ /**
63
+ * Reads the locale union out of a config, for typing your own helpers.
64
+ */
65
+ type LocaleOf<TConfig> = TConfig extends I18nConfig<infer TLocales> ? TLocales[number] : never;
66
+ /**
67
+ * The per-locale dictionary loaders passed to `createServerI18n`.
68
+ *
69
+ * Every loader returns the widened schema rather than `TDictionary` itself: the
70
+ * default locale's dictionary holds its own literal messages, which no other
71
+ * locale could ever be assignable to.
72
+ */
73
+ type DictionaryLoaders<TLocales extends LocaleList, TDictionary> = Record<TLocales[number], () => Promise<MessageSchema<TDictionary>>>;
74
+ //#endregion
75
+ export { DictionaryLoaders, I18nConfig, I18nOptions, LocaleList, LocaleOf };
76
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1,63 @@
1
+ //#region src/types/messages.d.ts
2
+ /**
3
+ * The value types that end a key path — everything else is a nested namespace.
4
+ */
5
+ type Leaf = string | readonly string[];
6
+ /**
7
+ * The dot-separated keys of a dictionary that hold a message, e.g. `"home.title"`,
8
+ * used to type the key `t` takes. A key naming a nested object is excluded.
9
+ */
10
+ type MessagePath<TDictionary> = TDictionary extends object ? { [K in keyof TDictionary & string]: TDictionary[K] extends Leaf ? K : `${K}.${MessagePath<TDictionary[K]>}` }[keyof TDictionary & string] : never;
11
+ /**
12
+ * The dot-separated keys of the nested objects in a dictionary, e.g. `"home"`,
13
+ * used to type the namespace `getTranslations` and `useTranslations` take.
14
+ */
15
+ type NamespacePath<TDictionary> = TDictionary extends object ? { [K in keyof TDictionary & string]: TDictionary[K] extends Leaf ? never : K | `${K}.${NamespacePath<TDictionary[K]>}` }[keyof TDictionary & string] : never;
16
+ /**
17
+ * The value stored at a dot path, used to type what `t` returns and which
18
+ * interpolation params it demands. Resolves a namespace to its sub-dictionary
19
+ * as well as a message key to its string.
20
+ */
21
+ type MessageAt<TDictionary, TPath extends string> = TPath extends `${infer Head}.${infer Rest}` ? Head extends keyof TDictionary ? MessageAt<TDictionary[Head], Rest> : never : TPath extends keyof TDictionary ? TDictionary[TPath] : never;
22
+ /**
23
+ * Collapses an intersection chain into a single object type, so a message's
24
+ * params read as one object.
25
+ */
26
+ type Flatten<T> = { [K in keyof T]: T[K] };
27
+ /**
28
+ * The interpolation params of a message, collected one `{placeholder}` at a time.
29
+ */
30
+ type ExtractArgs<TMessage> = TMessage extends `${string}{${infer Name}}${infer Rest}` ? Record<Name, string | number> & ExtractArgs<Rest> : {};
31
+ /**
32
+ * The interpolation params a message needs, derived from its `{placeholder}`
33
+ * tokens. A message with no placeholders needs none.
34
+ */
35
+ type MessageArgs<TMessage> = Flatten<ExtractArgs<TMessage>>;
36
+ /**
37
+ * The rest params `t` takes for a given message: none when the message has no
38
+ * placeholders, one required params object when it does.
39
+ */
40
+ type TranslatorArgs<TMessage> = [keyof MessageArgs<TMessage>] extends [never] ? [] : [args: MessageArgs<TMessage>];
41
+ /**
42
+ * The value `t` returns for a given message, widened off the literal held in
43
+ * the default locale's dictionary.
44
+ */
45
+ type MessageValue<TMessage> = TMessage extends readonly string[] ? readonly string[] : string;
46
+ /**
47
+ * The `t` function returned by `getTranslations` and `useTranslations`.
48
+ */
49
+ type Translator<TDictionary> = <TPath extends MessagePath<TDictionary>>(key: TPath, ...args: TranslatorArgs<MessageAt<TDictionary, TPath>>) => MessageValue<MessageAt<TDictionary, TPath>>;
50
+ /**
51
+ * The dictionary a `t` function reads from, narrowed to a namespace's
52
+ * sub-dictionary when one is given and the whole dictionary when it is not.
53
+ */
54
+ type Scoped<TDictionary, TNamespace> = TNamespace extends string ? MessageAt<TDictionary, TNamespace> : TDictionary;
55
+ /**
56
+ * The shape every dictionary must match, with each message widened to `string`.
57
+ * Non-default locales pair it with `satisfies` so a missing or stray key is a
58
+ * compile error.
59
+ */
60
+ type MessageSchema<TDictionary> = { [K in keyof TDictionary]: TDictionary[K] extends string ? string : TDictionary[K] extends readonly string[] ? readonly string[] : MessageSchema<TDictionary[K]> };
61
+ //#endregion
62
+ export { MessageArgs, MessageAt, MessagePath, MessageSchema, NamespacePath, Scoped, Translator };
63
+ //# sourceMappingURL=messages.d.cts.map