fix(back-end): fix load product
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user