langsys-js-react 0.6.6 → 0.6.7

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 CHANGED
@@ -1,3 +1,11 @@
1
+
2
+ ## 0.6.7 - 2026-08-21
3
+
4
+ ### Docs
5
+
6
+ - `%key%` guidance now names the expensive consequence, not just the mechanism. A bare `{key}` in JSX is evaluated before the SDK's walker runs, so the *value* is captured as part of the phrase and **every distinct value hashes to its own content block** — a `<Translate>` around a live counter mints a catalog entry per tick while rendering correctly in the base locale throughout. Previously documented only as "interpolation silently breaks". Verified against the shipped `tokenizeElement`/`generateCustomId`. README and `<Translate>` JSDoc.
7
+ - Same gap closed in `<Phrase>`'s JSDoc, which reaches a different identity path and needed its own measurement: `<Phrase>` keys on the encoded phrase string from `encodeRichText`, not on a token array, so a substituted value becomes part of the key directly (`"Based on 0 {m0o}reviews{m0c}"` per value, versus a stable `"Based on {n} {m0o}reviews{m0c}"`). Same outcome, different mechanism. JSDoc matters more than usual here — it ships in `dist/` and is what IDE hover renders, so for anyone who never opens the README it *is* the documentation.
8
+
1
9
  ## 0.6.6 - 2026-08-16
2
10
 
3
11
  ### Changed
package/README.md CHANGED
@@ -203,10 +203,21 @@ The component:
203
203
  </Translate>
204
204
  ```
205
205
 
206
- > **Use `%key%` in `<Translate>`/`<Phrase>` markup, not bare `{key}`.** In JSX a literal `{count}` is a JavaScript expression that React evaluates *before* the SDK's DOM walker sees the text — the braces vanish and interpolation silently breaks (it still looks fine in the base locale, which hides it). `%count%` passes through JSX as plain text; the SDK normalizes it to canonical `{count}` at capture, so **translators and the catalog only ever see `{count}`**, and both spellings hash to the same content-block id. Keys are identifier-shaped (`%[A-Za-z_][A-Za-z0-9_]*%`), so a stray `%` in prose ("50% off") is left untouched. An unknown `%key%` with no matching param renders as `{key}` — matching `t()`'s behavior for unknown keys. (`t()` and the hooks keep `{key}`: JS strings reach the SDK literally, so there's no collision.)
206
+ > **Use `%key%` in `<Translate>`/`<Phrase>` markup, not bare `{key}`.** In JSX a literal `{count}` is a JavaScript expression that React evaluates *before* the SDK's DOM walker sees the text — the braces vanish and interpolation silently breaks (it still looks fine in the base locale, which hides it). Worse than breaking: the evaluated value is captured *as part of the phrase*, so every distinct value hashes to its own content block. `%count%` passes through JSX as plain text; the SDK normalizes it to canonical `{count}` at capture, so **translators and the catalog only ever see `{count}`**, and both spellings hash to the same content-block id. Keys are identifier-shaped (`%[A-Za-z_][A-Za-z0-9_]*%`), so a stray `%` in prose ("50% off") is left untouched. An unknown `%key%` with no matching param renders as `{key}` — matching `t()`'s behavior for unknown keys. (`t()` and the hooks keep `{key}`: JS strings reach the SDK literally, so there's no collision.)
207
207
 
208
208
  > Since base SDK 0.4.2, running with `debug: true` catches this mistake for you: if you pass `params` whose keys match no placeholder in the captured content, the SDK warns and names the fix (`… received params with no matching placeholder … write %count% instead`). It's silent in production, treats ICU slots as legitimate, and only re-warns when the set of param keys changes.
209
209
 
210
+ Why that second consequence is the expensive one — measured against the shipped tokenizer (`tokenizeElement` + `generateCustomId`):
211
+
212
+ ```
213
+ {count} evaluated by JSX %count% placeholder
214
+ "You have 0 items" 31ff32bd… "You have {count} items" 88642c82…
215
+ "You have 1 items" 5aa5eef5… "You have {count} items" 88642c82…
216
+ "You have 2 items" 89f09f5e… "You have {count} items" 88642c82…
217
+ ```
218
+
219
+ The `%key%` spelling normalizes to canonical `{count}` at capture, so all values share one stable id. The JSX spelling registers a **new content block per distinct value** — wrap a live counter in `<Translate>` and you mint a catalog entry per tick. Nothing looks wrong while it happens: the base locale renders correctly throughout, and the damage shows up later as a Translation Manager full of near-duplicate junk.
220
+
210
221
  `<Translate>` props: `category?`, `custom_id?`, `label?`, `params?`, `tag?` (defaults to `translate`), `className?`, `children`.
211
222
 
212
223
  ### `<Phrase>` — markup-bearing phrases (pluralization)
package/dist/index.d.mts CHANGED
@@ -100,6 +100,13 @@ interface TranslateProps {
100
100
  * `{key}` at capture). Applied to content-block text nodes, translatable
101
101
  * attributes, `<option>` text, and single-token content — including
102
102
  * untranslated fallbacks. Number/Date values get CLDR locale formatting.
103
+ *
104
+ * A bare `{key}` doesn't just break interpolation — JSX evaluates it before
105
+ * the walker runs, so the *value* is captured as part of the phrase and each
106
+ * distinct value hashes to its own content block. A `<Translate>` around a
107
+ * live counter registers a new block per tick while rendering correctly in
108
+ * the base locale the whole time. `%key%` yields one stable id for all
109
+ * values.
103
110
  */
104
111
  params?: Record<string, ParamPrimitive>;
105
112
  /** Host element tag. Defaults to a `<translate>` custom element. */
@@ -136,7 +143,12 @@ declare function Translate({ category, custom_id, label, params, tag, className,
136
143
  interface PhraseProps {
137
144
  /** Category the phrase registers under (disambiguation for translators). */
138
145
  category?: string;
139
- /** Interpolation params. Write placeholders as `%n%` / `%name%` in the markup (a bare `{n}` in JSX is an expression). */
146
+ /**
147
+ * Interpolation params. Write placeholders as `%n%` / `%name%` in the markup.
148
+ * A bare `{n}` in JSX is an expression React evaluates before the SDK sees the
149
+ * text, so the *value* lands in the captured phrase and each distinct value
150
+ * becomes its own phrase to translate. `%n%` yields one stable phrase.
151
+ */
140
152
  params?: Record<string, ParamPrimitive>;
141
153
  /** Host element tag. Defaults to `<span>`. */
142
154
  tag?: string;
@@ -158,6 +170,15 @@ interface PhraseProps {
158
170
  * expression React evaluates before the SDK sees the text. `%n%` passes through
159
171
  * as literal text and the SDK normalizes it to canonical `{n}` at capture.
160
172
  *
173
+ * The cost of getting this wrong is not a broken placeholder, it's catalog
174
+ * churn. `<Phrase>` keys on the encoded phrase string, so the substituted value
175
+ * becomes part of the key — a new phrase per distinct value, none reusable,
176
+ * with the base locale rendering correctly throughout. Measured against the
177
+ * shipped `encodeRichText`:
178
+ *
179
+ * {n} via JSX "Based on 0 {m0o}reviews{m0c}" (and 1, 2, … each new)
180
+ * %n% placeholder "Based on {n} {m0o}reviews{m0c}" (stable for all values)
181
+ *
161
182
  * The inline markup never reaches the translator — it's replaced with neutral
162
183
  * tokens and the real elements are reconstituted at render (see richtext.ts in
163
184
  * the base SDK). The host carries `data-ls-phrase` so a wrapping `<Translate>`
package/dist/index.d.ts CHANGED
@@ -100,6 +100,13 @@ interface TranslateProps {
100
100
  * `{key}` at capture). Applied to content-block text nodes, translatable
101
101
  * attributes, `<option>` text, and single-token content — including
102
102
  * untranslated fallbacks. Number/Date values get CLDR locale formatting.
103
+ *
104
+ * A bare `{key}` doesn't just break interpolation — JSX evaluates it before
105
+ * the walker runs, so the *value* is captured as part of the phrase and each
106
+ * distinct value hashes to its own content block. A `<Translate>` around a
107
+ * live counter registers a new block per tick while rendering correctly in
108
+ * the base locale the whole time. `%key%` yields one stable id for all
109
+ * values.
103
110
  */
104
111
  params?: Record<string, ParamPrimitive>;
105
112
  /** Host element tag. Defaults to a `<translate>` custom element. */
@@ -136,7 +143,12 @@ declare function Translate({ category, custom_id, label, params, tag, className,
136
143
  interface PhraseProps {
137
144
  /** Category the phrase registers under (disambiguation for translators). */
138
145
  category?: string;
139
- /** Interpolation params. Write placeholders as `%n%` / `%name%` in the markup (a bare `{n}` in JSX is an expression). */
146
+ /**
147
+ * Interpolation params. Write placeholders as `%n%` / `%name%` in the markup.
148
+ * A bare `{n}` in JSX is an expression React evaluates before the SDK sees the
149
+ * text, so the *value* lands in the captured phrase and each distinct value
150
+ * becomes its own phrase to translate. `%n%` yields one stable phrase.
151
+ */
140
152
  params?: Record<string, ParamPrimitive>;
141
153
  /** Host element tag. Defaults to `<span>`. */
142
154
  tag?: string;
@@ -158,6 +170,15 @@ interface PhraseProps {
158
170
  * expression React evaluates before the SDK sees the text. `%n%` passes through
159
171
  * as literal text and the SDK normalizes it to canonical `{n}` at capture.
160
172
  *
173
+ * The cost of getting this wrong is not a broken placeholder, it's catalog
174
+ * churn. `<Phrase>` keys on the encoded phrase string, so the substituted value
175
+ * becomes part of the key — a new phrase per distinct value, none reusable,
176
+ * with the base locale rendering correctly throughout. Measured against the
177
+ * shipped `encodeRichText`:
178
+ *
179
+ * {n} via JSX "Based on 0 {m0o}reviews{m0c}" (and 1, 2, … each new)
180
+ * %n% placeholder "Based on {n} {m0o}reviews{m0c}" (stable for all values)
181
+ *
161
182
  * The inline markup never reaches the translator — it's replaced with neutral
162
183
  * tokens and the real elements are reconstituted at render (see richtext.ts in
163
184
  * the base SDK). The host carries `data-ls-phrase` so a wrapping `<Translate>`
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 *\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"]}
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 * A bare `{key}` doesn't just break interpolation — JSX evaluates it before\n * the walker runs, so the *value* is captured as part of the phrase and each\n * distinct value hashes to its own content block. A `<Translate>` around a\n * live counter registers a new block per tick while rendering correctly in\n * the base locale the whole time. `%key%` yields one stable id for all\n * values.\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 /**\n * Interpolation params. Write placeholders as `%n%` / `%name%` in the markup.\n * A bare `{n}` in JSX is an expression React evaluates before the SDK sees the\n * text, so the *value* lands in the captured phrase and each distinct value\n * becomes its own phrase to translate. `%n%` yields one stable phrase.\n */\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 cost of getting this wrong is not a broken placeholder, it's catalog\n * churn. `<Phrase>` keys on the encoded phrase string, so the substituted value\n * becomes part of the key — a new phrase per distinct value, none reusable,\n * with the base locale rendering correctly throughout. Measured against the\n * shipped `encodeRichText`:\n *\n * {n} via JSX \"Based on 0 {m0o}reviews{m0c}\" (and 1, 2, … each new)\n * %n% placeholder \"Based on {n} {m0o}reviews{m0c}\" (stable for all values)\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"]}
@@ -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":";;;;;AAwBO,SAAS,UAAa,MAAA,EAAsB;AAC/C,EAAA,OAAO,qBAAqB,MAAA,CAAO,SAAA,EAAW,MAAA,CAAO,GAAA,EAAK,OAAO,GAAG,CAAA;AACxE;AAgBO,SAAS,iBAAA,CAAkB,UAAU,OAAA,EAAyB;AACjE,EAAA,OAAO,aAAqB,OAAO,CAAA;AACvC;AC3BO,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;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,GAAU,OAA2B,IAAI,CAAA;AAC/C,EAAA,MAAM,WAAA,GAAc,OAAyB,MAAS,CAAA;AAEtD,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,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,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,WAAA,CAAY,OAAA,EAAS,UAAU,MAAM,CAAA;AAAA,EACzC,CAAC,CAAA;AAED,EAAA,OAAO,cAAc,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,GAAUC,OAA2B,IAAI,CAAA;AAC/C,EAAA,MAAM,WAAA,GAAcA,OAAsB,MAAS,CAAA;AAEnD,EAAAC,UAAU,MAAM;AACZ,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,WAAW,IAAIC,QAAA,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,EAAAD,UAAU,MAAM;AACZ,IAAA,WAAA,CAAY,OAAA,EAAS,UAAU,MAAM,CAAA;AAAA,EACzC,CAAC,CAAA;AAED,EAAA,OAAOE,aAAAA,CAAc,GAAA,EAAK,EAAE,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,CAAC,kBAAkB,GAAG,EAAA,EAAG,EAAG,QAAQ,CAAA;AAC7F;AChCO,SAAS,cAAc,EAAE,GAAA,GAAM,MAAA,EAAQ,SAAA,EAAW,UAAS,EAAuB;AACrF,EAAA,OAAOA,aAAAA;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,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 *\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"]}
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 * A bare `{key}` doesn't just break interpolation — JSX evaluates it before\n * the walker runs, so the *value* is captured as part of the phrase and each\n * distinct value hashes to its own content block. A `<Translate>` around a\n * live counter registers a new block per tick while rendering correctly in\n * the base locale the whole time. `%key%` yields one stable id for all\n * values.\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 /**\n * Interpolation params. Write placeholders as `%n%` / `%name%` in the markup.\n * A bare `{n}` in JSX is an expression React evaluates before the SDK sees the\n * text, so the *value* lands in the captured phrase and each distinct value\n * becomes its own phrase to translate. `%n%` yields one stable phrase.\n */\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 cost of getting this wrong is not a broken placeholder, it's catalog\n * churn. `<Phrase>` keys on the encoded phrase string, so the substituted value\n * becomes part of the key — a new phrase per distinct value, none reusable,\n * with the base locale rendering correctly throughout. Measured against the\n * shipped `encodeRichText`:\n *\n * {n} via JSX \"Based on 0 {m0o}reviews{m0c}\" (and 1, 2, … each new)\n * %n% placeholder \"Based on {n} {m0o}reviews{m0c}\" (stable for all values)\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.6",
3
+ "version": "0.6.7",
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",