From 1e7518a207bfa8126b531372a92df03cc0cbf5ee Mon Sep 17 00:00:00 2001 From: bill-kopp-ai-dev Date: Mon, 13 Jul 2026 07:30:04 -0300 Subject: [PATCH] feat(webui): add Brazilian Portuguese (pt-BR) locale Translate the WebUI common.json to pt-BR, register the locale in config.ts (with normalizeLocale rules for "pt", "pt-BR", "pt-PT", ...) and index.ts resources, and add a Brazilian Portuguese overview assertion in i18n.test.tsx. Notes: - Generic "pt" and any "pt-*" variant resolve to pt-BR. A future pt-PT locale can be added without affecting existing users. - Universally borrowed technical terms (nanobot, BYOK, MCP, JSON, URL, Web, Apps when used as a product name, Skills) are kept in English, matching the pattern used by es/fr/ja/ko/vi/id. - Empty-thread greetings, quick-action prompts, and slash command descriptions are translated; quick-action prompts are intentionally phrased so the agent still receives a clear task spec. Verified with bun run test (536 passing), bun run lint (clean), and bun run build (7.18s). --- webui/src/i18n/config.ts | 4 + webui/src/i18n/index.ts | 2 + webui/src/i18n/locales/pt-BR/common.json | 1155 ++++++++++++++++++++++ webui/src/tests/i18n.test.tsx | 14 + 4 files changed, 1175 insertions(+) create mode 100644 webui/src/i18n/locales/pt-BR/common.json diff --git a/webui/src/i18n/config.ts b/webui/src/i18n/config.ts index 02a704a5..50385fdb 100644 --- a/webui/src/i18n/config.ts +++ b/webui/src/i18n/config.ts @@ -8,6 +8,7 @@ export const supportedLocales = [ { code: "ja", label: "Japanese", nativeLabel: "日本語" }, { code: "ko", label: "Korean", nativeLabel: "한국어" }, { code: "es", label: "Spanish", nativeLabel: "Español" }, + { code: "pt-BR", label: "Portuguese (Brazil)", nativeLabel: "Português (Brasil)" }, { code: "vi", label: "Vietnamese", nativeLabel: "Tiếng Việt" }, { code: "id", label: "Indonesian", nativeLabel: "Bahasa Indonesia" }, ] as const; @@ -39,6 +40,9 @@ export function normalizeLocale( ) { return "zh-TW"; } + if (lower === "pt" || lower.startsWith("pt-")) { + return "pt-BR"; + } const base = lower.split("-")[0]; const baseMatch = supportedLocales.find( diff --git a/webui/src/i18n/index.ts b/webui/src/i18n/index.ts index 64f713bb..2978189c 100644 --- a/webui/src/i18n/index.ts +++ b/webui/src/i18n/index.ts @@ -19,6 +19,7 @@ import frCommon from "./locales/fr/common.json"; import jaCommon from "./locales/ja/common.json"; import koCommon from "./locales/ko/common.json"; import esCommon from "./locales/es/common.json"; +import ptBRCommon from "./locales/pt-BR/common.json"; import viCommon from "./locales/vi/common.json"; import idCommon from "./locales/id/common.json"; @@ -30,6 +31,7 @@ export const resources = { ja: { common: jaCommon }, ko: { common: koCommon }, es: { common: esCommon }, + "pt-BR": { common: ptBRCommon }, vi: { common: viCommon }, id: { common: idCommon }, } as const; diff --git a/webui/src/i18n/locales/pt-BR/common.json b/webui/src/i18n/locales/pt-BR/common.json new file mode 100644 index 00000000..cfe23f35 --- /dev/null +++ b/webui/src/i18n/locales/pt-BR/common.json @@ -0,0 +1,1155 @@ +{ + "app": { + "brand": "nanobot", + "loading": { + "connecting": "Conectando-se ao nanobot…", + "boot": "Carregando nanobot…" + }, + "error": { + "title": "Não foi possível conectar-se com o nanobot", + "gatewayHint": "Verifique se o gateway está em execução (`nanobot gateway`) e se esta página está aberta na mesma máquina." + }, + "auth": { + "title": "Autenticação necessária", + "hint": "Informe o segredo configurado como tokenIssueSecret na configuração do gateway.", + "placeholder": "Senha", + "submit": "Conectar", + "invalid": "Senha inválida. Tente novamente." + }, + "account": { + "section": "Conta", + "logoutHint": "Desconecta este navegador do gateway.", + "logout": "Sair" + }, + "system": { + "section": "Sistema", + "restartHint": "Reinicie o nanobot para aplicar as alterações de runtime.", + "restart": "Reiniciar nanobot", + "restarting": "Reiniciando nanobot...", + "restartEngine": "Reiniciar motor", + "restartingEngine": "Reiniciando motor..." + }, + "restart": { + "completed": "Reinício concluído em {{seconds}}s." + }, + "documentTitle": { + "base": "nanobot", + "chat": "{{title}} · nanobot" + }, + "meta": { + "description": "Interface web do nanobot — converse com o seu workspace do nanobot." + } + }, + "sidebar": { + "navigation": "Navegação da barra lateral", + "collapse": "Recolher barra lateral", + "newChat": "Nova conversa", + "searchAria": "Buscar", + "searchPlaceholder": "Buscar", + "searchResults": "Resultados", + "noSearchResults": "Nenhuma conversa encontrada.", + "recent": "Recentes", + "settings": "Configurações", + "language": { + "label": "Idioma", + "ariaLabel": "Trocar idioma" + }, + "apps": "Apps", + "automations": "Automações", + "skills": { + "title": "Skills" + } + }, + "settings": { + "backToChat": "Voltar para a conversa", + "sidebar": { + "title": "Configurações", + "ariaLabel": "Seções de configurações" + }, + "nav": { + "general": "Geral", + "byok": "BYOK", + "overview": "Visão geral", + "appearance": "Aparência", + "models": "Modelos", + "providers": "Provedores", + "image": "Imagem", + "voice": "Voz", + "browser": "Web", + "channels": "Canais", + "cliApps": "Apps CLI", + "mcp": "MCP", + "runtime": "Sistema", + "advanced": "Segurança", + "apps": "Aplicativos", + "automations": "Automações", + "skills": "Skills" + }, + "sections": { + "interface": "Interface do usuário", + "ai": "IA", + "system": "Sistema", + "about": "Sobre", + "status": "Status", + "localPreferences": "Preferências locais", + "presets": "Predefinições", + "imageGeneration": "Geração de imagens", + "imageDefaults": "Padrões", + "webSearch": "Busca na web", + "webBehavior": "Comportamento", + "cliApps": "Apps CLI", + "mcp": "Servidores MCP", + "identity": "Identidade", + "webuiSafety": "Segurança da WebUI", + "capabilities": "Capacidades", + "apps": "Aplicativos", + "nativeHost": "Host nativo", + "hostSafety": "Segurança do app", + "voiceInput": "Entrada de voz" + }, + "models": { + "selectModel": "Selecionar modelo", + "addConfiguration": "Adicionar configuração", + "newConfiguration": "Nova configuração de modelo", + "newConfigurationHelp": "Salve um provedor e um modelo como uma opção de um clique.", + "configurationName": "Nome da configuração", + "configurationNameHelp": "Renomeie esta configuração de modelo.", + "configurationNamePlaceholder": "Escrita rápida", + "searchModels": "Buscar ou digitar ID do modelo", + "useCustomModel": "Usar", + "loadingModels": "Carregando modelos...", + "searchCatalog": "Pesquise o catálogo do provedor para escolher um modelo.", + "modelsAvailable": "disponíveis", + "noModelResults": "Nenhum modelo correspondente.", + "loadFailed": "Lista de modelos indisponível.", + "unsupportedModelList": "Digite o ID do modelo manualmente.", + "providerNotConfigured": "Configure este provedor antes de carregar modelos.", + "autoProviderCustomOnly": "O modo de provedor automático usa IDs de modelo personalizados." + }, + "rows": { + "theme": "Tema", + "language": "Idioma", + "provider": "Provedor", + "model": "Modelo", + "restart": "Reiniciar nanobot", + "configPath": "Caminho da configuração", + "activePreset": "Predefinição ativa", + "gateway": "Gateway", + "restartState": "Estado do reinício", + "pendingChanges": "Alterações pendentes", + "currentModel": "Configuração atual", + "selectedPreset": "Predefinição selecionada", + "presetModel": "Modelo da predefinição", + "density": "Densidade", + "activityMode": "Detalhe de atividade", + "fileEditDisplay": "Exibição de edição de arquivo", + "codeWrap": "Quebra de linha no código", + "brandLogos": "Logos de marca", + "maxResults": "Máx. de resultados", + "timeout": "Tempo limite", + "jinaReader": "Leitor Jina", + "imageGeneration": "Geração de imagens", + "imageProvider": "Provedor de imagem", + "imageProviderStatus": "Status do provedor", + "imageProviderBase": "Base do provedor", + "imageModel": "Modelo de imagem", + "defaultAspectRatio": "Proporção padrão", + "defaultImageSize": "Tamanho padrão", + "maxImagesPerTurn": "Máx. de imagens por turno", + "imageSaveDir": "Diretório de salvamento", + "botName": "Nome do bot", + "botIcon": "Ícone do bot", + "timezone": "Fuso horário", + "workspacePath": "Workspace padrão", + "localServiceAccess": "Serviços locais", + "webuiDefaultAccess": "Acesso padrão", + "cliAppsCatalog": "Catálogo", + "cliAppsFilter": "Filtro", + "engine": "Engine", + "logs": "Logs", + "diagnostics": "Diagnóstico", + "contextWindow": "Janela de contexto", + "transcription": "Transcrição", + "transcriptionProvider": "Provedor", + "transcriptionProviderStatus": "Status do provedor", + "transcriptionModel": "Modelo", + "transcriptionLanguage": "Idioma", + "voiceLimits": "Limites" + }, + "help": { + "theme": "Alterna entre aparência clara e escura.", + "language": "Escolha o idioma usado pela WebUI.", + "provider": "Selecione o provedor que atenderá as novas requisições de modelo.", + "model": "Defina o nome do modelo padrão usado pelo nanobot.", + "configPath": "O arquivo de configuração do gateway em uso.", + "currentModel": "Usado para novas respostas.", + "selectedModelProvider": "Definido pelo modelo selecionado.", + "selectedModelValue": "Definido pelo modelo selecionado.", + "selectedPreset": "As predefinições nomeadas são somente leitura aqui; edite-as em config.json.", + "presetModel": "Mude para Default para editar modelo e provedor pela WebUI.", + "density": "Armazenado apenas neste navegador.", + "activityMode": "Escolha quanto detalhe de atividade do agente é exibido por padrão.", + "fileEditDisplay": "Escolha se a atividade de edição de arquivo é exibida como contagem de linhas ou como diff.", + "codeWrap": "Mantém linhas longas de código legíveis em telas menores.", + "brandLogos": "Mostra logotipos de provedores terceiros e de CLIs em Configurações.", + "maxResults": "Resultados retornados por cada chamada de web_search.", + "timeout": "Segundos antes de uma requisição de busca expirar.", + "jinaReader": "Usa o Jina Reader para web_fetch quando disponível.", + "imageGeneration": "Expõe generate_image nas conversas quando há um provedor de imagem configurado.", + "imageProvider": "Escolha o provedor do registro usado por generate_image.", + "imageProviderStatus": "A geração de imagens reaproveita as credenciais de Provedores.", + "imageModel": "Nome do modelo enviado ao provedor de imagem selecionado.", + "defaultAspectRatio": "Usado quando o prompt não escolhe uma proporção.", + "defaultImageSize": "Dica de tamanho enviada a provedores compatíveis.", + "maxImagesPerTurn": "Limite superior para uma requisição de generate_image.", + "botName": "Exibido sempre que o nanobot usa um nome visível.", + "botIcon": "Emoji ou texto curto exibido junto ao nome do bot.", + "timezone": "Usado para agendamentos e respostas sensíveis ao horário.", + "cliAppsCatalog": "Instale apenas os adaptadores CLI de apps que o nanobot pode executar localmente; apps nativos permanecem intactos.", + "cliAppsFilter": "Busque por app, categoria ou capacidade.", + "localServiceAccess": "Permite que comandos shell com Acesso Total alcancem serviços localhost.", + "webuiDefaultAccess": "Usado por chats web sem permissão específica de projeto.", + "securityManagedControls": "As buscas na web sempre protegem serviços locais, privados e de metadados. A segurança essencial dos canais fica em config.json.", + "logs": "Abre a pasta de logs do motor nativo.", + "diagnostics": "Exporta um pequeno relatório de runtime para o suporte.", + "localServiceAccessNative": "Permite que comandos shell com Acesso Total alcancem serviços neste Mac.", + "webuiDefaultAccessNative": "Usado por chats nativos sem permissão específica de projeto.", + "contextWindow": "Escolha o orçamento de contexto padrão para esta configuração de modelo.", + "transcription": "Transcreve a entrada do microfone antes de enviá-la. Mensagens de voz dos canais de chat usam as mesmas configurações.", + "transcriptionProvider": "Usa as credenciais do provedor correspondente em Provedores.", + "transcriptionProviderStatus": "As chaves de API ficam em provedores, não nas configurações de transcrição.", + "transcriptionModel": "Deixe como o padrão resolvido, a menos que seu provedor precise de um id de modelo personalizado.", + "transcriptionLanguage": "Dica ISO-639 opcional, como en, zh, ja ou ko." + }, + "timezone": { + "select": "Selecionar fuso horário", + "search": "Buscar fuso horário", + "empty": "Nenhum fuso horário correspondente." + }, + "cliApps": { + "allCategories": "Todas as categorias", + "availableCount": "{{count}} apps", + "installedCount": "{{count}} CLIs instaladas", + "summary": "{{installed}} de {{total}} CLIs instaladas", + "filterAll": "Todos", + "filterInstalled": "CLIs instaladas", + "filterNotInstalled": "Não instaladas", + "searchPlaceholder": "Buscar CLIs", + "loading": "Carregando Apps CLI...", + "empty": "Nenhum App CLI corresponde a este filtro.", + "statusInstalled": "App pronto", + "statusMissing": "Faltando", + "statusAvailable": "Disponível", + "statusUnsupported": "Não compatível", + "statusNotInstalled": "App não instalado", + "requires": "Requer", + "test": "Testar app", + "update": "Atualizar app", + "uninstall": "Desinstalar app", + "install": "Instalar app", + "readyTitle": "@{{name}} está pronto", + "readyStatus": "Pronto", + "readyTry": "Experimentar @{{name}}", + "readyCopied": "Copiado", + "readyPrompt": "Peça ao nanobot para usar @{{name}} nesta tarefa.", + "openChat": "Abrir conversa", + "unsupported": "Não compatível", + "unavailable": "Indisponível", + "noDescription": "Nenhuma descrição disponível." + }, + "about": { + "version": "Versão", + "checking": "Verificando...", + "checkForUpdates": "Verificar atualizações", + "upToDate": "Você está atualizado", + "updateAvailable": "Atualização disponível v{{version}}" + }, + "mcp": { + "allCategories": "Todas as categorias", + "summary": "{{installed}} de {{total}} predefinições habilitadas", + "filterAll": "Todas", + "filterInstalled": "Habilitadas", + "filterNotInstalled": "Não habilitadas", + "searchPlaceholder": "Buscar predefinições MCP", + "moreOptions": "Adicionar integração", + "moreOptionsSubtitle": "Conecte um servidor de ferramentas personalizado ou importe uma configuração existente.", + "customTitle": "MCP personalizado", + "customSubtitle": "Adicione qualquer servidor MCP stdio, HTTP ou SSE.", + "customAction": "Personalizado", + "importAction": "Importar", + "serverName": "Nome do servidor", + "serverUrl": "URL", + "transport": "Transporte", + "command": "Comando", + "args": "Args JSON", + "headers": "Headers JSON", + "env": "Env JSON", + "timeout": "Tempo limite da ferramenta", + "advancedOptions": "Opções avançadas", + "hideAdvanced": "Ocultar avançado", + "saveCustom": "Salvar MCP", + "configImport": "Importar mcp.json", + "importConfig": "Importar", + "restartRequired": "Reinicie o nanobot para conectar as ferramentas MCP atualizadas.", + "toolsFound": "{{count}} ferramentas", + "loading": "Carregando predefinições MCP...", + "empty": "Nenhuma predefinição MCP corresponde a este filtro.", + "openDocs": "Abrir documentação", + "test": "Testar", + "remove": "Remover", + "enable": "Habilitar", + "enabled": "Habilitado", + "setup": "Conectar", + "configure": "Conectar", + "connectTitle": "Conectar {{name}}", + "connectHint": "Adicione a chave a partir das configurações da sua conta.", + "saveAndEnable": "Salvar e habilitar", + "updateSetup": "Atualizar configuração", + "configured": "configurado", + "keepExisting": "Deixe em branco para manter o valor atual", + "statusConfigured": "Configurado", + "statusMissingCredentials": "Precisa de chave", + "statusMissingDependency": "Precisa de dependência", + "statusComingSoon": "Em breve", + "statusNotInstalled": "Não habilitado", + "toolScope": "Ferramentas", + "allTools": "Todas", + "noTools": "Nenhuma", + "testForTools": "Execute Testar para inspecionar e escolher ferramentas individuais." + }, + "values": { + "light": "Claro", + "dark": "Escuro", + "notAvailable": "Indisponível", + "enabled": "Habilitado", + "disabled": "Desabilitado", + "restartPending": "Reinício pendente", + "ready": "Pronto", + "privateEngine": "Motor privado", + "unixSocket": "Socket Unix", + "defaultWorkspace": "Workspace padrão", + "comfortable": "Confortável", + "compact": "Compacto", + "auto": "Automático", + "expanded": "Expandido", + "summary": "Resumo", + "diff": "Diff", + "collapsedDiff": "Diff recolhido", + "on": "Ligado", + "off": "Desligado", + "defaultPermission": "Permissão padrão", + "fullAccess": "Acesso total", + "configured": "Configurado", + "notConfigured": "Não configurado", + "pending": "Pendente", + "restartingEngine": "Reiniciando" + }, + "status": { + "loading": "Carregando configurações...", + "loadError": "Não foi possível carregar as configurações", + "unsaved": "Alterações não salvas.", + "upToDate": "Atualizado.", + "savedRestart": "Salvo. Reinicie o nanobot para aplicar.", + "restartAfterSaving": "Salve as alterações e reinicie quando estiver pronto.", + "savedRestartApply": "Salvo. Reinicie quando estiver pronto.", + "imageProviderRestart": "Alterações do provedor de imagem salvas. Reinicie quando estiver pronto.", + "hostRestartAfterSaving": "Ao salvar, o nanobot reiniciará o motor.", + "hostRestartPending": "Salvo. O motor será reiniciado quando estiver pronto.", + "hostApiUnavailable": "As ações do host só estão disponíveis dentro do app nativo.", + "logsOpened": "Pasta de logs aberta.", + "logsOpenFailed": "Não foi possível abrir a pasta de logs.", + "diagnosticsExported": "Diagnóstico exportado para {{path}}.", + "diagnosticsExportFailed": "Não foi possível exportar o diagnóstico." + }, + "actions": { + "save": "Salvar", + "saving": "Salvando", + "edit": "Editar", + "cancel": "Cancelar", + "open": "Abrir", + "export": "Exportar", + "opening": "Abrindo...", + "exporting": "Exportando..." + }, + "byok": { + "description": "Traga suas próprias chaves de provedor. O nanobot lê esses valores da configuração atual e apenas provedores configurados podem ser selecionados em Geral.", + "configured": "Configurado", + "notConfigured": "Não configurado", + "configuredSection": "Configurados", + "notConfiguredSection": "Não configurados", + "showMore": "Mostrar mais {{count}}", + "showLess": "Mostrar menos", + "apiKey": "Chave de API", + "apiBase": "Base da API", + "apiKeyPlaceholder": "Informe a chave de API", + "apiKeyConfiguredPlaceholder": "Deixe em branco para manter a chave atual", + "configuredKeyHint": "Chave configurada", + "apiBasePlaceholder": "Usar padrão do provedor", + "apiKeyRequired": "É necessária uma chave de API para configurar este provedor.", + "showApiKey": "Mostrar chave de API", + "hideApiKey": "Ocultar chave de API", + "noConfiguredProviders": "Nenhum provedor configurado", + "configureFirst": "Configure primeiro um provedor em BYOK.", + "openByok": "Abrir BYOK", + "tabs": { + "ariaLabel": "Tipo de credencial BYOK", + "llm": "LLM", + "webSearch": "Busca na web" + }, + "webSearch": { + "provider": "Provedor de busca", + "providerHelp": "Escolha o backend usado pela ferramenta de busca na web.", + "selectProvider": "Selecionar provedor", + "credentials": "Credenciais", + "noCredentialRequired": "Não requer chave", + "noCredentialHelp": "DuckDuckGo funciona sem uma chave de API salva.", + "apiKeyHelp": "Armazenado em config e mascarado após salvar.", + "baseUrl": "URL base", + "baseUrlHelp": "O SearXNG precisa da URL da sua própria instância.", + "baseUrlPlaceholder": "https://search.example.com", + "apiKeyRequired": "É necessária uma chave de API para este provedor de busca.", + "baseUrlRequired": "É necessária uma URL base para o SearXNG.", + "missingCredential": "Adicione a credencial necessária antes de salvar.", + "saveHint": "As alterações se aplicam a novas requisições de busca na web." + } + }, + "overview": { + "model": "Modelo atual", + "providers": "Provedores", + "configuredCount": "{{count}} configurados", + "totalProviders": "{{count}} disponíveis", + "webSearch": "Busca na web", + "imageGeneration": "Geração de imagens", + "voiceInput": "Entrada de voz", + "workspace": "Workspace" + }, + "usage": { + "title": "Atividade de tokens", + "shortTitle": "Uso de tokens", + "subtitle": "Uso reportado pelo provedor nos últimos 12 meses.", + "empty": "A atividade de tokens aparecerá após novas respostas do modelo.", + "totalTokens": "Total de tokens", + "peakTokens": "Pico de tokens", + "thirtyDayTokens": "Tokens em 30 dias", + "currentStreak": "Sequência atual", + "longestStreak": "Maior sequência", + "daysValue": "{{count}}d", + "last30": "30 dias", + "activeDays": "Dias ativos", + "requests": "Requisições", + "estimated": "estimado", + "includesEstimates": "inclui estimativas", + "cellTitle": "{{date}}: {{tokens}} tokens, {{requests}} requisições", + "sources": { + "user": "Chat", + "api": "API", + "cron": "Automações", + "dream": "Memória", + "system": "Sistema" + } + }, + "providers": { + "searchPlaceholder": "Buscar provedores", + "noMatches": "Nenhum provedor corresponde a esta busca.", + "saveProvider": "Salvar provedor" + }, + "legal": { + "thirdPartyBrands": "Nomes de produtos, logotipos e marcas são propriedades de seus respectivos donos. O uso é apenas para identificação e não implica endosso." + }, + "image": { + "selectProvider": "Selecionar provedor", + "selectAspect": "Selecionar proporção", + "selectSize": "Selecionar tamanho", + "configureProvider": "Configurar provedor", + "missingCredential": "Configure o provedor antes de habilitar a geração de imagens." + }, + "api": { + "title": "Servidor de API", + "openaiCompatible": "API compatível com OpenAI", + "description": "Conecte SDKs e agentes por meio de um endpoint /v1 local.", + "start": "Iniciar servidor de API", + "starting": "Iniciando...", + "stop": "Parar", + "stopping": "Parando...", + "access": "Acesso", + "thisDevice": "Este dispositivo", + "localNetwork": "Rede local", + "localHelp": "Apenas este dispositivo pode se conectar.", + "networkHelp": "Outros dispositivos podem se conectar; é necessária uma chave de API.", + "port": "Porta", + "portHelp": "A API usa esta porta local.", + "apiKey": "Chave de API", + "apiKeyHelp": "Os clientes enviam isto como um token Bearer.", + "apiKeyRequired": "Obrigatória antes de expor a API na rede.", + "apiKeyPlaceholder": "Informe uma chave de API", + "autoInstall": "O suporte à API será instalado automaticamente quando você iniciá-lo." + }, + "observability": { + "title": "Observabilidade", + "configured": "Credenciais de tracing estão disponíveis para o nanobot.", + "environment": "Defina LANGFUSE_SECRET_KEY e LANGFUSE_PUBLIC_KEY e reinicie o nanobot.", + "enable": "Habilitar suporte a tracing" + }, + "apps": { + "description": "Adicione ferramentas ao nanobot e mencione-as com @ na conversa.", + "cliLabel": "App", + "mcpLabel": "Integração", + "channelLabel": "Canal", + "featureLabel": "Recurso", + "filterAll": "Prontos", + "filterPlugins": "Complementos", + "filterCli": "Apps", + "filterMcp": "Integrações", + "enabledSummary": "{{count}} prontos", + "caption": "{{cli}} apps · {{mcp}} integrações", + "searchPlaceholder": "Buscar ferramentas", + "featured": "Ferramentas", + "loading": "Carregando Apps...", + "empty": "Nenhuma ferramenta corresponde a esta visualização.", + "restartRequired": "Reinicie o nanobot para aplicar os apps e integrações atualizados." + }, + "channels": { + "description": "Conecte apps de chat, e-mail e WebUI ao nanobot.", + "caption": "{{enabled}} habilitados · {{total}} canais", + "searchPlaceholder": "Buscar canais", + "backToChannels": "Todos os canais", + "catalog": "Canais", + "loading": "Carregando Canais...", + "empty": "Nenhum canal corresponde a este filtro.", + "restartRequired": "Reinicie o nanobot para aplicar o suporte de canais atualizado.", + "requires": "Requer: {{requirements}}", + "setUp": "Configurar", + "setupGuide": "Guia de configuração", + "setupSummary": "Habilitar apenas ativa o suporte no nanobot. Adicione as credenciais da plataforma e reinicie o nanobot.", + "configKeys": "Chaves de configuração", + "enable": "Habilitar canal", + "disable": "Desabilitar canal", + "needsConfig": "Precisa de configuração", + "connect": "Conectar", + "reconnect": "Reconectar", + "feishuQrAlt": "QR code de conexão do Feishu", + "feishuScanTitle": "Escaneie com o Feishu", + "feishuScanDescription": "Use o Feishu ou o Lark no seu celular para escanear este código. O nanobot concluirá a configuração automaticamente após a autorização.", + "feishuWaiting": "Aguardando autorização...", + "feishuConnected": "Feishu está conectado.", + "feishuConnectStopped": "Conexão interrompida.", + "feishuConnecting": "Conectando..." + }, + "nanobotFeatures": { + "enabled": "Habilitado", + "enable": "Habilitar", + "disable": "Desabilitar", + "ready": "Pronto", + "missingDependency": "Suporte ausente", + "installSupport": "Instalar suporte", + "installConfirmTitle": "Instalar suporte para {{name}}?", + "installConfirmDescription": "O nanobot adicionará o que {{name}} precisa e depois o ativará. Continuar?", + "installConfirmAction": "Instalar e habilitar", + "websocketRequired": "Necessário para a WebUI", + "channelDisabled": "Canal desabilitado", + "notEnabled": "Não habilitado" + }, + "automations": { + "filters": { + "all": "Todas", + "active": "Ativas", + "paused": "Pausadas", + "failed": "Precisam de atenção", + "system": "Sistema" + }, + "sort": { + "next": "Próxima execução", + "last": "Última execução", + "updated": "Atualizada", + "name": "Nome" + }, + "search": "Buscar tarefa, mensagem, conversa vinculada ou agendamento", + "queue": "Fila", + "loading": "Carregando automações...", + "noMatches": "Nenhuma automação corresponde a esta visualização.", + "empty": "Nenhuma automação ainda.", + "emptyHint": "Crie uma de onde ela deve rodar para que o nanobot mantenha o contexto correto.", + "oneShot": "Uma vez", + "systemTask": "Automação gerenciada pelo sistema", + "localTrigger": "Gatilho local", + "labels": { + "schedule": "Agendamento", + "next": "Próxima", + "origin": "Conversa vinculada", + "created": "Criada", + "updated": "Atualizada" + }, + "runNow": "Executar agora", + "pause": "Pausar", + "resume": "Retomar", + "edit": "Editar", + "delete": "Excluir", + "protected": "Protegida", + "editTitle": "Editar automação", + "save": "Salvar", + "deleteTitle": "Excluir automação", + "deleteDescription": "Isso remove {{name}} do store de cron. As mensagens anteriores da conversa permanecem na sessão.", + "cancel": "Cancelar", + "status": { + "system": "Sistema", + "running": "Executando agora", + "paused": "Pausada", + "failed": "Falhou", + "completed": "Concluída", + "noSchedule": "Sem agendamento", + "active": "Ativa" + }, + "origin": { + "system": "Sistema", + "unknown": "Sem conversa vinculada" + }, + "channels": { + "api": "API", + "cli": "CLI", + "dingtalk": "DingTalk", + "discord": "Discord", + "email": "E-mail", + "feishu": "Feishu", + "matrix": "Matrix", + "msteams": "Microsoft Teams", + "qq": "QQ", + "slack": "Slack", + "telegram": "Telegram", + "wechat": "WeChat", + "wecom": "WeCom", + "weixin": "WeChat", + "whatsapp": "WhatsApp" + }, + "schedule": { + "at": "Às {{time}}", + "every": "A cada {{duration}}", + "cron": "Cron {{expr}}", + "cronWithTz": "Cron {{expr}} · {{tz}}", + "withTz": "{{summary}} · {{tz}}", + "dailyAt": "Diariamente às {{time}}", + "weekdaysAt": "Dias úteis às {{time}}", + "hourlyAt": "A cada hora em :{{minute}}", + "hourlyWindow": "A cada hora {{start}}-{{end}} em :{{minute}}", + "local": "Gatilho local", + "custom": "Agendamento personalizado" + }, + "next": { + "paused": "Pausada", + "pending": "Executando agora", + "local": "Aguardando gatilho", + "none": "Sem próxima execução" + }, + "message": { + "showMore": "Mostrar mensagem completa", + "showLess": "Mostrar menos" + }, + "fields": { + "name": "Nome", + "message": "Mensagem", + "scheduleType": "Tipo de agendamento", + "every": "A cada", + "unit": "Unidade", + "cronExpression": "Expressão cron", + "timezone": "Fuso horário", + "runAt": "Executar em" + }, + "scheduleTypes": { + "every": "Intervalo", + "cron": "Cron", + "at": "Uma vez" + }, + "everyUnits": { + "second": "Segundos", + "minute": "Minutos", + "hour": "Horas", + "day": "Dias" + }, + "validation": { + "nameRequired": "O nome é obrigatório.", + "messageRequired": "A mensagem é obrigatória.", + "intervalRequired": "O intervalo deve ser um número positivo.", + "cronRequired": "A expressão cron é obrigatória.", + "timeRequired": "O horário de execução é obrigatório.", + "futureRequired": "O horário de execução deve estar no futuro." + } + }, + "oauth": { + "authentication": "Autenticação OAuth", + "signIn": "Entrar", + "signingIn": "Entrando...", + "signInAgain": "Entrar novamente", + "signOut": "Sair", + "signedInAs": "Conectado como {{account}}", + "signInHelp": "Entre por este dispositivo; nenhuma chave de API é armazenada em config.", + "signInRequired": "Login necessário", + "signInBeforeSaving": "Entre antes de salvar este provedor OAuth como provedor de modelo ativo.", + "signedIn": "Conectado", + "notSignedIn": "Desconectado" + }, + "skills": { + "description": "Revise as skills de instrução que este agente pode carregar durante uma conversa.", + "caption": "{{available}} disponíveis · {{total}} no total", + "featured": "Skills do agente", + "empty": "Nenhuma skill disponível.", + "sourceWorkspace": "Personalizada", + "sourceBuiltin": "Embutida", + "statusAvailable": "Disponível", + "statusUnavailable": "Indisponível", + "unavailableReason": "Faltando: {{reason}}", + "openDetails": "Abrir detalhes de {{name}}", + "loadingDetail": "Carregando detalhes da skill...", + "loadFailed": "Não foi possível carregar os detalhes da skill.", + "descriptionTitle": "Descrição", + "source": "Origem", + "status": "Status", + "requirements": "Requisitos", + "noRequirements": "Sem requisitos explícitos.", + "commands": "Comandos", + "environment": "Variáveis de ambiente", + "missingCommands": "CLI ausente", + "missingEnvironment": "ENV ausente", + "unavailableReasonLabel": "Motivo da indisponibilidade", + "rawInstructions": "SKILL.md bruto", + "rawInstructionsEmpty": "Sem instruções brutas.", + "detailDescription": "Detalhes de {{name}}." + }, + "voice": { + "selectProvider": "Selecionar provedor", + "configureProvider": "Configurar provedor", + "languageAuto": "Auto" + } + }, + "chat": { + "fallbackTitle": "Conversa {{id}}", + "forkTitle": "Fork: {{title}}", + "loading": "Carregando…", + "noSessions": "Nenhuma sessão ainda.", + "showMore": "Mostrar mais {{count}}", + "collapsed": "{{count}} conversas ocultas", + "showLess": "Mostrar menos", + "actions": "Ações da conversa {{title}}", + "newInProject": "Iniciar uma nova conversa em {{project}}", + "activity": { + "running": "Agente em execução", + "complete": "Agente finalizado", + "updated": "Nova atividade" + }, + "pin": "Fixar", + "unpin": "Desafixar", + "rename": "Renomear", + "renameTitle": "Renomear conversa", + "renameDescription": "Escolha um nome local da barra lateral para esta conversa.", + "renamePlaceholder": "Nome da conversa", + "renameProjectTitle": "Renomear projeto", + "renameProjectDescription": "Escolha um nome local da barra lateral para este projeto.", + "renameProjectPlaceholder": "Nome do projeto", + "renameSave": "Salvar", + "archive": "Arquivar", + "unarchive": "Desarquivar", + "showArchived": "Mostrar arquivadas", + "hideArchived": "Ocultar arquivadas", + "delete": "Excluir", + "newChat": "Nova conversa", + "groups": { + "pinned": "Fixadas", + "all": "Conversas", + "projects": "Projetos", + "today": "Hoje", + "yesterday": "Ontem", + "earlier": "Anteriores", + "archived": "Arquivadas" + } + }, + "deleteConfirm": { + "title": "Excluir esta conversa?", + "description": "Esta ação não pode ser desfeita.", + "cancel": "Cancelar", + "confirm": "Excluir", + "automationsDescription": "Esta conversa possui automações agendadas. Excluí-la também excluirá essas automações.", + "moreAutomations": "+ {{count}} a mais", + "confirmWithAutomations": "Excluir", + "schedule": { + "at": "{{time}}", + "every": "A cada {{duration}}", + "cron": "Cron {{expr}}", + "cronWithTz": "Cron {{expr}} · {{tz}}", + "local": "Gatilho local", + "unknown": "Agendamento personalizado" + }, + "next": { + "label": "Próxima: {{time}}", + "disabled": "Pausada", + "local": "Aguardando gatilho", + "none": "Sem próxima execução" + } + }, + "connection": { + "idle": "Ocioso", + "connecting": "Conectando…", + "open": "Conectado", + "reconnecting": "Reconectando…", + "closed": "Desconectado", + "error": "Erro de conexão" + }, + "thread": { + "loadingConversation": "Carregando conversa…", + "empty": { + "greetings": { + "workOn": "Em que vamos trabalhar?", + "start": "Por onde começamos?", + "build": "O que vamos construir hoje?", + "tackle": "O que vamos resolver juntos?" + }, + "quickActions": { + "plan": { + "title": "Criar um plano de projeto", + "prompt": "Crie um plano de projeto conciso para o que eu deveria construir em seguida." + }, + "analyze": { + "title": "Analisar estes dados", + "prompt": "Ajude-me a analisar estes dados e destaque os padrões mais importantes." + }, + "brainstorm": { + "title": "Fazer um brainstorming", + "prompt": "Sugira algumas ideias práticas e tradeoffs para este problema." + }, + "code": { + "title": "Escrever código", + "prompt": "Ajude-me a escrever o código desta tarefa, começando pela menor alteração útil." + }, + "summarize": { + "title": "Resumir este documento", + "prompt": "Resuma este documento e liste os principais destaques." + }, + "more": { + "title": "Mais", + "prompt": "Mostre-me algumas formas úteis com as quais você pode ajudar neste workspace." + } + }, + "imageQuickActions": { + "icon": { + "title": "Desenhar um ícone de app", + "prompt": "Gere um ícone de app 1:1 limpo para o nanobot: robô amigável, estilo vetorial simples, paleta suave em azul e branco, sem texto." + }, + "sticker": { + "title": "Criar um sticker", + "prompt": "Gere uma imagem estilo sticker de um pequeno assistente robô, com fundo de aparência transparente, expressivo e divertido." + }, + "poster": { + "title": "Criar um pôster", + "prompt": "Gere um conceito de pôster polido para um assistente pessoal de IA, composição moderna, hierarquia visual forte, adequado para uma landing page." + }, + "product": { + "title": "Mockup de produto", + "prompt": "Gere uma imagem limpa de mockup de produto para um app web de IA conversacional, interface mínima, iluminação premium, moldura de dispositivo realista." + }, + "portrait": { + "title": "Retrato estilizado", + "prompt": "Gere um retrato estilizado de um companheiro de IA amigável, luz suave, detalhado mas acessível, estilo de ilustração moderna." + }, + "edit": { + "title": "Editar uma imagem", + "prompt": "Ajude-me a editar uma imagem. Peça-me para enviar ou referenciar a imagem primeiro e, em seguida, gere o resultado editado." + } + } + }, + "header": { + "toggleSidebar": "Alternar barra lateral", + "newChat": "Iniciar uma nova conversa", + "toggleTheme": "Alternar tema pelo cabeçalho", + "settings": "Abrir configurações", + "sessionInfo": "Detalhes da sessão" + }, + "sessionInfo": { + "title": "Sessão", + "untitled": "Conversa sem título", + "automations": "Automações", + "count": "{{count}}", + "loading": "Carregando automações...", + "loadFailed": "Não foi possível carregar as automações.", + "empty": "Ainda não há automações nesta sessão.", + "disabled": "Desligado", + "schedule": { + "at": "{{time}}", + "every": "A cada {{duration}}", + "cron": "Cron {{expr}}", + "cronWithTz": "Cron {{expr}} · {{tz}}", + "local": "Gatilho local", + "unknown": "Agendamento personalizado" + }, + "next": { + "label": "{{time}}", + "pending": "Executará em breve", + "disabled": "Pausada", + "local": "Aguardando gatilho", + "none": "Sem próxima execução" + } + }, + "composer": { + "placeholderThread": "Digite sua mensagem…", + "placeholderHero": "Pergunte qualquer coisa...", + "placeholderOpening": "Abrindo uma nova conversa…", + "placeholderStreaming": "O modelo está respondendo…", + "inputAria": "Campo de mensagem", + "sendHint": "Enter para enviar · Shift+Enter para nova linha", + "runRuntimeTitle": "Executando · {{elapsed}}", + "goalStateStrip": "Objetivo · {{label}}", + "goalStateFallback": "Objetivo", + "goalStateExpandAria": "Mostrar objetivo completo", + "goalStateSheetTitle": "Objetivo", + "goalStateCloseAria": "Fechar objetivo", + "send": "Enviar mensagem", + "stop": "Parar resposta", + "modelNotConfigured": "Modelo não configurado", + "configureModel": "Configurar modelo", + "queued": { + "label": "Guia em fila", + "guide": "Guiar", + "delete": "Excluir guia", + "edit": "Editar guia", + "drag": "Arraste para reordenar" + }, + "attachImage": "Anexar imagem", + "imageMode": { + "label": "Geração de imagens", + "toggle": "Alternar modo de geração de imagens", + "placeholder": "Descreva ou edite uma imagem…", + "aspectAria": "Proporção da imagem", + "aspectLabel": "Formato da imagem", + "aspect": { + "auto": "Automático", + "1_1": "Quadrado 1:1", + "3_4": "Retrato 3:4", + "9_16": "Story 9:16", + "4_3": "Paisagem 4:3", + "16_9": "Panorâmico 16:9" + } + }, + "tools": { + "search": "Buscar", + "reason": "Raciocinar", + "deepResearch": "Pesquisa aprofundada", + "voice": "Entrada de voz" + }, + "voice": { + "hint": "Clique para ditar ou segure", + "stop": "Parar gravação", + "transcribing": "Transcrevendo...", + "recordingStatus": "Gravando {{time}}" + }, + "voiceErrors": { + "unsupported": "A entrada de voz não é compatível com este navegador.", + "permission": "É necessária permissão do microfone.", + "notConfigured": "Configure primeiro um provedor de transcrição.", + "tooLong": "A gravação é longa demais.", + "tooShort": "Segure um pouco mais para gravar voz.", + "noInput": "Não foi detectada entrada do microfone.", + "failed": "Não foi possível transcrever o áudio." + }, + "slash": { + "ariaLabel": "Comandos de barra", + "label": "comandos", + "navigateHint": "↑↓ Navegar", + "selectHint": "Enter/Tab Selecionar", + "closeHint": "Esc Fechar", + "badges": { + "current": "Atual", + "recent": "Recente" + }, + "details": { + "goalActive": "Objetivo em execução", + "goalReady": "Iniciar um objetivo sustentado", + "history": "Mostrar mensagens recentes", + "stopRunning": "Executando agora" + }, + "commands": { + "new": { + "title": "Nova conversa", + "description": "Reinicia esta conversa e inicia uma conversa nova." + }, + "stop": { + "title": "Parar tarefa atual", + "description": "Cancela o turno ativo do agente nesta conversa." + }, + "restart": { + "title": "Reiniciar nanobot", + "description": "Reinicia o processo do bot no mesmo lugar." + }, + "status": { + "title": "Mostrar status", + "description": "Exibe o status de runtime, provedor e canais." + }, + "model": { + "title": "Modelo", + "description": "Mostra ou troca a predefinição de modelo ativa." + }, + "history": { + "title": "Mostrar histórico da conversa", + "description": "Imprime as últimas N mensagens persistidas da conversa." + }, + "dream": { + "title": "Executar Dream", + "description": "Aciona manualmente a consolidação de memória." + }, + "dream_log": { + "title": "Mostrar log do Dream", + "description": "Mostra o que a última consolidação do Dream alterou." + }, + "dream_restore": { + "title": "Restaurar memória", + "description": "Reverte a memória para um snapshot anterior do Dream." + }, + "dream_prompt": { + "title": "Memória do Dream", + "description": "Diz ao Dream como organizar a memória deste workspace." + }, + "goal": { + "title": "Objetivo de longa duração", + "description": "Diz ao agente para tratar isto como um objetivo sustentado de várias etapas." + }, + "trigger": { + "title": "Criar gatilho local", + "description": "Cria um gatilho de CLI vinculado a esta sessão de conversa." + }, + "help": { + "title": "Mostrar ajuda", + "description": "Lista os comandos de barra disponíveis." + }, + "pairing": { + "title": "Pareamento", + "description": "Gerencia solicitações de pareamento." + } + } + }, + "mentions": { + "ariaLabel": "Apps", + "label": "Apps", + "cliGroup": "Apps CLI", + "mcpGroup": "Serviços MCP", + "cliBadge": "CLI", + "mcpBadge": "MCP", + "cliDescription": "Usar @{{name}} como app CLI local", + "mcpDescription": "Usar @{{name}} como servidor MCP" + }, + "encoding": "Codificando…", + "remove": "Remover anexo", + "normalizedSizeHint": "{{orig}} → {{current}} (auto)", + "imageRejected": { + "unsupported_type": "Tipo de arquivo não compatível", + "too_many_images": "Máx. de {{max}} imagens por mensagem", + "magic_mismatch": "O arquivo não parece uma imagem real", + "decode_failed": "Não foi possível decodificar esta imagem", + "too_large": "A imagem é grande demais — tente uma menor", + "io": "Não foi possível ler este arquivo" + }, + "workspace": { + "accessAria": "Modo de acesso ao workspace", + "projectAria": "Escolher projeto", + "projectPlaceholder": "Selecionar projeto", + "default": "Permissão padrão", + "full": "Acesso total" + } + }, + "scrollToBottom": "Rolar para o final", + "loadEarlier": "Carregar mensagens anteriores", + "forkedFromHistory": "Fork a partir do histórico", + "promptNavigator": { + "open": "Abrir navegador de prompts", + "title": "Prompts", + "search": "Buscar prompts", + "noResults": "Nenhum prompt correspondente.", + "jumpTo": "Ir para o prompt: {{label}}" + } + }, + "message": { + "streaming": "transmitindo", + "assistantTyping": "Assistente está digitando", + "toolSingle": "Usando uma ferramenta", + "toolMany": "Foram usadas {{count}} ferramentas", + "toolSummary": "{{count}} ferramenta", + "toolSummaryMany": "{{count}} ferramentas", + "reasoningTools": "Raciocínio · {{count}} ferramentas", + "reasoningToolsSingular": "Raciocínio · 1 ferramenta", + "reasoning": "Pensando", + "reasoningStreaming": "Pensando…", + "reasoningSummary": "Raciocínio", + "agentActivitySummary": "{{reasoning}} etapas · {{tools}} chamadas de ferramenta", + "agentActivityToolsOnly": "{{tools}} chamadas de ferramenta", + "agentActivityLiveSummary": "Trabalhando… · {{reasoning}} etapas · {{tools}} chamadas de ferramenta", + "agentActivityLiveToolsOnly": "Trabalhando… · {{tools}} chamadas de ferramenta", + "activityThinkingFor": "Pensando por {{duration}}", + "activityThought": "Pensamento", + "activityThoughtFor": "Pensou por {{duration}}", + "activityWorkingFor": "Trabalhando por {{duration}}", + "activityWorked": "Trabalhou", + "activityWorkedFor": "Trabalhou por {{duration}}", + "cliActivityRunningOne": "Usando @{{name}}", + "cliActivityRanOne": "Usou @{{name}}", + "cliActivityFailedOne": "Falhou em @{{name}}", + "cliActivityRunningMany": "Usando {{count}} apps CLI", + "cliActivityRanMany": "Usou {{count}} apps CLI", + "cliActivityFailedMany": "{{count}} apps CLI falharam", + "cliRunRunning": "Usando", + "cliRunRan": "Usou", + "cliRunFailed": "Falhou", + "imageAttachment": "Anexo de imagem", + "automationSourceFallback": "Automação", + "automationTriggered": "Acionada automaticamente", + "forkFromHere": "Fazer fork", + "copyReply": "Copiar", + "copiedReply": "Copiado", + "turnLatencyTitle": "Tempo de resposta (ponta a ponta)", + "fileEditViewDiff": "Ver diff", + "fileEditViewLargeDiff": "Ver diff grande", + "fileEditDiffLineCount": "{{count}} linhas", + "fileEditUnchangedLinesHidden": "{{count}} linhas inalteradas ocultas", + "fileEditShowMoreLines": "Mostrar mais {{count}} linhas", + "fileEditShowFewerLines": "Mostrar menos linhas", + "fileEditOpenFile": "Abrir arquivo", + "fileEditDiffTruncated": "Diff truncado. Abra o arquivo para ver a alteração completa." + }, + "lightbox": { + "title": "Pré-visualização de imagem", + "open": "Ver imagem", + "prev": "Imagem anterior", + "next": "Próxima imagem", + "close": "Fechar pré-visualização" + }, + "filePreview": { + "aria": "Pré-visualização de arquivo", + "close": "Fechar pré-visualização de arquivo", + "loading": "Carregando pré-visualização...", + "failed": "Não foi possível pré-visualizar este arquivo.", + "routeMissing": "A pré-visualização de arquivo precisa do gateway mais recente. Reinicie o nanobot gateway e tente novamente.", + "resize": "Redimensionar pré-visualização de arquivo", + "truncated": "A pré-visualização foi truncada porque este arquivo é grande." + }, + "code": { + "fallbackLanguage": "código", + "copyAria": "Copiar código", + "copy": "Copiar", + "copied": "Copiado" + }, + "common": { + "dismiss": "Descartar" + }, + "errors": { + "messageTooBig": { + "title": "Mensagem grande demais", + "body": "O servidor rejeitou sua última mensagem porque ela excedeu o limite de tamanho. Remova algumas imagens ou tente arquivos menores e envie novamente." + }, + "workspaceScopeRejected": { + "title": "O workspace não foi alterado", + "body": "O nanobot manteve o workspace anterior porque o projeto ou modo de acesso solicitado foi rejeitado pelo gateway." + } + }, + "workspace": { + "dialog": { + "defaultProject": "Workspace padrão", + "manual": "Colar caminho", + "manualPlaceholder": "/Users/nome/projeto", + "usePath": "Usar caminho", + "absolutePathRequired": "Informe um caminho absoluto de pasta nesta máquina." + } + } +} \ No newline at end of file diff --git a/webui/src/tests/i18n.test.tsx b/webui/src/tests/i18n.test.tsx index ddf97255..43e34280 100644 --- a/webui/src/tests/i18n.test.tsx +++ b/webui/src/tests/i18n.test.tsx @@ -311,4 +311,18 @@ describe("webui i18n", () => { expect(settings.overview.webSearch).toBe("网页搜索"); expect(settings.overview.workspace).toBe("工作区"); }); + + it("keeps Brazilian Portuguese settings overview copy localized", () => { + const settings = resources["pt-BR"].common.settings; + const sidebar = resources["pt-BR"].common.sidebar; + const chat = resources["pt-BR"].common.chat; + + expect(sidebar.settings).toBe("Configurações"); + expect(chat.newChat).toBe("Nova conversa"); + expect(settings.nav.browser).toBe("Web"); + expect(settings.sections.webSearch).toBe("Busca na web"); + expect(settings.byok.tabs.webSearch).toBe("Busca na web"); + expect(settings.overview.webSearch).toBe("Busca na web"); + expect(settings.overview.workspace).toBe("Workspace"); + }); });