feat(i18n): enforce shared keyspace contract across django and spa
All checks were successful
CI / test-and-quality (push) Successful in 2m17s
CI / test-and-quality (pull_request) Successful in 2m23s

This commit is contained in:
2026-03-01 19:24:12 +00:00
parent 377722eb9a
commit b55b379134
3 changed files with 111 additions and 17 deletions

View File

@@ -0,0 +1,53 @@
import { describe, expect, it } from 'vitest';
import lobbyCatalog from '../../shared/i18n/lobby.json';
import { lobbyMessage, lobbyMessageFromApiPayload } from '../src/spa/lobby-i18n';
describe('shared i18n keyspace contract', () => {
it('keeps en as default and da/en matrix for frontend error keys', () => {
expect(lobbyCatalog.locales.default).toBe('en');
expect(lobbyCatalog.locales.supported).toEqual(expect.arrayContaining(['en', 'da']));
for (const [key, translations] of Object.entries(lobbyCatalog.frontend.errors)) {
expect(translations.en, `${key} missing en`).toBeTruthy();
expect(translations.da, `${key} missing da`).toBeTruthy();
}
});
it('keeps backend error-code keyspace aligned with backend translations', () => {
for (const [code, key] of Object.entries(lobbyCatalog.backend.error_codes)) {
expect(code).toBe(key);
expect(lobbyCatalog.backend.errors[key as keyof typeof lobbyCatalog.backend.errors]).toBeDefined();
}
for (const [key, translations] of Object.entries(lobbyCatalog.backend.errors)) {
expect(translations.en, `${key} missing en`).toBeTruthy();
expect(translations.da, `${key} missing da`).toBeTruthy();
}
});
});
describe('lobbyMessage locale handling', () => {
it('uses english by default and falls back to default for unsupported locale', () => {
expect(lobbyMessage('session_code_required')).toBe('Session code is required.');
expect(lobbyMessage('session_code_required', 'fr')).toBe('Session code is required.');
});
it('resolves locale from api payload and maps known backend error codes directly', () => {
expect(
lobbyMessageFromApiPayload(
{ error_code: 'session_not_found', locale: 'da' },
'join_failed',
),
).toBe('Sessionskoden er ugyldig, eller sessionen findes ikke længere.');
});
it('falls back when backend error code has no frontend translation key', () => {
expect(
lobbyMessageFromApiPayload(
{ error_code: 'session_not_joinable', locale: 'da' },
'join_failed',
),
).toBe('Kunne ikke joine. Tjek kode eller kaldenavn og prøv igen.');
});
});