@fluenti/react 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 +244 -0
- package/dist/__tests__/DateTime.test.d.ts +2 -0
- package/dist/__tests__/DateTime.test.d.ts.map +1 -0
- package/dist/__tests__/Number.test.d.ts +2 -0
- package/dist/__tests__/Number.test.d.ts.map +1 -0
- package/dist/__tests__/Plural.test.d.ts +2 -0
- package/dist/__tests__/Plural.test.d.ts.map +1 -0
- package/dist/__tests__/Select.test.d.ts +2 -0
- package/dist/__tests__/Select.test.d.ts.map +1 -0
- package/dist/__tests__/Trans.test.d.ts +2 -0
- package/dist/__tests__/Trans.test.d.ts.map +1 -0
- package/dist/__tests__/locale-switch.test.d.ts +2 -0
- package/dist/__tests__/locale-switch.test.d.ts.map +1 -0
- package/dist/__tests__/plural-core.test.d.ts +2 -0
- package/dist/__tests__/plural-core.test.d.ts.map +1 -0
- package/dist/__tests__/provider.test.d.ts +2 -0
- package/dist/__tests__/provider.test.d.ts.map +1 -0
- package/dist/__tests__/server.test.d.ts +2 -0
- package/dist/__tests__/server.test.d.ts.map +1 -0
- package/dist/__tests__/trans-core.test.d.ts +2 -0
- package/dist/__tests__/trans-core.test.d.ts.map +1 -0
- package/dist/compile-time-t.d.ts +3 -0
- package/dist/compile-time-t.d.ts.map +1 -0
- package/dist/components/DateTime.d.ts +16 -0
- package/dist/components/DateTime.d.ts.map +1 -0
- package/dist/components/Number.d.ts +16 -0
- package/dist/components/Number.d.ts.map +1 -0
- package/dist/components/Plural.d.ts +35 -0
- package/dist/components/Plural.d.ts.map +1 -0
- package/dist/components/Select.d.ts +32 -0
- package/dist/components/Select.d.ts.map +1 -0
- package/dist/components/Trans.d.ts +29 -0
- package/dist/components/Trans.d.ts.map +1 -0
- package/dist/components/icu-rich.d.ts +22 -0
- package/dist/components/icu-rich.d.ts.map +1 -0
- package/dist/components/plural-core.d.ts +15 -0
- package/dist/components/plural-core.d.ts.map +1 -0
- package/dist/components/trans-core.d.ts +28 -0
- package/dist/components/trans-core.d.ts.map +1 -0
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/global-registry.d.ts +18 -0
- package/dist/global-registry.d.ts.map +1 -0
- package/dist/hooks/__useI18n.d.ts +12 -0
- package/dist/hooks/__useI18n.d.ts.map +1 -0
- package/dist/hooks/useI18n.d.ts +11 -0
- package/dist/hooks/useI18n.d.ts.map +1 -0
- package/dist/icu-rich-DBeWY1k6.js +108 -0
- package/dist/icu-rich-DBeWY1k6.js.map +1 -0
- package/dist/icu-rich-XY1SdM5K.cjs +2 -0
- package/dist/icu-rich-XY1SdM5K.cjs.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/dist/msg.d.ts +2 -0
- package/dist/msg.d.ts.map +1 -0
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/server.cjs +2 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.ts +240 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +122 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { I18nContextValue } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Primary hook for accessing i18n functions.
|
|
4
|
+
*
|
|
5
|
+
* Returns locale, setLocale, isLoading, loadedLocales, preloadLocale,
|
|
6
|
+
* and the underlying i18n instance with t(), d(), n() methods.
|
|
7
|
+
*
|
|
8
|
+
* @throws If used outside of `<I18nProvider>`
|
|
9
|
+
*/
|
|
10
|
+
export declare function useI18n(): I18nContextValue;
|
|
11
|
+
//# sourceMappingURL=useI18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useI18n.d.ts","sourceRoot":"","sources":["../../src/hooks/useI18n.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEhD;;;;;;;GAOG;AACH,wBAAgB,OAAO,IAAI,gBAAgB,CAS1C"}
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./icu-rich-XY1SdM5K.cjs`);let t=require(`react`),n=require(`@fluenti/core`),r=require(`react/jsx-runtime`);var i=(0,t.createContext)(null),a=Symbol.for(`fluenti.runtime.react`);function o(){let e=globalThis[a];return typeof e==`object`&&e?e:null}function s({locale:e,fallbackLocale:a,messages:s,loadMessages:c,fallbackChain:l,dateFormats:u,numberFormats:d,missing:f,children:p}){let[m,h]=(0,t.useState)(e),[g,_]=(0,t.useState)(!1),[v,y]=(0,t.useState)(s??{}),[b,x]=(0,t.useState)(s?Object.keys(s):[]),S=(0,t.useRef)(v);S.current=v;let C=(0,t.useRef)(0),w=(0,t.useMemo)(()=>{let e={locale:m,messages:v};return a!==void 0&&(e.fallbackLocale=a),l!==void 0&&(e.fallbackChain=l),u!==void 0&&(e.dateFormats=u),d!==void 0&&(e.numberFormats=d),f!==void 0&&(e.missing=f),(0,n.createFluent)(e)},[m,v,a,l,u,d,f]);(0,t.useEffect)(()=>{e!==m&&T(e)},[e]);let T=(0,t.useCallback)(async e=>{let t=++C.current;if(S.current[e]&&!c){h(e);return}let n=c?o():null;if(S.current[e]){n?.__switchLocale&&await n.__switchLocale(e),h(e);return}if(!c){console.warn(`[fluenti] No messages for locale "${e}" and no loadMessages function provided`);return}_(!0);try{let r=await c(e);if(t!==C.current)return;let i=typeof r==`object`&&r&&`default`in r?r.default:r;y(t=>({...t,[e]:i})),x(t=>[...new Set([...t,e])]),n?.__switchLocale&&await n.__switchLocale(e),h(e)}catch(n){t===C.current&&console.error(`[fluenti] Failed to load locale "${e}"`,n)}finally{t===C.current&&_(!1)}},[c]),E=(0,t.useCallback)(async e=>{let t=o();if(!(S.current[e]||!c))try{let n=await c(e),r=typeof n==`object`&&n&&`default`in n?n.default:n;y(t=>({...t,[e]:r})),x(t=>[...new Set([...t,e])]),t?.__preloadLocale&&await t.__preloadLocale(e)}catch{}},[c]),D=(0,t.useMemo)(()=>({i18n:w,t:w.t.bind(w),d:w.d.bind(w),n:w.n.bind(w),format:w.format.bind(w),loadMessages:w.loadMessages.bind(w),getLocales:w.getLocales.bind(w),locale:m,setLocale:T,isLoading:g,loadedLocales:b,preloadLocale:E}),[w,m,T,g,b,E]);return(0,r.jsx)(i.Provider,{value:D,children:p})}function c(){let e=(0,t.useContext)(i);if(!e)throw Error(`[fluenti] useI18n() must be used within an <I18nProvider>. Wrap your app with <I18nProvider> to provide i18n context.`);return e}var l=((...e)=>{throw Error("[fluenti] `t` imported from '@fluenti/react' is a compile-time API. Use it only with the Fluenti build transform inside a component or custom hook. For runtime lookups, use useI18n().t(...).")}),u=(0,t.memo)(function({children:a,id:o,context:s,comment:c,render:l,__id:u,__message:d,__components:f}){let p=(0,t.useContext)(i);if(!p)throw Error(`[fluenti] <Trans> must be used within an <I18nProvider>`);let m=d!==void 0,{message:h,components:g}=(0,t.useMemo)(()=>m?{message:d,components:f??[]}:e.s(a),[m,d,f,a]),_=(0,t.useMemo)(()=>o??u??(0,n.hashMessage)(h,s),[o,u,h,s]),v=e.c(p.i18n.t({id:_,message:h,...s===void 0?{}:{context:s},...c===void 0?{}:{comment:c}}),g);return l?l(v):(0,r.jsx)(r.Fragment,{children:v})}),d=(0,t.memo)(function({value:a,id:o,context:s,comment:c,zero:l,one:u,two:d,few:f,many:p,other:m,offset:h}){let g=(0,t.useContext)(i);if(!g)throw Error(`[fluenti] <Plural> must be used within an <I18nProvider>`);let{messages:_,components:v}=e.a(e.o,{zero:l,one:u,two:d,few:f,many:p,other:m}),y=e.t({..._.zero!==void 0&&{zero:_.zero},..._.one!==void 0&&{one:_.one},..._.two!==void 0&&{two:_.two},..._.few!==void 0&&{few:_.few},..._.many!==void 0&&{many:_.many},other:_.other??``},h);return(0,r.jsx)(r.Fragment,{children:e.i({id:o??(s===void 0?y:(0,n.hashMessage)(y,s)),message:y,...s===void 0?{}:{context:s},...c===void 0?{}:{comment:c}},{count:a},(e,t)=>g.i18n.t(e,t),v)})});function f(a){let o=(0,t.useContext)(i);if(!o)throw Error(`[fluenti] <Select> must be used within an <I18nProvider>`);let{value:s,id:c,context:l,comment:u,other:d,options:f,...p}=a,m=f===void 0?{...Object.fromEntries(Object.entries(p).filter(([e])=>![`value`,`id`,`context`,`comment`,`options`,`other`].includes(e))),other:d}:{...f,other:d},h=[...Object.keys(m).filter(e=>e!==`other`),`other`],{messages:g,components:_}=e.a(h,m),v=e.r(Object.fromEntries([...h].map(e=>[e,g[e]??``]))),y=e.n(v.forms);return(0,r.jsx)(r.Fragment,{children:e.i({id:c??(l===void 0?y:(0,n.hashMessage)(y,l)),message:y,...l===void 0?{}:{context:l},...u===void 0?{}:{comment:u}},{value:v.valueMap[s]??`other`},(e,t)=>o.i18n.t(e,t),_)})}function p({value:e,style:n}){let a=(0,t.useContext)(i);if(!a)throw Error(`[fluenti] <DateTime> must be used within an <I18nProvider>`);return(0,r.jsx)(r.Fragment,{children:a.i18n.d(e,n)})}function m({value:e,style:n}){let a=(0,t.useContext)(i);if(!a)throw Error(`[fluenti] <Number> must be used within an <I18nProvider>`);return(0,r.jsx)(r.Fragment,{children:a.i18n.n(e,n)})}exports.DateTime=p,exports.I18nContext=i,exports.I18nProvider=s,exports.NumberFormat=m,exports.Plural=d,exports.Select=f,exports.Trans=u,Object.defineProperty(exports,`msg`,{enumerable:!0,get:function(){return n.msg}}),exports.t=l,exports.useI18n=c;
|
|
3
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useI18n.ts","../src/compile-time-t.ts","../src/components/Trans.tsx","../src/components/Plural.tsx","../src/components/Select.tsx","../src/components/DateTime.tsx","../src/components/Number.tsx"],"sourcesContent":["import { createContext } from 'react'\nimport type { I18nContextValue } from './types'\n\nexport const I18nContext = createContext<I18nContextValue | null>(null)\n","import { useState, useCallback, useEffect, useMemo, useRef } from 'react'\nimport { createFluent } from '@fluenti/core'\nimport type { Messages } from '@fluenti/core'\nimport { I18nContext } from './context'\nimport type { I18nProviderProps } from './types'\n\ninterface SplitRuntimeModule {\n __switchLocale?: (locale: string) => Promise<void>\n __preloadLocale?: (locale: string) => Promise<void>\n}\n\nconst SPLIT_RUNTIME_KEY = Symbol.for('fluenti.runtime.react')\n\nfunction getSplitRuntimeModule(): SplitRuntimeModule | null {\n const runtime = (globalThis as Record<PropertyKey, unknown>)[SPLIT_RUNTIME_KEY]\n return typeof runtime === 'object' && runtime !== null\n ? runtime as SplitRuntimeModule\n : null\n}\n\nexport function I18nProvider({\n locale,\n fallbackLocale,\n messages,\n loadMessages,\n fallbackChain,\n dateFormats,\n numberFormats,\n missing,\n children,\n}: I18nProviderProps) {\n const [currentLocale, setCurrentLocale] = useState(locale)\n const [isLoading, setIsLoading] = useState(false)\n const [loadedMessages, setLoadedMessages] = useState<Record<string, Messages>>(\n messages ?? {},\n )\n const [loadedLocales, setLoadedLocales] = useState<string[]>(\n messages ? Object.keys(messages) : [],\n )\n\n // Use ref to avoid stale closures in callbacks\n const loadedMessagesRef = useRef(loadedMessages)\n loadedMessagesRef.current = loadedMessages\n\n // Guard against out-of-order async locale loads (race condition protection)\n const localeRequestRef = useRef(0)\n\n const i18n = useMemo(() => {\n const config: Parameters<typeof createFluent>[0] = {\n locale: currentLocale,\n messages: loadedMessages,\n }\n if (fallbackLocale !== undefined) config.fallbackLocale = fallbackLocale\n if (fallbackChain !== undefined) config.fallbackChain = fallbackChain\n if (dateFormats !== undefined) config.dateFormats = dateFormats\n if (numberFormats !== undefined) config.numberFormats = numberFormats\n if (missing !== undefined) config.missing = missing\n return createFluent(config)\n }, [currentLocale, loadedMessages, fallbackLocale, fallbackChain, dateFormats, numberFormats, missing])\n\n // Sync external locale prop changes\n useEffect(() => {\n if (locale !== currentLocale) {\n void handleSetLocale(locale)\n }\n // Intentionally only depend on `locale` — we want to sync when the\n // external prop changes, not when internal state (`currentLocale`,\n // `handleSetLocale`) updates, which would cause infinite re-renders.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [locale])\n\n const handleSetLocale = useCallback(\n async (newLocale: string) => {\n const requestId = ++localeRequestRef.current\n\n if (loadedMessagesRef.current[newLocale] && !loadMessages) {\n setCurrentLocale(newLocale)\n return\n }\n\n const splitRuntime = loadMessages ? getSplitRuntimeModule() : null\n\n if (loadedMessagesRef.current[newLocale]) {\n if (splitRuntime?.__switchLocale) {\n await splitRuntime.__switchLocale(newLocale)\n }\n setCurrentLocale(newLocale)\n return\n }\n\n if (!loadMessages) {\n console.warn(\n `[fluenti] No messages for locale \"${newLocale}\" and no loadMessages function provided`,\n )\n return\n }\n\n setIsLoading(true)\n try {\n const msgs = await loadMessages(newLocale)\n\n // A newer request has superseded this one — discard stale result\n if (requestId !== localeRequestRef.current) return\n\n const resolved: Messages =\n typeof msgs === 'object' && msgs !== null && 'default' in msgs\n ? (msgs as { default: Messages }).default\n : (msgs as Messages)\n setLoadedMessages((prev) => ({ ...prev, [newLocale]: resolved }))\n setLoadedLocales((prev) => [...new Set([...prev, newLocale])])\n if (splitRuntime?.__switchLocale) {\n await splitRuntime.__switchLocale(newLocale)\n }\n setCurrentLocale(newLocale)\n } catch (err) {\n // Only log if this request is still the latest\n if (requestId === localeRequestRef.current) {\n console.error(`[fluenti] Failed to load locale \"${newLocale}\"`, err)\n }\n } finally {\n if (requestId === localeRequestRef.current) {\n setIsLoading(false)\n }\n }\n },\n [loadMessages],\n )\n\n const preloadLocale = useCallback(\n async (loc: string) => {\n const splitRuntime = getSplitRuntimeModule()\n if (loadedMessagesRef.current[loc] || !loadMessages) return\n try {\n const msgs = await loadMessages(loc)\n const resolved: Messages =\n typeof msgs === 'object' && msgs !== null && 'default' in msgs\n ? (msgs as { default: Messages }).default\n : (msgs as Messages)\n setLoadedMessages((prev) => ({ ...prev, [loc]: resolved }))\n setLoadedLocales((prev) => [...new Set([...prev, loc])])\n if (splitRuntime?.__preloadLocale) {\n await splitRuntime.__preloadLocale(loc)\n }\n } catch {\n // Silent fail for preload\n }\n },\n [loadMessages],\n )\n\n const ctx = useMemo(\n () => ({\n i18n,\n t: i18n.t.bind(i18n),\n d: i18n.d.bind(i18n),\n n: i18n.n.bind(i18n),\n format: i18n.format.bind(i18n),\n loadMessages: i18n.loadMessages.bind(i18n),\n getLocales: i18n.getLocales.bind(i18n),\n locale: currentLocale,\n setLocale: handleSetLocale,\n isLoading,\n loadedLocales,\n preloadLocale,\n }),\n [i18n, currentLocale, handleSetLocale, isLoading, loadedLocales, preloadLocale],\n )\n\n return <I18nContext.Provider value={ctx}>{children}</I18nContext.Provider>\n}\n","import { useContext } from 'react'\nimport { I18nContext } from '../context'\nimport type { I18nContextValue } from '../types'\n\n/**\n * Primary hook for accessing i18n functions.\n *\n * Returns locale, setLocale, isLoading, loadedLocales, preloadLocale,\n * and the underlying i18n instance with t(), d(), n() methods.\n *\n * @throws If used outside of `<I18nProvider>`\n */\nexport function useI18n(): I18nContextValue {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error(\n '[fluenti] useI18n() must be used within an <I18nProvider>. ' +\n 'Wrap your app with <I18nProvider> to provide i18n context.',\n )\n }\n return ctx\n}\n","import type { CompileTimeT } from '@fluenti/core'\n\nexport const t: CompileTimeT = ((..._args: unknown[]) => {\n throw new Error(\n \"[fluenti] `t` imported from '@fluenti/react' is a compile-time API. \" +\n 'Use it only with the Fluenti build transform inside a component or custom hook. ' +\n 'For runtime lookups, use useI18n().t(...).',\n )\n}) as CompileTimeT\n","import {\n memo,\n useContext,\n useMemo,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport { I18nContext } from '../context'\nimport { hashMessage, extractMessage, reconstruct } from './trans-core'\n\nexport interface TransProps {\n /** Source text with embedded components */\n children: ReactNode\n /** Override auto-generated hash ID */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Context comment for translators */\n comment?: string\n /** Custom render wrapper */\n render?: (translation: ReactNode) => ReactNode\n /** @internal Pre-computed message ID from build plugin */\n __id?: string\n /** @internal Pre-computed ICU message from build plugin */\n __message?: string\n /** @internal Pre-computed component list from build plugin */\n __components?: ReactElement[]\n}\n\n/**\n * `<Trans>` component for rich-text translations containing nested React elements.\n *\n * @example\n * ```tsx\n * <Trans>Read the <a href=\"/docs\">documentation</a> for more info.</Trans>\n * ```\n */\nexport const Trans = memo(function Trans({\n children,\n id,\n context,\n comment,\n render,\n __id,\n __message,\n __components,\n}: TransProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Trans> must be used within an <I18nProvider>')\n }\n\n // Fast path: build plugin pre-computed message and components\n const hasPrecomputed = __message !== undefined\n\n const { message, components } = useMemo(\n () => hasPrecomputed\n ? { message: __message!, components: __components ?? [] }\n : extractMessage(children),\n [hasPrecomputed, __message, __components, children],\n )\n const messageId = useMemo(\n () => id ?? __id ?? hashMessage(message, context),\n [id, __id, message, context],\n )\n\n const translated = ctx.i18n.t(\n {\n id: messageId,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n },\n )\n\n const result = reconstruct(translated, components)\n return render ? render(result) : <>{result}</>\n})\n","import { memo, useContext, type ReactNode } from 'react'\nimport { hashMessage } from '@fluenti/core'\nimport { I18nContext } from '../context'\nimport { PLURAL_CATEGORIES, type PluralCategory } from './plural-core'\nimport { buildICUPluralMessage, renderRichTranslation, serializeRichForms } from './icu-rich'\n\nexport interface PluralProps {\n /** The count value */\n value: number\n /** Override the auto-generated synthetic ICU message id */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Translator-facing note preserved in extraction catalogs */\n comment?: string\n /** Text for zero (if language supports) */\n zero?: ReactNode\n /** Singular form. `#` replaced with value */\n one?: ReactNode\n /** Dual form (Arabic, etc.) */\n two?: ReactNode\n /** Few form (Slavic languages, etc.) */\n few?: ReactNode\n /** Many form */\n many?: ReactNode\n /** Default plural form */\n other: ReactNode\n /** Offset from value before selecting form */\n offset?: number\n}\n\n/**\n * `<Plural>` — ICU plural handling as a component.\n *\n * @example\n * ```tsx\n * <Plural value={count} zero=\"No messages\" one=\"# message\" other=\"# messages\" />\n * ```\n */\nexport const Plural = memo(function Plural({\n value,\n id,\n context,\n comment,\n zero,\n one,\n two,\n few,\n many,\n other,\n offset,\n}: PluralProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Plural> must be used within an <I18nProvider>')\n }\n\n const forms: Record<PluralCategory, ReactNode | undefined> = {\n zero,\n one,\n two,\n few,\n many,\n other,\n }\n const { messages, components } = serializeRichForms(PLURAL_CATEGORIES, forms)\n const icuMessage = buildICUPluralMessage(\n {\n ...(messages.zero !== undefined && { zero: messages.zero }),\n ...(messages.one !== undefined && { one: messages.one }),\n ...(messages.two !== undefined && { two: messages.two }),\n ...(messages.few !== undefined && { few: messages.few }),\n ...(messages.many !== undefined && { many: messages.many }),\n other: messages.other ?? '',\n },\n offset,\n )\n\n const descriptor = {\n id: id ?? (context === undefined ? icuMessage : hashMessage(icuMessage, context)),\n message: icuMessage,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n }\n\n return <>{renderRichTranslation(descriptor, { count: value }, (desc, values) => ctx.i18n.t(desc, values), components)}</>\n})\n","import { useContext, type ReactNode } from 'react'\nimport { hashMessage } from '@fluenti/core'\nimport { I18nContext } from '../context'\nimport { buildICUSelectMessage, normalizeSelectForms, renderRichTranslation, serializeRichForms } from './icu-rich'\n\nexport interface SelectProps {\n /** The selector value */\n value: string\n /** Override the auto-generated synthetic ICU message id */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Translator-facing note preserved in extraction catalogs */\n comment?: string\n /** Default case */\n other: ReactNode\n /** Type-safe named options. Takes precedence over direct case props. */\n options?: Record<string, ReactNode>\n /** Named cases — any string key maps to a ReactNode */\n [key: string]: ReactNode | Record<string, ReactNode> | undefined\n}\n\n/**\n * `<Select>` — ICU select for gender, role, or other categorical values.\n *\n * @example\n * ```tsx\n * <Select\n * value={gender}\n * male=\"He liked your post\"\n * female=\"She liked your post\"\n * other=\"They liked your post\"\n * />\n * ```\n */\nexport function Select(props: SelectProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Select> must be used within an <I18nProvider>')\n }\n\n const { value, id, context, comment, other, options, ...cases } = props\n const forms: Record<string, ReactNode | undefined> = options === undefined\n ? {\n ...Object.fromEntries(\n Object.entries(cases).filter(([key]) => !['value', 'id', 'context', 'comment', 'options', 'other'].includes(key)),\n ),\n other,\n }\n : {\n ...options,\n other,\n }\n\n const orderedKeys = [...Object.keys(forms).filter(key => key !== 'other'), 'other'] as const\n const { messages, components } = serializeRichForms(orderedKeys, forms)\n const normalized = normalizeSelectForms(\n Object.fromEntries(\n [...orderedKeys].map((key) => [key, messages[key] ?? '']),\n ),\n )\n const icuMessage = buildICUSelectMessage(normalized.forms)\n\n const descriptor = {\n id: id ?? (context === undefined ? icuMessage : hashMessage(icuMessage, context)),\n message: icuMessage,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n }\n\n return <>{renderRichTranslation(\n descriptor,\n { value: normalized.valueMap[value] ?? 'other' },\n (desc, values) => ctx.i18n.t(desc, values),\n components,\n )}</>\n}\n","import { useContext } from 'react'\nimport { I18nContext } from '../context'\n\nexport interface DateTimeProps {\n /** Date value to format */\n value: Date | number\n /** Named format style */\n style?: string\n}\n\n/**\n * `<DateTime>` — formatting component using Intl APIs.\n *\n * @example\n * ```tsx\n * <DateTime value={new Date()} style=\"long\" />\n * ```\n */\nexport function DateTime({ value, style }: DateTimeProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <DateTime> must be used within an <I18nProvider>')\n }\n return <>{ctx.i18n.d(value, style)}</>\n}\n","import { useContext } from 'react'\nimport { I18nContext } from '../context'\n\nexport interface NumberProps {\n /** Number value to format */\n value: number\n /** Named format style */\n style?: string\n}\n\n/**\n * `<Number>` — number formatting component using Intl APIs.\n *\n * @example\n * ```tsx\n * <Number value={1234.56} style=\"currency\" />\n * ```\n */\nexport function NumberFormat({ value, style }: NumberProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Number> must be used within an <I18nProvider>')\n }\n return <>{ctx.i18n.n(value, style)}</>\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { I18nProvider } from './provider';
|
|
2
|
+
export { I18nContext } from './context';
|
|
3
|
+
export { useI18n } from './hooks/useI18n';
|
|
4
|
+
export { t } from './compile-time-t';
|
|
5
|
+
export { Trans } from './components/Trans';
|
|
6
|
+
export { Plural } from './components/Plural';
|
|
7
|
+
export { Select } from './components/Select';
|
|
8
|
+
export { DateTime } from './components/DateTime';
|
|
9
|
+
export { NumberFormat } from './components/Number';
|
|
10
|
+
export { msg } from './msg';
|
|
11
|
+
export type { I18nContextValue, I18nProviderProps, Messages, AllMessages, MessageDescriptor, CompileTimeMessageDescriptor, CompileTimeT, Locale, DateFormatOptions, NumberFormatOptions, FluentInstanceExtended, } from './types';
|
|
12
|
+
export type { TransProps } from './components/Trans';
|
|
13
|
+
export type { PluralProps } from './components/Plural';
|
|
14
|
+
export type { SelectProps } from './components/Select';
|
|
15
|
+
export type { DateTimeProps } from './components/DateTime';
|
|
16
|
+
export type { NumberProps } from './components/Number';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGzC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,kBAAkB,CAAA;AAGpC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAG3B,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,4BAA4B,EAC5B,YAAY,EACZ,MAAM,EACN,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,SAAS,CAAA;AAChB,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./icu-rich-DBeWY1k6.js";
|
|
4
|
+
import { createContext as l, memo as u, useCallback as d, useContext as f, useEffect as p, useMemo as m, useRef as h, useState as g } from "react";
|
|
5
|
+
import { createFluent as _, hashMessage as v, msg as y } from "@fluenti/core";
|
|
6
|
+
import { Fragment as b, jsx as x } from "react/jsx-runtime";
|
|
7
|
+
//#region src/context.ts
|
|
8
|
+
var S = l(null), C = Symbol.for("fluenti.runtime.react");
|
|
9
|
+
function w() {
|
|
10
|
+
let e = globalThis[C];
|
|
11
|
+
return typeof e == "object" && e ? e : null;
|
|
12
|
+
}
|
|
13
|
+
function T({ locale: e, fallbackLocale: t, messages: n, loadMessages: r, fallbackChain: i, dateFormats: a, numberFormats: o, missing: s, children: c }) {
|
|
14
|
+
let [l, u] = g(e), [f, v] = g(!1), [y, b] = g(n ?? {}), [C, T] = g(n ? Object.keys(n) : []), E = h(y);
|
|
15
|
+
E.current = y;
|
|
16
|
+
let D = h(0), O = m(() => {
|
|
17
|
+
let e = {
|
|
18
|
+
locale: l,
|
|
19
|
+
messages: y
|
|
20
|
+
};
|
|
21
|
+
return t !== void 0 && (e.fallbackLocale = t), i !== void 0 && (e.fallbackChain = i), a !== void 0 && (e.dateFormats = a), o !== void 0 && (e.numberFormats = o), s !== void 0 && (e.missing = s), _(e);
|
|
22
|
+
}, [
|
|
23
|
+
l,
|
|
24
|
+
y,
|
|
25
|
+
t,
|
|
26
|
+
i,
|
|
27
|
+
a,
|
|
28
|
+
o,
|
|
29
|
+
s
|
|
30
|
+
]);
|
|
31
|
+
p(() => {
|
|
32
|
+
e !== l && k(e);
|
|
33
|
+
}, [e]);
|
|
34
|
+
let k = d(async (e) => {
|
|
35
|
+
let t = ++D.current;
|
|
36
|
+
if (E.current[e] && !r) {
|
|
37
|
+
u(e);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let n = r ? w() : null;
|
|
41
|
+
if (E.current[e]) {
|
|
42
|
+
n?.__switchLocale && await n.__switchLocale(e), u(e);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!r) {
|
|
46
|
+
console.warn(`[fluenti] No messages for locale "${e}" and no loadMessages function provided`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
v(!0);
|
|
50
|
+
try {
|
|
51
|
+
let i = await r(e);
|
|
52
|
+
if (t !== D.current) return;
|
|
53
|
+
let a = typeof i == "object" && i && "default" in i ? i.default : i;
|
|
54
|
+
b((t) => ({
|
|
55
|
+
...t,
|
|
56
|
+
[e]: a
|
|
57
|
+
})), T((t) => [...new Set([...t, e])]), n?.__switchLocale && await n.__switchLocale(e), u(e);
|
|
58
|
+
} catch (n) {
|
|
59
|
+
t === D.current && console.error(`[fluenti] Failed to load locale "${e}"`, n);
|
|
60
|
+
} finally {
|
|
61
|
+
t === D.current && v(!1);
|
|
62
|
+
}
|
|
63
|
+
}, [r]), A = d(async (e) => {
|
|
64
|
+
let t = w();
|
|
65
|
+
if (!(E.current[e] || !r)) try {
|
|
66
|
+
let n = await r(e), i = typeof n == "object" && n && "default" in n ? n.default : n;
|
|
67
|
+
b((t) => ({
|
|
68
|
+
...t,
|
|
69
|
+
[e]: i
|
|
70
|
+
})), T((t) => [...new Set([...t, e])]), t?.__preloadLocale && await t.__preloadLocale(e);
|
|
71
|
+
} catch {}
|
|
72
|
+
}, [r]), j = m(() => ({
|
|
73
|
+
i18n: O,
|
|
74
|
+
t: O.t.bind(O),
|
|
75
|
+
d: O.d.bind(O),
|
|
76
|
+
n: O.n.bind(O),
|
|
77
|
+
format: O.format.bind(O),
|
|
78
|
+
loadMessages: O.loadMessages.bind(O),
|
|
79
|
+
getLocales: O.getLocales.bind(O),
|
|
80
|
+
locale: l,
|
|
81
|
+
setLocale: k,
|
|
82
|
+
isLoading: f,
|
|
83
|
+
loadedLocales: C,
|
|
84
|
+
preloadLocale: A
|
|
85
|
+
}), [
|
|
86
|
+
O,
|
|
87
|
+
l,
|
|
88
|
+
k,
|
|
89
|
+
f,
|
|
90
|
+
C,
|
|
91
|
+
A
|
|
92
|
+
]);
|
|
93
|
+
return /* @__PURE__ */ x(S.Provider, {
|
|
94
|
+
value: j,
|
|
95
|
+
children: c
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/hooks/useI18n.ts
|
|
100
|
+
function E() {
|
|
101
|
+
let e = f(S);
|
|
102
|
+
if (!e) throw Error("[fluenti] useI18n() must be used within an <I18nProvider>. Wrap your app with <I18nProvider> to provide i18n context.");
|
|
103
|
+
return e;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/compile-time-t.ts
|
|
107
|
+
var D = ((...e) => {
|
|
108
|
+
throw Error("[fluenti] `t` imported from '@fluenti/react' is a compile-time API. Use it only with the Fluenti build transform inside a component or custom hook. For runtime lookups, use useI18n().t(...).");
|
|
109
|
+
}), O = u(function({ children: e, id: n, context: i, comment: a, render: o, __id: c, __message: l, __components: u }) {
|
|
110
|
+
let d = f(S);
|
|
111
|
+
if (!d) throw Error("[fluenti] <Trans> must be used within an <I18nProvider>");
|
|
112
|
+
let p = l !== void 0, { message: h, components: g } = m(() => p ? {
|
|
113
|
+
message: l,
|
|
114
|
+
components: u ?? []
|
|
115
|
+
} : s(e), [
|
|
116
|
+
p,
|
|
117
|
+
l,
|
|
118
|
+
u,
|
|
119
|
+
e
|
|
120
|
+
]), _ = m(() => n ?? c ?? t(h, i), [
|
|
121
|
+
n,
|
|
122
|
+
c,
|
|
123
|
+
h,
|
|
124
|
+
i
|
|
125
|
+
]), v = r(d.i18n.t({
|
|
126
|
+
id: _,
|
|
127
|
+
message: h,
|
|
128
|
+
...i === void 0 ? {} : { context: i },
|
|
129
|
+
...a === void 0 ? {} : { comment: a }
|
|
130
|
+
}), g);
|
|
131
|
+
return o ? o(v) : /* @__PURE__ */ x(b, { children: v });
|
|
132
|
+
}), k = u(function({ value: t, id: r, context: i, comment: o, zero: s, one: l, two: u, few: d, many: p, other: m, offset: h }) {
|
|
133
|
+
let g = f(S);
|
|
134
|
+
if (!g) throw Error("[fluenti] <Plural> must be used within an <I18nProvider>");
|
|
135
|
+
let { messages: _, components: y } = e(a, {
|
|
136
|
+
zero: s,
|
|
137
|
+
one: l,
|
|
138
|
+
two: u,
|
|
139
|
+
few: d,
|
|
140
|
+
many: p,
|
|
141
|
+
other: m
|
|
142
|
+
}), C = c({
|
|
143
|
+
..._.zero !== void 0 && { zero: _.zero },
|
|
144
|
+
..._.one !== void 0 && { one: _.one },
|
|
145
|
+
..._.two !== void 0 && { two: _.two },
|
|
146
|
+
..._.few !== void 0 && { few: _.few },
|
|
147
|
+
..._.many !== void 0 && { many: _.many },
|
|
148
|
+
other: _.other ?? ""
|
|
149
|
+
}, h);
|
|
150
|
+
return /* @__PURE__ */ x(b, { children: n({
|
|
151
|
+
id: r ?? (i === void 0 ? C : v(C, i)),
|
|
152
|
+
message: C,
|
|
153
|
+
...i === void 0 ? {} : { context: i },
|
|
154
|
+
...o === void 0 ? {} : { comment: o }
|
|
155
|
+
}, { count: t }, (e, t) => g.i18n.t(e, t), y) });
|
|
156
|
+
});
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/components/Select.tsx
|
|
159
|
+
function A(t) {
|
|
160
|
+
let r = f(S);
|
|
161
|
+
if (!r) throw Error("[fluenti] <Select> must be used within an <I18nProvider>");
|
|
162
|
+
let { value: a, id: s, context: c, comment: l, other: u, options: d, ...p } = t, m = d === void 0 ? {
|
|
163
|
+
...Object.fromEntries(Object.entries(p).filter(([e]) => ![
|
|
164
|
+
"value",
|
|
165
|
+
"id",
|
|
166
|
+
"context",
|
|
167
|
+
"comment",
|
|
168
|
+
"options",
|
|
169
|
+
"other"
|
|
170
|
+
].includes(e))),
|
|
171
|
+
other: u
|
|
172
|
+
} : {
|
|
173
|
+
...d,
|
|
174
|
+
other: u
|
|
175
|
+
}, h = [...Object.keys(m).filter((e) => e !== "other"), "other"], { messages: g, components: _ } = e(h, m), y = o(Object.fromEntries([...h].map((e) => [e, g[e] ?? ""]))), C = i(y.forms);
|
|
176
|
+
return /* @__PURE__ */ x(b, { children: n({
|
|
177
|
+
id: s ?? (c === void 0 ? C : v(C, c)),
|
|
178
|
+
message: C,
|
|
179
|
+
...c === void 0 ? {} : { context: c },
|
|
180
|
+
...l === void 0 ? {} : { comment: l }
|
|
181
|
+
}, { value: y.valueMap[a] ?? "other" }, (e, t) => r.i18n.t(e, t), _) });
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/components/DateTime.tsx
|
|
185
|
+
function j({ value: e, style: t }) {
|
|
186
|
+
let n = f(S);
|
|
187
|
+
if (!n) throw Error("[fluenti] <DateTime> must be used within an <I18nProvider>");
|
|
188
|
+
return /* @__PURE__ */ x(b, { children: n.i18n.d(e, t) });
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/components/Number.tsx
|
|
192
|
+
function M({ value: e, style: t }) {
|
|
193
|
+
let n = f(S);
|
|
194
|
+
if (!n) throw Error("[fluenti] <Number> must be used within an <I18nProvider>");
|
|
195
|
+
return /* @__PURE__ */ x(b, { children: n.i18n.n(e, t) });
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
export { j as DateTime, S as I18nContext, T as I18nProvider, M as NumberFormat, k as Plural, A as Select, O as Trans, y as msg, D as t, E as useI18n };
|
|
199
|
+
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useI18n.ts","../src/compile-time-t.ts","../src/components/Trans.tsx","../src/components/Plural.tsx","../src/components/Select.tsx","../src/components/DateTime.tsx","../src/components/Number.tsx"],"sourcesContent":["import { createContext } from 'react'\nimport type { I18nContextValue } from './types'\n\nexport const I18nContext = createContext<I18nContextValue | null>(null)\n","import { useState, useCallback, useEffect, useMemo, useRef } from 'react'\nimport { createFluent } from '@fluenti/core'\nimport type { Messages } from '@fluenti/core'\nimport { I18nContext } from './context'\nimport type { I18nProviderProps } from './types'\n\ninterface SplitRuntimeModule {\n __switchLocale?: (locale: string) => Promise<void>\n __preloadLocale?: (locale: string) => Promise<void>\n}\n\nconst SPLIT_RUNTIME_KEY = Symbol.for('fluenti.runtime.react')\n\nfunction getSplitRuntimeModule(): SplitRuntimeModule | null {\n const runtime = (globalThis as Record<PropertyKey, unknown>)[SPLIT_RUNTIME_KEY]\n return typeof runtime === 'object' && runtime !== null\n ? runtime as SplitRuntimeModule\n : null\n}\n\nexport function I18nProvider({\n locale,\n fallbackLocale,\n messages,\n loadMessages,\n fallbackChain,\n dateFormats,\n numberFormats,\n missing,\n children,\n}: I18nProviderProps) {\n const [currentLocale, setCurrentLocale] = useState(locale)\n const [isLoading, setIsLoading] = useState(false)\n const [loadedMessages, setLoadedMessages] = useState<Record<string, Messages>>(\n messages ?? {},\n )\n const [loadedLocales, setLoadedLocales] = useState<string[]>(\n messages ? Object.keys(messages) : [],\n )\n\n // Use ref to avoid stale closures in callbacks\n const loadedMessagesRef = useRef(loadedMessages)\n loadedMessagesRef.current = loadedMessages\n\n // Guard against out-of-order async locale loads (race condition protection)\n const localeRequestRef = useRef(0)\n\n const i18n = useMemo(() => {\n const config: Parameters<typeof createFluent>[0] = {\n locale: currentLocale,\n messages: loadedMessages,\n }\n if (fallbackLocale !== undefined) config.fallbackLocale = fallbackLocale\n if (fallbackChain !== undefined) config.fallbackChain = fallbackChain\n if (dateFormats !== undefined) config.dateFormats = dateFormats\n if (numberFormats !== undefined) config.numberFormats = numberFormats\n if (missing !== undefined) config.missing = missing\n return createFluent(config)\n }, [currentLocale, loadedMessages, fallbackLocale, fallbackChain, dateFormats, numberFormats, missing])\n\n // Sync external locale prop changes\n useEffect(() => {\n if (locale !== currentLocale) {\n void handleSetLocale(locale)\n }\n // Intentionally only depend on `locale` — we want to sync when the\n // external prop changes, not when internal state (`currentLocale`,\n // `handleSetLocale`) updates, which would cause infinite re-renders.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [locale])\n\n const handleSetLocale = useCallback(\n async (newLocale: string) => {\n const requestId = ++localeRequestRef.current\n\n if (loadedMessagesRef.current[newLocale] && !loadMessages) {\n setCurrentLocale(newLocale)\n return\n }\n\n const splitRuntime = loadMessages ? getSplitRuntimeModule() : null\n\n if (loadedMessagesRef.current[newLocale]) {\n if (splitRuntime?.__switchLocale) {\n await splitRuntime.__switchLocale(newLocale)\n }\n setCurrentLocale(newLocale)\n return\n }\n\n if (!loadMessages) {\n console.warn(\n `[fluenti] No messages for locale \"${newLocale}\" and no loadMessages function provided`,\n )\n return\n }\n\n setIsLoading(true)\n try {\n const msgs = await loadMessages(newLocale)\n\n // A newer request has superseded this one — discard stale result\n if (requestId !== localeRequestRef.current) return\n\n const resolved: Messages =\n typeof msgs === 'object' && msgs !== null && 'default' in msgs\n ? (msgs as { default: Messages }).default\n : (msgs as Messages)\n setLoadedMessages((prev) => ({ ...prev, [newLocale]: resolved }))\n setLoadedLocales((prev) => [...new Set([...prev, newLocale])])\n if (splitRuntime?.__switchLocale) {\n await splitRuntime.__switchLocale(newLocale)\n }\n setCurrentLocale(newLocale)\n } catch (err) {\n // Only log if this request is still the latest\n if (requestId === localeRequestRef.current) {\n console.error(`[fluenti] Failed to load locale \"${newLocale}\"`, err)\n }\n } finally {\n if (requestId === localeRequestRef.current) {\n setIsLoading(false)\n }\n }\n },\n [loadMessages],\n )\n\n const preloadLocale = useCallback(\n async (loc: string) => {\n const splitRuntime = getSplitRuntimeModule()\n if (loadedMessagesRef.current[loc] || !loadMessages) return\n try {\n const msgs = await loadMessages(loc)\n const resolved: Messages =\n typeof msgs === 'object' && msgs !== null && 'default' in msgs\n ? (msgs as { default: Messages }).default\n : (msgs as Messages)\n setLoadedMessages((prev) => ({ ...prev, [loc]: resolved }))\n setLoadedLocales((prev) => [...new Set([...prev, loc])])\n if (splitRuntime?.__preloadLocale) {\n await splitRuntime.__preloadLocale(loc)\n }\n } catch {\n // Silent fail for preload\n }\n },\n [loadMessages],\n )\n\n const ctx = useMemo(\n () => ({\n i18n,\n t: i18n.t.bind(i18n),\n d: i18n.d.bind(i18n),\n n: i18n.n.bind(i18n),\n format: i18n.format.bind(i18n),\n loadMessages: i18n.loadMessages.bind(i18n),\n getLocales: i18n.getLocales.bind(i18n),\n locale: currentLocale,\n setLocale: handleSetLocale,\n isLoading,\n loadedLocales,\n preloadLocale,\n }),\n [i18n, currentLocale, handleSetLocale, isLoading, loadedLocales, preloadLocale],\n )\n\n return <I18nContext.Provider value={ctx}>{children}</I18nContext.Provider>\n}\n","import { useContext } from 'react'\nimport { I18nContext } from '../context'\nimport type { I18nContextValue } from '../types'\n\n/**\n * Primary hook for accessing i18n functions.\n *\n * Returns locale, setLocale, isLoading, loadedLocales, preloadLocale,\n * and the underlying i18n instance with t(), d(), n() methods.\n *\n * @throws If used outside of `<I18nProvider>`\n */\nexport function useI18n(): I18nContextValue {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error(\n '[fluenti] useI18n() must be used within an <I18nProvider>. ' +\n 'Wrap your app with <I18nProvider> to provide i18n context.',\n )\n }\n return ctx\n}\n","import type { CompileTimeT } from '@fluenti/core'\n\nexport const t: CompileTimeT = ((..._args: unknown[]) => {\n throw new Error(\n \"[fluenti] `t` imported from '@fluenti/react' is a compile-time API. \" +\n 'Use it only with the Fluenti build transform inside a component or custom hook. ' +\n 'For runtime lookups, use useI18n().t(...).',\n )\n}) as CompileTimeT\n","import {\n memo,\n useContext,\n useMemo,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport { I18nContext } from '../context'\nimport { hashMessage, extractMessage, reconstruct } from './trans-core'\n\nexport interface TransProps {\n /** Source text with embedded components */\n children: ReactNode\n /** Override auto-generated hash ID */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Context comment for translators */\n comment?: string\n /** Custom render wrapper */\n render?: (translation: ReactNode) => ReactNode\n /** @internal Pre-computed message ID from build plugin */\n __id?: string\n /** @internal Pre-computed ICU message from build plugin */\n __message?: string\n /** @internal Pre-computed component list from build plugin */\n __components?: ReactElement[]\n}\n\n/**\n * `<Trans>` component for rich-text translations containing nested React elements.\n *\n * @example\n * ```tsx\n * <Trans>Read the <a href=\"/docs\">documentation</a> for more info.</Trans>\n * ```\n */\nexport const Trans = memo(function Trans({\n children,\n id,\n context,\n comment,\n render,\n __id,\n __message,\n __components,\n}: TransProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Trans> must be used within an <I18nProvider>')\n }\n\n // Fast path: build plugin pre-computed message and components\n const hasPrecomputed = __message !== undefined\n\n const { message, components } = useMemo(\n () => hasPrecomputed\n ? { message: __message!, components: __components ?? [] }\n : extractMessage(children),\n [hasPrecomputed, __message, __components, children],\n )\n const messageId = useMemo(\n () => id ?? __id ?? hashMessage(message, context),\n [id, __id, message, context],\n )\n\n const translated = ctx.i18n.t(\n {\n id: messageId,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n },\n )\n\n const result = reconstruct(translated, components)\n return render ? render(result) : <>{result}</>\n})\n","import { memo, useContext, type ReactNode } from 'react'\nimport { hashMessage } from '@fluenti/core'\nimport { I18nContext } from '../context'\nimport { PLURAL_CATEGORIES, type PluralCategory } from './plural-core'\nimport { buildICUPluralMessage, renderRichTranslation, serializeRichForms } from './icu-rich'\n\nexport interface PluralProps {\n /** The count value */\n value: number\n /** Override the auto-generated synthetic ICU message id */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Translator-facing note preserved in extraction catalogs */\n comment?: string\n /** Text for zero (if language supports) */\n zero?: ReactNode\n /** Singular form. `#` replaced with value */\n one?: ReactNode\n /** Dual form (Arabic, etc.) */\n two?: ReactNode\n /** Few form (Slavic languages, etc.) */\n few?: ReactNode\n /** Many form */\n many?: ReactNode\n /** Default plural form */\n other: ReactNode\n /** Offset from value before selecting form */\n offset?: number\n}\n\n/**\n * `<Plural>` — ICU plural handling as a component.\n *\n * @example\n * ```tsx\n * <Plural value={count} zero=\"No messages\" one=\"# message\" other=\"# messages\" />\n * ```\n */\nexport const Plural = memo(function Plural({\n value,\n id,\n context,\n comment,\n zero,\n one,\n two,\n few,\n many,\n other,\n offset,\n}: PluralProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Plural> must be used within an <I18nProvider>')\n }\n\n const forms: Record<PluralCategory, ReactNode | undefined> = {\n zero,\n one,\n two,\n few,\n many,\n other,\n }\n const { messages, components } = serializeRichForms(PLURAL_CATEGORIES, forms)\n const icuMessage = buildICUPluralMessage(\n {\n ...(messages.zero !== undefined && { zero: messages.zero }),\n ...(messages.one !== undefined && { one: messages.one }),\n ...(messages.two !== undefined && { two: messages.two }),\n ...(messages.few !== undefined && { few: messages.few }),\n ...(messages.many !== undefined && { many: messages.many }),\n other: messages.other ?? '',\n },\n offset,\n )\n\n const descriptor = {\n id: id ?? (context === undefined ? icuMessage : hashMessage(icuMessage, context)),\n message: icuMessage,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n }\n\n return <>{renderRichTranslation(descriptor, { count: value }, (desc, values) => ctx.i18n.t(desc, values), components)}</>\n})\n","import { useContext, type ReactNode } from 'react'\nimport { hashMessage } from '@fluenti/core'\nimport { I18nContext } from '../context'\nimport { buildICUSelectMessage, normalizeSelectForms, renderRichTranslation, serializeRichForms } from './icu-rich'\n\nexport interface SelectProps {\n /** The selector value */\n value: string\n /** Override the auto-generated synthetic ICU message id */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Translator-facing note preserved in extraction catalogs */\n comment?: string\n /** Default case */\n other: ReactNode\n /** Type-safe named options. Takes precedence over direct case props. */\n options?: Record<string, ReactNode>\n /** Named cases — any string key maps to a ReactNode */\n [key: string]: ReactNode | Record<string, ReactNode> | undefined\n}\n\n/**\n * `<Select>` — ICU select for gender, role, or other categorical values.\n *\n * @example\n * ```tsx\n * <Select\n * value={gender}\n * male=\"He liked your post\"\n * female=\"She liked your post\"\n * other=\"They liked your post\"\n * />\n * ```\n */\nexport function Select(props: SelectProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Select> must be used within an <I18nProvider>')\n }\n\n const { value, id, context, comment, other, options, ...cases } = props\n const forms: Record<string, ReactNode | undefined> = options === undefined\n ? {\n ...Object.fromEntries(\n Object.entries(cases).filter(([key]) => !['value', 'id', 'context', 'comment', 'options', 'other'].includes(key)),\n ),\n other,\n }\n : {\n ...options,\n other,\n }\n\n const orderedKeys = [...Object.keys(forms).filter(key => key !== 'other'), 'other'] as const\n const { messages, components } = serializeRichForms(orderedKeys, forms)\n const normalized = normalizeSelectForms(\n Object.fromEntries(\n [...orderedKeys].map((key) => [key, messages[key] ?? '']),\n ),\n )\n const icuMessage = buildICUSelectMessage(normalized.forms)\n\n const descriptor = {\n id: id ?? (context === undefined ? icuMessage : hashMessage(icuMessage, context)),\n message: icuMessage,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n }\n\n return <>{renderRichTranslation(\n descriptor,\n { value: normalized.valueMap[value] ?? 'other' },\n (desc, values) => ctx.i18n.t(desc, values),\n components,\n )}</>\n}\n","import { useContext } from 'react'\nimport { I18nContext } from '../context'\n\nexport interface DateTimeProps {\n /** Date value to format */\n value: Date | number\n /** Named format style */\n style?: string\n}\n\n/**\n * `<DateTime>` — formatting component using Intl APIs.\n *\n * @example\n * ```tsx\n * <DateTime value={new Date()} style=\"long\" />\n * ```\n */\nexport function DateTime({ value, style }: DateTimeProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <DateTime> must be used within an <I18nProvider>')\n }\n return <>{ctx.i18n.d(value, style)}</>\n}\n","import { useContext } from 'react'\nimport { I18nContext } from '../context'\n\nexport interface NumberProps {\n /** Number value to format */\n value: number\n /** Named format style */\n style?: string\n}\n\n/**\n * `<Number>` — number formatting component using Intl APIs.\n *\n * @example\n * ```tsx\n * <Number value={1234.56} style=\"currency\" />\n * ```\n */\nexport function NumberFormat({ value, style }: NumberProps) {\n const ctx = useContext(I18nContext)\n if (!ctx) {\n throw new Error('[fluenti] <Number> must be used within an <I18nProvider>')\n }\n return <>{ctx.i18n.n(value, style)}</>\n}\n"],"mappings":"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"}
|
package/dist/msg.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg.d.ts","sourceRoot":"","sources":["../src/msg.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { I18nProviderProps } from './types';
|
|
2
|
+
export declare function I18nProvider({ locale, fallbackLocale, messages, loadMessages, fallbackChain, dateFormats, numberFormats, missing, children, }: I18nProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAgBhD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,OAAO,EACP,QAAQ,GACT,EAAE,iBAAiB,2CA2InB"}
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./icu-rich-XY1SdM5K.cjs`);let t=require(`react`),n=require(`@fluenti/core`);function r(r){let i=(0,t.cache)(()=>({locale:null,instance:null})),a=(0,t.cache)(()=>new Map),o=null,s=0,c=0;function l(e){i().locale=e,s++,o=null}async function u(e){let t=a(),n=t.get(e);if(n)return n;let i=await r.loadMessages(e),o=typeof i==`object`&&i&&`default`in i?i.default:i;return t.set(e,o),o}async function d(){let e=i();!e.locale&&r.resolveLocale&&(e.locale=await r.resolveLocale());let t=e.locale;if(!t)throw Error(`[fluenti] No locale set. Call setLocale(locale) in your layout before using getI18n(), or provide a resolveLocale function in createServerI18n config to auto-detect locale in Server Actions and other contexts where the layout does not run.`);if(e.instance&&e.instance.locale===t)return e.instance;let a={};a[t]=await u(t),r.fallbackLocale&&r.fallbackLocale!==t&&(a[r.fallbackLocale]=await u(r.fallbackLocale));let l={locale:t,messages:a};return r.fallbackLocale!==void 0&&(l.fallbackLocale=r.fallbackLocale),r.fallbackChain!==void 0&&(l.fallbackChain=r.fallbackChain),r.dateFormats!==void 0&&(l.dateFormats=r.dateFormats),r.numberFormats!==void 0&&(l.numberFormats=r.numberFormats),r.missing!==void 0&&(l.missing=r.missing),e.instance=(0,n.createFluent)(l),o=e.instance,c=s,e.instance}async function f({children:r,id:i,context:a,comment:o,render:s}){let c=await d(),{message:l,components:u}=e.s(r),f=i??(0,n.hashMessage)(l,a),p=e.c(c.t({id:f,message:l,...a===void 0?{}:{context:a},...o===void 0?{}:{comment:o}}),u);return(0,t.createElement)(t.Fragment,null,s?s(p):p)}async function p({value:r,id:i,context:a,comment:o,zero:s,one:c,two:l,few:u,many:f,other:p,offset:m}){let h=await d(),{messages:g,components:_}=e.a(e.o,{zero:s,one:c,two:l,few:u,many:f,other:p}),v=e.t({...g.zero!==void 0&&{zero:g.zero},...g.one!==void 0&&{one:g.one},...g.two!==void 0&&{two:g.two},...g.few!==void 0&&{few:g.few},...g.many!==void 0&&{many:g.many},other:g.other??``},m);return(0,t.createElement)(t.Fragment,null,e.i({id:i??(a===void 0?v:(0,n.hashMessage)(v,a)),message:v,...a===void 0?{}:{context:a},...o===void 0?{}:{comment:o}},{count:r},(e,t)=>h.t(e,t),_))}async function m({value:r,id:i,context:a,comment:o,other:s,options:c,...l}){let u=await d(),f=c===void 0?{...Object.fromEntries(Object.entries(l).filter(([e])=>![`value`,`id`,`context`,`comment`,`options`,`other`].includes(e))),other:s}:{...c,other:s},p=[...Object.keys(f).filter(e=>e!==`other`),`other`],{messages:m,components:h}=e.a(p,f),g=e.r(Object.fromEntries([...p].map(e=>[e,m[e]??``]))),_=e.n(g.forms);return(0,t.createElement)(t.Fragment,null,e.i({id:i??(a===void 0?_:(0,n.hashMessage)(_,a)),message:_,...a===void 0?{}:{context:a},...o===void 0?{}:{comment:o}},{value:g.valueMap[r]??`other`},(e,t)=>u.t(e,t),h))}async function h({value:e,style:n}){return(0,t.createElement)(t.Fragment,null,(await d()).d(e,n))}async function g({value:e,style:n}){return(0,t.createElement)(t.Fragment,null,(await d()).n(e,n))}function _(){let e=i();if(e.instance)return e.instance;if(o&&c===s)return o;let t=e.locale??r.fallbackLocale??`en`,l=a(),u={},d=l.get(t);if(d&&(u[t]=d),r.fallbackLocale&&r.fallbackLocale!==t){let e=l.get(r.fallbackLocale);e&&(u[r.fallbackLocale]=e)}let f={locale:t,messages:u};return r.fallbackLocale!==void 0&&(f.fallbackLocale=r.fallbackLocale),r.fallbackChain!==void 0&&(f.fallbackChain=r.fallbackChain),r.dateFormats!==void 0&&(f.dateFormats=r.dateFormats),r.numberFormats!==void 0&&(f.numberFormats=r.numberFormats),r.missing!==void 0&&(f.missing=r.missing),e.instance=(0,n.createFluent)(f),e.instance}return{setLocale:l,getI18n:d,__getSyncInstance:_,Trans:f,Plural:p,Select:m,DateTime:h,NumberFormat:g}}exports.createServerI18n=r,Object.defineProperty(exports,`detectLocale`,{enumerable:!0,get:function(){return n.detectLocale}}),Object.defineProperty(exports,`getDirection`,{enumerable:!0,get:function(){return n.getDirection}}),Object.defineProperty(exports,`getHydratedLocale`,{enumerable:!0,get:function(){return n.getHydratedLocale}}),Object.defineProperty(exports,`getSSRLocaleScript`,{enumerable:!0,get:function(){return n.getSSRLocaleScript}}),Object.defineProperty(exports,`isRTL`,{enumerable:!0,get:function(){return n.isRTL}});
|
|
2
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.cjs","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { cache } from 'react'\nimport { createFluent, hashMessage as hashSyntheticMessage } from '@fluenti/core'\nimport type {\n FluentInstanceExtended,\n FluentConfigExtended,\n Locale,\n Messages,\n DateFormatOptions,\n NumberFormatOptions,\n} from '@fluenti/core'\nimport { createElement, Fragment, type ReactNode, type ReactElement } from 'react'\nimport { hashMessage, extractMessage, reconstruct } from './components/trans-core'\nimport { PLURAL_CATEGORIES, type PluralCategory } from './components/plural-core'\nimport { buildICUPluralMessage, buildICUSelectMessage, normalizeSelectForms, renderRichTranslation, serializeRichForms } from './components/icu-rich'\n\n// Re-export SSR utilities from core for convenience\nexport { detectLocale, getSSRLocaleScript, getHydratedLocale, isRTL, getDirection } from '@fluenti/core'\nexport type { DetectLocaleOptions } from '@fluenti/core'\n\n/**\n * Configuration for `createServerI18n`.\n */\nexport interface ServerI18nConfig {\n /** Load messages for a given locale. Called once per locale per request. */\n loadMessages: (locale: string) => Promise<Messages | { default: Messages }>\n /** Fallback locale when a translation is missing */\n fallbackLocale?: string\n /**\n * Auto-resolve locale when `setLocale()` was not called.\n *\n * This is the fallback for contexts where the layout doesn't run — most\n * notably **Server Actions** (`'use server'`), which are independent\n * requests that skip the layout tree entirely.\n *\n * Common patterns:\n * - Read from a cookie (Next.js: `cookies().get('locale')`)\n * - Read from a request header set by middleware\n * - Query the database for the authenticated user's preference\n *\n * If omitted and `setLocale()` was not called, `getI18n()` will throw.\n *\n * @example\n * ```ts\n * resolveLocale: async () => {\n * const { cookies } = await import('next/headers')\n * return (await cookies()).get('locale')?.value ?? 'en'\n * }\n * ```\n */\n resolveLocale?: () => string | Promise<string>\n /** Custom fallback chains per locale */\n fallbackChain?: Record<string, Locale[]>\n /** Custom date format styles */\n dateFormats?: DateFormatOptions\n /** Custom number format styles */\n numberFormats?: NumberFormatOptions\n /** Handler for missing translation keys */\n missing?: (locale: Locale, id: string) => string | undefined\n}\n\n// ─── Server Component Props ──────────────────────────────────────────────────\n\nexport interface ServerTransProps {\n /** Source text with embedded components */\n children: ReactNode\n /** Override auto-generated hash ID */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Context comment for translators */\n comment?: string\n /** Custom render wrapper */\n render?: (translation: ReactNode) => ReactNode\n}\n\nexport interface ServerPluralProps {\n /** The count value */\n value: number\n /** Override the auto-generated synthetic ICU message id */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Translator-facing note preserved in extraction catalogs */\n comment?: string\n /** Text for zero (if language supports) */\n zero?: ReactNode\n /** Singular form. `#` replaced with value */\n one?: ReactNode\n /** Dual form (Arabic, etc.) */\n two?: ReactNode\n /** Few form (Slavic languages, etc.) */\n few?: ReactNode\n /** Many form */\n many?: ReactNode\n /** Default plural form */\n other: ReactNode\n /** Offset from value before selecting form */\n offset?: number\n}\n\nexport interface ServerSelectProps {\n /** The selector value */\n value: string\n /** Override the auto-generated synthetic ICU message id */\n id?: string\n /** Message context used for identity and translator disambiguation */\n context?: string\n /** Translator-facing note preserved in extraction catalogs */\n comment?: string\n /** Default case */\n other: ReactNode\n /** Type-safe named options. Takes precedence over direct case props. */\n options?: Record<string, ReactNode>\n /** Named cases — any string key maps to a ReactNode */\n [key: string]: ReactNode | Record<string, ReactNode> | string | undefined\n}\n\nexport interface ServerDateTimeProps {\n /** Date value to format */\n value: Date | number\n /** Named format style */\n style?: string\n}\n\nexport interface ServerNumberProps {\n /** Number value to format */\n value: number\n /** Named format style */\n style?: string\n}\n\n// ─── Server Component Types ──────────────────────────────────────────────────\n\ntype ServerTransComponent = (props: ServerTransProps) => Promise<ReactElement>\ntype ServerPluralComponent = (props: ServerPluralProps) => Promise<ReactElement>\ntype ServerSelectComponent = (props: ServerSelectProps) => Promise<ReactElement>\ntype ServerDateTimeComponent = (props: ServerDateTimeProps) => Promise<ReactElement>\ntype ServerNumberComponent = (props: ServerNumberProps) => Promise<ReactElement>\n\n/**\n * The object returned by `createServerI18n`.\n */\nexport interface ServerI18n {\n /**\n * Set the locale for the current server request.\n * Call this once in your root layout or page before any `getI18n()` calls.\n *\n * Uses `React.cache()` — scoped to the current request automatically.\n */\n setLocale: (locale: string) => void\n\n /**\n * Get a fully configured i18n instance for the current request.\n * Messages are loaded lazily and cached per-request.\n *\n * @example\n * ```tsx\n * // In any Server Component\n * const { t, d, n, locale } = await getI18n()\n * return <h1>{t('welcome')}</h1>\n * ```\n */\n getI18n: () => Promise<FluentInstanceExtended & { locale: string }>\n\n /**\n * `<Trans>` for React Server Components.\n * Async component — automatically resolves the i18n instance.\n *\n * @example\n * ```tsx\n * <Trans>Read the <a href=\"/docs\">documentation</a>.</Trans>\n * ```\n */\n Trans: ServerTransComponent\n\n /**\n * `<Plural>` for React Server Components.\n *\n * @example\n * ```tsx\n * <Plural value={count} one=\"# item\" other=\"# items\" />\n * ```\n */\n Plural: ServerPluralComponent\n\n /**\n * `<Select>` for React Server Components.\n *\n * @example\n * ```tsx\n * <Select value={gender} male=\"He liked this\" other=\"They liked this\" />\n * ```\n */\n Select: ServerSelectComponent\n\n /**\n * `<DateTime>` for React Server Components.\n *\n * @example\n * ```tsx\n * <DateTime value={new Date()} style=\"long\" />\n * ```\n */\n DateTime: ServerDateTimeComponent\n\n /**\n * `<NumberFormat>` for React Server Components.\n *\n * @example\n * ```tsx\n * <NumberFormat value={1234.56} style=\"currency\" />\n * ```\n */\n NumberFormat: ServerNumberComponent\n\n /**\n * Synchronous accessor for the cached i18n instance.\n * Used internally by @fluenti/next webpack loader.\n * @internal\n */\n __getSyncInstance: () => FluentInstanceExtended & { locale: string }\n}\n\n/**\n * Create server-side i18n utilities for React Server Components.\n *\n * Uses `React.cache()` to share state within a single server request\n * without React Context (which is unavailable in Server Components).\n *\n * @example\n * ```ts\n * // lib/i18n.server.ts — define once\n * import { createServerI18n } from '@fluenti/react/server'\n *\n * export const { setLocale, getI18n, Trans, Plural, Select, DateTime, NumberFormat } = createServerI18n({\n * loadMessages: (locale) => import(`../messages/${locale}.json`),\n * fallbackLocale: 'en',\n * })\n * ```\n *\n * ```tsx\n * // app/[locale]/layout.tsx — set locale once\n * import { setLocale } from '@/lib/i18n.server'\n *\n * export default async function Layout({ params, children }) {\n * const { locale } = await params\n * setLocale(locale)\n * return <html lang={locale}><body>{children}</body></html>\n * }\n * ```\n *\n * ```tsx\n * // app/[locale]/page.tsx — use Trans, Plural, etc. directly\n * import { Trans, Plural, Select } from '@/lib/i18n.server'\n *\n * export default async function Page() {\n * return (\n * <div>\n * <Trans>Read the <a href=\"/docs\">documentation</a>.</Trans>\n * <Plural value={5} one=\"# item\" other=\"# items\" />\n * <Select value=\"male\" male=\"He liked this\" other=\"They liked this\" />\n * </div>\n * )\n * }\n * ```\n */\nexport function createServerI18n(config: ServerI18nConfig): ServerI18n {\n // Request-scoped store using React.cache()\n // Each server request gets its own isolated state\n const getRequestStore = cache((): {\n locale: string | null\n instance: (FluentInstanceExtended & { locale: string }) | null\n } => ({\n locale: null,\n instance: null,\n }))\n\n // Cache loaded messages per-request to avoid redundant imports\n const getMessageCache = cache((): Map<string, Messages> => new Map())\n\n // Module-level fallback for server actions where React.cache()\n // may not share state with the page render.\n let _lastInstance: (FluentInstanceExtended & { locale: string }) | null = null\n let _requestId = 0\n let _lastRequestId = 0\n\n function setLocale(locale: string): void {\n getRequestStore().locale = locale\n _requestId++\n _lastInstance = null\n }\n\n async function loadLocaleMessages(locale: string): Promise<Messages> {\n const messageCache = getMessageCache()\n const cached = messageCache.get(locale)\n if (cached) return cached\n\n const raw = await config.loadMessages(locale)\n const messages: Messages =\n typeof raw === 'object' && raw !== null && 'default' in raw\n ? (raw as { default: Messages }).default\n : (raw as Messages)\n\n messageCache.set(locale, messages)\n return messages\n }\n\n async function getI18n(): Promise<FluentInstanceExtended & { locale: string }> {\n const store = getRequestStore()\n\n // If setLocale() was never called (e.g. Server Action — independent request\n // that skips the layout), try the resolveLocale fallback.\n if (!store.locale && config.resolveLocale) {\n store.locale = await config.resolveLocale()\n }\n\n const locale = store.locale\n\n if (!locale) {\n throw new Error(\n '[fluenti] No locale set. Call setLocale(locale) in your layout before using getI18n(), ' +\n 'or provide a resolveLocale function in createServerI18n config to auto-detect locale ' +\n 'in Server Actions and other contexts where the layout does not run.',\n )\n }\n\n // Return cached instance if locale hasn't changed\n if (store.instance && store.instance.locale === locale) {\n return store.instance\n }\n\n // Load messages for current locale (and fallback if configured)\n const allMessages: Record<string, Messages> = {}\n allMessages[locale] = await loadLocaleMessages(locale)\n\n if (config.fallbackLocale && config.fallbackLocale !== locale) {\n allMessages[config.fallbackLocale] = await loadLocaleMessages(config.fallbackLocale)\n }\n\n const fluentConfig: FluentConfigExtended = {\n locale,\n messages: allMessages,\n }\n if (config.fallbackLocale !== undefined) fluentConfig.fallbackLocale = config.fallbackLocale\n if (config.fallbackChain !== undefined) fluentConfig.fallbackChain = config.fallbackChain\n if (config.dateFormats !== undefined) fluentConfig.dateFormats = config.dateFormats\n if (config.numberFormats !== undefined) fluentConfig.numberFormats = config.numberFormats\n if (config.missing !== undefined) fluentConfig.missing = config.missing\n\n store.instance = createFluent(fluentConfig)\n _lastInstance = store.instance\n _lastRequestId = _requestId\n return store.instance\n }\n\n // ─── Async Server Components ─────────────────────────────────────────────\n\n async function Trans({ children, id, context, comment, render }: ServerTransProps): Promise<ReactElement> {\n const i18n = await getI18n()\n const { message, components } = extractMessage(children)\n const messageId = id ?? hashMessage(message, context)\n const translated = i18n.t({\n id: messageId,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n })\n const result = reconstruct(translated, components)\n return createElement(Fragment, null, render ? render(result) : result)\n }\n\n async function Plural({\n value,\n id,\n context,\n comment,\n zero,\n one,\n two,\n few,\n many,\n other,\n offset,\n }: ServerPluralProps): Promise<ReactElement> {\n const i18n = await getI18n()\n const forms: Record<PluralCategory, ReactNode | undefined> = {\n zero,\n one,\n two,\n few,\n many,\n other,\n }\n const { messages, components } = serializeRichForms(PLURAL_CATEGORIES, forms)\n const icuMessage = buildICUPluralMessage(\n {\n ...(messages.zero !== undefined && { zero: messages.zero }),\n ...(messages.one !== undefined && { one: messages.one }),\n ...(messages.two !== undefined && { two: messages.two }),\n ...(messages.few !== undefined && { few: messages.few }),\n ...(messages.many !== undefined && { many: messages.many }),\n other: messages.other ?? '',\n },\n offset,\n )\n\n const result = renderRichTranslation(\n {\n id: id ?? (context === undefined ? icuMessage : hashSyntheticMessage(icuMessage, context)),\n message: icuMessage,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n },\n { count: value },\n (descriptor, values) => i18n.t(descriptor, values),\n components,\n )\n\n return createElement(Fragment, null, result)\n }\n\n async function Select({\n value,\n id,\n context,\n comment,\n other,\n options,\n ...cases\n }: ServerSelectProps): Promise<ReactElement> {\n const i18n = await getI18n()\n const forms: Record<string, ReactNode | undefined> = options === undefined\n ? {\n ...Object.fromEntries(\n Object.entries(cases).filter(([key]) => !['value', 'id', 'context', 'comment', 'options', 'other'].includes(key)),\n ),\n other,\n }\n : {\n ...options,\n other,\n }\n\n const orderedKeys = [...Object.keys(forms).filter(key => key !== 'other'), 'other'] as const\n const { messages, components } = serializeRichForms(orderedKeys, forms)\n const normalized = normalizeSelectForms(\n Object.fromEntries(\n [...orderedKeys].map((key) => [key, messages[key] ?? '']),\n ),\n )\n const icuMessage = buildICUSelectMessage(normalized.forms)\n\n const result = renderRichTranslation(\n {\n id: id ?? (context === undefined ? icuMessage : hashSyntheticMessage(icuMessage, context)),\n message: icuMessage,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n },\n { value: normalized.valueMap[value] ?? 'other' },\n (descriptor, values) => i18n.t(descriptor, values),\n components,\n )\n\n return createElement(Fragment, null, result)\n }\n\n async function DateTime({ value, style }: ServerDateTimeProps): Promise<ReactElement> {\n const i18n = await getI18n()\n return createElement(Fragment, null, i18n.d(value, style))\n }\n\n async function NumberFormat({ value, style }: ServerNumberProps): Promise<ReactElement> {\n const i18n = await getI18n()\n return createElement(Fragment, null, i18n.n(value, style))\n }\n\n /**\n * Synchronous accessor for the cached i18n instance.\n * Used by @fluenti/next webpack loader for t`` in RSC.\n * Throws if getI18n() hasn't been called yet in this request.\n * @internal\n */\n function __getSyncInstance(): FluentInstanceExtended & { locale: string } {\n const store = getRequestStore()\n if (store.instance) {\n return store.instance\n }\n\n // Module-level fallback for server actions where React.cache()\n // may not share state across different call contexts.\n // Only use within the same request (tracked by _requestId).\n if (_lastInstance && _lastRequestId === _requestId) {\n return _lastInstance\n }\n\n // Final fallback: create instance synchronously with the default locale.\n // This handles Suspense boundaries and streamed components where\n // the React.cache store may not have been populated yet.\n const locale = store.locale ?? config.fallbackLocale ?? 'en'\n const messageCache = getMessageCache()\n const messages: Record<string, Messages> = {}\n const cached = messageCache.get(locale)\n if (cached) messages[locale] = cached\n if (config.fallbackLocale && config.fallbackLocale !== locale) {\n const fallback = messageCache.get(config.fallbackLocale)\n if (fallback) messages[config.fallbackLocale] = fallback\n }\n\n const fluentConfig: FluentConfigExtended = { locale, messages }\n if (config.fallbackLocale !== undefined) fluentConfig.fallbackLocale = config.fallbackLocale\n if (config.fallbackChain !== undefined) fluentConfig.fallbackChain = config.fallbackChain\n if (config.dateFormats !== undefined) fluentConfig.dateFormats = config.dateFormats\n if (config.numberFormats !== undefined) fluentConfig.numberFormats = config.numberFormats\n if (config.missing !== undefined) fluentConfig.missing = config.missing\n\n store.instance = createFluent(fluentConfig)\n return store.instance\n }\n\n return { setLocale, getI18n, __getSyncInstance, Trans, Plural, Select, DateTime, NumberFormat }\n}\n"],"mappings":"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"}
|