diff --git a/src/app/widgets/resolvers/data-source-resolver.service.ts b/src/app/widgets/resolvers/data-source-resolver.service.ts index 0d9bfc6..4722f5f 100644 --- a/src/app/widgets/resolvers/data-source-resolver.service.ts +++ b/src/app/widgets/resolvers/data-source-resolver.service.ts @@ -2,7 +2,8 @@ import { Injectable, inject } from '@angular/core'; import { Observable, of, map, switchMap } from 'rxjs'; import { CategoryFacade } from '../../facades/platform/category.facade'; import { ProductFacade } from '../../facades/platform/product.facade'; -import { SectionConfig, WidgetConfig } from '../../shared/models/config'; +import { LanguageService } from '../../services/language.service'; +import { LocalizedTextContent, SectionConfig, WidgetConfig } from '../../shared/models/config'; import { WidgetManifestEntry, WidgetDataSourceName } from '../contracts/widget-manifest.contract'; import { BannerWidgetData, CategoriesWidgetData, FooterWidgetData, HeroWidgetData, HtmlWidgetData, PartnersWidgetData, ProductCollectionWidgetData } from '../contracts/widget-data.contract'; import { WidgetManifestService } from '../registry/widget-manifest.service'; @@ -14,6 +15,7 @@ export class DataSourceResolverService { private readonly widgetManifest = inject(WidgetManifestService); private readonly categoryFacade = inject(CategoryFacade); private readonly productFacade = inject(ProductFacade); + private readonly languageService = inject(LanguageService); resolve(widget: WidgetConfig, section: SectionConfig): Observable { return this.widgetManifest.getWidget(widget.type).pipe( @@ -84,15 +86,32 @@ export class DataSourceResolverService { return typeof source === 'string' ? source as WidgetDataSourceName : undefined; } + /** + * Hero text props accept either a plain string (existing tenants, unchanged) or a + * per-locale map (`{ ru: '...', en: '...' }`, same shape as NavigationLocalizedText) + * so the hero doesn't render in one hardcoded language on every locale. + */ + private resolveLocalizedWidgetText(value: unknown): string | undefined { + if (value == null) { + return undefined; + } + if (typeof value !== 'object') { + return String(value); + } + const lang = this.languageService.currentLanguage(); + const text = value as LocalizedTextContent; + return text[lang] ?? text['en'] ?? Object.values(text)[0]; + } + private toHeroData(section: SectionConfig, settings: Record): HeroWidgetData { const rawSlides = settings['slides']; const slides = Array.isArray(rawSlides) ? rawSlides .filter((slide): slide is Record => !!slide && typeof slide === 'object') .map(slide => ({ - title: String(slide['title'] ?? ''), - subtitle: slide['subtitle'] != null ? String(slide['subtitle']) : undefined, - ctaLabel: slide['ctaLabel'] != null ? String(slide['ctaLabel']) : undefined, + title: this.resolveLocalizedWidgetText(slide['title']) ?? '', + subtitle: this.resolveLocalizedWidgetText(slide['subtitle']), + ctaLabel: this.resolveLocalizedWidgetText(slide['ctaLabel']), })) .filter(slide => slide.title) : undefined; @@ -100,9 +119,9 @@ export class DataSourceResolverService { return { section, settings, - title: String(settings['title'] ?? ''), - subtitle: settings['subtitle'] != null ? String(settings['subtitle']) : undefined, - ctaLabel: settings['ctaLabel'] != null ? String(settings['ctaLabel']) : undefined, + title: this.resolveLocalizedWidgetText(settings['title']) ?? '', + subtitle: this.resolveLocalizedWidgetText(settings['subtitle']), + ctaLabel: this.resolveLocalizedWidgetText(settings['ctaLabel']), slides, autoplay: settings['autoplay'] === true, }; diff --git a/src/assets/mock/bootstrap/bootstrap.json b/src/assets/mock/bootstrap/bootstrap.json index e947c11..1d3a493 100644 --- a/src/assets/mock/bootstrap/bootstrap.json +++ b/src/assets/mock/bootstrap/bootstrap.json @@ -398,9 +398,21 @@ }, "visible": true, "props": { - "title": "Welcome to Marketplace Platform", - "subtitle": "Configuration-driven multi-tenant commerce", - "ctaLabel": "Start Shopping" + "title": { + "ru": "Добро пожаловать на Маркетплейс", + "en": "Welcome to Marketplace Platform", + "hy": "Բարի գալուստ Մարքեթփլեյս" + }, + "subtitle": { + "ru": "Конфигурируемая мультитенантная торговая площадка", + "en": "Configuration-driven multi-tenant commerce", + "hy": "Կոնֆիգուրացիայով կառավարվող բազմատենանտ առևտրի հարթակ" + }, + "ctaLabel": { + "ru": "Начать покупки", + "en": "Start Shopping", + "hy": "Սկսել գնումները" + } } } ] diff --git a/src/assets/mock/bootstrap/homepage.json b/src/assets/mock/bootstrap/homepage.json index 9cc1713..697486d 100644 --- a/src/assets/mock/bootstrap/homepage.json +++ b/src/assets/mock/bootstrap/homepage.json @@ -33,9 +33,21 @@ "version": "1.0.0", "visible": true, "props": { - "title": "Welcome to Marketplace Platform", - "subtitle": "Configuration-driven multi-tenant commerce", - "ctaLabel": "Start Shopping" + "title": { + "ru": "Добро пожаловать на Маркетплейс", + "en": "Welcome to Marketplace Platform", + "hy": "Բարի գալուստ Մարքեթփլեյս" + }, + "subtitle": { + "ru": "Конфигурируемая мультитенантная торговая площадка", + "en": "Configuration-driven multi-tenant commerce", + "hy": "Կոնֆիգուրացիայով կառավարվող բազմատենանտ առևտրի հարթակ" + }, + "ctaLabel": { + "ru": "Начать покупки", + "en": "Start Shopping", + "hy": "Սկսել գնումները" + } } } ]