Rebuild du site sur Astro : i18n multilingue, faits verifies, Docker, CI, tests
- Astro statique, EN par defaut + /fr/ + /nl/, detection langue navigateur - i18n par fichiers JSON, ajouter une langue = ajouter des traductions - Contenu original porte a l'identique (diff d'inventaire par langue) - Chronologie legislative corrigee sur sources primaires (PE, Conseil, votes) - Timeline 23 precedents + observatoire 35 items + annuaire 66 outils FOSS, chaque affirmation verifiee et sourcee - Zero requete tierce (teste), lisible JS coupe, axe WCAG AA x2 themes - Version offline monofichier par langue a chaque build - Docker multi-stage vers nginx + CSP stricte auto-generee - CI GitHub Actions (SHA-pinnees) + verification hebdo des liens - CONTRIBUTING : divulgation d'affiliation obligatoire, allowlist de domaines testee en CI
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import en from '../../src/i18n/en.json'
|
||||
import fr from '../../src/i18n/fr.json'
|
||||
import nl from '../../src/i18n/nl.json'
|
||||
import { defaultLocale, locales } from '../../src/i18n/locales'
|
||||
|
||||
// Every locale dictionary must have exactly the English key shape:
|
||||
// a missing key would silently fall back to English in production.
|
||||
function keyPaths(node: unknown, prefix = ''): string[] {
|
||||
if (typeof node === 'string') return [prefix]
|
||||
if (node && typeof node === 'object') {
|
||||
return Object.entries(node).flatMap(([k, v]) => keyPaths(v, prefix ? `${prefix}.${k}` : k))
|
||||
}
|
||||
return [prefix]
|
||||
}
|
||||
|
||||
const dicts: Record<string, unknown> = { en, fr, nl }
|
||||
|
||||
describe('i18n dictionary parity', () => {
|
||||
it('declares a dictionary for every locale', () => {
|
||||
for (const locale of locales) expect(dicts[locale], `src/i18n/${locale}.json`).toBeDefined()
|
||||
})
|
||||
|
||||
const reference = keyPaths(dicts[defaultLocale]).sort()
|
||||
|
||||
for (const locale of locales.filter((l) => l !== defaultLocale)) {
|
||||
it(`${locale}.json matches the ${defaultLocale}.json key shape`, () => {
|
||||
const keys = keyPaths(dicts[locale]).sort()
|
||||
expect(keys).toEqual(reference)
|
||||
})
|
||||
}
|
||||
|
||||
it('has no empty strings', () => {
|
||||
for (const [locale, dict] of Object.entries(dicts)) {
|
||||
const walk = (node: unknown, path: string) => {
|
||||
if (typeof node === 'string') {
|
||||
expect(node.trim(), `${locale}:${path}`).not.toBe('')
|
||||
} else if (node && typeof node === 'object') {
|
||||
for (const [k, v] of Object.entries(node)) walk(v, `${path}.${k}`)
|
||||
}
|
||||
}
|
||||
walk(dict, locale)
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user