feat(i18n): Niederländisch (nl) als Sprache aufnehmen

NL war als Halbzustand angelegt (Piper-Modell nl_NL-mls-medium,
Chatterbox-nl.wav, LLM-Sprachname, Cartesia-Ausschluss, leeres
pronunciation.nl.yaml), fehlte aber im Sprachmenü und Backend-Mapping.

- app.js: NL in ALL_LANGS, LANG_BCP47, default_language-Auswahl und
  piper_voice-Liste (jeweils zwischen IT und PT)
- dependencies.py: LANG_TO_PIPER_VOICE nl -> nl_NL-mls-medium
  (kein GENDERED-Eintrag: nur eine Stimme, wie PT/AR)
- safety/emergency.py: NL-Texte in allen 4 Hinweis-Dicts

STT (faster-whisper large-v3) und LLM (_LANG_NAMES) unterstützen nl
bereits automatisch. Offen: deutsche Ordinalzahlen nur für language=de
(NL fällt durch, kosmetisch).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-28 15:13:29 +02:00
commit 3d3973dc16
3 changed files with 10 additions and 5 deletions

View file

@ -165,6 +165,7 @@ LANG_TO_PIPER_VOICE: dict[str, str] = {
"fr": "fr_FR-siwis-medium",
"es": "es_ES-sharvard-medium",
"it": "it_IT-paola-medium",
"nl": "nl_NL-mls-medium",
"pt": "pt_BR-faber-medium",
"pl": "pl_PL-darkman-medium",
"ar": "ar_JO-kareem-medium",

View file

@ -35,6 +35,7 @@ _NO_NOTIFY_NOTICE: dict[str, str] = {
"fr": "ATTENTION : aucune notification n'est encore configurée.",
"es": "ATENCIÓN: todavía no se ha configurado ninguna notificación.",
"it": "ATTENZIONE: non è ancora stata configurata alcuna notifica.",
"nl": "LET OP: er is nog geen melding ingesteld.",
"pt": "ATENÇÃO: ainda não há nenhuma notificação configurada.",
"pl": "UWAGA: Nie skonfigurowano jeszcze żadnego powiadomienia.",
"ar": "تنبيه: لم يتم إعداد أي إشعار بعد.",
@ -49,6 +50,7 @@ _SENT_NOTICE: dict[str, str] = {
"fr": "Les secours ont été alertés. Un proche a été prévenu.",
"es": "Se ha avisado a ayuda. Se notificó a un contacto.",
"it": "L'aiuto è stato allertato. Una persona di contatto è stata avvisata.",
"nl": "Er is hulp gewaarschuwd. Een contactpersoon is op de hoogte gesteld.",
"pt": "A ajuda foi avisada. Uma pessoa de contato foi notificada.",
"pl": "Pomoc została powiadomiona. Osoba kontaktowa została powiadomiona.",
"ar": "تم تنبيه المساعدة. تم إخطار شخص الاتصال.",
@ -64,6 +66,7 @@ _NO_RECIPIENTS_NOTICE: dict[str, str] = {
"fr": "Personne n'a pu être prévenu aucun contact d'urgence n'est configuré. Veuillez appeler vous-même le 112.",
"es": "No se pudo avisar a nadie: no hay contactos de emergencia configurados. Por favor, llame usted mismo al 112.",
"it": "Non è stato possibile avvisare nessuno: nessun contatto di emergenza è configurato. Per favore chiami lei stesso il 112.",
"nl": "Er kon niemand worden gewaarschuwd er zijn geen noodcontacten ingesteld. Bel zelf het alarmnummer 112.",
"pt": "Não foi possível avisar ninguém não há contactos de emergência configurados. Por favor, ligue você mesmo para o 112.",
"pl": "Nie udało się nikogo powiadomić nie skonfigurowano kontaktów alarmowych. Proszę samodzielnie zadzwonić pod numer 112.",
"ar": "تعذّر إخطار أي شخص لا توجد جهات اتصال للطوارئ. يرجى الاتصال بنفسك برقم الطوارئ 112.",
@ -79,6 +82,7 @@ _DELIVERY_FAILED_NOTICE: dict[str, str] = {
"fr": "La notification n'a pas pu être transmise. Veuillez réessayer ou appeler vous-même le 112.",
"es": "No se pudo entregar la notificación. Por favor, inténtelo de nuevo o llame usted mismo al 112.",
"it": "Non è stato possibile recapitare la notifica. Per favore riprovi o chiami lei stesso il 112.",
"nl": "De melding kon niet worden afgeleverd. Probeer het opnieuw of bel zelf het alarmnummer 112.",
"pt": "Não foi possível entregar a notificação. Por favor, tente novamente ou ligue você mesmo para o 112.",
"pl": "Nie udało się dostarczyć powiadomienia. Proszę spróbować ponownie lub samodzielnie zadzwonić pod numer 112.",
"ar": "تعذّر تسليم الإشعار. يرجى المحاولة مرة أخرى أو الاتصال بنفسك برقم الطوارئ 112.",

View file

@ -68,7 +68,7 @@ async function savePrefs(obj) {
const TTS_SUPPORTED = typeof window !== "undefined" && "speechSynthesis" in window;
const LANG_BCP47 = {
de: "de-DE", en: "en-US", fr: "fr-FR", es: "es-ES",
it: "it-IT", pt: "pt-BR", pl: "pl-PL", ar: "ar-SA", ru: "ru-RU", zh: "zh-CN",
it: "it-IT", nl: "nl-NL", pt: "pt-BR", pl: "pl-PL", ar: "ar-SA", ru: "ru-RU", zh: "zh-CN",
};
function isMobile() {
@ -359,7 +359,7 @@ async function copyText(text, btn) {
}
const ALL_LANGS = [["de","DE"],["en","EN"],["fr","FR"],["es","ES"],["it","IT"],
["pt","PT"],["pl","PL"],["ar","AR"],["ru","RU"],["zh","ZH"]];
["nl","NL"],["pt","PT"],["pl","PL"],["ar","AR"],["ru","RU"],["zh","ZH"]];
// --- Format-Validierung der Notfall-Profil-Felder ------------------------
const _isEmail = (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
@ -1955,12 +1955,12 @@ const FIELD_META = {
default_stt_provider: { ui: "combo", opts: ["openrouter","faster-whisper"], test: "stt" },
default_llm_provider: { ui: "combo", opts: ["openrouter","local-openai-compatible"], test: "llm" },
default_tts_provider: { ui: "combo", opts: ["openrouter","piper","chatterbox"], test: "tts" },
default_language: { ui: "combo", opts: ["de","en","fr","es","it","pt","pl","ar"],
labels: { de:"Deutsch",en:"Englisch",fr:"Französisch",es:"Spanisch",it:"Italienisch",pt:"Portugiesisch",pl:"Polnisch",ar:"Arabisch" } },
default_language: { ui: "combo", opts: ["de","en","fr","es","it","nl","pt","pl","ar"],
labels: { de:"Deutsch",en:"Englisch",fr:"Französisch",es:"Spanisch",it:"Italienisch",nl:"Niederländisch",pt:"Portugiesisch",pl:"Polnisch",ar:"Arabisch" } },
openrouter_llm_model: { ui: "datalist", test: "llm" },
openrouter_tts_model: { ui: "datalist", test: "tts", testProvider: "openrouter" },
openrouter_tts_voice: { ui: "combo", opts: ["Zephyr","Puck","Charon","Kore","Fenrir","Leda","Orus","Aoede","Callirrhoe","Enceladus","Algieba","Despina","Achernar"], test: "tts", testProvider: "openrouter" },
piper_voice: { ui: "combo", opts: ["de_DE-thorsten-high","de_DE-kerstin-low","en_US-lessac-high","en_US-amy-medium","en_US-ryan-high","en_GB-cori-high","fr_FR-siwis-medium","fr_FR-tom-medium","es_ES-sharvard-medium","it_IT-paola-medium","it_IT-riccardo-x_low","pt_BR-faber-medium","pl_PL-gosia-medium","pl_PL-darkman-medium","ar_JO-kareem-medium","ru_RU-irina-medium","ru_RU-ruslan-medium","zh_CN-huayan-medium"], test: "tts", testProvider: "piper" },
piper_voice: { ui: "combo", opts: ["de_DE-thorsten-high","de_DE-kerstin-low","en_US-lessac-high","en_US-amy-medium","en_US-ryan-high","en_GB-cori-high","fr_FR-siwis-medium","fr_FR-tom-medium","es_ES-sharvard-medium","it_IT-paola-medium","it_IT-riccardo-x_low","nl_NL-mls-medium","pt_BR-faber-medium","pl_PL-gosia-medium","pl_PL-darkman-medium","ar_JO-kareem-medium","ru_RU-irina-medium","ru_RU-ruslan-medium","zh_CN-huayan-medium"], test: "tts", testProvider: "piper" },
local_llm_system_prompt: { ui: "textarea", test: "llm" },
local_llm_temperature: { ui: "range", min: 0, max: 2, step: 0.1, test: "llm" },
local_llm_top_p: { ui: "range", min: 0, max: 1, step: 0.05, test: "llm" },