2026-02-26 23:09:20 +04:00
|
|
|
import { Translations } from './translations';
|
|
|
|
|
|
|
|
|
|
export const en: Translations = {
|
|
|
|
|
header: {
|
|
|
|
|
home: 'Home',
|
|
|
|
|
search: 'Search',
|
|
|
|
|
about: 'About',
|
|
|
|
|
contacts: 'Contacts',
|
|
|
|
|
searchPlaceholder: 'Search...',
|
|
|
|
|
catalog: 'Catalog',
|
2026-07-09 01:13:54 +04:00
|
|
|
wishlist: 'Wishlist',
|
|
|
|
|
compare: 'Compare',
|
2026-07-09 02:29:12 +04:00
|
|
|
openMenu: 'Open menu',
|
|
|
|
|
closeMenu: 'Close menu',
|
2026-02-26 23:09:20 +04:00
|
|
|
},
|
|
|
|
|
footer: {
|
|
|
|
|
description: 'A modern marketplace for comfortable shopping',
|
|
|
|
|
company: 'Company',
|
|
|
|
|
aboutUs: 'About us',
|
|
|
|
|
contacts: 'Contacts',
|
|
|
|
|
requisites: 'Company details',
|
|
|
|
|
support: 'Support',
|
|
|
|
|
faq: 'FAQ',
|
|
|
|
|
delivery: 'Delivery',
|
|
|
|
|
guarantee: 'Warranty',
|
|
|
|
|
legal: 'Legal information',
|
|
|
|
|
offer: 'Public offer',
|
|
|
|
|
privacy: 'Privacy policy',
|
|
|
|
|
returns: 'Returns',
|
|
|
|
|
info: 'Information',
|
|
|
|
|
aboutCompany: 'About company',
|
|
|
|
|
documents: 'Documents',
|
|
|
|
|
paymentRules: 'Payment terms',
|
|
|
|
|
returnPolicy: 'Return policy',
|
|
|
|
|
publicOffer: 'Public offer',
|
|
|
|
|
help: 'Help',
|
|
|
|
|
payment: 'Payment',
|
|
|
|
|
allRightsReserved: 'All rights reserved.',
|
|
|
|
|
},
|
|
|
|
|
home: {
|
|
|
|
|
welcomeTo: 'Welcome to {{brand}}',
|
|
|
|
|
subtitle: 'Find everything you need in one place',
|
|
|
|
|
startSearch: 'Start search',
|
|
|
|
|
loading: 'Loading categories...',
|
|
|
|
|
errorTitle: 'Something went wrong',
|
|
|
|
|
retry: 'Try again',
|
|
|
|
|
categoriesTitle: 'Product categories',
|
|
|
|
|
categoriesSubtitle: 'Choose a category',
|
|
|
|
|
categoriesEmpty: 'Categories coming soon',
|
|
|
|
|
categoriesEmptyDesc: 'We are working on filling the catalog',
|
|
|
|
|
dexarHeroTitle: 'Find everything here',
|
|
|
|
|
dexarHeroSubtitle: 'Thousands of products in one place',
|
|
|
|
|
dexarHeroTagline: 'simple and convenient',
|
|
|
|
|
goToCatalog: 'Go to catalog',
|
|
|
|
|
findProduct: 'Find a product',
|
|
|
|
|
loadingDexar: 'Loading categories...',
|
|
|
|
|
catalogTitle: 'Product catalog',
|
|
|
|
|
emptyCategoriesDexar: 'No categories yet',
|
|
|
|
|
categoriesSoonDexar: 'Categories will appear here soon',
|
|
|
|
|
itemsCount: '{{count}} products',
|
|
|
|
|
},
|
|
|
|
|
cart: {
|
|
|
|
|
title: 'Cart',
|
|
|
|
|
clear: 'Clear',
|
|
|
|
|
empty: 'Cart is empty',
|
|
|
|
|
emptyDesc: 'Add products to start shopping',
|
|
|
|
|
goShopping: 'Start shopping',
|
|
|
|
|
total: 'Total',
|
|
|
|
|
items: 'Products',
|
|
|
|
|
deliveryLabel: 'Delivery',
|
2026-06-21 23:13:01 +04:00
|
|
|
deliveryMethod: 'Delivery option',
|
|
|
|
|
selectDelivery: 'Select delivery option',
|
|
|
|
|
deliveryPlace: 'Place',
|
|
|
|
|
deliveryTime: 'Delivery time',
|
|
|
|
|
digitalDelivery: 'Digital delivery',
|
|
|
|
|
deliveryRequired: 'Select delivery for every shippable item before checkout.',
|
2026-02-26 23:09:20 +04:00
|
|
|
toPay: 'To pay',
|
|
|
|
|
agreeWith: 'I agree with the',
|
|
|
|
|
publicOffer: 'public offer',
|
|
|
|
|
returnPolicy: 'return policy',
|
|
|
|
|
guaranteeTerms: 'warranty terms',
|
|
|
|
|
privacyPolicy: 'privacy policy',
|
|
|
|
|
and: 'and',
|
|
|
|
|
checkout: 'Place order',
|
2026-06-29 00:06:18 +04:00
|
|
|
payViaQr: 'Pay via QR',
|
|
|
|
|
payViaCard: 'Pay via card',
|
2026-02-26 23:09:20 +04:00
|
|
|
close: 'Close',
|
|
|
|
|
creatingPayment: 'Creating payment...',
|
|
|
|
|
waitFewSeconds: 'Please wait a few seconds',
|
|
|
|
|
scanQr: 'Scan the QR code to pay',
|
|
|
|
|
amountToPay: 'Amount due:',
|
|
|
|
|
waitingPayment: 'Waiting for payment...',
|
|
|
|
|
copied: '✓ Copied',
|
|
|
|
|
copyLink: 'Copy link',
|
|
|
|
|
openNewTab: 'Open in new tab',
|
|
|
|
|
paymentSuccess: 'Congratulations! Payment successful!',
|
|
|
|
|
paymentSuccessDesc: 'Enter your contact details and we will send your purchase within a few minutes',
|
|
|
|
|
sending: 'Sending...',
|
|
|
|
|
send: 'Send',
|
2026-06-02 00:57:36 +04:00
|
|
|
paymentError: 'Unable to create payment',
|
|
|
|
|
paymentErrorDesc: 'We could not prepare the payment QR code right now. Please try again in a moment.',
|
|
|
|
|
retryPayment: 'Try again',
|
2026-02-26 23:09:20 +04:00
|
|
|
paymentTimeout: 'Payment timed out',
|
|
|
|
|
paymentTimeoutDesc: 'We did not receive payment confirmation within 3 minutes.',
|
|
|
|
|
autoClose: 'Window will close automatically...',
|
|
|
|
|
confirmClear: 'Are you sure you want to clear the cart?',
|
2026-07-09 02:29:12 +04:00
|
|
|
removeItem: 'Remove item',
|
|
|
|
|
paymentQrAlt: 'Payment QR code',
|
2026-02-26 23:09:20 +04:00
|
|
|
acceptTerms: 'Please accept the offer terms, return policy, and warranty terms to proceed with the order.',
|
|
|
|
|
copyError: 'Copy error:',
|
|
|
|
|
emailSuccess: 'Email sent successfully! Check your inbox.',
|
|
|
|
|
emailError: 'An error occurred while sending the email. Please try again.',
|
|
|
|
|
phoneRequired: 'Phone number is required',
|
|
|
|
|
phoneMoreDigits: 'Enter {{count}} more digits',
|
|
|
|
|
phoneTooMany: 'Too many digits',
|
|
|
|
|
emailRequired: 'Email is required',
|
|
|
|
|
emailTooShort: 'Email is too short (minimum 5 characters)',
|
|
|
|
|
emailTooLong: 'Email is too long (maximum 100 characters)',
|
|
|
|
|
emailNeedsAt: 'Email must contain @',
|
|
|
|
|
emailNeedsDomain: 'Email must contain a domain (.com, .ru, etc.)',
|
|
|
|
|
emailInvalid: 'Invalid email format',
|
2026-03-24 00:09:11 +04:00
|
|
|
loginRequired: 'Log in to checkout',
|
|
|
|
|
loginRequiredDesc: 'Please log in via Telegram to place your order',
|
|
|
|
|
loginWithTelegram: 'Log in with Telegram',
|
|
|
|
|
orScanQr: 'Or scan the QR code',
|
2026-02-26 23:09:20 +04:00
|
|
|
},
|
|
|
|
|
search: {
|
|
|
|
|
title: 'Product search',
|
|
|
|
|
placeholder: 'Enter product name...',
|
2026-07-10 13:25:31 +04:00
|
|
|
searchBarAria: 'Search bar',
|
2026-02-26 23:09:20 +04:00
|
|
|
resultsCount: 'Products found:',
|
|
|
|
|
searching: 'Searching...',
|
|
|
|
|
retry: 'Try again',
|
|
|
|
|
noResults: 'Nothing found',
|
|
|
|
|
noResultsFor: 'No products found for "{{query}}"',
|
|
|
|
|
noResultsHint: 'Try changing your query or using different keywords',
|
2026-07-10 13:25:31 +04:00
|
|
|
emptyResultsAria: 'Empty search results',
|
|
|
|
|
popularCategories: 'Popular categories',
|
|
|
|
|
recommendedProducts: 'Recommended products',
|
|
|
|
|
aiSuggestionHint: 'AI suggestion (future)',
|
|
|
|
|
suggestionType: {
|
|
|
|
|
product: 'Product',
|
|
|
|
|
category: 'Category',
|
|
|
|
|
brand: 'Brand',
|
|
|
|
|
collection: 'Collection',
|
|
|
|
|
seller: 'Seller',
|
|
|
|
|
'static-page': 'Page',
|
|
|
|
|
ai: 'AI',
|
|
|
|
|
},
|
2026-02-26 23:09:20 +04:00
|
|
|
addToCart: 'Add to cart',
|
|
|
|
|
loadingMore: 'Loading...',
|
|
|
|
|
allLoaded: 'All results loaded',
|
|
|
|
|
emptyState: 'Enter a query to search for products',
|
2026-07-05 01:10:56 +04:00
|
|
|
minLength: 'Enter at least {{count}} characters to search',
|
2026-02-26 23:09:20 +04:00
|
|
|
of: 'of',
|
|
|
|
|
},
|
|
|
|
|
category: {
|
|
|
|
|
retry: 'Try again',
|
|
|
|
|
addToCart: 'Add to cart',
|
|
|
|
|
loadingMore: 'Loading...',
|
|
|
|
|
allLoaded: 'All products loaded',
|
|
|
|
|
emptyTitle: 'Oops! Nothing here yet',
|
|
|
|
|
emptyDesc: 'There are no products in this category yet, but they will appear soon',
|
|
|
|
|
goHome: 'Go home',
|
|
|
|
|
loading: 'Loading products...',
|
|
|
|
|
},
|
2026-07-05 01:36:21 +04:00
|
|
|
catalog: {
|
|
|
|
|
title: 'Catalog',
|
|
|
|
|
breadcrumb: 'Catalog breadcrumb',
|
|
|
|
|
allCategories: 'All categories',
|
|
|
|
|
products: 'Products',
|
|
|
|
|
loading: 'Loading catalog...',
|
|
|
|
|
loadingProducts: 'Loading products...',
|
|
|
|
|
errorTitle: 'Catalog is unavailable',
|
|
|
|
|
error: 'Could not load catalog data. Try again later.',
|
|
|
|
|
retry: 'Try again',
|
|
|
|
|
categoryHint: 'Choose a category to continue',
|
|
|
|
|
productHint: 'Browse products in this category',
|
|
|
|
|
emptyTitle: 'Nothing here yet',
|
|
|
|
|
emptyCategories: 'There are no child categories in this section yet.',
|
|
|
|
|
emptyProducts: 'There are no products in this category yet.',
|
|
|
|
|
addToCart: 'Add to cart',
|
|
|
|
|
categoriesCount: '{{count}} categories',
|
|
|
|
|
productsCount: '{{count}} products',
|
2026-07-09 01:40:22 +04:00
|
|
|
entriesCount: '{{count}} entries',
|
|
|
|
|
categoryBannerPlaceholder: 'Category banner placeholder for backend-driven media and description.',
|
|
|
|
|
navigationPlaceholder: 'Navigation layout placeholder prepared for backend/bootstrap-driven rendering.',
|
|
|
|
|
removeSavedSearch: 'Remove saved search',
|
|
|
|
|
resetFilters: 'Reset',
|
|
|
|
|
filtersTitle: 'Filters',
|
2026-07-09 02:57:48 +04:00
|
|
|
from: 'From',
|
|
|
|
|
to: 'To',
|
2026-07-09 01:40:22 +04:00
|
|
|
minValue: 'Min {{value}}',
|
|
|
|
|
maxValue: 'Max {{value}}',
|
|
|
|
|
enabled: 'Enabled',
|
|
|
|
|
searchPlaceholder: 'Search products, brands, categories',
|
|
|
|
|
searchSubmit: 'Search',
|
2026-07-09 02:29:12 +04:00
|
|
|
clearSearch: 'Clear',
|
2026-07-09 01:40:22 +04:00
|
|
|
suggestionsTitle: 'Suggestions',
|
2026-07-09 02:29:12 +04:00
|
|
|
popularSearchesTitle: 'Popular searches',
|
2026-07-09 01:40:22 +04:00
|
|
|
recentSearchesTitle: 'Recent searches',
|
|
|
|
|
searchHistoryTitle: 'Search history',
|
|
|
|
|
clearHistory: 'Clear',
|
|
|
|
|
searchNoResultsHint: 'No results found for this query. Try broader keywords.',
|
2026-07-09 02:29:12 +04:00
|
|
|
searchProductsFound: '{{total}} products found',
|
|
|
|
|
searchResultsSummary: '{{total}} results for "{{query}}"',
|
2026-07-10 15:55:38 +04:00
|
|
|
loadMore: 'Load more',
|
2026-07-09 01:40:22 +04:00
|
|
|
sortBy: 'Sort by',
|
2026-07-09 01:46:32 +04:00
|
|
|
sortRelevance: 'Recommended',
|
2026-07-09 01:40:22 +04:00
|
|
|
sortLatest: 'Newest',
|
|
|
|
|
sortPriceAsc: 'Price Low -> High',
|
|
|
|
|
sortPriceDesc: 'Price High -> Low',
|
|
|
|
|
sortRating: 'Highest Rated',
|
|
|
|
|
sortPopular: 'Most Popular',
|
|
|
|
|
sortDiscount: 'Discount',
|
|
|
|
|
layoutGrid: 'Grid',
|
|
|
|
|
layoutLargeGrid: 'Large grid',
|
|
|
|
|
layoutCompactGrid: 'Compact grid',
|
2026-07-10 15:55:38 +04:00
|
|
|
layoutGrid2: '2 Columns',
|
|
|
|
|
layoutGrid3: '3 Columns',
|
|
|
|
|
layoutGrid4: '4 Columns',
|
|
|
|
|
layoutCompact: 'Compact',
|
2026-07-09 01:40:22 +04:00
|
|
|
layoutList: 'List',
|
|
|
|
|
noResults: 'No results',
|
|
|
|
|
itemsCount: '{{count}} items',
|
|
|
|
|
emptyResultsTitle: 'No results found',
|
|
|
|
|
emptyResultsDescription: 'Try changing filters, sorting, or search keywords.',
|
|
|
|
|
previousPage: 'Previous',
|
|
|
|
|
nextPage: 'Next',
|
|
|
|
|
pageOf: 'Page {{page}} / {{total}}',
|
2026-07-09 01:46:32 +04:00
|
|
|
emptyCategoryTitle: 'This category has no products yet',
|
|
|
|
|
emptyCategoryMessage: 'Try browsing parent categories or return to the full catalog.',
|
2026-07-10 15:55:38 +04:00
|
|
|
goToParentCategory: 'Go to parent category',
|
2026-07-09 01:46:32 +04:00
|
|
|
browseCategories: 'Browse Categories',
|
2026-07-10 15:55:38 +04:00
|
|
|
offlineTitle: 'You are offline',
|
|
|
|
|
offlineMessage: 'Reconnect to continue browsing this catalog.',
|
2026-07-09 01:46:32 +04:00
|
|
|
noFilterMatchTitle: 'No products match your selected filters.',
|
|
|
|
|
noFilterMatchMessage: 'Try broadening price, attributes, or brand filters.',
|
|
|
|
|
clearFilters: 'Clear Filters',
|
|
|
|
|
openFilters: 'Open Filters',
|
|
|
|
|
applyFilters: 'Apply',
|
|
|
|
|
close: 'Close',
|
|
|
|
|
filtersDrawerAria: 'Filters drawer',
|
|
|
|
|
sortSheetAria: 'Sort options',
|
|
|
|
|
mobileToolbarAria: 'Catalog mobile toolbar',
|
|
|
|
|
gridCycle: 'Grid',
|
|
|
|
|
filtersPanelAria: 'Catalog filters',
|
2026-07-09 01:40:22 +04:00
|
|
|
filterPrice: 'Price',
|
|
|
|
|
filterAvailability: 'Availability',
|
|
|
|
|
filterInStock: 'In stock',
|
|
|
|
|
filterLowStock: 'Low stock',
|
|
|
|
|
filterOutOfStock: 'Out of stock',
|
|
|
|
|
filterRating: 'Rating',
|
|
|
|
|
filterStars: '{{count}} stars',
|
|
|
|
|
filterBrand: 'Brand',
|
|
|
|
|
filterCategory: 'Category',
|
|
|
|
|
filterCategoryValue: 'Category {{value}}',
|
|
|
|
|
filterSubcategory: 'Subcategory',
|
|
|
|
|
filterDiscount: 'Discount',
|
|
|
|
|
filterNew: 'New',
|
|
|
|
|
filterColor: 'Color',
|
|
|
|
|
filterSize: 'Size',
|
|
|
|
|
filterAttributes: 'Attributes',
|
|
|
|
|
},
|
|
|
|
|
productDetails: {
|
|
|
|
|
loading: 'Loading product...',
|
|
|
|
|
errorTitle: 'Product unavailable',
|
|
|
|
|
retry: 'Try again',
|
|
|
|
|
missingTitle: 'Product not found',
|
|
|
|
|
missingDescription: 'This product does not exist or is no longer available.',
|
|
|
|
|
backToCatalog: 'Back to catalog',
|
|
|
|
|
tabsAria: 'Product tabs',
|
|
|
|
|
mediaAria: 'Product media',
|
|
|
|
|
ratingStarsAria: 'Rating stars',
|
|
|
|
|
selectRatingAria: 'Select rating',
|
|
|
|
|
reviewsEmpty: 'No reviews yet.',
|
|
|
|
|
questionsEmpty: 'No questions yet.',
|
|
|
|
|
askQuestion: 'Ask question',
|
|
|
|
|
hideQuestionForm: 'Hide question form',
|
|
|
|
|
questionFormTitle: 'Ask a question',
|
|
|
|
|
questionFormPlaceholder: 'Ask about size, delivery, compatibility or usage',
|
|
|
|
|
questionFormAnonymous: 'Ask anonymously',
|
|
|
|
|
submitQuestion: 'Submit question',
|
|
|
|
|
reviewFormTitle: 'Leave a review',
|
|
|
|
|
reviewRatingLabel: 'Rating',
|
|
|
|
|
reviewTitleLabel: 'Title',
|
|
|
|
|
reviewTitlePlaceholder: 'Short headline',
|
|
|
|
|
reviewTextLabel: 'Review',
|
|
|
|
|
reviewTextPlaceholder: 'Describe your experience',
|
|
|
|
|
reviewAnonymous: 'Submit anonymously',
|
|
|
|
|
reviewUploadPlaceholder: 'Photo upload is planned for a future sprint.',
|
2026-07-10 13:10:35 +04:00
|
|
|
reviewRatingRequired: 'Please select a rating.',
|
|
|
|
|
reviewTextRequired: 'Please add your review text.',
|
|
|
|
|
reviewSuccess: 'Review submitted successfully.',
|
2026-07-09 01:40:22 +04:00
|
|
|
submitReview: 'Submit review',
|
|
|
|
|
submitting: 'Submitting...',
|
2026-07-10 13:10:35 +04:00
|
|
|
loadMore: 'Load more',
|
|
|
|
|
showingOf: 'Showing {{shown}} of {{total}}',
|
|
|
|
|
questionsSubmissionFuture: 'Question submission can be enabled later without component changes.',
|
|
|
|
|
specificationsGeneral: 'General',
|
|
|
|
|
actionsAria: 'Product actions',
|
|
|
|
|
buyNow: 'Buy now',
|
|
|
|
|
wishlist: 'Wishlist',
|
|
|
|
|
compare: 'Compare',
|
|
|
|
|
share: 'Share',
|
|
|
|
|
notifyMe: 'Notify me',
|
|
|
|
|
zoom: 'Zoom',
|
|
|
|
|
fullscreen: 'Fullscreen',
|
|
|
|
|
pdfDocument: 'Product PDF document',
|
|
|
|
|
manualDocument: 'Product manual',
|
|
|
|
|
warrantyDocument: 'Warranty document',
|
|
|
|
|
openDocument: 'Open document',
|
2026-07-09 01:40:22 +04:00
|
|
|
specificationsEmpty: 'No specifications provided yet.',
|
|
|
|
|
warrantyTitle: 'Warranty and returns',
|
|
|
|
|
warrantyItem1: 'Warranty terms are provided by the seller and local law.',
|
|
|
|
|
warrantyItem2: 'Return eligibility depends on item condition and category.',
|
|
|
|
|
warrantyItem3: 'Detailed return instructions are available after purchase.',
|
|
|
|
|
verifiedPurchase: 'Verified purchase',
|
|
|
|
|
officialSeller: 'Official seller',
|
|
|
|
|
acceptedAnswer: 'Accepted answer',
|
|
|
|
|
like: 'Like',
|
|
|
|
|
dislike: 'Dislike',
|
|
|
|
|
reviewsCountLabel: '{{count}} reviews',
|
|
|
|
|
previous: 'Previous',
|
|
|
|
|
next: 'Next',
|
|
|
|
|
pageOf: 'Page {{page}} / {{total}}',
|
2026-07-05 01:36:21 +04:00
|
|
|
},
|
2026-02-26 23:09:20 +04:00
|
|
|
subcategories: {
|
|
|
|
|
loading: 'Loading subcategories...',
|
|
|
|
|
retry: 'Try again',
|
|
|
|
|
emptyTitle: 'Oops! No subcategories yet',
|
|
|
|
|
emptyDesc: 'There are no subcategories in this section yet, but they will appear soon',
|
|
|
|
|
goHome: 'Go home',
|
2026-03-24 00:09:11 +04:00
|
|
|
itemsInCategory: 'Items in this category',
|
2026-07-05 01:09:55 +04:00
|
|
|
childrenCount: '{{count}} categories',
|
|
|
|
|
productsCount: '{{count}} products',
|
|
|
|
|
includesProducts: 'Includes products',
|
2026-02-26 23:09:20 +04:00
|
|
|
},
|
|
|
|
|
itemDetail: {
|
|
|
|
|
loading: 'Loading...',
|
|
|
|
|
loadingDexar: 'Loading product...',
|
|
|
|
|
back: 'Go back',
|
|
|
|
|
backHome: 'Back to home',
|
|
|
|
|
noImage: 'No image',
|
|
|
|
|
stock: 'Availability:',
|
|
|
|
|
inStock: 'In stock',
|
|
|
|
|
lowStock: 'Few left',
|
|
|
|
|
lastItems: 'Last items',
|
|
|
|
|
mediumStock: 'Running low',
|
|
|
|
|
addToCart: 'Add to cart',
|
|
|
|
|
description: 'Description',
|
2026-02-28 16:13:14 +04:00
|
|
|
specifications: 'Specifications',
|
2026-02-26 23:09:20 +04:00
|
|
|
reviews: 'Reviews',
|
|
|
|
|
yourReview: 'Your review',
|
|
|
|
|
leaveReview: 'Leave a review',
|
|
|
|
|
rating: 'Rating:',
|
|
|
|
|
reviewPlaceholder: 'Share your experience with this product...',
|
|
|
|
|
reviewPlaceholderDexar: 'Share your thoughts on this product...',
|
|
|
|
|
anonymous: 'Anonymous',
|
|
|
|
|
submitting: 'Submitting...',
|
|
|
|
|
submit: 'Submit',
|
|
|
|
|
reviewSuccess: 'Thank you for your review!',
|
|
|
|
|
reviewError: 'Submission failed. Please try later.',
|
|
|
|
|
defaultUser: 'User',
|
|
|
|
|
defaultUserDexar: 'Anonymous',
|
|
|
|
|
noReviews: 'No reviews yet. Be the first!',
|
|
|
|
|
qna: 'Questions & Answers',
|
|
|
|
|
photo: 'Photo',
|
|
|
|
|
reviewsCount: 'reviews',
|
|
|
|
|
today: 'Today',
|
|
|
|
|
yesterday: 'Yesterday',
|
|
|
|
|
daysAgo: 'd. ago',
|
|
|
|
|
weeksAgo: 'w. ago',
|
2026-03-24 00:09:11 +04:00
|
|
|
colour: 'Colour',
|
|
|
|
|
size: 'Size',
|
2026-07-05 01:07:53 +04:00
|
|
|
relatedProducts: 'Related products',
|
2026-02-26 23:09:20 +04:00
|
|
|
},
|
|
|
|
|
app: {
|
|
|
|
|
connecting: 'Connecting to server...',
|
|
|
|
|
serverUnavailable: 'Server unavailable',
|
|
|
|
|
serverError: 'Could not connect to the server. Please check your internet connection.',
|
|
|
|
|
retryConnection: 'Retry',
|
|
|
|
|
pageTitle: 'Marketplace of goods and services',
|
|
|
|
|
},
|
2026-07-09 02:29:12 +04:00
|
|
|
platform: {
|
|
|
|
|
loadingPage: 'Loading platform page...',
|
|
|
|
|
emptyConfig: 'No page configuration found.',
|
|
|
|
|
},
|
|
|
|
|
backoffice: {
|
|
|
|
|
title: 'Backoffice Sandbox',
|
|
|
|
|
subtitle: 'Business data management domain (mocked for platform foundation).',
|
|
|
|
|
loading: 'Loading backoffice data...',
|
|
|
|
|
products: 'Products',
|
|
|
|
|
categories: 'Categories',
|
|
|
|
|
otherDomains: 'Other Domains',
|
|
|
|
|
sku: 'SKU',
|
|
|
|
|
titleColumn: 'Title',
|
|
|
|
|
price: 'Price',
|
|
|
|
|
status: 'Status',
|
|
|
|
|
itemsCount: 'items',
|
|
|
|
|
orders: 'Orders',
|
|
|
|
|
customers: 'Customers',
|
|
|
|
|
inventory: 'Inventory',
|
|
|
|
|
media: 'Media',
|
|
|
|
|
settings: 'Settings',
|
|
|
|
|
notAvailable: 'n/a',
|
|
|
|
|
},
|
|
|
|
|
builder: {
|
2026-07-10 13:43:53 +04:00
|
|
|
title: 'Marketplace Project Editor',
|
|
|
|
|
subtitle: 'Configuration-only bootstrap editing surface.',
|
|
|
|
|
general: 'General',
|
2026-07-09 02:29:12 +04:00
|
|
|
branding: 'Branding',
|
2026-07-10 13:43:53 +04:00
|
|
|
theme: 'Theme',
|
|
|
|
|
header: 'Header',
|
|
|
|
|
footer: 'Footer',
|
|
|
|
|
homepage: 'Homepage',
|
|
|
|
|
widgets: 'Widgets',
|
2026-07-10 13:52:01 +04:00
|
|
|
staticPages: 'Static Pages',
|
2026-07-10 13:43:53 +04:00
|
|
|
marketplaceFeatures: 'Marketplace Features',
|
|
|
|
|
preview: 'Preview',
|
2026-07-09 02:29:12 +04:00
|
|
|
brandName: 'Brand Name',
|
2026-07-10 13:43:53 +04:00
|
|
|
marketplaceName: 'Marketplace Name',
|
|
|
|
|
domain: 'Domain',
|
|
|
|
|
descriptionLabel: 'Description',
|
|
|
|
|
defaultLanguage: 'Default Language',
|
|
|
|
|
supportedLanguages: 'Supported Languages',
|
|
|
|
|
logo: 'Logo',
|
|
|
|
|
smallLogo: 'Small Logo',
|
|
|
|
|
favicon: 'Favicon',
|
|
|
|
|
marketplaceTitle: 'Marketplace Title',
|
|
|
|
|
primaryColor: 'Primary Color',
|
|
|
|
|
secondaryColor: 'Secondary Color',
|
|
|
|
|
backgroundColor: 'Background',
|
|
|
|
|
surfaceColor: 'Surface',
|
|
|
|
|
textColor: 'Text',
|
|
|
|
|
successColor: 'Success',
|
|
|
|
|
warningColor: 'Warning',
|
|
|
|
|
dangerColor: 'Danger',
|
|
|
|
|
showLogo: 'Logo',
|
|
|
|
|
showSearch: 'Search',
|
|
|
|
|
showCategories: 'Categories',
|
|
|
|
|
showLanguages: 'Languages',
|
|
|
|
|
showCart: 'Cart',
|
|
|
|
|
showProfile: 'Profile',
|
|
|
|
|
showWishlist: 'Wishlist',
|
|
|
|
|
showCompare: 'Compare',
|
|
|
|
|
showRegion: 'Region',
|
|
|
|
|
companyName: 'Company Information',
|
|
|
|
|
address: 'Address',
|
|
|
|
|
phone: 'Phone',
|
|
|
|
|
email: 'Email',
|
|
|
|
|
copyright: 'Copyright',
|
|
|
|
|
paymentIcons: 'Payment Icons',
|
|
|
|
|
socialLinks: 'Social Links',
|
2026-07-16 02:06:26 +04:00
|
|
|
footerLogo: 'Footer Logo',
|
|
|
|
|
addPaymentIcon: 'Add Payment Icon',
|
|
|
|
|
removePaymentIcon: 'Remove',
|
|
|
|
|
iconAltLabel: 'Icon Label',
|
|
|
|
|
addSocialLink: 'Add Social Link',
|
|
|
|
|
removeSocialLink: 'Remove',
|
|
|
|
|
socialLinkLabel: 'Platform',
|
|
|
|
|
socialLinkUrl: 'URL',
|
|
|
|
|
invalidUrl: 'Enter a valid URL starting with http:// or https://.',
|
2026-07-10 13:52:01 +04:00
|
|
|
createPage: 'Create Page',
|
|
|
|
|
deletePage: 'Delete Page',
|
|
|
|
|
pageId: 'Page ID',
|
|
|
|
|
slug: 'Slug',
|
|
|
|
|
iconLabel: 'Icon',
|
|
|
|
|
showInFooter: 'Show In Footer',
|
|
|
|
|
showInHeader: 'Show In Header',
|
|
|
|
|
showInSitemap: 'Show In Sitemap',
|
|
|
|
|
requiresAuthentication: 'Requires Authentication',
|
|
|
|
|
footerGroup: 'Footer Group',
|
|
|
|
|
orderLabel: 'Order',
|
|
|
|
|
translationTitle: 'Translation Title',
|
|
|
|
|
htmlPreview: 'HTML',
|
2026-07-15 04:11:18 +04:00
|
|
|
seoSection: 'SEO',
|
|
|
|
|
seoTitle: 'SEO Title',
|
|
|
|
|
seoDescription: 'SEO Description',
|
|
|
|
|
seoKeywords: 'Keywords',
|
|
|
|
|
seoCanonical: 'Canonical URL',
|
|
|
|
|
seoOgTitle: 'OG Title',
|
|
|
|
|
seoOgDescription: 'OG Description',
|
|
|
|
|
seoOgImage: 'OG Image',
|
2026-07-10 13:43:53 +04:00
|
|
|
visible: 'Visible',
|
|
|
|
|
layoutLabel: 'Layout',
|
|
|
|
|
columns: 'Columns',
|
|
|
|
|
height: 'Height',
|
|
|
|
|
overlay: 'Overlay',
|
|
|
|
|
autoplay: 'Autoplay',
|
|
|
|
|
cardsPerRow: 'Cards Per Row',
|
|
|
|
|
filtersLabel: 'Filters',
|
|
|
|
|
showBadges: 'Show Badges',
|
|
|
|
|
showRating: 'Show Rating',
|
|
|
|
|
showPrice: 'Show Price',
|
|
|
|
|
widgetJson: 'Widget JSON',
|
|
|
|
|
reviews: 'Reviews',
|
|
|
|
|
questions: 'Questions',
|
|
|
|
|
comments: 'Comments',
|
|
|
|
|
recommendations: 'Recommendations',
|
|
|
|
|
recentlyViewed: 'Recently Viewed',
|
|
|
|
|
searchSuggestions: 'Search Suggestions',
|
|
|
|
|
searchHistory: 'Search History',
|
|
|
|
|
livePreview: 'Live Preview',
|
2026-07-17 09:00:47 +04:00
|
|
|
previewChangesTitle: 'Changes since last publish',
|
|
|
|
|
previewNoIssues: 'No validation issues.',
|
|
|
|
|
previewNoChanges: 'No changes since the last publish.',
|
|
|
|
|
previewChangeField: 'Field',
|
|
|
|
|
previewChangeBefore: 'Before',
|
|
|
|
|
previewChangeAfter: 'After',
|
2026-07-13 08:10:00 +04:00
|
|
|
languagesTab: 'Languages',
|
|
|
|
|
addLanguage: 'Add Language',
|
|
|
|
|
removeLanguage: 'Remove',
|
|
|
|
|
setDefaultLanguage: 'Set as Default',
|
|
|
|
|
defaultLanguageLabel: 'Default',
|
2026-07-13 08:57:42 +04:00
|
|
|
navigationTab: 'Navigation',
|
|
|
|
|
navigationHeader: 'Header Navigation',
|
|
|
|
|
navigationFooter: 'Footer Navigation',
|
|
|
|
|
navigationFooterGrouped: 'Footer navigation uses grouped columns and is not editable here yet — edit via the Footer tab.',
|
|
|
|
|
addLink: 'Add Link',
|
|
|
|
|
removeLink: 'Remove',
|
|
|
|
|
linkLabel: 'Label',
|
|
|
|
|
linkUrl: 'URL',
|
2026-07-10 13:43:53 +04:00
|
|
|
exportBootstrap: 'Export Bootstrap',
|
|
|
|
|
importBootstrap: 'Import Bootstrap',
|
|
|
|
|
exportedBootstrap: 'Exported Bootstrap',
|
|
|
|
|
importSource: 'Import Source',
|
|
|
|
|
importError: 'Import failed. Invalid bootstrap JSON.',
|
2026-07-09 02:29:12 +04:00
|
|
|
featureFlags: 'Feature Flags',
|
2026-07-13 10:51:05 +04:00
|
|
|
validationMissingLogo: 'Branding is missing a logo.',
|
|
|
|
|
validationNoLanguages: 'No languages are configured.',
|
|
|
|
|
validationInvalidUrl: 'The marketplace URL is invalid.',
|
|
|
|
|
validationDuplicateSlugs: 'Two or more static pages share the same slug.',
|
|
|
|
|
validationEmptyHomepage: 'The homepage has no sections.',
|
|
|
|
|
validationMissingWidget: 'A homepage section has a widget with no type.',
|
|
|
|
|
validationDuplicateNavLinks: 'Two or more header navigation links are duplicates.',
|
|
|
|
|
validationInvalidColors: 'One or more theme colors are not valid hex colors.',
|
2026-07-14 12:21:33 +04:00
|
|
|
validationMissingTranslations: 'Some content is missing translations for a supported language.',
|
|
|
|
|
validationInvalidLayouts: 'One or more layouts reference an unknown layout type.',
|
feat(project-editor): centralize schema-driven validation
Milestone 2 of the Configuration Engine sprint.
- Add schema/validators/primitives: pure isValidHexColor/HttpUrl/Email,
validateJson, validateCss, extractStyleBlocks, normalizeRoute. One function
per concern, no duplicated validator logic.
- Refactor ProjectValidator to compose the primitives and tag every issue with
section + fieldKey + severity ('error' blocks publish, 'warning' advisory).
Preserves all existing codes/messages; adds duplicate-routes, invalid-css,
invalid-widget-config checks.
- Facade: issuesByField, issuesBySection, blockingIssues, hasBlockingIssues;
publish() now gates on severity==='error' instead of any issue.
- i18n: add validationInvalidJson/Css/DuplicateRoutes/InvalidWidgetConfig to
the Translations interface + en/ru/hy.
- Specs: primitives + ProjectValidator (25 passing total).
Gate: tsc --noEmit, npm test (25/25), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:58:36 +04:00
|
|
|
validationInvalidJson: 'The configuration is not valid JSON.',
|
|
|
|
|
validationInvalidCss: 'A static page contains invalid CSS.',
|
|
|
|
|
validationDuplicateRoutes: 'Two or more pages share the same route.',
|
|
|
|
|
validationInvalidWidgetConfig: 'A widget is missing a required field (id, type, version, or props).',
|
2026-07-17 02:13:34 +04:00
|
|
|
undo: 'Undo',
|
|
|
|
|
redo: 'Redo',
|
2026-07-13 15:00:03 +04:00
|
|
|
statusDraft: 'Draft',
|
|
|
|
|
statusPublished: 'Published',
|
|
|
|
|
unsavedChanges: 'Unsaved changes',
|
|
|
|
|
save: 'Save',
|
|
|
|
|
publish: 'Publish',
|
2026-07-13 15:57:54 +04:00
|
|
|
confirmLeaveUnsaved: 'You have unsaved changes. Leave anyway?',
|
|
|
|
|
promptLinkUrl: 'URL',
|
|
|
|
|
promptImageUrl: 'Image URL',
|
|
|
|
|
htmlEditorCode: 'Code',
|
|
|
|
|
htmlEditorPreview: 'Preview',
|
2026-07-14 09:50:03 +04:00
|
|
|
lastSaved: 'Last saved',
|
|
|
|
|
draftRestored: 'A local draft was restored from your last session.',
|
|
|
|
|
dismiss: 'Dismiss',
|
|
|
|
|
resetDraft: 'Reset draft',
|
|
|
|
|
resetSection: 'Reset section',
|
|
|
|
|
confirmResetDraft: 'This discards all unpublished changes and restores the last published configuration. Continue?',
|
|
|
|
|
confirmResetSection: 'This discards unpublished changes in this section only. Continue?',
|
2026-07-14 16:05:09 +04:00
|
|
|
marketplaceNameDesc: 'The name shown to shoppers across the storefront and browser tab.',
|
|
|
|
|
domainDesc: 'The domain shoppers use to reach this marketplace. Changing it does not move DNS.',
|
|
|
|
|
descriptionFieldDesc: 'A short summary used for search engines and social link previews.',
|
|
|
|
|
defaultLanguageDesc: 'The language shown when a shopper has no saved language preference.',
|
|
|
|
|
supportedLanguagesDesc: 'Comma-separated language codes shoppers can switch to (e.g. en, ru, hy).',
|
|
|
|
|
logoDesc: 'Full logo image URL, shown in the header on desktop.',
|
|
|
|
|
smallLogoDesc: 'Compact logo image URL, used where space is limited (e.g. mobile header).',
|
|
|
|
|
faviconDesc: 'Browser tab icon image URL. Should be a small square image.',
|
|
|
|
|
marketplaceTitleDesc: 'The page title used by search engines and browser tabs.',
|
|
|
|
|
primaryColorDesc: 'The main brand color, used on primary buttons and highlights. Must be a valid hex color.',
|
|
|
|
|
secondaryColorDesc: 'A supporting brand color used for secondary UI elements. Must be a valid hex color.',
|
|
|
|
|
backgroundColorDesc: 'The default page background color. Must be a valid hex color.',
|
|
|
|
|
surfaceColorDesc: 'The background color for cards and raised surfaces. Must be a valid hex color.',
|
|
|
|
|
textColorDesc: 'The default body text color. Must be a valid hex color.',
|
|
|
|
|
successColorDesc: 'Color used for success messages and positive states. Must be a valid hex color.',
|
|
|
|
|
warningColorDesc: 'Color used for warning messages and caution states. Must be a valid hex color.',
|
|
|
|
|
dangerColorDesc: 'Color used for errors and destructive actions. Must be a valid hex color.',
|
|
|
|
|
themeModeLabel: 'Color mode',
|
|
|
|
|
themeModeDesc: 'Controls whether the storefront renders in light, dark, or the shopper\'s system preference.',
|
|
|
|
|
themeModeLight: 'Light',
|
|
|
|
|
themeModeLightDesc: 'Always use the light color scheme.',
|
|
|
|
|
themeModeDark: 'Dark',
|
|
|
|
|
themeModeDarkDesc: 'Always use the dark color scheme.',
|
|
|
|
|
themeModeSystem: 'System',
|
|
|
|
|
themeModeSystemDesc: 'Follow the shopper\'s device/browser color scheme preference.',
|
|
|
|
|
siteLayoutLabel: 'Site layout',
|
|
|
|
|
siteLayoutDesc: 'The overall page chrome mode used across the storefront.',
|
|
|
|
|
siteLayoutDefault: 'Default',
|
|
|
|
|
siteLayoutDefaultDesc: 'Standard layout with a top navigation bar.',
|
|
|
|
|
siteLayoutSidebarLeft: 'Sidebar (left)',
|
|
|
|
|
siteLayoutSidebarLeftDesc: 'Adds a left-hand sidebar, typically for category navigation.',
|
|
|
|
|
siteLayoutCarouselHome: 'Carousel home',
|
|
|
|
|
siteLayoutCarouselHomeDesc: 'Homepage leads with a full-width carousel/hero.',
|
|
|
|
|
siteLayoutMinimal: 'Minimal',
|
|
|
|
|
siteLayoutMinimalDesc: 'Stripped-down chrome with minimal navigation elements.',
|
|
|
|
|
showLogoDesc: 'Show the marketplace logo in the header.',
|
|
|
|
|
showSearchDesc: 'Show the search bar in the header.',
|
|
|
|
|
showCategoriesDesc: 'Show the categories menu entry point in the header.',
|
|
|
|
|
showLanguagesDesc: 'Show the language switcher in the header.',
|
|
|
|
|
showCartDesc: 'Show the cart icon/link in the header.',
|
|
|
|
|
showProfileDesc: 'Show the account/profile entry point in the header.',
|
|
|
|
|
showWishlistDesc: 'Show the wishlist entry point in the header.',
|
|
|
|
|
showCompareDesc: 'Show the compare-products entry point in the header.',
|
|
|
|
|
showRegionDesc: 'Show the region/country selector in the header.',
|
|
|
|
|
companyNameDesc: 'Legal company name shown in the footer and static pages.',
|
|
|
|
|
addressDesc: 'Company street address shown in the footer.',
|
|
|
|
|
phoneDesc: 'Contact phone number shown in the footer.',
|
|
|
|
|
emailDesc: 'Contact email address shown in the footer.',
|
|
|
|
|
copyrightDesc: 'Copyright line shown at the bottom of every page.',
|
|
|
|
|
paymentIconsDesc: 'One payment icon entry per line (image URL and label) shown in the footer.',
|
|
|
|
|
socialLinksDesc: 'One social link entry per line (platform and URL) shown in the footer.',
|
2026-07-16 02:06:26 +04:00
|
|
|
footerLogoDesc: 'Logo shown in the footer, if different from the header logo.',
|
|
|
|
|
iconAltLabelDesc: 'Alt text describing the payment icon for accessibility.',
|
|
|
|
|
socialLinkUrlDesc: 'Full URL to the social media profile, must start with http:// or https://.',
|
2026-07-14 16:05:09 +04:00
|
|
|
staticPagesFieldDesc: 'Static pages (about, terms, privacy, etc.) linked from the footer.',
|
|
|
|
|
sectionVisibleDesc: 'Whether this homepage section is shown to shoppers.',
|
|
|
|
|
layoutStrategyLabel: 'Layout strategy',
|
|
|
|
|
layoutStrategyDesc: 'How widgets inside this section are arranged.',
|
|
|
|
|
layoutStrategyStack: 'Stack',
|
|
|
|
|
layoutStrategyStackDesc: 'Widgets stack vertically, one after another.',
|
|
|
|
|
layoutStrategyGrid: 'Grid',
|
|
|
|
|
layoutStrategyGridDesc: 'Widgets arrange in a multi-column grid.',
|
|
|
|
|
layoutStrategyHero: 'Hero',
|
|
|
|
|
layoutStrategyHeroDesc: 'A single large, full-width promotional layout.',
|
|
|
|
|
layoutStrategyCarousel: 'Carousel',
|
|
|
|
|
layoutStrategyCarouselDesc: 'Widgets scroll horizontally as a carousel.',
|
|
|
|
|
layoutStrategySplit: 'Split',
|
|
|
|
|
layoutStrategySplitDesc: 'Content is split into two side-by-side panes.',
|
|
|
|
|
sectionColumnsDesc: 'Number of columns to use when the layout strategy supports columns.',
|
|
|
|
|
widgetLayoutDesc: 'Free-text layout variant name understood by this widget (e.g. "full-bleed", "boxed").',
|
|
|
|
|
widgetHeightDesc: 'Preferred height for this widget (e.g. "480px" or "60vh").',
|
|
|
|
|
widgetOverlayDesc: 'Show a dark overlay behind hero text for readability.',
|
|
|
|
|
widgetAutoplayDesc: 'Automatically rotate hero slides without shopper interaction.',
|
|
|
|
|
widgetColumnsDesc: 'Number of category tiles per row.',
|
|
|
|
|
widgetCardsPerRowDesc: 'Number of product cards shown per row.',
|
|
|
|
|
widgetFiltersDesc: 'Show filter controls alongside the product collection.',
|
|
|
|
|
widgetShowBadgesDesc: 'Show badges such as "New" or "Sale" on product cards.',
|
|
|
|
|
widgetShowRatingDesc: 'Show the star rating on product cards.',
|
|
|
|
|
widgetShowPriceDesc: 'Show the price on product cards.',
|
|
|
|
|
widgetJsonDesc: 'Raw widget properties as JSON. Used when no dedicated editor exists for this widget type.',
|
|
|
|
|
wishlistFeatureDesc: 'Let shoppers save products to a wishlist.',
|
|
|
|
|
compareFeatureDesc: 'Let shoppers compare multiple products side by side.',
|
|
|
|
|
reviewsFeatureDesc: 'Let shoppers leave and read product reviews.',
|
|
|
|
|
questionsFeatureDesc: 'Let shoppers ask and read product questions and answers.',
|
|
|
|
|
commentsFeatureDesc: 'Enable general comments on marketplace content.',
|
|
|
|
|
recommendationsFeatureDesc: 'Show recommended-product blocks across the storefront.',
|
|
|
|
|
recentlyViewedFeatureDesc: 'Show a "recently viewed" product list to shoppers.',
|
|
|
|
|
searchSuggestionsDesc: 'Show live search suggestions as shoppers type.',
|
|
|
|
|
searchHistoryDesc: 'Remember and show a shopper\'s recent searches.',
|
|
|
|
|
catalogNavigationModeLabel: 'Catalog navigation mode',
|
|
|
|
|
catalogNavigationModeDesc: 'How shoppers navigate categories in the catalog.',
|
|
|
|
|
catalogNavDefault: 'Default',
|
|
|
|
|
catalogNavDefaultDesc: 'Standard breadcrumb-driven category navigation.',
|
|
|
|
|
catalogNavLeftCategory: 'Left category navigation',
|
|
|
|
|
catalogNavLeftCategoryDesc: 'Persistent category tree in a left-hand sidebar.',
|
|
|
|
|
catalogNavMegaCategory: 'Mega category layout',
|
|
|
|
|
catalogNavMegaCategoryDesc: 'Expanded dropdown showing subcategories and featured items.',
|
|
|
|
|
catalogNavTopCarousel: 'Top category carousel',
|
|
|
|
|
catalogNavTopCarouselDesc: 'Horizontally scrollable category chips above the catalog grid.',
|
|
|
|
|
newLanguageDesc: 'ISO language code to add as a supported storefront language (e.g. de, fr).',
|
|
|
|
|
languagesTabDesc: 'Languages available to shoppers. The default language is used as a fallback everywhere.',
|
|
|
|
|
linkLabelDesc: 'Text shown for this navigation link.',
|
|
|
|
|
linkUrlDesc: 'Path or URL this navigation link points to.',
|
|
|
|
|
linkVisibleDesc: 'Whether this navigation link is shown to shoppers.',
|
2026-07-09 02:29:12 +04:00
|
|
|
},
|
2026-07-14 12:21:33 +04:00
|
|
|
dashboard: {
|
|
|
|
|
title: 'Dashboard',
|
|
|
|
|
subtitle: 'Overview of your marketplace',
|
|
|
|
|
cardMarketplaceStatus: 'Marketplace Status',
|
|
|
|
|
cardProjectName: 'Project Name',
|
|
|
|
|
cardCurrentTheme: 'Current Theme',
|
|
|
|
|
cardLanguages: 'Languages',
|
|
|
|
|
cardCategoriesCount: 'Categories',
|
|
|
|
|
cardProductsCount: 'Products',
|
|
|
|
|
cardOrders: 'Orders',
|
|
|
|
|
cardRevenue: 'Revenue',
|
|
|
|
|
cardLastPublish: 'Last Publish',
|
|
|
|
|
cardLastDraftSave: 'Last Draft Save',
|
|
|
|
|
cardBootstrapVersion: 'Bootstrap Version',
|
|
|
|
|
cardActiveLayout: 'Active Layout',
|
|
|
|
|
cardEnabledWidgets: 'Enabled Widgets',
|
|
|
|
|
stateLoading: 'Loading...',
|
|
|
|
|
stateEmpty: 'No data yet',
|
|
|
|
|
stateError: 'Could not load this card',
|
|
|
|
|
statePendingBackend: 'Awaiting backend integration',
|
|
|
|
|
never: 'Never',
|
|
|
|
|
quickActionsTitle: 'Quick Actions',
|
|
|
|
|
actionEditProject: 'Edit Project',
|
|
|
|
|
actionCategories: 'Categories',
|
|
|
|
|
actionProducts: 'Products',
|
|
|
|
|
actionStaticPages: 'Static Pages',
|
|
|
|
|
actionTransactions: 'Transactions',
|
|
|
|
|
actionOrders: 'Orders',
|
|
|
|
|
actionMediaLibrary: 'Media Library',
|
2026-07-15 20:25:12 +04:00
|
|
|
actionUsers: 'Users & Permissions',
|
|
|
|
|
actionMonitoring: 'Monitoring',
|
|
|
|
|
actionAnalytics: 'Analytics',
|
2026-07-14 12:21:33 +04:00
|
|
|
actionPreviewMarketplace: 'Preview Marketplace',
|
|
|
|
|
activityTitle: 'Recent Activity',
|
|
|
|
|
activityEmpty: 'No activity yet. Save or publish a change to see it here.',
|
|
|
|
|
activityDraftSaved: 'Draft saved',
|
|
|
|
|
activityPublished: 'Published',
|
|
|
|
|
healthTitle: 'System Health',
|
|
|
|
|
healthAllClear: 'All checks passed',
|
|
|
|
|
healthBootstrapValid: 'Bootstrap valid',
|
|
|
|
|
healthConfigurationValid: 'Configuration valid',
|
|
|
|
|
healthMissingTranslations: 'Missing translations',
|
|
|
|
|
healthInvalidColors: 'Invalid colors',
|
|
|
|
|
healthInvalidWidgetReferences: 'Invalid widget references',
|
|
|
|
|
healthInvalidLayouts: 'Invalid layouts',
|
|
|
|
|
comingSoonTitle: 'Coming soon',
|
|
|
|
|
comingSoonDescription: 'This section is not built yet. Check back in a future update.',
|
|
|
|
|
backToDashboard: 'Back to dashboard',
|
|
|
|
|
},
|
2026-07-10 13:52:01 +04:00
|
|
|
staticPages: {
|
|
|
|
|
notFound: 'Page not found',
|
|
|
|
|
backHome: 'Back to home',
|
|
|
|
|
duplicateSlug: 'Duplicate slugs are not allowed.',
|
|
|
|
|
emptyTitle: 'Each page must have at least one title.',
|
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: 'Two or more pages share the same route.',
|
|
|
|
|
invalidHtml: 'This page has malformed HTML content.',
|
|
|
|
|
invalidSeo: 'This page has an invalid SEO field (canonical/OG image URL or robots value).',
|
|
|
|
|
searchPlaceholder: 'Search pages…',
|
|
|
|
|
filterAll: 'All statuses',
|
|
|
|
|
filterAllLocales: 'All languages',
|
|
|
|
|
confirmDeletePage: 'Delete this page? This cannot be undone.',
|
|
|
|
|
confirmBulkDelete: 'Delete the selected pages? This cannot be undone.',
|
|
|
|
|
duplicatePage: 'Duplicate',
|
|
|
|
|
selectAll: 'Select all',
|
|
|
|
|
selectedCount: 'selected',
|
|
|
|
|
bulkDelete: 'Delete selected',
|
|
|
|
|
bulkEnable: 'Enable selected',
|
|
|
|
|
bulkDisable: 'Disable selected',
|
|
|
|
|
bulkPublishAction: 'Publish selected',
|
|
|
|
|
bulkUnpublish: 'Unpublish selected',
|
|
|
|
|
routeLabel: 'Route',
|
|
|
|
|
enabledLabel: 'Enabled',
|
|
|
|
|
statusLabel: 'Status',
|
|
|
|
|
publishPageAction: 'Publish page',
|
|
|
|
|
unpublishPageAction: 'Unpublish page',
|
|
|
|
|
heroImageLabel: 'Hero image',
|
|
|
|
|
thumbnailLabel: 'Thumbnail',
|
|
|
|
|
galleryLabel: 'Gallery',
|
|
|
|
|
customTemplateLabel: 'Custom template',
|
|
|
|
|
seoRobotsLabel: 'Robots',
|
|
|
|
|
disabledBadge: 'Disabled',
|
2026-07-10 13:52:01 +04:00
|
|
|
},
|
2026-07-09 02:29:12 +04:00
|
|
|
widgets: {
|
|
|
|
|
unavailable: 'Widget unavailable',
|
|
|
|
|
},
|
2026-02-26 23:09:20 +04:00
|
|
|
carousel: {
|
|
|
|
|
loading: 'Loading products...',
|
|
|
|
|
addToCart: 'Add to cart',
|
|
|
|
|
},
|
|
|
|
|
common: {
|
|
|
|
|
retry: 'Try again',
|
|
|
|
|
loading: 'Loading...',
|
|
|
|
|
},
|
2026-02-28 17:18:24 +04:00
|
|
|
location: {
|
|
|
|
|
allRegions: 'All regions',
|
|
|
|
|
chooseRegion: 'Choose region',
|
|
|
|
|
detectAuto: 'Detect automatically',
|
|
|
|
|
},
|
|
|
|
|
auth: {
|
|
|
|
|
loginRequired: 'Login required',
|
|
|
|
|
loginDescription: 'Please log in via Telegram to proceed with your order',
|
|
|
|
|
checking: 'Checking...',
|
|
|
|
|
loginWithTelegram: 'Log in with Telegram',
|
|
|
|
|
orScanQr: 'Or scan the QR code',
|
|
|
|
|
loginNote: 'You will be redirected back after login',
|
2026-03-25 15:32:50 +04:00
|
|
|
qrExpired: 'QR code expired. Click to refresh',
|
2026-06-01 00:47:26 +04:00
|
|
|
qrError: 'Could not create login session. Click to retry',
|
2026-02-28 17:18:24 +04:00
|
|
|
},
|
2026-07-09 01:13:54 +04:00
|
|
|
ux: {
|
|
|
|
|
items: 'items',
|
|
|
|
|
wishlistTitle: 'Wishlist',
|
|
|
|
|
wishlistEmptyTitle: 'Your wishlist is empty',
|
|
|
|
|
wishlistEmptyDescription: 'Save products to revisit them anytime.',
|
|
|
|
|
clearWishlist: 'Clear wishlist',
|
2026-07-09 02:29:12 +04:00
|
|
|
wishlistCleared: 'Wishlist cleared',
|
2026-07-09 01:13:54 +04:00
|
|
|
compareTitle: 'Compare Products',
|
|
|
|
|
compareEmptyTitle: 'No products to compare',
|
|
|
|
|
compareEmptyDescription: 'Add products from catalog cards to compare specs and prices.',
|
|
|
|
|
clearCompare: 'Clear compare',
|
|
|
|
|
hideIdentical: 'Hide identical values',
|
|
|
|
|
highlightDifferences: 'Highlight differences',
|
|
|
|
|
clearRecentlyViewed: 'Clear recently viewed',
|
|
|
|
|
recentlyViewedEmpty: 'No recently viewed products yet.',
|
|
|
|
|
saveSearch: 'Save search',
|
|
|
|
|
goToCatalog: 'Go to catalog',
|
2026-07-09 01:40:22 +04:00
|
|
|
wishlistAdded: 'Added to wishlist',
|
|
|
|
|
wishlistRemoved: 'Removed from wishlist',
|
|
|
|
|
compareAdded: 'Added to compare',
|
|
|
|
|
compareRemoved: 'Removed from compare',
|
|
|
|
|
compareLimitReached: 'Compare limit reached ({{maxItems}})',
|
|
|
|
|
productShared: 'Product shared',
|
|
|
|
|
productLinkCopied: 'Product link copied',
|
|
|
|
|
sharingUnsupported: 'Sharing is not supported on this device',
|
2026-07-09 02:29:12 +04:00
|
|
|
addedToCart: 'Added to cart',
|
2026-07-09 01:40:22 +04:00
|
|
|
enterQueryBeforeSave: 'Enter a search query before saving',
|
|
|
|
|
savedSearchNamed: 'Saved search: {{name}}',
|
2026-07-09 02:29:12 +04:00
|
|
|
recentlyViewedTitle: 'Recently Viewed',
|
|
|
|
|
compareAttribute: 'Attribute',
|
|
|
|
|
comparePrice: 'Price',
|
|
|
|
|
compareRating: 'Rating',
|
|
|
|
|
compareStock: 'Stock',
|
|
|
|
|
compareDiscount: 'Discount',
|
|
|
|
|
compareColor: 'Color',
|
|
|
|
|
compareSize: 'Size',
|
|
|
|
|
compareUnknown: 'unknown',
|
2026-07-09 01:13:54 +04:00
|
|
|
},
|
2026-07-10 13:34:25 +04:00
|
|
|
diagnostics: {
|
|
|
|
|
title: 'Marketplace Diagnostics',
|
|
|
|
|
disabledTitle: 'Diagnostics unavailable',
|
|
|
|
|
disabledDescription: 'Diagnostics page is available only in development mode.',
|
|
|
|
|
generatedAt: 'Generated at {{value}}',
|
|
|
|
|
healthScore: 'Health score',
|
|
|
|
|
summary: 'Summary',
|
|
|
|
|
passedChecks: '{{count}} passed checks',
|
|
|
|
|
warnings: '{{count}} warnings',
|
|
|
|
|
errors: '{{count}} errors',
|
|
|
|
|
critical: '{{count}} critical',
|
|
|
|
|
healthChecks: 'Health checks',
|
|
|
|
|
results: 'Detailed results',
|
|
|
|
|
noIssues: 'No diagnostics issues found.',
|
|
|
|
|
ok: 'OK',
|
|
|
|
|
failed: 'Failed',
|
|
|
|
|
affectedComponent: 'Affected component',
|
|
|
|
|
suggestedResolution: 'Suggested resolution',
|
|
|
|
|
severity: {
|
|
|
|
|
info: 'Info',
|
|
|
|
|
warning: 'Warning',
|
|
|
|
|
error: 'Error',
|
|
|
|
|
critical: 'Critical',
|
|
|
|
|
},
|
|
|
|
|
checks: {
|
|
|
|
|
bootstrapLoaded: 'Bootstrap loaded',
|
|
|
|
|
tenantResolved: 'Tenant resolved',
|
|
|
|
|
runtimeInitialized: 'Runtime initialized',
|
|
|
|
|
themeLoaded: 'Theme loaded',
|
|
|
|
|
widgetManifestLoaded: 'Widget manifest loaded',
|
|
|
|
|
sectionEngineInitialized: 'Section engine reachable',
|
|
|
|
|
configurationEngineInitialized: 'Configuration engine initialized',
|
|
|
|
|
translationResourcesAvailable: 'Translations available',
|
|
|
|
|
requiredAssetsAvailable: 'Required assets healthy',
|
|
|
|
|
},
|
|
|
|
|
bootstrapMissingTitle: 'Bootstrap missing',
|
|
|
|
|
bootstrapMissingDescription: 'Bootstrap configuration could not be loaded.',
|
|
|
|
|
bootstrapMissingResolution: 'Verify bootstrap provider response and local mock availability.',
|
|
|
|
|
requiredPropertyMissingTitle: 'Required property missing',
|
|
|
|
|
requiredPropertyMissingDescription: 'Required bootstrap property is missing.',
|
|
|
|
|
requiredPropertyMissingResolution: 'Add missing property to bootstrap payload.',
|
|
|
|
|
unknownWidgetTypeTitle: 'Unknown widget type',
|
|
|
|
|
unknownWidgetTypeDescription: 'Widget type is not present in widget manifest.',
|
|
|
|
|
unknownWidgetTypeResolution: 'Register widget in manifest or remove invalid widget config.',
|
|
|
|
|
duplicateIdTitle: 'Duplicate identifier',
|
|
|
|
|
duplicatePageIdDescription: 'Duplicate page id detected.',
|
|
|
|
|
duplicateSectionIdDescription: 'Duplicate section id detected.',
|
|
|
|
|
duplicateWidgetIdDescription: 'Duplicate widget id detected.',
|
|
|
|
|
duplicateIdResolution: 'Ensure ids are unique within bootstrap config.',
|
|
|
|
|
unknownLayoutTitle: 'Unknown layout',
|
|
|
|
|
unknownLayoutDescription: 'Page layout value is not recognized.',
|
|
|
|
|
unknownLayoutResolution: 'Use supported layout or extend diagnostics allow-list.',
|
|
|
|
|
invalidFeatureFlagTitle: 'Invalid feature flag',
|
|
|
|
|
invalidFeatureFlagDescription: 'Feature flag value must be boolean.',
|
|
|
|
|
invalidFeatureFlagResolution: 'Normalize feature flag values to true or false.',
|
|
|
|
|
brokenPageDefinitionTitle: 'Broken page definition',
|
|
|
|
|
brokenPageDefinitionDescription: 'Page definition is incomplete.',
|
|
|
|
|
brokenPageDefinitionResolution: 'Add missing route, title, key, or sections.',
|
|
|
|
|
invalidNavigationTargetTitle: 'Invalid navigation target',
|
|
|
|
|
invalidNavigationTargetDescription: 'Navigation route does not match known routes.',
|
|
|
|
|
invalidNavigationTargetResolution: 'Update route target or add matching route surface.',
|
|
|
|
|
missingTranslationTitle: 'Missing translation',
|
|
|
|
|
missingTranslationDescription: 'Translation key resolves to itself.',
|
|
|
|
|
missingTranslationResolution: 'Add missing key to translation resources.',
|
|
|
|
|
missingMediaTitle: 'Missing media',
|
|
|
|
|
missingMediaDescription: 'Expected branding media is missing.',
|
|
|
|
|
missingMediaResolution: 'Provide valid media URLs in bootstrap branding config.',
|
|
|
|
|
runtimeNotInitializedTitle: 'Runtime not initialized',
|
|
|
|
|
runtimeNotInitializedDescription: 'Platform runtime state has not been marked initialized.',
|
|
|
|
|
runtimeNotInitializedResolution: 'Verify PlatformRuntimeService initialize sequence.',
|
|
|
|
|
configFallbackUsageTitle: 'Configuration fallback in use',
|
|
|
|
|
configFallbackUsageDescription: 'Development runtime is using fallback or mock configuration.',
|
|
|
|
|
configFallbackUsageResolution: 'Switch provider mode if real bootstrap validation is needed.',
|
|
|
|
|
missingDatasourceTitle: 'Missing datasource',
|
|
|
|
|
missingDatasourceDescription: 'Widget supports datasource selection but none was declared.',
|
|
|
|
|
missingDatasourceResolution: 'Define source in widget props or manifest default settings.',
|
|
|
|
|
widgetRenderFailureTitle: 'Widget render failure',
|
|
|
|
|
widgetRenderFailureDescription: 'Widget fell back to unknown widget renderer.',
|
|
|
|
|
widgetRenderFailureResolution: 'Register widget component or fix manifest component key.',
|
|
|
|
|
brokenRouteTitle: 'Broken route expectation',
|
|
|
|
|
brokenRouteDescription: 'Expected route surface is missing.',
|
|
|
|
|
brokenRouteResolution: 'Restore route registration or update diagnostics expectation.',
|
|
|
|
|
imageLoadFailedTitle: 'Image load failed',
|
|
|
|
|
imageLoadFailedDescription: 'Rendered image failed to load.',
|
|
|
|
|
imageLoadFailedResolution: 'Verify asset path, CORS, and media availability.',
|
|
|
|
|
optionalDataMissingTitle: 'Optional data missing',
|
|
|
|
|
optionalDataMissingDescription: 'Optional configuration data is absent.',
|
|
|
|
|
optionalDataMissingResolution: 'Add optional value if UX depends on it.',
|
|
|
|
|
},
|
feat(media): add Media Manager UI (grid, upload, delete)
MediaLibraryPageComponent replaces the coming-soon placeholder at
/backoffice/media. Built entirely on Sprint 6 Design System primitives
(app-card, app-button, app-input, app-empty-state, app-dialog, app-pagination,
app-skeleton) and Sprint 4 Task 2's MediaRepository/MockMediaRepository.
- Grid view with per-tile filename/size and delete action
- Hidden native file input triggered by an app-button, uploads via
MediaLibraryFacade -> MediaRepository.upload()
- Delete requires confirmation through app-dialog (destructive action)
- Search + pagination wired to MockMediaRepository's list() params
- Loading state shows app-skeleton tiles; empty state shows app-empty-state
- New mediaLibrary.* translation namespace across en/ru/hy
Verified in browser (via a temporary unguarded route, reverted before
commit - /backoffice/media itself requires Telegram QR admin auth not
available in this session): empty state renders correctly with translated
copy, search input and upload button present, no console errors.
2026-07-15 07:14:16 +04:00
|
|
|
mediaLibrary: {
|
|
|
|
|
title: 'Media Library',
|
|
|
|
|
upload: 'Upload',
|
|
|
|
|
uploading: 'Uploading...',
|
|
|
|
|
searchPlaceholder: 'Search files...',
|
|
|
|
|
delete: 'Delete',
|
|
|
|
|
deleteConfirmTitle: 'Delete file?',
|
|
|
|
|
deleteConfirmBody: 'This cannot be undone.',
|
|
|
|
|
cancel: 'Cancel',
|
|
|
|
|
confirm: 'Delete',
|
|
|
|
|
emptyTitle: 'No files yet',
|
|
|
|
|
emptyDescription: 'Upload an image or document to get started.',
|
|
|
|
|
},
|
2026-07-16 00:19:41 +04:00
|
|
|
adminProducts: {
|
|
|
|
|
emptyTitle: 'No products found',
|
|
|
|
|
emptyDescription: 'Try adjusting your filters, or create a new product.',
|
|
|
|
|
},
|
|
|
|
|
adminUsers: {
|
|
|
|
|
emptyTitle: 'No users found',
|
|
|
|
|
emptyDescription: 'Invite a teammate to get started.',
|
|
|
|
|
invitationsEmptyTitle: 'No pending invitations',
|
|
|
|
|
},
|
|
|
|
|
adminMonitoring: {
|
|
|
|
|
eventsEmptyTitle: 'No events found',
|
|
|
|
|
eventsEmptyDescription: 'Try a different category filter or search term.',
|
|
|
|
|
},
|
|
|
|
|
adminAnalytics: {
|
|
|
|
|
topProductsEmptyTitle: 'No product sales in this period',
|
|
|
|
|
topProductsEmptyDescription: 'Try a wider date range.',
|
|
|
|
|
},
|
2026-02-26 23:09:20 +04:00
|
|
|
};
|