feat(i18n): complete Russian localization, centralized dictionary, and translation cleanliness test

This commit is contained in:
ochenstarik 2026-09-11 11:26:51 +07:00
parent 30722da89e
commit 6681e0ef56
8 changed files with 246 additions and 29 deletions

View file

@ -124,7 +124,7 @@ export const SYSTEM_MODULES: ModuleDefinition[] = [
},
{
id: 'helpdesk',
label: 'HelpDesk',
label: 'Поддержка',
iconName: 'Headset',
route: '/helpdesk',
requiredCapability: 'helpdesk',
@ -200,7 +200,7 @@ export const SYSTEM_MODULES: ModuleDefinition[] = [
route: '/automation',
requiredCapability: 'automation',
sections: [
{ id: 'scenarios', label: 'Сценарии & Workflows', route: '/automation' },
{ id: 'scenarios', label: 'Сценарии автоматизации', route: '/automation' },
{ id: 'builder', label: 'Конструктор цепочек', route: '/automation/builder' },
{ id: 'triggers', label: 'Триггеры событий', route: '/automation/triggers' },
{ id: 'history', label: 'Лог выполнения', route: '/automation/history' },

View file

@ -23,15 +23,15 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
owner: {
id: 'owner',
label: 'Владелец бизнеса',
badge: 'Owner',
description: 'Полный доступ ко всем модулям, финансовой аналитике, настройкам и AI-центру',
badge: 'Владелец',
description: 'Полный доступ ко всем модулям, финансовой аналитике, настройкам и ИИ-центру',
allowedModules: ['*'],
defaultRoute: '/',
},
administrator: {
id: 'administrator',
label: 'Администратор',
badge: 'Admin',
badge: 'Администратор',
description: 'Управление процессами, персоналом, точками, правами и операционной деятельностью',
allowedModules: ['*'],
defaultRoute: '/',
@ -39,7 +39,7 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
manager: {
id: 'manager',
label: 'Управляющий',
badge: 'Manager',
badge: 'Управляющий',
description: 'Операции, смены, заказы, доставка, склад, контроль персонала и операционная аналитика',
allowedModules: ['dashboard', 'operations', 'crm', 'sales', 'inventory', 'employees', 'tasks', 'communications', 'analytics'],
defaultRoute: '/operations',
@ -47,7 +47,7 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
cashier: {
id: 'cashier',
label: 'Кассир / Оператор',
badge: 'POS',
badge: 'Кассир',
description: 'Кассовый терминал, оформление заказов, прием оплаты, клиентская база',
allowedModules: ['operations', 'sales', 'crm'],
defaultRoute: '/operations/pos',
@ -55,7 +55,7 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
cook: {
id: 'cook',
label: 'Повар / Шеф-повар',
badge: 'KDS',
badge: 'Кухня',
description: 'Кухонный дисплей (KDS), отметка готовности блюд, стоп-лист',
allowedModules: ['operations'],
defaultRoute: '/operations/kds',
@ -63,7 +63,7 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
courier: {
id: 'courier',
label: 'Курьер / Доставка',
badge: 'Courier',
badge: 'Курьер',
description: 'Мобильный экран доставки: маршруты, заказы, адрес клиента, отметка вручения',
allowedModules: ['operations'],
defaultRoute: '/operations/courier',
@ -71,7 +71,7 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
specialist: {
id: 'specialist',
label: 'Специалист / Мастер',
badge: 'Service',
badge: 'Мастер',
description: 'Журнал записи, заказ-наряды постов, расписание смен, услуги',
allowedModules: ['operations', 'crm', 'tasks'],
defaultRoute: '/operations/work-orders',
@ -79,15 +79,15 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
marketer: {
id: 'marketer',
label: 'Маркетолог',
badge: 'Marketing',
description: 'Маркетинговые кампании, CRM сегменты, сайты/CMS, рассылки, сквозная аналитика, AI',
badge: 'Маркетолог',
description: 'Маркетинговые кампании, CRM сегменты, сайты/CMS, рассылки, сквозная аналитика, ИИ',
allowedModules: ['dashboard', 'crm', 'marketing', 'cms', 'communications', 'analytics', 'ai'],
defaultRoute: '/marketing',
},
warehouse: {
id: 'warehouse',
label: 'Складской оператор',
badge: 'Warehouse',
badge: 'Склад',
description: 'Приходные накладные, списания, партии, инвентаризация и поставщики',
allowedModules: ['operations', 'inventory', 'tasks'],
defaultRoute: '/operations/inventory',
@ -95,7 +95,7 @@ export const SYSTEM_ROLES: Record<SystemRoleId, RoleInfo> = {
support: {
id: 'support',
label: 'Специалист поддержки',
badge: 'Support',
badge: 'Поддержка',
description: 'Очередь тикетов, омниканальные чаты, база знаний и SLA',
allowedModules: ['helpdesk', 'communications', 'crm'],
defaultRoute: '/helpdesk',

View file

@ -59,9 +59,9 @@ export const BUSINESS_PROFILES: Record<string, BusinessProfile> = {
restaurant_delivery_only: {
id: 'restaurant_delivery_only',
name: 'Ресторан: Только Доставка / Dark Kitchen',
label: 'Суши & Гёдза Express (Dark Kitchen)',
category: 'Общепит / Dark Kitchen',
name: 'Ресторан: Только Доставка / Кухня доставки',
label: 'Суши & Гёдза Express (Кухня доставки)',
category: 'Общепит / Кухня доставки',
companyName: 'Суши & Гёдза Express',
locations: ['Красный пр-т 186а (Кухня 1)'],
capabilities: {
@ -100,7 +100,7 @@ export const BUSINESS_PROFILES: Record<string, BusinessProfile> = {
],
terminology: {
itemLabel: 'Позиция доставки',
itemsLabel: 'Ассортимент Dark Kitchen',
itemsLabel: 'Ассортимент кухни доставки',
bookingResourceLabel: 'Линия сборки',
orderSourceLabel: 'Доставка / Агрегаторы',
clientIdentifierLabel: 'Получатель заказа',
@ -113,8 +113,8 @@ export const BUSINESS_PROFILES: Record<string, BusinessProfile> = {
retail: {
id: 'retail',
name: 'Розничная торговля / Retail',
label: 'Krona Retail & Store',
name: 'Розничная торговля',
label: 'Krona Розничная торговля & Store',
category: 'Торговля',
companyName: 'Krona Concept Store',
locations: ['Флагманский магазин — Ленина 12', 'Островок в ТЦ'],
@ -579,8 +579,8 @@ export const BUSINESS_PROFILES: Record<string, BusinessProfile> = {
custom_meta: {
id: 'custom_meta',
name: 'Кастомный профиль / Meta-profile',
label: 'Универсальный Business OS (Все модули)',
name: 'Кастомный профиль / Настраиваемый профиль',
label: 'Универсальная Бизнес-платформа (Все модули)',
category: 'Конфигурируемый',
companyName: 'Бизнес-экосистема Крона',
locations: ['Главный офис', 'Локация 2'],

View file

@ -0,0 +1,91 @@
import { describe, it, expect } from 'vitest';
import { SYSTEM_ROLES } from '../../core/permissions/types';
import { BUSINESS_PROFILES } from '../../core/profiles/profiles';
import { SYSTEM_MODULES } from '../../core/modules/registry';
import { LOCALIZATION_DICTIONARY } from '../../shared/lib/localizationDictionary';
describe('Localization Cleanliness Test Suite', () => {
it('verifies all SYSTEM_ROLES badges and labels are strictly localized in Russian', () => {
const englishBadges = [
'Owner',
'Admin',
'Manager',
'POS',
'KDS',
'Courier',
'Service',
'Marketing',
'Warehouse',
'Support',
];
Object.values(SYSTEM_ROLES).forEach((role) => {
// Must not use any legacy English badge
expect(englishBadges).not.toContain(role.badge);
// Badge and label must contain Cyrillic characters
expect(role.badge).toMatch(/[а-яА-ЯёЁ]/);
expect(role.label).toMatch(/[а-яА-ЯёЁ]/);
expect(role.description).toMatch(/[а-яА-ЯёЁ]/);
});
// Verify expected Russian badges exist
expect(SYSTEM_ROLES.owner.badge).toBe('Владелец');
expect(SYSTEM_ROLES.administrator.badge).toBe('Администратор');
expect(SYSTEM_ROLES.manager.badge).toBe('Управляющий');
expect(SYSTEM_ROLES.cashier.badge).toBe('Кассир');
expect(SYSTEM_ROLES.cook.badge).toBe('Кухня');
expect(SYSTEM_ROLES.courier.badge).toBe('Курьер');
expect(SYSTEM_ROLES.specialist.badge).toBe('Мастер');
expect(SYSTEM_ROLES.marketer.badge).toBe('Маркетолог');
expect(SYSTEM_ROLES.warehouse.badge).toBe('Склад');
expect(SYSTEM_ROLES.support.badge).toBe('Поддержка');
});
it('verifies BUSINESS_PROFILES contains zero legacy untranslated markers', () => {
const forbiddenSubstrings = ['Dark Kitchen', 'Meta-profile', 'Business OS'];
Object.values(BUSINESS_PROFILES).forEach((profile) => {
forbiddenSubstrings.forEach((forbidden) => {
expect(profile.name).not.toContain(forbidden);
expect(profile.label).not.toContain(forbidden);
});
});
expect(BUSINESS_PROFILES.restaurant_delivery_only.name).toContain('Кухня доставки');
expect(BUSINESS_PROFILES.retail.name).toContain('Розничная торговля');
expect(BUSINESS_PROFILES.custom_meta.name).toContain('Настраиваемый профиль');
expect(BUSINESS_PROFILES.custom_meta.label).toContain('Бизнес-платформа');
});
it('verifies SYSTEM_MODULES labels and sections have clean Russian localization', () => {
const allowedAcronyms = ['CRM', 'CMS'];
SYSTEM_MODULES.forEach((module) => {
if (!allowedAcronyms.includes(module.label)) {
expect(module.label).toMatch(/[а-яА-ЯёЁ]/);
}
module.sections.forEach((section) => {
expect(section.label).not.toContain('& Workflows');
});
});
const aiModule = SYSTEM_MODULES.find((m) => m.id === 'ai');
expect(aiModule?.label).toBe('ИИ-центр');
const autoModule = SYSTEM_MODULES.find((m) => m.id === 'automation');
const scenariosSection = autoModule?.sections.find((s) => s.id === 'scenarios');
expect(scenariosSection?.label).toBe('Сценарии автоматизации');
});
it('verifies LOCALIZATION_DICTIONARY contains standard Russian keys and values', () => {
expect(LOCALIZATION_DICTIONARY.maturityLevels.pilot1.label).toBe('Пилотный режим');
expect(LOCALIZATION_DICTIONARY.maturityLevels.target.label).toBe('Интерфейс подготовлен');
expect(LOCALIZATION_DICTIONARY.maturityLevels.demo.label).toBe('Только демонстрация');
expect(LOCALIZATION_DICTIONARY.maturityLevels.planned.label).toBe('Запланирован');
expect(LOCALIZATION_DICTIONARY.roles.owner.badge).toBe('Владелец');
expect(LOCALIZATION_DICTIONARY.roles.cashier.badge).toBe('Кассир');
expect(LOCALIZATION_DICTIONARY.modules.ai).toBe('ИИ-центр');
});
});

View file

@ -13,7 +13,7 @@ describe('Phase E: Target Prototype & Maturity Badges Tests', () => {
render(<MaturityBadge level="pilot1" />);
const badge = screen.getByTestId('maturity-badge-pilot1');
expect(badge).toBeInTheDocument();
expect(badge).toHaveTextContent('Pilot 1');
expect(badge).toHaveTextContent('Пилотный режим');
expect(badge.className).toContain('text-emerald-800');
});
@ -21,7 +21,7 @@ describe('Phase E: Target Prototype & Maturity Badges Tests', () => {
render(<MaturityBadge level="target" />);
const badge = screen.getByTestId('maturity-badge-target');
expect(badge).toBeInTheDocument();
expect(badge).toHaveTextContent('Target prototype');
expect(badge).toHaveTextContent('Интерфейс подготовлен');
expect(badge.className).toContain('text-sky-800');
});
@ -29,7 +29,7 @@ describe('Phase E: Target Prototype & Maturity Badges Tests', () => {
render(<MaturityBadge level="demo" />);
const badge = screen.getByTestId('maturity-badge-demo');
expect(badge).toBeInTheDocument();
expect(badge).toHaveTextContent('Demo only');
expect(badge).toHaveTextContent('Только демонстрация');
expect(badge.className).toContain('text-purple-800');
});

View file

@ -0,0 +1,126 @@
/**
* Centralized Russian localization dictionary for Krona Universal Business OS.
* Contains standard localized names, badges, roles, and status mappings.
*/
export const LOCALIZATION_DICTIONARY = {
roles: {
owner: {
label: 'Владелец бизнеса',
badge: 'Владелец',
description: 'Полный доступ ко всем модулям, финансовой аналитике, настройкам и ИИ-центру',
},
administrator: {
label: 'Администратор',
badge: 'Администратор',
description: 'Управление процессами, персоналом, точками, правами и операционной деятельностью',
},
manager: {
label: 'Управляющий',
badge: 'Управляющий',
description: 'Операции, смены, заказы, доставка, склад, контроль персонала и операционная аналитика',
},
cashier: {
label: 'Кассир / Оператор',
badge: 'Кассир',
description: 'Кассовый терминал, оформление заказов, прием оплаты, клиентская база',
},
cook: {
label: 'Повар / Шеф-повар',
badge: 'Кухня',
description: 'Кухонный дисплей (KDS), отметка готовности блюд, стоп-лист',
},
courier: {
label: 'Курьер / Доставка',
badge: 'Курьер',
description: 'Мобильный экран доставки: маршруты, заказы, адрес клиента, отметка вручения',
},
specialist: {
label: 'Специалист / Мастер',
badge: 'Мастер',
description: 'Журнал записи, заказ-наряды постов, расписание смен, услуги',
},
marketer: {
label: 'Маркетолог',
badge: 'Маркетолог',
description: 'Маркетинговые кампании, CRM сегменты, сайты/CMS, рассылки, сквозная аналитика, ИИ',
},
warehouse: {
label: 'Складской оператор',
badge: 'Склад',
description: 'Приходные накладные, списания, партии, инвентаризация и поставщики',
},
support: {
label: 'Специалист поддержки',
badge: 'Поддержка',
description: 'Очередь тикетов, омниканальные чаты, база знаний и SLA',
},
},
maturityLevels: {
pilot1: {
label: 'Пилотный режим',
description: 'Готово к пилоту (IndexedDB, транзакционный контур)',
},
target: {
label: 'Интерфейс подготовлен',
description: 'Целевой прототип (интерактивный макет с бизнес-логикой)',
},
demo: {
label: 'Только демонстрация',
description: 'Демонстрационный режим (симуляция)',
},
planned: {
label: 'Запланирован',
description: 'В разработке (дорожная карта Q4)',
},
},
profiles: {
restaurant_hall_delivery: 'Ресторан: Зал + Доставка',
restaurant_delivery_only: 'Ресторан: Только Доставка / Кухня доставки',
retail: 'Розничная торговля',
services_booking: 'Услуги и запись',
dark_kitchen: 'Кухня доставки',
custom_meta: 'Настраиваемый профиль',
},
modules: {
dashboard: 'Главная',
operations: 'Операции',
crm: 'CRM',
sales: 'Продажи',
inventory: 'Склад',
procurement: 'Закупки',
finance: 'Финансы',
employees: 'Сотрудники',
communications: 'Коммуникации',
helpdesk: 'Поддержка клиентов',
tasks: 'Задачи & Проекты',
cms: 'Сайты',
marketing: 'Маркетинг',
analytics: 'Аналитика',
automation: 'Автоматизация',
ai: 'ИИ-центр',
apps: 'Приложения',
settings: 'Настройки',
},
common: {
create: 'Создать',
edit: 'Редактировать',
delete: 'Удалить',
save: 'Сохранить',
cancel: 'Отмена',
reset: 'Сбросить',
apply: 'Применить',
filter: 'Фильтры',
search: 'Поиск',
close: 'Закрыть',
export: 'Экспорт',
import: 'Импорт',
all: 'Все',
details: 'О модуле',
open: 'Перейти',
},
} as const;

View file

@ -20,21 +20,21 @@ export const MaturityBadge: React.FC<MaturityBadgeProps> = ({
}) => {
const config = {
pilot1: {
label: 'Pilot 1',
label: 'Пилотный режим',
description: 'Готово к пилоту (IndexedDB, транзакционный контур)',
badgeClass: 'bg-emerald-50 text-emerald-800 border-emerald-300 dark:bg-emerald-950/40 dark:text-emerald-300 dark:border-emerald-700',
icon: CheckCircle2,
iconClass: 'text-emerald-600 dark:text-emerald-400',
},
target: {
label: 'Target prototype',
label: 'Интерфейс подготовлен',
description: 'Целевой прототип (интерактивный макет с бизнес-логикой)',
badgeClass: 'bg-sky-50 text-sky-800 border-sky-300 dark:bg-sky-950/40 dark:text-sky-300 dark:border-sky-700',
icon: Layers,
iconClass: 'text-sky-600 dark:text-sky-400',
},
demo: {
label: 'Demo only',
label: 'Только демонстрация',
description: 'Демонстрационный режим (симуляция)',
badgeClass: 'bg-purple-50 text-purple-800 border-purple-300 dark:bg-purple-950/40 dark:text-purple-300 dark:border-purple-700',
icon: FlaskConical,

File diff suppressed because one or more lines are too long