Files
autocrm/lang/ru/portal.php
T
Vasyka 113610ea8f feat: WO apply_margin at fișă level + full RO/RU i18n on client portal
Two changes in one commit:

== 1. Moved apply_margin toggle from line-level to Fișă-level ==

The per-manoperă apply_margin toggle is gone from the Manopere tab.
In its place: a single "Aplică marjă internă" toggle in Fișa's
"Plată & total" section (next to override_margin_pct). One decision
per Fișă instead of per line — cleaner mental model, matches how the
shop actually thinks about at-cost vs. billable work.

Migration: work_orders.apply_margin boolean default true (idempotent).
WorkOrderWork::saving now reads WO.apply_margin from DB directly (not
via belongsTo cache) to determine salary_base:
  - WO.apply_margin=false → every line gets salary_base=total, applied_margin_pct=0
  - WO.apply_margin=true  → resolver chain (WO.override → user margin → tenant default)

Old wo_works.apply_margin column stays untouched (backward-compat with
existing rows), but no longer exposed in UI. Tests updated to new
semantic. All existing tests green.

== 2. Full i18n audit on client-facing portal — RO/RU separated ==

Problem: user selecting Russian saw Romanian mixed into headings,
buttons, labels. Every client-facing Blade file was 100% hardcoded
Romanian — zero __() calls.

Fix: created lang/ro/portal.php + lang/ru/portal.php with 131 keys
across 3 namespaces:
  - portal.common (email, phone, save, total, powered_by, ...)
  - portal.invitation (welcome_name, activate_account, expired_body, ...)
  - portal.tracking (title_fisa, approve, approval_needed_title,
                    ready_estimated, hours, unit_pcs, ...)
  - portal.shop (catalog, cart, checkout_title, order_number, vin_title,
                signin_title, add_to_cart, in_stock, ...)

Converted 15 Blade files to __() calls:
  - resources/views/invitations/{accept,expired,invalid}.blade.php
  - resources/views/tracking/show.blade.php
  - resources/views/shop/{layout,catalog,cart,checkout,order,account,part,vin}.blade.php
  - resources/views/shop/auth/{login,register,forgot,reset}.blade.php

Each view's <html lang="{{ app()->getLocale() }}"> now reflects the
resolved locale (was hardcoded lang="ro").

SetLocale middleware resolves locale in this order:
  1. session locale (user picked via language switcher)
  2. authenticated user.locale
  3. tenant.settings.language
  4. app.locale default (now 'ro')

Config change: config/app.php default locale + fallback both = 'ro'
(was 'en'). English falls back to Romanian for portal.* keys since
we don't ship English portal translations — a Romanian shop that
switches to English shows Romanian text, which is safer than showing
"portal.invitation.activate_account" literals.

phpunit.xml sets APP_LOCALE=ro so test assertSee() calls that look
for Romanian text pass.

Verified via portal.* grep: 131 __() calls across 15 files. Zero
hardcoded Romanian nouns/verbs left in any client-facing view.

Suite: 303 passed (840 assertions). Unchanged count — refactor,
not new tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-13 04:56:04 +00:00

156 lines
7.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
return [
// ─── Общие ───
'common' => [
'email' => 'Email',
'phone' => 'Телефон',
'name' => 'Имя',
'password' => 'Пароль',
'save' => 'Сохранить',
'cancel' => 'Отмена',
'back' => 'Назад',
'search' => 'Поиск',
'total' => 'Итого',
'yes' => 'Да',
'no' => 'Нет',
'required' => '*',
'currency_mdl' => 'MDL',
'powered_by' => 'Работает на AutoCRM',
],
// ─── Приглашение ───
'invitation' => [
'title_accept' => 'Принять приглашение',
'welcome_name' => 'Добро пожаловать, :name!',
'welcome_body' => 'Вас пригласили в :company. Задайте пароль, чтобы активировать аккаунт.',
'new_password' => 'Новый пароль',
'confirm_password' => 'Подтвердите пароль',
'activate_account' => 'Активировать аккаунт',
'expired_title' => 'Приглашение просрочено',
'expired_body' => 'Срок действия ссылки истёк (максимум 7 дней). Попросите администратора выслать приглашение повторно.',
'invalid_title' => 'Недействительное приглашение',
'invalid_body' => 'Ссылка больше не действительна или уже была использована.',
],
// ─── Отслеживание заказ-наряда (публично) ───
'tracking' => [
'title_fisa' => 'Заказ-наряд #:number',
'ready_estimated' => 'Готово ориентировочно:',
'details' => 'Подробности',
'auto' => 'Автомобиль',
'mileage' => 'Пробег',
'opened' => 'Открыт',
'master' => 'Мастер',
'stages' => 'Этапы',
'what_asked' => 'Что вы просили',
'recommendations' => 'Рекомендации',
'photos' => 'Фотографии',
'approval_needed_title' => '⚠ Требуется ваше подтверждение',
'approval_needed_body' => 'Обнаружены дополнительные работы. Пожалуйста, примите решение ниже.',
'hours' => 'ч.',
'approve' => '✅ Согласен',
'decline' => '❌ Не согласен',
'unit_pcs' => 'шт.',
],
// ─── Магазин: навигация и лейаут ───
'shop' => [
'catalog' => 'Каталог',
'search_by_vin' => 'Поиск по VIN',
'cart' => '🛒 Корзина',
'login' => 'Войти',
'register' => 'Регистрация',
'logout' => 'Выйти',
// Каталог
'catalog_title' => 'Каталог запчастей',
'search_placeholder' => 'Найти по названию, коду, бренду, кросс-номеру…',
'all_categories' => 'Все категории',
'in_stock_only' => 'Только в наличии',
'no_parts_found' => 'Запчасти не найдены',
'no_parts_found_for' => 'Запчасти не найдены по запросу „:term"',
'in_stock' => '● В наличии',
'on_order' => '○ Под заказ',
'add_to_cart' => 'Добавить в корзину',
// Корзина
'cart_title' => 'Моя корзина',
'cart_empty' => 'Корзина пуста.',
'view_catalog' => 'Перейти в каталог',
'part' => 'Запчасть',
'price' => 'Цена',
'qty' => 'Кол-во',
'update_cart' => 'Обновить корзину',
'subtotal' => 'Промежуточный итог:',
'checkout_arrow' => 'Оформить заказ →',
// Оформление
'checkout_title' => 'Оформление заказа',
'full_name' => 'ФИО *',
'phone_req' => 'Телефон *',
'delivery_req' => 'Доставка *',
'address_delivery' => 'Адрес (для курьера/почты)',
'notes' => 'Комментарий',
'place_order' => 'Оформить заказ',
'summary' => 'Сводка',
'delivery_fee_note' => 'Стоимость доставки рассчитывается в зависимости от выбранного способа.',
// Статус заказа
'order_number' => 'Заказ',
'status' => 'Статус',
'products' => 'Товары',
'delivery' => 'Доставка',
'delivery_data' => 'Данные доставки',
'continue_shopping' => '← Продолжить покупки',
// Детали запчасти
'back_to_catalog' => '← Назад в каталог',
'brand_label' => 'Бренд:',
'code_label' => 'Код:',
'in_stock_qty' => '● В наличии (:qty :unit)',
'cross_refs_title' => 'Кросс-номера',
'description' => 'Описание',
// VIN
'vin_title' => 'Поиск запчастей по VIN',
'vin_intro' => 'Введите VIN-код (17 символов), чтобы мы определили автомобиль. Затем найдите запчасти в каталоге.',
'vin_decode' => 'Расшифровать',
'vin_invalid' => 'Неверный VIN — должно быть 17 символов.',
'vin_identified' => 'Автомобиль определён',
'vin_manufacturer' => 'Производитель',
'vin_year' => 'Год модели',
'vin_country' => 'Страна',
'vin_region' => 'Регион',
'vin_search_parts' => 'Найти запчасти для :maker →',
'vin_contact_note' => 'Для точного подбора по модели/двигателю свяжитесь с сервисом, указав этот VIN.',
'this_car' => 'этого автомобиля',
// Аккаунт
'my_account' => 'Мой аккаунт',
'hello_name' => 'Привет, :name!',
'contact_data' => 'Контактные данные',
'my_orders' => 'Мои заказы (:count)',
'no_orders_yet' => 'У вас пока нет заказов.',
'order_nr' => '№',
'order_date' => 'Дата',
'order_items' => 'Позиции',
'order_details_arrow' => 'Подробнее →',
// Аутентификация
'signin_title' => 'Войти в аккаунт',
'signin_button' => 'Войти',
'forgot_password' => 'Забыли пароль',
'no_account' => 'Нет аккаунта?',
'register_title' => 'Регистрация аккаунта',
'register_button' => 'Создать аккаунт',
'have_account' => 'Уже есть аккаунт?',
'forgot_title' => 'Забыли пароль',
'forgot_intro' => 'Введите email, с которым регистрировались — вышлем ссылку для сброса.',
'send_reset_link' => 'Выслать ссылку',
'back_to_login' => '← Вернуться ко входу',
'reset_title' => 'Задать новый пароль',
'reset_button' => 'Установить пароль',
],
];