From 5f7ffe162bd977f536cbce0ad4bc1881248559bf Mon Sep 17 00:00:00 2001 From: Viren Mohindra Date: Fri, 12 Dec 2025 20:24:23 +0530 Subject: [PATCH] feat: add german, japanese, and chinese translations (#444) * feat: add german, japanese, and chinese translations adds complete translations for: - german (de) - japanese (ja) - chinese simplified (zh) builds on the internationalization foundation added in #437. updates CONTRIBUTING_TRANSLATIONS.md to reflect supported languages. * fix: escape Chinese quotation marks in zh translation * cleanup * remove extra keys for now --------- Co-authored-by: CJ Pais --- CLAUDE.md | 130 +++++++++ CONTRIBUTING_TRANSLATIONS.md | 8 +- src/components/Sidebar.tsx | 9 +- src/i18n/index.ts | 19 +- src/i18n/languages.ts | 14 +- src/i18n/locales/de/translation.json | 410 +++++++++++++++++++++++++++ src/i18n/locales/ja/translation.json | 410 +++++++++++++++++++++++++++ src/i18n/locales/zh/translation.json | 410 +++++++++++++++++++++++++++ 8 files changed, 1396 insertions(+), 14 deletions(-) create mode 100644 CLAUDE.md create mode 100644 src/i18n/locales/de/translation.json create mode 100644 src/i18n/locales/ja/translation.json create mode 100644 src/i18n/locales/zh/translation.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b6f625a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,130 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Commands + +**Prerequisites:** [Rust](https://rustup.rs/) (latest stable), [Bun](https://bun.sh/) + +```bash +# Install dependencies +bun install + +# Run in development mode +bun run tauri dev +# If cmake error on macOS: +CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev + +# Build for production +bun run tauri build + +# Linting and formatting (run before committing) +bun run lint # ESLint for frontend +bun run lint:fix # ESLint with auto-fix +bun run format # Prettier + cargo fmt +bun run format:check # Check formatting without changes +``` + +**Model Setup (Required for Development):** + +```bash +mkdir -p src-tauri/resources/models +curl -o src-tauri/resources/models/silero_vad_v4.onnx https://blob.handy.computer/silero_vad_v4.onnx +``` + +## Architecture Overview + +Handy is a cross-platform desktop speech-to-text app built with Tauri 2.x (Rust backend + React/TypeScript frontend). + +### Backend Structure (src-tauri/src/) + +- `lib.rs` - Main entry point, Tauri setup, manager initialization +- `managers/` - Core business logic: + - `audio.rs` - Audio recording and device management + - `model.rs` - Model downloading and management + - `transcription.rs` - Speech-to-text processing pipeline + - `history.rs` - Transcription history storage +- `audio_toolkit/` - Low-level audio processing: + - `audio/` - Device enumeration, recording, resampling + - `vad/` - Voice Activity Detection (Silero VAD) +- `commands/` - Tauri command handlers for frontend communication +- `shortcut.rs` - Global keyboard shortcut handling +- `settings.rs` - Application settings management + +### Frontend Structure (src/) + +- `App.tsx` - Main component with onboarding flow +- `components/settings/` - Settings UI (35+ files) +- `components/model-selector/` - Model management interface +- `components/onboarding/` - First-run experience +- `hooks/useSettings.ts`, `useModels.ts` - State management hooks +- `stores/settingsStore.ts` - Zustand store for settings +- `bindings.ts` - Auto-generated Tauri type bindings (via tauri-specta) +- `overlay/` - Recording overlay window code + +### Key Patterns + +**Manager Pattern:** Core functionality organized into managers (Audio, Model, Transcription) initialized at startup and managed via Tauri state. + +**Command-Event Architecture:** Frontend → Backend via Tauri commands; Backend → Frontend via events. + +**Pipeline Processing:** Audio → VAD → Whisper/Parakeet → Text output → Clipboard/Paste + +**State Flow:** Zustand → Tauri Command → Rust State → Persistence (tauri-plugin-store) + +## Internationalization (i18n) + +All user-facing strings must use i18next translations. ESLint enforces this (no hardcoded strings in JSX). + +**Adding new text:** + +1. Add key to `src/i18n/locales/en/translation.json` +2. Use in component: `const { t } = useTranslation(); t('key.path')` + +**File structure:** + +``` +src/i18n/ +├── index.ts # i18n setup +├── languages.ts # Language metadata +└── locales/ + ├── en/translation.json # English (source) + ├── es/translation.json # Spanish + ├── fr/translation.json # French + └── vi/translation.json # Vietnamese +``` + +## Code Style + +**Rust:** + +- Run `cargo fmt` and `cargo clippy` before committing +- Handle errors explicitly (avoid unwrap in production) +- Use descriptive names, add doc comments for public APIs + +**TypeScript/React:** + +- Strict TypeScript, avoid `any` types +- Functional components with hooks +- Tailwind CSS for styling +- Path aliases: `@/` → `./src/` + +## Commit Guidelines + +Use conventional commits: + +- `feat:` new features +- `fix:` bug fixes +- `docs:` documentation +- `refactor:` code refactoring +- `chore:` maintenance + +## Debug Mode + +Access debug features: `Cmd+Shift+D` (macOS) or `Ctrl+Shift+D` (Windows/Linux) + +## Platform Notes + +- **macOS**: Metal acceleration, accessibility permissions required +- **Windows**: Vulkan acceleration, code signing +- **Linux**: OpenBLAS + Vulkan, limited Wayland support, overlay disabled by default diff --git a/CONTRIBUTING_TRANSLATIONS.md b/CONTRIBUTING_TRANSLATIONS.md index d82a142..8d856ed 100644 --- a/CONTRIBUTING_TRANSLATIONS.md +++ b/CONTRIBUTING_TRANSLATIONS.md @@ -154,17 +154,17 @@ Some languages have complex plural rules. For now, use a general form that works | Language | Code | Status | | ---------- | ---- | ----------------- | | English | `en` | Complete (source) | -| Spanish | `es` | Complete | +| Chinese | `zh` | Complete | | French | `fr` | Complete | +| German | `de` | Complete | +| Japanese | `ja` | Complete | +| Spanish | `es` | Complete | | Vietnamese | `vi` | Complete | ## Requested Languages We'd love help with: -- German (`de`) -- Japanese (`ja`) -- Chinese (`zh`) - Korean (`ko`) - Portuguese (`pt`) - And more! diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index a156ca6..2d42cb6 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -103,8 +103,13 @@ export const Sidebar: React.FC = ({ }`} onClick={() => onSectionChange(section.id)} > - -

{t(section.labelKey)}

+ +

+ {t(section.labelKey)} +

); })} diff --git a/src/i18n/index.ts b/src/i18n/index.ts index da47642..2f30908 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -24,11 +24,24 @@ export const SUPPORTED_LANGUAGES = Object.keys(resources) const meta = LANGUAGE_METADATA[code]; if (!meta) { console.warn(`Missing metadata for locale "${code}" in languages.ts`); - return { code, name: code, nativeName: code }; + return { code, name: code, nativeName: code, priority: undefined }; } - return { code, name: meta.name, nativeName: meta.nativeName }; + return { + code, + name: meta.name, + nativeName: meta.nativeName, + priority: meta.priority, + }; }) - .sort((a, b) => a.name.localeCompare(b.name)); + .sort((a, b) => { + // Sort by priority first (lower = higher), then alphabetically + if (a.priority !== undefined && b.priority !== undefined) { + return a.priority - b.priority; + } + if (a.priority !== undefined) return -1; + if (b.priority !== undefined) return 1; + return a.name.localeCompare(b.name); + }); export type SupportedLanguageCode = string; diff --git a/src/i18n/languages.ts b/src/i18n/languages.ts index 1fbfdb9..d552973 100644 --- a/src/i18n/languages.ts +++ b/src/i18n/languages.ts @@ -4,13 +4,17 @@ * To add a new language: * 1. Create a new folder: src/i18n/locales/{code}/translation.json * 2. Add an entry here with the language code, English name, and native name + * 3. Optionally add a priority (lower = higher in dropdown, no priority = alphabetical at end) */ export const LANGUAGE_METADATA: Record< string, - { name: string; nativeName: string } + { name: string; nativeName: string; priority?: number } > = { - en: { name: "English", nativeName: "English" }, - es: { name: "Spanish", nativeName: "Español" }, - fr: { name: "French", nativeName: "Français" }, - vi: { name: "Vietnamese", nativeName: "Tiếng Việt" }, + en: { name: "English", nativeName: "English", priority: 1 }, + zh: { name: "Chinese", nativeName: "中文", priority: 2 }, + es: { name: "Spanish", nativeName: "Español", priority: 3 }, + fr: { name: "French", nativeName: "Français", priority: 4 }, + de: { name: "German", nativeName: "Deutsch", priority: 5 }, + ja: { name: "Japanese", nativeName: "日本語", priority: 6 }, + vi: { name: "Vietnamese", nativeName: "Tiếng Việt", priority: 7 }, }; diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json new file mode 100644 index 0000000..a44d654 --- /dev/null +++ b/src/i18n/locales/de/translation.json @@ -0,0 +1,410 @@ +{ + "sidebar": { + "general": "Allgemein", + "advanced": "Erweitert", + "postProcessing": "Nachbearbeitung", + "history": "Verlauf", + "debug": "Debug", + "about": "Info" + }, + "onboarding": { + "subtitle": "Wähle ein Transkriptionsmodell, um loszulegen", + "recommended": "Empfohlen", + "download": "Herunterladen", + "downloading": "Wird heruntergeladen...", + "modelCard": { + "accuracy": "Genauigkeit", + "speed": "Geschwindigkeit" + }, + "models": { + "small": { + "name": "Whisper Small", + "description": "Schnell und recht genau." + }, + "medium": { + "name": "Whisper Medium", + "description": "Gute Genauigkeit, mittlere Geschwindigkeit" + }, + "turbo": { + "name": "Whisper Turbo", + "description": "Ausgewogene Genauigkeit und Geschwindigkeit." + }, + "large": { + "name": "Whisper Large", + "description": "Gute Genauigkeit, aber langsam." + }, + "parakeet-tdt-0.6b-v2": { + "name": "Parakeet V2", + "description": "Nur Englisch. Das beste Modell für englischsprachige Nutzer." + }, + "parakeet-tdt-0.6b-v3": { + "name": "Parakeet V3", + "description": "Schnell und genau" + } + }, + "errors": { + "loadModels": "Verfügbare Modelle konnten nicht geladen werden", + "downloadModel": "Modell konnte nicht heruntergeladen werden: {{error}}" + } + }, + "modelSelector": { + "welcome": "Willkommen bei Handy!", + "downloadPrompt": "Lade ein Modell herunter, um mit der Transkription zu beginnen.", + "availableModels": "Verfügbare Modelle", + "downloadModels": "Modelle herunterladen", + "chooseModel": "Modell auswählen", + "active": "Aktiv", + "download": "Herunterladen", + "downloadSize": "Downloadgröße", + "noModelsAvailable": "Keine Modelle verfügbar", + "extracting": "Entpacke {{modelName}}...", + "extractingMultiple": "Entpacke {{count}} Modelle...", + "extractingGeneric": "Wird entpackt...", + "downloading": "Herunterladen {{percentage}}%", + "downloadingMultiple": "Lade {{count}} Modelle herunter...", + "modelReady": "Modell bereit", + "loading": "Lade {{modelName}}...", + "loadingGeneric": "Wird geladen...", + "modelError": "Modellfehler", + "modelUnloaded": "Modell entladen", + "noModelDownloadRequired": "Kein Modell - Download erforderlich", + "deleteModel": "{{modelName}} löschen" + }, + "settings": { + "general": { + "title": "Allgemein", + "shortcut": { + "title": "Handy-Tastenkürzel", + "description": "Tastenkürzel für die Sprachaufnahme konfigurieren", + "loading": "Tastenkürzel werden geladen...", + "none": "Keine Tastenkürzel konfiguriert", + "notFound": "Tastenkürzel nicht gefunden", + "pressKeys": "Tasten drücken...", + "bindings": { + "transcribe": { + "name": "Transkribieren", + "description": "Wandelt Sprache in Text um." + }, + "cancel": { + "name": "Abbrechen", + "description": "Bricht die aktuelle Aufnahme ab." + } + }, + "errors": { + "restore": "Ursprüngliches Tastenkürzel konnte nicht wiederhergestellt werden", + "set": "Tastenkürzel konnte nicht gesetzt werden: {{error}}", + "reset": "Tastenkürzel konnte nicht auf Originalwert zurückgesetzt werden" + } + }, + "language": { + "title": "Sprache", + "description": "Wähle die Sprache für die Spracherkennung. Auto erkennt die Sprache automatisch, die Auswahl einer bestimmten Sprache kann die Genauigkeit verbessern.", + "descriptionUnsupported": "Das Parakeet-Modell erkennt die Sprache automatisch. Keine manuelle Auswahl erforderlich.", + "searchPlaceholder": "Sprachen suchen...", + "noResults": "Keine Sprachen gefunden", + "auto": "Auto" + }, + "pushToTalk": { + "label": "Push-to-Talk", + "description": "Gedrückt halten zum Aufnehmen, loslassen zum Stoppen" + } + }, + "sound": { + "title": "Ton", + "microphone": { + "title": "Mikrofon", + "description": "Bevorzugtes Mikrofon auswählen", + "placeholder": "Mikrofon auswählen...", + "loading": "Wird geladen..." + }, + "audioFeedback": { + "label": "Audio-Feedback", + "description": "Ton bei Start und Ende der Aufnahme abspielen" + }, + "outputDevice": { + "title": "Ausgabegerät", + "description": "Bevorzugtes Audioausgabegerät für Feedback-Töne auswählen", + "placeholder": "Ausgabegerät auswählen...", + "loading": "Wird geladen..." + }, + "volume": { + "title": "Lautstärke", + "description": "Lautstärke der Audio-Feedback-Töne anpassen" + } + }, + "advanced": { + "title": "Erweitert", + "startHidden": { + "label": "Versteckt starten", + "description": "In den Systembereich starten, ohne das Fenster zu öffnen." + }, + "autostart": { + "label": "Beim Start ausführen", + "description": "Handy automatisch beim Anmelden starten." + }, + "overlay": { + "title": "Overlay-Position", + "description": "Visuelles Feedback-Overlay während Aufnahme und Transkription anzeigen. Unter Linux wird 'Keine' empfohlen.", + "options": { + "none": "Keine", + "bottom": "Unten", + "top": "Oben" + } + }, + "pasteMethod": { + "title": "Einfügemethode", + "description": "Wähle, wie Text eingefügt wird. Direkt: simuliert Tippen über Systemeingabe. Keine: überspringt Einfügen, aktualisiert nur Verlauf/Zwischenablage.", + "options": { + "clipboard": "Zwischenablage ({{modifier}}+V)", + "clipboardCtrlShiftV": "Zwischenablage (Strg+Umschalt+V)", + "clipboardShiftInsert": "Zwischenablage (Umschalt+Einfg)", + "direct": "Direkt", + "none": "Keine" + } + }, + "clipboardHandling": { + "title": "Zwischenablage-Verhalten", + "description": "Zwischenablage nicht ändern bewahrt den aktuellen Inhalt nach der Transkription. In Zwischenablage kopieren hinterlässt das Transkriptionsergebnis in der Zwischenablage.", + "options": { + "dontModify": "Zwischenablage nicht ändern", + "copyToClipboard": "In Zwischenablage kopieren" + } + }, + "translateToEnglish": { + "label": "Ins Englische übersetzen", + "description": "Sprache aus anderen Sprachen automatisch während der Transkription ins Englische übersetzen.", + "descriptionUnsupported": "Übersetzung wird vom {{model}}-Modell nicht unterstützt." + }, + "modelUnload": { + "title": "Modell entladen", + "description": "GPU/CPU-Speicher automatisch freigeben, wenn das Modell für die angegebene Zeit nicht verwendet wurde", + "options": { + "never": "Nie", + "immediately": "Sofort", + "min2": "Nach 2 Minuten", + "min5": "Nach 5 Minuten", + "min10": "Nach 10 Minuten", + "min15": "Nach 15 Minuten", + "hour1": "Nach 1 Stunde", + "sec5": "Nach 5 Sekunden (Debug)" + } + }, + "customWords": { + "title": "Benutzerdefinierte Wörter", + "description": "Wörter hinzufügen, die oft falsch gehört oder geschrieben werden. Das System korrigiert automatisch ähnlich klingende Wörter entsprechend deiner Liste.", + "placeholder": "Wort hinzufügen", + "add": "Hinzufügen", + "remove": "{{word}} entfernen" + } + }, + "postProcessing": { + "title": "Nachbearbeitung", + "disabledNotice": "Die Nachbearbeitung ist derzeit deaktiviert. Aktiviere sie in den Debug-Einstellungen, um sie zu konfigurieren.", + "api": { + "title": "API (OpenAI-kompatibel)", + "provider": { + "title": "Anbieter", + "description": "Wähle einen OpenAI-kompatiblen Anbieter." + }, + "appleIntelligence": { + "title": "Apple Intelligence", + "description": "Läuft vollständig auf dem Gerät. Kein API-Schlüssel oder Netzwerkzugriff erforderlich.", + "requirements": "Erfordert einen Apple Silicon Mac mit macOS Tahoe (26.0) oder neuer. Apple Intelligence muss in den Systemeinstellungen aktiviert sein." + }, + "baseUrl": { + "title": "Basis-URL", + "description": "API-Basis-URL für den ausgewählten Anbieter. Nur der benutzerdefinierte Anbieter kann bearbeitet werden.", + "placeholder": "https://api.openai.com/v1" + }, + "apiKey": { + "title": "API-Schlüssel", + "description": "API-Schlüssel für den ausgewählten Anbieter.", + "placeholder": "sk-..." + }, + "model": { + "title": "Modell", + "descriptionApple": "Gib ein optionales numerisches Token-Limit an oder behalte die Standard-Gerätevorgabe.", + "descriptionCustom": "Gib die Modellkennung an, die von deinem benutzerdefinierten Endpunkt erwartet wird.", + "descriptionDefault": "Wähle ein Modell des ausgewählten Anbieters.", + "placeholderApple": "Apple Intelligence", + "placeholderWithOptions": "Modell suchen oder auswählen", + "placeholderNoOptions": "Modellnamen eingeben", + "refreshModels": "Modelle aktualisieren" + } + }, + "prompts": { + "title": "Prompt", + "selectedPrompt": { + "title": "Ausgewählter Prompt", + "description": "Wähle eine Vorlage zur Verfeinerung von Transkriptionen oder erstelle eine neue. Verwende ${output} im Prompt-Text, um auf das erfasste Transkript zu verweisen." + }, + "noPrompts": "Keine Prompts verfügbar", + "selectPrompt": "Prompt auswählen", + "createNew": "Neuen Prompt erstellen", + "promptLabel": "Prompt-Name", + "promptLabelPlaceholder": "Prompt-Namen eingeben", + "promptInstructions": "Prompt-Anweisungen", + "promptInstructionsPlaceholder": "Schreibe die Anweisungen, die nach der Transkription ausgeführt werden sollen. Beispiel: Verbessere Grammatik und Klarheit für folgenden Text: ${output}", + "promptTip": "Tipp: Verwende ${output}, um den transkribierten Text in deinen Prompt einzufügen.", + "updatePrompt": "Prompt aktualisieren", + "deletePrompt": "Prompt löschen", + "createPrompt": "Prompt erstellen", + "cancel": "Abbrechen", + "selectToEdit": "Wähle oben einen Prompt aus, um dessen Details anzuzeigen und zu bearbeiten.", + "createFirst": "Klicke oben auf 'Neuen Prompt erstellen', um deinen ersten Nachbearbeitungs-Prompt zu erstellen." + } + }, + "history": { + "title": "Verlauf", + "openFolder": "Aufnahmeordner öffnen", + "loading": "Verlauf wird geladen...", + "empty": "Noch keine Transkriptionen. Starte eine Aufnahme, um deinen Verlauf aufzubauen!", + "copyToClipboard": "Transkription in Zwischenablage kopieren", + "save": "Transkription speichern", + "unsave": "Aus Gespeicherten entfernen", + "delete": "Eintrag löschen", + "deleteError": "Eintrag konnte nicht gelöscht werden. Bitte versuche es erneut." + }, + "debug": { + "title": "Debug", + "logDirectory": { + "title": "Log-Verzeichnis", + "description": "Speicherort der Log-Dateien" + }, + "logLevel": { + "title": "Log-Level", + "description": "Ausführlichkeit der Protokollierung festlegen" + }, + "updateChecks": { + "label": "Nach Updates suchen", + "description": "Automatisch nach neuen Versionen von Handy suchen" + }, + "soundTheme": { + "label": "Sound-Thema", + "description": "Sound-Thema für Aufnahme-Start und -Stop-Feedback auswählen" + }, + "wordCorrectionThreshold": { + "title": "Wortkorrektur-Schwelle", + "description": "Empfindlichkeit für benutzerdefinierte Wortkorrekturen" + }, + "historyLimit": { + "title": "Verlaufslimit", + "description": "Maximale Anzahl der Verlaufseinträge", + "entries": "Einträge" + }, + "recordingRetention": { + "title": "Aufnahme-Aufbewahrung", + "description": "Wie lange Audioaufnahmen aufbewahrt werden sollen", + "never": "Nie", + "preserveLimit": "{{count}} Aufnahmen behalten", + "days3": "Nach 3 Tagen", + "weeks2": "Nach 2 Wochen", + "months3": "Nach 3 Monaten", + "placeholder": "Aufbewahrungszeitraum auswählen..." + }, + "alwaysOnMicrophone": { + "label": "Mikrofon immer aktiv", + "description": "Mikrofon für schnellere Reaktion aktiv halten" + }, + "clamshellMicrophone": { + "title": "Clamshell-Mikrofon", + "description": "Mikrofon bei geschlossenem Laptop-Deckel" + }, + "postProcessingToggle": { + "label": "Nachbearbeitung", + "description": "KI-gestützte Textverfeinerung nach der Transkription aktivieren" + }, + "muteWhileRecording": { + "label": "Während Aufnahme stummschalten", + "description": "Systemaudio während der Aufnahme stummschalten" + }, + "appendTrailingSpace": { + "label": "Leerzeichen anhängen", + "description": "Leerzeichen nach eingefügter Transkription hinzufügen" + }, + "paths": { + "appData": "App-Daten:", + "models": "Modelle:", + "settings": "Einstellungen:" + } + }, + "about": { + "title": "Info", + "version": { + "title": "Version", + "description": "Aktuelle Version von Handy" + }, + "appDataDirectory": { + "title": "App-Datenverzeichnis", + "description": "Speicherort der Handy-Daten" + }, + "sourceCode": { + "title": "Quellcode", + "description": "Quellcode ansehen und beitragen", + "button": "Auf GitHub ansehen" + }, + "supportDevelopment": { + "title": "Entwicklung unterstützen", + "description": "Hilf uns, Handy weiterzuentwickeln", + "button": "Spenden" + }, + "acknowledgments": { + "title": "Danksagungen", + "whisper": { + "title": "Whisper.cpp", + "description": "Hochleistungs-Inferenz des Whisper-Spracherkennungsmodells von OpenAI", + "details": "Handy verwendet Whisper.cpp für schnelle, lokale Sprach-zu-Text-Verarbeitung. Dank an Georgi Gerganov und die Mitwirkenden für ihre großartige Arbeit." + } + } + } + }, + "footer": { + "downloadingModel": "Lade {{model}} herunter...", + "checkingUpdates": "Suche nach Updates...", + "updateAvailable": "Update verfügbar: {{version}}", + "updateAvailableShort": "Update verfügbar", + "upToDate": "Aktuell", + "downloadUpdate": "Update herunterladen", + "restart": "Neustart", + "updateCheckingDisabled": "Update-Prüfung deaktiviert", + "downloading": "Wird heruntergeladen... {{progress}}%", + "installing": "Wird installiert...", + "preparing": "Wird vorbereitet...", + "checkForUpdates": "Nach Updates suchen" + }, + "common": { + "loading": "Wird geladen...", + "save": "Speichern", + "cancel": "Abbrechen", + "reset": "Zurücksetzen", + "add": "Hinzufügen", + "remove": "Entfernen", + "delete": "Löschen", + "edit": "Bearbeiten", + "create": "Erstellen", + "update": "Aktualisieren", + "close": "Schließen", + "open": "Öffnen", + "default": "Standard", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "on": "An", + "off": "Aus", + "yes": "Ja", + "no": "Nein", + "noOptionsFound": "Keine Optionen gefunden" + }, + "accessibility": { + "permissionsRequired": "Bedienungshilfen-Berechtigungen erforderlich", + "permissionsDescription": "Handy benötigt Bedienungshilfen-Berechtigungen, um transkribierten Text einzugeben.", + "openSettings": "Systemeinstellungen öffnen", + "dismiss": "Schließen" + }, + "errors": { + "loadDirectory": "Fehler beim Laden des Verzeichnisses: {{error}}" + }, + "appLanguage": { + "title": "Anwendungssprache", + "description": "Sprache der Handy-Oberfläche ändern" + } +} diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json new file mode 100644 index 0000000..ee8fa33 --- /dev/null +++ b/src/i18n/locales/ja/translation.json @@ -0,0 +1,410 @@ +{ + "sidebar": { + "general": "一般", + "advanced": "詳細設定", + "postProcessing": "後処理", + "history": "履歴", + "debug": "デバッグ", + "about": "概要" + }, + "onboarding": { + "subtitle": "開始するには、文字起こしモデルを選択してください", + "recommended": "おすすめ", + "download": "ダウンロード", + "downloading": "ダウンロード中...", + "modelCard": { + "accuracy": "精度", + "speed": "速度" + }, + "models": { + "small": { + "name": "Whisper Small", + "description": "高速でそこそこ正確。" + }, + "medium": { + "name": "Whisper Medium", + "description": "良好な精度、中程度の速度" + }, + "turbo": { + "name": "Whisper Turbo", + "description": "精度と速度のバランスが良い。" + }, + "large": { + "name": "Whisper Large", + "description": "良好な精度、ただし低速。" + }, + "parakeet-tdt-0.6b-v2": { + "name": "Parakeet V2", + "description": "英語のみ。英語話者に最適なモデル。" + }, + "parakeet-tdt-0.6b-v3": { + "name": "Parakeet V3", + "description": "高速で正確" + } + }, + "errors": { + "loadModels": "利用可能なモデルの読み込みに失敗しました", + "downloadModel": "モデルのダウンロードに失敗しました: {{error}}" + } + }, + "modelSelector": { + "welcome": "Handyへようこそ!", + "downloadPrompt": "文字起こしを開始するには、下からモデルをダウンロードしてください。", + "availableModels": "利用可能なモデル", + "downloadModels": "モデルをダウンロード", + "chooseModel": "モデルを選択", + "active": "アクティブ", + "download": "ダウンロード", + "downloadSize": "ダウンロードサイズ", + "noModelsAvailable": "利用可能なモデルがありません", + "extracting": "{{modelName}}を展開中...", + "extractingMultiple": "{{count}}個のモデルを展開中...", + "extractingGeneric": "展開中...", + "downloading": "ダウンロード中 {{percentage}}%", + "downloadingMultiple": "{{count}}個のモデルをダウンロード中...", + "modelReady": "モデル準備完了", + "loading": "{{modelName}}を読み込み中...", + "loadingGeneric": "読み込み中...", + "modelError": "モデルエラー", + "modelUnloaded": "モデルがアンロードされました", + "noModelDownloadRequired": "モデルなし - ダウンロードが必要", + "deleteModel": "{{modelName}}を削除" + }, + "settings": { + "general": { + "title": "一般", + "shortcut": { + "title": "Handyショートカット", + "description": "音声録音を開始するキーボードショートカットを設定", + "loading": "ショートカットを読み込み中...", + "none": "ショートカットが設定されていません", + "notFound": "ショートカットが見つかりません", + "pressKeys": "キーを押してください...", + "bindings": { + "transcribe": { + "name": "文字起こし", + "description": "音声をテキストに変換します。" + }, + "cancel": { + "name": "キャンセル", + "description": "現在の録音をキャンセルします。" + } + }, + "errors": { + "restore": "元のショートカットを復元できませんでした", + "set": "ショートカットを設定できませんでした: {{error}}", + "reset": "ショートカットを元の値にリセットできませんでした" + } + }, + "language": { + "title": "言語", + "description": "音声認識の言語を選択してください。自動を選択すると言語を自動的に判定します。特定の言語を選択すると、その言語の精度が向上する場合があります。", + "descriptionUnsupported": "Parakeetモデルは言語を自動的に検出します。手動選択は不要です。", + "searchPlaceholder": "言語を検索...", + "noResults": "言語が見つかりません", + "auto": "自動" + }, + "pushToTalk": { + "label": "プッシュトゥトーク", + "description": "押し続けて録音、離して停止" + } + }, + "sound": { + "title": "サウンド", + "microphone": { + "title": "マイク", + "description": "使用するマイクデバイスを選択", + "placeholder": "マイクを選択...", + "loading": "読み込み中..." + }, + "audioFeedback": { + "label": "音声フィードバック", + "description": "録音の開始と停止時にサウンドを再生" + }, + "outputDevice": { + "title": "出力デバイス", + "description": "フィードバックサウンド用の音声出力デバイスを選択", + "placeholder": "出力デバイスを選択...", + "loading": "読み込み中..." + }, + "volume": { + "title": "音量", + "description": "音声フィードバックの音量を調整" + } + }, + "advanced": { + "title": "詳細設定", + "startHidden": { + "label": "非表示で起動", + "description": "ウィンドウを開かずにシステムトレイに起動。" + }, + "autostart": { + "label": "起動時に実行", + "description": "コンピューターにログインしたときにHandyを自動的に起動。" + }, + "overlay": { + "title": "オーバーレイ位置", + "description": "録音と文字起こし中に視覚的なフィードバックオーバーレイを表示。Linuxでは「なし」を推奨。", + "options": { + "none": "なし", + "bottom": "下", + "top": "上" + } + }, + "pasteMethod": { + "title": "貼り付け方法", + "description": "テキストの挿入方法を選択。直接:システム入力でタイピングをシミュレート。なし:貼り付けをスキップし、履歴/クリップボードのみ更新。", + "options": { + "clipboard": "クリップボード ({{modifier}}+V)", + "clipboardCtrlShiftV": "クリップボード (Ctrl+Shift+V)", + "clipboardShiftInsert": "クリップボード (Shift+Insert)", + "direct": "直接", + "none": "なし" + } + }, + "clipboardHandling": { + "title": "クリップボードの処理", + "description": "クリップボードを変更しないを選択すると、文字起こし後も現在のクリップボード内容が保持されます。クリップボードにコピーを選択すると、貼り付け後も文字起こし結果がクリップボードに残ります。", + "options": { + "dontModify": "クリップボードを変更しない", + "copyToClipboard": "クリップボードにコピー" + } + }, + "translateToEnglish": { + "label": "英語に翻訳", + "description": "文字起こし中に他の言語から英語に自動的に翻訳。", + "descriptionUnsupported": "翻訳は{{model}}モデルではサポートされていません。" + }, + "modelUnload": { + "title": "モデルのアンロード", + "description": "指定時間モデルが使用されていない場合、GPU/CPUメモリを自動的に解放", + "options": { + "never": "しない", + "immediately": "即座に", + "min2": "2分後", + "min5": "5分後", + "min10": "10分後", + "min15": "15分後", + "hour1": "1時間後", + "sec5": "5秒後(デバッグ)" + } + }, + "customWords": { + "title": "カスタム単語", + "description": "よく誤認識または誤入力される単語を追加します。システムは自動的に類似した発音の単語をリストに合わせて修正します。", + "placeholder": "単語を追加", + "add": "追加", + "remove": "{{word}}を削除" + } + }, + "postProcessing": { + "title": "後処理", + "disabledNotice": "後処理は現在無効です。設定するにはデバッグ設定で有効にしてください。", + "api": { + "title": "API(OpenAI互換)", + "provider": { + "title": "プロバイダー", + "description": "OpenAI互換のプロバイダーを選択。" + }, + "appleIntelligence": { + "title": "Apple Intelligence", + "description": "完全にデバイス上で動作。APIキーやネットワークアクセスは不要。", + "requirements": "macOS Tahoe(26.0)以降を実行するApple Silicon Macが必要です。システム設定でApple Intelligenceを有効にする必要があります。" + }, + "baseUrl": { + "title": "ベースURL", + "description": "選択したプロバイダーのAPIベースURL。カスタムプロバイダーのみ編集可能。", + "placeholder": "https://api.openai.com/v1" + }, + "apiKey": { + "title": "APIキー", + "description": "選択したプロバイダーのAPIキー。", + "placeholder": "sk-..." + }, + "model": { + "title": "モデル", + "descriptionApple": "オプションの数値トークン制限を指定するか、デフォルトのオンデバイスプリセットを使用。", + "descriptionCustom": "カスタムエンドポイントが期待するモデル識別子を指定。", + "descriptionDefault": "選択したプロバイダーが提供するモデルを選択。", + "placeholderApple": "Apple Intelligence", + "placeholderWithOptions": "モデルを検索または選択", + "placeholderNoOptions": "モデル名を入力", + "refreshModels": "モデルを更新" + } + }, + "prompts": { + "title": "プロンプト", + "selectedPrompt": { + "title": "選択したプロンプト", + "description": "文字起こしを改善するテンプレートを選択するか、新しく作成します。プロンプトテキスト内で${output}を使用して、キャプチャした文字起こしを参照します。" + }, + "noPrompts": "プロンプトがありません", + "selectPrompt": "プロンプトを選択", + "createNew": "新しいプロンプトを作成", + "promptLabel": "プロンプト名", + "promptLabelPlaceholder": "プロンプト名を入力", + "promptInstructions": "プロンプトの指示", + "promptInstructionsPlaceholder": "文字起こし後に実行する指示を記述します。例:以下のテキストの文法と明瞭さを改善してください: ${output}", + "promptTip": "ヒント:${output}を使用して、文字起こしテキストをプロンプトに挿入します。", + "updatePrompt": "プロンプトを更新", + "deletePrompt": "プロンプトを削除", + "createPrompt": "プロンプトを作成", + "cancel": "キャンセル", + "selectToEdit": "上からプロンプトを選択して、詳細を表示・編集します。", + "createFirst": "上の「新しいプロンプトを作成」をクリックして、最初の後処理プロンプトを作成してください。" + } + }, + "history": { + "title": "履歴", + "openFolder": "録音フォルダを開く", + "loading": "履歴を読み込み中...", + "empty": "まだ文字起こしがありません。録音を開始して履歴を作成しましょう!", + "copyToClipboard": "文字起こしをクリップボードにコピー", + "save": "文字起こしを保存", + "unsave": "保存から削除", + "delete": "エントリーを削除", + "deleteError": "エントリーの削除に失敗しました。もう一度お試しください。" + }, + "debug": { + "title": "デバッグ", + "logDirectory": { + "title": "ログディレクトリ", + "description": "ログファイルの保存場所" + }, + "logLevel": { + "title": "ログレベル", + "description": "ログの詳細度を設定" + }, + "updateChecks": { + "label": "アップデートを確認", + "description": "Handyの新しいバージョンを自動的にチェック" + }, + "soundTheme": { + "label": "サウンドテーマ", + "description": "録音開始・停止フィードバックのサウンドテーマを選択" + }, + "wordCorrectionThreshold": { + "title": "単語修正しきい値", + "description": "カスタム単語修正の感度" + }, + "historyLimit": { + "title": "履歴上限", + "description": "保持する履歴エントリーの最大数", + "entries": "件" + }, + "recordingRetention": { + "title": "録音の保持", + "description": "音声録音を保持する期間", + "never": "しない", + "preserveLimit": "{{count}}件の録音を保持", + "days3": "3日後", + "weeks2": "2週間後", + "months3": "3ヶ月後", + "placeholder": "保持期間を選択..." + }, + "alwaysOnMicrophone": { + "label": "マイク常時オン", + "description": "より速い応答のためにマイクをアクティブに保つ" + }, + "clamshellMicrophone": { + "title": "クラムシェルマイク", + "description": "ノートパソコンの蓋を閉じたときに使用するマイク" + }, + "postProcessingToggle": { + "label": "後処理", + "description": "文字起こし後のAIによるテキスト改善を有効化" + }, + "muteWhileRecording": { + "label": "録音中にミュート", + "description": "録音中にシステムオーディオをミュート" + }, + "appendTrailingSpace": { + "label": "末尾にスペースを追加", + "description": "貼り付けた文字起こしの後にスペースを追加" + }, + "paths": { + "appData": "アプリデータ:", + "models": "モデル:", + "settings": "設定:" + } + }, + "about": { + "title": "概要", + "version": { + "title": "バージョン", + "description": "Handyの現在のバージョン" + }, + "appDataDirectory": { + "title": "アプリデータディレクトリ", + "description": "Handyがデータを保存する場所" + }, + "sourceCode": { + "title": "ソースコード", + "description": "ソースコードを見て貢献する", + "button": "GitHubで見る" + }, + "supportDevelopment": { + "title": "開発を支援", + "description": "Handyの開発を支援してください", + "button": "寄付する" + }, + "acknowledgments": { + "title": "謝辞", + "whisper": { + "title": "Whisper.cpp", + "description": "OpenAIのWhisper自動音声認識モデルの高性能推論", + "details": "Handyは高速でローカルな音声からテキストへの変換にWhisper.cppを使用しています。Georgi Gerganov氏と貢献者の皆様の素晴らしい仕事に感謝します。" + } + } + } + }, + "footer": { + "downloadingModel": "{{model}}をダウンロード中...", + "checkingUpdates": "アップデートを確認中...", + "updateAvailable": "アップデートあり: {{version}}", + "updateAvailableShort": "アップデートあり", + "upToDate": "最新です", + "downloadUpdate": "アップデートをダウンロード", + "restart": "再起動", + "updateCheckingDisabled": "アップデート確認無効", + "downloading": "ダウンロード中... {{progress}}%", + "installing": "インストール中...", + "preparing": "準備中...", + "checkForUpdates": "アップデートを確認" + }, + "common": { + "loading": "読み込み中...", + "save": "保存", + "cancel": "キャンセル", + "reset": "リセット", + "add": "追加", + "remove": "削除", + "delete": "削除", + "edit": "編集", + "create": "作成", + "update": "更新", + "close": "閉じる", + "open": "開く", + "default": "デフォルト", + "enabled": "有効", + "disabled": "無効", + "on": "オン", + "off": "オフ", + "yes": "はい", + "no": "いいえ", + "noOptionsFound": "オプションが見つかりません" + }, + "accessibility": { + "permissionsRequired": "アクセシビリティ権限が必要です", + "permissionsDescription": "Handyが文字起こしテキストを入力するには、アクセシビリティ権限が必要です。", + "openSettings": "システム設定を開く", + "dismiss": "閉じる" + }, + "errors": { + "loadDirectory": "ディレクトリの読み込みエラー: {{error}}" + }, + "appLanguage": { + "title": "アプリケーション言語", + "description": "Handyインターフェースの言語を変更" + } +} diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json new file mode 100644 index 0000000..358a947 --- /dev/null +++ b/src/i18n/locales/zh/translation.json @@ -0,0 +1,410 @@ +{ + "sidebar": { + "general": "通用", + "advanced": "高级", + "postProcessing": "后处理", + "history": "历史记录", + "debug": "调试", + "about": "关于" + }, + "onboarding": { + "subtitle": "请选择一个转录模型以开始使用", + "recommended": "推荐", + "download": "下载", + "downloading": "下载中...", + "modelCard": { + "accuracy": "准确度", + "speed": "速度" + }, + "models": { + "small": { + "name": "Whisper Small", + "description": "快速且相当准确。" + }, + "medium": { + "name": "Whisper Medium", + "description": "准确度高,速度适中" + }, + "turbo": { + "name": "Whisper Turbo", + "description": "准确度和速度均衡。" + }, + "large": { + "name": "Whisper Large", + "description": "准确度高,但速度较慢。" + }, + "parakeet-tdt-0.6b-v2": { + "name": "Parakeet V2", + "description": "仅支持英语。英语用户的最佳模型。" + }, + "parakeet-tdt-0.6b-v3": { + "name": "Parakeet V3", + "description": "快速且准确" + } + }, + "errors": { + "loadModels": "无法加载可用模型", + "downloadModel": "模型下载失败: {{error}}" + } + }, + "modelSelector": { + "welcome": "欢迎使用 Handy!", + "downloadPrompt": "请下载以下模型以开始转录。", + "availableModels": "可用模型", + "downloadModels": "下载模型", + "chooseModel": "选择模型", + "active": "活跃", + "download": "下载", + "downloadSize": "下载大小", + "noModelsAvailable": "没有可用的模型", + "extracting": "正在解压 {{modelName}}...", + "extractingMultiple": "正在解压 {{count}} 个模型...", + "extractingGeneric": "解压中...", + "downloading": "下载中 {{percentage}}%", + "downloadingMultiple": "正在下载 {{count}} 个模型...", + "modelReady": "模型已就绪", + "loading": "正在加载 {{modelName}}...", + "loadingGeneric": "加载中...", + "modelError": "模型错误", + "modelUnloaded": "模型已卸载", + "noModelDownloadRequired": "无模型 - 需要下载", + "deleteModel": "删除 {{modelName}}" + }, + "settings": { + "general": { + "title": "通用", + "shortcut": { + "title": "Handy 快捷键", + "description": "配置启动语音转文字录制的键盘快捷键", + "loading": "加载快捷键中...", + "none": "未配置快捷键", + "notFound": "未找到快捷键", + "pressKeys": "请按键...", + "bindings": { + "transcribe": { + "name": "转录", + "description": "将语音转换为文字。" + }, + "cancel": { + "name": "取消", + "description": "取消当前录制。" + } + }, + "errors": { + "restore": "无法恢复原始快捷键", + "set": "无法设置快捷键: {{error}}", + "reset": "无法将快捷键重置为原始值" + } + }, + "language": { + "title": "语言", + "description": "选择语音识别的语言。自动将自动确定语言,选择特定语言可以提高该语言的准确度。", + "descriptionUnsupported": "Parakeet 模型会自动检测语言,无需手动选择。", + "searchPlaceholder": "搜索语言...", + "noResults": "未找到语言", + "auto": "自动" + }, + "pushToTalk": { + "label": "按住说话", + "description": "按住录制,松开停止" + } + }, + "sound": { + "title": "声音", + "microphone": { + "title": "麦克风", + "description": "选择您偏好的麦克风设备", + "placeholder": "选择麦克风...", + "loading": "加载中..." + }, + "audioFeedback": { + "label": "音频反馈", + "description": "录制开始和停止时播放声音" + }, + "outputDevice": { + "title": "输出设备", + "description": "选择用于反馈声音的音频输出设备", + "placeholder": "选择输出设备...", + "loading": "加载中..." + }, + "volume": { + "title": "音量", + "description": "调整音频反馈的音量" + } + }, + "advanced": { + "title": "高级", + "startHidden": { + "label": "隐藏启动", + "description": "启动到系统托盘而不打开窗口。" + }, + "autostart": { + "label": "开机启动", + "description": "登录计算机时自动启动 Handy。" + }, + "overlay": { + "title": "悬浮窗位置", + "description": "在录制和转录期间显示可视反馈悬浮窗。在 Linux 上建议选择「无」。", + "options": { + "none": "无", + "bottom": "底部", + "top": "顶部" + } + }, + "pasteMethod": { + "title": "粘贴方式", + "description": "选择文字插入方式。直接:通过系统输入模拟打字。无:跳过粘贴,仅更新历史记录/剪贴板。", + "options": { + "clipboard": "剪贴板 ({{modifier}}+V)", + "clipboardCtrlShiftV": "剪贴板 (Ctrl+Shift+V)", + "clipboardShiftInsert": "剪贴板 (Shift+Insert)", + "direct": "直接", + "none": "无" + } + }, + "clipboardHandling": { + "title": "剪贴板处理", + "description": "不修改剪贴板将在转录后保留当前剪贴板内容。复制到剪贴板将在粘贴后将转录结果留在剪贴板中。", + "options": { + "dontModify": "不修改剪贴板", + "copyToClipboard": "复制到剪贴板" + } + }, + "translateToEnglish": { + "label": "翻译为英语", + "description": "在转录过程中自动将其他语言的语音翻译为英语。", + "descriptionUnsupported": "{{model}} 模型不支持翻译功能。" + }, + "modelUnload": { + "title": "卸载模型", + "description": "当模型在指定时间内未使用时自动释放 GPU/CPU 内存", + "options": { + "never": "从不", + "immediately": "立即", + "min2": "2 分钟后", + "min5": "5 分钟后", + "min10": "10 分钟后", + "min15": "15 分钟后", + "hour1": "1 小时后", + "sec5": "5 秒后(调试)" + } + }, + "customWords": { + "title": "自定义词汇", + "description": "添加经常被误听或拼写错误的词汇。系统将自动将发音相似的词汇修正为您列表中的词汇。", + "placeholder": "添加词汇", + "add": "添加", + "remove": "删除 {{word}}" + } + }, + "postProcessing": { + "title": "后处理", + "disabledNotice": "后处理当前已禁用。请在调试设置中启用以进行配置。", + "api": { + "title": "API(兼容 OpenAI)", + "provider": { + "title": "提供商", + "description": "选择一个兼容 OpenAI 的提供商。" + }, + "appleIntelligence": { + "title": "Apple Intelligence", + "description": "完全在设备上运行。无需 API 密钥或网络访问。", + "requirements": "需要运行 macOS Tahoe(26.0)或更高版本的 Apple Silicon Mac。必须在系统设置中启用 Apple Intelligence。" + }, + "baseUrl": { + "title": "基础 URL", + "description": "所选提供商的 API 基础 URL。仅自定义提供商可编辑。", + "placeholder": "https://api.openai.com/v1" + }, + "apiKey": { + "title": "API 密钥", + "description": "所选提供商的 API 密钥。", + "placeholder": "sk-..." + }, + "model": { + "title": "模型", + "descriptionApple": "提供可选的数字令牌限制或保持默认的设备预设。", + "descriptionCustom": "提供自定义端点期望的模型标识符。", + "descriptionDefault": "选择所选提供商提供的模型。", + "placeholderApple": "Apple Intelligence", + "placeholderWithOptions": "搜索或选择模型", + "placeholderNoOptions": "输入模型名称", + "refreshModels": "刷新模型" + } + }, + "prompts": { + "title": "提示词", + "selectedPrompt": { + "title": "已选提示词", + "description": "选择用于优化转录的模板或创建新模板。在提示词文本中使用 ${output} 来引用捕获的转录。" + }, + "noPrompts": "没有可用的提示词", + "selectPrompt": "选择提示词", + "createNew": "创建新提示词", + "promptLabel": "提示词名称", + "promptLabelPlaceholder": "输入提示词名称", + "promptInstructions": "提示词指令", + "promptInstructionsPlaceholder": "编写转录后要执行的指令。示例:改进以下文本的语法和清晰度: ${output}", + "promptTip": "提示:使用 ${output} 将转录文本插入到您的提示词中。", + "updatePrompt": "更新提示词", + "deletePrompt": "删除提示词", + "createPrompt": "创建提示词", + "cancel": "取消", + "selectToEdit": "选择上方的提示词以查看和编辑其详细信息。", + "createFirst": "点击上方的「创建新提示词」来创建您的第一个后处理提示词。" + } + }, + "history": { + "title": "历史记录", + "openFolder": "打开录音文件夹", + "loading": "加载历史记录中...", + "empty": "还没有转录记录。开始录制以建立您的历史记录!", + "copyToClipboard": "复制转录到剪贴板", + "save": "保存转录", + "unsave": "从已保存中移除", + "delete": "删除条目", + "deleteError": "删除条目失败,请重试。" + }, + "debug": { + "title": "调试", + "logDirectory": { + "title": "日志目录", + "description": "日志文件的存储位置" + }, + "logLevel": { + "title": "日志级别", + "description": "设置日志的详细程度" + }, + "updateChecks": { + "label": "检查更新", + "description": "自动检查 Handy 的新版本" + }, + "soundTheme": { + "label": "声音主题", + "description": "选择录制开始和停止反馈的声音主题" + }, + "wordCorrectionThreshold": { + "title": "词汇修正阈值", + "description": "自定义词汇修正的灵敏度" + }, + "historyLimit": { + "title": "历史记录上限", + "description": "保留的最大历史条目数", + "entries": "条" + }, + "recordingRetention": { + "title": "录音保留", + "description": "保留音频录音的时长", + "never": "从不", + "preserveLimit": "保留 {{count}} 条录音", + "days3": "3 天后", + "weeks2": "2 周后", + "months3": "3 个月后", + "placeholder": "选择保留期限..." + }, + "alwaysOnMicrophone": { + "label": "麦克风常开", + "description": "保持麦克风活跃以获得更快的响应" + }, + "clamshellMicrophone": { + "title": "合盖麦克风", + "description": "笔记本电脑盖子关闭时使用的麦克风" + }, + "postProcessingToggle": { + "label": "后处理", + "description": "启用转录后的 AI 文本优化" + }, + "muteWhileRecording": { + "label": "录制时静音", + "description": "录制期间静音系统音频" + }, + "appendTrailingSpace": { + "label": "追加尾部空格", + "description": "在粘贴的转录后添加空格" + }, + "paths": { + "appData": "应用数据:", + "models": "模型:", + "settings": "设置:" + } + }, + "about": { + "title": "关于", + "version": { + "title": "版本", + "description": "Handy 的当前版本" + }, + "appDataDirectory": { + "title": "应用数据目录", + "description": "Handy 存储数据的位置" + }, + "sourceCode": { + "title": "源代码", + "description": "查看源代码并贡献", + "button": "在 GitHub 上查看" + }, + "supportDevelopment": { + "title": "支持开发", + "description": "帮助我们继续开发 Handy", + "button": "捐赠" + }, + "acknowledgments": { + "title": "致谢", + "whisper": { + "title": "Whisper.cpp", + "description": "OpenAI Whisper 自动语音识别模型的高性能推理", + "details": "Handy 使用 Whisper.cpp 进行快速的本地语音转文字处理。感谢 Georgi Gerganov 和贡献者们的出色工作。" + } + } + } + }, + "footer": { + "downloadingModel": "正在下载 {{model}}...", + "checkingUpdates": "检查更新中...", + "updateAvailable": "有可用更新: {{version}}", + "updateAvailableShort": "有可用更新", + "upToDate": "已是最新版本", + "downloadUpdate": "下载更新", + "restart": "重启", + "updateCheckingDisabled": "更新检查已禁用", + "downloading": "下载中... {{progress}}%", + "installing": "安装中...", + "preparing": "准备中...", + "checkForUpdates": "检查更新" + }, + "common": { + "loading": "加载中...", + "save": "保存", + "cancel": "取消", + "reset": "重置", + "add": "添加", + "remove": "移除", + "delete": "删除", + "edit": "编辑", + "create": "创建", + "update": "更新", + "close": "关闭", + "open": "打开", + "default": "默认", + "enabled": "已启用", + "disabled": "已禁用", + "on": "开", + "off": "关", + "yes": "是", + "no": "否", + "noOptionsFound": "未找到选项" + }, + "accessibility": { + "permissionsRequired": "需要辅助功能权限", + "permissionsDescription": "Handy 需要辅助功能权限才能输入转录的文字。", + "openSettings": "打开系统设置", + "dismiss": "关闭" + }, + "errors": { + "loadDirectory": "加载目录时出错: {{error}}" + }, + "appLanguage": { + "title": "应用语言", + "description": "更改 Handy 界面的语言" + } +}