langsys-js-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/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ ## 0.1.0 - 2026-05-29
2
+
3
+ Initial release. `langsys-js-react` is a thin React binding over the framework-agnostic [`langsys-js-typescript`](https://github.com/langsys/langsys-js-typescript) package — the React sibling of `langsys-js-svelte`. The base SDK owns the API client, translation lifecycle, token discovery, DOM tokenizer, and SSR-aware token strategies; this package adds only the React-native concerns.
4
+
5
+ ### Added
6
+
7
+ - **`LangsysApp`** — wrapper whose `init` accepts a `Signal<string>` as `UserLocaleStore` and delegates every other method to the base SDK singleton.
8
+ - **Hooks** built on `useSyncExternalStore`:
9
+ - `useT()` — returns the current `TFunction`; re-renders the component whenever translations or the loaded locale change. Signature `t(phrase, category?, params?)`.
10
+ - `useCurrentLocale()` — the locale whose translations are currently loaded.
11
+ - `useTranslations()` — the raw translation catalog.
12
+ - `useLocaleStore(initial?)` — creates one stable user-locale `Signal<string>`, reads it reactively, and returns `[locale, setLocale, store]`.
13
+ - `useSignal(signal)` — low-level bridge from any base-SDK `Signal<T>` to a reactive value.
14
+ - **`createLocaleStore(initial?)`** — make a user-locale store outside React (the analog of Svelte's `writable`).
15
+ - **`<Translate>`** — React component wrapping the base SDK's vanilla DOM `Translate` class via a ref + mount/destroy effect. Props: `category?`, `custom_id?`, `label?`, `tag?` (default `translate`), `className?`, `children`.
16
+ - **Raw signal re-exports** — `t` (the base SDK's `tSignal`), `currentlyLoadedLocale`, `sTranslations`, plus `createSignal`, for direct subscription outside React.
17
+ - **`{name}`-style placeholder interpolation** with compile-time-checked params via template-literal types. Allowed value types: `string | number | Date | boolean`; `Date` serializes to ISO 8601.
18
+ - **Type re-exports** sourced from `langsys-js-typescript`: `TFunction`, `TranslationParams`, `ParamPrimitive`, `ExtractParamKeys`, `ParamsFor`, `TArgs`, `Signal`, `iLangsysInitConfig` (React-flavored), `iLangsysResponse`, `iCategories`, `iTranslations`, `iContentBlock`, `iCountry`, `iCountryDialCode`, `iCountryList`, `iCurrency`, `iCurrencyList`, `iLanguageName`, `iLocaleData`, `iLocaleDefault`, `iLocaleFlat`, `iProject`.
19
+ - **`LangsysAppAPI`** re-export for direct (vanilla) API access.
20
+ - **`README-SSR.md`** documenting Next.js App Router and Pages Router usage.
21
+ - **`example/`** — a Vite playground (`npm run dev`) demonstrating `useT`, interpolation, categorization, locale switching, and `<Translate>`.
22
+
23
+ ### Tooling
24
+
25
+ - Built with `tsup` (ESM + CJS + `.d.ts`), matching `langsys-js-typescript`.
26
+ - `react` (`^18 || ^19`) is a peer dependency.
27
+ - CI (`typecheck` + `test`) and trusted-publishing release workflow mirror the Langsys SDK family.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Flexark International Ltda.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README-SSR.md ADDED
@@ -0,0 +1,249 @@
1
+ # SSR Usage Guide (Next.js / Remix)
2
+
3
+ This guide shows how to use `langsys-js-react` with Server-Side Rendering (SSR) to eliminate duplicate API calls and improve performance.
4
+
5
+ ## The problem
6
+
7
+ In a traditional SSR flow:
8
+ 1. The server fetches translations during render.
9
+ 2. The client re-fetches the same translations after hydration.
10
+ 3. Duplicate API calls, slower initial render, possible flash of untranslated content.
11
+
12
+ ## The solution
13
+
14
+ Pass pre-fetched translations from server to client using the `initialTranslations` config option. The client SDK uses them as-is and skips the initial fetch. Because the hooks are built on `useSyncExternalStore` with a server snapshot, the first paint already reflects the seeded translations.
15
+
16
+ ## Next.js — App Router
17
+
18
+ ### Step 1: Fetch translations on the server
19
+
20
+ ```tsx
21
+ // app/layout.tsx (Server Component)
22
+ import type { iCategories } from 'langsys-js-react';
23
+ import { LangsysClient } from './LangsysClient';
24
+
25
+ async function getTranslations(locale: string): Promise<iCategories> {
26
+ const res = await fetch(
27
+ `https://api.langsys.dev/api/projects/${process.env.LANGSYS_PROJECT_ID}/translations?locale=${locale}`,
28
+ { headers: { 'x-Authorization': process.env.LANGSYS_API_KEY!, 'Content-Type': 'application/json' } }
29
+ );
30
+ const result = await res.json();
31
+ return result.data as iCategories;
32
+ }
33
+
34
+ export default async function RootLayout({ children }: { children: React.ReactNode }) {
35
+ const locale = 'en'; // from cookie / Accept-Language / route segment
36
+ const translations = await getTranslations(locale);
37
+
38
+ return (
39
+ <html lang={locale}>
40
+ <body>
41
+ <LangsysClient
42
+ locale={locale}
43
+ translations={translations}
44
+ projectId={process.env.LANGSYS_PROJECT_ID!}
45
+ apiKey={process.env.NEXT_PUBLIC_LANGSYS_API_KEY!} // read-only key for the client
46
+ >
47
+ {children}
48
+ </LangsysClient>
49
+ </body>
50
+ </html>
51
+ );
52
+ }
53
+ ```
54
+
55
+ ### Step 2: Initialize on the client
56
+
57
+ ```tsx
58
+ // app/LangsysClient.tsx
59
+ 'use client';
60
+
61
+ import { useEffect, useState, type ReactNode } from 'react';
62
+ import { LangsysApp, useLocaleStore, type iCategories } from 'langsys-js-react';
63
+
64
+ export function LangsysClient({
65
+ locale,
66
+ translations,
67
+ projectId,
68
+ apiKey,
69
+ children,
70
+ }: {
71
+ locale: string;
72
+ translations: iCategories;
73
+ projectId: string;
74
+ apiKey: string;
75
+ children: ReactNode;
76
+ }) {
77
+ const [, , localeStore] = useLocaleStore(locale);
78
+
79
+ useEffect(() => {
80
+ LangsysApp.init({
81
+ projectid: projectId,
82
+ key: apiKey,
83
+ UserLocaleStore: localeStore,
84
+ baseLocale: 'en',
85
+ initialTranslations: translations,
86
+ initialTranslationsLocale: locale,
87
+ ssrTokenStrategy: 'client',
88
+ });
89
+ }, [localeStore, projectId, apiKey, translations, locale]);
90
+
91
+ return <>{children}</>;
92
+ }
93
+ ```
94
+
95
+ ### Step 3: Use translations in any client component
96
+
97
+ ```tsx
98
+ 'use client';
99
+ import { useT } from 'langsys-js-react';
100
+
101
+ export function Hero() {
102
+ const t = useT();
103
+ return (
104
+ <>
105
+ <h1>{t('Welcome', 'HomePage')}</h1>
106
+ <p>{t('Hello, {name}!', 'HomePage', { name: 'Sarah' })}</p>
107
+ </>
108
+ );
109
+ }
110
+ ```
111
+
112
+ ## Next.js — Pages Router
113
+
114
+ ```tsx
115
+ // pages/_app.tsx
116
+ import type { AppProps } from 'next/app';
117
+ import { useEffect } from 'react';
118
+ import { LangsysApp, useLocaleStore } from 'langsys-js-react';
119
+
120
+ export default function App({ Component, pageProps }: AppProps) {
121
+ const locale = pageProps.locale ?? 'en';
122
+ const [, , localeStore] = useLocaleStore(locale);
123
+
124
+ useEffect(() => {
125
+ LangsysApp.init({
126
+ projectid: process.env.NEXT_PUBLIC_LANGSYS_PROJECT_ID!,
127
+ key: process.env.NEXT_PUBLIC_LANGSYS_API_KEY!,
128
+ UserLocaleStore: localeStore,
129
+ baseLocale: 'en',
130
+ initialTranslations: pageProps.translations,
131
+ initialTranslationsLocale: locale,
132
+ });
133
+ }, [localeStore, pageProps.translations, locale]);
134
+
135
+ return <Component {...pageProps} />;
136
+ }
137
+ ```
138
+
139
+ ```tsx
140
+ // pages/index.tsx — fetch translations in getServerSideProps
141
+ import type { iCategories } from 'langsys-js-react';
142
+
143
+ export async function getServerSideProps() {
144
+ const locale = 'en';
145
+ const res = await fetch(
146
+ `https://api.langsys.dev/api/projects/${process.env.LANGSYS_PROJECT_ID}/translations?locale=${locale}`,
147
+ { headers: { 'x-Authorization': process.env.LANGSYS_API_KEY!, 'Content-Type': 'application/json' } }
148
+ );
149
+ const { data } = await res.json();
150
+ return { props: { locale, translations: data as iCategories } };
151
+ }
152
+ ```
153
+
154
+ ## Locale switching
155
+
156
+ Update the store from the same `useLocaleStore` call; the SDK reacts and fetches the new locale's translations:
157
+
158
+ ```tsx
159
+ 'use client';
160
+ import { LangsysApp, useLocaleStore } from 'langsys-js-react';
161
+
162
+ export function LocaleSwitcher() {
163
+ const [locale, setLocale] = useLocaleStore('en');
164
+
165
+ function changeLocale(next: string) {
166
+ setLocale(next); // subscribers in the SDK trigger a fetch
167
+ return LangsysApp.translationsLoadingPromise; // optional: await the in-flight fetch
168
+ }
169
+
170
+ return (
171
+ <select value={locale} onChange={(e) => changeLocale(e.target.value)}>
172
+ <option value="en">English</option>
173
+ <option value="es">Español</option>
174
+ <option value="fr">Français</option>
175
+ </select>
176
+ );
177
+ }
178
+ ```
179
+
180
+ > Keep one locale store for the app (created where you call `init`) and thread `setLocale` down via context or props, rather than calling `useLocaleStore` with a fresh initial value in unrelated trees.
181
+
182
+ ## Benefits
183
+
184
+ ### Performance
185
+ - No duplicate API calls (server + client).
186
+ - Translations ready immediately on hydration.
187
+ - Faster Time to Interactive (TTI).
188
+ - Reduced API usage and costs.
189
+
190
+ ### User experience
191
+ - No flash of untranslated content.
192
+ - Instant translation display.
193
+ - Better SEO with server-rendered translations.
194
+
195
+ ### Developer experience
196
+ - Simple configuration.
197
+ - Full TypeScript support, including compile-time-checked interpolation params on `t()`.
198
+
199
+ ## Configuration options
200
+
201
+ ### SSR token strategy
202
+
203
+ ```typescript
204
+ { ssrTokenStrategy: 'client' | 'server' | 'auto' }
205
+ ```
206
+
207
+ - `'client'` (default) — queue tokens, send from client after hydration.
208
+ - `'server'` — send tokens immediately from server.
209
+ - `'auto'` — small batches (≤5) from server, larger batches from client.
210
+
211
+ ### Debug mode
212
+
213
+ ```typescript
214
+ { debug: true, initialTranslations: translations, initialTranslationsLocale: locale }
215
+ ```
216
+
217
+ Look for:
218
+ - `SSR initial translations config:` on init — confirms pre-fetched data is detected.
219
+ - `Using pre-fetched translations for locale` — confirms the initial fetch was skipped.
220
+ - `Locale change detected!` — fires on a subsequent locale switch.
221
+
222
+ ## Important notes
223
+
224
+ 1. **One-time use.** `initialTranslations` is consumed only at init. Locale changes after init go through the normal fetch path.
225
+ 2. **Matching locales.** Always provide `initialTranslationsLocale` with `initialTranslations` so the SDK knows what locale the data represents.
226
+ 3. **Data format.** The translations payload must match the `iCategories` shape returned by `LangsysAppAPI.getTranslations()`.
227
+ 4. **Cache.** The 60-second locale cache still applies. Pre-fetched translations count as cached.
228
+ 5. **Token creation.** Use a read-only API key for the client in production — missing tokens won't be sent. Keep the write key on the server (and ideally pre-populate tokens via your local dev environment).
229
+ 6. **`'use client'`.** `useT`, `useLocaleStore`, `<Translate>`, and `LangsysApp.init` run on the client — keep them in Client Components.
230
+
231
+ ## Troubleshooting
232
+
233
+ ### Translations not appearing
234
+ - Check that `initialTranslationsLocale` matches the `UserLocaleStore` value at init.
235
+ - Verify the translations payload matches the `iCategories` shape.
236
+ - Enable `debug: true` and look for the messages above.
237
+
238
+ ### Still seeing duplicate API calls
239
+ - Confirm both `initialTranslations` *and* `initialTranslationsLocale` are passed.
240
+ - Confirm init runs before any rendering that calls `t(...)`.
241
+ - Confirm the locale hasn't drifted between server and client.
242
+
243
+ ### Hydration mismatch warnings
244
+ - Make sure the `locale` you seed on the server matches the initial value you pass to `useLocaleStore` on the client.
245
+ - Keep `LangsysApp.init` inside `useEffect` (client-only) so the server render and the first client render agree.
246
+
247
+ ### TypeScript errors on `t()`
248
+ - Placeholders are compile-time-checked: `t('Hello, {name}!', 'Cat')` *requires* a params object with `name`. Either add the key or remove the placeholder.
249
+ - Allowed param value types: `string | number | Date | boolean`.
package/README.md ADDED
@@ -0,0 +1,254 @@
1
+ # Langsys SDK - React
2
+
3
+ Langsys revolutionizes localization for apps with easy to integrate, realtime, continuous translations. Read more about Langsys Translation Manager [at the website](https://Langsys.dev/).
4
+
5
+ Integrate the Langsys Translation Manager into your React, Next.js, Remix, or Vite applications using this SDK.
6
+
7
+ ## Requirements
8
+
9
+ - **React 18 or 19** (the reactive layer is built on `useSyncExternalStore`).
10
+
11
+ [![GitHub Release](https://img.shields.io/github/release/langsys/langsys-js-react.svg?style=flat)]()
12
+ [![GitHub last commit](https://img.shields.io/github/last-commit/langsys/langsys-js-react.svg?style=flat)]()
13
+ [![GitHub pull requests](https://img.shields.io/github/issues-pr/langsys/langsys-js-react.svg?style=flat)]()
14
+ [![PR's Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
15
+ [![NPM License](https://img.shields.io/npm/l/all-contributors.svg?style=flat)](https://github.com/langsys/langsys-js-react/blob/main/LICENSE)
16
+
17
+ ## How it's layered
18
+
19
+ `langsys-js-react` is a thin React binding over the framework-agnostic [`langsys-js-typescript`](https://github.com/langsys/langsys-js-typescript) package — which owns the API client, translation lifecycle, token discovery, DOM tokenizer, and SSR-aware token strategies. This package adds only the React-native concerns:
20
+
21
+ - A `LangsysApp` whose `init` accepts a `Signal<string>` (made with `createLocaleStore`) for the user locale
22
+ - Hooks — `useT`, `useCurrentLocale`, `useTranslations`, `useLocaleStore` — that re-render components when translations or the loaded locale change
23
+ - A `<Translate>` component wrapping the underlying DOM walker
24
+
25
+ If you need the SDK outside React (a Node script, a non-React web app), import from `langsys-js-typescript` directly.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ npm install langsys-js-react
31
+ ```
32
+
33
+ `langsys-js-typescript` is installed automatically as a transitive dependency. `react` is a peer dependency you already have.
34
+
35
+ ## Creating a Langsys project
36
+
37
+ Visit [Langsys.dev](https://Langsys.dev/) to create your account, then create your project. Take note of your project ID and API key.
38
+
39
+ ### API key permissions
40
+
41
+ - **Write key** (development): the SDK auto-creates new translation tokens and content blocks as they appear in your app.
42
+ - **Read-only key** (production): the SDK fetches translations only — no token creation, no content-block writes.
43
+
44
+ The SDK detects the key type automatically and behaves accordingly.
45
+
46
+ ## Initialization
47
+
48
+ Initialize once, high in your tree. Create the user-locale store with `useLocaleStore` and pass it to `LangsysApp.init`:
49
+
50
+ ```tsx
51
+ // src/LangsysProvider.tsx
52
+ import { useEffect, useState, type ReactNode } from 'react';
53
+ import { LangsysApp, useLocaleStore } from 'langsys-js-react';
54
+
55
+ export function LangsysGate({ children }: { children: ReactNode }) {
56
+ const [, , localeStore] = useLocaleStore('en-us');
57
+ const [ready, setReady] = useState(false);
58
+ const [error, setError] = useState<string | null>(null);
59
+
60
+ useEffect(() => {
61
+ LangsysApp.init({
62
+ projectid: import.meta.env.VITE_LANGSYS_PROJECT_ID,
63
+ key: import.meta.env.VITE_LANGSYS_API_KEY,
64
+ UserLocaleStore: localeStore,
65
+ baseLocale: 'en-us',
66
+ debug: false,
67
+ ssrTokenStrategy: 'client',
68
+ }).then((res) => {
69
+ if (res.status) setReady(true);
70
+ else setError(res.errors?.join(', ') ?? 'Init failed');
71
+ });
72
+ }, [localeStore]);
73
+
74
+ if (error) return <p>Langsys init failed: {error}</p>;
75
+ if (!ready) return <p>Loading…</p>;
76
+ return <>{children}</>;
77
+ }
78
+ ```
79
+
80
+ `UserLocaleStore` is a `Signal<string>` — switch it with `setLocale(...)` (from the same `useLocaleStore` call) or `localeStore.set('fr-fr')`, and the SDK reacts. If you'd rather keep the locale store at module scope, `const localeStore = createLocaleStore('en-us')` works too.
81
+
82
+ ### SSR token strategy
83
+
84
+ `ssrTokenStrategy` (default `'client'`) controls when missing tokens are sent during server rendering:
85
+
86
+ - `'client'` — tokens collected on the server are flushed from the client after hydration. Best for performance.
87
+ - `'server'` — tokens are sent immediately during SSR. Best for reliability and immediate registration.
88
+ - `'auto'` — small batches (≤5) sent from server, larger queued for client.
89
+
90
+ ## Using translations
91
+
92
+ ### `useT()` — the everyday API
93
+
94
+ `useT()` returns the current translation function and re-renders the component whenever translations or the loaded locale change.
95
+
96
+ ```tsx
97
+ import { useT } from 'langsys-js-react';
98
+
99
+ function Welcome() {
100
+ const t = useT();
101
+ return (
102
+ <>
103
+ <h1>{t('Welcome to my app', 'UI')}</h1>
104
+ <p>{t('Hello, {name}!', 'UI', { name: 'Sarah' })}</p>
105
+ </>
106
+ );
107
+ }
108
+ ```
109
+
110
+ The translation function signature is **`t(phrase, category?, params?)`**:
111
+
112
+ ```tsx
113
+ t('Save'); // no category, no params
114
+ t('Save', 'UI'); // categorized
115
+ t('Hello, {name}!', { name: 'X' }); // no category, with params
116
+ t('Hello, {name}!', 'Greetings', { name: 'X' }); // category + params
117
+ ```
118
+
119
+ The **phrase itself is the lookup key** *and* the base-language default — there's no separate keys file to maintain. The first render of a phrase registers it in the Translation Manager (when using a write key); from then on, translations are fetched and rendered automatically as locales change.
120
+
121
+ #### Interpolation
122
+
123
+ Curly-brace placeholders are substituted from the params argument:
124
+
125
+ ```tsx
126
+ t('You have {count} new messages', 'Notifications', { count: 3 });
127
+ ```
128
+
129
+ Placeholder names are extracted from the phrase at compile time and **type-checked**: omitting a required key or adding an extra one is a TypeScript error.
130
+
131
+ ```tsx
132
+ t('You have {count} new messages', 'Notifications', {});
133
+ // ❌ Property 'count' is missing in type '{}'
134
+
135
+ t('You have {count} new messages', 'Notifications', { count: 3, extra: 'x' });
136
+ // ❌ Object literal may only specify known properties, and 'extra' does not exist
137
+ ```
138
+
139
+ Allowed value types: `string | number | Date | boolean`. Dates serialize to ISO 8601.
140
+
141
+ > Future versions will swap the simple `{name}` runtime for ICU MessageFormat — adding plural / select / date formatting — without changing the public signature. Today's `t('{count} items', 'Cart', { count })` will evolve to `t('{count, plural, one {# item} other {# items}}', 'Cart', { count })`.
142
+
143
+ #### Categorization disambiguates context
144
+
145
+ Different categories give the *same* phrase different translations:
146
+
147
+ ```tsx
148
+ <strong>{t('Home', 'Main Menu')}</strong> {/* "Inicio" in Spanish */}
149
+ <strong>{t('Home', 'Home repairs')}</strong> {/* "Hogar" in Spanish */}
150
+ ```
151
+
152
+ Without categorization, "Home" would only have one translation — which can't work for both contexts. Langsys's philosophy is *translate once, use everywhere*; categorize when the same phrase legitimately means different things.
153
+
154
+ A good rule for category names: the module or feature the phrase lives in (`Account`, `Errors`, `Checkout`, `UI`).
155
+
156
+ ### `<Translate>` — HTML content blocks
157
+
158
+ For larger blocks of HTML where the structure should be preserved for the translator:
159
+
160
+ ```tsx
161
+ import { Translate } from 'langsys-js-react';
162
+
163
+ function Article() {
164
+ return (
165
+ <Translate category="Blog" tag="article">
166
+ <h1 className="title">My article title</h1>
167
+ <p>My content <strong>is the best</strong> when internationalized by Langsys.</p>
168
+ <p>Translators see this exactly as users do — same styling, same structure.</p>
169
+ </Translate>
170
+ );
171
+ }
172
+ ```
173
+
174
+ The component:
175
+ - Recursively tokenizes text nodes and translatable attributes (`placeholder`, `alt`, `title`, `aria-label`, plus button/input `value` attributes and `<option>` text).
176
+ - Captures semantic CSS so translators see the styled appearance in the Translation Manager.
177
+ - Registers the whole thing as a **content block** that translators handle as one unit while still translating the individual phrases inside.
178
+ - Auto re-translates on locale change.
179
+
180
+ `<Translate>` mounts the SDK's DOM walker on its host element and lets it mutate the rendered output in place, so **keep its children static** — prose, marketing copy, CMS-rendered articles, forms with placeholders. For dynamic per-string values that React owns, use `useT()`.
181
+
182
+ ```tsx
183
+ {/* CMS content goes through Translate as-is */}
184
+ <Translate category="News" tag="div">
185
+ <div dangerouslySetInnerHTML={{ __html: article?.content ?? '' }} />
186
+ </Translate>
187
+ ```
188
+
189
+ `<Translate>` props: `category?`, `custom_id?`, `label?`, `tag?` (defaults to `translate`), `className?`, `children`.
190
+
191
+ ## Hooks & reactive primitives
192
+
193
+ | Export | Type | Notes |
194
+ |---|---|---|
195
+ | `useT()` | `() => TFunction` | Re-renders on translations/locale change. Call as `const t = useT(); t('Phrase', 'Cat', params?)`. |
196
+ | `useCurrentLocale()` | `() => string` | The locale whose translations are currently loaded (lags the user-selected locale until the fetch completes). |
197
+ | `useTranslations()` | `() => iCategories` | Raw translation catalog. Rarely needed in app code. |
198
+ | `useLocaleStore(initial?)` | `() => [locale, setLocale, store]` | Creates a stable user-locale `Signal<string>`, reads it reactively, returns a setter. Pass `store` to `init`. |
199
+ | `useSignal(signal)` | `<T>(s: Signal<T>) => T` | Low-level: subscribe a component to any base-SDK signal. |
200
+ | `createLocaleStore(initial?)` | `(s?: string) => Signal<string>` | Make a user-locale store outside React (module scope). |
201
+ | `t` / `currentlyLoadedLocale` / `sTranslations` | `Signal<…>` | Raw signals for direct subscription outside React. In components, prefer the hooks. |
202
+
203
+ ## Server-Side Rendering (Next.js, Remix)
204
+
205
+ The SDK is SSR-compatible. The main pattern is to pre-fetch translations server-side and seed them through `initialTranslations` / `initialTranslationsLocale` so the client doesn't refetch on hydration. `useT` and friends are built on `useSyncExternalStore` with a server snapshot, so they hydrate without a flash of untranslated content when seeded.
206
+
207
+ 📖 **See [README-SSR.md](./README-SSR.md)** for a complete Next.js (App Router & Pages Router) walkthrough.
208
+
209
+ ## Utilities
210
+
211
+ `LangsysApp` exposes localized helpers (call them from effects / event handlers):
212
+
213
+ ```tsx
214
+ import { LangsysApp, type iCountryList, type iCurrencyList, type iLocaleDefault } from 'langsys-js-react';
215
+
216
+ const countries: iCountryList = await LangsysApp.getCountries(); // [{ code: "US", label: "United States" }, ...]
217
+ const dialCodes = await LangsysApp.getDialCodes(); // [{ country_code: "US", dial_code: "+1", name: "United States" }, ...]
218
+ const currencies: iCurrencyList = await LangsysApp.getCurrencies(); // [{ code: "USD", name: "US Dollar", symbol: "$", ... }, ...]
219
+ const locales: iLocaleDefault = await LangsysApp.getLocales(); // { "English": [{ code: "en-US", name: "English (US)" }, ...], ... }
220
+ const localeName = await LangsysApp.getLocaleNameWithLookup('es-es', true, 'fr-fr'); // "espagnol"
221
+ ```
222
+
223
+ ### Detecting the user's preferred locale
224
+
225
+ ```typescript
226
+ // Browser: navigator.languages → fallback to navigator.language
227
+ const locale = LangsysApp.detectPreferredLocale();
228
+ // Returns 'en-US', 'fr', etc., or false if not detected
229
+
230
+ // SSR (route handler / middleware): parses Accept-Language
231
+ const locale = LangsysApp.detectPreferredLocale(request.headers.get('Accept-Language'));
232
+
233
+ // Matched against your app's supported locales
234
+ const supportedLocales = (await LangsysApp.getLocalesFlat()).map((l) => l.code);
235
+ const locale = LangsysApp.detectPreferredLocale(request.headers.get('Accept-Language'), supportedLocales);
236
+ ```
237
+
238
+ The matcher tries exact match first (e.g. `en-US`), then language-only (`en` matches `en-GB`), then returns `false` if no match.
239
+
240
+ ### Waiting for translations to load
241
+
242
+ When changing locale mid-session, you may want to re-run dependent code after the new translations arrive:
243
+
244
+ ```tsx
245
+ useEffect(() => {
246
+ LangsysApp.translationsLoadingPromise.then(() => {
247
+ // re-render content / regenerate UI here
248
+ });
249
+ }, [locale]);
250
+ ```
251
+
252
+ ## License
253
+
254
+ MIT © Langsys
@@ -0,0 +1,161 @@
1
+ import * as langsys_js_typescript from 'langsys-js-typescript';
2
+ import { Signal, TFunction, iCategories, iLangsysInitConfig as iLangsysInitConfig$1, iLangsysResponse, iCountryList, iCurrencyList, iCountryDialCode, iLocaleDefault, iLocaleFlat, iLocaleData } from 'langsys-js-typescript';
3
+ export { ExtractParamKeys, LangsysAppAPI, ParamPrimitive, ParamsFor, Signal, TArgs, TFunction, TranslationParams, createSignal, currentlyLoadedLocale, iCategories, iContentBlock, iCountry, iCountryDialCode, iCountryList, iCurrency, iCurrencyList, iLangsysResponse, iLanguageName, iLocaleData, iLocaleDefault, iLocaleFlat, iProject, iTranslations, sTranslations, tSignal as t } from 'langsys-js-typescript';
4
+ import * as react from 'react';
5
+ import { ReactNode } from 'react';
6
+
7
+ /**
8
+ * Subscribe a React component to a base-SDK `Signal<T>` and return its current
9
+ * value, re-rendering whenever the signal changes.
10
+ *
11
+ * This is the React mirror of Svelte's `$store` auto-subscription. Where the
12
+ * Svelte wrapper adapts a native store *into* the SDK (`writable` → `Signal`),
13
+ * the React wrapper adapts the SDK's signals *out* to the render cycle. It is
14
+ * built on `useSyncExternalStore`, so it is concurrent-safe and SSR-safe — the
15
+ * server snapshot reads the same `.get()`, which the base SDK seeds from
16
+ * `initialTranslations` during SSR.
17
+ *
18
+ * The base SDK's signals are stable between changes (every `set` replaces the
19
+ * value, so `.get()` returns a fresh reference only after a real change). That
20
+ * is exactly the identity contract `useSyncExternalStore` requires — no tearing,
21
+ * no render loops.
22
+ *
23
+ * `signal` must be stable across renders: a module-level singleton (the SDK's
24
+ * `t` / `currentlyLoadedLocale` / `sTranslations`) or one created once with
25
+ * `useState(() => createLocaleStore(...))`. Passing a freshly-created signal on
26
+ * every render would resubscribe on every render.
27
+ */
28
+ declare function useSignal<T>(signal: Signal<T>): T;
29
+ /**
30
+ * Create a reactive `Signal<string>` to hold the user's selected locale — the
31
+ * React analog of Svelte's `writable('en-us')`.
32
+ *
33
+ * Pass the result as `UserLocaleStore` to `LangsysApp.init`, read it reactively
34
+ * with `useSignal(store)` (or use the all-in-one `useLocaleStore` hook), and
35
+ * switch locale with `store.set('fr-fr')`. The base SDK only ever reads and
36
+ * subscribes to it — it never writes.
37
+ */
38
+ declare function createLocaleStore(initial?: string): Signal<string>;
39
+
40
+ /**
41
+ * The current translation function, re-rendering the calling component whenever
42
+ * translations or the loaded locale change. This is the React analog of Svelte's
43
+ * `$t` store read.
44
+ *
45
+ * const t = useT();
46
+ * return <h1>{t('Welcome to my app', 'UI')}</h1>;
47
+ *
48
+ * The phrase is both the lookup key and the base-language default. Signature:
49
+ * `t(phrase, category?, params?)`. Placeholder names in the phrase are
50
+ * type-checked against the params object at the call site.
51
+ */
52
+ declare function useT(): TFunction;
53
+ /**
54
+ * The locale whose translations are currently loaded. This lags the
55
+ * user-selected locale (`UserLocaleStore`) until the fetch for the new locale
56
+ * settles, which makes it the right value to gate "translations are ready" UI on.
57
+ */
58
+ declare function useCurrentLocale(): string;
59
+ /**
60
+ * The raw translation catalog. Rarely needed in app code — prefer `useT()`.
61
+ * Exposed for advanced cases (inspecting which categories/phrases are loaded).
62
+ */
63
+ declare function useTranslations(): iCategories;
64
+ /**
65
+ * All-in-one convenience for the user-locale store. Creates one stable
66
+ * `Signal<string>` (the React analog of Svelte's `writable`), subscribes the
67
+ * component to it, and returns `[locale, setLocale, store]`.
68
+ *
69
+ * const [locale, setLocale, store] = useLocaleStore('en-us');
70
+ * useEffect(() => {
71
+ * LangsysApp.init({ projectid, key, UserLocaleStore: store });
72
+ * }, [store]);
73
+ *
74
+ * <select value={locale} onChange={(e) => setLocale(e.target.value)}>...
75
+ *
76
+ * The store is created lazily once and never re-created across renders, so it is
77
+ * safe to pass to `LangsysApp.init` and to depend on in effects.
78
+ */
79
+ declare function useLocaleStore(initial?: string): [string, (locale: string) => void, Signal<string>];
80
+
81
+ /**
82
+ * Props for the React `Translate` component. Mirrors the Svelte component's
83
+ * props 1:1, with Svelte's `class` renamed to React's idiomatic `className`.
84
+ */
85
+ interface TranslateProps {
86
+ /** Optional category under which tokens are registered. Helps translators disambiguate. */
87
+ category?: string;
88
+ /** Optional stable id for the content block. If omitted, the SDK hashes category + tokens. */
89
+ custom_id?: string;
90
+ /** Optional human-readable label shown in the Translation Manager. */
91
+ label?: string;
92
+ /** Host element tag. Defaults to a `<translate>` custom element. */
93
+ tag?: string;
94
+ /** Class applied to the host element. */
95
+ className?: string;
96
+ children?: ReactNode;
97
+ }
98
+ /**
99
+ * React wrapper around the vanilla `Translate` DOM class from
100
+ * `langsys-js-typescript`. It renders a host element, then on mount lets the
101
+ * vanilla class walk and tokenize the rendered children (text nodes plus
102
+ * translatable attributes), register the content block, and re-translate on
103
+ * locale change. On unmount it tears the instance down.
104
+ *
105
+ * This is the React analog of the Svelte `<Translate>` component — pure
106
+ * mount/destroy glue. The DOM walking, content-block registration, attribute
107
+ * harvesting, and re-translation lifecycle all live in the base SDK.
108
+ *
109
+ * Like the Svelte component, this lets the SDK mutate the rendered DOM in place,
110
+ * so keep the children static: prose, marketing copy, CMS-rendered HTML — the
111
+ * content-block use case. For dynamic per-string values that React owns and
112
+ * re-renders, use `useT()` instead.
113
+ */
114
+ declare function Translate({ category, custom_id, label, tag, className, children, }: TranslateProps): react.ReactElement<{
115
+ ref: react.RefObject<HTMLElement | null>;
116
+ className: string | undefined;
117
+ }, string | react.JSXElementConstructor<any>>;
118
+
119
+ /**
120
+ * React-flavored init config. Identical to the base SDK's config except
121
+ * `UserLocaleStore` is typed as a `Signal<string>` — create one with
122
+ * `createLocaleStore()` (or get one from the `useLocaleStore` hook). The base
123
+ * SDK only reads and subscribes to it.
124
+ */
125
+ interface iLangsysInitConfig extends Omit<iLangsysInitConfig$1, 'UserLocaleStore'> {
126
+ UserLocaleStore: Signal<string>;
127
+ }
128
+ /**
129
+ * React SDK entry point. Delegates everything to the underlying
130
+ * `langsys-js-typescript` singleton. Because React's locale store is already a
131
+ * `Signal` (unlike Svelte's `Writable`, which needs adapting), `init` is a
132
+ * straight passthrough — the React-native concerns live in the hooks and the
133
+ * `<Translate>` component, not here.
134
+ */
135
+ declare class LangsysAppReact {
136
+ /** Initialize Langsys. Pass a `Signal<string>` (from `createLocaleStore`) as `UserLocaleStore`. */
137
+ init(config: iLangsysInitConfig): Promise<iLangsysResponse>;
138
+ get Translations(): langsys_js_typescript.Translations;
139
+ get translationsLoadingPromise(): Promise<unknown>;
140
+ /** Current translation function. Reads fresh state on every call (not reactive on its own — use `useT()` in components). */
141
+ get t(): TFunction;
142
+ get debug(): langsys_js_typescript.Logger;
143
+ refresh(): Promise<boolean>;
144
+ getCountries(inLocale?: string): Promise<iCountryList>;
145
+ getCountryName(forCountryCode: string, inLocale?: string): Promise<string>;
146
+ getCurrencies(inLocale?: string): Promise<iCurrencyList>;
147
+ getCurrencyName(forCurrencyCode: string, inLocale?: string): Promise<string>;
148
+ getDialCodes(inLocale?: string): Promise<iCountryDialCode[]>;
149
+ getLocales(inLocale?: string): Promise<iLocaleDefault>;
150
+ getLocalesFlat(inLocale?: string): Promise<iLocaleFlat[]>;
151
+ getLocalesData(inLocale?: string, forceRefresh?: boolean): Promise<iLocaleData[]>;
152
+ getLocalesFormat(format?: '' | 'flat' | 'data', inLocale?: string): Promise<iLocaleFlat[] | iLocaleDefault | iLocaleData[]>;
153
+ getLocaleName(forLocale: string, shortName?: boolean, inLocale?: string): string;
154
+ getLocaleNameWithLookup(forLocale: string, shortName?: boolean, inLocale?: string): Promise<string>;
155
+ /** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */
156
+ getLanguageName(forLocale: string, shortName?: boolean, inLocale?: string): Promise<string>;
157
+ detectPreferredLocale(acceptLanguageHeader?: string | null, supportedLocales?: string[]): string | false;
158
+ }
159
+ declare const LangsysApp: LangsysAppReact;
160
+
161
+ export { LangsysApp, Translate, type TranslateProps, createLocaleStore, type iLangsysInitConfig, useCurrentLocale, useLocaleStore, useSignal, useT, useTranslations };
@@ -0,0 +1,161 @@
1
+ import * as langsys_js_typescript from 'langsys-js-typescript';
2
+ import { Signal, TFunction, iCategories, iLangsysInitConfig as iLangsysInitConfig$1, iLangsysResponse, iCountryList, iCurrencyList, iCountryDialCode, iLocaleDefault, iLocaleFlat, iLocaleData } from 'langsys-js-typescript';
3
+ export { ExtractParamKeys, LangsysAppAPI, ParamPrimitive, ParamsFor, Signal, TArgs, TFunction, TranslationParams, createSignal, currentlyLoadedLocale, iCategories, iContentBlock, iCountry, iCountryDialCode, iCountryList, iCurrency, iCurrencyList, iLangsysResponse, iLanguageName, iLocaleData, iLocaleDefault, iLocaleFlat, iProject, iTranslations, sTranslations, tSignal as t } from 'langsys-js-typescript';
4
+ import * as react from 'react';
5
+ import { ReactNode } from 'react';
6
+
7
+ /**
8
+ * Subscribe a React component to a base-SDK `Signal<T>` and return its current
9
+ * value, re-rendering whenever the signal changes.
10
+ *
11
+ * This is the React mirror of Svelte's `$store` auto-subscription. Where the
12
+ * Svelte wrapper adapts a native store *into* the SDK (`writable` → `Signal`),
13
+ * the React wrapper adapts the SDK's signals *out* to the render cycle. It is
14
+ * built on `useSyncExternalStore`, so it is concurrent-safe and SSR-safe — the
15
+ * server snapshot reads the same `.get()`, which the base SDK seeds from
16
+ * `initialTranslations` during SSR.
17
+ *
18
+ * The base SDK's signals are stable between changes (every `set` replaces the
19
+ * value, so `.get()` returns a fresh reference only after a real change). That
20
+ * is exactly the identity contract `useSyncExternalStore` requires — no tearing,
21
+ * no render loops.
22
+ *
23
+ * `signal` must be stable across renders: a module-level singleton (the SDK's
24
+ * `t` / `currentlyLoadedLocale` / `sTranslations`) or one created once with
25
+ * `useState(() => createLocaleStore(...))`. Passing a freshly-created signal on
26
+ * every render would resubscribe on every render.
27
+ */
28
+ declare function useSignal<T>(signal: Signal<T>): T;
29
+ /**
30
+ * Create a reactive `Signal<string>` to hold the user's selected locale — the
31
+ * React analog of Svelte's `writable('en-us')`.
32
+ *
33
+ * Pass the result as `UserLocaleStore` to `LangsysApp.init`, read it reactively
34
+ * with `useSignal(store)` (or use the all-in-one `useLocaleStore` hook), and
35
+ * switch locale with `store.set('fr-fr')`. The base SDK only ever reads and
36
+ * subscribes to it — it never writes.
37
+ */
38
+ declare function createLocaleStore(initial?: string): Signal<string>;
39
+
40
+ /**
41
+ * The current translation function, re-rendering the calling component whenever
42
+ * translations or the loaded locale change. This is the React analog of Svelte's
43
+ * `$t` store read.
44
+ *
45
+ * const t = useT();
46
+ * return <h1>{t('Welcome to my app', 'UI')}</h1>;
47
+ *
48
+ * The phrase is both the lookup key and the base-language default. Signature:
49
+ * `t(phrase, category?, params?)`. Placeholder names in the phrase are
50
+ * type-checked against the params object at the call site.
51
+ */
52
+ declare function useT(): TFunction;
53
+ /**
54
+ * The locale whose translations are currently loaded. This lags the
55
+ * user-selected locale (`UserLocaleStore`) until the fetch for the new locale
56
+ * settles, which makes it the right value to gate "translations are ready" UI on.
57
+ */
58
+ declare function useCurrentLocale(): string;
59
+ /**
60
+ * The raw translation catalog. Rarely needed in app code — prefer `useT()`.
61
+ * Exposed for advanced cases (inspecting which categories/phrases are loaded).
62
+ */
63
+ declare function useTranslations(): iCategories;
64
+ /**
65
+ * All-in-one convenience for the user-locale store. Creates one stable
66
+ * `Signal<string>` (the React analog of Svelte's `writable`), subscribes the
67
+ * component to it, and returns `[locale, setLocale, store]`.
68
+ *
69
+ * const [locale, setLocale, store] = useLocaleStore('en-us');
70
+ * useEffect(() => {
71
+ * LangsysApp.init({ projectid, key, UserLocaleStore: store });
72
+ * }, [store]);
73
+ *
74
+ * <select value={locale} onChange={(e) => setLocale(e.target.value)}>...
75
+ *
76
+ * The store is created lazily once and never re-created across renders, so it is
77
+ * safe to pass to `LangsysApp.init` and to depend on in effects.
78
+ */
79
+ declare function useLocaleStore(initial?: string): [string, (locale: string) => void, Signal<string>];
80
+
81
+ /**
82
+ * Props for the React `Translate` component. Mirrors the Svelte component's
83
+ * props 1:1, with Svelte's `class` renamed to React's idiomatic `className`.
84
+ */
85
+ interface TranslateProps {
86
+ /** Optional category under which tokens are registered. Helps translators disambiguate. */
87
+ category?: string;
88
+ /** Optional stable id for the content block. If omitted, the SDK hashes category + tokens. */
89
+ custom_id?: string;
90
+ /** Optional human-readable label shown in the Translation Manager. */
91
+ label?: string;
92
+ /** Host element tag. Defaults to a `<translate>` custom element. */
93
+ tag?: string;
94
+ /** Class applied to the host element. */
95
+ className?: string;
96
+ children?: ReactNode;
97
+ }
98
+ /**
99
+ * React wrapper around the vanilla `Translate` DOM class from
100
+ * `langsys-js-typescript`. It renders a host element, then on mount lets the
101
+ * vanilla class walk and tokenize the rendered children (text nodes plus
102
+ * translatable attributes), register the content block, and re-translate on
103
+ * locale change. On unmount it tears the instance down.
104
+ *
105
+ * This is the React analog of the Svelte `<Translate>` component — pure
106
+ * mount/destroy glue. The DOM walking, content-block registration, attribute
107
+ * harvesting, and re-translation lifecycle all live in the base SDK.
108
+ *
109
+ * Like the Svelte component, this lets the SDK mutate the rendered DOM in place,
110
+ * so keep the children static: prose, marketing copy, CMS-rendered HTML — the
111
+ * content-block use case. For dynamic per-string values that React owns and
112
+ * re-renders, use `useT()` instead.
113
+ */
114
+ declare function Translate({ category, custom_id, label, tag, className, children, }: TranslateProps): react.ReactElement<{
115
+ ref: react.RefObject<HTMLElement | null>;
116
+ className: string | undefined;
117
+ }, string | react.JSXElementConstructor<any>>;
118
+
119
+ /**
120
+ * React-flavored init config. Identical to the base SDK's config except
121
+ * `UserLocaleStore` is typed as a `Signal<string>` — create one with
122
+ * `createLocaleStore()` (or get one from the `useLocaleStore` hook). The base
123
+ * SDK only reads and subscribes to it.
124
+ */
125
+ interface iLangsysInitConfig extends Omit<iLangsysInitConfig$1, 'UserLocaleStore'> {
126
+ UserLocaleStore: Signal<string>;
127
+ }
128
+ /**
129
+ * React SDK entry point. Delegates everything to the underlying
130
+ * `langsys-js-typescript` singleton. Because React's locale store is already a
131
+ * `Signal` (unlike Svelte's `Writable`, which needs adapting), `init` is a
132
+ * straight passthrough — the React-native concerns live in the hooks and the
133
+ * `<Translate>` component, not here.
134
+ */
135
+ declare class LangsysAppReact {
136
+ /** Initialize Langsys. Pass a `Signal<string>` (from `createLocaleStore`) as `UserLocaleStore`. */
137
+ init(config: iLangsysInitConfig): Promise<iLangsysResponse>;
138
+ get Translations(): langsys_js_typescript.Translations;
139
+ get translationsLoadingPromise(): Promise<unknown>;
140
+ /** Current translation function. Reads fresh state on every call (not reactive on its own — use `useT()` in components). */
141
+ get t(): TFunction;
142
+ get debug(): langsys_js_typescript.Logger;
143
+ refresh(): Promise<boolean>;
144
+ getCountries(inLocale?: string): Promise<iCountryList>;
145
+ getCountryName(forCountryCode: string, inLocale?: string): Promise<string>;
146
+ getCurrencies(inLocale?: string): Promise<iCurrencyList>;
147
+ getCurrencyName(forCurrencyCode: string, inLocale?: string): Promise<string>;
148
+ getDialCodes(inLocale?: string): Promise<iCountryDialCode[]>;
149
+ getLocales(inLocale?: string): Promise<iLocaleDefault>;
150
+ getLocalesFlat(inLocale?: string): Promise<iLocaleFlat[]>;
151
+ getLocalesData(inLocale?: string, forceRefresh?: boolean): Promise<iLocaleData[]>;
152
+ getLocalesFormat(format?: '' | 'flat' | 'data', inLocale?: string): Promise<iLocaleFlat[] | iLocaleDefault | iLocaleData[]>;
153
+ getLocaleName(forLocale: string, shortName?: boolean, inLocale?: string): string;
154
+ getLocaleNameWithLookup(forLocale: string, shortName?: boolean, inLocale?: string): Promise<string>;
155
+ /** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */
156
+ getLanguageName(forLocale: string, shortName?: boolean, inLocale?: string): Promise<string>;
157
+ detectPreferredLocale(acceptLanguageHeader?: string | null, supportedLocales?: string[]): string | false;
158
+ }
159
+ declare const LangsysApp: LangsysAppReact;
160
+
161
+ export { LangsysApp, Translate, type TranslateProps, createLocaleStore, type iLangsysInitConfig, useCurrentLocale, useLocaleStore, useSignal, useT, useTranslations };
package/dist/index.js ADDED
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+
3
+ var langsysJsTypescript = require('langsys-js-typescript');
4
+ var react = require('react');
5
+
6
+ // src/index.ts
7
+ function useSignal(signal) {
8
+ return react.useSyncExternalStore(signal.subscribe, signal.get, signal.get);
9
+ }
10
+ function createLocaleStore(initial = "en-us") {
11
+ return langsysJsTypescript.createSignal(initial);
12
+ }
13
+ function useT() {
14
+ return useSignal(langsysJsTypescript.tSignal);
15
+ }
16
+ function useCurrentLocale() {
17
+ return useSignal(langsysJsTypescript.currentlyLoadedLocale);
18
+ }
19
+ function useTranslations() {
20
+ return useSignal(langsysJsTypescript.sTranslations);
21
+ }
22
+ function useLocaleStore(initial = "en-us") {
23
+ const [store] = react.useState(() => createLocaleStore(initial));
24
+ const locale = useSignal(store);
25
+ return [locale, store.set, store];
26
+ }
27
+ function Translate({
28
+ category = "",
29
+ custom_id = "",
30
+ label = "",
31
+ tag = "translate",
32
+ className,
33
+ children
34
+ }) {
35
+ const hostRef = react.useRef(null);
36
+ react.useEffect(() => {
37
+ const host = hostRef.current;
38
+ if (!host) return;
39
+ const instance = new langsysJsTypescript.Translate(host, { category, custom_id, label });
40
+ return () => instance.destroy();
41
+ }, [category, custom_id, label]);
42
+ return react.createElement(tag, { ref: hostRef, className }, children);
43
+ }
44
+
45
+ // src/index.ts
46
+ var LangsysAppReact = class {
47
+ /** Initialize Langsys. Pass a `Signal<string>` (from `createLocaleStore`) as `UserLocaleStore`. */
48
+ init(config) {
49
+ return langsysJsTypescript.LangsysApp.init(config);
50
+ }
51
+ get Translations() {
52
+ return langsysJsTypescript.LangsysApp.Translations;
53
+ }
54
+ get translationsLoadingPromise() {
55
+ return langsysJsTypescript.LangsysApp.translationsLoadingPromise;
56
+ }
57
+ /** Current translation function. Reads fresh state on every call (not reactive on its own — use `useT()` in components). */
58
+ get t() {
59
+ return langsysJsTypescript.LangsysApp.t;
60
+ }
61
+ get debug() {
62
+ return langsysJsTypescript.LangsysApp.debug;
63
+ }
64
+ refresh() {
65
+ return langsysJsTypescript.LangsysApp.refresh();
66
+ }
67
+ getCountries(inLocale) {
68
+ return langsysJsTypescript.LangsysApp.getCountries(inLocale);
69
+ }
70
+ getCountryName(forCountryCode, inLocale) {
71
+ return langsysJsTypescript.LangsysApp.getCountryName(forCountryCode, inLocale);
72
+ }
73
+ getCurrencies(inLocale) {
74
+ return langsysJsTypescript.LangsysApp.getCurrencies(inLocale);
75
+ }
76
+ getCurrencyName(forCurrencyCode, inLocale) {
77
+ return langsysJsTypescript.LangsysApp.getCurrencyName(forCurrencyCode, inLocale);
78
+ }
79
+ getDialCodes(inLocale) {
80
+ return langsysJsTypescript.LangsysApp.getDialCodes(inLocale);
81
+ }
82
+ getLocales(inLocale) {
83
+ return langsysJsTypescript.LangsysApp.getLocales(inLocale);
84
+ }
85
+ getLocalesFlat(inLocale) {
86
+ return langsysJsTypescript.LangsysApp.getLocalesFlat(inLocale);
87
+ }
88
+ getLocalesData(inLocale, forceRefresh) {
89
+ return langsysJsTypescript.LangsysApp.getLocalesData(inLocale, forceRefresh);
90
+ }
91
+ getLocalesFormat(format = "", inLocale) {
92
+ return langsysJsTypescript.LangsysApp.getLocalesFormat(format, inLocale);
93
+ }
94
+ getLocaleName(forLocale, shortName, inLocale) {
95
+ return langsysJsTypescript.LangsysApp.getLocaleName(forLocale, shortName, inLocale);
96
+ }
97
+ getLocaleNameWithLookup(forLocale, shortName, inLocale) {
98
+ return langsysJsTypescript.LangsysApp.getLocaleNameWithLookup(forLocale, shortName, inLocale);
99
+ }
100
+ /** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */
101
+ getLanguageName(forLocale, shortName, inLocale) {
102
+ return langsysJsTypescript.LangsysApp.getLanguageName(forLocale, shortName, inLocale);
103
+ }
104
+ detectPreferredLocale(acceptLanguageHeader, supportedLocales) {
105
+ return langsysJsTypescript.LangsysApp.detectPreferredLocale(acceptLanguageHeader, supportedLocales);
106
+ }
107
+ };
108
+ var LangsysApp = new LangsysAppReact();
109
+
110
+ Object.defineProperty(exports, "LangsysAppAPI", {
111
+ enumerable: true,
112
+ get: function () { return langsysJsTypescript.LangsysAppAPI; }
113
+ });
114
+ Object.defineProperty(exports, "createSignal", {
115
+ enumerable: true,
116
+ get: function () { return langsysJsTypescript.createSignal; }
117
+ });
118
+ Object.defineProperty(exports, "currentlyLoadedLocale", {
119
+ enumerable: true,
120
+ get: function () { return langsysJsTypescript.currentlyLoadedLocale; }
121
+ });
122
+ Object.defineProperty(exports, "sTranslations", {
123
+ enumerable: true,
124
+ get: function () { return langsysJsTypescript.sTranslations; }
125
+ });
126
+ Object.defineProperty(exports, "t", {
127
+ enumerable: true,
128
+ get: function () { return langsysJsTypescript.tSignal; }
129
+ });
130
+ exports.LangsysApp = LangsysApp;
131
+ exports.Translate = Translate;
132
+ exports.createLocaleStore = createLocaleStore;
133
+ exports.useCurrentLocale = useCurrentLocale;
134
+ exports.useLocaleStore = useLocaleStore;
135
+ exports.useSignal = useSignal;
136
+ exports.useT = useT;
137
+ exports.useTranslations = useTranslations;
138
+ //# sourceMappingURL=index.js.map
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters.ts","../src/hooks.ts","../src/components/Translate.tsx","../src/index.ts"],"names":["useSyncExternalStore","createSignal","tSignal","currentlyLoadedLocale","sTranslations","useState","useRef","useEffect","VanillaTranslate","createElement","_LangsysApp"],"mappings":"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","file":"index.js","sourcesContent":["import { useSyncExternalStore } from 'react';\nimport { createSignal, type Signal } from 'langsys-js-typescript';\n\n/**\n * Subscribe a React component to a base-SDK `Signal<T>` and return its current\n * value, re-rendering whenever the signal changes.\n *\n * This is the React mirror of Svelte's `$store` auto-subscription. Where the\n * Svelte wrapper adapts a native store *into* the SDK (`writable` → `Signal`),\n * the React wrapper adapts the SDK's signals *out* to the render cycle. It is\n * built on `useSyncExternalStore`, so it is concurrent-safe and SSR-safe — the\n * server snapshot reads the same `.get()`, which the base SDK seeds from\n * `initialTranslations` during SSR.\n *\n * The base SDK's signals are stable between changes (every `set` replaces the\n * value, so `.get()` returns a fresh reference only after a real change). That\n * is exactly the identity contract `useSyncExternalStore` requires — no tearing,\n * no render loops.\n *\n * `signal` must be stable across renders: a module-level singleton (the SDK's\n * `t` / `currentlyLoadedLocale` / `sTranslations`) or one created once with\n * `useState(() => createLocaleStore(...))`. Passing a freshly-created signal on\n * every render would resubscribe on every render.\n */\nexport function useSignal<T>(signal: Signal<T>): T {\n return useSyncExternalStore(signal.subscribe, signal.get, signal.get);\n}\n\n/**\n * Create a reactive `Signal<string>` to hold the user's selected locale — the\n * React analog of Svelte's `writable('en-us')`.\n *\n * Pass the result as `UserLocaleStore` to `LangsysApp.init`, read it reactively\n * with `useSignal(store)` (or use the all-in-one `useLocaleStore` hook), and\n * switch locale with `store.set('fr-fr')`. The base SDK only ever reads and\n * subscribes to it — it never writes.\n */\nexport function createLocaleStore(initial = 'en-us'): Signal<string> {\n return createSignal<string>(initial);\n}\n","import { useState } from 'react';\nimport { currentlyLoadedLocale, sTranslations, tSignal } from 'langsys-js-typescript';\nimport type { Signal, TFunction, iCategories } from 'langsys-js-typescript';\nimport { createLocaleStore, useSignal } from './adapters.js';\n\n/**\n * The current translation function, re-rendering the calling component whenever\n * translations or the loaded locale change. This is the React analog of Svelte's\n * `$t` store read.\n *\n * const t = useT();\n * return <h1>{t('Welcome to my app', 'UI')}</h1>;\n *\n * The phrase is both the lookup key and the base-language default. Signature:\n * `t(phrase, category?, params?)`. Placeholder names in the phrase are\n * type-checked against the params object at the call site.\n */\nexport function useT(): TFunction {\n return useSignal(tSignal);\n}\n\n/**\n * The locale whose translations are currently loaded. This lags the\n * user-selected locale (`UserLocaleStore`) until the fetch for the new locale\n * settles, which makes it the right value to gate \"translations are ready\" UI on.\n */\nexport function useCurrentLocale(): string {\n return useSignal(currentlyLoadedLocale);\n}\n\n/**\n * The raw translation catalog. Rarely needed in app code — prefer `useT()`.\n * Exposed for advanced cases (inspecting which categories/phrases are loaded).\n */\nexport function useTranslations(): iCategories {\n return useSignal(sTranslations);\n}\n\n/**\n * All-in-one convenience for the user-locale store. Creates one stable\n * `Signal<string>` (the React analog of Svelte's `writable`), subscribes the\n * component to it, and returns `[locale, setLocale, store]`.\n *\n * const [locale, setLocale, store] = useLocaleStore('en-us');\n * useEffect(() => {\n * LangsysApp.init({ projectid, key, UserLocaleStore: store });\n * }, [store]);\n *\n * <select value={locale} onChange={(e) => setLocale(e.target.value)}>...\n *\n * The store is created lazily once and never re-created across renders, so it is\n * safe to pass to `LangsysApp.init` and to depend on in effects.\n */\nexport function useLocaleStore(initial = 'en-us'): [string, (locale: string) => void, Signal<string>] {\n const [store] = useState(() => createLocaleStore(initial));\n const locale = useSignal(store);\n return [locale, store.set, store];\n}\n","import { createElement, useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { Translate as VanillaTranslate } from 'langsys-js-typescript';\n\n/**\n * Props for the React `Translate` component. Mirrors the Svelte component's\n * props 1:1, with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface TranslateProps {\n /** Optional category under which tokens are registered. Helps translators disambiguate. */\n category?: string;\n /** Optional stable id for the content block. If omitted, the SDK hashes category + tokens. */\n custom_id?: string;\n /** Optional human-readable label shown in the Translation Manager. */\n label?: string;\n /** Host element tag. Defaults to a `<translate>` custom element. */\n tag?: string;\n /** Class applied to the host element. */\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * React wrapper around the vanilla `Translate` DOM class from\n * `langsys-js-typescript`. It renders a host element, then on mount lets the\n * vanilla class walk and tokenize the rendered children (text nodes plus\n * translatable attributes), register the content block, and re-translate on\n * locale change. On unmount it tears the instance down.\n *\n * This is the React analog of the Svelte `<Translate>` component — pure\n * mount/destroy glue. The DOM walking, content-block registration, attribute\n * harvesting, and re-translation lifecycle all live in the base SDK.\n *\n * Like the Svelte component, this lets the SDK mutate the rendered DOM in place,\n * so keep the children static: prose, marketing copy, CMS-rendered HTML — the\n * content-block use case. For dynamic per-string values that React owns and\n * re-renders, use `useT()` instead.\n */\nexport function Translate({\n category = '',\n custom_id = '',\n label = '',\n tag = 'translate',\n className,\n children,\n}: TranslateProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaTranslate(host, { category, custom_id, label });\n return () => instance.destroy();\n }, [category, custom_id, label]);\n\n return createElement(tag, { ref: hostRef, className }, children);\n}\n\nexport default Translate;\n","/**\n * langsys-js-react — idiomatic React binding over `langsys-js-typescript`.\n *\n * Public API:\n * - `LangsysApp` — `init` accepts a `Signal<string>` (make one with\n * `createLocaleStore`) for the user locale; every other method delegates.\n * - Hooks — `useT`, `useCurrentLocale`, `useTranslations`, `useLocaleStore`,\n * and the low-level `useSignal`. These are the reactive layer; in components\n * prefer them over the raw signals.\n * - `createLocaleStore` — make the user-locale store (React analog of Svelte's\n * `writable`).\n * - `Translate` — React component wrapping the vanilla DOM `Translate` class.\n * - Raw signals `t` / `currentlyLoadedLocale` / `sTranslations` — re-exported\n * for advanced/direct subscription outside React's render cycle.\n */\n\nimport {\n LangsysApp as _LangsysApp,\n type ExtractParamKeys,\n type ParamPrimitive,\n type ParamsFor,\n type Signal,\n type TArgs,\n type TFunction,\n type TranslationParams,\n type iCategories,\n type iContentBlock,\n type iCountry,\n type iCountryDialCode,\n type iCountryList,\n type iCurrency,\n type iCurrencyList,\n type iLangsysInitConfig as iVanillaInitConfig,\n type iLangsysResponse,\n type iLanguageName,\n type iLocaleData,\n type iLocaleDefault,\n type iLocaleFlat,\n type iProject,\n type iTranslations,\n} from 'langsys-js-typescript';\n\n// Reactive primitives (raw signals) — re-exported for advanced/direct\n// subscription. `tSignal` is exposed under the friendlier name `t`. In\n// components, prefer the hooks (`useT`, `useCurrentLocale`, …).\nexport { currentlyLoadedLocale, createSignal, sTranslations, tSignal as t } from 'langsys-js-typescript';\n\n// API client (vanilla — no React concerns)\nexport { LangsysAppAPI } from 'langsys-js-typescript';\n\n// Hooks + adapters (the React-idiomatic reactive layer)\nexport { createLocaleStore, useSignal } from './adapters.js';\nexport { useCurrentLocale, useLocaleStore, useT, useTranslations } from './hooks.js';\n\n// Component\nexport { Translate, type TranslateProps } from './components/Translate.js';\n\n// Type re-exports — these are framework-agnostic, so consumers can rely on them\n// directly without reaching into `langsys-js-typescript`.\nexport type {\n ExtractParamKeys,\n ParamPrimitive,\n ParamsFor,\n Signal,\n TArgs,\n TFunction,\n TranslationParams,\n iCategories,\n iContentBlock,\n iCountry,\n iCountryDialCode,\n iCountryList,\n iCurrency,\n iCurrencyList,\n iLangsysResponse,\n iLanguageName,\n iLocaleData,\n iLocaleDefault,\n iLocaleFlat,\n iProject,\n iTranslations,\n};\n\n/**\n * React-flavored init config. Identical to the base SDK's config except\n * `UserLocaleStore` is typed as a `Signal<string>` — create one with\n * `createLocaleStore()` (or get one from the `useLocaleStore` hook). The base\n * SDK only reads and subscribes to it.\n */\nexport interface iLangsysInitConfig extends Omit<iVanillaInitConfig, 'UserLocaleStore'> {\n UserLocaleStore: Signal<string>;\n}\n\n/**\n * React SDK entry point. Delegates everything to the underlying\n * `langsys-js-typescript` singleton. Because React's locale store is already a\n * `Signal` (unlike Svelte's `Writable`, which needs adapting), `init` is a\n * straight passthrough — the React-native concerns live in the hooks and the\n * `<Translate>` component, not here.\n */\nclass LangsysAppReact {\n /** Initialize Langsys. Pass a `Signal<string>` (from `createLocaleStore`) as `UserLocaleStore`. */\n public init(config: iLangsysInitConfig): Promise<iLangsysResponse> {\n return _LangsysApp.init(config);\n }\n\n public get Translations() {\n return _LangsysApp.Translations;\n }\n\n public get translationsLoadingPromise() {\n return _LangsysApp.translationsLoadingPromise;\n }\n\n /** Current translation function. Reads fresh state on every call (not reactive on its own — use `useT()` in components). */\n public get t(): TFunction {\n return _LangsysApp.t;\n }\n\n public get debug() {\n return _LangsysApp.debug;\n }\n\n public refresh() {\n return _LangsysApp.refresh();\n }\n\n public getCountries(inLocale?: string) {\n return _LangsysApp.getCountries(inLocale);\n }\n public getCountryName(forCountryCode: string, inLocale?: string) {\n return _LangsysApp.getCountryName(forCountryCode, inLocale);\n }\n public getCurrencies(inLocale?: string) {\n return _LangsysApp.getCurrencies(inLocale);\n }\n public getCurrencyName(forCurrencyCode: string, inLocale?: string) {\n return _LangsysApp.getCurrencyName(forCurrencyCode, inLocale);\n }\n public getDialCodes(inLocale?: string) {\n return _LangsysApp.getDialCodes(inLocale);\n }\n\n public getLocales(inLocale?: string) {\n return _LangsysApp.getLocales(inLocale);\n }\n public getLocalesFlat(inLocale?: string) {\n return _LangsysApp.getLocalesFlat(inLocale);\n }\n public getLocalesData(inLocale?: string, forceRefresh?: boolean) {\n return _LangsysApp.getLocalesData(inLocale, forceRefresh);\n }\n public getLocalesFormat(format: '' | 'flat' | 'data' = '', inLocale?: string) {\n return _LangsysApp.getLocalesFormat(format, inLocale);\n }\n public getLocaleName(forLocale: string, shortName?: boolean, inLocale?: string) {\n return _LangsysApp.getLocaleName(forLocale, shortName, inLocale);\n }\n public getLocaleNameWithLookup(forLocale: string, shortName?: boolean, inLocale?: string) {\n return _LangsysApp.getLocaleNameWithLookup(forLocale, shortName, inLocale);\n }\n\n /** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */\n public getLanguageName(forLocale: string, shortName?: boolean, inLocale?: string) {\n return _LangsysApp.getLanguageName(forLocale, shortName, inLocale);\n }\n\n public detectPreferredLocale(acceptLanguageHeader?: string | null, supportedLocales?: string[]) {\n return _LangsysApp.detectPreferredLocale(acceptLanguageHeader, supportedLocales);\n }\n}\n\nexport const LangsysApp = new LangsysAppReact();\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,111 @@
1
+ import { createSignal, tSignal, currentlyLoadedLocale, sTranslations, Translate as Translate$1, LangsysApp as LangsysApp$1 } from 'langsys-js-typescript';
2
+ export { LangsysAppAPI, createSignal, currentlyLoadedLocale, sTranslations, tSignal as t } from 'langsys-js-typescript';
3
+ import { useSyncExternalStore, useState, useRef, useEffect, createElement } from 'react';
4
+
5
+ // src/index.ts
6
+ function useSignal(signal) {
7
+ return useSyncExternalStore(signal.subscribe, signal.get, signal.get);
8
+ }
9
+ function createLocaleStore(initial = "en-us") {
10
+ return createSignal(initial);
11
+ }
12
+ function useT() {
13
+ return useSignal(tSignal);
14
+ }
15
+ function useCurrentLocale() {
16
+ return useSignal(currentlyLoadedLocale);
17
+ }
18
+ function useTranslations() {
19
+ return useSignal(sTranslations);
20
+ }
21
+ function useLocaleStore(initial = "en-us") {
22
+ const [store] = useState(() => createLocaleStore(initial));
23
+ const locale = useSignal(store);
24
+ return [locale, store.set, store];
25
+ }
26
+ function Translate({
27
+ category = "",
28
+ custom_id = "",
29
+ label = "",
30
+ tag = "translate",
31
+ className,
32
+ children
33
+ }) {
34
+ const hostRef = useRef(null);
35
+ useEffect(() => {
36
+ const host = hostRef.current;
37
+ if (!host) return;
38
+ const instance = new Translate$1(host, { category, custom_id, label });
39
+ return () => instance.destroy();
40
+ }, [category, custom_id, label]);
41
+ return createElement(tag, { ref: hostRef, className }, children);
42
+ }
43
+
44
+ // src/index.ts
45
+ var LangsysAppReact = class {
46
+ /** Initialize Langsys. Pass a `Signal<string>` (from `createLocaleStore`) as `UserLocaleStore`. */
47
+ init(config) {
48
+ return LangsysApp$1.init(config);
49
+ }
50
+ get Translations() {
51
+ return LangsysApp$1.Translations;
52
+ }
53
+ get translationsLoadingPromise() {
54
+ return LangsysApp$1.translationsLoadingPromise;
55
+ }
56
+ /** Current translation function. Reads fresh state on every call (not reactive on its own — use `useT()` in components). */
57
+ get t() {
58
+ return LangsysApp$1.t;
59
+ }
60
+ get debug() {
61
+ return LangsysApp$1.debug;
62
+ }
63
+ refresh() {
64
+ return LangsysApp$1.refresh();
65
+ }
66
+ getCountries(inLocale) {
67
+ return LangsysApp$1.getCountries(inLocale);
68
+ }
69
+ getCountryName(forCountryCode, inLocale) {
70
+ return LangsysApp$1.getCountryName(forCountryCode, inLocale);
71
+ }
72
+ getCurrencies(inLocale) {
73
+ return LangsysApp$1.getCurrencies(inLocale);
74
+ }
75
+ getCurrencyName(forCurrencyCode, inLocale) {
76
+ return LangsysApp$1.getCurrencyName(forCurrencyCode, inLocale);
77
+ }
78
+ getDialCodes(inLocale) {
79
+ return LangsysApp$1.getDialCodes(inLocale);
80
+ }
81
+ getLocales(inLocale) {
82
+ return LangsysApp$1.getLocales(inLocale);
83
+ }
84
+ getLocalesFlat(inLocale) {
85
+ return LangsysApp$1.getLocalesFlat(inLocale);
86
+ }
87
+ getLocalesData(inLocale, forceRefresh) {
88
+ return LangsysApp$1.getLocalesData(inLocale, forceRefresh);
89
+ }
90
+ getLocalesFormat(format = "", inLocale) {
91
+ return LangsysApp$1.getLocalesFormat(format, inLocale);
92
+ }
93
+ getLocaleName(forLocale, shortName, inLocale) {
94
+ return LangsysApp$1.getLocaleName(forLocale, shortName, inLocale);
95
+ }
96
+ getLocaleNameWithLookup(forLocale, shortName, inLocale) {
97
+ return LangsysApp$1.getLocaleNameWithLookup(forLocale, shortName, inLocale);
98
+ }
99
+ /** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */
100
+ getLanguageName(forLocale, shortName, inLocale) {
101
+ return LangsysApp$1.getLanguageName(forLocale, shortName, inLocale);
102
+ }
103
+ detectPreferredLocale(acceptLanguageHeader, supportedLocales) {
104
+ return LangsysApp$1.detectPreferredLocale(acceptLanguageHeader, supportedLocales);
105
+ }
106
+ };
107
+ var LangsysApp = new LangsysAppReact();
108
+
109
+ export { LangsysApp, Translate, createLocaleStore, useCurrentLocale, useLocaleStore, useSignal, useT, useTranslations };
110
+ //# sourceMappingURL=index.mjs.map
111
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters.ts","../src/hooks.ts","../src/components/Translate.tsx","../src/index.ts"],"names":["VanillaTranslate","_LangsysApp"],"mappings":";;;;;AAwBO,SAAS,UAAa,MAAA,EAAsB;AAC/C,EAAA,OAAO,qBAAqB,MAAA,CAAO,SAAA,EAAW,MAAA,CAAO,GAAA,EAAK,OAAO,GAAG,CAAA;AACxE;AAWO,SAAS,iBAAA,CAAkB,UAAU,OAAA,EAAyB;AACjE,EAAA,OAAO,aAAqB,OAAO,CAAA;AACvC;ACtBO,SAAS,IAAA,GAAkB;AAC9B,EAAA,OAAO,UAAU,OAAO,CAAA;AAC5B;AAOO,SAAS,gBAAA,GAA2B;AACvC,EAAA,OAAO,UAAU,qBAAqB,CAAA;AAC1C;AAMO,SAAS,eAAA,GAA+B;AAC3C,EAAA,OAAO,UAAU,aAAa,CAAA;AAClC;AAiBO,SAAS,cAAA,CAAe,UAAU,OAAA,EAA6D;AAClG,EAAA,MAAM,CAAC,KAAK,CAAA,GAAI,SAAS,MAAM,iBAAA,CAAkB,OAAO,CAAC,CAAA;AACzD,EAAA,MAAM,MAAA,GAAS,UAAU,KAAK,CAAA;AAC9B,EAAA,OAAO,CAAC,MAAA,EAAQ,KAAA,CAAM,GAAA,EAAK,KAAK,CAAA;AACpC;ACnBO,SAAS,SAAA,CAAU;AAAA,EACtB,QAAA,GAAW,EAAA;AAAA,EACX,SAAA,GAAY,EAAA;AAAA,EACZ,KAAA,GAAQ,EAAA;AAAA,EACR,GAAA,GAAM,WAAA;AAAA,EACN,SAAA;AAAA,EACA;AACJ,CAAA,EAAmB;AACf,EAAA,MAAM,OAAA,GAAU,OAA2B,IAAI,CAAA;AAE/C,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,QAAA,GAAW,IAAIA,WAAA,CAAiB,IAAA,EAAM,EAAE,QAAA,EAAU,SAAA,EAAW,OAAO,CAAA;AAC1E,IAAA,OAAO,MAAM,SAAS,OAAA,EAAQ;AAAA,EAClC,CAAA,EAAG,CAAC,QAAA,EAAU,SAAA,EAAW,KAAK,CAAC,CAAA;AAE/B,EAAA,OAAO,cAAc,GAAA,EAAK,EAAE,KAAK,OAAA,EAAS,SAAA,IAAa,QAAQ,CAAA;AACnE;;;AC4CA,IAAM,kBAAN,MAAsB;AAAA;AAAA,EAEX,KAAK,MAAA,EAAuD;AAC/D,IAAA,OAAOC,YAAA,CAAY,KAAK,MAAM,CAAA;AAAA,EAClC;AAAA,EAEA,IAAW,YAAA,GAAe;AACtB,IAAA,OAAOA,YAAA,CAAY,YAAA;AAAA,EACvB;AAAA,EAEA,IAAW,0BAAA,GAA6B;AACpC,IAAA,OAAOA,YAAA,CAAY,0BAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAW,CAAA,GAAe;AACtB,IAAA,OAAOA,YAAA,CAAY,CAAA;AAAA,EACvB;AAAA,EAEA,IAAW,KAAA,GAAQ;AACf,IAAA,OAAOA,YAAA,CAAY,KAAA;AAAA,EACvB;AAAA,EAEO,OAAA,GAAU;AACb,IAAA,OAAOA,aAAY,OAAA,EAAQ;AAAA,EAC/B;AAAA,EAEO,aAAa,QAAA,EAAmB;AACnC,IAAA,OAAOA,YAAA,CAAY,aAAa,QAAQ,CAAA;AAAA,EAC5C;AAAA,EACO,cAAA,CAAe,gBAAwB,QAAA,EAAmB;AAC7D,IAAA,OAAOA,YAAA,CAAY,cAAA,CAAe,cAAA,EAAgB,QAAQ,CAAA;AAAA,EAC9D;AAAA,EACO,cAAc,QAAA,EAAmB;AACpC,IAAA,OAAOA,YAAA,CAAY,cAAc,QAAQ,CAAA;AAAA,EAC7C;AAAA,EACO,eAAA,CAAgB,iBAAyB,QAAA,EAAmB;AAC/D,IAAA,OAAOA,YAAA,CAAY,eAAA,CAAgB,eAAA,EAAiB,QAAQ,CAAA;AAAA,EAChE;AAAA,EACO,aAAa,QAAA,EAAmB;AACnC,IAAA,OAAOA,YAAA,CAAY,aAAa,QAAQ,CAAA;AAAA,EAC5C;AAAA,EAEO,WAAW,QAAA,EAAmB;AACjC,IAAA,OAAOA,YAAA,CAAY,WAAW,QAAQ,CAAA;AAAA,EAC1C;AAAA,EACO,eAAe,QAAA,EAAmB;AACrC,IAAA,OAAOA,YAAA,CAAY,eAAe,QAAQ,CAAA;AAAA,EAC9C;AAAA,EACO,cAAA,CAAe,UAAmB,YAAA,EAAwB;AAC7D,IAAA,OAAOA,YAAA,CAAY,cAAA,CAAe,QAAA,EAAU,YAAY,CAAA;AAAA,EAC5D;AAAA,EACO,gBAAA,CAAiB,MAAA,GAA+B,EAAA,EAAI,QAAA,EAAmB;AAC1E,IAAA,OAAOA,YAAA,CAAY,gBAAA,CAAiB,MAAA,EAAQ,QAAQ,CAAA;AAAA,EACxD;AAAA,EACO,aAAA,CAAc,SAAA,EAAmB,SAAA,EAAqB,QAAA,EAAmB;AAC5E,IAAA,OAAOA,YAAA,CAAY,aAAA,CAAc,SAAA,EAAW,SAAA,EAAW,QAAQ,CAAA;AAAA,EACnE;AAAA,EACO,uBAAA,CAAwB,SAAA,EAAmB,SAAA,EAAqB,QAAA,EAAmB;AACtF,IAAA,OAAOA,YAAA,CAAY,uBAAA,CAAwB,SAAA,EAAW,SAAA,EAAW,QAAQ,CAAA;AAAA,EAC7E;AAAA;AAAA,EAGO,eAAA,CAAgB,SAAA,EAAmB,SAAA,EAAqB,QAAA,EAAmB;AAC9E,IAAA,OAAOA,YAAA,CAAY,eAAA,CAAgB,SAAA,EAAW,SAAA,EAAW,QAAQ,CAAA;AAAA,EACrE;AAAA,EAEO,qBAAA,CAAsB,sBAAsC,gBAAA,EAA6B;AAC5F,IAAA,OAAOA,YAAA,CAAY,qBAAA,CAAsB,oBAAA,EAAsB,gBAAgB,CAAA;AAAA,EACnF;AACJ,CAAA;AAEO,IAAM,UAAA,GAAa,IAAI,eAAA","file":"index.mjs","sourcesContent":["import { useSyncExternalStore } from 'react';\nimport { createSignal, type Signal } from 'langsys-js-typescript';\n\n/**\n * Subscribe a React component to a base-SDK `Signal<T>` and return its current\n * value, re-rendering whenever the signal changes.\n *\n * This is the React mirror of Svelte's `$store` auto-subscription. Where the\n * Svelte wrapper adapts a native store *into* the SDK (`writable` → `Signal`),\n * the React wrapper adapts the SDK's signals *out* to the render cycle. It is\n * built on `useSyncExternalStore`, so it is concurrent-safe and SSR-safe — the\n * server snapshot reads the same `.get()`, which the base SDK seeds from\n * `initialTranslations` during SSR.\n *\n * The base SDK's signals are stable between changes (every `set` replaces the\n * value, so `.get()` returns a fresh reference only after a real change). That\n * is exactly the identity contract `useSyncExternalStore` requires — no tearing,\n * no render loops.\n *\n * `signal` must be stable across renders: a module-level singleton (the SDK's\n * `t` / `currentlyLoadedLocale` / `sTranslations`) or one created once with\n * `useState(() => createLocaleStore(...))`. Passing a freshly-created signal on\n * every render would resubscribe on every render.\n */\nexport function useSignal<T>(signal: Signal<T>): T {\n return useSyncExternalStore(signal.subscribe, signal.get, signal.get);\n}\n\n/**\n * Create a reactive `Signal<string>` to hold the user's selected locale — the\n * React analog of Svelte's `writable('en-us')`.\n *\n * Pass the result as `UserLocaleStore` to `LangsysApp.init`, read it reactively\n * with `useSignal(store)` (or use the all-in-one `useLocaleStore` hook), and\n * switch locale with `store.set('fr-fr')`. The base SDK only ever reads and\n * subscribes to it — it never writes.\n */\nexport function createLocaleStore(initial = 'en-us'): Signal<string> {\n return createSignal<string>(initial);\n}\n","import { useState } from 'react';\nimport { currentlyLoadedLocale, sTranslations, tSignal } from 'langsys-js-typescript';\nimport type { Signal, TFunction, iCategories } from 'langsys-js-typescript';\nimport { createLocaleStore, useSignal } from './adapters.js';\n\n/**\n * The current translation function, re-rendering the calling component whenever\n * translations or the loaded locale change. This is the React analog of Svelte's\n * `$t` store read.\n *\n * const t = useT();\n * return <h1>{t('Welcome to my app', 'UI')}</h1>;\n *\n * The phrase is both the lookup key and the base-language default. Signature:\n * `t(phrase, category?, params?)`. Placeholder names in the phrase are\n * type-checked against the params object at the call site.\n */\nexport function useT(): TFunction {\n return useSignal(tSignal);\n}\n\n/**\n * The locale whose translations are currently loaded. This lags the\n * user-selected locale (`UserLocaleStore`) until the fetch for the new locale\n * settles, which makes it the right value to gate \"translations are ready\" UI on.\n */\nexport function useCurrentLocale(): string {\n return useSignal(currentlyLoadedLocale);\n}\n\n/**\n * The raw translation catalog. Rarely needed in app code — prefer `useT()`.\n * Exposed for advanced cases (inspecting which categories/phrases are loaded).\n */\nexport function useTranslations(): iCategories {\n return useSignal(sTranslations);\n}\n\n/**\n * All-in-one convenience for the user-locale store. Creates one stable\n * `Signal<string>` (the React analog of Svelte's `writable`), subscribes the\n * component to it, and returns `[locale, setLocale, store]`.\n *\n * const [locale, setLocale, store] = useLocaleStore('en-us');\n * useEffect(() => {\n * LangsysApp.init({ projectid, key, UserLocaleStore: store });\n * }, [store]);\n *\n * <select value={locale} onChange={(e) => setLocale(e.target.value)}>...\n *\n * The store is created lazily once and never re-created across renders, so it is\n * safe to pass to `LangsysApp.init` and to depend on in effects.\n */\nexport function useLocaleStore(initial = 'en-us'): [string, (locale: string) => void, Signal<string>] {\n const [store] = useState(() => createLocaleStore(initial));\n const locale = useSignal(store);\n return [locale, store.set, store];\n}\n","import { createElement, useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { Translate as VanillaTranslate } from 'langsys-js-typescript';\n\n/**\n * Props for the React `Translate` component. Mirrors the Svelte component's\n * props 1:1, with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface TranslateProps {\n /** Optional category under which tokens are registered. Helps translators disambiguate. */\n category?: string;\n /** Optional stable id for the content block. If omitted, the SDK hashes category + tokens. */\n custom_id?: string;\n /** Optional human-readable label shown in the Translation Manager. */\n label?: string;\n /** Host element tag. Defaults to a `<translate>` custom element. */\n tag?: string;\n /** Class applied to the host element. */\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * React wrapper around the vanilla `Translate` DOM class from\n * `langsys-js-typescript`. It renders a host element, then on mount lets the\n * vanilla class walk and tokenize the rendered children (text nodes plus\n * translatable attributes), register the content block, and re-translate on\n * locale change. On unmount it tears the instance down.\n *\n * This is the React analog of the Svelte `<Translate>` component — pure\n * mount/destroy glue. The DOM walking, content-block registration, attribute\n * harvesting, and re-translation lifecycle all live in the base SDK.\n *\n * Like the Svelte component, this lets the SDK mutate the rendered DOM in place,\n * so keep the children static: prose, marketing copy, CMS-rendered HTML — the\n * content-block use case. For dynamic per-string values that React owns and\n * re-renders, use `useT()` instead.\n */\nexport function Translate({\n category = '',\n custom_id = '',\n label = '',\n tag = 'translate',\n className,\n children,\n}: TranslateProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaTranslate(host, { category, custom_id, label });\n return () => instance.destroy();\n }, [category, custom_id, label]);\n\n return createElement(tag, { ref: hostRef, className }, children);\n}\n\nexport default Translate;\n","/**\n * langsys-js-react — idiomatic React binding over `langsys-js-typescript`.\n *\n * Public API:\n * - `LangsysApp` — `init` accepts a `Signal<string>` (make one with\n * `createLocaleStore`) for the user locale; every other method delegates.\n * - Hooks — `useT`, `useCurrentLocale`, `useTranslations`, `useLocaleStore`,\n * and the low-level `useSignal`. These are the reactive layer; in components\n * prefer them over the raw signals.\n * - `createLocaleStore` — make the user-locale store (React analog of Svelte's\n * `writable`).\n * - `Translate` — React component wrapping the vanilla DOM `Translate` class.\n * - Raw signals `t` / `currentlyLoadedLocale` / `sTranslations` — re-exported\n * for advanced/direct subscription outside React's render cycle.\n */\n\nimport {\n LangsysApp as _LangsysApp,\n type ExtractParamKeys,\n type ParamPrimitive,\n type ParamsFor,\n type Signal,\n type TArgs,\n type TFunction,\n type TranslationParams,\n type iCategories,\n type iContentBlock,\n type iCountry,\n type iCountryDialCode,\n type iCountryList,\n type iCurrency,\n type iCurrencyList,\n type iLangsysInitConfig as iVanillaInitConfig,\n type iLangsysResponse,\n type iLanguageName,\n type iLocaleData,\n type iLocaleDefault,\n type iLocaleFlat,\n type iProject,\n type iTranslations,\n} from 'langsys-js-typescript';\n\n// Reactive primitives (raw signals) — re-exported for advanced/direct\n// subscription. `tSignal` is exposed under the friendlier name `t`. In\n// components, prefer the hooks (`useT`, `useCurrentLocale`, …).\nexport { currentlyLoadedLocale, createSignal, sTranslations, tSignal as t } from 'langsys-js-typescript';\n\n// API client (vanilla — no React concerns)\nexport { LangsysAppAPI } from 'langsys-js-typescript';\n\n// Hooks + adapters (the React-idiomatic reactive layer)\nexport { createLocaleStore, useSignal } from './adapters.js';\nexport { useCurrentLocale, useLocaleStore, useT, useTranslations } from './hooks.js';\n\n// Component\nexport { Translate, type TranslateProps } from './components/Translate.js';\n\n// Type re-exports — these are framework-agnostic, so consumers can rely on them\n// directly without reaching into `langsys-js-typescript`.\nexport type {\n ExtractParamKeys,\n ParamPrimitive,\n ParamsFor,\n Signal,\n TArgs,\n TFunction,\n TranslationParams,\n iCategories,\n iContentBlock,\n iCountry,\n iCountryDialCode,\n iCountryList,\n iCurrency,\n iCurrencyList,\n iLangsysResponse,\n iLanguageName,\n iLocaleData,\n iLocaleDefault,\n iLocaleFlat,\n iProject,\n iTranslations,\n};\n\n/**\n * React-flavored init config. Identical to the base SDK's config except\n * `UserLocaleStore` is typed as a `Signal<string>` — create one with\n * `createLocaleStore()` (or get one from the `useLocaleStore` hook). The base\n * SDK only reads and subscribes to it.\n */\nexport interface iLangsysInitConfig extends Omit<iVanillaInitConfig, 'UserLocaleStore'> {\n UserLocaleStore: Signal<string>;\n}\n\n/**\n * React SDK entry point. Delegates everything to the underlying\n * `langsys-js-typescript` singleton. Because React's locale store is already a\n * `Signal` (unlike Svelte's `Writable`, which needs adapting), `init` is a\n * straight passthrough — the React-native concerns live in the hooks and the\n * `<Translate>` component, not here.\n */\nclass LangsysAppReact {\n /** Initialize Langsys. Pass a `Signal<string>` (from `createLocaleStore`) as `UserLocaleStore`. */\n public init(config: iLangsysInitConfig): Promise<iLangsysResponse> {\n return _LangsysApp.init(config);\n }\n\n public get Translations() {\n return _LangsysApp.Translations;\n }\n\n public get translationsLoadingPromise() {\n return _LangsysApp.translationsLoadingPromise;\n }\n\n /** Current translation function. Reads fresh state on every call (not reactive on its own — use `useT()` in components). */\n public get t(): TFunction {\n return _LangsysApp.t;\n }\n\n public get debug() {\n return _LangsysApp.debug;\n }\n\n public refresh() {\n return _LangsysApp.refresh();\n }\n\n public getCountries(inLocale?: string) {\n return _LangsysApp.getCountries(inLocale);\n }\n public getCountryName(forCountryCode: string, inLocale?: string) {\n return _LangsysApp.getCountryName(forCountryCode, inLocale);\n }\n public getCurrencies(inLocale?: string) {\n return _LangsysApp.getCurrencies(inLocale);\n }\n public getCurrencyName(forCurrencyCode: string, inLocale?: string) {\n return _LangsysApp.getCurrencyName(forCurrencyCode, inLocale);\n }\n public getDialCodes(inLocale?: string) {\n return _LangsysApp.getDialCodes(inLocale);\n }\n\n public getLocales(inLocale?: string) {\n return _LangsysApp.getLocales(inLocale);\n }\n public getLocalesFlat(inLocale?: string) {\n return _LangsysApp.getLocalesFlat(inLocale);\n }\n public getLocalesData(inLocale?: string, forceRefresh?: boolean) {\n return _LangsysApp.getLocalesData(inLocale, forceRefresh);\n }\n public getLocalesFormat(format: '' | 'flat' | 'data' = '', inLocale?: string) {\n return _LangsysApp.getLocalesFormat(format, inLocale);\n }\n public getLocaleName(forLocale: string, shortName?: boolean, inLocale?: string) {\n return _LangsysApp.getLocaleName(forLocale, shortName, inLocale);\n }\n public getLocaleNameWithLookup(forLocale: string, shortName?: boolean, inLocale?: string) {\n return _LangsysApp.getLocaleNameWithLookup(forLocale, shortName, inLocale);\n }\n\n /** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */\n public getLanguageName(forLocale: string, shortName?: boolean, inLocale?: string) {\n return _LangsysApp.getLanguageName(forLocale, shortName, inLocale);\n }\n\n public detectPreferredLocale(acceptLanguageHeader?: string | null, supportedLocales?: string[]) {\n return _LangsysApp.detectPreferredLocale(acceptLanguageHeader, supportedLocales);\n }\n}\n\nexport const LangsysApp = new LangsysAppReact();\n"]}
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "langsys-js-react",
3
+ "version": "0.1.0",
4
+ "description": "React binding for the Langsys translation SDK — realtime continuous translations with automatic token discovery, delivered as hooks and a <Translate> component.",
5
+ "author": "Langsys",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/langsys/langsys-js-react",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+ssh://git@github.com/langsys/langsys-js-react.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/langsys/langsys-js-react/issues"
14
+ },
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "main": "dist/index.js",
19
+ "module": "dist/index.mjs",
20
+ "types": "dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.mjs",
25
+ "require": "./dist/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "README-SSR.md",
32
+ "CHANGELOG.md",
33
+ "LICENSE"
34
+ ],
35
+ "sideEffects": false,
36
+ "scripts": {
37
+ "dev": "vite",
38
+ "build": "tsup",
39
+ "build:watch": "tsup --watch",
40
+ "typecheck": "tsc --noEmit",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "lint": "prettier --check . && eslint .",
44
+ "format": "prettier --write .",
45
+ "prepublishOnly": "npm run build",
46
+ "release": "./_dev_/publish.sh"
47
+ },
48
+ "keywords": [
49
+ "i18n",
50
+ "l10n",
51
+ "react",
52
+ "react-hooks",
53
+ "language",
54
+ "localization",
55
+ "internationalization",
56
+ "translation",
57
+ "langsys",
58
+ "sdk"
59
+ ],
60
+ "peerDependencies": {
61
+ "react": "^18.0.0 || ^19.0.0"
62
+ },
63
+ "dependencies": {
64
+ "langsys-js-typescript": "^0.2.0"
65
+ },
66
+ "devDependencies": {
67
+ "@eslint/js": "^9.0.0",
68
+ "@types/react": "^19.0.0",
69
+ "@types/react-dom": "^19.0.0",
70
+ "@vitejs/plugin-react": "^5.0.0",
71
+ "eslint": "^9.0.0",
72
+ "eslint-config-prettier": "^10.0.0",
73
+ "eslint-plugin-react-hooks": "^5.0.0",
74
+ "prettier": "^3.3.0",
75
+ "react": "^19.0.0",
76
+ "react-dom": "^19.0.0",
77
+ "tsup": "^8.4.0",
78
+ "typescript": "^5.8.3",
79
+ "typescript-eslint": "^8.0.0",
80
+ "vite": "^6.0.0",
81
+ "vitest": "^4.1.7"
82
+ }
83
+ }