@reformer/core 11.0.0-beta.5 → 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/create-form.d.ts +6 -6
- package/dist/form/factories/node-factory.d.ts +16 -16
- package/dist/form/nodes/array-node.d.ts +3 -3
- package/dist/form/nodes/field-node.d.ts +3 -3
- package/dist/form/nodes/group-node.d.ts +7 -7
- package/dist/form/nodes/model-array-node.d.ts +2 -2
- package/dist/form/types/contracts.d.ts +6 -0
- package/dist/form/types/deep-schema.d.ts +5 -5
- 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 +548 -601
- 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/field-binding/field-adapter.d.ts +87 -0
- package/dist/platforms/react/field-binding/field-handle.d.ts +50 -0
- 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/platforms/react/index.d.ts +4 -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/02-quick-start.md +7 -7
- package/docs/llms/03-api-signatures.md +11 -5
- package/docs/llms/05-common-mistakes.md +1 -1
- package/docs/llms/09-formschema.md +12 -12
- package/docs/llms/10-arrays.md +34 -4
- package/docs/llms/14-extended-mistakes.md +1 -1
- package/docs/llms/15-project-structure.md +15 -13
- package/docs/llms/16-ui-components.md +36 -9
- package/docs/llms/17-nonexistent-api.md +16 -2
- package/docs/llms/29-async-preload.md +4 -4
- package/docs/llms/30-type-safety-recipes.md +1 -1
- package/docs/llms/34-i18n.md +272 -0
- package/llms-index.json +1 -1
- package/llms.txt +1925 -175
- package/package.json +22 -3
- package/dist/create-model-Dxh9cfLp.js +0 -97
- package/dist/model-signals-proxy-mV3WGdlO.js +0 -233
|
@@ -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';
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { h as b, f as T, p as U, D as V } from "./loader-Dv_Hl7JC.js";
|
|
2
|
+
import { M as Oe, b as he, a as Fe, c as Ae, e as Ie, d as _e, m as $e, v as Te } from "./loader-Dv_Hl7JC.js";
|
|
3
|
+
import { m as D } from "./en-CjAqn7h0.js";
|
|
4
|
+
import { Signal as P } from "@preact/signals-core";
|
|
5
|
+
import { createContext as G, createElement as g, useMemo as p, useRef as K, useEffect as k, Fragment as x, useContext as M } from "react";
|
|
6
|
+
import { s as W } from "./index-BrNkQj1y.js";
|
|
7
|
+
function q(e, t, n = {}) {
|
|
8
|
+
const r = /* @__PURE__ */ new Set(), o = e.peek?.(t);
|
|
9
|
+
let a = {
|
|
10
|
+
lang: t,
|
|
11
|
+
locale: o ?? null,
|
|
12
|
+
pending: o === void 0,
|
|
13
|
+
error: void 0
|
|
14
|
+
}, c = null, i = o === void 0 ? null : t;
|
|
15
|
+
const f = (s) => {
|
|
16
|
+
a = s;
|
|
17
|
+
for (const u of [...r]) u();
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
getState: () => a,
|
|
21
|
+
subscribe(s) {
|
|
22
|
+
return r.add(s), () => {
|
|
23
|
+
r.delete(s);
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
setLang(s) {
|
|
27
|
+
if (c === s) return;
|
|
28
|
+
if (i === s) {
|
|
29
|
+
c = null, (a.lang !== s || a.pending || a.error !== void 0) && f({ ...a, lang: s, pending: !1, error: void 0 });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const u = e.peek?.(s);
|
|
33
|
+
if (u !== void 0) {
|
|
34
|
+
c = null, i = s, f({ lang: s, locale: u, pending: !1, error: void 0 });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
c = s, f({ ...a, lang: s, pending: !0, error: void 0 }), e(s).then(
|
|
38
|
+
(l) => {
|
|
39
|
+
c === s && (c = null, i = s, f({ lang: s, locale: l, pending: !1, error: void 0 }));
|
|
40
|
+
},
|
|
41
|
+
(l) => {
|
|
42
|
+
c === s && (c = null, f({ ...a, pending: !1, error: l }), n.onError?.(l, s));
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const w = "en", N = { onMissingArgument: (e) => `{${e}}` }, L = /* @__PURE__ */ new WeakMap(), O = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new WeakMap();
|
|
49
|
+
function C() {
|
|
50
|
+
return process.env.NODE_ENV !== "production";
|
|
51
|
+
}
|
|
52
|
+
function j(e, t) {
|
|
53
|
+
try {
|
|
54
|
+
if (typeof e != "string") throw new TypeError("сообщение не является строкой");
|
|
55
|
+
return U(e);
|
|
56
|
+
} catch (n) {
|
|
57
|
+
return C() && console.error(
|
|
58
|
+
`[@reformer/core/i18n] ${t}: ${n instanceof Error ? n.message : String(n)}`
|
|
59
|
+
), null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function B(e, t, n) {
|
|
63
|
+
if (!b(e, t)) return;
|
|
64
|
+
let r = L.get(e);
|
|
65
|
+
r === void 0 && L.set(e, r = /* @__PURE__ */ new Map());
|
|
66
|
+
let o = r.get(t);
|
|
67
|
+
return o === void 0 && (o = j(e[t], `локаль «${n}», ключ «${t}»`), r.set(t, o)), o;
|
|
68
|
+
}
|
|
69
|
+
function S(e, t, n, r) {
|
|
70
|
+
const o = B(e, n, t);
|
|
71
|
+
if (o != null)
|
|
72
|
+
return T(o, r ?? {}, t, N);
|
|
73
|
+
}
|
|
74
|
+
function Z(e, t, n, r) {
|
|
75
|
+
return S(e.messages, e.code, n, r) ?? S(t, w, n, r) ?? n;
|
|
76
|
+
}
|
|
77
|
+
function J(e, t) {
|
|
78
|
+
let n = O.get(e);
|
|
79
|
+
return n === void 0 && (n = j(e, `defaultMessage «${e}»`), O.set(e, n)), n === null ? void 0 : T(n, t ?? {}, w, N);
|
|
80
|
+
}
|
|
81
|
+
const F = ["b", "kb", "mb", "gb", "tb"], X = /* @__PURE__ */ new Map(), Y = /* @__PURE__ */ new Map();
|
|
82
|
+
function A(e, t, n, r) {
|
|
83
|
+
const o = n === void 0 ? t : `${t}|${JSON.stringify(n)}`;
|
|
84
|
+
let a = e.get(o);
|
|
85
|
+
return a === void 0 && e.set(o, a = r()), a;
|
|
86
|
+
}
|
|
87
|
+
const I = /* @__PURE__ */ new WeakMap();
|
|
88
|
+
function v(e) {
|
|
89
|
+
const t = I.get(e);
|
|
90
|
+
if (t !== void 0) return t;
|
|
91
|
+
const { code: n, messages: r } = e, o = (i, f) => A(X, n, f, () => new Intl.NumberFormat(n, f)).format(i), a = (i, f, s) => {
|
|
92
|
+
const u = S(r, n, i, f);
|
|
93
|
+
if (u !== void 0) return u;
|
|
94
|
+
if (s !== void 0) {
|
|
95
|
+
const d = J(s, f);
|
|
96
|
+
if (d !== void 0) return d;
|
|
97
|
+
}
|
|
98
|
+
const l = S(D, w, i, f);
|
|
99
|
+
if (l !== void 0) return l;
|
|
100
|
+
if (C()) {
|
|
101
|
+
let d = h.get(r);
|
|
102
|
+
d === void 0 && h.set(r, d = /* @__PURE__ */ new Set()), d.has(i) || (d.add(i), console.warn(
|
|
103
|
+
`[@reformer/core/i18n] ключа «${i}» нет в локали «${n}», и defaultMessage не задан — показан сам ключ`
|
|
104
|
+
));
|
|
105
|
+
}
|
|
106
|
+
return i;
|
|
107
|
+
}, c = {
|
|
108
|
+
locale: e,
|
|
109
|
+
code: n,
|
|
110
|
+
has: (i) => b(r, i),
|
|
111
|
+
t: a,
|
|
112
|
+
number: o,
|
|
113
|
+
date: (i, f) => A(Y, n, f, () => new Intl.DateTimeFormat(n, f)).format(
|
|
114
|
+
i
|
|
115
|
+
),
|
|
116
|
+
fileSize: (i) => {
|
|
117
|
+
if (!Number.isFinite(i) || i < 0) return "";
|
|
118
|
+
let f = i, s = 0;
|
|
119
|
+
for (; f >= 1024 && s < F.length - 1; )
|
|
120
|
+
f /= 1024, s += 1;
|
|
121
|
+
const u = o(f, { maximumFractionDigits: s === 0 ? 0 : 1, useGrouping: !1 });
|
|
122
|
+
return a(`format.fileSize.${F[s]}`, { value: u });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
return I.set(e, c), c;
|
|
126
|
+
}
|
|
127
|
+
const H = "invalid", Q = /* @__PURE__ */ new Set([
|
|
128
|
+
"maxFileSize",
|
|
129
|
+
"minFileSize",
|
|
130
|
+
"actualSize",
|
|
131
|
+
"maxTotalFileSize",
|
|
132
|
+
"actualTotal"
|
|
133
|
+
]), _ = /* @__PURE__ */ new Map();
|
|
134
|
+
function ee(e, t) {
|
|
135
|
+
const n = t.map(String);
|
|
136
|
+
let r = _.get(e);
|
|
137
|
+
if (r === void 0) {
|
|
138
|
+
try {
|
|
139
|
+
r = new Intl.ListFormat(e, { style: "short", type: "unit" });
|
|
140
|
+
} catch {
|
|
141
|
+
r = null;
|
|
142
|
+
}
|
|
143
|
+
_.set(e, r);
|
|
144
|
+
}
|
|
145
|
+
return r === null ? n.join(", ") : r.format(n);
|
|
146
|
+
}
|
|
147
|
+
function $(e, t) {
|
|
148
|
+
if (e === void 0) return;
|
|
149
|
+
const n = {};
|
|
150
|
+
for (const r of Object.keys(e)) {
|
|
151
|
+
const o = e[r];
|
|
152
|
+
o instanceof Date ? n[r] = t.date(o, { dateStyle: "medium" }) : Array.isArray(o) ? n[r] = ee(t.code, o) : typeof o == "number" && Q.has(r) ? n[r] = t.fileSize(o) : n[r] = o;
|
|
153
|
+
}
|
|
154
|
+
return n;
|
|
155
|
+
}
|
|
156
|
+
function te(e, t) {
|
|
157
|
+
if (e.messageKey !== void 0 && t.has(e.messageKey))
|
|
158
|
+
return t.t(e.messageKey, $(e.params, t));
|
|
159
|
+
if (typeof e.message == "string" && e.message !== "" && e.message !== H)
|
|
160
|
+
return e.message;
|
|
161
|
+
const n = `validation.${e.code}`;
|
|
162
|
+
return t.has(n) || b(D, n) ? t.t(n, $(e.params, t)) : e.code;
|
|
163
|
+
}
|
|
164
|
+
const z = Symbol.for("reformer.i18n.message");
|
|
165
|
+
function ne(e, t, n) {
|
|
166
|
+
return Object.freeze({
|
|
167
|
+
[z]: !0,
|
|
168
|
+
key: e,
|
|
169
|
+
...t === void 0 ? {} : { values: t },
|
|
170
|
+
...n === void 0 ? {} : { defaultMessage: n }
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function re(e) {
|
|
174
|
+
return e !== null && typeof e == "object" && e[z] === !0;
|
|
175
|
+
}
|
|
176
|
+
function Se(e) {
|
|
177
|
+
return { msg: (t, n) => ne(t, n, e[t]) };
|
|
178
|
+
}
|
|
179
|
+
function oe(e) {
|
|
180
|
+
const t = Object.getPrototypeOf(e);
|
|
181
|
+
return t === Object.prototype || t === null;
|
|
182
|
+
}
|
|
183
|
+
function se(e) {
|
|
184
|
+
if (e instanceof P) return !0;
|
|
185
|
+
const t = e;
|
|
186
|
+
return t.$$typeof !== void 0 || t.__path !== void 0 || t.__kind !== void 0;
|
|
187
|
+
}
|
|
188
|
+
function ie(e) {
|
|
189
|
+
if (e === void 0) return;
|
|
190
|
+
const t = {};
|
|
191
|
+
for (const n of Object.keys(e)) {
|
|
192
|
+
const r = e[n];
|
|
193
|
+
t[n] = r instanceof P ? r.value : r;
|
|
194
|
+
}
|
|
195
|
+
return t;
|
|
196
|
+
}
|
|
197
|
+
function E(e, t, n) {
|
|
198
|
+
if (e === null || typeof e != "object") return e;
|
|
199
|
+
if (re(e))
|
|
200
|
+
return t.t(e.key, ie(e.values), e.defaultMessage);
|
|
201
|
+
if (se(e) || n.has(e)) return e;
|
|
202
|
+
if (Array.isArray(e)) {
|
|
203
|
+
n.add(e);
|
|
204
|
+
let o = !1;
|
|
205
|
+
const a = e.map((c) => {
|
|
206
|
+
const i = E(c, t, n);
|
|
207
|
+
return i !== c && (o = !0), i;
|
|
208
|
+
});
|
|
209
|
+
return n.delete(e), o ? a : e;
|
|
210
|
+
}
|
|
211
|
+
if (!oe(e)) return e;
|
|
212
|
+
n.add(e);
|
|
213
|
+
let r;
|
|
214
|
+
for (const o of Object.keys(e)) {
|
|
215
|
+
const a = e[o], c = E(a, t, n);
|
|
216
|
+
c !== a && (r ??= { ...e }, r[o] = c);
|
|
217
|
+
}
|
|
218
|
+
return n.delete(e), r ?? e;
|
|
219
|
+
}
|
|
220
|
+
function ve(e, t) {
|
|
221
|
+
return E(e, t, /* @__PURE__ */ new Set());
|
|
222
|
+
}
|
|
223
|
+
const ae = "ReformerI18nContext";
|
|
224
|
+
function y(e, t, n) {
|
|
225
|
+
return { ...e, pending: t, error: n };
|
|
226
|
+
}
|
|
227
|
+
const ce = y(v(V), !1, void 0), m = G(ce);
|
|
228
|
+
m.displayName = ae;
|
|
229
|
+
function fe({ locale: e, children: t }) {
|
|
230
|
+
const n = p(() => y(v(e), !1, void 0), [e]);
|
|
231
|
+
return g(m.Provider, { value: n }, t);
|
|
232
|
+
}
|
|
233
|
+
function ue({
|
|
234
|
+
lang: e,
|
|
235
|
+
load: t,
|
|
236
|
+
fallback: n,
|
|
237
|
+
onError: r,
|
|
238
|
+
children: o
|
|
239
|
+
}) {
|
|
240
|
+
const a = K(r);
|
|
241
|
+
a.current = r;
|
|
242
|
+
const c = p(
|
|
243
|
+
() => q(t, e, {
|
|
244
|
+
onError: (d, R) => a.current?.(d, R)
|
|
245
|
+
}),
|
|
246
|
+
[t]
|
|
247
|
+
);
|
|
248
|
+
k(() => {
|
|
249
|
+
c.setLang(e);
|
|
250
|
+
}, [c, e]);
|
|
251
|
+
const i = W.useSyncExternalStore(c.subscribe, c.getState, c.getState), { locale: f, pending: s, error: u } = i, l = p(
|
|
252
|
+
() => f === null ? null : y(v(f), s, u),
|
|
253
|
+
[f, s, u]
|
|
254
|
+
);
|
|
255
|
+
return l === null ? n === void 0 ? null : g(x, null, n) : g(m.Provider, { value: l }, o);
|
|
256
|
+
}
|
|
257
|
+
function Ee(e) {
|
|
258
|
+
return "locale" in e ? g(fe, e) : g(ue, e);
|
|
259
|
+
}
|
|
260
|
+
function be() {
|
|
261
|
+
return M(m);
|
|
262
|
+
}
|
|
263
|
+
function Me(e) {
|
|
264
|
+
const { locale: t } = M(m);
|
|
265
|
+
return p(
|
|
266
|
+
() => (n, r) => Z(t, e, n, r),
|
|
267
|
+
[t, e]
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
function we() {
|
|
271
|
+
const { locale: e } = M(m);
|
|
272
|
+
return p(() => {
|
|
273
|
+
const t = v(e);
|
|
274
|
+
return (n) => te(n, t);
|
|
275
|
+
}, [e]);
|
|
276
|
+
}
|
|
277
|
+
export {
|
|
278
|
+
V as DEFAULT_LOCALE,
|
|
279
|
+
ae as I18N_CONTEXT_MARKER,
|
|
280
|
+
Ee as I18nProvider,
|
|
281
|
+
z as MESSAGE_DESCRIPTOR,
|
|
282
|
+
Oe as MessageSyntaxError,
|
|
283
|
+
he as bundledLocaleSource,
|
|
284
|
+
v as createI18n,
|
|
285
|
+
Fe as createLocaleLoader,
|
|
286
|
+
q as createLocaleStore,
|
|
287
|
+
Ae as createMessageFormatter,
|
|
288
|
+
Se as defineMessages,
|
|
289
|
+
Ie as extendLocale,
|
|
290
|
+
_e as fetchMessages,
|
|
291
|
+
T as formatPattern,
|
|
292
|
+
re as isMessageDescriptor,
|
|
293
|
+
$e as messageArguments,
|
|
294
|
+
ne as msg,
|
|
295
|
+
U as parseMessage,
|
|
296
|
+
ve as resolveLocalized,
|
|
297
|
+
te as resolveValidationError,
|
|
298
|
+
Z as translateBuiltin,
|
|
299
|
+
be as useI18n,
|
|
300
|
+
Me as useMessages,
|
|
301
|
+
we as useValidationMessage,
|
|
302
|
+
Te as validateLocale
|
|
303
|
+
};
|