langsys-js-react 0.6.7 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import * as langsys_js_typescript from 'langsys-js-typescript';
2
- import { Signal, TFunction, iCategories, ParamPrimitive, iLangsysInitConfig as iLangsysInitConfig$1, iLangsysResponse, iCountryList, iCurrencyList, iCountryDialCode, iLocaleDefault, iLocaleFlat, iLocaleData } from 'langsys-js-typescript';
3
- export { ExtractParamKeys, LangsysAppAPI, ParamPrimitive, ParamsFor, Signal, TArgs, TFunction, TranslationParams, canonicalizeLocale, createSignal, currentlyLoadedLocale, iCategories, iContentBlock, iCountry, iCountryDialCode, iCountryList, iCurrency, iCurrencyList, iLangsysResponse, iLanguageName, iLocaleData, iLocaleDefault, iLocaleFlat, iProject, iTranslations, sTranslations, tSignal as t } from 'langsys-js-typescript';
1
+ import { Signal, ServerMessage, TFunction, iCategories, ParamPrimitive, RequestSeed, RequestScope } from 'langsys-js-typescript';
2
+ export { CatalogSnapshot, CategoryMoveOptions, CategoryRef, DEFAULT_SERVER_MESSAGE_CATEGORY, ExtractParamKeys, LangsysAppAPI, LangsysConfigurationError, LegacyFormatError, LegacyKeyFile, ParamPrimitive, ParamsFor, RegistrationReason, RegistrationResult, ResolveServerMessagesOptions, ServerMessage, ServerMessagePieces, Signal, SnapshotError, TArgs, TFunction, TranslationParams, WriteGrant, canonicalizeLocale, createSignal, currentlyLoadedLocale, iCategories, iContentBlock, iCountry, iCountryDialCode, iCountryList, iCurrency, iCurrencyList, iLangsysResponse, iLanguageName, iLocaleData, iLocaleDefault, iLocaleFlat, iProject, iTranslations, inCategory, interpolate, isCategoryRef, localeHeaders, notifyNavigation, renderServerMessage, resolveServerMessages, sTranslations, setWriteGrant, tSignal as t } from 'langsys-js-typescript';
4
3
  import * as react from 'react';
5
4
  import { ReactNode } from 'react';
5
+ export { L as LangsysApp, c as createLocaleStore, i as iLangsysInitConfig } from './app-zQKL7-vo.js';
6
6
 
7
7
  /**
8
8
  * Subscribe a React component to a base-SDK `Signal<T>` and return its current
@@ -11,9 +11,12 @@ import { ReactNode } from 'react';
11
11
  * This is the React mirror of Svelte's `$store` auto-subscription. Where the
12
12
  * Svelte wrapper adapts a native store *into* the SDK (`writable` → `Signal`),
13
13
  * the React wrapper adapts the SDK's signals *out* to the render cycle. It is
14
- * built on `useSyncExternalStore`, so it is concurrent-safe and SSR-safe — the
15
- * server snapshot reads the same `.get()`, which the base SDK seeds from
16
- * `initialTranslations` during SSR.
14
+ * built on `useSyncExternalStore`, so it is concurrent-safe: a render never
15
+ * observes two different values of the same signal (no tearing). On the
16
+ * server the snapshot reads the value for the current request: inside a core
17
+ * request scope the signals answer from the scope, and under a
18
+ * `<LangsysProvider seed>` the server snapshot — used for the server render and
19
+ * the hydration render — reads through the scope the provider rebuilt.
17
20
  *
18
21
  * The base SDK's signals are stable between changes (every `set` replaces the
19
22
  * value, so `.get()` returns a fresh reference only after a real change). That
@@ -26,21 +29,6 @@ import { ReactNode } from 'react';
26
29
  * every render would resubscribe on every render.
27
30
  */
28
31
  declare function useSignal<T>(signal: Signal<T>): T;
29
- /**
30
- * Create a reactive `Signal<string>` to hold the user's selected locale — the
31
- * React analog of Svelte's `writable('en-US')`.
32
- *
33
- * Pass the result as `UserLocaleStore` to `LangsysApp.init`, read it reactively
34
- * with `useSignal(store)` (or use the all-in-one `useLocaleStore` hook), and
35
- * switch locale with `store.set('fr-FR')`. The base SDK only ever reads and
36
- * subscribes to it — it never writes.
37
- *
38
- * Locale identifiers are canonicalized to BCP 47 by the base SDK (v0.3.0+), so
39
- * `'en-us'` still works on input — but `currentlyLoadedLocale` always emits the
40
- * canonical form (`'en-US'`), so prefer canonical casing to keep comparisons
41
- * against it straightforward.
42
- */
43
- declare function createLocaleStore(initial?: string): Signal<string>;
44
32
 
45
33
  /**
46
34
  * The current translation function, re-rendering the calling component whenever
@@ -82,6 +70,66 @@ declare function useTranslations(): iCategories;
82
70
  * safe to pass to `LangsysApp.init` and to depend on in effects.
83
71
  */
84
72
  declare function useLocaleStore(initial?: string): [string, (locale: string) => void, Signal<string>];
73
+ /**
74
+ * Whether the current session may register content, as decided by the server.
75
+ *
76
+ * Tri-state, and the three states are genuinely distinct:
77
+ * - `undefined` — authorization hasn't landed yet. Not the same as read-only.
78
+ * - `false` — read-only session; the SDK may report the page URL instead,
79
+ * subject to the key's auto-discovery permission. A read-only
80
+ * key with discovery disallowed reports nothing at all.
81
+ * - `true` — this session registers content directly.
82
+ *
83
+ * Don't collapse it to a boolean and don't default `undefined` to `false`: the
84
+ * same key can be write-enabled from one IP and read-only from another, so only
85
+ * the server can answer, and "not yet known" is a real state to render for.
86
+ *
87
+ * Unlike the other hooks this does *not* go through `useSignal`. `writeEnabled`
88
+ * is browser-authoritative — it is only ever written client-side and stays
89
+ * `undefined` during SSR. Reusing `useSignal` would pass `signal.get` as the
90
+ * server snapshot, and `getServerSnapshot` is used both on the server *and* for
91
+ * the hydration render on the client. If authorization resolved before
92
+ * hydration, that render would disagree with the server HTML and React would
93
+ * throw the markup away. Pinning the server snapshot to `undefined` keeps the
94
+ * hydration pass matching the server; the real value arrives right after.
95
+ */
96
+ declare function useWriteEnabled(): boolean | undefined;
97
+ /**
98
+ * Tell the SDK the route changed, whenever `location` changes. Call it once, anywhere under
99
+ * your router, with the router's current location:
100
+ *
101
+ * // React Router
102
+ * useNotifyNavigation(useLocation().key);
103
+ * // Next.js App Router
104
+ * useNotifyNavigation(usePathname() + '?' + useSearchParams());
105
+ *
106
+ * Why it exists: content that stays mounted across a route change — a persistent layout, a
107
+ * header — is never re-rendered by React when only the route changes, because React skips
108
+ * a subtree whose element is unchanged. Nothing then calls back into the SDK, so the new
109
+ * page is never credited with that content. `notifyNavigation()` makes every mounted
110
+ * translated node look itself up again at the new URL. This hook only times that call; the
111
+ * SDK decides everything else (spec HINT-13).
112
+ *
113
+ * This package cannot know which router an app uses, so it takes the router's location as a
114
+ * plain value instead of importing any router. Calling `notifyNavigation` directly from a
115
+ * router's own after-navigation hook is equivalent.
116
+ */
117
+ declare function useNotifyNavigation(location: unknown): void;
118
+ /**
119
+ * A function that renders server message entries, re-rendering the calling component whenever
120
+ * translations or the loaded locale change:
121
+ *
122
+ * const render = useRenderServerMessage();
123
+ * const entries = resolveServerMessages(responseBody);
124
+ * return <ul>{entries.map((e, i) => <li key={i}>{render(e)}</li>)}</ul>;
125
+ *
126
+ * It is the SDK's `renderServerMessage` and decides nothing itself: the entry's template is
127
+ * looked up under the messages category (`messagesCategory` in `init`, `Errors` by default) and
128
+ * filled from its params, and the entry's `message` is shown when the catalog has no
129
+ * translation for it (spec MSG-5). The returned function changes identity exactly when a
130
+ * lookup could give a different answer, so it can be depended on in memos and effects.
131
+ */
132
+ declare function useRenderServerMessage(): (entry: ServerMessage, category?: string) => string;
85
133
 
86
134
  /**
87
135
  * Props for the React `Translate` component. Mirrors the Svelte component's
@@ -90,6 +138,15 @@ declare function useLocaleStore(initial?: string): [string, (locale: string) =>
90
138
  interface TranslateProps {
91
139
  /** Optional category under which tokens are registered. Helps translators disambiguate. */
92
140
  category?: string;
141
+ /**
142
+ * The category this content was registered under before it moved to `category` (MOVE-1):
143
+ * `''` for none, absent for no move. Its translations follow it instead of being made, and
144
+ * charged for, again; until the catalog holds it under `category`, the previous category's
145
+ * entry renders and the host says so (MOVE-4, MOVE-5). Never part of its identity (MOVE-2),
146
+ * and sent only on the registration a miss triggers (MOVE-3), so once the catalog has it the
147
+ * prop does nothing and can be deleted whenever convenient.
148
+ */
149
+ previousCategory?: string | null;
93
150
  /** Optional stable id for the content block. If omitted, the SDK hashes category + tokens. */
94
151
  custom_id?: string;
95
152
  /** Optional human-readable label shown in the Translation Manager. */
@@ -114,24 +171,30 @@ interface TranslateProps {
114
171
  /** Class applied to the host element. */
115
172
  className?: string;
116
173
  children?: ReactNode;
174
+ /**
175
+ * Set by the build transform when the block would store a value it left as written (inside
176
+ * MathML or SVG, `<script>` or `<style>`, or an element that holds text only): the block
177
+ * registers nothing (VAR-7) and renders what the catalog holds.
178
+ */
179
+ 'data-ls-unnamed'?: string;
117
180
  }
118
181
  /**
119
- * React wrapper around the vanilla `Translate` DOM class from
120
- * `langsys-js-typescript`. It renders a host element, then on mount lets the
121
- * vanilla class walk and tokenize the rendered children (text nodes plus
122
- * translatable attributes), register the content block, and re-translate on
123
- * locale change. On unmount it tears the instance down.
182
+ * Translates a block of static content: prose, markup, CMS copy.
124
183
  *
125
- * This is the React analog of the Svelte `<Translate>` component — pure
126
- * mount/destroy glue. The DOM walking, content-block registration, attribute
127
- * harvesting, and re-translation lifecycle all live in the base SDK.
184
+ * Content made of host elements, text and numbers renders through the core's `renderBlock`: the
185
+ * translated tree, the block's id and its markers come back as data, and this component renders
186
+ * them as React elements — the originals, cloned with their handlers, refs and keys, in the
187
+ * places the translation puts them. The same call runs on a server (inside a request scope) and
188
+ * in the browser (over the seeded catalog), so the first client render matches the server's HTML,
189
+ * and a locale change re-renders it. Registration goes through the core's `registerBlock`; no DOM
190
+ * class ever walks text nodes React owns.
128
191
  *
129
- * Like the Svelte component, this lets the SDK mutate the rendered DOM in place,
130
- * so keep the children static: prose, marketing copy, CMS-rendered HTML — the
131
- * content-block use case. For dynamic per-string values that React owns and
132
- * re-renders, use `useT()` instead.
192
+ * Content whose DOM is unknown until React renders it — a component, `lazy`, `Suspense`, or
193
+ * `dangerouslySetInnerHTML` — is rendered as it is, with an explicit `custom_id` stamped, and the
194
+ * core's DOM `Translate` class translates it after mount. On a server the core warns once per
195
+ * reason that such a block was served as source.
133
196
  */
134
- declare function Translate({ category, custom_id, label, params, tag, className, children, }: TranslateProps): react.ReactElement<{
197
+ declare function Translate({ category: named, previousCategory: namedPrevious, custom_id, label, params: given, tag, className, children: content, 'data-ls-unnamed': unmarked, }: TranslateProps): react.ReactElement<{
135
198
  ref: react.RefObject<HTMLElement | null>;
136
199
  className: string | undefined;
137
200
  }, string | react.JSXElementConstructor<any>>;
@@ -143,6 +206,15 @@ declare function Translate({ category, custom_id, label, params, tag, className,
143
206
  interface PhraseProps {
144
207
  /** Category the phrase registers under (disambiguation for translators). */
145
208
  category?: string;
209
+ /**
210
+ * The category this content was registered under before it moved to `category` (MOVE-1):
211
+ * `''` for none, absent for no move. Its translations follow it instead of being made, and
212
+ * charged for, again; until the catalog holds it under `category`, the previous category's
213
+ * entry renders and the host says so (MOVE-4, MOVE-5). Never part of its identity (MOVE-2),
214
+ * and sent only on the registration a miss triggers (MOVE-3), so once the catalog has it the
215
+ * prop does nothing and can be deleted whenever convenient.
216
+ */
217
+ previousCategory?: string | null;
146
218
  /**
147
219
  * Interpolation params. Write placeholders as `%n%` / `%name%` in the markup.
148
220
  * A bare `{n}` in JSX is an expression React evaluates before the SDK sees the
@@ -155,6 +227,13 @@ interface PhraseProps {
155
227
  /** Class applied to the host element. */
156
228
  className?: string;
157
229
  children?: ReactNode;
230
+ /**
231
+ * Set by the build transform when every value of the phrase is marked (it holds a marker of its
232
+ * own) and one in its translated text stands where no marker can (in a translated `<svg>`, or an
233
+ * element that holds text only), so it is left as written: the phrase registers nothing (VAR-7)
234
+ * and renders what the catalog holds.
235
+ */
236
+ 'data-ls-unnamed'?: string;
158
237
  }
159
238
  /**
160
239
  * React wrapper around the vanilla `Phrase` rich-text handler.
@@ -187,10 +266,9 @@ interface PhraseProps {
187
266
  * Keep children static (literal markup): the handler takes over the rendered
188
267
  * subtree. For values React owns and re-renders, pass them through `params`.
189
268
  */
190
- declare function Phrase({ category, params, tag, className, children }: PhraseProps): react.ReactElement<{
269
+ declare function Phrase({ category: named, previousCategory: namedPrevious, params: given, tag, className, children: content, 'data-ls-unnamed': unmarked, }: PhraseProps): react.ReactElement<{
191
270
  ref: react.RefObject<HTMLElement | null>;
192
271
  className: string | undefined;
193
- "data-ls-phrase": string;
194
272
  }, string | react.JSXElementConstructor<any>>;
195
273
 
196
274
  /**
@@ -213,7 +291,9 @@ interface DontTranslateProps {
213
291
  * The host carries the standard `translate="no"` attribute, which both the
214
292
  * tokenizer and the renderer in the base SDK already honor — so its content is
215
293
  * never tokenized, registered, or replaced. Pure presentational glue; no
216
- * vanilla handler needed.
294
+ * vanilla handler needed. Inside `<Translate>` / `<Phrase>` the block mapping reads it as the host
295
+ * element it renders (`toBlockNodes`), so the unit around it stays on the core's tree path, and the
296
+ * build transform marks a value inside it rather than replacing it (VAR-3).
217
297
  *
218
298
  * `data-ls-dont-translate` is a **wrapper-level hook only** — a styling and
219
299
  * debugging handle. The base SDK has never matched on it (verified against
@@ -228,46 +308,30 @@ declare function DontTranslate({ tag, className, children }: DontTranslateProps)
228
308
  className: string | undefined;
229
309
  }, string | react.JSXElementConstructor<any>>;
230
310
 
231
- /**
232
- * React-flavored init config. Identical to the base SDK's config except
233
- * `UserLocaleStore` is typed as a `Signal<string>` — create one with
234
- * `createLocaleStore()` (or get one from the `useLocaleStore` hook). The base
235
- * SDK only reads and subscribes to it.
236
- */
237
- interface iLangsysInitConfig extends Omit<iLangsysInitConfig$1, 'UserLocaleStore'> {
238
- UserLocaleStore: Signal<string>;
311
+ interface LangsysProviderProps {
312
+ /** The request's seed, from `scope.seed()` in a server component, passed down as a prop. */
313
+ seed: RequestSeed | null | undefined;
314
+ children?: ReactNode;
239
315
  }
240
316
  /**
241
- * React SDK entry point. Delegates everything to the underlying
242
- * `langsys-js-typescript` singleton. Because React's locale store is already a
243
- * `Signal` (unlike Svelte's `Writable`, which needs adapting), `init` is a
244
- * straight passthrough — the React-native concerns live in the hooks and the
245
- * `<Translate>` component, not here.
317
+ * Renders a request in its own locale and catalog where the framework renders client
318
+ * components separately from the code that opened the request's scope — Next.js's App Router,
319
+ * where server components and client components use separate copies of the SDK.
320
+ *
321
+ * // app/layout.tsx (a server component)
322
+ * const scope = await createRequestScope({ locale, url }); // from langsys-js-react/server
323
+ * return <LangsysProvider seed={scope.seed()}>{children}</LangsysProvider>;
324
+ *
325
+ * On the server, the provider rebuilds the request's scope from the seed with the core's
326
+ * `scopeFromSeed` and hands it down: `useT()`, `useCurrentLocale()`, `useTranslations()`,
327
+ * `<Translate>` and `<Phrase>` render from it. In the browser it seeds the SDK with the same
328
+ * catalog before any child's effect runs, and the hydration render reads the same scope, so the
329
+ * client's first render matches the server's HTML.
330
+ *
331
+ * Content a client component misses during the server render is registered by the browser after
332
+ * hydration. Under the `server` SSR strategy those misses are not sent from the server: the
333
+ * provider's side of the render has no after-response hook.
246
334
  */
247
- declare class LangsysAppReact {
248
- /** Initialize Langsys. Pass a `Signal<string>` (from `createLocaleStore`) as `UserLocaleStore`. */
249
- init(config: iLangsysInitConfig): Promise<iLangsysResponse>;
250
- get Translations(): langsys_js_typescript.Translations;
251
- get translationsLoadingPromise(): Promise<unknown>;
252
- /** Current translation function. Reads fresh state on every call (not reactive on its own — use `useT()` in components). */
253
- get t(): TFunction;
254
- get debug(): langsys_js_typescript.Logger;
255
- refresh(): Promise<boolean>;
256
- getCountries(inLocale?: string): Promise<iCountryList>;
257
- getCountryName(forCountryCode: string, inLocale?: string): Promise<string>;
258
- getCurrencies(inLocale?: string): Promise<iCurrencyList>;
259
- getCurrencyName(forCurrencyCode: string, inLocale?: string): Promise<string>;
260
- getDialCodes(inLocale?: string): Promise<iCountryDialCode[]>;
261
- getLocales(inLocale?: string): Promise<iLocaleDefault>;
262
- getLocalesFlat(inLocale?: string): Promise<iLocaleFlat[]>;
263
- getLocalesData(inLocale?: string, forceRefresh?: boolean): Promise<iLocaleData[]>;
264
- getLocalesFormat(format?: '' | 'flat' | 'data', inLocale?: string): Promise<iLocaleFlat[] | iLocaleDefault | iLocaleData[]>;
265
- getLocaleName(forLocale: string, shortName?: boolean, inLocale?: string): string;
266
- getLocaleNameWithLookup(forLocale: string, shortName?: boolean, inLocale?: string): Promise<string>;
267
- /** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */
268
- getLanguageName(forLocale: string, shortName?: boolean, inLocale?: string): Promise<string>;
269
- detectPreferredLocale(acceptLanguageHeader?: string | null, supportedLocales?: string[]): string | false;
270
- }
271
- declare const LangsysApp: LangsysAppReact;
335
+ declare function LangsysProvider({ seed, children }: LangsysProviderProps): react.FunctionComponentElement<react.ProviderProps<RequestScope | null>>;
272
336
 
273
- export { DontTranslate, type DontTranslateProps, LangsysApp, Phrase, type PhraseProps, Translate, type TranslateProps, createLocaleStore, type iLangsysInitConfig, useCurrentLocale, useLocaleStore, useSignal, useT, useTranslations };
337
+ export { DontTranslate, type DontTranslateProps, LangsysProvider, type LangsysProviderProps, Phrase, type PhraseProps, Translate, type TranslateProps, useCurrentLocale, useLocaleStore, useNotifyNavigation, useRenderServerMessage, useSignal, useT, useTranslations, useWriteEnabled };