@fluenti/react 0.2.1 → 0.3.1

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 (59) hide show
  1. package/dist/components/DateTime.d.ts +2 -2
  2. package/dist/components/DateTime.d.ts.map +1 -1
  3. package/dist/components/Number.d.ts +2 -2
  4. package/dist/components/Number.d.ts.map +1 -1
  5. package/dist/components/Plural.d.ts +2 -2
  6. package/dist/components/Plural.d.ts.map +1 -1
  7. package/dist/components/Select.d.ts +5 -3
  8. package/dist/components/Select.d.ts.map +1 -1
  9. package/dist/components/Trans.d.ts +4 -2
  10. package/dist/components/Trans.d.ts.map +1 -1
  11. package/dist/components/icu-rich.d.ts +2 -9
  12. package/dist/components/icu-rich.d.ts.map +1 -1
  13. package/dist/components/plural-core.d.ts +2 -2
  14. package/dist/components/plural-core.d.ts.map +1 -1
  15. package/dist/components/trans-core.d.ts +1 -2
  16. package/dist/components/trans-core.d.ts.map +1 -1
  17. package/dist/context.d.ts +2 -2
  18. package/dist/context.d.ts.map +1 -1
  19. package/dist/create-fluenti.d.ts +87 -0
  20. package/dist/create-fluenti.d.ts.map +1 -0
  21. package/dist/global-registry.d.ts +4 -4
  22. package/dist/global-registry.d.ts.map +1 -1
  23. package/dist/hooks/__useI18n.d.ts +2 -2
  24. package/dist/hooks/__useI18n.d.ts.map +1 -1
  25. package/dist/hooks/useI18n.d.ts +2 -2
  26. package/dist/hooks/useI18n.d.ts.map +1 -1
  27. package/dist/icu-rich-BOtj4Oxu.js +71 -0
  28. package/dist/icu-rich-BOtj4Oxu.js.map +1 -0
  29. package/dist/icu-rich-vPU-0wGQ.cjs +2 -0
  30. package/dist/icu-rich-vPU-0wGQ.cjs.map +1 -0
  31. package/dist/index.cjs +1 -1
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.ts +8 -6
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +252 -107
  36. package/dist/index.js.map +1 -1
  37. package/dist/provider.d.ts +2 -2
  38. package/dist/provider.d.ts.map +1 -1
  39. package/dist/react-runtime.d.ts.map +1 -1
  40. package/dist/server.cjs +1 -1
  41. package/dist/server.cjs.map +1 -1
  42. package/dist/server.d.ts +13 -3
  43. package/dist/server.d.ts.map +1 -1
  44. package/dist/server.js +24 -13
  45. package/dist/server.js.map +1 -1
  46. package/dist/types.d.ts +17 -10
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/vite-plugin.cjs +1 -109
  49. package/dist/vite-plugin.cjs.map +1 -1
  50. package/dist/vite-plugin.js +13 -119
  51. package/dist/vite-plugin.js.map +1 -1
  52. package/llms-full.txt +185 -0
  53. package/llms-migration.txt +272 -0
  54. package/llms.txt +62 -0
  55. package/package.json +7 -5
  56. package/dist/icu-rich-DBeWY1k6.js +0 -108
  57. package/dist/icu-rich-DBeWY1k6.js.map +0 -1
  58. package/dist/icu-rich-XY1SdM5K.cjs +0 -2
  59. package/dist/icu-rich-XY1SdM5K.cjs.map +0 -1
@@ -0,0 +1,272 @@
1
+ # Migrating to @fluenti/react from react-i18next / react-intl
2
+
3
+ > Step-by-step guide to migrate a React app from react-i18next or react-intl (FormatJS) to Fluenti.
4
+
5
+ ## Overview
6
+
7
+ Fluenti is a **compile-time** i18n library — translations are compiled to optimized JS at build time. Unlike react-i18next and react-intl which parse messages at runtime, Fluenti produces zero-overhead string functions.
8
+
9
+ Key differences:
10
+ - No runtime message parser — messages become plain strings or template literal functions
11
+ - ICU MessageFormat for all syntax (plurals, selects, numbers, dates)
12
+ - PO or JSON catalog format, managed by CLI extraction and compilation
13
+ - Vite plugin or Next.js plugin for build-time transforms
14
+
15
+ ---
16
+
17
+ ## Migrating from react-i18next
18
+
19
+ ### 1. Install Fluenti
20
+
21
+ ```bash
22
+ pnpm add @fluenti/core @fluenti/react
23
+ pnpm add -D @fluenti/cli
24
+ ```
25
+
26
+ ### 2. Replace provider setup
27
+
28
+ Before (react-i18next):
29
+ ```tsx
30
+ import i18n from 'i18next'
31
+ import { initReactI18next, I18nextProvider } from 'react-i18next'
32
+
33
+ i18n.use(initReactI18next).init({
34
+ resources: { en: { translation: messages } },
35
+ lng: 'en',
36
+ })
37
+
38
+ <I18nextProvider i18n={i18n}><App /></I18nextProvider>
39
+ ```
40
+
41
+ After (Fluenti):
42
+ ```tsx
43
+ import { I18nProvider } from '@fluenti/react'
44
+ import en from './locales/compiled/en'
45
+
46
+ <I18nProvider locale="en" fallbackLocale="en" messages={{ en }}>
47
+ <App />
48
+ </I18nProvider>
49
+ ```
50
+
51
+ ### 3. Update hook usage
52
+
53
+ Before (react-i18next):
54
+ ```tsx
55
+ import { useTranslation } from 'react-i18next'
56
+ const { t, i18n } = useTranslation()
57
+ t('hello', { name: 'World' })
58
+ i18n.changeLanguage('ja')
59
+ ```
60
+
61
+ After (Fluenti):
62
+ ```tsx
63
+ import { useI18n } from '@fluenti/react'
64
+ const { i18n, setLocale } = useI18n()
65
+ i18n.t('hello', { name: 'World' })
66
+ setLocale('ja')
67
+ ```
68
+
69
+ ### 4. Replace components
70
+
71
+ Before (react-i18next):
72
+ ```tsx
73
+ import { Trans } from 'react-i18next'
74
+ <Trans i18nKey="welcome">
75
+ Welcome to <strong>our app</strong>
76
+ </Trans>
77
+ ```
78
+
79
+ After (Fluenti):
80
+ ```tsx
81
+ import { Trans } from '@fluenti/react'
82
+ <Trans message="Welcome to {0}our app{1}">
83
+ {(text) => <strong>{text}</strong>}
84
+ </Trans>
85
+ ```
86
+
87
+ ### 5. Convert message syntax
88
+
89
+ react-i18next interpolation → ICU:
90
+ ```
91
+ # react-i18next
92
+ "hello": "Hello, {{name}}!"
93
+
94
+ # Fluenti (ICU MessageFormat)
95
+ "hello": "Hello, {name}!"
96
+ ```
97
+
98
+ react-i18next plurals → ICU plural:
99
+ ```
100
+ # react-i18next
101
+ "items": "{{count}} item"
102
+ "items_plural": "{{count}} items"
103
+
104
+ # Fluenti (ICU)
105
+ "items": "{count, plural, one {{count} item} other {{count} items}}"
106
+ ```
107
+
108
+ ### API Mapping: react-i18next → Fluenti
109
+
110
+ | react-i18next | Fluenti |
111
+ |---------------|---------|
112
+ | `useTranslation()` | `useI18n()` |
113
+ | `t(key, options)` | `i18n.t(key, values)` |
114
+ | `i18n.changeLanguage(lng)` | `setLocale(locale)` |
115
+ | `i18n.language` | `locale` |
116
+ | `<Trans>` | `<Trans>` |
117
+ | `<I18nextProvider>` | `<I18nProvider>` |
118
+ | `{{var}}` interpolation | `{var}` interpolation |
119
+ | `_plural` suffix | ICU `{n, plural, ...}` |
120
+ | Namespaces | Not needed (use message IDs) |
121
+ | Backend plugins | `loadMessages` prop on provider |
122
+
123
+ ---
124
+
125
+ ## Migrating from react-intl (FormatJS)
126
+
127
+ ### 1. Install Fluenti
128
+
129
+ ```bash
130
+ pnpm add @fluenti/core @fluenti/react
131
+ pnpm add -D @fluenti/cli
132
+ ```
133
+
134
+ ### 2. Replace provider
135
+
136
+ Before (react-intl):
137
+ ```tsx
138
+ import { IntlProvider } from 'react-intl'
139
+ <IntlProvider locale="en" messages={messages}>
140
+ <App />
141
+ </IntlProvider>
142
+ ```
143
+
144
+ After (Fluenti):
145
+ ```tsx
146
+ import { I18nProvider } from '@fluenti/react'
147
+ import en from './locales/compiled/en'
148
+ <I18nProvider locale="en" fallbackLocale="en" messages={{ en }}>
149
+ <App />
150
+ </I18nProvider>
151
+ ```
152
+
153
+ ### 3. Update hook usage
154
+
155
+ Before (react-intl):
156
+ ```tsx
157
+ import { useIntl } from 'react-intl'
158
+ const intl = useIntl()
159
+ intl.formatMessage({ id: 'hello' }, { name: 'World' })
160
+ intl.formatDate(date, { dateStyle: 'long' })
161
+ intl.formatNumber(1234, { style: 'decimal' })
162
+ ```
163
+
164
+ After (Fluenti):
165
+ ```tsx
166
+ import { useI18n } from '@fluenti/react'
167
+ const { i18n } = useI18n()
168
+ i18n.t('hello', { name: 'World' })
169
+ i18n.d(date, 'long')
170
+ i18n.n(1234, 'decimal')
171
+ ```
172
+
173
+ ### 4. Replace components
174
+
175
+ Before (react-intl):
176
+ ```tsx
177
+ import { FormattedMessage, FormattedDate, FormattedNumber } from 'react-intl'
178
+ <FormattedMessage id="hello" values={{ name: 'World' }} />
179
+ <FormattedDate value={date} dateStyle="long" />
180
+ <FormattedNumber value={1234} style="decimal" />
181
+ ```
182
+
183
+ After (Fluenti):
184
+ ```tsx
185
+ import { Trans, DateTime, NumberFormat } from '@fluenti/react'
186
+ <Trans message="Hello, {name}!" values={{ name: 'World' }} />
187
+ <DateTime value={date} style="long" />
188
+ <NumberFormat value={1234} style="decimal" />
189
+ ```
190
+
191
+ ### 5. Message format
192
+
193
+ react-intl already uses ICU MessageFormat, so most messages work as-is. Key differences:
194
+
195
+ ```
196
+ # react-intl — uses defaultMessage
197
+ defineMessage({ id: 'hello', defaultMessage: 'Hello, {name}!' })
198
+
199
+ # Fluenti — messages live in catalog files, referenced by ID
200
+ i18n.t('hello', { name: 'World' })
201
+ ```
202
+
203
+ ### API Mapping: react-intl → Fluenti
204
+
205
+ | react-intl | Fluenti |
206
+ |------------|---------|
207
+ | `<IntlProvider>` | `<I18nProvider>` |
208
+ | `useIntl()` | `useI18n()` |
209
+ | `intl.formatMessage()` | `i18n.t()` |
210
+ | `intl.formatDate()` | `i18n.d()` |
211
+ | `intl.formatNumber()` | `i18n.n()` |
212
+ | `<FormattedMessage>` | `<Trans>` |
213
+ | `<FormattedDate>` | `<DateTime>` |
214
+ | `<FormattedNumber>` | `<NumberFormat>` |
215
+ | `<FormattedPlural>` | `<Plural>` |
216
+ | `defineMessage()` | `msg()` |
217
+ | ICU MessageFormat | ICU MessageFormat (compatible) |
218
+
219
+ ---
220
+
221
+ ## Common Steps for Both
222
+
223
+ ### Set up Fluenti CLI config
224
+
225
+ ```ts
226
+ // fluenti.config.ts
227
+ export default {
228
+ sourceLocale: 'en',
229
+ locales: ['en', 'ja', 'zh-CN'],
230
+ catalogDir: './locales',
231
+ format: 'po',
232
+ include: ['./src/**/*.{tsx,jsx,ts,js}'],
233
+ compileOutDir: './locales/compiled',
234
+ }
235
+ ```
236
+
237
+ ### Extract and compile
238
+
239
+ ```bash
240
+ npx fluenti extract # Extract messages from source
241
+ npx fluenti compile # Compile to JS modules
242
+ ```
243
+
244
+ ### Vite plugin setup
245
+
246
+ ```ts
247
+ // vite.config.ts
248
+ import react from '@vitejs/plugin-react'
249
+ import fluentiReact from '@fluenti/react/vite-plugin'
250
+
251
+ export default {
252
+ plugins: [react(), fluentiReact()],
253
+ }
254
+ ```
255
+
256
+ ### For Next.js projects
257
+
258
+ Use @fluenti/next instead of the Vite plugin:
259
+
260
+ ```ts
261
+ // next.config.ts
262
+ import { withFluenti } from '@fluenti/next'
263
+ export default withFluenti({})
264
+ ```
265
+
266
+ ## Key Behavioral Differences
267
+
268
+ 1. **Compile-time** — syntax errors caught at build time, not runtime
269
+ 2. **No runtime parser** — smaller bundle, faster rendering
270
+ 3. **PO file support** — compatible with Poedit, Crowdin, Weblate
271
+ 4. **CLI-managed catalogs** — extract → translate → compile workflow
272
+ 5. **AI translation** — `npx fluenti translate --provider claude` for automated translation
package/llms.txt ADDED
@@ -0,0 +1,62 @@
1
+ # @fluenti/react
2
+
3
+ > React bindings for Fluenti — compile-time `t`, runtime-capable components, and client/server i18n helpers.
4
+
5
+ @fluenti/react provides the React runtime for Fluenti. The recommended public path is:
6
+
7
+ - compile-time authoring: `import { t } from '@fluenti/react'`
8
+ - runtime / imperative access: `useI18n()`
9
+ - server runtime access: `createServerI18n()` from `@fluenti/react/server`
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add @fluenti/core @fluenti/react
15
+ pnpm add -D @fluenti/cli @fluenti/vite-plugin
16
+ ```
17
+
18
+ ## Main Exports
19
+
20
+ Main entry (`@fluenti/react`):
21
+
22
+ - `I18nProvider`
23
+ - `useI18n()`
24
+ - `t`
25
+ - `Trans`, `Plural`, `Select`, `DateTime`, `NumberFormat`
26
+ - `msg`
27
+ - `I18nContext`
28
+
29
+ Type re-exports from `@fluenti/react`:
30
+
31
+ - `FluentiContext`, `I18nProviderProps`, `Messages`, `AllMessages`, `MessageDescriptor`, `CompileTimeMessageDescriptor`, `CompileTimeT`, `Locale`, `DateFormatOptions`, `NumberFormatOptions`
32
+ - `FluentiTransProps`, `FluentiPluralProps`, `FluentiSelectProps`, `FluentiDateTimeProps`, `NumberFormatProps` — component prop types
33
+
34
+ Server entry (`@fluenti/react/server`):
35
+
36
+ - `createServerI18n()`
37
+ - SSR helpers re-exported from `@fluenti/core`
38
+
39
+ ## Important Boundaries
40
+
41
+ - imported `t` is compile-time only and requires the Fluenti plugin / loader
42
+ - `useI18n().t()` is the full runtime API on the client
43
+ - `createServerI18n()` is the full runtime API on the server
44
+ - `Trans / Plural / Select / DateTime / NumberFormat` remain runtime-capable without the build plugin
45
+
46
+ ## Preferred Usage (compile-time first)
47
+
48
+ 1. t\`\` tagged template — t\`Hello {name}\` (primary compile-time API)
49
+ 2. `<Trans>` / `<Plural>` / `<Select>` — rich text with inline markup
50
+ 3. `msg` tagged template — outside components (route meta, stores)
51
+ 4. `useI18n().t()` / `createServerI18n().t()` — runtime fallback for dynamic keys only
52
+
53
+ ❌ AVOID: `t('some.key')` as the default — this is a legacy i18n pattern.
54
+
55
+ ## Lazy locale loading
56
+
57
+ React uses `loadMessages(locale)` on `I18nProvider` for async locale chunks.
58
+
59
+ ## Docs
60
+
61
+ - Full docs: https://fluenti.dev
62
+ - Source: https://github.com/usefluenti/fluenti
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluenti/react",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "description": "React bindings for Fluenti — I18nProvider, useI18n, Trans/Plural/Select components",
6
6
  "homepage": "https://fluenti.dev",
@@ -62,7 +62,8 @@
62
62
  }
63
63
  },
64
64
  "files": [
65
- "dist"
65
+ "dist",
66
+ "llms*.txt"
66
67
  ],
67
68
  "peerDependencies": {
68
69
  "react": ">=18.0.0",
@@ -74,8 +75,8 @@
74
75
  }
75
76
  },
76
77
  "dependencies": {
77
- "@fluenti/core": "0.2.1",
78
- "@fluenti/vite-plugin": "0.2.1"
78
+ "@fluenti/core": "0.3.1",
79
+ "@fluenti/vite-plugin": "0.3.1"
79
80
  },
80
81
  "devDependencies": {
81
82
  "typescript": "^5.9",
@@ -94,6 +95,7 @@
94
95
  "build": "vite build",
95
96
  "dev": "vite build --watch",
96
97
  "test": "vitest run",
97
- "typecheck": "tsc --noEmit"
98
+ "typecheck": "tsc --noEmit",
99
+ "bench": "vitest bench"
98
100
  }
99
101
  }
@@ -1,108 +0,0 @@
1
- import { Children as e, Fragment as t, cloneElement as n, createElement as r, isValidElement as i } from "react";
2
- import { hashMessage as a } from "@fluenti/core";
3
- //#region src/components/trans-core.ts
4
- function o(n) {
5
- let r = [], a = "";
6
- return e.forEach(n, (e) => {
7
- if (typeof e == "string" || typeof e == "number") a += String(e);
8
- else if (i(e)) {
9
- if (e.type === t) {
10
- let t = o(e.props.children);
11
- a += s(t.message, r.length), r.push(...t.components);
12
- return;
13
- }
14
- let n = r.length, i = o(e.props.children);
15
- r.push(e), r.push(...i.components), a += `<${n}>${s(i.message, n + 1)}</${n}>`;
16
- }
17
- }), {
18
- message: a,
19
- components: r
20
- };
21
- }
22
- function s(e, t) {
23
- return t === 0 ? e : e.replace(/<(\d+)(\/?>)/g, (e, n, r) => `<${Number(n) + t}${r}`).replace(/<\/(\d+)>/g, (e, n) => `</${Number(n) + t}>`);
24
- }
25
- function c(e, i) {
26
- let a = /<(\d+)>([\s\S]*?)<\/\1>/g, o = [], s = 0, l;
27
- for (a.lastIndex = 0, l = a.exec(e); l !== null;) {
28
- l.index > s && o.push(e.slice(s, l.index));
29
- let t = Number(l[1]), r = l[2], u = i[t];
30
- if (u) {
31
- let e = c(r, i);
32
- o.push(n(u, { key: `trans-${t}` }, e));
33
- } else o.push(r);
34
- s = a.lastIndex, l = a.exec(e);
35
- }
36
- return s < e.length && o.push(e.slice(s)), o.length === 1 ? o[0] : r(t, null, ...o);
37
- }
38
- //#endregion
39
- //#region src/components/plural-core.ts
40
- var l = [
41
- "zero",
42
- "one",
43
- "two",
44
- "few",
45
- "many",
46
- "other"
47
- ];
48
- //#endregion
49
- //#region src/components/icu-rich.tsx
50
- function u(e, t) {
51
- let n = [];
52
- for (let t of l) {
53
- let r = e[t];
54
- r !== void 0 && n.push(`${t === "zero" ? "=0" : t} {${r}}`);
55
- }
56
- return `{count, plural, ${t ? `offset:${t} ` : ""}${n.join(" ")}}`;
57
- }
58
- function d(e) {
59
- return `{value, select, ${Object.entries(e).filter(([, e]) => e !== void 0).map(([e, t]) => `${e} {${t}}`).join(" ")}}`;
60
- }
61
- function f(e) {
62
- let t = {}, n = {}, r = 0;
63
- for (let [i, a] of Object.entries(e)) {
64
- if (i === "other") {
65
- t.other = a;
66
- continue;
67
- }
68
- let e = /^[A-Za-z0-9_]+$/.test(i) ? i : `case_${r++}`;
69
- t[e] = a, n[i] = e;
70
- }
71
- return t.other === void 0 && (t.other = ""), {
72
- forms: t,
73
- valueMap: n
74
- };
75
- }
76
- function p(e) {
77
- let t = o(e);
78
- return {
79
- message: t.message,
80
- components: t.components
81
- };
82
- }
83
- function m(e, t) {
84
- let n = [], r = {};
85
- for (let i of e) {
86
- let e = t[i];
87
- if (e === void 0) continue;
88
- let a = p(e);
89
- r[i] = s(a.message, n.length), n.push(...a.components);
90
- }
91
- for (let [i, a] of Object.entries(t)) {
92
- if (e.includes(i) || a === void 0) continue;
93
- let t = p(a);
94
- r[i] = s(t.message, n.length), n.push(...t.components);
95
- }
96
- return {
97
- messages: r,
98
- components: n
99
- };
100
- }
101
- function h(e, t, n, r) {
102
- let i = n(e, t);
103
- return r.length > 0 ? c(i, r) : i;
104
- }
105
- //#endregion
106
- export { m as a, a as c, h as i, c as l, d as n, l as o, f as r, o as s, u as t };
107
-
108
- //# sourceMappingURL=icu-rich-DBeWY1k6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"icu-rich-DBeWY1k6.js","names":[],"sources":["../src/components/trans-core.ts","../src/components/plural-core.ts","../src/components/icu-rich.tsx"],"sourcesContent":["import {\n Children,\n isValidElement,\n cloneElement,\n createElement,\n Fragment,\n type ReactNode,\n type ReactElement,\n} from 'react'\nimport { hashMessage } from '@fluenti/core'\n\nexport { hashMessage }\n\n/**\n * Extract a message string and component list from React children.\n *\n * Converts:\n * <Trans>Hello <b>{name}</b>, welcome!</Trans>\n * Into:\n * message: \"Hello <0>{name}</0>, welcome!\"\n * components: [<b>{name}</b>]\n *\n * @internal\n */\nexport function extractMessage(children: ReactNode): {\n message: string\n components: ReactElement[]\n} {\n const components: ReactElement[] = []\n let message = ''\n\n Children.forEach(children, (child) => {\n if (typeof child === 'string' || typeof child === 'number') {\n message += String(child)\n } else if (isValidElement(child)) {\n if (child.type === Fragment) {\n const inner = extractMessage((child.props as { children?: ReactNode }).children)\n message += offsetIndices(inner.message, components.length)\n components.push(...inner.components)\n return\n }\n\n const idx = components.length\n const inner = extractMessage((child.props as { children?: ReactNode }).children)\n components.push(child)\n components.push(...inner.components)\n message += `<${idx}>${offsetIndices(inner.message, idx + 1)}</${idx}>`\n }\n })\n\n return { message, components }\n}\n\nexport function offsetIndices(message: string, offset: number): string {\n if (offset === 0) return message\n return message\n .replace(/<(\\d+)(\\/?>)/g, (_match, index: string, suffix: string) => `<${Number(index) + offset}${suffix}`)\n .replace(/<\\/(\\d+)>/g, (_match, index: string) => `</${Number(index) + offset}>`)\n}\n\n/**\n * Reconstruct a translated message string back into React elements.\n *\n * Parses \"<0>content</0>\" tags and replaces them with cloned components.\n *\n * @internal\n */\nexport function reconstruct(\n translated: string,\n components: ReactElement[],\n): ReactNode {\n const TAG_RE = /<(\\d+)>([\\s\\S]*?)<\\/\\1>/g\n const result: ReactNode[] = []\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n TAG_RE.lastIndex = 0\n match = TAG_RE.exec(translated)\n\n while (match !== null) {\n if (match.index > lastIndex) {\n result.push(translated.slice(lastIndex, match.index))\n }\n\n const idx = Number(match[1])\n const innerText = match[2]!\n const component = components[idx]\n\n if (component) {\n // Recursively reconstruct inner content\n const innerContent = reconstruct(innerText, components)\n result.push(\n cloneElement(component, { key: `trans-${idx}` }, innerContent),\n )\n } else {\n result.push(innerText)\n }\n\n lastIndex = TAG_RE.lastIndex\n match = TAG_RE.exec(translated)\n }\n\n if (lastIndex < translated.length) {\n result.push(translated.slice(lastIndex))\n }\n\n return result.length === 1 ? result[0]! : createElement(Fragment, null, ...result)\n}\n","import type { ReactNode } from 'react'\n\nexport const PLURAL_CATEGORIES = ['zero', 'one', 'two', 'few', 'many', 'other'] as const\nexport type PluralCategory = (typeof PLURAL_CATEGORIES)[number]\n\n/**\n * Resolve which plural category to use.\n * Checks for exact =0 match first, then falls back to CLDR rules.\n * @internal\n */\nexport function resolveCategory(\n value: number,\n locale: string,\n available: Record<string, boolean>,\n): PluralCategory {\n if (value === 0 && available['zero']) return 'zero'\n const cldr = new Intl.PluralRules(locale).select(value) as PluralCategory\n if (available[cldr]) return cldr\n return 'other'\n}\n\n/**\n * Replace `#` with the formatted value in a ReactNode.\n * @internal\n */\nexport function replaceHash(node: ReactNode, formatted: string): ReactNode {\n if (typeof node === 'string') {\n return node.replace(/#/g, formatted)\n }\n return node\n}\n","import type { MessageDescriptor } from '@fluenti/core'\nimport type { ReactElement, ReactNode } from 'react'\nimport { extractMessage, offsetIndices, reconstruct } from './trans-core'\nimport { PLURAL_CATEGORIES, type PluralCategory } from './plural-core'\n\nexport interface RichMessagePart {\n message: string\n components: ReactElement[]\n}\n\nexport function buildICUPluralMessage(\n forms: Partial<Record<PluralCategory, string>> & { other: string },\n offset?: number,\n): string {\n const parts: string[] = []\n for (const cat of PLURAL_CATEGORIES) {\n const text = forms[cat]\n if (text === undefined) continue\n parts.push(`${cat === 'zero' ? '=0' : cat} {${text}}`)\n }\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{count, plural, ${offsetPrefix}${parts.join(' ')}}`\n}\n\nexport function buildICUSelectMessage(\n forms: Record<string, string>,\n): string {\n const entries = Object.entries(forms).filter(([, text]) => text !== undefined)\n return `{value, select, ${entries.map(([key, text]) => `${key} {${text}}`).join(' ')}}`\n}\n\nexport function normalizeSelectForms(\n forms: Record<string, string>,\n): {\n forms: Record<string, string>\n valueMap: Record<string, string>\n} {\n const normalized: Record<string, string> = {}\n const valueMap: Record<string, string> = {}\n let index = 0\n\n for (const [key, text] of Object.entries(forms)) {\n if (key === 'other') {\n normalized['other'] = text\n continue\n }\n\n const safeKey = /^[A-Za-z0-9_]+$/.test(key) ? key : `case_${index++}`\n normalized[safeKey] = text\n valueMap[key] = safeKey\n }\n\n if (normalized['other'] === undefined) {\n normalized['other'] = ''\n }\n\n return { forms: normalized, valueMap }\n}\n\nexport function serializeRichNode(node: ReactNode): RichMessagePart {\n const extracted = extractMessage(node)\n return {\n message: extracted.message,\n components: extracted.components,\n }\n}\n\nexport function serializeRichForms<T extends string>(\n keys: readonly T[],\n forms: Partial<Record<T, ReactNode>> & Record<string, ReactNode | undefined>,\n): {\n messages: Partial<Record<T, string>> & Record<string, string>\n components: ReactElement[]\n} {\n const components: ReactElement[] = []\n const messages: Record<string, string> = {}\n\n for (const key of keys) {\n const value = forms[key]\n if (value === undefined) continue\n const extracted = serializeRichNode(value)\n messages[key] = offsetIndices(extracted.message, components.length)\n components.push(...extracted.components)\n }\n\n for (const [key, value] of Object.entries(forms)) {\n if (keys.includes(key as T) || value === undefined) continue\n const extracted = serializeRichNode(value)\n messages[key] = offsetIndices(extracted.message, components.length)\n components.push(...extracted.components)\n }\n\n return { messages: messages as Partial<Record<T, string>> & Record<string, string>, components }\n}\n\nexport function renderRichTranslation(\n descriptor: MessageDescriptor,\n values: Record<string, unknown> | undefined,\n translate: (descriptor: MessageDescriptor, values?: Record<string, unknown>) => string,\n components: ReactElement[],\n): ReactNode {\n const translated = translate(descriptor, values)\n return components.length > 0 ? reconstruct(translated, components) : translated\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- let e=require(`react`);require(`@fluenti/core`);function t(r){let i=[],a=``;return e.Children.forEach(r,r=>{if(typeof r==`string`||typeof r==`number`)a+=String(r);else if((0,e.isValidElement)(r)){if(r.type===e.Fragment){let e=t(r.props.children);a+=n(e.message,i.length),i.push(...e.components);return}let o=i.length,s=t(r.props.children);i.push(r),i.push(...s.components),a+=`<${o}>${n(s.message,o+1)}</${o}>`}}),{message:a,components:i}}function n(e,t){return t===0?e:e.replace(/<(\d+)(\/?>)/g,(e,n,r)=>`<${Number(n)+t}${r}`).replace(/<\/(\d+)>/g,(e,n)=>`</${Number(n)+t}>`)}function r(t,n){let i=/<(\d+)>([\s\S]*?)<\/\1>/g,a=[],o=0,s;for(i.lastIndex=0,s=i.exec(t);s!==null;){s.index>o&&a.push(t.slice(o,s.index));let c=Number(s[1]),l=s[2],u=n[c];if(u){let t=r(l,n);a.push((0,e.cloneElement)(u,{key:`trans-${c}`},t))}else a.push(l);o=i.lastIndex,s=i.exec(t)}return o<t.length&&a.push(t.slice(o)),a.length===1?a[0]:(0,e.createElement)(e.Fragment,null,...a)}var i=[`zero`,`one`,`two`,`few`,`many`,`other`];function a(e,t){let n=[];for(let t of i){let r=e[t];r!==void 0&&n.push(`${t===`zero`?`=0`:t} {${r}}`)}return`{count, plural, ${t?`offset:${t} `:``}${n.join(` `)}}`}function o(e){return`{value, select, ${Object.entries(e).filter(([,e])=>e!==void 0).map(([e,t])=>`${e} {${t}}`).join(` `)}}`}function s(e){let t={},n={},r=0;for(let[i,a]of Object.entries(e)){if(i===`other`){t.other=a;continue}let e=/^[A-Za-z0-9_]+$/.test(i)?i:`case_${r++}`;t[e]=a,n[i]=e}return t.other===void 0&&(t.other=``),{forms:t,valueMap:n}}function c(e){let n=t(e);return{message:n.message,components:n.components}}function l(e,t){let r=[],i={};for(let a of e){let e=t[a];if(e===void 0)continue;let o=c(e);i[a]=n(o.message,r.length),r.push(...o.components)}for(let[a,o]of Object.entries(t)){if(e.includes(a)||o===void 0)continue;let t=c(o);i[a]=n(t.message,r.length),r.push(...t.components)}return{messages:i,components:r}}function u(e,t,n,i){let a=n(e,t);return i.length>0?r(a,i):a}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return a}});
2
- //# sourceMappingURL=icu-rich-XY1SdM5K.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"icu-rich-XY1SdM5K.cjs","names":[],"sources":["../src/components/trans-core.ts","../src/components/plural-core.ts","../src/components/icu-rich.tsx"],"sourcesContent":["import {\n Children,\n isValidElement,\n cloneElement,\n createElement,\n Fragment,\n type ReactNode,\n type ReactElement,\n} from 'react'\nimport { hashMessage } from '@fluenti/core'\n\nexport { hashMessage }\n\n/**\n * Extract a message string and component list from React children.\n *\n * Converts:\n * <Trans>Hello <b>{name}</b>, welcome!</Trans>\n * Into:\n * message: \"Hello <0>{name}</0>, welcome!\"\n * components: [<b>{name}</b>]\n *\n * @internal\n */\nexport function extractMessage(children: ReactNode): {\n message: string\n components: ReactElement[]\n} {\n const components: ReactElement[] = []\n let message = ''\n\n Children.forEach(children, (child) => {\n if (typeof child === 'string' || typeof child === 'number') {\n message += String(child)\n } else if (isValidElement(child)) {\n if (child.type === Fragment) {\n const inner = extractMessage((child.props as { children?: ReactNode }).children)\n message += offsetIndices(inner.message, components.length)\n components.push(...inner.components)\n return\n }\n\n const idx = components.length\n const inner = extractMessage((child.props as { children?: ReactNode }).children)\n components.push(child)\n components.push(...inner.components)\n message += `<${idx}>${offsetIndices(inner.message, idx + 1)}</${idx}>`\n }\n })\n\n return { message, components }\n}\n\nexport function offsetIndices(message: string, offset: number): string {\n if (offset === 0) return message\n return message\n .replace(/<(\\d+)(\\/?>)/g, (_match, index: string, suffix: string) => `<${Number(index) + offset}${suffix}`)\n .replace(/<\\/(\\d+)>/g, (_match, index: string) => `</${Number(index) + offset}>`)\n}\n\n/**\n * Reconstruct a translated message string back into React elements.\n *\n * Parses \"<0>content</0>\" tags and replaces them with cloned components.\n *\n * @internal\n */\nexport function reconstruct(\n translated: string,\n components: ReactElement[],\n): ReactNode {\n const TAG_RE = /<(\\d+)>([\\s\\S]*?)<\\/\\1>/g\n const result: ReactNode[] = []\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n TAG_RE.lastIndex = 0\n match = TAG_RE.exec(translated)\n\n while (match !== null) {\n if (match.index > lastIndex) {\n result.push(translated.slice(lastIndex, match.index))\n }\n\n const idx = Number(match[1])\n const innerText = match[2]!\n const component = components[idx]\n\n if (component) {\n // Recursively reconstruct inner content\n const innerContent = reconstruct(innerText, components)\n result.push(\n cloneElement(component, { key: `trans-${idx}` }, innerContent),\n )\n } else {\n result.push(innerText)\n }\n\n lastIndex = TAG_RE.lastIndex\n match = TAG_RE.exec(translated)\n }\n\n if (lastIndex < translated.length) {\n result.push(translated.slice(lastIndex))\n }\n\n return result.length === 1 ? result[0]! : createElement(Fragment, null, ...result)\n}\n","import type { ReactNode } from 'react'\n\nexport const PLURAL_CATEGORIES = ['zero', 'one', 'two', 'few', 'many', 'other'] as const\nexport type PluralCategory = (typeof PLURAL_CATEGORIES)[number]\n\n/**\n * Resolve which plural category to use.\n * Checks for exact =0 match first, then falls back to CLDR rules.\n * @internal\n */\nexport function resolveCategory(\n value: number,\n locale: string,\n available: Record<string, boolean>,\n): PluralCategory {\n if (value === 0 && available['zero']) return 'zero'\n const cldr = new Intl.PluralRules(locale).select(value) as PluralCategory\n if (available[cldr]) return cldr\n return 'other'\n}\n\n/**\n * Replace `#` with the formatted value in a ReactNode.\n * @internal\n */\nexport function replaceHash(node: ReactNode, formatted: string): ReactNode {\n if (typeof node === 'string') {\n return node.replace(/#/g, formatted)\n }\n return node\n}\n","import type { MessageDescriptor } from '@fluenti/core'\nimport type { ReactElement, ReactNode } from 'react'\nimport { extractMessage, offsetIndices, reconstruct } from './trans-core'\nimport { PLURAL_CATEGORIES, type PluralCategory } from './plural-core'\n\nexport interface RichMessagePart {\n message: string\n components: ReactElement[]\n}\n\nexport function buildICUPluralMessage(\n forms: Partial<Record<PluralCategory, string>> & { other: string },\n offset?: number,\n): string {\n const parts: string[] = []\n for (const cat of PLURAL_CATEGORIES) {\n const text = forms[cat]\n if (text === undefined) continue\n parts.push(`${cat === 'zero' ? '=0' : cat} {${text}}`)\n }\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{count, plural, ${offsetPrefix}${parts.join(' ')}}`\n}\n\nexport function buildICUSelectMessage(\n forms: Record<string, string>,\n): string {\n const entries = Object.entries(forms).filter(([, text]) => text !== undefined)\n return `{value, select, ${entries.map(([key, text]) => `${key} {${text}}`).join(' ')}}`\n}\n\nexport function normalizeSelectForms(\n forms: Record<string, string>,\n): {\n forms: Record<string, string>\n valueMap: Record<string, string>\n} {\n const normalized: Record<string, string> = {}\n const valueMap: Record<string, string> = {}\n let index = 0\n\n for (const [key, text] of Object.entries(forms)) {\n if (key === 'other') {\n normalized['other'] = text\n continue\n }\n\n const safeKey = /^[A-Za-z0-9_]+$/.test(key) ? key : `case_${index++}`\n normalized[safeKey] = text\n valueMap[key] = safeKey\n }\n\n if (normalized['other'] === undefined) {\n normalized['other'] = ''\n }\n\n return { forms: normalized, valueMap }\n}\n\nexport function serializeRichNode(node: ReactNode): RichMessagePart {\n const extracted = extractMessage(node)\n return {\n message: extracted.message,\n components: extracted.components,\n }\n}\n\nexport function serializeRichForms<T extends string>(\n keys: readonly T[],\n forms: Partial<Record<T, ReactNode>> & Record<string, ReactNode | undefined>,\n): {\n messages: Partial<Record<T, string>> & Record<string, string>\n components: ReactElement[]\n} {\n const components: ReactElement[] = []\n const messages: Record<string, string> = {}\n\n for (const key of keys) {\n const value = forms[key]\n if (value === undefined) continue\n const extracted = serializeRichNode(value)\n messages[key] = offsetIndices(extracted.message, components.length)\n components.push(...extracted.components)\n }\n\n for (const [key, value] of Object.entries(forms)) {\n if (keys.includes(key as T) || value === undefined) continue\n const extracted = serializeRichNode(value)\n messages[key] = offsetIndices(extracted.message, components.length)\n components.push(...extracted.components)\n }\n\n return { messages: messages as Partial<Record<T, string>> & Record<string, string>, components }\n}\n\nexport function renderRichTranslation(\n descriptor: MessageDescriptor,\n values: Record<string, unknown> | undefined,\n translate: (descriptor: MessageDescriptor, values?: Record<string, unknown>) => string,\n components: ReactElement[],\n): ReactNode {\n const translated = translate(descriptor, values)\n return components.length > 0 ? reconstruct(translated, components) : translated\n}\n"],"mappings":"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"}