fix(i18n): русские сообщения валидации — lang/ru/validation.php
Accessibility (Pa11y live) / a11y (push) Has been cancelled

UI-аудит раунд 2: APP_LOCALE=ru, но директории lang/ не было → Laravel отдавал
сырой ключ «validation.required» во все формы без кастомных messages() (профиль,
создание проекта, реквизиты для оплаты и т.д.). Auth-формы свой messages() имеют.

Добавлен каноничный ru-перевод (laravel-lang) + секция attributes с русскими
именами полей продукта (Имя/Телефон/Лимит лидов в день/ИНН/…).

Верификация: trans(validation.required)→«Поле … обязательно для заполнения.»;
форма профиля в Playwright показывает «Поле Имя обязательно для заполнения.»
вместо «validation.required».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-06-21 14:37:33 +03:00
parent 1a0c9f5c8d
commit c1b7cb7d61
2 changed files with 225 additions and 22 deletions
+204
View File
@@ -0,0 +1,204 @@
<?php
declare(strict_types=1);
/*
* Русские сообщения валидации (UI-аудит 21.06.2026).
*
* Причина: APP_LOCALE=ru, но директории lang/ не было Laravel отдавал сырой
* ключ «validation.required» во все формы без кастомных messages() (профиль,
* создание проекта и т.д.). Auth-формы свои messages() имеют их не затрагивает.
*
* Базис каноничный перевод laravel-lang/lang (ru).
*/
return [
'accepted' => 'Вы должны принять :attribute.',
'accepted_if' => 'Вы должны принять :attribute, когда :other равно :value.',
'active_url' => 'Поле :attribute содержит недействительный URL.',
'after' => 'Поле :attribute должно содержать дату после :date.',
'after_or_equal' => 'Поле :attribute должно содержать дату не раньше :date.',
'alpha' => 'Поле :attribute может содержать только буквы.',
'alpha_dash' => 'Поле :attribute может содержать только буквы, цифры, дефис и нижнее подчёркивание.',
'alpha_num' => 'Поле :attribute может содержать только буквы и цифры.',
'array' => 'Поле :attribute должно быть массивом.',
'ascii' => 'Поле :attribute может содержать только однобайтовые буквенно-цифровые символы.',
'before' => 'Поле :attribute должно содержать дату до :date.',
'before_or_equal' => 'Поле :attribute должно содержать дату не позже :date.',
'between' => [
'array' => 'Количество элементов в поле :attribute должно быть от :min до :max.',
'file' => 'Размер файла в поле :attribute должен быть от :min до :max Кбайт.',
'numeric' => 'Поле :attribute должно быть от :min до :max.',
'string' => 'Количество символов в поле :attribute должно быть от :min до :max.',
],
'boolean' => 'Поле :attribute должно иметь значение логического типа.',
'can' => 'Поле :attribute содержит недопустимое значение.',
'confirmed' => 'Поле :attribute не совпадает с подтверждением.',
'current_password' => 'Неверный пароль.',
'date' => 'Поле :attribute не является датой.',
'date_equals' => 'Поле :attribute должно содержать дату, равную :date.',
'date_format' => 'Поле :attribute не соответствует формату :format.',
'decimal' => 'Поле :attribute должно иметь :decimal знаков после запятой.',
'declined' => 'Поле :attribute должно быть отклонено.',
'declined_if' => 'Поле :attribute должно быть отклонено, когда :other равно :value.',
'different' => 'Поля :attribute и :other должны различаться.',
'digits' => 'Длина цифрового поля :attribute должна быть :digits.',
'digits_between' => 'Длина цифрового поля :attribute должна быть от :min до :max.',
'dimensions' => 'Поле :attribute имеет недопустимые размеры изображения.',
'distinct' => 'Поле :attribute содержит повторяющееся значение.',
'doesnt_end_with' => 'Поле :attribute не должно заканчиваться одним из следующих значений: :values.',
'doesnt_start_with' => 'Поле :attribute не должно начинаться с одного из следующих значений: :values.',
'email' => 'Поле :attribute должно быть действительным электронным адресом.',
'ends_with' => 'Поле :attribute должно заканчиваться одним из следующих значений: :values.',
'enum' => 'Выбранное значение для :attribute некорректно.',
'exists' => 'Выбранное значение для :attribute некорректно.',
'extensions' => 'Поле :attribute должно иметь одно из следующих расширений: :values.',
'file' => 'Поле :attribute должно быть файлом.',
'filled' => 'Поле :attribute обязательно для заполнения.',
'gt' => [
'array' => 'Количество элементов в поле :attribute должно быть больше :value.',
'file' => 'Размер файла в поле :attribute должен быть больше :value Кбайт.',
'numeric' => 'Поле :attribute должно быть больше :value.',
'string' => 'Количество символов в поле :attribute должно быть больше :value.',
],
'gte' => [
'array' => 'Количество элементов в поле :attribute должно быть не меньше :value.',
'file' => 'Размер файла в поле :attribute должен быть не меньше :value Кбайт.',
'numeric' => 'Поле :attribute должно быть не меньше :value.',
'string' => 'Количество символов в поле :attribute должно быть не меньше :value.',
],
'hex_color' => 'Поле :attribute должно содержать корректный шестнадцатеричный цвет.',
'image' => 'Поле :attribute должно быть изображением.',
'in' => 'Выбранное значение для :attribute некорректно.',
'in_array' => 'Поле :attribute должно существовать в :other.',
'integer' => 'Поле :attribute должно быть целым числом.',
'ip' => 'Поле :attribute должно быть действительным IP-адресом.',
'ipv4' => 'Поле :attribute должно быть действительным IPv4-адресом.',
'ipv6' => 'Поле :attribute должно быть действительным IPv6-адресом.',
'json' => 'Поле :attribute должно быть JSON-строкой.',
'lowercase' => 'Поле :attribute должно быть в нижнем регистре.',
'lt' => [
'array' => 'Количество элементов в поле :attribute должно быть меньше :value.',
'file' => 'Размер файла в поле :attribute должен быть меньше :value Кбайт.',
'numeric' => 'Поле :attribute должно быть меньше :value.',
'string' => 'Количество символов в поле :attribute должно быть меньше :value.',
],
'lte' => [
'array' => 'Количество элементов в поле :attribute должно быть не больше :value.',
'file' => 'Размер файла в поле :attribute должен быть не больше :value Кбайт.',
'numeric' => 'Поле :attribute должно быть не больше :value.',
'string' => 'Количество символов в поле :attribute должно быть не больше :value.',
],
'mac_address' => 'Поле :attribute должно быть корректным MAC-адресом.',
'max' => [
'array' => 'Количество элементов в поле :attribute не может превышать :max.',
'file' => 'Размер файла в поле :attribute не может быть больше :max Кбайт.',
'numeric' => 'Поле :attribute не может быть больше :max.',
'string' => 'Количество символов в поле :attribute не может превышать :max.',
],
'max_digits' => 'Поле :attribute не должно содержать более :max цифр.',
'mimes' => 'Поле :attribute должно быть файлом одного из типов: :values.',
'mimetypes' => 'Поле :attribute должно быть файлом одного из типов: :values.',
'min' => [
'array' => 'Количество элементов в поле :attribute должно быть не меньше :min.',
'file' => 'Размер файла в поле :attribute должен быть не меньше :min Кбайт.',
'numeric' => 'Поле :attribute должно быть не меньше :min.',
'string' => 'Количество символов в поле :attribute должно быть не меньше :min.',
],
'min_digits' => 'Поле :attribute должно содержать не менее :min цифр.',
'missing' => 'Поле :attribute должно отсутствовать.',
'missing_if' => 'Поле :attribute должно отсутствовать, когда :other равно :value.',
'missing_unless' => 'Поле :attribute должно отсутствовать, если :other не равно :value.',
'missing_with' => 'Поле :attribute должно отсутствовать, когда :values присутствует.',
'missing_with_all' => 'Поле :attribute должно отсутствовать, когда :values присутствуют.',
'multiple_of' => 'Поле :attribute должно быть кратным :value.',
'not_in' => 'Выбранное значение для :attribute некорректно.',
'not_regex' => 'Поле :attribute имеет некорректный формат.',
'numeric' => 'Поле :attribute должно быть числом.',
'password' => [
'letters' => 'Поле :attribute должно содержать хотя бы одну букву.',
'mixed' => 'Поле :attribute должно содержать буквы верхнего и нижнего регистра.',
'numbers' => 'Поле :attribute должно содержать хотя бы одну цифру.',
'symbols' => 'Поле :attribute должно содержать хотя бы один символ.',
'uncompromised' => 'Указанное значение :attribute было найдено в утечке данных. Пожалуйста, выберите другое значение :attribute.',
],
'present' => 'Поле :attribute должно присутствовать.',
'present_if' => 'Поле :attribute должно присутствовать, когда :other равно :value.',
'present_unless' => 'Поле :attribute должно присутствовать, если :other не равно :value.',
'present_with' => 'Поле :attribute должно присутствовать, когда :values присутствует.',
'present_with_all' => 'Поле :attribute должно присутствовать, когда :values присутствуют.',
'prohibited' => 'Поле :attribute запрещено.',
'prohibited_if' => 'Поле :attribute запрещено, когда :other равно :value.',
'prohibited_unless' => 'Поле :attribute запрещено, если :other не входит в :values.',
'prohibits' => 'Поле :attribute запрещает присутствие :other.',
'regex' => 'Поле :attribute имеет некорректный формат.',
'required' => 'Поле :attribute обязательно для заполнения.',
'required_array_keys' => 'Поле :attribute должно содержать записи для: :values.',
'required_if' => 'Поле :attribute обязательно для заполнения, когда :other равно :value.',
'required_if_accepted' => 'Поле :attribute обязательно для заполнения, когда :other принято.',
'required_unless' => 'Поле :attribute обязательно для заполнения, когда :other не входит в :values.',
'required_with' => 'Поле :attribute обязательно для заполнения, когда :values указано.',
'required_with_all' => 'Поле :attribute обязательно для заполнения, когда :values указаны.',
'required_without' => 'Поле :attribute обязательно для заполнения, когда :values не указано.',
'required_without_all' => 'Поле :attribute обязательно для заполнения, когда ни одно из :values не указано.',
'same' => 'Значение поля :attribute должно совпадать со значением :other.',
'size' => [
'array' => 'Количество элементов в поле :attribute должно быть равным :size.',
'file' => 'Размер файла в поле :attribute должен быть равен :size Кбайт.',
'numeric' => 'Поле :attribute должно быть равным :size.',
'string' => 'Количество символов в поле :attribute должно быть равным :size.',
],
'starts_with' => 'Поле :attribute должно начинаться с одного из следующих значений: :values.',
'string' => 'Поле :attribute должно быть строкой.',
'timezone' => 'Поле :attribute должно содержать корректную временную зону.',
'unique' => 'Такое значение поля :attribute уже существует.',
'uploaded' => 'Загрузка поля :attribute не удалась.',
'uppercase' => 'Поле :attribute должно быть в верхнем регистре.',
'url' => 'Поле :attribute должно содержать корректный URL.',
'ulid' => 'Поле :attribute должно содержать корректный ULID.',
'uuid' => 'Поле :attribute должно содержать корректный UUID.',
/*
| Кастомные сообщения для конкретных пар «атрибут.правило».
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
| Человекочитаемые имена полей (вместо сырых first_name и т.п.).
*/
'attributes' => [
'first_name' => 'Имя',
'last_name' => 'Фамилия',
'name' => 'Название',
'email' => 'Email',
'phone' => 'Телефон',
'password' => 'Пароль',
'password_confirmation' => 'Подтверждение пароля',
'timezone' => 'Тайм-зона',
'comment' => 'Комментарий',
'daily_limit' => 'Лимит лидов в день',
'signal_identifier' => 'Источник',
'signal_type' => 'Тип',
'sms_senders' => 'Отправители',
'sms_keyword' => 'Ключевое слово',
'regions' => 'Регионы',
'inn' => 'ИНН',
'kpp' => 'КПП',
'ogrn' => 'ОГРН',
'ogrnip' => 'ОГРНИП',
'legal_address' => 'Юридический адрес',
'organization_name' => 'Название организации',
'contact_name' => 'Контактное имя',
'contact_phone' => 'Контактный телефон',
'bank_name' => 'Наименование банка',
'bik' => 'БИК',
'account_number' => 'Расчётный счёт',
'corr_account' => 'Корреспондентский счёт',
'message' => 'Сообщение',
'contact' => 'Контакт',
],
];
+21 -22
View File
@@ -1,6 +1,6 @@
# Brain Status (auto-generated)
Last updated: 2026-06-21T01:43:28.079Z
Last updated: 2026-06-21T11:34:36.544Z
| Контролёр | Состояние | Детали |
|---|---|---|
@@ -33,22 +33,29 @@ Last updated: 2026-06-21T01:43:28.079Z
| enforce-coverage-verify.mjs | `enforce-coverage-verify.mjs` | 🔴 |
| enforce-todowrite-skill-verifier.mjs | `enforce-todowrite-skill-verifier.mjs` | 🔴 |
Недавние escape владельца: 0 · Недавние блоки: 10
Недавние escape владельца: 2 · Недавние блоки: 10
**Недавние escape владельца (детали):**
| Время | Действие | Причина |
|---|---|---|
| 2026-06-21T06:38:08.264Z | plan-done | escape владельца |
| 2026-06-21T02:22:23.069Z | owner-seal:e0f8a7dad28f952f5f18bf323a26f82cabe9f7bd3fd45c8477773af19b8f1af1 | escape владельца |
**Недавние блоки (детали):**
| Время | Действие | Причина |
|---|---|---|
| 2026-06-21T01:43:31.680Z | powershell:$f = "$env:TEMP\claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json"; if (Test-Path $f) { Get-Content $f | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T01:43:28.478Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl» pr |
| 2026-06-21T01:43:23.370Z | bash:ls $TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json 2>/dev/null && cat $TEMP/claude-economy-07bce0ef- | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T01:43:23.221Z | bash:ls $TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json 2>/dev/null && cat $TEMP/claude-economy-07bce0ef- | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:ls $TEMP/claude-econom |
| 2026-06-21T01:42:58.754Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T01:42:48.892Z | powershell:$path = "$env:TEMP\claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json"; if (Test-Path $path) { Get-Cont | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T01:42:45.501Z | bash:cat "$TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T01:42:45.325Z | bash:cat "$TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ |
| 2026-06-21T01:42:41.095Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl» pr |
| 2026-06-21T01:37:32.150Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T11:20:27.250Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T11:20:18.178Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl» pr |
| 2026-06-21T11:20:10.771Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T11:20:10.641Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ |
| 2026-06-21T11:19:59.051Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T11:19:48.786Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl» pr |
| 2026-06-21T11:19:43.736Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T11:19:43.631Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ |
| 2026-06-21T11:10:44.290Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
| 2026-06-21T11:10:36.250Z | bash:ls "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null && cat "$TEMP/claude-economy-dbbe0e | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) |
## Метрики (информационные, не алерты)
@@ -128,15 +135,7 @@ Episodes since last run: 542 / threshold: 10
## System Health
Топ-3 процессов с CPU > 1ч:
| PID | Имя | CPU-время | Возраст |
|---|---|---|---|
| 3024 | Code | 4.90ч | 0.0ч |
| 3260 | MsMpEng | 3.99ч | 0.0ч |
| 1108 | svchost | 2.07ч | NaNч |
⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.
Долго работающих процессов нет (порог CPU > 1ч).
## Очередь обучения роутера
@@ -150,7 +149,7 @@ Episodes since last run: 542 / threshold: 10
## Целостность журналов действий
🔴 Битые цепочки (3 из 97):
🔴 Битые цепочки (3 из 102):
| session | broken at seq |
|---|---|