diff --git a/messages/de.json b/messages/de.json index 62d66abfb..3fbe7be8f 100644 --- a/messages/de.json +++ b/messages/de.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portugal, täglich 15:30 bis 0:30 Uhr)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Niederlande, rund um die Uhr, kostenlos, anonym, auch per Chat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (Belgien, rund um die Uhr, kostenlos, anonym)" } }, "instrumentDescription": { diff --git a/messages/en.json b/messages/en.json index 40930b705..a9d17bec0 100644 --- a/messages/en.json +++ b/messages/en.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portugal, daily 3.30 pm to 12.30 am)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Netherlands, 24/7, free, anonymous, also by chat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (Belgium, 24/7, free, anonymous)" } }, "instrumentDescription": { diff --git a/messages/es.json b/messages/es.json index f8877cd7f..88fa5e906 100644 --- a/messages/es.json +++ b/messages/es.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portugal, todos los días de 15:30 a 0:30)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Países Bajos, 24 horas, gratuito, anónimo, también por chat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (Bélgica, 24 horas, gratuito, anónimo)" } }, "instrumentDescription": { diff --git a/messages/fr.json b/messages/fr.json index 6d95bfc01..6bc7867c3 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portugal, tous les jours de 15h30 à 0h30)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Pays-Bas, 24h/24, 7j/7, gratuit et anonyme, aussi par chat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (Belgique, 24h/24, 7j/7, gratuit et anonyme)" } }, "instrumentDescription": { diff --git a/messages/it.json b/messages/it.json index 0196eeced..f0b82d04e 100644 --- a/messages/it.json +++ b/messages/it.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portogallo, tutti i giorni dalle 15:30 alle 0:30)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Paesi Bassi, 24 ore su 24, gratuito, anonimo, anche in chat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (Belgio, 24 ore su 24, gratuito, anonimo)" } }, "instrumentDescription": { diff --git a/messages/ko.json b/messages/ko.json index f1b5df923..190f1e83d 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (포르투갈, 매일 15:30~00:30)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (네덜란드, 24시간, 무료, 익명, 채팅 가능)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (벨기에, 24시간, 무료, 익명)" } }, "instrumentDescription": { diff --git a/messages/nl.json b/messages/nl.json index 772217c4e..2fa0ed1b9 100644 --- a/messages/nl.json +++ b/messages/nl.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portugal, dagelijks van 15.30 tot 0.30 uur)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Nederland, dag en nacht, gratis, anoniem, ook via chat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (België, dag en nacht, gratis, anoniem)" } }, "instrumentDescription": { diff --git a/messages/pl.json b/messages/pl.json index d0d5efacf..cb9b94acc 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portugalia, codziennie od 15:30 do 0:30)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Holandia, całą dobę, bezpłatnie, anonimowo, także przez czat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (Belgia, całą dobę, bezpłatnie, anonimowo)" } }, "instrumentDescription": { diff --git a/messages/pt-BR.json b/messages/pt-BR.json index 619751d54..dfa846444 100644 --- a/messages/pt-BR.json +++ b/messages/pt-BR.json @@ -11267,6 +11267,12 @@ }, "sosVozAmiga": { "name": "SOS Voz Amiga (Portugal, todos os dias das 15h30 às 0h30)" + }, + "zelfmoordpreventie113": { + "name": "113 Zelfmoordpreventie (Países Baixos, 24 horas, gratuito, anônimo, também por chat)" + }, + "zelfmoordlijn1813": { + "name": "Zelfmoordlijn 1813 (Bélgica, 24 horas, gratuito, anônimo)" } }, "instrumentDescription": { diff --git a/src/lib/mental-health/__tests__/crisis-resources.test.ts b/src/lib/mental-health/__tests__/crisis-resources.test.ts index 535faf119..657ff6cd6 100644 --- a/src/lib/mental-health/__tests__/crisis-resources.test.ts +++ b/src/lib/mental-health/__tests__/crisis-resources.test.ts @@ -53,13 +53,54 @@ describe("crisisResourcesForLocale", () => { } }); + it("gives a Dutch reader with no region 112, then the Dutch and Belgian lines by name", () => { + for (const locale of ["nl", "nl-SR"]) { + const set = crisisResourcesForLocale(locale); + expect(set.emergencyNumber, locale).toBe("112"); + expect(set.resources).toEqual([ + { + id: "zelfmoordpreventie113", + contacts: ["113", "0800-0113", "113.nl"], + }, + { id: "zelfmoordlijn1813", contacts: ["1813", "zelfmoord1813.be"] }, + { id: "findahelpline", contacts: ["findahelpline.com"] }, + ]); + } + const { t } = getServerTranslator("nl"); + expect( + t("mentalHealth.crisisResource.zelfmoordpreventie113.name"), + ).toContain("Nederland"); + expect(t("mentalHealth.crisisResource.zelfmoordlijn1813.name")).toContain( + "België", + ); + }); + + it("gives an explicit Dutch region 113 and an explicit Belgian one 1813", () => { + for (const locale of ["nl-NL", "nl_NL"]) { + const ids = crisisResourcesForLocale(locale).resources.map((r) => r.id); + expect(ids, locale).toEqual(["zelfmoordpreventie113", "findahelpline"]); + } + for (const locale of ["nl-BE", "nl_BE", "be"]) { + const ids = crisisResourcesForLocale(locale).resources.map((r) => r.id); + expect(ids, locale).toEqual(["zelfmoordlijn1813", "findahelpline"]); + } + }); + it("names every crisis resource in every language", () => { // History re-derives the set from the row's stored locale and renders it // in whatever language the reader uses now. const ids = new Set( - ["pt-BR", "pt-PT", "br", "de", "de-AT", "de-CH", "en-US", "fr"].flatMap( - (l) => crisisResourcesForLocale(l).resources.map((r) => r.id), - ), + [ + "pt-BR", + "pt-PT", + "br", + "de", + "de-AT", + "de-CH", + "en-US", + "fr", + "nl", + ].flatMap((l) => crisisResourcesForLocale(l).resources.map((r) => r.id)), ); for (const locale of locales) { const { t } = getServerTranslator(locale); diff --git a/src/lib/mental-health/crisis-resources.ts b/src/lib/mental-health/crisis-resources.ts index dbece16ec..402290cc3 100644 --- a/src/lib/mental-health/crisis-resources.ts +++ b/src/lib/mental-health/crisis-resources.ts @@ -149,6 +149,51 @@ const PT_BR_AMBIGUOUS: CrisisResourceSet = { ], }; +/** + * The Netherlands (explicit "nl-NL" / "nl_NL"). 112 is the emergency number. + * 113 Zelfmoordpreventie answers by phone on 113 or the free 0800-0113 and by + * chat on 113.nl, anonymously. + */ +const NL: CrisisResourceSet = { + emergencyNumber: "112", + resources: [ + { + id: "zelfmoordpreventie113", + contacts: ["113", "0800-0113", "113.nl"], + }, + { id: "findahelpline", contacts: ["findahelpline.com"] }, + ], +}; + +/** + * Belgium (explicit "nl-BE" / "nl_BE", or the bare region "be"). 112 is the + * emergency number. The Zelfmoordlijn answers 1813 around the clock and runs + * a chat in the evenings (zelfmoord1813.be). + */ +const BE: CrisisResourceSet = { + emergencyNumber: "112", + resources: [ + { id: "zelfmoordlijn1813", contacts: ["1813", "zelfmoord1813.be"] }, + { id: "findahelpline", contacts: ["findahelpline.com"] }, + ], +}; + +/** + * Dutch with no region signal. The app ships one Dutch bundle, `nl`, read in + * the Netherlands and in Flanders alike, and the account keeps no country to + * tell them apart. Following DE_AT_CH, the set leads with 112 (the emergency + * number in both countries) and names each country's line with the country + * in its label, so nothing reads as if it reached everyone. + */ +const NL_BE: CrisisResourceSet = { + emergencyNumber: "112", + resources: [ + ...NL.resources.filter((r) => r.id !== "findahelpline"), + ...BE.resources.filter((r) => r.id !== "findahelpline"), + { id: "findahelpline", contacts: ["findahelpline.com"] }, + ], +}; + /** * Resolve a crisis-resource set from the user's locale. Region detection is * coarse (locale prefix) and defaults to the international set — the goal is to @@ -156,8 +201,9 @@ const PT_BR_AMBIGUOUS: CrisisResourceSet = { * * A region-qualified tag ("de-AT", "de_CH", "pt-PT", …) resolves to the exact * country set when the caller has one to give us. The web stores the app - * locale, which is a bare "de" or the folded "pt-BR", so it lands on the - * combined sets: never assume Germany or Brazil when the region is unknown. + * locale, which is a bare "de", "nl" or the folded "pt-BR", so it lands on the + * combined sets: never assume Germany, the Netherlands or Brazil when the + * region is unknown. */ export function crisisResourcesForLocale( locale: string | null | undefined, @@ -173,5 +219,9 @@ export function crisisResourcesForLocale( // PT_BR_AMBIGUOUS. Any other Portuguese tag (pt, pt-AO, pt-MZ) lands here // too, and still gets 112 and the worldwide directory first. if (lc === "pt" || lc.startsWith("pt-")) return PT_BR_AMBIGUOUS; + if (lc === "nl-nl") return NL; + if (lc === "nl-be" || lc === "be") return BE; + // The bare "nl" the web stores, and any other Dutch tag: both countries. + if (lc === "nl" || lc.startsWith("nl-")) return NL_BE; return INTERNATIONAL; }