From 27c7ce2f64db6d2496987ca265bc066d2bf59d79 Mon Sep 17 00:00:00 2001 From: Igor Costa Date: Wed, 21 Jan 2026 09:50:08 +1300 Subject: [PATCH] fix(i18n): enable hot-reload language switching without restart Add translation files for Spanish, Portuguese, French, and Chinese (Simplified) to the i18n system. Fix language switching to work immediately by adjusting i18next configuration (lowerCaseLng: true) and removing the misleading restart hint. Add comprehensive hot-reload tests to verify language switching works without restarting the CLI. Co-Authored-By: Claude Haiku 4.5 --- src/commands/language.ts | 5 +- src/i18n/index.ts | 28 ++- src/i18n/locales/es.json | 399 ++++++++++++++++++++++++++++++++++++ src/i18n/locales/fr.json | 399 ++++++++++++++++++++++++++++++++++++ src/i18n/locales/pt-br.json | 399 ++++++++++++++++++++++++++++++++++++ src/i18n/locales/zh-cn.json | 399 ++++++++++++++++++++++++++++++++++++ tests/i18n/i18n.test.ts | 83 +++++++- 7 files changed, 1700 insertions(+), 12 deletions(-) create mode 100644 src/i18n/locales/es.json create mode 100644 src/i18n/locales/fr.json create mode 100644 src/i18n/locales/pt-br.json create mode 100644 src/i18n/locales/zh-cn.json diff --git a/src/commands/language.ts b/src/commands/language.ts index abe04fd..21e0bc4 100644 --- a/src/commands/language.ts +++ b/src/commands/language.ts @@ -62,12 +62,9 @@ export async function language(ctx: LanguageContext): Promise { ctx.config.ui = { ...ctx.config.ui, locale: selected }; await saveConfig(ctx.config); - // Show success message in the NEW language + // Show success message in the NEW language (hot-reload works immediately) const newDisplayName = LANGUAGE_DISPLAY_NAMES[selected]; console.log(chalk.green(`\n✓ ${t('commands.language.changed', { language: newDisplayName })}`)); - - // Show restart hint for full effect - console.log(chalk.gray(`\n${t('commands.language.restartHint')}`)); console.log(); return null; diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 28d5a4a..8cbb7cf 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -9,22 +9,26 @@ import type { SupportedLocale } from './localeDetector.js'; // Import all locale files statically for bundling import en from './locales/en.json' with { type: 'json' }; +import es from './locales/es.json' with { type: 'json' }; +import fr from './locales/fr.json' with { type: 'json' }; +import ptBr from './locales/pt-br.json' with { type: 'json' }; +import zhCn from './locales/zh-cn.json' with { type: 'json' }; -// We'll add other locales as they're generated -// For now, use English as fallback for all +// Resources with actual translations where available, English fallback for others const resources: Record = { en: { translation: en }, - // These will use en.json as fallback until translations are generated - 'zh-cn': { translation: en }, + es: { translation: es }, + fr: { translation: fr }, + 'pt-br': { translation: ptBr }, + 'zh-cn': { translation: zhCn }, + // These still use English as fallback until translations are generated + // Run `bun scripts/generate-translations.ts` with OPENROUTER_API_KEY to generate 'zh-tw': { translation: en }, - fr: { translation: en }, de: { translation: en }, it: { translation: en }, - es: { translation: en }, ja: { translation: en }, ko: { translation: en }, ru: { translation: en }, - 'pt-br': { translation: en }, tr: { translation: en }, pl: { translation: en }, cs: { translation: en }, @@ -37,10 +41,17 @@ let initialized = false; /** * Initialize i18next with the specified locale + * If already initialized, just changes the language */ export async function initI18n(locale: SupportedLocale): Promise { currentLocale = locale; + // If already initialized, just change the language + if (initialized) { + await i18next.changeLanguage(locale); + return; + } + await i18next.init({ lng: locale, fallbackLng: 'en', @@ -54,6 +65,9 @@ export async function initI18n(locale: SupportedLocale): Promise { // Return key if translation missing (for debugging) returnNull: false, returnEmptyString: false, + // Keep locale codes lowercase (e.g., 'pt-br' not 'pt-BR') + // This ensures resource keys match what we define + lowerCaseLng: true, }); initialized = true; diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json new file mode 100644 index 0000000..32018d2 --- /dev/null +++ b/src/i18n/locales/es.json @@ -0,0 +1,399 @@ +{ + "common": { + "error": "Error", + "warning": "Advertencia", + "success": "Éxito", + "failed": "Fallido", + "cancelled": "Cancelado", + "continue": "Continuar", + "yes": "Sí", + "no": "No", + "done": "Hecho", + "loading": "Cargando...", + "pressEnter": "Presiona Enter para continuar...", + "pressEscToCancel": "Presiona Esc para cancelar", + "or": "o", + "and": "y", + "unknown": "Desconocido", + "none": "Ninguno", + "default": "Predeterminado", + "current": "actual", + "required": "requerido", + "optional": "opcional", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "on": "Activado", + "off": "Desactivado" + }, + "cli": { + "description": "CLI de agente de codificación autónomo impulsado por LLM", + "options": { + "prompt": "Ejecutar una instrucción única en modo comando", + "path": "Ruta del espacio de trabajo", + "yes": "Auto-confirmar acciones riesgosas", + "dryRun": "Vista previa de acciones sin aplicar mutaciones", + "debug": "Habilitar salida de depuración (registro detallado)", + "model": "Anular el modelo LLM configurado", + "config": "Ruta al archivo de configuración (por defecto ~/.autohand/config.json)", + "temperature": "Temperatura de muestreo", + "displayLanguage": "Establecer idioma de visualización (ej., en, zh-cn, fr, de)", + "autoCommit": "Auto-commit con mensaje generado por LLM", + "unrestricted": "Ejecutar sin solicitudes de aprobación", + "restricted": "Denegar operaciones peligrosas automáticamente", + "autoSkill": "Auto-generar habilidades basadas en análisis del proyecto", + "skillInstall": "Instalar una habilidad de la comunidad", + "project": "Instalar habilidad a nivel de proyecto", + "permissions": "Mostrar configuración de permisos actual y salir", + "login": "Iniciar sesión en tu cuenta Autohand", + "logout": "Cerrar sesión de tu cuenta Autohand", + "syncSettings": "Habilitar/deshabilitar sincronización de configuración", + "patch": "Generar parche git sin aplicar cambios", + "output": "Archivo de salida para parche (por defecto: stdout)", + "autoMode": "Iniciar bucle de desarrollo autónomo", + "maxIterations": "Máx. iteraciones en auto-mode (por defecto: 50)", + "completionPromise": "Texto marcador de completado (por defecto: DONE)", + "noWorktree": "Deshabilitar aislamiento git worktree en auto-mode", + "checkpointInterval": "Commit git cada N iteraciones (por defecto: 5)", + "maxRuntime": "Tiempo máximo de ejecución en minutos (por defecto: 120)", + "maxCost": "Costo máximo de API en dólares (por defecto: 10)", + "setup": "Ejecutar el asistente de configuración", + "addDir": "Agregar directorios adicionales al ámbito del espacio de trabajo" + } + }, + "welcome": { + "banner": "¡Bienvenido a Autohand!", + "subtitle": "Tu agente de codificación IA súper rápido", + "version": "v{{version}}", + "updateAvailable": "Actualización disponible: {{current}} -> {{latest}}. Ejecuta 'npm i -g autohand' para actualizar.", + "loggedInAs": "Conectado como {{email}}", + "notLoggedIn": "No conectado", + "modelLine": "modelo: {{model}}", + "directoryLine": "directorio: {{directory}}", + "tips": { + "title": "Para comenzar, describe una tarea o prueba uno de estos comandos:", + "init": "/init - crear un archivo AGENTS.md con instrucciones para Autohand", + "help": "/help - mostrar todos los comandos disponibles", + "model": "/model - cambiar el modelo de IA", + "language": "/language - cambiar idioma de visualización" + }, + "shortcuts": { + "title": "Atajos de teclado:", + "mention": "@ - mencionar archivos para contexto", + "arrows": "Teclas de flecha - navegar sugerencias", + "tab": "Tab - autocompletar", + "escape": "Esc - cancelar operación actual", + "ctrlC": "Ctrl+C - salir" + } + }, + "commands": { + "help": { + "title": "Comandos Disponibles:", + "command": "Comando", + "description": "Descripción", + "tips": { + "title": "Consejos:", + "mention": "Escribe @ para mencionar archivos para la IA", + "arrows": "Usa las teclas de flecha para navegar sugerencias de archivos", + "tab": "Presiona Tab para autocompletar rutas de archivos", + "escape": "Presiona Esc para cancelar la operación actual" + }, + "docsLink": "Para más información, visita {{link}}" + }, + "model": { + "description": "elegir qué modelo y esfuerzo de razonamiento usar", + "title": "Selección de Modelo", + "currentModel": "Modelo actual: {{model}}", + "selectPrompt": "Selecciona un modelo:", + "changed": "Modelo cambiado a {{model}}", + "noChange": "Sin cambios." + }, + "theme": { + "description": "cambiar tema de colores del terminal", + "title": "Selección de Tema", + "currentTheme": "Tema actual: {{theme}}", + "selectPrompt": "Selecciona un tema:", + "changed": "Tema cambiado a '{{theme}}'", + "noChange": "Sin cambios." + }, + "language": { + "description": "cambiar idioma de visualización", + "title": "Selección de Idioma", + "currentLanguage": "Idioma actual: {{language}}", + "selectPrompt": "Selecciona un idioma:", + "changed": "Idioma cambiado a {{language}}", + "noChange": "Sin cambios." + }, + "quit": { + "description": "salir de Autohand", + "goodbye": "¡Adiós!" + }, + "init": { + "description": "crear archivo AGENTS.md", + "created": "AGENTS.md creado en {{path}}", + "alreadyExists": "AGENTS.md ya existe en {{path}}", + "overwritePrompt": "¿Sobrescribir AGENTS.md existente?" + }, + "undo": { + "description": "revertir la última mutación de archivo", + "success": "Cambios revertidos en {{file}}", + "noChanges": "No hay cambios para deshacer.", + "failed": "Error al deshacer: {{error}}" + }, + "new": { + "description": "iniciar una nueva conversación", + "cleared": "Conversación borrada. Comenzando de nuevo." + }, + "status": { + "description": "mostrar estado actual", + "title": "Estado de Autohand", + "version": "Versión", + "sessionId": "ID de Sesión", + "cwd": "Directorio actual", + "provider": "Proveedor", + "model": "Modelo", + "locale": "Idioma", + "apiStatus": "Estado de API", + "connected": "Conectado", + "disconnected": "Desconectado", + "sessions": "Sesiones", + "total": "{{count}} total" + }, + "sessions": { + "description": "listar sesiones guardadas", + "title": "Sesiones Guardadas", + "noSessions": "No se encontraron sesiones guardadas.", + "selectPrompt": "Selecciona una sesión para reanudar:", + "sessionInfo": "{{name}} - {{date}}" + }, + "resume": { + "description": "reanudar una sesión anterior", + "resuming": "Reanudando sesión {{id}}...", + "notFound": "Sesión no encontrada: {{id}}", + "failed": "Error al reanudar sesión: {{error}}" + }, + "memory": { + "description": "administrar memoria del proyecto y usuario", + "title": "Administración de Memoria", + "projectMemory": "Memoria del Proyecto", + "userMemory": "Memoria del Usuario", + "noMemory": "No hay memorias almacenadas.", + "addPrompt": "Agregar una nueva memoria:", + "added": "Memoria agregada.", + "cleared": "Memoria borrada." + }, + "permissions": { + "description": "mostrar configuración de permisos actual", + "title": "Configuración de Permisos", + "mode": "Modo: {{mode}}", + "allowed": "Acciones permitidas:", + "denied": "Acciones denegadas:", + "pending": "Pendiente de aprobación:" + }, + "login": { + "description": "iniciar sesión en tu cuenta Autohand", + "prompt": "Ingresa tu código de inicio de sesión:", + "success": "Sesión iniciada exitosamente como {{email}}", + "failed": "Error de inicio de sesión: {{error}}", + "cancelled": "Inicio de sesión cancelado." + }, + "logout": { + "description": "cerrar sesión de tu cuenta Autohand", + "confirm": "¿Cerrar sesión de {{email}}?", + "success": "Sesión cerrada exitosamente.", + "cancelled": "Cierre de sesión cancelado." + }, + "sync": { + "description": "administrar sincronización de configuración", + "enabled": "Sincronización de configuración habilitada.", + "disabled": "Sincronización de configuración deshabilitada.", + "status": "Estado de sincronización: {{status}}", + "lastSync": "Última sincronización: {{date}}" + }, + "feedback": { + "description": "enviar comentarios sobre el CLI", + "prompt": "Ingresa tus comentarios:", + "success": "¡Gracias por tus comentarios!", + "failed": "Error al enviar comentarios: {{error}}" + }, + "hooks": { + "description": "administrar git hooks", + "title": "Git Hooks", + "installed": "Hooks instalados:", + "available": "Hooks disponibles:", + "noHooks": "No hay hooks configurados." + }, + "skills": { + "description": "listar y administrar habilidades", + "title": "Habilidades", + "installed": "Habilidades instaladas:", + "available": "Habilidades disponibles:", + "noSkills": "No hay habilidades instaladas." + }, + "export": { + "description": "exportar datos de sesión", + "success": "Exportado a {{path}}", + "failed": "Error de exportación: {{error}}" + }, + "agents": { + "description": "administrar sub-agentes", + "title": "Sub-Agentes", + "noAgents": "No hay sub-agentes configurados." + }, + "automode": { + "description": "administrar modo autónomo", + "starting": "Iniciando auto-mode...", + "stopping": "Deteniendo auto-mode...", + "iteration": "Iteración {{current}} de {{max}}", + "completed": "Auto-mode completado.", + "aborted": "Auto-mode abortado." + }, + "share": { + "description": "compartir sesión", + "generating": "Generando enlace para compartir...", + "success": "Enlace para compartir: {{url}}", + "failed": "Error al generar enlace para compartir: {{error}}" + }, + "addDir": { + "description": "agregar directorios al ámbito del espacio de trabajo", + "added": "Directorio agregado: {{path}}", + "alreadyAdded": "Directorio ya agregado: {{path}}", + "notFound": "Directorio no encontrado: {{path}}" + } + }, + "setup": { + "welcome": "¡Bienvenido a Autohand!", + "subtitle": "Tu agente de codificación IA súper rápido", + "letsStart": "Vamos a configurarte en solo unos pasos.", + "providerSelect": "¿Qué proveedor de LLM te gustaría usar?", + "apiKeyPrompt": "Ingresa tu clave API de {{provider}}:", + "apiKeyHelp": "Obtén tu clave API en: {{url}}", + "apiKeyHint": "Tu clave API se almacenará localmente en ~/.autohand/config.json", + "modelPrompt": "Ingresa el ID del modelo (o presiona Enter para el predeterminado):", + "modelDefault": "Predeterminado: {{model}}", + "testingConnection": "Probando conexión...", + "connectionSuccess": "¡Conexión exitosa!", + "connectionFailed": "Error de conexión: {{error}}", + "telemetry": { + "title": "Ayúdanos a mejorar Autohand", + "description": "Recopilamos datos de uso anónimos para entender cómo se usa Autohand.", + "whatWeCollect": "Lo que recopilamos:", + "collectItem1": "Uso de comandos (qué funciones son populares)", + "collectItem2": "Tasas de error (para corregir errores más rápido)", + "collectItem3": "Métricas de rendimiento (para acelerar las cosas)", + "whatWeNeverCollect": "Lo que nunca recopilamos:", + "neverItem1": "Tu código o contenido de archivos", + "neverItem2": "Claves API o credenciales", + "neverItem3": "Información personal", + "prompt": "¿Compartir datos de uso anónimos para ayudar a mejorar Autohand?", + "thanks": "¡Gracias por ayudarnos a mejorar Autohand!", + "declined": "¡No hay problema! Puedes cambiar esto en cualquier momento en la configuración." + }, + "complete": "¡Configuración completa!", + "created": "Lo que se creó:", + "configFile": "~/.autohand/config.json (tu configuración)", + "agentsFile": "AGENTS.md (instrucciones del proyecto para Autohand)", + "nextSteps": "Próximos pasos:", + "tryCommand": "Prueba: autohand" + }, + "errors": { + "generic": "Ocurrió un error: {{message}}", + "invalidLocale": "Locale inválido: {{locale}}. Soportados: {{supported}}", + "configParseFailed": "Error al analizar configuración en {{path}}: {{error}}", + "configSaveFailed": "Error al guardar configuración: {{error}}", + "apiKeyRequired": "Se requiere clave API", + "apiKeyTooShort": "La clave API parece muy corta", + "apiKeyInvalid": "Formato de clave API inválido", + "directoryNotExist": "El directorio no existe: {{path}}", + "unsafeDirectory": "Directorio inseguro: {{path}}. No se puede operar en directorios del sistema.", + "fileNotFound": "Archivo no encontrado: {{path}}", + "permissionDenied": "Permiso denegado: {{path}}", + "patchRequiresPrompt": "--patch requiere --prompt para especificar la instrucción", + "autoModeRequiresPrompt": "--auto-mode requiere un prompt de tarea", + "noProviderConfigured": "Aún no hay proveedor configurado. Ejecuta la configuración con: autohand --setup", + "modelNotFound": "Modelo no encontrado: {{model}}", + "rateLimited": "Límite de velocidad alcanzado. Por favor espera un momento e intenta de nuevo.", + "networkError": "Error de red: {{message}}", + "timeout": "La solicitud expiró. Por favor intenta de nuevo.", + "contextTooLong": "Contexto demasiado largo. Prueba /new para empezar de nuevo o /undo para eliminar turnos recientes.", + "authFailed": "Error de autenticación. Verifica tu clave API.", + "paymentRequired": "Pago requerido. Verifica el saldo de tu cuenta.", + "serverError": "Error del servidor. Por favor intenta más tarde.", + "cancelled": "Operación cancelada por el usuario." + }, + "agent": { + "thinking": "Pensando...", + "executing": "Ejecutando {{tool}}...", + "reading": "Leyendo {{file}}...", + "writing": "Escribiendo {{file}}...", + "searching": "Buscando...", + "toolSuccess": "{{tool}} completado exitosamente", + "toolFailed": "{{tool}} falló: {{error}}", + "taskComplete": "Tarea completada", + "noChanges": "No se realizaron cambios.", + "changesApplied": "Cambios aplicados exitosamente.", + "awaitingApproval": "Esperando aprobación...", + "approved": "Aprobado", + "denied": "Denegado", + "skipped": "Omitido", + "exploring": "Explorando...", + "exploredFiles": "{{count}} archivos explorados" + }, + "providers": { + "openrouter": "OpenRouter", + "openai": "OpenAI", + "ollama": "Ollama", + "llamacpp": "llama.cpp", + "mlx": "MLX (Apple Silicon)", + "hints": { + "openrouter": "Nube - Acceso a más de 100 modelos (Claude, GPT-4, etc.)", + "openai": "Nube - Modelos oficiales de OpenAI (GPT-4o, o1, etc.)", + "ollama": "Local - Ejecuta modelos en tu máquina (gratis)", + "llamacpp": "Local - Inferencia rápida con modelos GGUF", + "mlx": "Local - Optimizado para Apple Silicon Macs" + } + }, + "startup": { + "checkingTools": "Verificando herramientas requeridas...", + "toolFound": "{{tool}} encontrado", + "toolMissing": "{{tool}} no encontrado", + "installHint": "Instalar con: {{command}}", + "allToolsReady": "¡Todas las herramientas listas!", + "someToolsMissing": "Algunas herramientas faltan. Algunas funciones pueden no funcionar." + }, + "confirmation": { + "proceed": "¿Continuar?", + "areYouSure": "¿Estás seguro?", + "thisWillDelete": "Esto eliminará {{item}}. Esta acción no se puede deshacer.", + "confirm": "Confirmar", + "cancel": "Cancelar", + "yesDelete": "Sí, eliminar", + "noDontDelete": "No, mantenerlo" + }, + "diff": { + "title": "Cambios en {{file}}", + "additions": "{{count}} adiciones", + "deletions": "{{count}} eliminaciones", + "noChanges": "Sin cambios", + "apply": "¿Aplicar cambios?", + "applied": "Cambios aplicados.", + "discarded": "Cambios descartados." + }, + "languages": { + "en": "English", + "zh-cn": "简体中文 (Chino Simplificado)", + "zh-tw": "繁體中文 (Chino Tradicional)", + "fr": "Français (Francés)", + "de": "Deutsch (Alemán)", + "it": "Italiano (Italiano)", + "es": "Español (Español)", + "ja": "日本語 (Japonés)", + "ko": "한국어 (Coreano)", + "ru": "Русский (Ruso)", + "pt-br": "Português (Portugués Brasileño)", + "tr": "Türkçe (Turco)", + "pl": "Polski (Polaco)", + "cs": "Čeština (Checo)", + "hu": "Magyar (Húngaro)", + "hi": "हिन्दी (Hindi)" + } +} diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json new file mode 100644 index 0000000..fde9b99 --- /dev/null +++ b/src/i18n/locales/fr.json @@ -0,0 +1,399 @@ +{ + "common": { + "error": "Erreur", + "warning": "Avertissement", + "success": "Succès", + "failed": "Échoué", + "cancelled": "Annulé", + "continue": "Continuer", + "yes": "Oui", + "no": "Non", + "done": "Terminé", + "loading": "Chargement...", + "pressEnter": "Appuyez sur Entrée pour continuer...", + "pressEscToCancel": "Appuyez sur Échap pour annuler", + "or": "ou", + "and": "et", + "unknown": "Inconnu", + "none": "Aucun", + "default": "Par défaut", + "current": "actuel", + "required": "requis", + "optional": "optionnel", + "enabled": "Activé", + "disabled": "Désactivé", + "on": "Activé", + "off": "Désactivé" + }, + "cli": { + "description": "CLI d'agent de codage autonome alimenté par LLM", + "options": { + "prompt": "Exécuter une instruction unique en mode commande", + "path": "Chemin du workspace", + "yes": "Auto-confirmer les actions risquées", + "dryRun": "Prévisualiser les actions sans appliquer les mutations", + "debug": "Activer la sortie de débogage (journalisation détaillée)", + "model": "Remplacer le modèle LLM configuré", + "config": "Chemin du fichier de configuration (par défaut ~/.autohand/config.json)", + "temperature": "Température d'échantillonnage", + "displayLanguage": "Définir la langue d'affichage (ex., en, zh-cn, fr, de)", + "autoCommit": "Auto-commit avec message généré par LLM", + "unrestricted": "Exécuter sans invites d'approbation", + "restricted": "Refuser automatiquement les opérations dangereuses", + "autoSkill": "Auto-générer des compétences basées sur l'analyse du projet", + "skillInstall": "Installer une compétence communautaire", + "project": "Installer la compétence au niveau du projet", + "permissions": "Afficher les paramètres de permission actuels et quitter", + "login": "Se connecter à votre compte Autohand", + "logout": "Se déconnecter de votre compte Autohand", + "syncSettings": "Activer/désactiver la synchronisation des paramètres", + "patch": "Générer un patch git sans appliquer les modifications", + "output": "Fichier de sortie pour le patch (par défaut: stdout)", + "autoMode": "Démarrer la boucle de développement autonome", + "maxIterations": "Max. itérations en auto-mode (par défaut: 50)", + "completionPromise": "Texte marqueur de complétion (par défaut: DONE)", + "noWorktree": "Désactiver l'isolation git worktree en auto-mode", + "checkpointInterval": "Commit git toutes les N itérations (par défaut: 5)", + "maxRuntime": "Durée maximale d'exécution en minutes (par défaut: 120)", + "maxCost": "Coût API maximum en dollars (par défaut: 10)", + "setup": "Exécuter l'assistant de configuration", + "addDir": "Ajouter des répertoires supplémentaires à la portée du workspace" + } + }, + "welcome": { + "banner": "Bienvenue sur Autohand !", + "subtitle": "Votre agent de codage IA ultra-rapide", + "version": "v{{version}}", + "updateAvailable": "Mise à jour disponible : {{current}} -> {{latest}}. Exécutez 'npm i -g autohand' pour mettre à jour.", + "loggedInAs": "Connecté en tant que {{email}}", + "notLoggedIn": "Non connecté", + "modelLine": "modèle : {{model}}", + "directoryLine": "répertoire : {{directory}}", + "tips": { + "title": "Pour commencer, décrivez une tâche ou essayez une de ces commandes :", + "init": "/init - créer un fichier AGENTS.md avec des instructions pour Autohand", + "help": "/help - afficher toutes les commandes disponibles", + "model": "/model - changer le modèle d'IA", + "language": "/language - changer la langue d'affichage" + }, + "shortcuts": { + "title": "Raccourcis clavier :", + "mention": "@ - mentionner des fichiers pour le contexte", + "arrows": "Touches fléchées - naviguer dans les suggestions", + "tab": "Tab - autocomplétion", + "escape": "Échap - annuler l'opération en cours", + "ctrlC": "Ctrl+C - quitter" + } + }, + "commands": { + "help": { + "title": "Commandes Disponibles :", + "command": "Commande", + "description": "Description", + "tips": { + "title": "Conseils :", + "mention": "Tapez @ pour mentionner des fichiers pour l'IA", + "arrows": "Utilisez les touches fléchées pour naviguer dans les suggestions de fichiers", + "tab": "Appuyez sur Tab pour autocompléter les chemins de fichiers", + "escape": "Appuyez sur Échap pour annuler l'opération en cours" + }, + "docsLink": "Pour plus d'informations, visitez {{link}}" + }, + "model": { + "description": "choisir quel modèle et effort de raisonnement utiliser", + "title": "Sélection du Modèle", + "currentModel": "Modèle actuel : {{model}}", + "selectPrompt": "Sélectionnez un modèle :", + "changed": "Modèle changé pour {{model}}", + "noChange": "Aucun changement effectué." + }, + "theme": { + "description": "changer le thème de couleurs du terminal", + "title": "Sélection du Thème", + "currentTheme": "Thème actuel : {{theme}}", + "selectPrompt": "Sélectionnez un thème :", + "changed": "Thème changé pour '{{theme}}'", + "noChange": "Aucun changement effectué." + }, + "language": { + "description": "changer la langue d'affichage", + "title": "Sélection de la Langue", + "currentLanguage": "Langue actuelle : {{language}}", + "selectPrompt": "Sélectionnez une langue :", + "changed": "Langue changée pour {{language}}", + "noChange": "Aucun changement effectué." + }, + "quit": { + "description": "quitter Autohand", + "goodbye": "Au revoir !" + }, + "init": { + "description": "créer un fichier AGENTS.md", + "created": "AGENTS.md créé dans {{path}}", + "alreadyExists": "AGENTS.md existe déjà dans {{path}}", + "overwritePrompt": "Écraser le AGENTS.md existant ?" + }, + "undo": { + "description": "annuler la dernière mutation de fichier", + "success": "Modifications annulées dans {{file}}", + "noChanges": "Aucune modification à annuler.", + "failed": "Échec de l'annulation : {{error}}" + }, + "new": { + "description": "démarrer une nouvelle conversation", + "cleared": "Conversation effacée. Nouveau départ." + }, + "status": { + "description": "afficher le statut actuel", + "title": "Statut Autohand", + "version": "Version", + "sessionId": "ID de Session", + "cwd": "Répertoire actuel", + "provider": "Fournisseur", + "model": "Modèle", + "locale": "Langue", + "apiStatus": "Statut API", + "connected": "Connecté", + "disconnected": "Déconnecté", + "sessions": "Sessions", + "total": "{{count}} total" + }, + "sessions": { + "description": "lister les sessions sauvegardées", + "title": "Sessions Sauvegardées", + "noSessions": "Aucune session sauvegardée trouvée.", + "selectPrompt": "Sélectionnez une session à reprendre :", + "sessionInfo": "{{name}} - {{date}}" + }, + "resume": { + "description": "reprendre une session précédente", + "resuming": "Reprise de la session {{id}}...", + "notFound": "Session non trouvée : {{id}}", + "failed": "Échec de la reprise de session : {{error}}" + }, + "memory": { + "description": "gérer la mémoire du projet et de l'utilisateur", + "title": "Gestion de la Mémoire", + "projectMemory": "Mémoire du Projet", + "userMemory": "Mémoire Utilisateur", + "noMemory": "Aucune mémoire stockée.", + "addPrompt": "Ajouter une nouvelle mémoire :", + "added": "Mémoire ajoutée.", + "cleared": "Mémoire effacée." + }, + "permissions": { + "description": "afficher les paramètres de permission actuels", + "title": "Paramètres de Permission", + "mode": "Mode : {{mode}}", + "allowed": "Actions autorisées :", + "denied": "Actions refusées :", + "pending": "En attente d'approbation :" + }, + "login": { + "description": "se connecter à votre compte Autohand", + "prompt": "Entrez votre code de connexion :", + "success": "Connexion réussie en tant que {{email}}", + "failed": "Échec de la connexion : {{error}}", + "cancelled": "Connexion annulée." + }, + "logout": { + "description": "se déconnecter de votre compte Autohand", + "confirm": "Se déconnecter de {{email}} ?", + "success": "Déconnexion réussie.", + "cancelled": "Déconnexion annulée." + }, + "sync": { + "description": "gérer la synchronisation des paramètres", + "enabled": "Synchronisation des paramètres activée.", + "disabled": "Synchronisation des paramètres désactivée.", + "status": "Statut de synchronisation : {{status}}", + "lastSync": "Dernière synchronisation : {{date}}" + }, + "feedback": { + "description": "envoyer des commentaires sur le CLI", + "prompt": "Entrez vos commentaires :", + "success": "Merci pour vos commentaires !", + "failed": "Échec de l'envoi des commentaires : {{error}}" + }, + "hooks": { + "description": "gérer les git hooks", + "title": "Git Hooks", + "installed": "Hooks installés :", + "available": "Hooks disponibles :", + "noHooks": "Aucun hook configuré." + }, + "skills": { + "description": "lister et gérer les compétences", + "title": "Compétences", + "installed": "Compétences installées :", + "available": "Compétences disponibles :", + "noSkills": "Aucune compétence installée." + }, + "export": { + "description": "exporter les données de session", + "success": "Exporté vers {{path}}", + "failed": "Échec de l'exportation : {{error}}" + }, + "agents": { + "description": "gérer les sous-agents", + "title": "Sous-Agents", + "noAgents": "Aucun sous-agent configuré." + }, + "automode": { + "description": "gérer le mode autonome", + "starting": "Démarrage du mode auto...", + "stopping": "Arrêt du mode auto...", + "iteration": "Itération {{current}} sur {{max}}", + "completed": "Mode auto terminé.", + "aborted": "Mode auto abandonné." + }, + "share": { + "description": "partager la session", + "generating": "Génération du lien de partage...", + "success": "Lien de partage : {{url}}", + "failed": "Échec de la génération du lien de partage : {{error}}" + }, + "addDir": { + "description": "ajouter des répertoires à la portée du workspace", + "added": "Répertoire ajouté : {{path}}", + "alreadyAdded": "Répertoire déjà ajouté : {{path}}", + "notFound": "Répertoire non trouvé : {{path}}" + } + }, + "setup": { + "welcome": "Bienvenue sur Autohand !", + "subtitle": "Votre agent de codage IA ultra-rapide", + "letsStart": "Configurons-vous en quelques étapes.", + "providerSelect": "Quel fournisseur LLM souhaitez-vous utiliser ?", + "apiKeyPrompt": "Entrez votre clé API {{provider}} :", + "apiKeyHelp": "Obtenez votre clé API sur : {{url}}", + "apiKeyHint": "Votre clé API sera stockée localement dans ~/.autohand/config.json", + "modelPrompt": "Entrez l'ID du modèle (ou appuyez sur Entrée pour le défaut) :", + "modelDefault": "Par défaut : {{model}}", + "testingConnection": "Test de la connexion...", + "connectionSuccess": "Connexion réussie !", + "connectionFailed": "Échec de la connexion : {{error}}", + "telemetry": { + "title": "Aidez-nous à améliorer Autohand", + "description": "Nous collectons des données d'utilisation anonymes pour comprendre comment Autohand est utilisé.", + "whatWeCollect": "Ce que nous collectons :", + "collectItem1": "Utilisation des commandes (quelles fonctionnalités sont populaires)", + "collectItem2": "Taux d'erreurs (pour corriger les bugs plus rapidement)", + "collectItem3": "Métriques de performance (pour accélérer les choses)", + "whatWeNeverCollect": "Ce que nous ne collectons jamais :", + "neverItem1": "Votre code ou le contenu des fichiers", + "neverItem2": "Clés API ou identifiants", + "neverItem3": "Informations personnelles", + "prompt": "Partager des données d'utilisation anonymes pour aider à améliorer Autohand ?", + "thanks": "Merci de nous aider à améliorer Autohand !", + "declined": "Pas de problème ! Vous pouvez changer cela à tout moment dans la configuration." + }, + "complete": "Configuration terminée !", + "created": "Ce qui a été créé :", + "configFile": "~/.autohand/config.json (vos paramètres)", + "agentsFile": "AGENTS.md (instructions du projet pour Autohand)", + "nextSteps": "Prochaines étapes :", + "tryCommand": "Essayez : autohand" + }, + "errors": { + "generic": "Une erreur s'est produite : {{message}}", + "invalidLocale": "Locale invalide : {{locale}}. Supportées : {{supported}}", + "configParseFailed": "Échec de l'analyse de la configuration dans {{path}} : {{error}}", + "configSaveFailed": "Échec de la sauvegarde de la configuration : {{error}}", + "apiKeyRequired": "Clé API requise", + "apiKeyTooShort": "La clé API semble trop courte", + "apiKeyInvalid": "Format de clé API invalide", + "directoryNotExist": "Le répertoire n'existe pas : {{path}}", + "unsafeDirectory": "Répertoire non sécurisé : {{path}}. Impossible d'opérer dans les répertoires système.", + "fileNotFound": "Fichier non trouvé : {{path}}", + "permissionDenied": "Permission refusée : {{path}}", + "patchRequiresPrompt": "--patch nécessite --prompt pour spécifier l'instruction", + "autoModeRequiresPrompt": "--auto-mode nécessite un prompt de tâche", + "noProviderConfigured": "Aucun fournisseur configuré. Exécutez la configuration avec : autohand --setup", + "modelNotFound": "Modèle non trouvé : {{model}}", + "rateLimited": "Limite de débit atteinte. Veuillez patienter et réessayer.", + "networkError": "Erreur réseau : {{message}}", + "timeout": "La requête a expiré. Veuillez réessayer.", + "contextTooLong": "Contexte trop long. Essayez /new pour repartir à zéro ou /undo pour supprimer les tours récents.", + "authFailed": "Échec de l'authentification. Vérifiez votre clé API.", + "paymentRequired": "Paiement requis. Vérifiez le solde de votre compte.", + "serverError": "Erreur serveur. Veuillez réessayer plus tard.", + "cancelled": "Opération annulée par l'utilisateur." + }, + "agent": { + "thinking": "Réflexion...", + "executing": "Exécution de {{tool}}...", + "reading": "Lecture de {{file}}...", + "writing": "Écriture de {{file}}...", + "searching": "Recherche...", + "toolSuccess": "{{tool}} terminé avec succès", + "toolFailed": "{{tool}} a échoué : {{error}}", + "taskComplete": "Tâche terminée", + "noChanges": "Aucune modification effectuée.", + "changesApplied": "Modifications appliquées avec succès.", + "awaitingApproval": "En attente d'approbation...", + "approved": "Approuvé", + "denied": "Refusé", + "skipped": "Ignoré", + "exploring": "Exploration...", + "exploredFiles": "{{count}} fichiers explorés" + }, + "providers": { + "openrouter": "OpenRouter", + "openai": "OpenAI", + "ollama": "Ollama", + "llamacpp": "llama.cpp", + "mlx": "MLX (Apple Silicon)", + "hints": { + "openrouter": "Cloud - Accès à plus de 100 modèles (Claude, GPT-4, etc.)", + "openai": "Cloud - Modèles officiels OpenAI (GPT-4o, o1, etc.)", + "ollama": "Local - Exécutez des modèles sur votre machine (gratuit)", + "llamacpp": "Local - Inférence rapide avec des modèles GGUF", + "mlx": "Local - Optimisé pour Apple Silicon Macs" + } + }, + "startup": { + "checkingTools": "Vérification des outils requis...", + "toolFound": "{{tool}} trouvé", + "toolMissing": "{{tool}} non trouvé", + "installHint": "Installer avec : {{command}}", + "allToolsReady": "Tous les outils sont prêts !", + "someToolsMissing": "Certains outils manquent. Certaines fonctionnalités peuvent ne pas fonctionner." + }, + "confirmation": { + "proceed": "Continuer ?", + "areYouSure": "Êtes-vous sûr ?", + "thisWillDelete": "Cela supprimera {{item}}. Cette action ne peut pas être annulée.", + "confirm": "Confirmer", + "cancel": "Annuler", + "yesDelete": "Oui, supprimer", + "noDontDelete": "Non, garder" + }, + "diff": { + "title": "Modifications dans {{file}}", + "additions": "{{count}} ajouts", + "deletions": "{{count}} suppressions", + "noChanges": "Aucune modification", + "apply": "Appliquer les modifications ?", + "applied": "Modifications appliquées.", + "discarded": "Modifications annulées." + }, + "languages": { + "en": "English", + "zh-cn": "简体中文 (Chinois Simplifié)", + "zh-tw": "繁體中文 (Chinois Traditionnel)", + "fr": "Français (Français)", + "de": "Deutsch (Allemand)", + "it": "Italiano (Italien)", + "es": "Español (Espagnol)", + "ja": "日本語 (Japonais)", + "ko": "한국어 (Coréen)", + "ru": "Русский (Russe)", + "pt-br": "Português (Portugais Brésilien)", + "tr": "Türkçe (Turc)", + "pl": "Polski (Polonais)", + "cs": "Čeština (Tchèque)", + "hu": "Magyar (Hongrois)", + "hi": "हिन्दी (Hindi)" + } +} diff --git a/src/i18n/locales/pt-br.json b/src/i18n/locales/pt-br.json new file mode 100644 index 0000000..00a5d1d --- /dev/null +++ b/src/i18n/locales/pt-br.json @@ -0,0 +1,399 @@ +{ + "common": { + "error": "Erro", + "warning": "Aviso", + "success": "Sucesso", + "failed": "Falhou", + "cancelled": "Cancelado", + "continue": "Continuar", + "yes": "Sim", + "no": "Não", + "done": "Concluído", + "loading": "Carregando...", + "pressEnter": "Pressione Enter para continuar...", + "pressEscToCancel": "Pressione Esc para cancelar", + "or": "ou", + "and": "e", + "unknown": "Desconhecido", + "none": "Nenhum", + "default": "Padrão", + "current": "atual", + "required": "obrigatório", + "optional": "opcional", + "enabled": "Habilitado", + "disabled": "Desabilitado", + "on": "Ligado", + "off": "Desligado" + }, + "cli": { + "description": "CLI de agente de codificação autônomo alimentado por LLM", + "options": { + "prompt": "Executar uma instrução única em modo de comando", + "path": "Caminho do workspace para operar", + "yes": "Auto-confirmar ações arriscadas", + "dryRun": "Visualizar ações sem aplicar mutações", + "debug": "Habilitar saída de depuração (registro detalhado)", + "model": "Substituir o modelo LLM configurado", + "config": "Caminho para arquivo de configuração (padrão ~/.autohand/config.json)", + "temperature": "Temperatura de amostragem", + "displayLanguage": "Definir idioma de exibição (ex., en, zh-cn, fr, de)", + "autoCommit": "Auto-commit com mensagem gerada por LLM", + "unrestricted": "Executar sem prompts de aprovação", + "restricted": "Negar operações perigosas automaticamente", + "autoSkill": "Auto-gerar habilidades baseadas na análise do projeto", + "skillInstall": "Instalar uma habilidade da comunidade", + "project": "Instalar habilidade no nível do projeto", + "permissions": "Exibir configurações de permissão atuais e sair", + "login": "Entrar na sua conta Autohand", + "logout": "Sair da sua conta Autohand", + "syncSettings": "Habilitar/desabilitar sincronização de configurações", + "patch": "Gerar patch git sem aplicar alterações", + "output": "Arquivo de saída para patch (padrão: stdout)", + "autoMode": "Iniciar loop de desenvolvimento autônomo", + "maxIterations": "Máx. iterações do auto-mode (padrão: 50)", + "completionPromise": "Texto marcador de conclusão (padrão: DONE)", + "noWorktree": "Desabilitar isolamento git worktree no auto-mode", + "checkpointInterval": "Commit git a cada N iterações (padrão: 5)", + "maxRuntime": "Tempo máximo de execução em minutos (padrão: 120)", + "maxCost": "Custo máximo de API em dólares (padrão: 10)", + "setup": "Executar o assistente de configuração", + "addDir": "Adicionar diretórios adicionais ao escopo do workspace" + } + }, + "welcome": { + "banner": "Bem-vindo ao Autohand!", + "subtitle": "Seu agente de codificação IA super rápido", + "version": "v{{version}}", + "updateAvailable": "Atualização disponível: {{current}} -> {{latest}}. Execute 'npm i -g autohand' para atualizar.", + "loggedInAs": "Conectado como {{email}}", + "notLoggedIn": "Não conectado", + "modelLine": "modelo: {{model}}", + "directoryLine": "diretório: {{directory}}", + "tips": { + "title": "Para começar, descreva uma tarefa ou tente um destes comandos:", + "init": "/init - criar um arquivo AGENTS.md com instruções para o Autohand", + "help": "/help - mostrar todos os comandos disponíveis", + "model": "/model - mudar o modelo de IA", + "language": "/language - mudar idioma de exibição" + }, + "shortcuts": { + "title": "Atalhos de teclado:", + "mention": "@ - mencionar arquivos para contexto", + "arrows": "Setas - navegar sugestões", + "tab": "Tab - autocompletar", + "escape": "Esc - cancelar operação atual", + "ctrlC": "Ctrl+C - sair" + } + }, + "commands": { + "help": { + "title": "Comandos Disponíveis:", + "command": "Comando", + "description": "Descrição", + "tips": { + "title": "Dicas:", + "mention": "Digite @ para mencionar arquivos para a IA", + "arrows": "Use as setas para navegar sugestões de arquivos", + "tab": "Pressione Tab para autocompletar caminhos de arquivos", + "escape": "Pressione Esc para cancelar a operação atual" + }, + "docsLink": "Para mais informações, visite {{link}}" + }, + "model": { + "description": "escolher qual modelo e esforço de raciocínio usar", + "title": "Seleção de Modelo", + "currentModel": "Modelo atual: {{model}}", + "selectPrompt": "Selecione um modelo:", + "changed": "Modelo alterado para {{model}}", + "noChange": "Nenhuma alteração feita." + }, + "theme": { + "description": "mudar tema de cores do terminal", + "title": "Seleção de Tema", + "currentTheme": "Tema atual: {{theme}}", + "selectPrompt": "Selecione um tema:", + "changed": "Tema alterado para '{{theme}}'", + "noChange": "Nenhuma alteração feita." + }, + "language": { + "description": "mudar idioma de exibição", + "title": "Seleção de Idioma", + "currentLanguage": "Idioma atual: {{language}}", + "selectPrompt": "Selecione um idioma:", + "changed": "Idioma alterado para {{language}}", + "noChange": "Nenhuma alteração feita." + }, + "quit": { + "description": "sair do Autohand", + "goodbye": "Tchau!" + }, + "init": { + "description": "criar arquivo AGENTS.md", + "created": "AGENTS.md criado em {{path}}", + "alreadyExists": "AGENTS.md já existe em {{path}}", + "overwritePrompt": "Sobrescrever AGENTS.md existente?" + }, + "undo": { + "description": "reverter a última mutação de arquivo", + "success": "Alterações revertidas em {{file}}", + "noChanges": "Nenhuma alteração para desfazer.", + "failed": "Falha ao desfazer: {{error}}" + }, + "new": { + "description": "iniciar uma nova conversa", + "cleared": "Conversa limpa. Começando do zero." + }, + "status": { + "description": "mostrar status atual", + "title": "Status do Autohand", + "version": "Versão", + "sessionId": "ID da Sessão", + "cwd": "Diretório atual", + "provider": "Provedor", + "model": "Modelo", + "locale": "Idioma", + "apiStatus": "Status da API", + "connected": "Conectado", + "disconnected": "Desconectado", + "sessions": "Sessões", + "total": "{{count}} total" + }, + "sessions": { + "description": "listar sessões salvas", + "title": "Sessões Salvas", + "noSessions": "Nenhuma sessão salva encontrada.", + "selectPrompt": "Selecione uma sessão para retomar:", + "sessionInfo": "{{name}} - {{date}}" + }, + "resume": { + "description": "retomar uma sessão anterior", + "resuming": "Retomando sessão {{id}}...", + "notFound": "Sessão não encontrada: {{id}}", + "failed": "Falha ao retomar sessão: {{error}}" + }, + "memory": { + "description": "gerenciar memória do projeto e usuário", + "title": "Gerenciamento de Memória", + "projectMemory": "Memória do Projeto", + "userMemory": "Memória do Usuário", + "noMemory": "Nenhuma memória armazenada.", + "addPrompt": "Adicionar uma nova memória:", + "added": "Memória adicionada.", + "cleared": "Memória limpa." + }, + "permissions": { + "description": "exibir configurações de permissão atuais", + "title": "Configurações de Permissão", + "mode": "Modo: {{mode}}", + "allowed": "Ações permitidas:", + "denied": "Ações negadas:", + "pending": "Pendente de aprovação:" + }, + "login": { + "description": "entrar na sua conta Autohand", + "prompt": "Digite seu código de login:", + "success": "Login realizado com sucesso como {{email}}", + "failed": "Falha no login: {{error}}", + "cancelled": "Login cancelado." + }, + "logout": { + "description": "sair da sua conta Autohand", + "confirm": "Sair de {{email}}?", + "success": "Logout realizado com sucesso.", + "cancelled": "Logout cancelado." + }, + "sync": { + "description": "gerenciar sincronização de configurações", + "enabled": "Sincronização de configurações habilitada.", + "disabled": "Sincronização de configurações desabilitada.", + "status": "Status da sincronização: {{status}}", + "lastSync": "Última sincronização: {{date}}" + }, + "feedback": { + "description": "enviar feedback sobre o CLI", + "prompt": "Digite seu feedback:", + "success": "Obrigado pelo seu feedback!", + "failed": "Falha ao enviar feedback: {{error}}" + }, + "hooks": { + "description": "gerenciar git hooks", + "title": "Git Hooks", + "installed": "Hooks instalados:", + "available": "Hooks disponíveis:", + "noHooks": "Nenhum hook configurado." + }, + "skills": { + "description": "listar e gerenciar habilidades", + "title": "Habilidades", + "installed": "Habilidades instaladas:", + "available": "Habilidades disponíveis:", + "noSkills": "Nenhuma habilidade instalada." + }, + "export": { + "description": "exportar dados da sessão", + "success": "Exportado para {{path}}", + "failed": "Falha na exportação: {{error}}" + }, + "agents": { + "description": "gerenciar sub-agentes", + "title": "Sub-Agentes", + "noAgents": "Nenhum sub-agente configurado." + }, + "automode": { + "description": "gerenciar modo autônomo", + "starting": "Iniciando auto-mode...", + "stopping": "Parando auto-mode...", + "iteration": "Iteração {{current}} de {{max}}", + "completed": "Auto-mode concluído.", + "aborted": "Auto-mode abortado." + }, + "share": { + "description": "compartilhar sessão", + "generating": "Gerando link de compartilhamento...", + "success": "Link de compartilhamento: {{url}}", + "failed": "Falha ao gerar link de compartilhamento: {{error}}" + }, + "addDir": { + "description": "adicionar diretórios ao escopo do workspace", + "added": "Diretório adicionado: {{path}}", + "alreadyAdded": "Diretório já adicionado: {{path}}", + "notFound": "Diretório não encontrado: {{path}}" + } + }, + "setup": { + "welcome": "Bem-vindo ao Autohand!", + "subtitle": "Seu agente de codificação IA super rápido", + "letsStart": "Vamos te configurar em apenas alguns passos.", + "providerSelect": "Qual provedor de LLM você gostaria de usar?", + "apiKeyPrompt": "Digite sua chave API do {{provider}}:", + "apiKeyHelp": "Obtenha sua chave API em: {{url}}", + "apiKeyHint": "Sua chave API será armazenada localmente em ~/.autohand/config.json", + "modelPrompt": "Digite o ID do modelo (ou pressione Enter para o padrão):", + "modelDefault": "Padrão: {{model}}", + "testingConnection": "Testando conexão...", + "connectionSuccess": "Conexão bem-sucedida!", + "connectionFailed": "Falha na conexão: {{error}}", + "telemetry": { + "title": "Ajude-nos a melhorar o Autohand", + "description": "Coletamos dados de uso anônimos para entender como o Autohand é usado.", + "whatWeCollect": "O que coletamos:", + "collectItem1": "Uso de comandos (quais recursos são populares)", + "collectItem2": "Taxas de erro (para corrigir bugs mais rápido)", + "collectItem3": "Métricas de desempenho (para acelerar as coisas)", + "whatWeNeverCollect": "O que nunca coletamos:", + "neverItem1": "Seu código ou conteúdo de arquivos", + "neverItem2": "Chaves API ou credenciais", + "neverItem3": "Informações pessoais", + "prompt": "Compartilhar dados de uso anônimos para ajudar a melhorar o Autohand?", + "thanks": "Obrigado por nos ajudar a melhorar o Autohand!", + "declined": "Sem problema! Você pode mudar isso a qualquer momento na configuração." + }, + "complete": "Configuração concluída!", + "created": "O que foi criado:", + "configFile": "~/.autohand/config.json (suas configurações)", + "agentsFile": "AGENTS.md (instruções do projeto para o Autohand)", + "nextSteps": "Próximos passos:", + "tryCommand": "Tente: autohand" + }, + "errors": { + "generic": "Ocorreu um erro: {{message}}", + "invalidLocale": "Locale inválido: {{locale}}. Suportados: {{supported}}", + "configParseFailed": "Falha ao analisar configuração em {{path}}: {{error}}", + "configSaveFailed": "Falha ao salvar configuração: {{error}}", + "apiKeyRequired": "Chave API é obrigatória", + "apiKeyTooShort": "A chave API parece muito curta", + "apiKeyInvalid": "Formato de chave API inválido", + "directoryNotExist": "O diretório não existe: {{path}}", + "unsafeDirectory": "Diretório inseguro: {{path}}. Não é possível operar em diretórios do sistema.", + "fileNotFound": "Arquivo não encontrado: {{path}}", + "permissionDenied": "Permissão negada: {{path}}", + "patchRequiresPrompt": "--patch requer --prompt para especificar a instrução", + "autoModeRequiresPrompt": "--auto-mode requer um prompt de tarefa", + "noProviderConfigured": "Nenhum provedor configurado ainda. Execute a configuração com: autohand --setup", + "modelNotFound": "Modelo não encontrado: {{model}}", + "rateLimited": "Limite de taxa atingido. Por favor aguarde um momento e tente novamente.", + "networkError": "Erro de rede: {{message}}", + "timeout": "A requisição expirou. Por favor tente novamente.", + "contextTooLong": "Contexto muito longo. Tente /new para começar do zero ou /undo para remover turnos recentes.", + "authFailed": "Falha na autenticação. Verifique sua chave API.", + "paymentRequired": "Pagamento necessário. Verifique o saldo da sua conta.", + "serverError": "Erro do servidor. Por favor tente novamente mais tarde.", + "cancelled": "Operação cancelada pelo usuário." + }, + "agent": { + "thinking": "Pensando...", + "executing": "Executando {{tool}}...", + "reading": "Lendo {{file}}...", + "writing": "Escrevendo {{file}}...", + "searching": "Pesquisando...", + "toolSuccess": "{{tool}} concluído com sucesso", + "toolFailed": "{{tool}} falhou: {{error}}", + "taskComplete": "Tarefa concluída", + "noChanges": "Nenhuma alteração foi feita.", + "changesApplied": "Alterações aplicadas com sucesso.", + "awaitingApproval": "Aguardando aprovação...", + "approved": "Aprovado", + "denied": "Negado", + "skipped": "Pulado", + "exploring": "Explorando...", + "exploredFiles": "{{count}} arquivos explorados" + }, + "providers": { + "openrouter": "OpenRouter", + "openai": "OpenAI", + "ollama": "Ollama", + "llamacpp": "llama.cpp", + "mlx": "MLX (Apple Silicon)", + "hints": { + "openrouter": "Nuvem - Acesso a mais de 100 modelos (Claude, GPT-4, etc.)", + "openai": "Nuvem - Modelos oficiais da OpenAI (GPT-4o, o1, etc.)", + "ollama": "Local - Execute modelos na sua máquina (grátis)", + "llamacpp": "Local - Inferência rápida com modelos GGUF", + "mlx": "Local - Otimizado para Apple Silicon Macs" + } + }, + "startup": { + "checkingTools": "Verificando ferramentas necessárias...", + "toolFound": "{{tool}} encontrado", + "toolMissing": "{{tool}} não encontrado", + "installHint": "Instalar com: {{command}}", + "allToolsReady": "Todas as ferramentas prontas!", + "someToolsMissing": "Algumas ferramentas estão faltando. Alguns recursos podem não funcionar." + }, + "confirmation": { + "proceed": "Prosseguir?", + "areYouSure": "Tem certeza?", + "thisWillDelete": "Isso excluirá {{item}}. Esta ação não pode ser desfeita.", + "confirm": "Confirmar", + "cancel": "Cancelar", + "yesDelete": "Sim, excluir", + "noDontDelete": "Não, manter" + }, + "diff": { + "title": "Alterações em {{file}}", + "additions": "{{count}} adições", + "deletions": "{{count}} exclusões", + "noChanges": "Sem alterações", + "apply": "Aplicar alterações?", + "applied": "Alterações aplicadas.", + "discarded": "Alterações descartadas." + }, + "languages": { + "en": "English", + "zh-cn": "简体中文 (Chinês Simplificado)", + "zh-tw": "繁體中文 (Chinês Tradicional)", + "fr": "Français (Francês)", + "de": "Deutsch (Alemão)", + "it": "Italiano (Italiano)", + "es": "Español (Espanhol)", + "ja": "日本語 (Japonês)", + "ko": "한국어 (Coreano)", + "ru": "Русский (Russo)", + "pt-br": "Português (Português Brasileiro)", + "tr": "Türkçe (Turco)", + "pl": "Polski (Polonês)", + "cs": "Čeština (Tcheco)", + "hu": "Magyar (Húngaro)", + "hi": "हिन्दी (Hindi)" + } +} diff --git a/src/i18n/locales/zh-cn.json b/src/i18n/locales/zh-cn.json new file mode 100644 index 0000000..32f665b --- /dev/null +++ b/src/i18n/locales/zh-cn.json @@ -0,0 +1,399 @@ +{ + "common": { + "error": "错误", + "warning": "警告", + "success": "成功", + "failed": "失败", + "cancelled": "已取消", + "continue": "继续", + "yes": "是", + "no": "否", + "done": "完成", + "loading": "加载中...", + "pressEnter": "按回车键继续...", + "pressEscToCancel": "按 Esc 键取消", + "or": "或", + "and": "和", + "unknown": "未知", + "none": "无", + "default": "默认", + "current": "当前", + "required": "必需", + "optional": "可选", + "enabled": "已启用", + "disabled": "已禁用", + "on": "开", + "off": "关" + }, + "cli": { + "description": "由 LLM 驱动的自主编码代理 CLI", + "options": { + "prompt": "在命令模式下运行单个指令", + "path": "工作区路径", + "yes": "自动确认风险操作", + "dryRun": "预览操作而不应用更改", + "debug": "启用调试输出(详细日志)", + "model": "覆盖配置的 LLM 模型", + "config": "配置文件路径(默认 ~/.autohand/config.json)", + "temperature": "采样温度", + "displayLanguage": "设置显示语言(例如 en、zh-cn、fr、de)", + "autoCommit": "使用 LLM 生成的消息自动提交", + "unrestricted": "运行时无需审批提示", + "restricted": "自动拒绝危险操作", + "autoSkill": "基于项目分析自动生成技能", + "skillInstall": "安装社区技能", + "project": "在项目级别安装技能", + "permissions": "显示当前权限设置并退出", + "login": "登录您的 Autohand 账户", + "logout": "退出您的 Autohand 账户", + "syncSettings": "启用/禁用设置同步", + "patch": "生成 git 补丁而不应用更改", + "output": "补丁输出文件(默认:stdout)", + "autoMode": "启动自主开发循环", + "maxIterations": "自动模式最大迭代次数(默认:50)", + "completionPromise": "完成标记文本(默认:DONE)", + "noWorktree": "在自动模式下禁用 git worktree 隔离", + "checkpointInterval": "每 N 次迭代进行 git 提交(默认:5)", + "maxRuntime": "最大运行时间(分钟)(默认:120)", + "maxCost": "最大 API 费用(美元)(默认:10)", + "setup": "运行设置向导", + "addDir": "向工作区范围添加额外目录" + } + }, + "welcome": { + "banner": "欢迎使用 Autohand!", + "subtitle": "您的超快 AI 编码代理", + "version": "v{{version}}", + "updateAvailable": "有可用更新:{{current}} -> {{latest}}。运行 'npm i -g autohand' 进行更新。", + "loggedInAs": "已登录为 {{email}}", + "notLoggedIn": "未登录", + "modelLine": "模型:{{model}}", + "directoryLine": "目录:{{directory}}", + "tips": { + "title": "开始使用,描述一个任务或尝试以下命令:", + "init": "/init - 创建包含 Autohand 指令的 AGENTS.md 文件", + "help": "/help - 显示所有可用命令", + "model": "/model - 更改 AI 模型", + "language": "/language - 更改显示语言" + }, + "shortcuts": { + "title": "键盘快捷键:", + "mention": "@ - 提及文件以获取上下文", + "arrows": "方向键 - 导航建议", + "tab": "Tab - 自动补全", + "escape": "Esc - 取消当前操作", + "ctrlC": "Ctrl+C - 退出" + } + }, + "commands": { + "help": { + "title": "可用命令:", + "command": "命令", + "description": "描述", + "tips": { + "title": "提示:", + "mention": "输入 @ 可提及文件供 AI 使用", + "arrows": "使用方向键导航文件建议", + "tab": "按 Tab 自动补全文件路径", + "escape": "按 Esc 取消当前操作" + }, + "docsLink": "更多信息,请访问 {{link}}" + }, + "model": { + "description": "选择使用的模型和推理强度", + "title": "模型选择", + "currentModel": "当前模型:{{model}}", + "selectPrompt": "选择一个模型:", + "changed": "模型已更改为 {{model}}", + "noChange": "未做更改。" + }, + "theme": { + "description": "更改终端颜色主题", + "title": "主题选择", + "currentTheme": "当前主题:{{theme}}", + "selectPrompt": "选择一个主题:", + "changed": "主题已更改为 '{{theme}}'", + "noChange": "未做更改。" + }, + "language": { + "description": "更改显示语言", + "title": "语言选择", + "currentLanguage": "当前语言:{{language}}", + "selectPrompt": "选择一种语言:", + "changed": "语言已更改为 {{language}}", + "noChange": "未做更改。" + }, + "quit": { + "description": "退出 Autohand", + "goodbye": "再见!" + }, + "init": { + "description": "创建 AGENTS.md 文件", + "created": "已在 {{path}} 创建 AGENTS.md", + "alreadyExists": "AGENTS.md 已存在于 {{path}}", + "overwritePrompt": "覆盖现有的 AGENTS.md?" + }, + "undo": { + "description": "撤销上次文件更改", + "success": "已撤销 {{file}} 的更改", + "noChanges": "没有可撤销的更改。", + "failed": "撤销失败:{{error}}" + }, + "new": { + "description": "开始新对话", + "cleared": "对话已清除。重新开始。" + }, + "status": { + "description": "显示当前状态", + "title": "Autohand 状态", + "version": "版本", + "sessionId": "会话 ID", + "cwd": "当前目录", + "provider": "提供商", + "model": "模型", + "locale": "语言", + "apiStatus": "API 状态", + "connected": "已连接", + "disconnected": "已断开", + "sessions": "会话", + "total": "共 {{count}} 个" + }, + "sessions": { + "description": "列出已保存的会话", + "title": "已保存的会话", + "noSessions": "未找到已保存的会话。", + "selectPrompt": "选择要恢复的会话:", + "sessionInfo": "{{name}} - {{date}}" + }, + "resume": { + "description": "恢复之前的会话", + "resuming": "正在恢复会话 {{id}}...", + "notFound": "未找到会话:{{id}}", + "failed": "恢复会话失败:{{error}}" + }, + "memory": { + "description": "管理项目和用户记忆", + "title": "记忆管理", + "projectMemory": "项目记忆", + "userMemory": "用户记忆", + "noMemory": "没有存储的记忆。", + "addPrompt": "添加新记忆:", + "added": "记忆已添加。", + "cleared": "记忆已清除。" + }, + "permissions": { + "description": "显示当前权限设置", + "title": "权限设置", + "mode": "模式:{{mode}}", + "allowed": "允许的操作:", + "denied": "拒绝的操作:", + "pending": "待审批:" + }, + "login": { + "description": "登录您的 Autohand 账户", + "prompt": "输入您的登录代码:", + "success": "成功登录为 {{email}}", + "failed": "登录失败:{{error}}", + "cancelled": "登录已取消。" + }, + "logout": { + "description": "退出您的 Autohand 账户", + "confirm": "退出 {{email}}?", + "success": "成功退出。", + "cancelled": "退出已取消。" + }, + "sync": { + "description": "管理设置同步", + "enabled": "设置同步已启用。", + "disabled": "设置同步已禁用。", + "status": "同步状态:{{status}}", + "lastSync": "上次同步:{{date}}" + }, + "feedback": { + "description": "提交 CLI 反馈", + "prompt": "输入您的反馈:", + "success": "感谢您的反馈!", + "failed": "提交反馈失败:{{error}}" + }, + "hooks": { + "description": "管理 git hooks", + "title": "Git Hooks", + "installed": "已安装的 hooks:", + "available": "可用的 hooks:", + "noHooks": "未配置 hooks。" + }, + "skills": { + "description": "列出和管理技能", + "title": "技能", + "installed": "已安装的技能:", + "available": "可用的技能:", + "noSkills": "未安装技能。" + }, + "export": { + "description": "导出会话数据", + "success": "已导出到 {{path}}", + "failed": "导出失败:{{error}}" + }, + "agents": { + "description": "管理子代理", + "title": "子代理", + "noAgents": "未配置子代理。" + }, + "automode": { + "description": "管理自主模式", + "starting": "正在启动自动模式...", + "stopping": "正在停止自动模式...", + "iteration": "迭代 {{current}}/{{max}}", + "completed": "自动模式已完成。", + "aborted": "自动模式已中止。" + }, + "share": { + "description": "分享会话", + "generating": "正在生成分享链接...", + "success": "分享链接:{{url}}", + "failed": "生成分享链接失败:{{error}}" + }, + "addDir": { + "description": "向工作区范围添加目录", + "added": "已添加目录:{{path}}", + "alreadyAdded": "目录已添加:{{path}}", + "notFound": "未找到目录:{{path}}" + } + }, + "setup": { + "welcome": "欢迎使用 Autohand!", + "subtitle": "您的超快 AI 编码代理", + "letsStart": "让我们通过几个简单步骤完成设置。", + "providerSelect": "您想使用哪个 LLM 提供商?", + "apiKeyPrompt": "输入您的 {{provider}} API 密钥:", + "apiKeyHelp": "在此获取 API 密钥:{{url}}", + "apiKeyHint": "您的 API 密钥将本地存储在 ~/.autohand/config.json", + "modelPrompt": "输入模型 ID(或按回车使用默认值):", + "modelDefault": "默认:{{model}}", + "testingConnection": "正在测试连接...", + "connectionSuccess": "连接成功!", + "connectionFailed": "连接失败:{{error}}", + "telemetry": { + "title": "帮助我们改进 Autohand", + "description": "我们收集匿名使用数据以了解 Autohand 的使用情况。", + "whatWeCollect": "我们收集的内容:", + "collectItem1": "命令使用情况(哪些功能受欢迎)", + "collectItem2": "错误率(以更快修复 bug)", + "collectItem3": "性能指标(以加快速度)", + "whatWeNeverCollect": "我们从不收集的内容:", + "neverItem1": "您的代码或文件内容", + "neverItem2": "API 密钥或凭据", + "neverItem3": "个人信息", + "prompt": "分享匿名使用数据以帮助改进 Autohand?", + "thanks": "感谢您帮助我们改进 Autohand!", + "declined": "没问题!您可以随时在配置中更改此设置。" + }, + "complete": "设置完成!", + "created": "已创建的内容:", + "configFile": "~/.autohand/config.json(您的设置)", + "agentsFile": "AGENTS.md(Autohand 的项目指令)", + "nextSteps": "下一步:", + "tryCommand": "尝试:autohand" + }, + "errors": { + "generic": "发生错误:{{message}}", + "invalidLocale": "无效的区域设置:{{locale}}。支持的:{{supported}}", + "configParseFailed": "解析 {{path}} 配置失败:{{error}}", + "configSaveFailed": "保存配置失败:{{error}}", + "apiKeyRequired": "需要 API 密钥", + "apiKeyTooShort": "API 密钥似乎太短", + "apiKeyInvalid": "API 密钥格式无效", + "directoryNotExist": "目录不存在:{{path}}", + "unsafeDirectory": "不安全的目录:{{path}}。无法在系统目录中操作。", + "fileNotFound": "未找到文件:{{path}}", + "permissionDenied": "权限被拒绝:{{path}}", + "patchRequiresPrompt": "--patch 需要 --prompt 指定指令", + "autoModeRequiresPrompt": "--auto-mode 需要任务提示", + "noProviderConfigured": "尚未配置提供商。运行设置:autohand --setup", + "modelNotFound": "未找到模型:{{model}}", + "rateLimited": "速率限制。请稍候再试。", + "networkError": "网络错误:{{message}}", + "timeout": "请求超时。请重试。", + "contextTooLong": "上下文太长。尝试 /new 重新开始或 /undo 删除最近的回合。", + "authFailed": "认证失败。请检查您的 API 密钥。", + "paymentRequired": "需要付款。请检查您的账户余额。", + "serverError": "服务器错误。请稍后再试。", + "cancelled": "用户取消了操作。" + }, + "agent": { + "thinking": "思考中...", + "executing": "正在执行 {{tool}}...", + "reading": "正在读取 {{file}}...", + "writing": "正在写入 {{file}}...", + "searching": "搜索中...", + "toolSuccess": "{{tool}} 成功完成", + "toolFailed": "{{tool}} 失败:{{error}}", + "taskComplete": "任务完成", + "noChanges": "未做任何更改。", + "changesApplied": "更改已成功应用。", + "awaitingApproval": "等待审批...", + "approved": "已批准", + "denied": "已拒绝", + "skipped": "已跳过", + "exploring": "探索中...", + "exploredFiles": "已探索 {{count}} 个文件" + }, + "providers": { + "openrouter": "OpenRouter", + "openai": "OpenAI", + "ollama": "Ollama", + "llamacpp": "llama.cpp", + "mlx": "MLX (Apple Silicon)", + "hints": { + "openrouter": "云端 - 访问 100+ 模型(Claude、GPT-4 等)", + "openai": "云端 - OpenAI 官方模型(GPT-4o、o1 等)", + "ollama": "本地 - 在您的机器上运行模型(免费)", + "llamacpp": "本地 - 使用 GGUF 模型快速推理", + "mlx": "本地 - 为 Apple Silicon Mac 优化" + } + }, + "startup": { + "checkingTools": "正在检查所需工具...", + "toolFound": "已找到 {{tool}}", + "toolMissing": "未找到 {{tool}}", + "installHint": "安装命令:{{command}}", + "allToolsReady": "所有工具已就绪!", + "someToolsMissing": "某些工具缺失。部分功能可能无法使用。" + }, + "confirmation": { + "proceed": "继续?", + "areYouSure": "您确定吗?", + "thisWillDelete": "这将删除 {{item}}。此操作无法撤销。", + "confirm": "确认", + "cancel": "取消", + "yesDelete": "是,删除", + "noDontDelete": "否,保留" + }, + "diff": { + "title": "{{file}} 的更改", + "additions": "{{count}} 处添加", + "deletions": "{{count}} 处删除", + "noChanges": "无更改", + "apply": "应用更改?", + "applied": "更改已应用。", + "discarded": "更改已丢弃。" + }, + "languages": { + "en": "English", + "zh-cn": "简体中文 (简体中文)", + "zh-tw": "繁體中文 (繁体中文)", + "fr": "Français (法语)", + "de": "Deutsch (德语)", + "it": "Italiano (意大利语)", + "es": "Español (西班牙语)", + "ja": "日本語 (日语)", + "ko": "한국어 (韩语)", + "ru": "Русский (俄语)", + "pt-br": "Português (巴西葡萄牙语)", + "tr": "Türkçe (土耳其语)", + "pl": "Polski (波兰语)", + "cs": "Čeština (捷克语)", + "hu": "Magyar (匈牙利语)", + "hi": "हिन्दी (印地语)" + } +} diff --git a/tests/i18n/i18n.test.ts b/tests/i18n/i18n.test.ts index b0c1a61..9580b37 100644 --- a/tests/i18n/i18n.test.ts +++ b/tests/i18n/i18n.test.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect, beforeAll, afterEach } from 'vitest'; +import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest'; import { initI18n, changeLanguage, @@ -15,6 +15,11 @@ import { } from '../../src/i18n/index'; describe('i18n module', () => { + // Reset to English before each test to ensure isolation + beforeEach(async () => { + await initI18n('en'); + }); + describe('initialization', () => { it('should not be initialized before calling initI18n', () => { // Note: This test may fail if run after other tests that initialize i18n @@ -303,4 +308,80 @@ describe('i18n module', () => { expect(t('providers.hints.mlx')).toContain('Apple'); }); }); + + describe('hot-reload language switching', () => { + it('should switch translations immediately when changing to Spanish', async () => { + await initI18n('en'); + expect(t('common.yes')).toBe('Yes'); + expect(t('common.no')).toBe('No'); + expect(t('welcome.banner')).toBe('Welcome to Autohand!'); + + await changeLanguage('es'); + expect(getCurrentLocale()).toBe('es'); + expect(t('common.yes')).toBe('Sí'); + expect(t('common.no')).toBe('No'); + expect(t('welcome.banner')).toBe('¡Bienvenido a Autohand!'); + }); + + it('should switch translations immediately when changing to Portuguese', async () => { + await initI18n('en'); + expect(t('common.success')).toBe('Success'); + + await changeLanguage('pt-br'); + expect(getCurrentLocale()).toBe('pt-br'); + expect(t('common.success')).toBe('Sucesso'); + expect(t('welcome.banner')).toBe('Bem-vindo ao Autohand!'); + }); + + it('should switch translations immediately when changing to French', async () => { + await initI18n('en'); + expect(t('commands.quit.goodbye')).toBe('Goodbye!'); + + await changeLanguage('fr'); + expect(getCurrentLocale()).toBe('fr'); + expect(t('commands.quit.goodbye')).toBe('Au revoir !'); + expect(t('welcome.banner')).toBe('Bienvenue sur Autohand !'); + }); + + it('should switch translations immediately when changing to Chinese', async () => { + await initI18n('en'); + expect(t('common.loading')).toBe('Loading...'); + + await changeLanguage('zh-cn'); + expect(getCurrentLocale()).toBe('zh-cn'); + expect(t('common.loading')).toBe('加载中...'); + expect(t('welcome.banner')).toBe('欢迎使用 Autohand!'); + }); + + it('should show language change message in the new language', async () => { + await initI18n('en'); + expect(t('commands.language.changed', { language: 'Spanish' })).toBe('Language changed to Spanish'); + + await changeLanguage('es'); + expect(t('commands.language.changed', { language: 'Español' })).toBe('Idioma cambiado a Español'); + }); + + it('should switch back to English from another language', async () => { + await initI18n('es'); + expect(t('common.yes')).toBe('Sí'); + + await changeLanguage('en'); + expect(getCurrentLocale()).toBe('en'); + expect(t('common.yes')).toBe('Yes'); + }); + + it('should switch between non-English languages', async () => { + await initI18n('es'); + expect(t('common.done')).toBe('Hecho'); + + await changeLanguage('fr'); + expect(t('common.done')).toBe('Terminé'); + + await changeLanguage('pt-br'); + expect(t('common.done')).toBe('Concluído'); + + await changeLanguage('zh-cn'); + expect(t('common.done')).toBe('完成'); + }); + }); });