@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.
Files changed (76) hide show
  1. package/dist/behaviors.js +124 -124
  2. package/dist/create-model-B8U-COv-.js +103 -0
  3. package/dist/en-CjAqn7h0.js +41 -0
  4. package/dist/form/behaviors/internals.d.ts +9 -0
  5. package/dist/form/types/contracts.d.ts +6 -0
  6. package/dist/form/types/validation-schema.d.ts +13 -1
  7. package/dist/form/validation/operators.d.ts +6 -1
  8. package/dist/form/validators/validation-error.d.ts +8 -0
  9. package/dist/i18n/descriptor.d.ts +54 -0
  10. package/dist/i18n/en.json.d.ts +45 -0
  11. package/dist/i18n/loader.d.ts +71 -0
  12. package/dist/i18n/locale-store.d.ts +39 -0
  13. package/dist/i18n/locale.d.ts +90 -0
  14. package/dist/i18n/message-format.d.ts +116 -0
  15. package/dist/i18n/resolve-localized.d.ts +17 -0
  16. package/dist/i18n/ru.json.d.ts +45 -0
  17. package/dist/i18n/translator.d.ts +50 -0
  18. package/dist/i18n/validation-message.d.ts +18 -0
  19. package/dist/i18n.d.ts +31 -0
  20. package/dist/i18n.js +303 -0
  21. package/dist/index-BrNkQj1y.js +105 -0
  22. package/dist/index.js +639 -736
  23. package/dist/loader-Dv_Hl7JC.js +216 -0
  24. package/dist/locale/en.d.ts +2 -0
  25. package/dist/locale/en.js +5 -0
  26. package/dist/locale/en.json +42 -0
  27. package/dist/locale/index.d.ts +18 -0
  28. package/dist/locale/ru.d.ts +2 -0
  29. package/dist/locale/ru.js +41 -0
  30. package/dist/locale/ru.json +42 -0
  31. package/dist/locale.d.ts +2 -0
  32. package/dist/locale.js +9 -0
  33. package/dist/model/create-model.d.ts +17 -3
  34. package/dist/model/index.d.ts +3 -3
  35. package/dist/model/model-signals-proxy.d.ts +25 -0
  36. package/dist/model/types.d.ts +19 -5
  37. package/dist/model-signals-proxy-2vqS243J.js +250 -0
  38. package/dist/model.js +18 -16
  39. package/dist/platforms/react/hooks/types.d.ts +6 -3
  40. package/dist/platforms/react/hooks/useFormControlValue.d.ts +2 -1
  41. package/dist/platforms/react/i18n/context.d.ts +13 -0
  42. package/dist/platforms/react/i18n/hooks.d.ts +59 -0
  43. package/dist/platforms/react/i18n/provider.d.ts +52 -0
  44. package/dist/{strategy-DA3JwxsR.js → strategy-BXS_gBfz.js} +7 -7
  45. package/dist/validation-error-B-U3RsV0.js +11 -0
  46. package/dist/validation.js +1 -1
  47. package/dist/validators/email.js +5 -8
  48. package/dist/validators/future-date.js +8 -11
  49. package/dist/validators/integer.js +4 -7
  50. package/dist/validators/is-date.js +5 -8
  51. package/dist/validators/is-number.js +4 -7
  52. package/dist/validators/max-age.js +8 -11
  53. package/dist/validators/max-date.js +8 -11
  54. package/dist/validators/max-length.js +5 -8
  55. package/dist/validators/max.js +4 -7
  56. package/dist/validators/min-age.js +8 -11
  57. package/dist/validators/min-date.js +8 -11
  58. package/dist/validators/min-length.js +5 -8
  59. package/dist/validators/min.js +4 -7
  60. package/dist/validators/multiple-of.js +5 -8
  61. package/dist/validators/non-negative.js +4 -7
  62. package/dist/validators/non-zero.js +4 -7
  63. package/dist/validators/past-date.js +8 -11
  64. package/dist/validators/pattern.js +5 -8
  65. package/dist/validators/phone.js +6 -9
  66. package/dist/validators/required.js +4 -7
  67. package/dist/validators/url.js +9 -14
  68. package/dist/validators.js +84 -106
  69. package/docs/llms/03-api-signatures.md +6 -0
  70. package/docs/llms/10-arrays.md +30 -0
  71. package/docs/llms/34-i18n.md +272 -0
  72. package/llms-index.json +1 -1
  73. package/llms.txt +1589 -98
  74. package/package.json +22 -3
  75. package/dist/create-model-Dxh9cfLp.js +0 -97
  76. package/dist/model-signals-proxy-mV3WGdlO.js +0 -233
@@ -0,0 +1,54 @@
1
+ import { Messages } from './locale';
2
+ /**
3
+ * Бренд описателя. `Symbol.for` — чтобы описатель узнавался и через границу двух копий модуля
4
+ * (разные чанки, разные версии пакета на странице).
5
+ */
6
+ export declare const MESSAGE_DESCRIPTOR: unique symbol;
7
+ /**
8
+ * Описатель сообщения. Намеренно без ключей `value`, `array`, `item`, `component`: обход схемы в
9
+ * `createForm` узнаёт по ним узел, а описатель узлом не является.
10
+ */
11
+ export interface MessageDescriptor {
12
+ readonly [MESSAGE_DESCRIPTOR]: true;
13
+ /** Ключ сообщения в словаре приложения. */
14
+ readonly key: string;
15
+ /** Значения подстановок. */
16
+ readonly values?: Readonly<Record<string, unknown>>;
17
+ /** Английский текст на случай, если ключа в локали нет. */
18
+ readonly defaultMessage?: string;
19
+ }
20
+ /** Текст, который можно задать строкой или описателем. */
21
+ export type LocalizableText = string | MessageDescriptor;
22
+ /**
23
+ * Создаёт описатель сообщения.
24
+ *
25
+ * @param key - Ключ в словаре приложения.
26
+ * @param values - Значения подстановок.
27
+ * @param defaultMessage - Английский текст на случай, если ключа в локали нет.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * { value: model.$.email, component: Input, componentProps: { label: msg('profile.email.label') } }
32
+ * ```
33
+ */
34
+ export declare function msg(key: string, values?: Readonly<Record<string, unknown>>, defaultMessage?: string): MessageDescriptor;
35
+ /** Является ли значение описателем сообщения. */
36
+ export declare function isMessageDescriptor(value: unknown): value is MessageDescriptor;
37
+ /**
38
+ * `msg` с типизированными ключами: принимает английский словарь приложения, проверяет ключ при
39
+ * компиляции и подставляет английский текст как `defaultMessage` — без провайдера и при промахе
40
+ * по локали форма покажет его, а не ключ.
41
+ *
42
+ * @param defaults - Английский словарь приложения.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * import appEn from './locales/en.json';
47
+ *
48
+ * export const { msg } = defineMessages(appEn);
49
+ * msg('profile.email.label'); // опечатка в ключе — ошибка компиляции
50
+ * ```
51
+ */
52
+ export declare function defineMessages<D extends Messages>(defaults: D): {
53
+ msg(key: keyof D & string, values?: Readonly<Record<string, unknown>>): MessageDescriptor;
54
+ };
@@ -0,0 +1,45 @@
1
+ declare const _default: {
2
+ "format.fileSize.b": "{value} B",
3
+ "format.fileSize.kb": "{value} KB",
4
+ "format.fileSize.mb": "{value} MB",
5
+ "format.fileSize.gb": "{value} GB",
6
+ "format.fileSize.tb": "{value} TB",
7
+
8
+ "validation.required": "This field is required",
9
+ "validation.pattern": "Invalid format",
10
+ "validation.email": "Enter a valid email address",
11
+ "validation.url": "Enter a valid URL",
12
+ "validation.url_protocol": "The URL must use one of these protocols: {allowedProtocols}",
13
+ "validation.phone": "Enter a valid phone number",
14
+
15
+ "validation.min": "Must be at least {min}",
16
+ "validation.max": "Must be at most {max}",
17
+ "validation.minLength": "Enter at least {minLength, plural, one{# character} other{# characters}}",
18
+ "validation.maxLength": "Enter no more than {maxLength, plural, one{# character} other{# characters}}",
19
+ "validation.isNumber": "Enter a number",
20
+ "validation.integer": "Enter a whole number",
21
+ "validation.multipleOf": "Must be a multiple of {multipleOf}",
22
+ "validation.nonNegative": "Must not be negative",
23
+ "validation.nonZero": "Must not be zero",
24
+
25
+ "validation.date_invalid": "Enter a valid date",
26
+ "validation.date_min": "The date must not be earlier than {minDate}",
27
+ "validation.date_max": "The date must not be later than {maxDate}",
28
+ "validation.date_past": "The date must not be in the past",
29
+ "validation.date_future": "The date must not be in the future",
30
+ "validation.date_min_age": "Age must be at least {minAge}",
31
+ "validation.date_max_age": "Age must be at most {maxAge}",
32
+
33
+ "validation.fileType": "File {fileName} has a type that is not allowed. Allowed: {accept}",
34
+ "validation.maxFileSize": "File {fileName} is larger than {maxFileSize}",
35
+ "validation.minFileSize": "File {fileName} is smaller than {minFileSize}",
36
+ "validation.maxFiles": "No more than {maxFiles, plural, one{# file} other{# files}}",
37
+ "validation.minFiles": "At least {minFiles, plural, one{# file} other{# files}}",
38
+ "validation.maxTotalFileSize": "The total size of the files exceeds {maxTotalFileSize}",
39
+ "validation.fileExists": "File {fileName} has already been added",
40
+ "validation.uploadAborted": "Upload cancelled",
41
+ "validation.uploadFailed": "Failed to upload {fileName}"
42
+ }
43
+ ;
44
+
45
+ export default _default;
@@ -0,0 +1,71 @@
1
+ import { FormLocale, Messages } from './locale';
2
+ /** Что может вернуть источник: локаль целиком, её часть, голый словарь или ничего. */
3
+ export type LocaleSourceResult = Partial<FormLocale> | Messages | null | undefined;
4
+ /**
5
+ * Источник локали. Получает код языка, отдаёт локаль или словарь — сразу или промисом.
6
+ * `null`/`undefined` означает «для этого языка у меня ничего нет» и ошибкой не считается.
7
+ */
8
+ export type LocaleSource = (code: string) => LocaleSourceResult | Promise<LocaleSourceResult>;
9
+ /** Загрузчик локалей с кэшем. */
10
+ export interface LocaleLoader {
11
+ /** Загрузить локаль языка. Повторный вызов возвращает тот же промис и ту же локаль. */
12
+ (code: string): Promise<FormLocale>;
13
+ /** Уже загруженная локаль или `undefined`. Позволяет провайдеру отрисоваться синхронно. */
14
+ peek(code: string): FormLocale | undefined;
15
+ /** То же, что вызов загрузчика; имя для чтения в коде старта приложения. */
16
+ preload(code: string): Promise<FormLocale>;
17
+ }
18
+ /**
19
+ * Собирает загрузчик из источников. Источники опрашиваются параллельно, а сливаются по порядку:
20
+ * поздний перекрывает ранний. Результат кэшируется по коду языка, идущий запрос не дублируется.
21
+ * Отказ любого источника — отказ загрузки; из кэша такой запрос удаляется, и следующая попытка
22
+ * пойдёт заново. Источник, которому можно промахнуться, приложение оборачивает само:
23
+ * `(code) => source(code).catch(() => null)`.
24
+ *
25
+ * Код языка у результата — всегда запрошенный: по нему работает `Intl`, и он не должен зависеть
26
+ * от того, что написано внутри файла.
27
+ *
28
+ * @param sources - Источники в порядке возрастания приоритета.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { createLocaleLoader, fetchMessages } from '@reformer/core/i18n';
33
+ * import { loadKitLocale } from '@reformer/ui-kit/locale';
34
+ *
35
+ * export const loadLocale = createLocaleLoader([
36
+ * loadKitLocale, // подписи кита — чанк нужного языка
37
+ * fetchMessages((code) => `/locales/${code}.json`), // ключи приложения и правки — с сервера
38
+ * ]);
39
+ * ```
40
+ */
41
+ export declare function createLocaleLoader(sources: readonly LocaleSource[]): LocaleLoader;
42
+ /**
43
+ * Источник локали по сети: забирает JSON по адресу, построенному из кода языка. Файл может быть
44
+ * словарём (`{ "profile.title": "Профиль" }`) или локалью целиком (`{ "code": "ru", "messages": … }`).
45
+ * Ответ не `2xx` — ошибка загрузки.
46
+ *
47
+ * @param url - Адрес файла для языка.
48
+ * @param init - Параметры запроса, передаются в `fetch` как есть.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * fetchMessages((code) => `/locales/${code}.json`);
53
+ * ```
54
+ */
55
+ export declare function fetchMessages(url: (code: string) => string, init?: RequestInit): LocaleSource;
56
+ /**
57
+ * Источник встроенных локалей пакета: по чанку на язык. Язык ищется точно, затем по основной части
58
+ * кода (`ru-RU` → `ru`). Языка нет — источник отдаёт `null`: подписи пакета останутся английскими,
59
+ * пока приложение не даст их своим источником.
60
+ *
61
+ * @param loaders - Код языка → ленивый импорт его локали.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * export const loadKitLocale = bundledLocaleSource({
66
+ * en: () => import('./en').then((m) => m.en),
67
+ * ru: () => import('./ru').then((m) => m.ru),
68
+ * });
69
+ * ```
70
+ */
71
+ export declare function bundledLocaleSource(loaders: Readonly<Record<string, () => Promise<FormLocale>>>): LocaleSource;
@@ -0,0 +1,39 @@
1
+ import { FormLocale } from './locale';
2
+ /** Функция загрузки локали. `peek` — необязательный синхронный кэш (есть у `createLocaleLoader`). */
3
+ export interface LocaleLoad {
4
+ (code: string): Promise<FormLocale>;
5
+ peek?(code: string): FormLocale | undefined;
6
+ }
7
+ /** Состояние хранилища. Объект неизменяемый: новая ссылка — новое состояние. */
8
+ export interface LocaleStoreState {
9
+ /** Запрошенный язык. Пока `pending`, может отличаться от языка `locale`. */
10
+ readonly lang: string;
11
+ /** Локаль, которая сейчас на экране; `null` — ни одна ещё не загружена. */
12
+ readonly locale: FormLocale | null;
13
+ /** Идёт загрузка запрошенного языка. */
14
+ readonly pending: boolean;
15
+ /** Ошибка последней загрузки; сбрасывается следующей удачной. */
16
+ readonly error: unknown;
17
+ }
18
+ /** Хранилище активной локали. */
19
+ export interface LocaleStore {
20
+ getState(): LocaleStoreState;
21
+ subscribe(listener: () => void): () => void;
22
+ /** Запросить язык. Повторный запрос того же языка, пока он грузится или уже показан, — no-op. */
23
+ setLang(lang: string): void;
24
+ }
25
+ /** Настройки хранилища. */
26
+ export interface LocaleStoreOptions {
27
+ /** Вызывается при отказе загрузки языка, который всё ещё запрошен. */
28
+ readonly onError?: (error: unknown, lang: string) => void;
29
+ }
30
+ /**
31
+ * Создаёт хранилище. Загрузка при создании НЕ начинается: начальное состояние считается чисто —
32
+ * из кэша загрузчика, если локаль там уже есть, — а запрос уходит на первый {@link LocaleStore.setLang}.
33
+ * Это позволяет создавать хранилище прямо в рендере.
34
+ *
35
+ * @param load - Загрузчик локалей.
36
+ * @param lang - Начальный язык.
37
+ * @param options - См. {@link LocaleStoreOptions}.
38
+ */
39
+ export declare function createLocaleStore(load: LocaleLoad, lang: string, options?: LocaleStoreOptions): LocaleStore;
@@ -0,0 +1,90 @@
1
+ import { MessagePattern } from './message-format';
2
+ /** Плоский словарь: ключ → сообщение в формате ICU (см. `message-format.ts`). */
3
+ export type Messages = Readonly<Record<string, string>>;
4
+ /** День недели, с которого начинается календарь: `0` — воскресенье, `1` — понедельник. */
5
+ export type WeekStart = 0 | 1 | 2 | 3 | 4 | 5 | 6;
6
+ /**
7
+ * Локаль формы.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const ru: FormLocale = {
12
+ * code: 'ru',
13
+ * weekStartsOn: 1,
14
+ * messages: { 'kit.select.selected': 'Выбрано: {count}' },
15
+ * };
16
+ * ```
17
+ */
18
+ export interface FormLocale {
19
+ /** Код языка BCP 47 (`en`, `ru`, `pt-BR`): по нему работают `Intl` и множественные формы. */
20
+ readonly code: string;
21
+ /** Словарь сообщений. Ключа нет — пакет подставит встроенный английский. */
22
+ readonly messages: Messages;
23
+ /** Первый день недели календаря. По умолчанию — воскресенье. */
24
+ readonly weekStartsOn?: WeekStart;
25
+ /**
26
+ * Необязательное переопределение: объект локали `react-day-picker` / `date-fns`. Для ядра
27
+ * непрозрачен. Без него даты форматируются через `Intl` по {@link FormLocale.code}.
28
+ */
29
+ readonly dateLocale?: unknown;
30
+ /** Необязательное переопределение: формат даты в токенах `date-fns` для DatePicker. */
31
+ readonly dateFormat?: string;
32
+ }
33
+ /**
34
+ * Локаль «провайдера нет»: английский, пустой словарь. Каждый пакет при промахе по словарю берёт
35
+ * свою встроенную английскую таблицу, поэтому пустой словарь означает «всё по-английски».
36
+ */
37
+ export declare const DEFAULT_LOCALE: FormLocale;
38
+ /**
39
+ * Новая локаль на основе существующей: словари сливаются (ключи из `patch` важнее), остальные
40
+ * поля заменяются. Исходный объект не меняется.
41
+ *
42
+ * @param base - Локаль-основа, например поставляемая китом.
43
+ * @param patch - Свои ключи, переопределения китовых подписей и поля локали.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { ru } from '@reformer/ui-kit/locale/ru';
48
+ *
49
+ * const appRu = extendLocale(ru, {
50
+ * messages: { 'profile.title': 'Профиль', 'kit.formWizard.submit': 'Отправить' },
51
+ * });
52
+ * ```
53
+ */
54
+ export declare function extendLocale(base: FormLocale, patch: Partial<FormLocale>): FormLocale;
55
+ /** `Object.hasOwn` без требования ES2022 к рантайму потребителя. */
56
+ export declare function hasOwn(target: object, key: string): boolean;
57
+ /** Что не так со словарём. */
58
+ export interface LocaleIssue {
59
+ readonly key: string;
60
+ /**
61
+ * - `syntax` — сообщение не разбирается;
62
+ * - `missing` — ключ есть в образце, но его нет в словаре;
63
+ * - `extra` — ключа нет в образце;
64
+ * - `arguments` — набор подстановок не совпадает с образцом.
65
+ */
66
+ readonly kind: 'syntax' | 'missing' | 'extra' | 'arguments';
67
+ readonly message: string;
68
+ }
69
+ /**
70
+ * Имена аргументов сообщения, включая те, что встречаются только внутри веток `plural`/`select`.
71
+ *
72
+ * @param pattern - Результат `parseMessage`.
73
+ */
74
+ export declare function messageArguments(pattern: MessagePattern, out?: Set<string>): Set<string>;
75
+ /**
76
+ * Проверяет словарь: каждое сообщение разбирается, а при заданном образце — набор ключей и имена
77
+ * подстановок совпадают с ним. Нужна там, где компилятор не поможет: словарь пришёл по сети или
78
+ * написан вне репозитория. В рантайме вызывать не обязательно — битое или пропущенное сообщение
79
+ * и так откатится на встроенный английский; проверка нужна тестам и CI.
80
+ *
81
+ * @param messages - Проверяемый словарь.
82
+ * @param reference - Образец, обычно английский словарь того же пакета.
83
+ * @returns Список расхождений; пустой — словарь в порядке.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * expect(validateLocale(ruJson, enJson)).toEqual([]);
88
+ * ```
89
+ */
90
+ export declare function validateLocale(messages: Messages, reference?: Messages): LocaleIssue[];
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Формат сообщений интерфейса — подмножество ICU MessageFormat без зависимостей.
3
+ *
4
+ * Один на подписи форм (`@reformer/core/i18n`) и на интерфейс билдера. Поддерживает ровно то, что
5
+ * им нужно, и ничего сверх:
6
+ * - **подстановка** — `Открыт файл {name}`;
7
+ * - **множественные формы** — `{count, plural, one{# файл} few{# файла} many{# файлов} other{# файла}}`,
8
+ * категории берутся из {@link Intl.PluralRules} активной локали, `#` подставляет само число через
9
+ * {@link Intl.NumberFormat};
10
+ * - **выбор по значению** — `{gender, select, male{он} female{она} other{оно}}`;
11
+ * - **экранирование** — апострофом, как в ICU: `'{'` → `{`, `'}'` → `}`, `''` → `'`.
12
+ *
13
+ * Три свойства, ради которых формат устроен именно так:
14
+ *
15
+ * 1. **Сообщение разбирается в дерево, а не форматируется заменами по строке.** Значение аргумента
16
+ * попадает в результат как текст и повторно не разбирается: `{name}` со значением
17
+ * `{count, plural, ...}` выведет эти символы, а не выполнит их. Замена регулярным выражением
18
+ * такой гарантии не даёт.
19
+ * 2. **Синтаксис — честное подмножество ICU.** Любое сообщение, которое понимает этот модуль,
20
+ * `intl-messageformat` (в режиме `ignoreTag`, со строковыми и числовыми значениями) понимает так
21
+ * же и выводит посимвольно то же самое; это проверено дифференциальным тестом на общем наборе
22
+ * сообщений (`tests/i18n/message-format.differential.test.ts`). Отсюда страховка из контракта:
23
+ * если подмножества перестанет хватать, библиотека подставляется под тот же вызов, и словари
24
+ * переписывать не придётся.
25
+ * 3. **Ошибка синтаксиса и отсутствие ветки `other` — отказ на разборе**, а не молчаливая
26
+ * неправильная строка в интерфейсе. Разбор словаря на старте и в CI ловит их до пользователя.
27
+ *
28
+ * Отличий от `intl-messageformat` в выводе нет; отличий два, и оба — сужение, а не расхождение:
29
+ * - **Грамматика уже.** Типы аргументов `number`, `date`, `time`, `selectordinal` не поддержаны и
30
+ * отвергаются на разборе. Сообщение с ними не пройдёт в словарь, а не отформатируется молча
31
+ * неправильно. Заметь: голое `{n}` форматируется как `String(n)` и здесь, и в `intl-messageformat`
32
+ * (`{year}` → `2026`, а не `2 026`); по локали форматируется только `#` внутри `plural`.
33
+ * - **Пропущенный аргумент не бросает исключение**, а подставляет заметный маркер `⟦name⟧`: строка
34
+ * интерфейса не должна ронять панель. Поведение настраивается через `onMissingArgument`.
35
+ *
36
+ * @module i18n/message-format
37
+ */
38
+ /** Узел разобранного сообщения. */
39
+ export type MessageNode = {
40
+ readonly kind: 'text';
41
+ readonly value: string;
42
+ } | {
43
+ readonly kind: 'arg';
44
+ readonly name: string;
45
+ } | {
46
+ readonly kind: 'number';
47
+ readonly name: string;
48
+ } | {
49
+ readonly kind: 'plural';
50
+ readonly name: string;
51
+ readonly branches: MessageBranches;
52
+ } | {
53
+ readonly kind: 'select';
54
+ readonly name: string;
55
+ readonly branches: MessageBranches;
56
+ };
57
+ /** Разобранное сообщение — плоская последовательность узлов. */
58
+ export type MessagePattern = readonly MessageNode[];
59
+ /** Ветки `plural`/`select`: категория CLDR (`one`, `few`, …), точное совпадение (`=0`) или значение `select`. */
60
+ export type MessageBranches = ReadonlyMap<string, MessagePattern>;
61
+ /** Значения аргументов сообщения. */
62
+ export type MessageValues = Readonly<Record<string, unknown>>;
63
+ /** Настройки форматирования. */
64
+ export interface MessageFormatOptions {
65
+ /**
66
+ * Что подставить вместо аргумента, которого нет в значениях (или чьё значение не число там, где
67
+ * ожидалось число). По умолчанию — заметный маркер `⟦name⟧`, тот же приём, что и для пропущенного
68
+ * ключа: пропуск должен попадаться разработчику, а не пользователю.
69
+ */
70
+ readonly onMissingArgument?: (name: string) => string;
71
+ }
72
+ /** Отказ разбора сообщения: синтаксис или отсутствие обязательной ветки `other`. */
73
+ export declare class MessageSyntaxError extends Error {
74
+ readonly name = "MessageSyntaxError";
75
+ }
76
+ /**
77
+ * Разбирает сообщение в дерево. Бросает {@link MessageSyntaxError} на ошибке синтаксиса и на
78
+ * `plural`/`select` без ветки `other`.
79
+ *
80
+ * @param source - Текст сообщения из словаря.
81
+ * @returns Разобранное сообщение для {@link formatPattern}.
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * parseMessage('{count, plural, one{# файл} few{# файла} many{# файлов} other{# файла}}');
86
+ * ```
87
+ */
88
+ export declare function parseMessage(source: string): MessagePattern;
89
+ /**
90
+ * Собирает строку из разобранного сообщения. Значения подставляются как текст и повторно
91
+ * не разбираются.
92
+ *
93
+ * @param pattern - Результат {@link parseMessage}.
94
+ * @param values - Значения аргументов.
95
+ * @param locale - Локаль для {@link Intl.PluralRules} и {@link Intl.NumberFormat} (`ru`, `en`, …).
96
+ * @param options - Настройки; см. {@link MessageFormatOptions}.
97
+ * @returns Готовая строка интерфейса.
98
+ */
99
+ export declare function formatPattern(pattern: MessagePattern, values: MessageValues, locale: string, options?: MessageFormatOptions): string;
100
+ /**
101
+ * Форматтер одной локали с кэшем разбора: сообщение разбирается один раз, на первый вызов.
102
+ * Это внутренность `I18nService.t()` — сам `t()` добавляет сверху поиск ключа в словаре, маркер
103
+ * пропущенного ключа и откат на резервную локаль.
104
+ *
105
+ * @param locale - Локаль форматтера.
106
+ * @param options - Настройки; см. {@link MessageFormatOptions}.
107
+ * @returns Функция `(сообщение, значения) => строка`.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const format = createMessageFormatter('ru');
112
+ * format('{count, plural, one{# файл} few{# файла} many{# файлов} other{# файла}}', { count: 5 });
113
+ * // '5 файлов'
114
+ * ```
115
+ */
116
+ export declare function createMessageFormatter(locale: string, options?: MessageFormatOptions): (source: string, values?: MessageValues) => string;
@@ -0,0 +1,17 @@
1
+ import { I18nHandle } from './translator';
2
+ /**
3
+ * Заменяет описатели сообщений на строки по активной локали. Обходит массивы и простые
4
+ * объекты-литералы — подписи опций, заголовки шагов; сигналы, React-элементы, функции и
5
+ * экземпляры классов (узлы формы) возвращает как есть. Поддерево без описателей сохраняет ссылку:
6
+ * если раскрывать было нечего, возвращается тот же объект.
7
+ *
8
+ * @param value - Пропсы, массив или одиночное значение.
9
+ * @param i18n - Ручка активной локали (`createI18n` / `useI18n`).
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * resolveLocalized({ label: msg('profile.email.label'), options: [{ value: 'a', label: msg('opt.a') }] }, i18n);
14
+ * // { label: 'Почта', options: [{ value: 'a', label: 'Вариант А' }] }
15
+ * ```
16
+ */
17
+ export declare function resolveLocalized<T>(value: T, i18n: I18nHandle): T;
@@ -0,0 +1,45 @@
1
+ declare const _default: {
2
+ "format.fileSize.b": "{value} Б",
3
+ "format.fileSize.kb": "{value} КБ",
4
+ "format.fileSize.mb": "{value} МБ",
5
+ "format.fileSize.gb": "{value} ГБ",
6
+ "format.fileSize.tb": "{value} ТБ",
7
+
8
+ "validation.required": "Обязательное поле",
9
+ "validation.pattern": "Неверный формат",
10
+ "validation.email": "Введите корректный email",
11
+ "validation.url": "Введите корректный адрес (URL)",
12
+ "validation.url_protocol": "Адрес должен начинаться с одного из протоколов: {allowedProtocols}",
13
+ "validation.phone": "Введите корректный номер телефона",
14
+
15
+ "validation.min": "Не меньше {min}",
16
+ "validation.max": "Не больше {max}",
17
+ "validation.minLength": "Не меньше {minLength, plural, one{# символа} few{# символов} many{# символов} other{# символа}}",
18
+ "validation.maxLength": "Не больше {maxLength, plural, one{# символа} few{# символов} many{# символов} other{# символа}}",
19
+ "validation.isNumber": "Введите число",
20
+ "validation.integer": "Введите целое число",
21
+ "validation.multipleOf": "Число должно быть кратно {multipleOf}",
22
+ "validation.nonNegative": "Число не может быть отрицательным",
23
+ "validation.nonZero": "Число не может быть нулём",
24
+
25
+ "validation.date_invalid": "Введите корректную дату",
26
+ "validation.date_min": "Дата не раньше {minDate}",
27
+ "validation.date_max": "Дата не позже {maxDate}",
28
+ "validation.date_past": "Дата не может быть в прошлом",
29
+ "validation.date_future": "Дата не может быть в будущем",
30
+ "validation.date_min_age": "Возраст не меньше {minAge, plural, one{# года} few{# лет} many{# лет} other{# года}}",
31
+ "validation.date_max_age": "Возраст не больше {maxAge, plural, one{# года} few{# лет} many{# лет} other{# года}}",
32
+
33
+ "validation.fileType": "Недопустимый тип файла {fileName}. Разрешено: {accept}",
34
+ "validation.maxFileSize": "Файл {fileName} больше {maxFileSize}",
35
+ "validation.minFileSize": "Файл {fileName} меньше {minFileSize}",
36
+ "validation.maxFiles": "Не больше {maxFiles, plural, one{# файла} few{# файлов} many{# файлов} other{# файла}}",
37
+ "validation.minFiles": "Не меньше {minFiles, plural, one{# файла} few{# файлов} many{# файлов} other{# файла}}",
38
+ "validation.maxTotalFileSize": "Суммарный размер файлов больше {maxTotalFileSize}",
39
+ "validation.fileExists": "Файл {fileName} уже добавлен",
40
+ "validation.uploadAborted": "Загрузка прервана",
41
+ "validation.uploadFailed": "Не удалось загрузить файл {fileName}"
42
+ }
43
+ ;
44
+
45
+ export default _default;
@@ -0,0 +1,50 @@
1
+ import { FormLocale, Messages } from './locale';
2
+ import { MessageValues } from './message-format';
3
+ /**
4
+ * Перевод ключа пакета: словарь локали, затем встроенная английская таблица пакета, затем сам
5
+ * ключ. Английская таблица форматируется по английским правилам — её множественные формы и
6
+ * группировка чисел не зависят от активного языка.
7
+ *
8
+ * @param locale - Активная локаль.
9
+ * @param builtin - Встроенная английская таблица пакета-владельца ключа.
10
+ * @param key - Ключ сообщения.
11
+ * @param values - Значения подстановок.
12
+ */
13
+ export declare function translateBuiltin(locale: FormLocale, builtin: Messages, key: string, values?: MessageValues): string;
14
+ /** Ручка локали: перевод и форматирование по одному языку. */
15
+ export interface I18nHandle {
16
+ /** Локаль, по которой работает ручка. */
17
+ readonly locale: FormLocale;
18
+ /** Код языка — то же, что `locale.code`. */
19
+ readonly code: string;
20
+ /** Есть ли ключ в словаре локали (встроенные английские таблицы не учитываются). */
21
+ has(key: string): boolean;
22
+ /**
23
+ * Перевод ключа приложения: словарь локали → `defaultMessage` → встроенная таблица ядра → ключ.
24
+ *
25
+ * @param key - Ключ сообщения.
26
+ * @param values - Значения подстановок.
27
+ * @param defaultMessage - Английский текст на случай, если ключа в локали нет.
28
+ */
29
+ t(key: string, values?: MessageValues, defaultMessage?: string): string;
30
+ /** Число по правилам языка. */
31
+ number(value: number, options?: Intl.NumberFormatOptions): string;
32
+ /** Дата по правилам языка. */
33
+ date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;
34
+ /** Размер файла: `1536` → `1.5 KB` (`1,5 КБ` в русской локали). База — 1024. */
35
+ fileSize(bytes: number): string;
36
+ }
37
+ /**
38
+ * Ручка для локали. Для одного и того же объекта локали возвращается один и тот же объект —
39
+ * ручку можно держать в зависимостях хуков.
40
+ *
41
+ * @param locale - Локаль; см. {@link FormLocale}.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const i18n = createI18n({ code: 'ru', messages: { 'cart.items': '{count, plural, one{# товар} few{# товара} other{# товаров}}' } });
46
+ * i18n.t('cart.items', { count: 3 }); // '3 товара'
47
+ * i18n.fileSize(1536); // '1,5 КБ'
48
+ * ```
49
+ */
50
+ export declare function createI18n(locale: FormLocale): I18nHandle;
@@ -0,0 +1,18 @@
1
+ import { ValidationError } from '../form/types/contracts';
2
+ import { I18nHandle } from './translator';
3
+ /**
4
+ * Отображаемый текст ошибки валидации. Порядок источников — в шапке модуля.
5
+ *
6
+ * @param error - Ошибка от правила: `code`, `message`, `messageKey`, `params`.
7
+ * @param i18n - Ручка активной локали (`createI18n` / `useI18n`).
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * resolveValidationError({ code: 'minLength', message: '', params: { minLength: 8 } }, i18n);
12
+ * // 'Enter at least 8 characters' · в русской локали — 'Не меньше 8 символов'
13
+ *
14
+ * resolveValidationError({ code: 'required', message: 'Укажите телефон' }, i18n);
15
+ * // 'Укажите телефон' — явное сообщение автора важнее словаря
16
+ * ```
17
+ */
18
+ export declare function resolveValidationError(error: ValidationError, i18n: I18nHandle): string;
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Подпуть `@reformer/core/i18n` — локализация форм: объект локали, загрузка словарей, провайдер,
3
+ * перевод и форматирование, описатели авторских подписей.
4
+ *
5
+ * Вторая точка сшивки слоёв после корневого `index.ts`: чистая часть лежит в `src/i18n/`
6
+ * (без React), привязка к React — в `src/platforms/react/i18n/`. Из главной бочки `@reformer/core`
7
+ * этот модуль не реэкспортируется: канонический импорт один, а контекст обязан существовать в
8
+ * единственном экземпляре на все пакеты `@reformer/*`.
9
+ *
10
+ * @group i18n
11
+ * @module i18n
12
+ */
13
+ export { parseMessage, formatPattern, createMessageFormatter, MessageSyntaxError, } from './i18n/message-format';
14
+ export type { MessageNode, MessagePattern, MessageBranches, MessageValues, MessageFormatOptions, } from './i18n/message-format';
15
+ export { DEFAULT_LOCALE, extendLocale, validateLocale, messageArguments } from './i18n/locale';
16
+ export type { FormLocale, Messages, WeekStart, LocaleIssue } from './i18n/locale';
17
+ export { createLocaleLoader, fetchMessages, bundledLocaleSource } from './i18n/loader';
18
+ export type { LocaleLoader, LocaleSource, LocaleSourceResult } from './i18n/loader';
19
+ export { createLocaleStore } from './i18n/locale-store';
20
+ export type { LocaleLoad, LocaleStore, LocaleStoreState, LocaleStoreOptions, } from './i18n/locale-store';
21
+ export { createI18n, translateBuiltin } from './i18n/translator';
22
+ export type { I18nHandle } from './i18n/translator';
23
+ export { resolveValidationError } from './i18n/validation-message';
24
+ export { msg, defineMessages, isMessageDescriptor, MESSAGE_DESCRIPTOR } from './i18n/descriptor';
25
+ export type { MessageDescriptor, LocalizableText } from './i18n/descriptor';
26
+ export { resolveLocalized } from './i18n/resolve-localized';
27
+ export { I18nProvider } from './platforms/react/i18n/provider';
28
+ export type { I18nProviderProps, I18nStaticProviderProps, I18nLoadingProviderProps, } from './platforms/react/i18n/provider';
29
+ export { useI18n, useMessages, useValidationMessage } from './platforms/react/i18n/hooks';
30
+ export { I18N_CONTEXT_MARKER } from './platforms/react/i18n/context';
31
+ export type { I18nContextValue } from './platforms/react/i18n/context';