@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.
- package/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/client/I18nProvider.cjs +101 -0
- package/dist/client/I18nProvider.d.ts +84 -0
- package/dist/client/I18nProvider.d.ts.map +1 -0
- package/dist/client/I18nProvider.js +99 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client.cjs +31 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +5 -0
- package/dist/createNextI18n.cjs +153 -0
- package/dist/createNextI18n.d.ts +183 -0
- package/dist/createNextI18n.d.ts.map +1 -0
- package/dist/createNextI18n.js +152 -0
- package/dist/index.cjs +17 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/middleware/createMiddleware.cjs +185 -0
- package/dist/middleware/createMiddleware.d.ts +38 -0
- package/dist/middleware/createMiddleware.d.ts.map +1 -0
- package/dist/middleware/createMiddleware.js +184 -0
- package/dist/middleware/types.d.ts +87 -0
- package/dist/middleware/types.d.ts.map +1 -0
- package/dist/middleware.cjs +3 -0
- package/dist/middleware.d.ts +3 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +2 -0
- package/dist/navigation.cjs +8 -0
- package/dist/navigation.d.ts +5 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +5 -0
- package/dist/routing/Link.cjs +42 -0
- package/dist/routing/Link.d.ts +25 -0
- package/dist/routing/Link.d.ts.map +1 -0
- package/dist/routing/Link.js +39 -0
- package/dist/routing/context.cjs +21 -0
- package/dist/routing/context.d.ts +9 -0
- package/dist/routing/context.d.ts.map +1 -0
- package/dist/routing/context.js +18 -0
- package/dist/routing/defineRouting.cjs +141 -0
- package/dist/routing/defineRouting.d.ts +123 -0
- package/dist/routing/defineRouting.d.ts.map +1 -0
- package/dist/routing/defineRouting.js +139 -0
- package/dist/routing/hooks.cjs +104 -0
- package/dist/routing/hooks.d.ts +66 -0
- package/dist/routing/hooks.d.ts.map +1 -0
- package/dist/routing/hooks.js +102 -0
- package/dist/routing/types.d.ts +35 -0
- package/dist/routing/types.d.ts.map +1 -0
- package/dist/routing/utils.cjs +94 -0
- package/dist/routing/utils.d.ts +8 -0
- package/dist/routing/utils.d.ts.map +1 -0
- package/dist/routing/utils.js +91 -0
- package/dist/routing.cjs +5 -0
- package/dist/routing.d.ts +4 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +2 -0
- package/dist/server/cache.cjs +69 -0
- package/dist/server/cache.d.ts +42 -0
- package/dist/server/cache.d.ts.map +1 -0
- package/dist/server/cache.js +66 -0
- package/dist/server/ensureInitialized.cjs +19 -0
- package/dist/server/ensureInitialized.d.ts +7 -0
- package/dist/server/ensureInitialized.d.ts.map +1 -0
- package/dist/server/ensureInitialized.js +19 -0
- package/dist/server/getI18n.cjs +115 -0
- package/dist/server/getI18n.d.ts +61 -0
- package/dist/server/getI18n.d.ts.map +1 -0
- package/dist/server/getI18n.js +114 -0
- package/dist/server/getLocale.cjs +37 -0
- package/dist/server/getLocale.d.ts +22 -0
- package/dist/server/getLocale.d.ts.map +1 -0
- package/dist/server/getLocale.js +36 -0
- package/dist/server/loadTranslations.cjs +54 -0
- package/dist/server/loadTranslations.d.ts +34 -0
- package/dist/server/loadTranslations.d.ts.map +1 -0
- package/dist/server/loadTranslations.js +54 -0
- package/dist/server/setRequestLocale.cjs +31 -0
- package/dist/server/setRequestLocale.d.ts +26 -0
- package/dist/server/setRequestLocale.d.ts.map +1 -0
- package/dist/server/setRequestLocale.js +31 -0
- package/dist/server/types.d.ts +43 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server.cjs +11 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +6 -0
- 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"}
|
package/dist/client.cjs
ADDED
|
@@ -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
|
+
});
|
package/dist/client.d.ts
ADDED
|
@@ -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"}
|