fix(i18n): translate the illness labels left in English

The illness screens showed "Weight", "Pulse", "Recovery" and "Day" in
English in German, French, Spanish, Italian and Polish: the four keys
had been copied from English into those catalogs, while their
neighbours were translated. The same copy left the medication wizard's
"Name" step in French, Spanish, Italian and Polish and the dashboard
layout's "Chart" column in German in English. A test now holds the
illness keys translated in every locale.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 22:29:24 +01:00
1 parent aa29d1b432
commit d77fe9aa7d
6 files changed
+80 -25

No files matched your search

+5 -5
View File
@@ -737,7 +737,7 @@
"moveUp": "Nach oben",
"moveDown": "Nach unten",
"layoutTileColumn": "Kachel",
"layoutChartColumn": "Chart",
"layoutChartColumn": "Diagramm",
"layoutNativeOnlyTitle": "In der mobilen App sichtbar",
"layoutNativeOnlyHint": "Diese Widgets zeichnet die mobile App, nicht diese Seite. Sie liegen im selben gespeicherten Layout, hier wird ihre Sichtbarkeit gesetzt.",
"dragHandle": "Zum Verschieben ziehen",
@@ -10999,7 +10999,7 @@
"notePlaceholder": "Wie sich der Tag anfühlte (verschlüsselt).",
"severityFor": "Stärke für {symptom}",
"saveError": "Der Tag konnte nicht gespeichert werden. Bitte erneut versuchen.",
"day": "Day"
"day": "Tag"
},
"impact": {
"0": "Voll belastbar",
@@ -11088,14 +11088,14 @@
"RESTING_HEART_RATE": "Ruheherzfrequenz",
"HEART_RATE_VARIABILITY": "Herzfrequenzvariabilität",
"HRV_RMSSD": "HRV (RMSSD)",
"RECOVERY_SCORE": "Recovery",
"RECOVERY_SCORE": "Erholung",
"OXYGEN_SATURATION": "Sauerstoffsättigung",
"BODY_TEMPERATURE": "Körpertemperatur",
"RESPIRATORY_RATE": "Atemfrequenz",
"BLOOD_PRESSURE_SYS": "Systolischer Blutdruck",
"BLOOD_PRESSURE_DIA": "Diastolischer Blutdruck",
"PULSE": "Pulse",
"WEIGHT": "Weight",
"PULSE": "Puls",
"WEIGHT": "Gewicht",
"FUNCTIONAL_IMPACT": "Funktionelle Beeinträchtigung"
},
"listLoadError": "Episoden konnten nicht geladen werden."
+5 -5
View File
@@ -1673,7 +1673,7 @@
"step1": {
"title": "¿Cómo se llama el medicamento?",
"subline": "Exactamente como aparece en la caja.",
"label": "Name",
"label": "Nombre",
"placeholder": "p. ej. Ramipril",
"short": "Nombre"
},
@@ -10999,7 +10999,7 @@
"notePlaceholder": "Cómo te sentiste hoy (cifrado).",
"severityFor": "Gravedad de {symptom}",
"saveError": "No se pudo guardar el día. Inténtalo de nuevo.",
"day": "Day"
"day": "Día"
},
"impact": {
"0": "Plenamente funcional",
@@ -11088,14 +11088,14 @@
"RESTING_HEART_RATE": "Frecuencia cardíaca en reposo",
"HEART_RATE_VARIABILITY": "Variabilidad de la frecuencia cardíaca",
"HRV_RMSSD": "VFC (RMSSD)",
"RECOVERY_SCORE": "Recovery",
"RECOVERY_SCORE": "Recuperación",
"OXYGEN_SATURATION": "Saturación de oxígeno",
"BODY_TEMPERATURE": "Temperatura corporal",
"RESPIRATORY_RATE": "Frecuencia respiratoria",
"BLOOD_PRESSURE_SYS": "Presión arterial sistólica",
"BLOOD_PRESSURE_DIA": "Presión arterial diastólica",
"PULSE": "Pulse",
"WEIGHT": "Weight",
"PULSE": "Pulso",
"WEIGHT": "Peso",
"FUNCTIONAL_IMPACT": "Impacto funcional"
},
"listLoadError": "No se pudieron cargar tus episodios."
+5 -5
View File
@@ -1673,7 +1673,7 @@
"step1": {
"title": "Comment s’appelle le médicament ?",
"subline": "Exactement comme indiqué sur la boîte.",
"label": "Name",
"label": "Nom",
"placeholder": "par exemple Ramipril",
"short": "Nom"
},
@@ -10999,7 +10999,7 @@
"notePlaceholder": "Comment s'est passée la journée (chiffré).",
"severityFor": "Intensité de {symptom}",
"saveError": "Impossible d'enregistrer la journée. Veuillez réessayer.",
"day": "Day"
"day": "Jour"
},
"impact": {
"0": "Pleinement fonctionnel",
@@ -11088,14 +11088,14 @@
"RESTING_HEART_RATE": "Fréquence cardiaque au repos",
"HEART_RATE_VARIABILITY": "Variabilité de la fréquence cardiaque",
"HRV_RMSSD": "VFC (RMSSD)",
"RECOVERY_SCORE": "Recovery",
"RECOVERY_SCORE": "Récupération",
"OXYGEN_SATURATION": "Saturation en oxygène",
"BODY_TEMPERATURE": "Température corporelle",
"RESPIRATORY_RATE": "Fréquence respiratoire",
"BLOOD_PRESSURE_SYS": "Pression artérielle systolique",
"BLOOD_PRESSURE_DIA": "Pression artérielle diastolique",
"PULSE": "Pulse",
"WEIGHT": "Weight",
"PULSE": "Pouls",
"WEIGHT": "Poids",
"FUNCTIONAL_IMPACT": "Retentissement fonctionnel"
},
"listLoadError": "Impossible de charger vos épisodes."
+5 -5
View File
@@ -1673,7 +1673,7 @@
"step1": {
"title": "Come si chiama il farmaco?",
"subline": "Esattamente come riportato sulla confezione.",
"label": "Name",
"label": "Nome",
"placeholder": "ad es. Ramipril",
"short": "Nome"
},
@@ -10999,7 +10999,7 @@
"notePlaceholder": "Com'è andata la giornata (cifrato).",
"severityFor": "Intensità di {symptom}",
"saveError": "Impossibile salvare la giornata. Riprova.",
"day": "Day"
"day": "Giorno"
},
"impact": {
"0": "Pienamente funzionale",
@@ -11088,14 +11088,14 @@
"RESTING_HEART_RATE": "Frequenza cardiaca a riposo",
"HEART_RATE_VARIABILITY": "Variabilità della frequenza cardiaca",
"HRV_RMSSD": "HRV (RMSSD)",
"RECOVERY_SCORE": "Recovery",
"RECOVERY_SCORE": "Recupero",
"OXYGEN_SATURATION": "Saturazione di ossigeno",
"BODY_TEMPERATURE": "Temperatura corporea",
"RESPIRATORY_RATE": "Frequenza respiratoria",
"BLOOD_PRESSURE_SYS": "Pressione arteriosa sistolica",
"BLOOD_PRESSURE_DIA": "Pressione arteriosa diastolica",
"PULSE": "Pulse",
"WEIGHT": "Weight",
"PULSE": "Polso",
"WEIGHT": "Peso",
"FUNCTIONAL_IMPACT": "Impatto funzionale"
},
"listLoadError": "Impossibile caricare gli episodi."
+5 -5
View File
@@ -1673,7 +1673,7 @@
"step1": {
"title": "Jak nazywa się lek?",
"subline": "Dokładnie tak, jak na opakowaniu.",
"label": "Name",
"label": "Nazwa",
"placeholder": "np. Ramipryl",
"short": "Nazwa"
},
@@ -10999,7 +10999,7 @@
"notePlaceholder": "Jak minął dzień (zaszyfrowane).",
"severityFor": "Nasilenie: {symptom}",
"saveError": "Nie udało się zapisać dnia. Spróbuj ponownie.",
"day": "Day"
"day": "Dzień"
},
"impact": {
"0": "Pełna sprawność",
@@ -11088,14 +11088,14 @@
"RESTING_HEART_RATE": "Spoczynkowe tętno",
"HEART_RATE_VARIABILITY": "Zmienność rytmu serca",
"HRV_RMSSD": "HRV (RMSSD)",
"RECOVERY_SCORE": "Recovery",
"RECOVERY_SCORE": "Regeneracja",
"OXYGEN_SATURATION": "Saturacja tlenem",
"BODY_TEMPERATURE": "Temperatura ciała",
"RESPIRATORY_RATE": "Częstość oddechów",
"BLOOD_PRESSURE_SYS": "Ciśnienie skurczowe",
"BLOOD_PRESSURE_DIA": "Ciśnienie rozkurczowe",
"PULSE": "Pulse",
"WEIGHT": "Weight",
"PULSE": "Tętno",
"WEIGHT": "Waga",
"FUNCTIONAL_IMPACT": "Wpływ na funkcjonowanie"
},
"listLoadError": "Nie udało się załadować epizodów."
@@ -0,0 +1,55 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { resolveKey } from "@/lib/i18n/resolve-key";
/**
* The illness screens name the vital that drives a recovery gap and label the
* day picker of the log sheet through these keys. They were once copied from
* English into five catalogs, so a German reader saw "Weight", "Pulse",
* "Recovery" and "Day" among translated neighbours. The English leak guard
* skips single words, so it cannot see this; the pin below can.
*/
const MESSAGES_DIR = join(__dirname, "../../../../messages");
function catalog(locale: string): Record<string, unknown> {
return JSON.parse(
readFileSync(join(MESSAGES_DIR, `${locale}.json`), "utf8"),
) as Record<string, unknown>;
}
const KEYS = [
"illness.vital.WEIGHT",
"illness.vital.PULSE",
"illness.vital.RECOVERY_SCORE",
"illness.sheet.day",
] as const;
const english = catalog("en");
describe("illness labels", () => {
for (const locale of [
"de",
"fr",
"es",
"it",
"pl",
"nl",
"pt-BR",
"ko",
"ru",
]) {
it(`are translated in ${locale}`, () => {
const messages = catalog(locale);
for (const key of KEYS) {
const value = resolveKey(messages, key);
expect(value, `${locale} ${key}`).toBeTruthy();
expect(value, `${locale} ${key} is still English`).not.toBe(
resolveKey(english, key),
);
}
});
}
});