locl-js 1.0.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,118 @@
1
+ import React, { ReactElement, ReactNode } from 'react';
2
+ import { S as ScopeType, N as NestedKeyOf, T as TranslationObjectFor, f as PluralKeys, e as IsEmptyParams, c as ParamsFor, d as PathValue, I as InterpolationOptions, a as LocL, g as Language, h as Scope } from './LocL-v4yNIiKt.js';
3
+
4
+ type TransKey<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined> = (NestedKeyOf<TranslationObjectFor<S, T, Fallback>> | PluralKeys<S, T, Fallback>) & string;
5
+ type TransValues<T extends Record<string, any>, Fallback extends keyof T & string, S extends ScopeType<T, Fallback>, K extends string> = IsEmptyParams<ParamsFor<PathValue<TranslationObjectFor<S, T, Fallback>, K>>> extends true ? InterpolationOptions | undefined : ParamsFor<PathValue<TranslationObjectFor<S, T, Fallback>, K>> & InterpolationOptions;
6
+ type TransProps<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined, K extends TransKey<T, Fallback, S> = TransKey<T, Fallback, S>> = {
7
+ /** Strictly typed translation key */
8
+ i18nKey: K;
9
+ values?: TransValues<T, Fallback, S, K>;
10
+ components?: Record<string, ReactElement | ((content: ReactNode) => ReactNode)>;
11
+ translator?: LocL<T, any, any, any, any>;
12
+ scope?: S;
13
+ fallback?: string;
14
+ } | {
15
+ /** Dynamic / fallback key allowed ONLY when fallback string is provided */
16
+ i18nKey: K | (string & {});
17
+ values?: InterpolationOptions;
18
+ components?: Record<string, ReactElement | ((content: ReactNode) => ReactNode)>;
19
+ translator?: LocL<T, any, any, any, any>;
20
+ scope?: S;
21
+ fallback: string;
22
+ };
23
+ /**
24
+ * Renders translated text with rich React component interpolation without `dangerouslySetInnerHTML`.
25
+ * Subscribes reactively to language updates and supports strict type checking and autocompletion.
26
+ * @example
27
+ * ```tsx
28
+ * <Trans
29
+ * i18nKey="agreement"
30
+ * values={{ name: "Alice" }}
31
+ * components={{
32
+ * bold: <strong />,
33
+ * link: (content) => <a href="/terms">{content}</a>
34
+ * }}
35
+ * />
36
+ * ```
37
+ */
38
+ declare function Trans<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined, K extends TransKey<T, Fallback, S> = TransKey<T, Fallback, S>>(props: TransProps<T, Fallback, S, K>): ReactElement;
39
+
40
+ /**
41
+ * Global type registry for LocL React.
42
+ * Augment this interface in user application code for zero-boilerplate,
43
+ * 100% type-safe autocompletion across all hooks and components:
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * declare module "locl-js/react" {
48
+ * interface LocLRegister {
49
+ * translator: typeof translator;
50
+ * }
51
+ * }
52
+ * ```
53
+ */
54
+ interface LocLRegister {
55
+ }
56
+ type DefaultResources = LocLRegister extends {
57
+ translator: LocL<infer Res, any, any, any, any>;
58
+ } ? Res : LocLRegister extends {
59
+ resources: infer Res extends Record<string, any>;
60
+ } ? Res : Record<string, any>;
61
+ type DefaultFallback<T = DefaultResources> = LocLRegister extends {
62
+ translator: LocL<any, infer FB, any, any, any>;
63
+ } ? FB : LocLRegister extends {
64
+ fallbackLanguage: infer FB extends string;
65
+ } ? FB : [keyof T & string] extends [never] ? string : keyof T & string;
66
+ declare const LocLContext: React.Context<LocL<any, any, any, any, any> | null>;
67
+ interface LocLProviderProps<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined> {
68
+ translator: LocL<T, Fallback, S, any, any>;
69
+ children: ReactNode;
70
+ }
71
+ declare function LocLProvider<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined>({ translator, children }: LocLProviderProps<T, Fallback, S>): React.JSX.Element;
72
+ interface UseLocLResult<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined> {
73
+ translator: LocL<T, Fallback, S, any, any>;
74
+ language: Language<T>;
75
+ changeLanguage: (lang: Language<T>) => void;
76
+ t: LocL<T, Fallback, S>["t"];
77
+ plural: LocL<T, Fallback, S>["plural"];
78
+ rich: LocL<T, Fallback, S>["rich"];
79
+ format: LocL<T, Fallback, S>["format"];
80
+ }
81
+ interface UseTranslationResult<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined> extends UseLocLResult<T, Fallback, S> {
82
+ }
83
+ declare function useLocL<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined>(customTranslator?: LocL<T, Fallback, S, any, any> | LocL<any, any, any, any, any>): UseLocLResult<T, Fallback, S>;
84
+ declare function useTranslation<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>>(scope?: undefined, customTranslator?: LocL<T, Fallback, any, any, any> | LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, undefined>;
85
+ declare function useTranslation<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = ScopeType<T, Fallback>>(scope: S, customTranslator?: LocL<T, Fallback, any, any, any> | LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, S>;
86
+ interface BoundLocLProviderProps<T extends Record<string, any>, Fallback extends keyof T & string> {
87
+ translator?: LocL<any, any, any, any, any>;
88
+ children: ReactNode;
89
+ }
90
+ interface LocLReactSuite<T extends Record<string, any>, Fallback extends keyof T & string> {
91
+ LocLContext: React.Context<LocL<any, any, any, any, any> | null>;
92
+ LocLProvider: (props: BoundLocLProviderProps<T, Fallback>) => ReactElement;
93
+ useLocL: <S extends ScopeType<T, Fallback> = undefined>(customTranslator?: LocL<any, any, any, any, any>) => UseLocLResult<T, Fallback, S>;
94
+ useTranslation: {
95
+ (): UseTranslationResult<T, Fallback, undefined>;
96
+ <S extends Scope<T, Fallback>>(scope: S): UseTranslationResult<T, Fallback, S>;
97
+ <S extends Scope<T, Fallback>[]>(scope: S): UseTranslationResult<T, Fallback, S>;
98
+ (scope?: undefined, customTranslator?: LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, undefined>;
99
+ <S extends ScopeType<T, Fallback>>(scope: S, customTranslator?: LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, S>;
100
+ };
101
+ Trans: <S extends ScopeType<T, Fallback> = undefined, K extends TransKey<T, Fallback, S> = TransKey<T, Fallback, S>>(props: TransProps<T, Fallback, S, K>) => ReactElement;
102
+ translator: LocL<T, Fallback, any, any, any>;
103
+ }
104
+ /**
105
+ * Creates a pre-bound, zero-declaration React integration suite for a LocL translator instance.
106
+ *
107
+ * Inifers resources, fallback language, translation keys, and namespace scopes
108
+ * automatically without requiring `.d.ts` module declarations or manual generics.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * // i18n-react.ts
113
+ * export const { LocLProvider, useTranslation, useLocL, Trans } = createLocLReact(translator);
114
+ * ```
115
+ */
116
+ declare function createLocLReact<T extends Record<string, any>, Fallback extends keyof T & string>(defaultTranslator: LocL<T, Fallback, any, any, any>): LocLReactSuite<T, Fallback>;
117
+
118
+ export { type BoundLocLProviderProps, type DefaultFallback, type DefaultResources, LocLContext, LocLProvider, type LocLProviderProps, type LocLReactSuite, type LocLRegister, Trans, type TransKey, type TransProps, type TransValues, type UseLocLResult, type UseTranslationResult, createLocLReact, useLocL, useTranslation };
package/dist/react.js ADDED
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/react/index.tsx
31
+ var react_exports = {};
32
+ __export(react_exports, {
33
+ LocLContext: () => LocLContext,
34
+ LocLProvider: () => LocLProvider,
35
+ Trans: () => Trans,
36
+ createLocLReact: () => createLocLReact,
37
+ useLocL: () => useLocL,
38
+ useTranslation: () => useTranslation
39
+ });
40
+ module.exports = __toCommonJS(react_exports);
41
+ var import_react2 = require("react");
42
+
43
+ // src/react/Trans.tsx
44
+ var import_react = __toESM(require("react"));
45
+ var import_jsx_runtime = require("react/jsx-runtime");
46
+ function Trans(props) {
47
+ const {
48
+ i18nKey,
49
+ values,
50
+ components = {},
51
+ translator: customTranslator,
52
+ scope,
53
+ fallback
54
+ } = props;
55
+ const contextTranslator = (0, import_react.useContext)(LocLContext);
56
+ const activeTranslator = customTranslator ?? contextTranslator;
57
+ const getSnapshot = () => activeTranslator ? activeTranslator.getLanguage() : "";
58
+ const language = (0, import_react.useSyncExternalStore)(
59
+ (onStoreChange) => activeTranslator ? activeTranslator.subscribe(onStoreChange) : () => {
60
+ },
61
+ getSnapshot,
62
+ getSnapshot
63
+ );
64
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
65
+ const scopedTranslator = (0, import_react.useMemo)(() => {
66
+ if (!activeTranslator) return null;
67
+ return scope !== void 0 ? activeTranslator.withConfig({ scope }) : activeTranslator;
68
+ }, [activeTranslator, scopeKey, language]);
69
+ const raw = (0, import_react.useMemo)(() => {
70
+ if (!scopedTranslator) {
71
+ return String(fallback ?? i18nKey);
72
+ }
73
+ const tInstance = scopedTranslator;
74
+ const res = tInstance.t(i18nKey, values);
75
+ const keyExists = tInstance.get(i18nKey) !== void 0;
76
+ if (keyExists) {
77
+ return String(res);
78
+ }
79
+ return String(fallback ?? i18nKey);
80
+ }, [scopedTranslator, i18nKey, values, fallback, language]);
81
+ const tagRegex = /<([a-zA-Z0-9_-]+)>([\s\S]*?)<\/\1>/g;
82
+ const nodes = [];
83
+ let lastIndex = 0;
84
+ let match;
85
+ let counter = 0;
86
+ while ((match = tagRegex.exec(raw)) !== null) {
87
+ if (match.index > lastIndex) {
88
+ nodes.push(raw.slice(lastIndex, match.index));
89
+ }
90
+ const tagName = match[1];
91
+ const content = match[2];
92
+ const component = components[tagName];
93
+ if ((0, import_react.isValidElement)(component)) {
94
+ nodes.push(
95
+ (0, import_react.cloneElement)(component, { key: `trans-${tagName}-${counter++}` }, content)
96
+ );
97
+ } else if (typeof component === "function") {
98
+ nodes.push(
99
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.default.Fragment, { children: component(content) }, `trans-${tagName}-${counter++}`)
100
+ );
101
+ } else {
102
+ nodes.push(match[0]);
103
+ }
104
+ lastIndex = match.index + match[0].length;
105
+ }
106
+ if (lastIndex < raw.length) {
107
+ nodes.push(raw.slice(lastIndex));
108
+ }
109
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: nodes });
110
+ }
111
+
112
+ // src/react/index.tsx
113
+ var import_jsx_runtime2 = require("react/jsx-runtime");
114
+ var LocLContext = (0, import_react2.createContext)(null);
115
+ function LocLProvider({ translator, children }) {
116
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocLContext.Provider, { value: translator, children });
117
+ }
118
+ function useLocL(customTranslator) {
119
+ const contextTranslator = (0, import_react2.useContext)(LocLContext);
120
+ const translator = customTranslator ?? contextTranslator;
121
+ if (!translator) {
122
+ throw new Error(
123
+ "[LocL] `useLocL` or `useTranslation` must be used within a `<LocLProvider>` or passed an explicit translator instance."
124
+ );
125
+ }
126
+ const getSnapshot = () => translator.getLanguage();
127
+ const language = (0, import_react2.useSyncExternalStore)(
128
+ (onStoreChange) => translator.subscribe(onStoreChange),
129
+ getSnapshot,
130
+ getSnapshot
131
+ );
132
+ return {
133
+ translator,
134
+ language,
135
+ changeLanguage: (lang) => translator.changeLanguage(lang),
136
+ t: translator.t.bind(translator),
137
+ plural: translator.plural.bind(translator),
138
+ rich: translator.rich.bind(translator),
139
+ format: translator.format.bind(translator)
140
+ };
141
+ }
142
+ function useTranslation(scope, customTranslator) {
143
+ const { translator, language, changeLanguage } = useLocL(customTranslator);
144
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
145
+ const scopedTranslator = (0, import_react2.useMemo)(() => {
146
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
147
+ }, [translator, scopeKey]);
148
+ return {
149
+ translator: scopedTranslator,
150
+ language,
151
+ changeLanguage,
152
+ t: scopedTranslator.t.bind(scopedTranslator),
153
+ plural: scopedTranslator.plural.bind(scopedTranslator),
154
+ rich: scopedTranslator.rich.bind(scopedTranslator),
155
+ format: scopedTranslator.format.bind(scopedTranslator)
156
+ };
157
+ }
158
+ function createLocLReact(defaultTranslator) {
159
+ const BoundContext = (0, import_react2.createContext)(null);
160
+ function BoundLocLProvider({
161
+ translator = defaultTranslator,
162
+ children
163
+ }) {
164
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocLContext.Provider, { value: translator, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BoundContext.Provider, { value: translator, children }) });
165
+ }
166
+ function useBoundLocL(customTranslator) {
167
+ const contextTranslator = (0, import_react2.useContext)(BoundContext);
168
+ const translator = customTranslator ?? contextTranslator ?? defaultTranslator;
169
+ const getSnapshot = () => translator.getLanguage();
170
+ const language = (0, import_react2.useSyncExternalStore)(
171
+ (onStoreChange) => translator.subscribe(onStoreChange),
172
+ getSnapshot,
173
+ getSnapshot
174
+ );
175
+ return {
176
+ translator,
177
+ language,
178
+ changeLanguage: (lang) => translator.changeLanguage(lang),
179
+ t: translator.t.bind(translator),
180
+ plural: translator.plural.bind(translator),
181
+ rich: translator.rich.bind(translator),
182
+ format: translator.format.bind(translator)
183
+ };
184
+ }
185
+ function useBoundTranslation(scope, customTranslator) {
186
+ const { translator, language, changeLanguage } = useBoundLocL(customTranslator);
187
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
188
+ const scopedTranslator = (0, import_react2.useMemo)(() => {
189
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
190
+ }, [translator, scopeKey]);
191
+ return {
192
+ translator: scopedTranslator,
193
+ language,
194
+ changeLanguage,
195
+ t: scopedTranslator.t.bind(scopedTranslator),
196
+ plural: scopedTranslator.plural.bind(scopedTranslator),
197
+ rich: scopedTranslator.rich.bind(scopedTranslator),
198
+ format: scopedTranslator.format.bind(scopedTranslator)
199
+ };
200
+ }
201
+ function BoundTrans(props) {
202
+ const contextTranslator = (0, import_react2.useContext)(BoundContext);
203
+ const activeTranslator = props.translator ?? contextTranslator ?? defaultTranslator;
204
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
205
+ Trans,
206
+ {
207
+ ...props,
208
+ translator: activeTranslator
209
+ }
210
+ );
211
+ }
212
+ return {
213
+ LocLContext: BoundContext,
214
+ LocLProvider: BoundLocLProvider,
215
+ useLocL: useBoundLocL,
216
+ useTranslation: useBoundTranslation,
217
+ Trans: BoundTrans,
218
+ translator: defaultTranslator
219
+ };
220
+ }
221
+ // Annotate the CommonJS export names for ESM import in node:
222
+ 0 && (module.exports = {
223
+ LocLContext,
224
+ LocLProvider,
225
+ Trans,
226
+ createLocLReact,
227
+ useLocL,
228
+ useTranslation
229
+ });
package/dist/react.mjs ADDED
@@ -0,0 +1,189 @@
1
+ // src/react/index.tsx
2
+ import { createContext, useContext as useContext2, useSyncExternalStore as useSyncExternalStore2, useMemo as useMemo2 } from "react";
3
+
4
+ // src/react/Trans.tsx
5
+ import React, { isValidElement, cloneElement, useContext, useMemo, useSyncExternalStore } from "react";
6
+ import { Fragment, jsx } from "react/jsx-runtime";
7
+ function Trans(props) {
8
+ const {
9
+ i18nKey,
10
+ values,
11
+ components = {},
12
+ translator: customTranslator,
13
+ scope,
14
+ fallback
15
+ } = props;
16
+ const contextTranslator = useContext(LocLContext);
17
+ const activeTranslator = customTranslator ?? contextTranslator;
18
+ const getSnapshot = () => activeTranslator ? activeTranslator.getLanguage() : "";
19
+ const language = useSyncExternalStore(
20
+ (onStoreChange) => activeTranslator ? activeTranslator.subscribe(onStoreChange) : () => {
21
+ },
22
+ getSnapshot,
23
+ getSnapshot
24
+ );
25
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
26
+ const scopedTranslator = useMemo(() => {
27
+ if (!activeTranslator) return null;
28
+ return scope !== void 0 ? activeTranslator.withConfig({ scope }) : activeTranslator;
29
+ }, [activeTranslator, scopeKey, language]);
30
+ const raw = useMemo(() => {
31
+ if (!scopedTranslator) {
32
+ return String(fallback ?? i18nKey);
33
+ }
34
+ const tInstance = scopedTranslator;
35
+ const res = tInstance.t(i18nKey, values);
36
+ const keyExists = tInstance.get(i18nKey) !== void 0;
37
+ if (keyExists) {
38
+ return String(res);
39
+ }
40
+ return String(fallback ?? i18nKey);
41
+ }, [scopedTranslator, i18nKey, values, fallback, language]);
42
+ const tagRegex = /<([a-zA-Z0-9_-]+)>([\s\S]*?)<\/\1>/g;
43
+ const nodes = [];
44
+ let lastIndex = 0;
45
+ let match;
46
+ let counter = 0;
47
+ while ((match = tagRegex.exec(raw)) !== null) {
48
+ if (match.index > lastIndex) {
49
+ nodes.push(raw.slice(lastIndex, match.index));
50
+ }
51
+ const tagName = match[1];
52
+ const content = match[2];
53
+ const component = components[tagName];
54
+ if (isValidElement(component)) {
55
+ nodes.push(
56
+ cloneElement(component, { key: `trans-${tagName}-${counter++}` }, content)
57
+ );
58
+ } else if (typeof component === "function") {
59
+ nodes.push(
60
+ /* @__PURE__ */ jsx(React.Fragment, { children: component(content) }, `trans-${tagName}-${counter++}`)
61
+ );
62
+ } else {
63
+ nodes.push(match[0]);
64
+ }
65
+ lastIndex = match.index + match[0].length;
66
+ }
67
+ if (lastIndex < raw.length) {
68
+ nodes.push(raw.slice(lastIndex));
69
+ }
70
+ return /* @__PURE__ */ jsx(Fragment, { children: nodes });
71
+ }
72
+
73
+ // src/react/index.tsx
74
+ import { jsx as jsx2 } from "react/jsx-runtime";
75
+ var LocLContext = createContext(null);
76
+ function LocLProvider({ translator, children }) {
77
+ return /* @__PURE__ */ jsx2(LocLContext.Provider, { value: translator, children });
78
+ }
79
+ function useLocL(customTranslator) {
80
+ const contextTranslator = useContext2(LocLContext);
81
+ const translator = customTranslator ?? contextTranslator;
82
+ if (!translator) {
83
+ throw new Error(
84
+ "[LocL] `useLocL` or `useTranslation` must be used within a `<LocLProvider>` or passed an explicit translator instance."
85
+ );
86
+ }
87
+ const getSnapshot = () => translator.getLanguage();
88
+ const language = useSyncExternalStore2(
89
+ (onStoreChange) => translator.subscribe(onStoreChange),
90
+ getSnapshot,
91
+ getSnapshot
92
+ );
93
+ return {
94
+ translator,
95
+ language,
96
+ changeLanguage: (lang) => translator.changeLanguage(lang),
97
+ t: translator.t.bind(translator),
98
+ plural: translator.plural.bind(translator),
99
+ rich: translator.rich.bind(translator),
100
+ format: translator.format.bind(translator)
101
+ };
102
+ }
103
+ function useTranslation(scope, customTranslator) {
104
+ const { translator, language, changeLanguage } = useLocL(customTranslator);
105
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
106
+ const scopedTranslator = useMemo2(() => {
107
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
108
+ }, [translator, scopeKey]);
109
+ return {
110
+ translator: scopedTranslator,
111
+ language,
112
+ changeLanguage,
113
+ t: scopedTranslator.t.bind(scopedTranslator),
114
+ plural: scopedTranslator.plural.bind(scopedTranslator),
115
+ rich: scopedTranslator.rich.bind(scopedTranslator),
116
+ format: scopedTranslator.format.bind(scopedTranslator)
117
+ };
118
+ }
119
+ function createLocLReact(defaultTranslator) {
120
+ const BoundContext = createContext(null);
121
+ function BoundLocLProvider({
122
+ translator = defaultTranslator,
123
+ children
124
+ }) {
125
+ return /* @__PURE__ */ jsx2(LocLContext.Provider, { value: translator, children: /* @__PURE__ */ jsx2(BoundContext.Provider, { value: translator, children }) });
126
+ }
127
+ function useBoundLocL(customTranslator) {
128
+ const contextTranslator = useContext2(BoundContext);
129
+ const translator = customTranslator ?? contextTranslator ?? defaultTranslator;
130
+ const getSnapshot = () => translator.getLanguage();
131
+ const language = useSyncExternalStore2(
132
+ (onStoreChange) => translator.subscribe(onStoreChange),
133
+ getSnapshot,
134
+ getSnapshot
135
+ );
136
+ return {
137
+ translator,
138
+ language,
139
+ changeLanguage: (lang) => translator.changeLanguage(lang),
140
+ t: translator.t.bind(translator),
141
+ plural: translator.plural.bind(translator),
142
+ rich: translator.rich.bind(translator),
143
+ format: translator.format.bind(translator)
144
+ };
145
+ }
146
+ function useBoundTranslation(scope, customTranslator) {
147
+ const { translator, language, changeLanguage } = useBoundLocL(customTranslator);
148
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
149
+ const scopedTranslator = useMemo2(() => {
150
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
151
+ }, [translator, scopeKey]);
152
+ return {
153
+ translator: scopedTranslator,
154
+ language,
155
+ changeLanguage,
156
+ t: scopedTranslator.t.bind(scopedTranslator),
157
+ plural: scopedTranslator.plural.bind(scopedTranslator),
158
+ rich: scopedTranslator.rich.bind(scopedTranslator),
159
+ format: scopedTranslator.format.bind(scopedTranslator)
160
+ };
161
+ }
162
+ function BoundTrans(props) {
163
+ const contextTranslator = useContext2(BoundContext);
164
+ const activeTranslator = props.translator ?? contextTranslator ?? defaultTranslator;
165
+ return /* @__PURE__ */ jsx2(
166
+ Trans,
167
+ {
168
+ ...props,
169
+ translator: activeTranslator
170
+ }
171
+ );
172
+ }
173
+ return {
174
+ LocLContext: BoundContext,
175
+ LocLProvider: BoundLocLProvider,
176
+ useLocL: useBoundLocL,
177
+ useTranslation: useBoundTranslation,
178
+ Trans: BoundTrans,
179
+ translator: defaultTranslator
180
+ };
181
+ }
182
+ export {
183
+ LocLContext,
184
+ LocLProvider,
185
+ Trans,
186
+ createLocLReact,
187
+ useLocL,
188
+ useTranslation
189
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locl-js",
3
- "version": "1.0.2",
3
+ "version": "2.0.1",
4
4
  "description": "Modern TypeScript internationalization (i18n) library with type-safety, performance, and backend usability",
5
5
  "license": "MIT",
6
6
  "author": "Sztrida Sámuel <sztsam@gmail.com>",
@@ -9,8 +9,27 @@
9
9
  "types": "dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
+ "types": "./dist/index.d.ts",
12
13
  "import": "./dist/index.mjs",
13
14
  "require": "./dist/index.cjs"
15
+ },
16
+ "./react": {
17
+ "types": "./dist/react.d.ts",
18
+ "import": "./dist/react.mjs",
19
+ "require": "./dist/react.cjs"
20
+ },
21
+ "./compat": {
22
+ "types": "./dist/compat.d.ts",
23
+ "import": "./dist/compat.mjs",
24
+ "require": "./dist/compat.cjs"
25
+ }
26
+ },
27
+ "peerDependencies": {
28
+ "react": ">=16.8.0"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "react": {
32
+ "optional": true
14
33
  }
15
34
  },
16
35
  "repository": {
@@ -26,7 +45,11 @@
26
45
  "devDependencies": {
27
46
  "@types/jest": "^30.0.0",
28
47
  "@types/node": "^24.5.2",
48
+ "@types/react": "^19.3.0",
49
+ "@types/react-dom": "^19.3.0",
29
50
  "jest": "^30.2.0",
51
+ "react": "^19.3.0",
52
+ "react-dom": "^19.3.0",
30
53
  "ts-jest": "^29.4.6",
31
54
  "ts-node": "^10.9.2",
32
55
  "tsup": "^8.5.1",