feat(issue-222): wire angular host/player i18n to backend shell locale

This commit is contained in:
2026-03-01 21:54:32 +00:00
committed by DEV-bot
parent 515d5be5ec
commit 8f772673f9
7 changed files with 35 additions and 7 deletions

View File

@@ -55,7 +55,7 @@ type LeaderboardResponse = FinishGameResponse;
<pre *ngIf="scoreboardPayload">{{ scoreboardPayload }}</pre> <pre *ngIf="scoreboardPayload">{{ scoreboardPayload }}</pre>
<div *ngIf="finalLeaderboard.length"> <div *ngIf="finalLeaderboard.length">
<h3>{{ copy('host.final_leaderboard') }}</h3> <h3>{{ copy('host.final_leaderboard') }}</h3>
<p *ngIf="finalWinner"><strong>{{ copy('host.winner') }}:</strong> {{ finalWinner.nickname }} ({{ finalWinner.score }} pts)</p> <p *ngIf="finalWinner"><strong>{{ copy('host.winner') }}:</strong> {{ finalWinner.nickname }} ({{ finalWinner.score }} {{ copy('common.points_short') }})</p>
<ol> <ol>
<li *ngFor="let entry of finalLeaderboard">{{ entry.nickname }}: {{ entry.score }}</li> <li *ngFor="let entry of finalLeaderboard">{{ entry.nickname }}: {{ entry.score }}</li>
</ol> </ol>

View File

@@ -255,7 +255,7 @@ export class PlayerShellComponent implements OnInit, OnDestroy {
if (error instanceof Error && error.message) { if (error instanceof Error && error.message) {
return error.message; return error.message;
} }
return 'Unknown error'; return this.copy('common.unknown_error');
} }
private markOnline(): void { private markOnline(): void {

View File

@@ -45,6 +45,22 @@ describe('lobby i18n locale propagation', () => {
expect(updates).toEqual(['en', 'da']); expect(updates).toEqual(['en', 'da']);
}); });
it('prefers backend-provided shell locale over browser defaults', async () => {
vi.stubGlobal('window', {
location: { search: '' },
localStorage: storageMock(),
});
vi.stubGlobal('document', {
body: { dataset: { wppLocale: 'da' } },
querySelector: vi.fn(() => null),
});
vi.stubGlobal('navigator', { language: 'en-US' });
const i18n = await import('./lobby-i18n');
expect(i18n.resolvePreferredLocale()).toBe('da');
});
it('falls back to default en translation when da key is intentionally missing', async () => { it('falls back to default en translation when da key is intentionally missing', async () => {
vi.stubGlobal('window', { vi.stubGlobal('window', {
location: { search: '' }, location: { search: '' },

View File

@@ -32,11 +32,14 @@ export function resolvePreferredLocale(): SupportedLocale {
return activeLocale; return activeLocale;
} }
const rootLocale =
typeof document !== 'undefined' ? document.querySelector<HTMLElement>('app-root')?.dataset?.['wppLocale'] : null;
const shellLocale = typeof document !== 'undefined' ? document.body?.dataset?.['wppLocale'] : null;
const queryLocale = new URLSearchParams(window.location?.search ?? '').get('lang'); const queryLocale = new URLSearchParams(window.location?.search ?? '').get('lang');
const storedLocale = window.localStorage?.getItem?.('wpp.locale'); const storedLocale = window.localStorage?.getItem?.('wpp.locale');
const browserLocale = typeof navigator !== 'undefined' ? navigator.language : ''; const browserLocale = typeof navigator !== 'undefined' ? navigator.language : '';
activeLocale = normalizeLocale(queryLocale || storedLocale || browserLocale || DEFAULT_LOCALE); activeLocale = normalizeLocale(rootLocale || shellLocale || queryLocale || storedLocale || browserLocale || DEFAULT_LOCALE);
return activeLocale; return activeLocale;
} }

View File

@@ -1,13 +1,13 @@
<!doctype html> <!doctype html>
<html lang="da"> <html lang="{{ shell_locale|default:'en' }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>WPP SPA Shell</title> <title>WPP SPA Shell</title>
<link rel="stylesheet" href="{{ spa_asset_base }}/styles.css?v={{ spa_asset_version|urlencode }}"> <link rel="stylesheet" href="{{ spa_asset_base }}/styles.css?v={{ spa_asset_version|urlencode }}">
</head> </head>
<body data-wpp-shell-route="{{ shell_route }}" data-wpp-shell-kind="{{ shell_kind }}"> <body data-wpp-shell-route="{{ shell_route }}" data-wpp-shell-kind="{{ shell_kind }}" data-wpp-locale="{{ shell_locale|default:'en' }}">
<app-root data-wpp-shell-route="{{ shell_route }}" data-wpp-shell-kind="{{ shell_kind }}">Indlæser Angular app-shell…</app-root> <app-root data-wpp-shell-route="{{ shell_route }}" data-wpp-shell-kind="{{ shell_kind }}" data-wpp-locale="{{ shell_locale|default:'en' }}">Indlæser Angular app-shell…</app-root>
<script type="module" src="{{ spa_asset_base }}/main.js?v={{ spa_asset_version|urlencode }}"></script> <script type="module" src="{{ spa_asset_base }}/main.js?v={{ spa_asset_version|urlencode }}"></script>
</body> </body>
</html> </html>

View File

@@ -5,7 +5,7 @@ from django.shortcuts import render
from fupogfakta.models import Category from fupogfakta.models import Category
from .feature_flags import use_spa_ui from .feature_flags import use_spa_ui
from .i18n import lobby_i18n_catalog from .i18n import lobby_i18n_catalog, resolve_locale
def _render_spa_shell(request, shell_route: str, shell_kind: str): def _render_spa_shell(request, shell_route: str, shell_kind: str):
@@ -18,6 +18,7 @@ def _render_spa_shell(request, shell_route: str, shell_kind: str):
"spa_asset_base": settings.WPP_SPA_ASSET_BASE, "spa_asset_base": settings.WPP_SPA_ASSET_BASE,
"spa_asset_version": getattr(settings, "WPP_SPA_ASSET_VERSION", "dev"), "spa_asset_version": getattr(settings, "WPP_SPA_ASSET_VERSION", "dev"),
"lobby_i18n": lobby_i18n_catalog(), "lobby_i18n": lobby_i18n_catalog(),
"shell_locale": resolve_locale(request),
}, },
) )

View File

@@ -74,6 +74,14 @@
"round": { "round": {
"en": "round", "en": "round",
"da": "runde" "da": "runde"
},
"points_short": {
"en": "pts",
"da": "point"
},
"unknown_error": {
"en": "Unknown error",
"da": "Ukendt fejl"
} }
}, },
"app": { "app": {