locl-js 1.0.2 → 2.0.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.
@@ -0,0 +1,120 @@
1
+ import React, { ReactElement, ReactNode } from 'react';
2
+ import { S as ScopeType, N as NestedKeyOf, T as TranslationObjectFor, P as PluralKeys, I as InterpolationOptions, a as LocL, c as Language } from './LocL-fN9Yari8.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
+ interface TransProps<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined, K extends string = TransKey<T, Fallback, S>> {
6
+ /** The translation key to look up (with IDE autocomplete + fallback tolerance) */
7
+ i18nKey: K | (string & {});
8
+ /** Values to interpolate into variable placeholders ({name}, {{name}}) */
9
+ values?: InterpolationOptions;
10
+ /**
11
+ * Component elements or functions to substitute for `<tag>content</tag>`
12
+ * @example
13
+ * components={{
14
+ * bold: <strong />,
15
+ * link: (content) => <a href="/terms">{content}</a>
16
+ * }}
17
+ */
18
+ components?: Record<string, ReactElement | ((content: ReactNode) => ReactNode)>;
19
+ /** Optional custom translator instance if not using LocLContext */
20
+ translator?: LocL<any, any, any, any, any>;
21
+ /** Optional namespace scope for the translation key */
22
+ scope?: S;
23
+ /** Fallback string if key is not found */
24
+ fallback?: string;
25
+ }
26
+ /**
27
+ * Renders translated text with rich React component interpolation without `dangerouslySetInnerHTML`.
28
+ * Subscribes reactively to language updates and supports strict type checking and autocompletion.
29
+ * @example
30
+ * ```tsx
31
+ * <Trans
32
+ * i18nKey="agreement"
33
+ * values={{ name: "Alice" }}
34
+ * components={{
35
+ * bold: <strong />,
36
+ * link: <a href="/terms" />
37
+ * }}
38
+ * />
39
+ * ```
40
+ */
41
+ declare function Trans<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined, K extends string = TransKey<T, Fallback, S>>({ i18nKey, values, components, translator: customTranslator, scope, fallback }: TransProps<T, Fallback, S, K>): ReactElement;
42
+
43
+ /**
44
+ * Global type registry for LocL React.
45
+ * Augment this interface in user application code for zero-boilerplate,
46
+ * 100% type-safe autocompletion across all hooks and components:
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * declare module "locl-js/react" {
51
+ * interface LocLRegister {
52
+ * translator: typeof translator;
53
+ * }
54
+ * }
55
+ * ```
56
+ */
57
+ interface LocLRegister {
58
+ }
59
+ type DefaultResources = LocLRegister extends {
60
+ translator: LocL<infer Res, any, any, any, any>;
61
+ } ? Res : LocLRegister extends {
62
+ resources: infer Res extends Record<string, any>;
63
+ } ? Res : Record<string, any>;
64
+ type DefaultFallback<T = DefaultResources> = LocLRegister extends {
65
+ translator: LocL<any, infer FB, any, any, any>;
66
+ } ? FB : LocLRegister extends {
67
+ fallbackLanguage: infer FB extends string;
68
+ } ? FB : [keyof T & string] extends [never] ? string : keyof T & string;
69
+ declare const LocLContext: React.Context<LocL<any, any, any, any, any> | null>;
70
+ interface LocLProviderProps<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined> {
71
+ translator: LocL<T, Fallback, S, any, any>;
72
+ children: ReactNode;
73
+ }
74
+ 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;
75
+ interface UseLocLResult<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined> {
76
+ translator: LocL<T, Fallback, S, any, any>;
77
+ language: Language<T>;
78
+ changeLanguage: (lang: Language<T>) => void;
79
+ t: LocL<T, Fallback, S>["t"];
80
+ plural: LocL<T, Fallback, S>["plural"];
81
+ rich: LocL<T, Fallback, S>["rich"];
82
+ format: LocL<T, Fallback, S>["format"];
83
+ }
84
+ 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> {
85
+ }
86
+ declare function useLocL<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>, S extends ScopeType<T, Fallback> = undefined>(customTranslator?: LocL<any, any, any, any, any>): UseLocLResult<T, Fallback, S>;
87
+ declare function useTranslation<T extends Record<string, any> = DefaultResources, Fallback extends keyof T & string = DefaultFallback<T>>(scope?: undefined, customTranslator?: LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, undefined>;
88
+ 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<any, any, any, any, any>): UseTranslationResult<T, Fallback, S>;
89
+ interface BoundLocLProviderProps<T extends Record<string, any>, Fallback extends keyof T & string> {
90
+ /** Optional override translator; defaults to the instance passed to `createLocLReact` */
91
+ translator?: LocL<any, any, any, any, any>;
92
+ children: ReactNode;
93
+ }
94
+ interface LocLReactSuite<T extends Record<string, any>, Fallback extends keyof T & string> {
95
+ LocLContext: React.Context<LocL<any, any, any, any, any> | null>;
96
+ LocLProvider: (props: BoundLocLProviderProps<T, Fallback>) => ReactElement;
97
+ useLocL: <S extends ScopeType<T, Fallback> = undefined>(customTranslator?: LocL<any, any, any, any, any>) => UseLocLResult<T, Fallback, S>;
98
+ useTranslation: {
99
+ (scope?: undefined, customTranslator?: LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, undefined>;
100
+ <S extends ScopeType<T, Fallback>>(scope: S, customTranslator?: LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, S>;
101
+ <S extends ScopeType<T, Fallback> = any>(scope?: S, customTranslator?: LocL<any, any, any, any, any>): UseTranslationResult<T, Fallback, S>;
102
+ };
103
+ Trans: <S extends ScopeType<T, Fallback> = undefined, K extends string = TransKey<T, Fallback, S>>(props: TransProps<T, Fallback, S, K>) => ReactElement;
104
+ translator: LocL<T, Fallback, any, any, any>;
105
+ }
106
+ /**
107
+ * Creates a pre-bound, zero-declaration React integration suite for a LocL translator instance.
108
+ *
109
+ * Inifers resources, fallback language, translation keys, and namespace scopes
110
+ * automatically without requiring `.d.ts` module declarations or manual generics.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * // i18n-react.ts
115
+ * export const { LocLProvider, useTranslation, useLocL, Trans } = createLocLReact(translator);
116
+ * ```
117
+ */
118
+ declare function createLocLReact<T extends Record<string, any>, Fallback extends keyof T & string>(defaultTranslator: LocL<T, Fallback, any, any, any>): LocLReactSuite<T, Fallback>;
119
+
120
+ export { type BoundLocLProviderProps, type DefaultFallback, type DefaultResources, LocLContext, LocLProvider, type LocLProviderProps, type LocLReactSuite, type LocLRegister, Trans, type TransKey, type TransProps, type UseLocLResult, type UseTranslationResult, createLocLReact, useLocL, useTranslation };
package/dist/react.js ADDED
@@ -0,0 +1,228 @@
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({
47
+ i18nKey,
48
+ values,
49
+ components = {},
50
+ translator: customTranslator,
51
+ scope,
52
+ fallback
53
+ }) {
54
+ const contextTranslator = (0, import_react.useContext)(LocLContext);
55
+ const activeTranslator = customTranslator ?? contextTranslator;
56
+ const getSnapshot = () => activeTranslator ? activeTranslator.getLanguage() : "";
57
+ const language = (0, import_react.useSyncExternalStore)(
58
+ (onStoreChange) => activeTranslator ? activeTranslator.subscribe(onStoreChange) : () => {
59
+ },
60
+ getSnapshot,
61
+ getSnapshot
62
+ );
63
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
64
+ const scopedTranslator = (0, import_react.useMemo)(() => {
65
+ if (!activeTranslator) return null;
66
+ return scope !== void 0 ? activeTranslator.withConfig({ scope }) : activeTranslator;
67
+ }, [activeTranslator, scopeKey, language]);
68
+ const raw = (0, import_react.useMemo)(() => {
69
+ if (!scopedTranslator) {
70
+ return String(fallback ?? i18nKey);
71
+ }
72
+ const tInstance = scopedTranslator;
73
+ const res = tInstance.t(i18nKey, values);
74
+ const keyExists = tInstance.get(i18nKey) !== void 0;
75
+ if (keyExists) {
76
+ return String(res);
77
+ }
78
+ return String(fallback ?? i18nKey);
79
+ }, [scopedTranslator, i18nKey, values, fallback, language]);
80
+ const tagRegex = /<([a-zA-Z0-9_-]+)>([\s\S]*?)<\/\1>/g;
81
+ const nodes = [];
82
+ let lastIndex = 0;
83
+ let match;
84
+ let counter = 0;
85
+ while ((match = tagRegex.exec(raw)) !== null) {
86
+ if (match.index > lastIndex) {
87
+ nodes.push(raw.slice(lastIndex, match.index));
88
+ }
89
+ const tagName = match[1];
90
+ const content = match[2];
91
+ const component = components[tagName];
92
+ if ((0, import_react.isValidElement)(component)) {
93
+ nodes.push(
94
+ (0, import_react.cloneElement)(component, { key: `trans-${tagName}-${counter++}` }, content)
95
+ );
96
+ } else if (typeof component === "function") {
97
+ nodes.push(
98
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.default.Fragment, { children: component(content) }, `trans-${tagName}-${counter++}`)
99
+ );
100
+ } else {
101
+ nodes.push(match[0]);
102
+ }
103
+ lastIndex = match.index + match[0].length;
104
+ }
105
+ if (lastIndex < raw.length) {
106
+ nodes.push(raw.slice(lastIndex));
107
+ }
108
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: nodes });
109
+ }
110
+
111
+ // src/react/index.tsx
112
+ var import_jsx_runtime2 = require("react/jsx-runtime");
113
+ var LocLContext = (0, import_react2.createContext)(null);
114
+ function LocLProvider({ translator, children }) {
115
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocLContext.Provider, { value: translator, children });
116
+ }
117
+ function useLocL(customTranslator) {
118
+ const contextTranslator = (0, import_react2.useContext)(LocLContext);
119
+ const translator = customTranslator ?? contextTranslator;
120
+ if (!translator) {
121
+ throw new Error(
122
+ "[LocL] `useLocL` or `useTranslation` must be used within a `<LocLProvider>` or passed an explicit translator instance."
123
+ );
124
+ }
125
+ const getSnapshot = () => translator.getLanguage();
126
+ const language = (0, import_react2.useSyncExternalStore)(
127
+ (onStoreChange) => translator.subscribe(onStoreChange),
128
+ getSnapshot,
129
+ getSnapshot
130
+ );
131
+ return {
132
+ translator,
133
+ language,
134
+ changeLanguage: (lang) => translator.changeLanguage(lang),
135
+ t: translator.t.bind(translator),
136
+ plural: translator.plural.bind(translator),
137
+ rich: translator.rich.bind(translator),
138
+ format: translator.format.bind(translator)
139
+ };
140
+ }
141
+ function useTranslation(scope, customTranslator) {
142
+ const { translator, language, changeLanguage } = useLocL(customTranslator);
143
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
144
+ const scopedTranslator = (0, import_react2.useMemo)(() => {
145
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
146
+ }, [translator, scopeKey]);
147
+ return {
148
+ translator: scopedTranslator,
149
+ language,
150
+ changeLanguage,
151
+ t: scopedTranslator.t.bind(scopedTranslator),
152
+ plural: scopedTranslator.plural.bind(scopedTranslator),
153
+ rich: scopedTranslator.rich.bind(scopedTranslator),
154
+ format: scopedTranslator.format.bind(scopedTranslator)
155
+ };
156
+ }
157
+ function createLocLReact(defaultTranslator) {
158
+ const BoundContext = (0, import_react2.createContext)(null);
159
+ function BoundLocLProvider({
160
+ translator = defaultTranslator,
161
+ children
162
+ }) {
163
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocLContext.Provider, { value: translator, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BoundContext.Provider, { value: translator, children }) });
164
+ }
165
+ function useBoundLocL(customTranslator) {
166
+ const contextTranslator = (0, import_react2.useContext)(BoundContext);
167
+ const translator = customTranslator ?? contextTranslator ?? defaultTranslator;
168
+ const getSnapshot = () => translator.getLanguage();
169
+ const language = (0, import_react2.useSyncExternalStore)(
170
+ (onStoreChange) => translator.subscribe(onStoreChange),
171
+ getSnapshot,
172
+ getSnapshot
173
+ );
174
+ return {
175
+ translator,
176
+ language,
177
+ changeLanguage: (lang) => translator.changeLanguage(lang),
178
+ t: translator.t.bind(translator),
179
+ plural: translator.plural.bind(translator),
180
+ rich: translator.rich.bind(translator),
181
+ format: translator.format.bind(translator)
182
+ };
183
+ }
184
+ function useBoundTranslation(scope, customTranslator) {
185
+ const { translator, language, changeLanguage } = useBoundLocL(customTranslator);
186
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
187
+ const scopedTranslator = (0, import_react2.useMemo)(() => {
188
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
189
+ }, [translator, scopeKey]);
190
+ return {
191
+ translator: scopedTranslator,
192
+ language,
193
+ changeLanguage,
194
+ t: scopedTranslator.t.bind(scopedTranslator),
195
+ plural: scopedTranslator.plural.bind(scopedTranslator),
196
+ rich: scopedTranslator.rich.bind(scopedTranslator),
197
+ format: scopedTranslator.format.bind(scopedTranslator)
198
+ };
199
+ }
200
+ function BoundTrans(props) {
201
+ const contextTranslator = (0, import_react2.useContext)(BoundContext);
202
+ const activeTranslator = props.translator ?? contextTranslator ?? defaultTranslator;
203
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
204
+ Trans,
205
+ {
206
+ ...props,
207
+ translator: activeTranslator
208
+ }
209
+ );
210
+ }
211
+ return {
212
+ LocLContext: BoundContext,
213
+ LocLProvider: BoundLocLProvider,
214
+ useLocL: useBoundLocL,
215
+ useTranslation: useBoundTranslation,
216
+ Trans: BoundTrans,
217
+ translator: defaultTranslator
218
+ };
219
+ }
220
+ // Annotate the CommonJS export names for ESM import in node:
221
+ 0 && (module.exports = {
222
+ LocLContext,
223
+ LocLProvider,
224
+ Trans,
225
+ createLocLReact,
226
+ useLocL,
227
+ useTranslation
228
+ });
package/dist/react.mjs ADDED
@@ -0,0 +1,188 @@
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({
8
+ i18nKey,
9
+ values,
10
+ components = {},
11
+ translator: customTranslator,
12
+ scope,
13
+ fallback
14
+ }) {
15
+ const contextTranslator = useContext(LocLContext);
16
+ const activeTranslator = customTranslator ?? contextTranslator;
17
+ const getSnapshot = () => activeTranslator ? activeTranslator.getLanguage() : "";
18
+ const language = useSyncExternalStore(
19
+ (onStoreChange) => activeTranslator ? activeTranslator.subscribe(onStoreChange) : () => {
20
+ },
21
+ getSnapshot,
22
+ getSnapshot
23
+ );
24
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
25
+ const scopedTranslator = useMemo(() => {
26
+ if (!activeTranslator) return null;
27
+ return scope !== void 0 ? activeTranslator.withConfig({ scope }) : activeTranslator;
28
+ }, [activeTranslator, scopeKey, language]);
29
+ const raw = useMemo(() => {
30
+ if (!scopedTranslator) {
31
+ return String(fallback ?? i18nKey);
32
+ }
33
+ const tInstance = scopedTranslator;
34
+ const res = tInstance.t(i18nKey, values);
35
+ const keyExists = tInstance.get(i18nKey) !== void 0;
36
+ if (keyExists) {
37
+ return String(res);
38
+ }
39
+ return String(fallback ?? i18nKey);
40
+ }, [scopedTranslator, i18nKey, values, fallback, language]);
41
+ const tagRegex = /<([a-zA-Z0-9_-]+)>([\s\S]*?)<\/\1>/g;
42
+ const nodes = [];
43
+ let lastIndex = 0;
44
+ let match;
45
+ let counter = 0;
46
+ while ((match = tagRegex.exec(raw)) !== null) {
47
+ if (match.index > lastIndex) {
48
+ nodes.push(raw.slice(lastIndex, match.index));
49
+ }
50
+ const tagName = match[1];
51
+ const content = match[2];
52
+ const component = components[tagName];
53
+ if (isValidElement(component)) {
54
+ nodes.push(
55
+ cloneElement(component, { key: `trans-${tagName}-${counter++}` }, content)
56
+ );
57
+ } else if (typeof component === "function") {
58
+ nodes.push(
59
+ /* @__PURE__ */ jsx(React.Fragment, { children: component(content) }, `trans-${tagName}-${counter++}`)
60
+ );
61
+ } else {
62
+ nodes.push(match[0]);
63
+ }
64
+ lastIndex = match.index + match[0].length;
65
+ }
66
+ if (lastIndex < raw.length) {
67
+ nodes.push(raw.slice(lastIndex));
68
+ }
69
+ return /* @__PURE__ */ jsx(Fragment, { children: nodes });
70
+ }
71
+
72
+ // src/react/index.tsx
73
+ import { jsx as jsx2 } from "react/jsx-runtime";
74
+ var LocLContext = createContext(null);
75
+ function LocLProvider({ translator, children }) {
76
+ return /* @__PURE__ */ jsx2(LocLContext.Provider, { value: translator, children });
77
+ }
78
+ function useLocL(customTranslator) {
79
+ const contextTranslator = useContext2(LocLContext);
80
+ const translator = customTranslator ?? contextTranslator;
81
+ if (!translator) {
82
+ throw new Error(
83
+ "[LocL] `useLocL` or `useTranslation` must be used within a `<LocLProvider>` or passed an explicit translator instance."
84
+ );
85
+ }
86
+ const getSnapshot = () => translator.getLanguage();
87
+ const language = useSyncExternalStore2(
88
+ (onStoreChange) => translator.subscribe(onStoreChange),
89
+ getSnapshot,
90
+ getSnapshot
91
+ );
92
+ return {
93
+ translator,
94
+ language,
95
+ changeLanguage: (lang) => translator.changeLanguage(lang),
96
+ t: translator.t.bind(translator),
97
+ plural: translator.plural.bind(translator),
98
+ rich: translator.rich.bind(translator),
99
+ format: translator.format.bind(translator)
100
+ };
101
+ }
102
+ function useTranslation(scope, customTranslator) {
103
+ const { translator, language, changeLanguage } = useLocL(customTranslator);
104
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
105
+ const scopedTranslator = useMemo2(() => {
106
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
107
+ }, [translator, scopeKey]);
108
+ return {
109
+ translator: scopedTranslator,
110
+ language,
111
+ changeLanguage,
112
+ t: scopedTranslator.t.bind(scopedTranslator),
113
+ plural: scopedTranslator.plural.bind(scopedTranslator),
114
+ rich: scopedTranslator.rich.bind(scopedTranslator),
115
+ format: scopedTranslator.format.bind(scopedTranslator)
116
+ };
117
+ }
118
+ function createLocLReact(defaultTranslator) {
119
+ const BoundContext = createContext(null);
120
+ function BoundLocLProvider({
121
+ translator = defaultTranslator,
122
+ children
123
+ }) {
124
+ return /* @__PURE__ */ jsx2(LocLContext.Provider, { value: translator, children: /* @__PURE__ */ jsx2(BoundContext.Provider, { value: translator, children }) });
125
+ }
126
+ function useBoundLocL(customTranslator) {
127
+ const contextTranslator = useContext2(BoundContext);
128
+ const translator = customTranslator ?? contextTranslator ?? defaultTranslator;
129
+ const getSnapshot = () => translator.getLanguage();
130
+ const language = useSyncExternalStore2(
131
+ (onStoreChange) => translator.subscribe(onStoreChange),
132
+ getSnapshot,
133
+ getSnapshot
134
+ );
135
+ return {
136
+ translator,
137
+ language,
138
+ changeLanguage: (lang) => translator.changeLanguage(lang),
139
+ t: translator.t.bind(translator),
140
+ plural: translator.plural.bind(translator),
141
+ rich: translator.rich.bind(translator),
142
+ format: translator.format.bind(translator)
143
+ };
144
+ }
145
+ function useBoundTranslation(scope, customTranslator) {
146
+ const { translator, language, changeLanguage } = useBoundLocL(customTranslator);
147
+ const scopeKey = Array.isArray(scope) ? scope.join("|") : scope;
148
+ const scopedTranslator = useMemo2(() => {
149
+ return scope !== void 0 ? translator.withConfig({ scope }) : translator;
150
+ }, [translator, scopeKey]);
151
+ return {
152
+ translator: scopedTranslator,
153
+ language,
154
+ changeLanguage,
155
+ t: scopedTranslator.t.bind(scopedTranslator),
156
+ plural: scopedTranslator.plural.bind(scopedTranslator),
157
+ rich: scopedTranslator.rich.bind(scopedTranslator),
158
+ format: scopedTranslator.format.bind(scopedTranslator)
159
+ };
160
+ }
161
+ function BoundTrans(props) {
162
+ const contextTranslator = useContext2(BoundContext);
163
+ const activeTranslator = props.translator ?? contextTranslator ?? defaultTranslator;
164
+ return /* @__PURE__ */ jsx2(
165
+ Trans,
166
+ {
167
+ ...props,
168
+ translator: activeTranslator
169
+ }
170
+ );
171
+ }
172
+ return {
173
+ LocLContext: BoundContext,
174
+ LocLProvider: BoundLocLProvider,
175
+ useLocL: useBoundLocL,
176
+ useTranslation: useBoundTranslation,
177
+ Trans: BoundTrans,
178
+ translator: defaultTranslator
179
+ };
180
+ }
181
+ export {
182
+ LocLContext,
183
+ LocLProvider,
184
+ Trans,
185
+ createLocLReact,
186
+ useLocL,
187
+ useTranslation
188
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locl-js",
3
- "version": "1.0.2",
3
+ "version": "2.0.0",
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",