@reformer/renderer-json 9.0.0-beta.1 → 9.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-Bu5XPCQ7.js → index-DrpWfdbU.js} +24 -14
- package/dist/index.d.ts +11 -4
- package/dist/index.js +541 -162
- package/dist/locale/i18n.d.ts +21 -0
- package/dist/locale/locale-context.d.ts +36 -0
- package/dist/locale/locale-service.d.ts +76 -0
- package/dist/locale/locale-service.test.d.ts +1 -0
- package/dist/locale/use-signal-value.d.ts +15 -0
- package/dist/locale/use-signal-value.test.d.ts +1 -0
- package/dist/operators.d.ts +37 -3
- package/dist/operators.test.d.ts +1 -0
- package/dist/registry/component-registry.d.ts +2 -0
- package/dist/registry/component-registry.test.d.ts +1 -0
- package/dist/registry/constants.d.ts +17 -0
- package/dist/registry/types.d.ts +24 -3
- package/dist/schema/index.d.ts +18 -0
- package/dist/validate.d.ts +5 -1
- package/dist/validate.js +1156 -1155
- package/llms.txt +663 -12
- package/package.json +1 -1
package/llms.txt
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
- 05-cookbook.md — Cookbook
|
|
13
13
|
- 06-validation.md — Validation
|
|
14
14
|
- 07-form-wizard.md — FormWizard
|
|
15
|
+
- 08-i18n.md — i18n / Локализация
|
|
15
16
|
- API Reference (auto-generated from JSDoc)
|
|
16
17
|
|
|
17
18
|
## 1. Installation
|
|
@@ -147,8 +148,10 @@ function MyFormPage() {
|
|
|
147
148
|
- `'$model(path)'` — путь к полю/массиву модели (лист → `model.signalAt(path)`, массив → value-прокси массива).
|
|
148
149
|
- `'$component(Name)'` — имя компонента в реестре (`reg.component`).
|
|
149
150
|
- `'$dataSource(NAME)'` — имя registry-source (`reg.dataSource`): options, itemLabel, константы, loading-компоненты.
|
|
151
|
+
- `'$fn(name)'` — имя функции в реестре (`reg.fn`): форматтеры, компараторы, itemLabel, обработчики. Резолвится в саму функцию (передаётся в проп как есть). Отдельный от `$dataSource` вид — `validate` ловит перепутанные `$fn`/`$dataSource` и `reg.fn` бросает на не-функцию.
|
|
152
|
+
- `'$locale(key)'` — ключ строки для сервиса локализации (`reg.locale`). Резолвится в **строку на этапе конвертации** (`registry.getLocale().resolve(key)`); промах ключа / нет сервиса → сам ключ. Только `componentProps` (label/placeholder/title/aria-*), не структурные позиции. С параметрами — структурная форма `{ "$locale": "key", "params": { … } }` (объект, params — литералы). Реактивный/markdown-текст — компонент `$component(I18n)`, см. [08-i18n.md](08-i18n.md).
|
|
150
153
|
- **`selector`** — plain-строка, id узла для render-behavior (`schema.node('…')`, `hideWhen`, `patchProps`). **Не** путь модели.
|
|
151
|
-
- **`componentProps`** — что прокидывается в React-компонент. Значения могут содержать строки-операторы (`'$dataSource(NAME)'`, `'$model(...)'`, `'$component(...)'`) или вложенные `JsonNode` — конвертер резолвит их рекурсивно. Обычные значения (числа, инлайн-массивы options, `label`) идут как есть.
|
|
154
|
+
- **`componentProps`** — что прокидывается в React-компонент. Значения могут содержать строки-операторы (`'$dataSource(NAME)'`, `'$model(...)'`, `'$component(...)'`, `'$fn(...)'`, `'$locale(...)'`) или вложенные `JsonNode` — конвертер резолвит их рекурсивно. Обычные значения (числа, инлайн-массивы options, `label`) идут как есть.
|
|
152
155
|
|
|
153
156
|
## 8. Type Guards
|
|
154
157
|
|
|
@@ -206,7 +209,21 @@ const schema: JsonFormSchema = {
|
|
|
206
209
|
}
|
|
207
210
|
```
|
|
208
211
|
|
|
209
|
-
|
|
212
|
+
Функция из реестра (`$fn`) и локализованный текст (`$locale`) в `componentProps`:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
{
|
|
216
|
+
value: '$model(email)',
|
|
217
|
+
component: '$component(Input)',
|
|
218
|
+
componentProps: {
|
|
219
|
+
label: '$locale(fields.email.label)', // → строка из сервиса локализации
|
|
220
|
+
placeholder: '$locale(fields.email.placeholder)',
|
|
221
|
+
format: '$fn(formatEmail)', // → сама функция из reg.fn, передаётся в проп
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Массив с шаблоном элемента (`array` + `item.$template` + `initialValue`). Внутри `$template` пути `$model(...)` резолвятся **относительно элемента** массива. `itemLabel` — функция `(control, index) => string`, поэтому идиоматично через `$fn`:
|
|
210
227
|
|
|
211
228
|
```typescript
|
|
212
229
|
{
|
|
@@ -214,9 +231,9 @@ const schema: JsonFormSchema = {
|
|
|
214
231
|
array: '$model(properties)',
|
|
215
232
|
initialValue: { type: 'apartment', description: '', estimatedValue: 0, hasEncumbrance: false },
|
|
216
233
|
componentProps: {
|
|
217
|
-
title: '
|
|
218
|
-
addButtonLabel: '
|
|
219
|
-
itemLabel: '$
|
|
234
|
+
title: '$locale(properties.title)',
|
|
235
|
+
addButtonLabel: '$locale(properties.add)',
|
|
236
|
+
itemLabel: '$fn(propertyItemLabel)',
|
|
220
237
|
},
|
|
221
238
|
item: {
|
|
222
239
|
$template: {
|
|
@@ -241,6 +258,10 @@ const schema: JsonFormSchema = {
|
|
|
241
258
|
- **`initialValue` как FieldConfig** — это plain-литерал по форме элемента (`{ field: value }`), а не `{ value, component }`. Клонируется через `JSON.parse(JSON.stringify(...))`, поэтому только сериализуемые значения.
|
|
242
259
|
- **Ссылаться на `$dataSource(NAME)` без регистрации** — при `validate` неизвестное имя даст ошибку; без валидации строка просто прокинется как есть (молчаливый баг).
|
|
243
260
|
- **Класть `validators` в field-node** — `JsonFieldNode` несёт только layout, поля `validators` в нём нет и оператора `$validator(...)` не существует. Валидация значений живёт в отдельной TS-схеме над моделью (`validateFormModel`), а не в JSON — см. [06-validation.md](06-validation.md).
|
|
261
|
+
- **Путать `$fn` и `$dataSource`** — функции регистрируй через `reg.fn` и ссылайся через `$fn(name)`, данные — через `reg.dataSource`/`$dataSource(NAME)`. Перекрёстное использование (`$fn(LOAN_TYPES)`, `$dataSource(comparator)`) `validateFormSchema` отклонит, а рантайм бросит. `reg.fn` дополнительно бросает при регистрации не-функции.
|
|
262
|
+
- **Аргументы у `$fn`** — оператор передаёт функцию **по ссылке**; биндинга аргументов (`$fn(goToStep, 2)`) нет. Нужен предзаданный аргумент — зарегистрируй уже связанную функцию через `reg.fn`.
|
|
263
|
+
- **`$locale` для reactive-переключения языка** — ключ резолвится в строку **на этапе конвертации** (иначе signal уронил бы строковые компоненты). Смена языка = новый сервис в `reg.locale` + пересборка дерева, а не «живое» обновление. Для live-переключения и markdown/rich — компонент `$component(I18n)` + `LocaleProvider`, см. [08-i18n.md](08-i18n.md).
|
|
264
|
+
- **Динамический/составной ключ `$locale`** — ключ это статичный литерал (`$locale(fields.email.label)`); вложить в него `$model(...)`/выражение нельзя. При наличии каталога опечатка ключа ловится на `validate`; без каталога промах молча деградирует до самого ключа.
|
|
244
265
|
|
|
245
266
|
## 11. See also
|
|
246
267
|
|
|
@@ -254,6 +275,8 @@ const schema: JsonFormSchema = {
|
|
|
254
275
|
|
|
255
276
|
- **component** — любой React-компонент, зарегистрированный под именем и доступный в схеме как `component: '$component(name)'`. Один метод `reg.component(name, Component)` регистрирует и компоненты-листья (Input/Select — узел несёт `value: '$model(path)'`), и контейнеры-обёртки (Box/Section/FormField — узел несёт `children`). Роль узла (лист vs контейнер) определяется **структурой узла в схеме** (`value` vs `children`), а не тем, как компонент зарегистрирован.
|
|
256
277
|
- **dataSource value** — именованная константа, функция или React-компонент, на которые ссылаются строкой `'$dataSource(NAME)'` из `componentProps`. Регистрируется через `reg.dataSource(name, value)`.
|
|
278
|
+
- **fn** — функция (форматтер/компаратор/itemLabel/обработчик), на которую ссылаются строкой `'$fn(name)'` из `componentProps`. Регистрируется через `reg.fn(name, fn)`. Отдельный от `dataSource` вид: `reg.fn` бросает при регистрации не-функции, а `validateFormSchema` ловит перепутанные `$fn`/`$dataSource`. Рантайм передаёт функцию в проп по ссылке (как `$dataSource`-функцию), новизна — в статической проверке.
|
|
279
|
+
- **locale service** — сервис локализации для `$locale(key)` (строковый путь) и компонента `I18n` (реактивный путь). Регистрируется через `reg.locale(serviceOrResolver)` (ключ `LOCALE_SERVICE`) и/или подаётся в `LocaleProvider`. Интерфейс `LocaleService`: `resolve(key, params?) => string` (+ опциональный `render(key, params?) => ReactNode` для markdown/JSX, + `keys` для validate-проверки). Фабрики без зависимостей: `createLocaleResolver(catalog)` (плоский каталог), `createLocaleService(table)` (`ключ → (params) => строка` — параметры/склонение без ICU). ICU (`intl-messageformat`) и markdown подключает потребитель своей реализацией сервиса. Полный гид — [08-i18n.md](08-i18n.md).
|
|
257
280
|
- **`FIELD_WRAPPER`** — зарезервированное имя (`'$fieldWrapper'`) для контейнера-обёртки полей (label, error, hint). Обычно регистрируется как `FormField` из `@reformer/ui-kit`.
|
|
258
281
|
|
|
259
282
|
## 13. Builder API
|
|
@@ -262,6 +285,8 @@ const schema: JsonFormSchema = {
|
|
|
262
285
|
| -------------------------------- | ----------------------------------------------------- |
|
|
263
286
|
| `reg.component(name, Component)` | Регистрирует компонент (лист или контейнер — роль решает структура узла). |
|
|
264
287
|
| `reg.dataSource(name, value)` | Регистрирует dataSource-значение (константу или функцию). |
|
|
288
|
+
| `reg.fn(name, fn)` | Регистрирует функцию для `$fn(name)` (бросает на не-функцию). |
|
|
289
|
+
| `reg.locale(serviceOrResolver)` | Регистрирует единственный сервис локализации для `$locale(key)`. |
|
|
265
290
|
|
|
266
291
|
## 14. Examples
|
|
267
292
|
|
|
@@ -298,6 +323,38 @@ const registry = defineRegistry((reg) => {
|
|
|
298
323
|
}
|
|
299
324
|
```
|
|
300
325
|
|
|
326
|
+
Функции (`$fn`) и сервис локализации (`$locale`):
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { defineRegistry, createLocaleResolver } from '@reformer/renderer-json';
|
|
330
|
+
|
|
331
|
+
const registry = defineRegistry((reg) => {
|
|
332
|
+
reg.component('Input', Input);
|
|
333
|
+
// функции — форматтеры, компараторы, itemLabel
|
|
334
|
+
reg.fn('propertyItemLabel', (_control, index) => `Имущество #${index + 1}`);
|
|
335
|
+
reg.fn('formatCurrency', (v: number) => `${v} ₽`);
|
|
336
|
+
// единственный сервис локализации; каталог включает validate-time проверку ключей
|
|
337
|
+
reg.locale(
|
|
338
|
+
createLocaleResolver({
|
|
339
|
+
'fields.email.label': 'Email',
|
|
340
|
+
'fields.email.placeholder': 'you@example.com',
|
|
341
|
+
})
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// В JSON-схеме:
|
|
346
|
+
{
|
|
347
|
+
value: '$model(email)',
|
|
348
|
+
component: '$component(Input)',
|
|
349
|
+
componentProps: {
|
|
350
|
+
label: '$locale(fields.email.label)', // → строка
|
|
351
|
+
format: '$fn(formatCurrency)', // → функция по ссылке
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Смена языка — пересобрать сервис на другом каталоге и передать новый ref в `reg.locale` (плюс пересборка дерева); `$locale` резолвится в строку при конвертации, «живого» переключения нет.
|
|
357
|
+
|
|
301
358
|
## 15. Anti-patterns
|
|
302
359
|
|
|
303
360
|
- **Забыть зарегистрировать `FIELD_WRAPPER`** — поля будут рендериться без обёртки (нет label/error). В большинстве случаев это ошибка.
|
|
@@ -305,11 +362,13 @@ const registry = defineRegistry((reg) => {
|
|
|
305
362
|
- **Ожидать наследования между вложенными `JsonRendererProvider`** — реестр сливается через `withParent`, дубли решаются по приоритету (внешний > внутренний).
|
|
306
363
|
- **Использовать `$dataSource(NAME)` без регистрации** — без `validate` строка просто прокинется в проп как есть (молчаливый баг); с `validate` даст ошибку `unknown dataSource "NAME"`.
|
|
307
364
|
- **Ссылаться на dataSource как на компонент** — `component: '$component(LoadingState)'`, где `LoadingState` зарегистрирован через `reg.dataSource`, бросит `Entry "..." is a 'dataSource' and cannot be used as $component(...)`. dataSource — только для значений в `componentProps`.
|
|
365
|
+
- **Регистрировать функцию как `dataSource` и ссылаться `$fn`** (или наоборот) — виды раздельны: `$fn(name)` резолвит только `reg.fn`-записи, `$dataSource(NAME)` — только `reg.dataSource`. Перекрёстная ссылка бросит `Entry "..." is a '...' and cannot be used as $fn(...)` и отклонится на `validate`.
|
|
366
|
+
- **Регистрировать несколько сервисов локализации** — сервис один (кладётся под `LOCALE_SERVICE`); повторный `reg.locale(...)` перезапишет предыдущий. Разные языки — это разные каталоги, передаваемые в `reg.locale` по одному за раз.
|
|
308
367
|
|
|
309
368
|
## 16. See also
|
|
310
369
|
|
|
311
370
|
- [01-overview.md](01-overview.md) — как реестр прокидывается через `JsonRendererProvider`.
|
|
312
|
-
- [02-json-schema.md](02-json-schema.md) — где имена
|
|
371
|
+
- [02-json-schema.md](02-json-schema.md) — где имена появляются в схеме (операторы `$component`/`$dataSource`/`$fn`/`$locale`).
|
|
313
372
|
|
|
314
373
|
## 17. Component "X" not found in registry
|
|
315
374
|
|
|
@@ -955,7 +1014,150 @@ Ground truth: golden `complex-multy-step-form-renderer/render-behavior.ts` — `
|
|
|
955
1014
|
- renderer-react [03-render-behavior.md](../../../reformer-renderer-react/docs/llms/03-render-behavior.md) — семантика `hideWhen`/`renderEffect`/`onComponentEvent`/`onInit`.
|
|
956
1015
|
- renderer-react [01-overview.md](../../../reformer-renderer-react/docs/llms/01-overview.md#multi-step-forms) — канонический `FormWizard`, форма шага `{ number, title, icon, body }`.
|
|
957
1016
|
|
|
958
|
-
## 47.
|
|
1017
|
+
## 47. Два пути (важно выбрать правильный)
|
|
1018
|
+
|
|
1019
|
+
Значение попадает в один из двух приёмников, и это определяет путь:
|
|
1020
|
+
|
|
1021
|
+
| Приёмник | Что можно | Путь |
|
|
1022
|
+
|---|---|---|
|
|
1023
|
+
| **Строковый проп** (`label`, `placeholder`, `title`, `aria-*`) | только `string`, **статично** | `$locale(key)` / `{ $locale, params }` |
|
|
1024
|
+
| **Контент** (видимый текст, сообщения) | `string` **или** rich/JSX, **реактивно** | компонент `$component(I18n)` |
|
|
1025
|
+
|
|
1026
|
+
Причина: `componentProps` спредятся в компонент как есть — signal/ReactNode в `label` уронит его (`Objects are not valid as a React child`). Строковый путь резолвится один раз при конвертации (смена языка = пересборка дерева); реактивный путь читает сервис из React-контекста на этапе рендера (live-переключение).
|
|
1027
|
+
|
|
1028
|
+
## 48. Сервис локализации
|
|
1029
|
+
|
|
1030
|
+
```ts
|
|
1031
|
+
interface LocaleService {
|
|
1032
|
+
resolve(key: string, params?: Record<string, unknown>): string; // строка (обязателен)
|
|
1033
|
+
render?(key: string, params?: Record<string, unknown>): ReactNode; // rich/markdown (опционален)
|
|
1034
|
+
keys?: readonly string[]; // validate-time проверка ключей
|
|
1035
|
+
}
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
Фабрики (0-dependency):
|
|
1039
|
+
|
|
1040
|
+
```ts
|
|
1041
|
+
import { createLocaleResolver, createLocaleService } from '@reformer/renderer-json';
|
|
1042
|
+
|
|
1043
|
+
// плоский каталог ключ→строка
|
|
1044
|
+
createLocaleResolver({ 'fields.email.label': 'Email' });
|
|
1045
|
+
|
|
1046
|
+
// таблица ключ→(params)=>строка — параметры и склонение без ICU (стиль cdk createMessageResolver)
|
|
1047
|
+
createLocaleService({
|
|
1048
|
+
'fields.min': (p) => `Минимум ${p?.count} символов`,
|
|
1049
|
+
'users.count': (p) => plural(Number(p?.count), ['пользователь', 'пользователя', 'пользователей']),
|
|
1050
|
+
});
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
### ICU MessageFormat (подключает потребитель)
|
|
1054
|
+
|
|
1055
|
+
Стандарт для склонения по CLDR-правилам. Библиотека — `intl-messageformat`; сервис пишешь сам:
|
|
1056
|
+
|
|
1057
|
+
```ts
|
|
1058
|
+
import { IntlMessageFormat } from 'intl-messageformat';
|
|
1059
|
+
|
|
1060
|
+
function createIcuLocaleService(catalog: Record<string, string>, locale: string): LocaleService {
|
|
1061
|
+
return {
|
|
1062
|
+
resolve: (key, params) =>
|
|
1063
|
+
key in catalog ? (new IntlMessageFormat(catalog[key], locale).format(params) as string) : key,
|
|
1064
|
+
keys: Object.keys(catalog),
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
// catalog: { 'users.count': 'There {count, plural, one {is # user} other {are # users}}' }
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
### Markdown / rich (подключает потребитель через `render`)
|
|
1071
|
+
|
|
1072
|
+
```ts
|
|
1073
|
+
import ReactMarkdown from 'react-markdown';
|
|
1074
|
+
|
|
1075
|
+
const service: LocaleService = {
|
|
1076
|
+
resolve: (k) => catalog[k] ?? k,
|
|
1077
|
+
render: (k, p) => <ReactMarkdown>{format(catalog[k] ?? k, p)}</ReactMarkdown>,
|
|
1078
|
+
keys: Object.keys(catalog),
|
|
1079
|
+
};
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
## 49. Путь 1 — строковые пропы (статично)
|
|
1083
|
+
|
|
1084
|
+
Без параметров — строковый оператор:
|
|
1085
|
+
|
|
1086
|
+
```jsonc
|
|
1087
|
+
{ "value": "$model(email)", "component": "$component(Input)",
|
|
1088
|
+
"componentProps": { "label": "$locale(fields.email.label)" } }
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
С параметрами — **структурная форма** (объект, не оператор — без парсинга аргументов):
|
|
1092
|
+
|
|
1093
|
+
```jsonc
|
|
1094
|
+
{ "value": "$model(password)", "component": "$component(Input)",
|
|
1095
|
+
"componentProps": { "label": { "$locale": "fields.min", "params": { "count": 8 } } } }
|
|
1096
|
+
// → resolve("fields.min", { count: 8 }) → "Минимум 8 символов"
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
Параметры — обычно **литералы** (путь статичный). Можно сослаться и на `$model(path)` в `params` — тогда берётся **снимок** текущего значения (`.peek()`, на момент конвертации) и печатается dev-warning: снимок не реактивен, при изменении модели строка не обновится. Для живого значения — `I18n` (путь 2).
|
|
1100
|
+
|
|
1101
|
+
Регистрация сервиса — в реестре (для конвертера):
|
|
1102
|
+
|
|
1103
|
+
```ts
|
|
1104
|
+
const registry = defineRegistry((reg) => {
|
|
1105
|
+
reg.locale(createLocaleService({ 'fields.min': (p) => `Минимум ${p?.count} символов` }));
|
|
1106
|
+
});
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
## 50. Путь 2 — реактивный компонент `I18n`
|
|
1110
|
+
|
|
1111
|
+
Для markdown/rich и/или реактивных параметров и live-переключения языка. Регистрируется как компонент, оборачивается `LocaleProvider`:
|
|
1112
|
+
|
|
1113
|
+
```tsx
|
|
1114
|
+
import { I18n, LocaleProvider, createLocaleService, JsonFormRenderer } from '@reformer/renderer-json';
|
|
1115
|
+
|
|
1116
|
+
const services = { ru: createLocaleService(ruTable), en: createLocaleService(enTable) };
|
|
1117
|
+
|
|
1118
|
+
function App() {
|
|
1119
|
+
const [lang, setLang] = useState<'ru' | 'en'>('ru');
|
|
1120
|
+
const registry = useMemo(() => defineRegistry((reg) => {
|
|
1121
|
+
reg.component('I18n', I18n);
|
|
1122
|
+
// ...остальные компоненты
|
|
1123
|
+
}), []);
|
|
1124
|
+
return (
|
|
1125
|
+
<LocaleProvider service={services[lang]}>
|
|
1126
|
+
<JsonRendererProvider settings={{ registry, model }}>
|
|
1127
|
+
<JsonFormRenderer schema={schema} />
|
|
1128
|
+
</JsonRendererProvider>
|
|
1129
|
+
</LocaleProvider>
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
```jsonc
|
|
1135
|
+
// в схеме — контейнер-узел; values.count приходит сигналом и обновляется вживую
|
|
1136
|
+
{ "component": "$component(I18n)",
|
|
1137
|
+
"componentProps": { "id": "users.count", "values": { "count": "$model(userCount)" } } }
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
`I18n` рендерит `service.render?.()` (markdown/JSX) либо `service.resolve()` (строка) — формат решает сервис. Перерисовывается при смене сигнала `values.count` и при смене `service` в `LocaleProvider` (переключение языка).
|
|
1141
|
+
|
|
1142
|
+
## 51. Валидация
|
|
1143
|
+
|
|
1144
|
+
`validateFormSchema` проверяет ключи `$locale(...)` и структурной формы `{ $locale }` против `localeKeys` (из `service.keys` или явного списка) — опечатка ключа ловится до рендера. Нет каталога → проверка мягко пропускается (ключи свободные).
|
|
1145
|
+
|
|
1146
|
+
## 52. Anti-patterns
|
|
1147
|
+
|
|
1148
|
+
- **markdown/JSX в строковый проп** — `label: "$locale(...)"` с сервисом, возвращающим ReactNode, уронит компонент. Rich-контент только через `$component(I18n)` (или свой `$component(RichText)`).
|
|
1149
|
+
- **реактивный параметр в строковом `$locale`** — `{ $locale, params: { count: "$model(...)" } }` берётся снимком и **не обновляется** (путь статичный); в dev печатается warning. Для реактивных model-параметров → `I18n` с `values`.
|
|
1150
|
+
- **`I18n` без `LocaleProvider`** — без провайдера сервис по умолчанию отдаёт сам ключ (fallback-to-key). Оберни поддерево в `LocaleProvider`.
|
|
1151
|
+
- **ждать live-переключения от `$locale`** — строковый путь статичен; смена языка для label/placeholder требует пересборки (новый сервис в `reg.locale` + пере-render). Live только у `I18n`.
|
|
1152
|
+
- **тащить ICU/markdown в схему** — форматтер живёт в реализации `LocaleService` у потребителя, не в JSON.
|
|
1153
|
+
|
|
1154
|
+
## 53. See also
|
|
1155
|
+
|
|
1156
|
+
- [02-json-schema.md](02-json-schema.md) — где `$locale`/`I18n` появляются в схеме.
|
|
1157
|
+
- [03-registry.md](03-registry.md) — `reg.locale`, `createLocaleService`, регистрация `I18n`.
|
|
1158
|
+
- [06-validation.md](06-validation.md) — валидация значений (отдельно от локализации).
|
|
1159
|
+
|
|
1160
|
+
## 54. API Reference
|
|
959
1161
|
|
|
960
1162
|
_Auto-generated from JSDoc on public exports._
|
|
961
1163
|
|
|
@@ -990,13 +1192,13 @@ _Source: src/schema/index.ts_
|
|
|
990
1192
|
|
|
991
1193
|
**Kind:** `interface`
|
|
992
1194
|
|
|
993
|
-
Запись реестра. Хранит
|
|
1195
|
+
Запись реестра. Хранит значение (компонент / dataSource / функцию / сервис локализации) и его роль.
|
|
994
1196
|
|
|
995
1197
|
**Signature:**
|
|
996
1198
|
```typescript
|
|
997
1199
|
export interface ComponentMetadata<P = any> {
|
|
998
1200
|
component: ComponentType<P> | unknown;
|
|
999
|
-
type: 'component' | 'dataSource';
|
|
1201
|
+
type: 'component' | 'dataSource' | 'fn' | 'locale';
|
|
1000
1202
|
description?: string;
|
|
1001
1203
|
}
|
|
1002
1204
|
```
|
|
@@ -1037,6 +1239,12 @@ Read-only API реестра, доступное в рантайме.
|
|
|
1037
1239
|
export interface ComponentRegistry {
|
|
1038
1240
|
get(name: string): ComponentMetadata | undefined;
|
|
1039
1241
|
getDataSource<T = unknown>(name: string): T | undefined;
|
|
1242
|
+
/**
|
|
1243
|
+
* Единственный сервис локализации (для оператора `$locale(key)`), если зарегистрирован через
|
|
1244
|
+
* `reg.locale(...)`. Опционален — сторонние реализации `ComponentRegistry` без него продолжают
|
|
1245
|
+
* компилироваться; при его отсутствии `$locale` мягко деградирует до самого ключа.
|
|
1246
|
+
*/
|
|
1247
|
+
getLocale?(): LocaleService | undefined;
|
|
1040
1248
|
has(name: string): boolean;
|
|
1041
1249
|
names(): string[];
|
|
1042
1250
|
}
|
|
@@ -1091,6 +1299,85 @@ behavior,
|
|
|
1091
1299
|
|
|
1092
1300
|
_Source: src/converter/json-to-render-schema.ts_
|
|
1093
1301
|
|
|
1302
|
+
### createLocaleResolver
|
|
1303
|
+
|
|
1304
|
+
**Kind:** `function`
|
|
1305
|
+
|
|
1306
|
+
Строит {@link LocaleService} из каталога `ключ → строка`. Промах ключа → сам ключ (fallback-to-key,
|
|
1307
|
+
зеркало `createMessageResolver` из `@reformer/cdk`). `keys` каталога включает validate-time проверку
|
|
1308
|
+
опечаток. Смена языка — пересобрать сервис на другом каталоге и передать новый ref в `reg.locale`.
|
|
1309
|
+
|
|
1310
|
+
**Signature:**
|
|
1311
|
+
```typescript
|
|
1312
|
+
export function createLocaleResolver(catalog: Record<string, string>): LocaleService
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
**Parameters:**
|
|
1316
|
+
- `catalog` — - Таблица `ключ → локализованная строка`.
|
|
1317
|
+
|
|
1318
|
+
**Returns:**
|
|
1319
|
+
|
|
1320
|
+
**Examples:**
|
|
1321
|
+
|
|
1322
|
+
```ts
|
|
1323
|
+
import { defineRegistry } from '@reformer/renderer-json';
|
|
1324
|
+
import { createLocaleResolver } from '@reformer/renderer-json';
|
|
1325
|
+
|
|
1326
|
+
const registry = defineRegistry((reg) => {
|
|
1327
|
+
reg.locale(createLocaleResolver({
|
|
1328
|
+
'fields.email.label': 'Email',
|
|
1329
|
+
'fields.email.placeholder': 'you@example.com',
|
|
1330
|
+
}));
|
|
1331
|
+
});
|
|
1332
|
+
// В схеме: componentProps: { label: '$locale(fields.email.label)' }
|
|
1333
|
+
// '$locale(fields.unknown)' при наличии каталога → ошибка на validateFormSchema.
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
_Source: src/locale/locale-service.ts_
|
|
1337
|
+
|
|
1338
|
+
### createLocaleService
|
|
1339
|
+
|
|
1340
|
+
**Kind:** `function`
|
|
1341
|
+
|
|
1342
|
+
Строит {@link LocaleService} из таблицы `ключ → (params) => строка` — 0-dependency вариант для
|
|
1343
|
+
параметров и склонения без ICU (стиль `createMessageResolver` из `@reformer/cdk`). Промах ключа →
|
|
1344
|
+
сам ключ. `keys` таблицы включает validate-time проверку опечаток.
|
|
1345
|
+
|
|
1346
|
+
Для ICU-склонения по CLDR-правилам подключи `intl-messageformat` в СВОЁМ сервисе:
|
|
1347
|
+
`reg.locale({ resolve: (k, p) => new IntlMessageFormat(catalog[k], lng).format(p) as string, keys })`.
|
|
1348
|
+
Для markdown добавь `render` (напр. через `react-markdown`).
|
|
1349
|
+
|
|
1350
|
+
**Signature:**
|
|
1351
|
+
```typescript
|
|
1352
|
+
export function createLocaleService(
|
|
1353
|
+
table: Record<string, (params?: LocaleParams) => string>
|
|
1354
|
+
): LocaleService
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
**Parameters:**
|
|
1358
|
+
- `table` — - Таблица `ключ → (params) => локализованная строка`.
|
|
1359
|
+
|
|
1360
|
+
**Returns:**
|
|
1361
|
+
|
|
1362
|
+
**Examples:**
|
|
1363
|
+
|
|
1364
|
+
```ts
|
|
1365
|
+
import { createLocaleService } from '@reformer/renderer-json';
|
|
1366
|
+
|
|
1367
|
+
const ru = createLocaleService({
|
|
1368
|
+
'fields.min': (p) => `Минимум ${p?.count} символов`,
|
|
1369
|
+
'users.count': (p) => {
|
|
1370
|
+
const n = Number(p?.count);
|
|
1371
|
+
const forms = ['пользователь', 'пользователя', 'пользователей'];
|
|
1372
|
+
const idx = n % 10 === 1 && n % 100 !== 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
|
|
1373
|
+
return `${n} ${forms[idx]}`;
|
|
1374
|
+
},
|
|
1375
|
+
});
|
|
1376
|
+
ru.resolve('fields.min', { count: 3 }); // 'Минимум 3 символов'
|
|
1377
|
+
```
|
|
1378
|
+
|
|
1379
|
+
_Source: src/locale/locale-service.ts_
|
|
1380
|
+
|
|
1094
1381
|
### createRenderSchemaFromJsonM1
|
|
1095
1382
|
|
|
1096
1383
|
**Kind:** `function`
|
|
@@ -1143,6 +1430,19 @@ export type DataSourceOp = `$dataSource(${string})`;
|
|
|
1143
1430
|
|
|
1144
1431
|
_Source: src/operators.ts_
|
|
1145
1432
|
|
|
1433
|
+
### defaultLocaleResolver
|
|
1434
|
+
|
|
1435
|
+
**Kind:** `const`
|
|
1436
|
+
|
|
1437
|
+
Дефолтный резолвер: отдаёт сам ключ (fallback-to-key). Применяется, когда сервис не зарегистрирован.
|
|
1438
|
+
|
|
1439
|
+
**Signature:**
|
|
1440
|
+
```typescript
|
|
1441
|
+
export const defaultLocaleResolver: LocaleResolver
|
|
1442
|
+
```
|
|
1443
|
+
|
|
1444
|
+
_Source: src/locale/locale-service.ts_
|
|
1445
|
+
|
|
1146
1446
|
### defineRegistry
|
|
1147
1447
|
|
|
1148
1448
|
**Kind:** `function`
|
|
@@ -1211,6 +1511,19 @@ const registry = defineRegistry((reg) => {
|
|
|
1211
1511
|
|
|
1212
1512
|
_Source: src/registry/constants.ts_
|
|
1213
1513
|
|
|
1514
|
+
### FnOp
|
|
1515
|
+
|
|
1516
|
+
**Kind:** `type`
|
|
1517
|
+
|
|
1518
|
+
Строка-оператор ссылки на функцию реестра: `` `$fn(${name})` ``.
|
|
1519
|
+
|
|
1520
|
+
**Signature:**
|
|
1521
|
+
```typescript
|
|
1522
|
+
export type FnOp = `$fn(${string})`;
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
_Source: src/operators.ts_
|
|
1526
|
+
|
|
1214
1527
|
### formSchemaMetaSchema
|
|
1215
1528
|
|
|
1216
1529
|
**Kind:** `const`
|
|
@@ -1285,6 +1598,87 @@ getDataSourceNames(registry); // ['LOAN_TYPES', 'GENDERS', 'LoadingState', ...]
|
|
|
1285
1598
|
|
|
1286
1599
|
_Source: src/schema/index.ts_
|
|
1287
1600
|
|
|
1601
|
+
### getFnNames
|
|
1602
|
+
|
|
1603
|
+
**Kind:** `function`
|
|
1604
|
+
|
|
1605
|
+
Имена функций реестра (`reg.fn`) — то, что валидно в `$fn(...)`: форматтеры/компараторы/itemLabel/
|
|
1606
|
+
обработчики. Отдельно от {@link getDataSourceNames}, поэтому `validateFormSchema` ловит перепутанные
|
|
1607
|
+
`$fn`/`$dataSource`.
|
|
1608
|
+
|
|
1609
|
+
**Signature:**
|
|
1610
|
+
```typescript
|
|
1611
|
+
export function getFnNames(registry: ComponentRegistry): string[]
|
|
1612
|
+
```
|
|
1613
|
+
|
|
1614
|
+
**Parameters:**
|
|
1615
|
+
- `registry` — - Реестр (см. {@link defineRegistry}).
|
|
1616
|
+
|
|
1617
|
+
**Returns:** Массив имён функций.
|
|
1618
|
+
|
|
1619
|
+
_Source: src/schema/index.ts_
|
|
1620
|
+
|
|
1621
|
+
### getLocaleKeys
|
|
1622
|
+
|
|
1623
|
+
**Kind:** `function`
|
|
1624
|
+
|
|
1625
|
+
Известные ключи локализации сервиса реестра (`reg.locale` с каталогом) — то, что валидно в
|
|
1626
|
+
`$locale(...)`. `undefined`, если сервис не зарегистрирован или задан голым резолвером без `keys`
|
|
1627
|
+
(тогда `validateFormSchema` мягко пропускает проверку ключей, как для `$model`-путей).
|
|
1628
|
+
|
|
1629
|
+
**Signature:**
|
|
1630
|
+
```typescript
|
|
1631
|
+
export function getLocaleKeys(registry: ComponentRegistry): readonly string[] | undefined
|
|
1632
|
+
```
|
|
1633
|
+
|
|
1634
|
+
**Parameters:**
|
|
1635
|
+
- `registry` — - Реестр (см. {@link defineRegistry}).
|
|
1636
|
+
|
|
1637
|
+
**Returns:** Массив ключей либо `undefined`.
|
|
1638
|
+
|
|
1639
|
+
_Source: src/schema/index.ts_
|
|
1640
|
+
|
|
1641
|
+
### I18n
|
|
1642
|
+
|
|
1643
|
+
**Kind:** `function`
|
|
1644
|
+
|
|
1645
|
+
Реактивный локализованный текст. Читает {@link LocaleService} из {@link LocaleProvider} (live-смена
|
|
1646
|
+
языка) и разворачивает `values`-сигналы (live-обновление параметров). Если сервис умеет `render` —
|
|
1647
|
+
рендерит rich-контент (markdown/JSX), иначе строку через `resolve`.
|
|
1648
|
+
|
|
1649
|
+
**Signature:**
|
|
1650
|
+
```typescript
|
|
1651
|
+
export function I18n({ id, values }: I18nProps): ReactNode
|
|
1652
|
+
```
|
|
1653
|
+
|
|
1654
|
+
**Parameters:**
|
|
1655
|
+
- `props` — - {@link I18nProps} (`id` + опциональные `values`).
|
|
1656
|
+
|
|
1657
|
+
**Returns:** Локализованный `ReactNode`.
|
|
1658
|
+
|
|
1659
|
+
_Source: src/locale/i18n.tsx_
|
|
1660
|
+
|
|
1661
|
+
### I18nProps
|
|
1662
|
+
|
|
1663
|
+
**Kind:** `interface`
|
|
1664
|
+
|
|
1665
|
+
Props {@link I18n}.
|
|
1666
|
+
|
|
1667
|
+
**Signature:**
|
|
1668
|
+
```typescript
|
|
1669
|
+
export interface I18nProps {
|
|
1670
|
+
/** Ключ сообщения в сервисе локализации. */
|
|
1671
|
+
id: string;
|
|
1672
|
+
/**
|
|
1673
|
+
* Параметры интерполяции/склонения. Значения могут быть сигналами (`$model(...)`) — тогда текст
|
|
1674
|
+
* обновляется при их изменении — или литералами.
|
|
1675
|
+
*/
|
|
1676
|
+
values?: LocaleParams;
|
|
1677
|
+
}
|
|
1678
|
+
```
|
|
1679
|
+
|
|
1680
|
+
_Source: src/locale/i18n.tsx_
|
|
1681
|
+
|
|
1288
1682
|
### isArrayNode
|
|
1289
1683
|
|
|
1290
1684
|
**Kind:** `function`
|
|
@@ -1422,6 +1816,58 @@ node.component; // ComponentOp | undefined
|
|
|
1422
1816
|
|
|
1423
1817
|
_Source: src/types/json-schema.ts_
|
|
1424
1818
|
|
|
1819
|
+
### isFnOp
|
|
1820
|
+
|
|
1821
|
+
**Kind:** `const`
|
|
1822
|
+
|
|
1823
|
+
Type-guard: строка — оператор `"$fn(...)"` (ссылка на функцию реестра).
|
|
1824
|
+
|
|
1825
|
+
**Signature:**
|
|
1826
|
+
```typescript
|
|
1827
|
+
export const isFnOp
|
|
1828
|
+
```
|
|
1829
|
+
|
|
1830
|
+
**Parameters:**
|
|
1831
|
+
- `v` — - Проверяемое значение.
|
|
1832
|
+
|
|
1833
|
+
**Returns:** `true`, если `v` — {@link FnOp}.
|
|
1834
|
+
|
|
1835
|
+
**Examples:**
|
|
1836
|
+
|
|
1837
|
+
```ts
|
|
1838
|
+
if (isFnOp(props.itemLabel)) {
|
|
1839
|
+
const name = parseOperator(props.itemLabel)!.arg; // 'propertyItemLabel'
|
|
1840
|
+
}
|
|
1841
|
+
```
|
|
1842
|
+
|
|
1843
|
+
_Source: src/operators.ts_
|
|
1844
|
+
|
|
1845
|
+
### isLocaleOp
|
|
1846
|
+
|
|
1847
|
+
**Kind:** `const`
|
|
1848
|
+
|
|
1849
|
+
Type-guard: строка — оператор `"$locale(...)"` (ключ строки для сервиса локализации).
|
|
1850
|
+
|
|
1851
|
+
**Signature:**
|
|
1852
|
+
```typescript
|
|
1853
|
+
export const isLocaleOp
|
|
1854
|
+
```
|
|
1855
|
+
|
|
1856
|
+
**Parameters:**
|
|
1857
|
+
- `v` — - Проверяемое значение.
|
|
1858
|
+
|
|
1859
|
+
**Returns:** `true`, если `v` — {@link LocaleOp}.
|
|
1860
|
+
|
|
1861
|
+
**Examples:**
|
|
1862
|
+
|
|
1863
|
+
```ts
|
|
1864
|
+
if (isLocaleOp(props.label)) {
|
|
1865
|
+
const key = parseOperator(props.label)!.arg; // 'fields.email.label'
|
|
1866
|
+
}
|
|
1867
|
+
```
|
|
1868
|
+
|
|
1869
|
+
_Source: src/operators.ts_
|
|
1870
|
+
|
|
1425
1871
|
### isModelOp
|
|
1426
1872
|
|
|
1427
1873
|
**Kind:** `const`
|
|
@@ -1686,7 +2132,7 @@ _Source: src/types/json-schema.ts_
|
|
|
1686
2132
|
|
|
1687
2133
|
**Signature:**
|
|
1688
2134
|
```typescript
|
|
1689
|
-
export type JsonOperator = ModelOp | ComponentOp | DataSourceOp;
|
|
2135
|
+
export type JsonOperator = ModelOp | ComponentOp | DataSourceOp | FnOp | LocaleOp;
|
|
1690
2136
|
```
|
|
1691
2137
|
|
|
1692
2138
|
_Source: src/operators.ts_
|
|
@@ -1764,6 +2210,146 @@ export interface JsonRendererSettings extends RendererSettings {
|
|
|
1764
2210
|
|
|
1765
2211
|
_Source: src/context/json-renderer-context.tsx_
|
|
1766
2212
|
|
|
2213
|
+
### LOCALE_SERVICE
|
|
2214
|
+
|
|
2215
|
+
**Kind:** `const`
|
|
2216
|
+
|
|
2217
|
+
Зарезервированный ключ реестра для единственного сервиса локализации.
|
|
2218
|
+
|
|
2219
|
+
`reg.locale(...)` кладёт сервис под этот ключ; оператор `"$locale(key)"` резолвится через него
|
|
2220
|
+
(`registry.getLocale()`). Ключ намеренно отличается от имени оператора, чтобы `$dataSource`/`$fn`
|
|
2221
|
+
не могли достать сервис локализации по имени.
|
|
2222
|
+
|
|
2223
|
+
**Signature:**
|
|
2224
|
+
```typescript
|
|
2225
|
+
export const LOCALE_SERVICE
|
|
2226
|
+
```
|
|
2227
|
+
|
|
2228
|
+
**Examples:**
|
|
2229
|
+
|
|
2230
|
+
```typescript
|
|
2231
|
+
import { defineRegistry, createLocaleResolver } from '@reformer/renderer-json';
|
|
2232
|
+
|
|
2233
|
+
const registry = defineRegistry((reg) => {
|
|
2234
|
+
reg.locale(createLocaleResolver({ 'fields.email.label': 'Email' }));
|
|
2235
|
+
});
|
|
2236
|
+
```
|
|
2237
|
+
|
|
2238
|
+
_Source: src/registry/constants.ts_
|
|
2239
|
+
|
|
2240
|
+
### LocaleOp
|
|
2241
|
+
|
|
2242
|
+
**Kind:** `type`
|
|
2243
|
+
|
|
2244
|
+
Строка-оператор ссылки на ключ локализации: `` `$locale(${key})` ``.
|
|
2245
|
+
|
|
2246
|
+
**Signature:**
|
|
2247
|
+
```typescript
|
|
2248
|
+
export type LocaleOp = `$locale(${string})`;
|
|
2249
|
+
```
|
|
2250
|
+
|
|
2251
|
+
_Source: src/operators.ts_
|
|
2252
|
+
|
|
2253
|
+
### LocaleParams
|
|
2254
|
+
|
|
2255
|
+
**Kind:** `type`
|
|
2256
|
+
|
|
2257
|
+
Параметры для интерполяции/склонения (ICU-values, i18next-values и т.п.).
|
|
2258
|
+
|
|
2259
|
+
**Signature:**
|
|
2260
|
+
```typescript
|
|
2261
|
+
export type LocaleParams = Record<string, unknown>;
|
|
2262
|
+
```
|
|
2263
|
+
|
|
2264
|
+
_Source: src/locale/locale-service.ts_
|
|
2265
|
+
|
|
2266
|
+
### LocaleProvider
|
|
2267
|
+
|
|
2268
|
+
**Kind:** `function`
|
|
2269
|
+
|
|
2270
|
+
Провайдер сервиса локализации для реактивного пути. Оберни форму (или её часть) и передавай
|
|
2271
|
+
новый `service` при смене языка — вложенные `I18n` перерисуются без пересборки схемы.
|
|
2272
|
+
|
|
2273
|
+
**Signature:**
|
|
2274
|
+
```typescript
|
|
2275
|
+
export function LocaleProvider({ service, children }: LocaleProviderProps): ReactNode
|
|
2276
|
+
```
|
|
2277
|
+
|
|
2278
|
+
**Examples:**
|
|
2279
|
+
|
|
2280
|
+
Переключение языка
|
|
2281
|
+
```tsx
|
|
2282
|
+
import { LocaleProvider, createLocaleService, JsonFormRenderer } from '@reformer/renderer-json';
|
|
2283
|
+
|
|
2284
|
+
const services = { ru: createLocaleService(ruTable), en: createLocaleService(enTable) };
|
|
2285
|
+
function App() {
|
|
2286
|
+
const [lang, setLang] = useState<'ru' | 'en'>('ru');
|
|
2287
|
+
return (
|
|
2288
|
+
<LocaleProvider service={services[lang]}>
|
|
2289
|
+
<JsonFormRenderer schema={schema} />
|
|
2290
|
+
</LocaleProvider>
|
|
2291
|
+
);
|
|
2292
|
+
}
|
|
2293
|
+
```
|
|
2294
|
+
|
|
2295
|
+
_Source: src/locale/locale-context.tsx_
|
|
2296
|
+
|
|
2297
|
+
### LocaleProviderProps
|
|
2298
|
+
|
|
2299
|
+
**Kind:** `interface`
|
|
2300
|
+
|
|
2301
|
+
Props {@link LocaleProvider}.
|
|
2302
|
+
|
|
2303
|
+
**Signature:**
|
|
2304
|
+
```typescript
|
|
2305
|
+
export interface LocaleProviderProps {
|
|
2306
|
+
/** Текущий сервис локализации. Смена ссылки (напр. при переключении языка) перерендеривает все `I18n`. */
|
|
2307
|
+
service: LocaleService;
|
|
2308
|
+
/** Поддерево, в котором `I18n`/`useLocale` видят этот сервис. */
|
|
2309
|
+
children: ReactNode;
|
|
2310
|
+
}
|
|
2311
|
+
```
|
|
2312
|
+
|
|
2313
|
+
_Source: src/locale/locale-context.tsx_
|
|
2314
|
+
|
|
2315
|
+
### LocaleResolver
|
|
2316
|
+
|
|
2317
|
+
**Kind:** `type`
|
|
2318
|
+
|
|
2319
|
+
Резолвер ключа (+опциональные параметры) в строку.
|
|
2320
|
+
|
|
2321
|
+
**Signature:**
|
|
2322
|
+
```typescript
|
|
2323
|
+
export type LocaleResolver = (key: string, params?: LocaleParams) => string;
|
|
2324
|
+
```
|
|
2325
|
+
|
|
2326
|
+
_Source: src/locale/locale-service.ts_
|
|
2327
|
+
|
|
2328
|
+
### LocaleService
|
|
2329
|
+
|
|
2330
|
+
**Kind:** `interface`
|
|
2331
|
+
|
|
2332
|
+
Сервис локализации в реестре / контексте. `resolve` обязателен (строковый путь); `render`
|
|
2333
|
+
опционален (rich/markdown-путь через `Trans`/`RichText`); `keys` (если задан — напр., из каталога)
|
|
2334
|
+
включает проверку опечаток ключей на этапе `validateFormSchema`.
|
|
2335
|
+
|
|
2336
|
+
**Signature:**
|
|
2337
|
+
```typescript
|
|
2338
|
+
export interface LocaleService {
|
|
2339
|
+
/** Ключ (+params) → локализованная строка (при промахе — сам ключ). Для label/placeholder и fallback `Trans`. */
|
|
2340
|
+
resolve: LocaleResolver;
|
|
2341
|
+
/**
|
|
2342
|
+
* Ключ (+params) → rich-контент (markdown/JSX). Опционален. Используется `Trans`/`RichText`, когда
|
|
2343
|
+
* нужен не голый текст, а разметка. Реализуется потребителем (напр. через `react-markdown`).
|
|
2344
|
+
*/
|
|
2345
|
+
render?(key: string, params?: LocaleParams): ReactNode;
|
|
2346
|
+
/** Множество известных ключей. Есть → `validate` ловит `unknown locale key`; нет → проверка мягко пропускается (как для `$model`). */
|
|
2347
|
+
keys?: readonly string[];
|
|
2348
|
+
}
|
|
2349
|
+
```
|
|
2350
|
+
|
|
2351
|
+
_Source: src/locale/locale-service.ts_
|
|
2352
|
+
|
|
1767
2353
|
### ModelOp
|
|
1768
2354
|
|
|
1769
2355
|
**Kind:** `type`
|
|
@@ -1781,12 +2367,12 @@ _Source: src/operators.ts_
|
|
|
1781
2367
|
|
|
1782
2368
|
**Kind:** `interface`
|
|
1783
2369
|
|
|
1784
|
-
Разобранный оператор: тип + аргумент (
|
|
2370
|
+
Разобранный оператор: тип + аргумент (путь/имя/ключ).
|
|
1785
2371
|
|
|
1786
2372
|
**Signature:**
|
|
1787
2373
|
```typescript
|
|
1788
2374
|
export interface ParsedOperator {
|
|
1789
|
-
op: 'model' | 'component' | 'dataSource';
|
|
2375
|
+
op: 'model' | 'component' | 'dataSource' | 'fn' | 'locale';
|
|
1790
2376
|
arg: string;
|
|
1791
2377
|
}
|
|
1792
2378
|
```
|
|
@@ -1838,6 +2424,19 @@ export interface RegistryBuilder {
|
|
|
1838
2424
|
*/
|
|
1839
2425
|
component<P>(name: string, component: ComponentType<P>, description?: string): void;
|
|
1840
2426
|
dataSource<T>(name: string, value: T, description?: string): void;
|
|
2427
|
+
/**
|
|
2428
|
+
* Зарегистрировать функцию под именем — доступна в схеме как `$fn(name)` (форматтеры,
|
|
2429
|
+
* компараторы, `itemLabel`, обработчики). Отдельный от `dataSource` вид: бросает при регистрации,
|
|
2430
|
+
* если передана не функция, и `validateFormSchema` отклоняет перепутанные `$fn`/`$dataSource`.
|
|
2431
|
+
*/
|
|
2432
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2433
|
+
fn<F extends (...args: any[]) => any>(name: string, fn: F, description?: string): void;
|
|
2434
|
+
/**
|
|
2435
|
+
* Зарегистрировать единственный сервис локализации для оператора `$locale(key)`. Принимает
|
|
2436
|
+
* {@link LocaleService} (каталог через `createLocaleResolver` — включает validate-time проверку
|
|
2437
|
+
* ключей) либо голый резолвер `(key) => string`. Кладётся под зарезервированный ключ `LOCALE_SERVICE`.
|
|
2438
|
+
*/
|
|
2439
|
+
locale(service: LocaleService | LocaleResolver, description?: string): void;
|
|
1841
2440
|
}
|
|
1842
2441
|
```
|
|
1843
2442
|
|
|
@@ -1848,6 +2447,8 @@ defineRegistry((reg: RegistryBuilder) => {
|
|
|
1848
2447
|
reg.component('Input', Input);
|
|
1849
2448
|
reg.component('Box', Box);
|
|
1850
2449
|
reg.dataSource('LOAN_TYPES', LOAN_TYPES);
|
|
2450
|
+
reg.fn('formatCurrency', formatCurrency);
|
|
2451
|
+
reg.locale(createLocaleResolver({ 'fields.email.label': 'Email' }));
|
|
1851
2452
|
});
|
|
1852
2453
|
```
|
|
1853
2454
|
|
|
@@ -1901,6 +2502,20 @@ export interface SchemaErrorPanelProps {
|
|
|
1901
2502
|
|
|
1902
2503
|
_Source: src/components/schema-error-panel.tsx_
|
|
1903
2504
|
|
|
2505
|
+
### unwrapSignalValues
|
|
2506
|
+
|
|
2507
|
+
**Kind:** `function`
|
|
2508
|
+
|
|
2509
|
+
Разворачивает значения-сигналы в обычные (читает `.value`), литералы отдаёт как есть. Если сигналов
|
|
2510
|
+
не было — возвращает исходный объект (стабильная ссылка). Pure — тестируется без DOM.
|
|
2511
|
+
|
|
2512
|
+
**Signature:**
|
|
2513
|
+
```typescript
|
|
2514
|
+
export function unwrapSignalValues(values?: LocaleParams): LocaleParams | undefined
|
|
2515
|
+
```
|
|
2516
|
+
|
|
2517
|
+
_Source: src/locale/use-signal-value.ts_
|
|
2518
|
+
|
|
1904
2519
|
### useJsonRendererSettings
|
|
1905
2520
|
|
|
1906
2521
|
**Kind:** `function`
|
|
@@ -1927,3 +2542,39 @@ function MyControl() {
|
|
|
1927
2542
|
```
|
|
1928
2543
|
|
|
1929
2544
|
_Source: src/context/json-renderer-context.tsx_
|
|
2545
|
+
|
|
2546
|
+
### useLocale
|
|
2547
|
+
|
|
2548
|
+
**Kind:** `function`
|
|
2549
|
+
|
|
2550
|
+
Текущий сервис локализации из {@link LocaleProvider}. Без провайдера — fallback-to-key.
|
|
2551
|
+
Используется внутри {@link I18n}; вызывай напрямую для собственного localized-компонента.
|
|
2552
|
+
|
|
2553
|
+
**Signature:**
|
|
2554
|
+
```typescript
|
|
2555
|
+
export function useLocale(): LocaleService
|
|
2556
|
+
```
|
|
2557
|
+
|
|
2558
|
+
**Returns:** Активный {@link LocaleService}.
|
|
2559
|
+
|
|
2560
|
+
_Source: src/locale/locale-context.tsx_
|
|
2561
|
+
|
|
2562
|
+
### useSignalValues
|
|
2563
|
+
|
|
2564
|
+
**Kind:** `function`
|
|
2565
|
+
|
|
2566
|
+
Реактивно разворачивает `values`-сигналы. Перерендер компонента при изменении любого сигнала;
|
|
2567
|
+
литералы проходят как есть. Снапшот кэшируется (стабильная ссылка при неизменных значениях) —
|
|
2568
|
+
иначе `useSyncExternalStore` зациклится.
|
|
2569
|
+
|
|
2570
|
+
**Signature:**
|
|
2571
|
+
```typescript
|
|
2572
|
+
export function useSignalValues(values?: LocaleParams): LocaleParams | undefined
|
|
2573
|
+
```
|
|
2574
|
+
|
|
2575
|
+
**Parameters:**
|
|
2576
|
+
- `values` — - Параметры сообщения (сигналы и/или литералы).
|
|
2577
|
+
|
|
2578
|
+
**Returns:** Развёрнутые значения для передачи в `LocaleService.resolve/render`.
|
|
2579
|
+
|
|
2580
|
+
_Source: src/locale/use-signal-value.ts_
|