@reformer/core 11.0.0-beta.6 → 11.0.0-beta.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/dist/behaviors.js +124 -124
- package/dist/create-model-B8U-COv-.js +103 -0
- package/dist/en-CjAqn7h0.js +41 -0
- package/dist/form/behaviors/internals.d.ts +9 -0
- package/dist/form/types/contracts.d.ts +6 -0
- package/dist/form/types/validation-schema.d.ts +13 -1
- package/dist/form/validation/operators.d.ts +6 -1
- package/dist/form/validators/validation-error.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +54 -0
- package/dist/i18n/en.json.d.ts +45 -0
- package/dist/i18n/loader.d.ts +71 -0
- package/dist/i18n/locale-store.d.ts +39 -0
- package/dist/i18n/locale.d.ts +90 -0
- package/dist/i18n/message-format.d.ts +116 -0
- package/dist/i18n/resolve-localized.d.ts +17 -0
- package/dist/i18n/ru.json.d.ts +45 -0
- package/dist/i18n/translator.d.ts +50 -0
- package/dist/i18n/validation-message.d.ts +18 -0
- package/dist/i18n.d.ts +31 -0
- package/dist/i18n.js +303 -0
- package/dist/index-BrNkQj1y.js +105 -0
- package/dist/index.js +639 -736
- package/dist/loader-Dv_Hl7JC.js +216 -0
- package/dist/locale/en.d.ts +2 -0
- package/dist/locale/en.js +5 -0
- package/dist/locale/en.json +42 -0
- package/dist/locale/index.d.ts +18 -0
- package/dist/locale/ru.d.ts +2 -0
- package/dist/locale/ru.js +41 -0
- package/dist/locale/ru.json +42 -0
- package/dist/locale.d.ts +2 -0
- package/dist/locale.js +9 -0
- package/dist/model/create-model.d.ts +17 -3
- package/dist/model/index.d.ts +3 -3
- package/dist/model/model-signals-proxy.d.ts +25 -0
- package/dist/model/types.d.ts +19 -5
- package/dist/model-signals-proxy-2vqS243J.js +250 -0
- package/dist/model.js +18 -16
- package/dist/platforms/react/hooks/types.d.ts +6 -3
- package/dist/platforms/react/hooks/useFormControlValue.d.ts +2 -1
- package/dist/platforms/react/i18n/context.d.ts +13 -0
- package/dist/platforms/react/i18n/hooks.d.ts +59 -0
- package/dist/platforms/react/i18n/provider.d.ts +52 -0
- package/dist/{strategy-DA3JwxsR.js → strategy-BXS_gBfz.js} +7 -7
- package/dist/validation-error-B-U3RsV0.js +11 -0
- package/dist/validation.js +1 -1
- package/dist/validators/email.js +5 -8
- package/dist/validators/future-date.js +8 -11
- package/dist/validators/integer.js +4 -7
- package/dist/validators/is-date.js +5 -8
- package/dist/validators/is-number.js +4 -7
- package/dist/validators/max-age.js +8 -11
- package/dist/validators/max-date.js +8 -11
- package/dist/validators/max-length.js +5 -8
- package/dist/validators/max.js +4 -7
- package/dist/validators/min-age.js +8 -11
- package/dist/validators/min-date.js +8 -11
- package/dist/validators/min-length.js +5 -8
- package/dist/validators/min.js +4 -7
- package/dist/validators/multiple-of.js +5 -8
- package/dist/validators/non-negative.js +4 -7
- package/dist/validators/non-zero.js +4 -7
- package/dist/validators/past-date.js +8 -11
- package/dist/validators/pattern.js +5 -8
- package/dist/validators/phone.js +6 -9
- package/dist/validators/required.js +4 -7
- package/dist/validators/url.js +9 -14
- package/dist/validators.js +84 -106
- package/docs/llms/03-api-signatures.md +6 -0
- package/docs/llms/10-arrays.md +30 -0
- package/docs/llms/34-i18n.md +272 -0
- package/llms-index.json +1 -1
- package/llms.txt +1589 -98
- package/package.json +22 -3
- package/dist/create-model-Dxh9cfLp.js +0 -97
- package/dist/model-signals-proxy-mV3WGdlO.js +0 -233
package/llms.txt
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
- 31-async-validator-debounce.md — async-validator-debounce
|
|
39
39
|
- 32-async-options-loading.md — async-options-loading
|
|
40
40
|
- 33-validation-strategy.md — useFormValidation — Единый выбор стратегии валидации
|
|
41
|
+
- 34-i18n.md — i18n — локаль, загрузка словарей и провайдер
|
|
41
42
|
- API Reference (auto-generated from JSDoc)
|
|
42
43
|
|
|
43
44
|
## 1. 1. API Reference
|
|
@@ -320,6 +321,12 @@ minAge(years: number, options?: { message?: string })
|
|
|
320
321
|
maxAge(years: number, options?: { message?: string })
|
|
321
322
|
```
|
|
322
323
|
|
|
324
|
+
Кроме `message`, каждое правило принимает `messageKey` (ключ словаря приложения) и `params`.
|
|
325
|
+
Без `message` правило кладёт в ошибку пустую строку, а текст при отображении берётся из словаря
|
|
326
|
+
локали по коду (`validation.<code>`): «Обязательное поле» под русской локалью, английская фраза без
|
|
327
|
+
провайдера. Явное `message` важнее словаря, но языком не переключается; для локализуемого текста —
|
|
328
|
+
`messageKey`. Подробно — [34-i18n.md](./34-i18n.md).
|
|
329
|
+
|
|
323
330
|
Использование: layout-дерево `createForm` привязывает поля к сигналам, правила живут отдельной
|
|
324
331
|
`ValidationSchema` и гоняются раннером `validateModel` по требованию.
|
|
325
332
|
|
|
@@ -1258,6 +1265,36 @@ const form = createForm<MyForm>({ model, schema });
|
|
|
1258
1265
|
> он не совместим с `Record<string, FormValue>` и `ArrayNode<Item>` его отвергнет.
|
|
1259
1266
|
> См. `30-type-safety-recipes.md`.
|
|
1260
1267
|
|
|
1268
|
+
### Массив как ОДНО значение поля
|
|
1269
|
+
|
|
1270
|
+
Массив бывает не набором под-форм, а одним значением: мультивыбор, теги, список файлов. Чем он
|
|
1271
|
+
является, решает **схема**, а не данные: `{ array, item }` — набор под-форм, а узел поля
|
|
1272
|
+
`{ value: model.$.<путь>, component }` — одно значение. Массив, к которому в схеме ничего не
|
|
1273
|
+
привязано, в форму не попадает.
|
|
1274
|
+
|
|
1275
|
+
```typescript
|
|
1276
|
+
type MyForm = { tags: string[] };
|
|
1277
|
+
|
|
1278
|
+
const model = createModel<MyForm>({ tags: [] });
|
|
1279
|
+
|
|
1280
|
+
const schema = {
|
|
1281
|
+
tags: { value: model.$.tags, component: SelectMulti }, // поле над массивом целиком
|
|
1282
|
+
};
|
|
1283
|
+
const form = createForm<MyForm>({ model, schema });
|
|
1284
|
+
|
|
1285
|
+
form.tags.setValue(['a', 'b']); // FieldNode<string[]>
|
|
1286
|
+
model.$.tags.value = ['c']; // ручка значения: заменяет массив целиком
|
|
1287
|
+
validate(model.$.tags, [required(), maxLength(3)]); // правило получает массив
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
- `model.$.<массив>` — записываемая ручка значения (`PathAwareSignal`): подходит для `value:` в
|
|
1291
|
+
схеме, `validate`/`cross`, `enableWhen`/`copyFrom` и для `model.signalAt(path)`.
|
|
1292
|
+
- Узел-массив хранит только массив: запись `null`/`undefined` даёт `[]`. Нужно отличать «не
|
|
1293
|
+
выбирали» от «выбрали ничего» — объявляй поле `T[] | null` с начальным `null`: в рантайме это
|
|
1294
|
+
лист, и привязывается он так же.
|
|
1295
|
+
- Запись в узел-ГРУППУ (`model.$.<группа>.value = …`) — ошибка: группа пишется по полям или через
|
|
1296
|
+
`model.patch(...)`.
|
|
1297
|
+
|
|
1261
1298
|
### Один массив — три слоя (три разных движка)
|
|
1262
1299
|
|
|
1263
1300
|
Одна и та же коллекция описывается **тремя разными формами** — по одной на движок. Их легко
|
|
@@ -4465,7 +4502,280 @@ if (typeof window !== 'undefined') ctrl.start();
|
|
|
4465
4502
|
- [31-async-validator-debounce.md](./31-async-validator-debounce.md) — async-правила и отмена устаревших прогонов
|
|
4466
4503
|
- [13-multi-step.md](./13-multi-step.md) — per-step / полная валидация wizard (`validateStep` / `validateAll`)
|
|
4467
4504
|
|
|
4468
|
-
## 82.
|
|
4505
|
+
## 82. Purpose
|
|
4506
|
+
|
|
4507
|
+
**i18n — локаль, загрузка словарей и провайдер**
|
|
4508
|
+
|
|
4509
|
+
Подпуть `@reformer/core/i18n` — общий рантайм локализации для всех пакетов `@reformer/*`: объект
|
|
4510
|
+
локали, загрузка словарей, React-провайдер, перевод и форматирование. Язык задаётся провайдером,
|
|
4511
|
+
подгружается динамически и переключается на лету; узлы формы при этом не пересоздаются.
|
|
4512
|
+
|
|
4513
|
+
Без провайдера всё работает на английском: у каждого пакета есть встроенная английская таблица.
|
|
4514
|
+
|
|
4515
|
+
> Импорт всегда из `@reformer/core/i18n`. Из главной бочки `@reformer/core` этот модуль не
|
|
4516
|
+
> реэкспортируется: React-контекст локализации обязан существовать в одном экземпляре.
|
|
4517
|
+
|
|
4518
|
+
## 83. Локаль — это данные
|
|
4519
|
+
|
|
4520
|
+
```typescript
|
|
4521
|
+
interface FormLocale {
|
|
4522
|
+
code: string; // BCP 47: 'en', 'ru', 'pt-BR' — по нему работают Intl и plural
|
|
4523
|
+
messages: Record<string, string>; // плоский словарь: ключ → сообщение ICU
|
|
4524
|
+
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
4525
|
+
dateLocale?: unknown; // необязательно: объект локали react-day-picker / date-fns
|
|
4526
|
+
dateFormat?: string; // необязательно: токены date-fns для DatePicker
|
|
4527
|
+
}
|
|
4528
|
+
```
|
|
4529
|
+
|
|
4530
|
+
Функций в локали нет, поэтому её можно положить JSON-файлом на сервер и получить по сети:
|
|
4531
|
+
|
|
4532
|
+
```json
|
|
4533
|
+
{ "code": "ru", "weekStartsOn": 1, "messages": { "profile.title": "Профиль" } }
|
|
4534
|
+
```
|
|
4535
|
+
|
|
4536
|
+
`extendLocale(base, patch)` возвращает новую локаль: словари сливаются (ключи из `patch` важнее),
|
|
4537
|
+
остальные поля заменяются.
|
|
4538
|
+
|
|
4539
|
+
## 84. Формат сообщений — подмножество ICU
|
|
4540
|
+
|
|
4541
|
+
| Конструкция | Пример |
|
|
4542
|
+
| ------------------- | ------------------------------------------------------------------------- |
|
|
4543
|
+
| подстановка | `Открыт файл {name}` |
|
|
4544
|
+
| множественные формы | `{count, plural, one{# файл} few{# файла} many{# файлов} other{# файла}}` |
|
|
4545
|
+
| точное значение | `{count, plural, =0{файлов нет} other{# файлов}}` |
|
|
4546
|
+
| выбор | `{gender, select, male{он} female{она} other{оно}}` |
|
|
4547
|
+
| экранирование | `'{'` → `{`, `'}'` → `}`, `''` → `'` |
|
|
4548
|
+
|
|
4549
|
+
- Ветка `other` обязательна. `#` внутри `plural` — число, отформатированное по языку.
|
|
4550
|
+
- Типы `number`, `date`, `time`, `selectordinal` не поддерживаются: числа и даты форматируйте
|
|
4551
|
+
отдельно (`useI18n().number`, `.date`) и передавайте готовой строкой.
|
|
4552
|
+
- Категории (`one`/`few`/`many`) берутся из `Intl.PluralRules` языка локали.
|
|
4553
|
+
|
|
4554
|
+
## 85. Загрузка локалей
|
|
4555
|
+
|
|
4556
|
+
Откуда брать словарь, решает приложение: источник — функция `(code) => локаль | словарь`.
|
|
4557
|
+
|
|
4558
|
+
```typescript
|
|
4559
|
+
import { createLocaleLoader, fetchMessages } from '@reformer/core/i18n';
|
|
4560
|
+
import { loadCoreLocale } from '@reformer/core/locale';
|
|
4561
|
+
|
|
4562
|
+
export const loadLocale = createLocaleLoader([
|
|
4563
|
+
loadCoreLocale, // встроенные словари ядра — чанк нужного языка
|
|
4564
|
+
fetchMessages((code) => `/locales/${code}.json`), // ключи приложения и правки — с сервера
|
|
4565
|
+
]);
|
|
4566
|
+
```
|
|
4567
|
+
|
|
4568
|
+
- Источники опрашиваются параллельно, сливаются по порядку: поздний перекрывает ранний.
|
|
4569
|
+
- Источник отдаёт локаль целиком (`{ code, messages, … }`) или голый словарь (`{ "a.b": "…" }`);
|
|
4570
|
+
`null` означает «для этого языка у меня ничего нет» и ошибкой не считается.
|
|
4571
|
+
- Результат кэшируется по коду языка, идущий запрос не дублируется.
|
|
4572
|
+
- Отказ любого источника — отказ загрузки. Источник, которому можно промахнуться, оборачивайте:
|
|
4573
|
+
`(code) => source(code).catch(() => null)`.
|
|
4574
|
+
- `loadLocale.peek(code)` — уже загруженная локаль или `undefined`; `loadLocale.preload(code)` — то
|
|
4575
|
+
же, что вызов загрузчика.
|
|
4576
|
+
|
|
4577
|
+
Ядро отдаёт свои локали в трёх видах:
|
|
4578
|
+
|
|
4579
|
+
| Вид | Импорт | Для чего |
|
|
4580
|
+
| ----------------- | ------------------------------------------- | ----------------------------------------------- |
|
|
4581
|
+
| загрузчик | `loadCoreLocale` из `@reformer/core/locale` | основной путь: чанк языка по запросу |
|
|
4582
|
+
| JSON-файл | `@reformer/core/locale/ru.json` | выложить на свой сервер, править без пересборки |
|
|
4583
|
+
| синхронный модуль | `ru` из `@reformer/core/locale/ru` | SSR и тесты |
|
|
4584
|
+
|
|
4585
|
+
Встроенные языки ядра — `en` и `ru`. Словарь ядра несёт тексты ошибок валидации
|
|
4586
|
+
(`validation.<code>`) и единицы размера файла (`format.fileSize.b|kb|mb|gb|tb`).
|
|
4587
|
+
|
|
4588
|
+
Пакеты выше отдают локаль накопительно и в тех же трёх видах, поэтому источник встроенных словарей
|
|
4589
|
+
в загрузчике всегда один — самого верхнего из используемых пакетов:
|
|
4590
|
+
|
|
4591
|
+
| Пакеты приложения | Источник | Что в локали |
|
|
4592
|
+
| ------------------------------- | -------------------------------------------- | --------------------------------- |
|
|
4593
|
+
| только ядро | `loadCoreLocale` из `@reformer/core/locale` | ошибки валидации, размер файла |
|
|
4594
|
+
| ядро + `@reformer/cdk` | `loadCdkLocale` из `@reformer/cdk/locale` | то же плюс строки cdk (`cdk.*`) |
|
|
4595
|
+
| ядро + cdk + `@reformer/ui-kit` | `loadKitLocale` из `@reformer/ui-kit/locale` | то же плюс подписи кита (`kit.*`) |
|
|
4596
|
+
|
|
4597
|
+
## 86. Провайдер
|
|
4598
|
+
|
|
4599
|
+
```tsx
|
|
4600
|
+
import { I18nProvider } from '@reformer/core/i18n';
|
|
4601
|
+
|
|
4602
|
+
function App() {
|
|
4603
|
+
const [lang, setLang] = useState('ru');
|
|
4604
|
+
return (
|
|
4605
|
+
<I18nProvider lang={lang} load={loadLocale}>
|
|
4606
|
+
<LangSwitch value={lang} onChange={setLang} />
|
|
4607
|
+
<CreditForm />
|
|
4608
|
+
</I18nProvider>
|
|
4609
|
+
);
|
|
4610
|
+
}
|
|
4611
|
+
```
|
|
4612
|
+
|
|
4613
|
+
| Проп | Назначение |
|
|
4614
|
+
| --------------- | ------------------------------------------------------------------------ |
|
|
4615
|
+
| `lang` + `load` | провайдер грузит локаль сам |
|
|
4616
|
+
| `fallback` | что показать, пока не загружена самая первая локаль; по умолчанию ничего |
|
|
4617
|
+
| `onError` | отказ загрузки `(error, lang)` |
|
|
4618
|
+
| `locale` | готовый объект вместо `lang` + `load` — SSR, тесты |
|
|
4619
|
+
|
|
4620
|
+
Поведение при `lang` + `load`:
|
|
4621
|
+
|
|
4622
|
+
- при смене `lang` на экране остаётся прежний язык, пока новый не загрузится;
|
|
4623
|
+
`useI18n().pending` — для индикатора;
|
|
4624
|
+
- при быстром переключении применяется только последний запрос;
|
|
4625
|
+
- ошибка загрузки язык не меняет и попадает в `useI18n().error` и `onError`;
|
|
4626
|
+
- если локаль уже в кэше (`await loadLocale.preload(lang)` до монтирования), первый рендер
|
|
4627
|
+
синхронный и `fallback` не показывается.
|
|
4628
|
+
|
|
4629
|
+
SSR: эффекты на сервере не выполняются, поэтому там передавайте готовый объект —
|
|
4630
|
+
`<I18nProvider locale={ru}>` — либо заранее вызовите `preload`.
|
|
4631
|
+
|
|
4632
|
+
Вложенный провайдер заменяет локаль для своего поддерева целиком.
|
|
4633
|
+
|
|
4634
|
+
## 87. Хуки
|
|
4635
|
+
|
|
4636
|
+
```typescript
|
|
4637
|
+
const { code, locale, pending, error, t, has, number, date, fileSize } = useI18n();
|
|
4638
|
+
|
|
4639
|
+
t('profile.title'); // ключ приложения
|
|
4640
|
+
t('cart.items', { count: 3 }); // с подстановками
|
|
4641
|
+
t('cart.empty', undefined, 'Your cart is empty'); // английский текст на случай промаха
|
|
4642
|
+
number(1234.5); // '1 234,5' в ru
|
|
4643
|
+
date(new Date(), { dateStyle: 'long' }); // '15 января 2024 г.' в ru
|
|
4644
|
+
fileSize(1536); // '1,5 КБ' в ru, '1.5 KB' без провайдера
|
|
4645
|
+
```
|
|
4646
|
+
|
|
4647
|
+
`useMessages(builtinEn)` — для авторов пакетов и китов: ищет ключ в локали провайдера, а при
|
|
4648
|
+
промахе берёт встроенную английскую таблицу пакета. Тип ключа выводится из таблицы.
|
|
4649
|
+
|
|
4650
|
+
```tsx
|
|
4651
|
+
import en from './locale/en.json';
|
|
4652
|
+
|
|
4653
|
+
function ClearButton() {
|
|
4654
|
+
const t = useMessages(en);
|
|
4655
|
+
return <button aria-label={t('kit.select.clear')} />;
|
|
4656
|
+
}
|
|
4657
|
+
```
|
|
4658
|
+
|
|
4659
|
+
## 88. Тексты ошибок валидации
|
|
4660
|
+
|
|
4661
|
+
Правила из `@reformer/core/validators` несут только `code` и `params`; готового текста у них нет
|
|
4662
|
+
(`message` по умолчанию — пустая строка). Строку даёт резолвер при отображении ошибки:
|
|
4663
|
+
|
|
4664
|
+
1. `messageKey` автора правила — если такой ключ есть в активной локали;
|
|
4665
|
+
2. явное `message` автора правила;
|
|
4666
|
+
3. `validation.<code>` из словаря локали;
|
|
4667
|
+
4. `validation.<code>` из встроенной английской таблицы ядра;
|
|
4668
|
+
5. сам код — для своих кодов, которых нет ни в одном словаре.
|
|
4669
|
+
|
|
4670
|
+
```typescript
|
|
4671
|
+
validate(model.$.email, [
|
|
4672
|
+
required(), // «Обязательное поле» в ru, английская фраза без провайдера
|
|
4673
|
+
minLength(5, { message: 'Слишком коротко' }), // явный текст: важнее словаря, но языком не переключается
|
|
4674
|
+
email({ messageKey: 'profile.email.invalid' }), // ключ словаря приложения: переключается вместе с языком
|
|
4675
|
+
]);
|
|
4676
|
+
```
|
|
4677
|
+
|
|
4678
|
+
- Текст получается при отображении, а не при проверке: смена языка переводит уже показанные
|
|
4679
|
+
ошибки без повторной валидации.
|
|
4680
|
+
- `FormField` (`@reformer/cdk`, `@reformer/ui-kit`) резолвит ошибку сам. В своём компоненте —
|
|
4681
|
+
`useValidationMessage()`; вне React — `resolveValidationError(error, createI18n(locale))`.
|
|
4682
|
+
- Свой код ошибки локализуется ключом `validation.<свой_код>` в словаре приложения либо
|
|
4683
|
+
`messageKey` в самом правиле.
|
|
4684
|
+
- Встроенный текст меняется тем же ключом в словаре приложения:
|
|
4685
|
+
`extendLocale(ru, { messages: { 'validation.required': 'Заполните поле' } })`.
|
|
4686
|
+
- Параметры готовятся до подстановки: `Date` — по языку (`15 янв. 2030 г.`), массив — перечнем,
|
|
4687
|
+
размеры в байтах (`maxFileSize`, `minFileSize`, `actualSize`, `maxTotalFileSize`, `actualTotal`) —
|
|
4688
|
+
строкой с единицей (`5 МБ`). Числа остаются числами, поэтому в сообщении работает `plural`.
|
|
4689
|
+
|
|
4690
|
+
```tsx
|
|
4691
|
+
function FieldErrors({ control }: { control: FieldNode<string> }) {
|
|
4692
|
+
const message = useValidationMessage();
|
|
4693
|
+
const { errors, shouldShowError } = useFormControl(control);
|
|
4694
|
+
return shouldShowError ? <span role="alert">{message(errors[0])}</span> : null;
|
|
4695
|
+
}
|
|
4696
|
+
```
|
|
4697
|
+
|
|
4698
|
+
Смонтированный `ValidationMessagesProvider` из `@reformer/cdk` заменяет этот резолвер целиком:
|
|
4699
|
+
локаль для ошибок в его поддереве не участвует.
|
|
4700
|
+
|
|
4701
|
+
## 89. Что показывается, когда ключа нет
|
|
4702
|
+
|
|
4703
|
+
| Ключ | Нет в локали | Сообщение не разбирается |
|
|
4704
|
+
| -------------------------- | --------------------------------------------------------------- | ----------------------------------------------------- |
|
|
4705
|
+
| пакета (`useMessages`) | встроенный английский пакета | встроенный английский, `console.error` вне production |
|
|
4706
|
+
| приложения (`useI18n().t`) | `defaultMessage`, затем сам ключ; `console.warn` вне production | то же, `console.error` |
|
|
4707
|
+
|
|
4708
|
+
Пропущенная подстановка выводится как `{name}`. Форма не падает ни в одном из случаев: неполный
|
|
4709
|
+
словарь, пришедший по сети, — законный сценарий.
|
|
4710
|
+
|
|
4711
|
+
## 90. Проверка словаря в тестах
|
|
4712
|
+
|
|
4713
|
+
```typescript
|
|
4714
|
+
import { validateLocale } from '@reformer/core/i18n';
|
|
4715
|
+
|
|
4716
|
+
expect(validateLocale(ruJson, enJson)).toEqual([]);
|
|
4717
|
+
// находит: syntax (не разбирается), missing / extra (ключи), arguments (потерянная подстановка)
|
|
4718
|
+
```
|
|
4719
|
+
|
|
4720
|
+
## 91. Описатели сообщений (низкий уровень)
|
|
4721
|
+
|
|
4722
|
+
`msg(key, values?, defaultMessage?)` создаёт описатель сообщения, `defineMessages(appEn)` — `msg` с
|
|
4723
|
+
типизированными ключами, `resolveLocalized(value, i18n)` раскрывает описатели в объекте пропсов.
|
|
4724
|
+
|
|
4725
|
+
> ⚠️ Рендереры и `FormField` описатели сами пока НЕ раскрывают. Не кладите `msg(...)` в
|
|
4726
|
+
> `componentProps` схемы: компонент получит объект вместо строки. До появления раскрытия в
|
|
4727
|
+
> рендерерах вызывайте `resolveLocalized` вручную или используйте `useI18n().t`.
|
|
4728
|
+
|
|
4729
|
+
## 92. Anti-patterns
|
|
4730
|
+
|
|
4731
|
+
```typescript
|
|
4732
|
+
// ❌ Импорт из главной бочки — i18n там нет, а второй путь импорта дал бы вторую копию контекста
|
|
4733
|
+
import { I18nProvider } from '@reformer/core';
|
|
4734
|
+
|
|
4735
|
+
// ✅ Единственный путь импорта — подпуть i18n
|
|
4736
|
+
import { I18nProvider } from '@reformer/core/i18n';
|
|
4737
|
+
```
|
|
4738
|
+
|
|
4739
|
+
```tsx
|
|
4740
|
+
// ❌ Новый загрузчик на каждый рендер: кэш теряется, локаль грузится заново, первый кадр — fallback
|
|
4741
|
+
<I18nProvider lang={lang} load={createLocaleLoader([loadCoreLocale])}>
|
|
4742
|
+
|
|
4743
|
+
// ✅ Загрузчик — константа модуля: один кэш на приложение
|
|
4744
|
+
const loadLocale = createLocaleLoader([loadCoreLocale]);
|
|
4745
|
+
<I18nProvider lang={lang} load={loadLocale}>
|
|
4746
|
+
```
|
|
4747
|
+
|
|
4748
|
+
```typescript
|
|
4749
|
+
// ❌ Число или дата типами ICU внутри сообщения — не поддерживается, сообщение не разберётся
|
|
4750
|
+
const message = 'Итого: {total, number}';
|
|
4751
|
+
|
|
4752
|
+
// ✅ Отформатировать отдельно и передать готовой строкой
|
|
4753
|
+
t('cart.total', { total: number(total, { style: 'currency', currency: 'RUB' }) });
|
|
4754
|
+
```
|
|
4755
|
+
|
|
4756
|
+
```tsx
|
|
4757
|
+
// ❌ Ошибка выводится полем message: у правил без своего текста оно пустое — на экране ничего
|
|
4758
|
+
<span>{errors[0].message}</span>;
|
|
4759
|
+
|
|
4760
|
+
// ✅ Текст ошибки — через резолвер: он знает словарь локали, messageKey и явное сообщение
|
|
4761
|
+
const message = useValidationMessage();
|
|
4762
|
+
<span>{message(errors[0])}</span>;
|
|
4763
|
+
```
|
|
4764
|
+
|
|
4765
|
+
```typescript
|
|
4766
|
+
// ❌ Настройки локали без словаря: объект без поля messages принимается за словарь сообщений
|
|
4767
|
+
const source = () => ({ weekStartsOn: 1 });
|
|
4768
|
+
|
|
4769
|
+
// ✅ Часть локали узнаётся по полю messages — отдавайте его хотя бы пустым
|
|
4770
|
+
const source = () => ({ weekStartsOn: 1, messages: {} });
|
|
4771
|
+
```
|
|
4772
|
+
|
|
4773
|
+
## 93. See also
|
|
4774
|
+
|
|
4775
|
+
- [28-submit-and-reset.md](./28-submit-and-reset.md) — жизненный цикл формы; смена языка его не затрагивает
|
|
4776
|
+
- [33-validation-strategy.md](./33-validation-strategy.md) — когда запускается валидация
|
|
4777
|
+
|
|
4778
|
+
## 94. API Reference
|
|
4469
4779
|
|
|
4470
4780
|
_Auto-generated from JSDoc on public exports._
|
|
4471
4781
|
|
|
@@ -4932,6 +5242,35 @@ await v!.validateAll(); // все шаги + extras
|
|
|
4932
5242
|
|
|
4933
5243
|
_Source: src/form/validation/config.ts_
|
|
4934
5244
|
|
|
5245
|
+
### bundledLocaleSource
|
|
5246
|
+
|
|
5247
|
+
**Kind:** `function`
|
|
5248
|
+
|
|
5249
|
+
Источник встроенных локалей пакета: по чанку на язык. Язык ищется точно, затем по основной части
|
|
5250
|
+
кода (`ru-RU` → `ru`). Языка нет — источник отдаёт `null`: подписи пакета останутся английскими,
|
|
5251
|
+
пока приложение не даст их своим источником.
|
|
5252
|
+
|
|
5253
|
+
**Signature:**
|
|
5254
|
+
```typescript
|
|
5255
|
+
export function bundledLocaleSource(
|
|
5256
|
+
loaders: Readonly<Record<string, () => Promise<FormLocale>>>
|
|
5257
|
+
): LocaleSource
|
|
5258
|
+
```
|
|
5259
|
+
|
|
5260
|
+
**Parameters:**
|
|
5261
|
+
- `loaders` — - Код языка → ленивый импорт его локали.
|
|
5262
|
+
|
|
5263
|
+
**Examples:**
|
|
5264
|
+
|
|
5265
|
+
```ts
|
|
5266
|
+
export const loadKitLocale = bundledLocaleSource({
|
|
5267
|
+
en: () => import('./en').then((m) => m.en),
|
|
5268
|
+
ru: () => import('./ru').then((m) => m.ru),
|
|
5269
|
+
});
|
|
5270
|
+
```
|
|
5271
|
+
|
|
5272
|
+
_Source: src/i18n/loader.ts_
|
|
5273
|
+
|
|
4935
5274
|
### ChangeContext
|
|
4936
5275
|
|
|
4937
5276
|
**Kind:** `interface`
|
|
@@ -5049,6 +5388,19 @@ copyFrom(model.$.email, model.$.emailAdditional, { when: () => model.sameEmail }
|
|
|
5049
5388
|
|
|
5050
5389
|
_Source: src/model/behaviors-value.ts_
|
|
5051
5390
|
|
|
5391
|
+
### CORE_LOCALES
|
|
5392
|
+
|
|
5393
|
+
**Kind:** `const`
|
|
5394
|
+
|
|
5395
|
+
Языки, которые ядро поставляет само.
|
|
5396
|
+
|
|
5397
|
+
**Signature:**
|
|
5398
|
+
```typescript
|
|
5399
|
+
export const CORE_LOCALES: readonly string[]
|
|
5400
|
+
```
|
|
5401
|
+
|
|
5402
|
+
_Source: src/locale/index.ts_
|
|
5403
|
+
|
|
5052
5404
|
### CORE_RUNTIME_TOKEN
|
|
5053
5405
|
|
|
5054
5406
|
**Kind:** `const`
|
|
@@ -5359,6 +5711,122 @@ dispose();
|
|
|
5359
5711
|
|
|
5360
5712
|
_Source: src/form/validation/strategy.ts_
|
|
5361
5713
|
|
|
5714
|
+
### createI18n
|
|
5715
|
+
|
|
5716
|
+
**Kind:** `function`
|
|
5717
|
+
|
|
5718
|
+
Ручка для локали. Для одного и того же объекта локали возвращается один и тот же объект —
|
|
5719
|
+
ручку можно держать в зависимостях хуков.
|
|
5720
|
+
|
|
5721
|
+
**Signature:**
|
|
5722
|
+
```typescript
|
|
5723
|
+
export function createI18n(locale: FormLocale): I18nHandle
|
|
5724
|
+
```
|
|
5725
|
+
|
|
5726
|
+
**Parameters:**
|
|
5727
|
+
- `locale` — - Локаль; см. {@link FormLocale}.
|
|
5728
|
+
|
|
5729
|
+
**Examples:**
|
|
5730
|
+
|
|
5731
|
+
```ts
|
|
5732
|
+
const i18n = createI18n({ code: 'ru', messages: { 'cart.items': '{count, plural, one{# товар} few{# товара} other{# товаров}}' } });
|
|
5733
|
+
i18n.t('cart.items', { count: 3 }); // '3 товара'
|
|
5734
|
+
i18n.fileSize(1536); // '1,5 КБ'
|
|
5735
|
+
```
|
|
5736
|
+
|
|
5737
|
+
_Source: src/i18n/translator.ts_
|
|
5738
|
+
|
|
5739
|
+
### createLocaleLoader
|
|
5740
|
+
|
|
5741
|
+
**Kind:** `function`
|
|
5742
|
+
|
|
5743
|
+
Собирает загрузчик из источников. Источники опрашиваются параллельно, а сливаются по порядку:
|
|
5744
|
+
поздний перекрывает ранний. Результат кэшируется по коду языка, идущий запрос не дублируется.
|
|
5745
|
+
Отказ любого источника — отказ загрузки; из кэша такой запрос удаляется, и следующая попытка
|
|
5746
|
+
пойдёт заново. Источник, которому можно промахнуться, приложение оборачивает само:
|
|
5747
|
+
`(code) => source(code).catch(() => null)`.
|
|
5748
|
+
|
|
5749
|
+
Код языка у результата — всегда запрошенный: по нему работает `Intl`, и он не должен зависеть
|
|
5750
|
+
от того, что написано внутри файла.
|
|
5751
|
+
|
|
5752
|
+
**Signature:**
|
|
5753
|
+
```typescript
|
|
5754
|
+
export function createLocaleLoader(sources: readonly LocaleSource[]): LocaleLoader
|
|
5755
|
+
```
|
|
5756
|
+
|
|
5757
|
+
**Parameters:**
|
|
5758
|
+
- `sources` — - Источники в порядке возрастания приоритета.
|
|
5759
|
+
|
|
5760
|
+
**Examples:**
|
|
5761
|
+
|
|
5762
|
+
```ts
|
|
5763
|
+
import { createLocaleLoader, fetchMessages } from '@reformer/core/i18n';
|
|
5764
|
+
import { loadKitLocale } from '@reformer/ui-kit/locale';
|
|
5765
|
+
|
|
5766
|
+
export const loadLocale = createLocaleLoader([
|
|
5767
|
+
loadKitLocale, // подписи кита — чанк нужного языка
|
|
5768
|
+
fetchMessages((code) => `/locales/${code}.json`), // ключи приложения и правки — с сервера
|
|
5769
|
+
]);
|
|
5770
|
+
```
|
|
5771
|
+
|
|
5772
|
+
_Source: src/i18n/loader.ts_
|
|
5773
|
+
|
|
5774
|
+
### createLocaleStore
|
|
5775
|
+
|
|
5776
|
+
**Kind:** `function`
|
|
5777
|
+
|
|
5778
|
+
Создаёт хранилище. Загрузка при создании НЕ начинается: начальное состояние считается чисто —
|
|
5779
|
+
из кэша загрузчика, если локаль там уже есть, — а запрос уходит на первый {@link LocaleStore.setLang}.
|
|
5780
|
+
Это позволяет создавать хранилище прямо в рендере.
|
|
5781
|
+
|
|
5782
|
+
**Signature:**
|
|
5783
|
+
```typescript
|
|
5784
|
+
export function createLocaleStore(
|
|
5785
|
+
load: LocaleLoad,
|
|
5786
|
+
lang: string,
|
|
5787
|
+
options: LocaleStoreOptions = {}
|
|
5788
|
+
): LocaleStore
|
|
5789
|
+
```
|
|
5790
|
+
|
|
5791
|
+
**Parameters:**
|
|
5792
|
+
- `load` — - Загрузчик локалей.
|
|
5793
|
+
- `lang` — - Начальный язык.
|
|
5794
|
+
- `options` — - См. {@link LocaleStoreOptions}.
|
|
5795
|
+
|
|
5796
|
+
_Source: src/i18n/locale-store.ts_
|
|
5797
|
+
|
|
5798
|
+
### createMessageFormatter
|
|
5799
|
+
|
|
5800
|
+
**Kind:** `function`
|
|
5801
|
+
|
|
5802
|
+
Форматтер одной локали с кэшем разбора: сообщение разбирается один раз, на первый вызов.
|
|
5803
|
+
Это внутренность `I18nService.t()` — сам `t()` добавляет сверху поиск ключа в словаре, маркер
|
|
5804
|
+
пропущенного ключа и откат на резервную локаль.
|
|
5805
|
+
|
|
5806
|
+
**Signature:**
|
|
5807
|
+
```typescript
|
|
5808
|
+
export function createMessageFormatter(
|
|
5809
|
+
locale: string,
|
|
5810
|
+
options: MessageFormatOptions = {}
|
|
5811
|
+
): (source: string, values?: MessageValues) => string
|
|
5812
|
+
```
|
|
5813
|
+
|
|
5814
|
+
**Parameters:**
|
|
5815
|
+
- `locale` — - Локаль форматтера.
|
|
5816
|
+
- `options` — - Настройки; см. {@link MessageFormatOptions}.
|
|
5817
|
+
|
|
5818
|
+
**Returns:** Функция `(сообщение, значения) => строка`.
|
|
5819
|
+
|
|
5820
|
+
**Examples:**
|
|
5821
|
+
|
|
5822
|
+
```ts
|
|
5823
|
+
const format = createMessageFormatter('ru');
|
|
5824
|
+
format('{count, plural, one{# файл} few{# файла} many{# файлов} other{# файла}}', { count: 5 });
|
|
5825
|
+
// '5 файлов'
|
|
5826
|
+
```
|
|
5827
|
+
|
|
5828
|
+
_Source: src/i18n/message-format.ts_
|
|
5829
|
+
|
|
5362
5830
|
### createModel
|
|
5363
5831
|
|
|
5364
5832
|
**Kind:** `function`
|
|
@@ -5413,6 +5881,20 @@ export function cross<TSnapshot>(
|
|
|
5413
5881
|
|
|
5414
5882
|
_Source: src/form/validation/operators.ts_
|
|
5415
5883
|
|
|
5884
|
+
### DEFAULT_LOCALE
|
|
5885
|
+
|
|
5886
|
+
**Kind:** `const`
|
|
5887
|
+
|
|
5888
|
+
Локаль «провайдера нет»: английский, пустой словарь. Каждый пакет при промахе по словарю берёт
|
|
5889
|
+
свою встроенную английскую таблицу, поэтому пустой словарь означает «всё по-английски».
|
|
5890
|
+
|
|
5891
|
+
**Signature:**
|
|
5892
|
+
```typescript
|
|
5893
|
+
export const DEFAULT_LOCALE: FormLocale
|
|
5894
|
+
```
|
|
5895
|
+
|
|
5896
|
+
_Source: src/i18n/locale.ts_
|
|
5897
|
+
|
|
5416
5898
|
### defer
|
|
5417
5899
|
|
|
5418
5900
|
**Kind:** `function`
|
|
@@ -5449,6 +5931,37 @@ export const myBehavior = defineFormBehavior<MyForm>(({ model, form }) => {
|
|
|
5449
5931
|
|
|
5450
5932
|
_Source: src/form/behaviors/context.ts_
|
|
5451
5933
|
|
|
5934
|
+
### defineMessages
|
|
5935
|
+
|
|
5936
|
+
**Kind:** `function`
|
|
5937
|
+
|
|
5938
|
+
`msg` с типизированными ключами: принимает английский словарь приложения, проверяет ключ при
|
|
5939
|
+
компиляции и подставляет английский текст как `defaultMessage` — без провайдера и при промахе
|
|
5940
|
+
по локали форма покажет его, а не ключ.
|
|
5941
|
+
|
|
5942
|
+
**Signature:**
|
|
5943
|
+
```typescript
|
|
5944
|
+
export function defineMessages<D extends Messages>(
|
|
5945
|
+
defaults: D
|
|
5946
|
+
): {
|
|
5947
|
+
msg(key: keyof D & string, values?: Readonly<Record<string, unknown>>): MessageDescriptor;
|
|
5948
|
+
}
|
|
5949
|
+
```
|
|
5950
|
+
|
|
5951
|
+
**Parameters:**
|
|
5952
|
+
- `defaults` — - Английский словарь приложения.
|
|
5953
|
+
|
|
5954
|
+
**Examples:**
|
|
5955
|
+
|
|
5956
|
+
```ts
|
|
5957
|
+
import appEn from './locales/en.json';
|
|
5958
|
+
|
|
5959
|
+
export const { msg } = defineMessages(appEn);
|
|
5960
|
+
msg('profile.email.label'); // опечатка в ключе — ошибка компиляции
|
|
5961
|
+
```
|
|
5962
|
+
|
|
5963
|
+
_Source: src/i18n/descriptor.ts_
|
|
5964
|
+
|
|
5452
5965
|
### defineValidationSchema
|
|
5453
5966
|
|
|
5454
5967
|
**Kind:** `function`
|
|
@@ -5507,7 +6020,8 @@ _Source: src/form/behaviors/node.ts_
|
|
|
5507
6020
|
**Kind:** `function`
|
|
5508
6021
|
|
|
5509
6022
|
Применить под-правила к КАЖДОМУ элементу текущего массива модели.
|
|
5510
|
-
`U extends object` — элементы должны быть под-моделями (объектами);
|
|
6023
|
+
`U extends object` — элементы должны быть под-моделями (объектами); массив примитивов — одно
|
|
6024
|
+
значение поля, его проверяют целиком: `validate(model.$.<массив>, [...])`.
|
|
5511
6025
|
|
|
5512
6026
|
**Signature:**
|
|
5513
6027
|
```typescript
|
|
@@ -5526,9 +6040,11 @@ _Source: src/form/validation/operators.ts_
|
|
|
5526
6040
|
Обойти ВСЕ листовые сигналы модели (включая элементы массивов), вызвав `visit` на каждом.
|
|
5527
6041
|
|
|
5528
6042
|
Внутри реактивного `effect` служит подпиской «любое поле изменилось»: `visit(sig => void sig.value)`
|
|
5529
|
-
подписывает на значения листьев, а обход массивов через `items.value` — на их состав.
|
|
5530
|
-
|
|
5531
|
-
|
|
6043
|
+
подписывает на значения листьев, а обход массивов через `items.value` — на их состав. Тот же
|
|
6044
|
+
паттерн, что `revalidateWhen`, но без ручного перечисления зависимостей.
|
|
6045
|
+
|
|
6046
|
+
Сами массивы обход не посещает — только их элементы. Поле, привязанное к массиву целиком
|
|
6047
|
+
(мультивыбор), живёт на сигнале массива; чтобы увидеть и его, берите {@link eachValueSignal}.
|
|
5532
6048
|
|
|
5533
6049
|
**Signature:**
|
|
5534
6050
|
```typescript
|
|
@@ -5540,6 +6056,28 @@ export function eachLeafSignal<T>(
|
|
|
5540
6056
|
|
|
5541
6057
|
_Source: src/model/create-model.ts_
|
|
5542
6058
|
|
|
6059
|
+
### eachValueSignal
|
|
6060
|
+
|
|
6061
|
+
**Kind:** `function`
|
|
6062
|
+
|
|
6063
|
+
Обойти ВСЕ ручки значений модели: листья (включая элементы массивов) И сами массивы.
|
|
6064
|
+
|
|
6065
|
+
Отличие от {@link eachLeafSignal} — узлы-массивы: массив бывает одним значением поля
|
|
6066
|
+
(`{ value: model.$.tags, component }`), и нода такого поля привязана к сигналу массива, а не
|
|
6067
|
+
к сигналам элементов. Обходчику, которому нужна именно НОДА поля (стратегия `blur` подписывается
|
|
6068
|
+
на её `touched`), одних листьев мало — он не увидел бы мультивыбор. Так строятся триггеры
|
|
6069
|
+
`change`/`blur` стратегий валидации (см. `createFormValidation`).
|
|
6070
|
+
|
|
6071
|
+
**Signature:**
|
|
6072
|
+
```typescript
|
|
6073
|
+
export function eachValueSignal<T>(
|
|
6074
|
+
model: FormModel<T>,
|
|
6075
|
+
visit: (signal: PathAwareSignal<unknown>) => void
|
|
6076
|
+
): void
|
|
6077
|
+
```
|
|
6078
|
+
|
|
6079
|
+
_Source: src/model/create-model.ts_
|
|
6080
|
+
|
|
5543
6081
|
### effect
|
|
5544
6082
|
|
|
5545
6083
|
**Kind:** `function`
|
|
@@ -5713,26 +6251,79 @@ exclusiveFlag(model.$.contacts, (row) => row.$.primary);
|
|
|
5713
6251
|
|
|
5714
6252
|
_Source: src/form/behaviors/collections.ts_
|
|
5715
6253
|
|
|
5716
|
-
###
|
|
6254
|
+
### extendLocale
|
|
5717
6255
|
|
|
5718
|
-
**Kind:** `
|
|
6256
|
+
**Kind:** `function`
|
|
5719
6257
|
|
|
5720
|
-
|
|
5721
|
-
|
|
6258
|
+
Новая локаль на основе существующей: словари сливаются (ключи из `patch` важнее), остальные
|
|
6259
|
+
поля заменяются. Исходный объект не меняется.
|
|
5722
6260
|
|
|
5723
6261
|
**Signature:**
|
|
5724
6262
|
```typescript
|
|
5725
|
-
export
|
|
6263
|
+
export function extendLocale(base: FormLocale, patch: Partial<FormLocale>): FormLocale
|
|
5726
6264
|
```
|
|
5727
6265
|
|
|
5728
|
-
|
|
6266
|
+
**Parameters:**
|
|
6267
|
+
- `base` — - Локаль-основа, например поставляемая китом.
|
|
6268
|
+
- `patch` — - Свои ключи, переопределения китовых подписей и поля локали.
|
|
5729
6269
|
|
|
5730
|
-
|
|
6270
|
+
**Examples:**
|
|
5731
6271
|
|
|
5732
|
-
|
|
6272
|
+
```ts
|
|
6273
|
+
import { ru } from '@reformer/ui-kit/locale/ru';
|
|
5733
6274
|
|
|
5734
|
-
|
|
5735
|
-
|
|
6275
|
+
const appRu = extendLocale(ru, {
|
|
6276
|
+
messages: { 'profile.title': 'Профиль', 'kit.formWizard.submit': 'Отправить' },
|
|
6277
|
+
});
|
|
6278
|
+
```
|
|
6279
|
+
|
|
6280
|
+
_Source: src/i18n/locale.ts_
|
|
6281
|
+
|
|
6282
|
+
### fetchMessages
|
|
6283
|
+
|
|
6284
|
+
**Kind:** `function`
|
|
6285
|
+
|
|
6286
|
+
Источник локали по сети: забирает JSON по адресу, построенному из кода языка. Файл может быть
|
|
6287
|
+
словарём (`{ "profile.title": "Профиль" }`) или локалью целиком (`{ "code": "ru", "messages": … }`).
|
|
6288
|
+
Ответ не `2xx` — ошибка загрузки.
|
|
6289
|
+
|
|
6290
|
+
**Signature:**
|
|
6291
|
+
```typescript
|
|
6292
|
+
export function fetchMessages(url: (code: string) => string, init?: RequestInit): LocaleSource
|
|
6293
|
+
```
|
|
6294
|
+
|
|
6295
|
+
**Parameters:**
|
|
6296
|
+
- `url` — - Адрес файла для языка.
|
|
6297
|
+
- `init` — - Параметры запроса, передаются в `fetch` как есть.
|
|
6298
|
+
|
|
6299
|
+
**Examples:**
|
|
6300
|
+
|
|
6301
|
+
```ts
|
|
6302
|
+
fetchMessages((code) => `/locales/${code}.json`);
|
|
6303
|
+
```
|
|
6304
|
+
|
|
6305
|
+
_Source: src/i18n/loader.ts_
|
|
6306
|
+
|
|
6307
|
+
### FIELD_WRAPPER_ONLY_PROPS
|
|
6308
|
+
|
|
6309
|
+
**Kind:** `const`
|
|
6310
|
+
|
|
6311
|
+
Props, которые адресованы обёртке поля, а не контролу: `labelTooltip` рисует `FormField` у
|
|
6312
|
+
подписи. Они приходят в общем мешке `componentProps`, и без среза утекли бы в DOM контрола.
|
|
6313
|
+
|
|
6314
|
+
**Signature:**
|
|
6315
|
+
```typescript
|
|
6316
|
+
export const FIELD_WRAPPER_ONLY_PROPS: readonly string[]
|
|
6317
|
+
```
|
|
6318
|
+
|
|
6319
|
+
_Source: src/platforms/react/field-binding/field-adapter.ts_
|
|
6320
|
+
|
|
6321
|
+
### FieldAdapter
|
|
6322
|
+
|
|
6323
|
+
**Kind:** `interface`
|
|
6324
|
+
|
|
6325
|
+
Адаптер поля: описывает диалект контрола (`checked`/`onCheckedChange`, `value`/`onValueChange`,
|
|
6326
|
+
DOM-событие в `onChange`, …). Все ключи необязательны — по умолчанию seam проходит как есть.
|
|
5736
6327
|
|
|
5737
6328
|
**Signature:**
|
|
5738
6329
|
```typescript
|
|
@@ -5895,11 +6486,12 @@ export interface FieldControlState<T> {
|
|
|
5895
6486
|
* @example
|
|
5896
6487
|
* ```tsx
|
|
5897
6488
|
* const { errors } = useFormControl(field);
|
|
6489
|
+
* const message = useValidationMessage(); // из '@reformer/core/i18n'
|
|
5898
6490
|
*
|
|
5899
6491
|
* return (
|
|
5900
6492
|
* <ul className="error-list">
|
|
5901
6493
|
* {errors.map((error, i) => (
|
|
5902
|
-
* <li key={i}>{error
|
|
6494
|
+
* <li key={i}>{message(error)}</li>
|
|
5903
6495
|
* ))}
|
|
5904
6496
|
* </ul>
|
|
5905
6497
|
* );
|
|
@@ -5962,12 +6554,13 @@ export interface FieldControlState<T> {
|
|
|
5962
6554
|
* @example
|
|
5963
6555
|
* ```tsx
|
|
5964
6556
|
* const { shouldShowError, errors } = useFormControl(field);
|
|
6557
|
+
* const message = useValidationMessage(); // из '@reformer/core/i18n'
|
|
5965
6558
|
*
|
|
5966
6559
|
* return (
|
|
5967
6560
|
* <div>
|
|
5968
6561
|
* <input {...props} />
|
|
5969
6562
|
* {shouldShowError && (
|
|
5970
|
-
* <span className="error">{errors[0]
|
|
6563
|
+
* <span className="error">{message(errors[0])}</span>
|
|
5971
6564
|
* )}
|
|
5972
6565
|
* </div>
|
|
5973
6566
|
* );
|
|
@@ -6030,6 +6623,7 @@ control: FieldNode<string>;
|
|
|
6030
6623
|
|
|
6031
6624
|
function TextField({ control }: Props) {
|
|
6032
6625
|
const state = useFormControl(control);
|
|
6626
|
+
const message = useValidationMessage(); // из '@reformer/core/i18n'
|
|
6033
6627
|
|
|
6034
6628
|
return (
|
|
6035
6629
|
<div>
|
|
@@ -6039,7 +6633,7 @@ disabled={state.disabled}
|
|
|
6039
6633
|
onChange={e => control.setValue(e.target.value)}
|
|
6040
6634
|
/>
|
|
6041
6635
|
{state.shouldShowError && state.errors[0] && (
|
|
6042
|
-
<span className="error">{state.errors[0]
|
|
6636
|
+
<span className="error">{message(state.errors[0])}</span>
|
|
6043
6637
|
)}
|
|
6044
6638
|
</div>
|
|
6045
6639
|
);
|
|
@@ -6262,6 +6856,33 @@ todos.forEach((item, i) => {
|
|
|
6262
6856
|
|
|
6263
6857
|
_Source: src/form/types/form-proxy.ts_
|
|
6264
6858
|
|
|
6859
|
+
### formatPattern
|
|
6860
|
+
|
|
6861
|
+
**Kind:** `function`
|
|
6862
|
+
|
|
6863
|
+
Собирает строку из разобранного сообщения. Значения подставляются как текст и повторно
|
|
6864
|
+
не разбираются.
|
|
6865
|
+
|
|
6866
|
+
**Signature:**
|
|
6867
|
+
```typescript
|
|
6868
|
+
export function formatPattern(
|
|
6869
|
+
pattern: MessagePattern,
|
|
6870
|
+
values: MessageValues,
|
|
6871
|
+
locale: string,
|
|
6872
|
+
options: MessageFormatOptions = {}
|
|
6873
|
+
): string
|
|
6874
|
+
```
|
|
6875
|
+
|
|
6876
|
+
**Parameters:**
|
|
6877
|
+
- `pattern` — - Результат {@link parseMessage}.
|
|
6878
|
+
- `values` — - Значения аргументов.
|
|
6879
|
+
- `locale` — - Локаль для {@link Intl.PluralRules} и {@link Intl.NumberFormat} (`ru`, `en`, …).
|
|
6880
|
+
- `options` — - Настройки; см. {@link MessageFormatOptions}.
|
|
6881
|
+
|
|
6882
|
+
**Returns:** Готовая строка интерфейса.
|
|
6883
|
+
|
|
6884
|
+
_Source: src/i18n/message-format.ts_
|
|
6885
|
+
|
|
6265
6886
|
### FormBehavior
|
|
6266
6887
|
|
|
6267
6888
|
**Kind:** `interface`
|
|
@@ -6407,6 +7028,43 @@ try {
|
|
|
6407
7028
|
|
|
6408
7029
|
_Source: src/form/validation/error-handler.ts_
|
|
6409
7030
|
|
|
7031
|
+
### FormLocale
|
|
7032
|
+
|
|
7033
|
+
**Kind:** `interface`
|
|
7034
|
+
|
|
7035
|
+
Локаль формы.
|
|
7036
|
+
|
|
7037
|
+
**Signature:**
|
|
7038
|
+
```typescript
|
|
7039
|
+
export interface FormLocale {
|
|
7040
|
+
/** Код языка BCP 47 (`en`, `ru`, `pt-BR`): по нему работают `Intl` и множественные формы. */
|
|
7041
|
+
readonly code: string;
|
|
7042
|
+
/** Словарь сообщений. Ключа нет — пакет подставит встроенный английский. */
|
|
7043
|
+
readonly messages: Messages;
|
|
7044
|
+
/** Первый день недели календаря. По умолчанию — воскресенье. */
|
|
7045
|
+
readonly weekStartsOn?: WeekStart;
|
|
7046
|
+
/**
|
|
7047
|
+
* Необязательное переопределение: объект локали `react-day-picker` / `date-fns`. Для ядра
|
|
7048
|
+
* непрозрачен. Без него даты форматируются через `Intl` по {@link FormLocale.code}.
|
|
7049
|
+
*/
|
|
7050
|
+
readonly dateLocale?: unknown;
|
|
7051
|
+
/** Необязательное переопределение: формат даты в токенах `date-fns` для DatePicker. */
|
|
7052
|
+
readonly dateFormat?: string;
|
|
7053
|
+
}
|
|
7054
|
+
```
|
|
7055
|
+
|
|
7056
|
+
**Examples:**
|
|
7057
|
+
|
|
7058
|
+
```ts
|
|
7059
|
+
const ru: FormLocale = {
|
|
7060
|
+
code: 'ru',
|
|
7061
|
+
weekStartsOn: 1,
|
|
7062
|
+
messages: { 'kit.select.selected': 'Выбрано: {count}' },
|
|
7063
|
+
};
|
|
7064
|
+
```
|
|
7065
|
+
|
|
7066
|
+
_Source: src/i18n/locale.ts_
|
|
7067
|
+
|
|
6410
7068
|
### FormModel
|
|
6411
7069
|
|
|
6412
7070
|
**Kind:** `type`
|
|
@@ -6988,6 +7646,163 @@ export interface GroupNodeConfig<T> {
|
|
|
6988
7646
|
|
|
6989
7647
|
_Source: src/form/types/index.ts_
|
|
6990
7648
|
|
|
7649
|
+
### I18N_CONTEXT_MARKER
|
|
7650
|
+
|
|
7651
|
+
**Kind:** `const`
|
|
7652
|
+
|
|
7653
|
+
Имя контекста и одновременно маркер, по которому страж ищет его копии в сборках.
|
|
7654
|
+
|
|
7655
|
+
**Signature:**
|
|
7656
|
+
```typescript
|
|
7657
|
+
export const I18N_CONTEXT_MARKER
|
|
7658
|
+
```
|
|
7659
|
+
|
|
7660
|
+
_Source: src/platforms/react/i18n/context.ts_
|
|
7661
|
+
|
|
7662
|
+
### I18nContextValue
|
|
7663
|
+
|
|
7664
|
+
**Kind:** `interface`
|
|
7665
|
+
|
|
7666
|
+
Что видит компонент: ручка активной локали и состояние её загрузки.
|
|
7667
|
+
|
|
7668
|
+
**Signature:**
|
|
7669
|
+
```typescript
|
|
7670
|
+
export interface I18nContextValue extends I18nHandle {
|
|
7671
|
+
/** Идёт загрузка другого языка; на экране пока прежний. Без загрузчика всегда `false`. */
|
|
7672
|
+
readonly pending: boolean;
|
|
7673
|
+
/** Ошибка последней загрузки языка. */
|
|
7674
|
+
readonly error: unknown;
|
|
7675
|
+
}
|
|
7676
|
+
```
|
|
7677
|
+
|
|
7678
|
+
_Source: src/platforms/react/i18n/context.ts_
|
|
7679
|
+
|
|
7680
|
+
### I18nHandle
|
|
7681
|
+
|
|
7682
|
+
**Kind:** `interface`
|
|
7683
|
+
|
|
7684
|
+
Ручка локали: перевод и форматирование по одному языку.
|
|
7685
|
+
|
|
7686
|
+
**Signature:**
|
|
7687
|
+
```typescript
|
|
7688
|
+
export interface I18nHandle {
|
|
7689
|
+
/** Локаль, по которой работает ручка. */
|
|
7690
|
+
readonly locale: FormLocale;
|
|
7691
|
+
/** Код языка — то же, что `locale.code`. */
|
|
7692
|
+
readonly code: string;
|
|
7693
|
+
/** Есть ли ключ в словаре локали (встроенные английские таблицы не учитываются). */
|
|
7694
|
+
has(key: string): boolean;
|
|
7695
|
+
/**
|
|
7696
|
+
* Перевод ключа приложения: словарь локали → `defaultMessage` → встроенная таблица ядра → ключ.
|
|
7697
|
+
*
|
|
7698
|
+
* @param key - Ключ сообщения.
|
|
7699
|
+
* @param values - Значения подстановок.
|
|
7700
|
+
* @param defaultMessage - Английский текст на случай, если ключа в локали нет.
|
|
7701
|
+
*/
|
|
7702
|
+
t(key: string, values?: MessageValues, defaultMessage?: string): string;
|
|
7703
|
+
/** Число по правилам языка. */
|
|
7704
|
+
number(value: number, options?: Intl.NumberFormatOptions): string;
|
|
7705
|
+
/** Дата по правилам языка. */
|
|
7706
|
+
date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;
|
|
7707
|
+
/** Размер файла: `1536` → `1.5 KB` (`1,5 КБ` в русской локали). База — 1024. */
|
|
7708
|
+
fileSize(bytes: number): string;
|
|
7709
|
+
}
|
|
7710
|
+
```
|
|
7711
|
+
|
|
7712
|
+
_Source: src/i18n/translator.ts_
|
|
7713
|
+
|
|
7714
|
+
### I18nLoadingProviderProps
|
|
7715
|
+
|
|
7716
|
+
**Kind:** `interface`
|
|
7717
|
+
|
|
7718
|
+
Режим «провайдер грузит сам».
|
|
7719
|
+
|
|
7720
|
+
**Signature:**
|
|
7721
|
+
```typescript
|
|
7722
|
+
export interface I18nLoadingProviderProps {
|
|
7723
|
+
/** Код языка. Смена кода запускает загрузку; до её конца на экране прежний язык. */
|
|
7724
|
+
lang: string;
|
|
7725
|
+
/** Загрузчик локалей — обычно результат `createLocaleLoader`. */
|
|
7726
|
+
load: LocaleLoad;
|
|
7727
|
+
/**
|
|
7728
|
+
* Что показать, пока не загружена самая первая локаль. По умолчанию — ничего: иначе на кадр
|
|
7729
|
+
* мелькнул бы встроенный английский. Если локаль уже в кэше загрузчика (`load.preload(lang)`
|
|
7730
|
+
* до монтирования), первый рендер синхронный и `fallback` не показывается.
|
|
7731
|
+
*/
|
|
7732
|
+
fallback?: ReactNode;
|
|
7733
|
+
/** Отказ загрузки. Язык на экране при этом не меняется. */
|
|
7734
|
+
onError?: (error: unknown, lang: string) => void;
|
|
7735
|
+
children?: ReactNode;
|
|
7736
|
+
}
|
|
7737
|
+
```
|
|
7738
|
+
|
|
7739
|
+
_Source: src/platforms/react/i18n/provider.ts_
|
|
7740
|
+
|
|
7741
|
+
### I18nProvider
|
|
7742
|
+
|
|
7743
|
+
**Kind:** `function`
|
|
7744
|
+
|
|
7745
|
+
Провайдер локализации для подписей компонентов, ошибок валидации и авторских подписей формы.
|
|
7746
|
+
Без провайдера компоненты говорят встроенным английским.
|
|
7747
|
+
|
|
7748
|
+
При смене `lang` на экране остаётся прежний язык, пока новый не загрузится (`useI18n().pending`
|
|
7749
|
+
— для индикатора). При быстром переключении применяется только последний запрос. Вложенный
|
|
7750
|
+
провайдер заменяет локаль для своего поддерева целиком.
|
|
7751
|
+
|
|
7752
|
+
**Signature:**
|
|
7753
|
+
```typescript
|
|
7754
|
+
export function I18nProvider(props: I18nProviderProps): ReactElement | null
|
|
7755
|
+
```
|
|
7756
|
+
|
|
7757
|
+
**Examples:**
|
|
7758
|
+
|
|
7759
|
+
Провайдер грузит локаль сам
|
|
7760
|
+
```tsx
|
|
7761
|
+
const [lang, setLang] = useState('ru');
|
|
7762
|
+
|
|
7763
|
+
<I18nProvider lang={lang} load={loadLocale}>
|
|
7764
|
+
<LangSwitch value={lang} onChange={setLang} />
|
|
7765
|
+
<CreditForm />
|
|
7766
|
+
</I18nProvider>
|
|
7767
|
+
```
|
|
7768
|
+
|
|
7769
|
+
Готовая локаль — SSR и тесты
|
|
7770
|
+
```tsx
|
|
7771
|
+
import { ru } from '@reformer/ui-kit/locale/ru';
|
|
7772
|
+
|
|
7773
|
+
<I18nProvider locale={ru}><CreditForm /></I18nProvider>
|
|
7774
|
+
```
|
|
7775
|
+
|
|
7776
|
+
_Source: src/platforms/react/i18n/provider.ts_
|
|
7777
|
+
|
|
7778
|
+
### I18nProviderProps
|
|
7779
|
+
|
|
7780
|
+
**Kind:** `type`
|
|
7781
|
+
|
|
7782
|
+
**Signature:**
|
|
7783
|
+
```typescript
|
|
7784
|
+
export type I18nProviderProps = I18nStaticProviderProps | I18nLoadingProviderProps;
|
|
7785
|
+
```
|
|
7786
|
+
|
|
7787
|
+
_Source: src/platforms/react/i18n/provider.ts_
|
|
7788
|
+
|
|
7789
|
+
### I18nStaticProviderProps
|
|
7790
|
+
|
|
7791
|
+
**Kind:** `interface`
|
|
7792
|
+
|
|
7793
|
+
Режим «готовая локаль».
|
|
7794
|
+
|
|
7795
|
+
**Signature:**
|
|
7796
|
+
```typescript
|
|
7797
|
+
export interface I18nStaticProviderProps {
|
|
7798
|
+
/** Локаль. Смена объекта переключает язык сразу. */
|
|
7799
|
+
locale: FormLocale;
|
|
7800
|
+
children?: ReactNode;
|
|
7801
|
+
}
|
|
7802
|
+
```
|
|
7803
|
+
|
|
7804
|
+
_Source: src/platforms/react/i18n/provider.ts_
|
|
7805
|
+
|
|
6991
7806
|
### integer
|
|
6992
7807
|
|
|
6993
7808
|
**Kind:** `function`
|
|
@@ -7233,6 +8048,19 @@ if (isGroupNode(node)) {
|
|
|
7233
8048
|
|
|
7234
8049
|
_Source: src/form/type-guards.ts_
|
|
7235
8050
|
|
|
8051
|
+
### isMessageDescriptor
|
|
8052
|
+
|
|
8053
|
+
**Kind:** `function`
|
|
8054
|
+
|
|
8055
|
+
Является ли значение описателем сообщения.
|
|
8056
|
+
|
|
8057
|
+
**Signature:**
|
|
8058
|
+
```typescript
|
|
8059
|
+
export function isMessageDescriptor(value: unknown): value is MessageDescriptor
|
|
8060
|
+
```
|
|
8061
|
+
|
|
8062
|
+
_Source: src/i18n/descriptor.ts_
|
|
8063
|
+
|
|
7236
8064
|
### isModelContainerSignal
|
|
7237
8065
|
|
|
7238
8066
|
**Kind:** `function`
|
|
@@ -7293,6 +8121,207 @@ validate(model.$.amount, [required(), isNumber({ message: 'Введите чис
|
|
|
7293
8121
|
|
|
7294
8122
|
_Source: src/form/validators/is-number.ts_
|
|
7295
8123
|
|
|
8124
|
+
### isValueSignal
|
|
8125
|
+
|
|
8126
|
+
**Kind:** `function`
|
|
8127
|
+
|
|
8128
|
+
«Ручка значения», к которой можно привязать поле: лист (`instanceof Signal`) либо узел-массив
|
|
8129
|
+
дерева `model.$` (значение — массив целиком, `.value` читается и пишется).
|
|
8130
|
+
|
|
8131
|
+
Группа сюда не входит: она читается целиком, но не пишется и полем не бывает. Этой проверкой
|
|
8132
|
+
`createForm` и рендереры отличают узел-поле схемы (`{ value, component }`) от контейнера —
|
|
8133
|
+
вместо голого `value instanceof Signal`, который узел-массив не проходит.
|
|
8134
|
+
|
|
8135
|
+
**Signature:**
|
|
8136
|
+
```typescript
|
|
8137
|
+
export function isValueSignal(value: unknown): value is Signal<unknown>
|
|
8138
|
+
```
|
|
8139
|
+
|
|
8140
|
+
**Examples:**
|
|
8141
|
+
|
|
8142
|
+
```typescript
|
|
8143
|
+
const model = createModel({ name: '', tags: [] as string[], address: { city: '' } });
|
|
8144
|
+
isValueSignal(model.$.name); // true — лист
|
|
8145
|
+
isValueSignal(model.$.tags); // true — массив как значение
|
|
8146
|
+
isValueSignal(model.$.address); // false — группа
|
|
8147
|
+
```
|
|
8148
|
+
|
|
8149
|
+
_Source: src/model/model-signals-proxy.ts_
|
|
8150
|
+
|
|
8151
|
+
### loadCoreLocale
|
|
8152
|
+
|
|
8153
|
+
**Kind:** `const`
|
|
8154
|
+
|
|
8155
|
+
Источник локалей ядра для `createLocaleLoader`: чанк нужного языка по запросу.
|
|
8156
|
+
|
|
8157
|
+
**Signature:**
|
|
8158
|
+
```typescript
|
|
8159
|
+
export const loadCoreLocale
|
|
8160
|
+
```
|
|
8161
|
+
|
|
8162
|
+
**Examples:**
|
|
8163
|
+
|
|
8164
|
+
```ts
|
|
8165
|
+
const loadLocale = createLocaleLoader([loadCoreLocale, fetchMessages((code) => `/locales/${code}.json`)]);
|
|
8166
|
+
```
|
|
8167
|
+
|
|
8168
|
+
_Source: src/locale/index.ts_
|
|
8169
|
+
|
|
8170
|
+
### LocaleIssue
|
|
8171
|
+
|
|
8172
|
+
**Kind:** `interface`
|
|
8173
|
+
|
|
8174
|
+
Что не так со словарём.
|
|
8175
|
+
|
|
8176
|
+
**Signature:**
|
|
8177
|
+
```typescript
|
|
8178
|
+
export interface LocaleIssue {
|
|
8179
|
+
readonly key: string;
|
|
8180
|
+
/**
|
|
8181
|
+
* - `syntax` — сообщение не разбирается;
|
|
8182
|
+
* - `missing` — ключ есть в образце, но его нет в словаре;
|
|
8183
|
+
* - `extra` — ключа нет в образце;
|
|
8184
|
+
* - `arguments` — набор подстановок не совпадает с образцом.
|
|
8185
|
+
*/
|
|
8186
|
+
readonly kind: 'syntax' | 'missing' | 'extra' | 'arguments';
|
|
8187
|
+
readonly message: string;
|
|
8188
|
+
}
|
|
8189
|
+
```
|
|
8190
|
+
|
|
8191
|
+
_Source: src/i18n/locale.ts_
|
|
8192
|
+
|
|
8193
|
+
### LocaleLoad
|
|
8194
|
+
|
|
8195
|
+
**Kind:** `interface`
|
|
8196
|
+
|
|
8197
|
+
Функция загрузки локали. `peek` — необязательный синхронный кэш (есть у `createLocaleLoader`).
|
|
8198
|
+
|
|
8199
|
+
**Signature:**
|
|
8200
|
+
```typescript
|
|
8201
|
+
export interface LocaleLoad {
|
|
8202
|
+
(code: string): Promise<FormLocale>;
|
|
8203
|
+
peek?(code: string): FormLocale | undefined;
|
|
8204
|
+
}
|
|
8205
|
+
```
|
|
8206
|
+
|
|
8207
|
+
_Source: src/i18n/locale-store.ts_
|
|
8208
|
+
|
|
8209
|
+
### LocaleLoader
|
|
8210
|
+
|
|
8211
|
+
**Kind:** `interface`
|
|
8212
|
+
|
|
8213
|
+
Загрузчик локалей с кэшем.
|
|
8214
|
+
|
|
8215
|
+
**Signature:**
|
|
8216
|
+
```typescript
|
|
8217
|
+
export interface LocaleLoader {
|
|
8218
|
+
/** Загрузить локаль языка. Повторный вызов возвращает тот же промис и ту же локаль. */
|
|
8219
|
+
(code: string): Promise<FormLocale>;
|
|
8220
|
+
/** Уже загруженная локаль или `undefined`. Позволяет провайдеру отрисоваться синхронно. */
|
|
8221
|
+
peek(code: string): FormLocale | undefined;
|
|
8222
|
+
/** То же, что вызов загрузчика; имя для чтения в коде старта приложения. */
|
|
8223
|
+
preload(code: string): Promise<FormLocale>;
|
|
8224
|
+
}
|
|
8225
|
+
```
|
|
8226
|
+
|
|
8227
|
+
_Source: src/i18n/loader.ts_
|
|
8228
|
+
|
|
8229
|
+
### LocaleSource
|
|
8230
|
+
|
|
8231
|
+
**Kind:** `type`
|
|
8232
|
+
|
|
8233
|
+
Источник локали. Получает код языка, отдаёт локаль или словарь — сразу или промисом.
|
|
8234
|
+
`null`/`undefined` означает «для этого языка у меня ничего нет» и ошибкой не считается.
|
|
8235
|
+
|
|
8236
|
+
**Signature:**
|
|
8237
|
+
```typescript
|
|
8238
|
+
export type LocaleSource = (code: string) => LocaleSourceResult | Promise<LocaleSourceResult>;
|
|
8239
|
+
```
|
|
8240
|
+
|
|
8241
|
+
_Source: src/i18n/loader.ts_
|
|
8242
|
+
|
|
8243
|
+
### LocaleSourceResult
|
|
8244
|
+
|
|
8245
|
+
**Kind:** `type`
|
|
8246
|
+
|
|
8247
|
+
Что может вернуть источник: локаль целиком, её часть, голый словарь или ничего.
|
|
8248
|
+
|
|
8249
|
+
**Signature:**
|
|
8250
|
+
```typescript
|
|
8251
|
+
export type LocaleSourceResult = Partial<FormLocale> | Messages | null | undefined;
|
|
8252
|
+
```
|
|
8253
|
+
|
|
8254
|
+
_Source: src/i18n/loader.ts_
|
|
8255
|
+
|
|
8256
|
+
### LocaleStore
|
|
8257
|
+
|
|
8258
|
+
**Kind:** `interface`
|
|
8259
|
+
|
|
8260
|
+
Хранилище активной локали.
|
|
8261
|
+
|
|
8262
|
+
**Signature:**
|
|
8263
|
+
```typescript
|
|
8264
|
+
export interface LocaleStore {
|
|
8265
|
+
getState(): LocaleStoreState;
|
|
8266
|
+
subscribe(listener: () => void): () => void;
|
|
8267
|
+
/** Запросить язык. Повторный запрос того же языка, пока он грузится или уже показан, — no-op. */
|
|
8268
|
+
setLang(lang: string): void;
|
|
8269
|
+
}
|
|
8270
|
+
```
|
|
8271
|
+
|
|
8272
|
+
_Source: src/i18n/locale-store.ts_
|
|
8273
|
+
|
|
8274
|
+
### LocaleStoreOptions
|
|
8275
|
+
|
|
8276
|
+
**Kind:** `interface`
|
|
8277
|
+
|
|
8278
|
+
Настройки хранилища.
|
|
8279
|
+
|
|
8280
|
+
**Signature:**
|
|
8281
|
+
```typescript
|
|
8282
|
+
export interface LocaleStoreOptions {
|
|
8283
|
+
/** Вызывается при отказе загрузки языка, который всё ещё запрошен. */
|
|
8284
|
+
readonly onError?: (error: unknown, lang: string) => void;
|
|
8285
|
+
}
|
|
8286
|
+
```
|
|
8287
|
+
|
|
8288
|
+
_Source: src/i18n/locale-store.ts_
|
|
8289
|
+
|
|
8290
|
+
### LocaleStoreState
|
|
8291
|
+
|
|
8292
|
+
**Kind:** `interface`
|
|
8293
|
+
|
|
8294
|
+
Состояние хранилища. Объект неизменяемый: новая ссылка — новое состояние.
|
|
8295
|
+
|
|
8296
|
+
**Signature:**
|
|
8297
|
+
```typescript
|
|
8298
|
+
export interface LocaleStoreState {
|
|
8299
|
+
/** Запрошенный язык. Пока `pending`, может отличаться от языка `locale`. */
|
|
8300
|
+
readonly lang: string;
|
|
8301
|
+
/** Локаль, которая сейчас на экране; `null` — ни одна ещё не загружена. */
|
|
8302
|
+
readonly locale: FormLocale | null;
|
|
8303
|
+
/** Идёт загрузка запрошенного языка. */
|
|
8304
|
+
readonly pending: boolean;
|
|
8305
|
+
/** Ошибка последней загрузки; сбрасывается следующей удачной. */
|
|
8306
|
+
readonly error: unknown;
|
|
8307
|
+
}
|
|
8308
|
+
```
|
|
8309
|
+
|
|
8310
|
+
_Source: src/i18n/locale-store.ts_
|
|
8311
|
+
|
|
8312
|
+
### LocalizableText
|
|
8313
|
+
|
|
8314
|
+
**Kind:** `type`
|
|
8315
|
+
|
|
8316
|
+
Текст, который можно задать строкой или описателем.
|
|
8317
|
+
|
|
8318
|
+
**Signature:**
|
|
8319
|
+
```typescript
|
|
8320
|
+
export type LocalizableText = string | MessageDescriptor;
|
|
8321
|
+
```
|
|
8322
|
+
|
|
8323
|
+
_Source: src/i18n/descriptor.ts_
|
|
8324
|
+
|
|
7296
8325
|
### makeElementFieldHandle
|
|
7297
8326
|
|
|
7298
8327
|
**Kind:** `function`
|
|
@@ -7538,114 +8567,272 @@ _Source: src/form/validators/max-files.ts_
|
|
|
7538
8567
|
|
|
7539
8568
|
**Signature:**
|
|
7540
8569
|
```typescript
|
|
7541
|
-
export function maxFileSize<TForm = unknown, TField = unknown>(
|
|
7542
|
-
maxSize: number,
|
|
7543
|
-
options?: ValidateOptions
|
|
7544
|
-
): Validator<TForm, TField>
|
|
8570
|
+
export function maxFileSize<TForm = unknown, TField = unknown>(
|
|
8571
|
+
maxSize: number,
|
|
8572
|
+
options?: ValidateOptions
|
|
8573
|
+
): Validator<TForm, TField>
|
|
8574
|
+
```
|
|
8575
|
+
|
|
8576
|
+
**Parameters:**
|
|
8577
|
+
- `maxSize` — - Максимально допустимый размер файла в байтах (включительно)
|
|
8578
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
8579
|
+
попадают `maxFileSize`, `fileName` и `actualSize`.
|
|
8580
|
+
|
|
8581
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
8582
|
+
|
|
8583
|
+
**Examples:**
|
|
8584
|
+
|
|
8585
|
+
Ограничение размера вложений
|
|
8586
|
+
```typescript
|
|
8587
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8588
|
+
import { maxFileSize } from '@reformer/core/validators';
|
|
8589
|
+
|
|
8590
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8591
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8592
|
+
validate(model.$.documents, [maxFileSize(5 * 1024 * 1024, { message: 'Файл больше 5 МБ' })]);
|
|
8593
|
+
});
|
|
8594
|
+
```
|
|
8595
|
+
|
|
8596
|
+
_Source: src/form/validators/max-file-size.ts_
|
|
8597
|
+
|
|
8598
|
+
### maxLength
|
|
8599
|
+
|
|
8600
|
+
**Kind:** `function`
|
|
8601
|
+
|
|
8602
|
+
Фабрика валидатора максимальной длины строки или массива.
|
|
8603
|
+
|
|
8604
|
+
Работает со строкой или массивом (проверяется `value.length`). Пустые значения
|
|
8605
|
+
(`null`/`undefined`/`''`) и значения без числового `length` пропускаются
|
|
8606
|
+
(используйте {@link required} для обязательности).
|
|
8607
|
+
|
|
8608
|
+
**Signature:**
|
|
8609
|
+
```typescript
|
|
8610
|
+
export function maxLength<TForm = unknown, TField = unknown>(
|
|
8611
|
+
maxLen: number,
|
|
8612
|
+
options?: ValidateOptions
|
|
8613
|
+
): Validator<TForm, TField>
|
|
8614
|
+
```
|
|
8615
|
+
|
|
8616
|
+
**Parameters:**
|
|
8617
|
+
- `maxLen` — - Максимально допустимая длина (включительно)
|
|
8618
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
8619
|
+
попадают `maxLength` и `actualLength`.
|
|
8620
|
+
|
|
8621
|
+
**Returns:** Чистый валидатор {@link Validator} для строки или массива
|
|
8622
|
+
|
|
8623
|
+
**Examples:**
|
|
8624
|
+
|
|
8625
|
+
Максимальная длина строки
|
|
8626
|
+
```typescript
|
|
8627
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8628
|
+
import { maxLength } from '@reformer/core/validators';
|
|
8629
|
+
|
|
8630
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8631
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8632
|
+
validate(model.$.name, [maxLength(50)]);
|
|
8633
|
+
validate(model.$.bio, [maxLength(500, { message: 'Максимум 500 символов' })]);
|
|
8634
|
+
});
|
|
8635
|
+
```
|
|
8636
|
+
|
|
8637
|
+
_Source: src/form/validators/max-length.ts_
|
|
8638
|
+
|
|
8639
|
+
### maxTotalFileSize
|
|
8640
|
+
|
|
8641
|
+
**Kind:** `function`
|
|
8642
|
+
|
|
8643
|
+
Фабрика валидатора суммарного размера всех файлов.
|
|
8644
|
+
|
|
8645
|
+
Складывает `size` всех файлов значения (элементы без числового `size` в сумму
|
|
8646
|
+
не входят). Пустые значения (`null`/`undefined`/`''`/`[]`) пропускаются
|
|
8647
|
+
(используйте {@link required} для обязательности).
|
|
8648
|
+
|
|
8649
|
+
**Signature:**
|
|
8650
|
+
```typescript
|
|
8651
|
+
export function maxTotalFileSize<TForm = unknown, TField = unknown>(
|
|
8652
|
+
maxTotal: number,
|
|
8653
|
+
options?: ValidateOptions
|
|
8654
|
+
): Validator<TForm, TField>
|
|
8655
|
+
```
|
|
8656
|
+
|
|
8657
|
+
**Parameters:**
|
|
8658
|
+
- `maxTotal` — - Максимально допустимый суммарный размер в байтах (включительно)
|
|
8659
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
8660
|
+
попадают `maxTotalFileSize` и `actualTotal`.
|
|
8661
|
+
|
|
8662
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
8663
|
+
|
|
8664
|
+
**Examples:**
|
|
8665
|
+
|
|
8666
|
+
Суммарно не более 20 МБ
|
|
8667
|
+
```typescript
|
|
8668
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8669
|
+
import { maxTotalFileSize } from '@reformer/core/validators';
|
|
8670
|
+
|
|
8671
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8672
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8673
|
+
validate(model.$.documents, [maxTotalFileSize(20 * 1024 * 1024, { message: 'Суммарно не более 20 МБ' })]);
|
|
8674
|
+
});
|
|
8675
|
+
```
|
|
8676
|
+
|
|
8677
|
+
_Source: src/form/validators/max-total-file-size.ts_
|
|
8678
|
+
|
|
8679
|
+
### MESSAGE_DESCRIPTOR
|
|
8680
|
+
|
|
8681
|
+
**Kind:** `const`
|
|
8682
|
+
|
|
8683
|
+
Бренд описателя. `Symbol.for` — чтобы описатель узнавался и через границу двух копий модуля
|
|
8684
|
+
(разные чанки, разные версии пакета на странице).
|
|
8685
|
+
|
|
8686
|
+
**Signature:**
|
|
8687
|
+
```typescript
|
|
8688
|
+
export const MESSAGE_DESCRIPTOR: unique symbol
|
|
8689
|
+
```
|
|
8690
|
+
|
|
8691
|
+
_Source: src/i18n/descriptor.ts_
|
|
8692
|
+
|
|
8693
|
+
### messageArguments
|
|
8694
|
+
|
|
8695
|
+
**Kind:** `function`
|
|
8696
|
+
|
|
8697
|
+
Имена аргументов сообщения, включая те, что встречаются только внутри веток `plural`/`select`.
|
|
8698
|
+
|
|
8699
|
+
**Signature:**
|
|
8700
|
+
```typescript
|
|
8701
|
+
export function messageArguments(
|
|
8702
|
+
pattern: MessagePattern,
|
|
8703
|
+
out: Set<string> = new Set()
|
|
8704
|
+
): Set<string>
|
|
8705
|
+
```
|
|
8706
|
+
|
|
8707
|
+
**Parameters:**
|
|
8708
|
+
- `pattern` — - Результат `parseMessage`.
|
|
8709
|
+
|
|
8710
|
+
_Source: src/i18n/locale.ts_
|
|
8711
|
+
|
|
8712
|
+
### MessageBranches
|
|
8713
|
+
|
|
8714
|
+
**Kind:** `type`
|
|
8715
|
+
|
|
8716
|
+
Ветки `plural`/`select`: категория CLDR (`one`, `few`, …), точное совпадение (`=0`) или значение `select`.
|
|
8717
|
+
|
|
8718
|
+
**Signature:**
|
|
8719
|
+
```typescript
|
|
8720
|
+
export type MessageBranches = ReadonlyMap<string, MessagePattern>;
|
|
8721
|
+
```
|
|
8722
|
+
|
|
8723
|
+
_Source: src/i18n/message-format.ts_
|
|
8724
|
+
|
|
8725
|
+
### MessageDescriptor
|
|
8726
|
+
|
|
8727
|
+
**Kind:** `interface`
|
|
8728
|
+
|
|
8729
|
+
Описатель сообщения. Намеренно без ключей `value`, `array`, `item`, `component`: обход схемы в
|
|
8730
|
+
`createForm` узнаёт по ним узел, а описатель узлом не является.
|
|
8731
|
+
|
|
8732
|
+
**Signature:**
|
|
8733
|
+
```typescript
|
|
8734
|
+
export interface MessageDescriptor {
|
|
8735
|
+
readonly [MESSAGE_DESCRIPTOR]: true;
|
|
8736
|
+
/** Ключ сообщения в словаре приложения. */
|
|
8737
|
+
readonly key: string;
|
|
8738
|
+
/** Значения подстановок. */
|
|
8739
|
+
readonly values?: Readonly<Record<string, unknown>>;
|
|
8740
|
+
/** Английский текст на случай, если ключа в локали нет. */
|
|
8741
|
+
readonly defaultMessage?: string;
|
|
8742
|
+
}
|
|
8743
|
+
```
|
|
8744
|
+
|
|
8745
|
+
_Source: src/i18n/descriptor.ts_
|
|
8746
|
+
|
|
8747
|
+
### MessageFormatOptions
|
|
8748
|
+
|
|
8749
|
+
**Kind:** `interface`
|
|
8750
|
+
|
|
8751
|
+
Настройки форматирования.
|
|
8752
|
+
|
|
8753
|
+
**Signature:**
|
|
8754
|
+
```typescript
|
|
8755
|
+
export interface MessageFormatOptions {
|
|
8756
|
+
/**
|
|
8757
|
+
* Что подставить вместо аргумента, которого нет в значениях (или чьё значение не число там, где
|
|
8758
|
+
* ожидалось число). По умолчанию — заметный маркер `⟦name⟧`, тот же приём, что и для пропущенного
|
|
8759
|
+
* ключа: пропуск должен попадаться разработчику, а не пользователю.
|
|
8760
|
+
*/
|
|
8761
|
+
readonly onMissingArgument?: (name: string) => string;
|
|
8762
|
+
}
|
|
7545
8763
|
```
|
|
7546
8764
|
|
|
7547
|
-
|
|
7548
|
-
- `maxSize` — - Максимально допустимый размер файла в байтах (включительно)
|
|
7549
|
-
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7550
|
-
попадают `maxFileSize`, `fileName` и `actualSize`.
|
|
8765
|
+
_Source: src/i18n/message-format.ts_
|
|
7551
8766
|
|
|
7552
|
-
|
|
8767
|
+
### MessageNode
|
|
7553
8768
|
|
|
7554
|
-
**
|
|
8769
|
+
**Kind:** `type`
|
|
7555
8770
|
|
|
7556
|
-
|
|
7557
|
-
```typescript
|
|
7558
|
-
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7559
|
-
import { maxFileSize } from '@reformer/core/validators';
|
|
8771
|
+
Узел разобранного сообщения.
|
|
7560
8772
|
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
}
|
|
8773
|
+
**Signature:**
|
|
8774
|
+
```typescript
|
|
8775
|
+
export type MessageNode =
|
|
8776
|
+
| { readonly kind: 'text'; readonly value: string }
|
|
8777
|
+
| { readonly kind: 'arg'; readonly name: string }
|
|
8778
|
+
| { readonly kind: 'number'; readonly name: string }
|
|
8779
|
+
| { readonly kind: 'plural'; readonly name: string; readonly branches: MessageBranches }
|
|
8780
|
+
| { readonly kind: 'select'; readonly name: string; readonly branches: MessageBranches };
|
|
7565
8781
|
```
|
|
7566
8782
|
|
|
7567
|
-
_Source: src/
|
|
7568
|
-
|
|
7569
|
-
### maxLength
|
|
8783
|
+
_Source: src/i18n/message-format.ts_
|
|
7570
8784
|
|
|
7571
|
-
|
|
8785
|
+
### MessagePattern
|
|
7572
8786
|
|
|
7573
|
-
|
|
8787
|
+
**Kind:** `type`
|
|
7574
8788
|
|
|
7575
|
-
|
|
7576
|
-
(`null`/`undefined`/`''`) и значения без числового `length` пропускаются
|
|
7577
|
-
(используйте {@link required} для обязательности).
|
|
8789
|
+
Разобранное сообщение — плоская последовательность узлов.
|
|
7578
8790
|
|
|
7579
8791
|
**Signature:**
|
|
7580
8792
|
```typescript
|
|
7581
|
-
export
|
|
7582
|
-
maxLen: number,
|
|
7583
|
-
options?: ValidateOptions
|
|
7584
|
-
): Validator<TForm, TField>
|
|
8793
|
+
export type MessagePattern = readonly MessageNode[];
|
|
7585
8794
|
```
|
|
7586
8795
|
|
|
7587
|
-
|
|
7588
|
-
- `maxLen` — - Максимально допустимая длина (включительно)
|
|
7589
|
-
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7590
|
-
попадают `maxLength` и `actualLength`.
|
|
8796
|
+
_Source: src/i18n/message-format.ts_
|
|
7591
8797
|
|
|
7592
|
-
|
|
8798
|
+
### Messages
|
|
7593
8799
|
|
|
7594
|
-
**
|
|
8800
|
+
**Kind:** `type`
|
|
7595
8801
|
|
|
7596
|
-
|
|
7597
|
-
```typescript
|
|
7598
|
-
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7599
|
-
import { maxLength } from '@reformer/core/validators';
|
|
8802
|
+
Плоский словарь: ключ → сообщение в формате ICU (см. `message-format.ts`).
|
|
7600
8803
|
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
validate(model.$.bio, [maxLength(500, { message: 'Максимум 500 символов' })]);
|
|
7605
|
-
});
|
|
8804
|
+
**Signature:**
|
|
8805
|
+
```typescript
|
|
8806
|
+
export type Messages = Readonly<Record<string, string>>;
|
|
7606
8807
|
```
|
|
7607
8808
|
|
|
7608
|
-
_Source: src/
|
|
7609
|
-
|
|
7610
|
-
### maxTotalFileSize
|
|
8809
|
+
_Source: src/i18n/locale.ts_
|
|
7611
8810
|
|
|
7612
|
-
|
|
8811
|
+
### MessageSyntaxError
|
|
7613
8812
|
|
|
7614
|
-
|
|
8813
|
+
**Kind:** `class`
|
|
7615
8814
|
|
|
7616
|
-
|
|
7617
|
-
не входят). Пустые значения (`null`/`undefined`/`''`/`[]`) пропускаются
|
|
7618
|
-
(используйте {@link required} для обязательности).
|
|
8815
|
+
Отказ разбора сообщения: синтаксис или отсутствие обязательной ветки `other`.
|
|
7619
8816
|
|
|
7620
8817
|
**Signature:**
|
|
7621
8818
|
```typescript
|
|
7622
|
-
export
|
|
7623
|
-
maxTotal: number,
|
|
7624
|
-
options?: ValidateOptions
|
|
7625
|
-
): Validator<TForm, TField>
|
|
8819
|
+
export class MessageSyntaxError extends Error { /* … */ }
|
|
7626
8820
|
```
|
|
7627
8821
|
|
|
7628
|
-
|
|
7629
|
-
- `maxTotal` — - Максимально допустимый суммарный размер в байтах (включительно)
|
|
7630
|
-
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7631
|
-
попадают `maxTotalFileSize` и `actualTotal`.
|
|
8822
|
+
_Source: src/i18n/message-format.ts_
|
|
7632
8823
|
|
|
7633
|
-
|
|
8824
|
+
### MessageValues
|
|
7634
8825
|
|
|
7635
|
-
**
|
|
8826
|
+
**Kind:** `type`
|
|
7636
8827
|
|
|
7637
|
-
|
|
7638
|
-
```typescript
|
|
7639
|
-
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7640
|
-
import { maxTotalFileSize } from '@reformer/core/validators';
|
|
8828
|
+
Значения аргументов сообщения.
|
|
7641
8829
|
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
});
|
|
8830
|
+
**Signature:**
|
|
8831
|
+
```typescript
|
|
8832
|
+
export type MessageValues = Readonly<Record<string, unknown>>;
|
|
7646
8833
|
```
|
|
7647
8834
|
|
|
7648
|
-
_Source: src/
|
|
8835
|
+
_Source: src/i18n/message-format.ts_
|
|
7649
8836
|
|
|
7650
8837
|
### min
|
|
7651
8838
|
|
|
@@ -7923,7 +9110,10 @@ export interface ModelApi<T> {
|
|
|
7923
9110
|
reset(): void;
|
|
7924
9111
|
/** Зафиксировать текущие значения как новый initial-снимок («точка отсчёта»). */
|
|
7925
9112
|
captureInitial(): void;
|
|
7926
|
-
/**
|
|
9113
|
+
/**
|
|
9114
|
+
* Резолв строкового пути в сигнал (для error-routing/мостов): лист либо массив целиком
|
|
9115
|
+
* (ручка значения массива — см. {@link ModelArraySignals}). Для пути группы — `undefined`.
|
|
9116
|
+
*/
|
|
7927
9117
|
signalAt(path: string): PathAwareSignal<unknown> | undefined;
|
|
7928
9118
|
}
|
|
7929
9119
|
```
|
|
@@ -8052,12 +9242,23 @@ _Source: src/form/nodes/model-array-node.ts_
|
|
|
8052
9242
|
**Kind:** `type`
|
|
8053
9243
|
|
|
8054
9244
|
Узел-массив дерева `model.$`: индексируемый доступ к под-сигналам элементов, реактивная `length`
|
|
8055
|
-
и
|
|
8056
|
-
|
|
9245
|
+
и сигнал значения массива целиком (реагирует и на правку элемента, и на изменение состава —
|
|
9246
|
+
push/removeAt/move).
|
|
9247
|
+
|
|
9248
|
+
В отличие от группы, узел ЗАПИСЫВАЕМ: `model.$.tags.value = ['a']` заменяет массив целиком. Это
|
|
9249
|
+
делает его {@link PathAwareSignal} — к нему привязывают поле (`{ value: model.$.tags, component }`),
|
|
9250
|
+
правило (`validate(model.$.tags, …)`) и поведение, как к листу. Так массив служит ОДНИМ значением
|
|
9251
|
+
поля: мультивыбор, теги, список файлов.
|
|
9252
|
+
|
|
9253
|
+
`V` — объявленный тип поля, включая `null`/`undefined`: поле `string[] | null` с начальным `null`
|
|
9254
|
+
в рантайме — лист с тем же `.value`/`peek`/`subscribe`, поэтому общая часть честна для обоих
|
|
9255
|
+
случаев. `length` и индексы есть только у настоящего узла-массива (начальное значение — массив).
|
|
8057
9256
|
|
|
8058
9257
|
**Signature:**
|
|
8059
9258
|
```typescript
|
|
8060
|
-
export type ModelArraySignals<U, V> =
|
|
9259
|
+
export type ModelArraySignals<U, V> = Signal<V> & {
|
|
9260
|
+
/** Путь массива в модели (dot-нотация). */
|
|
9261
|
+
readonly __path: string;
|
|
8061
9262
|
readonly length: number;
|
|
8062
9263
|
readonly [index: number]: ModelSignalNode<U>;
|
|
8063
9264
|
};
|
|
@@ -8140,6 +9341,34 @@ export type ModelValue<V> =
|
|
|
8140
9341
|
|
|
8141
9342
|
_Source: src/model/types.ts_
|
|
8142
9343
|
|
|
9344
|
+
### msg
|
|
9345
|
+
|
|
9346
|
+
**Kind:** `function`
|
|
9347
|
+
|
|
9348
|
+
Создаёт описатель сообщения.
|
|
9349
|
+
|
|
9350
|
+
**Signature:**
|
|
9351
|
+
```typescript
|
|
9352
|
+
export function msg(
|
|
9353
|
+
key: string,
|
|
9354
|
+
values?: Readonly<Record<string, unknown>>,
|
|
9355
|
+
defaultMessage?: string
|
|
9356
|
+
): MessageDescriptor
|
|
9357
|
+
```
|
|
9358
|
+
|
|
9359
|
+
**Parameters:**
|
|
9360
|
+
- `key` — - Ключ в словаре приложения.
|
|
9361
|
+
- `values` — - Значения подстановок.
|
|
9362
|
+
- `defaultMessage` — - Английский текст на случай, если ключа в локали нет.
|
|
9363
|
+
|
|
9364
|
+
**Examples:**
|
|
9365
|
+
|
|
9366
|
+
```ts
|
|
9367
|
+
{ value: model.$.email, component: Input, componentProps: { label: msg('profile.email.label') } }
|
|
9368
|
+
```
|
|
9369
|
+
|
|
9370
|
+
_Source: src/i18n/descriptor.ts_
|
|
9371
|
+
|
|
8143
9372
|
### multipleOf
|
|
8144
9373
|
|
|
8145
9374
|
**Kind:** `function`
|
|
@@ -8356,6 +9585,31 @@ export function onDispose(cleanup: BehaviorCleanup): void
|
|
|
8356
9585
|
|
|
8357
9586
|
_Source: src/form/behaviors/context.ts_
|
|
8358
9587
|
|
|
9588
|
+
### parseMessage
|
|
9589
|
+
|
|
9590
|
+
**Kind:** `function`
|
|
9591
|
+
|
|
9592
|
+
Разбирает сообщение в дерево. Бросает {@link MessageSyntaxError} на ошибке синтаксиса и на
|
|
9593
|
+
`plural`/`select` без ветки `other`.
|
|
9594
|
+
|
|
9595
|
+
**Signature:**
|
|
9596
|
+
```typescript
|
|
9597
|
+
export function parseMessage(source: string): MessagePattern
|
|
9598
|
+
```
|
|
9599
|
+
|
|
9600
|
+
**Parameters:**
|
|
9601
|
+
- `source` — - Текст сообщения из словаря.
|
|
9602
|
+
|
|
9603
|
+
**Returns:** Разобранное сообщение для {@link formatPattern}.
|
|
9604
|
+
|
|
9605
|
+
**Examples:**
|
|
9606
|
+
|
|
9607
|
+
```ts
|
|
9608
|
+
parseMessage('{count, plural, one{# файл} few{# файла} many{# файлов} other{# файла}}');
|
|
9609
|
+
```
|
|
9610
|
+
|
|
9611
|
+
_Source: src/i18n/message-format.ts_
|
|
9612
|
+
|
|
8359
9613
|
### pastDate
|
|
8360
9614
|
|
|
8361
9615
|
**Kind:** `function`
|
|
@@ -8620,6 +9874,60 @@ resetWhen(model.$.cardNumber, () => model.paymentType !== 'card', { resetValue:
|
|
|
8620
9874
|
|
|
8621
9875
|
_Source: src/model/behaviors-value.ts_
|
|
8622
9876
|
|
|
9877
|
+
### resolveLocalized
|
|
9878
|
+
|
|
9879
|
+
**Kind:** `function`
|
|
9880
|
+
|
|
9881
|
+
Заменяет описатели сообщений на строки по активной локали. Обходит массивы и простые
|
|
9882
|
+
объекты-литералы — подписи опций, заголовки шагов; сигналы, React-элементы, функции и
|
|
9883
|
+
экземпляры классов (узлы формы) возвращает как есть. Поддерево без описателей сохраняет ссылку:
|
|
9884
|
+
если раскрывать было нечего, возвращается тот же объект.
|
|
9885
|
+
|
|
9886
|
+
**Signature:**
|
|
9887
|
+
```typescript
|
|
9888
|
+
export function resolveLocalized<T>(value: T, i18n: I18nHandle): T
|
|
9889
|
+
```
|
|
9890
|
+
|
|
9891
|
+
**Parameters:**
|
|
9892
|
+
- `value` — - Пропсы, массив или одиночное значение.
|
|
9893
|
+
- `i18n` — - Ручка активной локали (`createI18n` / `useI18n`).
|
|
9894
|
+
|
|
9895
|
+
**Examples:**
|
|
9896
|
+
|
|
9897
|
+
```ts
|
|
9898
|
+
resolveLocalized({ label: msg('profile.email.label'), options: [{ value: 'a', label: msg('opt.a') }] }, i18n);
|
|
9899
|
+
// { label: 'Почта', options: [{ value: 'a', label: 'Вариант А' }] }
|
|
9900
|
+
```
|
|
9901
|
+
|
|
9902
|
+
_Source: src/i18n/resolve-localized.ts_
|
|
9903
|
+
|
|
9904
|
+
### resolveValidationError
|
|
9905
|
+
|
|
9906
|
+
**Kind:** `function`
|
|
9907
|
+
|
|
9908
|
+
Отображаемый текст ошибки валидации. Порядок источников — в шапке модуля.
|
|
9909
|
+
|
|
9910
|
+
**Signature:**
|
|
9911
|
+
```typescript
|
|
9912
|
+
export function resolveValidationError(error: ValidationError, i18n: I18nHandle): string
|
|
9913
|
+
```
|
|
9914
|
+
|
|
9915
|
+
**Parameters:**
|
|
9916
|
+
- `error` — - Ошибка от правила: `code`, `message`, `messageKey`, `params`.
|
|
9917
|
+
- `i18n` — - Ручка активной локали (`createI18n` / `useI18n`).
|
|
9918
|
+
|
|
9919
|
+
**Examples:**
|
|
9920
|
+
|
|
9921
|
+
```ts
|
|
9922
|
+
resolveValidationError({ code: 'minLength', message: '', params: { minLength: 8 } }, i18n);
|
|
9923
|
+
// 'Enter at least 8 characters' · в русской локали — 'Не меньше 8 символов'
|
|
9924
|
+
|
|
9925
|
+
resolveValidationError({ code: 'required', message: 'Укажите телефон' }, i18n);
|
|
9926
|
+
// 'Укажите телефон' — явное сообщение автора важнее словаря
|
|
9927
|
+
```
|
|
9928
|
+
|
|
9929
|
+
_Source: src/i18n/validation-message.ts_
|
|
9930
|
+
|
|
8623
9931
|
### revalidateWhen
|
|
8624
9932
|
|
|
8625
9933
|
**Kind:** `function`
|
|
@@ -8984,6 +10292,32 @@ transformValue(model.$.promoCode, (v) => (v ?? '').toUpperCase());
|
|
|
8984
10292
|
|
|
8985
10293
|
_Source: src/model/behaviors-value.ts_
|
|
8986
10294
|
|
|
10295
|
+
### translateBuiltin
|
|
10296
|
+
|
|
10297
|
+
**Kind:** `function`
|
|
10298
|
+
|
|
10299
|
+
Перевод ключа пакета: словарь локали, затем встроенная английская таблица пакета, затем сам
|
|
10300
|
+
ключ. Английская таблица форматируется по английским правилам — её множественные формы и
|
|
10301
|
+
группировка чисел не зависят от активного языка.
|
|
10302
|
+
|
|
10303
|
+
**Signature:**
|
|
10304
|
+
```typescript
|
|
10305
|
+
export function translateBuiltin(
|
|
10306
|
+
locale: FormLocale,
|
|
10307
|
+
builtin: Messages,
|
|
10308
|
+
key: string,
|
|
10309
|
+
values?: MessageValues
|
|
10310
|
+
): string
|
|
10311
|
+
```
|
|
10312
|
+
|
|
10313
|
+
**Parameters:**
|
|
10314
|
+
- `locale` — - Активная локаль.
|
|
10315
|
+
- `builtin` — - Встроенная английская таблица пакета-владельца ключа.
|
|
10316
|
+
- `key` — - Ключ сообщения.
|
|
10317
|
+
- `values` — - Значения подстановок.
|
|
10318
|
+
|
|
10319
|
+
_Source: src/i18n/translator.ts_
|
|
10320
|
+
|
|
8987
10321
|
### UnknownRecord
|
|
8988
10322
|
|
|
8989
10323
|
**Kind:** `type`
|
|
@@ -9227,6 +10561,7 @@ export function useFormControl(
|
|
|
9227
10561
|
Текстовое поле с валидацией
|
|
9228
10562
|
```tsx
|
|
9229
10563
|
import { useFormControl } from '@reformer/core';
|
|
10564
|
+
import { useValidationMessage } from '@reformer/core/i18n';
|
|
9230
10565
|
import type { FieldNode } from '@reformer/core';
|
|
9231
10566
|
|
|
9232
10567
|
interface TextFieldProps {
|
|
@@ -9242,6 +10577,8 @@ shouldShowError,
|
|
|
9242
10577
|
errors,
|
|
9243
10578
|
pending
|
|
9244
10579
|
} = useFormControl(control);
|
|
10580
|
+
// Текст ошибки даёт резолвер: у правил без своего сообщения `message` пуст.
|
|
10581
|
+
const message = useValidationMessage();
|
|
9245
10582
|
|
|
9246
10583
|
return (
|
|
9247
10584
|
<div className="field">
|
|
@@ -9261,7 +10598,7 @@ return (
|
|
|
9261
10598
|
|
|
9262
10599
|
{shouldShowError && errors[0] && (
|
|
9263
10600
|
<span className="error" role="alert">
|
|
9264
|
-
{errors[0]
|
|
10601
|
+
{message(errors[0])}
|
|
9265
10602
|
</span>
|
|
9266
10603
|
)}
|
|
9267
10604
|
</div>
|
|
@@ -9309,6 +10646,7 @@ control: FieldNode<string>;
|
|
|
9309
10646
|
|
|
9310
10647
|
function Select({ control }: SelectProps) {
|
|
9311
10648
|
const { value, disabled, componentProps, shouldShowError, errors } = useFormControl(control);
|
|
10649
|
+
const message = useValidationMessage(); // из '@reformer/core/i18n'
|
|
9312
10650
|
const options = componentProps.options as Array<{ value: string; label: string }>;
|
|
9313
10651
|
|
|
9314
10652
|
return (
|
|
@@ -9326,7 +10664,7 @@ onBlur={() => control.markAsTouched()}
|
|
|
9326
10664
|
</option>
|
|
9327
10665
|
))}
|
|
9328
10666
|
</select>
|
|
9329
|
-
{shouldShowError && <span className="error">{errors[0]
|
|
10667
|
+
{shouldShowError && <span className="error">{message(errors[0])}</span>}
|
|
9330
10668
|
</div>
|
|
9331
10669
|
);
|
|
9332
10670
|
}
|
|
@@ -9564,6 +10902,7 @@ dangerouslySetInnerHTML={{ __html: html }}
|
|
|
9564
10902
|
// Основной редактор использует useFormControl для полного состояния
|
|
9565
10903
|
function MarkdownEditor({ control }: MarkdownEditorProps) {
|
|
9566
10904
|
const { value, shouldShowError, errors } = useFormControl(control);
|
|
10905
|
+
const message = useValidationMessage(); // из '@reformer/core/i18n'
|
|
9567
10906
|
|
|
9568
10907
|
return (
|
|
9569
10908
|
<div className="editor-container">
|
|
@@ -9571,7 +10910,7 @@ return (
|
|
|
9571
10910
|
value={value}
|
|
9572
10911
|
onChange={e => control.setValue(e.target.value)}
|
|
9573
10912
|
/>
|
|
9574
|
-
{shouldShowError && <span className="error">{errors[0]
|
|
10913
|
+
{shouldShowError && <span className="error">{message(errors[0])}</span>}
|
|
9575
10914
|
|
|
9576
10915
|
{/* Preview обновляется только при изменении value *}
|
|
9577
10916
|
<MarkdownPreview control={control} />
|
|
@@ -9675,12 +11014,104 @@ export interface UseFormValidationResult {
|
|
|
9675
11014
|
|
|
9676
11015
|
_Source: src/platforms/react/hooks/use-form-validation.ts_
|
|
9677
11016
|
|
|
11017
|
+
### useI18n
|
|
11018
|
+
|
|
11019
|
+
**Kind:** `function`
|
|
11020
|
+
|
|
11021
|
+
Активная локаль: перевод ключей приложения, форматирование чисел, дат и размеров, состояние
|
|
11022
|
+
загрузки языка. Без провайдера — английский с пустым словарём.
|
|
11023
|
+
|
|
11024
|
+
**Signature:**
|
|
11025
|
+
```typescript
|
|
11026
|
+
export function useI18n(): I18nContextValue
|
|
11027
|
+
```
|
|
11028
|
+
|
|
11029
|
+
**Examples:**
|
|
11030
|
+
|
|
11031
|
+
```tsx
|
|
11032
|
+
function LangBadge() {
|
|
11033
|
+
const { code, pending, t } = useI18n();
|
|
11034
|
+
return <span aria-busy={pending}>{t('app.language')}: {code}</span>;
|
|
11035
|
+
}
|
|
11036
|
+
```
|
|
11037
|
+
|
|
11038
|
+
_Source: src/platforms/react/i18n/hooks.ts_
|
|
11039
|
+
|
|
11040
|
+
### useMessages
|
|
11041
|
+
|
|
11042
|
+
**Kind:** `function`
|
|
11043
|
+
|
|
11044
|
+
Переводчик пакета: ищет ключ в локали провайдера, а при промахе берёт встроенную английскую
|
|
11045
|
+
таблицу этого пакета. Так пакет говорит по-английски без провайдера и не зависит от того, знает
|
|
11046
|
+
ли локаль его ключи. Тип ключа выводится из таблицы — опечатка в ключе не скомпилируется.
|
|
11047
|
+
|
|
11048
|
+
`defaults` должен быть стабильной ссылкой (импорт JSON-модуля), иначе переводчик будет
|
|
11049
|
+
пересоздаваться на каждый рендер.
|
|
11050
|
+
|
|
11051
|
+
**Signature:**
|
|
11052
|
+
```typescript
|
|
11053
|
+
export function useMessages<D extends Messages>(
|
|
11054
|
+
defaults: D
|
|
11055
|
+
): (key: keyof D & string, values?: MessageValues) => string
|
|
11056
|
+
```
|
|
11057
|
+
|
|
11058
|
+
**Parameters:**
|
|
11059
|
+
- `defaults` — - Встроенная английская таблица пакета.
|
|
11060
|
+
|
|
11061
|
+
**Returns:** Функция `(ключ, значения?) => строка`; её идентичность меняется вместе с локалью.
|
|
11062
|
+
|
|
11063
|
+
**Examples:**
|
|
11064
|
+
|
|
11065
|
+
```tsx
|
|
11066
|
+
import en from './locale/en.json';
|
|
11067
|
+
|
|
11068
|
+
const useKitMessages = () => useMessages(en);
|
|
11069
|
+
|
|
11070
|
+
function ClearButton() {
|
|
11071
|
+
const t = useKitMessages();
|
|
11072
|
+
return <button aria-label={t('kit.select.clear')} />;
|
|
11073
|
+
}
|
|
11074
|
+
```
|
|
11075
|
+
|
|
11076
|
+
_Source: src/platforms/react/i18n/hooks.ts_
|
|
11077
|
+
|
|
11078
|
+
### useValidationMessage
|
|
11079
|
+
|
|
11080
|
+
**Kind:** `function`
|
|
11081
|
+
|
|
11082
|
+
Резолвер текста ошибки валидации по активной локали: `messageKey` автора → явное `message` →
|
|
11083
|
+
словарь локали по коду → встроенный английский → код. Идентичность функции меняется вместе с
|
|
11084
|
+
локалью, поэтому показанные ошибки переводятся при смене языка без повторной валидации.
|
|
11085
|
+
|
|
11086
|
+
`FormField` из `@reformer/cdk` использует его сам; вызывайте напрямую, если рисуете ошибки
|
|
11087
|
+
своим компонентом.
|
|
11088
|
+
|
|
11089
|
+
**Signature:**
|
|
11090
|
+
```typescript
|
|
11091
|
+
export function useValidationMessage(): (error: ValidationError) => string
|
|
11092
|
+
```
|
|
11093
|
+
|
|
11094
|
+
**Examples:**
|
|
11095
|
+
|
|
11096
|
+
```tsx
|
|
11097
|
+
function FieldErrors({ control }: { control: FieldNode<string> }) {
|
|
11098
|
+
const message = useValidationMessage();
|
|
11099
|
+
const { errors } = useFormControl(control);
|
|
11100
|
+
return <>{errors.map((error) => <span key={error.code}>{message(error)}</span>)}</>;
|
|
11101
|
+
}
|
|
11102
|
+
```
|
|
11103
|
+
|
|
11104
|
+
_Source: src/platforms/react/i18n/hooks.ts_
|
|
11105
|
+
|
|
9678
11106
|
### validate
|
|
9679
11107
|
|
|
9680
11108
|
**Kind:** `function`
|
|
9681
11109
|
|
|
9682
11110
|
Синхронные правила поля.
|
|
9683
11111
|
|
|
11112
|
+
Поле — лист либо массив целиком: `model.$.<массив>` — такая же ручка значения, и правило
|
|
11113
|
+
получает массив (мультивыбор, теги, список файлов).
|
|
11114
|
+
|
|
9684
11115
|
**Signature:**
|
|
9685
11116
|
```typescript
|
|
9686
11117
|
export function validate<TField>(sig: PathAwareSignal<TField>, rules: Rule<TField>[]): void
|
|
@@ -9690,6 +11121,7 @@ export function validate<TField>(sig: PathAwareSignal<TField>, rules: Rule<TFiel
|
|
|
9690
11121
|
|
|
9691
11122
|
```ts
|
|
9692
11123
|
validate(model.$.loanAmount, [required({ message: 'Сумма' }), min(50000)]);
|
|
11124
|
+
validate(model.$.tags, [required(), maxLength(3)]); // tags: string[] — массив как значение
|
|
9693
11125
|
```
|
|
9694
11126
|
|
|
9695
11127
|
_Source: src/form/validation/operators.ts_
|
|
@@ -9720,6 +11152,34 @@ validateAsync(model.$.username, [async (v, { signal }) => {
|
|
|
9720
11152
|
|
|
9721
11153
|
_Source: src/form/validation/operators.ts_
|
|
9722
11154
|
|
|
11155
|
+
### validateLocale
|
|
11156
|
+
|
|
11157
|
+
**Kind:** `function`
|
|
11158
|
+
|
|
11159
|
+
Проверяет словарь: каждое сообщение разбирается, а при заданном образце — набор ключей и имена
|
|
11160
|
+
подстановок совпадают с ним. Нужна там, где компилятор не поможет: словарь пришёл по сети или
|
|
11161
|
+
написан вне репозитория. В рантайме вызывать не обязательно — битое или пропущенное сообщение
|
|
11162
|
+
и так откатится на встроенный английский; проверка нужна тестам и CI.
|
|
11163
|
+
|
|
11164
|
+
**Signature:**
|
|
11165
|
+
```typescript
|
|
11166
|
+
export function validateLocale(messages: Messages, reference?: Messages): LocaleIssue[]
|
|
11167
|
+
```
|
|
11168
|
+
|
|
11169
|
+
**Parameters:**
|
|
11170
|
+
- `messages` — - Проверяемый словарь.
|
|
11171
|
+
- `reference` — - Образец, обычно английский словарь того же пакета.
|
|
11172
|
+
|
|
11173
|
+
**Returns:** Список расхождений; пустой — словарь в порядке.
|
|
11174
|
+
|
|
11175
|
+
**Examples:**
|
|
11176
|
+
|
|
11177
|
+
```ts
|
|
11178
|
+
expect(validateLocale(ruJson, enJson)).toEqual([]);
|
|
11179
|
+
```
|
|
11180
|
+
|
|
11181
|
+
_Source: src/i18n/locale.ts_
|
|
11182
|
+
|
|
9723
11183
|
### validateModel
|
|
9724
11184
|
|
|
9725
11185
|
**Kind:** `function`
|
|
@@ -9776,8 +11236,20 @@ _Source: src/form/validation/run.ts_
|
|
|
9776
11236
|
```typescript
|
|
9777
11237
|
export interface ValidateOptions {
|
|
9778
11238
|
/** Готовое сообщение об ошибке. Если не задано, валидаторы кладут `''`, и отображаемый текст
|
|
9779
|
-
*
|
|
11239
|
+
* берётся из словаря локали по `code` (`validation.<code>`; см. `resolveValidationError` в
|
|
11240
|
+
* `@reformer/core/i18n`). Явное сообщение важнее словаря, но не переключается вместе с языком —
|
|
11241
|
+
* для локализуемого текста используйте {@link ValidateOptions.messageKey}. */
|
|
9780
11242
|
message?: string;
|
|
11243
|
+
/**
|
|
11244
|
+
* Ключ сообщения в словаре приложения — локализуемый текст автора правила. Если ключ есть в
|
|
11245
|
+
* активной локали, он важнее и `message`, и текста по `code`; если нет — в ход идёт `message`.
|
|
11246
|
+
*
|
|
11247
|
+
* @example
|
|
11248
|
+
* ```ts
|
|
11249
|
+
* required({ messageKey: 'profile.name.required', message: 'Name is required' });
|
|
11250
|
+
* ```
|
|
11251
|
+
*/
|
|
11252
|
+
messageKey?: string;
|
|
9781
11253
|
/** Параметры ошибки (подстановка в шаблон сообщения / i18n). */
|
|
9782
11254
|
params?: Record<string, FormValue>;
|
|
9783
11255
|
}
|
|
@@ -9809,7 +11281,13 @@ _Source: src/form/validation/operators.ts_
|
|
|
9809
11281
|
```typescript
|
|
9810
11282
|
export interface ValidationError {
|
|
9811
11283
|
code: string;
|
|
11284
|
+
/**
|
|
11285
|
+
* Готовый текст ошибки. Пустая строка — «автор правила текста не задавал»: отображаемую строку
|
|
11286
|
+
* тогда даёт резолвер по `code` (словарь локали `validation.<code>`).
|
|
11287
|
+
*/
|
|
9812
11288
|
message: string;
|
|
11289
|
+
/** Ключ сообщения в словаре приложения; при наличии в активной локали важнее `message`. */
|
|
11290
|
+
messageKey?: string;
|
|
9813
11291
|
params?: Record<string, FormValue>;
|
|
9814
11292
|
/** Severity level: 'error' (default) blocks submission, 'warning' shows message but allows submission */
|
|
9815
11293
|
severity?: 'error' | 'warning';
|
|
@@ -9929,3 +11407,16 @@ watchField(model.$.country, async (country) => {
|
|
|
9929
11407
|
```
|
|
9930
11408
|
|
|
9931
11409
|
_Source: src/model/behaviors-value.ts_
|
|
11410
|
+
|
|
11411
|
+
### WeekStart
|
|
11412
|
+
|
|
11413
|
+
**Kind:** `type`
|
|
11414
|
+
|
|
11415
|
+
День недели, с которого начинается календарь: `0` — воскресенье, `1` — понедельник.
|
|
11416
|
+
|
|
11417
|
+
**Signature:**
|
|
11418
|
+
```typescript
|
|
11419
|
+
export type WeekStart = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
11420
|
+
```
|
|
11421
|
+
|
|
11422
|
+
_Source: src/i18n/locale.ts_
|