@arch-cadre/intl 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +29 -0
  2. package/dist/client/index.cjs +48 -0
  3. package/dist/client/index.d.cts +35 -0
  4. package/dist/client/index.d.cts.map +1 -0
  5. package/dist/client/index.d.mts +35 -0
  6. package/dist/client/index.d.mts.map +1 -0
  7. package/dist/client/index.mjs +44 -0
  8. package/dist/client/index.mjs.map +1 -0
  9. package/dist/index.cjs +14 -0
  10. package/dist/index.d.cts +5 -0
  11. package/dist/index.d.mts +5 -0
  12. package/dist/index.mjs +5 -0
  13. package/dist/server/index.cjs +95 -0
  14. package/dist/server/index.d.cts +27 -0
  15. package/dist/server/index.d.cts.map +1 -0
  16. package/dist/server/index.d.mts +27 -0
  17. package/dist/server/index.d.mts.map +1 -0
  18. package/dist/server/index.mjs +84 -0
  19. package/dist/server/index.mjs.map +1 -0
  20. package/dist/server/module-loader.cjs +75 -0
  21. package/dist/server/module-loader.d.cts +9 -0
  22. package/dist/server/module-loader.d.cts.map +1 -0
  23. package/dist/server/module-loader.d.mts +11 -0
  24. package/dist/server/module-loader.d.mts.map +1 -0
  25. package/dist/server/module-loader.mjs +72 -0
  26. package/dist/server/module-loader.mjs.map +1 -0
  27. package/dist/shared/constants.cjs +10 -0
  28. package/dist/shared/constants.d.cts +7 -0
  29. package/dist/shared/constants.d.cts.map +1 -0
  30. package/dist/shared/constants.d.mts +7 -0
  31. package/dist/shared/constants.d.mts.map +1 -0
  32. package/dist/shared/constants.mjs +8 -0
  33. package/dist/shared/constants.mjs.map +1 -0
  34. package/dist/shared/utils.cjs +34 -0
  35. package/dist/shared/utils.d.cts +8 -0
  36. package/dist/shared/utils.d.cts.map +1 -0
  37. package/dist/shared/utils.d.mts +8 -0
  38. package/dist/shared/utils.d.mts.map +1 -0
  39. package/dist/shared/utils.mjs +33 -0
  40. package/dist/shared/utils.mjs.map +1 -0
  41. package/dist/types.d.cts +15 -0
  42. package/dist/types.d.cts.map +1 -0
  43. package/dist/types.d.mts +15 -0
  44. package/dist/types.d.mts.map +1 -0
  45. package/package.json +51 -0
@@ -0,0 +1,29 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+
29
+ exports.__toESM = __toESM;
@@ -0,0 +1,48 @@
1
+ "use client";
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
5
+ const require_utils = require('../shared/utils.cjs');
6
+ let react = require("react");
7
+ react = require_runtime.__toESM(react);
8
+
9
+ //#region src/client/index.ts
10
+ const CONTEXT_SYMBOL = Symbol.for("kryo-intl-context");
11
+ const IntlContext = globalThis[CONTEXT_SYMBOL] || (0, react.createContext)(null);
12
+ if (!globalThis[CONTEXT_SYMBOL]) globalThis[CONTEXT_SYMBOL] = IntlContext;
13
+ function I18nProvider({ children, locale, messages }) {
14
+ return react.createElement(IntlContext.Provider, { value: {
15
+ locale,
16
+ messages
17
+ } }, children);
18
+ }
19
+ function useLocale() {
20
+ const context = (0, react.useContext)(IntlContext);
21
+ if (!context) throw new Error("useLocale must be used within I18nProvider");
22
+ return context.locale;
23
+ }
24
+ /**
25
+ * Client-side hook for translations with IntelliSense support.
26
+ */
27
+ function useTranslation(namespace) {
28
+ const context = (0, react.useContext)(IntlContext);
29
+ if (!context) throw new Error("useTranslation must be used within I18nProvider");
30
+ const { messages, locale } = context;
31
+ const t = (key, values) => {
32
+ const fullKey = namespace ? `${String(namespace)}.${String(key)}` : String(key);
33
+ const message = require_utils.getNestedMessage(messages, fullKey);
34
+ if (typeof message !== "string") return String(fullKey);
35
+ return require_utils.formatMessage(message, values);
36
+ };
37
+ return {
38
+ t,
39
+ locale
40
+ };
41
+ }
42
+ const useTranslations = useTranslation;
43
+
44
+ //#endregion
45
+ exports.I18nProvider = I18nProvider;
46
+ exports.useLocale = useLocale;
47
+ exports.useTranslation = useTranslation;
48
+ exports.useTranslations = useTranslations;
@@ -0,0 +1,35 @@
1
+ import { AbstractIntlMessages, TranslationKeys } from "../types.cjs";
2
+ import * as React$1 from "react";
3
+
4
+ //#region src/client/index.d.ts
5
+ declare function I18nProvider({
6
+ children,
7
+ locale,
8
+ messages
9
+ }: {
10
+ children: React$1.ReactNode;
11
+ locale: string;
12
+ messages: AbstractIntlMessages;
13
+ }): React$1.CElement<{
14
+ value: {
15
+ locale: string;
16
+ messages: AbstractIntlMessages;
17
+ };
18
+ }, React$1.Component<{
19
+ value: {
20
+ locale: string;
21
+ messages: AbstractIntlMessages;
22
+ };
23
+ }, any, any>>;
24
+ declare function useLocale(): string;
25
+ /**
26
+ * Client-side hook for translations with IntelliSense support.
27
+ */
28
+ declare function useTranslation<N extends keyof KryoCustomMessages | undefined = undefined>(namespace?: N): {
29
+ t: (key: TranslationKeys<N>, values?: Record<string, any>) => string;
30
+ locale: string;
31
+ };
32
+ declare const useTranslations: typeof useTranslation;
33
+ //#endregion
34
+ export { I18nProvider, useLocale, useTranslation, useTranslations };
35
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/client/index.ts"],"mappings":";;;;iBA2BgB,YAAA,CAAA;EACd,QAAA;EACA,MAAA;EACA;AAAA;EAEA,QAAA,EAAU,OAAA,CAAM,SAAA;EAChB,MAAA;EACA,QAAA,EAAU,oBAAA;AAAA,IACX,OAAA,CAAA,QAAA;;;;;;;;;;;iBAQe,SAAA,CAAA;;;;iBAWA,cAAA,iBACE,kBAAA,yBAAA,CAChB,SAAA,GAAY,CAAA;WAQI,eAAA,CAAgB,CAAA,GAAE,MAAA,GAAW,MAAA;;;cAgBlC,eAAA,SAAe,cAAA"}
@@ -0,0 +1,35 @@
1
+ import { AbstractIntlMessages, TranslationKeys } from "../types.mjs";
2
+ import * as React$1 from "react";
3
+
4
+ //#region src/client/index.d.ts
5
+ declare function I18nProvider({
6
+ children,
7
+ locale,
8
+ messages
9
+ }: {
10
+ children: React$1.ReactNode;
11
+ locale: string;
12
+ messages: AbstractIntlMessages;
13
+ }): React$1.CElement<{
14
+ value: {
15
+ locale: string;
16
+ messages: AbstractIntlMessages;
17
+ };
18
+ }, React$1.Component<{
19
+ value: {
20
+ locale: string;
21
+ messages: AbstractIntlMessages;
22
+ };
23
+ }, any, any>>;
24
+ declare function useLocale(): string;
25
+ /**
26
+ * Client-side hook for translations with IntelliSense support.
27
+ */
28
+ declare function useTranslation<N extends keyof KryoCustomMessages | undefined = undefined>(namespace?: N): {
29
+ t: (key: TranslationKeys<N>, values?: Record<string, any>) => string;
30
+ locale: string;
31
+ };
32
+ declare const useTranslations: typeof useTranslation;
33
+ //#endregion
34
+ export { I18nProvider, useLocale, useTranslation, useTranslations };
35
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/client/index.ts"],"mappings":";;;;iBA2BgB,YAAA,CAAA;EACd,QAAA;EACA,MAAA;EACA;AAAA;EAEA,QAAA,EAAU,OAAA,CAAM,SAAA;EAChB,MAAA;EACA,QAAA,EAAU,oBAAA;AAAA,IACX,OAAA,CAAA,QAAA;;;;;;;;;;;iBAQe,SAAA,CAAA;;;;iBAWA,cAAA,iBACE,kBAAA,yBAAA,CAChB,SAAA,GAAY,CAAA;WAQI,eAAA,CAAgB,CAAA,GAAE,MAAA,GAAW,MAAA;;;cAgBlC,eAAA,SAAe,cAAA"}
@@ -0,0 +1,44 @@
1
+ "use client";
2
+
3
+ import { formatMessage, getNestedMessage } from "../shared/utils.mjs";
4
+ import * as React$1 from "react";
5
+ import { createContext, useContext } from "react";
6
+
7
+ //#region src/client/index.ts
8
+ const CONTEXT_SYMBOL = Symbol.for("kryo-intl-context");
9
+ const IntlContext = globalThis[CONTEXT_SYMBOL] || createContext(null);
10
+ if (!globalThis[CONTEXT_SYMBOL]) globalThis[CONTEXT_SYMBOL] = IntlContext;
11
+ function I18nProvider({ children, locale, messages }) {
12
+ return React$1.createElement(IntlContext.Provider, { value: {
13
+ locale,
14
+ messages
15
+ } }, children);
16
+ }
17
+ function useLocale() {
18
+ const context = useContext(IntlContext);
19
+ if (!context) throw new Error("useLocale must be used within I18nProvider");
20
+ return context.locale;
21
+ }
22
+ /**
23
+ * Client-side hook for translations with IntelliSense support.
24
+ */
25
+ function useTranslation(namespace) {
26
+ const context = useContext(IntlContext);
27
+ if (!context) throw new Error("useTranslation must be used within I18nProvider");
28
+ const { messages, locale } = context;
29
+ const t = (key, values) => {
30
+ const fullKey = namespace ? `${String(namespace)}.${String(key)}` : String(key);
31
+ const message = getNestedMessage(messages, fullKey);
32
+ if (typeof message !== "string") return String(fullKey);
33
+ return formatMessage(message, values);
34
+ };
35
+ return {
36
+ t,
37
+ locale
38
+ };
39
+ }
40
+ const useTranslations = useTranslation;
41
+
42
+ //#endregion
43
+ export { I18nProvider, useLocale, useTranslation, useTranslations };
44
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["React"],"sources":["../../src/client/index.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createContext, useContext } from \"react\";\n\nimport type {\n AbstractIntlMessages,\n // @ts-expect-error\n KryoCustomMessages,\n TranslationKeys,\n} from \"../types\";\nimport { getNestedMessage, formatMessage } from \"../shared/utils\";\n\ntype ContextType = {\n locale: string;\n messages: AbstractIntlMessages;\n};\n\nconst CONTEXT_SYMBOL = Symbol.for(\"kryo-intl-context\");\nconst IntlContext =\n (globalThis as any)[CONTEXT_SYMBOL] ||\n createContext<ContextType | null>(null);\n\nif (!(globalThis as any)[CONTEXT_SYMBOL]) {\n (globalThis as any)[CONTEXT_SYMBOL] = IntlContext;\n}\n\nexport function I18nProvider({\n children,\n locale,\n messages,\n}: {\n children: React.ReactNode;\n locale: string;\n messages: AbstractIntlMessages;\n}) {\n return React.createElement(\n IntlContext.Provider,\n { value: { locale, messages } },\n children,\n );\n}\n\nexport function useLocale() {\n const context = useContext(IntlContext) as ContextType | null;\n if (!context) {\n throw new Error(\"useLocale must be used within I18nProvider\");\n }\n return context.locale;\n}\n\n/**\n * Client-side hook for translations with IntelliSense support.\n */\nexport function useTranslation<\n N extends keyof KryoCustomMessages | undefined = undefined,\n>(namespace?: N) {\n const context = useContext(IntlContext) as ContextType | null;\n if (!context) {\n throw new Error(\"useTranslation must be used within I18nProvider\");\n }\n\n const { messages, locale } = context;\n\n const t = (key: TranslationKeys<N>, values?: Record<string, any>) => {\n const fullKey = namespace\n ? `${String(namespace)}.${String(key)}`\n : String(key);\n const message = getNestedMessage(messages, fullKey);\n\n if (typeof message !== \"string\") {\n return String(fullKey);\n }\n\n return formatMessage(message, values);\n };\n\n return { t, locale };\n}\n\nexport const useTranslations = useTranslation;\n"],"mappings":";;;;;;;AAkBA,MAAM,iBAAiB,OAAO,IAAI,oBAAoB;AACtD,MAAM,cACH,WAAmB,mBACpB,cAAkC,KAAK;AAEzC,IAAI,CAAE,WAAmB,gBACvB,CAAC,WAAmB,kBAAkB;AAGxC,SAAgB,aAAa,EAC3B,UACA,QACA,YAKC;AACD,QAAOA,QAAM,cACX,YAAY,UACZ,EAAE,OAAO;EAAE;EAAQ;EAAU,EAAE,EAC/B,SACD;;AAGH,SAAgB,YAAY;CAC1B,MAAM,UAAU,WAAW,YAAY;AACvC,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,6CAA6C;AAE/D,QAAO,QAAQ;;;;;AAMjB,SAAgB,eAEd,WAAe;CACf,MAAM,UAAU,WAAW,YAAY;AACvC,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,kDAAkD;CAGpE,MAAM,EAAE,UAAU,WAAW;CAE7B,MAAM,KAAK,KAAyB,WAAiC;EACnE,MAAM,UAAU,YACZ,GAAG,OAAO,UAAU,CAAC,GAAG,OAAO,IAAI,KACnC,OAAO,IAAI;EACf,MAAM,UAAU,iBAAiB,UAAU,QAAQ;AAEnD,MAAI,OAAO,YAAY,SACrB,QAAO,OAAO,QAAQ;AAGxB,SAAO,cAAc,SAAS,OAAO;;AAGvC,QAAO;EAAE;EAAG;EAAQ;;AAGtB,MAAa,kBAAkB"}
package/dist/index.cjs ADDED
@@ -0,0 +1,14 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_utils = require('./shared/utils.cjs');
3
+ const require_constants = require('./shared/constants.cjs');
4
+ const require_client_index = require('./client/index.cjs');
5
+
6
+ exports.COOKIE_NAME = require_constants.COOKIE_NAME;
7
+ exports.DEFAULT_LOCALE = require_constants.DEFAULT_LOCALE;
8
+ exports.I18nProvider = require_client_index.I18nProvider;
9
+ exports.LOCALES = require_constants.LOCALES;
10
+ exports.formatMessage = require_utils.formatMessage;
11
+ exports.getNestedMessage = require_utils.getNestedMessage;
12
+ exports.useLocale = require_client_index.useLocale;
13
+ exports.useTranslation = require_client_index.useTranslation;
14
+ exports.useTranslations = require_client_index.useTranslations;
@@ -0,0 +1,5 @@
1
+ import { AbstractIntlMessages, NestedKeyOf, TranslationKeys } from "./types.cjs";
2
+ import { I18nProvider, useLocale, useTranslation, useTranslations } from "./client/index.cjs";
3
+ import { formatMessage, getNestedMessage } from "./shared/utils.cjs";
4
+ import { COOKIE_NAME, DEFAULT_LOCALE, LOCALES } from "./shared/constants.cjs";
5
+ export { AbstractIntlMessages, COOKIE_NAME, DEFAULT_LOCALE, I18nProvider, LOCALES, NestedKeyOf, TranslationKeys, formatMessage, getNestedMessage, useLocale, useTranslation, useTranslations };
@@ -0,0 +1,5 @@
1
+ import { AbstractIntlMessages, NestedKeyOf, TranslationKeys } from "./types.mjs";
2
+ import { I18nProvider, useLocale, useTranslation, useTranslations } from "./client/index.mjs";
3
+ import { formatMessage, getNestedMessage } from "./shared/utils.mjs";
4
+ import { COOKIE_NAME, DEFAULT_LOCALE, LOCALES } from "./shared/constants.mjs";
5
+ export { AbstractIntlMessages, COOKIE_NAME, DEFAULT_LOCALE, I18nProvider, LOCALES, NestedKeyOf, TranslationKeys, formatMessage, getNestedMessage, useLocale, useTranslation, useTranslations };
package/dist/index.mjs ADDED
@@ -0,0 +1,5 @@
1
+ import { formatMessage, getNestedMessage } from "./shared/utils.mjs";
2
+ import { COOKIE_NAME, DEFAULT_LOCALE, LOCALES } from "./shared/constants.mjs";
3
+ import { I18nProvider, useLocale, useTranslation, useTranslations } from "./client/index.mjs";
4
+
5
+ export { COOKIE_NAME, DEFAULT_LOCALE, I18nProvider, LOCALES, formatMessage, getNestedMessage, useLocale, useTranslation, useTranslations };
@@ -0,0 +1,95 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
+ const require_utils = require('../shared/utils.cjs');
4
+ const require_constants = require('../shared/constants.cjs');
5
+ const require_client_index = require('../client/index.cjs');
6
+ const require_module_loader = require('./module-loader.cjs');
7
+ let react = require("react");
8
+ react = require_runtime.__toESM(react);
9
+ let _formatjs_intl_localematcher = require("@formatjs/intl-localematcher");
10
+ let negotiator = require("negotiator");
11
+ negotiator = require_runtime.__toESM(negotiator);
12
+ let node_fs_promises = require("node:fs/promises");
13
+ node_fs_promises = require_runtime.__toESM(node_fs_promises);
14
+ let node_path = require("node:path");
15
+ node_path = require_runtime.__toESM(node_path);
16
+ let next_headers = require("next/headers");
17
+
18
+ //#region src/server/index.ts
19
+ function deepMerge(target, source) {
20
+ const result = { ...target };
21
+ for (const key of Object.keys(source)) if (source[key] instanceof Object && key in target && target[key] instanceof Object) result[key] = deepMerge(target[key], source[key]);
22
+ else result[key] = source[key];
23
+ return result;
24
+ }
25
+ let registeredPaths = null;
26
+ /**
27
+ * Register additional directories to search for locales.
28
+ * Can be called multiple times, but usually called once in RootLayout.
29
+ */
30
+ function registerLocalePaths(paths) {
31
+ registeredPaths = Array.from(new Set([...registeredPaths || [], ...paths]));
32
+ }
33
+ async function getMessages(locale, searchDirectories) {
34
+ if (registeredPaths === null || registeredPaths.length === 0) registerLocalePaths(await require_module_loader.discoverLocalePaths());
35
+ let messages = {};
36
+ const defaultDir = node_path.default.join(process.cwd(), "locales");
37
+ const directoriesToScan = /* @__PURE__ */ new Set();
38
+ if (registeredPaths) for (const p of registeredPaths) directoriesToScan.add(p);
39
+ if (searchDirectories) for (const p of searchDirectories) directoriesToScan.add(p);
40
+ directoriesToScan.add(defaultDir);
41
+ for (const dir of Array.from(directoriesToScan)) {
42
+ const localeDir = node_path.default.join(dir, locale);
43
+ try {
44
+ await node_fs_promises.default.access(localeDir);
45
+ const files = await node_fs_promises.default.readdir(localeDir);
46
+ for (const file of files) if (file.endsWith(".json")) {
47
+ const namespace = file.replace(".json", "");
48
+ const filePath = node_path.default.join(localeDir, file);
49
+ const fileContent = await node_fs_promises.default.readFile(filePath, "utf-8");
50
+ const jsonContent = JSON.parse(fileContent);
51
+ if (messages[namespace]) messages[namespace] = deepMerge(messages[namespace], jsonContent);
52
+ else messages[namespace] = jsonContent;
53
+ }
54
+ } catch (error) {}
55
+ }
56
+ return messages;
57
+ }
58
+ function detectLocale(supportedLocales, defaultLocale) {
59
+ const headerList = (0, next_headers.headers)();
60
+ return (0, _formatjs_intl_localematcher.match)(new negotiator.default({ headers: Object.fromEntries(headerList.entries()) }).languages(), supportedLocales, defaultLocale);
61
+ }
62
+ /**
63
+ * Smart Server Component that handles locale detection and message loading automatically.
64
+ */
65
+ async function I18nProvider({ children }) {
66
+ const locale = (await (0, next_headers.cookies)()).get(require_constants.COOKIE_NAME)?.value || require_constants.DEFAULT_LOCALE;
67
+ const messages = await getMessages(locale);
68
+ return react.default.createElement(require_client_index.I18nProvider, {
69
+ locale,
70
+ messages
71
+ }, children);
72
+ }
73
+ async function getTranslation(namespace) {
74
+ const locale = (await (0, next_headers.cookies)()).get(require_constants.COOKIE_NAME)?.value || require_constants.DEFAULT_LOCALE;
75
+ const messages = await getMessages(locale);
76
+ const t = (key, values) => {
77
+ const fullKey = namespace ? `${String(namespace)}.${String(key)}` : String(key);
78
+ const message = require_utils.getNestedMessage(messages, fullKey);
79
+ if (typeof message !== "string") return String(fullKey);
80
+ return require_utils.formatMessage(message, values);
81
+ };
82
+ return {
83
+ t,
84
+ locale
85
+ };
86
+ }
87
+
88
+ //#endregion
89
+ exports.I18nProvider = I18nProvider;
90
+ exports.detectLocale = detectLocale;
91
+ exports.discoverLocalePaths = require_module_loader.discoverLocalePaths;
92
+ exports.getMessages = getMessages;
93
+ exports.getModuleLocalePaths = require_module_loader.getModuleLocalePaths;
94
+ exports.getTranslation = getTranslation;
95
+ exports.registerLocalePaths = registerLocalePaths;
@@ -0,0 +1,27 @@
1
+ import { AbstractIntlMessages, TranslationKeys } from "../types.cjs";
2
+ import { discoverLocalePaths, getModuleLocalePaths } from "./module-loader.cjs";
3
+ import React from "react";
4
+
5
+ //#region src/server/index.d.ts
6
+ /**
7
+ * Register additional directories to search for locales.
8
+ * Can be called multiple times, but usually called once in RootLayout.
9
+ */
10
+ declare function registerLocalePaths(paths: string[]): void;
11
+ declare function getMessages(locale: string, searchDirectories?: string[]): Promise<AbstractIntlMessages>;
12
+ declare function detectLocale(supportedLocales: string[], defaultLocale: string): string;
13
+ /**
14
+ * Smart Server Component that handles locale detection and message loading automatically.
15
+ */
16
+ declare function I18nProvider({
17
+ children
18
+ }: {
19
+ children: React.ReactNode;
20
+ }): Promise<React.DetailedReactHTMLElement<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>>;
21
+ declare function getTranslation<N extends keyof KryoCustomMessages | undefined = undefined>(namespace?: N): Promise<{
22
+ t: (key: TranslationKeys<N>, values?: Record<string, any>) => string;
23
+ locale: string;
24
+ }>;
25
+ //#endregion
26
+ export { I18nProvider, detectLocale, discoverLocalePaths, getMessages, getModuleLocalePaths, getTranslation, registerLocalePaths };
27
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/server/index.ts"],"mappings":";;;;;;;;AAwCA;iBAAgB,mBAAA,CAAoB,KAAA;AAAA,iBAId,WAAA,CACpB,MAAA,UACA,iBAAA,cAA4B,OAAA,CAAA,oBAAA;AAAA,iBAmDd,YAAA,CACd,gBAAA,YACA,aAAA;;AAvDF;;iBAoEsB,YAAA,CAAA;EACpB;AAAA;EAEA,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA,IACjB,OAAA,CAAA,KAAA,CAAA,wBAAA,CAAA,KAAA,CAAA,mBAAA,CAAA,gBAAA,GAAA,gBAAA;AAAA,iBAWqB,cAAA,iBACJ,kBAAA,yBAAA,CAChB,SAAA,GAAY,CAAA,GAAC,OAAA;WAQG,eAAA,CAAgB,CAAA,GAAE,MAAA,GAAW,MAAA"}
@@ -0,0 +1,27 @@
1
+ import { AbstractIntlMessages, TranslationKeys } from "../types.mjs";
2
+ import { discoverLocalePaths, getModuleLocalePaths } from "./module-loader.mjs";
3
+ import React from "react";
4
+
5
+ //#region src/server/index.d.ts
6
+ /**
7
+ * Register additional directories to search for locales.
8
+ * Can be called multiple times, but usually called once in RootLayout.
9
+ */
10
+ declare function registerLocalePaths(paths: string[]): void;
11
+ declare function getMessages(locale: string, searchDirectories?: string[]): Promise<AbstractIntlMessages>;
12
+ declare function detectLocale(supportedLocales: string[], defaultLocale: string): string;
13
+ /**
14
+ * Smart Server Component that handles locale detection and message loading automatically.
15
+ */
16
+ declare function I18nProvider({
17
+ children
18
+ }: {
19
+ children: React.ReactNode;
20
+ }): Promise<React.DetailedReactHTMLElement<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>>;
21
+ declare function getTranslation<N extends keyof KryoCustomMessages | undefined = undefined>(namespace?: N): Promise<{
22
+ t: (key: TranslationKeys<N>, values?: Record<string, any>) => string;
23
+ locale: string;
24
+ }>;
25
+ //#endregion
26
+ export { I18nProvider, detectLocale, discoverLocalePaths, getMessages, getModuleLocalePaths, getTranslation, registerLocalePaths };
27
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/server/index.ts"],"mappings":";;;;;;;;AAwCA;iBAAgB,mBAAA,CAAoB,KAAA;AAAA,iBAId,WAAA,CACpB,MAAA,UACA,iBAAA,cAA4B,OAAA,CAAA,oBAAA;AAAA,iBAmDd,YAAA,CACd,gBAAA,YACA,aAAA;;AAvDF;;iBAoEsB,YAAA,CAAA;EACpB;AAAA;EAEA,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA,IACjB,OAAA,CAAA,KAAA,CAAA,wBAAA,CAAA,KAAA,CAAA,mBAAA,CAAA,gBAAA,GAAA,gBAAA;AAAA,iBAWqB,cAAA,iBACJ,kBAAA,yBAAA,CAChB,SAAA,GAAY,CAAA,GAAC,OAAA;WAQG,eAAA,CAAgB,CAAA,GAAE,MAAA,GAAW,MAAA"}
@@ -0,0 +1,84 @@
1
+ import { formatMessage, getNestedMessage } from "../shared/utils.mjs";
2
+ import { COOKIE_NAME, DEFAULT_LOCALE } from "../shared/constants.mjs";
3
+ import { I18nProvider as I18nProvider$1 } from "../client/index.mjs";
4
+ import { discoverLocalePaths, getModuleLocalePaths } from "./module-loader.mjs";
5
+ import React from "react";
6
+ import { match } from "@formatjs/intl-localematcher";
7
+ import Negotiator from "negotiator";
8
+ import fs from "node:fs/promises";
9
+ import path from "node:path";
10
+ import { cookies, headers } from "next/headers";
11
+
12
+ //#region src/server/index.ts
13
+ function deepMerge(target, source) {
14
+ const result = { ...target };
15
+ for (const key of Object.keys(source)) if (source[key] instanceof Object && key in target && target[key] instanceof Object) result[key] = deepMerge(target[key], source[key]);
16
+ else result[key] = source[key];
17
+ return result;
18
+ }
19
+ let registeredPaths = null;
20
+ /**
21
+ * Register additional directories to search for locales.
22
+ * Can be called multiple times, but usually called once in RootLayout.
23
+ */
24
+ function registerLocalePaths(paths) {
25
+ registeredPaths = Array.from(new Set([...registeredPaths || [], ...paths]));
26
+ }
27
+ async function getMessages(locale, searchDirectories) {
28
+ if (registeredPaths === null || registeredPaths.length === 0) registerLocalePaths(await discoverLocalePaths());
29
+ let messages = {};
30
+ const defaultDir = path.join(process.cwd(), "locales");
31
+ const directoriesToScan = /* @__PURE__ */ new Set();
32
+ if (registeredPaths) for (const p of registeredPaths) directoriesToScan.add(p);
33
+ if (searchDirectories) for (const p of searchDirectories) directoriesToScan.add(p);
34
+ directoriesToScan.add(defaultDir);
35
+ for (const dir of Array.from(directoriesToScan)) {
36
+ const localeDir = path.join(dir, locale);
37
+ try {
38
+ await fs.access(localeDir);
39
+ const files = await fs.readdir(localeDir);
40
+ for (const file of files) if (file.endsWith(".json")) {
41
+ const namespace = file.replace(".json", "");
42
+ const filePath = path.join(localeDir, file);
43
+ const fileContent = await fs.readFile(filePath, "utf-8");
44
+ const jsonContent = JSON.parse(fileContent);
45
+ if (messages[namespace]) messages[namespace] = deepMerge(messages[namespace], jsonContent);
46
+ else messages[namespace] = jsonContent;
47
+ }
48
+ } catch (error) {}
49
+ }
50
+ return messages;
51
+ }
52
+ function detectLocale(supportedLocales, defaultLocale) {
53
+ const headerList = headers();
54
+ return match(new Negotiator({ headers: Object.fromEntries(headerList.entries()) }).languages(), supportedLocales, defaultLocale);
55
+ }
56
+ /**
57
+ * Smart Server Component that handles locale detection and message loading automatically.
58
+ */
59
+ async function I18nProvider({ children }) {
60
+ const locale = (await cookies()).get(COOKIE_NAME)?.value || DEFAULT_LOCALE;
61
+ const messages = await getMessages(locale);
62
+ return React.createElement(I18nProvider$1, {
63
+ locale,
64
+ messages
65
+ }, children);
66
+ }
67
+ async function getTranslation(namespace) {
68
+ const locale = (await cookies()).get(COOKIE_NAME)?.value || DEFAULT_LOCALE;
69
+ const messages = await getMessages(locale);
70
+ const t = (key, values) => {
71
+ const fullKey = namespace ? `${String(namespace)}.${String(key)}` : String(key);
72
+ const message = getNestedMessage(messages, fullKey);
73
+ if (typeof message !== "string") return String(fullKey);
74
+ return formatMessage(message, values);
75
+ };
76
+ return {
77
+ t,
78
+ locale
79
+ };
80
+ }
81
+
82
+ //#endregion
83
+ export { I18nProvider, detectLocale, discoverLocalePaths, getMessages, getModuleLocalePaths, getTranslation, registerLocalePaths };
84
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["ClientProvider"],"sources":["../../src/server/index.ts"],"sourcesContent":["import { match } from \"@formatjs/intl-localematcher\";\nimport Negotiator from \"negotiator\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { cookies, headers } from \"next/headers\";\nimport { COOKIE_NAME, DEFAULT_LOCALE } from \"../shared/constants\";\nimport { formatMessage, getNestedMessage } from \"../shared/utils\";\nimport type {\n AbstractIntlMessages,\n //@ts-expect-error\n KryoCustomMessages,\n TranslationKeys,\n} from \"../types\";\nimport { discoverLocalePaths } from \"./module-loader\";\nimport { I18nProvider as ClientProvider } from \"../client/index\";\nimport React from \"react\";\n\nfunction deepMerge(target: any, source: any) {\n const result = { ...target };\n for (const key of Object.keys(source)) {\n if (\n source[key] instanceof Object &&\n key in target &&\n target[key] instanceof Object\n ) {\n result[key] = deepMerge(target[key], source[key]);\n } else {\n result[key] = source[key];\n }\n }\n return result;\n}\n\n// Internal state for locale paths\nlet registeredPaths: string[] | null = null;\n\n/**\n * Register additional directories to search for locales.\n * Can be called multiple times, but usually called once in RootLayout.\n */\nexport function registerLocalePaths(paths: string[]) {\n registeredPaths = Array.from(new Set([...(registeredPaths || []), ...paths]));\n}\n\nexport async function getMessages(\n locale: string,\n searchDirectories?: string[],\n) {\n // Auto-discovery if no paths registered\n if (registeredPaths === null || registeredPaths.length === 0) {\n const discovered = await discoverLocalePaths();\n registerLocalePaths(discovered);\n }\n\n let messages: AbstractIntlMessages = {};\n\n const defaultDir = path.join(process.cwd(), \"locales\");\n\n // Use provided paths, or registered paths, or just default\n const directoriesToScan = new Set<string>();\n\n if (registeredPaths) {\n for (const p of registeredPaths) directoriesToScan.add(p);\n }\n if (searchDirectories) {\n for (const p of searchDirectories) directoriesToScan.add(p);\n }\n directoriesToScan.add(defaultDir);\n\n for (const dir of Array.from(directoriesToScan)) {\n const localeDir = path.join(dir, locale);\n try {\n await fs.access(localeDir);\n const files = await fs.readdir(localeDir);\n\n for (const file of files) {\n if (file.endsWith(\".json\")) {\n const namespace = file.replace(\".json\", \"\");\n const filePath = path.join(localeDir, file);\n const fileContent = await fs.readFile(filePath, \"utf-8\");\n const jsonContent = JSON.parse(fileContent);\n\n if (messages[namespace]) {\n messages[namespace] = deepMerge(messages[namespace], jsonContent);\n } else {\n messages[namespace] = jsonContent;\n }\n }\n }\n } catch (error) {\n // Ignore missing directories\n }\n }\n\n return messages;\n}\n\nexport function detectLocale(\n supportedLocales: string[],\n defaultLocale: string,\n) {\n const headerList = headers();\n const languages = new Negotiator({\n headers: Object.fromEntries((headerList as any).entries()),\n }).languages();\n\n return match(languages, supportedLocales, defaultLocale);\n}\n\n/**\n * Smart Server Component that handles locale detection and message loading automatically.\n */\nexport async function I18nProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const cookieStore = await cookies();\n const locale = cookieStore.get(COOKIE_NAME)?.value || DEFAULT_LOCALE;\n const messages = await getMessages(locale);\n\n // @ts-ignore\n return React.createElement(ClientProvider, { locale, messages }, children);\n}\n\nexport * from \"./module-loader\";\n\nexport async function getTranslation<\n N extends keyof KryoCustomMessages | undefined = undefined,\n>(namespace?: N) {\n const cookieStore = await cookies();\n const locale = cookieStore.get(COOKIE_NAME)?.value || DEFAULT_LOCALE;\n\n // If paths haven't been registered yet, we might be in a race condition.\n // In a real production app, we might want to have a deterministic way to load these.\n const messages = await getMessages(locale);\n\n const t = (key: TranslationKeys<N>, values?: Record<string, any>) => {\n const fullKey = namespace\n ? `${String(namespace)}.${String(key)}`\n : String(key);\n const message = getNestedMessage(messages, fullKey);\n\n if (typeof message !== \"string\") {\n return String(fullKey);\n }\n\n return formatMessage(message, values);\n };\n\n return { t, locale };\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAS,UAAU,QAAa,QAAa;CAC3C,MAAM,SAAS,EAAE,GAAG,QAAQ;AAC5B,MAAK,MAAM,OAAO,OAAO,KAAK,OAAO,CACnC,KACE,OAAO,gBAAgB,UACvB,OAAO,UACP,OAAO,gBAAgB,OAEvB,QAAO,OAAO,UAAU,OAAO,MAAM,OAAO,KAAK;KAEjD,QAAO,OAAO,OAAO;AAGzB,QAAO;;AAIT,IAAI,kBAAmC;;;;;AAMvC,SAAgB,oBAAoB,OAAiB;AACnD,mBAAkB,MAAM,KAAK,IAAI,IAAI,CAAC,GAAI,mBAAmB,EAAE,EAAG,GAAG,MAAM,CAAC,CAAC;;AAG/E,eAAsB,YACpB,QACA,mBACA;AAEA,KAAI,oBAAoB,QAAQ,gBAAgB,WAAW,EAEzD,qBADmB,MAAM,qBAAqB,CACf;CAGjC,IAAI,WAAiC,EAAE;CAEvC,MAAM,aAAa,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU;CAGtD,MAAM,oCAAoB,IAAI,KAAa;AAE3C,KAAI,gBACF,MAAK,MAAM,KAAK,gBAAiB,mBAAkB,IAAI,EAAE;AAE3D,KAAI,kBACF,MAAK,MAAM,KAAK,kBAAmB,mBAAkB,IAAI,EAAE;AAE7D,mBAAkB,IAAI,WAAW;AAEjC,MAAK,MAAM,OAAO,MAAM,KAAK,kBAAkB,EAAE;EAC/C,MAAM,YAAY,KAAK,KAAK,KAAK,OAAO;AACxC,MAAI;AACF,SAAM,GAAG,OAAO,UAAU;GAC1B,MAAM,QAAQ,MAAM,GAAG,QAAQ,UAAU;AAEzC,QAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,SAAS,QAAQ,EAAE;IAC1B,MAAM,YAAY,KAAK,QAAQ,SAAS,GAAG;IAC3C,MAAM,WAAW,KAAK,KAAK,WAAW,KAAK;IAC3C,MAAM,cAAc,MAAM,GAAG,SAAS,UAAU,QAAQ;IACxD,MAAM,cAAc,KAAK,MAAM,YAAY;AAE3C,QAAI,SAAS,WACX,UAAS,aAAa,UAAU,SAAS,YAAY,YAAY;QAEjE,UAAS,aAAa;;WAIrB,OAAO;;AAKlB,QAAO;;AAGT,SAAgB,aACd,kBACA,eACA;CACA,MAAM,aAAa,SAAS;AAK5B,QAAO,MAJW,IAAI,WAAW,EAC/B,SAAS,OAAO,YAAa,WAAmB,SAAS,CAAC,EAC3D,CAAC,CAAC,WAAW,EAEU,kBAAkB,cAAc;;;;;AAM1D,eAAsB,aAAa,EACjC,YAGC;CAED,MAAM,UADc,MAAM,SAAS,EACR,IAAI,YAAY,EAAE,SAAS;CACtD,MAAM,WAAW,MAAM,YAAY,OAAO;AAG1C,QAAO,MAAM,cAAcA,gBAAgB;EAAE;EAAQ;EAAU,EAAE,SAAS;;AAK5E,eAAsB,eAEpB,WAAe;CAEf,MAAM,UADc,MAAM,SAAS,EACR,IAAI,YAAY,EAAE,SAAS;CAItD,MAAM,WAAW,MAAM,YAAY,OAAO;CAE1C,MAAM,KAAK,KAAyB,WAAiC;EACnE,MAAM,UAAU,YACZ,GAAG,OAAO,UAAU,CAAC,GAAG,OAAO,IAAI,KACnC,OAAO,IAAI;EACf,MAAM,UAAU,iBAAiB,UAAU,QAAQ;AAEnD,MAAI,OAAO,YAAY,SACrB,QAAO,OAAO,QAAQ;AAGxB,SAAO,cAAc,SAAS,OAAO;;AAGvC,QAAO;EAAE;EAAG;EAAQ"}
@@ -0,0 +1,75 @@
1
+ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
2
+ let node_fs_promises = require("node:fs/promises");
3
+ node_fs_promises = require_runtime.__toESM(node_fs_promises);
4
+ let node_path = require("node:path");
5
+ node_path = require_runtime.__toESM(node_path);
6
+ require("server-only");
7
+
8
+ //#region src/server/module-loader.ts
9
+ async function discoverLocalePaths() {
10
+ const paths = [];
11
+ const root = process.cwd();
12
+ const findMonorepoRoot = async (current) => {
13
+ const indicators = [
14
+ "pnpm-workspace.yaml",
15
+ "turbo.json",
16
+ "package.json"
17
+ ];
18
+ let dir = current;
19
+ while (dir !== node_path.default.parse(dir).root) {
20
+ for (const indicator of indicators) try {
21
+ await node_fs_promises.default.access(node_path.default.join(dir, indicator));
22
+ if (indicator === "pnpm-workspace.yaml") return dir;
23
+ } catch {}
24
+ dir = node_path.default.dirname(dir);
25
+ }
26
+ return null;
27
+ };
28
+ const monorepoRoot = await findMonorepoRoot(root);
29
+ if (monorepoRoot) {
30
+ const packagesDir = node_path.default.join(monorepoRoot, "packages");
31
+ try {
32
+ const entries = await node_fs_promises.default.readdir(packagesDir, { withFileTypes: true });
33
+ for (const entry of entries) if (entry.isDirectory()) {
34
+ const localePath = node_path.default.join(packagesDir, entry.name, "locales");
35
+ try {
36
+ await node_fs_promises.default.access(localePath);
37
+ paths.push(localePath);
38
+ } catch {}
39
+ }
40
+ } catch (e) {}
41
+ }
42
+ const nodeModulesKryo = node_path.default.join(root, "node_modules", "@kryo");
43
+ try {
44
+ const entries = await node_fs_promises.default.readdir(nodeModulesKryo, { withFileTypes: true });
45
+ for (const entry of entries) {
46
+ const localePath = node_path.default.join(nodeModulesKryo, entry.name, "locales");
47
+ try {
48
+ await node_fs_promises.default.access(localePath);
49
+ paths.push(localePath);
50
+ } catch {}
51
+ }
52
+ } catch (e) {}
53
+ const localModulesDir = node_path.default.join(root, "modules");
54
+ try {
55
+ const entries = await node_fs_promises.default.readdir(localModulesDir, { withFileTypes: true });
56
+ for (const entry of entries) if (entry.isDirectory()) {
57
+ const localePath = node_path.default.join(localModulesDir, entry.name, "locales");
58
+ try {
59
+ await node_fs_promises.default.access(localePath);
60
+ paths.push(localePath);
61
+ } catch {}
62
+ }
63
+ } catch (e) {}
64
+ return Array.from(new Set(paths));
65
+ }
66
+ /**
67
+ * Legacy support or manual overrides
68
+ */
69
+ async function getModuleLocalePaths(packageModules = [], localModulesDir = "modules") {
70
+ return discoverLocalePaths();
71
+ }
72
+
73
+ //#endregion
74
+ exports.discoverLocalePaths = discoverLocalePaths;
75
+ exports.getModuleLocalePaths = getModuleLocalePaths;
@@ -0,0 +1,9 @@
1
+ //#region src/server/module-loader.d.ts
2
+ declare function discoverLocalePaths(): Promise<string[]>;
3
+ /**
4
+ * Legacy support or manual overrides
5
+ */
6
+ declare function getModuleLocalePaths(packageModules?: string[], localModulesDir?: string): Promise<string[]>;
7
+ //#endregion
8
+ export { discoverLocalePaths, getModuleLocalePaths };
9
+ //# sourceMappingURL=module-loader.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-loader.d.cts","names":[],"sources":["../../src/server/module-loader.ts"],"mappings":";iBAIsB,mBAAA,CAAA,GAAuB,OAAA;;;;iBA0EvB,oBAAA,CACpB,cAAA,aACA,eAAA,YACC,OAAA"}
@@ -0,0 +1,11 @@
1
+ import "server-only";
2
+
3
+ //#region src/server/module-loader.d.ts
4
+ declare function discoverLocalePaths(): Promise<string[]>;
5
+ /**
6
+ * Legacy support or manual overrides
7
+ */
8
+ declare function getModuleLocalePaths(packageModules?: string[], localModulesDir?: string): Promise<string[]>;
9
+ //#endregion
10
+ export { discoverLocalePaths, getModuleLocalePaths };
11
+ //# sourceMappingURL=module-loader.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-loader.d.mts","names":[],"sources":["../../src/server/module-loader.ts"],"mappings":";;;iBAIsB,mBAAA,CAAA,GAAuB,OAAA;;AAA7C;;iBA0EsB,oBAAA,CACpB,cAAA,aACA,eAAA,YACC,OAAA"}
@@ -0,0 +1,72 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import "server-only";
4
+
5
+ //#region src/server/module-loader.ts
6
+ async function discoverLocalePaths() {
7
+ const paths = [];
8
+ const root = process.cwd();
9
+ const findMonorepoRoot = async (current) => {
10
+ const indicators = [
11
+ "pnpm-workspace.yaml",
12
+ "turbo.json",
13
+ "package.json"
14
+ ];
15
+ let dir = current;
16
+ while (dir !== path.parse(dir).root) {
17
+ for (const indicator of indicators) try {
18
+ await fs.access(path.join(dir, indicator));
19
+ if (indicator === "pnpm-workspace.yaml") return dir;
20
+ } catch {}
21
+ dir = path.dirname(dir);
22
+ }
23
+ return null;
24
+ };
25
+ const monorepoRoot = await findMonorepoRoot(root);
26
+ if (monorepoRoot) {
27
+ const packagesDir = path.join(monorepoRoot, "packages");
28
+ try {
29
+ const entries = await fs.readdir(packagesDir, { withFileTypes: true });
30
+ for (const entry of entries) if (entry.isDirectory()) {
31
+ const localePath = path.join(packagesDir, entry.name, "locales");
32
+ try {
33
+ await fs.access(localePath);
34
+ paths.push(localePath);
35
+ } catch {}
36
+ }
37
+ } catch (e) {}
38
+ }
39
+ const nodeModulesKryo = path.join(root, "node_modules", "@kryo");
40
+ try {
41
+ const entries = await fs.readdir(nodeModulesKryo, { withFileTypes: true });
42
+ for (const entry of entries) {
43
+ const localePath = path.join(nodeModulesKryo, entry.name, "locales");
44
+ try {
45
+ await fs.access(localePath);
46
+ paths.push(localePath);
47
+ } catch {}
48
+ }
49
+ } catch (e) {}
50
+ const localModulesDir = path.join(root, "modules");
51
+ try {
52
+ const entries = await fs.readdir(localModulesDir, { withFileTypes: true });
53
+ for (const entry of entries) if (entry.isDirectory()) {
54
+ const localePath = path.join(localModulesDir, entry.name, "locales");
55
+ try {
56
+ await fs.access(localePath);
57
+ paths.push(localePath);
58
+ } catch {}
59
+ }
60
+ } catch (e) {}
61
+ return Array.from(new Set(paths));
62
+ }
63
+ /**
64
+ * Legacy support or manual overrides
65
+ */
66
+ async function getModuleLocalePaths(packageModules = [], localModulesDir = "modules") {
67
+ return discoverLocalePaths();
68
+ }
69
+
70
+ //#endregion
71
+ export { discoverLocalePaths, getModuleLocalePaths };
72
+ //# sourceMappingURL=module-loader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-loader.mjs","names":[],"sources":["../../src/server/module-loader.ts"],"sourcesContent":["import \"server-only\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\n\nexport async function discoverLocalePaths(): Promise<string[]> {\n const paths: string[] = [];\n const root = process.cwd();\n\n // Helper to find monorepo root (looking for pnpm-workspace.yaml or turbo.json)\n const findMonorepoRoot = async (current: string): Promise<string | null> => {\n const indicators = [\"pnpm-workspace.yaml\", \"turbo.json\", \"package.json\"];\n let dir = current;\n while (dir !== path.parse(dir).root) {\n for (const indicator of indicators) {\n try {\n await fs.access(path.join(dir, indicator));\n // If we find pnpm-workspace.yaml, this is definitely it\n if (indicator === \"pnpm-workspace.yaml\") return dir;\n } catch {}\n }\n dir = path.dirname(dir);\n }\n return null;\n };\n\n const monorepoRoot = await findMonorepoRoot(root);\n\n // 1. Monorepo 'packages' scan\n if (monorepoRoot) {\n const packagesDir = path.join(monorepoRoot, \"packages\");\n try {\n const entries = await fs.readdir(packagesDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const localePath = path.join(packagesDir, entry.name, \"locales\");\n try {\n await fs.access(localePath);\n paths.push(localePath);\n } catch {}\n }\n }\n } catch (e) {}\n }\n\n // 2. node_modules/@kryo scan (relative to app root)\n const nodeModulesKryo = path.join(root, \"node_modules\", \"@kryo\");\n try {\n const entries = await fs.readdir(nodeModulesKryo, { withFileTypes: true });\n for (const entry of entries) {\n const localePath = path.join(nodeModulesKryo, entry.name, \"locales\");\n try {\n await fs.access(localePath);\n paths.push(localePath);\n } catch {}\n }\n } catch (e) {}\n\n // 3. Local 'modules' scan (relative to app root)\n const localModulesDir = path.join(root, \"modules\");\n try {\n const entries = await fs.readdir(localModulesDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const localePath = path.join(localModulesDir, entry.name, \"locales\");\n try {\n await fs.access(localePath);\n paths.push(localePath);\n } catch {}\n }\n }\n } catch (e) {}\n\n return Array.from(new Set(paths));\n}\n\n/**\n * Legacy support or manual overrides\n */\nexport async function getModuleLocalePaths(\n packageModules: string[] = [],\n localModulesDir: string = \"modules\",\n): Promise<string[]> {\n // ... rest of the existing function if we want to keep it,\n // but we'll probably favor discoverLocalePaths\n return discoverLocalePaths();\n}\n"],"mappings":";;;;;AAIA,eAAsB,sBAAyC;CAC7D,MAAM,QAAkB,EAAE;CAC1B,MAAM,OAAO,QAAQ,KAAK;CAG1B,MAAM,mBAAmB,OAAO,YAA4C;EAC1E,MAAM,aAAa;GAAC;GAAuB;GAAc;GAAe;EACxE,IAAI,MAAM;AACV,SAAO,QAAQ,KAAK,MAAM,IAAI,CAAC,MAAM;AACnC,QAAK,MAAM,aAAa,WACtB,KAAI;AACF,UAAM,GAAG,OAAO,KAAK,KAAK,KAAK,UAAU,CAAC;AAE1C,QAAI,cAAc,sBAAuB,QAAO;WAC1C;AAEV,SAAM,KAAK,QAAQ,IAAI;;AAEzB,SAAO;;CAGT,MAAM,eAAe,MAAM,iBAAiB,KAAK;AAGjD,KAAI,cAAc;EAChB,MAAM,cAAc,KAAK,KAAK,cAAc,WAAW;AACvD,MAAI;GACF,MAAM,UAAU,MAAM,GAAG,QAAQ,aAAa,EAAE,eAAe,MAAM,CAAC;AACtE,QAAK,MAAM,SAAS,QAClB,KAAI,MAAM,aAAa,EAAE;IACvB,MAAM,aAAa,KAAK,KAAK,aAAa,MAAM,MAAM,UAAU;AAChE,QAAI;AACF,WAAM,GAAG,OAAO,WAAW;AAC3B,WAAM,KAAK,WAAW;YAChB;;WAGL,GAAG;;CAId,MAAM,kBAAkB,KAAK,KAAK,MAAM,gBAAgB,QAAQ;AAChE,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,iBAAiB,EAAE,eAAe,MAAM,CAAC;AAC1E,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,aAAa,KAAK,KAAK,iBAAiB,MAAM,MAAM,UAAU;AACpE,OAAI;AACF,UAAM,GAAG,OAAO,WAAW;AAC3B,UAAM,KAAK,WAAW;WAChB;;UAEH,GAAG;CAGZ,MAAM,kBAAkB,KAAK,KAAK,MAAM,UAAU;AAClD,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,iBAAiB,EAAE,eAAe,MAAM,CAAC;AAC1E,OAAK,MAAM,SAAS,QAClB,KAAI,MAAM,aAAa,EAAE;GACvB,MAAM,aAAa,KAAK,KAAK,iBAAiB,MAAM,MAAM,UAAU;AACpE,OAAI;AACF,UAAM,GAAG,OAAO,WAAW;AAC3B,UAAM,KAAK,WAAW;WAChB;;UAGL,GAAG;AAEZ,QAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;;;;;AAMnC,eAAsB,qBACpB,iBAA2B,EAAE,EAC7B,kBAA0B,WACP;AAGnB,QAAO,qBAAqB"}
@@ -0,0 +1,10 @@
1
+
2
+ //#region src/shared/constants.ts
3
+ const COOKIE_NAME = "NEXT_LOCALE";
4
+ const DEFAULT_LOCALE = "pl";
5
+ const LOCALES = ["pl", "en"];
6
+
7
+ //#endregion
8
+ exports.COOKIE_NAME = COOKIE_NAME;
9
+ exports.DEFAULT_LOCALE = DEFAULT_LOCALE;
10
+ exports.LOCALES = LOCALES;
@@ -0,0 +1,7 @@
1
+ //#region src/shared/constants.d.ts
2
+ declare const COOKIE_NAME = "NEXT_LOCALE";
3
+ declare const DEFAULT_LOCALE = "pl";
4
+ declare const LOCALES: string[];
5
+ //#endregion
6
+ export { COOKIE_NAME, DEFAULT_LOCALE, LOCALES };
7
+ //# sourceMappingURL=constants.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.cts","names":[],"sources":["../../src/shared/constants.ts"],"mappings":";cAAa,WAAA;AAAA,cACA,cAAA;AAAA,cACA,OAAA"}
@@ -0,0 +1,7 @@
1
+ //#region src/shared/constants.d.ts
2
+ declare const COOKIE_NAME = "NEXT_LOCALE";
3
+ declare const DEFAULT_LOCALE = "pl";
4
+ declare const LOCALES: string[];
5
+ //#endregion
6
+ export { COOKIE_NAME, DEFAULT_LOCALE, LOCALES };
7
+ //# sourceMappingURL=constants.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.mts","names":[],"sources":["../../src/shared/constants.ts"],"mappings":";cAAa,WAAA;AAAA,cACA,cAAA;AAAA,cACA,OAAA"}
@@ -0,0 +1,8 @@
1
+ //#region src/shared/constants.ts
2
+ const COOKIE_NAME = "NEXT_LOCALE";
3
+ const DEFAULT_LOCALE = "pl";
4
+ const LOCALES = ["pl", "en"];
5
+
6
+ //#endregion
7
+ export { COOKIE_NAME, DEFAULT_LOCALE, LOCALES };
8
+ //# sourceMappingURL=constants.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.mjs","names":[],"sources":["../../src/shared/constants.ts"],"sourcesContent":["export const COOKIE_NAME = \"NEXT_LOCALE\";\nexport const DEFAULT_LOCALE = \"pl\";\nexport const LOCALES = [\"pl\", \"en\"];\n"],"mappings":";AAAA,MAAa,cAAc;AAC3B,MAAa,iBAAiB;AAC9B,MAAa,UAAU,CAAC,MAAM,KAAK"}
@@ -0,0 +1,34 @@
1
+
2
+ //#region src/shared/utils.ts
3
+ function getNestedMessage(messages, path) {
4
+ if (!messages) return void 0;
5
+ if (path in messages) return messages[path];
6
+ const parts = path.split(".");
7
+ let current = messages;
8
+ let i = 0;
9
+ while (i < parts.length) {
10
+ if (typeof current !== "object" || current === null) return void 0;
11
+ let found = false;
12
+ for (let j = parts.length; j > i; j--) {
13
+ const keyAttempt = parts.slice(i, j).join(".");
14
+ if (keyAttempt in current) {
15
+ current = current[keyAttempt];
16
+ i = j;
17
+ found = true;
18
+ break;
19
+ }
20
+ }
21
+ if (!found) return;
22
+ }
23
+ return current;
24
+ }
25
+ function formatMessage(message, values) {
26
+ if (!values) return message;
27
+ return message.replace(/{(\w+)}/g, (match, key) => {
28
+ return values[key] !== void 0 ? String(values[key]) : match;
29
+ });
30
+ }
31
+
32
+ //#endregion
33
+ exports.formatMessage = formatMessage;
34
+ exports.getNestedMessage = getNestedMessage;
@@ -0,0 +1,8 @@
1
+ import { AbstractIntlMessages } from "../types.cjs";
2
+
3
+ //#region src/shared/utils.d.ts
4
+ declare function getNestedMessage(messages: AbstractIntlMessages | undefined, path: string): string | AbstractIntlMessages | undefined;
5
+ declare function formatMessage(message: string, values?: Record<string, any>): string;
6
+ //#endregion
7
+ export { formatMessage, getNestedMessage };
8
+ //# sourceMappingURL=utils.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.cts","names":[],"sources":["../../src/shared/utils.ts"],"mappings":";;;iBAEgB,gBAAA,CACd,QAAA,EAAU,oBAAA,cACV,IAAA,oBACU,oBAAA;AAAA,iBAmCI,aAAA,CACd,OAAA,UACA,MAAA,GAAS,MAAA"}
@@ -0,0 +1,8 @@
1
+ import { AbstractIntlMessages } from "../types.mjs";
2
+
3
+ //#region src/shared/utils.d.ts
4
+ declare function getNestedMessage(messages: AbstractIntlMessages | undefined, path: string): string | AbstractIntlMessages | undefined;
5
+ declare function formatMessage(message: string, values?: Record<string, any>): string;
6
+ //#endregion
7
+ export { formatMessage, getNestedMessage };
8
+ //# sourceMappingURL=utils.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.mts","names":[],"sources":["../../src/shared/utils.ts"],"mappings":";;;iBAEgB,gBAAA,CACd,QAAA,EAAU,oBAAA,cACV,IAAA,oBACU,oBAAA;AAAA,iBAmCI,aAAA,CACd,OAAA,UACA,MAAA,GAAS,MAAA"}
@@ -0,0 +1,33 @@
1
+ //#region src/shared/utils.ts
2
+ function getNestedMessage(messages, path) {
3
+ if (!messages) return void 0;
4
+ if (path in messages) return messages[path];
5
+ const parts = path.split(".");
6
+ let current = messages;
7
+ let i = 0;
8
+ while (i < parts.length) {
9
+ if (typeof current !== "object" || current === null) return void 0;
10
+ let found = false;
11
+ for (let j = parts.length; j > i; j--) {
12
+ const keyAttempt = parts.slice(i, j).join(".");
13
+ if (keyAttempt in current) {
14
+ current = current[keyAttempt];
15
+ i = j;
16
+ found = true;
17
+ break;
18
+ }
19
+ }
20
+ if (!found) return;
21
+ }
22
+ return current;
23
+ }
24
+ function formatMessage(message, values) {
25
+ if (!values) return message;
26
+ return message.replace(/{(\w+)}/g, (match, key) => {
27
+ return values[key] !== void 0 ? String(values[key]) : match;
28
+ });
29
+ }
30
+
31
+ //#endregion
32
+ export { formatMessage, getNestedMessage };
33
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../../src/shared/utils.ts"],"sourcesContent":["import type { AbstractIntlMessages } from \"../types\";\n\nexport function getNestedMessage(\n messages: AbstractIntlMessages | undefined,\n path: string,\n): string | AbstractIntlMessages | undefined {\n if (!messages) return undefined;\n\n // Try direct match first (optimization + exact match with dots)\n if (path in messages) return messages[path];\n\n const parts = path.split(\".\");\n let current: any = messages;\n\n let i = 0;\n while (i < parts.length) {\n if (typeof current !== \"object\" || current === null) return undefined;\n\n // Try to match longest possible key from current position\n let found = false;\n for (let j = parts.length; j > i; j--) {\n const keyAttempt = parts.slice(i, j).join(\".\");\n if (keyAttempt in current) {\n current = current[keyAttempt];\n i = j;\n found = true;\n break;\n }\n }\n\n if (!found) {\n // If we can't match anything, check if we are at the end and looking for a partial key?\n // But strictly, we failed to traverse.\n return undefined;\n }\n }\n\n return current;\n}\n\nexport function formatMessage(\n message: string,\n values?: Record<string, any>,\n): string {\n if (!values) return message;\n\n return message.replace(/{(\\w+)}/g, (match, key) => {\n return values[key] !== undefined ? String(values[key]) : match;\n });\n}\n"],"mappings":";AAEA,SAAgB,iBACd,UACA,MAC2C;AAC3C,KAAI,CAAC,SAAU,QAAO;AAGtB,KAAI,QAAQ,SAAU,QAAO,SAAS;CAEtC,MAAM,QAAQ,KAAK,MAAM,IAAI;CAC7B,IAAI,UAAe;CAEnB,IAAI,IAAI;AACR,QAAO,IAAI,MAAM,QAAQ;AACvB,MAAI,OAAO,YAAY,YAAY,YAAY,KAAM,QAAO;EAG5D,IAAI,QAAQ;AACZ,OAAK,IAAI,IAAI,MAAM,QAAQ,IAAI,GAAG,KAAK;GACrC,MAAM,aAAa,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;AAC9C,OAAI,cAAc,SAAS;AACzB,cAAU,QAAQ;AAClB,QAAI;AACJ,YAAQ;AACR;;;AAIJ,MAAI,CAAC,MAGH;;AAIJ,QAAO;;AAGT,SAAgB,cACd,SACA,QACQ;AACR,KAAI,CAAC,OAAQ,QAAO;AAEpB,QAAO,QAAQ,QAAQ,aAAa,OAAO,QAAQ;AACjD,SAAO,OAAO,SAAS,SAAY,OAAO,OAAO,KAAK,GAAG;GACzD"}
@@ -0,0 +1,15 @@
1
+ //#region src/types.d.ts
2
+ type AbstractIntlMessages = Record<string, any>;
3
+ /**
4
+ * Recursive type to get all nested keys joined by dots.
5
+ */
6
+ type NestedKeyOf<ObjectType> = { [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object ? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}` : `${Key}` }[keyof ObjectType & (string | number)];
7
+ /**
8
+ * Gets translation keys for a specific namespace or all keys if none is provided.
9
+ */
10
+ type TranslationKeys<N = undefined> = N extends keyof KryoCustomMessages ? // @ts-expect-error - Allow string keys that are not in KryoCustomMessages
11
+ NestedKeyOf<KryoCustomMessages[N]> | (string & {}) : // @ts-expect-error - Allow string keys that are not in KryoCustomMessages
12
+ NestedKeyOf<KryoCustomMessages> | (string & {});
13
+ //#endregion
14
+ export { AbstractIntlMessages, NestedKeyOf, TranslationKeys };
15
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"mappings":";KAOY,oBAAA,GAAuB,MAAA;AAAnC;;;AAAA,KAKY,WAAA,+BACI,UAAA,uBAAiC,UAAA,CAAW,GAAA,sBACnD,GAAA,QAAW,GAAA,IAAO,WAAA,CAAY,UAAA,CAAW,GAAA,UACzC,GAAA,WACD,UAAA;;;;KAMI,eAAA,kBAAiC,CAAA,eAAgB,kBAAA;AAEzD,WAAA,CAAY,kBAAA,CAAmB,CAAA;AAE/B,WAAA,CAAY,kBAAA"}
@@ -0,0 +1,15 @@
1
+ //#region src/types.d.ts
2
+ type AbstractIntlMessages = Record<string, any>;
3
+ /**
4
+ * Recursive type to get all nested keys joined by dots.
5
+ */
6
+ type NestedKeyOf<ObjectType> = { [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object ? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}` : `${Key}` }[keyof ObjectType & (string | number)];
7
+ /**
8
+ * Gets translation keys for a specific namespace or all keys if none is provided.
9
+ */
10
+ type TranslationKeys<N = undefined> = N extends keyof KryoCustomMessages ? // @ts-expect-error - Allow string keys that are not in KryoCustomMessages
11
+ NestedKeyOf<KryoCustomMessages[N]> | (string & {}) : // @ts-expect-error - Allow string keys that are not in KryoCustomMessages
12
+ NestedKeyOf<KryoCustomMessages> | (string & {});
13
+ //#endregion
14
+ export { AbstractIntlMessages, NestedKeyOf, TranslationKeys };
15
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";KAOY,oBAAA,GAAuB,MAAA;AAAnC;;;AAAA,KAKY,WAAA,+BACI,UAAA,uBAAiC,UAAA,CAAW,GAAA,sBACnD,GAAA,QAAW,GAAA,IAAO,WAAA,CAAY,UAAA,CAAW,GAAA,UACzC,GAAA,WACD,UAAA;;;;KAMI,eAAA,kBAAiC,CAAA,eAAgB,kBAAA;AAEzD,WAAA,CAAY,kBAAA,CAAmB,CAAA;AAE/B,WAAA,CAAY,kBAAA"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@arch-cadre/intl",
3
+ "version": "0.0.6",
4
+ "description": "Core Intl for Kryo framework",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.mjs",
8
+ "require": "./dist/index.cjs"
9
+ },
10
+ "./client": {
11
+ "import": "./dist/client/index.mjs",
12
+ "require": "./dist/client/index.cjs"
13
+ },
14
+ "./server": {
15
+ "import": "./dist/server/index.mjs",
16
+ "require": "./dist/server/index.cjs"
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "release": "npm publish --access public --no-git-checks",
25
+ "dev": "tsdown --watch",
26
+ "build": "tsdown"
27
+ },
28
+ "dependencies": {
29
+ "@formatjs/intl-localematcher": "^0.5.4",
30
+ "date-fns": "^4.1.0",
31
+ "negotiator": "^0.6.3",
32
+ "server-only": "^0.0.1",
33
+ "zod": "^4.1.12"
34
+ },
35
+ "peerDependencies": {
36
+ "next": ">=15.0.0",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@biomejs/biome": "2.3.8",
42
+ "@types/negotiator": "^0.6.3",
43
+ "@types/react": "^19",
44
+ "@types/react-dom": "^19",
45
+ "tsdown": "^0.20.3",
46
+ "typescript": "^5"
47
+ },
48
+ "types": "./dist/index.d.cts",
49
+ "main": "./dist/index.cjs",
50
+ "module": "./dist/index.mjs"
51
+ }