@comvi/next 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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +256 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  4. package/dist/client/I18nProvider.cjs +101 -0
  5. package/dist/client/I18nProvider.d.ts +84 -0
  6. package/dist/client/I18nProvider.d.ts.map +1 -0
  7. package/dist/client/I18nProvider.js +99 -0
  8. package/dist/client/index.d.ts +5 -0
  9. package/dist/client/index.d.ts.map +1 -0
  10. package/dist/client.cjs +31 -0
  11. package/dist/client.d.ts +5 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +5 -0
  14. package/dist/createNextI18n.cjs +153 -0
  15. package/dist/createNextI18n.d.ts +183 -0
  16. package/dist/createNextI18n.d.ts.map +1 -0
  17. package/dist/createNextI18n.js +152 -0
  18. package/dist/index.cjs +17 -0
  19. package/dist/index.d.ts +9 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +3 -0
  22. package/dist/middleware/createMiddleware.cjs +185 -0
  23. package/dist/middleware/createMiddleware.d.ts +38 -0
  24. package/dist/middleware/createMiddleware.d.ts.map +1 -0
  25. package/dist/middleware/createMiddleware.js +184 -0
  26. package/dist/middleware/types.d.ts +87 -0
  27. package/dist/middleware/types.d.ts.map +1 -0
  28. package/dist/middleware.cjs +3 -0
  29. package/dist/middleware.d.ts +3 -0
  30. package/dist/middleware.d.ts.map +1 -0
  31. package/dist/middleware.js +2 -0
  32. package/dist/navigation.cjs +8 -0
  33. package/dist/navigation.d.ts +5 -0
  34. package/dist/navigation.d.ts.map +1 -0
  35. package/dist/navigation.js +5 -0
  36. package/dist/routing/Link.cjs +42 -0
  37. package/dist/routing/Link.d.ts +25 -0
  38. package/dist/routing/Link.d.ts.map +1 -0
  39. package/dist/routing/Link.js +39 -0
  40. package/dist/routing/context.cjs +21 -0
  41. package/dist/routing/context.d.ts +9 -0
  42. package/dist/routing/context.d.ts.map +1 -0
  43. package/dist/routing/context.js +18 -0
  44. package/dist/routing/defineRouting.cjs +141 -0
  45. package/dist/routing/defineRouting.d.ts +123 -0
  46. package/dist/routing/defineRouting.d.ts.map +1 -0
  47. package/dist/routing/defineRouting.js +139 -0
  48. package/dist/routing/hooks.cjs +104 -0
  49. package/dist/routing/hooks.d.ts +66 -0
  50. package/dist/routing/hooks.d.ts.map +1 -0
  51. package/dist/routing/hooks.js +102 -0
  52. package/dist/routing/types.d.ts +35 -0
  53. package/dist/routing/types.d.ts.map +1 -0
  54. package/dist/routing/utils.cjs +94 -0
  55. package/dist/routing/utils.d.ts +8 -0
  56. package/dist/routing/utils.d.ts.map +1 -0
  57. package/dist/routing/utils.js +91 -0
  58. package/dist/routing.cjs +5 -0
  59. package/dist/routing.d.ts +4 -0
  60. package/dist/routing.d.ts.map +1 -0
  61. package/dist/routing.js +2 -0
  62. package/dist/server/cache.cjs +69 -0
  63. package/dist/server/cache.d.ts +42 -0
  64. package/dist/server/cache.d.ts.map +1 -0
  65. package/dist/server/cache.js +66 -0
  66. package/dist/server/ensureInitialized.cjs +19 -0
  67. package/dist/server/ensureInitialized.d.ts +7 -0
  68. package/dist/server/ensureInitialized.d.ts.map +1 -0
  69. package/dist/server/ensureInitialized.js +19 -0
  70. package/dist/server/getI18n.cjs +115 -0
  71. package/dist/server/getI18n.d.ts +61 -0
  72. package/dist/server/getI18n.d.ts.map +1 -0
  73. package/dist/server/getI18n.js +114 -0
  74. package/dist/server/getLocale.cjs +37 -0
  75. package/dist/server/getLocale.d.ts +22 -0
  76. package/dist/server/getLocale.d.ts.map +1 -0
  77. package/dist/server/getLocale.js +36 -0
  78. package/dist/server/loadTranslations.cjs +54 -0
  79. package/dist/server/loadTranslations.d.ts +34 -0
  80. package/dist/server/loadTranslations.d.ts.map +1 -0
  81. package/dist/server/loadTranslations.js +54 -0
  82. package/dist/server/setRequestLocale.cjs +31 -0
  83. package/dist/server/setRequestLocale.d.ts +26 -0
  84. package/dist/server/setRequestLocale.d.ts.map +1 -0
  85. package/dist/server/setRequestLocale.js +31 -0
  86. package/dist/server/types.d.ts +43 -0
  87. package/dist/server/types.d.ts.map +1 -0
  88. package/dist/server.cjs +11 -0
  89. package/dist/server.d.ts +8 -0
  90. package/dist/server.d.ts.map +1 -0
  91. package/dist/server.js +6 -0
  92. package/package.json +111 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-present Comvi (https://comvi.io)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,256 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="../../.github/assets/header-logo-dark.png">
4
+ <img alt="Comvi" src="../../.github/assets/header-logo-light.png" width="860">
5
+ </picture>
6
+ </p>
7
+
8
+ <h1 align="center">@comvi/next</h1>
9
+
10
+ <p align="center">Next.js App Router integration for Comvi i18n — SSR, middleware, and locale routing.</p>
11
+
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/@comvi/next"><img src="https://img.shields.io/npm/v/@comvi/next?color=blue" alt="npm"></a>
14
+ <a href="https://bundlejs.com/?q=%40comvi%2Fnext"><img src="https://deno.bundlejs.com/?q=@comvi/next&badge=&badge-style=flat&badge-raster" alt="Bundle size"></a>
15
+ <a href="https://github.com/comvi-io/comvi-js/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
16
+ </p>
17
+
18
+ ---
19
+
20
+ `@comvi/next` builds on [`@comvi/react`](../react) with SSR utilities and locale routing for the Next.js App Router. `createNextI18n()` handles i18n setup plus routing config; server-only functions load translations on the server side; client-only entries provide the React provider and locale-aware navigation.
21
+
22
+ Designed for Next.js 14+ / 15+ with React 18+ / 19+.
23
+
24
+ 📖 **Documentation:** https://comvi.io/docs/i18n/next/
25
+
26
+ ## Why Comvi i18n?
27
+
28
+ Comvi i18n is a modern, framework-agnostic internationalization library built on three principles: type-safe translations, real ICU MessageFormat, and zero compromises on bundle size or security.
29
+
30
+ - **Rich text without XSS.** Embed components inside translation strings (`Click <link>here</link>`) — translators see clean markup, you decide what each tag renders to. No raw HTML, no unsafe DOM injection, no splitting a sentence across template fragments.
31
+ - **Real ICU MessageFormat.** Plurals, ordinals, and select all follow locale-correct grammar via `Intl.PluralRules` — Polish, Ukrainian, Arabic, Welsh, and the rest. Same syntax every major TMS (Crowdin, Lokalise, Phrase) already speaks.
32
+ - **Locale-aware formatters built in.** `formatNumber`, `formatDate`, `formatCurrency`, and `formatRelativeTime` follow the active locale via native `Intl`, with reactive updates in every framework binding.
33
+ - **~8 kB gzipped, zero runtime dependencies.** No `eval` or `new Function` anywhere — runs under a strict CSP without `unsafe-eval`. Safe for Chrome extensions, Cloudflare Workers, and locked-down enterprise apps.
34
+ - **Pluggable, not monolithic.** Translation loading (CDN/API), locale detection, and in-context editing are opt-in plugins via `@comvi/plugin-fetch-loader`, `@comvi/plugin-locale-detector`, and `@comvi/plugin-in-context-editor`. You only ship what you use.
35
+ - **Same API across 6 frameworks.** `useI18n()` and `<T>` look the same in [Vue](https://www.npmjs.com/package/@comvi/vue), [React](https://www.npmjs.com/package/@comvi/react), [SolidJS](https://www.npmjs.com/package/@comvi/solid), [Svelte](https://www.npmjs.com/package/@comvi/svelte), [Next.js](https://www.npmjs.com/package/@comvi/next), and [Nuxt](https://www.npmjs.com/package/@comvi/nuxt) — switch frameworks without relearning your i18n layer.
36
+ - **First-class SSR.** `@comvi/next` and `@comvi/nuxt` ship server-side translation loading, locale-routed layouts, and middleware for redirect-on-detect — no flash of untranslated content.
37
+
38
+ ## Why @comvi/next?
39
+
40
+ - **No client-side translation flash.** `loadTranslations()` loads translations server-side for Server Components — users see complete content on first load, no suspense waterfall for i18n.
41
+ - **Built-in `[locale]` segment routing.** `createNextI18n()` creates routing config for your `[locale]/layout.tsx` setup and pairs it with `createMiddleware()` for automatic locale detection and redirect-on-first-visit.
42
+ - **Server-side cache friendly.** Use `loadTranslations()` in Server Components; Next.js can dedupe underlying `fetch()` calls within a request, while the core loader deduplicates concurrent locale/namespace loads.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ npm install @comvi/next
48
+ # Peers: next ^14 || ^15, react ^18 || ^19
49
+ ```
50
+
51
+ ## Quick start
52
+
53
+ ```ts
54
+ // src/i18n/config.ts
55
+ import { createNextI18n } from "@comvi/next";
56
+
57
+ export const nextI18n = createNextI18n({
58
+ locales: ["en", "uk", "de"],
59
+ defaultLocale: "en",
60
+ localePrefix: "as-needed",
61
+ fallbackLocale: "en",
62
+ });
63
+
64
+ nextI18n.i18n.registerLoader({
65
+ en: () => import("./locales/en.json"),
66
+ uk: () => import("./locales/uk.json"),
67
+ de: () => import("./locales/de.json"),
68
+ });
69
+
70
+ export const { i18n, routing } = nextI18n;
71
+ ```
72
+
73
+ ```ts
74
+ // src/i18n/server.ts
75
+ import "server-only";
76
+ import { setI18n } from "@comvi/next/server";
77
+ import { i18n } from "./config";
78
+
79
+ setI18n(i18n);
80
+ ```
81
+
82
+ ```tsx
83
+ // src/i18n/ComviProvider.tsx
84
+ "use client";
85
+
86
+ import { I18nProvider, type MessagesMap } from "@comvi/next/client";
87
+ import { i18n, routing } from "./config";
88
+
89
+ export function ComviProvider({
90
+ children,
91
+ locale,
92
+ messages,
93
+ }: {
94
+ children: React.ReactNode;
95
+ locale: string;
96
+ messages: MessagesMap;
97
+ }) {
98
+ return (
99
+ <I18nProvider i18n={i18n} locale={locale} messages={messages} routing={routing}>
100
+ {children}
101
+ </I18nProvider>
102
+ );
103
+ }
104
+ ```
105
+
106
+ ```ts
107
+ // src/middleware.ts
108
+ import { createMiddleware } from "@comvi/next/middleware";
109
+ import { routing } from "./i18n/config";
110
+
111
+ export default createMiddleware(routing);
112
+ export const config = { matcher: ["/((?!api|_next|.*\\..*).*)"] };
113
+ ```
114
+
115
+ The full setup also includes a `[locale]/layout.tsx` that imports the server registration once, calls `loadTranslations(locale)`, and renders the client wrapper above. See the [documentation](https://comvi.io/docs/i18n/next/) for locale-aware `<Link>`, `useLocalizedRouter`, server/client subpath imports, and the lazy-plugin API.
116
+
117
+ ## Server-side translation loading
118
+
119
+ `loadTranslations()` is a server-only function that loads translations for a locale using the i18n instance registered with `setI18n(i18n)`. Call it in Server Components or Server Actions, and pass the result to `<I18nProvider>` to hydrate the client without a flash of untranslated content.
120
+
121
+ ```tsx
122
+ // app/[locale]/layout.tsx
123
+ import "@/i18n/server";
124
+ import { loadTranslations } from "@comvi/next/server";
125
+ import { ComviProvider } from "@/i18n/ComviProvider";
126
+
127
+ export default async function LocaleLayout({
128
+ children,
129
+ params,
130
+ }: {
131
+ children: React.ReactNode;
132
+ params: Promise<{ locale: string }>;
133
+ }) {
134
+ const { locale } = await params;
135
+ const messages = await loadTranslations(locale);
136
+
137
+ return (
138
+ <html lang={locale}>
139
+ <body>
140
+ <ComviProvider locale={locale} messages={messages}>
141
+ {children}
142
+ </ComviProvider>
143
+ </body>
144
+ </html>
145
+ );
146
+ }
147
+ ```
148
+
149
+ Next.js deduplicates `fetch()` calls across Server Components within a single request, and the core loader deduplicates concurrent requests for the same locale/namespace.
150
+
151
+ ## Locale routing
152
+
153
+ Create a `[locale]` dynamic segment in your app directory, and use `createMiddleware()` in `middleware.ts` for locale detection and redirect-on-first-visit.
154
+
155
+ ```tsx
156
+ // middleware.ts
157
+ import { createMiddleware } from "@comvi/next/middleware";
158
+ import { routing } from "@/i18n/config";
159
+
160
+ export default createMiddleware(routing);
161
+ export const config = { matcher: ["/((?!api|_next|.*\\..*).*)"] };
162
+ ```
163
+
164
+ The middleware extracts locale from the URL path first, then checks cookies and the Accept-Language header in your configured order. On first visit without a stored locale, it detects the user's language and redirects to the localized URL while persisting the choice in a cookie.
165
+
166
+ For more routing details and custom locale-aware navigation helpers, see the [documentation](https://comvi.io/docs/i18n/next/).
167
+
168
+ ## Rich text with `<T>`
169
+
170
+ The `<T>` component is inherited from [`@comvi/react`](../react). Embed components in translation strings without raw HTML or unsafe DOM injection.
171
+
172
+ ```json
173
+ { "help": "Click <link>here</link> for more information." }
174
+ ```
175
+
176
+ ```tsx
177
+ import { T } from "@comvi/next/client";
178
+
179
+ export function Help() {
180
+ return (
181
+ <T
182
+ i18nKey="help"
183
+ components={{
184
+ link: <a href="/help" />,
185
+ }}
186
+ />
187
+ );
188
+ }
189
+ ```
190
+
191
+ See the [React README](../react) for the full Rich Text section with tag interpolation examples and validation options.
192
+
193
+ ## ICU MessageFormat — locale-correct grammar, not just singular/plural
194
+
195
+ ICU MessageFormat handles plurals, ordinals, and select with locale-correct grammar via `Intl.PluralRules` — Comvi i18n inherits the full ICU runtime from the underlying binding.
196
+
197
+ ```tsx
198
+ import { useI18n } from "@comvi/next/client";
199
+
200
+ function Stats() {
201
+ const { t } = useI18n();
202
+ return <p>{t("items", { count: 5 })}</p>;
203
+ }
204
+ ```
205
+
206
+ See the [@comvi/react ICU section](../react#icu-messageformat--locale-correct-grammar-not-just-singularplural) for the full multilingual examples, ordinals, and select.
207
+
208
+ ## Type-safe translation keys
209
+
210
+ Extend the `TranslationKeys` interface via declaration merging for autocomplete and parameter validation. Type definitions can be generated automatically from the Comvi Platform via `@comvi/cli` or from local JSON files via `@comvi/vite-plugin`.
211
+
212
+ ```typescript
213
+ // src/types/i18n.d.ts
214
+ declare module "@comvi/core" {
215
+ interface TranslationKeys {
216
+ welcome: { name: string };
217
+ greeting: never;
218
+ "errors:NOT_FOUND": never;
219
+ }
220
+ }
221
+ ```
222
+
223
+ ```tsx
224
+ import { useI18n } from "@comvi/next/client";
225
+
226
+ export function Welcome() {
227
+ const { t } = useI18n();
228
+
229
+ // ✓ Autocomplete, params required
230
+ const msg = t("welcome", { name: "Alice" });
231
+
232
+ return <h1>{msg}</h1>;
233
+ }
234
+ ```
235
+
236
+ **What TypeScript catches:**
237
+
238
+ ```ts
239
+ // ✗ Expected 2 arguments, but got 1
240
+ t("welcome");
241
+
242
+ // ✗ Property 'name' is missing in type '{ age: number }'
243
+ t("welcome", { age: 5 });
244
+
245
+ // ✗ Type 'number' is not assignable to type 'string'
246
+ t("welcome", { name: 42 });
247
+
248
+ // ✗ Argument of type '"typo"' is not assignable to parameter
249
+ t("typo", { name: "Alice" });
250
+ ```
251
+
252
+ For CDN-delivered translations and visual in-context editing, pair with the [Comvi Platform](https://comvi.io) via [`@comvi/plugin-fetch-loader`](../plugin-fetch-loader) and [`@comvi/plugin-in-context-editor`](../plugin-in-context-editor).
253
+
254
+ ## License
255
+
256
+ [MIT](https://github.com/comvi-io/comvi-js/blob/main/LICENSE) © Comvi
@@ -0,0 +1,23 @@
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") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ exports.__toESM = __toESM;
@@ -0,0 +1,101 @@
1
+ "use client";
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
+ const require_context = require("../routing/context.cjs");
4
+ let react = require("react");
5
+ react = require_runtime.__toESM(react);
6
+ let _comvi_react = require("@comvi/react");
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ //#region src/client/I18nProvider.tsx
9
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
10
+ var isServer = typeof window === "undefined";
11
+ /**
12
+ * I18nProvider for Next.js App Router
13
+ *
14
+ * This provider handles hydration by syncing the server locale with the client
15
+ * i18n instance, preventing hydration mismatches.
16
+ *
17
+ * Translations should be pre-loaded in the i18n instance via the `translation`
18
+ * option in `createI18n()`.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * // i18n/index.ts
23
+ * import { createI18n } from '@comvi/next';
24
+ * import { setI18n } from '@comvi/next/server';
25
+ * import { translations } from './translations';
26
+ *
27
+ * export const i18n = createI18n({
28
+ * locale: 'en',
29
+ * defaultNs: 'default',
30
+ * translation: translations, // Pre-loaded translations
31
+ * });
32
+ *
33
+ * setI18n(i18n);
34
+ * ```
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * // app/[locale]/layout.tsx
39
+ * import { I18nProvider } from '@comvi/next/client';
40
+ * import { i18n } from '@/i18n';
41
+ *
42
+ * export default async function LocaleLayout({
43
+ * children,
44
+ * params
45
+ * }: {
46
+ * children: React.ReactNode;
47
+ * params: Promise<{ locale: string }>;
48
+ * }) {
49
+ * const { locale } = await params;
50
+ *
51
+ * return (
52
+ * <html lang={locale}>
53
+ * <body>
54
+ * <I18nProvider i18n={i18n} locale={locale}>
55
+ * {children}
56
+ * </I18nProvider>
57
+ * </body>
58
+ * </html>
59
+ * );
60
+ * }
61
+ * ```
62
+ */
63
+ function I18nProvider({ children, i18n, locale, messages, autoInit = true, routing, ...props }) {
64
+ const lastAddedMessagesRef = (0, react.useRef)(void 0);
65
+ const isFirstRenderRef = (0, react.useRef)(true);
66
+ if (isServer || isFirstRenderRef.current) {
67
+ if (i18n.locale !== locale) i18n.locale = locale;
68
+ if (messages && messages !== lastAddedMessagesRef.current) {
69
+ i18n.addTranslations(messages);
70
+ lastAddedMessagesRef.current = messages;
71
+ }
72
+ if (!isServer) isFirstRenderRef.current = false;
73
+ }
74
+ useIsomorphicLayoutEffect(() => {
75
+ if (i18n.locale !== locale) i18n.locale = locale;
76
+ if (messages && messages !== lastAddedMessagesRef.current) {
77
+ i18n.addTranslations(messages);
78
+ lastAddedMessagesRef.current = messages;
79
+ }
80
+ }, [
81
+ i18n,
82
+ locale,
83
+ messages
84
+ ]);
85
+ const content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_comvi_react.I18nProvider, {
86
+ i18n,
87
+ autoInit,
88
+ ssrInitialLocale: locale,
89
+ ssrInitialIsLoading: false,
90
+ ssrInitialIsInitializing: false,
91
+ ...props,
92
+ children
93
+ });
94
+ return routing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_context.RoutingProvider, {
95
+ routing,
96
+ children: content
97
+ }) : content;
98
+ }
99
+ I18nProvider.displayName = "I18nProvider";
100
+ //#endregion
101
+ exports.I18nProvider = I18nProvider;
@@ -0,0 +1,84 @@
1
+ import { I18nProviderProps as ReactI18nProviderProps } from '@comvi/react';
2
+ import { TranslationValue } from '@comvi/core';
3
+ import { RoutingConfig } from '../routing/types';
4
+ /**
5
+ * Translations result keyed by "locale:namespace"
6
+ */
7
+ export type MessagesMap = Record<string, Record<string, TranslationValue>>;
8
+ export interface I18nProviderProps extends Omit<ReactI18nProviderProps, "ssrInitialLocale"> {
9
+ /** Locale passed from server (from params.locale) */
10
+ locale: string;
11
+ /**
12
+ * Pre-loaded messages from loadTranslations().
13
+ * These will be added to the i18n cache on mount.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * const messages = await loadTranslations(locale);
18
+ * <I18nProvider i18n={i18n} locale={locale} messages={messages}>
19
+ * ```
20
+ */
21
+ messages?: MessagesMap;
22
+ /**
23
+ * Routing configuration for locale prefixing and pathnames.
24
+ * Pass the `routing` value returned by createNextI18n().
25
+ */
26
+ routing?: RoutingConfig;
27
+ }
28
+ /**
29
+ * I18nProvider for Next.js App Router
30
+ *
31
+ * This provider handles hydration by syncing the server locale with the client
32
+ * i18n instance, preventing hydration mismatches.
33
+ *
34
+ * Translations should be pre-loaded in the i18n instance via the `translation`
35
+ * option in `createI18n()`.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * // i18n/index.ts
40
+ * import { createI18n } from '@comvi/next';
41
+ * import { setI18n } from '@comvi/next/server';
42
+ * import { translations } from './translations';
43
+ *
44
+ * export const i18n = createI18n({
45
+ * locale: 'en',
46
+ * defaultNs: 'default',
47
+ * translation: translations, // Pre-loaded translations
48
+ * });
49
+ *
50
+ * setI18n(i18n);
51
+ * ```
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * // app/[locale]/layout.tsx
56
+ * import { I18nProvider } from '@comvi/next/client';
57
+ * import { i18n } from '@/i18n';
58
+ *
59
+ * export default async function LocaleLayout({
60
+ * children,
61
+ * params
62
+ * }: {
63
+ * children: React.ReactNode;
64
+ * params: Promise<{ locale: string }>;
65
+ * }) {
66
+ * const { locale } = await params;
67
+ *
68
+ * return (
69
+ * <html lang={locale}>
70
+ * <body>
71
+ * <I18nProvider i18n={i18n} locale={locale}>
72
+ * {children}
73
+ * </I18nProvider>
74
+ * </body>
75
+ * </html>
76
+ * );
77
+ * }
78
+ * ```
79
+ */
80
+ export declare function I18nProvider({ children, i18n, locale, messages, autoInit, routing, ...props }: I18nProviderProps): import("react/jsx-runtime").JSX.Element;
81
+ export declare namespace I18nProvider {
82
+ var displayName: string;
83
+ }
84
+ //# sourceMappingURL=I18nProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"I18nProvider.d.ts","sourceRoot":"","sources":["../../src/client/I18nProvider.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,IAAI,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAE3E,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,sBAAsB,EAAE,kBAAkB,CAAC;IACzF,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,QAAe,EACf,OAAO,EACP,GAAG,KAAK,EACT,EAAE,iBAAiB,2CAuDnB;yBA/De,YAAY"}
@@ -0,0 +1,99 @@
1
+ "use client";
2
+ import { RoutingProvider } from "../routing/context.js";
3
+ import { useEffect, useLayoutEffect, useRef } from "react";
4
+ import { I18nProvider } from "@comvi/react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ //#region src/client/I18nProvider.tsx
7
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
8
+ var isServer = typeof window === "undefined";
9
+ /**
10
+ * I18nProvider for Next.js App Router
11
+ *
12
+ * This provider handles hydration by syncing the server locale with the client
13
+ * i18n instance, preventing hydration mismatches.
14
+ *
15
+ * Translations should be pre-loaded in the i18n instance via the `translation`
16
+ * option in `createI18n()`.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * // i18n/index.ts
21
+ * import { createI18n } from '@comvi/next';
22
+ * import { setI18n } from '@comvi/next/server';
23
+ * import { translations } from './translations';
24
+ *
25
+ * export const i18n = createI18n({
26
+ * locale: 'en',
27
+ * defaultNs: 'default',
28
+ * translation: translations, // Pre-loaded translations
29
+ * });
30
+ *
31
+ * setI18n(i18n);
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * // app/[locale]/layout.tsx
37
+ * import { I18nProvider } from '@comvi/next/client';
38
+ * import { i18n } from '@/i18n';
39
+ *
40
+ * export default async function LocaleLayout({
41
+ * children,
42
+ * params
43
+ * }: {
44
+ * children: React.ReactNode;
45
+ * params: Promise<{ locale: string }>;
46
+ * }) {
47
+ * const { locale } = await params;
48
+ *
49
+ * return (
50
+ * <html lang={locale}>
51
+ * <body>
52
+ * <I18nProvider i18n={i18n} locale={locale}>
53
+ * {children}
54
+ * </I18nProvider>
55
+ * </body>
56
+ * </html>
57
+ * );
58
+ * }
59
+ * ```
60
+ */
61
+ function I18nProvider$1({ children, i18n, locale, messages, autoInit = true, routing, ...props }) {
62
+ const lastAddedMessagesRef = useRef(void 0);
63
+ const isFirstRenderRef = useRef(true);
64
+ if (isServer || isFirstRenderRef.current) {
65
+ if (i18n.locale !== locale) i18n.locale = locale;
66
+ if (messages && messages !== lastAddedMessagesRef.current) {
67
+ i18n.addTranslations(messages);
68
+ lastAddedMessagesRef.current = messages;
69
+ }
70
+ if (!isServer) isFirstRenderRef.current = false;
71
+ }
72
+ useIsomorphicLayoutEffect(() => {
73
+ if (i18n.locale !== locale) i18n.locale = locale;
74
+ if (messages && messages !== lastAddedMessagesRef.current) {
75
+ i18n.addTranslations(messages);
76
+ lastAddedMessagesRef.current = messages;
77
+ }
78
+ }, [
79
+ i18n,
80
+ locale,
81
+ messages
82
+ ]);
83
+ const content = /* @__PURE__ */ jsx(I18nProvider, {
84
+ i18n,
85
+ autoInit,
86
+ ssrInitialLocale: locale,
87
+ ssrInitialIsLoading: false,
88
+ ssrInitialIsInitializing: false,
89
+ ...props,
90
+ children
91
+ });
92
+ return routing ? /* @__PURE__ */ jsx(RoutingProvider, {
93
+ routing,
94
+ children: content
95
+ }) : content;
96
+ }
97
+ I18nProvider$1.displayName = "I18nProvider";
98
+ //#endregion
99
+ export { I18nProvider$1 as I18nProvider };
@@ -0,0 +1,5 @@
1
+ export { useI18n, useI18nContext, T, createI18n } from '@comvi/react';
2
+ export type { UseI18nReturn, TProps } from '@comvi/react';
3
+ export { I18nProvider } from './I18nProvider';
4
+ export type { I18nProviderProps } from './I18nProvider';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEtE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ "use client";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ require("./_virtual/_rolldown/runtime.cjs");
5
+ const require_I18nProvider = require("./client/I18nProvider.cjs");
6
+ let _comvi_react = require("@comvi/react");
7
+ exports.I18nProvider = require_I18nProvider.I18nProvider;
8
+ Object.defineProperty(exports, "T", {
9
+ enumerable: true,
10
+ get: function() {
11
+ return _comvi_react.T;
12
+ }
13
+ });
14
+ Object.defineProperty(exports, "createI18n", {
15
+ enumerable: true,
16
+ get: function() {
17
+ return _comvi_react.createI18n;
18
+ }
19
+ });
20
+ Object.defineProperty(exports, "useI18n", {
21
+ enumerable: true,
22
+ get: function() {
23
+ return _comvi_react.useI18n;
24
+ }
25
+ });
26
+ Object.defineProperty(exports, "useI18nContext", {
27
+ enumerable: true,
28
+ get: function() {
29
+ return _comvi_react.useI18nContext;
30
+ }
31
+ });
@@ -0,0 +1,5 @@
1
+ export { useI18n, useI18nContext, T, createI18n } from '@comvi/react';
2
+ export type { UseI18nReturn, TProps } from '@comvi/react';
3
+ export { I18nProvider } from './client/I18nProvider';
4
+ export type { I18nProviderProps, MessagesMap } from './client/I18nProvider';
5
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEtE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,5 @@
1
+ "use client";
2
+ "use client";
3
+ import { I18nProvider } from "./client/I18nProvider.js";
4
+ import { T, createI18n, useI18n, useI18nContext } from "@comvi/react";
5
+ export { I18nProvider, T, createI18n, useI18n, useI18nContext };