fix(front-end): seo translated
This commit is contained in:
@@ -6,9 +6,8 @@ export const ABOUT_ROUTES: Routes = [
|
||||
path: '',
|
||||
component: AboutPageComponent,
|
||||
data: {
|
||||
seoTitle: 'Chi siamo | 3D fab',
|
||||
seoDescription:
|
||||
'Siamo un laboratorio di stampa 3D orientato a prototipi, ricambi e produzioni su misura.',
|
||||
seoTitleKey: 'SEO.ROUTES.ABOUT.TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.ABOUT.DESCRIPTION',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -8,9 +8,8 @@ export const CALCULATOR_ROUTES: Routes = [
|
||||
component: CalculatorPageComponent,
|
||||
data: {
|
||||
mode: 'easy',
|
||||
seoTitle: 'Calcolatore stampa 3D base | 3D fab',
|
||||
seoDescription:
|
||||
'Calcola rapidamente il prezzo della tua stampa 3D in modalita base.',
|
||||
seoTitleKey: 'SEO.ROUTES.CALCULATOR.BASIC.TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.CALCULATOR.BASIC.DESCRIPTION',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -18,9 +17,8 @@ export const CALCULATOR_ROUTES: Routes = [
|
||||
component: CalculatorPageComponent,
|
||||
data: {
|
||||
mode: 'advanced',
|
||||
seoTitle: 'Calcolatore stampa 3D avanzato | 3D fab',
|
||||
seoDescription:
|
||||
'Configura parametri avanzati e ottieni un preventivo preciso con slicing reale.',
|
||||
seoTitleKey: 'SEO.ROUTES.CALCULATOR.ADVANCED.TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.CALCULATOR.ADVANCED.DESCRIPTION',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -101,7 +101,11 @@
|
||||
|
||||
<p class="upload-privacy-note">
|
||||
{{ "LEGAL.CONSENT.UPLOAD_NOTICE_PREFIX" | translate }}
|
||||
<a href="/privacy" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/privacy')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.UPLOAD_NOTICE_LINK" | translate
|
||||
}}</a
|
||||
>.
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
VariantOption,
|
||||
} from '../../services/quote-estimator.service';
|
||||
import { getColorHex } from '../../../../core/constants/colors.const';
|
||||
import { LanguageService } from '../../../../core/services/language.service';
|
||||
|
||||
interface FormItem {
|
||||
file: File;
|
||||
@@ -106,6 +107,7 @@ export class UploadFormComponent implements OnInit {
|
||||
private estimator = inject(QuoteEstimatorService);
|
||||
private fb = inject(FormBuilder);
|
||||
private translate = inject(TranslateService);
|
||||
readonly languageService = inject(LanguageService);
|
||||
|
||||
form: FormGroup;
|
||||
|
||||
|
||||
@@ -120,11 +120,19 @@
|
||||
<input type="checkbox" formControlName="acceptLegal" />
|
||||
<span>
|
||||
{{ "LEGAL.CONSENT.LABEL_PREFIX" | translate }}
|
||||
<a href="/terms" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/terms')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.TERMS_LINK" | translate
|
||||
}}</a>
|
||||
{{ "LEGAL.CONSENT.AND" | translate }}
|
||||
<a href="/privacy" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/privacy')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.PRIVACY_LINK" | translate
|
||||
}}</a
|
||||
>.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, input, output, signal } from '@angular/core';
|
||||
import { Component, inject, input, output, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
ReactiveFormsModule,
|
||||
@@ -11,6 +11,7 @@ import { AppCardComponent } from '../../../../shared/components/app-card/app-car
|
||||
import { AppInputComponent } from '../../../../shared/components/app-input/app-input.component';
|
||||
import { AppButtonComponent } from '../../../../shared/components/app-button/app-button.component';
|
||||
import { QuoteResult } from '../../services/quote-estimator.service';
|
||||
import { LanguageService } from '../../../../core/services/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-details',
|
||||
@@ -30,6 +31,7 @@ export class UserDetailsComponent {
|
||||
quote = input<QuoteResult>();
|
||||
submitOrder = output<any>();
|
||||
cancel = output<void>();
|
||||
readonly languageService = inject(LanguageService);
|
||||
|
||||
form: FormGroup;
|
||||
submitting = signal(false);
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div class="container ui-page-hero ui-page-hero--spacious checkout-hero">
|
||||
<h1 class="ui-page-title">{{ "CHECKOUT.TITLE" | translate }}</h1>
|
||||
<p class="ui-page-subtitle cad-subtitle" *ngIf="isCadSession()">
|
||||
Servizio CAD
|
||||
{{ "CHECKOUT.CAD_SERVICE" | translate }}
|
||||
<ng-container *ngIf="cadRequestId()">
|
||||
riferito alla richiesta contatto #{{ cadRequestId() }}
|
||||
{{ "CHECKOUT.CAD_REQUEST_REF" | translate: { id: cadRequestId() } }}
|
||||
</ng-container>
|
||||
</p>
|
||||
</div>
|
||||
@@ -204,11 +204,19 @@
|
||||
<span class="ui-checkbox__mark"></span>
|
||||
<span>
|
||||
{{ "LEGAL.CONSENT.LABEL_PREFIX" | translate }}
|
||||
<a href="/terms" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/terms')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.TERMS_LINK" | translate
|
||||
}}</a>
|
||||
{{ "LEGAL.CONSENT.AND" | translate }}
|
||||
<a href="/privacy" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/privacy')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.PRIVACY_LINK" | translate
|
||||
}}</a
|
||||
>.
|
||||
@@ -329,7 +337,9 @@
|
||||
</div>
|
||||
<div class="summary-item cad-summary-item" *ngIf="cadTotal() > 0">
|
||||
<div class="item-details">
|
||||
<span class="item-name">Servizio CAD</span>
|
||||
<span class="item-name">{{
|
||||
"CHECKOUT.CAD_SERVICE" | translate
|
||||
}}</span>
|
||||
<div class="item-specs-sub">{{ cadHours() }}h</div>
|
||||
</div>
|
||||
<div class="item-price">
|
||||
|
||||
@@ -51,7 +51,7 @@ export class CheckoutComponent implements OnInit {
|
||||
private quoteService = inject(QuoteEstimatorService);
|
||||
private router = inject(Router);
|
||||
private route = inject(ActivatedRoute);
|
||||
private languageService = inject(LanguageService);
|
||||
readonly languageService = inject(LanguageService);
|
||||
|
||||
checkoutForm: FormGroup;
|
||||
sessionId: string | null = null;
|
||||
@@ -147,7 +147,7 @@ export class CheckoutComponent implements OnInit {
|
||||
this.sessionId = params['session'];
|
||||
if (!this.sessionId) {
|
||||
this.error = 'CHECKOUT.ERR_NO_SESSION_START';
|
||||
this.router.navigate(['/']); // Redirect if no session
|
||||
this.router.navigate(['/', this.languageService.selectedLang()]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,11 @@
|
||||
<p class="ui-form-hint">{{ "CONTACT.UPLOAD_HINT" | translate }}</p>
|
||||
<p class="ui-form-hint upload-privacy-note">
|
||||
{{ "LEGAL.CONSENT.UPLOAD_NOTICE_PREFIX" | translate }}
|
||||
<a href="/privacy" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/privacy')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.UPLOAD_NOTICE_LINK" | translate
|
||||
}}</a
|
||||
>.
|
||||
@@ -161,11 +165,19 @@
|
||||
<span class="ui-checkbox__mark"></span>
|
||||
<span>
|
||||
{{ "LEGAL.CONSENT.LABEL_PREFIX" | translate }}
|
||||
<a href="/terms" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/terms')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.TERMS_LINK" | translate
|
||||
}}</a>
|
||||
{{ "LEGAL.CONSENT.AND" | translate }}
|
||||
<a href="/privacy" target="_blank" rel="noopener">{{
|
||||
<a
|
||||
[href]="languageService.localizedPath('/privacy')"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{
|
||||
"LEGAL.CONSENT.PRIVACY_LINK" | translate
|
||||
}}</a
|
||||
>.
|
||||
|
||||
@@ -70,7 +70,7 @@ export class ContactFormComponent implements OnDestroy {
|
||||
];
|
||||
|
||||
private quoteRequestService = inject(QuoteRequestService);
|
||||
private languageService = inject(LanguageService);
|
||||
readonly languageService = inject(LanguageService);
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
|
||||
@@ -6,9 +6,8 @@ export const CONTACT_ROUTES: Routes = [
|
||||
loadComponent: () =>
|
||||
import('./contact-page.component').then((m) => m.ContactPageComponent),
|
||||
data: {
|
||||
seoTitle: 'Contatti | 3D fab',
|
||||
seoDescription:
|
||||
'Richiedi informazioni, preventivi personalizzati o supporto per progetti di stampa 3D.',
|
||||
seoTitleKey: 'SEO.ROUTES.CONTACT.TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.CONTACT.DESCRIPTION',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -16,15 +16,27 @@
|
||||
{{ "HOME.HERO_SUBTITLE" | translate }}
|
||||
</p>
|
||||
<div class="hero-actions ui-inline-actions ui-inline-actions--wide">
|
||||
<app-button variant="primary" routerLink="/calculator/basic">{{
|
||||
<app-button
|
||||
variant="primary"
|
||||
[routerLink]="languageService.localizedPath('/calculator/basic')"
|
||||
>{{
|
||||
"HOME.BTN_CALCULATE" | translate
|
||||
}}</app-button>
|
||||
<app-button variant="outline" routerLink="/shop">{{
|
||||
}}</app-button
|
||||
>
|
||||
<app-button
|
||||
variant="outline"
|
||||
[routerLink]="languageService.localizedPath('/shop')"
|
||||
>{{
|
||||
"HOME.BTN_SHOP" | translate
|
||||
}}</app-button>
|
||||
<app-button variant="text" routerLink="/contact">{{
|
||||
}}</app-button
|
||||
>
|
||||
<app-button
|
||||
variant="text"
|
||||
[routerLink]="languageService.localizedPath('/contact')"
|
||||
>{{
|
||||
"HOME.BTN_CONTACT" | translate
|
||||
}}</app-button>
|
||||
}}</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="hero-swiss-card">
|
||||
@@ -136,13 +148,13 @@
|
||||
<app-button
|
||||
variant="primary"
|
||||
[fullWidth]="true"
|
||||
routerLink="/calculator/basic"
|
||||
[routerLink]="languageService.localizedPath('/calculator/basic')"
|
||||
>{{ "HOME.BTN_OPEN_CALC" | translate }}</app-button
|
||||
>
|
||||
<app-button
|
||||
variant="outline"
|
||||
[fullWidth]="true"
|
||||
routerLink="/contact"
|
||||
[routerLink]="languageService.localizedPath('/contact')"
|
||||
>{{ "HOME.BTN_CONTACT" | translate }}</app-button
|
||||
>
|
||||
</div>
|
||||
@@ -167,12 +179,20 @@
|
||||
<li>{{ "HOME.SEC_SHOP_LIST_3" | translate }}</li>
|
||||
</ul>
|
||||
<div class="shop-actions ui-inline-actions">
|
||||
<app-button variant="primary" routerLink="/shop">{{
|
||||
<app-button
|
||||
variant="primary"
|
||||
[routerLink]="languageService.localizedPath('/shop')"
|
||||
>{{
|
||||
"HOME.BTN_DISCOVER" | translate
|
||||
}}</app-button>
|
||||
<app-button variant="outline" routerLink="/contact">{{
|
||||
}}</app-button
|
||||
>
|
||||
<app-button
|
||||
variant="outline"
|
||||
[routerLink]="languageService.localizedPath('/contact')"
|
||||
>{{
|
||||
"HOME.BTN_REQ_SOLUTION" | translate
|
||||
}}</app-button>
|
||||
}}</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -237,12 +257,20 @@
|
||||
{{ "HOME.SEC_ABOUT_TEXT" | translate }}
|
||||
</p>
|
||||
<div class="about-actions ui-inline-actions">
|
||||
<app-button variant="primary" routerLink="/about">{{
|
||||
<app-button
|
||||
variant="primary"
|
||||
[routerLink]="languageService.localizedPath('/about')"
|
||||
>{{
|
||||
"HOME.SEC_ABOUT_TITLE" | translate
|
||||
}}</app-button>
|
||||
<app-button variant="outline" routerLink="/contact">{{
|
||||
}}</app-button
|
||||
>
|
||||
<app-button
|
||||
variant="outline"
|
||||
[routerLink]="languageService.localizedPath('/contact')"
|
||||
>{{
|
||||
"HOME.BTN_CONTACT" | translate
|
||||
}}</app-button>
|
||||
}}</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="about-media">
|
||||
|
||||
@@ -5,6 +5,7 @@ import { RouterLink } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AppButtonComponent } from '../../shared/components/app-button/app-button.component';
|
||||
import { AppCardComponent } from '../../shared/components/app-card/app-card.component';
|
||||
import { LanguageService } from '../../core/services/language.service';
|
||||
import {
|
||||
buildPublicMediaUsageScopeKey,
|
||||
PublicMediaDisplayImage,
|
||||
@@ -69,6 +70,7 @@ const HOME_CAPABILITY_CONFIGS: readonly HomeCapabilityConfig[] = [
|
||||
})
|
||||
export class HomeComponent {
|
||||
private readonly publicMediaService = inject(PublicMediaService);
|
||||
readonly languageService = inject(LanguageService);
|
||||
|
||||
private readonly mediaByUsage = toSignal(
|
||||
this.publicMediaService.getUsageCollections([
|
||||
|
||||
@@ -6,9 +6,8 @@ export const LEGAL_ROUTES: Routes = [
|
||||
loadComponent: () =>
|
||||
import('./privacy/privacy.component').then((m) => m.PrivacyComponent),
|
||||
data: {
|
||||
seoTitle: 'Privacy Policy | 3D fab',
|
||||
seoDescription:
|
||||
'Informativa privacy di 3D fab: trattamento dati, finalita e contatti.',
|
||||
seoTitleKey: 'SEO.ROUTES.LEGAL.PRIVACY.TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.LEGAL.PRIVACY.DESCRIPTION',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -16,9 +15,8 @@ export const LEGAL_ROUTES: Routes = [
|
||||
loadComponent: () =>
|
||||
import('./terms/terms.component').then((m) => m.TermsComponent),
|
||||
data: {
|
||||
seoTitle: 'Termini e condizioni | 3D fab',
|
||||
seoDescription:
|
||||
'Termini e condizioni del servizio di stampa 3D e del calcolatore preventivi.',
|
||||
seoTitleKey: 'SEO.ROUTES.LEGAL.TERMS.TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.LEGAL.TERMS.DESCRIPTION',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -245,7 +245,9 @@ export class OrderComponent implements OnInit {
|
||||
amount: order?.subtotalChf ?? 0,
|
||||
},
|
||||
{
|
||||
label: `Servizio CAD (${order?.cadHours || 0}h)`,
|
||||
label: this.translate.instant('ORDER.CAD_SERVICE', {
|
||||
hours: order?.cadHours || 0,
|
||||
}),
|
||||
amount: order?.cadTotalChf ?? 0,
|
||||
visible: (order?.cadTotalChf ?? 0) > 0,
|
||||
},
|
||||
|
||||
@@ -366,7 +366,7 @@ export class ProductDetailComponent {
|
||||
if (!sessionId) {
|
||||
return;
|
||||
}
|
||||
this.router.navigate(['/checkout'], {
|
||||
this.router.navigate(['/', this.languageService.selectedLang(), 'checkout'], {
|
||||
queryParams: { session: sessionId },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -239,7 +239,10 @@
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<app-button variant="primary" routerLink="/contact">
|
||||
<app-button
|
||||
variant="primary"
|
||||
[routerLink]="languageService.localizedPath('/contact')"
|
||||
>
|
||||
{{ "NAV.CONTACT" | translate }}
|
||||
</app-button>
|
||||
</div>
|
||||
|
||||
@@ -59,7 +59,7 @@ export class ShopPageComponent {
|
||||
private readonly router = inject(Router);
|
||||
private readonly translate = inject(TranslateService);
|
||||
private readonly seoService = inject(SeoService);
|
||||
private readonly languageService = inject(LanguageService);
|
||||
readonly languageService = inject(LanguageService);
|
||||
private readonly shopRouteService = inject(ShopRouteService);
|
||||
readonly shopService = inject(ShopService);
|
||||
|
||||
@@ -242,7 +242,7 @@ export class ShopPageComponent {
|
||||
if (!sessionId) {
|
||||
return;
|
||||
}
|
||||
this.router.navigate(['/checkout'], {
|
||||
this.router.navigate(['/', this.languageService.selectedLang(), 'checkout'], {
|
||||
queryParams: {
|
||||
session: sessionId,
|
||||
},
|
||||
|
||||
@@ -7,30 +7,32 @@ export const SHOP_ROUTES: Routes = [
|
||||
path: '',
|
||||
component: ShopPageComponent,
|
||||
data: {
|
||||
seoTitle: 'Shop 3D fab',
|
||||
seoDescription:
|
||||
'Catalogo prodotti stampati in 3D, accessori tecnici e soluzioni pratiche pronte all uso.',
|
||||
seoTitleKey: 'SEO.ROUTES.SHOP.TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.SHOP.DESCRIPTION',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'p/:productSlug',
|
||||
component: ProductDetailComponent,
|
||||
data: {
|
||||
seoTitle: 'Prodotto | 3D fab',
|
||||
seoTitleKey: 'SEO.ROUTES.SHOP.PRODUCT_TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.SHOP.PRODUCT_DESCRIPTION',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':categorySlug/:productSlug',
|
||||
component: ProductDetailComponent,
|
||||
data: {
|
||||
seoTitle: 'Prodotto | 3D fab',
|
||||
seoTitleKey: 'SEO.ROUTES.SHOP.PRODUCT_TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.SHOP.PRODUCT_DESCRIPTION',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':categorySlug',
|
||||
component: ShopPageComponent,
|
||||
data: {
|
||||
seoTitle: 'Categoria Shop | 3D fab',
|
||||
seoTitleKey: 'SEO.ROUTES.SHOP.CATEGORY_TITLE',
|
||||
seoDescriptionKey: 'SEO.ROUTES.SHOP.CATEGORY_DESCRIPTION',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user