From 038f8b10cdf3165cc2fbbeacbc6b6cfd91b435b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 19:28:29 +0200 Subject: [PATCH] feat(mental-health): one check-in pattern for every questionnaire, worded as published MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every instrument now runs the same wizard: the form's own question stem above each item (the PHQ-9 and GAD-7 stems were missing), the item's exact answer options as one radio group in source order, progress, Back, number-key answers, and a review step that lists every answer, lets any of them be changed and is the only step that submits. The result leads with the crisis card when it applies, then shows the score on the instrument's scale, the band, what the band means, the change since the last check-in against the meaningful-change threshold, and the next step. Scoring is untouched. The wording was read against the published versions. German follows the PHQ-D and Löwe's GAD-7 verbatim, including the follow-up question in its published formal address. Spanish, Italian and Polish carry the official PHQ-9 and GAD-7 translations, French the official GAD-7, and the GAD-7 gets its own answer set because the Italian forms word it differently. Where a version could not be matched to a published form (the French PHQ-9, every Korean form) the language now shows the English original. Each locale list names its sources, and spot checks pin the corrected wording. --- CONTRIBUTING.md | 2 +- e2e/mental-health-check-in.spec.ts | 173 ++++++ messages/de.json | 67 ++- messages/en.json | 55 +- messages/es.json | 97 ++- messages/fr.json | 82 ++- messages/it.json | 83 ++- messages/ko.json | 98 ++-- messages/nl.json | 43 +- messages/pl.json | 89 ++- messages/pt-BR.json | 43 +- .../baselines/source-function-lines.json | 6 +- .../baselines/version-stamp-comments.json | 7 +- .../__tests__/check-in-nav.test.ts | 59 +- .../__tests__/mental-wellbeing.test.tsx | 4 +- .../mental-health/assessment-result.tsx | 93 +-- src/components/mental-health/check-in-nav.ts | 58 +- .../mental-health/check-in-wizard.tsx | 551 +++++++++++++----- .../mental-health/instrument-card.tsx | 8 +- .../mental-health/mental-wellbeing.tsx | 9 +- .../__tests__/instrument-wording.test.ts | 56 ++ .../__tests__/instruments.test.ts | 37 +- src/lib/mental-health/instrument-wording.ts | 8 + src/lib/mental-health/instruments.ts | 105 +++- 24 files changed, 1398 insertions(+), 435 deletions(-) create mode 100644 e2e/mental-health-check-in.spec.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0e2037f0c..22f7d22e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -151,7 +151,7 @@ What a reviewer checks on a translation pull request: - Medical terms are the ones the language's clinical guidelines use, and the register matches the rest of the locale (`du` in German, `você` in Brazilian Portuguese). - Words the app or the Telegram bot matches literally stay as they are: slash commands, `taken `, and the unit symbols. - Machine translation is fine as a first draft, but someone who reads the language has gone through every string before the pull request is opened, and the description says so. -- Validated questionnaire items (the PHQ-9, GAD-7, WHO-5 and SCI items, their answer options and their stems under `mentalHealth.items`, `mentalHealth.options`, `mentalHealth.who5Options`, `mentalHealth.sciOptions`, `mentalHealth.stems`, `mentalHealth.functional*`) are never translated freely, not even as a draft. A language gets them only from a published, licensed validated version, copied verbatim and named in `src/lib/mental-health/instruments.ts`; until then the app shows the English original and the bundle carries none of those keys, which the i18n tests enforce. +- Validated questionnaire items (the PHQ-9, GAD-7, WHO-5 and SCI items, their answer options and their stems under `mentalHealth.items`, `mentalHealth.options`, `mentalHealth.gad7Options`, `mentalHealth.who5Options`, `mentalHealth.sciOptions`, `mentalHealth.stems`, `mentalHealth.functional*`) are never translated freely, not even as a draft. A language gets them only from a published, licensed validated version, copied verbatim and named in `src/lib/mental-health/instruments.ts`; until then the app shows the English original and the bundle carries none of those keys, which the i18n tests enforce. To run the checks Fink shows on a local checkout: `npx @inlang/cli@3.4.4 check --project ./project.inlang`. It reads the files and changes nothing. diff --git a/e2e/mental-health-check-in.spec.ts b/e2e/mental-health-check-in.spec.ts new file mode 100644 index 000000000..3dda44f2e --- /dev/null +++ b/e2e/mental-health-check-in.spec.ts @@ -0,0 +1,173 @@ +import AxeBuilder from "@axe-core/playwright"; +import type { Page } from "@playwright/test"; +import { expect, test } from "./setup/test"; + +import { STORAGE_STATE_PATH } from "./setup/global-setup"; + +/** + * One full check-in per instrument through the shared wizard: the stem is + * on every step, the answers form one radio group in the source order, the + * number keys answer, the review lists every answer and changes one, and + * the result carries score, band, meaning and the next step. The PHQ-9 run + * answers item 9 above zero, so its result must lead with the crisis card. + * + * The total on the result is the server's; this journey adds the chosen + * option values itself and compares, which is the scoring contract seen + * from the outside (`instruments.test.ts` pins it from the inside). Each + * screen is scanned with axe on the way. + * + * Anchored on data attributes only, so the journey reads the same at every + * width and in every language. + */ + +type Case = { + id: "PHQ9" | "GAD7" | "WHO5" | "SCI"; + items: number; + options: number; + multiplier: number; + followUp: boolean; +}; + +const CASES: readonly Case[] = [ + { id: "PHQ9", items: 9, options: 4, multiplier: 1, followUp: true }, + { id: "GAD7", items: 7, options: 4, multiplier: 1, followUp: false }, + { id: "WHO5", items: 5, options: 6, multiplier: 4, followUp: false }, + { id: "SCI", items: 8, options: 5, multiplier: 1, followUp: false }, +]; + +async function expectNoSeriousViolations(page: Page, label: string) { + // The pointer rests where the last tap landed; a hover tint under it is + // not what the screen shows at rest. + await page.mouse.move(0, 0); + await page.waitForTimeout(300); + const results = await new AxeBuilder({ page }) + .include( + '[data-slot="mental-health-check-in"], [data-slot="mental-health-result"]', + ) + .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"]) + .analyze(); + const blocking = results.violations + .filter((v) => v.impact === "serious" || v.impact === "critical") + .map( + (v) => + `${v.id}: ${v.nodes.map((n) => `${n.html} ${n.failureSummary ?? ""}`).join(" | ")}`, + ); + expect(blocking, `axe on ${label}`).toEqual([]); +} + +test.describe("mental-health check-in, every instrument", () => { + test.use({ storageState: STORAGE_STATE_PATH }); + + for (const c of CASES) { + test(`${c.id}: steps, review, result`, async ({ page }) => { + test.setTimeout(90_000); + await page.goto("/mental-wellbeing"); + const card = page.locator( + `[data-slot="instrument-card"][data-instrument="${c.id}"]`, + ); + await card.locator('[data-slot="instrument-card-start"]').click(); + + const wizard = page.locator( + `[data-slot="mental-health-check-in"][data-instrument="${c.id}"]`, + ); + await expect(wizard).toBeVisible(); + + const values: number[] = []; + for (let step = 1; step <= c.items; step++) { + const current = wizard.locator( + `[data-slot="check-in-step"][data-step="${step}"]`, + ); + await expect(current).toBeVisible(); + await expect( + current.locator('[data-slot="check-in-stem"]'), + ).toHaveCount(1); + const group = current.locator('[role="radiogroup"]'); + await expect(group.locator('[role="radio"]')).toHaveCount(c.options); + if (step === 1) await expectNoSeriousViolations(page, `${c.id} step 1`); + + // PHQ-9 item 9 answered above zero: the option in the last position + // is "nearly every day" (value 3). + const position = + c.id === "PHQ9" && step === 9 ? c.options : (step % c.options) + 1; + const answered = group.locator('[role="radio"]').nth(position - 1); + values.push(Number(await answered.getAttribute("data-value"))); + if (step % 2 === 0) { + // Keyboard: the number key picks the answer in that position. + await group.locator('[role="radio"]').first().focus(); + await page.keyboard.press(String(position)); + } else { + await group + .locator('[role="radio"]') + .nth(position - 1) + .click(); + } + } + + if (c.followUp) { + const followUp = wizard.locator( + '[data-slot="check-in-step"][data-functional]', + ); + await expect(followUp).toBeVisible(); + await followUp.locator('[role="radio"]').nth(1).click(); + await wizard.locator('[data-slot="check-in-next"]').click(); + } + + const review = wizard.locator('[data-slot="check-in-review"]'); + await expect(review).toBeVisible(); + await expect( + review.locator('[data-slot="check-in-review-row"]'), + ).toHaveCount(c.items + (c.followUp ? 1 : 0)); + await expectNoSeriousViolations(page, `${c.id} review`); + + // Change the second answer from the review; the wizard returns there. + await review.locator('[data-review-edit="2"]').click(); + const second = wizard.locator( + '[data-slot="check-in-step"][data-step="2"]', + ); + await expect(second).toBeVisible(); + const other = + values[1] === + Number( + await second + .locator('[role="radio"]') + .first() + .getAttribute("data-value"), + ) + ? 1 + : 0; + await second.locator('[role="radio"]').nth(other).click(); + await expect(review).toBeVisible(); + values[1] = Number( + await review + .locator('[data-slot="check-in-review-row"][data-step="2"]') + .getAttribute("data-value"), + ); + + await wizard.locator('[data-slot="check-in-submit"]').click(); + + const result = page.locator( + `[data-slot="mental-health-result"][data-instrument="${c.id}"]`, + ); + await expect(result).toBeVisible(); + const total = values.reduce((sum, v) => sum + v, 0) * c.multiplier; + await expect(result.locator('[data-slot="result-score"]')).toContainText( + String(total), + ); + await expect(result.locator('[data-slot="result-band"]')).toBeVisible(); + await expect( + result.locator('[data-slot="result-meaning"]'), + ).toBeVisible(); + await expect( + result.locator( + '[data-slot="result-follow-up-hint"], [data-slot="result-next-step"]', + ), + ).toHaveCount(1); + if (c.id === "PHQ9") { + await expect( + result.locator('[data-slot="mental-health-crisis-card"]'), + ).toBeVisible(); + } + await expectNoSeriousViolations(page, `${c.id} result`); + }); + } +}); diff --git a/messages/de.json b/messages/de.json index 10d2b5101..1b8503088 100644 --- a/messages/de.json +++ b/messages/de.json @@ -10953,11 +10953,17 @@ "2": "An mehr als der Hälfte der Tage", "3": "Beinahe jeden Tag" }, - "functionalTitle": "Wenn du eine oder mehrere Beschwerden angegeben hast: Wie sehr haben diese deine Arbeit, deinen Haushalt oder den Umgang mit anderen Menschen erschwert?", + "gad7Options": { + "0": "Überhaupt nicht", + "1": "An einzelnen Tagen", + "2": "An mehr als der Hälfte der Tage", + "3": "Beinahe jeden Tag" + }, + "functionalTitle": "Wenn eines oder mehrere der bisher in diesem Fragebogen beschriebenen Probleme bei Ihnen vorliegen, geben Sie bitte an, wie sehr diese Probleme es Ihnen erschwert haben, Ihre Arbeit zu tun, Ihren Haushalt zu regeln oder mit anderen Menschen zurecht zu kommen:", "functional": { "0": "Überhaupt nicht erschwert", "1": "Etwas erschwert", - "2": "Sehr erschwert", + "2": "Stark erschwert", "3": "Extrem erschwert" }, "functionalOptionalHint": "Optional: Du kannst auch ohne Antwort auf diese Frage absenden.", @@ -10965,7 +10971,10 @@ "title": "Dein Ergebnis", "totalLabel": "Gesamtwert", "bandLabel": "Bereich für dieses Screening", - "takeAnother": "Weiteren Check-in machen" + "takeAnother": "Weiteren Check-in machen", + "scoreOf": "von {max}", + "firstCheckIn": "Das ist dein erstes Ergebnis mit diesem Fragebogen. Ab dem nächsten siehst du hier die Veränderung.", + "nextStep": "Du kannst den Check-in in zwei Wochen oder später wiederholen, um die Veränderung zu sehen. Wenn dich etwas beunruhigt, sprich jederzeit mit einer Fachperson." }, "band": { "PHQ9": { @@ -11014,8 +11023,8 @@ "4": "Müdigkeit oder Gefühl, keine Energie zu haben", "5": "Verminderter Appetit oder übermäßiges Bedürfnis zu essen", "6": "Schlechte Meinung von sich selbst; Gefühl, ein Versager zu sein oder die Familie enttäuscht zu haben", - "7": "Schwierigkeiten, sich auf etwas zu konzentrieren, z. B. beim Zeitunglesen oder Fernsehen", - "8": "Waren Ihre Bewegungen oder Ihre Sprache so verlangsamt, dass es auch anderen auffallen würde? Oder waren Sie im Gegenteil „zappelig“ oder ruhelos und hatten einen stärkeren Bewegungsdrang als sonst?", + "7": "Schwierigkeiten, sich auf etwas zu konzentrieren, z. B. beim Zeitungslesen oder Fernsehen", + "8": "Waren Ihre Bewegungen oder Ihre Sprache so verlangsamt, dass es auch anderen auffallen würde? Oder waren Sie im Gegenteil „zappelig“ oder ruhelos und hatten dadurch einen stärkeren Bewegungsdrang als sonst?", "9": "Gedanken, dass Sie lieber tot wären oder sich Leid zufügen möchten" }, "gad7": { @@ -11023,9 +11032,9 @@ "2": "Nicht in der Lage sein, Sorgen zu stoppen oder zu kontrollieren", "3": "Übermäßige Sorgen bezüglich verschiedener Angelegenheiten", "4": "Schwierigkeiten zu entspannen", - "5": "Rastlosigkeit, sodass Stillsitzen schwerfällt", + "5": "Rastlosigkeit, so dass Stillsitzen schwer fällt", "6": "Schnelle Verärgerung oder Gereiztheit", - "7": "Gefühl der Angst, als würde etwas Schlimmes passieren" + "7": "Gefühl der Angst, so als würde etwas Schlimmes passieren" }, "who5": { "1": "… war ich froh und guter Laune", @@ -11082,7 +11091,6 @@ "who5": "Wohlbefindens-Check mit 5 Fragen. Beziehe dich auf die letzten zwei Wochen; höhere Werte bedeuten besseres Wohlbefinden.", "sci": "Schlaf-Check mit 8 Fragen. Die meisten Fragen beziehen sich auf den letzten Monat; höhere Werte bedeuten besseren Schlaf." }, - "progress": "{current} / {total}", "next": "Weiter", "lastResult": "Letzter Test", "lastScore": "Letztes Ergebnis", @@ -11104,6 +11112,12 @@ "5": "Die ganze Zeit" }, "stems": { + "phq9": { + "period": "Wie oft fühlten Sie sich im Verlauf der letzten 2 Wochen durch die folgenden Beschwerden beeinträchtigt?" + }, + "gad7": { + "period": "Wie oft fühlten Sie sich im Verlauf der letzten 2 Wochen durch die folgenden Beschwerden beeinträchtigt?" + }, "who5": { "period": "In den letzten zwei Wochen …" } @@ -11120,6 +11134,43 @@ "worsened": "Das erreicht die Veränderung von {threshold} Punkten, die Studien als bedeutsam ansehen, in die weniger gute Richtung.", "within_threshold": "Das ist weniger als die Veränderung von {threshold} Punkten, die Studien als bedeutsam ansehen." } + }, + "wizard": { + "progressLabel": "Frage {current} von {total}", + "followUpLabel": "Zusatzfrage (freiwillig)", + "keyboardHint": "Mit den Zahlentasten 1 bis {count} wählst du eine Antwort.", + "allAnswered": "Alle {total} Fragen beantwortet" + }, + "review": { + "title": "Antworten prüfen", + "description": "Gespeichert wird erst beim Absenden, bis dahin lässt sich jede Antwort noch ändern.", + "change": "Ändern", + "changeLabel": "Antwort auf Frage {number} ändern", + "notAnswered": "Nicht beantwortet", + "open": "Antworten prüfen" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Deine Antworten weisen für die letzten zwei Wochen auf keine oder kaum depressive Beschwerden hin.", + "mild": "Deine Antworten weisen für die letzten zwei Wochen auf leichte depressive Beschwerden hin.", + "moderate": "Deine Antworten weisen für die letzten zwei Wochen auf mittelgradige depressive Beschwerden hin.", + "modSevere": "Deine Antworten weisen für die letzten zwei Wochen auf mittelschwere depressive Beschwerden hin.", + "severe": "Deine Antworten weisen für die letzten zwei Wochen auf schwere depressive Beschwerden hin." + }, + "GAD7": { + "minimal": "Deine Antworten weisen für die letzten zwei Wochen auf keine oder kaum Angstbeschwerden hin.", + "mild": "Deine Antworten weisen für die letzten zwei Wochen auf leichte Angstbeschwerden hin.", + "moderate": "Deine Antworten weisen für die letzten zwei Wochen auf mittelgradige Angstbeschwerden hin.", + "severe": "Deine Antworten weisen für die letzten zwei Wochen auf schwere Angstbeschwerden hin." + }, + "WHO5": { + "low": "Dein Wohlbefinden lag in den letzten zwei Wochen unter dem Bereich, den der WHO-5 als gut wertet.", + "good": "Dein Wohlbefinden lag in den letzten zwei Wochen in dem Bereich, den der WHO-5 als gut wertet." + }, + "SCI": { + "belowThreshold": "Dein Schlaf lag im letzten Monat in dem Bereich, in dem die Studie hinter diesem Fragebogen eine Insomnie für wahrscheinlich hielt.", + "aboveThreshold": "Dein Schlaf lag im letzten Monat außerhalb des Bereichs, den diese Studie mit Insomnie verbindet." + } } }, "records": { diff --git a/messages/en.json b/messages/en.json index 567eb2fe3..07b2f0e74 100644 --- a/messages/en.json +++ b/messages/en.json @@ -10953,6 +10953,12 @@ "2": "More than half the days", "3": "Nearly every day" }, + "gad7Options": { + "0": "Not at all", + "1": "Several days", + "2": "More than half the days", + "3": "Nearly every day" + }, "functionalTitle": "If you checked off any problems, how difficult have these problems made it for you to do your work, take care of things at home, or get along with other people?", "functional": { "0": "Not difficult at all", @@ -10965,7 +10971,10 @@ "title": "Your result", "totalLabel": "Total score", "bandLabel": "Range for this screen", - "takeAnother": "Take another check-in" + "takeAnother": "Take another check-in", + "scoreOf": "of {max}", + "firstCheckIn": "This is your first result for this questionnaire. From the next one on, the change shows here.", + "nextStep": "You can repeat this check-in in two weeks or later to follow the change. If something worries you, talk to a health professional at any time." }, "band": { "PHQ9": { @@ -11092,7 +11101,6 @@ "who5": "5-question well-being check. Answer for the last two weeks; higher numbers mean better well-being.", "sci": "8-question sleep check. Most questions ask about the past month; higher scores mean better sleep." }, - "progress": "{current} / {total}", "next": "Next", "lastResult": "Last test", "lastScore": "Last result", @@ -11151,6 +11159,12 @@ } }, "stems": { + "phq9": { + "period": "Over the last 2 weeks, how often have you been bothered by any of the following problems?" + }, + "gad7": { + "period": "Over the last 2 weeks, how often have you been bothered by the following problems?" + }, "who5": { "period": "Over the last two weeks …" }, @@ -11172,6 +11186,43 @@ "worsened": "That reaches the {threshold}-point change studies treat as meaningful, in the less good direction.", "within_threshold": "That is smaller than the {threshold}-point change studies treat as meaningful." } + }, + "wizard": { + "progressLabel": "Question {current} of {total}", + "followUpLabel": "Follow-up question (optional)", + "keyboardHint": "Number keys 1 to {count} choose an answer.", + "allAnswered": "All {total} questions answered" + }, + "review": { + "title": "Check your answers", + "description": "Nothing is saved until you submit, and every answer can still be changed.", + "change": "Change", + "changeLabel": "Change the answer to question {number}", + "notAnswered": "Not answered", + "open": "Review answers" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Your answers point to few or no symptoms of depression over the last two weeks.", + "mild": "Your answers point to mild symptoms of depression over the last two weeks.", + "moderate": "Your answers point to moderate symptoms of depression over the last two weeks.", + "modSevere": "Your answers point to moderately severe symptoms of depression over the last two weeks.", + "severe": "Your answers point to severe symptoms of depression over the last two weeks." + }, + "GAD7": { + "minimal": "Your answers point to few or no symptoms of anxiety over the last two weeks.", + "mild": "Your answers point to mild symptoms of anxiety over the last two weeks.", + "moderate": "Your answers point to moderate symptoms of anxiety over the last two weeks.", + "severe": "Your answers point to severe symptoms of anxiety over the last two weeks." + }, + "WHO5": { + "low": "Your well-being over the last two weeks was below the range the WHO-5 counts as good.", + "good": "Your well-being over the last two weeks was in the range the WHO-5 counts as good." + }, + "SCI": { + "belowThreshold": "Your sleep over the last month was in the range where the study behind this questionnaire found insomnia likely.", + "aboveThreshold": "Your sleep over the last month was outside the range that study linked with insomnia." + } } }, "records": { diff --git a/messages/es.json b/messages/es.json index ff8fcbfb5..36448140f 100644 --- a/messages/es.json +++ b/messages/es.json @@ -10951,21 +10951,30 @@ "0": "Nunca", "1": "Varios días", "2": "Más de la mitad de los días", - "3": "Casi todos los días" + "3": "Casi cada día" }, - "functionalTitle": "Si marcaste algún problema, ¿cuánta dificultad te han causado para trabajar, ocuparte de la casa o relacionarte con otras personas?", + "gad7Options": { + "0": "Nunca", + "1": "Varios días", + "2": "Más de la mitad de los días", + "3": "Casi cada día" + }, + "functionalTitle": "Si ha marcado alguno de los problemas de este cuestionario, ¿hasta qué punto estos problemas le han creado dificultades para hacer su trabajo, ocuparse de la casa o relacionarse con los demás?", "functional": { "0": "Ninguna dificultad", - "1": "Algo de dificultad", - "2": "Mucha dificultad", - "3": "Dificultad extrema" + "1": "Algunas dificultades", + "2": "Muchas dificultades", + "3": "Muchísimas dificultades" }, "functionalOptionalHint": "Opcional: puedes enviar sin responder esta pregunta.", "result": { "title": "Tu resultado", "totalLabel": "Puntuación total", "bandLabel": "Rango para este cribado", - "takeAnother": "Hacer otra revisión" + "takeAnother": "Hacer otra revisión", + "scoreOf": "de {max}", + "firstCheckIn": "Es tu primer resultado con este cuestionario. A partir del próximo verás aquí el cambio.", + "nextStep": "Puedes repetir este cuestionario dentro de dos semanas o más tarde para seguir el cambio. Si algo te preocupa, habla con un profesional de la salud en cualquier momento." }, "band": { "PHQ9": { @@ -11008,24 +11017,24 @@ }, "items": { "phq9": { - "1": "Tener poco interés o placer en hacer las cosas", - "2": "Sentirse desanimado/a, deprimido/a o sin esperanza", - "3": "Tener problemas para dormir o mantenerse dormido/a, o dormir demasiado", - "4": "Sentirse cansado/a o tener poca energía", - "5": "Tener poco apetito o comer en exceso", - "6": "Sentirse mal con usted mismo/a, o sentir que es un fracaso o que se ha fallado a sí mismo/a o a su familia", - "7": "Tener dificultad para concentrarse en cosas tales como leer el periódico o ver la televisión", - "8": "¿Moverse o hablar tan despacio que otras personas podrían haberlo notado? O lo contrario: estar tan inquieto/a o agitado/a que se ha estado moviendo mucho más de lo habitual", - "9": "Pensar que estaría mejor muerto/a o en hacerse daño de alguna manera" + "1": "Poco interés o alegría por hacer cosas", + "2": "Sensación de estar decaído/a, deprimido/a o desesperanzado/a", + "3": "Problemas para quedarse dormido/a, para seguir durmiendo o dormir demasiado", + "4": "Sensación de cansancio o de tener poca energía", + "5": "Poco apetito o comer demasiado", + "6": "Sentirse mal consigo mismo/a; sentir que es un/a fracasado/a o que ha decepcionado a su familia o a sí mismo/a", + "7": "Problemas para concentrarse en algo, como leer el periódico o ver la televisión", + "8": "Moverse o hablar tan despacio que los demás pueden haberlo notado. O lo contrario: estar tan inquieto/a o agitado/a que se ha estado moviendo de un lado a otro más de lo habitual", + "9": "Pensamientos de que estaría mejor muerto/a o de querer hacerse daño de algún modo" }, "gad7": { - "1": "Sentirse nervioso/a, ansioso/a o muy alterado/a", - "2": "No poder dejar de preocuparse o no poder controlar la preocupación", - "3": "Preocuparse demasiado por diferentes cosas", - "4": "Tener dificultad para relajarse", - "5": "Estar tan inquieto/a que es difícil quedarse sentado/a tranquilamente", - "6": "Molestarse o irritarse fácilmente", - "7": "Sentir miedo como si algo terrible fuera a suceder" + "1": "Sentirse nervioso/a, angustiado/a o muy tenso/a", + "2": "Ser incapaz de dejar de preocuparse o de controlar la preocupación", + "3": "Preocuparse demasiado por diferentes cuestiones", + "4": "Tener problemas para relajarse", + "5": "Estar tan inquieto/a que le resulta difícil permanecer sentado/a", + "6": "Enfadarse o irritarse con facilidad", + "7": "Sentir miedo de que algo terrible pueda ocurrir" }, "who5": { "1": "Me he sentido alegre y de buen humor", @@ -11082,7 +11091,6 @@ "who5": "Test de bienestar de 5 preguntas. Responde pensando en las últimas dos semanas; cifras mayores significan mayor bienestar.", "sci": "Test de sueño de 8 preguntas. La mayoría se refiere al último mes; puntuaciones mayores significan mejor sueño." }, - "progress": "{current} / {total}", "next": "Siguiente", "lastResult": "Última prueba", "lastScore": "Último resultado", @@ -11104,6 +11112,12 @@ "5": "Todo el tiempo" }, "stems": { + "phq9": { + "period": "Durante las últimas 2 semanas ¿con qué frecuencia le ha molestado alguno de los siguientes problemas?" + }, + "gad7": { + "period": "En los últimos 14 días, ¿con qué frecuencia le han supuesto una molestia los siguientes problemas?" + }, "who5": { "period": "Durante las últimas dos semanas:" } @@ -11120,6 +11134,43 @@ "worsened": "Alcanza el cambio de {threshold} puntos que los estudios consideran relevante, en la dirección menos favorable.", "within_threshold": "Es menor que el cambio de {threshold} puntos que los estudios consideran relevante." } + }, + "wizard": { + "progressLabel": "Pregunta {current} de {total}", + "followUpLabel": "Pregunta adicional (opcional)", + "keyboardHint": "Las teclas 1 a {count} eligen una respuesta.", + "allAnswered": "Las {total} preguntas tienen respuesta" + }, + "review": { + "title": "Revisar respuestas", + "description": "No se guarda nada hasta que lo envíes, y cada respuesta todavía se puede cambiar.", + "change": "Cambiar", + "changeLabel": "Cambiar la respuesta a la pregunta {number}", + "notAnswered": "Sin respuesta", + "open": "Revisar respuestas" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Tus respuestas apuntan a pocos o ningún síntoma depresivo en las últimas dos semanas.", + "mild": "Tus respuestas apuntan a síntomas depresivos leves en las últimas dos semanas.", + "moderate": "Tus respuestas apuntan a síntomas depresivos moderados en las últimas dos semanas.", + "modSevere": "Tus respuestas apuntan a síntomas depresivos moderadamente graves en las últimas dos semanas.", + "severe": "Tus respuestas apuntan a síntomas depresivos graves en las últimas dos semanas." + }, + "GAD7": { + "minimal": "Tus respuestas apuntan a pocos o ningún síntoma de ansiedad en las últimas dos semanas.", + "mild": "Tus respuestas apuntan a síntomas de ansiedad leves en las últimas dos semanas.", + "moderate": "Tus respuestas apuntan a síntomas de ansiedad moderados en las últimas dos semanas.", + "severe": "Tus respuestas apuntan a síntomas de ansiedad graves en las últimas dos semanas." + }, + "WHO5": { + "low": "Tu bienestar en las últimas dos semanas estuvo por debajo del rango que el WHO-5 considera bueno.", + "good": "Tu bienestar en las últimas dos semanas estuvo dentro del rango que el WHO-5 considera bueno." + }, + "SCI": { + "belowThreshold": "Tu sueño del último mes estuvo en el rango en el que el estudio detrás de este cuestionario consideró probable un insomnio.", + "aboveThreshold": "Tu sueño del último mes estuvo fuera del rango que ese estudio asocia con el insomnio." + } } }, "records": { diff --git a/messages/fr.json b/messages/fr.json index ef428a625..a8236ec64 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -10947,25 +10947,21 @@ "who5": "Test de bien-être, 5 questions", "sci": "Test de sommeil, 8 questions" }, - "options": { + "gad7Options": { "0": "Jamais", "1": "Plusieurs jours", - "2": "Plus de la moitié des jours", + "2": "Plus de la moitié du temps", "3": "Presque tous les jours" }, - "functionalTitle": "Si tu as coché des problèmes, à quel point t'ont-ils gêné(e) dans ton travail, à la maison ou dans tes relations avec les autres ?", - "functional": { - "0": "Pas du tout gênant", - "1": "Un peu gênant", - "2": "Très gênant", - "3": "Extrêmement gênant" - }, "functionalOptionalHint": "Facultatif : tu peux envoyer sans répondre à cette question.", "result": { "title": "Ton résultat", "totalLabel": "Score total", "bandLabel": "Plage pour ce dépistage", - "takeAnother": "Faire un autre bilan" + "takeAnother": "Faire un autre bilan", + "scoreOf": "sur {max}", + "firstCheckIn": "C’est votre premier résultat pour ce questionnaire. À partir du prochain, l’évolution s’affichera ici.", + "nextStep": "Vous pouvez refaire ce questionnaire dans deux semaines ou plus tard pour suivre l’évolution. Si quelque chose vous inquiète, parlez-en à tout moment à un professionnel de santé." }, "band": { "PHQ9": { @@ -11007,25 +11003,14 @@ "resourcesTitle": "Des personnes qui peuvent aider" }, "items": { - "phq9": { - "1": "Peu d'intérêt ou de plaisir à faire les choses", - "2": "Se sentir triste, déprimé(e) ou désespéré(e)", - "3": "Difficultés à s'endormir ou à rester endormi(e), ou dormir trop", - "4": "Se sentir fatigué(e) ou avoir peu d'énergie", - "5": "Avoir peu d'appétit ou manger trop", - "6": "Avoir une mauvaise opinion de soi-même — ou avoir le sentiment d'être nul(le) ou d'avoir déçu sa famille ou soi-même", - "7": "Avoir du mal à se concentrer, par exemple pour lire le journal ou regarder la télévision", - "8": "Bouger ou parler si lentement que les autres ont pu le remarquer. Ou au contraire, être si agité(e) que vous avez bougé beaucoup plus que d'habitude", - "9": "Penser qu'il vaudrait mieux mourir ou envisager de vous faire du mal d'une manière ou d'une autre" - }, "gad7": { - "1": "Se sentir nerveux/nerveuse, anxieux/anxieuse ou tendu(e)", - "2": "Être incapable d'arrêter de vous inquiéter ou de contrôler vos inquiétudes", - "3": "Vous inquiéter trop à propos de choses diverses", - "4": "Avoir du mal à vous détendre", - "5": "Être si agité(e) qu'il est difficile de rester tranquille", - "6": "Devenir facilement contrarié(e) ou irritable", - "7": "Avoir peur que quelque chose d'épouvantable puisse arriver" + "1": "Un sentiment de nervosité, d’anxiété ou de tension", + "2": "Une incapacité à arrêter de s’inquiéter ou à contrôler ses inquiétudes", + "3": "Une inquiétude excessive à propos de différentes choses", + "4": "Des difficultés à se détendre", + "5": "Une agitation telle qu’il est difficile à tenir en place", + "6": "Une tendance à être facilement contrarié(e) ou irritable", + "7": "Un sentiment de peur comme si quelque chose de terrible risquait de se produire" }, "who5": { "1": "Je me suis senti(e) bien et de bonne humeur", @@ -11082,7 +11067,6 @@ "who5": "Test de bien-être en 5 questions. Répondez en pensant aux deux dernières semaines ; des chiffres plus élevés signifient un meilleur bien-être.", "sci": "Test de sommeil en 8 questions. La plupart des questions portent sur le dernier mois ; des scores plus élevés signifient un meilleur sommeil." }, - "progress": "{current} / {total}", "next": "Suivant", "lastResult": "Dernier test", "lastScore": "Dernier résultat", @@ -11104,6 +11088,9 @@ "5": "Tout le temps" }, "stems": { + "gad7": { + "period": "Au cours des 2 dernières semaines, selon quelle fréquence avez-vous été gêné(e) par les problèmes suivants ?" + }, "who5": { "period": "Au cours des deux dernières semaines" } @@ -11120,6 +11107,43 @@ "worsened": "Cela atteint la variation de {threshold} points que les études jugent significative, dans le sens moins favorable.", "within_threshold": "C'est moins que la variation de {threshold} points que les études jugent significative." } + }, + "wizard": { + "progressLabel": "Question {current} sur {total}", + "followUpLabel": "Question complémentaire (facultative)", + "keyboardHint": "Les touches 1 à {count} choisissent une réponse.", + "allAnswered": "Les {total} questions ont une réponse" + }, + "review": { + "title": "Vérifier les réponses", + "description": "Rien n’est enregistré avant l’envoi, et chaque réponse peut encore être modifiée.", + "change": "Modifier", + "changeLabel": "Modifier la réponse à la question {number}", + "notAnswered": "Sans réponse", + "open": "Vérifier les réponses" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Vos réponses indiquent peu ou pas de symptômes dépressifs au cours des deux dernières semaines.", + "mild": "Vos réponses indiquent des symptômes dépressifs légers au cours des deux dernières semaines.", + "moderate": "Vos réponses indiquent des symptômes dépressifs modérés au cours des deux dernières semaines.", + "modSevere": "Vos réponses indiquent des symptômes dépressifs modérément sévères au cours des deux dernières semaines.", + "severe": "Vos réponses indiquent des symptômes dépressifs sévères au cours des deux dernières semaines." + }, + "GAD7": { + "minimal": "Vos réponses indiquent peu ou pas de symptômes d’anxiété au cours des deux dernières semaines.", + "mild": "Vos réponses indiquent des symptômes d’anxiété légers au cours des deux dernières semaines.", + "moderate": "Vos réponses indiquent des symptômes d’anxiété modérés au cours des deux dernières semaines.", + "severe": "Vos réponses indiquent des symptômes d’anxiété sévères au cours des deux dernières semaines." + }, + "WHO5": { + "low": "Votre bien-être des deux dernières semaines était en dessous de la zone que le WHO-5 considère comme bonne.", + "good": "Votre bien-être des deux dernières semaines était dans la zone que le WHO-5 considère comme bonne." + }, + "SCI": { + "belowThreshold": "Votre sommeil du dernier mois se situait dans la zone où l’étude à l’origine de ce questionnaire jugeait une insomnie probable.", + "aboveThreshold": "Votre sommeil du dernier mois se situait en dehors de la zone que cette étude associe à l’insomnie." + } } }, "records": { diff --git a/messages/it.json b/messages/it.json index f3e5d5620..99ee64dd2 100644 --- a/messages/it.json +++ b/messages/it.json @@ -10948,15 +10948,21 @@ "sci": "Test sul sonno, 8 domande" }, "options": { - "0": "Per niente", + "0": "Mai", "1": "Alcuni giorni", - "2": "Più della metà dei giorni", + "2": "Per più della metà dei giorni", "3": "Quasi ogni giorno" }, - "functionalTitle": "Se hai indicato dei problemi, quanto ti hanno reso difficile lavorare, occuparti della casa o andare d'accordo con gli altri?", + "gad7Options": { + "0": "Mai", + "1": "Alcuni giorni", + "2": "Per oltre la metà dei giorni", + "3": "Quasi ogni giorno" + }, + "functionalTitle": "Se ha fatto una crocetta su uno qualsiasi di questi problemi, quanto questi problemi le hanno reso difficile fare il suo lavoro, occuparsi delle sue cose a casa o avere buoni rapporti con gli altri?", "functional": { "0": "Per niente difficile", - "1": "Un po' difficile", + "1": "Abbastanza difficile", "2": "Molto difficile", "3": "Estremamente difficile" }, @@ -10965,7 +10971,10 @@ "title": "Il tuo risultato", "totalLabel": "Punteggio totale", "bandLabel": "Intervallo per questo screening", - "takeAnother": "Fai un altro check-in" + "takeAnother": "Fai un altro check-in", + "scoreOf": "su {max}", + "firstCheckIn": "È il tuo primo risultato con questo questionario. Dal prossimo vedrai qui il cambiamento.", + "nextStep": "Puoi ripetere questo questionario tra due settimane o più tardi per seguire il cambiamento. Se qualcosa ti preoccupa, parlane in qualsiasi momento con un professionista sanitario." }, "band": { "PHQ9": { @@ -11009,23 +11018,23 @@ "items": { "phq9": { "1": "Scarso interesse o piacere nel fare le cose", - "2": "Sentirsi giù di morale, depresso/a o disperato/a", - "3": "Difficoltà ad addormentarsi o a mantenere il sonno, oppure dormire troppo", + "2": "Sentirsi giù, triste o disperato/a", + "3": "Problemi ad addormentarsi o a dormire tutta la notte senza svegliarsi, o a dormire troppo", "4": "Sentirsi stanco/a o avere poca energia", "5": "Scarso appetito o mangiare troppo", - "6": "Avere una cattiva opinione di sé, o sentirsi un/una fallito/a o aver deluso se stesso/a o i propri familiari", - "7": "Difficoltà a concentrarsi, ad esempio nel leggere il giornale o nel guardare la televisione", - "8": "Muoversi o parlare così lentamente che altre persone potrebbero averlo notato? Oppure, al contrario, essere così agitato/a o irrequieto/a da muoversi molto più del solito", - "9": "Pensare che sarebbe meglio morire o di farsi del male in qualche modo" + "6": "Avere una scarsa opinione di sé, o sentirsi un/una fallito/a o aver deluso se stesso/a o i propri familiari", + "7": "Difficoltà a concentrarsi su qualcosa, per esempio leggere il giornale o guardare la televisione", + "8": "Muoversi o parlare così lentamente da poter essere notato/a da altre persone. O, al contrario, essere così irrequieto/a da muoversi molto più del solito", + "9": "Pensare che sarebbe meglio morire o farsi del male in un modo o nell’altro" }, "gad7": { "1": "Sentirsi nervoso/a, ansioso/a o teso/a", - "2": "Non riuscire a smettere di preoccuparsi o a controllare le preoccupazioni", - "3": "Preoccuparsi troppo per cose diverse", + "2": "Non riuscire a smettere di preoccuparsi o a tenere sotto controllo le preoccupazioni", + "3": "Preoccuparsi troppo per varie cose", "4": "Avere difficoltà a rilassarsi", - "5": "Essere così irrequieto/a da far fatica a stare seduto/a fermo/a", + "5": "Essere talmente irrequieto/a da far fatica a stare seduto/a fermo/a", "6": "Infastidirsi o irritarsi facilmente", - "7": "Avere paura come se potesse accadere qualcosa di terribile" + "7": "Avere paura che possa succedere qualcosa di terribile" }, "who5": { "1": "Mi sono sentito/a allegro/a e di buon umore", @@ -11082,7 +11091,6 @@ "who5": "Test sul benessere in 5 domande. Rispondi pensando alle ultime due settimane; numeri più alti indicano un maggior benessere.", "sci": "Test sul sonno in 8 domande. La maggior parte riguarda l'ultimo mese; punteggi più alti indicano un sonno migliore." }, - "progress": "{current} / {total}", "next": "Avanti", "lastResult": "Ultimo test", "lastScore": "Ultimo risultato", @@ -11104,6 +11112,12 @@ "5": "Sempre" }, "stems": { + "phq9": { + "period": "Nelle ultime 2 settimane, con quale frequenza le ha dato fastidio ciascuno dei seguenti problemi?" + }, + "gad7": { + "period": "Nelle ultime 2 settimane, con quale frequenza le ha dato fastidio ciascuno dei seguenti problemi?" + }, "who5": { "period": "Nelle ultime due settimane" } @@ -11120,6 +11134,43 @@ "worsened": "Raggiunge la variazione di {threshold} punti che gli studi considerano rilevante, nella direzione meno buona.", "within_threshold": "È meno della variazione di {threshold} punti che gli studi considerano rilevante." } + }, + "wizard": { + "progressLabel": "Domanda {current} di {total}", + "followUpLabel": "Domanda aggiuntiva (facoltativa)", + "keyboardHint": "I tasti da 1 a {count} scelgono una risposta.", + "allAnswered": "Tutte le {total} domande hanno una risposta" + }, + "review": { + "title": "Controlla le risposte", + "description": "Nulla viene salvato prima dell’invio, e ogni risposta si può ancora cambiare.", + "change": "Modifica", + "changeLabel": "Modifica la risposta alla domanda {number}", + "notAnswered": "Senza risposta", + "open": "Controlla le risposte" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Le tue risposte indicano sintomi depressivi assenti o minimi nelle ultime due settimane.", + "mild": "Le tue risposte indicano sintomi depressivi lievi nelle ultime due settimane.", + "moderate": "Le tue risposte indicano sintomi depressivi moderati nelle ultime due settimane.", + "modSevere": "Le tue risposte indicano sintomi depressivi moderatamente gravi nelle ultime due settimane.", + "severe": "Le tue risposte indicano sintomi depressivi gravi nelle ultime due settimane." + }, + "GAD7": { + "minimal": "Le tue risposte indicano sintomi d’ansia assenti o minimi nelle ultime due settimane.", + "mild": "Le tue risposte indicano sintomi d’ansia lievi nelle ultime due settimane.", + "moderate": "Le tue risposte indicano sintomi d’ansia moderati nelle ultime due settimane.", + "severe": "Le tue risposte indicano sintomi d’ansia gravi nelle ultime due settimane." + }, + "WHO5": { + "low": "Il tuo benessere nelle ultime due settimane era sotto la fascia che il WHO-5 considera buona.", + "good": "Il tuo benessere nelle ultime due settimane era nella fascia che il WHO-5 considera buona." + }, + "SCI": { + "belowThreshold": "Il tuo sonno nell’ultimo mese era nella fascia in cui lo studio alla base di questo questionario riteneva probabile un’insonnia.", + "aboveThreshold": "Il tuo sonno nell’ultimo mese era fuori dalla fascia che quello studio collega all’insonnia." + } } }, "records": { diff --git a/messages/ko.json b/messages/ko.json index a7994951e..286e9d099 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -10947,25 +10947,15 @@ "who5": "웰빙 점검, 5문항", "sci": "수면 점검, 8문항" }, - "options": { - "0": "전혀 방해 받지 않았다", - "1": "며칠 동안 방해 받았다", - "2": "7 일 이상 방해 받았다", - "3": "거의 매일 방해 받았다" - }, - "functionalTitle": "만일 당신이 위의 문제 중 하나 이상 \"예\" 라고 응답하셨으면, 이러한 문제들로 인해서 당신은 일을 하거나 가정일을 돌보거나 다른 사람과 어울리는 것이 얼마나 어려웠습니까?", - "functional": { - "0": "전혀 어렵지 않았다", - "1": "약간 어려웠다", - "2": "많이 어려웠다", - "3": "매우 많이 어려웠다" - }, "functionalOptionalHint": "선택 사항이에요. 이 문항에 답하지 않아도 제출할 수 있어요.", "result": { "title": "내 결과", "totalLabel": "총점", "bandLabel": "이 검사의 구간", - "takeAnother": "다른 점검 하기" + "takeAnother": "다른 점검 하기", + "scoreOf": "/ {max}", + "firstCheckIn": "이 설문의 첫 결과예요. 다음 결과부터 여기에 변화가 표시돼요.", + "nextStep": "변화를 보려면 2주 뒤나 그 이후에 다시 해 볼 수 있어요. 걱정되는 점이 있으면 언제든 전문가와 상담하세요." }, "band": { "PHQ9": { @@ -11006,35 +10996,6 @@ "ifDanger": "당장 위험한 상황이라면 지금 {emergency}로 전화하세요.", "resourcesTitle": "도움을 줄 수 있는 곳" }, - "items": { - "phq9": { - "1": "일 또는 여가 활동을 하는 데 흥미나 즐거움을 느끼지 못함", - "2": "기분이 가라앉거나, 우울하거나, 희망이 없음", - "3": "잠이 들거나 계속 잠을 자는 것이 어려움, 또는 잠을 너무 많이 잠", - "4": "피곤하다고 느끼거나 기운이 거의 없음", - "5": "입맛이 없거나 과식을 함", - "6": "자신을 부정적으로 봄 — 혹은 자신이 실패자라고 느끼거나 자신 또는 가족을 실망시킴", - "7": "신문을 읽거나 텔레비전 보는 것과 같은 일에 집중하는 것이 어려움", - "8": "다른 사람들이 주목할 정도로 너무 느리게 움직이거나 말을 함. 또는 반대로 평상시보다 많이 움직여서, 너무 안절부절 못하거나 들떠 있음", - "9": "자신이 죽는 것이 더 낫다고 생각하거나 어떤 식으로든 자신을 해칠 것이라고 생각함" - }, - "gad7": { - "1": "초조하거나 불안하거나 조마조마하게 느낀다", - "2": "걱정하는 것을 멈추거나 조절할 수가 없다", - "3": "여러 가지 것들에 대해 걱정을 너무 많이 한다", - "4": "편하게 있기가 어렵다", - "5": "너무 안절부절못해서 가만히 있기가 힘들다", - "6": "쉽게 짜증이 나거나 쉽게 성을 내게 된다", - "7": "마치 끔찍한 일이 생길 것처럼 두렵게 느껴진다" - }, - "who5": { - "1": "나는 즐겁고 기분이 좋다.", - "2": "나는 평온하고 느긋하다.", - "3": "나는 의욕적이고 활기차다.", - "4": "나는 자고 일어나면 상쾌하고 푹 쉬었다는 느낌이 든다.", - "5": "나의 일상생활은 흥미로운 일들로 가득하다." - } - }, "crisisResource": { "telefonSeelsorge": { "name": "TelefonSeelsorge (24시간 연중무휴, 무료, 비밀 보장)" @@ -11082,7 +11043,6 @@ "who5": "5문항 웰빙 점검이에요. 지난 2주를 기준으로 답해 주세요. 숫자가 클수록 웰빙이 좋아요.", "sci": "8문항 수면 점검이에요. 대부분 지난 한 달을 묻고, 점수가 높을수록 수면이 좋아요." }, - "progress": "{current} / {total}", "next": "다음", "lastResult": "최근 검사", "lastScore": "최근 결과", @@ -11095,19 +11055,6 @@ "sci": "이 설문의 바탕이 된 연구에서는 16점 이하가 불면증 가능성과 관련이 있었어요. 수면 문제가 계속 힘들게 한다면 전문가와 이야기해 보는 게 도움이 될 수 있어요. 진단은 아니에요." }, "validatedInEnglishNote": "이 설문은 영어로 검증된 도구예요. 문항은 원문 영어 표현 그대로 보여줘요.", - "who5Options": { - "0": "전혀 그렇지 않다.", - "1": "가끔 그렇다.", - "2": "절반 미만이 그렇다.", - "3": "절반 이상이 그렇다.", - "4": "거의 그렇다.", - "5": "항상 그렇다." - }, - "stems": { - "who5": { - "period": "지난 2주 동안" - } - }, "openDetail": "자세히 보기", "historyLoadError": "검사 기록을 불러오지 못했어요", "historyLoadErrorHint": "결과가 없다는 뜻은 아니에요. 목록을 가져오지 못했을 뿐이에요. 다시 시도해 주세요.", @@ -11120,6 +11067,43 @@ "worsened": "연구에서 의미 있다고 보는 {threshold}점 변화에 도달했고, 덜 좋은 방향입니다.", "within_threshold": "연구에서 의미 있다고 보는 {threshold}점 변화보다 작습니다." } + }, + "wizard": { + "progressLabel": "{total}개 중 {current}번째 질문", + "followUpLabel": "추가 질문(선택)", + "keyboardHint": "숫자 키 1부터 {count}까지로 답을 고를 수 있어요.", + "allAnswered": "{total}개 질문에 모두 답했어요" + }, + "review": { + "title": "답변 확인", + "description": "제출하기 전에는 아무것도 저장되지 않으며, 모든 답변을 아직 바꿀 수 있어요.", + "change": "변경", + "changeLabel": "{number}번 질문의 답변 변경", + "notAnswered": "답하지 않음", + "open": "답변 확인" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "지난 2주 동안 우울 증상이 거의 또는 전혀 없었던 것으로 보여요.", + "mild": "지난 2주 동안 가벼운 우울 증상이 있었던 것으로 보여요.", + "moderate": "지난 2주 동안 중간 정도의 우울 증상이 있었던 것으로 보여요.", + "modSevere": "지난 2주 동안 다소 심한 우울 증상이 있었던 것으로 보여요.", + "severe": "지난 2주 동안 심한 우울 증상이 있었던 것으로 보여요." + }, + "GAD7": { + "minimal": "지난 2주 동안 불안 증상이 거의 또는 전혀 없었던 것으로 보여요.", + "mild": "지난 2주 동안 가벼운 불안 증상이 있었던 것으로 보여요.", + "moderate": "지난 2주 동안 중간 정도의 불안 증상이 있었던 것으로 보여요.", + "severe": "지난 2주 동안 심한 불안 증상이 있었던 것으로 보여요." + }, + "WHO5": { + "low": "지난 2주 동안의 웰빙이 WHO-5가 좋다고 보는 범위보다 낮았어요.", + "good": "지난 2주 동안의 웰빙이 WHO-5가 좋다고 보는 범위에 있었어요." + }, + "SCI": { + "belowThreshold": "지난 한 달 동안의 수면이 이 설문의 바탕이 된 연구에서 불면증 가능성이 높다고 본 범위에 있었어요.", + "aboveThreshold": "지난 한 달 동안의 수면이 그 연구가 불면증과 연결한 범위 밖에 있었어요." + } } }, "records": { diff --git a/messages/nl.json b/messages/nl.json index 5c5998d61..4a6ae7395 100644 --- a/messages/nl.json +++ b/messages/nl.json @@ -10952,7 +10952,10 @@ "title": "Je resultaat", "totalLabel": "Totaalscore", "bandLabel": "Bereik voor deze screening", - "takeAnother": "Nog een check-in doen" + "takeAnother": "Nog een check-in doen", + "scoreOf": "van {max}", + "firstCheckIn": "Dit is je eerste uitslag voor deze vragenlijst. Vanaf de volgende zie je hier de verandering.", + "nextStep": "Je kunt deze check-in over twee weken of later herhalen om de verandering te volgen. Maak je je ergens zorgen over, praat dan op elk moment met een zorgverlener." }, "band": { "PHQ9": { @@ -11040,7 +11043,6 @@ "who5": "Welzijnscheck met 5 vragen. Beantwoord ze voor de afgelopen twee weken; hogere getallen betekenen een beter welzijn.", "sci": "Slaapcheck met 8 vragen. De meeste vragen gaan over de afgelopen maand; hogere scores betekenen een betere slaap." }, - "progress": "{current} / {total}", "next": "Volgende", "lastResult": "Laatste test", "lastScore": "Laatste resultaat", @@ -11065,6 +11067,43 @@ "worsened": "Dat bereikt de verandering van {threshold} punten die studies als betekenisvol zien, in de minder goede richting.", "within_threshold": "Dat is minder dan de verandering van {threshold} punten die studies als betekenisvol zien." } + }, + "wizard": { + "progressLabel": "Vraag {current} van {total}", + "followUpLabel": "Aanvullende vraag (niet verplicht)", + "keyboardHint": "Met de cijfertoetsen 1 tot en met {count} kies je een antwoord.", + "allAnswered": "Alle {total} vragen beantwoord" + }, + "review": { + "title": "Antwoorden controleren", + "description": "Er wordt niets opgeslagen voordat je verstuurt, en elk antwoord kun je nog wijzigen.", + "change": "Wijzigen", + "changeLabel": "Antwoord op vraag {number} wijzigen", + "notAnswered": "Niet beantwoord", + "open": "Antwoorden controleren" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Je antwoorden wijzen op weinig of geen depressieve klachten in de afgelopen twee weken.", + "mild": "Je antwoorden wijzen op lichte depressieve klachten in de afgelopen twee weken.", + "moderate": "Je antwoorden wijzen op matige depressieve klachten in de afgelopen twee weken.", + "modSevere": "Je antwoorden wijzen op matig ernstige depressieve klachten in de afgelopen twee weken.", + "severe": "Je antwoorden wijzen op ernstige depressieve klachten in de afgelopen twee weken." + }, + "GAD7": { + "minimal": "Je antwoorden wijzen op weinig of geen angstklachten in de afgelopen twee weken.", + "mild": "Je antwoorden wijzen op lichte angstklachten in de afgelopen twee weken.", + "moderate": "Je antwoorden wijzen op matige angstklachten in de afgelopen twee weken.", + "severe": "Je antwoorden wijzen op ernstige angstklachten in de afgelopen twee weken." + }, + "WHO5": { + "low": "Je welzijn in de afgelopen twee weken lag onder het bereik dat de WHO-5 als goed beschouwt.", + "good": "Je welzijn in de afgelopen twee weken lag in het bereik dat de WHO-5 als goed beschouwt." + }, + "SCI": { + "belowThreshold": "Je slaap in de afgelopen maand lag in het bereik waarin het onderzoek achter deze vragenlijst slapeloosheid waarschijnlijk vond.", + "aboveThreshold": "Je slaap in de afgelopen maand lag buiten het bereik dat dat onderzoek met slapeloosheid verbindt." + } } }, "records": { diff --git a/messages/pl.json b/messages/pl.json index 7590291fd..e82a5da9e 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -10948,24 +10948,33 @@ "sci": "Test snu, 8 pytań" }, "options": { - "0": "Wcale", + "0": "Wcale nie dokuczały", "1": "Kilka dni", "2": "Więcej niż połowę dni", "3": "Niemal codziennie" }, - "functionalTitle": "Jeśli zaznaczyłeś(-aś) jakiekolwiek problemy, na ile utrudniły Ci one pracę, prowadzenie domu lub kontakty z innymi ludźmi?", + "gad7Options": { + "0": "Wcale nie dokuczały", + "1": "Kilka dni", + "2": "Więcej niż połowę dni", + "3": "Niemal codziennie" + }, + "functionalTitle": "Jeżeli zaznaczył/-a Pan/Pani którekolwiek z problemów, jak bardzo utrudniły one Panu/Pani wykonywanie pracy, zajmowanie się domem lub relacje z innymi ludźmi?", "functional": { - "0": "Wcale nie utrudniły", + "0": "W ogóle nie utrudniły", "1": "Trochę utrudniły", "2": "Bardzo utrudniły", - "3": "Skrajnie utrudniły" + "3": "Niezmiernie utrudniły" }, "functionalOptionalHint": "Opcjonalne: możesz wysłać bez odpowiedzi na to pytanie.", "result": { "title": "Twój wynik", "totalLabel": "Wynik łączny", "bandLabel": "Zakres dla tego przesiewu", - "takeAnother": "Wykonaj kolejne sprawdzenie" + "takeAnother": "Wykonaj kolejne sprawdzenie", + "scoreOf": "z {max}", + "firstCheckIn": "To Twój pierwszy wynik w tym kwestionariuszu. Od następnego zobaczysz tu zmianę.", + "nextStep": "Możesz powtórzyć ten kwestionariusz za dwa tygodnie lub później, aby śledzić zmianę. Jeśli coś Cię niepokoi, porozmawiaj w każdej chwili z profesjonalistą." }, "band": { "PHQ9": { @@ -11009,23 +11018,23 @@ "items": { "phq9": { "1": "Niewielkie zainteresowanie lub odczuwanie przyjemności z wykonywania czynności", - "2": "Uczucie przygnębienia, depresji lub beznadziejności", - "3": "Kłopoty z zaśnięciem lub utrzymaniem snu albo zbyt długi sen", + "2": "Uczucie smutku, przygnębienia lub beznadziejności", + "3": "Kłopoty z zaśnięciem lub przerywany sen, albo zbyt długi sen", "4": "Uczucie zmęczenia lub brak energii", "5": "Brak apetytu lub przejadanie się", - "6": "Złe myślenie o sobie — poczucie, że jest się nieudacznikiem lub że zawiodło się siebie albo rodzinę", - "7": "Trudności ze skupieniem się, na przykład podczas czytania gazety lub oglądania telewizji", - "8": "Poruszanie się lub mówienie tak wolno, że inni mogli to zauważyć? Albo przeciwnie — niepokój lub pobudzenie powodujące, że poruszasz się znacznie więcej niż zwykle", - "9": "Myśli, że lepiej byłoby umrzeć albo chęć zrobienia sobie w jakiś sposób krzywdy" + "6": "Poczucie niezadowolenia z siebie — lub uczucie, że jest się do niczego, albo że zawiódł/zawiodła Pan/Pani siebie lub rodzinę", + "7": "Problemy ze skupieniem się na przykład przy czytaniu gazety lub oglądaniu telewizji", + "8": "Poruszanie się lub mówienie tak wolno, że inni mogliby to zauważyć? Albo wręcz przeciwnie — niemożność usiedzenia w miejscu lub podenerwowanie powodujące ruchliwość znacznie większą niż zwykle", + "9": "Myśli, że lepiej byłoby umrzeć, albo chęć zrobienia sobie jakiejś krzywdy" }, "gad7": { - "1": "Odczuwanie zdenerwowania, lęku lub napięcia", - "2": "Niemożność przerwania lub kontrolowania zamartwiania się", - "3": "Zbytnie zamartwianie się różnymi sprawami", - "4": "Trudności z odprężeniem się", - "5": "Niepokój tak duży, że trudno usiedzieć w miejscu", - "6": "Łatwe wpadanie w rozdrażnienie lub irytację", - "7": "Odczuwanie lęku, jakby miało się zdarzyć coś strasznego" + "1": "Czuł(a) się Pan(i) podenerwowany(a), niespokojny(a), mocno spięty(a)", + "2": "Nie mógł(a) Pan(i) przestać się martwić albo zapanować nad tym", + "3": "Za bardzo się Pan(i) martwił(a) różnymi rzeczami", + "4": "Miał(a) Pan(i) trudności z relaksowaniem się", + "5": "Był(a) Pan(i) tak niespokojny(a), że nie mógł(a) usiedzieć na miejscu", + "6": "Łatwo stawał(a) się Pan(i) rozdrażniony(a) lub poirytowany(a)", + "7": "Obawiał(a) się Pan(i), tak jakby miało się stać coś strasznego" }, "who5": { "1": "Czułam się wesoła/Czułem się wesoły i w dobrym nastroju", @@ -11082,7 +11091,6 @@ "who5": "Test samopoczucia, 5 pytań. Odpowiadaj, myśląc o ostatnich dwóch tygodniach; wyższe cyfry oznaczają lepsze samopoczucie.", "sci": "Test snu, 8 pytań. Większość pytań dotyczy ostatniego miesiąca; wyższe wyniki oznaczają lepszy sen." }, - "progress": "{current} / {total}", "next": "Dalej", "lastResult": "Ostatni test", "lastScore": "Ostatni wynik", @@ -11104,6 +11112,12 @@ "5": "Cały czas" }, "stems": { + "phq9": { + "period": "Jak często w ciągu ostatnich 2 tygodni dokuczały Panu/Pani następujące problemy?" + }, + "gad7": { + "period": "Jak często w ciągu ostatnich 2 tygodni dokuczały Panu(i) następujące problemy?" + }, "who5": { "period": "W ciągu ostatnich dwóch tygodni" } @@ -11120,6 +11134,43 @@ "worsened": "To osiąga zmianę o {threshold} pkt, którą badania uznają za istotną, w mniej korzystnym kierunku.", "within_threshold": "To mniej niż zmiana o {threshold} pkt, którą badania uznają za istotną." } + }, + "wizard": { + "progressLabel": "Pytanie {current} z {total}", + "followUpLabel": "Pytanie dodatkowe (nieobowiązkowe)", + "keyboardHint": "Klawisze od 1 do {count} wybierają odpowiedź.", + "allAnswered": "Odpowiedziano na wszystkie pytania ({total})" + }, + "review": { + "title": "Sprawdź odpowiedzi", + "description": "Nic nie zostanie zapisane przed wysłaniem, a każdą odpowiedź można jeszcze zmienić.", + "change": "Zmień", + "changeLabel": "Zmień odpowiedź na pytanie {number}", + "notAnswered": "Bez odpowiedzi", + "open": "Sprawdź odpowiedzi" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Twoje odpowiedzi wskazują na brak lub niewiele objawów depresji w ostatnich dwóch tygodniach.", + "mild": "Twoje odpowiedzi wskazują na łagodne objawy depresji w ostatnich dwóch tygodniach.", + "moderate": "Twoje odpowiedzi wskazują na umiarkowane objawy depresji w ostatnich dwóch tygodniach.", + "modSevere": "Twoje odpowiedzi wskazują na umiarkowanie ciężkie objawy depresji w ostatnich dwóch tygodniach.", + "severe": "Twoje odpowiedzi wskazują na ciężkie objawy depresji w ostatnich dwóch tygodniach." + }, + "GAD7": { + "minimal": "Twoje odpowiedzi wskazują na brak lub niewiele objawów lęku w ostatnich dwóch tygodniach.", + "mild": "Twoje odpowiedzi wskazują na łagodne objawy lęku w ostatnich dwóch tygodniach.", + "moderate": "Twoje odpowiedzi wskazują na umiarkowane objawy lęku w ostatnich dwóch tygodniach.", + "severe": "Twoje odpowiedzi wskazują na ciężkie objawy lęku w ostatnich dwóch tygodniach." + }, + "WHO5": { + "low": "Twoje samopoczucie w ostatnich dwóch tygodniach było poniżej zakresu, który WHO-5 uznaje za dobry.", + "good": "Twoje samopoczucie w ostatnich dwóch tygodniach mieściło się w zakresie, który WHO-5 uznaje za dobry." + }, + "SCI": { + "belowThreshold": "Twój sen w ostatnim miesiącu mieścił się w zakresie, w którym badanie stojące za tym kwestionariuszem uznało bezsenność za prawdopodobną.", + "aboveThreshold": "Twój sen w ostatnim miesiącu był poza zakresem, który to badanie wiąże z bezsennością." + } } }, "records": { diff --git a/messages/pt-BR.json b/messages/pt-BR.json index 31a83e353..00030a2e0 100644 --- a/messages/pt-BR.json +++ b/messages/pt-BR.json @@ -10952,7 +10952,10 @@ "title": "Seu resultado", "totalLabel": "Pontuação total", "bandLabel": "Faixa para esta triagem", - "takeAnother": "Fazer outro check-in" + "takeAnother": "Fazer outro check-in", + "scoreOf": "de {max}", + "firstCheckIn": "Este é o seu primeiro resultado com este questionário. A partir do próximo, a mudança aparece aqui.", + "nextStep": "Você pode repetir este questionário daqui a duas semanas ou depois para acompanhar a mudança. Se algo preocupar você, fale com um profissional de saúde a qualquer momento." }, "band": { "PHQ9": { @@ -11040,7 +11043,6 @@ "who5": "Check de bem-estar de 5 perguntas. Responda sobre as últimas duas semanas; números mais altos significam melhor bem-estar.", "sci": "Check de sono de 8 perguntas. A maioria das perguntas trata do último mês; pontuações mais altas significam melhor sono." }, - "progress": "{current} / {total}", "next": "Próximo", "lastResult": "Último teste", "lastScore": "Último resultado", @@ -11065,6 +11067,43 @@ "worsened": "Isso alcança a mudança de {threshold} pontos que os estudos consideram significativa, na direção menos favorável.", "within_threshold": "Isso é menor do que a mudança de {threshold} pontos que os estudos consideram significativa." } + }, + "wizard": { + "progressLabel": "Pergunta {current} de {total}", + "followUpLabel": "Pergunta adicional (opcional)", + "keyboardHint": "As teclas de 1 a {count} escolhem uma resposta.", + "allAnswered": "Todas as {total} perguntas respondidas" + }, + "review": { + "title": "Conferir respostas", + "description": "Nada é salvo antes do envio, e cada resposta ainda pode ser alterada.", + "change": "Alterar", + "changeLabel": "Alterar a resposta da pergunta {number}", + "notAnswered": "Sem resposta", + "open": "Conferir respostas" + }, + "bandMeaning": { + "PHQ9": { + "minimal": "Suas respostas indicam poucos ou nenhum sintoma de depressão nas últimas duas semanas.", + "mild": "Suas respostas indicam sintomas leves de depressão nas últimas duas semanas.", + "moderate": "Suas respostas indicam sintomas moderados de depressão nas últimas duas semanas.", + "modSevere": "Suas respostas indicam sintomas moderadamente graves de depressão nas últimas duas semanas.", + "severe": "Suas respostas indicam sintomas graves de depressão nas últimas duas semanas." + }, + "GAD7": { + "minimal": "Suas respostas indicam poucos ou nenhum sintoma de ansiedade nas últimas duas semanas.", + "mild": "Suas respostas indicam sintomas leves de ansiedade nas últimas duas semanas.", + "moderate": "Suas respostas indicam sintomas moderados de ansiedade nas últimas duas semanas.", + "severe": "Suas respostas indicam sintomas graves de ansiedade nas últimas duas semanas." + }, + "WHO5": { + "low": "Seu bem-estar nas últimas duas semanas ficou abaixo da faixa que o WHO-5 considera boa.", + "good": "Seu bem-estar nas últimas duas semanas ficou na faixa que o WHO-5 considera boa." + }, + "SCI": { + "belowThreshold": "Seu sono no último mês ficou na faixa em que o estudo por trás deste questionário considerou provável uma insônia.", + "aboveThreshold": "Seu sono no último mês ficou fora da faixa que esse estudo associa à insônia." + } } }, "records": { diff --git a/src/__tests__/baselines/source-function-lines.json b/src/__tests__/baselines/source-function-lines.json index 86967afbb..6ee015b92 100644 --- a/src/__tests__/baselines/source-function-lines.json +++ b/src/__tests__/baselines/source-function-lines.json @@ -1,7 +1,7 @@ { "$comment": "Production functions over 200 lines, keyed \"file :: name\", and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.", "count": 411, - "total": 148369, + "total": 148357, "entries": { "app/achievements/page.tsx :: AchievementsPage": 206, "app/api/admin/backups/upload/route.ts :: apiHandler(arg)": 314, @@ -210,8 +210,8 @@ "components/medications/wizard/medication-wizard-dialog.tsx :: WizardDialogShell": 516, "components/medications/wizard/steps/step3-dose.tsx :: Step3Dose": 222, "components/medications/wizard/steps/step8-summary.tsx :: Step8Summary": 230, - "components/mental-health/check-in-wizard.tsx :: CheckInWizard": 214, - "components/mental-health/mental-wellbeing.tsx :: MentalWellbeing": 210, + "components/mental-health/check-in-wizard.tsx :: CheckInWizard": 205, + "components/mental-health/mental-wellbeing.tsx :: MentalWellbeing": 209, "components/mood/manage/tag-groups-card.tsx :: TagGroupsCard": 256, "components/mood/manage/tag-manager-card.tsx :: TagManagerCard": 270, "components/mood/mood-form.tsx :: MoodForm": 513, diff --git a/src/__tests__/baselines/version-stamp-comments.json b/src/__tests__/baselines/version-stamp-comments.json index 2efe81ae5..27b89b3ca 100644 --- a/src/__tests__/baselines/version-stamp-comments.json +++ b/src/__tests__/baselines/version-stamp-comments.json @@ -1,7 +1,7 @@ { "$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.", - "count": 2957, - "total": 10516, + "count": 2954, + "total": 10508, "entries": { "__tests__/account-payload-consumer-guard.test.ts": 2, "__tests__/acting-account-boundary-guard.test.ts": 7, @@ -1245,10 +1245,7 @@ "components/mental-health/__tests__/mental-wellbeing.test.tsx": 3, "components/mental-health/assessment-history-chart.tsx": 4, "components/mental-health/assessment-history.tsx": 2, - "components/mental-health/assessment-result.tsx": 2, "components/mental-health/assessment-trend-note.tsx": 1, - "components/mental-health/check-in-nav.ts": 2, - "components/mental-health/check-in-wizard.tsx": 4, "components/mental-health/crisis-card.tsx": 1, "components/mental-health/instrument-card.tsx": 5, "components/mental-health/instrument-detail.tsx": 3, diff --git a/src/components/mental-health/__tests__/check-in-nav.test.ts b/src/components/mental-health/__tests__/check-in-nav.test.ts index 784690c16..a85fdea9d 100644 --- a/src/components/mental-health/__tests__/check-in-nav.test.ts +++ b/src/components/mental-health/__tests__/check-in-nav.test.ts @@ -2,19 +2,19 @@ import { describe, it, expect } from "vitest"; import { UNANSWERED, + checkInSteps, + firstUnanswered, isComplete, - isLastStep, nextStep, prevStep, } from "../check-in-nav"; /** - * The one-question-at-a-time check-in is LINEAR: the steps are exactly the - * questions `1..itemCount`, backward navigation is always allowed, and the - * last question arms the submit directly (v1.27.6 — no review/summary step). - * These are the contracts the wizard's advance / back behaviour rests on - * (the SSR-only test convention can't drive the clicks, so the pure grammar - * is pinned here — the medication wizard's `wizard-payload` precedent). + * The check-in is LINEAR and the same for every instrument: the items, the + * PHQ-9's optional follow-up, then a review step that is the only place that + * submits. These are the contracts the wizard's advance / back behaviour + * rests on (the SSR-only component tests cannot drive clicks; the e2e + * journey drives them end to end). */ const PHQ9 = 9; @@ -24,30 +24,37 @@ function blank(count: number): number[] { return Array(count).fill(UNANSWERED); } -describe("check-in step grammar (v1.27.6 — questions only, no review step)", () => { - it("advances forward and stays put on the last question (submit, not advance)", () => { - expect(nextStep(1, PHQ9)).toBe(2); - expect(nextStep(8, PHQ9)).toBe(9); - // The last question is the end of the line — no review step behind it. - expect(nextStep(9, PHQ9)).toBe(9); - expect(nextStep(7, GAD7)).toBe(7); +describe("check-in step layout", () => { + it("puts the review step after the last question of every instrument", () => { + expect(checkInSteps(PHQ9, true)).toEqual({ + questions: 10, + functionalStep: 10, + review: 11, + }); + expect(checkInSteps(GAD7, false)).toEqual({ + questions: 7, + functionalStep: null, + review: 8, + }); + expect(checkInSteps(5, false).review).toBe(6); + expect(checkInSteps(8, false).review).toBe(9); + }); + + it("advances forward and stops at the review step", () => { + const { review } = checkInSteps(PHQ9, true); + expect(nextStep(1, review)).toBe(2); + expect(nextStep(10, review)).toBe(11); + expect(nextStep(11, review)).toBe(11); }); it("steps back, clamped at the first question", () => { - expect(prevStep(9)).toBe(8); + expect(prevStep(11)).toBe(10); expect(prevStep(2)).toBe(1); expect(prevStep(1)).toBe(1); }); - - it("identifies the last question per instrument", () => { - expect(isLastStep(8, PHQ9)).toBe(false); - expect(isLastStep(9, PHQ9)).toBe(true); - expect(isLastStep(6, GAD7)).toBe(false); - expect(isLastStep(7, GAD7)).toBe(true); - }); }); -describe("completeness (gates the submit on the last question)", () => { +describe("completeness (gates the review and the submit)", () => { it("isComplete requires every item answered", () => { expect(isComplete(blank(PHQ9))).toBe(false); const partial = blank(PHQ9).map((_, i) => (i < 5 ? 1 : UNANSWERED)); @@ -55,4 +62,10 @@ describe("completeness (gates the submit on the last question)", () => { expect(isComplete(blank(PHQ9).map(() => 0))).toBe(true); expect(isComplete(blank(GAD7).map(() => 3))).toBe(true); }); + + it("names the first unanswered item's step", () => { + expect(firstUnanswered(blank(GAD7))).toBe(1); + expect(firstUnanswered([0, 1, UNANSWERED, 2])).toBe(3); + expect(firstUnanswered([0, 1, 2, 3])).toBeNull(); + }); }); diff --git a/src/components/mental-health/__tests__/mental-wellbeing.test.tsx b/src/components/mental-health/__tests__/mental-wellbeing.test.tsx index cd074c4e3..fb1f2a260 100644 --- a/src/components/mental-health/__tests__/mental-wellbeing.test.tsx +++ b/src/components/mental-health/__tests__/mental-wellbeing.test.tsx @@ -257,7 +257,7 @@ describe("WHO-5 / SCI on the same infrastructure (v1.27.9)", () => { // English items are the validated wording for en — no translation note. expect(html).not.toContain('data-slot="check-in-validated-note"'); // No functional follow-up outside the PHQ-9: 5 steps total. - expect(html).toContain("1 / 5"); + expect(html).toContain("Question 1 of 5"); }); it("SCI wizard opens on item 1 with the item-specific duration anchors and section stem", () => { @@ -276,7 +276,7 @@ describe("WHO-5 / SCI on the same infrastructure (v1.27.9)", () => { for (const v of ["4", "3", "2", "1", "0"] as const) { expect(html).toContain(mh.sciOptions.duration[v]); } - expect(html).toContain("1 / 8"); + expect(html).toContain("Question 1 of 8"); }); it("shows the gentle PHQ-9 pointer on a WHO-5 total of 50 or below", () => { diff --git a/src/components/mental-health/assessment-result.tsx b/src/components/mental-health/assessment-result.tsx index 321e6389b..df36ed551 100644 --- a/src/components/mental-health/assessment-result.tsx +++ b/src/components/mental-health/assessment-result.tsx @@ -1,50 +1,58 @@ "use client"; /** - * v1.25.3 — the contained result block: instrument header → result card → - * crisis card (on a positive PHQ-9 item 9) → required attribution → "take - * another". Content is unchanged from the v1.25 monolith; it is re-housed as - * one stack inside the page spine instead of loose top-level sections. + * The result of one check-in, the same layout for every instrument. * - * v1.27.9 — direction-aware follow-up hints from the registry: PHQ-9/GAD-7 - * keep the ≥10 professional-help suggestion, a WHO-5 total of 50 or below - * gets a gentle pointer to the PHQ-9 check-in (per the WHO scoring comment), - * and an SCI total of 16 or below gets the paper's neutral band wording. - * The attribution footer now comes from the instrument definition. + * Order: the crisis card first when PHQ-9 item 9 was answered above zero + * (support before numbers), then one result card with the score on the + * instrument's own scale, its band, what the band means in plain words, the + * change since the last check-in measured against the meaningful-change + * threshold, and the next step: the direction-aware follow-up hint when the + * score crosses the instrument's threshold, a routine note otherwise. The + * attribution and "take another" close the view. * - * SAFETY: the crisis set rides the POST response (`crisis`) on any non-zero - * item 9 and renders immediately; this surface never invites an AI Coach - * conversation about item content. + * Every number and verdict is the server's (the create response and the + * history read's `trends`); nothing is re-scored here. SAFETY: this surface + * never invites an AI Coach conversation about item content. */ import { useEffect, useRef } from "react"; import { ArrowLeft } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { useTranslations } from "@/lib/i18n/context"; +import { PageHeader } from "@/components/ui/page-header"; +import { useFormatters, useTranslations } from "@/lib/i18n/context"; import { INSTRUMENTS, needsFollowUp } from "@/lib/mental-health/instruments"; +import { AssessmentTrendNote } from "./assessment-trend-note"; import { CrisisCard } from "./crisis-card"; -import type { CreateResponse } from "./types"; -import { PageHeader } from "@/components/ui/page-header"; +import type { AssessmentTrendDto, CreateResponse } from "./types"; export function AssessmentResult({ result, + trend, onTakeAnother, onBack, }: { result: CreateResponse; + /** This instrument's trend, once the history read includes this result. */ + trend?: AssessmentTrendDto; onTakeAnother: () => void; onBack: () => void; }) { const { t } = useTranslations(); - const instrument = result.assessment.instrument; + const { integer } = useFormatters(); + const { assessment } = result; + const instrument = assessment.instrument; const def = INSTRUMENTS[instrument]; const key = def.i18nKey; + const band = assessment.severityBand; + // The trend counts only once it ends on this very result. + const ownTrend = + trend && trend.latest.takenAt === assessment.takenAt ? trend : undefined; // a11y: on submit the wizard unmounts and this view mounts; move focus to the - // result heading so keyboard / screen-reader users land on the new content - // rather than being dropped to . + // result heading so keyboard / screen-reader users land on the new content. const headingRef = useRef(null); useEffect(() => { headingRef.current?.focus(); @@ -54,6 +62,8 @@ export function AssessmentResult({
- ); - })} - - - {isError && ( -

- {t("mentalHealth.error")} -

- )} - + )}
@@ -233,30 +222,23 @@ export function CheckInWizard({ type="button" variant="ghost" className="h-11" - onClick={() => setStep((s) => prevStep(s))} + onClick={() => { + setFromReview(false); + setStep((s) => prevStep(s)); + }} disabled={step === 1 || isPending} data-slot="check-in-back" > {t("mentalHealth.back")} - {!onLast ? ( - - ) : ( + {onReview ? ( + ) : ( + )}
); } + +function CheckInProgress({ + answered, + current, + total, + label, +}: { + answered: number; + current: number; + total: number; + label: string; +}) { + return ( +
+

+ {label} +

+ +
+ ); +} + +function QuestionStep({ + instrument, + step, + onFunctional, + selected, + wording, + headingRef, + disabled, + onChoose, +}: { + instrument: InstrumentId; + step: number; + onFunctional: boolean; + selected: number; + wording: Wording; + headingRef: RefObject; + disabled: boolean; + onChoose: (value: number) => void; +}) { + const { t } = useTranslations(); + const def = INSTRUMENTS[instrument]; + const index = step - 1; + const stem = onFunctional ? null : stemKey(instrument, index); + const question = onFunctional + ? wording(FUNCTIONAL_TITLE_KEY) + : wording(itemKey(instrument, index)); + const scale = onFunctional ? FUNCTIONAL_SCALE : def.optionOrder; + const options = scale.map((value) => ({ + value, + label: onFunctional + ? wording(functionalOptionKey(value)) + : wording(optionLabelKey(instrument, index, value)), + })); + const questionId = `check-in-question-${instrument}-${step}`; + + return ( +
+ {stem && ( +

+ {wording(`mentalHealth.stems.${stem}`)} +

+ )} +

+ {question} +

+ {onFunctional && ( +

+ {t("mentalHealth.functionalOptionalHint")} +

+ )} + +

+ {t("mentalHealth.wizard.keyboardHint", { count: options.length })} +

+
+ ); +} + +/** + * The answer control every instrument shares: a radio group of full-width + * options in the order the source form prints them. + */ +function AnswerGroup({ + labelledBy, + options, + selected, + disabled, + onChoose, +}: { + labelledBy: string; + options: ReadonlyArray<{ value: number; label: string }>; + selected: number; + disabled: boolean; + onChoose: (value: number) => void; +}) { + const refs = useRef>([]); + const selectedIndex = options.findIndex((o) => o.value === selected); + const tabIndexAt = selectedIndex >= 0 ? selectedIndex : 0; + + function onKeyDown(event: KeyboardEvent) { + const focused = refs.current.findIndex( + (el) => el === document.activeElement, + ); + const from = focused >= 0 ? focused : tabIndexAt; + const last = options.length - 1; + const move: Record = { + ArrowDown: from === last ? 0 : from + 1, + ArrowRight: from === last ? 0 : from + 1, + ArrowUp: from === 0 ? last : from - 1, + ArrowLeft: from === 0 ? last : from - 1, + Home: 0, + End: last, + }; + if (event.key in move) { + event.preventDefault(); + refs.current[move[event.key]]?.focus(); + return; + } + const position = Number.parseInt(event.key, 10); + if (position >= 1 && position <= options.length && !disabled) { + event.preventDefault(); + onChoose(options[position - 1].value); + } + } + + return ( +
+ {options.map((option, i) => { + const checked = option.value === selected; + return ( + + ); + })} +
+ ); +} + +function ReviewStep({ + instrument, + items, + functional, + functionalStep, + wording, + headingRef, + onEdit, +}: { + instrument: InstrumentId; + items: readonly number[]; + functional: number; + functionalStep: number | null; + wording: Wording; + headingRef: RefObject; + onEdit: (step: number) => void; +}) { + const { t } = useTranslations(); + const rows = items.map((value, index) => ({ + step: index + 1, + question: wording(itemKey(instrument, index)), + answer: wording(optionLabelKey(instrument, index, value)), + value: value as number | null, + stem: stemKey(instrument, index), + })); + if (functionalStep !== null) { + rows.push({ + step: functionalStep, + question: wording(FUNCTIONAL_TITLE_KEY), + answer: + functional >= 0 + ? wording(functionalOptionKey(functional)) + : t("mentalHealth.review.notAnswered"), + value: null, + stem: null, + }); + } + + return ( +
+

+ {t("mentalHealth.review.title")} +

+

{t("mentalHealth.review.description")}

+
    + {rows.map((row, i) => ( +
  1. + {row.stem && row.stem !== rows[i - 1]?.stem && ( +

    + {wording(`mentalHealth.stems.${row.stem}`)} +

    + )} +
    + {row.question} + onEdit(row.step)} + aria-label={t("mentalHealth.review.changeLabel", { + number: row.step, + })} + data-review-edit={row.step} + > + {t("mentalHealth.review.change")} + +
    + + {row.answer} + {row.value !== null && ( + + {" "} + ({row.value}) + + )} + +
  2. + ))} +
+
+ ); +} diff --git a/src/components/mental-health/instrument-card.tsx b/src/components/mental-health/instrument-card.tsx index b5360a63f..d56e313f1 100644 --- a/src/components/mental-health/instrument-card.tsx +++ b/src/components/mental-health/instrument-card.tsx @@ -89,6 +89,7 @@ export function InstrumentCard({ {/* Source / licence — a discreet info control at the top-right of the header, at the heading's height. A sibling of the detail-open @@ -187,7 +188,12 @@ export function InstrumentCard({ {canManageMind && (
-
diff --git a/src/components/mental-health/mental-wellbeing.tsx b/src/components/mental-health/mental-wellbeing.tsx index a6341fb47..a51b548ef 100644 --- a/src/components/mental-health/mental-wellbeing.tsx +++ b/src/components/mental-health/mental-wellbeing.tsx @@ -203,11 +203,7 @@ export function MentalWellbeing() { )} - {/* The pointer to mood history that used to sit here is gone. The - cross-link on `/mood` still points at this page, which is the - direction that earns its keep — someone logging a mood may not - know the screeners exist, while someone who came here for a - screener already knows where their mood diary is. */} + {/* No mood pointer here: `/mood` links to this page instead. */} {/* Per-instrument detail, opened from a card body: last result, trend chart, dated history, Start — the Verlauf lives here, @@ -255,6 +251,9 @@ export function MentalWellbeing() { {phase === "result" && result && ( trend.instrument === result.assessment.instrument, + )} onTakeAnother={backToLanding} onBack={backToLanding} /> diff --git a/src/lib/mental-health/__tests__/instrument-wording.test.ts b/src/lib/mental-health/__tests__/instrument-wording.test.ts index 151a48dcb..9cf9a6fba 100644 --- a/src/lib/mental-health/__tests__/instrument-wording.test.ts +++ b/src/lib/mental-health/__tests__/instrument-wording.test.ts @@ -122,6 +122,62 @@ describe("instrument wording", () => { }); }); +describe("validated languages carry the published wording", () => { + // Spot checks against the published forms named at the locale lists in + // instruments.ts: the places where the bundles had drifted (an informal + // follow-up question, a reworded anchor, a modernised spelling, the + // Italian GAD-7 anchor that differs from the Italian PHQ-9 one). + const PUBLISHED: Record> = { + de: { + "mentalHealth.stems.phq9.period": + "Wie oft fühlten Sie sich im Verlauf der letzten 2 Wochen durch die folgenden Beschwerden beeinträchtigt?", + "mentalHealth.items.phq9.7": + "Schwierigkeiten, sich auf etwas zu konzentrieren, z. B. beim Zeitungslesen oder Fernsehen", + "mentalHealth.items.gad7.5": + "Rastlosigkeit, so dass Stillsitzen schwer fällt", + "mentalHealth.items.gad7.7": + "Gefühl der Angst, so als würde etwas Schlimmes passieren", + "mentalHealth.functional.2": "Stark erschwert", + "mentalHealth.who5Options.5": "Die ganze Zeit", + }, + es: { + "mentalHealth.options.3": "Casi cada día", + "mentalHealth.items.phq9.9": + "Pensamientos de que estaría mejor muerto/a o de querer hacerse daño de algún modo", + "mentalHealth.items.gad7.1": + "Sentirse nervioso/a, angustiado/a o muy tenso/a", + }, + it: { + "mentalHealth.options.2": "Per più della metà dei giorni", + "mentalHealth.gad7Options.2": "Per oltre la metà dei giorni", + "mentalHealth.items.phq9.2": "Sentirsi giù, triste o disperato/a", + }, + pl: { + "mentalHealth.options.0": "Wcale nie dokuczały", + "mentalHealth.items.gad7.1": + "Czuł(a) się Pan(i) podenerwowany(a), niespokojny(a), mocno spięty(a)", + }, + fr: { + "mentalHealth.gad7Options.2": "Plus de la moitié du temps", + "mentalHealth.items.gad7.1": + "Un sentiment de nervosité, d’anxiété ou de tension", + }, + }; + + it.each(Object.keys(PUBLISHED))("%s", (locale) => { + const flat = bundle(locale); + for (const [key, text] of Object.entries(PUBLISHED[locale])) { + expect(flat.get(key), `${locale} ${key}`).toBe(text); + } + }); + + it("keeps the German follow-up question in the published formal address", () => { + const title = bundle("de").get("mentalHealth.functionalTitle") ?? ""; + expect(title).toMatch(/^Wenn eines oder mehrere der bisher/); + expect(title).not.toMatch(/\b(?:du|dich|dir|dein\w*)\b/i); + }); +}); + describe("no surface renders instrument wording through a bare t()", () => { // Item text, answer labels and stems must go through `instrumentText`. A // bare `t()` on one of these keys reads the bundle whatever the language. diff --git a/src/lib/mental-health/__tests__/instruments.test.ts b/src/lib/mental-health/__tests__/instruments.test.ts index 7808e53f0..e585b8e1f 100644 --- a/src/lib/mental-health/__tests__/instruments.test.ts +++ b/src/lib/mental-health/__tests__/instruments.test.ts @@ -166,6 +166,7 @@ describe("mental-health instruments", () => { it("resolves option-label keys per scheme (shared vs SCI per-item groups)", () => { expect(optionLabelKey("PHQ9", 0, 2)).toBe("mentalHealth.options.2"); + expect(optionLabelKey("GAD7", 0, 2)).toBe("mentalHealth.gad7Options.2"); expect(optionLabelKey("WHO5", 4, 5)).toBe("mentalHealth.who5Options.5"); // SCI: items 1–2 duration, 3 nights, 4 quality, 5–7 impact, 8 problem duration. expect(optionLabelKey("SCI", 0, 4)).toBe( @@ -185,29 +186,37 @@ describe("mental-health instruments", () => { ); }); - it("maps recall stems (WHO-5 one stem; SCI three sections; PHQ/GAD none)", () => { + it("maps every item to its form's stem (PHQ/GAD/WHO-5 one each; SCI three sections)", () => { expect(stemKey("WHO5", 0)).toBe("who5.period"); expect(stemKey("SCI", 0)).toBe("sci.night"); expect(stemKey("SCI", 3)).toBe("sci.night"); expect(stemKey("SCI", 4)).toBe("sci.impact"); expect(stemKey("SCI", 7)).toBe("sci.finally"); - expect(stemKey("PHQ9", 0)).toBeNull(); - expect(stemKey("GAD7", 0)).toBeNull(); + expect(stemKey("PHQ9", 0)).toBe("phq9.period"); + expect(stemKey("PHQ9", 8)).toBe("phq9.period"); + expect(stemKey("GAD7", 6)).toBe("gad7.period"); + for (const def of Object.values(INSTRUMENTS)) { + expect(def.stemKeys).toHaveLength(def.itemCount); + } }); - it("declares validated item locales honestly (SCI = English only)", () => { - for (const locale of ["de", "en", "es", "fr", "it", "pl", "ko"]) { - expect(hasValidatedItems("WHO5", locale)).toBe(true); + it("declares validated item locales honestly, per instrument", () => { + // Each listed language reproduces a published version word for word + // (sources at the lists in instruments.ts). + for (const locale of ["de", "en", "es", "it", "pl"]) { expect(hasValidatedItems("PHQ9", locale)).toBe(true); } - // The Dutch wording has not been read against the published forms yet. - expect(hasValidatedItems("PHQ9", "nl")).toBe(false); - expect(hasValidatedItems("GAD7", "nl")).toBe(false); - expect(hasValidatedItems("WHO5", "nl")).toBe(false); - // Nor has the Brazilian Portuguese one. - expect(hasValidatedItems("PHQ9", "pt-BR")).toBe(false); - expect(hasValidatedItems("GAD7", "pt-BR")).toBe(false); - expect(hasValidatedItems("WHO5", "pt-BR")).toBe(false); + for (const locale of ["de", "en", "es", "fr", "it", "pl"]) { + expect(hasValidatedItems("GAD7", locale)).toBe(true); + expect(hasValidatedItems("WHO5", locale)).toBe(true); + } + // No obtainable French-for-France PHQ-9; no checkable Korean forms. + expect(hasValidatedItems("PHQ9", "fr")).toBe(false); + for (const id of ["PHQ9", "GAD7", "WHO5"] as const) { + expect(hasValidatedItems(id, "ko")).toBe(false); + expect(hasValidatedItems(id, "nl")).toBe(false); + expect(hasValidatedItems(id, "pt-BR")).toBe(false); + } expect(hasValidatedItems("SCI", "en")).toBe(true); expect(hasValidatedItems("SCI", "en-GB")).toBe(true); expect(hasValidatedItems("SCI", "de")).toBe(false); diff --git a/src/lib/mental-health/instrument-wording.ts b/src/lib/mental-health/instrument-wording.ts index 9f9fa9cd1..e7741370a 100644 --- a/src/lib/mental-health/instrument-wording.ts +++ b/src/lib/mental-health/instrument-wording.ts @@ -72,6 +72,10 @@ export const SOURCE_WORDING: Readonly> = { "mentalHealth.options.1": "Several days", "mentalHealth.options.2": "More than half the days", "mentalHealth.options.3": "Nearly every day", + "mentalHealth.gad7Options.0": "Not at all", + "mentalHealth.gad7Options.1": "Several days", + "mentalHealth.gad7Options.2": "More than half the days", + "mentalHealth.gad7Options.3": "Nearly every day", "mentalHealth.functionalTitle": "If you checked off any problems, how difficult have these problems made it for you to do your work, take care of things at home, or get along with other people?", "mentalHealth.functional.0": "Not difficult at all", @@ -110,6 +114,10 @@ export const SOURCE_WORDING: Readonly> = { "mentalHealth.sciOptions.problemDuration.3": "1–2 months", "mentalHealth.sciOptions.problemDuration.4": "I don't have a problem / < 1 month", + "mentalHealth.stems.phq9.period": + "Over the last 2 weeks, how often have you been bothered by any of the following problems?", + "mentalHealth.stems.gad7.period": + "Over the last 2 weeks, how often have you been bothered by the following problems?", "mentalHealth.stems.who5.period": "Over the last two weeks …", "mentalHealth.stems.sci.night": "Thinking about a typical night in the last month …", diff --git a/src/lib/mental-health/instruments.ts b/src/lib/mental-health/instruments.ts index 92575701d..6a3c32a32 100644 --- a/src/lib/mental-health/instruments.ts +++ b/src/lib/mental-health/instruments.ts @@ -11,9 +11,8 @@ * - PHQ-9: Kroenke 2001; GAD-7: Spitzer 2006 (public-domain Pfizer grant). * - WHO-5: WHO/UCN/MSD/MHE/2024.1 (© WHO 2024, CC BY-NC-SA 3.0 IGO) — raw * 0–25, × 4 → 0–100; "a percentage score below 50 … an indication for - * further assessment". Six app locales ship the WHO/PRU translations - * verbatim; Korean, which neither distribution carries, ships the - * published Kim 2010 translation instead. + * further assessment". Five other app languages ship the PRU + * translations WHO republishes, verbatim (sources at `WHO5_LOCALES`). * - SCI: Espie et al., BMJ Open 2014;4:e004183 (© Sleepio Ltd, licensed * free for non-commercial use, CC BY-NC) — 8 items, 0–4 each, total 0–32, * HIGHER = better sleep; "a score of ≤16 … seems reasonable to detect @@ -62,12 +61,12 @@ export interface InstrumentDefinition { optionNamespace: string; optionGroups?: readonly string[]; /** - * Recall-stem caption key per item under `mentalHealth.stems.` — the WHO-5 - * and SCI items are fragments under a shared stem on the source forms, so - * the wizard paints the applicable stem above the question. PHQ/GAD keep - * their frame in the instrument description line (shipped contract). + * Stem key per item under `mentalHealth.stems.`: the question the source + * form prints above its items (the PHQ-9 / GAD-7 "Over the last 2 weeks…", + * the WHO-5 "Over the last two weeks …", the SCI section stems). The + * wizard keeps it visible above every item. */ - stemKeys?: readonly string[]; + stemKeys: readonly string[]; /** Multiplier from item-sum to the reported total (WHO-5: × 4). */ scoreMultiplier: number; /** Inclusive REPORTED total range (WHO-5: 0–100 percentage). */ @@ -101,15 +100,58 @@ export interface InstrumentDefinition { attribution: string; } -/** - * The app languages whose PHQ-9 / GAD-7 / WHO-5 item wording is bundled from - * a published validated version. `nl` and `pt-BR` are absent on purpose: no - * published Dutch or Brazilian Portuguese form has been read and bundled yet, - * so those readers get the English items, answers and stems plus the honest - * note, and their bundles carry none of that wording. Adding a language here - * means bundling its validated form verbatim in the same change. +/* + * Which languages carry an instrument's own wording, and where it comes from. + * + * A language is listed only when its bundle reproduces a published version + * word for word: the question stem, every item, every answer option and, for + * the PHQ-9, the follow-up question. Each list below names its sources. A + * language that is not listed shows the English original with the honest + * note, and its bundle carries none of the instrument's keys + * (`instrument-wording.ts`). Adding a language means bundling its published + * form verbatim in the same change, with the source named here. + * + * Paper-form instructions ("mark your answer with a cross") and the + * row-end full stops of the printed tables are left out; nothing else is + * changed. The app's own copy may address the reader informally; the + * instruments keep the address form they were published in. */ -const ALL_APP_LOCALES = ["de", "en", "es", "fr", "it", "pl", "ko"] as const; + +/** + * PHQ-9. English: the Pfizer form (phqscreeners.com). The others are the + * official Pfizer translations, read from the PDFs as distributed: + * - de: "Gesundheitsfragebogen für Patienten (PHQ-9)", German for Germany, + * the PHQ-D wording of Löwe, Spitzer, Zipfel & Herzog (Pfizer 2002). + * - es: Spanish for Spain. it: Italian for Italy. pl: Polish for Poland. + * (ECFS Mental Health working group mirror of the Pfizer files: + * ecfs.eu/sites/default/files/general-content-files/working-groups/ + * Mental Health/PHQ9_ for .pdf) + * Not listed: fr (the France version could not be obtained; the bundle's + * wording matched neither it nor the Belgian one), ko (no published Korean + * form could be checked), nl and pt-BR (never bundled). + */ +const PHQ9_LOCALES = ["en", "de", "es", "it", "pl"] as const; + +/** + * GAD-7. English: the Pfizer form. de: Löwe B et al., "Gesundheitsfragebogen + * für Patienten (GAD-7)", German version © Bernd Löwe 2015, Universitätsklinikum + * Hamburg-Eppendorf (validation: Löwe et al., Med Care 2008;46:266-274). es, + * fr, it, pl: the Pfizer translations for Spain, France, Italy and Poland + * (same ECFS mirror, GAD7_ for .pdf). Not listed: ko, nl, + * pt-BR, as for the PHQ-9. + */ +const GAD7_LOCALES = ["en", "de", "es", "fr", "it", "pl"] as const; + +/** + * WHO-5. English: WHO/UCN/MSD/MHE/2024.1. de, es, fr, it, pl: the Psychiatric + * Research Unit translations (1998/1999 versions) that WHO republishes beside + * the 2024 English text (cdn.who.int …/five-well-being-index-(who-5)/ + * who-5_.pdf; the Spanish one via infocop.es). The Spanish sheet + * misprints item 4 as "descandado"; the bundle keeps "descansado". Not + * listed: ko (the Kim 2010 translation could not be checked against the + * paper), nl and pt-BR. + */ +const WHO5_LOCALES = ["en", "de", "es", "fr", "it", "pl"] as const; /** * The PHQ family is public-domain (Pfizer 2010); this is the canonical footer @@ -139,10 +181,13 @@ const PHQ9: InstrumentDefinition = { { key: "modSevere", min: 15, max: 19 }, { key: "severe", min: 20, max: 27 }, ], + // The form's own question, "Over the last 2 weeks, how often have you been + // bothered by any of the following problems?", stands above every item. + stemKeys: Array(9).fill("phq9.period"), actionThreshold: 10, // Item 9 ("Thoughts that you would be better off dead…") — the safety field. safetyItemIndex: 8, - validatedItemLocales: ALL_APP_LOCALES, + validatedItemLocales: PHQ9_LOCALES, attribution: PHQ_GAD_ATTRIBUTION, }; @@ -152,7 +197,10 @@ const GAD7: InstrumentDefinition = { itemCount: 7, itemMax: 3, optionOrder: [0, 1, 2, 3], - optionNamespace: "mentalHealth.options", + // Its own anchors: the Italian GAD-7 words option 2 differently from the + // Italian PHQ-9, so the two cannot share one set. + optionNamespace: "mentalHealth.gad7Options", + stemKeys: Array(7).fill("gad7.period"), scoreMultiplier: 1, minScore: 0, maxScore: 21, @@ -166,7 +214,7 @@ const GAD7: InstrumentDefinition = { ], actionThreshold: 10, safetyItemIndex: null, - validatedItemLocales: ALL_APP_LOCALES, + validatedItemLocales: GAD7_LOCALES, attribution: PHQ_GAD_ATTRIBUTION, }; @@ -198,18 +246,11 @@ const WHO5: InstrumentDefinition = { ], actionThreshold: 50, safetyItemIndex: null, - // WHO publishes official de/en/es/fr/it/pl versions — all bundled verbatim. - // There is no Korean one, so `ko` carries the published forward/back- - // translation from Kim 2010 instead, which is CC BY-NC and so - // redistributable. Its validation sample was community-dwelling elderly, - // not general adults — narrower than the other six arms. - validatedItemLocales: ALL_APP_LOCALES, + validatedItemLocales: WHO5_LOCALES, attribution: "World Health Organization. The World Health Organization-Five Well-Being " + "Index (WHO-5). Geneva: World Health Organization; 2024. Licence: " + - "CC BY-NC-SA 3.0 IGO. WHO does not endorse this application. Korean " + - "translation: Kim HJ, Moon YS, Son BK, Lee SK, Rho HJ, Kim DH. J Korean " + - "Geriatr Psychiatry 2010;14(2):90-96 (CC BY-NC 3.0).", + "CC BY-NC-SA 3.0 IGO. WHO does not endorse this application.", }; /** @@ -260,8 +301,10 @@ const SCI: InstrumentDefinition = { safetyItemIndex: null, // Validated in English (the CC BY-NC BMJ Open publication). French/Italian // validations exist in the literature but their item text is not openly - // redistributable, so every non-EN locale presents the English items with - // an honest note — never a self-made translation. + // redistributable, and no validated German version is published (a search + // in October 2026 found the German ISI, Gerber et al. 2016, not the SCI), + // so every non-EN locale presents the English items with an honest note — + // never a self-made translation. validatedItemLocales: ["en"], attribution: "Espie CA, Kyle SD, Hames P, et al. The Sleep Condition Indicator: a " + @@ -341,7 +384,7 @@ export function optionLabelKey( /** Stem caption key (under `mentalHealth.stems.`) for an item, or null. */ export function stemKey(id: InstrumentId, itemIndex: number): string | null { - return INSTRUMENTS[id].stemKeys?.[itemIndex] ?? null; + return INSTRUMENTS[id].stemKeys[itemIndex] ?? null; } /**