Internationalize UI text w/ german as first second lang

Adds rust-i18n and refactors all of the text copy in the app to use the
translation function so that the UI language can be dynamically updated
in the settings.
This commit is contained in:
2026-03-17 04:29:25 +00:00
parent 895e04d6ce
commit 6d5de33f55
24 changed files with 2924 additions and 820 deletions

457
locales/de.yml Normal file
View File

@@ -0,0 +1,457 @@
# Main menu
menu:
subtitle: 'Terminal-Tipptrainer'
adaptive_drill: 'Adaptive Lektion'
adaptive_drill_desc: 'Phonetische Woerter mit adaptiver Buchstabenfreischaltung'
code_drill: 'Code-Lektion'
code_drill_desc: 'Code-Syntax tippen ueben'
passage_drill: 'Textpassagen-Lektion'
passage_drill_desc: 'Passagen aus Buechern abtippen'
skill_tree: 'Faehigkeitenbaum'
skill_tree_desc: 'Fortschrittszweige ansehen und Lektionen starten'
keyboard: 'Tastatur'
keyboard_desc: 'Tastaturlayout und Tastenstatistiken erkunden'
statistics: 'Statistik'
statistics_desc: 'Tippstatistiken ansehen'
settings: 'Einstellungen'
settings_desc: 'keydr konfigurieren'
day_streak: ' | %{days} Tage Serie'
key_progress: ' Tastenfortschritt %{unlocked}/%{total} (%{mastered} gemeistert) | Ziel %{target} WPM%{streak}'
hint_start: '[1-3] Start'
hint_skill_tree: '[t] Faehigkeitenbaum'
hint_keyboard: '[b] Tastatur'
hint_stats: '[s] Statistik'
hint_settings: '[c] Einstellungen'
hint_quit: '[q] Beenden'
# Drill screen
drill:
title: ' Lektion '
mode_adaptive: 'Adaptiv'
mode_code: 'Code (ohne Wertung)'
mode_passage: 'Textpassage (ohne Wertung)'
focus_char: 'Fokus: ''%{ch}'''
focus_bigram: 'Fokus: "%{bigram}"'
focus_both: 'Fokus: ''%{ch}'' + "%{bigram}"'
header_wpm: 'WPM'
header_acc: 'Gen'
header_err: 'Feh'
code_source: ' Code-Quelle '
passage_source: ' Textquelle '
footer: '[ESC] Lektion beenden [Backspace] Loeschen'
keys_reenabled: 'Tasten nach %{ms}ms wieder aktiv'
hint_end: '[ESC] Lektion beenden'
hint_backspace: '[Backspace] Loeschen'
# Dashboard / drill result
dashboard:
title: ' Lektion abgeschlossen '
results: 'Ergebnisse'
unranked_note_prefix: ' (Ohne Wertung'
unranked_note_suffix: ' zaehlt nicht fuer den Faehigkeitenbaum)'
speed: ' Tempo: '
accuracy_label: ' Genauigkeit: '
time_label: ' Zeit: '
errors_label: ' Fehler: '
correct_detail: ' (%{correct}/%{total} korrekt)'
input_blocked: ' Eingabe voruebergehend blockiert '
input_blocked_ms: '(%{ms}ms verbleibend)'
hint_continue: '[c/Enter/Space] Weiter'
hint_retry: '[r] Wiederholen'
hint_menu: '[q] Menue'
hint_stats: '[s] Statistik'
hint_delete: '[x] Loeschen'
# Stats sidebar (during drill)
sidebar:
title: ' Statistik '
wpm: 'WPM: '
target: 'Ziel: '
target_wpm: '%{wpm} WPM'
accuracy: 'Genauigkeit: '
progress: 'Fortschritt: '
correct: 'Korrekt: '
errors: 'Fehler: '
time: 'Zeit: '
last_drill: ' Letzte Lektion '
vs_avg: ' vs Schnitt: '
# Statistics dashboard
stats:
title: ' Statistik '
empty: 'Noch keine Lektionen abgeschlossen. Fang an zu tippen!'
tab_dashboard: '[1] Dashboard'
tab_history: '[2] Verlauf'
tab_activity: '[3] Aktivitaet'
tab_accuracy: '[4] Genauigkeit'
tab_timing: '[5] Timing'
tab_ngrams: '[6] N-Gramme'
hint_back: '[ESC] Zurueck'
hint_next_tab: '[Tab] Naechster Tab'
hint_switch_tab: '[1-6] Tab wechseln'
hint_navigate: '[j/k] Navigieren'
hint_page: '[PgUp/PgDn] Seite'
hint_delete: '[x] Loeschen'
summary_title: ' Zusammenfassung '
drills: ' Lektionen: '
avg_wpm: ' Schnitt WPM: '
best_wpm: ' Bestes WPM: '
accuracy_label: ' Genauigkeit: '
total_time: ' Gesamtzeit: '
wpm_chart_title: ' WPM pro Lektion (Letzte 20, Ziel: %{target}) '
accuracy_chart_title: ' Genauigkeit %% (Letzte 50 Lektionen) '
chart_drill: 'Lektion #'
chart_accuracy_pct: 'Genauigkeit %%'
sessions_title: ' Letzte Sitzungen '
session_header: ' # WPM Roh Gen%% Zeit Datum/Uhrzeit Modus Gewertet Teilw.'
session_separator: ' ─────────────────────────────────────────────────────────────────────'
delete_confirm: 'Sitzung #%{idx} loeschen? (y/n)'
confirm_title: ' Bestaetigen '
yes: 'ja'
no: 'nein'
keyboard_accuracy_title: ' Tastatur-Genauigkeit %% '
keyboard_timing_title: ' Tastatur-Timing (ms) '
slowest_keys_title: ' Langsamste Tasten (ms) '
fastest_keys_title: ' Schnellste Tasten (ms) '
worst_accuracy_title: ' Schlechteste Genauigkeit (%%) '
best_accuracy_title: ' Beste Genauigkeit (%%) '
not_enough_data: ' Nicht genug Daten'
streaks_title: ' Serien '
current_streak: ' Aktuell: '
best_streak: ' Beste: '
active_days: ' Aktive Tage: '
top_days_none: ' Top-Tage: keine'
top_days: ' Top-Tage: %{days}'
wpm_label: ' WPM: %{avg}/%{target} (%{pct}%%)'
acc_label: ' Gen: %{pct}%%'
keys_label: ' Tasten: %{unlocked}/%{total} (%{mastered} gemeistert)'
ngram_empty: 'Schliesse einige adaptive Lektionen ab, um N-Gramm-Daten zu sehen'
ngram_header_speed_narrow: ' Bgrm Tempo Erw. Anom%'
ngram_header_error_narrow: ' Bgrm Feh Stp Rate Erw Anom%'
ngram_header_speed: ' Bigramm Tempo Erwartet Stichpr. Anom%'
ngram_header_error: ' Bigramm Fehler Stichpr. Rate Erwartet Anom%'
focus_title: ' Aktiver Fokus '
focus_char_label: ' Fokus: '
focus_bigram_value: 'Bigramm %{label}'
focus_plus: ' + '
anomaly_error: 'Fehler'
anomaly_speed: 'Tempo'
focus_detail_both: ' Zeichen ''%{ch}'': schwaechste Taste | Bigramm %{label}: %{type}-Anomalie %{pct}%%'
focus_detail_char_only: ' Zeichen ''%{ch}'': schwaechste Taste, keine bestaetigten Bigramm-Anomalien'
focus_detail_bigram_only: ' (%{type}-Anomalie: %{pct}%%)'
focus_empty: ' Schliesse einige adaptive Lektionen ab, um Fokusdaten zu sehen'
error_anomalies_title: ' Fehler-Anomalien (%{count}) '
no_error_anomalies: ' Keine Fehler-Anomalien erkannt'
speed_anomalies_title: ' Tempo-Anomalien (%{count}) '
no_speed_anomalies: ' Keine Tempo-Anomalien erkannt'
scope_label_prefix: ' '
bi_label: ' | Bi: %{count}'
tri_label: ' | Tri: %{count}'
hes_label: ' | Hes: >%{ms}ms'
gain_label: ' | Gewinn: %{value}'
gain_interval: ' (alle 50)'
focus_char_value: 'Zeichen ''%{ch}'''
# Activity heatmap
heatmap:
title: ' Taegliche Aktivitaet (Sitzungen pro Tag) '
jan: 'Jan'
feb: 'Feb'
mar: 'Mär'
apr: 'Apr'
may: 'Mai'
jun: 'Jun'
jul: 'Jul'
aug: 'Aug'
sep: 'Sep'
oct: 'Okt'
nov: 'Nov'
dec: 'Dez'
# Chart
chart:
wpm_over_time: ' WPM im Zeitverlauf '
drill_number: 'Lektion #'
# Settings
settings:
title: ' Einstellungen '
subtitle: 'Pfeiltasten zum Navigieren, Enter/Rechts zum Aendern, ESC zum Speichern'
target_wpm: 'Ziel-WPM'
theme: 'Farbschema'
word_count: 'Wortanzahl'
ui_language: 'UI-Sprache'
dictionary_language: 'Woerterbuchsprache'
keyboard_layout: 'Tastaturlayout'
code_language: 'Codesprache'
code_downloads: 'Code-Downloads'
on: 'An'
off: 'Aus'
code_download_dir: 'Code-Downloadverz.'
snippets_per_repo: 'Schnipsel pro Repo'
unlimited: 'Unbegrenzt'
download_code_now: 'Code jetzt laden'
run_downloader: 'Download starten'
passage_downloads: 'Text-Downloads'
passage_download_dir: 'Text-Downloadverz.'
paragraphs_per_book: 'Absaetze pro Buch'
whole_book: 'Ganzes Buch'
download_passages_now: 'Texte jetzt laden'
export_path: 'Exportpfad'
export_data: 'Daten exportieren'
export_now: 'Jetzt exportieren'
import_path: 'Importpfad'
import_data: 'Daten importieren'
import_now: 'Jetzt importieren'
hint_save_back: '[ESC] Speichern & zurueck'
hint_change_value: '[Enter/Pfeile] Wert aendern'
hint_edit_path: '[Enter auf Pfad] Bearbeiten'
hint_move: '[←→] Bewegen'
hint_tab_complete: '[Tab] Vervollstaendigen (am Ende)'
hint_confirm: '[Enter] Bestaetigen'
hint_cancel: '[Esc] Abbrechen'
success_title: ' Erfolg '
error_title: ' Fehler '
press_any_key: 'Beliebige Taste druecken'
file_exists_title: ' Datei existiert '
file_exists: 'An diesem Pfad existiert bereits eine Datei.'
overwrite_rename: '[d] Ueberschreiben [r] Umbenennen [Esc] Abbrechen'
erase_warning: 'Dies wird Ihre aktuellen Daten loeschen.'
export_first: 'Exportieren Sie zuerst, wenn Sie sie behalten moechten.'
proceed_yn: 'Fortfahren? (y/n)'
confirm_import_title: ' Import bestaetigen '
# Selection screens
select:
dictionary_language_title: ' Woerterbuchsprache waehlen '
keyboard_layout_title: ' Tastaturlayout waehlen '
code_language_title: ' Codesprache waehlen '
passage_source_title: ' Textquelle waehlen '
ui_language_title: ' UI-Sprache waehlen '
more_above: '... %{count} weitere oben ...'
more_below: '... %{count} weitere unten ...'
current: ' (aktuell)'
disabled: ' (deaktiviert)'
enabled_default: ' (aktiviert, Standard: %{layout})'
enabled: ' (aktiviert)'
disabled_blocked: ' (deaktiviert: gesperrt)'
built_in: ' (eingebaut)'
cached: ' (gespeichert)'
disabled_download: ' (deaktiviert: Download erforderlich)'
download_required: ' (Download erforderlich)'
hint_navigate: '[Auf/Ab/BildAuf/BildAb] Navigieren'
hint_confirm: '[Enter] Bestaetigen'
hint_back: '[ESC] Zurueck'
language_resets_layout: 'Die Sprachauswahl setzt das Tastaturlayout auf den Standard der Sprache zurueck.'
layout_no_language_change: 'Layoutaenderungen aendern nicht die Woerterbuchsprache.'
disabled_network_notice: 'Einige Sprachen sind deaktiviert: Netzwerk-Downloads in Intro/Einstellungen aktivieren.'
disabled_sources_notice: 'Einige Quellen sind deaktiviert: Netzwerk-Downloads in Intro/Einstellungen aktivieren.'
passage_all: 'Alle (Eingebaut + alle Buecher)'
passage_builtin: 'Nur eingebaute Passagen'
passage_book_prefix: 'Buch: %{title}'
# Progress
progress:
overall_key_progress: 'Gesamter Tastenfortschritt'
unlocked_mastered: '%{unlocked}/%{total} freigeschaltet (%{mastered} gemeistert)'
# Skill tree
skill_tree:
title: ' Faehigkeitenbaum '
locked: 'Gesperrt'
unlocked: 'freigeschaltet'
mastered: 'gemeistert'
in_progress: 'in Bearbeitung'
complete: 'abgeschlossen'
locked_status: 'gesperrt'
locked_notice: '%{count} Grundbuchstaben abschliessen, um Zweige freizuschalten'
branches_separator: 'Zweige (verfuegbar nach %{count} Grundbuchstaben)'
unlocked_letters: '%{unlocked}/%{total} Buchstaben freigeschaltet'
level: 'Stufe %{current}/%{total}'
level_zero: 'Stufe 0/%{total}'
in_focus: ' im Fokus'
hint_navigate: '[↑↓/jk] Navigieren'
hint_scroll: '[BildAuf/BildAb oder Strg+U/Strg+D] Scrollen'
hint_back: '[q] Zurueck'
hint_unlock: '[Enter] Freischalten'
hint_start_drill: '[Enter] Lektion starten'
unlock_msg_1: 'Nach dem Freischalten werden freigeschaltete Tasten dieses Zweigs in die adaptive Lektion eingemischt.'
unlock_msg_2: 'Um nur diesen Zweig zu ueben, starte eine Lektion direkt aus diesem Zweig im Faehigkeitenbaum.'
confirm_unlock: '%{branch} freischalten?'
confirm_yn: '[y] Freischalten [n/ESC] Abbrechen'
lvl_prefix: 'Lvl'
branch_primary_letters: 'Grundbuchstaben'
branch_capital_letters: 'Grossbuchstaben'
branch_numbers: 'Zahlen 0-9'
branch_prose_punctuation: 'Interpunktion'
branch_whitespace: 'Leerzeichen'
branch_code_symbols: 'Code-Symbole'
level_frequency_order: 'Haeufigkeitsfolge'
level_common_sentence_capitals: 'Haeufige Satzanfaenge'
level_name_capitals: 'Namensgrossbuchst.'
level_remaining_capitals: 'Restl. Grossbuchst.'
level_common_digits: 'Haeufige Ziffern'
level_all_digits: 'Alle Ziffern'
level_essential: 'Grundlegend'
level_common: 'Haeufig'
level_expressive: 'Ausdruck'
level_enter_return: 'Enter/Return'
level_tab_indent: 'Tab/Einrueckung'
level_arithmetic_assignment: 'Arithmetik & Zuweisung'
level_grouping: 'Gruppierung'
level_logic_reference: 'Logik & Referenz'
level_special: 'Spezial'
# Milestones
milestones:
unlock_title: ' Taste freigeschaltet! '
mastery_title: ' Taste gemeistert! '
branches_title: ' Neue Faehigkeitenzweige verfuegbar! '
branch_complete_title: ' Zweig abgeschlossen! '
all_unlocked_title: ' Alle Tasten freigeschaltet! '
all_mastered_title: ' Volle Tastaturbeherrschung! '
unlocked: 'freigeschaltet'
mastered: 'gemeistert'
use_finger: 'Benutze deinen %{finger}'
hold_right_shift: 'Rechte Umschalttaste halten (rechter kleiner Finger)'
hold_left_shift: 'Linke Umschalttaste halten (linker kleiner Finger)'
congratulations_all_letters: 'Glueckwunsch! Du hast alle %{count} Grundbuchstaben gemeistert'
new_branches_available: 'Neue Faehigkeitenzweige sind jetzt verfuegbar:'
visit_skill_tree: 'Besuche den Faehigkeitenbaum, um einen neuen Zweig'
and_start_training: 'freizuschalten und zu trainieren!'
open_skill_tree: 'Druecke [t], um den Faehigkeitenbaum zu oeffnen'
branch_complete_msg: 'Du hast den Zweig %{branch} abgeschlossen!'
all_levels_mastered: 'Alle %{count} Stufen gemeistert.'
all_keys_confident: 'Jede Taste in diesem Zweig hat volle Sicherheit.'
all_unlocked_msg: 'Du hast jede Taste auf der Tastatur freigeschaltet!'
all_unlocked_desc: 'Jedes Zeichen, Symbol und jeder Modifikator ist jetzt in deinen Lektionen verfuegbar.'
keep_practicing_mastery: 'Uebe weiter, um Meisterschaft aufzubauen — wenn jede Taste volle'
confidence_complete: 'Sicherheit erreicht hat, hast du die volle Tastaturbeherrschung!'
all_mastered_msg: 'Glueckwunsch — du hast volle Tastaturbeherrschung erreicht!'
all_mastered_desc: 'Jede Taste auf der Tastatur hat maximale Sicherheit.'
mastery_takes_practice: 'Meisterschaft ist kein Ziel — sie erfordert staendiges Ueben.'
keep_drilling: 'Uebe weiter, um dein Koennen zu erhalten.'
hint_skill_tree_continue: '[t] Faehigkeitenbaum [Andere Taste] Weiter'
hint_any_key: 'Beliebige Taste zum Fortfahren'
input_blocked: 'Eingabe voruebergehend blockiert (%{ms}ms verbleibend)'
unlock_msg_1: 'Gut gemacht! Baue deine Tippfaehigkeiten weiter aus.'
unlock_msg_2: 'Eine weitere Taste in deinem Arsenal!'
unlock_msg_3: 'Deine Tastatur waechst! Weiter so.'
unlock_msg_4: 'Einen Schritt naeher an voller Tastaturbeherrschung!'
mastery_msg_1: 'Diese Taste hat jetzt volle Sicherheit!'
mastery_msg_2: 'Diese Taste sitzt perfekt!'
mastery_msg_3: 'Muskelgedaechtnis verankert!'
mastery_msg_4: 'Eine weitere Taste bezwungen!'
# Keyboard explorer
keyboard:
title: ' Tastatur '
subtitle: 'Druecke eine Taste oder klicke darauf'
hint_navigate: '[←→↑↓/hjkl/Tab] Navigieren'
hint_back: '[q/ESC] Zurueck'
key_label: 'Taste: '
finger_label: 'Finger: '
hand_left: 'Links'
hand_right: 'Rechts'
finger_index: 'Zeigefinger'
finger_middle: 'Mittelfinger'
finger_ring: 'Ringfinger'
finger_pinky: 'Kleiner Finger'
finger_thumb: 'Daumen'
overall_accuracy: ' Gesamtgenauigkeit: %{correct}/%{total} (%{pct}%%)'
ranked_accuracy: ' Gewertete Genauigkeit: %{correct}/%{total} (%{pct}%%)'
confidence: 'Sicherheit: '
no_data: 'Noch keine Daten'
no_data_short: 'Keine Daten'
key_details: ' Tastendetails '
key_details_char: ' Tastendetails: ''%{ch}'' '
key_details_name: ' Tastendetails: %{name} '
press_key_hint: 'Druecke eine Taste fuer Details'
shift_label: 'Umschalt: '
shift_no: 'Nein'
overall_avg_time: 'Gesamt Schnittzeit: '
overall_best_time: 'Gesamt Bestzeit: '
overall_samples: 'Gesamt Stichproben: '
overall_accuracy_label: 'Gesamt Genauigkeit: '
branch_label: 'Zweig: '
level_label: 'Stufe: '
built_in_key: 'Eingebaute Taste'
unlocked_label: 'Freigeschaltet: '
yes: 'Ja'
no: 'Nein'
in_focus_label: 'Im Fokus?: '
mastery_label: 'Meisterschaft: '
mastery_locked: 'Gesperrt'
ranked_avg_time: 'Gewertete Schnittzeit: '
ranked_best_time: 'Gewertete Bestzeit: '
ranked_samples: 'Gewertete Stichproben: '
ranked_accuracy_label: 'Gewertete Genauigkeit: '
# Intro dialogs
intro:
passage_title: ' Textpassagen-Download Einrichtung '
code_title: ' Code-Download Einrichtung '
enable_downloads: 'Netzwerk-Downloads aktivieren'
download_dir: 'Download-Verzeichnis'
paragraphs_per_book: 'Absaetze pro Buch (0 = ganz)'
whole_book: 'ganzes Buch'
snippets_per_repo: 'Schnipsel pro Repo (0 = unbegrenzt)'
unlimited: 'unbegrenzt'
start_passage_drill: 'Textpassagen-Lektion starten'
start_code_drill: 'Code-Lektion starten'
confirm: 'Bestaetigen'
hint_navigate: '[Auf/Ab] Navigieren'
hint_adjust: '[Links/Rechts] Anpassen'
hint_edit: '[Tippen/Backspace] Bearbeiten'
hint_confirm: '[Enter] Bestaetigen'
hint_cancel: '[ESC] Abbrechen'
preparing_download: 'Download wird vorbereitet...'
download_passage_title: ' Textquelle wird heruntergeladen '
download_code_title: ' Code-Quelle wird heruntergeladen '
book_label: ' Buch: %{name}'
repo_label: ' Repo: %{name}'
progress_bytes: '[%{name}] %{downloaded}/%{total} Bytes'
downloaded_bytes: 'Heruntergeladen: %{bytes} Bytes'
downloading_book_progress: 'Aktuelles Buch wird geladen: [%{bar}] %{downloaded}/%{total} Bytes'
downloading_book_bytes: 'Aktuelles Buch wird geladen: %{bytes} Bytes'
downloading_code_progress: 'Wird heruntergeladen: [%{bar}] %{downloaded}/%{total} Bytes'
downloading_code_bytes: 'Wird heruntergeladen: %{bytes} Bytes'
current_book: 'Aktuell: %{name} (Buch %{done}/%{total})'
current_repo: 'Aktuell: %{name} (Repo %{done}/%{total})'
passage_instructions_1: 'keydr kann Textpassagen von Project Gutenberg zum Tippueben herunterladen.'
passage_instructions_2: 'Buecher werden einmal heruntergeladen und lokal gespeichert.'
passage_instructions_3: 'Konfiguriere die Download-Einstellungen unten und starte eine Textpassagen-Lektion.'
code_instructions_1: 'keydr kann Open-Source-Code von GitHub zum Tippueben herunterladen.'
code_instructions_2: 'Code wird einmal heruntergeladen und lokal gespeichert.'
code_instructions_3: 'Konfiguriere die Download-Einstellungen unten und starte eine Code-Lektion.'
# Status messages (from app.rs)
status:
recovery_files: 'Wiederherstellungsdateien von unterbrochenem Import gefunden. Daten koennten inkonsistent sein — erneuter Import empfohlen.'
dir_not_exist: 'Verzeichnis existiert nicht: %{path}'
no_data_store: 'Kein Datenspeicher verfuegbar'
serialization_error: 'Serialisierungsfehler: %{error}'
exported_to: 'Exportiert nach %{path}'
export_failed: 'Export fehlgeschlagen: %{error}'
could_not_read: 'Datei konnte nicht gelesen werden: %{error}'
invalid_export: 'Ungueltige Exportdatei: %{error}'
unsupported_version: 'Nicht unterstuetzte Exportversion: %{got} (erwartet %{expected})'
import_failed: 'Import fehlgeschlagen: %{error}'
imported_theme_fallback: 'Erfolgreich importiert (Farbschema ''%{theme}'' nicht gefunden, Standard wird verwendet)'
imported_success: 'Erfolgreich importiert'
adaptive_unavailable: 'Adaptiver gewerteter Modus nicht verfuegbar: %{error}'
switched_to: 'Gewechselt zu %{name}'
layout_changed: 'Layout geaendert zu %{name}'
# Errors (for UI boundary translation)
errors:
unknown_language: 'Unbekannte Sprache: %{key}'
unknown_layout: 'Unbekanntes Tastaturlayout: %{key}'
unsupported_pair: 'Nicht unterstuetztes Sprach-/Layout-Paar: %{language} + %{layout}'
language_blocked: 'Sprache durch Unterstuetzungsstufe gesperrt: %{key}'
# Common
common:
wpm: 'WPM'
cpm: 'ZPM'
back: 'Zurueck'