Files
marketplaces/src/app/i18n/ru.ts

2127 lines
134 KiB
TypeScript
Raw Normal View History

2026-02-26 23:09:20 +04:00
import { Translations } from './translations';
export const ru: Translations = {
header: {
home: 'Главная',
search: 'Поиск',
about: 'О нас',
contacts: 'Контакты',
searchPlaceholder: 'Искать...',
catalog: 'Каталог',
wishlist: 'Избранное',
compare: 'Сравнение',
openMenu: 'Открыть меню',
closeMenu: 'Закрыть меню',
login: 'Войти',
logout: 'Выйти',
2026-02-26 23:09:20 +04:00
},
footer: {
description: 'Современный маркетплейс для комфортных покупок',
company: 'Компания',
aboutUs: 'О нас',
contacts: 'Контакты',
requisites: 'Реквизиты',
support: 'Поддержка',
faq: 'FAQ',
delivery: 'Доставка',
guarantee: 'Гарантия',
legal: 'Правовая информация',
offer: 'Оферта',
privacy: 'Конфиденциальность',
returns: 'Возврат',
info: 'Информация',
aboutCompany: 'О компании',
documents: 'Документы',
paymentRules: 'Правила оплаты',
returnPolicy: 'Политика возврата',
publicOffer: 'Публичная оферта',
help: 'Помощь',
payment: 'Оплата',
allRightsReserved: 'Все права защищены.',
},
home: {
welcomeTo: 'Добро пожаловать в {{brand}}',
subtitle: 'Найдите всё, что нужно, в одном месте',
startSearch: 'Начать поиск',
loading: 'Загружаем категории...',
errorTitle: 'Что-то пошло не так',
retry: 'Попробовать снова',
categoriesTitle: 'Категории товаров',
categoriesSubtitle: 'Выберите интересующую категорию',
categoriesEmpty: 'Категории скоро появятся',
categoriesEmptyDesc: 'Мы работаем над наполнением каталога',
dexarHeroTitle: 'Здесь ты найдёшь всё',
dexarHeroSubtitle: 'Тысячи товаров в одном месте',
dexarHeroTagline: 'просто и удобно',
goToCatalog: 'Перейти в каталог',
findProduct: 'Найти товар',
loadingDexar: 'Загрузка категорий...',
catalogTitle: 'Каталог товаров',
emptyCategoriesDexar: 'Категории пока отсутствуют',
categoriesSoonDexar: 'Скоро здесь появятся категории товаров',
itemsCount: '{{count}} товаров',
},
cart: {
title: 'Корзина',
clear: 'Очистить',
empty: 'Корзина пуста',
emptyDesc: 'Добавьте товары, чтобы начать покупки',
goShopping: 'Перейти к покупкам',
total: 'Итого',
items: 'Товары',
deliveryLabel: 'Доставка',
2026-06-21 23:13:01 +04:00
deliveryMethod: 'Способ доставки',
selectDelivery: 'Выберите способ доставки',
deliveryPlace: 'Место',
deliveryTime: 'Срок доставки',
digitalDelivery: 'Цифровая доставка',
deliveryRequired: 'Выберите доставку для всех товаров с доставкой перед оформлением заказа.',
2026-02-26 23:09:20 +04:00
toPay: 'К оплате',
agreeWith: 'Я согласен с',
publicOffer: 'публичной офертой',
returnPolicy: 'политикой возврата',
guaranteeTerms: 'условиями гарантии',
privacyPolicy: 'политикой конфиденциальности',
and: 'и',
checkout: 'Оформить заказ',
2026-06-29 00:06:18 +04:00
payViaQr: 'Оплатить через QR',
payViaCard: 'Оплатить картой',
2026-02-26 23:09:20 +04:00
close: 'Закрыть',
creatingPayment: 'Создание платежа...',
waitFewSeconds: 'Подождите несколько секунд',
scanQr: 'Сканируйте QR-код для оплаты',
amountToPay: 'Сумма к оплате:',
waitingPayment: 'Ожидание оплаты...',
copied: '✓ Скопировано',
copyLink: 'Скопировать ссылку',
openNewTab: 'Открыть в новой вкладке',
paymentSuccess: 'Поздравляем! Оплата прошла успешно!',
paymentSuccessDesc: 'Введите ваши контактные данные, и мы отправим вам покупку в течение нескольких минут',
sending: 'Отправка...',
send: 'Отправить',
2026-06-02 00:57:36 +04:00
paymentError: 'Не удалось создать платеж',
paymentErrorDesc: 'Сейчас не получилось подготовить QR-код для оплаты. Попробуйте еще раз через минуту.',
retryPayment: 'Попробовать снова',
2026-02-26 23:09:20 +04:00
paymentTimeout: 'Время ожидания истекло',
paymentTimeoutDesc: 'Мы не получили подтверждение оплаты в течение 3 минут.',
autoClose: 'Окно закроется автоматически...',
confirmClear: 'Вы уверены, что хотите очистить корзину?',
removeItem: 'Удалить товар',
increaseQuantity: 'Увеличить количество',
decreaseQuantity: 'Уменьшить количество',
paymentQrAlt: 'QR-код для оплаты',
2026-02-26 23:09:20 +04:00
acceptTerms: 'Пожалуйста, примите условия оферты, политику возврата и возврата для подтверждения оформления заказа.',
copyError: 'Ошибка копирования:',
emailSuccess: 'Email успешно отправлен! Проверьте свою почту.',
emailError: 'Произошла ошибка при отправке email. Пожалуйста, попробуйте снова.',
phoneRequired: 'Номер телефона обязателен',
phoneMoreDigits: 'Введите ещё {{count}} цифр',
phoneTooMany: 'Слишком много цифр',
emailRequired: 'Email обязателен',
emailTooShort: 'Email слишком короткий (минимум 5 символов)',
emailTooLong: 'Email слишком длинный (максимум 100 символов)',
emailNeedsAt: 'Email должен содержать @',
emailNeedsDomain: 'Email должен содержать домен (.com, .ru и т.д.)',
emailInvalid: 'Некорректный формат email',
telegramIdMissing: 'Не удалось определить ваш Telegram-аккаунт, поэтому контактные данные не сохранены. Оплата прошла успешно.',
emailPlaceholder: 'you@example.com',
phonePlaceholder: '+7 (___) ___-__-__',
2026-03-24 00:09:11 +04:00
loginRequired: 'Войдите для оформления',
loginRequiredDesc: 'Для оформления заказа войдите через Telegram',
loginWithTelegram: 'Войти через Telegram',
orScanQr: 'Или отсканируйте QR-код',
2026-02-26 23:09:20 +04:00
},
search: {
title: 'Поиск товаров',
placeholder: 'Введите название товара...',
searchBarAria: 'Поисковая строка',
2026-02-26 23:09:20 +04:00
resultsCount: 'Найдено товаров:',
searching: 'Поиск...',
retry: 'Попробовать снова',
noResults: 'Ничего не найдено',
noResultsFor: 'По запросу "{{query}}" товары не найдены',
noResultsHint: 'Попробуйте изменить запрос или используйте другие ключевые слова',
emptyResultsAria: 'Пустые результаты поиска',
popularCategories: 'Популярные категории',
recommendedProducts: 'Рекомендуемые товары',
aiSuggestionHint: 'AI-подсказка (в будущем)',
suggestionType: {
product: 'Товар',
category: 'Категория',
brand: 'Бренд',
collection: 'Подборка',
seller: 'Продавец',
'static-page': 'Страница',
ai: 'AI',
},
2026-02-26 23:09:20 +04:00
addToCart: 'В корзину',
loadingMore: 'Загрузка...',
allLoaded: 'Все результаты загружены',
emptyState: 'Введите запрос для поиска товаров',
2026-07-05 01:10:56 +04:00
minLength: 'Введите минимум {{count}} символа для поиска',
2026-02-26 23:09:20 +04:00
of: 'из',
},
category: {
retry: 'Попробовать снова',
addToCart: 'В корзину',
loadingMore: 'Загрузка...',
allLoaded: 'Все товары загружены',
emptyTitle: 'Здесь пока пусто',
2026-02-26 23:09:20 +04:00
emptyDesc: 'В этой категории ещё нет товаров, но скоро они появятся',
goHome: 'На главную',
loading: 'Загрузка товаров...',
},
2026-07-05 01:36:21 +04:00
catalog: {
title: 'Каталог',
breadcrumb: 'Навигация по каталогу',
allCategories: 'Все категории',
products: 'Товары',
loading: 'Загрузка каталога...',
loadingProducts: 'Загрузка товаров...',
errorTitle: 'Каталог недоступен',
error: 'Не удалось загрузить каталог. Попробуйте позже.',
retry: 'Попробовать снова',
categoryHint: 'Выберите категорию, чтобы продолжить',
productHint: 'Товары в выбранной категории',
emptyTitle: 'Здесь пока пусто',
emptyCategories: 'В этом разделе пока нет дочерних категорий.',
emptyProducts: 'В этой категории пока нет товаров.',
addToCart: 'В корзину',
categoriesCount: '{{count}} категорий',
productsCount: '{{count}} товаров',
2026-07-09 01:40:22 +04:00
entriesCount: '{{count}} позиций',
categoryBannerPlaceholder: 'Баннер категории подготовлен для медиа и описания из backend.',
navigationPlaceholder: 'Макет навигации подготовлен для конфигурации из backend/bootstrap.',
removeSavedSearch: 'Удалить сохраненный поиск',
resetFilters: 'Сбросить',
filtersTitle: 'Фильтры',
from: 'От',
to: 'До',
2026-07-09 01:40:22 +04:00
minValue: 'От {{value}}',
maxValue: 'До {{value}}',
enabled: 'Включено',
searchPlaceholder: 'Поиск товаров, брендов и категорий',
searchSubmit: 'Найти',
clearSearch: 'Очистить',
2026-07-09 01:40:22 +04:00
suggestionsTitle: 'Подсказки',
popularSearchesTitle: 'Популярные запросы',
2026-07-09 01:40:22 +04:00
recentSearchesTitle: 'Недавние запросы',
searchHistoryTitle: 'История поиска',
clearHistory: 'Очистить',
searchNoResultsHint: 'Ничего не найдено. Попробуйте более общий запрос.',
searchProductsFound: 'Найдено товаров: {{total}}',
searchResultsSummary: '{{total}} результатов по запросу "{{query}}"',
2026-07-10 15:55:38 +04:00
loadMore: 'Показать еще',
2026-07-09 01:40:22 +04:00
sortBy: 'Сортировать по',
sortRelevance: 'Рекомендуемые',
2026-07-09 01:40:22 +04:00
sortLatest: 'Новизне',
sortPriceAsc: 'Цене: по возрастанию',
sortPriceDesc: 'Цене: по убыванию',
sortRating: 'Рейтингу',
sortPopular: 'Популярности',
sortDiscount: 'Скидке',
layoutGrid: 'Сетка',
layoutLargeGrid: 'Крупная сетка',
layoutCompactGrid: 'Компактная сетка',
2026-07-10 15:55:38 +04:00
layoutGrid2: '2 колонки',
layoutGrid3: '3 колонки',
layoutGrid4: '4 колонки',
layoutCompact: 'Компактно',
2026-07-09 01:40:22 +04:00
layoutList: 'Список',
noResults: 'Нет результатов',
itemsCount: '{{count}} товаров',
emptyResultsTitle: 'Результаты не найдены',
emptyResultsDescription: 'Попробуйте изменить фильтры, сортировку или поисковый запрос.',
previousPage: 'Назад',
nextPage: 'Вперед',
pageOf: 'Страница {{page}} / {{total}}',
emptyCategoryTitle: 'В этой категории пока нет товаров',
emptyCategoryMessage: 'Попробуйте перейти в родительские категории или вернуться в общий каталог.',
2026-07-10 15:55:38 +04:00
goToParentCategory: 'Перейти в родительскую категорию',
browseCategories: 'Смотреть категории',
2026-07-10 15:55:38 +04:00
offlineTitle: 'Нет подключения',
offlineMessage: 'Подключитесь к сети, чтобы продолжить просмотр каталога.',
noFilterMatchTitle: 'Нет товаров, соответствующих выбранным фильтрам.',
noFilterMatchMessage: 'Попробуйте расширить диапазон цены, атрибуты или бренды.',
clearFilters: 'Очистить фильтры',
openFilters: 'Открыть фильтры',
applyFilters: 'Применить',
close: 'Закрыть',
filtersDrawerAria: 'Панель фильтров',
sortSheetAria: 'Параметры сортировки',
mobileToolbarAria: 'Мобильная панель каталога',
gridCycle: 'Сетка',
filtersPanelAria: 'Фильтры каталога',
2026-07-09 01:40:22 +04:00
filterPrice: 'Цена',
filterAvailability: 'Наличие',
filterInStock: 'В наличии',
filterLowStock: 'Мало в наличии',
filterOutOfStock: 'Нет в наличии',
filterRating: 'Рейтинг',
filterStars: '{{count}} звезд',
filterBrand: 'Бренд',
filterCategory: 'Категория',
filterCategoryValue: 'Категория {{value}}',
filterSubcategory: 'Подкатегория',
filterDiscount: 'Скидка',
filterNew: 'Новинки',
filterColor: 'Цвет',
filterSize: 'Размер',
filterAttributes: 'Атрибуты',
favorite: 'В избранное',
compare: 'Сравнить',
share: 'Поделиться',
quickView: 'Быстрый просмотр',
stockHigh: 'В наличии',
stockMedium: 'Ограниченно',
stockLow: 'Почти распродано',
stockOut: 'Нет в наличии',
2026-07-09 01:40:22 +04:00
},
productDetails: {
loading: 'Загрузка товара...',
errorTitle: 'Товар недоступен',
retry: 'Попробовать снова',
missingTitle: 'Товар не найден',
missingDescription: 'Этот товар не существует или больше недоступен.',
backToCatalog: 'Вернуться в каталог',
tabsAria: 'Вкладки товара',
mediaAria: 'Медиа товара',
ratingStarsAria: 'Рейтинг в звездах',
selectRatingAria: 'Выберите оценку',
fix(storefront): premium UX polish for product, compare, wishlist - delivery-information, product-actions, product-description, product-gallery, product-information, related-products, variant-selector: normalize hardcoded hex colors to design tokens (--text-primary, --text-secondary, --border-color, --bg-primary/ --bg-secondary, --primary-color/--primary-hover); stock status and discount badge now use semantic --success-color/--warning-color/ --error-color instead of near-duplicate literal hex - product-actions: add aria-pressed to wishlist/compare toggle buttons; add hover/active/disabled states to action buttons - product-gallery: add aria-current + aria-label to active thumbnail button; add focus-visible ring and hover state on thumbnails and toolbar buttons - variant-selector: add aria-pressed to colour/size option buttons; add a visible checkmark glyph on the selected colour swatch so selection isn't color-only; add hover states - product-tabs: add role="tab"/aria-selected to tab buttons - star-selector: add per-star aria-label (new starsLabel i18n key added to en/hy/ru + translations.ts interface) - question-list: add aria-expanded to the ask-question disclosure toggle; swap plain empty-state <p> for app-empty-state; add hover/disabled states to pager buttons - review-list: swap plain empty-state <p> for app-empty-state; add hover/disabled states to pager and load-more buttons - question-card, question-form, review-form: normalize accepted/ success/error colors to semantic tokens; add focus-visible and hover/disabled states to inputs and submit buttons - compare-table: add scope="col"/scope="row" to table headers; make header row and attribute column sticky for easier comparison on long tables - compare-page: add hover/focus states to the remove-from-compare chip button Build verified green via `npm run build`. Out of scope / skipped: - src/app/pages/item-detail/* is dead code (not referenced by any route or component) - left untouched - wishlist page and product-details-container were already fully composed with shared skeleton/empty-state/button components from the RC-Visual-02 pass - no changes needed - stars.component display-only rating glyphs use a light gray not an exact token match - left as-is to avoid an unintended visual shift Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 11:47:57 +04:00
starsLabel: 'звёзд',
2026-07-09 01:40:22 +04:00
reviewsEmpty: 'Пока нет отзывов.',
questionsEmpty: 'Пока нет вопросов.',
askQuestion: 'Задать вопрос',
hideQuestionForm: 'Скрыть форму вопроса',
questionFormTitle: 'Задать вопрос',
questionFormPlaceholder: 'Спросите о размере, доставке, совместимости или использовании',
questionFormAnonymous: 'Задать анонимно',
submitQuestion: 'Отправить вопрос',
reviewFormTitle: 'Оставить отзыв',
reviewRatingLabel: 'Оценка',
reviewTitleLabel: 'Заголовок',
reviewTitlePlaceholder: 'Короткий заголовок',
reviewTextLabel: 'Отзыв',
reviewTextPlaceholder: 'Опишите ваш опыт использования',
reviewAnonymous: 'Отправить анонимно',
reviewUploadPlaceholder: 'Загрузка фото запланирована в следующем спринте.',
reviewRatingRequired: 'Выберите оценку.',
reviewTextRequired: 'Добавьте текст отзыва.',
reviewSuccess: 'Отзыв успешно отправлен.',
2026-07-09 01:40:22 +04:00
submitReview: 'Отправить отзыв',
submitting: 'Отправка...',
loadMore: 'Показать еще',
showingOf: 'Показано {{shown}} из {{total}}',
questionsSubmissionFuture: 'Отправку вопросов можно включить позже без изменения компонента.',
specificationsGeneral: 'Общие',
actionsAria: 'Действия с товаром',
buyNow: 'Купить сейчас',
wishlist: 'В избранное',
compare: 'Сравнить',
share: 'Поделиться',
notifyMe: 'Сообщить о наличии',
zoom: 'Увеличить',
fullscreen: 'Полный экран',
pdfDocument: 'PDF документ товара',
manualDocument: 'Инструкция товара',
warrantyDocument: 'Гарантийный документ',
openDocument: 'Открыть документ',
2026-07-09 01:40:22 +04:00
specificationsEmpty: 'Характеристики пока не указаны.',
warrantyTitle: 'Гарантия и возврат',
warrantyItem1: 'Условия гарантии определяются продавцом и местным законодательством.',
warrantyItem2: 'Возможность возврата зависит от состояния и категории товара.',
warrantyItem3: 'Подробная инструкция по возврату доступна после покупки.',
verifiedPurchase: 'Подтвержденная покупка',
officialSeller: 'Официальный продавец',
acceptedAnswer: 'Принятый ответ',
like: 'Нравится',
dislike: 'Не нравится',
reviewsCountLabel: '{{count}} отзывов',
previous: 'Назад',
next: 'Вперед',
pageOf: 'Страница {{page}} / {{total}}',
2026-07-05 01:36:21 +04:00
},
2026-02-26 23:09:20 +04:00
subcategories: {
loading: 'Загрузка подкатегорий...',
retry: 'Попробовать снова',
emptyTitle: 'Подкатегорий пока нет',
2026-02-26 23:09:20 +04:00
emptyDesc: 'В этом разделе ещё нет подкатегорий, но скоро они появятся',
goHome: 'На главную',
2026-03-24 00:09:11 +04:00
itemsInCategory: 'Товары в этой категории',
2026-07-05 01:09:55 +04:00
childrenCount: '{{count}} категорий',
productsCount: '{{count}} товаров',
includesProducts: 'Есть товары',
2026-02-26 23:09:20 +04:00
},
itemDetail: {
loading: 'Загрузка...',
loadingDexar: 'Загрузка товара...',
back: 'Вернуться',
backHome: 'Вернуться на главную',
noImage: 'Нет изображения',
stock: 'Наличие:',
inStock: 'В наличии',
lowStock: 'Осталось немного',
lastItems: 'Последние штуки',
mediumStock: 'Заканчивается',
addToCart: 'Добавить в корзину',
description: 'Описание',
specifications: 'Характеристики',
2026-02-26 23:09:20 +04:00
reviews: 'Отзывы',
yourReview: 'Ваш отзыв',
leaveReview: 'Оставить отзыв',
rating: 'Оценка:',
reviewPlaceholder: 'Поделитесь своими впечатлениями о товаре...',
reviewPlaceholderDexar: 'Поделитесь впечатлениями о товаре...',
anonymous: 'Анонимно',
submitting: 'Отправка...',
submit: 'Отправить',
reviewSuccess: 'Спасибо за ваш отзыв!',
reviewError: 'Ошибка отправки. Попробуйте позже.',
defaultUser: 'Пользователь',
defaultUserDexar: 'Аноним',
noReviews: 'Пока нет отзывов. Станьте первым!',
qna: 'Вопросы и ответы',
photo: 'Фото',
reviewsCount: 'отзывов',
today: 'Сегодня',
yesterday: 'Вчера',
daysAgo: 'дн. назад',
weeksAgo: 'нед. назад',
2026-03-24 00:09:11 +04:00
colour: 'Цвет',
size: 'Размер',
2026-07-05 01:07:53 +04:00
relatedProducts: 'Похожие товары',
2026-02-26 23:09:20 +04:00
},
app: {
connecting: 'Подключение к серверу...',
serverUnavailable: 'Сервер недоступен',
serverError: 'Не удалось подключиться к серверу. Проверьте подключение к интернету.',
retryConnection: 'Повторить попытку',
pageTitle: 'Маркетплейс товаров и услуг',
fix(storefront): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, storefront + shared app-shell chrome only. Builds on RC-Visual-02/RC-Premium-01/RC STORE-01 without redoing that work. - Skip link: added first-focusable "skip to main content" link (app.html, styles.scss .skip-link/.sr-only), targeting new #main-content landmark. New app.skipToContent i18n key in en/ru/hy. - Header: mobile menu items stayed keyboard-focusable and screen-reader reachable while visually collapsed (max-height:0 with no visibility toggle) - fixed with visibility:hidden + matched transition-delay. Desktop search input (readonly, click-to-navigate) had no keyboard activation - added aria-label + (keydown.enter). - Cart payment/bank-payment modals: custom (non-app-dialog) UI had no focus trap, no Escape handling, and never returned focus to the triggering element - ported app-dialog's confirmed-correct focus-trap/Escape/return-focus pattern directly onto cart.component.ts. Added role="dialog"/aria-modal/aria-label to both panels and role="status"|"alert"/aria-live to every payment-status screen so screen readers announce state changes (creating/waiting/success/ error/timeout). - Search combobox: suggestion listbox had no role="combobox" wiring on the input and suggestion buttons weren't role="option" - added aria-autocomplete, aria-controls, aria-activedescendant, aria-selected so the existing arrow-key navigation is announced to screen readers. - Product tabs: tablist/tab pattern was incomplete (no role="tablist", no tabpanel) - added role="tablist" + ids to product-tabs.component, role="tabpanel"/aria-labelledby to the content panel in product-details-container. - Review form: rating/text validation errors weren't associated with their controls (no aria-describedby, no role="alert") - fixed; added aria-required to the review textarea. - delivery-selector: added aria-required to the delivery <select> when a selection is mandatory. - Shared app-icon component: doc comment claimed "decorative by default (aria-hidden)" but no aria-hidden was ever applied - fixed to actually set aria-hidden="true" when undecorated, and role="img"/aria-label when ariaLabel is passed. Shared component, affects every icon-only usage app-wide, no visual change. - Color contrast: --text-light fails WCAG AA 4.5:1 for normal text in every theme (dexar 3.39:1, lavero/novo 2.54:1 against white). The two in-scope usages (company-details org-short/basis, review-form upload-placeholder) switched to --text-secondary (4.55:1-7.56:1, passes), same visual family, no layout change. Flagged, not fixed (design-system decisions, not polish): - --border-color fails WCAG 1.4.11 3:1 for UI-component boundaries in every theme (dexar 1.42:1, lavero/novo 1.24:1 vs white) - pervasive token used by hundreds of borders app-wide; needs theme-owner sign-off. - --success-color/--warning-color/--error-color/--info-color used as plain text-on-white in several places (product-information, question-card, review-form, compare-page) fail 4.5:1 (2.15-3.76:1) - genuine brand semantic colors, changing them to pass would visibly shift the palette; needs a deliberate token decision. - Header mobile-menu max-height/padding transition (pre-existing, unrelated to this fix) flagged by design lint as layout-thrashing; left as-is per the "no layout/business-logic changes" constraint. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing bundle-budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 08:46:18 +04:00
skipToContent: 'Перейти к основному содержимому',
2026-02-26 23:09:20 +04:00
},
platform: {
loadingPage: 'Загрузка страницы платформы...',
emptyConfig: 'Конфигурация страницы не найдена.',
},
backoffice: {
title: 'Песочница Backoffice',
subtitle: 'Домен управления бизнес-данными (mock для платформенного фундамента).',
loading: 'Загрузка данных backoffice...',
products: 'Товары',
categories: 'Категории',
otherDomains: 'Другие домены',
sku: 'SKU',
titleColumn: 'Название',
price: 'Цена',
status: 'Статус',
itemsCount: 'шт.',
orders: 'Заказы',
customers: 'Клиенты',
inventory: 'Склад',
media: 'Медиа',
settings: 'Настройки',
notAvailable: 'н/д',
},
builder: {
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
title: 'Конструктор маркетплейса',
subtitle: 'Соберите свой маркетплейс шаг за шагом.',
2026-07-10 13:43:53 +04:00
general: 'Общие',
branding: 'Брендинг',
2026-07-10 13:43:53 +04:00
theme: 'Тема',
header: 'Хедер',
footer: 'Футер',
homepage: 'Главная страница',
widgets: 'Виджеты',
2026-07-10 13:52:01 +04:00
staticPages: 'Статические страницы',
2026-07-10 13:43:53 +04:00
marketplaceFeatures: 'Функции маркетплейса',
preview: 'Превью',
brandName: 'Название бренда',
2026-07-10 13:43:53 +04:00
marketplaceName: 'Название маркетплейса',
domain: 'Домен',
descriptionLabel: 'Описание',
defaultLanguage: 'Язык по умолчанию',
supportedLanguages: 'Поддерживаемые языки',
logo: 'Логотип',
smallLogo: 'Малый логотип',
favicon: 'Favicon',
socialImage: 'Изображение для соцсетей',
gallery: 'Галерея',
addGalleryImage: 'Добавить изображение',
headerLayout: 'Расположение шапки',
headerLayoutDefault: 'По умолчанию',
headerLayoutCentered: 'По центру',
headerSticky: 'Липкая шапка',
2026-07-10 13:43:53 +04:00
marketplaceTitle: 'Заголовок маркетплейса',
primaryColor: 'Основной цвет',
secondaryColor: 'Вторичный цвет',
backgroundColor: 'Фон',
surfaceColor: 'Поверхность',
textColor: 'Текст',
successColor: 'Success',
warningColor: 'Warning',
dangerColor: 'Danger',
showLogo: 'Логотип',
showSearch: 'Поиск',
showCategories: 'Категории',
showLanguages: 'Языки',
showCart: 'Корзина',
showProfile: 'Профиль',
showWishlist: 'Избранное',
showCompare: 'Сравнение',
showRegion: 'Регион',
companyName: 'Информация о компании',
address: 'Адрес',
phone: 'Телефон',
email: 'Email',
copyright: 'Копирайт',
paymentIcons: 'Иконки оплаты',
socialLinks: 'Социальные ссылки',
footerLogo: 'Логотип в футере',
addPaymentIcon: 'Добавить иконку оплаты',
removePaymentIcon: 'Удалить',
iconAltLabel: 'Подпись иконки',
addSocialLink: 'Добавить соцсеть',
removeSocialLink: 'Удалить',
socialLinkLabel: 'Платформа',
socialLinkUrl: 'URL',
invalidUrl: 'Введите корректный URL, начинающийся с http:// или https://.',
2026-07-10 13:52:01 +04:00
createPage: 'Создать страницу',
deletePage: 'Удалить страницу',
pageId: 'ID страницы',
slug: 'Slug',
iconLabel: 'Иконка',
showInFooter: 'Показывать в футере',
showInHeader: 'Показывать в хедере',
showInSitemap: 'Показывать в sitemap',
requiresAuthentication: 'Требует авторизацию',
footerGroup: 'Группа футера',
orderLabel: 'Порядок',
translationTitle: 'Заголовок перевода',
htmlPreview: 'HTML',
seoSection: 'SEO',
seoTitle: 'SEO заголовок',
seoDescription: 'SEO описание',
seoKeywords: 'Ключевые слова',
seoCanonical: 'Canonical URL',
seoOgTitle: 'OG заголовок',
seoOgDescription: 'OG описание',
seoOgImage: 'OG изображение',
2026-07-10 13:43:53 +04:00
visible: 'Видимость',
layoutLabel: 'Layout',
columns: 'Колонки',
height: 'Высота',
overlay: 'Overlay',
autoplay: 'Autoplay',
cardsPerRow: 'Карточек в ряд',
filtersLabel: 'Фильтры',
showBadges: 'Показывать badges',
showRating: 'Показывать рейтинг',
showPrice: 'Показывать цену',
widgetJson: 'JSON виджета',
reviews: 'Отзывы',
questions: 'Вопросы',
comments: 'Комментарии',
recommendations: 'Рекомендации',
recentlyViewed: 'Недавно просмотренные',
searchSuggestions: 'Поисковые подсказки',
searchHistory: 'История поиска',
livePreview: 'Живое превью',
previewChangesTitle: 'Изменения с последней публикации',
previewNoIssues: 'Нет проблем валидации.',
previewNoChanges: 'Нет изменений с последней публикации.',
previewChangeField: 'Поле',
previewChangeBefore: 'До',
previewChangeAfter: 'После',
languagesTab: 'Языки',
addLanguage: 'Добавить язык',
removeLanguage: 'Удалить',
setDefaultLanguage: 'Сделать языком по умолчанию',
defaultLanguageLabel: 'По умолчанию',
navigationTab: 'Навигация',
navigationHeader: 'Навигация в шапке',
navigationFooter: 'Навигация в подвале',
navigationFooterGrouped: 'Навигация подвала сгруппирована по колонкам и пока недоступна для редактирования здесь — используйте вкладку "Подвал".',
addLink: 'Добавить ссылку',
insertPageLink: 'Вставить ссылку на страницу',
linkedToPage: 'Связано со страницей',
removeLink: 'Удалить',
linkLabel: 'Название',
linkUrl: 'URL',
2026-07-10 13:43:53 +04:00
exportBootstrap: 'Экспорт bootstrap',
importBootstrap: 'Импорт bootstrap',
exportedBootstrap: 'Экспортированный bootstrap',
importSource: 'Источник импорта',
importError: 'Импорт не удался. Некорректный JSON bootstrap.',
featureFlags: 'Фичи-флаги',
validationMissingLogo: 'В брендинге отсутствует логотип.',
validationNoLanguages: 'Не настроены языки.',
validationDefaultLocaleNotSupported: 'Язык по умолчанию отсутствует в списке поддерживаемых языков.',
validationInvalidUrl: 'Некорректный URL маркетплейса.',
validationDuplicateSlugs: 'Две или более статические страницы имеют одинаковый slug.',
validationEmptyHomepage: 'На главной странице нет секций.',
validationMissingWidget: 'В секции главной страницы есть виджет без типа.',
validationDuplicateNavLinks: 'Две или более ссылки в навигации шапки дублируются.',
validationInvalidColors: 'Один или несколько цветов темы указаны некорректно.',
validationMissingTranslations: 'Часть контента не переведена на один из поддерживаемых языков.',
validationInvalidLayouts: 'Один или несколько макетов ссылаются на неизвестный тип раскладки.',
validationInvalidJson: 'Конфигурация не является допустимым JSON.',
validationInvalidCss: 'Статическая страница содержит недопустимый CSS.',
validationDuplicateRoutes: 'Две или более страницы используют один и тот же маршрут.',
validationInvalidWidgetConfig: 'У виджета отсутствует обязательное поле (id, type, version или props).',
validationInvalidWidgetSettings: 'Настройки виджета не соответствуют ожидаемым для его типа (поле отсутствует или неверного типа).',
validationInvalidContactEmail: 'Контактный email компании указан некорректно.',
validationInvalidSocialLinkUrl: 'У одной или нескольких социальных ссылок в футере некорректный URL.',
validationIncompletePaymentIcon: 'У иконки оплаты в футере отсутствует изображение или альтернативный текст.',
undo: 'Отменить',
redo: 'Повторить',
statusDraft: 'Черновик',
statusPublished: 'Опубликовано',
unsavedChanges: 'Есть несохранённые изменения',
save: 'Сохранить',
publish: 'Опубликовать',
confirmLeaveUnsaved: 'Есть несохранённые изменения. Всё равно уйти?',
confirmPublish: 'Это сразу применит текущие изменения на витрине магазина вместо того, что покупатели видят сейчас. Продолжить?',
confirmRemoveLanguage: 'Это удалит язык и весь переведённый на него контент. Покупатели, использующие этот язык, увидят язык по умолчанию. Продолжить?',
promptLinkUrl: 'URL',
promptImageUrl: 'URL изображения',
promptEmbedUrl: 'URL для встраивания',
htmlEditorInvalidHtml: 'В этом HTML есть незакрытый или несовпадающий тег. Исправьте перед возвратом к визуальному редактору.',
htmlEditorCode: 'Код',
htmlEditorPreview: 'Предпросмотр',
lastSaved: 'Последнее сохранение',
draftRestored: 'Локальный черновик восстановлен из предыдущей сессии.',
dismiss: 'Скрыть',
resetDraft: 'Сбросить черновик',
resetSection: 'Сбросить секцию',
confirmResetDraft: 'Это отменит все неопубликованные изменения и восстановит последнюю опубликованную конфигурацию. Продолжить?',
confirmResetSection: 'Это отменит неопубликованные изменения только в этой секции. Продолжить?',
marketplaceNameDesc: 'Название, которое видят покупатели на витрине и во вкладке браузера.',
domainDesc: 'Домен, по которому покупатели заходят на маркетплейс. Изменение поля не переносит DNS.',
descriptionFieldDesc: 'Краткое описание для поисковых систем и превью в соцсетях.',
defaultLanguageDesc: 'Язык, который показывается, если у покупателя нет сохранённого предпочтения.',
supportedLanguagesDesc: 'Коды языков через запятую, между которыми может переключаться покупатель (например, en, ru, hy).',
logoDesc: 'URL полного логотипа, отображается в шапке на десктопе.',
smallLogoDesc: 'URL компактного логотипа для ограниченного пространства (например, мобильная шапка).',
faviconDesc: 'URL иконки вкладки браузера. Должен быть небольшим квадратным изображением.',
socialImageDesc: 'Изображение по умолчанию при публикации маркетплейса в соцсетях (Open Graph).',
galleryDesc: 'Дополнительные брендовые изображения для будущего использования в витрине.',
headerLayoutDesc: 'Как расположен контент шапки: по умолчанию (логотип слева, навигация разнесена) или по центру.',
headerStickyDesc: 'Закреплять шапку вверху экрана при прокрутке.',
marketplaceTitleDesc: 'Заголовок страницы для поисковых систем и вкладок браузера.',
primaryColorDesc: 'Основной цвет бренда для главных кнопок и акцентов. Должен быть корректным hex-цветом.',
secondaryColorDesc: 'Дополнительный цвет бренда для второстепенных элементов. Должен быть корректным hex-цветом.',
backgroundColorDesc: 'Цвет фона страницы по умолчанию. Должен быть корректным hex-цветом.',
surfaceColorDesc: 'Цвет фона карточек и приподнятых поверхностей. Должен быть корректным hex-цветом.',
textColorDesc: 'Цвет основного текста. Должен быть корректным hex-цветом.',
successColorDesc: 'Цвет для сообщений об успехе и положительных состояний. Должен быть корректным hex-цветом.',
warningColorDesc: 'Цвет для предупреждающих сообщений. Должен быть корректным hex-цветом.',
dangerColorDesc: 'Цвет для ошибок и разрушительных действий. Должен быть корректным hex-цветом.',
themeModeLabel: 'Цветовой режим',
themeModeDesc: 'Определяет, отображается ли витрина в светлой, тёмной теме или по системным настройкам покупателя.',
themeModeLight: 'Светлый',
themeModeLightDesc: 'Всегда использовать светлую тему.',
themeModeDark: 'Тёмный',
themeModeDarkDesc: 'Всегда использовать тёмную тему.',
themeModeSystem: 'Системный',
themeModeSystemDesc: 'Использовать системные/браузерные настройки цветовой темы устройства покупателя.',
siteLayoutLabel: 'Макет сайта',
siteLayoutDesc: 'Общий режим оформления страниц на витрине.',
siteLayoutDefault: 'По умолчанию',
siteLayoutDefaultDesc: 'Стандартный макет с верхней навигацией.',
siteLayoutSidebarLeft: 'Боковая панель (слева)',
siteLayoutSidebarLeftDesc: 'Добавляет левую боковую панель, обычно для навигации по категориям.',
siteLayoutCarouselHome: 'Карусель на главной',
siteLayoutCarouselHomeDesc: 'Главная страница начинается с полноширинной карусели/hero-блока.',
siteLayoutMinimal: 'Минимальный',
siteLayoutMinimalDesc: 'Упрощённое оформление с минимумом элементов навигации.',
showLogoDesc: 'Показывать логотип маркетплейса в шапке.',
showSearchDesc: 'Показывать строку поиска в шапке.',
showCategoriesDesc: 'Показывать вход в меню категорий в шапке.',
showLanguagesDesc: 'Показывать переключатель языка в шапке.',
showCartDesc: 'Показывать иконку/ссылку корзины в шапке.',
showProfileDesc: 'Показывать вход в профиль/аккаунт в шапке.',
showWishlistDesc: 'Показывать вход в избранное в шапке.',
showCompareDesc: 'Показывать вход в сравнение товаров в шапке.',
showRegionDesc: 'Показывать выбор региона/страны в шапке.',
companyNameDesc: 'Юридическое название компании, показывается в футере и на статических страницах.',
addressDesc: 'Юридический/фактический адрес компании, показывается в футере.',
phoneDesc: 'Контактный телефон, показывается в футере.',
emailDesc: 'Контактный email, показывается в футере.',
copyrightDesc: 'Строка копирайта внизу каждой страницы.',
paymentIconsDesc: 'По одной иконке оплаты на строку (URL изображения и подпись), показываются в футере.',
socialLinksDesc: 'По одной социальной ссылке на строку (платформа и URL), показываются в футере.',
footerLogoDesc: 'Логотип, отображаемый в футере, если отличается от логотипа в шапке.',
iconAltLabelDesc: 'Альтернативный текст для иконки оплаты (для доступности).',
socialLinkUrlDesc: 'Полный URL профиля в соцсети, должен начинаться с http:// или https://.',
staticPagesFieldDesc: 'Статические страницы (о компании, условия, политика и т.д.), на которые ссылается футер.',
feat(builder): visual footer builder with drag-and-drop columns Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> P0 user feedback: footer static pages were comma-separated text; no way to add extra phones/emails for different countries. - New FooterColumnConfig/FooterLinkConfig model (footer-config.model.ts): columns of links, each link pointing at an existing static page (resolved by key, so it survives route renames) or a custom URL - Footer Builder UI: add/remove columns and links, per-link toggle between 'existing page' (dropdown of real static pages) and 'custom URL', drag-and-drop reordering of both columns and links via @angular/cdk/drag-drop (same primitive already used by the homepage section builder) - Wired FooterResolverService (the service the real storefront footer actually renders through) to read footer.columns as the primary source when present - without this the builder would have saved data nobody ever displayed. Falls back to the existing legacy static-page auto-grouping when no columns are configured, so existing sites are unaffected - CompanyContactConfig gains additionalPhones/additionalEmails (primary phone/email field unchanged) with add/remove UI for country-specific support lines - Old comma-separated staticPageKeys input removed from the UI; field kept on the model as deprecated/read-compat only - New builder.* i18n keys (en/ru/hy); fixed an accidental duplicate-key collision with pre-existing navigation-section addLink/removeLink keys during the rename pass - Verified in browser: added column, added link, switched link source page->custom, added phone number - all reactive and error-free
2026-07-19 13:58:57 +04:00
additionalPhones: 'Дополнительные номера телефонов',
additionalPhonesDesc: 'Добавьте номера, если поддерживаете клиентов в разных странах.',
addPhone: 'Добавить номер',
removePhone: 'Удалить номер',
additionalEmails: 'Дополнительные адреса email',
additionalEmailsDesc: 'Добавьте адреса для разных регионов или отделов.',
addEmail: 'Добавить email',
removeEmail: 'Удалить email',
footerColumns: 'Колонки подвала',
footerColumnsDesc: 'Группируйте ссылки по колонкам, как в настоящем подвале сайта. Перетаскивайте колонки и ссылки, чтобы изменить порядок.',
footerColumnTitlePlaceholder: 'Название колонки, напр. Компания',
footerLinkLabelPlaceholder: 'Текст ссылки',
footerLinkExistingPage: 'Существующая страница',
footerLinkCustomUrl: 'Свой URL',
addColumn: 'Добавить колонку',
removeColumn: 'Удалить колонку',
addColumnLink: 'Добавить ссылку',
removeColumnLink: 'Удалить ссылку',
sectionVisibleDesc: 'Показывать ли эту секцию главной страницы покупателям.',
layoutStrategyLabel: 'Стратегия раскладки',
layoutStrategyDesc: 'Как располагаются виджеты внутри этой секции.',
layoutStrategyStack: 'Стопка',
layoutStrategyStackDesc: 'Виджеты располагаются друг под другом вертикально.',
layoutStrategyGrid: 'Сетка',
layoutStrategyGridDesc: 'Виджеты располагаются в многоколоночной сетке.',
layoutStrategyHero: 'Hero',
layoutStrategyHeroDesc: 'Один крупный полноширинный промо-блок.',
layoutStrategyCarousel: 'Карусель',
layoutStrategyCarouselDesc: 'Виджеты прокручиваются горизонтально как карусель.',
layoutStrategySplit: 'Разделение',
layoutStrategySplitDesc: 'Контент делится на две колонки рядом друг с другом.',
sectionColumnsDesc: 'Количество колонок, если стратегия раскладки их поддерживает.',
widgetLayoutDesc: 'Название варианта раскладки в свободной форме, понятное этому виджету (например, "full-bleed", "boxed").',
widgetHeightDesc: 'Предпочтительная высота виджета (например, "480px" или "60vh").',
widgetOverlayDesc: 'Показывать тёмную подложку под текстом hero для читаемости.',
widgetAutoplayDesc: 'Автоматически прокручивать слайды hero без участия покупателя.',
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel. Homepage section (sections list -> visual blocks): - Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html - Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3') - Added duplicate and remove per block, alongside the existing drag-to-reorder - Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end Widgets section (hero widget): - 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind - 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values - New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before - HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control Carousel arrows root cause and fix: - widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place - Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise - Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
heroLayoutFullBleed: 'На всю ширину (край в край)',
heroLayoutBoxed: 'В рамке (с отступами страницы)',
heroHeightCompact: 'Компактная',
heroHeightMedium: 'Средняя',
heroHeightTall: 'Высокая',
heroHeightFullScreen: 'На весь экран',
heroSlides: 'Слайды',
heroSlidesHint: 'Добавьте ещё слайды, чтобы получился слайд-шоу. Поля выше — это слайд 1. Автопрокрутка и точки появляются только при более чем одном слайде.',
heroSlideTitlePlaceholder: 'Заголовок слайда',
heroSlideSubtitlePlaceholder: 'Подзаголовок слайда',
addSlide: 'Добавить слайд',
removeSlide: 'Удалить слайд',
widgetColumnsDesc: 'Количество плиток категорий в ряду.',
widgetCardsPerRowDesc: 'Количество карточек товаров в ряду.',
widgetFiltersDesc: 'Показывать элементы управления фильтрами рядом с подборкой товаров.',
widgetShowBadgesDesc: 'Показывать бейджи, например "Новинка" или "Скидка", на карточках товаров.',
widgetShowRatingDesc: 'Показывать рейтинг звёздами на карточках товаров.',
widgetShowPriceDesc: 'Показывать цену на карточках товаров.',
widgetJsonDesc: 'Сырые свойства виджета в формате JSON. Используется, если для этого типа виджета нет отдельного редактора.',
widgetJsonInvalid: 'Некорректный JSON — сохранена последняя корректная версия.',
wishlistFeatureDesc: 'Разрешить покупателям сохранять товары в избранное.',
compareFeatureDesc: 'Разрешить покупателям сравнивать несколько товаров одновременно.',
reviewsFeatureDesc: 'Разрешить покупателям оставлять и читать отзывы о товарах.',
questionsFeatureDesc: 'Разрешить покупателям задавать вопросы о товарах и читать ответы.',
commentsFeatureDesc: 'Включить общие комментарии к контенту маркетплейса.',
recommendationsFeatureDesc: 'Показывать блоки рекомендованных товаров на витрине.',
recentlyViewedFeatureDesc: 'Показывать покупателям список "недавно просмотренные".',
searchSuggestionsDesc: 'Показывать живые подсказки поиска по мере ввода запроса.',
searchHistoryDesc: 'Запоминать и показывать недавние поисковые запросы покупателя.',
catalogNavigationModeLabel: 'Режим навигации каталога',
catalogNavigationModeDesc: 'Как покупатели перемещаются по категориям в каталоге.',
catalogNavDefault: 'По умолчанию',
catalogNavDefaultDesc: 'Стандартная навигация по категориям через хлебные крошки.',
catalogNavLeftCategory: 'Левая навигация по категориям',
catalogNavLeftCategoryDesc: 'Постоянное дерево категорий в левой боковой панели.',
catalogNavMegaCategory: 'Мега-меню категорий',
catalogNavMegaCategoryDesc: 'Развёрнутый выпадающий список с подкатегориями и избранными товарами.',
catalogNavTopCarousel: 'Карусель категорий сверху',
catalogNavTopCarouselDesc: 'Горизонтально прокручиваемые чипы категорий над сеткой каталога.',
fix(builder): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, Builder (project-editor) + content-management static pages editor. Builds on 4ebc15f's composition pass without redoing it. - Skip link: Builder's /edit and /edit/:section routes render outside the storefront app-shell (isAdminRoute() branch has no skip link/landmark, only a bare router-outlet, unlike the storefront's app.html) - added a "skip to content" link targeting a new #builder-main-content landmark on project-editor-page.component, reusing the existing adminShell.skipToContent key and global .skip-link style. Nav landmark already had aria-label (builder.appName) from the prior pass, verified correct. - Save bar: added role="status"/aria-live="polite" to the save/publish status block and role="status" to the draft-restored notice so save/publish state changes and draft recovery are announced to screen readers (previously silent DOM updates). - Media picker (shared, used by both Builder and static-pages editor): upload error message had no aria-live wiring - added role="alert". - HTML editor (marketplace-html-editor): the contenteditable rich-text surface had no accessible role/name - added role="textbox", aria-multiline="true", aria-label. - Drag-and-drop keyboard fallback (WCAG 2.1.1): homepage-section's block list and footer-section's column list + per-column link list use Angular CDK drag-drop (cdkDrag/cdkDropList), which has no built-in keyboard reordering. Added move-up/move-down icon buttons (disabled at the first/last boundary), matching the existing pattern already used by widgets-section and navigation-section. - Color picker: the swatch <input type="color"> had no accessible name (only the paired text input was labelled via app-form-field) - added explicit ariaLabel bindings to all 8 color-picker instances in theme-section. - Languages: the new-locale code input relied on a placeholder ("de") as its only accessible name - added ariaLabel + new builder.newLanguageLabel i18n key (en/ru/hy). - Undefined CSS var --color-primary (never defined anywhere, silently used its hardcoded hex fallback and never responded to tenant theming - same recurring bug class as 4ebc15f) - remapped to the real --primary-color token in marketplace-html-editor, homepage-section, and section.shared (7 usages). - role="alert" added to all 11 validation-error <p class="editor-error"> occurrences across footer/homepage/languages/navigation/preview/widgets sections and the static-pages editor, so field/section validation messages are announced. - scope="col" added to preview-section's change-summary table headers. Flagged, not fixed (design-system decisions, matching the storefront pass's precedent): - Save bar's --warning-color/--error-color/--info-color text fail WCAG AA 4.5:1 in some themes - same genuine brand semantic colors flagged (not fixed) in fb1afb7's storefront pass; needs a deliberate token decision, not a Builder-specific issue. - locale-tabs (app-locale-tabs, shared) has role="tablist"/"tab" and aria-selected but no roving-tabindex/arrow-key navigation - matches the same partial-tablist pattern already accepted for product-tabs in the storefront pass; all tabs remain natively Tab-focusable, so this meets 4.1.2/2.1.1 without the full ARIA authoring-practice pattern. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing bundle-budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 09:01:38 +04:00
newLanguageLabel: 'Код нового языка',
newLanguageDesc: 'Код языка ISO для добавления в поддерживаемые языки витрины (например, de, fr).',
languageAlreadyAdded: 'Этот язык уже добавлен.',
languagesTabDesc: 'Языки, доступные покупателям. Язык по умолчанию используется как резервный везде.',
linkLabelDesc: 'Текст, отображаемый для этой ссылки навигации.',
linkUrlDesc: 'Путь или URL, на который ведёт эта ссылка навигации.',
linkVisibleDesc: 'Показывать ли эту ссылку навигации покупателям.',
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
appName: 'Конструктор маркетплейса',
backToDashboard: 'Назад в панель управления',
languagesSummaryDesc: 'Языки, на которых доступен ваш маркетплейс. Первый — тот, что покупатели видят по умолчанию.',
manageLanguages: 'Управлять языками',
htmlToolBold: 'Жирный',
htmlToolItalic: 'Курсив',
htmlToolUnderline: 'Подчёркнутый',
htmlToolHeading: 'Заголовок',
htmlToolSubheading: 'Подзаголовок',
htmlToolBulletList: 'Маркированный список',
htmlToolNumberedList: 'Нумерованный список',
htmlToolLink: 'Вставить ссылку',
htmlToolImage: 'Вставить изображение',
htmlToolTable: 'Вставить таблицу',
htmlToolDivider: 'Разделительная линия',
htmlToolCodeBlock: 'Блок кода',
htmlToolEmbed: 'Встроить видео',
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
breadcrumbRoot: 'Конструктор маркетплейса',
statusComplete: 'Готово',
statusInProgress: 'В процессе',
statusNotStarted: 'Не начато',
statusUnknown: 'Пока не отслеживается',
draftBadge: 'Черновик',
publishedBadge: 'Опубликовано',
modifiedBadge: 'Есть несохранённые изменения',
helpToggleShow: 'Что это такое?',
helpToggleHide: 'Скрыть подсказку',
helpWhatIsThis: 'Что это',
helpWhySkip: 'Зачем это настраивать',
helpWhereVisible: 'Где это видят покупатели',
helpWhatIfSkip: 'Что будет, если пропустить',
helpWhenReturn: 'Когда возвращаться к этому разделу',
openSidebar: 'Открыть меню разделов',
closeSidebar: 'Закрыть меню разделов',
overviewTitle: 'Конструктор маркетплейса',
overviewSubtitle: 'Соберите свой маркетплейс шаг за шагом',
overviewReadinessLabel: 'Маркетплейс настроен на {{percent}}%',
overviewReadinessHint: 'Основано на обязательных полях, которые вы уже заполнили.',
overviewNextStepLabel: 'Рекомендуемый следующий шаг',
overviewNextStepFallback: 'Все обязательные шаги выполнены — можно опубликовать маркетплейс.',
overviewQuickLinksTitle: 'Быстрые ссылки',
overviewSectionsTitle: 'Разделы конструктора',
overviewOpenSection: 'Открыть раздел',
overviewReadinessTitle: 'Готовность маркетплейса',
readinessLogoUploaded: 'Логотип загружен',
readinessHomepageConfigured: 'Главная страница настроена',
readinessThemeConfigured: 'Тема настроена',
readinessLanguagesConfigured: 'Языки настроены',
readinessContentPagesAdded: 'Добавлены информационные страницы',
readinessPreviewReviewed: 'Просмотр проверен',
readinessPublished: 'Маркетплейс опубликован',
readinessCategoriesExist: 'Есть категории',
readinessProductsExist: 'Есть товары',
groupMarketplaceLabel: 'Маркетплейс',
groupMarketplaceDescription: 'Название, адрес и основная информация о вашем маркетплейсе.',
groupMarketplacePurpose: 'Базовая информация, которая определяет ваш маркетплейс: название, домен и язык по умолчанию.',
groupMarketplaceWhere: 'Название и адрес видны во вкладке браузера, поисковой выдаче и на всех страницах магазина.',
groupMarketplaceSkip: 'Без названия и адреса маркетплейс не будет выглядеть завершённым для покупателей.',
groupBrandingLabel: 'Брендинг и оформление',
groupBrandingDescription: 'Логотип, цвета, шапка и подвал — визуальный стиль вашего магазина.',
groupBrandingPurpose: 'Настройте, как выглядит ваш маркетплейс: логотип, цветовую палитру и элементы шапки/подвала сайта.',
groupBrandingWhere: 'Изменения видны на каждой странице, которую видят покупатели.',
groupBrandingSkip: 'Маркетплейс будет использовать оформление по умолчанию, не отражающее ваш бренд.',
groupHomepageLabel: 'Главная страница',
groupHomepageDescription: 'Баннеры, витрины и виджеты, которые покупатели видят первыми.',
groupHomepagePurpose: 'Соберите главную страницу из блоков: приветственный баннер, подборки категорий и товаров, акции.',
groupHomepageWhere: 'Это первая страница, которую видит покупатель, открыв ваш маркетплейс.',
groupHomepageSkip: 'Главная страница останется пустой или будет использовать макет по умолчанию.',
groupContentLabel: 'Контент',
groupContentDescription: 'О компании, контакты, доставка, возврат и другие информационные страницы.',
groupContentPurpose: 'Добавьте страницы, которым покупатели доверяют: о компании, контакты, условия доставки и возврата.',
groupContentWhere: 'Ссылки на эти страницы обычно показываются в подвале сайта.',
groupContentSkip: 'У покупателей не будет ответов на вопросы о доставке, возврате и контактах — это снижает доверие.',
groupLanguagesLabel: 'Языки',
groupLanguagesDescription: 'На каких языках работает ваш маркетплейс.',
groupLanguagesPurpose: 'Выберите язык по умолчанию и добавьте дополнительные языки для покупателей из других регионов.',
groupLanguagesWhere: 'Переключатель языка виден в шапке сайта на каждой странице.',
groupLanguagesSkip: 'Маркетплейс будет доступен только на одном языке по умолчанию.',
groupFeaturesLabel: 'Функции маркетплейса',
groupFeaturesDescription: 'Список желаний, сравнение, отзывы, оформление заказа без регистрации и другое.',
groupFeaturesPurpose: 'Включите или отключите функции для покупателей: список желаний, сравнение товаров, гостевой заказ.',
groupFeaturesWhere: 'Влияет на страницы каталога, товара и оформления заказа.',
groupFeaturesSkip: 'Маркетплейс будет использовать функции по умолчанию.',
groupNavigationLabel: 'Навигация и поиск',
groupNavigationDescription: 'Меню сайта и то, как покупатели находят товары.',
groupNavigationPurpose: 'Настройте пункты меню и то, как устроена навигация по каталогу.',
groupNavigationWhere: 'Видно в шапке сайта на каждой странице.',
groupNavigationSkip: 'Покупатели будут использовать навигацию по умолчанию.',
groupPreviewLabel: 'Просмотр',
groupPreviewDescription: 'Посмотрите, как маркетплейс выглядит на десктопе, планшете и телефоне.',
groupPreviewPurpose: 'Проверьте маркетплейс глазами покупателя перед публикацией — на разных экранах.',
groupPreviewWhere: 'Это предпросмотр — покупатели его не видят.',
groupPreviewSkip: 'Вы можете опубликовать изменения непроверенными.',
feat(builder): implement professional brand management experience New BrandOverviewComponent, mounted at the top of the existing Branding section page (no route/schema/business-logic changes): - Completion ring + recommended next step + last-modified timestamp, computed from real facade signals (branding.logoUrl/faviconUrl/ socialImageUrl, theme.palette, theme.typography, lastSavedAt) - nothing fabricated. - Brand Health checklist (6 items: logo, favicon, colors, typography, social image, accessibility) - icon+text, never color alone. - Color palette preview: 10 swatches from the real ThemePaletteConfig (primary/secondary/accent/success/warning/danger/backgrounds/text/ border), each with a readable label. - Real WCAG contrast check (contrast.util.ts, relative-luminance formula) between configured text and background colors - shows an inline warning when below 4.5:1, never blocks publishing. - Typography live preview using the configured heading/body font families and base size. - Social share preview card (Open Graph-style) using the existing socialImageUrl + seo.default.title/description, with an explicit empty state when no image is set yet. Scope cut from the full brief given this session's cost already well over budget entering this sprint - see Known limitations in the final report (no multi-variant logo/favicon management, no live responsive device preview, no new media library/asset picker; all reuse the existing single-logo/single-favicon fields and image-field upload component as-is). Could not verify live in-browser this sprint: port 4200 is held by another chat's dev server. Verified via `tsc --noEmit` (clean) and manual template/binding review only; one binding (`--pct` custom- property style binding) was replaced with a plain computed string to remove an unverifiable risk rather than ship it unverified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 10:13:35 +04:00
brandOverviewTitle: 'Идентичность бренда',
brandOverviewSubtitle: 'Логотип, цвета и шрифты, которые видят покупатели на каждой странице.',
brandOverviewComplete: 'Бренд полностью настроен.',
brandLastModified: 'Последнее изменение',
brandFaviconUploaded: 'Иконка сайта загружена',
brandColorsSelected: 'Цвета бренда выбраны',
brandTypographyConfigured: 'Шрифты настроены',
brandSocialImageUploaded: 'Изображение для соцсетей загружено',
brandAccessibilityPassed: 'Контрастность текста в норме',
brandColorsPreviewTitle: 'Цвета маркетплейса',
brandTypographyPreviewTitle: 'Шрифты',
brandSocialPreviewTitle: 'Как это выглядит при публикации ссылки',
brandTypeSampleHeading: 'Пример заголовка',
brandTypeSampleBody: 'Пример основного текста, который видят покупатели.',
brandContrastWarning: 'Текст на фоне читается плохо — рассмотрите более контрастные цвета. Публикацию это не блокирует.',
brandSocialEmpty: 'Изображение для соцсетей ещё не загружено — при публикации ссылки покупатели увидят пустую карточку.',
brandAccentColor: 'Акцентный',
brandBorderColor: 'Границы',
homepageOverviewTitle: 'Главная страница',
homepageOverviewSubtitle: 'Из чего состоит главная страница вашего маркетплейса.',
homepageOverviewComplete: 'Главная страница полностью настроена.',
homepageActiveSections: 'Активные блоки',
homepageDisabledSections: 'Скрытые блоки',
homepageHealthHero: 'Приветственный баннер добавлен',
homepageHealthCategories: 'Витрина категорий добавлена',
homepageHealthProducts: 'Витрина товаров добавлена',
homepageHealthPromotion: 'Блок акции добавлен',
homepageHealthNewsletter: 'Форма подписки добавлена',
homepageHealthAnySections: 'На странице есть хотя бы один блок',
widgetTypeHero: 'Приветственный баннер',
widgetTypeCategories: 'Витрина категорий',
widgetTypeProducts: 'Витрина товаров',
widgetHidden: 'Скрыт',
widgetVisible: 'Показывать',
widgetMoveUp: 'Переместить выше',
widgetMoveDown: 'Переместить ниже',
widgetDuplicate: 'Дублировать',
widgetRemove: 'Удалить',
widgetRemoveConfirm: 'Удалить этот блок с главной страницы?',
widgetAdvancedSettings: 'Дополнительные настройки',
widgetsEmptyTitle: 'Пока нет виджетов',
widgetsEmptyDesc: 'Добавьте блок на главную страницу во вкладке «Главная», и его настройки появятся здесь.',
homepageEmptyTitle: 'Главная страница не настроена',
homepageEmptyDesc: 'В вашем магазине пока не настроена главная страница. Обратитесь в поддержку, чтобы её создать.',
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel. Homepage section (sections list -> visual blocks): - Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html - Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3') - Added duplicate and remove per block, alongside the existing drag-to-reorder - Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end Widgets section (hero widget): - 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind - 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values - New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before - HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control Carousel arrows root cause and fix: - widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place - Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise - Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
homepageBlocksHint: 'Это блоки, которые покупатели видят на главной странице сверху вниз. Перетаскивайте за ручку, чтобы изменить порядок.',
livePreviewTitle: 'Живой предпросмотр',
livePreviewHint: 'Наведите курсор на поле, чтобы увидеть, что оно меняет.',
livePreviewEmpty: 'Пока нечего показать.',
livePreviewButtonLabel: 'В магазин',
usedByLabel: 'Используется в:',
usedByHeader: 'Шапка сайта',
usedByFooter: 'Подвал сайта',
usedBySeo: 'Результаты поиска',
usedByButtons: 'Кнопки',
usedByLinks: 'Ссылки',
usedByBadges: 'Значки',
usedByPageBackground: 'Фон страницы',
resetField: 'Сбросить',
propertySearchLabel: 'Поиск настроек',
propertySearchPlaceholder: 'Поиск настроек…',
propertySearchResultsLabel: 'Результаты поиска',
propertySearchNoResults: 'Совпадений не найдено.',
unsavedChangesPanelTitle: 'Несохранённые изменения',
navigationHeaderEmptyTitle: 'В шапке пока нет ссылок',
navigationHeaderEmptyDesc: 'Добавьте ссылку, чтобы покупатели могли перемещаться из шапки сайта.',
navigationFooterEmptyTitle: 'В подвале пока нет ссылок',
navigationFooterEmptyDesc: 'Добавьте ссылку, чтобы покупатели могли перемещаться из подвала сайта.',
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel. Homepage section (sections list -> visual blocks): - Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html - Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3') - Added duplicate and remove per block, alongside the existing drag-to-reorder - Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end Widgets section (hero widget): - 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind - 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values - New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before - HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control Carousel arrows root cause and fix: - widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place - Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise - Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
addBlockHint: 'Добавить блок на главную страницу:',
dragToReorder: 'Перетащите, чтобы изменить порядок',
blockRemoveConfirm: 'Удалить этот блок с главной страницы?',
blockHero: 'Приветственный баннер',
blockHeroDesc: 'Крупный баннер вверху главной страницы с заголовком и кнопкой призыва к действию.',
blockCategories: 'Категории',
blockCategoriesDesc: 'Сетка плиток категорий, чтобы покупатели могли просматривать разделы.',
blockFeaturedProducts: 'Рекомендуемые товары',
blockFeaturedProductsDesc: 'Сетка отобранных вручную или самых продаваемых товаров.',
blockProductCarousel: 'Карусель товаров',
blockProductCarouselDesc: 'Горизонтально прокручиваемый ряд товаров со стрелками для просмотра других.',
blockRecentlyViewed: 'Недавно просмотренные',
blockRecentlyViewedDesc: 'Показывает каждому покупателю товары, которые он недавно смотрел.',
blockBanner: 'Промо-баннер',
blockBannerDesc: 'Одно изображение с заголовком и кнопкой — для акций или объявлений.',
blockPartners: 'Логотипы партнёров',
blockPartnersDesc: 'Ряд логотипов брендов или партнёров для доверия.',
blockCustomHtml: 'Свой блок',
blockCustomHtmlDesc: 'Произвольный HTML для всего, что не покрывают другие блоки.',
},
dashboard: {
title: 'Панель управления',
subtitle: 'Обзор вашего маркетплейса',
cardMarketplaceStatus: 'Статус маркетплейса',
cardProjectName: 'Название проекта',
cardCurrentTheme: 'Текущая тема',
cardLanguages: 'Языки',
cardCategoriesCount: 'Категории',
cardProductsCount: 'Товары',
cardOrders: 'Заказы',
cardRevenue: 'Выручка',
cardLastPublish: 'Последняя публикация',
cardLastDraftSave: 'Последнее сохранение черновика',
cardBootstrapVersion: 'Версия конфигурации',
cardActiveLayout: 'Активный макет',
cardEnabledWidgets: 'Активные виджеты',
stateLoading: 'Загрузка...',
stateEmpty: 'Пока нет данных',
stateError: 'Не удалось загрузить карточку',
statePendingBackend: 'Ожидает интеграции с бэкендом',
never: 'Никогда',
quickActionsTitle: 'Быстрые действия',
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
actionEditProject: 'Конструктор маркетплейса',
actionCategories: 'Категории',
actionProducts: 'Товары',
actionStaticPages: 'Статические страницы',
actionTransactions: 'Транзакции',
actionOrders: 'Заказы',
actionMediaLibrary: 'Медиатека',
actionUsers: 'Пользователи и права',
actionMonitoring: 'Мониторинг',
actionAnalytics: 'Аналитика',
actionPreviewMarketplace: 'Просмотр маркетплейса',
activityTitle: 'Недавняя активность',
feat(admin): redesign dashboard into professional SaaS homepage Replaced the placeholder dashboard with a real business homepage built on 6 new reusable widget components (DashboardSection, DashboardCard, DashboardMetric, DashboardStatusRow, DashboardTimeline, DashboardShortcutCard), all wired through shared app-card/app-skeleton/ app-empty-state. Sections: Welcome (tenant, env badge, current user, last publish/save - all real facade signals, never blank), Quick Actions (large cards: add product, create category, open builder, edit homepage, media, orders), Draft Status (real dirty/modifiedFields/publish/discard from ProjectEditorFacade), Marketplace Health (9 checks - config, product/ category counts, missing translations, draft, static-pages-unpublished, homepage/theme configured, all real; images-without-alt shown as 'not tracked yet' rather than fabricated), Recent Activity (existing localStorage-backed history service, loading/empty states), Useful Shortcuts, Documentation (honest coming-soon list, no dead links). 12-column responsive grid: 3-across cards on desktop, 2-column on tablet, single column on mobile, no horizontal scroll. Keyboard/focus/ ARIA per shortcut card and status row; health status never conveyed by color alone (icon + text every time). AdminDashboardHealthCheck (boolean 'healthy' shape) and its facade signal are untouched - AdminMonitoringPageComponent also consumes them. New richer status data lives in a separate homeHealthChecks signal/ AdminDashboardHomeHealthCheck type instead of widening the shared one. Added ~50 new dashboard.* / kept existing i18n strings across ru/en/hy; no raw keys. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 08:59:29 +04:00
activityEmpty: 'Пока нет активности',
activityDraftSaved: 'Черновик сохранён',
activityPublished: 'Опубликовано',
healthTitle: 'Состояние системы',
healthAllClear: 'Все проверки пройдены',
healthBootstrapValid: 'Bootstrap корректен',
healthConfigurationValid: 'Конфигурация корректна',
healthMissingTranslations: 'Отсутствуют переводы',
healthInvalidColors: 'Некорректные цвета',
healthInvalidWidgetReferences: 'Некорректные ссылки виджетов',
healthInvalidLayouts: 'Некорректные макеты',
comingSoonTitle: 'Скоро',
comingSoonDescription: 'Этот раздел ещё не реализован. Загляните позже.',
backToDashboard: 'Вернуться на панель управления',
feat(admin): redesign dashboard into professional SaaS homepage Replaced the placeholder dashboard with a real business homepage built on 6 new reusable widget components (DashboardSection, DashboardCard, DashboardMetric, DashboardStatusRow, DashboardTimeline, DashboardShortcutCard), all wired through shared app-card/app-skeleton/ app-empty-state. Sections: Welcome (tenant, env badge, current user, last publish/save - all real facade signals, never blank), Quick Actions (large cards: add product, create category, open builder, edit homepage, media, orders), Draft Status (real dirty/modifiedFields/publish/discard from ProjectEditorFacade), Marketplace Health (9 checks - config, product/ category counts, missing translations, draft, static-pages-unpublished, homepage/theme configured, all real; images-without-alt shown as 'not tracked yet' rather than fabricated), Recent Activity (existing localStorage-backed history service, loading/empty states), Useful Shortcuts, Documentation (honest coming-soon list, no dead links). 12-column responsive grid: 3-across cards on desktop, 2-column on tablet, single column on mobile, no horizontal scroll. Keyboard/focus/ ARIA per shortcut card and status row; health status never conveyed by color alone (icon + text every time). AdminDashboardHealthCheck (boolean 'healthy' shape) and its facade signal are untouched - AdminMonitoringPageComponent also consumes them. New richer status data lives in a separate homeHealthChecks signal/ AdminDashboardHomeHealthCheck type instead of widening the shared one. Added ~50 new dashboard.* / kept existing i18n strings across ru/en/hy; no raw keys. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 08:59:29 +04:00
welcomeEyebrow: 'С возвращением',
welcomeFallbackTenant: 'Ваш маркетплейс',
envProduction: 'Продакшн',
envDevelopment: 'Разработка',
factCurrentUser: 'Текущий пользователь',
factTenant: 'Магазин',
quickActionsDescription: 'Самые частые задачи — начните здесь',
actionAddProduct: 'Добавить товар',
actionAddProductDescription: 'Создать новую карточку товара',
actionCreateCategory: 'Создать категорию',
actionCreateCategoryDescription: 'Добавить категорию в каталог',
actionEditProjectDescription: 'Открыть конструктор маркетплейса',
actionEditHomepage: 'Редактировать главную',
actionEditHomepageDescription: 'Настроить виджеты главной страницы',
actionMediaLibraryDescription: 'Загрузить и организовать файлы',
actionOrdersDescription: 'Просмотреть последние заказы',
draftStatusTitle: 'Статус черновика',
draftStatusHasChanges: 'Есть несохранённые изменения, ожидающие публикации.',
draftStatusAllPublished: 'Всё опубликовано. Черновика нет.',
draftStatusMoreFields: 'ещё {{count}}',
draftOpenBuilder: 'Открыть конструктор',
draftPublish: 'Опубликовать',
draftDiscard: 'Отменить черновик',
healthProductsCount: 'Товары',
healthCategoriesCount: 'Категории',
healthDraftExists: 'Черновик',
healthImagesWithoutAlt: 'Изображения без alt-текста',
healthStaticPagesUnpublished: 'Неопубликованные статические страницы',
healthHomepageConfigured: 'Главная страница настроена',
healthThemeConfigured: 'Тема настроена',
statusHealthy: 'В порядке',
statusAttention: 'Требует внимания',
statusUnhealthy: 'Проблема',
statusUnknown: 'Пока не отслеживается',
activityEmptyHint: 'Сохраните или опубликуйте изменение, чтобы увидеть его здесь.',
activityUnavailable: 'История активности недоступна',
activityUnavailableHint: 'Не удалось подключиться к источнику данных. Попробуйте обновить страницу позже.',
activityErrorHint: 'Не удалось загрузить недавнюю активность.',
shortcutsTitle: 'Полезные ссылки',
shortcutsDescription: 'Часто используемые разделы админ-панели',
shortcutMarketplaceBuilder: 'Конструктор маркетплейса',
shortcutSettings: 'Настройки',
shortcutContent: 'Контент',
docTitle: 'Документация',
docDocumentation: 'Документация',
docKeyboardShortcuts: 'Горячие клавиши',
docReportIssue: 'Сообщить о проблеме',
docReleaseNotes: 'Список изменений',
},
2026-07-10 13:52:01 +04:00
staticPages: {
notFound: 'Страница не найдена',
backHome: 'На главную',
duplicateSlug: 'Дублирующиеся slug запрещены.',
emptyTitle: 'У каждой страницы должен быть хотя бы один заголовок.',
feat(static-pages): CRUD completion + search/filter/bulk actions Milestone 2 of the Static Pages Module sprint. - StaticPagesEditorComponent: duplicate page, confirm-before-delete/bulk- delete (matches the resetDraft confirm pattern), route/enabled/customTemplate/ media(hero/thumbnail/gallery) fields wired into the card, per-page publish/ unpublish action, status + duplicate-route/invalid-html/invalid-seo badges. - Search (id/slug/route/title across all locales), filter by status (draft/published) and by locale (hides pages missing a translation for the selected locale) - all local computed() filters, no new service. - Bulk selection (per-row + select-all-visible checkboxes) with bulk delete/ enable/disable/publish/unpublish, one updateBootstrap() call each. - Correctness note: introduced `allPages` (unfiltered) vs `pages` (filtered view) computeds. Every mutation (create/duplicate/delete/move/bulk) reads from allPages(), never the filtered pages() - reading from the filtered view would have silently deleted whatever an active search/filter hid on the next persist(). Documented inline on persist() as a guardrail for future edits. - Fixed a template compile error found by the build gate: Angular templates don't support inline arrow functions in binding expressions ((ngModelChange)="...map(v => v.trim())..." failed to parse) - moved the gallery CSV-parsing into a component method (updateGallery). - SEO robots field added to the page card (validated against a known-token set from M1). - i18n: staticPages.* extended (search/filter/bulk/route/enabled/status/ media/robots/disabled labels) across the interface + en/ru/hy. Gate: tsc --noEmit, npm test (57/57), arch:check, build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:55:21 +04:00
duplicateRoute: 'Два или более страниц используют один и тот же маршрут.',
invalidHtml: 'На этой странице некорректный HTML-контент.',
invalidSeo: 'У этой страницы некорректное SEO-поле (canonical/OG-изображение или значение robots).',
searchPlaceholder: 'Поиск страниц…',
filterAll: 'Все статусы',
filterAllLocales: 'Все языки',
confirmDeletePage: 'Удалить эту страницу? Это действие нельзя отменить.',
confirmBulkDelete: 'Удалить выбранные страницы? Это действие нельзя отменить.',
duplicatePage: 'Дублировать',
selectAll: 'Выбрать все',
selectedCount: 'выбрано',
bulkDelete: 'Удалить выбранные',
bulkEnable: 'Включить выбранные',
bulkDisable: 'Отключить выбранные',
bulkPublishAction: 'Опубликовать выбранные',
bulkUnpublish: 'Снять с публикации выбранные',
routeLabel: 'Маршрут',
enabledLabel: 'Включена',
statusLabel: 'Статус',
publishPageAction: 'Опубликовать страницу',
unpublishPageAction: 'Снять страницу с публикации',
heroImageLabel: 'Главное изображение',
thumbnailLabel: 'Миниатюра',
galleryLabel: 'Галерея',
customTemplateLabel: 'Пользовательский шаблон',
seoRobotsLabel: 'Robots',
disabledBadge: 'Отключена',
previewDesktop: 'Десктоп',
previewTablet: 'Планшет',
previewMobile: 'Телефон',
previewToggle: 'Превью',
2026-07-10 13:52:01 +04:00
},
contentManagement: {
publishedPages: 'Опубликованные страницы',
draftPages: 'Черновики',
missingLegalPages: 'Отсутствующие юридические страницы',
seoHealth: 'SEO-здоровье',
lastEdited: 'Последнее изменение',
lastEditedNever: 'Никогда',
completionLabel: 'Готовность',
recommendedNext: 'Рекомендуемый следующий шаг',
recommendAddTitles: 'У некоторых страниц отсутствует заголовок хотя бы на одном языке.',
recommendFixSeo: 'На одной из страниц есть SEO-поле, которое нужно исправить (canonical/OG-изображение или robots).',
recommendPublish: 'Обязательная юридическая страница всё ещё черновик — опубликуйте её.',
recommendNone: 'Отлично — контент в хорошем состоянии.',
openAction: 'Открыть',
backToPages: 'Назад к страницам',
groupContent: 'Контент',
groupSeo: 'SEO',
groupSharing: 'Публикация в соцсетях',
groupAdvanced: 'Дополнительно',
groupAdvancedHtml: 'Исходный HTML (дополнительно)',
pageContentLabel: 'Содержимое страницы',
pageContentDesc: 'Напишите и оформите текст, изображения и ссылки, которые увидят покупатели на этой странице.',
seoExplainDesc: 'Это то, что видно в результатах поиска — понятный заголовок и описание помогают клиентам найти страницу.',
seoTitleHelp: 'Заголовок, отображаемый в результатах поиска. Делайте его коротким и понятным.',
seoDescriptionHelp: 'Краткое описание под заголовком в результатах поиска.',
seoCanonicalHelp: 'Официальный адрес страницы — помогает избежать дублирования контента.',
seoRobotsHelp: 'Определяет, могут ли поисковые системы показывать эту страницу.',
sharingExplain: 'Так страница выглядит при публикации в соцсетях (Facebook, Twitter/X и другие).',
mediaSelect: 'Выбрать изображение',
mediaReplace: 'Заменить',
mediaRemove: 'Удалить',
mediaAlt: 'Альтернативный текст',
mediaCaption: 'Подпись',
emptyStateGuideTitle: 'Начните создавать страницы магазина',
emptyStateGuideBody: 'Юридические страницы, такие как О нас, Конфиденциальность и Условия, повышают доверие клиентов. Рекомендуем сначала опубликовать О нас, Контакты, Конфиденциальность и Условия, затем добавить Cookies, Доставку, Возвраты и FAQ.',
seoStatusGood: 'SEO в порядке',
seoStatusPartial: 'SEO частично',
seoStatusWarning: 'Проблема SEO',
seoStatusMissing: 'SEO отсутствует',
needsAttention: 'Требует внимания',
legal: {
about: { label: 'О нас', why: 'Рассказывает клиентам, кто вы, и повышает доверие перед покупкой.' },
contacts: { label: 'Контакты', why: 'Даёт клиентам способ связаться с вами по вопросам.' },
privacy: { label: 'Конфиденциальность', why: 'Объясняет, как обрабатываются данные клиентов — часто обязательно по закону.' },
terms: { label: 'Условия', why: 'Устанавливает правила использования магазина — защищает вас и клиентов.' },
cookies: { label: 'Cookies', why: 'Раскрывает использование cookie — требуется во многих регионах.' },
delivery: { label: 'Доставка', why: 'Задаёт понятные ожидания по доставке и снижает число обращений в поддержку.' },
returns: { label: 'Возвраты', why: 'Объясняет политику возврата и повышает уверенность в покупке.' },
faq: { label: 'Вопросы и ответы', why: 'Отвечает на частые вопросы заранее.' },
},
},
widgets: {
unavailable: 'Виджет недоступен',
},
2026-02-26 23:09:20 +04:00
carousel: {
loading: 'Загрузка товаров...',
addToCart: 'Добавить в корзину',
},
common: {
retry: 'Попробовать снова',
loading: 'Загрузка...',
remove: 'Удалить',
fix(backoffice): add error+retry states to Users, Monitoring, Analytics, Reports Phase 8 (RC-01): these 4 list/dashboard pages had no error-state handling on their primary data-load subscriptions — on a gateway error, `loading` was either never reset (Users, Monitoring, Analytics: genuine infinite- spinner risk, nested subscribe chain in Analytics never resolved on failure) or there was no loading/empty/error handling at all (Reports queue: raw table with zero skeleton or fallback). - admin-users.facade.ts, admin-monitoring.facade.ts: add `error` signal, error callback on the primary load subscribe so `loading` always resolves. - admin-analytics.facade.ts: add `error` signal; every level of the 4-deep nested gateway subscribe chain (orders -> products -> categories -> reviews) now has an error handler that resolves loading instead of leaving it stuck true. - admin-moderation.facade.ts: add `reportsLoading`/`reportsError` signals (reports list had none previously). - Templates: reuse existing `app-skeleton`/`app-empty-state`/`app-button` primitives for the new error branch, `common.retry` label, two new generic `common.errorTitle`/`common.errorDescription` i18n keys added to en/ru/hy (reused across all 4 fixes instead of one-off per-page copy). Verified: tsc --noEmit clean, `npm run build` green (pre-existing bundle- budget warning only, unrelated). Live-checked Home (375px) and Backoffice Products (1024px) — no console errors, tables/cards render without overflow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 21:31:30 +04:00
errorTitle: 'Что-то пошло не так',
errorDescription: 'Не удалось загрузить данные. Попробуйте ещё раз.',
ratingLabel: 'Рейтинг {{value}}',
discountLabel: 'Скидка {{value}}%',
slideLabel: 'Слайд {{value}}',
slidesLabel: 'Слайды',
previousProducts: 'Предыдущие товары',
nextProducts: 'Следующие товары',
previousSlide: 'Предыдущий слайд',
nextSlide: 'Следующий слайд',
closeDialog: 'Закрыть диалог',
dismiss: 'Скрыть',
qrCode: 'QR-код',
bankCardPayment: 'Оплата банковской картой',
guest: 'Гость',
cancel: 'Отмена',
confirm: 'Подтвердить',
2026-02-26 23:09:20 +04:00
},
2026-02-28 17:18:24 +04:00
location: {
allRegions: 'Все регионы',
chooseRegion: 'Выберите регион',
detectAuto: 'Определить автоматически',
},
auth: {
loginRequired: 'Требуется авторизация',
loginDescription: 'Для оформления заказа войдите через Telegram',
checking: 'Проверка...',
loginWithTelegram: 'Войти через Telegram',
orScanQr: 'Или отсканируйте QR-код',
loginNote: 'После входа вы будете перенаправлены обратно',
2026-03-25 15:32:50 +04:00
qrExpired: 'QR-код устарел. Нажмите, чтобы обновить',
2026-06-01 00:47:26 +04:00
qrError: 'Не удалось создать сессию входа. Нажмите, чтобы повторить',
2026-02-28 17:18:24 +04:00
},
ux: {
items: 'товаров',
wishlistTitle: 'Избранное',
wishlistEmptyTitle: 'Избранное пока пусто',
wishlistEmptyDescription: 'Сохраняйте товары, чтобы быстро вернуться к ним позже.',
clearWishlist: 'Очистить избранное',
wishlistCleared: 'Избранное очищено',
compareTitle: 'Сравнение товаров',
compareEmptyTitle: 'Нет товаров для сравнения',
compareEmptyDescription: 'Добавьте товары из каталога, чтобы сравнить характеристики и цену.',
clearCompare: 'Очистить сравнение',
removeFromCompare: 'Убрать из сравнения',
hideIdentical: 'Скрыть одинаковые значения',
highlightDifferences: 'Подсвечивать различия',
clearRecentlyViewed: 'Очистить просмотренные',
recentlyViewedEmpty: 'Вы пока не просматривали товары.',
saveSearch: 'Сохранить поиск',
goToCatalog: 'Перейти в каталог',
2026-07-09 01:40:22 +04:00
wishlistAdded: 'Добавлено в избранное',
wishlistRemoved: 'Удалено из избранного',
compareAdded: 'Добавлено в сравнение',
compareRemoved: 'Удалено из сравнения',
compareLimitReached: 'Достигнут лимит сравнения ({{maxItems}})',
productShared: 'Товар отправлен',
productLinkCopied: 'Ссылка на товар скопирована',
sharingUnsupported: 'На этом устройстве нет поддержки шаринга',
addedToCart: 'Добавлено в корзину',
2026-07-09 01:40:22 +04:00
enterQueryBeforeSave: 'Введите поисковый запрос перед сохранением',
savedSearchNamed: 'Сохраненный поиск: {{name}}',
recentlyViewedTitle: 'Недавно просмотренные',
compareAttribute: 'Атрибут',
comparePrice: 'Цена',
compareRating: 'Рейтинг',
compareStock: 'Наличие',
compareDiscount: 'Скидка',
compareColor: 'Цвет',
compareSize: 'Размер',
compareUnknown: 'неизвестно',
},
2026-07-10 13:34:25 +04:00
diagnostics: {
title: 'Диагностика маркетплейса',
disabledTitle: 'Диагностика недоступна',
disabledDescription: 'Страница диагностики доступна только в режиме разработки.',
generatedAt: 'Сформировано: {{value}}',
healthScore: 'Оценка состояния',
summary: 'Сводка',
passedChecks: '{{count}} успешных проверок',
warnings: '{{count}} предупреждений',
errors: '{{count}} ошибок',
critical: '{{count}} критических',
healthChecks: 'Проверки состояния',
results: 'Подробные результаты',
noIssues: 'Проблем диагностики не найдено.',
ok: 'OK',
failed: 'Ошибка',
affectedComponent: 'Затронутый компонент',
suggestedResolution: 'Рекомендация',
severity: { info: 'Инфо', warning: 'Предупреждение', error: 'Ошибка', critical: 'Критично' },
checks: {
bootstrapLoaded: 'Bootstrap загружен',
tenantResolved: 'Tenant определен',
runtimeInitialized: 'Runtime инициализирован',
themeLoaded: 'Тема загружена',
widgetManifestLoaded: 'Манифест виджетов загружен',
sectionEngineInitialized: 'Section Engine доступен',
configurationEngineInitialized: 'Configuration Engine инициализирован',
translationResourcesAvailable: 'Переводы доступны',
requiredAssetsAvailable: 'Обязательные assets доступны',
},
bootstrapMissingTitle: 'Bootstrap отсутствует',
bootstrapMissingDescription: 'Конфигурация bootstrap не загружена.',
bootstrapMissingResolution: 'Проверьте provider bootstrap и локальный mock.',
requiredPropertyMissingTitle: 'Отсутствует обязательное свойство',
requiredPropertyMissingDescription: 'В bootstrap отсутствует обязательное свойство.',
requiredPropertyMissingResolution: 'Добавьте свойство в bootstrap payload.',
unknownWidgetTypeTitle: 'Неизвестный тип виджета',
unknownWidgetTypeDescription: 'Тип виджета отсутствует в widget manifest.',
unknownWidgetTypeResolution: 'Добавьте виджет в manifest или удалите неверную конфигурацию.',
widgetSettingsSchemaMismatchTitle: 'Настройки виджета не соответствуют схеме manifest',
widgetSettingsSchemaMismatchDescription: 'Props виджета не соответствуют settingsSchema, заданной для этого типа виджета.',
widgetSettingsSchemaMismatchResolution: 'Исправьте props виджета в редакторе в соответствии с обязательными полями/типами, либо обновите схему в manifest.',
2026-07-10 13:34:25 +04:00
duplicateIdTitle: 'Дублирующийся идентификатор',
duplicatePageIdDescription: 'Обнаружен дублирующийся id страницы.',
duplicateSectionIdDescription: 'Обнаружен дублирующийся id секции.',
duplicateWidgetIdDescription: 'Обнаружен дублирующийся id виджета.',
duplicateIdResolution: 'Сделайте id уникальными в bootstrap конфигурации.',
unknownLayoutTitle: 'Неизвестный layout',
unknownLayoutDescription: 'Значение layout страницы не распознано.',
unknownLayoutResolution: 'Используйте поддерживаемый layout или расширьте allow-list.',
invalidFeatureFlagTitle: 'Некорректный feature flag',
invalidFeatureFlagDescription: 'Значение feature flag должно быть boolean.',
invalidFeatureFlagResolution: 'Нормализуйте значение к true или false.',
brokenPageDefinitionTitle: 'Некорректное описание страницы',
brokenPageDefinitionDescription: 'Описание страницы неполное.',
brokenPageDefinitionResolution: 'Добавьте route, title, key или sections.',
invalidNavigationTargetTitle: 'Некорректная цель навигации',
invalidNavigationTargetDescription: 'Route навигации не совпадает с известными route.',
invalidNavigationTargetResolution: 'Исправьте target route или добавьте route.',
missingTranslationTitle: 'Отсутствует перевод',
missingTranslationDescription: 'Ключ перевода не разрешается.',
missingTranslationResolution: 'Добавьте ключ в translation resources.',
missingMediaTitle: 'Отсутствует media',
missingMediaDescription: 'Ожидаемый branding media ресурс отсутствует.',
missingMediaResolution: 'Укажите корректные media URL в branding конфиге.',
runtimeNotInitializedTitle: 'Runtime не инициализирован',
runtimeNotInitializedDescription: 'Platform runtime state не помечен как initialized.',
runtimeNotInitializedResolution: 'Проверьте цепочку initialize в PlatformRuntimeService.',
configFallbackUsageTitle: 'Используется fallback конфигурация',
configFallbackUsageDescription: 'Runtime разработки использует fallback или mock конфигурацию.',
configFallbackUsageResolution: 'Смените provider mode, если нужна проверка реального bootstrap.',
missingDatasourceTitle: 'Отсутствует datasource',
missingDatasourceDescription: 'Виджет поддерживает datasource, но он не задан.',
missingDatasourceResolution: 'Укажите source в props виджета или default settings manifest.',
widgetRenderFailureTitle: 'Сбой рендера виджета',
widgetRenderFailureDescription: 'Виджет откатился на unknown widget renderer.',
widgetRenderFailureResolution: 'Зарегистрируйте компонент виджета или исправьте component key.',
brokenRouteTitle: 'Нарушено ожидание route',
brokenRouteDescription: 'Ожидаемый route отсутствует.',
brokenRouteResolution: 'Верните регистрацию route или обновите правило диагностики.',
imageLoadFailedTitle: 'Ошибка загрузки изображения',
imageLoadFailedDescription: 'Отображаемое изображение не загрузилось.',
imageLoadFailedResolution: 'Проверьте asset path, CORS и доступность media.',
optionalDataMissingTitle: 'Отсутствуют необязательные данные',
optionalDataMissingDescription: 'Необязательное конфигурационное значение отсутствует.',
optionalDataMissingResolution: 'Добавьте значение, если оно нужно UX.',
},
adminModeration: {
search: 'Поиск по клиенту, товару или тексту отзыва…',
selectAllRows: 'Выбрать все отзывы',
itemsCount: 'отзывов',
allStatuses: 'Все статусы',
allRatings: 'Все оценки',
rating: 'Оценка',
customer: 'Клиент',
product: 'Товар',
reviewText: 'Отзыв',
photos: 'Фото',
workflow: 'Действия',
approve: 'Одобрить',
reject: 'Отклонить',
markSpam: 'Отметить как спам',
hide: 'Скрыть',
restore: 'Восстановить',
pin: 'Закрепить',
unpin: 'Открепить',
feature: 'Выделить',
unfeature: 'Убрать выделение',
featureDisabledHint: 'Выделять можно только одобренные отзывы.',
moderatorNotes: 'Заметки модератора',
notAvailable: 'Пока недоступно',
changeStatus: 'Изменить статус',
hideSelected: 'Скрыть',
archiveSelected: 'В архив',
reportsQueue: 'Очередь жалоб',
reportsColumn: 'Жалобы',
reportTarget: 'Объект жалобы',
reportType: 'Тип',
reportReason: 'Причина',
resolve: 'Решено',
dismiss: 'Отклонить',
reportsEmptyTitle: 'Жалоб нет',
reportsEmptyDescription: 'Жалобы клиентов на товары, отзывы или аккаунты появятся здесь.',
reportedCount: 'С жалобами',
averageRating: 'Средняя оценка',
moderationHealth: 'Состояние модерации',
emptyTitle: 'Отзывов пока нет',
emptyDescription: 'Отзывы появятся здесь после первых оценок от клиентов.',
emptyGuide: 'Клиенты оставляют оценку и текст после покупки. Новые отзывы получают статус «Ожидает» до одобрения, отклонения или пометки как спам модератором — одобренные отзывы становятся видны на витрине. Старайтесь регулярно разбирать очередь ожидающих отзывов.',
healthRating: 'Оценка',
healthText: 'Текст',
healthMedia: 'Медиа',
healthModeration: 'Промодерирован',
healthReportStatus: 'Нет открытых жалоб',
healthVisibility: 'Виден',
health: 'Состояние',
column_product: 'Товар',
column_rating: 'Оценка',
column_status: 'Статус',
column_reports: 'Жалобы',
column_created: 'Отправлен',
status: {
pending: 'Ожидает',
approved: 'Одобрен',
rejected: 'Отклонён',
spam: 'Спам',
},
timelineEvent: {
submitted: 'Отзыв отправлен',
statusChanged: 'Статус изменён на «{{status}}»',
restored: 'Отзыв восстановлен',
hidden: 'Отзыв скрыт',
},
actor: {
admin: 'Администратор',
customer: 'Клиент',
},
targetType: {
product: 'Товар',
review: 'Отзыв',
customer: 'Клиент',
category: 'Категория',
unknown: 'Неизвестно',
},
reportStatus: {
open: 'Открыта',
resolved: 'Решена',
dismissed: 'Отклонена',
},
},
adminOrders: {
back: 'Назад к заказам',
search: 'Поиск по номеру заказа, имени, email или телефону…',
export: 'Экспорт',
selectAllRows: 'Выбрать все заказы',
selectRow: 'Выбрать заказ',
view: 'Просмотр',
orderNumber: 'Заказ',
customer: 'Клиент',
payment: 'Оплата',
delivery: 'Доставка',
trackingNumber: 'Номер отслеживания',
createdAt: 'Оформлен',
items: 'Товары',
timeline: 'Хронология',
timelineEvent: {
created: 'Заказ создан',
statusChanged: 'Статус изменён на «{{status}}»',
refundRequested: 'Запрошен возврат',
},
notes: 'Заметки для клиента',
internalNotes: 'Внутренние заметки',
addNote: 'Добавить заметку',
changeStatus: 'Изменить статус',
applyStatus: 'Применить',
archiveSelected: 'В архив',
printSelected: 'Печать',
printInvoice: 'Печать накладной',
requestRefund: 'Запросить возврат',
cancelOrder: 'Отменить заказ',
confirmCancel: 'Отменить этот заказ?',
confirmRefund: 'Запросить возврат по этому заказу?',
emptyTitle: 'Заказов пока нет',
emptyDescription: 'Заказы появятся здесь после оформления покупок.',
column_customer: 'Клиент',
column_status: 'Статус',
column_payment: 'Оплата',
column_total: 'Сумма',
column_created: 'Оформлен',
status: {
all: 'Все статусы',
pending: 'Ожидает',
processing: 'Сборка',
shipped: 'Доставляется',
delivered: 'Завершён',
cancelled: 'Отменён',
refunded: 'Возвращён',
},
paymentStatus: {
unpaid: 'Не оплачен',
paid: 'Оплачен',
refund_requested: 'Запрошен возврат',
refunded: 'Возвращён',
},
itemsCount: 'заказов',
ordersToday: 'Заказов сегодня',
paidCount: 'Оплачено',
customersCount: 'Клиенты',
returningCustomers: 'Повторные клиенты',
averageOrder: 'Средний чек',
recentActivity: 'Недавняя активность',
noRecentActivity: 'Активности пока нет.',
alertStuckPending: 'заказов ожидают более 48 часов',
alertRefundRequested: 'заказов ждут запроса на возврат',
},
adminCustomers: {
back: 'Назад к клиентам',
search: 'Поиск по имени, email или телефону…',
name: 'Имя',
email: 'Email',
phone: 'Телефон',
orderCount: 'Заказы',
lifetimeValue: 'Общая сумма покупок',
returning: 'Повторный',
profile: 'Профиль',
statistics: 'Статистика',
firstOrder: 'Первый заказ',
lastOrder: 'Последний заказ',
addresses: 'Адреса',
notes: 'Заметки',
notesUnavailable: 'Заметок по клиенту пока нет — добавьте заметку к одному из его заказов.',
orders: 'Заказы',
activity: 'Активность',
emptyTitle: 'Клиентов пока нет',
emptyDescription: 'Клиенты появятся здесь после первого заказа.',
},
mediaLibrary: {
title: 'Медиатека',
upload: 'Загрузить',
uploading: 'Загрузка...',
searchPlaceholder: 'Поиск файлов...',
delete: 'Удалить',
deleteConfirmTitle: 'Удалить файл?',
deleteConfirmBody: 'Это действие необратимо.',
cancel: 'Отмена',
confirm: 'Удалить',
emptyTitle: 'Пока нет файлов',
emptyDescription: 'Загрузите изображение или документ, чтобы начать.',
allFolders: 'Все папки',
newFolder: 'Новая папка',
editTags: 'Изменить теги',
totalAssets: 'Всего файлов',
logos: 'Логотипы',
unused: 'Неиспользуемые',
storageUsed: 'Использовано места',
altCoverage: 'Покрытие alt-текстом',
allTypes: 'Все типы',
typeImage: 'Изображения',
typeSvg: 'SVG',
typePdf: 'PDF',
sortRecent: 'Недавние',
sortName: 'Имя',
sortSize: 'Размер',
viewMode: 'Вид',
viewGrid: 'Сетка',
viewList: 'Список',
dropHint: 'Перетащите изображения сюда или используйте кнопку «Загрузить» выше.',
cancelUpload: 'Отменить',
retry: 'Повторить',
errorTooLarge: 'Файл слишком большой. Попробуйте файл меньшего размера.',
errorUnsupportedType: 'Этот тип файла не поддерживается. Используйте JPG, PNG, WebP, GIF, SVG или PDF.',
errorGeneric: 'Не удалось загрузить файл. Попробуйте ещё раз.',
selectedCount: 'выбрано',
download: 'Скачать',
exportMetadata: 'Экспортировать метаданные',
bulkDelete: 'Удалить выбранные',
bulkDeleteConfirm: 'Удалить выбранные файлы? Это действие нельзя отменить.',
clearSelection: 'Снять выделение',
guideCopy: 'Повторное использование существующего файла избавляет от дублей и экономит место. Для баннеров и главных изображений рекомендуем размер около 1200×1200px и сжатие крупных фото перед загрузкой.',
missingAlt: 'Нет alt-текста',
usageCount: 'исп.',
detailsTitle: 'Информация о файле',
filename: 'Имя файла',
format: 'Формат',
fileSize: 'Размер файла',
dimensions: 'Размеры',
uploadDate: 'Загружен',
lastUsed: 'Последнее использование',
usageCountLabel: 'Количество использований',
usedIn: 'Используется в',
notUsed: 'Пока нигде не используется.',
unknownValue: 'Неизвестно',
altText: 'Alt-текст',
altTextHelp: 'Описывает изображение для программ чтения с экрана и поисковых систем.',
decorative: 'Декоративное (alt-текст не нужен)',
missingAltWarning: 'У этого изображения нет alt-текста, и оно не отмечено как декоративное — пользователи с программами чтения с экрана не узнают, что на нём изображено.',
caption: 'Подпись',
description: 'Описание',
usage: {
brand: 'Логотип',
theme: 'Тема',
header: 'Шапка',
footer: 'Подвал',
staticPage: 'Статическая страница',
homepage: 'Главная страница',
catalog: 'Каталог',
productPage: 'Страница товара',
widget: 'Виджет',
company: 'Информация о компании',
seo: 'SEO',
navigation: 'Навигация',
other: 'Другое',
},
},
fix(static-pages): backfill enabled/status in export, fix missing i18n key Independent review pass over the Static Pages Module sprint (M1-M7), including live browser E2E per the task's own verification checklist. - ProjectEditorFacade.normalize() now backfills enabled/status on record-format static pages (defaulting missing values to enabled+published, same rule ContentPageService.normalizePage applies for display - mirrored rather than imported, to avoid a project-editor <-> content-management circular dependency since ContentManagementFacade already depends on this facade). Found live: exporting a page that predates this sprint and was never touched/re-saved in the current session produced JSON missing enabled/status entirely - the editor UI and storefront resolver both normalize-on-read so nothing was actually broken live, but Export/Import fidelity should match what the editor shows. Verified fixed live (export now includes "enabled":true,"status":"published" for an untouched legacy page) and via the full gate. - Added the missing adminCategories.chooseImage i18n key (interface + en/ru/hy). Found live: the media-picker "choose image" button rendered as the literal string "adminCategories.chooseImage" - a pre-existing, repo-wide bug (5 templates reference this key; none of the locale files ever defined it) that I propagated into a 3rd/4th/5th... well, 2 new occurrences by copying the existing branding-section/footer-section pattern into static-pages-editor. Fixed the actual defect (missing translation) rather than renaming the key, which would have required touching 2 unrelated admin components outside this sprint's scope. Live-verified this pass: Static Pages editor renders with all new fields; create page works (page count 4->confirmed); device preview toggles desktop/tablet/mobile widths correctly; navigation "Insert page link" creates a real type:'staticPage' nav item end-to-end (confirmed in the exported JSON); export includes all Sprint X+2 fields after the fix; no console errors throughout. Gate: tsc --noEmit, npm test (57/57), arch:check, build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:27:29 +04:00
adminCategories: {
chooseImage: 'Выбрать изображение',
replaceImage: 'Заменить изображение',
feat(categories): implement professional category management experience Categories dashboard (real total/visible/hidden/empty/root/subcategory/missing-image/missing-SEO/last-modified/completion% stats, recommended next action); tree view is now a real expand/collapse hierarchy (state persisted via LocalStorageService) with keyboard navigation (arrow keys, jump-to-parent), search that force-expands and keeps only matching branches + their ancestors/descendants instead of silently dropping deep matches; added Table/Cards views alongside the tree with density and saved column visibility; real bulk actions (show/hide/delete/assign parent/assign image via the Media Library picker/duplicate via the existing createCategory call/CSV export) plus the pre-existing single-item actions; reusable CategoryHealthWidget (image/SEO/description/valid-parent/visibility/products-assigned + completion%); editor reorganized into General/Media/SEO/Visibility/Navigation/Attributes/Advanced tabs, media now uses the shared ImageFieldComponent, SEO explains fields in plain language with a live preview, Navigation tab shows real breadcrumb/children/visibility-based nav status, Attributes uses the shared KeyValueEditorComponent. Filled in the adminCategories i18n namespace (previously only 2 of ~90 referenced keys existed) across en/ru/hy. Also fixed the pre-existing bug where a filtered/searched category list could silently drop a matched descendant whose ancestor's title didn't match, by loading the full catalog once and filtering client-side.
2026-07-18 17:04:11 +04:00
search: 'Поиск категорий…',
showDeleted: 'Показать удалённые',
selectAllRows: 'Выбрать все категории',
selectRow: 'Выбрать категорию',
toggleVisibility: 'Переключить видимость',
feat(categories): implement professional category management experience Categories dashboard (real total/visible/hidden/empty/root/subcategory/missing-image/missing-SEO/last-modified/completion% stats, recommended next action); tree view is now a real expand/collapse hierarchy (state persisted via LocalStorageService) with keyboard navigation (arrow keys, jump-to-parent), search that force-expands and keeps only matching branches + their ancestors/descendants instead of silently dropping deep matches; added Table/Cards views alongside the tree with density and saved column visibility; real bulk actions (show/hide/delete/assign parent/assign image via the Media Library picker/duplicate via the existing createCategory call/CSV export) plus the pre-existing single-item actions; reusable CategoryHealthWidget (image/SEO/description/valid-parent/visibility/products-assigned + completion%); editor reorganized into General/Media/SEO/Visibility/Navigation/Attributes/Advanced tabs, media now uses the shared ImageFieldComponent, SEO explains fields in plain language with a live preview, Navigation tab shows real breadcrumb/children/visibility-based nav status, Attributes uses the shared KeyValueEditorComponent. Filled in the adminCategories i18n namespace (previously only 2 of ~90 referenced keys existed) across en/ru/hy. Also fixed the pre-existing bug where a filtered/searched category list could silently drop a matched descendant whose ancestor's title didn't match, by loading the full catalog once and filtering client-side.
2026-07-18 17:04:11 +04:00
create: 'Создать категорию',
edit: 'Редактировать категорию',
title: 'Название',
slug: 'URL-адрес',
slugTaken: 'Этот адрес уже используется другой категорией.',
slugCheckError: 'Не удалось проверить уникальность адреса. Повторите попытку перед сохранением.',
feat(categories): implement professional category management experience Categories dashboard (real total/visible/hidden/empty/root/subcategory/missing-image/missing-SEO/last-modified/completion% stats, recommended next action); tree view is now a real expand/collapse hierarchy (state persisted via LocalStorageService) with keyboard navigation (arrow keys, jump-to-parent), search that force-expands and keeps only matching branches + their ancestors/descendants instead of silently dropping deep matches; added Table/Cards views alongside the tree with density and saved column visibility; real bulk actions (show/hide/delete/assign parent/assign image via the Media Library picker/duplicate via the existing createCategory call/CSV export) plus the pre-existing single-item actions; reusable CategoryHealthWidget (image/SEO/description/valid-parent/visibility/products-assigned + completion%); editor reorganized into General/Media/SEO/Visibility/Navigation/Attributes/Advanced tabs, media now uses the shared ImageFieldComponent, SEO explains fields in plain language with a live preview, Navigation tab shows real breadcrumb/children/visibility-based nav status, Attributes uses the shared KeyValueEditorComponent. Filled in the adminCategories i18n namespace (previously only 2 of ~90 referenced keys existed) across en/ru/hy. Also fixed the pre-existing bug where a filtered/searched category list could silently drop a matched descendant whose ancestor's title didn't match, by loading the full catalog once and filtering client-side.
2026-07-18 17:04:11 +04:00
parent: 'Родительская категория',
noParent: 'Без родителя (верхний уровень)',
icon: 'Иконка',
image: 'Изображение',
imageHint: 'Показывается в списках категорий и навигации.',
description: 'Описание',
items: 'товаров',
status: { draft: 'Черновик', published: 'Опубликовано' },
restore: 'Восстановить',
saveDraft: 'Сохранить черновик',
publish: 'Опубликовать',
confirmDelete: 'Удалить эту категорию? Это действие нельзя отменить.',
confirmBulkDelete: 'Удалить выбранные категории? Это действие нельзя отменить.',
deleteTitle: 'Удалить категорию',
feat(categories): implement professional category management experience Categories dashboard (real total/visible/hidden/empty/root/subcategory/missing-image/missing-SEO/last-modified/completion% stats, recommended next action); tree view is now a real expand/collapse hierarchy (state persisted via LocalStorageService) with keyboard navigation (arrow keys, jump-to-parent), search that force-expands and keeps only matching branches + their ancestors/descendants instead of silently dropping deep matches; added Table/Cards views alongside the tree with density and saved column visibility; real bulk actions (show/hide/delete/assign parent/assign image via the Media Library picker/duplicate via the existing createCategory call/CSV export) plus the pre-existing single-item actions; reusable CategoryHealthWidget (image/SEO/description/valid-parent/visibility/products-assigned + completion%); editor reorganized into General/Media/SEO/Visibility/Navigation/Attributes/Advanced tabs, media now uses the shared ImageFieldComponent, SEO explains fields in plain language with a live preview, Navigation tab shows real breadcrumb/children/visibility-based nav status, Attributes uses the shared KeyValueEditorComponent. Filled in the adminCategories i18n namespace (previously only 2 of ~90 referenced keys existed) across en/ru/hy. Also fixed the pre-existing bug where a filtered/searched category list could silently drop a matched descendant whose ancestor's title didn't match, by loading the full catalog once and filtering client-side.
2026-07-18 17:04:11 +04:00
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
deleteBlocked: 'Эту категорию нельзя удалить, пока у неё есть подкатегории или назначенные товары.',
emptyTitle: 'Категорий пока нет',
emptyDescription: 'Создайте первую категорию, чтобы начать организовывать товары.',
emptyGuide: 'Категории помогают покупателям находить товары. Начните с нескольких верхнеуровневых категорий (например, Одежда, Электроника), затем добавляйте подкатегории по мере роста каталога — избегайте вложенности глубже 2-3 уровней.',
viewTree: 'Дерево',
viewCards: 'Карточки',
expandAll: 'Развернуть всё',
collapseAll: 'Свернуть всё',
expandNode: 'Развернуть',
collapseNode: 'Свернуть',
fix(backoffice): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, Backoffice (admin/*) area. Builds on 712a7b4/63c9cee's composition pass (scope="col", skeletons, empty-states) without redoing that work. - Sidebar nav landmark: admin-layout's <nav aria-label> reused the "Dashboard" nav-item translation key as its landmark label, misleadingly announcing the whole sidebar as "Dashboard" - added a dedicated adminShell.sidebarLabel key ("Admin sidebar navigation") in en/ru/hy. Skip link, #admin-content main landmark, Escape handling, and mobile-drawer focus management were already correct - verified, not touched. - Categories tree drag-and-drop keyboard fallback (WCAG 2.1.1): the category tree's native HTML5 DnD (draggable/dragstart/drop) reorders siblings with no keyboard equivalent - existing arrow-key tree navigation only expands/ collapses/selects, never reorders. Added per-row move-up/move-down icon buttons (disabled at sibling boundaries), reusing the existing `reorder` output so the facade's reorder logic is untouched; new adminCategories.moveUp/moveDown keys in en/ru/hy. - Screen-reader loading announcements: skeleton-row loading states across Products, Categories, Customers, Orders, Transactions, Users, Reviews, Monitoring (webhooks/events), and Analytics (summary cards + top products) were purely visual (app-skeleton is aria-hidden by design) with no accessible "loading" text, unlike the storefront/product-details pattern - added role="status"/aria-live="polite"/aria-busy + sr-only text using the existing common.loading key. - Table row headers: added scope="row" to the primary identifying cell (product/category/customer name, order number, transaction order number, user name, review customer, report target, top-products/low-stock product name, webhook endpoint) on 9 tables that only had scope="col". Added matching `tbody th[scope='row'] { font-weight/color/text-align/ vertical-align }` + last-row border resets in each component's own scss so the semantic change doesn't alter visuals (the shared app-table stylesheet styles all <th> as bold/muted by default). Verified via `git show --stat` of fb1afb7/a03260e and `docs/UI-COMPOSITION- REVIEW.md`'s Backoffice sections first, per instructions - confirmed scope="col" coverage already complete, all admin modals already route through the shared app-dialog (focus-trap/Escape/return-focus already correct, nothing to fix), and the bare-<select> filters still carry aria-label per the accepted Sprint 28 decision (not re-migrated to app-select). Flagged, not fixed: - No toast/notification system exists anywhere in this codebase (product/ category save and delete call the gateway with no success/error UI at all, not even a subscribe error handler) - there is nothing to wire aria-live onto without adding a new UI mechanism, which is out of scope for an a11y-only pass. A prerequisite feature-level fix, not an a11y regression. - Dashboard's per-card metric/status-row/timeline skeletons (dashboard- metric, dashboard-status-row, dashboard-timeline) were left without aria-live wiring - wrapping each of the ~10 simultaneous mini-widgets in its own live region would fire a burst of redundant announcements; needs a single page-level "loading dashboard" region instead, a larger change than this surgical pass. - Monitoring's events table and the notifications dropdown (role="menu" with a static empty-state message, aria-haspopup="true") were left as-is - matches the same partial-widget-pattern precedent already accepted for locale-tabs/product-tabs in the storefront and builder passes. - Analytics `lowStockProducts` table's missing loading-skeleton branch (already flagged, not fixed, in the RC-Visual-02 pass) - untouched again here for the same reason. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing 768.57 kB vs 700 kB initial-bundle budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 09:18:09 +04:00
moveUp: 'Переместить вверх',
moveDown: 'Переместить вниз',
feat(categories): implement professional category management experience Categories dashboard (real total/visible/hidden/empty/root/subcategory/missing-image/missing-SEO/last-modified/completion% stats, recommended next action); tree view is now a real expand/collapse hierarchy (state persisted via LocalStorageService) with keyboard navigation (arrow keys, jump-to-parent), search that force-expands and keeps only matching branches + their ancestors/descendants instead of silently dropping deep matches; added Table/Cards views alongside the tree with density and saved column visibility; real bulk actions (show/hide/delete/assign parent/assign image via the Media Library picker/duplicate via the existing createCategory call/CSV export) plus the pre-existing single-item actions; reusable CategoryHealthWidget (image/SEO/description/valid-parent/visibility/products-assigned + completion%); editor reorganized into General/Media/SEO/Visibility/Navigation/Attributes/Advanced tabs, media now uses the shared ImageFieldComponent, SEO explains fields in plain language with a live preview, Navigation tab shows real breadcrumb/children/visibility-based nav status, Attributes uses the shared KeyValueEditorComponent. Filled in the adminCategories i18n namespace (previously only 2 of ~90 referenced keys existed) across en/ru/hy. Also fixed the pre-existing bug where a filtered/searched category list could silently drop a matched descendant whose ancestor's title didn't match, by loading the full catalog once and filtering client-side.
2026-07-18 17:04:11 +04:00
jumpToParent: 'К родителю',
deletedBadge: 'Удалена',
column_slug: 'Адрес',
column_items: 'Товары',
column_status: 'Статус',
column_visibility: 'Видимость',
column_updated: 'Последнее изменение',
bulkAssignParent: 'Назначить родителя',
bulkAssignImage: 'Назначить изображение',
groupGeneral: 'Основное',
groupMedia: 'Медиа',
groupSeo: 'SEO',
groupVisibility: 'Видимость',
groupNavigation: 'Навигация',
groupAttributes: 'Атрибуты',
groupAdvanced: 'Дополнительно',
visibilityExplain: 'Скрытая категория и её товары не отображаются в навигации и поиске магазина, пока вы снова не сделаете её видимой.',
navBreadcrumb: 'Хлебные крошки',
navBreadcrumbHint: 'Это путь, который покупатели видят над категорией, напр. Главная Электроника Телефоны. Он строится из выбранной родительской категории.',
iconHint: 'Небольшой эмодзи или символ рядом с названием категории в меню, напр. 📱. Оставьте пустым, если не нужен.',
feat(categories): implement professional category management experience Categories dashboard (real total/visible/hidden/empty/root/subcategory/missing-image/missing-SEO/last-modified/completion% stats, recommended next action); tree view is now a real expand/collapse hierarchy (state persisted via LocalStorageService) with keyboard navigation (arrow keys, jump-to-parent), search that force-expands and keeps only matching branches + their ancestors/descendants instead of silently dropping deep matches; added Table/Cards views alongside the tree with density and saved column visibility; real bulk actions (show/hide/delete/assign parent/assign image via the Media Library picker/duplicate via the existing createCategory call/CSV export) plus the pre-existing single-item actions; reusable CategoryHealthWidget (image/SEO/description/valid-parent/visibility/products-assigned + completion%); editor reorganized into General/Media/SEO/Visibility/Navigation/Attributes/Advanced tabs, media now uses the shared ImageFieldComponent, SEO explains fields in plain language with a live preview, Navigation tab shows real breadcrumb/children/visibility-based nav status, Attributes uses the shared KeyValueEditorComponent. Filled in the adminCategories i18n namespace (previously only 2 of ~90 referenced keys existed) across en/ru/hy. Also fixed the pre-existing bug where a filtered/searched category list could silently drop a matched descendant whose ancestor's title didn't match, by loading the full catalog once and filtering client-side.
2026-07-18 17:04:11 +04:00
navRoot: 'Это категория верхнего уровня.',
navChildren: 'Подкатегории',
navNoChildren: 'Подкатегорий пока нет.',
navAppearsIn: 'Где это отображается',
navAppearsVisible: 'Видна в навигации каталога и поиске.',
navAppearsHidden: 'Скрыта — не отображается в навигации каталога, пока скрыта.',
itemsCountLabel: 'Назначенные товары',
createdAtLabel: 'Создана',
healthImage: 'Изображение',
healthSeo: 'SEO',
healthDescription: 'Описание',
healthParent: 'Родитель',
healthVisibility: 'Видимость',
healthProducts: 'Назначены товары',
totalCategories: 'Всего категорий',
visibleCount: 'Видимые',
hiddenCount: 'Скрытые',
emptyCount: 'Пустые',
rootCount: 'Корневые категории',
subCount: 'Подкатегории',
missingImagesCount: 'Без изображений',
missingSeoCount: 'Без SEO',
lastModified: 'Последнее изменение',
lastModifiedNever: 'Никогда',
completionLabel: 'Готовность',
recommendedNext: 'Рекомендуемый следующий шаг',
recommendAddImage: 'У категории нет изображения — добавьте его для доверия в листингах.',
recommendAddSeo: 'У категории нет заголовка или описания для поиска — добавьте их.',
recommendFillEmpty: 'В категории нет товаров — добавьте товары или скройте категорию.',
recommendNone: 'Отлично — структура категорий в хорошем состоянии.',
fix(static-pages): backfill enabled/status in export, fix missing i18n key Independent review pass over the Static Pages Module sprint (M1-M7), including live browser E2E per the task's own verification checklist. - ProjectEditorFacade.normalize() now backfills enabled/status on record-format static pages (defaulting missing values to enabled+published, same rule ContentPageService.normalizePage applies for display - mirrored rather than imported, to avoid a project-editor <-> content-management circular dependency since ContentManagementFacade already depends on this facade). Found live: exporting a page that predates this sprint and was never touched/re-saved in the current session produced JSON missing enabled/status entirely - the editor UI and storefront resolver both normalize-on-read so nothing was actually broken live, but Export/Import fidelity should match what the editor shows. Verified fixed live (export now includes "enabled":true,"status":"published" for an untouched legacy page) and via the full gate. - Added the missing adminCategories.chooseImage i18n key (interface + en/ru/hy). Found live: the media-picker "choose image" button rendered as the literal string "adminCategories.chooseImage" - a pre-existing, repo-wide bug (5 templates reference this key; none of the locale files ever defined it) that I propagated into a 3rd/4th/5th... well, 2 new occurrences by copying the existing branding-section/footer-section pattern into static-pages-editor. Fixed the actual defect (missing translation) rather than renaming the key, which would have required touching 2 unrelated admin components outside this sprint's scope. Live-verified this pass: Static Pages editor renders with all new fields; create page works (page count 4->confirmed); device preview toggles desktop/tablet/mobile widths correctly; navigation "Insert page link" creates a real type:'staticPage' nav item end-to-end (confirmed in the exported JSON); export includes all Sprint X+2 fields after the fix; no console errors throughout. Gate: tsc --noEmit, npm test (57/57), arch:check, build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:27:29 +04:00
},
adminProducts: {
fix(admin): products canDeactivate guard, unblock npm, drop dead deps RC-01 Phase 1 mechanical fixes (verified against current repo state, not blindly reapplied from TODO.md): - admin/products create/edit/duplicate now protected by an unsaved- changes guard (adminProductDirtyGuard), mirroring the existing categories pattern. AdminProductsFacade had zero dirty-tracking before this - added a dirty signal, set true on updateDraft(), cleared on load/create/successful save. Added confirmLeaveUnsaved to the adminProducts i18n section (en/ru/hy) - categories already had its own copy of this key, products didn't. - barry-cache bumped ^0.1.0 -> ^0.9.3 (the pinned range no longer resolved on the registry - ETARGET - which had been silently blocking every npm install/uninstall all cycle). - Removed primeng/primeicons (npm uninstall, now unblocked) - the only consumer (items-carousel) was already deleted in RC PERF-01. - Removed core/search/services/search-history.service.ts, a dead 1-line re-export with zero importers (verified: the real implementation is features/search/services/search-history.service.ts, used by search.facade.ts). Left core/search/models/* alone - those ARE live, imported by catalog components. Verified before touching: HeaderConfig.showProfile toggle is already removed from the header-section editor template (TODO.md was stale on this one) - no change needed, will correct the tracking doc separately. tsc --noEmit clean, npm run build green (bundle unchanged, primeng was already tree-shaken out, this just removes the dead dependency declaration itself). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 20:04:51 +04:00
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
deleteTitle: 'Удалить товар',
confirmDelete: 'Удалить этот товар? Это действие нельзя отменить.',
confirmBulkDelete: 'Удалить выбранные товары? Это действие нельзя отменить.',
emptyTitle: 'Товары не найдены',
emptyDescription: 'Измените фильтры или создайте новый товар.',
emptyGuide: 'Хорошему товару нужны понятное название, минимум одно фото, цена и короткое описание — этого достаточно для публикации. Остальное можно добавить позже.',
selectAllRows: 'Выбрать все товары',
selectRow: 'Выбрать товар',
search: 'Поиск товаров…',
category: 'Категория',
allCategories: 'Все категории',
visibility: 'Видимость',
allVisibility: 'Любая видимость',
visible: 'Видимый',
hidden: 'Скрытый',
stockStatus: 'Статус наличия',
allStock: 'Любой остаток',
inStock: 'В наличии',
lowStock: 'Мало на складе',
outOfStock: 'Нет в наличии',
sortTitle: 'Имя',
sortPrice: 'Цена',
sortPriority: 'Приоритет',
sortStock: 'Остаток',
sortUpdated: 'Последнее изменение',
showArchived: 'Показать архивные',
infiniteScroll: 'Бесконечная прокрутка',
viewMode: 'Вид',
viewTable: 'Таблица',
viewGrid: 'Сетка',
density: 'Плотность',
densityComfortable: 'Комфортная',
densityCompact: 'Компактная',
columns: 'Столбцы',
column_sku: 'Артикул',
column_brand: 'Бренд',
column_price: 'Цена',
column_stock: 'Остаток',
column_visibility: 'Видимость',
column_updated: 'Последнее изменение',
create: 'Создать товар',
edit: 'Редактировать товар',
duplicate: 'Дублировать',
selectedCount: 'выбрано',
bulkShow: 'Опубликовать',
bulkHide: 'Скрыть',
bulkAssignCategory: 'Назначить категорию',
bulkAssignTags: 'Назначить теги',
bulkDuplicateAction: 'Дублировать',
bulkExportAction: 'Экспорт',
bulkDelete: 'Удалить',
tagsPlaceholder: 'например, лето, скидка',
name: 'Название',
brand: 'Бренд',
healthColumn: 'Качество',
actions: 'Действия',
archive: 'В архив',
delete: 'Удалить',
items: 'товаров',
loadMore: 'Загрузить ещё',
noImage: 'Нет изображения',
slug: 'URL-адрес',
barcode: 'Штрихкод',
priority: 'Приоритет',
shortDescription: 'Краткое описание',
translations: 'Переводы',
primaryImage: 'Главное изображение',
primaryImageHint: 'Основное фото, показываемое в списках и результатах поиска.',
gallery: 'Галерея',
galleryHint: 'Дополнительные фото на странице товара. Порядок меняется стрелками.',
moveLeft: 'Переместить раньше',
moveRight: 'Переместить позже',
videos: 'Видео (по одной ссылке на строку)',
discount: 'Скидка %',
currency: 'Валюта',
stockQuantity: 'Количество на складе',
stockQuantityHint: 'Сколько единиц доступно для продажи прямо сейчас.',
availabilityStatus: 'Доступность',
availabilityStatusHint: 'Что видит покупатель: в наличии, заканчивается или нет в наличии.',
availabilityNote: 'Примечание о доступности',
availabilityNoteHint: 'Дополнительная информация для покупателя, например «Доставка за 3 дня».',
reservedNote: 'Резерв пока не учитывается отдельно — здесь показано общее доступное количество.',
relatedProducts: 'Похожие товары',
specifications: 'Характеристики',
attributes: 'Атрибуты',
addRow: 'Добавить строку',
removeRow: 'Удалить',
rowKey: 'Название',
rowValue: 'Значение',
addVariant: 'Добавить вариант',
variantName: 'Название варианта',
variantsHint: 'Каждый вариант — это отдельный покупаемый вариант товара (например, размер или цвет) со своей ценой и остатком.',
variants: 'Варианты',
feat(admin): Shopify-style variant attributes matching production data shape Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> P0 user feedback: variants were just free-text name+price, and the user shared the real production payload - a flat array of {color, size, price, currency, remaining} rows, colors as '0x8B4513' hex. - New model: AdminProductVariantAttributeDef (key/label/isColor/values) + AdminProductVariant (attributes: Record<string,string>, sku, image, remaining, prices: {currency,price}[]) - this IS the production shape, grouped by combo with one row per currency instead of flattened, so admins edit one variant card instead of 4 duplicate rows - Attribute manager: add custom attributes (Color, Size, or anything), color attributes get a native color picker + live swatch preview instead of typing hex; other attributes get plain value chips - 'Generate variants' computes the cartesian product of attribute values (Color x Size = 4 combos for 1 color x 4 sizes) and preserves existing sku/price/stock data for combos that still exist after regeneration - Multi-currency pricing per variant (matches prod: same combo priced in RUB/USD/EUR/AMD) with per-currency add/remove - Color hex kept in the exact '0x8B4513' production format (toBackendColor/toCssColor conversion helpers) - Fixed an Angular v21 control-flow parser bug hit while building this: an @if/@else block whose only content is a bare {{ interpolation }} touching the block's closing brace fails to parse (NG5002 'Unclosed block for' cascading from a completely unrelated line) - worked around by keeping interpolation in its own element - Verified end-to-end in browser: added Color (color picker) + Size (S/M/L/XL) attributes, generated 4 variant combos, added all 4 currencies to a variant - matches the shared production JSON exactly
2026-07-19 13:40:51 +04:00
variantAttributes: 'Опции',
variantAttributesHint: 'Добавьте опции вроде Цвет или Размер, затем перечислите значения. Комбинации ниже создаются автоматически.',
colorAttribute: 'Цвет',
removeAttribute: 'Удалить опцию',
removeValue: 'Удалить значение',
pickColor: 'Выбрать цвет',
newValuePlaceholder: 'напр. Маленький',
addValue: 'Добавить',
newAttributePlaceholder: 'напр. Размер',
addAttribute: 'Добавить опцию',
generateVariants: 'Сгенерировать варианты',
variantsList: 'Варианты',
remaining: 'Остаток',
variantImage: 'Изображение',
archived: 'В архиве',
slugHint: 'Веб-адрес этого товара. Заполняется автоматически из названия — меняйте, только если нужен особый URL.',
skuHint: 'SKU (артикул) — ваш внутренний код товара для учёта склада и поиска заказов. Подойдёт любой уникальный текст.',
generate: 'Сгенерировать',
seoGenerate: 'Заполнить из данных товара',
seoGenerateHint: 'Заполняет пустые поля из названия и краткого описания. Ваш текст не перезаписывается.',
translationsHint: 'Основные поля выше отображаются на языке магазина по умолчанию. Здесь добавьте переводы для других языков — если перевод пуст, покупатели увидят текст по умолчанию.',
badgesHint: 'Бейджи — небольшие цветные ярлыки, которые покупатели видят на фото товара. Выберите из стандартных или добавьте свой.',
addBadge: 'Добавить',
removeBadge: 'Убрать бейдж',
customBadgePlaceholder: 'Текст своего бейджа…',
seoExplain: 'Это то, что видно в результатах поиска — понятные заголовок и описание помогают найти товар.',
searchTitle: 'Заголовок для поиска',
searchTitleHint: 'Заголовок, показываемый в результатах поиска. Делайте его коротким.',
searchDescription: 'Описание для поиска',
searchDescriptionHint: 'Краткое описание под заголовком в результатах поиска.',
seoMissingTitle: 'Добавьте заголовок для поиска, чтобы товар можно было найти.',
seoMissingDescription: 'Добавьте описание для поиска, чтобы улучшить отображение товара.',
keywords: 'Ключевые слова',
featured: 'Рекомендуемый',
recommended: 'В подборке',
new: 'Новинка',
bestseller: 'Хит продаж',
badges: 'Значки (через запятую)',
htmlDescription: 'Подробное описание',
customer: 'Отзывы покупателей',
reviewsReadonly: 'Отзывы',
noReviews: 'Пока нет отзывов.',
questionsReadonly: 'Вопросы',
noQuestions: 'Пока нет вопросов.',
save: 'Сохранить товар',
groupGeneral: 'Основное',
groupMedia: 'Медиа',
groupPricing: 'Цена',
groupInventory: 'Склад',
groupCategories: 'Категории',
groupAttributes: 'Атрибуты',
groupSeo: 'SEO',
groupVisibility: 'Видимость',
groupAdvanced: 'Дополнительно',
healthImages: 'Изображения',
healthSeo: 'SEO',
healthPrice: 'Цена',
healthCategory: 'Категория',
healthDescription: 'Описание',
healthInventory: 'Склад',
totalProducts: 'Всего товаров',
publishedCount: 'Опубликовано',
draftsCount: 'Черновики',
outOfStockCount: 'Нет в наличии',
hiddenCount: 'Скрыто',
missingImagesCount: 'Без изображений',
missingSeoCount: 'Без SEO',
lowQualityCount: 'Низкое качество',
recentlyEdited: 'Недавно изменённые',
recommendedNext: 'Рекомендуемый следующий шаг',
openAction: 'Открыть',
recommendAddImages: 'У товара нет фото — добавьте хотя бы одно изображение.',
recommendAddSeo: 'У товара нет заголовка или описания для поиска — добавьте их.',
recommendRestock: 'Товар закончился на складе — пополните запас или скройте его.',
recommendNone: 'Отлично — каталог в хорошем состоянии.',
},
adminUsers: {
emptyTitle: 'Пользователи не найдены',
emptyDescription: 'Пригласите коллегу, чтобы начать.',
invitationsEmptyTitle: 'Нет ожидающих приглашений',
title: 'Команда',
name: 'Имя',
email: 'Email',
scope: 'Рабочая область',
role: 'Роль',
roles: 'Роли',
lastLogin: 'Последний вход',
sessions: 'Активные сессии',
currentSession: 'Текущая сессия',
invite: 'Пригласить участника',
sendInvite: 'Отправить приглашение',
revoke: 'Отозвать',
passwordlessHint: 'Участники входят через Telegram — пароли не нужны.',
confirmSuspend: 'Заблокировать этого участника? Доступ будет закрыт немедленно.',
suspend: 'Заблокировать',
reactivate: 'Восстановить',
scopeValue: {
marketplace: 'Витрина',
office: 'Бэк-офис',
},
statusValue: {
active: 'Активен',
invited: 'Приглашён',
suspended: 'Заблокирован',
},
invitationStatus: {
pending: 'Ожидает',
accepted: 'Принято',
expired: 'Истекло',
revoked: 'Отозвано',
},
roleValue: {
owner: 'Владелец',
admin: 'Администратор',
editor: 'Редактор',
viewer: 'Наблюдатель',
},
permission: {
all: 'Полный доступ ко всем разделам',
productsManage: 'Управление товарами',
productsView: 'Просмотр товаров',
categoriesManage: 'Управление категориями',
ordersManage: 'Управление заказами',
ordersView: 'Просмотр заказов',
mediaManage: 'Управление медиатекой',
},
actor: {
admin: 'Администратор',
},
audit: {
roleChanged: 'Роль изменена на «{{role}}»',
statusChanged: 'Статус изменён на «{{status}}»',
},
},
adminTransactions: {
search: 'Поиск транзакций',
type: 'Тип',
method: 'Способ оплаты',
itemsCount: 'транзакций',
fraud: 'Проверка на мошенничество',
flagged: 'Помечено',
flag: 'Пометить как подозрительную',
clearFlag: 'Снять пометку',
retry: 'Повторить',
audit: 'История',
emptyTitle: 'Транзакции не найдены',
emptyDescription: 'Попробуйте изменить фильтр или поисковый запрос.',
status: {
all: 'Все статусы',
pending: 'В обработке',
success: 'Успешно',
failed: 'Ошибка',
retried: 'Повторено',
},
typeValue: {
all: 'Все типы',
payment: 'Оплата',
refund: 'Возврат',
qr_payment: 'Оплата по QR',
},
methodValue: {
card: 'Карта',
qr: 'QR-код',
cash_on_delivery: 'Наличными при получении',
},
},
adminMonitoring: {
eventsEmptyTitle: 'События не найдены',
eventsEmptyDescription: 'Попробуйте изменить фильтр категории или поисковый запрос.',
health: 'Проверки состояния',
queues: 'Фоновые очереди',
depth: 'В ожидании',
webhooks: 'Доставка вебхуков',
endpoint: 'Получатель',
event: 'Событие',
events: 'Журнал активности',
category: 'Категория',
level: 'Важность',
message: 'Описание',
technicalDetails: 'Технические детали',
categoryValue: {
all: 'Все категории',
audit: 'Действия администраторов',
security: 'Безопасность',
login: 'Вход',
failed_login: 'Неудачный вход',
api: 'API',
error: 'Ошибка',
warning: 'Предупреждение',
},
queueStatus: {
healthy: 'Работает штатно',
degraded: 'Замедлена',
down: 'Остановлена',
},
webhookStatus: {
delivered: 'Доставлено',
failed: 'Ошибка',
pending: 'Ожидает',
},
levelValue: {
info: 'Инфо',
warning: 'Предупреждение',
error: 'Ошибка',
},
systemState: 'Состояние системы',
backendConnectivity: 'Бэкенд',
connected: 'Подключено',
loading: 'Загрузка',
draftState: 'Черновик',
unsavedChanges: 'Есть несохранённые изменения',
noDraft: 'Черновика нет',
lastSaved: 'Последнее сохранение',
lastPublished: 'Последняя публикация',
syncStatus: 'Синхронизация',
},
adminReports: {
sales: 'Продажи',
topProducts: 'Топ товаров',
marketplaceHealth: 'Состояние маркетплейса',
},
adminSettings: {
density: 'Плотность таблиц',
densityExplain: 'Уменьшить отступы строк в списках панели управления для более компактного вида.',
densityCompact: 'Компактные строки',
},
adminAnalytics: {
topProductsEmptyTitle: 'Нет продаж товаров за этот период',
topProductsEmptyDescription: 'Попробуйте выбрать более широкий диапазон дат.',
topProducts: 'Топ товаров',
print: 'Печать',
tabsLabel: 'Разделы аналитики',
tab: {
overview: 'Обзор',
health: 'Здоровье',
products: 'Товары',
customers: 'Клиенты',
search: 'Поиск',
traffic: 'Трафик',
recommendations: 'Рекомендации',
},
revenue: 'Выручка',
orders: 'Заказы',
customers: 'Клиенты',
avgOrderValue: 'Средний чек',
conversion: 'Конверсия',
completionPercent: 'Готовность',
unknown: 'Неизвестно',
salesChart: 'Продажи',
quantitySold: 'Продано, шт.',
lowStock: 'Мало на складе',
lowStockEmptyTitle: 'Нет товаров с низким остатком',
quantity: 'Количество',
status: 'Статус',
recentActivity: 'Недавняя активность',
recentActivityEmptyTitle: 'Нет недавней активности',
activityDraftSaved: 'Черновик сохранён',
activityPublished: 'Опубликовано',
warnings: 'Предупреждения',
topViewed: 'Самые просматриваемые',
hiddenProducts: 'Скрытые товары',
archivedProducts: 'Архивные товары',
mostReviewed: 'Больше всего отзывов',
worstRated: 'Худший рейтинг',
noReviewedProducts: 'Пока нет товаров с отзывами',
reviewCount: 'Отзывы',
avgRating: 'Средний рейтинг',
newCustomers: 'Новые клиенты',
returningCustomers: 'Повторные клиенты',
averageSpend: 'Средние траты',
retention: 'Удержание',
searchAnalytics: 'Аналитика поиска',
trafficAnalytics: 'Трафик',
availableAfterBackend: 'Будет доступно после интеграции с бэкендом.',
pendingBackend: 'Ожидает бэкенд',
pendingBackendHint: 'Отслеживание поисковых запросов пока не реализовано.',
funnelsHeatmaps: 'Воронки и тепловые карты',
visitors: 'Посетители',
products: 'Товары',
categories: 'Категории',
},
adminMarketplaceHealth: {
title: 'Здоровье маркетплейса',
complete: 'готово',
recommendationsTitle: 'Рекомендации',
recommendNone: 'Всё в порядке.',
checkImages: 'У товаров есть изображения',
checkSeo: 'У товаров заполнено SEO',
checkCategories: 'В категориях есть товары',
checkReviews: 'Отзывы модерируются',
checkOrders: 'Заказы обрабатываются',
checkTranslations: 'Переводы заполнены',
checkHomepage: 'Главная страница настроена',
checkBackend: 'Связь с бэкендом',
checkPerformance: 'Производительность',
recommendImages: 'Добавьте изображения товарам без них',
recommendImagesCount: 'Товары без изображений снижают конверсию.',
recommendSeo: 'Заполните SEO для товаров без него',
recommendSeoCount: 'Отсутствие SEO ухудшает видимость в поиске.',
recommendCategory: 'Назначьте категорию товарам без категории',
recommendCategoryCount: 'Товары без категории труднее найти.',
recommendEmptyCategories: 'Категории без товаров',
recommendEmptyCategoriesCount: 'Рассмотрите объединение или удаление пустых категорий.',
recommendHomepage: 'Главная страница не заполнена',
recommendStaticPages: 'Есть неопубликованные статические страницы',
severity: {
info: 'Инфо',
warning: 'Внимание',
critical: 'Критично',
},
},
adminShell: {
skipToContent: 'Перейти к содержимому',
fix(backoffice): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, Backoffice (admin/*) area. Builds on 712a7b4/63c9cee's composition pass (scope="col", skeletons, empty-states) without redoing that work. - Sidebar nav landmark: admin-layout's <nav aria-label> reused the "Dashboard" nav-item translation key as its landmark label, misleadingly announcing the whole sidebar as "Dashboard" - added a dedicated adminShell.sidebarLabel key ("Admin sidebar navigation") in en/ru/hy. Skip link, #admin-content main landmark, Escape handling, and mobile-drawer focus management were already correct - verified, not touched. - Categories tree drag-and-drop keyboard fallback (WCAG 2.1.1): the category tree's native HTML5 DnD (draggable/dragstart/drop) reorders siblings with no keyboard equivalent - existing arrow-key tree navigation only expands/ collapses/selects, never reorders. Added per-row move-up/move-down icon buttons (disabled at sibling boundaries), reusing the existing `reorder` output so the facade's reorder logic is untouched; new adminCategories.moveUp/moveDown keys in en/ru/hy. - Screen-reader loading announcements: skeleton-row loading states across Products, Categories, Customers, Orders, Transactions, Users, Reviews, Monitoring (webhooks/events), and Analytics (summary cards + top products) were purely visual (app-skeleton is aria-hidden by design) with no accessible "loading" text, unlike the storefront/product-details pattern - added role="status"/aria-live="polite"/aria-busy + sr-only text using the existing common.loading key. - Table row headers: added scope="row" to the primary identifying cell (product/category/customer name, order number, transaction order number, user name, review customer, report target, top-products/low-stock product name, webhook endpoint) on 9 tables that only had scope="col". Added matching `tbody th[scope='row'] { font-weight/color/text-align/ vertical-align }` + last-row border resets in each component's own scss so the semantic change doesn't alter visuals (the shared app-table stylesheet styles all <th> as bold/muted by default). Verified via `git show --stat` of fb1afb7/a03260e and `docs/UI-COMPOSITION- REVIEW.md`'s Backoffice sections first, per instructions - confirmed scope="col" coverage already complete, all admin modals already route through the shared app-dialog (focus-trap/Escape/return-focus already correct, nothing to fix), and the bare-<select> filters still carry aria-label per the accepted Sprint 28 decision (not re-migrated to app-select). Flagged, not fixed: - No toast/notification system exists anywhere in this codebase (product/ category save and delete call the gateway with no success/error UI at all, not even a subscribe error handler) - there is nothing to wire aria-live onto without adding a new UI mechanism, which is out of scope for an a11y-only pass. A prerequisite feature-level fix, not an a11y regression. - Dashboard's per-card metric/status-row/timeline skeletons (dashboard- metric, dashboard-status-row, dashboard-timeline) were left without aria-live wiring - wrapping each of the ~10 simultaneous mini-widgets in its own live region would fire a burst of redundant announcements; needs a single page-level "loading dashboard" region instead, a larger change than this surgical pass. - Monitoring's events table and the notifications dropdown (role="menu" with a static empty-state message, aria-haspopup="true") were left as-is - matches the same partial-widget-pattern precedent already accepted for locale-tabs/product-tabs in the storefront and builder passes. - Analytics `lowStockProducts` table's missing loading-skeleton branch (already flagged, not fixed, in the RC-Visual-02 pass) - untouched again here for the same reason. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing 768.57 kB vs 700 kB initial-bundle budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 09:18:09 +04:00
sidebarLabel: 'Навигация панели администратора',
openSidebar: 'Открыть меню',
closeSidebar: 'Закрыть меню',
collapseSidebar: 'Свернуть меню',
expandSidebar: 'Развернуть меню',
nav: {
dashboard: 'Панель управления',
catalogGroup: 'Каталог',
products: 'Товары',
categories: 'Категории',
orders: 'Заказы',
customers: 'Клиенты',
moderation: 'Отзывы и модерация',
transactions: 'Транзакции',
reviews: 'Отзывы',
reports: 'Отчёты',
feat(admin): Seller Management Phase 1 UI - Partners section, empty state, request/learn-more dialogs No backend, no CRUD, no API, no business logic - production-quality UI only, built entirely from existing shared components (app-dialog, app-empty-state, app-button, app-form-field, app-input, app-icon, app-badge). Gated per ADR-011: reads modules.sellerManagement.enabled from bootstrap (always false today, no backend sets it) rather than hardcoding disabled state. New: - AdminSellerManagementPageComponent (features/admin/seller-management/ pages/) - renders the specified empty state (title/description/ Request Access + Learn More buttons) using existing shared/ui primitives only, no new UI infrastructure. - Request Access dialog: Company/Email/Message form via app-form-field + app-input + a plain textarea (no dedicated textarea component exists yet, styled to match app-input's own tokens exactly). Submission is mocked (setTimeout), no API call. On submit: closes and opens a success dialog ("Thank you..."). - Learn More dialog: 6 capability bullets (seller dashboards, storefronts, permissions, analytics, product ownership, marketplace administration) under a "Coming Soon" badge. - New admin nav group "Partners" > "Seller Management" link (admin-nav.model.ts), new route /backoffice/partners/seller-management (app.routes.ts), using the same loadComponent/breadcrumb pattern as every other admin route. Translations: full en/ru/hy coverage, zero hardcoded strings - new adminShell.nav.{partnersGroup,sellerManagement}, adminShell.pages.sellerManagement, and a new adminSellerManagement.* namespace (emptyState/requestDialog/requestSuccessDialog/ learnMoreDialog) added to translations.ts (types) and all three locale files. Accessibility: inherited from app-dialog (role="dialog", aria-modal, focus trap on Tab/Shift+Tab, Escape to close, focus restored to trigger on close) - no new a11y code needed, reused as-is. Responsive: existing --space-*/--font-size-* tokens throughout, flex-wrap on button row, mobile breakpoint stacks actions full-width. Verified live (ru locale, devBypassAdmin): nav group/link render correctly, breadcrumb shows "Управление продавцами", empty state copy matches spec exactly, Request Access dialog opens with all 3 fields + Cancel/Send Request, filled + submitted -> success dialog with exact spec copy, Learn More dialog shows all 6 bullets + Coming Soon badge, no console errors, verified again at 375px mobile viewport. tsc --noEmit clean, ng build clean (pre-existing bundle- budget warning only), arch:check (boundaries + cycles) clean.
2026-07-26 20:47:18 +04:00
partnersGroup: 'Партнёры',
sellerManagement: 'Управление продавцами',
content: 'Контент',
mediaLibrary: 'Медиатека',
marketplaceBuilder: 'Конструктор маркетплейса',
users: 'Пользователи',
settings: 'Настройки',
monitoring: 'Мониторинг',
analytics: 'Аналитика',
documentation: 'Документация',
help: 'Помощь',
logout: 'Выйти',
comingSoonBadge: 'Скоро',
},
topbar: {
searchLabel: 'Поиск по админ-панели',
searchPlaceholder: 'Искать товары, заказы, страницы...',
notifications: 'Уведомления',
notificationsEmpty: 'Новых уведомлений нет',
account: 'Аккаунт администратора',
tenantSelector: 'Магазин',
tenantSelectorComingSoon: 'Переключение между магазинами скоро появится',
quickPublish: 'Опубликовать',
quickPublishComingSoon: 'Быстрая публикация скоро появится',
},
breadcrumbHome: 'Админ-панель',
pages: {
dashboard: { title: 'Панель управления', description: 'Обзор состояния вашего маркетплейса' },
products: { title: 'Товары', description: 'Управляйте карточками товаров и их видимостью' },
productCreate: { title: 'Новый товар', description: 'Заполните карточку, чтобы добавить товар в каталог' },
productEdit: { title: 'Редактирование товара', description: 'Измените данные товара' },
productDuplicate: { title: 'Копия товара', description: 'Создайте новый товар на основе существующего' },
categories: { title: 'Категории', description: 'Постройте дерево категорий каталога' },
categoryCreate: { title: 'Новая категория', description: 'Добавьте категорию в дерево каталога' },
categoryEdit: { title: 'Редактирование категории', description: 'Измените данные категории' },
orders: { title: 'Заказы', description: 'Просматривайте и обрабатывайте заказы покупателей' },
orderDetail: { title: 'Заказ', description: 'Детали и статус заказа' },
customers: { title: 'Клиенты', description: 'Все, кто оформлял заказ' },
customerDetail: { title: 'Клиент', description: 'Профиль клиента, заказы и активность' },
moderation: { title: 'Отзывы и модерация', description: 'Проверяйте и модерируйте отзывы клиентов' },
reviewDetail: { title: 'Отзыв', description: 'Детали отзыва и действия модерации' },
reportsQueue: { title: 'Очередь жалоб', description: 'Контент, на который пожаловались клиенты' },
transactions: { title: 'Транзакции', description: 'Платежи, возвраты и проверка мошенничества' },
media: { title: 'Медиатека', description: 'Изображения и файлы для товаров и страниц' },
users: { title: 'Пользователи', description: 'Команда администраторов и права доступа' },
monitoring: { title: 'Мониторинг', description: 'Состояние очередей, вебхуков и системных событий' },
analytics: { title: 'Аналитика', description: 'Продажи, товары и поведение покупателей' },
feat(admin): Seller Management Phase 1 UI - Partners section, empty state, request/learn-more dialogs No backend, no CRUD, no API, no business logic - production-quality UI only, built entirely from existing shared components (app-dialog, app-empty-state, app-button, app-form-field, app-input, app-icon, app-badge). Gated per ADR-011: reads modules.sellerManagement.enabled from bootstrap (always false today, no backend sets it) rather than hardcoding disabled state. New: - AdminSellerManagementPageComponent (features/admin/seller-management/ pages/) - renders the specified empty state (title/description/ Request Access + Learn More buttons) using existing shared/ui primitives only, no new UI infrastructure. - Request Access dialog: Company/Email/Message form via app-form-field + app-input + a plain textarea (no dedicated textarea component exists yet, styled to match app-input's own tokens exactly). Submission is mocked (setTimeout), no API call. On submit: closes and opens a success dialog ("Thank you..."). - Learn More dialog: 6 capability bullets (seller dashboards, storefronts, permissions, analytics, product ownership, marketplace administration) under a "Coming Soon" badge. - New admin nav group "Partners" > "Seller Management" link (admin-nav.model.ts), new route /backoffice/partners/seller-management (app.routes.ts), using the same loadComponent/breadcrumb pattern as every other admin route. Translations: full en/ru/hy coverage, zero hardcoded strings - new adminShell.nav.{partnersGroup,sellerManagement}, adminShell.pages.sellerManagement, and a new adminSellerManagement.* namespace (emptyState/requestDialog/requestSuccessDialog/ learnMoreDialog) added to translations.ts (types) and all three locale files. Accessibility: inherited from app-dialog (role="dialog", aria-modal, focus trap on Tab/Shift+Tab, Escape to close, focus restored to trigger on close) - no new a11y code needed, reused as-is. Responsive: existing --space-*/--font-size-* tokens throughout, flex-wrap on button row, mobile breakpoint stacks actions full-width. Verified live (ru locale, devBypassAdmin): nav group/link render correctly, breadcrumb shows "Управление продавцами", empty state copy matches spec exactly, Request Access dialog opens with all 3 fields + Cancel/Send Request, filled + submitted -> success dialog with exact spec copy, Learn More dialog shows all 6 bullets + Coming Soon badge, no console errors, verified again at 375px mobile viewport. tsc --noEmit clean, ng build clean (pre-existing bundle- budget warning only), arch:check (boundaries + cycles) clean.
2026-07-26 20:47:18 +04:00
sellerManagement: { title: 'Управление продавцами', description: 'Подключайте независимых продавцов к вашему маркетплейсу с централизованным администрированием' },
reports: { title: 'Отчёты', description: 'Экспорт отчётов по продажам, товарам и состоянию маркетплейса' },
settings: { title: 'Настройки', description: 'Настройки отображения панели управления' },
feat(admin): Seller Management Phase 1 UI - Partners section, empty state, request/learn-more dialogs No backend, no CRUD, no API, no business logic - production-quality UI only, built entirely from existing shared components (app-dialog, app-empty-state, app-button, app-form-field, app-input, app-icon, app-badge). Gated per ADR-011: reads modules.sellerManagement.enabled from bootstrap (always false today, no backend sets it) rather than hardcoding disabled state. New: - AdminSellerManagementPageComponent (features/admin/seller-management/ pages/) - renders the specified empty state (title/description/ Request Access + Learn More buttons) using existing shared/ui primitives only, no new UI infrastructure. - Request Access dialog: Company/Email/Message form via app-form-field + app-input + a plain textarea (no dedicated textarea component exists yet, styled to match app-input's own tokens exactly). Submission is mocked (setTimeout), no API call. On submit: closes and opens a success dialog ("Thank you..."). - Learn More dialog: 6 capability bullets (seller dashboards, storefronts, permissions, analytics, product ownership, marketplace administration) under a "Coming Soon" badge. - New admin nav group "Partners" > "Seller Management" link (admin-nav.model.ts), new route /backoffice/partners/seller-management (app.routes.ts), using the same loadComponent/breadcrumb pattern as every other admin route. Translations: full en/ru/hy coverage, zero hardcoded strings - new adminShell.nav.{partnersGroup,sellerManagement}, adminShell.pages.sellerManagement, and a new adminSellerManagement.* namespace (emptyState/requestDialog/requestSuccessDialog/ learnMoreDialog) added to translations.ts (types) and all three locale files. Accessibility: inherited from app-dialog (role="dialog", aria-modal, focus trap on Tab/Shift+Tab, Escape to close, focus restored to trigger on close) - no new a11y code needed, reused as-is. Responsive: existing --space-*/--font-size-* tokens throughout, flex-wrap on button row, mobile breakpoint stacks actions full-width. Verified live (ru locale, devBypassAdmin): nav group/link render correctly, breadcrumb shows "Управление продавцами", empty state copy matches spec exactly, Request Access dialog opens with all 3 fields + Cancel/Send Request, filled + submitted -> success dialog with exact spec copy, Learn More dialog shows all 6 bullets + Coming Soon badge, no console errors, verified again at 375px mobile viewport. tsc --noEmit clean, ng build clean (pre-existing bundle- budget warning only), arch:check (boundaries + cycles) clean.
2026-07-26 20:47:18 +04:00
},
},
adminSellerManagement: {
emptyState: {
title: 'Управление продавцами',
description: 'Позвольте торговым центрам, маркетплейсам и корпоративным клиентам подключать независимых продавцов, сохраняя централизованное администрирование. Этот модуль сейчас в разработке.',
requestAccess: 'Запросить доступ',
learnMore: 'Узнать больше',
},
requestDialog: {
title: 'Запросить доступ',
companyLabel: 'Компания',
companyPlaceholder: 'Название вашей компании',
emailLabel: 'Email',
emailPlaceholder: 'you@company.com',
messageLabel: 'Сообщение',
messagePlaceholder: 'Расскажите о вашем маркетплейсе и потребностях в продавцах',
cancel: 'Отмена',
send: 'Отправить запрос',
},
requestSuccessDialog: {
title: 'Спасибо',
message: 'Наша команда получила ваш запрос. Мы свяжемся с вами, как только Управление продавцами станет доступно.',
close: 'Закрыть',
},
learnMoreDialog: {
title: 'Управление продавцами',
comingSoonBadge: 'Скоро',
features: {
sellerDashboards: 'Личные кабинеты продавцов',
sellerStorefronts: 'Витрины продавцов',
sellerPermissions: 'Права доступа продавцов',
sellerAnalytics: 'Аналитика продавцов',
productOwnership: 'Владение товарами',
marketplaceAdministration: 'Администрирование маркетплейса',
},
close: 'Закрыть',
},
},
2026-02-26 23:09:20 +04:00
};