/** * Lightweight reactive i18n — no external deps. * * Locale priority: localStorage > VITE_LOCALE > navigator.language > 'en' * Changing locale at runtime triggers subscribed React components to re-render. */ const STORAGE_KEY = 'pinchchat-locale'; const en = { // Login screen 'login.title': 'PinchChat', 'login.subtitle': 'Connect to your OpenClaw gateway', 'login.gatewayUrl': 'Gateway URL', 'login.token': 'Token', 'login.tokenPlaceholder': 'Enter your gateway token', 'login.authToken': 'Token', 'login.authPassword': 'Password', 'login.password': 'Password', 'login.passwordPlaceholder': 'Enter your gateway password', 'login.connect': 'Connect', 'login.connecting': 'Connecting…', 'login.showToken': 'Show token', 'login.hideToken': 'Hide token', 'login.storedLocally': 'Credentials are stored locally in your browser', 'login.wsHint': 'URL must start with ws:// or wss://', 'login.advanced': 'Advanced', 'login.clientId': 'Client ID', 'login.clientIdHint': 'Sent in the WebSocket connect frame. Default: webchat', // Header 'header.title': 'PinchChat', 'header.connected': 'Connected', 'header.disconnected': 'Disconnected', 'header.logout': 'Logout', 'header.toggleSidebar': 'Toggle sidebar', 'header.changeLanguage': 'Change language', 'header.soundOn': 'Enable notification sound', 'header.soundOff': 'Disable notification sound', // Chat 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Send a message to get started', 'chat.suggestions': 'Try asking...', 'chat.suggestion1': 'Summarize my recent emails', 'chat.suggestion2': "What's on my calendar today?", 'chat.suggestion3': 'Search the web for latest news', 'chat.suggestion4': 'Help me write a script', 'chat.loadingHistory': 'Loading messages…', 'chat.inputPlaceholder': 'Type a message…', 'chat.inputLabel': 'Message', 'chat.attachFile': 'Attach file', 'chat.send': 'Send', 'chat.stop': 'Stop', 'chat.showPreview': 'Preview markdown', 'chat.hidePreview': 'Hide preview', 'chat.scrollToBottom': 'New messages', 'chat.scrollDown': 'Scroll to bottom', 'chat.collapseTools': 'Collapse all tools', 'chat.expandTools': 'Expand all tools', 'chat.messages': 'Chat messages', 'chat.thinking': 'Thinking…', // Sidebar 'sidebar.title': 'Sessions', 'sidebar.empty': 'No sessions', 'sidebar.search': 'Search sessions…', 'sidebar.noResults': 'No matching sessions', 'sidebar.pin': 'Pin session', 'sidebar.unpin': 'Unpin session', 'sidebar.pinned': 'Pinned', 'sidebar.delete': 'Delete session', 'sidebar.rename': 'Rename session', 'sidebar.deleteConfirm': 'Delete this session? This cannot be undone.', 'sidebar.deleteCancel': 'Cancel', 'sidebar.openSplit': 'Open in split view', 'sidebar.close': 'Close sidebar', 'sidebar.clearSearch': 'Clear search', 'sidebar.filterAll': 'All', 'sidebar.filterActive': 'Active', 'split.close': 'Close split view', 'app.mainChat': 'Main chat', 'app.splitPane': 'Split pane', 'app.skipToChat': 'Skip to chat input', // Thinking 'thinking.label': 'Thinking', 'thinking.reasoning': 'Reasoning…', // Tool call 'tool.parameters': 'Parameters', 'tool.result': 'Result', // Connection banner 'connection.reconnecting': 'Connection lost — reconnecting…', 'connection.reconnected': 'Reconnected!', 'connection.pairing': 'Device pairing pending — run `openclaw devices approve` on your gateway', // Message actions 'message.copy': 'Copy message', 'message.copied': 'Copied!', 'message.retry': 'Resend message', 'message.showMore': 'Show more', 'message.showLess': 'Show less', 'message.metadata': 'Message details', 'message.rawJson': 'Raw JSON', 'message.hideRawJson': 'Hide raw JSON', // Timestamps 'time.yesterday': 'Yesterday', 'time.today': 'Today', // Keyboard shortcuts 'shortcuts.title': 'Keyboard Shortcuts', 'shortcuts.send': 'Send message', 'shortcuts.newline': 'New line', 'shortcuts.search': 'Search sessions', 'shortcuts.switchSession': 'Previous / next session', 'shortcuts.closeSidebar': 'Close sidebar / search', 'shortcuts.stop': 'Stop generation', 'shortcuts.help': 'Show shortcuts', 'shortcuts.close': 'Close', 'shortcuts.chatSection': 'Chat', // Error boundary 'error.title': 'Something went wrong', 'error.description': 'An unexpected error occurred while rendering the interface. You can try again or reload the page.', 'error.retry': 'Try again', 'error.reload': 'Reload page', 'shortcuts.navigationSection': 'Navigation', 'shortcuts.generalSection': 'General', // Export 'header.export': 'Export conversation as Markdown', 'header.compact': 'Compact', 'header.compacting': 'Compacting…', 'header.sessionInfo': 'Session Info', 'sessionInfo.sessionKey': 'Session Key', 'sessionInfo.channel': 'Channel', 'sessionInfo.kind': 'Kind', 'sessionInfo.model': 'Model', 'sessionInfo.agent': 'Agent', 'sessionInfo.messages': 'Messages', 'sessionInfo.totalTokens': 'Total Tokens', 'sessionInfo.inputTokens': 'Input', 'sessionInfo.outputTokens': 'Output', 'sessionInfo.contextWindow': 'Context', 'sessionInfo.lastActive': 'Last Active', // Theme 'theme.title': 'Theme', 'theme.mode': 'Mode', 'theme.accent': 'Accent', 'theme.system': 'System', 'theme.dark': 'Dark', 'theme.light': 'Light', 'theme.oled': 'OLED', // Message search 'search.placeholder': 'Search messages…', 'search.noResults': '0 results', 'search.prev': 'Previous match', 'search.next': 'Next match', 'shortcuts.searchMessages': 'Search messages', // Send shortcut setting 'settings.title': 'Settings', 'settings.appearance': 'Appearance', 'settings.typography': 'Typography', 'settings.chat': 'Chat', 'settings.notifications': 'Notifications', 'settings.notificationSound': 'Notification sound', 'settings.language': 'Language', 'settings.sendShortcut': 'Send with', 'settings.sendEnter': 'Enter', 'settings.sendCtrlEnter': 'Ctrl+Enter', 'settings.tab.appearance': 'Appearance', 'settings.tab.typography': 'Typography', 'settings.tab.chat': 'Chat', 'settings.tab.notifications': 'Notifications', 'settings.fontUi': 'UI font', 'settings.fontMono': 'Code font', 'settings.fontSize': 'UI font size', 'settings.fontMonoSize': 'Code font size', 'settings.preview': 'Preview', 'settings.previewText': 'The quick brown fox jumps over the lazy dog — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira load automatically; Cascadia and SF use your system if installed.', // Bookmarks 'message.bookmark': 'Bookmark message', 'message.removeBookmark': 'Remove bookmark', 'message.reply': 'Reply', 'chat.replyingTo': 'Replying to', 'chat.bookmarks': 'Bookmarks', 'chat.export': 'Export conversation', 'chat.contextCompacted': 'Context compacted — older messages cached locally', 'slash.commands': 'Commands', 'slash.status': 'Show session status & usage', 'slash.reasoning': 'Toggle reasoning mode', 'slash.verbose': 'Toggle verbose output', 'slash.model': 'Switch model for this session', 'slash.compact': 'Compact conversation context', 'slash.reset': 'Reset the session', 'slash.new': 'Create a new chat session', 'slash.help': 'Show available commands', } as const; const fr: Record = { 'login.title': 'PinchChat', 'login.subtitle': 'Connectez-vous à votre gateway OpenClaw', 'login.gatewayUrl': 'URL de la gateway', 'login.token': 'Token', 'login.tokenPlaceholder': 'Entrez votre token gateway', 'login.authToken': 'Token', 'login.authPassword': 'Mot de passe', 'login.password': 'Mot de passe', 'login.passwordPlaceholder': 'Entrez votre mot de passe gateway', 'login.connect': 'Connexion', 'login.connecting': 'Connexion…', 'login.showToken': 'Afficher le token', 'login.hideToken': 'Masquer le token', 'login.storedLocally': 'Les identifiants sont stockés localement dans votre navigateur', 'login.wsHint': 'L\'URL doit commencer par ws:// ou wss://', 'login.advanced': 'Avancé', 'login.clientId': 'ID client', 'login.clientIdHint': 'Envoyé dans la trame de connexion WebSocket. Par défaut : webchat', 'header.title': 'PinchChat', 'header.connected': 'Connecté', 'header.disconnected': 'Déconnecté', 'header.logout': 'Déconnexion', 'header.toggleSidebar': 'Afficher/masquer la barre latérale', 'header.changeLanguage': 'Changer de langue', 'header.soundOn': 'Activer le son de notification', 'header.soundOff': 'Désactiver le son de notification', 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Envoyez un message pour commencer', 'chat.suggestions': 'Essayez par exemple...', 'chat.suggestion1': 'Résume mes derniers emails', 'chat.suggestion2': "Qu'est-ce que j'ai au calendrier aujourd'hui ?", 'chat.suggestion3': 'Cherche sur le web les dernières actus', 'chat.suggestion4': "Aide-moi à écrire un script", 'chat.loadingHistory': 'Chargement des messages…', 'chat.inputPlaceholder': 'Tapez un message…', 'chat.inputLabel': 'Message', 'chat.attachFile': 'Joindre un fichier', 'chat.send': 'Envoyer', 'chat.stop': 'Arrêter', 'chat.showPreview': 'Aperçu markdown', 'chat.hidePreview': 'Masquer l\'aperçu', 'chat.scrollToBottom': 'Nouveaux messages', 'chat.scrollDown': 'Défiler en bas', 'chat.collapseTools': 'Replier tous les outils', 'chat.expandTools': 'Déplier tous les outils', 'chat.messages': 'Messages du chat', 'chat.thinking': 'Réflexion…', 'sidebar.title': 'Sessions', 'sidebar.empty': 'Aucune session', 'sidebar.search': 'Rechercher…', 'sidebar.noResults': 'Aucun résultat', 'sidebar.pin': 'Épingler la session', 'sidebar.unpin': 'Désépingler la session', 'sidebar.pinned': 'Épinglées', 'sidebar.delete': 'Supprimer la session', 'sidebar.rename': 'Renommer la session', 'sidebar.deleteConfirm': 'Supprimer cette session ? Cette action est irréversible.', 'sidebar.deleteCancel': 'Annuler', 'sidebar.openSplit': 'Ouvrir en vue scindée', 'sidebar.close': 'Fermer la barre latérale', 'sidebar.clearSearch': 'Effacer la recherche', 'sidebar.filterAll': 'Tout', 'sidebar.filterActive': 'Actives', 'split.close': 'Fermer la vue scindée', 'app.mainChat': 'Chat principal', 'app.splitPane': 'Volet scindé', 'app.skipToChat': 'Aller au champ de saisie', 'thinking.label': 'Réflexion', 'thinking.reasoning': 'Réflexion…', 'tool.parameters': 'Paramètres', 'tool.result': 'Résultat', 'connection.reconnecting': 'Connexion perdue — reconnexion…', 'connection.reconnected': 'Reconnecté !', 'connection.pairing': 'Appairage en attente — exécutez `openclaw devices approve` sur votre gateway', 'message.copy': 'Copier le message', 'message.copied': 'Copié !', 'message.retry': 'Renvoyer le message', 'message.showMore': 'Afficher plus', 'message.showLess': 'Afficher moins', 'message.metadata': 'Détails du message', 'message.rawJson': 'JSON brut', 'message.hideRawJson': 'Masquer le JSON brut', 'time.yesterday': 'Hier', 'time.today': "Aujourd'hui", 'shortcuts.title': 'Raccourcis clavier', 'shortcuts.send': 'Envoyer le message', 'shortcuts.newline': 'Nouvelle ligne', 'shortcuts.search': 'Rechercher des sessions', 'shortcuts.switchSession': 'Session précédente / suivante', 'shortcuts.closeSidebar': 'Fermer la barre / recherche', 'shortcuts.stop': 'Arrêter la génération', 'shortcuts.help': 'Afficher les raccourcis', 'shortcuts.close': 'Fermer', 'shortcuts.chatSection': 'Chat', 'error.title': 'Quelque chose s\'est mal passé', 'error.description': 'Une erreur inattendue est survenue lors de l\'affichage. Vous pouvez réessayer ou recharger la page.', 'error.retry': 'Réessayer', 'error.reload': 'Recharger', 'shortcuts.navigationSection': 'Navigation', 'shortcuts.generalSection': 'Général', 'header.export': 'Exporter la conversation en Markdown', 'header.compact': 'Compacter', 'header.compacting': 'Compaction…', 'header.sessionInfo': 'Infos session', 'sessionInfo.sessionKey': 'Clé session', 'sessionInfo.channel': 'Canal', 'sessionInfo.kind': 'Type', 'sessionInfo.model': 'Modèle', 'sessionInfo.agent': 'Agent', 'sessionInfo.messages': 'Messages', 'sessionInfo.totalTokens': 'Tokens total', 'sessionInfo.inputTokens': 'Entrée', 'sessionInfo.outputTokens': 'Sortie', 'sessionInfo.contextWindow': 'Contexte', 'sessionInfo.lastActive': 'Dernière activité', 'theme.title': 'Thème', 'theme.mode': 'Mode', 'theme.accent': 'Accent', 'theme.system': 'Système', 'theme.dark': 'Sombre', 'theme.light': 'Clair', 'theme.oled': 'OLED', 'search.placeholder': 'Rechercher dans les messages…', 'search.noResults': '0 résultat', 'search.prev': 'Résultat précédent', 'search.next': 'Résultat suivant', 'shortcuts.searchMessages': 'Rechercher dans les messages', 'settings.title': 'Paramètres', 'settings.appearance': 'Apparence', 'settings.typography': 'Typographie', 'settings.chat': 'Chat', 'settings.notifications': 'Notifications', 'settings.notificationSound': 'Son de notification', 'settings.language': 'Langue', 'settings.sendShortcut': 'Envoyer avec', 'settings.sendEnter': 'Entrée', 'settings.sendCtrlEnter': 'Ctrl+Entrée', 'settings.tab.appearance': 'Apparence', 'settings.tab.typography': 'Typographie', 'settings.tab.chat': 'Chat', 'settings.tab.notifications': 'Notifications', 'settings.fontUi': 'Police UI', 'settings.fontMono': 'Police code', 'settings.fontSize': 'Taille police UI', 'settings.fontMonoSize': 'Taille police code', 'settings.preview': 'Aperçu', 'settings.previewText': 'Le vif renard brun saute par-dessus le chien paresseux — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira se chargent automatiquement ; Cascadia et SF dépendent des polices installées.', 'message.bookmark': 'Marquer le message', 'message.removeBookmark': 'Retirer le marque-page', 'message.reply': 'Répondre', 'chat.replyingTo': 'En réponse à', 'chat.bookmarks': 'Marque-pages', 'chat.export': 'Exporter la conversation', 'chat.contextCompacted': 'Contexte compacté — anciens messages en cache local', 'slash.commands': 'Commandes', 'slash.status': 'Afficher le statut et l\'utilisation', 'slash.reasoning': 'Activer/désactiver le raisonnement', 'slash.verbose': 'Activer/désactiver le mode verbeux', 'slash.model': 'Changer de modèle pour cette session', 'slash.compact': 'Compacter le contexte', 'slash.reset': 'Réinitialiser la session', 'slash.new': 'Créer une nouvelle session de chat', 'slash.help': 'Afficher les commandes disponibles', }; const es: Record = { 'login.title': 'PinchChat', 'login.subtitle': 'Conéctate a tu gateway OpenClaw', 'login.gatewayUrl': 'URL del gateway', 'login.token': 'Token', 'login.tokenPlaceholder': 'Introduce tu token de gateway', 'login.authToken': 'Token', 'login.authPassword': 'Contraseña', 'login.password': 'Contraseña', 'login.passwordPlaceholder': 'Introduce tu contraseña de gateway', 'login.connect': 'Conectar', 'login.connecting': 'Conectando…', 'login.showToken': 'Mostrar token', 'login.hideToken': 'Ocultar token', 'login.storedLocally': 'Las credenciales se guardan localmente en tu navegador', 'login.wsHint': 'La URL debe empezar con ws:// o wss://', 'login.advanced': 'Avanzado', 'login.clientId': 'ID de cliente', 'login.clientIdHint': 'Enviado en la trama de conexión WebSocket. Por defecto: webchat', 'header.title': 'PinchChat', 'header.connected': 'Conectado', 'header.disconnected': 'Desconectado', 'header.logout': 'Cerrar sesión', 'header.toggleSidebar': 'Mostrar/ocultar barra lateral', 'header.changeLanguage': 'Cambiar idioma', 'header.soundOn': 'Activar sonido de notificación', 'header.soundOff': 'Desactivar sonido de notificación', 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Envía un mensaje para comenzar', 'chat.suggestions': 'Prueba a preguntar...', 'chat.suggestion1': 'Resume mis últimos correos', 'chat.suggestion2': '¿Qué tengo en el calendario hoy?', 'chat.suggestion3': 'Busca en la web las últimas noticias', 'chat.suggestion4': 'Ayúdame a escribir un script', 'chat.loadingHistory': 'Cargando mensajes…', 'chat.inputPlaceholder': 'Escribe un mensaje…', 'chat.inputLabel': 'Mensaje', 'chat.attachFile': 'Adjuntar archivo', 'chat.send': 'Enviar', 'chat.stop': 'Detener', 'chat.showPreview': 'Vista previa markdown', 'chat.hidePreview': 'Ocultar vista previa', 'chat.scrollToBottom': 'Nuevos mensajes', 'chat.scrollDown': 'Ir al final', 'chat.collapseTools': 'Contraer todas las herramientas', 'chat.expandTools': 'Expandir todas las herramientas', 'chat.messages': 'Mensajes del chat', 'chat.thinking': 'Pensando…', 'sidebar.title': 'Sesiones', 'sidebar.empty': 'Sin sesiones', 'sidebar.search': 'Buscar sesiones…', 'sidebar.noResults': 'Sin resultados', 'sidebar.pin': 'Fijar sesión', 'sidebar.unpin': 'Desfijar sesión', 'sidebar.pinned': 'Fijadas', 'sidebar.delete': 'Eliminar sesión', 'sidebar.rename': 'Renombrar sesión', 'sidebar.deleteConfirm': '¿Eliminar esta sesión? Esta acción no se puede deshacer.', 'sidebar.deleteCancel': 'Cancelar', 'sidebar.openSplit': 'Abrir en vista dividida', 'sidebar.close': 'Cerrar barra lateral', 'sidebar.clearSearch': 'Limpiar búsqueda', 'sidebar.filterAll': 'Todas', 'sidebar.filterActive': 'Activas', 'split.close': 'Cerrar vista dividida', 'app.mainChat': 'Chat principal', 'app.splitPane': 'Panel dividido', 'app.skipToChat': 'Ir al campo de entrada', 'thinking.label': 'Pensamiento', 'thinking.reasoning': 'Razonando…', 'tool.parameters': 'Parámetros', 'tool.result': 'Resultado', 'connection.reconnecting': 'Conexión perdida — reconectando…', 'connection.reconnected': '¡Reconectado!', 'connection.pairing': 'Emparejamiento pendiente — ejecute `openclaw devices approve` en su gateway', 'message.copy': 'Copiar mensaje', 'message.copied': '¡Copiado!', 'message.retry': 'Reenviar mensaje', 'message.showMore': 'Ver más', 'message.showLess': 'Ver menos', 'message.metadata': 'Detalles del mensaje', 'message.rawJson': 'JSON sin formato', 'message.hideRawJson': 'Ocultar JSON sin formato', 'time.yesterday': 'Ayer', 'time.today': 'Hoy', 'shortcuts.title': 'Atajos de teclado', 'shortcuts.send': 'Enviar mensaje', 'shortcuts.newline': 'Nueva línea', 'shortcuts.search': 'Buscar sesiones', 'shortcuts.switchSession': 'Sesión anterior / siguiente', 'shortcuts.closeSidebar': 'Cerrar barra / búsqueda', 'shortcuts.stop': 'Detener generación', 'shortcuts.help': 'Mostrar atajos', 'shortcuts.close': 'Cerrar', 'shortcuts.chatSection': 'Chat', 'error.title': 'Algo salió mal', 'error.description': 'Ocurrió un error inesperado al mostrar la interfaz. Puedes intentar de nuevo o recargar la página.', 'error.retry': 'Reintentar', 'error.reload': 'Recargar página', 'shortcuts.navigationSection': 'Navegación', 'shortcuts.generalSection': 'General', 'header.export': 'Exportar conversación como Markdown', 'header.compact': 'Compactar', 'header.compacting': 'Compactando…', 'header.sessionInfo': 'Info de sesión', 'sessionInfo.sessionKey': 'Clave de sesión', 'sessionInfo.channel': 'Canal', 'sessionInfo.kind': 'Tipo', 'sessionInfo.model': 'Modelo', 'sessionInfo.agent': 'Agente', 'sessionInfo.messages': 'Mensajes', 'sessionInfo.totalTokens': 'Tokens totales', 'sessionInfo.inputTokens': 'Entrada', 'sessionInfo.outputTokens': 'Salida', 'sessionInfo.contextWindow': 'Contexto', 'sessionInfo.lastActive': 'Última actividad', 'theme.title': 'Tema', 'theme.mode': 'Modo', 'theme.accent': 'Acento', 'theme.system': 'Sistema', 'theme.dark': 'Oscuro', 'theme.light': 'Claro', 'theme.oled': 'OLED', 'search.placeholder': 'Buscar en mensajes…', 'search.noResults': '0 resultados', 'search.prev': 'Resultado anterior', 'search.next': 'Resultado siguiente', 'shortcuts.searchMessages': 'Buscar en mensajes', 'settings.title': 'Ajustes', 'settings.appearance': 'Apariencia', 'settings.typography': 'Tipografía', 'settings.chat': 'Chat', 'settings.notifications': 'Notificaciones', 'settings.notificationSound': 'Sonido de notificación', 'settings.language': 'Idioma', 'settings.sendShortcut': 'Enviar con', 'settings.sendEnter': 'Enter', 'settings.sendCtrlEnter': 'Ctrl+Enter', 'settings.tab.appearance': 'Apariencia', 'settings.tab.typography': 'Tipografía', 'settings.tab.chat': 'Chat', 'settings.tab.notifications': 'Notificaciones', 'settings.fontUi': 'Fuente UI', 'settings.fontMono': 'Fuente de código', 'settings.fontSize': 'Tamaño fuente UI', 'settings.fontMonoSize': 'Tamaño fuente código', 'settings.preview': 'Vista previa', 'settings.previewText': 'El veloz zorro marrón salta sobre el perro perezoso — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira se cargan automáticamente; Cascadia y SF dependen de que estén instaladas.', 'message.bookmark': 'Marcar mensaje', 'message.removeBookmark': 'Quitar marcador', 'message.reply': 'Responder', 'chat.replyingTo': 'Respondiendo a', 'chat.bookmarks': 'Marcadores', 'chat.export': 'Exportar conversación', 'chat.contextCompacted': 'Contexto compactado — mensajes anteriores en caché local', 'slash.commands': 'Comandos', 'slash.status': 'Mostrar estado y uso de la sesión', 'slash.reasoning': 'Activar/desactivar razonamiento', 'slash.verbose': 'Activar/desactivar modo detallado', 'slash.model': 'Cambiar modelo para esta sesión', 'slash.compact': 'Compactar contexto de conversación', 'slash.reset': 'Reiniciar la sesión', 'slash.new': 'Crear una nueva sesión de chat', 'slash.help': 'Mostrar comandos disponibles', }; export type TranslationKey = keyof typeof en; const de: Record = { 'login.title': 'PinchChat', 'login.subtitle': 'Mit deinem OpenClaw-Gateway verbinden', 'login.gatewayUrl': 'Gateway-URL', 'login.token': 'Token', 'login.tokenPlaceholder': 'Gateway-Token eingeben', 'login.authToken': 'Token', 'login.authPassword': 'Passwort', 'login.password': 'Passwort', 'login.passwordPlaceholder': 'Gateway-Passwort eingeben', 'login.connect': 'Verbinden', 'login.connecting': 'Verbinde…', 'login.showToken': 'Token anzeigen', 'login.hideToken': 'Token verbergen', 'login.storedLocally': 'Zugangsdaten werden lokal in deinem Browser gespeichert', 'login.wsHint': 'URL muss mit ws:// oder wss:// beginnen', 'login.advanced': 'Erweitert', 'login.clientId': 'Client-ID', 'login.clientIdHint': 'Wird im WebSocket-Connect-Frame gesendet. Standard: webchat', 'header.title': 'PinchChat', 'header.connected': 'Verbunden', 'header.disconnected': 'Getrennt', 'header.logout': 'Abmelden', 'header.toggleSidebar': 'Seitenleiste ein-/ausblenden', 'header.changeLanguage': 'Sprache ändern', 'header.soundOn': 'Benachrichtigungston aktivieren', 'header.soundOff': 'Benachrichtigungston deaktivieren', 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Sende eine Nachricht, um zu beginnen', 'chat.suggestions': 'Probiere z.\u00a0B. …', 'chat.suggestion1': 'Fasse meine letzten E-Mails zusammen', 'chat.suggestion2': 'Was steht heute in meinem Kalender?', 'chat.suggestion3': 'Suche im Web nach aktuellen Nachrichten', 'chat.suggestion4': 'Hilf mir, ein Skript zu schreiben', 'chat.loadingHistory': 'Nachrichten werden geladen…', 'chat.inputPlaceholder': 'Nachricht eingeben…', 'chat.inputLabel': 'Nachricht', 'chat.attachFile': 'Datei anhängen', 'chat.send': 'Senden', 'chat.stop': 'Stoppen', 'chat.showPreview': 'Markdown-Vorschau', 'chat.hidePreview': 'Vorschau ausblenden', 'chat.scrollToBottom': 'Neue Nachrichten', 'chat.scrollDown': 'Nach unten scrollen', 'chat.collapseTools': 'Alle Tools einklappen', 'chat.expandTools': 'Alle Tools ausklappen', 'chat.messages': 'Chat-Nachrichten', 'chat.thinking': 'Denkt nach…', 'sidebar.title': 'Sitzungen', 'sidebar.empty': 'Keine Sitzungen', 'sidebar.search': 'Sitzungen suchen…', 'sidebar.noResults': 'Keine Treffer', 'sidebar.pin': 'Sitzung anheften', 'sidebar.unpin': 'Sitzung lösen', 'sidebar.pinned': 'Angeheftet', 'sidebar.delete': 'Sitzung löschen', 'sidebar.rename': 'Sitzung umbenennen', 'sidebar.deleteConfirm': 'Diese Sitzung löschen? Dies kann nicht rückgängig gemacht werden.', 'sidebar.deleteCancel': 'Abbrechen', 'sidebar.openSplit': 'In geteilter Ansicht öffnen', 'sidebar.close': 'Seitenleiste schließen', 'sidebar.clearSearch': 'Suche leeren', 'sidebar.filterAll': 'Alle', 'sidebar.filterActive': 'Aktive', 'split.close': 'Geteilte Ansicht schließen', 'app.mainChat': 'Haupt-Chat', 'app.splitPane': 'Geteiltes Fenster', 'app.skipToChat': 'Zum Eingabefeld springen', 'thinking.label': 'Nachdenken', 'thinking.reasoning': 'Denkt nach…', 'tool.parameters': 'Parameter', 'tool.result': 'Ergebnis', 'connection.reconnecting': 'Verbindung verloren — wird wiederhergestellt…', 'connection.reconnected': 'Wieder verbunden!', 'connection.pairing': 'Gerätekopplung ausstehend — führen Sie `openclaw devices approve` auf Ihrem Gateway aus', 'message.copy': 'Nachricht kopieren', 'message.copied': 'Kopiert!', 'message.retry': 'Nachricht erneut senden', 'message.showMore': 'Mehr anzeigen', 'message.showLess': 'Weniger anzeigen', 'message.metadata': 'Nachrichtendetails', 'message.rawJson': 'Roh-JSON', 'message.hideRawJson': 'Roh-JSON ausblenden', 'time.yesterday': 'Gestern', 'time.today': 'Heute', 'shortcuts.title': 'Tastenkürzel', 'shortcuts.send': 'Nachricht senden', 'shortcuts.newline': 'Neue Zeile', 'shortcuts.search': 'Sitzungen suchen', 'shortcuts.switchSession': 'Vorherige / nächste Sitzung', 'shortcuts.closeSidebar': 'Seitenleiste / Suche schließen', 'shortcuts.stop': 'Generierung stoppen', 'shortcuts.help': 'Tastenkürzel anzeigen', 'shortcuts.close': 'Schließen', 'shortcuts.chatSection': 'Chat', 'error.title': 'Etwas ist schiefgelaufen', 'error.description': 'Beim Rendern der Oberfläche ist ein unerwarteter Fehler aufgetreten. Du kannst es erneut versuchen oder die Seite neu laden.', 'error.retry': 'Erneut versuchen', 'error.reload': 'Seite neu laden', 'shortcuts.navigationSection': 'Navigation', 'shortcuts.generalSection': 'Allgemein', 'header.export': 'Unterhaltung als Markdown exportieren', 'header.compact': 'Kompaktieren', 'header.compacting': 'Kompaktiere…', 'header.sessionInfo': 'Sitzungsinfo', 'sessionInfo.sessionKey': 'Sitzungsschlüssel', 'sessionInfo.channel': 'Kanal', 'sessionInfo.kind': 'Typ', 'sessionInfo.model': 'Modell', 'sessionInfo.agent': 'Agent', 'sessionInfo.messages': 'Nachrichten', 'sessionInfo.totalTokens': 'Tokens gesamt', 'sessionInfo.inputTokens': 'Eingabe', 'sessionInfo.outputTokens': 'Ausgabe', 'sessionInfo.contextWindow': 'Kontext', 'sessionInfo.lastActive': 'Letzte Aktivität', 'theme.title': 'Design', 'theme.mode': 'Modus', 'theme.accent': 'Akzent', 'theme.system': 'System', 'theme.dark': 'Dunkel', 'theme.light': 'Hell', 'theme.oled': 'OLED', 'search.placeholder': 'In Nachrichten suchen…', 'search.noResults': '0 Ergebnisse', 'search.prev': 'Vorheriger Treffer', 'search.next': 'Nächster Treffer', 'shortcuts.searchMessages': 'In Nachrichten suchen', 'settings.title': 'Einstellungen', 'settings.appearance': 'Darstellung', 'settings.typography': 'Typografie', 'settings.chat': 'Chat', 'settings.notifications': 'Benachrichtigungen', 'settings.notificationSound': 'Benachrichtigungston', 'settings.language': 'Sprache', 'settings.sendShortcut': 'Senden mit', 'settings.sendEnter': 'Enter', 'settings.sendCtrlEnter': 'Strg+Enter', 'settings.tab.appearance': 'Darstellung', 'settings.tab.typography': 'Typografie', 'settings.tab.chat': 'Chat', 'settings.tab.notifications': 'Benachrichtigungen', 'settings.fontUi': 'UI-Schriftart', 'settings.fontMono': 'Code-Schriftart', 'settings.fontSize': 'UI-Schriftgröße', 'settings.fontMonoSize': 'Code-Schriftgröße', 'settings.preview': 'Vorschau', 'settings.previewText': 'Falsches Üben von Xylophonmusik quält jeden größeren Zwerg — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira werden automatisch geladen; Cascadia und SF erfordern Systeminstallation.', 'message.bookmark': 'Nachricht markieren', 'message.reply': 'Antworten', 'chat.replyingTo': 'Antwort auf', 'message.removeBookmark': 'Lesezeichen entfernen', 'chat.bookmarks': 'Lesezeichen', 'chat.export': 'Unterhaltung exportieren', 'chat.contextCompacted': 'Kontext kompaktiert — ältere Nachrichten lokal zwischengespeichert', 'slash.commands': 'Befehle', 'slash.status': 'Sitzungsstatus und Nutzung anzeigen', 'slash.reasoning': 'Reasoning-Modus umschalten', 'slash.verbose': 'Ausführliche Ausgabe umschalten', 'slash.model': 'Modell für diese Sitzung wechseln', 'slash.compact': 'Gesprächskontext kompaktieren', 'slash.reset': 'Sitzung zurücksetzen', 'slash.new': 'Neue Chat-Sitzung erstellen', 'slash.help': 'Verfügbare Befehle anzeigen', }; const ja: Record = { 'login.title': 'PinchChat', 'login.subtitle': 'OpenClawゲートウェイに接続', 'login.gatewayUrl': 'ゲートウェイURL', 'login.token': 'トークン', 'login.tokenPlaceholder': 'ゲートウェイトークンを入力', 'login.authToken': 'トークン', 'login.authPassword': 'パスワード', 'login.password': 'パスワード', 'login.passwordPlaceholder': 'ゲートウェイパスワードを入力', 'login.connect': '接続', 'login.connecting': '接続中…', 'login.showToken': 'トークンを表示', 'login.hideToken': 'トークンを非表示', 'login.storedLocally': '認証情報はブラウザにローカル保存されます', 'login.wsHint': 'URLはws://またはwss://で始まる必要があります', 'login.advanced': '詳細設定', 'login.clientId': 'クライアントID', 'login.clientIdHint': 'WebSocket接続フレームで送信されます。デフォルト: webchat', 'header.title': 'PinchChat', 'header.connected': '接続済み', 'header.disconnected': '切断', 'header.logout': 'ログアウト', 'header.toggleSidebar': 'サイドバーの表示切替', 'header.changeLanguage': '言語を変更', 'header.soundOn': '通知音を有効にする', 'header.soundOff': '通知音を無効にする', 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'メッセージを送信して始めましょう', 'chat.suggestions': '例えば…', 'chat.suggestion1': '最近のメールをまとめて', 'chat.suggestion2': '今日のカレンダーは?', 'chat.suggestion3': '最新ニュースをウェブで検索して', 'chat.suggestion4': 'スクリプトを書くのを手伝って', 'chat.loadingHistory': 'メッセージを読み込み中…', 'chat.inputPlaceholder': 'メッセージを入力…', 'chat.inputLabel': 'メッセージ', 'chat.attachFile': 'ファイルを添付', 'chat.send': '送信', 'chat.stop': '停止', 'chat.showPreview': 'Markdownプレビュー', 'chat.hidePreview': 'プレビューを非表示', 'chat.scrollToBottom': '新しいメッセージ', 'chat.scrollDown': '一番下へスクロール', 'chat.collapseTools': 'すべてのツールを折りたたむ', 'chat.expandTools': 'すべてのツールを展開', 'chat.messages': 'チャットメッセージ', 'chat.thinking': '思考中…', 'sidebar.title': 'セッション', 'sidebar.empty': 'セッションなし', 'sidebar.search': 'セッションを検索…', 'sidebar.noResults': '結果なし', 'sidebar.pin': 'セッションをピン留め', 'sidebar.unpin': 'ピン留めを解除', 'sidebar.pinned': 'ピン留め', 'sidebar.delete': 'セッションを削除', 'sidebar.rename': 'セッション名変更', 'sidebar.deleteConfirm': 'このセッションを削除しますか?元に戻せません。', 'sidebar.deleteCancel': 'キャンセル', 'sidebar.openSplit': '分割ビューで開く', 'sidebar.close': 'サイドバーを閉じる', 'sidebar.clearSearch': '検索をクリア', 'sidebar.filterAll': 'すべて', 'sidebar.filterActive': 'アクティブ', 'split.close': '分割ビューを閉じる', 'app.mainChat': 'メインチャット', 'app.splitPane': '分割ペイン', 'app.skipToChat': '入力欄へ移動', 'thinking.label': '思考', 'thinking.reasoning': '推論中…', 'tool.parameters': 'パラメータ', 'tool.result': '結果', 'connection.reconnecting': '接続が切断されました — 再接続中…', 'connection.reconnected': '再接続しました!', 'connection.pairing': 'デバイスペアリング保留中 — ゲートウェイで `openclaw devices approve` を実行してください', 'message.copy': 'メッセージをコピー', 'message.copied': 'コピーしました!', 'message.retry': 'メッセージを再送信', 'message.showMore': 'もっと見る', 'message.showLess': '折りたたむ', 'message.metadata': 'メッセージの詳細', 'message.rawJson': '生JSON', 'message.hideRawJson': '生JSONを非表示', 'time.yesterday': '昨日', 'time.today': '今日', 'shortcuts.title': 'キーボードショートカット', 'shortcuts.send': 'メッセージを送信', 'shortcuts.newline': '改行', 'shortcuts.search': 'セッションを検索', 'shortcuts.switchSession': '前 / 次のセッション', 'shortcuts.closeSidebar': 'サイドバー / 検索を閉じる', 'shortcuts.stop': '生成を停止', 'shortcuts.help': 'ショートカットを表示', 'shortcuts.close': '閉じる', 'shortcuts.chatSection': 'チャット', 'error.title': 'エラーが発生しました', 'error.description': 'インターフェースの表示中に予期しないエラーが発生しました。再試行するかページをリロードしてください。', 'error.retry': '再試行', 'error.reload': 'ページをリロード', 'shortcuts.navigationSection': 'ナビゲーション', 'shortcuts.generalSection': '全般', 'header.export': '会話をMarkdownでエクスポート', 'header.compact': 'コンパクト化', 'header.compacting': 'コンパクト化中…', 'header.sessionInfo': 'セッション情報', 'sessionInfo.sessionKey': 'セッションキー', 'sessionInfo.channel': 'チャンネル', 'sessionInfo.kind': '種類', 'sessionInfo.model': 'モデル', 'sessionInfo.agent': 'エージェント', 'sessionInfo.messages': 'メッセージ', 'sessionInfo.totalTokens': '合計トークン', 'sessionInfo.inputTokens': '入力', 'sessionInfo.outputTokens': '出力', 'sessionInfo.contextWindow': 'コンテキスト', 'sessionInfo.lastActive': '最終アクティブ', 'theme.title': 'テーマ', 'theme.mode': 'モード', 'theme.accent': 'アクセント', 'theme.system': 'システム', 'theme.dark': 'ダーク', 'theme.light': 'ライト', 'theme.oled': 'OLED', 'search.placeholder': 'メッセージを検索…', 'search.noResults': '0件', 'search.prev': '前の結果', 'search.next': '次の結果', 'shortcuts.searchMessages': 'メッセージを検索', 'settings.title': '設定', 'settings.appearance': '外観', 'settings.typography': 'タイポグラフィ', 'settings.chat': 'チャット', 'settings.notifications': '通知', 'settings.notificationSound': '通知音', 'settings.language': '言語', 'settings.sendShortcut': '送信キー', 'settings.sendEnter': 'Enter', 'settings.sendCtrlEnter': 'Ctrl+Enter', 'settings.tab.appearance': '外観', 'settings.tab.typography': 'タイポグラフィ', 'settings.tab.chat': 'チャット', 'settings.tab.notifications': '通知', 'settings.fontUi': 'UIフォント', 'settings.fontMono': 'コードフォント', 'settings.fontSize': 'UIフォントサイズ', 'settings.fontMonoSize': 'コードフォントサイズ', 'settings.preview': 'プレビュー', 'settings.previewText': '素早い茶色の狐はのんびり犬を飛び越える — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira は自動読み込み。Cascadia と SF はシステムにある場合のみ使用。', 'message.bookmark': 'メッセージをブックマーク', 'message.reply': '返信', 'chat.replyingTo': '返信先', 'message.removeBookmark': 'ブックマークを削除', 'chat.bookmarks': 'ブックマーク', 'chat.export': '会話をエクスポート', 'chat.contextCompacted': 'コンテキストがコンパクト化されました — 古いメッセージはローカルキャッシュに保存', 'slash.commands': 'コマンド', 'slash.status': 'セッションのステータスと使用量を表示', 'slash.reasoning': '推論モードの切替', 'slash.verbose': '詳細出力の切替', 'slash.model': 'このセッションのモデルを変更', 'slash.compact': '会話コンテキストをコンパクト化', 'slash.reset': 'セッションをリセット', 'slash.new': '新しいチャットセッションを作成', 'slash.help': '利用可能なコマンドを表示', }; const pt: Record = { 'login.title': 'PinchChat', 'login.subtitle': 'Conectar ao seu gateway OpenClaw', 'login.gatewayUrl': 'URL do Gateway', 'login.token': 'Token', 'login.tokenPlaceholder': 'Insira o token do gateway', 'login.authToken': 'Token', 'login.authPassword': 'Senha', 'login.password': 'Senha', 'login.passwordPlaceholder': 'Insira a senha do gateway', 'login.connect': 'Conectar', 'login.connecting': 'Conectando…', 'login.showToken': 'Mostrar token', 'login.hideToken': 'Ocultar token', 'login.storedLocally': 'As credenciais são armazenadas localmente no navegador', 'login.wsHint': 'A URL deve começar com ws:// ou wss://', 'login.advanced': 'Avançado', 'login.clientId': 'ID do cliente', 'login.clientIdHint': 'Enviado no frame de conexão WebSocket. Padrão: webchat', 'header.title': 'PinchChat', 'header.connected': 'Conectado', 'header.disconnected': 'Desconectado', 'header.logout': 'Sair', 'header.toggleSidebar': 'Alternar barra lateral', 'header.changeLanguage': 'Alterar idioma', 'header.soundOn': 'Ativar som de notificação', 'header.soundOff': 'Desativar som de notificação', 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Envie uma mensagem para começar', 'chat.suggestions': 'Tente perguntar…', 'chat.suggestion1': 'Resuma meus e-mails recentes', 'chat.suggestion2': 'O que há na minha agenda hoje?', 'chat.suggestion3': 'Pesquise na web as últimas notícias', 'chat.suggestion4': 'Ajude-me a escrever um script', 'chat.loadingHistory': 'Carregando mensagens…', 'chat.inputPlaceholder': 'Digite uma mensagem…', 'chat.inputLabel': 'Mensagem', 'chat.attachFile': 'Anexar arquivo', 'chat.send': 'Enviar', 'chat.stop': 'Parar', 'chat.showPreview': 'Pré-visualizar markdown', 'chat.hidePreview': 'Ocultar pré-visualização', 'chat.scrollToBottom': 'Novas mensagens', 'chat.scrollDown': 'Rolar para baixo', 'chat.collapseTools': 'Recolher todas as ferramentas', 'chat.expandTools': 'Expandir todas as ferramentas', 'chat.messages': 'Mensagens do chat', 'chat.thinking': 'Pensando…', 'sidebar.title': 'Sessões', 'sidebar.empty': 'Nenhuma sessão', 'sidebar.search': 'Pesquisar sessões…', 'sidebar.noResults': 'Nenhuma sessão encontrada', 'sidebar.pin': 'Fixar sessão', 'sidebar.unpin': 'Desafixar sessão', 'sidebar.pinned': 'Fixada', 'sidebar.delete': 'Excluir sessão', 'sidebar.rename': 'Renomear sessão', 'sidebar.deleteConfirm': 'Excluir esta sessão? Esta ação não pode ser desfeita.', 'sidebar.deleteCancel': 'Cancelar', 'sidebar.openSplit': 'Abrir em visualização dividida', 'sidebar.close': 'Fechar barra lateral', 'sidebar.clearSearch': 'Limpar pesquisa', 'sidebar.filterAll': 'Todas', 'sidebar.filterActive': 'Ativas', 'split.close': 'Fechar visualização dividida', 'app.mainChat': 'Chat principal', 'app.splitPane': 'Painel dividido', 'app.skipToChat': 'Ir para o campo de entrada', 'thinking.label': 'Pensamento', 'thinking.reasoning': 'Raciocinando…', 'tool.parameters': 'Parâmetros', 'tool.result': 'Resultado', 'connection.reconnecting': 'Conexão perdida — reconectando…', 'connection.reconnected': 'Reconectado!', 'connection.pairing': 'Emparelhamento pendente — execute `openclaw devices approve` no seu gateway', 'message.copy': 'Copiar mensagem', 'message.copied': 'Copiado!', 'message.retry': 'Reenviar mensagem', 'message.showMore': 'Ver mais', 'message.showLess': 'Ver menos', 'message.metadata': 'Detalhes da mensagem', 'message.rawJson': 'JSON bruto', 'message.hideRawJson': 'Ocultar JSON bruto', 'time.yesterday': 'Ontem', 'time.today': 'Hoje', 'shortcuts.title': 'Atalhos de teclado', 'shortcuts.send': 'Enviar mensagem', 'shortcuts.newline': 'Nova linha', 'shortcuts.search': 'Pesquisar sessões', 'shortcuts.switchSession': 'Sessão anterior / próxima', 'shortcuts.closeSidebar': 'Fechar barra lateral / pesquisa', 'shortcuts.stop': 'Parar geração', 'shortcuts.help': 'Mostrar atalhos', 'shortcuts.close': 'Fechar', 'shortcuts.chatSection': 'Chat', 'error.title': 'Algo deu errado', 'error.description': 'Ocorreu um erro inesperado ao renderizar a interface. Tente novamente ou recarregue a página.', 'error.retry': 'Tentar novamente', 'error.reload': 'Recarregar página', 'shortcuts.navigationSection': 'Navegação', 'shortcuts.generalSection': 'Geral', 'header.export': 'Exportar conversa em Markdown', 'header.compact': 'Compactar', 'header.compacting': 'Compactando…', 'header.sessionInfo': 'Informações da sessão', 'sessionInfo.sessionKey': 'Chave da sessão', 'sessionInfo.channel': 'Canal', 'sessionInfo.kind': 'Tipo', 'sessionInfo.model': 'Modelo', 'sessionInfo.agent': 'Agente', 'sessionInfo.messages': 'Mensagens', 'sessionInfo.totalTokens': 'Total de tokens', 'sessionInfo.inputTokens': 'Entrada', 'sessionInfo.outputTokens': 'Saída', 'sessionInfo.contextWindow': 'Contexto', 'sessionInfo.lastActive': 'Última atividade', 'theme.title': 'Tema', 'theme.mode': 'Modo', 'theme.accent': 'Cor de destaque', 'theme.system': 'Sistema', 'theme.dark': 'Escuro', 'theme.light': 'Claro', 'theme.oled': 'OLED', 'search.placeholder': 'Pesquisar mensagens…', 'search.noResults': '0 resultados', 'search.prev': 'Resultado anterior', 'search.next': 'Próximo resultado', 'shortcuts.searchMessages': 'Pesquisar mensagens', 'settings.title': 'Configurações', 'settings.appearance': 'Aparência', 'settings.typography': 'Tipografia', 'settings.chat': 'Chat', 'settings.notifications': 'Notificações', 'settings.notificationSound': 'Som de notificação', 'settings.language': 'Idioma', 'settings.sendShortcut': 'Tecla de envio', 'settings.sendEnter': 'Enter', 'settings.sendCtrlEnter': 'Ctrl+Enter', 'settings.tab.appearance': 'Aparência', 'settings.tab.typography': 'Tipografia', 'settings.tab.chat': 'Chat', 'settings.tab.notifications': 'Notificações', 'settings.fontUi': 'Fonte da UI', 'settings.fontMono': 'Fonte de código', 'settings.fontSize': 'Tamanho da fonte UI', 'settings.fontMonoSize': 'Tamanho da fonte de código', 'settings.preview': 'Pré-visualização', 'settings.previewText': 'A rápida raposa marrom pula sobre o cão preguiçoso — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira carregam automaticamente; Cascadia e SF dependem de estarem instaladas.', 'message.bookmark': 'Marcar mensagem', 'message.reply': 'Responder', 'chat.replyingTo': 'Respondendo a', 'message.removeBookmark': 'Remover marcador', 'chat.bookmarks': 'Marcadores', 'chat.export': 'Exportar conversa', 'chat.contextCompacted': 'Contexto compactado — mensagens antigas salvas no cache local', 'slash.commands': 'Comandos', 'slash.status': 'Mostrar status e uso da sessão', 'slash.reasoning': 'Alternar modo de raciocínio', 'slash.verbose': 'Alternar saída detalhada', 'slash.model': 'Alterar modelo desta sessão', 'slash.compact': 'Compactar contexto da conversa', 'slash.reset': 'Redefinir sessão', 'slash.new': 'Criar uma nova sessão de chat', 'slash.help': 'Mostrar comandos disponíveis', }; const zh: Record = { 'login.title': 'PinchChat', 'login.subtitle': '连接到您的 OpenClaw 网关', 'login.gatewayUrl': '网关地址', 'login.token': '令牌', 'login.tokenPlaceholder': '输入网关令牌', 'login.authToken': '令牌', 'login.authPassword': '密码', 'login.password': '密码', 'login.passwordPlaceholder': '输入网关密码', 'login.connect': '连接', 'login.connecting': '连接中…', 'login.showToken': '显示令牌', 'login.hideToken': '隐藏令牌', 'login.storedLocally': '凭据仅存储在浏览器本地', 'login.wsHint': '地址必须以 ws:// 或 wss:// 开头', 'login.advanced': '高级', 'login.clientId': '客户端 ID', 'login.clientIdHint': '在 WebSocket 连接帧中发送。默认值:webchat', 'header.title': 'PinchChat', 'header.connected': '已连接', 'header.disconnected': '未连接', 'header.logout': '退出', 'header.toggleSidebar': '切换侧边栏', 'header.changeLanguage': '切换语言', 'header.soundOn': '开启通知音', 'header.soundOff': '关闭通知音', 'chat.welcome': 'PinchChat', 'chat.welcomeSub': '发送消息开始对话', 'chat.suggestions': '试试问…', 'chat.suggestion1': '总结我最近的邮件', 'chat.suggestion2': '今天的日程安排是什么?', 'chat.suggestion3': '搜索最新新闻', 'chat.suggestion4': '帮我写一个脚本', 'chat.loadingHistory': '加载消息中…', 'chat.inputPlaceholder': '输入消息…', 'chat.inputLabel': '消息', 'chat.attachFile': '添加附件', 'chat.send': '发送', 'chat.stop': '停止', 'chat.showPreview': '预览 Markdown', 'chat.hidePreview': '隐藏预览', 'chat.scrollToBottom': '新消息', 'chat.scrollDown': '滚动到底部', 'chat.collapseTools': '折叠所有工具', 'chat.expandTools': '展开所有工具', 'chat.messages': '聊天消息', 'chat.thinking': '思考中…', 'sidebar.title': '会话', 'sidebar.empty': '没有会话', 'sidebar.search': '搜索会话…', 'sidebar.noResults': '未找到匹配会话', 'sidebar.pin': '置顶会话', 'sidebar.unpin': '取消置顶', 'sidebar.pinned': '已置顶', 'sidebar.delete': '删除会话', 'sidebar.rename': '重命名会话', 'sidebar.deleteConfirm': '删除此会话?此操作无法撤消。', 'sidebar.deleteCancel': '取消', 'sidebar.openSplit': '在分屏中打开', 'sidebar.close': '关闭侧边栏', 'sidebar.clearSearch': '清除搜索', 'sidebar.filterAll': '全部', 'sidebar.filterActive': '活跃', 'split.close': '关闭分屏', 'app.mainChat': '主聊天', 'app.splitPane': '分屏面板', 'app.skipToChat': '跳转到输入框', 'thinking.label': '思考', 'thinking.reasoning': '推理中…', 'tool.parameters': '参数', 'tool.result': '结果', 'connection.reconnecting': '连接中断 — 正在重连…', 'connection.reconnected': '已重新连接!', 'connection.pairing': '设备配对待处理 — 在网关上运行 `openclaw devices approve`', 'message.copy': '复制消息', 'message.copied': '已复制!', 'message.retry': '重新发送', 'message.showMore': '展开更多', 'message.showLess': '收起', 'message.metadata': '消息详情', 'message.rawJson': '原始 JSON', 'message.hideRawJson': '隐藏原始 JSON', 'time.yesterday': '昨天', 'time.today': '今天', 'shortcuts.title': '键盘快捷键', 'shortcuts.send': '发送消息', 'shortcuts.newline': '换行', 'shortcuts.search': '搜索会话', 'shortcuts.switchSession': '上一个 / 下一个会话', 'shortcuts.closeSidebar': '关闭侧边栏 / 搜索', 'shortcuts.stop': '停止生成', 'shortcuts.help': '显示快捷键', 'shortcuts.close': '关闭', 'shortcuts.chatSection': '聊天', 'error.title': '出错了', 'error.description': '渲染界面时发生意外错误。您可以重试或刷新页面。', 'error.retry': '重试', 'error.reload': '刷新页面', 'shortcuts.navigationSection': '导航', 'shortcuts.generalSection': '通用', 'header.export': '导出会话为 Markdown', 'header.compact': '压缩', 'header.compacting': '压缩中…', 'header.sessionInfo': '会话信息', 'sessionInfo.sessionKey': '会话密钥', 'sessionInfo.channel': '频道', 'sessionInfo.kind': '类型', 'sessionInfo.model': '模型', 'sessionInfo.agent': '智能体', 'sessionInfo.messages': '消息数', 'sessionInfo.totalTokens': '总令牌数', 'sessionInfo.inputTokens': '输入', 'sessionInfo.outputTokens': '输出', 'sessionInfo.contextWindow': '上下文', 'sessionInfo.lastActive': '最后活跃', 'theme.title': '主题', 'theme.mode': '模式', 'theme.accent': '强调色', 'theme.system': '跟随系统', 'theme.dark': '深色', 'theme.light': '浅色', 'theme.oled': 'OLED', 'search.placeholder': '搜索消息…', 'search.noResults': '无结果', 'search.prev': '上一个', 'search.next': '下一个', 'shortcuts.searchMessages': '搜索消息', 'settings.title': '设置', 'settings.appearance': '外观', 'settings.typography': '排版', 'settings.chat': '聊天', 'settings.notifications': '通知', 'settings.notificationSound': '通知声音', 'settings.language': '语言', 'settings.sendShortcut': '发送方式', 'settings.sendEnter': 'Enter', 'settings.sendCtrlEnter': 'Ctrl+Enter', 'settings.tab.appearance': '外观', 'settings.tab.typography': '排版', 'settings.tab.chat': '聊天', 'settings.tab.notifications': '通知', 'settings.fontUi': '界面字体', 'settings.fontMono': '代码字体', 'settings.fontSize': '界面字号', 'settings.fontMonoSize': '代码字号', 'settings.preview': '预览', 'settings.previewText': '敏捷的棕狐跳过懒狗 — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira 自动加载;Cascadia 和 SF 依赖系统已安装字体。', 'message.bookmark': '收藏消息', 'message.reply': '回复', 'chat.replyingTo': '回复', 'message.removeBookmark': '取消收藏', 'chat.bookmarks': '收藏', 'chat.export': '导出会话', 'chat.contextCompacted': '上下文已压缩 — 旧消息已缓存到本地', 'slash.commands': '命令', 'slash.status': '显示会话状态和用量', 'slash.reasoning': '切换推理模式', 'slash.verbose': '切换详细输出', 'slash.model': '切换当前会话模型', 'slash.compact': '压缩会话上下文', 'slash.reset': '重置会话', 'slash.new': '创建新的聊天会话', 'slash.help': '显示可用命令', }; const it: Record = { 'login.title': 'PinchChat', 'login.subtitle': 'Connettiti al tuo gateway OpenClaw', 'login.gatewayUrl': 'URL del Gateway', 'login.token': 'Token', 'login.tokenPlaceholder': 'Inserisci il token del gateway', 'login.authToken': 'Token', 'login.authPassword': 'Password', 'login.password': 'Password', 'login.passwordPlaceholder': 'Inserisci la password del gateway', 'login.connect': 'Connetti', 'login.connecting': 'Connessione…', 'login.showToken': 'Mostra token', 'login.hideToken': 'Nascondi token', 'login.storedLocally': 'Le credenziali vengono salvate localmente nel browser', 'login.wsHint': 'L\'URL deve iniziare con ws:// o wss://', 'login.advanced': 'Avanzate', 'login.clientId': 'ID client', 'login.clientIdHint': 'Inviato nel frame di connessione WebSocket. Predefinito: webchat', 'header.title': 'PinchChat', 'header.connected': 'Connesso', 'header.disconnected': 'Disconnesso', 'header.logout': 'Esci', 'header.toggleSidebar': 'Mostra/nascondi barra laterale', 'header.changeLanguage': 'Cambia lingua', 'header.soundOn': 'Attiva suono notifiche', 'header.soundOff': 'Disattiva suono notifiche', 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Invia un messaggio per iniziare', 'chat.suggestions': 'Prova a chiedere…', 'chat.suggestion1': 'Riassumi le mie ultime email', 'chat.suggestion2': 'Cosa c\'è nel mio calendario oggi?', 'chat.suggestion3': 'Cerca sul web le ultime notizie', 'chat.suggestion4': 'Aiutami a scrivere uno script', 'chat.loadingHistory': 'Caricamento messaggi…', 'chat.inputPlaceholder': 'Scrivi un messaggio…', 'chat.inputLabel': 'Messaggio', 'chat.attachFile': 'Allega file', 'chat.send': 'Invia', 'chat.stop': 'Ferma', 'chat.showPreview': 'Anteprima markdown', 'chat.hidePreview': 'Nascondi anteprima', 'chat.scrollToBottom': 'Nuovi messaggi', 'chat.scrollDown': 'Scorri in fondo', 'chat.collapseTools': 'Comprimi tutti gli strumenti', 'chat.expandTools': 'Espandi tutti gli strumenti', 'chat.messages': 'Messaggi della chat', 'chat.thinking': 'Sto pensando…', 'sidebar.title': 'Sessioni', 'sidebar.empty': 'Nessuna sessione', 'sidebar.search': 'Cerca sessioni…', 'sidebar.noResults': 'Nessun risultato', 'sidebar.pin': 'Fissa sessione', 'sidebar.unpin': 'Sgancia sessione', 'sidebar.pinned': 'Fissate', 'sidebar.delete': 'Elimina sessione', 'sidebar.rename': 'Rinomina sessione', 'sidebar.deleteConfirm': 'Eliminare questa sessione? L\'azione è irreversibile.', 'sidebar.deleteCancel': 'Annulla', 'sidebar.openSplit': 'Apri in vista divisa', 'sidebar.close': 'Chiudi barra laterale', 'sidebar.clearSearch': 'Cancella ricerca', 'sidebar.filterAll': 'Tutte', 'sidebar.filterActive': 'Attive', 'split.close': 'Chiudi vista divisa', 'app.mainChat': 'Chat principale', 'app.splitPane': 'Pannello diviso', 'app.skipToChat': 'Vai al campo di input', 'thinking.label': 'Pensiero', 'thinking.reasoning': 'Ragionamento…', 'tool.parameters': 'Parametri', 'tool.result': 'Risultato', 'connection.reconnecting': 'Connessione persa — riconnessione…', 'connection.reconnected': 'Riconnesso!', 'connection.pairing': 'Accoppiamento dispositivo in attesa — esegui `openclaw devices approve` sul tuo gateway', 'message.copy': 'Copia messaggio', 'message.copied': 'Copiato!', 'message.retry': 'Reinvia messaggio', 'message.showMore': 'Mostra di più', 'message.showLess': 'Mostra meno', 'message.metadata': 'Dettagli messaggio', 'message.rawJson': 'JSON grezzo', 'message.hideRawJson': 'Nascondi JSON grezzo', 'time.yesterday': 'Ieri', 'time.today': 'Oggi', 'shortcuts.title': 'Scorciatoie da tastiera', 'shortcuts.send': 'Invia messaggio', 'shortcuts.newline': 'Nuova riga', 'shortcuts.search': 'Cerca sessioni', 'shortcuts.switchSession': 'Sessione precedente / successiva', 'shortcuts.closeSidebar': 'Chiudi barra laterale / ricerca', 'shortcuts.stop': 'Ferma generazione', 'shortcuts.help': 'Mostra scorciatoie', 'shortcuts.close': 'Chiudi', 'shortcuts.chatSection': 'Chat', 'error.title': 'Qualcosa è andato storto', 'error.description': 'Si è verificato un errore imprevisto durante il rendering dell\'interfaccia. Puoi riprovare o ricaricare la pagina.', 'error.retry': 'Riprova', 'error.reload': 'Ricarica pagina', 'shortcuts.navigationSection': 'Navigazione', 'shortcuts.generalSection': 'Generale', 'header.export': 'Esporta conversazione in Markdown', 'header.compact': 'Compatta', 'header.compacting': 'Compattamento…', 'header.sessionInfo': 'Info sessione', 'sessionInfo.sessionKey': 'Chiave sessione', 'sessionInfo.channel': 'Canale', 'sessionInfo.kind': 'Tipo', 'sessionInfo.model': 'Modello', 'sessionInfo.agent': 'Agente', 'sessionInfo.messages': 'Messaggi', 'sessionInfo.totalTokens': 'Token totali', 'sessionInfo.inputTokens': 'Input', 'sessionInfo.outputTokens': 'Output', 'sessionInfo.contextWindow': 'Contesto', 'sessionInfo.lastActive': 'Ultima attività', 'theme.title': 'Tema', 'theme.mode': 'Modalità', 'theme.accent': 'Accento', 'theme.system': 'Sistema', 'theme.dark': 'Scuro', 'theme.light': 'Chiaro', 'theme.oled': 'OLED', 'search.placeholder': 'Cerca nei messaggi…', 'search.noResults': '0 risultati', 'search.prev': 'Risultato precedente', 'search.next': 'Risultato successivo', 'shortcuts.searchMessages': 'Cerca nei messaggi', 'settings.title': 'Impostazioni', 'settings.appearance': 'Aspetto', 'settings.typography': 'Tipografia', 'settings.chat': 'Chat', 'settings.notifications': 'Notifiche', 'settings.notificationSound': 'Suono di notifica', 'settings.language': 'Lingua', 'settings.sendShortcut': 'Invia con', 'settings.sendEnter': 'Invio', 'settings.sendCtrlEnter': 'Ctrl+Invio', 'settings.tab.appearance': 'Aspetto', 'settings.tab.typography': 'Tipografia', 'settings.tab.chat': 'Chat', 'settings.tab.notifications': 'Notifiche', 'settings.fontUi': 'Font UI', 'settings.fontMono': 'Font codice', 'settings.fontSize': 'Dimensione font UI', 'settings.fontMonoSize': 'Dimensione font codice', 'settings.preview': 'Anteprima', 'settings.previewText': 'Quel vitello jazz fonde sciolto whiskey e cioccolato — 1234567890', 'settings.fontNote': 'Inter / JetBrains / Fira si caricano automaticamente; Cascadia e SF richiedono font di sistema.', 'message.bookmark': 'Aggiungi ai segnalibri', 'message.reply': 'Rispondi', 'chat.replyingTo': 'In risposta a', 'message.removeBookmark': 'Rimuovi segnalibro', 'chat.bookmarks': 'Segnalibri', 'chat.export': 'Esporta conversazione', 'chat.contextCompacted': 'Contesto compattato — messaggi precedenti nella cache locale', 'slash.commands': 'Comandi', 'slash.status': 'Mostra stato e utilizzo della sessione', 'slash.reasoning': 'Attiva/disattiva ragionamento', 'slash.verbose': 'Attiva/disattiva output dettagliato', 'slash.model': 'Cambia modello per questa sessione', 'slash.compact': 'Compatta il contesto della conversazione', 'slash.reset': 'Reimposta sessione', 'slash.new': 'Crea una nuova sessione chat', 'slash.help': 'Mostra comandi disponibili', }; const messages: Record> = { en, fr, es, de, ja, pt, zh, it }; export const supportedLocales = Object.keys(messages) as string[]; /** Labels shown in the language selector */ export const localeLabels: Record = { en: 'EN', fr: 'FR', es: 'ES', de: 'DE', ja: 'JA', pt: 'PT', zh: '中文', it: 'IT', }; function resolveInitialLocale(): string { // 1. localStorage try { const stored = localStorage.getItem(STORAGE_KEY); if (stored && messages[stored]) return stored; } catch { /* SSR or blocked storage */ } // 2. VITE_LOCALE env var const envLocale = (import.meta.env.VITE_LOCALE as string) || ''; if (envLocale && messages[envLocale]) return envLocale; // 3. navigator.language if (typeof navigator !== 'undefined') { const navLang = navigator.language?.split('-')[0]; if (navLang && messages[navLang]) return navLang; } // 4. fallback return 'en'; } let currentLocale = resolveInitialLocale(); let dict = messages[currentLocale] || messages.en; // Sync on initial load try { document.documentElement.lang = currentLocale; } catch { /* SSR */ } type Listener = () => void; const listeners = new Set(); /** Subscribe to locale changes. Returns unsubscribe function. */ export function onLocaleChange(fn: Listener): () => void { listeners.add(fn); return () => listeners.delete(fn); } /** Get the current locale code */ export function getLocale(): string { return currentLocale; } /** Switch locale at runtime. Persists to localStorage and notifies subscribers. */ export function setLocale(loc: string): void { if (!messages[loc] || loc === currentLocale) return; currentLocale = loc; dict = messages[loc]; try { localStorage.setItem(STORAGE_KEY, loc); } catch { /* noop */ } try { document.documentElement.lang = loc; } catch { /* SSR */ } listeners.forEach((fn) => fn()); } /** Return the translated string for the given key, falling back to English. */ export function t(key: TranslationKey): string { return dict[key] ?? (messages.en as Record)[key] ?? key; } // Keep backward-compat named export export { currentLocale as locale };