fix(back-end): fix load product

This commit is contained in:
2026-03-22 21:11:33 +01:00
parent 653082868a
commit 74d1b16b7c
18 changed files with 261 additions and 122 deletions

View File

@@ -29,8 +29,6 @@ import { serverOriginInterceptor } from './core/interceptors/server-origin.inter
import { catchError, firstValueFrom, of } from 'rxjs';
import { StaticTranslateLoader } from './core/i18n/static-translate.loader';
import {
getNavigatorLanguagePreferences,
parseAcceptLanguage,
resolveInitialLanguage,
SUPPORTED_LANGS,
} from './core/i18n/language-resolution';
@@ -72,11 +70,6 @@ export const appConfig: ApplicationConfig = {
(typeof request?.url === 'string' && request.url) || router.url || '/';
const lang = resolveInitialLanguage({
url: requestedUrl,
preferredLanguages: request
? parseAcceptLanguage(readRequestHeader(request, 'accept-language'))
: getNavigatorLanguagePreferences(
typeof navigator === 'undefined' ? null : navigator,
),
});
return firstValueFrom(
@@ -95,21 +88,3 @@ export const appConfig: ApplicationConfig = {
provideClientHydration(withEventReplay()),
],
};
function readRequestHeader(
request: {
headers?: Record<string, string | string[] | undefined>;
} | null,
headerName: string,
): string | null {
if (!request?.headers) {
return null;
}
const headerValue = request.headers[headerName.toLowerCase()];
if (Array.isArray(headerValue)) {
return headerValue[0] ?? null;
}
return typeof headerValue === 'string' ? headerValue : null;
}

View File

@@ -7,7 +7,6 @@ import {
} from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { LanguageService } from './language.service';
import { RequestLike } from '../../../core/request-origin';
describe('LanguageService', () => {
function createTranslateMock() {
@@ -83,14 +82,9 @@ describe('LanguageService', () => {
const translate = createTranslateMock();
const router = createRouterMock('/calculator?session=abc');
const navigateSpy = router.navigateByUrl as unknown as jasmine.Spy;
const request: RequestLike = {
headers: {
'accept-language': 'it-CH,it;q=0.9,en;q=0.8',
},
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const service = new LanguageService(translate, router, request);
const service = new LanguageService(translate, router);
expect(translate.use).toHaveBeenCalledWith('it');
expect((translate as any).setFallbackLang).toHaveBeenCalledWith('it');
@@ -103,41 +97,31 @@ describe('LanguageService', () => {
expect(navOptions.replaceUrl).toBeTrue();
});
it('uses the preferred browser language on the root URL', () => {
it('uses the default language on the root URL', () => {
const translate = createTranslateMock();
const router = createRouterMock('/');
const navigateSpy = router.navigateByUrl as unknown as jasmine.Spy;
const request: RequestLike = {
headers: {
'accept-language': 'de-CH,de;q=0.9,en;q=0.8,it;q=0.7',
},
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const service = new LanguageService(translate, router, request);
const service = new LanguageService(translate, router);
expect(translate.use).toHaveBeenCalledWith('de');
expect(translate.use).toHaveBeenCalledWith('it');
expect(navigateSpy).toHaveBeenCalledTimes(1);
const firstCall = navigateSpy.calls.mostRecent();
const tree = firstCall.args[0] as UrlTree;
expect(router.serializeUrl(tree)).toBe('/de');
expect(router.serializeUrl(tree)).toBe('/it');
});
it('uses the default language for non-root URLs without a language prefix', () => {
const translate = createTranslateMock();
const router = createRouterMock('/calculator?session=abc');
const navigateSpy = router.navigateByUrl as unknown as jasmine.Spy;
const request: RequestLike = {
headers: {
'accept-language': 'de-CH,de;q=0.9,en;q=0.8,it;q=0.7',
},
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const service = new LanguageService(translate, router, request);
const service = new LanguageService(translate, router);
expect(translate.use).toHaveBeenCalledWith('de');
expect(translate.use).toHaveBeenCalledWith('it');
expect(navigateSpy).toHaveBeenCalledTimes(1);
const firstCall = navigateSpy.calls.mostRecent();

View File

@@ -1,4 +1,4 @@
import { Inject, Injectable, Optional, REQUEST, signal } from '@angular/core';
import { Injectable, signal } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import {
NavigationEnd,
@@ -6,12 +6,7 @@ import {
Router,
UrlTree,
} from '@angular/router';
import {
getNavigatorLanguagePreferences,
parseAcceptLanguage,
resolveInitialLanguage,
} from '../i18n/language-resolution';
import { RequestLike } from '../../../core/request-origin';
import { resolveInitialLanguage } from '../i18n/language-resolution';
type SupportedLang = 'it' | 'en' | 'de' | 'fr';
type LocalizedRouteOverrides = Partial<Record<SupportedLang, string>>;
@@ -28,7 +23,6 @@ export class LanguageService {
constructor(
private translate: TranslateService,
private router: Router,
@Optional() @Inject(REQUEST) private request: RequestLike | null = null,
) {
this.translate.addLangs(this.supportedLangs);
this.translate.setFallbackLang('it');
@@ -43,11 +37,6 @@ export class LanguageService {
const initialTree = this.router.parseUrl(this.router.url);
const initialLang = resolveInitialLanguage({
url: this.router.url,
preferredLanguages: this.request
? parseAcceptLanguage(this.readRequestHeader('accept-language'))
: getNavigatorLanguagePreferences(
typeof navigator === 'undefined' ? null : navigator,
),
});
this.applyLanguage(initialLang);
this.ensureLanguageInPath(initialTree);
@@ -148,7 +137,7 @@ export class LanguageService {
const queryLang = this.getQueryLang(urlTree);
const rootLang = this.isSupportedLang(queryLang)
? queryLang
: this.currentLang();
: this.defaultLang;
if (rootLang !== this.currentLang()) {
this.applyLanguage(rootLang);
}
@@ -180,17 +169,6 @@ export class LanguageService {
return typeof lang === 'string' ? lang.toLowerCase() : null;
}
private readRequestHeader(headerName: string): string | null {
const headerValue =
this.request?.headers?.[headerName.toLowerCase()] ??
this.request?.get?.(headerName.toLowerCase());
if (Array.isArray(headerValue)) {
return headerValue[0] ?? null;
}
return typeof headerValue === 'string' ? headerValue : null;
}
private isSupportedLang(
lang: string | null | undefined,
): lang is SupportedLang {

View File

@@ -613,11 +613,11 @@
"HERO_TITLE": "3D-Druckservice.<br>Von der Datei zum fertigen Teil.",
"HERO_LEAD": "Mit dem fortschrittlichsten Rechner für Ihre 3D-Drucke: absolute Präzision und keine Überraschungen.",
"HERO_SUBTITLE": "Wir bieten auch CAD-Services für individuelle Teile an!",
"HERO_SWISS_TITLE": "Based in Switzerland",
"HERO_SWISS_TITLE": "Mit Sitz in der Schweiz",
"HERO_SWISS_COPY": "Produktion und Support in der Schweiz.",
"HERO_SWISS_LOCATIONS_LABEL": "Standorte",
"HERO_SWISS_LOCATION_1": "Ticino",
"HERO_SWISS_LOCATION_2": "Zurich",
"HERO_SWISS_LOCATION_2": "Zürich",
"HERO_SWISS_LOCATION_3": "Biel/Bienne",
"HERO_SWISS_NOTE": "In der ganzen Schweiz aktiv.",
"BTN_CALCULATE": "Angebot berechnen",

View File

@@ -84,7 +84,7 @@
"HERO_TITLE": "Service d'impression 3D.<br>Du fichier à la pièce finie.",
"HERO_LEAD": "Avec le calculateur le plus avancé pour vos impressions 3D : précision absolue et zéro surprise.",
"HERO_SUBTITLE": "Nous proposons aussi des services CAD pour des pièces personnalisées !",
"HERO_SWISS_TITLE": "Based in Switzerland",
"HERO_SWISS_TITLE": "Basés en Suisse",
"HERO_SWISS_COPY": "Production et support en Suisse.",
"HERO_SWISS_LOCATIONS_LABEL": "Sites",
"HERO_SWISS_LOCATION_1": "Ticino",

View File

@@ -84,11 +84,11 @@
"HERO_TITLE": "Servizio di stampa 3D.<br>Dal file al pezzo finito.",
"HERO_LEAD": "Con il calcolatore più avanzato per le tue stampe 3D: precisione assoluta e zero sorprese.",
"HERO_SUBTITLE": "Offriamo anche servizi di CAD per pezzi personalizzati!",
"HERO_SWISS_TITLE": "Based in Switzerland",
"HERO_SWISS_TITLE": "Con sede in Svizzera",
"HERO_SWISS_COPY": "Produzione e supporto in Svizzera",
"HERO_SWISS_LOCATIONS_LABEL": "Sedi",
"HERO_SWISS_LOCATION_1": "Ticino",
"HERO_SWISS_LOCATION_2": "Zurich",
"HERO_SWISS_LOCATION_2": "Zurigo",
"HERO_SWISS_LOCATION_3": "Biel/Bienne",
"HERO_SWISS_NOTE": "Operativi in tutta la Svizzera.",
"BTN_CALCULATE": "Calcola Preventivo",

View File

@@ -1,5 +1,5 @@
<!doctype html>
<html lang="it">
<html lang="it-CH">
<head>
<meta charset="utf-8" />
<title>3D fab | Stampa 3D su misura</title>

View File

@@ -1,8 +1,8 @@
import { resolvePublicRedirectTarget } from './server-routing';
describe('server routing redirects', () => {
it('does not force a fixed-language redirect for the root path', () => {
expect(resolvePublicRedirectTarget('/')).toBeNull();
it('redirects the root path to the default language', () => {
expect(resolvePublicRedirectTarget('/')).toBe('/it');
});
it('redirects unprefixed public pages to the default language', () => {

View File

@@ -13,7 +13,7 @@ export function resolvePublicRedirectTarget(pathname: string): string | null {
normalizedPath === '/' ? '/' : normalizedPath.replace(/\/+$/, '');
const segments = splitSegments(trimmedPath);
if (segments.length === 0) {
return null;
return `/${DEFAULT_LANG}`;
}
const firstSegment = segments[0].toLowerCase();

View File

@@ -5,10 +5,6 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import bootstrap from './main.server';
import { resolveRequestOrigin } from './core/request-origin';
import {
parseAcceptLanguage,
resolveInitialLanguage,
} from './app/core/i18n/language-resolution';
import { resolvePublicRedirectTarget } from './server-routing';
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
@@ -41,18 +37,6 @@ app.get(
}),
);
app.get('/', (req, res) => {
const acceptLanguage = req.get('accept-language');
const preferredLanguages = parseAcceptLanguage(acceptLanguage);
const lang = resolveInitialLanguage({
preferredLanguages,
});
res.setHeader('Vary', 'Accept-Language');
res.setHeader('Cache-Control', 'private, no-store');
res.redirect(302, `/${lang}${querySuffix(req.originalUrl)}`);
});
app.get('**', (req, res, next) => {
const targetPath = resolvePublicRedirectTarget(req.path);
if (!targetPath) {