langsys-js-react 0.6.0 → 0.6.2
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 +17 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 0.6.2 - 2026-08-16
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- **Base SDK bumped to `langsys-js-typescript@^0.6.2`** — cross-SDK exclusion fixes, inherited with no wrapper API change. `data-notrans` (langsys-php's author-facing alias for `translate="no"`) is now honored, so on SSR handoff content an author marked do-not-translate isn't harvested by whichever SDK walks the DOM first; `translate="no"` is matched case-insensitively; and phrase-marker values are trimmed to match a normalization on the PHP side.
|
|
6
|
+
|
|
7
|
+
### Fixed (documentation)
|
|
8
|
+
|
|
9
|
+
- **`<DontTranslate>` docs corrected.** The 0.2.0 entry claimed `translate="no"` and `data-ls-dont-translate` were "both already honored by the base SDK" — only the first is. Checked against every published base version from 0.1.0 through 0.6.2: `data-ls-dont-translate` appears nowhere in the SDK runtime and never has. **No behavior change and nothing to do** — the component always worked, because exclusion rests on `translate="no"`, which is honored (now case-insensitively). `data-ls-dont-translate` is a wrapper-level styling/debugging hook; it keeps being emitted so existing CSS selectors don't break, and is now documented as such in the component.
|
|
10
|
+
|
|
11
|
+
## 0.6.1 - 2026-08-15
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **Base SDK bumped to `langsys-js-typescript@^0.6.1`** — completes the `langsys-php` SSR interop from 0.6.0. `data-langsys-phrase="false"` and `="0"` are explicit author opt-outs, but 0.6.0 matched on attribute *presence* alone, so an author who deliberately un-marked a subtree got it skipped by this SDK and un-translated by PHP too — content translated by neither. No wrapper API change; inherited through the base SDK.
|
|
16
|
+
|
|
1
17
|
## 0.6.0 - 2026-08-15
|
|
2
18
|
|
|
3
19
|
### Changed
|
|
@@ -90,6 +106,7 @@
|
|
|
90
106
|
|
|
91
107
|
- **`<Phrase>`** — React component wrapping the base SDK's vanilla `Phrase` rich-text handler. Keeps a markup-bearing run as ONE translatable phrase (so a count variable stays next to the noun it pluralizes), encoding inline markup as neutral tokens and reconstituting the real framework-owned elements at render. Props: `category?`, `params?`, `tag?` (default `span`), `className?`, `children`. Brings the React SDK to parity with `langsys-js-svelte`.
|
|
92
108
|
- **`<DontTranslate>`** — marks a region as never-translated (renders `translate="no"` + `data-ls-dont-translate`, both already honored by the base SDK tokenizer/renderer), preserved verbatim. Props: `tag?` (default `span`), `className?`, `children`.
|
|
109
|
+
> **Correction (2026-08-16):** "both already honored" was wrong — only `translate="no"` is honored by the base SDK, and `data-ls-dont-translate` never was, in any version. The component has always worked, because `translate="no"` carries it. See the 0.6.2 entry.
|
|
93
110
|
|
|
94
111
|
## 0.1.0 - 2026-06-11
|
|
95
112
|
|
package/dist/index.d.mts
CHANGED
|
@@ -193,6 +193,13 @@ interface DontTranslateProps {
|
|
|
193
193
|
* tokenizer and the renderer in the base SDK already honor — so its content is
|
|
194
194
|
* never tokenized, registered, or replaced. Pure presentational glue; no
|
|
195
195
|
* vanilla handler needed.
|
|
196
|
+
*
|
|
197
|
+
* `data-ls-dont-translate` is a **wrapper-level hook only** — a styling and
|
|
198
|
+
* debugging handle. The base SDK has never matched on it (verified against
|
|
199
|
+
* every published version from 0.1.0 to 0.6.2); exclusion rests entirely on
|
|
200
|
+
* `translate="no"`. Don't remove it — consumers may select on it — but don't
|
|
201
|
+
* describe it as SDK-honored either. The SDK's exclusion set is
|
|
202
|
+
* `translate="no"` (case-insensitive) and PHP's `data-notrans` alias.
|
|
196
203
|
*/
|
|
197
204
|
declare function DontTranslate({ tag, className, children }: DontTranslateProps): react.ReactElement<{
|
|
198
205
|
translate: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -193,6 +193,13 @@ interface DontTranslateProps {
|
|
|
193
193
|
* tokenizer and the renderer in the base SDK already honor — so its content is
|
|
194
194
|
* never tokenized, registered, or replaced. Pure presentational glue; no
|
|
195
195
|
* vanilla handler needed.
|
|
196
|
+
*
|
|
197
|
+
* `data-ls-dont-translate` is a **wrapper-level hook only** — a styling and
|
|
198
|
+
* debugging handle. The base SDK has never matched on it (verified against
|
|
199
|
+
* every published version from 0.1.0 to 0.6.2); exclusion rests entirely on
|
|
200
|
+
* `translate="no"`. Don't remove it — consumers may select on it — but don't
|
|
201
|
+
* describe it as SDK-honored either. The SDK's exclusion set is
|
|
202
|
+
* `translate="no"` (case-insensitive) and PHP's `data-notrans` alias.
|
|
196
203
|
*/
|
|
197
204
|
declare function DontTranslate({ tag, className, children }: DontTranslateProps): react.ReactElement<{
|
|
198
205
|
translate: string;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters.ts","../src/hooks.ts","../src/components/Translate.tsx","../src/components/Phrase.tsx","../src/components/DontTranslate.tsx","../src/index.ts"],"names":["useSyncExternalStore","createSignal","tSignal","currentlyLoadedLocale","sTranslations","useState","useRef","useEffect","VanillaTranslate","createElement","VanillaPhrase","PHRASE_MARKER_ATTR","_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 *\n * Locale identifiers are canonicalized to BCP 47 by the base SDK (v0.3.0+), so\n * `'en-us'` still works on input — but `currentlyLoadedLocale` always emits the\n * canonical form (`'en-US'`), so prefer canonical casing to keep comparisons\n * against it straightforward.\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, type ParamPrimitive } 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 /**\n * Interpolation params. Write placeholders as `%key%` in the markup (a bare\n * `{key}` in JSX is a JS expression; the SDK normalizes `%key%` to canonical\n * `{key}` at capture). Applied to content-block text nodes, translatable\n * attributes, `<option>` text, and single-token content — including\n * untranslated fallbacks. Number/Date values get CLDR locale formatting.\n */\n params?: Record<string, ParamPrimitive>;\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 params,\n tag = 'translate',\n className,\n children,\n}: TranslateProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaTranslate>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaTranslate(host, { category, custom_id, label, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the identity props change; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category, custom_id, label]);\n\n // Re-render on changed params after mount, mirroring <Phrase>.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className }, children);\n}\n\nexport default Translate;\n","import { createElement, useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { Phrase as VanillaPhrase, PHRASE_MARKER_ATTR, type ParamPrimitive } from 'langsys-js-typescript';\n\n/**\n * Props for the React `Phrase` component. Mirrors the Svelte component's props,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface PhraseProps {\n /** Category the phrase registers under (disambiguation for translators). */\n category?: string;\n /** Interpolation params. Write placeholders as `%n%` / `%name%` in the markup (a bare `{n}` in JSX is an expression). */\n params?: Record<string, ParamPrimitive>;\n /** Host element tag. Defaults to `<span>`. */\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 `Phrase` rich-text handler.\n *\n * Use it to keep a markup-bearing run as ONE translatable phrase — e.g. so a\n * count variable stays next to the noun it pluralizes:\n *\n * <Phrase category=\"ProductCard\" params={{ n: reviewCount }}>\n * Based on %n% <strong>reviews</strong>\n * </Phrase>\n *\n * Write placeholders as `%n%` (not bare `{n}`): in JSX a literal `{n}` is a JS\n * expression React evaluates before the SDK sees the text. `%n%` passes through\n * as literal text and the SDK normalizes it to canonical `{n}` at capture.\n *\n * The inline markup never reaches the translator — it's replaced with neutral\n * tokens and the real elements are reconstituted at render (see richtext.ts in\n * the base SDK). The host carries `data-ls-phrase` so a wrapping `<Translate>`\n * skips it and lets this handler own it.\n *\n * Keep children static (literal markup): the handler takes over the rendered\n * subtree. For values React owns and re-renders, pass them through `params`.\n */\nexport function Phrase({ category = '', params = {}, tag = 'span', className, children }: PhraseProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaPhrase>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaPhrase(host, { category, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the category changes; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category]);\n\n // Re-render on a changed count/param after mount.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className, [PHRASE_MARKER_ATTR]: '' }, children);\n}\n\nexport default Phrase;\n","import { createElement } from 'react';\nimport type { ReactNode } from 'react';\n\n/**\n * Props for the React `DontTranslate` component. Mirrors the Svelte component,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface DontTranslateProps {\n /** Host element tag. Defaults to `<span>`. */\n tag?: string;\n /** Class applied to the host element. */\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * Marks a region as never-translated, preserved verbatim:\n *\n * Built with <DontTranslate>Kangen®</DontTranslate> on{' '}\n * <DontTranslate>langsys.dev</DontTranslate>\n *\n * The host carries the standard `translate=\"no\"` attribute, which both the\n * tokenizer and the renderer in the base SDK already honor — so its content is\n * never tokenized, registered, or replaced. Pure presentational glue; no\n * vanilla handler needed.\n */\nexport function DontTranslate({ tag = 'span', className, children }: DontTranslateProps) {\n return createElement(\n tag,\n { translate: 'no', 'data-ls-dont-translate': '', className },\n children,\n );\n}\n\nexport default DontTranslate;\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// Locale canonicalization (BCP 47) — the SDK canonicalizes all locale input\n// (v0.3.0+); re-exported so consumers can normalize their own values the same\n// way before comparing against `useCurrentLocale()` / `detectPreferredLocale()`.\nexport { canonicalizeLocale } 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// Components\nexport { Translate, type TranslateProps } from './components/Translate.js';\nexport { Phrase, type PhraseProps } from './components/Phrase.js';\nexport { DontTranslate, type DontTranslateProps } from './components/DontTranslate.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"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters.ts","../src/hooks.ts","../src/components/Translate.tsx","../src/components/Phrase.tsx","../src/components/DontTranslate.tsx","../src/index.ts"],"names":["useSyncExternalStore","createSignal","tSignal","currentlyLoadedLocale","sTranslations","useState","useRef","useEffect","VanillaTranslate","createElement","VanillaPhrase","PHRASE_MARKER_ATTR","_LangsysApp"],"mappings":";;;;;;AAwBO,SAAS,UAAa,MAAA,EAAsB;AAC/C,EAAA,OAAOA,2BAAqB,MAAA,CAAO,SAAA,EAAW,MAAA,CAAO,GAAA,EAAK,OAAO,GAAG,CAAA;AACxE;AAgBO,SAAS,iBAAA,CAAkB,UAAU,OAAA,EAAyB;AACjE,EAAA,OAAOC,iCAAqB,OAAO,CAAA;AACvC;AC3BO,SAAS,IAAA,GAAkB;AAC9B,EAAA,OAAO,UAAUC,2BAAO,CAAA;AAC5B;AAOO,SAAS,gBAAA,GAA2B;AACvC,EAAA,OAAO,UAAUC,yCAAqB,CAAA;AAC1C;AAMO,SAAS,eAAA,GAA+B;AAC3C,EAAA,OAAO,UAAUC,iCAAa,CAAA;AAClC;AAiBO,SAAS,cAAA,CAAe,UAAU,OAAA,EAA6D;AAClG,EAAA,MAAM,CAAC,KAAK,CAAA,GAAIC,eAAS,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;ACXO,SAAS,SAAA,CAAU;AAAA,EACtB,QAAA,GAAW,EAAA;AAAA,EACX,SAAA,GAAY,EAAA;AAAA,EACZ,KAAA,GAAQ,EAAA;AAAA,EACR,MAAA;AAAA,EACA,GAAA,GAAM,WAAA;AAAA,EACN,SAAA;AAAA,EACA;AACJ,CAAA,EAAmB;AACf,EAAA,MAAM,OAAA,GAAUC,aAA2B,IAAI,CAAA;AAC/C,EAAA,MAAM,WAAA,GAAcA,aAAyB,MAAS,CAAA;AAEtD,EAAAC,eAAA,CAAU,MAAM;AACZ,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,QAAA,GAAW,IAAIC,6BAAA,CAAiB,IAAA,EAAM,EAAE,QAAA,EAAU,SAAA,EAAW,KAAA,EAAO,MAAA,EAAQ,CAAA;AAClF,IAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AACtB,IAAA,OAAO,MAAM;AACT,MAAA,QAAA,CAAS,OAAA,EAAQ;AACjB,MAAA,WAAA,CAAY,OAAA,GAAU,MAAA;AAAA,IAC1B,CAAA;AAAA,EAGJ,CAAA,EAAG,CAAC,QAAA,EAAU,SAAA,EAAW,KAAK,CAAC,CAAA;AAG/B,EAAAD,eAAA,CAAU,MAAM;AACZ,IAAA,WAAA,CAAY,OAAA,EAAS,UAAU,MAAM,CAAA;AAAA,EACzC,CAAC,CAAA;AAED,EAAA,OAAOE,oBAAc,GAAA,EAAK,EAAE,KAAK,OAAA,EAAS,SAAA,IAAa,QAAQ,CAAA;AACnE;ACnCO,SAAS,MAAA,CAAO,EAAE,QAAA,GAAW,EAAA,EAAI,MAAA,GAAS,EAAC,EAAG,GAAA,GAAM,MAAA,EAAQ,SAAA,EAAW,QAAA,EAAS,EAAgB;AACnG,EAAA,MAAM,OAAA,GAAUH,aAA2B,IAAI,CAAA;AAC/C,EAAA,MAAM,WAAA,GAAcA,aAAsB,MAAS,CAAA;AAEnD,EAAAC,gBAAU,MAAM;AACZ,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,WAAW,IAAIG,0BAAA,CAAc,MAAM,EAAE,QAAA,EAAU,QAAQ,CAAA;AAC7D,IAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AACtB,IAAA,OAAO,MAAM;AACT,MAAA,QAAA,CAAS,OAAA,EAAQ;AACjB,MAAA,WAAA,CAAY,OAAA,GAAU,MAAA;AAAA,IAC1B,CAAA;AAAA,EAGJ,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAAH,gBAAU,MAAM;AACZ,IAAA,WAAA,CAAY,OAAA,EAAS,UAAU,MAAM,CAAA;AAAA,EACzC,CAAC,CAAA;AAED,EAAA,OAAOE,mBAAAA,CAAc,GAAA,EAAK,EAAE,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,CAACE,sCAAkB,GAAG,EAAA,EAAG,EAAG,QAAQ,CAAA;AAC7F;AChCO,SAAS,cAAc,EAAE,GAAA,GAAM,MAAA,EAAQ,SAAA,EAAW,UAAS,EAAuB;AACrF,EAAA,OAAOF,mBAAAA;AAAA,IACH,GAAA;AAAA,IACA,EAAE,SAAA,EAAW,IAAA,EAAM,wBAAA,EAA0B,IAAI,SAAA,EAAU;AAAA,IAC3D;AAAA,GACJ;AACJ;;;ACoEA,IAAM,kBAAN,MAAsB;AAAA;AAAA,EAEX,KAAK,MAAA,EAAuD;AAC/D,IAAA,OAAOG,8BAAA,CAAY,KAAK,MAAM,CAAA;AAAA,EAClC;AAAA,EAEA,IAAW,YAAA,GAAe;AACtB,IAAA,OAAOA,8BAAA,CAAY,YAAA;AAAA,EACvB;AAAA,EAEA,IAAW,0BAAA,GAA6B;AACpC,IAAA,OAAOA,8BAAA,CAAY,0BAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAW,CAAA,GAAe;AACtB,IAAA,OAAOA,8BAAA,CAAY,CAAA;AAAA,EACvB;AAAA,EAEA,IAAW,KAAA,GAAQ;AACf,IAAA,OAAOA,8BAAA,CAAY,KAAA;AAAA,EACvB;AAAA,EAEO,OAAA,GAAU;AACb,IAAA,OAAOA,+BAAY,OAAA,EAAQ;AAAA,EAC/B;AAAA,EAEO,aAAa,QAAA,EAAmB;AACnC,IAAA,OAAOA,8BAAA,CAAY,aAAa,QAAQ,CAAA;AAAA,EAC5C;AAAA,EACO,cAAA,CAAe,gBAAwB,QAAA,EAAmB;AAC7D,IAAA,OAAOA,8BAAA,CAAY,cAAA,CAAe,cAAA,EAAgB,QAAQ,CAAA;AAAA,EAC9D;AAAA,EACO,cAAc,QAAA,EAAmB;AACpC,IAAA,OAAOA,8BAAA,CAAY,cAAc,QAAQ,CAAA;AAAA,EAC7C;AAAA,EACO,eAAA,CAAgB,iBAAyB,QAAA,EAAmB;AAC/D,IAAA,OAAOA,8BAAA,CAAY,eAAA,CAAgB,eAAA,EAAiB,QAAQ,CAAA;AAAA,EAChE;AAAA,EACO,aAAa,QAAA,EAAmB;AACnC,IAAA,OAAOA,8BAAA,CAAY,aAAa,QAAQ,CAAA;AAAA,EAC5C;AAAA,EAEO,WAAW,QAAA,EAAmB;AACjC,IAAA,OAAOA,8BAAA,CAAY,WAAW,QAAQ,CAAA;AAAA,EAC1C;AAAA,EACO,eAAe,QAAA,EAAmB;AACrC,IAAA,OAAOA,8BAAA,CAAY,eAAe,QAAQ,CAAA;AAAA,EAC9C;AAAA,EACO,cAAA,CAAe,UAAmB,YAAA,EAAwB;AAC7D,IAAA,OAAOA,8BAAA,CAAY,cAAA,CAAe,QAAA,EAAU,YAAY,CAAA;AAAA,EAC5D;AAAA,EACO,gBAAA,CAAiB,MAAA,GAA+B,EAAA,EAAI,QAAA,EAAmB;AAC1E,IAAA,OAAOA,8BAAA,CAAY,gBAAA,CAAiB,MAAA,EAAQ,QAAQ,CAAA;AAAA,EACxD;AAAA,EACO,aAAA,CAAc,SAAA,EAAmB,SAAA,EAAqB,QAAA,EAAmB;AAC5E,IAAA,OAAOA,8BAAA,CAAY,aAAA,CAAc,SAAA,EAAW,SAAA,EAAW,QAAQ,CAAA;AAAA,EACnE;AAAA,EACO,uBAAA,CAAwB,SAAA,EAAmB,SAAA,EAAqB,QAAA,EAAmB;AACtF,IAAA,OAAOA,8BAAA,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,8BAAA,CAAY,eAAA,CAAgB,SAAA,EAAW,SAAA,EAAW,QAAQ,CAAA;AAAA,EACrE;AAAA,EAEO,qBAAA,CAAsB,sBAAsC,gBAAA,EAA6B;AAC5F,IAAA,OAAOA,8BAAA,CAAY,qBAAA,CAAsB,oBAAA,EAAsB,gBAAgB,CAAA;AAAA,EACnF;AACJ,CAAA;AAEO,IAAM,UAAA,GAAa,IAAI,eAAA","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 *\n * Locale identifiers are canonicalized to BCP 47 by the base SDK (v0.3.0+), so\n * `'en-us'` still works on input — but `currentlyLoadedLocale` always emits the\n * canonical form (`'en-US'`), so prefer canonical casing to keep comparisons\n * against it straightforward.\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, type ParamPrimitive } 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 /**\n * Interpolation params. Write placeholders as `%key%` in the markup (a bare\n * `{key}` in JSX is a JS expression; the SDK normalizes `%key%` to canonical\n * `{key}` at capture). Applied to content-block text nodes, translatable\n * attributes, `<option>` text, and single-token content — including\n * untranslated fallbacks. Number/Date values get CLDR locale formatting.\n */\n params?: Record<string, ParamPrimitive>;\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 params,\n tag = 'translate',\n className,\n children,\n}: TranslateProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaTranslate>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaTranslate(host, { category, custom_id, label, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the identity props change; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category, custom_id, label]);\n\n // Re-render on changed params after mount, mirroring <Phrase>.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className }, children);\n}\n\nexport default Translate;\n","import { createElement, useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { Phrase as VanillaPhrase, PHRASE_MARKER_ATTR, type ParamPrimitive } from 'langsys-js-typescript';\n\n/**\n * Props for the React `Phrase` component. Mirrors the Svelte component's props,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface PhraseProps {\n /** Category the phrase registers under (disambiguation for translators). */\n category?: string;\n /** Interpolation params. Write placeholders as `%n%` / `%name%` in the markup (a bare `{n}` in JSX is an expression). */\n params?: Record<string, ParamPrimitive>;\n /** Host element tag. Defaults to `<span>`. */\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 `Phrase` rich-text handler.\n *\n * Use it to keep a markup-bearing run as ONE translatable phrase — e.g. so a\n * count variable stays next to the noun it pluralizes:\n *\n * <Phrase category=\"ProductCard\" params={{ n: reviewCount }}>\n * Based on %n% <strong>reviews</strong>\n * </Phrase>\n *\n * Write placeholders as `%n%` (not bare `{n}`): in JSX a literal `{n}` is a JS\n * expression React evaluates before the SDK sees the text. `%n%` passes through\n * as literal text and the SDK normalizes it to canonical `{n}` at capture.\n *\n * The inline markup never reaches the translator — it's replaced with neutral\n * tokens and the real elements are reconstituted at render (see richtext.ts in\n * the base SDK). The host carries `data-ls-phrase` so a wrapping `<Translate>`\n * skips it and lets this handler own it.\n *\n * Keep children static (literal markup): the handler takes over the rendered\n * subtree. For values React owns and re-renders, pass them through `params`.\n */\nexport function Phrase({ category = '', params = {}, tag = 'span', className, children }: PhraseProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaPhrase>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaPhrase(host, { category, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the category changes; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category]);\n\n // Re-render on a changed count/param after mount.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className, [PHRASE_MARKER_ATTR]: '' }, children);\n}\n\nexport default Phrase;\n","import { createElement } from 'react';\nimport type { ReactNode } from 'react';\n\n/**\n * Props for the React `DontTranslate` component. Mirrors the Svelte component,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface DontTranslateProps {\n /** Host element tag. Defaults to `<span>`. */\n tag?: string;\n /** Class applied to the host element. */\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * Marks a region as never-translated, preserved verbatim:\n *\n * Built with <DontTranslate>Kangen®</DontTranslate> on{' '}\n * <DontTranslate>langsys.dev</DontTranslate>\n *\n * The host carries the standard `translate=\"no\"` attribute, which both the\n * tokenizer and the renderer in the base SDK already honor — so its content is\n * never tokenized, registered, or replaced. Pure presentational glue; no\n * vanilla handler needed.\n *\n * `data-ls-dont-translate` is a **wrapper-level hook only** — a styling and\n * debugging handle. The base SDK has never matched on it (verified against\n * every published version from 0.1.0 to 0.6.2); exclusion rests entirely on\n * `translate=\"no\"`. Don't remove it — consumers may select on it — but don't\n * describe it as SDK-honored either. The SDK's exclusion set is\n * `translate=\"no\"` (case-insensitive) and PHP's `data-notrans` alias.\n */\nexport function DontTranslate({ tag = 'span', className, children }: DontTranslateProps) {\n return createElement(\n tag,\n { translate: 'no', 'data-ls-dont-translate': '', className },\n children,\n );\n}\n\nexport default DontTranslate;\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// Locale canonicalization (BCP 47) — the SDK canonicalizes all locale input\n// (v0.3.0+); re-exported so consumers can normalize their own values the same\n// way before comparing against `useCurrentLocale()` / `detectPreferredLocale()`.\nexport { canonicalizeLocale } 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// Components\nexport { Translate, type TranslateProps } from './components/Translate.js';\nexport { Phrase, type PhraseProps } from './components/Phrase.js';\nexport { DontTranslate, type DontTranslateProps } from './components/DontTranslate.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.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters.ts","../src/hooks.ts","../src/components/Translate.tsx","../src/components/Phrase.tsx","../src/components/DontTranslate.tsx","../src/index.ts"],"names":["VanillaTranslate","useRef","useEffect","VanillaPhrase","createElement","_LangsysApp"],"mappings":"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","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 *\n * Locale identifiers are canonicalized to BCP 47 by the base SDK (v0.3.0+), so\n * `'en-us'` still works on input — but `currentlyLoadedLocale` always emits the\n * canonical form (`'en-US'`), so prefer canonical casing to keep comparisons\n * against it straightforward.\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, type ParamPrimitive } 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 /**\n * Interpolation params. Write placeholders as `%key%` in the markup (a bare\n * `{key}` in JSX is a JS expression; the SDK normalizes `%key%` to canonical\n * `{key}` at capture). Applied to content-block text nodes, translatable\n * attributes, `<option>` text, and single-token content — including\n * untranslated fallbacks. Number/Date values get CLDR locale formatting.\n */\n params?: Record<string, ParamPrimitive>;\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 params,\n tag = 'translate',\n className,\n children,\n}: TranslateProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaTranslate>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaTranslate(host, { category, custom_id, label, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the identity props change; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category, custom_id, label]);\n\n // Re-render on changed params after mount, mirroring <Phrase>.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className }, children);\n}\n\nexport default Translate;\n","import { createElement, useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { Phrase as VanillaPhrase, PHRASE_MARKER_ATTR, type ParamPrimitive } from 'langsys-js-typescript';\n\n/**\n * Props for the React `Phrase` component. Mirrors the Svelte component's props,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface PhraseProps {\n /** Category the phrase registers under (disambiguation for translators). */\n category?: string;\n /** Interpolation params. Write placeholders as `%n%` / `%name%` in the markup (a bare `{n}` in JSX is an expression). */\n params?: Record<string, ParamPrimitive>;\n /** Host element tag. Defaults to `<span>`. */\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 `Phrase` rich-text handler.\n *\n * Use it to keep a markup-bearing run as ONE translatable phrase — e.g. so a\n * count variable stays next to the noun it pluralizes:\n *\n * <Phrase category=\"ProductCard\" params={{ n: reviewCount }}>\n * Based on %n% <strong>reviews</strong>\n * </Phrase>\n *\n * Write placeholders as `%n%` (not bare `{n}`): in JSX a literal `{n}` is a JS\n * expression React evaluates before the SDK sees the text. `%n%` passes through\n * as literal text and the SDK normalizes it to canonical `{n}` at capture.\n *\n * The inline markup never reaches the translator — it's replaced with neutral\n * tokens and the real elements are reconstituted at render (see richtext.ts in\n * the base SDK). The host carries `data-ls-phrase` so a wrapping `<Translate>`\n * skips it and lets this handler own it.\n *\n * Keep children static (literal markup): the handler takes over the rendered\n * subtree. For values React owns and re-renders, pass them through `params`.\n */\nexport function Phrase({ category = '', params = {}, tag = 'span', className, children }: PhraseProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaPhrase>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaPhrase(host, { category, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the category changes; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category]);\n\n // Re-render on a changed count/param after mount.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className, [PHRASE_MARKER_ATTR]: '' }, children);\n}\n\nexport default Phrase;\n","import { createElement } from 'react';\nimport type { ReactNode } from 'react';\n\n/**\n * Props for the React `DontTranslate` component. Mirrors the Svelte component,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface DontTranslateProps {\n /** Host element tag. Defaults to `<span>`. */\n tag?: string;\n /** Class applied to the host element. */\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * Marks a region as never-translated, preserved verbatim:\n *\n * Built with <DontTranslate>Kangen®</DontTranslate> on{' '}\n * <DontTranslate>langsys.dev</DontTranslate>\n *\n * The host carries the standard `translate=\"no\"` attribute, which both the\n * tokenizer and the renderer in the base SDK already honor — so its content is\n * never tokenized, registered, or replaced. Pure presentational glue; no\n * vanilla handler needed.\n */\nexport function DontTranslate({ tag = 'span', className, children }: DontTranslateProps) {\n return createElement(\n tag,\n { translate: 'no', 'data-ls-dont-translate': '', className },\n children,\n );\n}\n\nexport default DontTranslate;\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// Locale canonicalization (BCP 47) — the SDK canonicalizes all locale input\n// (v0.3.0+); re-exported so consumers can normalize their own values the same\n// way before comparing against `useCurrentLocale()` / `detectPreferredLocale()`.\nexport { canonicalizeLocale } 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// Components\nexport { Translate, type TranslateProps } from './components/Translate.js';\nexport { Phrase, type PhraseProps } from './components/Phrase.js';\nexport { DontTranslate, type DontTranslateProps } from './components/DontTranslate.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"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters.ts","../src/hooks.ts","../src/components/Translate.tsx","../src/components/Phrase.tsx","../src/components/DontTranslate.tsx","../src/index.ts"],"names":["VanillaTranslate","useRef","useEffect","VanillaPhrase","createElement","_LangsysApp"],"mappings":"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","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 *\n * Locale identifiers are canonicalized to BCP 47 by the base SDK (v0.3.0+), so\n * `'en-us'` still works on input — but `currentlyLoadedLocale` always emits the\n * canonical form (`'en-US'`), so prefer canonical casing to keep comparisons\n * against it straightforward.\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, type ParamPrimitive } 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 /**\n * Interpolation params. Write placeholders as `%key%` in the markup (a bare\n * `{key}` in JSX is a JS expression; the SDK normalizes `%key%` to canonical\n * `{key}` at capture). Applied to content-block text nodes, translatable\n * attributes, `<option>` text, and single-token content — including\n * untranslated fallbacks. Number/Date values get CLDR locale formatting.\n */\n params?: Record<string, ParamPrimitive>;\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 params,\n tag = 'translate',\n className,\n children,\n}: TranslateProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaTranslate>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaTranslate(host, { category, custom_id, label, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the identity props change; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category, custom_id, label]);\n\n // Re-render on changed params after mount, mirroring <Phrase>.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className }, children);\n}\n\nexport default Translate;\n","import { createElement, useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { Phrase as VanillaPhrase, PHRASE_MARKER_ATTR, type ParamPrimitive } from 'langsys-js-typescript';\n\n/**\n * Props for the React `Phrase` component. Mirrors the Svelte component's props,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface PhraseProps {\n /** Category the phrase registers under (disambiguation for translators). */\n category?: string;\n /** Interpolation params. Write placeholders as `%n%` / `%name%` in the markup (a bare `{n}` in JSX is an expression). */\n params?: Record<string, ParamPrimitive>;\n /** Host element tag. Defaults to `<span>`. */\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 `Phrase` rich-text handler.\n *\n * Use it to keep a markup-bearing run as ONE translatable phrase — e.g. so a\n * count variable stays next to the noun it pluralizes:\n *\n * <Phrase category=\"ProductCard\" params={{ n: reviewCount }}>\n * Based on %n% <strong>reviews</strong>\n * </Phrase>\n *\n * Write placeholders as `%n%` (not bare `{n}`): in JSX a literal `{n}` is a JS\n * expression React evaluates before the SDK sees the text. `%n%` passes through\n * as literal text and the SDK normalizes it to canonical `{n}` at capture.\n *\n * The inline markup never reaches the translator — it's replaced with neutral\n * tokens and the real elements are reconstituted at render (see richtext.ts in\n * the base SDK). The host carries `data-ls-phrase` so a wrapping `<Translate>`\n * skips it and lets this handler own it.\n *\n * Keep children static (literal markup): the handler takes over the rendered\n * subtree. For values React owns and re-renders, pass them through `params`.\n */\nexport function Phrase({ category = '', params = {}, tag = 'span', className, children }: PhraseProps) {\n const hostRef = useRef<HTMLElement | null>(null);\n const instanceRef = useRef<VanillaPhrase>(undefined);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const instance = new VanillaPhrase(host, { category, params });\n instanceRef.current = instance;\n return () => {\n instance.destroy();\n instanceRef.current = undefined;\n };\n // Recreate only when the category changes; param changes flow through setParams below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [category]);\n\n // Re-render on a changed count/param after mount.\n useEffect(() => {\n instanceRef.current?.setParams(params);\n });\n\n return createElement(tag, { ref: hostRef, className, [PHRASE_MARKER_ATTR]: '' }, children);\n}\n\nexport default Phrase;\n","import { createElement } from 'react';\nimport type { ReactNode } from 'react';\n\n/**\n * Props for the React `DontTranslate` component. Mirrors the Svelte component,\n * with Svelte's `class` renamed to React's idiomatic `className`.\n */\nexport interface DontTranslateProps {\n /** Host element tag. Defaults to `<span>`. */\n tag?: string;\n /** Class applied to the host element. */\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * Marks a region as never-translated, preserved verbatim:\n *\n * Built with <DontTranslate>Kangen®</DontTranslate> on{' '}\n * <DontTranslate>langsys.dev</DontTranslate>\n *\n * The host carries the standard `translate=\"no\"` attribute, which both the\n * tokenizer and the renderer in the base SDK already honor — so its content is\n * never tokenized, registered, or replaced. Pure presentational glue; no\n * vanilla handler needed.\n *\n * `data-ls-dont-translate` is a **wrapper-level hook only** — a styling and\n * debugging handle. The base SDK has never matched on it (verified against\n * every published version from 0.1.0 to 0.6.2); exclusion rests entirely on\n * `translate=\"no\"`. Don't remove it — consumers may select on it — but don't\n * describe it as SDK-honored either. The SDK's exclusion set is\n * `translate=\"no\"` (case-insensitive) and PHP's `data-notrans` alias.\n */\nexport function DontTranslate({ tag = 'span', className, children }: DontTranslateProps) {\n return createElement(\n tag,\n { translate: 'no', 'data-ls-dont-translate': '', className },\n children,\n );\n}\n\nexport default DontTranslate;\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// Locale canonicalization (BCP 47) — the SDK canonicalizes all locale input\n// (v0.3.0+); re-exported so consumers can normalize their own values the same\n// way before comparing against `useCurrentLocale()` / `detectPreferredLocale()`.\nexport { canonicalizeLocale } 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// Components\nexport { Translate, type TranslateProps } from './components/Translate.js';\nexport { Phrase, type PhraseProps } from './components/Phrase.js';\nexport { DontTranslate, type DontTranslateProps } from './components/DontTranslate.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langsys-js-react",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "React binding for the Langsys translation SDK — realtime continuous translations with automatic token discovery, delivered as hooks and a <Translate> component.",
|
|
5
5
|
"author": "Langsys",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"react": "^18.0.0 || ^19.0.0"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"langsys-js-typescript": "^0.6.
|
|
64
|
+
"langsys-js-typescript": "^0.6.2"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@eslint/js": "^9.0.0",
|