diff --git a/docs/i18n.md b/docs/i18n.md index 9b3a69609..44b55b478 100644 --- a/docs/i18n.md +++ b/docs/i18n.md @@ -8,10 +8,12 @@ Paseo client UI translations live in `packages/app/src/i18n`. - `ar` - `es` - `fr` +- `ja` +- `pt-BR` - `ru` - `zh-CN` -The persisted app language setting is `"system" | "ar" | "en" | "es" | "fr" | "ru" | "zh-CN"`. `"system"` follows the device or browser locale when it maps to a supported locale; unsupported system locales fall back to English. +The persisted app language setting is `"system" | "ar" | "en" | "es" | "fr" | "ja" | "pt-BR" | "ru" | "zh-CN"`. `"system"` follows the device or browser locale when it maps to a supported locale; unsupported system locales fall back to English. Japanese maps from system locales `ja` and Japanese regional locales. Brazilian Portuguese maps from system locales `pt-BR` and bare `pt`; other Portuguese regional locales remain unsupported until explicitly added. ## Translation Scope @@ -33,7 +35,7 @@ Run: npx vitest run packages/app/src/i18n/resources.test.ts --bail=1 ``` -The parity test catches missing keys between English and Simplified Chinese resources. +The parity test catches missing keys across English and every supported locale resource. ## Migration Order @@ -77,3 +79,4 @@ Within a migrated surface, do not leave mixed-language neighboring labels when t - Batch 4X migrated descriptor and command chrome: Pair-device modal header, workspace setup sheet title, terminal panel fallback labels, command-center action titles, and file-pane host-disconnected fallback. Provider/catalog names, command-center search keywords, terminal runtime titles, file paths, and raw read errors remain runtime values. - Batch 4Y tightened the translation boundary so React components and custom hooks use `useTranslation()` while pure helpers keep direct `i18n.t(...)` fallbacks, and migrated remaining small UI/accessibility fallbacks across message details, menu backdrops, startup errors, sidebar PR badges, settings/project accessibility labels, composer send/create/download fallbacks, client slash-command descriptions, terminal subscribe errors, and desktop update completion text. Provider catalog metadata, shortcut registry fallbacks, agent/daemon/protocol reasons, terminal contents, raw runtime errors, and user/project/workspace names remain untranslated. - Batch 4Z expanded the supported locale set to the six UN official languages: Arabic, Chinese, English, French, Russian, and Spanish. Arabic, French, Russian, and Spanish now have full client-owned UI resource coverage, with key parity, fallback-ratio, and interpolation-placeholder tests guarding the generated translations. Arabic does not enable RTL layout direction in this batch. +- Batch 5A added Brazilian Portuguese (`pt-BR`) resource coverage, language selector labels, i18next registration, and system-locale mapping for `pt-BR` and bare `pt`. Non-Brazilian Portuguese regional locales remain unsupported until a matching resource is added. diff --git a/packages/app/src/i18n/i18next.ts b/packages/app/src/i18n/i18next.ts index 168f02ade..66296ee81 100644 --- a/packages/app/src/i18n/i18next.ts +++ b/packages/app/src/i18n/i18next.ts @@ -6,6 +6,7 @@ import { en } from "./resources/en"; import { es } from "./resources/es"; import { fr } from "./resources/fr"; import { ja } from "./resources/ja"; +import { ptBR } from "./resources/pt-BR"; import { ru } from "./resources/ru"; import { zhCN } from "./resources/zh-CN"; @@ -22,6 +23,7 @@ observeI18nInit( es: { translation: es }, fr: { translation: fr }, ja: { translation: ja }, + "pt-BR": { translation: ptBR }, ru: { translation: ru }, "zh-CN": { translation: zhCN }, }, diff --git a/packages/app/src/i18n/locales.test.ts b/packages/app/src/i18n/locales.test.ts index b85d9ae76..15413b898 100644 --- a/packages/app/src/i18n/locales.test.ts +++ b/packages/app/src/i18n/locales.test.ts @@ -8,16 +8,9 @@ import { describe("parseAppLanguage", () => { it("accepts system and all supported language locales", () => { - expect(["system", "ar", "en", "es", "fr", "ja", "ru", "zh-CN"].map(parseAppLanguage)).toEqual([ - "system", - "ar", - "en", - "es", - "fr", - "ja", - "ru", - "zh-CN", - ]); + expect( + ["system", "ar", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN"].map(parseAppLanguage), + ).toEqual(["system", "ar", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN"]); }); it("returns null for unknown values", () => { @@ -33,6 +26,7 @@ describe("parseAppLanguage", () => { "es", "fr", "ja", + "pt-BR", "ru", "zh-CN", ]); @@ -42,14 +36,24 @@ describe("parseAppLanguage", () => { describe("formatLanguageOptionLabel", () => { it("shows the native language name and English name in English UI", () => { const arabic = LANGUAGE_OPTIONS.find((option) => option.value === "ar"); + const japanese = LANGUAGE_OPTIONS.find((option) => option.value === "ja"); + const portuguese = LANGUAGE_OPTIONS.find((option) => option.value === "pt-BR"); const spanish = LANGUAGE_OPTIONS.find((option) => option.value === "es"); const chinese = LANGUAGE_OPTIONS.find((option) => option.value === "zh-CN"); expect([ formatLanguageOptionLabel(arabic!, "en", "System"), + formatLanguageOptionLabel(japanese!, "en", "System"), + formatLanguageOptionLabel(portuguese!, "en", "System"), formatLanguageOptionLabel(spanish!, "en", "System"), formatLanguageOptionLabel(chinese!, "en", "System"), - ]).toEqual(["العربية - Arabic", "Español - Spanish", "简体中文 - Simplified Chinese"]); + ]).toEqual([ + "العربية - Arabic", + "日本語 - Japanese", + "Português brasileiro - Brazilian Portuguese", + "Español - Spanish", + "简体中文 - Simplified Chinese", + ]); }); it("shows the native language name and Chinese name in Chinese UI", () => { @@ -66,8 +70,12 @@ describe("formatLanguageOptionLabel", () => { it("uses a single label when both language names match", () => { const english = LANGUAGE_OPTIONS.find((option) => option.value === "en"); + const japanese = LANGUAGE_OPTIONS.find((option) => option.value === "ja"); + const portuguese = LANGUAGE_OPTIONS.find((option) => option.value === "pt-BR"); expect(formatLanguageOptionLabel(english!, "en", "System")).toBe("English"); + expect(formatLanguageOptionLabel(japanese!, "ja", "システム")).toBe("日本語"); + expect(formatLanguageOptionLabel(portuguese!, "pt-BR", "Sistema")).toBe("Português brasileiro"); }); it("uses the active-language name for System", () => { @@ -84,6 +92,7 @@ describe("resolveSupportedLocale", () => { expect(resolveSupportedLocale("es", ["en-US"])).toBe("es"); expect(resolveSupportedLocale("fr", ["en-US"])).toBe("fr"); expect(resolveSupportedLocale("ja", ["en-US"])).toBe("ja"); + expect(resolveSupportedLocale("pt-BR", ["en-US"])).toBe("pt-BR"); expect(resolveSupportedLocale("ru", ["en-US"])).toBe("ru"); expect(resolveSupportedLocale("zh-CN", ["en-US"])).toBe("zh-CN"); }); @@ -94,9 +103,16 @@ describe("resolveSupportedLocale", () => { expect(resolveSupportedLocale("system", ["es-MX"])).toBe("es"); expect(resolveSupportedLocale("system", ["fr-CA"])).toBe("fr"); expect(resolveSupportedLocale("system", ["ja-JP"])).toBe("ja"); + expect(resolveSupportedLocale("system", ["pt-BR"])).toBe("pt-BR"); + expect(resolveSupportedLocale("system", ["pt"])).toBe("pt-BR"); expect(resolveSupportedLocale("system", ["ru-RU"])).toBe("ru"); }); + it("does not map non-Brazilian Portuguese system locales to Brazilian Portuguese", () => { + expect(resolveSupportedLocale("system", ["pt-PT"])).toBe("en"); + expect(resolveSupportedLocale("system", ["pt-AO"])).toBe("en"); + }); + it("keeps English when Spanish is a secondary system language", () => { expect(resolveSupportedLocale("system", ["en-US", "es-GB"])).toBe("en"); }); diff --git a/packages/app/src/i18n/locales.ts b/packages/app/src/i18n/locales.ts index 12e3454d1..699c4fa75 100644 --- a/packages/app/src/i18n/locales.ts +++ b/packages/app/src/i18n/locales.ts @@ -1,4 +1,4 @@ -export type SupportedLocale = "ar" | "en" | "es" | "fr" | "ja" | "ru" | "zh-CN"; +export type SupportedLocale = "ar" | "en" | "es" | "fr" | "ja" | "pt-BR" | "ru" | "zh-CN"; export type AppLanguage = "system" | SupportedLocale; export interface LanguageOption { @@ -15,6 +15,7 @@ export const LANGUAGE_OPTIONS: LanguageOption[] = [ { value: "es", labelKey: "settings.general.language.options.es" }, { value: "fr", labelKey: "settings.general.language.options.fr" }, { value: "ja", labelKey: "settings.general.language.options.ja" }, + { value: "pt-BR", labelKey: "settings.general.language.options.ptBR" }, { value: "ru", labelKey: "settings.general.language.options.ru" }, { value: "zh-CN", labelKey: "settings.general.language.options.zhCN" }, ]; @@ -26,6 +27,7 @@ const SUPPORTED_LANGUAGES = new Set([ "es", "fr", "ja", + "pt-BR", "ru", "zh-CN", ]); @@ -35,6 +37,7 @@ const LANGUAGE_NATIVE_NAMES: Record = { es: "Español", fr: "Français", ja: "日本語", + "pt-BR": "Português brasileiro", ru: "Русский", "zh-CN": "简体中文", }; @@ -45,6 +48,7 @@ const LANGUAGE_NAMES_BY_LOCALE: Record { expect(flattenKeys(es).sort()).toEqual(englishKeys); expect(flattenKeys(fr).sort()).toEqual(englishKeys); expect(flattenKeys(ja).sort()).toEqual(englishKeys); + expect(flattenKeys(ptBR).sort()).toEqual(englishKeys); expect(flattenKeys(ru).sort()).toEqual(englishKeys); expect(flattenKeys(zhCN).sort()).toEqual(englishKeys); }); @@ -119,7 +121,9 @@ describe("translation resources", () => { expect(countMatchingEnglishStrings(es)).toBeLessThan(maxFallbackStrings); expect(countMatchingEnglishStrings(fr)).toBeLessThan(maxFallbackStrings); expect(countMatchingEnglishStrings(ja)).toBeLessThan(maxFallbackStrings); + expect(countMatchingEnglishStrings(ptBR)).toBeLessThan(maxFallbackStrings); expect(countMatchingEnglishStrings(ru)).toBeLessThan(maxFallbackStrings); + expect(countMatchingEnglishStrings(zhCN)).toBeLessThan(maxFallbackStrings); }); it("preserves interpolation placeholders in every language", () => { @@ -127,6 +131,7 @@ describe("translation resources", () => { expect(findInterpolationMismatches(es)).toEqual([]); expect(findInterpolationMismatches(fr)).toEqual([]); expect(findInterpolationMismatches(ja)).toEqual([]); + expect(findInterpolationMismatches(ptBR)).toEqual([]); expect(findInterpolationMismatches(ru)).toEqual([]); expect(findInterpolationMismatches(zhCN)).toEqual([]); }); @@ -142,12 +147,14 @@ describe("translation resources", () => { expect(es.modelSelector.modelCountPlural).toBe("{{count}} modelos"); expect(fr.modelSelector.modelCountPlural).toBe("{{count}} modèles"); expect(ja.modelSelector.modelCountPlural).toBe("{{count}}つのモデル"); + expect(ptBR.modelSelector.modelCountPlural).toBe("{{count}} modelos"); expect(ru.modelSelector.modelCountPlural).toBe("{{count}} моделей"); expect(zhCN.modelSelector.modelCountPlural).toBe("{{count}} 个模型"); expect(ar.settings.providers.models.many).toBe("{{count}} نماذج"); expect(es.settings.providers.models.many).toBe("{{count}} modelos"); expect(fr.settings.providers.models.many).toBe("{{count}} modèles"); expect(ja.settings.providers.models.many).toBe("{{count}}つのモデル"); + expect(ptBR.settings.providers.models.many).toBe("{{count}} modelos"); expect(ru.settings.providers.models.many).toBe("{{count}} моделей"); expect(zhCN.settings.providers.models.many).toBe("{{count}} 个 Model"); }); diff --git a/packages/app/src/i18n/resources/ar.ts b/packages/app/src/i18n/resources/ar.ts index 3366f3469..5b838f613 100644 --- a/packages/app/src/i18n/resources/ar.ts +++ b/packages/app/src/i18n/resources/ar.ts @@ -1439,6 +1439,7 @@ export const ar: TranslationResources = { es: "Español", fr: "Français", ja: "日本語", + ptBR: "Português brasileiro", ru: "Русский", zhCN: "中文", }, diff --git a/packages/app/src/i18n/resources/en.ts b/packages/app/src/i18n/resources/en.ts index da31267a4..4b907c4c5 100644 --- a/packages/app/src/i18n/resources/en.ts +++ b/packages/app/src/i18n/resources/en.ts @@ -1445,6 +1445,7 @@ export const en = { es: "Spanish", fr: "French", ja: "Japanese", + ptBR: "Brazilian Portuguese", ru: "Russian", zhCN: "Simplified Chinese", }, diff --git a/packages/app/src/i18n/resources/es.ts b/packages/app/src/i18n/resources/es.ts index b129b73b6..41c094898 100644 --- a/packages/app/src/i18n/resources/es.ts +++ b/packages/app/src/i18n/resources/es.ts @@ -1476,6 +1476,7 @@ export const es: TranslationResources = { es: "Español", fr: "Français", ja: "日本語", + ptBR: "Português brasileiro", ru: "Русский", zhCN: "中文", }, diff --git a/packages/app/src/i18n/resources/fr.ts b/packages/app/src/i18n/resources/fr.ts index 922375a23..2c7c07ed3 100644 --- a/packages/app/src/i18n/resources/fr.ts +++ b/packages/app/src/i18n/resources/fr.ts @@ -1479,6 +1479,7 @@ export const fr: TranslationResources = { es: "Español", fr: "Français", ja: "日本語", + ptBR: "Português brasileiro", ru: "Русский", zhCN: "中文", }, diff --git a/packages/app/src/i18n/resources/ja.ts b/packages/app/src/i18n/resources/ja.ts index b7522d87a..1f436fa9b 100644 --- a/packages/app/src/i18n/resources/ja.ts +++ b/packages/app/src/i18n/resources/ja.ts @@ -1454,6 +1454,7 @@ export const ja: TranslationResources = { es: "スペイン語", fr: "フランス語", ja: "日本語", + ptBR: "ブラジルポルトガル語", ru: "ロシア語", zhCN: "簡体字中国語", }, diff --git a/packages/app/src/i18n/resources/pt-BR.ts b/packages/app/src/i18n/resources/pt-BR.ts new file mode 100644 index 000000000..207e74ee1 --- /dev/null +++ b/packages/app/src/i18n/resources/pt-BR.ts @@ -0,0 +1,1915 @@ +import type { TranslationResources } from "./en"; + +export const ptBR: TranslationResources = { + common: { + back: "Voltar", + loading: "Carregando...", + actions: { + back: "Voltar", + cancel: "Cancelar", + close: "Fechar", + copy: "Copiar", + dismiss: "Dispensar", + retry: "Tentar novamente", + search: "Buscar", + select: "Selecionar", + }, + placeholders: { + search: "Buscar...", + }, + empty: { + noResults: "Nenhum resultado encontrado", + noOptionsMatchSearch: "Nenhuma opção corresponde à sua busca.", + }, + states: { + loading: "Carregando...", + starting: "Iniciando...", + copied: "Copiado", + copiedLabel: "{{label}} copiado", + downloadComplete: "Download concluído", + downloadFailed: "Falha no download", + }, + errors: { + error: "Erro", + unableToSave: "Não foi possível salvar", + nameRequired: "O nome é obrigatório", + daemonUnavailable: "Daemon indisponível", + daemonClientUnavailable: "Cliente do daemon indisponível", + daemonClientDisconnected: "O cliente do daemon está desconectado", + noFileFound: "Nenhum arquivo encontrado para {{token}}", + unexpectedDictationError: "Ocorreu um erro inesperado ao processar o ditado.", + }, + connectionStatus: { + online: "Online", + connecting: "Conectando", + offline: "Offline", + error: "Erro", + idle: "Ocioso", + }, + }, + shell: { + menu: { + toggleSidebar: "Alternar barra lateral", + open: "Abrir menu", + close: "Fechar menu", + }, + commandCenter: { + placeholder: "Digite um comando ou busque agentes...", + noMatches: "Nenhuma correspondência", + actions: "Ações", + agents: "Agentes", + newAgent: "Novo agente", + openProject: "Abrir projeto", + home: "Início", + }, + }, + composer: { + placeholders: { + desktop: "Envie uma mensagem ao agente, marque @files ou use /commands e /skills", + mobile: "Mensagem, @files, /commands", + fallback: "Mensagem...", + }, + input: { + accessibilityLabel: "Enviar mensagem ao agente...", + focusHint: "{{shortcut}} para focar", + addAttachment: "Adicionar anexo", + interruptAgent: "Interromper agente", + queueMessage: "Enfileirar mensagem", + sendAndInterrupt: "Enviar e interromper", + sendMessage: "Enviar mensagem", + queue: "Fila", + send: "Enviar", + }, + cancel: { + cancelingAgent: "Cancelando agente", + stopAgent: "Parar agente", + interrupt: "Interromper", + }, + voice: { + enableVoiceMode: "Ativar modo de voz", + voiceMode: "Modo de voz", + unmuteVoiceMode: "Ativar som do modo de voz", + muteVoiceMode: "Silenciar modo de voz", + stopDictation: "Parar ditado", + startDictation: "Iniciar ditado", + unmuteVoice: "Ativar som da voz", + muteVoice: "Silenciar voz", + dictation: "Ditado", + interruptBeforeVoice: "Interrompa o agente antes de iniciar o modo de voz", + }, + attachments: { + addImage: "Adicionar imagem", + addFile: "Enviar arquivo", + addIssueOrPr: "Adicionar issue ou PR", + dropImagesHere: "Solte imagens aqui", + dropFilesHere: "Solte arquivos aqui", + editQueuedMessage: "Editar mensagem na fila", + sendQueuedMessageNow: "Enviar mensagem da fila agora", + openImage: "Abrir anexo de imagem", + removeImage: "Remover anexo de imagem", + removeFile: "Remover anexo de arquivo", + openGithub: "Abrir {{kind}} #{{number}}", + removeGithub: "Remover {{kind}} #{{number}}", + element: "Elemento", + openBrowserElement: "Abrir anexo de elemento do navegador", + removeBrowserElement: "Remover anexo de elemento do navegador", + openReview: "Abrir anexo de revisão", + removeReview: "Remover anexo de revisão", + }, + errors: { + failedToSend: "Falha ao enviar mensagem", + failedToCreateAgent: "Falha ao criar agente", + noHostSelected: "Nenhum host selecionado", + initialPromptRequired: "O prompt inicial é obrigatório", + alreadyLoading: "Já está carregando", + uploadFailed: "Falha ao enviar arquivo", + fileTooLarge: "{{fileName}} é grande demais (máximo {{size}})", + }, + clientCommands: { + archiveAgent: "Arquivar o agente atual", + freshDraft: "Arquivar este agente e iniciar um novo rascunho", + }, + github: { + searching: "Buscando...", + noResults: "Nenhum resultado encontrado.", + searchPlaceholder: "Buscar issues e PRs...", + title: "Anexar issue ou PR", + }, + }, + agentControls: { + provider: { + fallback: "Provedor", + select: "Selecionar provedor do agente", + }, + thinking: { + title: "Raciocínio", + unknown: "Desconhecido", + extraHigh: "Extra alto", + select: "Selecionar opção de raciocínio", + selectWithValue: "Selecionar opção de raciocínio ({{value}})", + }, + model: { + unknown: "Modelo desconhecido", + }, + features: { + title: "Recursos", + open: "Abrir recursos do agente", + on: "Ativado", + off: "Desativado", + }, + mode: { + title: "Modo", + searchPlaceholder: "Buscar modos...", + selectWithValue: "Selecionar modo do agente ({{value}})", + }, + hints: { + thinking: "Modo de raciocínio", + model: "Alterar modelo", + mode: "Alterar modo de permissão", + }, + }, + agentStream: { + empty: "Comece a conversar com este agente...", + scrollToBottom: "Rolar para o fim", + permission: { + plan: "Plano", + required: "Permissão necessária", + deny: "Negar", + accept: "Aceitar", + implement: "Implementar", + question: "Como você quer prosseguir?", + proposedPlan: "Plano proposto", + }, + }, + agentPanel: { + states: { + notFound: "Agente não encontrado", + failedToLoad: "Falha ao carregar agente", + reconnecting: "Reconectando...", + archivingTitle: "Arquivando agente...", + archivingSubtitle: "Aguarde enquanto arquivamos este agente.", + }, + unavailable: { + selectedHost: "Host selecionado", + unknownHost: + "Não é possível abrir este agente porque {{serverLabel}} não está configurado neste dispositivo.", + addHost: + "Adicione o host nas Configurações ou abra um agente em um servidor configurado para continuar.", + preparingSession: "Preparando sessão de {{serverLabel}}...", + connecting: "Conectando a {{serverLabel}}...", + showSoon: "Mostraremos este agente em instantes.", + showWhenOnline: "Mostraremos este agente quando o host estiver online.", + reconnectingTo: "Reconectando a {{serverLabel}}...", + showAgainWhenReachable: + "Mostraremos este agente novamente assim que o host estiver acessível.", + }, + archived: { + callout: "Este agente está arquivado", + unarchive: "Desarquivar", + }, + }, + sessions: { + title: "Histórico de agentes", + empty: "Nenhuma sessão ainda", + actions: { + loadMore: "Carregar mais", + }, + }, + agentList: { + fallbackTitle: "Nova sessão", + dateSections: { + recent: "Recentes", + today: "Hoje", + yesterday: "Ontem", + thisWeek: "Esta semana", + thisMonth: "Este mês", + older: "Mais antigas", + }, + status: { + initializing: "Iniciando", + idle: "Ocioso", + running: "Em execução", + error: "Erro", + closed: "Fechada", + }, + badges: { + archived: "Arquivado", + pending: "{{count}} pendente(s)", + attention: "Atenção", + }, + archiveSheet: { + hostOffline: "Host offline", + runningAgent: "Este agente ainda está em execução. Arquivá-lo interromperá o agente.", + archive: "Arquivar", + }, + }, + message: { + actions: { + copyCode: "Copiar código", + copyTurn: "Copiar turno", + copyMessage: "Copiar mensagem", + openFile: "Abrir arquivo", + copied: "Copiado", + }, + attachments: { + dismissImage: "Dispensar imagem", + closeImage: "Fechar imagem", + imageLoadFailed: "Não foi possível carregar a imagem", + imageUnavailable: "Imagem indisponível", + imagePreviewUnavailable: "Prévia da imagem indisponível.", + imagePreviewLoadFailed: "Não foi possível carregar a prévia da imagem.", + review: "Revisão", + commentsOne: "1 comentário", + commentsMany: "{{count}} comentários", + textAttachment: "Anexo de texto", + text: "Texto", + file: "Arquivo", + }, + speak: { + header: "Falou", + }, + activity: { + details: "Detalhes", + }, + dictation: { + start: "Iniciar ditado por voz", + cancel: "Cancelar ditado", + retry: "Tentar ditado novamente", + insert: "Inserir transcrição", + insertAndSend: "Inserir transcrição e enviar", + failed: "Falha no ditado: {{error}}", + failedRetry: "Falha no ditado. Toque para tentar novamente.", + }, + question: { + submit: "Enviar", + next: "Próximo", + answerPlaceholder: "Digite sua resposta...", + otherPlaceholder: "Outro...", + }, + todo: { + title: "Tarefas", + empty: "Nenhuma tarefa ainda.", + }, + compaction: { + loading: "Compactando...", + auto: "Contexto compactado automaticamente", + manual: "Contexto compactado manualmente", + withTokens: "Contexto compactado ({{tokens}}K tokens)", + completed: "Contexto compactado", + }, + }, + importSession: { + title: "Importar sessão", + filters: { + all: "Tudo", + }, + status: { + connectHost: "Conecte-se a um host para importar sessões", + updateHost: "Atualize o host para importar sessões.", + noProviders: "Nenhum provedor importável está ativado.", + loading: "Carregando sessões recentes...", + failedAll: "Não foi possível carregar sessões recentes.", + failedProviders: "Não foi possível carregar sessões de {{providers}}.", + failedImport: "Não foi possível importar a sessão selecionada.", + }, + actions: { + refresh: "Atualizar sessões", + }, + preview: { + untitledSession: "Sessão sem título", + noPrompt: "Nenhuma prévia de prompt", + }, + empty: { + noRecent: "Nenhuma sessão recente para importar.", + alreadyImported: "Todas as sessões recentes já foram importadas.", + noProviderSessions: "Nenhuma sessão de {{provider}} encontrada.", + }, + row: { + importing: "Importando...", + }, + }, + workspace: { + route: { + loading: "Carregando workspace", + restoring: "Restaurando workspace", + restoreFailed: + "Não foi possível restaurar este workspace — o diretório pode ter sido movido ou excluído", + connecting: "Conectando", + hostOffline: "{{hostName}} está offline", + cannotReachHost: "Não é possível acessar {{hostName}}", + hostStatus: "Status do host: {{status}}", + missing: "Workspace não encontrado", + needsHostUpgrade: "Atualize o host para restaurar este workspace", + manageHost: "Gerenciar host", + }, + hoverCard: { + scriptsAccessibility: "Scripts do workspace", + copyPath: "Copiar caminho", + copyBranchName: "Copiar nome da branch", + copied: "Copiado", + }, + fileExplorer: { + sort: { + name: "Nome", + modified: "Modificado", + size: "Tamanho", + }, + context: { + size: "Tamanho", + modified: "Modificado", + copyPath: "Copiar caminho", + download: "Baixar", + }, + actions: { + back: "Voltar", + retry: "Tentar novamente", + refresh: "Atualizar arquivos", + refreshing: "Atualizando arquivos", + hideHiddenFiles: "Ocultar arquivos ocultos", + showHiddenFiles: "Mostrar arquivos ocultos", + }, + empty: { + noFiles: "Nenhum arquivo", + noVisibleFiles: "Nenhum arquivo visível", + }, + states: { + unavailable: "Workspace indisponível", + loading: "Carregando arquivos...", + }, + errors: { + failedToListDirectory: "Falha ao listar diretório", + }, + }, + setup: { + descriptor: { + label: "Configuração", + completed: "Configuração concluída", + failed: "Falha na configuração", + workspace: "Configuração do workspace", + }, + status: { + running: "Em execução", + completed: "Concluído", + failed: "Falhou", + waiting: "Aguardando saída da configuração", + }, + waiting: "Configurando workspace...", + empty: { + noCommands: "Nenhum comando de configuração foi executado para este workspace.", + }, + accessibility: { + noCommands: "Nenhum comando de configuração foi executado para este workspace", + log: "Log de configuração do workspace", + }, + log: { + noOutput: "Nenhuma saída", + }, + }, + browser: { + unavailable: { + title: "O navegador é exclusivo do desktop", + subtitle: "Abra este workspace no Electron para usar o navegador integrado.", + }, + session: "Sessão do navegador {{browserId}}", + controls: { + back: "Voltar", + forward: "Avançar", + stopLoading: "Parar carregamento", + refresh: "Atualizar", + browserUrl: "URL do navegador", + enterUrl: "Inserir URL", + openDevTools: "Abrir ferramentas de desenvolvedor do navegador", + cancelSelector: "Cancelar seletor de elemento", + selectElement: "Selecionar elemento", + }, + errors: { + failedToLoad: "Falha ao carregar página", + invalidUrl: "URL de navegador inválida", + unsupportedProtocol: "URL de navegador sem suporte bloqueada: {{protocol}}", + }, + }, + terminal: { + hostDisconnected: "Host não está conectado", + unableToSubscribe: "Não foi possível assinar o terminal", + }, + tabs: { + loading: "Carregando...", + loadingAgentTitle: "Carregando título do agente", + emptyPane: "Nenhuma aba neste painel.", + fallback: { + newAgent: "Novo agente", + setup: "Configuração", + workspaceSetup: "Configuração do workspace", + terminal: "Terminal", + browser: "Navegador", + agent: "Agente", + workspace: "Workspace", + }, + switcher: { + trigger: "Alternar abas ({{count}} abertas)", + title: "Alternar aba", + searchPlaceholder: "Buscar abas", + }, + menu: { + openFor: "Abrir menu de {{label}}", + copyResumeCommand: "Copiar comando de retomada", + copyAgentId: "Copiar ID do agente", + copyFilePath: "Copiar caminho do arquivo", + rename: "Renomear", + closeAbove: "Fechar abas acima", + closeBelow: "Fechar abas abaixo", + closeLeft: "Fechar à esquerda", + closeRight: "Fechar à direita", + closeOthers: "Fechar outras abas", + reloadAgent: "Recarregar agente", + reloadAgentTooltip: "Recarregue o agente para atualizar skills, MCPs ou status de login.", + close: "Fechar", + renameTerminal: "Renomear terminal", + renameAgent: "Renomear agente", + }, + actions: { + newAgent: "Novo agente", + newTerminal: "Novo terminal", + preparingTerminal: "Preparando aba de terminal", + preparingTerminalTooltip: "Preparando terminal...", + newBrowser: "Novo navegador", + splitRight: "Dividir painel à direita", + splitDown: "Dividir painel abaixo", + terminalProfilesMenu: "Perfis de terminal", + editTerminalProfiles: "Editar perfis…", + pinTarget: "Fixar", + unpinTarget: "Desafixar", + }, + explorer: { + open: "Abrir explorador", + close: "Fechar explorador", + toggle: "Alternar explorador", + changes: "Alterações", + files: "Arquivos", + }, + toasts: { + copyFailed: "Falha ao copiar", + agentIdCopiedLabel: "ID do agente", + resumeCommandCopiedLabel: "comando de retomada", + filePathCopiedLabel: "Caminho do arquivo", + resumeIdUnavailable: "ID de retomada indisponível", + resumeCommandUnavailable: "Comando de retomada indisponível", + reloadingAgent: "Recarregando agente...", + reloadedAgent: "Agente recarregado", + failedToReloadAgent: "Falha ao recarregar agente", + }, + confirmations: { + close: "Fechar", + cancel: "Cancelar", + archive: "Arquivar", + closeTerminalTitle: "Fechar terminal?", + closeTerminalMessage: + "Qualquer processo em execução neste terminal será interrompido imediatamente.", + archiveRunningAgentTitle: "Arquivar agente em execução?", + archiveRunningAgentMessage: + "Este agente ainda está em execução. Arquivá-lo interromperá o agente e fechará a aba.", + closeTabsLeftTitle: "Fechar abas à esquerda?", + closeTabsRightTitle: "Fechar abas à direita?", + closeOtherTabsTitle: "Fechar outras abas?", + bulk: { + all: "Isso vai arquivar {{agents}} agente(s), fechar {{terminals}} terminal(ais) e fechar {{tabs}} aba(s). Qualquer processo em execução em um terminal fechado será interrompido imediatamente.", + agentsAndTerminals: + "Isso vai arquivar {{agents}} agente(s) e fechar {{terminals}} terminal(ais). Qualquer processo em execução em um terminal fechado será interrompido imediatamente.", + terminalsAndTabs: + "Isso vai fechar {{terminals}} terminal(ais) e fechar {{tabs}} aba(s). Qualquer processo em execução em um terminal fechado será interrompido imediatamente.", + agentsAndTabs: "Isso vai arquivar {{agents}} agente(s) e fechar {{tabs}} aba(s).", + terminals: + "Isso vai fechar {{terminals}} terminal(ais). Qualquer processo em execução em um terminal fechado será interrompido imediatamente.", + tabs: "Isso vai fechar {{tabs}} aba(s).", + agents: "Isso vai arquivar {{agents}} agente(s).", + }, + }, + }, + header: { + actions: { + workspaceActions: "Ações do workspace", + newAgent: "Novo agente", + newTerminal: "Novo terminal", + newBrowser: "Nova aba de navegador", + importSession: "Importar sessão", + copyPath: "Copiar caminho do workspace", + copyBranchName: "Copiar nome da branch", + showSetup: "Mostrar configuração", + }, + toasts: { + workspacePathUnavailable: "O caminho do workspace ainda não está disponível", + branchNameUnavailable: "Nome da branch indisponível", + terminalQueued: "Preparando workspace, abrindo terminal quando estiver pronto...", + workspacePathCopiedLabel: "Caminho do workspace", + branchNameCopiedLabel: "Nome da branch", + }, + }, + scripts: { + title: "Scripts", + actions: { + run: "Executar", + view: "Ver", + }, + accessibility: { + trigger: "Scripts do workspace", + openAt: "Abrir {{scriptName}} em {{label}}", + viewTerminal: "Ver terminal de {{scriptName}}", + runScript: "Executar script {{scriptName}}", + script: "script {{scriptName}}", + }, + states: { + exitCode: "saída {{code}}", + startFailed: "Falha ao iniciar {{scriptName}}", + }, + }, + git: { + actions: { + moreOptions: "Mais opções", + moreActions: "Mais ações", + commit: { + label: "Commit", + pending: "Fazendo commit...", + success: "Commit concluído", + }, + pull: { + label: "Pull", + pending: "Fazendo pull...", + success: "Pull concluído", + }, + push: { + label: "Push", + pending: "Fazendo push...", + success: "Push concluído", + }, + pullAndPush: { + label: "Pull e push", + pending: "Fazendo pull e push...", + success: "Pull e push concluídos", + }, + viewPr: "Ver PR", + createPr: { + label: "Criar PR", + pending: "Criando PR...", + success: "PR criada", + }, + mergeBranch: { + label: "Fazer merge localmente", + pending: "Fazendo merge...", + success: "Mergeada", + }, + mergeFromBase: { + label: "Atualizar a partir de {{baseRef}}", + pending: "Atualizando...", + success: "Atualizado", + }, + archive: { + label: "Arquivar worktree", + pending: "Arquivando...", + success: "Arquivado", + }, + mergePr: { + squash: "Merge", + merge: "Merge", + rebase: "Merge", + pending: "Fazendo merge da PR...", + success: "PR mergeada", + }, + autoMerge: { + enableSquash: "Merge automático", + enableMerge: "Merge automático", + enableRebase: "Merge automático", + enabled: "Merge automático ativado", + enabling: "Ativando merge automático...", + disabling: "Desativando merge automático...", + disabled: "Merge automático desativado", + }, + unavailable: { + viewPrNoGithub: "Ver PR não está disponível agora porque o GitHub não está conectado", + pullNoRemote: + "Pull não está disponível aqui porque esta branch ainda não está conectada a um remoto", + pullDirty: + "Pull não está disponível enquanto há alterações locais. Faça commit ou stash primeiro", + pullUpToDate: "Pull não está disponível porque esta branch já está atualizada", + pushNoRemote: + "Push não está disponível aqui porque esta branch ainda não está conectada a um remoto", + pushBehind: + "Push ainda não está disponível porque há alterações mais recentes para trazer primeiro", + pushNothing: "Push não está disponível porque não há nada novo para enviar", + pullAndPushNoRemote: + "Pull e push não estão disponíveis aqui porque esta branch ainda não está conectada a um remoto", + pullAndPushDirty: + "Pull e push não estão disponíveis enquanto há alterações locais. Faça commit ou stash primeiro", + pullAndPushInSync: + "Pull e push não estão disponíveis porque esta branch já está sincronizada", + createPrNoGithub: "Criar PR não está disponível agora porque o GitHub não está conectado", + createPrNoCommits: + "Criar PR não está disponível porque esta branch ainda não tem commits novos", + mergeNoBase: "Merge não está disponível porque não foi possível determinar a branch base", + mergeDirty: + "Merge não está disponível enquanto há alterações locais. Faça commit ou stash primeiro", + mergeNothing: + "Merge não está disponível porque esta branch ainda não tem nada novo para merge", + updateNoBase: + "Atualizar não está disponível porque não foi possível determinar a branch base", + updateDirty: + "Atualizar não está disponível enquanto há alterações locais. Faça commit ou stash primeiro", + updateCurrent: + "Atualizar não está disponível porque esta branch já está atualizada com {{baseRef}}", + archiveNotWorktree: + "Arquivar não está disponível aqui porque este workspace não foi criado como um worktree do Paseo", + mergePrNoGithub: + "Merge da PR não está disponível agora porque o GitHub não está conectado", + mergePrMissing: "Merge da PR não está disponível porque ainda não há uma pull request", + mergePrDraft: "Merge da PR não está disponível porque a pull request ainda é um rascunho", + mergePrMerged: "Merge da PR não está disponível porque a pull request já foi mergeada", + mergePrClosed: "Merge da PR não está disponível porque a pull request está fechada", + mergePrConflicts: "Merge da PR não está disponível porque a pull request tem conflitos", + mergePrQueue: + "Merge da PR não está disponível aqui porque este repositório usa uma merge queue", + mergePrNotReady: + "Merge da PR não está disponível até o GitHub informar que a pull request está pronta para merge", + autoMergeCannotDisable: + "O merge automático está ativado, mas esta conta não pode desativá-lo", + }, + toasts: { + failedCommit: "Falha ao fazer commit", + failedPull: "Falha ao fazer pull", + failedPush: "Falha ao fazer push", + failedPullAndPush: "Falha ao fazer pull e push", + failedCreatePr: "Falha ao criar PR", + failedMergePr: "Falha ao fazer merge da PR", + failedEnableAutoMerge: "Falha ao ativar merge automático", + failedDisableAutoMerge: "Falha ao desativar merge automático", + baseRefUnavailable: "Ref base indisponível", + failedMerge: "Falha ao fazer merge", + failedMergeFromBase: "Falha ao fazer merge da base", + worktreePathUnavailable: "Caminho do worktree indisponível", + failedArchive: "Falha ao arquivar worktree", + }, + archiveWarning: { + title: 'Arquivar "{{worktreeName}}"?', + confirm: "Arquivar", + cancel: "Cancelar", + uncommittedChanges: "Alterações sem commit", + uncommittedChangesWithDiff: "Alterações sem commit ({{diffStat}})", + addedLine: "{{count}} linha adicionada", + addedLines: "{{count}} linhas adicionadas", + deletedLine: "{{count}} linha removida", + deletedLines: "{{count}} linhas removidas", + unpushedCommit: "{{count}} commit não enviado", + unpushedCommits: "{{count}} commits não enviados", + }, + }, + diff: { + binaryFile: "Arquivo binário", + tooLarge: "Diff grande demais para exibir", + unified: "Diff unificado", + split: "Diff lado a lado", + hideWhitespace: "Ocultar espaços em branco", + scrollLongLines: "Rolar linhas longas", + wrapLongLines: "Quebrar linhas longas", + collapseAll: "Recolher todos os arquivos", + expandAll: "Expandir todos os arquivos", + refreshing: "Atualizando", + refresh: "Atualizar", + refreshState: "Atualizar estado do git e do GitHub", + failedRefresh: "Falha ao atualizar estado do git.", + emptyHiddenWhitespace: "Nenhuma alteração visível após ocultar espaços em branco", + emptyUncommitted: "Nenhuma alteração sem commit", + emptyAgainstBase: "Nenhuma alteração vs {{baseRef}}", + checkingRepository: "Verificando repositório...", + notRepository: "Não é um repositório git", + diffMode: "Modo de diff", + uncommitted: "Sem commit", + committed: "Com commit", + branchUnknown: "Desconhecido", + base: "base", + newFile: "Novo", + deletedFile: "Excluído", + }, + openInEditor: { + open: "Abrir", + chooseEditor: "Escolher editor", + openIn: "Abrir workspace em {{target}}", + openFileIn: "Abrir {{fileName}} em {{target}}", + failedOpen: "Falha ao abrir workspace", + }, + pr: { + actions: { + viewPullRequest: "Ver", + }, + sections: { + checks: "Verificações", + reviews: "Revisões", + }, + accessibility: { + pullRequest: "Pull request #{{number}}", + }, + states: { + draft: "Rascunho", + merged: "Mergeada", + closed: "Fechada", + open: "Aberta", + }, + activity: { + commented: "Comentou", + approved: "Aprovado", + requestedChanges: "Solicitou alterações", + reviewed: "Revisado", + }, + time: { + justNow: "agora mesmo", + }, + errors: { + statusLoadFailed: "Não foi possível carregar o status da pull request", + activityLoadFailed: "Não foi possível carregar a atividade da pull request", + }, + }, + }, + }, + sidebar: { + host: { + noHost: "Nenhum host", + switchTitle: "Trocar host", + searchPlaceholder: "Buscar hosts...", + }, + actions: { + addProject: "Adicionar projeto", + newWorkspace: "Novo workspace", + home: "Início", + settings: "Configurações", + closeSidebar: "Fechar barra lateral", + }, + sections: { + sessions: "Histórico", + }, + worktreeSetup: { + title: "Configurar scripts de worktree", + description: + "Adicione comandos de configuração para que novos worktrees instalem dependências e se preparem automaticamente.", + openProjectSettings: "Abrir configurações do projeto", + }, + project: { + actions: { + menu: "Ações do projeto", + openSettings: "Abrir configurações do projeto", + openNewWindow: "Abrir em nova janela", + openNewWindowFailed: "Não foi possível abrir uma nova janela", + remove: "Remover projeto", + removing: "Removendo...", + }, + confirmations: { + removeTitle: "Remover projeto?", + removeMessage: + 'Remover "{{projectName}}" da barra lateral?\n\nOs arquivos no disco não serão alterados.', + removeConfirm: "Remover", + cancel: "Cancelar", + }, + toasts: { + hostDisconnected: "Host não está conectado", + removeFailed: "Falha ao remover alguns workspaces", + updateHostToRemove: "Atualize o host para remover projetos.", + }, + empty: { + title: "Nenhum projeto ainda", + description: "Adicione um projeto para começar", + }, + }, + workspace: { + status: { + scriptsAvailable: "Scripts disponíveis", + creating: "Criando...", + }, + actions: { + menu: "Ações do workspace", + newWorkspace: "Novo workspace", + createWorkspaceFor: "Criar um novo workspace para {{projectName}}", + copyPath: "Copiar caminho", + copyBranchName: "Copiar nome da branch", + rename: "Renomear workspace", + archive: "Arquivar", + archiveWorktree: "Arquivar worktree", + hideFromSidebar: "Ocultar da barra lateral", + archiving: "Arquivando...", + hiding: "Ocultando...", + }, + confirmations: { + hideTitle: "Ocultar workspace?", + hideMessage: + 'Ocultar "{{workspaceName}}" da barra lateral?\n\nOs arquivos no disco não serão alterados.', + hideConfirm: "Ocultar", + cancel: "Cancelar", + }, + rename: { + title: "Renomear workspace", + submit: "Renomear", + invalidBranchName: "Nome de branch inválido", + }, + toasts: { + workspacePathUnavailable: "Caminho do workspace indisponível", + pathCopied: "Caminho copiado", + branchNameCopied: "Nome da branch copiado", + hostDisconnected: "Host não está conectado", + hideFailed: "Falha ao ocultar workspace", + archiveFailed: "Falha ao arquivar worktree", + }, + }, + }, + newWorkspace: { + title: "Novo workspace", + create: "Criar", + isolation: { + local: "Local", + worktree: "Novo worktree", + label: "Isolamento", + }, + fields: { + project: "Projeto", + base: "Base", + baseNotApplicable: "Não aplicável", + }, + titlePlaceholder: "Título (opcional)", + errors: { + hostDisconnected: "Host não está conectado", + createWorktreeFailed: "Falha ao criar worktree", + composerStateRequired: "O estado do composer é obrigatório", + selectModel: "Selecione um modelo", + }, + refPicker: { + startingRef: "Ref inicial", + chooseStart: "Escolha de onde começar", + checkoutHint: "Fazer checkout da PR #{{number}}?", + checkoutPr: "Fazer checkout da PR #{{number}}", + dismissCheckoutHint: "Dispensar dica de checkout da PR #{{number}}", + intoBase: "em {{baseRef}}", + searching: "Buscando...", + noMatchingRefs: "Nenhuma ref correspondente.", + searchPlaceholder: "Buscar branches e PRs", + title: "Começar de", + }, + }, + desktop: { + quitting: { + title: "Saindo do Paseo...", + detail: "Parando o daemon local.", + }, + daemon: { + title: "Daemon", + status: { + title: "Status", + builtInOnly: "Apenas o daemon desktop integrado é exibido aqui", + running: "em execução", + notRunning: "não está em execução", + pid: "PID {{pid}}", + }, + management: { + title: "Gerenciar daemon integrado", + hint: "Permitir que o Paseo inicie e pare o daemon integrado", + pauseTitle: "Pausar daemon integrado", + pauseMessage: + "Isso vai parar o daemon integrado imediatamente. Agentes e terminais em execução conectados ao daemon integrado serão interrompidos.", + pauseAndStop: "Pausar e parar", + registrationFailed: + "O daemon integrado foi iniciado, mas o Paseo não conseguiu salvar a conexão localhost. Desative e reative o gerenciamento do daemon ou adicione localhost manualmente.", + pausedStopFailed: + "O gerenciamento do daemon integrado foi pausado, mas o Paseo não conseguiu parar o daemon.", + updateFailed: "Não foi possível atualizar o gerenciamento do daemon integrado.", + }, + keepRunning: { + title: "Manter daemon em execução ao sair", + hint: "O daemon continua em execução quando você sai do Paseo", + }, + logs: { + title: "Arquivo de log", + modalTitle: "Logs do daemon", + unavailable: "Caminho do log indisponível", + empty: "(arquivo de log vazio)", + copied: "Caminho do log copiado.", + copyFailed: "Não foi possível copiar o caminho do log.", + open: "Abrir logs", + copyPath: "Copiar caminho", + }, + fullStatus: { + title: "Status completo", + modalTitle: "Status do daemon", + hint: "Executa `paseo daemon status` e mostra a saída", + view: "Ver status", + copied: "Status copiado para a área de transferência.", + fetchFailed: "Falha ao buscar status do daemon: {{message}}", + }, + advancedSettings: "Configurações avançadas", + openAdvancedSettings: "Abrir configurações avançadas do daemon", + versionMismatch: + "As versões do app e do daemon não coincidem. Atualize ambos para a mesma versão para a melhor experiência.", + loadFailed: "Não foi possível carregar o status do daemon desktop.", + }, + updates: { + status: { + checking: "Verificando atualizações do app...", + installing: "Instalando atualização do app...", + upToDate: "O app está atualizado.", + upToDateWithLastChecked: "Atualizado. Última verificação às {{time}}.", + pending: "Avisaremos quando a atualização estiver pronta.", + availableWithVersion: "Atualização pronta: {{version}}", + available: "Há uma atualização do app pronta para instalar.", + installed: "Atualização do app instalada. Reinicialização obrigatória.", + failed: "Falha ao atualizar o app.", + idle: "O status de atualização ainda não foi verificado.", + }, + installError: "Não foi possível instalar a atualização do app desktop.", + callout: { + installingTitle: "Instalando atualização", + failedTitle: "Falha na atualização", + availableTitle: "Atualização disponível", + genericError: "Algo deu errado.", + whatsNew: "Novidades", + installingAction: "Instalando...", + installAndRestart: "Instalar e reiniciar", + installingDescription: "Instalando e reiniciando...", + versionReady: "{{version}} está pronta para instalar.", + newVersionReady: "Uma nova versão está pronta para instalar.", + restartWarning: + "Atualizar o app vai interromper agentes em execução e fechar sessões de terminal.", + }, + }, + settings: { + loadFailed: "Não foi possível carregar as configurações do desktop.", + saveFailed: "Não foi possível salvar as configurações do desktop.", + }, + rosetta: { + title: "Baixar a build para Apple Silicon", + runningIntel: "Você está executando a build Intel do Paseo pelo Rosetta no Apple Silicon.", + highCpu: "Isso causa alto uso de CPU. Baixe a build para Apple Silicon para corrigir.", + download: "Baixar", + }, + permissions: { + notifications: { + allowed: "As notificações são permitidas pelo sistema operacional.", + denied: "As notificações estão negadas nas configurações do sistema.", + notGranted: "As notificações ainda não foram concedidas.", + webOnly: "O status de notificações desktop só está disponível no runtime web.", + supported: "Notificações desktop são compatíveis.", + unsupported: "Notificações desktop não são compatíveis nesta plataforma.", + apiUnavailable: "A Web Notification API não está disponível neste ambiente.", + requestsWebOnly: "Solicitações de notificação desktop só estão disponíveis no runtime web.", + requestUnavailable: "Web Notification API requestPermission() está indisponível.", + requestFailed: "Falha ao solicitar permissão de notificação: {{message}}", + unexpectedState: "Estado inesperado de permissão de notificação: {{state}}", + }, + microphone: { + webOnly: "O status do microfone desktop só está disponível no runtime web.", + navigatorUnavailable: "Navigator não está disponível neste ambiente.", + granted: "O acesso ao microfone foi concedido.", + denied: "O acesso ao microfone está negado nas configurações do sistema.", + notGranted: "A permissão do microfone ainda não foi concedida.", + unexpectedState: "Estado inesperado de permissão do microfone: {{state}}", + statusApiUnavailable: + "A API de status do microfone não está disponível neste runtime. Use Solicitar para verificar o acesso.", + queryFailed: "Falha ao consultar o status do microfone: {{message}}", + captureUnavailable: "A captura do microfone não está disponível neste ambiente.", + permissionApiUnavailable: + "A API de status de permissão não está disponível. Use Solicitar para verificar o acesso.", + requestsWebOnly: "Solicitações de microfone desktop só estão disponíveis no runtime web.", + captureApiUnavailable: "A API de captura do microfone não está disponível neste ambiente.", + requestDenied: "A permissão do microfone foi negada pelo usuário ou pelo sistema.", + noDevice: "Nenhum dispositivo de microfone foi encontrado.", + requestFailed: "Falha ao solicitar permissão do microfone: {{message}}", + }, + empty: { + notifications: "O status das notificações ainda não foi verificado.", + microphone: "O status do microfone ainda não foi verificado.", + }, + testNotification: { + title: "Teste de notificação do Paseo", + body: "Se você consegue ver isto, as notificações desktop funcionam.", + notDelivered: + "A notificação não foi entregue. Verifique Ajustes do Sistema > Notificações.", + failed: "Falha ao enviar notificação.", + }, + }, + integrations: { + cli: { + statusFailed: "Não foi possível verificar o status de instalação da CLI.", + installFailed: "Não foi possível instalar a CLI do Paseo.", + }, + skills: { + statusFailed: "Não foi possível verificar o status das skills de orquestração.", + installFailed: "Não foi possível instalar as skills de orquestração.", + updateFailed: "Não foi possível atualizar as skills de orquestração.", + uninstallFailed: "Não foi possível desinstalar as skills de orquestração.", + }, + }, + }, + startup: { + errorTitle: "Algo deu errado", + errorDescription: + "O servidor local falhou ao iniciar. Se isso continuar acontecendo, reporte o problema no GitHub e inclua os logs abaixo.", + logs: { + loading: "Carregando logs do daemon...", + unavailable: "Nenhum log do daemon disponível.", + loadFailed: "Não foi possível carregar logs do daemon: {{message}}", + }, + }, + openProject: { + tiles: { + addProject: { + title: "Adicionar um projeto", + description: "Abra uma pasta na sua máquina", + }, + importSession: { + title: "Importar sessão", + description: "Importe sessões externas recentes da CLI", + }, + setupProviders: { + title: "Configurar provedores", + description: "Configure Claude Code, Codex e outros", + }, + pairDevice: { + title: "Parear dispositivo", + description: "Conecte seu celular a este daemon", + }, + }, + }, + projectPicker: { + placeholder: "Digite um caminho de diretório...", + opening: "Abrindo projeto...", + empty: "Comece digitando um caminho", + errors: { + directory_not_found: "Diretório não encontrado.", + open_failed: "Não foi possível abrir o projeto.", + }, + openPath: "Abrir caminho", + }, + branchSwitcher: { + currentBranch: "Branch atual: {{branchName}}. Pressione para trocar de branch.", + placeholder: "Trocar branch...", + searchPlaceholder: "Filtrar branches...", + empty: "Nenhuma branch encontrada.", + title: "Trocar branch", + uncommittedTitle: "Alterações sem commit", + uncommittedMessage: "Você tem alterações sem commit. Fazer stash antes de trocar de branch?", + stashAndSwitch: "Fazer stash e trocar", + failedToStash: "Falha ao fazer stash das alterações", + failedToSwitch: "Falha ao trocar de branch", + restoreStashTitle: "Restaurar alterações em stash?", + restoreStashMessage: + "Esta branch tem alterações em stash de uma sessão anterior. Deseja restaurá-las?", + restore: "Restaurar", + later: "Depois", + stashRestored: "Alterações em stash restauradas", + }, + agentAutocomplete: { + searchingWorkspace: "Buscando workspace...", + loadingCommands: "Carregando comandos...", + noFiles: "Nenhum arquivo ou diretório encontrado", + noCommands: "Nenhum comando encontrado", + failedToLoad: "Falha ao carregar", + }, + loadOlderHistory: { + failed: "Não foi possível carregar o histórico mais antigo", + }, + imageAttachmentPicker: { + permissionTitle: "Permissão obrigatória", + permissionMessage: "Permita acesso à sua biblioteca de fotos para anexar imagens.", + errorTitle: "Erro", + failedToSelect: "Falha ao selecionar imagem", + dialogTitle: "Anexar imagens", + dialogFilterName: "Imagens", + }, + workspaceSetup: { + title: "Criar workspace", + errors: { + failedCreateWorktree: "Falha ao criar worktree", + failedOpenProject: "Falha ao abrir projeto", + selectModel: "Selecione um modelo", + hostDisconnected: "Host não está conectado", + pendingRequired: "Nenhuma configuração de workspace está pendente", + composerStateRequired: "O estado do composer da configuração do workspace é obrigatório", + }, + }, + onboarding: { + title: "Bem-vindo ao Paseo", + subtitle: "Conecte seu computador para começar", + actions: { + settings: "Configurações", + }, + }, + modelSelector: { + title: "Selecionar provedor", + selectModel: "Selecionar modelo", + selectedModel: "Selecionar modelo ({{model}})", + loading: "Carregando...", + loadingShort: "Carregando", + loadingSelector: "Carregando seletor de modelos...", + error: "Erro", + defaultModel: "Padrão", + favorites: "Favoritos", + favoriteModel: "Favoritar modelo", + unfavoriteModel: "Remover modelo dos favoritos", + modelCount: "{{count}} modelo", + modelCountPlural: "{{count}} modelos", + retry: "Tentar novamente", + retrying: "Tentando novamente...", + noMatches: "Nenhum modelo corresponde à sua busca", + searchPlaceholder: "Buscar modelos...", + openProviderSettings: "Abrir configurações de {{provider}}", + }, + providerCatalog: { + title: "Adicionar provedor", + search: "Buscar provedores", + noProviders: "Nenhum provedor encontrado", + actions: { + add: "Adicionar", + adding: "Adicionando", + installed: "Instalado", + cancel: "Cancelar", + installInstructions: "Instruções de instalação", + installInstructionsFor: "Instruções de instalação de {{provider}}", + }, + errors: { + unableToInstall: "Não foi possível instalar provedor", + }, + }, + providerSelection: { + defaultModel: "Padrão", + selectModel: "Selecionar modelo", + loading: "Carregando...", + error: "Erro", + unavailable: "Indisponível", + unknownError: "Erro desconhecido", + readiness: { + initialPromptRequired: "O prompt inicial é obrigatório", + noProviders: "Nenhum provedor disponível no host selecionado", + modelDefaultsLoading: "Os modelos padrão ainda estão carregando", + noModelAvailable: "Nenhum modelo disponível para o provedor selecionado", + workspaceDirectoryNotFound: "Diretório do workspace não encontrado", + hostDisconnected: "Host não está conectado", + }, + }, + pairing: { + connectionMethods: { + title: "Adicionar conexão", + direct: { + title: "Conexão direta", + description: "Rede local ou VPN.", + }, + scanQr: { + title: "Escanear QR code", + description: "Conexão relay criptografada.", + }, + pasteLink: { + title: "Colar link de pareamento", + description: "Conexão relay criptografada.", + }, + }, + direct: { + title: "Conexão direta", + helper: "Informe o endereço de um servidor Paseo.", + fields: { + host: "Host", + port: "Porta", + password: "Senha", + optional: "Opcional", + useSsl: "Usar SSL", + connectionUri: "URI da conexão", + }, + advanced: { + label: "Avançado", + show: "Mostrar avançado", + hide: "Ocultar avançado", + }, + passwordVisibility: { + show: "Mostrar senha", + hide: "Ocultar senha", + }, + actions: { + cancel: "Cancelar", + connect: "Conectar", + connecting: "Conectando...", + }, + errors: { + hostRequired: "Host é obrigatório", + invalidPort: "A porta deve estar entre 1 e 65535", + invalidConnection: "Conexão inválida", + failedTitle: "Falha na conexão", + failedToConnect: "Não foi possível conectar a {{endpoint}}.", + noAdditionalDetails: "{{detail}} (nenhum detalhe adicional fornecido)", + timedOut: "A conexão expirou. Verifique o host/porta e sua rede.", + refused: "Conexão recusada. O servidor está em execução neste endereço?", + hostNotFound: "Host não encontrado. Verifique o hostname e tente novamente.", + hostUnreachable: "Host inacessível. Verifique sua rede e firewall.", + tlsError: + "Erro de TLS. Conexões diretas usam SSL apenas quando há um terminador TLS na frente do daemon.", + unableToConnect: + "Não foi possível conectar. Verifique o host/porta e se o daemon está acessível.", + details: "Detalhes: {{detail}}", + }, + }, + link: { + title: "Colar link de pareamento", + helper: "Cole o link de pareamento do seu servidor.", + label: "Link de pareamento", + errors: { + required: "Cole um link de pareamento (.../#offer=...)", + missingOffer: "O link deve incluir #offer=...", + emptyOffer: "O payload da oferta está vazio", + invalid: "Link de pareamento inválido", + unableToPair: "Não foi possível parear host", + }, + alert: { + failedTitle: "Falha no pareamento", + }, + actions: { + cancel: "Cancelar", + pair: "Parear", + pairing: "Pareando...", + }, + }, + scan: { + title: "Escanear QR", + webUnavailableTitle: "Indisponível na web", + webUnavailableBody: + 'Escanear QR não é compatível com a build web. Use "Colar link" em vez disso.', + backToSettings: "Voltar para Configurações", + cameraPermissionTitle: "Permissão da câmera", + cameraPermissionBody: + "Permita acesso à câmera para escanear o QR code de pareamento do daemon.", + grantPermission: "Conceder permissão", + pairing: "Pareando...", + unableToPair: "Não foi possível parear host", + errorTitle: "Erro", + }, + device: { + loadingOffer: "Carregando oferta de pareamento...", + failedToLoadOffer: "Falha ao carregar oferta de pareamento.", + relayDisabled: "Relay não está ativado. Ative o relay para parear um dispositivo.", + unavailable: "Oferta de pareamento indisponível.", + hint: "Escaneie este QR code com o Paseo no seu celular ou copie o link abaixo.", + qrUnavailable: "QR code indisponível.", + retry: "Tentar novamente", + copy: "Copiar", + copied: "Copiado", + }, + }, + realtimeVoice: { + actions: { + mute: "Silenciar voz em tempo real", + unmute: "Ativar voz em tempo real", + stop: "Parar voz em tempo real e interromper a resposta", + }, + }, + rewind: { + tooltip: "Voltar para esta mensagem", + warning: "Esta ação não pode ser desfeita", + actions: { + conversation: "Reverter conversa", + files: "Reverter arquivos", + both: "Reverter conversa e arquivos", + }, + errors: { + failed: "Falha ao reverter agente", + }, + }, + diffViewer: { + empty: "Nenhuma alteração para exibir", + }, + serviceUrl: { + title: "Abrir URL do serviço", + message: "Abrir {{url}}?", + inPaseo: "No Paseo", + externalBrowser: "Navegador externo", + dontAskAgain: "Não perguntar novamente", + }, + downloads: { + requestTokenFailed: "Falha ao solicitar token de download.", + hostUnavailable: "Host de download indisponível.", + cancelled: "Download cancelado.", + failed: "Falha ao baixar arquivo.", + shareFile: "Compartilhar arquivo", + shareFileNamed: "Compartilhar {{fileName}}", + }, + menu: { + backdrop: "Fundo do menu", + }, + subagents: { + detachAction: "Desanexar {{label}}", + detachTooltip: "Desanexar subagente", + archiveAction: "Arquivar {{label}}", + archiveTooltip: "Arquivar subagente", + }, + panels: { + draft: { + newAgent: "Novo Agente", + creatingAgent: "Criando agente", + }, + file: { + directoryMissing: "Diretório do workspace não encontrado.", + loading: "Carregando arquivo...", + noPreview: "Nenhuma prévia disponível", + binaryPreviewUnavailable: "Prévia binária indisponível", + failedToLoad: "Falha ao carregar arquivo", + failedToLoadPreview: "Falha ao carregar prévia do arquivo", + }, + }, + toolCallDetails: { + error: "Erro", + empty: "Nenhum detalhe adicional disponível", + subAgentActivity: "Atividade do subagente", + input: "Entrada", + output: "Saída", + }, + renameModal: { + rename: "Renomear", + saving: "Salvando...", + }, + sidebarCallout: { + dismiss: "Dispensar", + }, + contextWindow: { + title: "Janela de contexto", + used: "{{percentage}}% usado", + tokens: "{{used}} / {{max}} tokens", + sessionCost: "Custo da sessão {{cost}}", + accessibility: "Janela de contexto {{percentage}}% usada", + }, + review: { + comment: { + add: "Adicionar comentário de revisão", + edit: "Editar comentário de revisão", + delete: "Excluir comentário de revisão", + label: "Comentário de revisão", + placeholder: "Deixe um comentário", + cancel: "Cancelar", + cancelAccessibility: "Cancelar comentário de revisão", + save: "Comentar", + saveAccessibility: "Salvar comentário de revisão", + }, + }, + settings: { + title: "Configurações", + loading: "Carregando configurações...", + groups: { + app: "App", + host: "Host", + }, + hostPicker: { + switchHost: "Trocar host", + local: "Local", + }, + backToWorkspace: "Voltar", + addHost: "Adicionar host", + projects: "Projetos", + projectList: { + hostLoadFailed: "Não foi possível carregar projetos do host {{hostName}}: {{message}}", + editProject: "Editar {{projectName}}", + }, + groupInfo: "Sobre {{title}}", + sections: { + general: "Geral", + daemon: "Daemon", + appearance: "Aparência", + shortcuts: "Atalhos", + integrations: "Integrações", + permissions: "Permissões", + diagnostics: "Diagnósticos", + about: "Sobre", + }, + hostSections: { + connections: "Conexões", + agents: "Agentes", + workspaces: "Workspaces", + providers: "Provedores", + usage: "Uso", + terminals: "Terminais", + host: "Host", + }, + general: { + title: "Geral", + defaultSend: { + label: "Envio padrão", + descriptions: { + interrupt: + "Quando o agente está em execução, Enter interrompe. Command/Ctrl+Enter enfileira.", + queue: "Quando o agente está em execução, Enter enfileira. Command/Ctrl+Enter envia.", + }, + options: { + interrupt: "Interromper", + queue: "Fila", + }, + }, + serviceUrls: { + label: "URLs de serviço", + description: "Onde abrir URLs de scripts em execução", + options: { + ask: "Perguntar", + inApp: "No Paseo", + external: "Navegador externo", + }, + }, + terminalScrollback: { + label: "Scrollback do terminal", + description: "Linhas mantidas no buffer do terminal integrado", + accessibilityLabel: "Linhas do scrollback do terminal", + }, + language: { + label: "Idioma", + description: "Idioma do app", + options: { + system: "Sistema", + ar: "Árabe", + en: "Inglês", + es: "Espanhol", + fr: "Francês", + ja: "Japonês", + ptBR: "Português brasileiro", + ru: "Russo", + zhCN: "Chinês simplificado", + }, + }, + }, + diagnostics: { + title: "Diagnósticos", + testAudio: "Testar áudio", + playTest: "Reproduzir teste", + playing: "Reproduzindo...", + playbackFailed: "Falha na reprodução: {{message}}", + }, + about: { + title: "Sobre", + appVersion: "Versão do app", + thisDevice: "Este dispositivo", + connectedHosts: "Hosts conectados", + offline: "Offline", + versionDiffers: "A versão é diferente da deste dispositivo", + releaseChannel: { + label: "Canal de lançamento", + description: "Troque para Beta para receber atualizações antes e ajudar a moldá-las", + stable: "Stable", + beta: "Beta", + }, + updates: { + label: "Atualizações do app", + readyToInstall: "Pronta para instalar: {{version}}", + installTitle: "Instalar atualização desktop", + installMessage: "Isso atualiza o Paseo neste computador", + installConfirm: "Instalar atualização", + update: "Atualizar", + updateTo: "Atualizar para {{version}}", + installing: "Instalando...", + check: "Verificar", + checking: "Verificando...", + alertTitle: "Erro", + alertMessage: "Não foi possível abrir o diálogo de confirmação da atualização.", + }, + }, + appearance: { + theme: { + title: "Tema", + accessibilityLabel: "Tema: {{value}}", + options: { + light: "Claro", + dark: "Escuro", + zinc: "Zinc", + midnight: "Midnight", + claude: "Claude", + ghostty: "Ghostty", + auto: "Sistema", + }, + }, + fonts: { + title: "Fontes", + systemDefault: "Sistema padrão", + interfaceFont: "Fonte da interface", + interfaceFontHint: "Usada em todo o app. Deixe vazio para usar o padrão do sistema", + interfaceFontAccessibility: "Família da fonte da interface", + interfaceSize: "Tamanho da interface", + interfaceSizeAccessibility: "Tamanho da fonte da interface", + codeFont: "Fonte de código", + codeFontHint: + "Usada em código, diffs e saída do terminal. Deixe vazio para usar o padrão do sistema", + codeFontAccessibility: "Família da fonte de código", + codeSize: "Tamanho do código", + codeSizeAccessibility: "Tamanho da fonte de código", + }, + syntax: { + title: "Sintaxe", + highlightTheme: "Tema de destaque", + highlightThemeHint: "Cores para código, independentes do tema do app", + highlightThemeAccessibility: "Tema de destaque: {{value}}", + previewAccessibility: "Prévia ao vivo do tema de sintaxe e da fonte de código", + }, + }, + shortcuts: { + dialogTitle: "Atalhos", + unavailableOnMobile: "Atalhos de teclado estão disponíveis apenas no desktop", + capturePrompt: "Pressione o atalho...", + actions: { + done: "Concluído", + cancel: "Cancelar", + rebind: "Reatribuir", + reset: "Redefinir", + resetAll: "Redefinir tudo", + }, + sections: { + navigation: "Navegação", + tabsPanes: "Abas e painéis", + projects: "Projetos", + panels: "Painéis", + agentInput: "Entrada do agente", + }, + help: { + openProject: "Abrir projeto", + newWorkspace: "Novo workspace", + newWorktree: "Novo worktree", + archiveWorktree: "Arquivar worktree", + newTab: "Nova aba", + closeCurrentTab: "Fechar aba atual", + jumpToWorkspace: "Ir para workspace", + jumpToTab: "Ir para aba", + previousWorkspace: "Workspace anterior", + nextWorkspace: "Próximo workspace", + previousTab: "Aba anterior", + nextTab: "Próxima aba", + splitPaneRight: "Dividir painel à direita", + splitPaneDown: "Dividir painel abaixo", + focusPaneLeft: "Focar painel à esquerda", + focusPaneRight: "Focar painel à direita", + focusPaneUp: "Focar painel acima", + focusPaneDown: "Focar painel abaixo", + moveTabLeft: "Mover aba para a esquerda", + moveTabRight: "Mover aba para a direita", + moveTabUp: "Mover aba para cima", + moveTabDown: "Mover aba para baixo", + closePane: "Fechar painel", + newTerminal: "Novo terminal", + toggleCommandCenter: "Alternar central de comandos", + showKeyboardShortcuts: "Mostrar atalhos de teclado", + toggleLeftSidebar: "Alternar barra lateral esquerda", + toggleRightSidebar: "Alternar barra lateral direita", + toggleBothSidebars: "Alternar ambas as barras laterais", + toggleSettings: "Alternar Configurações", + toggleFocusMode: "Alternar modo de foco", + cycleTheme: "Alternar tema", + focusMessageInput: "Focar entrada de mensagem", + cycleAgentMode: "Alternar modo do agente", + toggleVoiceMode: "Alternar modo de voz", + startStopDictation: "Iniciar/parar ditado", + interruptAgent: "Interromper agente", + sendMessage: "Enviar mensagem", + queueMessage: "Enfileirar mensagem", + muteUnmuteVoiceMode: "Silenciar/ativar modo de voz", + }, + helpNotes: { + showKeyboardShortcuts: + "Disponível quando o foco não está em um campo de texto ou terminal.", + }, + }, + integrations: { + title: "Integrações", + docs: { + cli: "Docs da CLI", + skills: "Docs das skills", + openCli: "Abrir documentação da CLI", + openSkills: "Abrir documentação das skills", + }, + commandLine: { + title: "Linha de comando", + description: "Controle agentes e execute scripts pelo terminal", + }, + skills: { + title: "Skills de orquestração", + description: "Ensine seus agentes a orquestrar pela CLI", + updateAvailable: "Atualização disponível", + updateTitle: "Atualizar Paseo skills?", + updateFallback: "Sincronize as skills incluídas com sua máquina.", + uninstallTitle: "Desinstalar Paseo skills?", + uninstallMessage: + "Remove todas as skills de orquestração do Paseo de ~/.agents, ~/.claude e ~/.codex.", + }, + actions: { + install: "Instalar", + installing: "Instalando...", + installed: "Instalado", + update: "Atualizar", + working: "Trabalhando...", + uninstall: "Desinstalar", + }, + operations: { + add: "Adicionar skill", + update: "Atualizar skill", + delete: "Excluir skill", + }, + }, + permissions: { + title: "Permissões", + notifications: "Notificações", + microphone: "Microfone", + refresh: "Atualizar", + refreshing: "Atualizando...", + refreshAccessibility: "Atualizar permissões do desktop", + test: "Testar", + actions: { + granted: "Concedida", + request: "Solicitar", + requesting: "Solicitando...", + busySuffix: "{{label}}...", + }, + }, + host: { + notFound: "Host não encontrado", + badges: { + relay: "Relay", + local: "Local", + }, + connections: { + title: "Conexões", + removeTitle: "Remover conexão", + removeMessage: "Remover {{name}}? Isso não pode ser desfeito.", + removeAction: "Remover", + removeErrorTitle: "Erro", + removeErrorMessage: "Não foi possível remover a conexão", + timeout: "Timeout", + }, + pairDevices: { + title: "Parear dispositivos", + rowTitle: "Parear um dispositivo", + rowHint: "Escaneie um QR code ou copie um link para conectar seu celular a este host", + }, + orchestration: { + title: "Orquestração", + unavailable: "Conecte-se a este host para gerenciar a orquestração", + enableTools: { + title: "Ativar ferramentas do Paseo", + hint: "Agentes poderão gerenciar worktrees, agentes e agendamentos", + accessibilityLabel: "Injetar ferramentas do Paseo", + }, + systemPrompt: { + title: "Prompt do sistema", + hint: "Adiciona um prompt do sistema a todos os agentes", + sheetTitle: "Acrescentar prompt do sistema", + accessibilityLabel: "Acrescentar prompt do sistema", + placeholder: "Mantenha as respostas sempre concisas.", + edit: "Editar", + reset: "Redefinir", + save: "Salvar", + saving: "Salvando...", + }, + }, + agents: { + unavailable: "Conecte-se a este host para gerenciar agentes", + }, + workspaces: { + unavailable: "Conecte-se a este host para gerenciar workspaces", + }, + terminalProfiles: { + unavailable: "Conecte-se a este host para gerenciar perfis de terminal", + sectionTitle: "Perfis de terminal", + editProfile: "Editar perfil", + addProfileTitle: "Adicionar perfil de terminal", + editProfileTitle: "Editar perfil de terminal", + namePlaceholder: "Claude Code", + commandPlaceholder: "claude", + argsPlaceholder: "--dangerously-skip-permissions", + nameLabel: "Nome", + commandLabel: "Comando", + argsLabel: "Argumentos", + nameRequired: "O nome é obrigatório", + commandRequired: "O comando é obrigatório", + argsHint: "Argumentos separados por espaço passados para o comando", + saving: "Salvando...", + remove: "Remover", + removeConfirmTitle: "Remover perfil?", + removeConfirmMessage: 'Remover "{{name}}"?', + moveUp: "Mover para cima", + moveDown: "Mover para baixo", + save: "Salvar", + emptyState: + "Nenhum perfil ainda. Adicione um para abrir terminais com um comando específico.", + }, + daemon: { + rename: { + editLabel: "Editar rótulo", + title: "Renomear host", + placeholder: "Meu Host", + submit: "Renomear", + }, + restart: { + title: "Reiniciar daemon", + hint: "Reinicia o processo do daemon. O app reconectará automaticamente", + confirm: "Reiniciar", + confirmTitle: "Reiniciar {{name}}", + confirmMessage: + "Isso vai reiniciar o daemon. Agentes em execução nele continuarão rodando; o app reconectará automaticamente.", + restarting: "Reiniciando...", + unableToReconnectTitle: "Não foi possível reconectar", + unableToReconnectMessage: + "{{name}} não voltou a ficar online. Verifique se ele reiniciou.", + unavailableTitle: "Host indisponível", + unavailableMessage: + "Este host não está conectado. Aguarde até ele ficar online antes de reiniciar.", + offlineTitle: "Host offline", + offlineMessage: + "Este host está offline. O Paseo reconecta automaticamente. Aguarde ele voltar a ficar online antes de reiniciar.", + requestFailedTitle: "Erro", + requestFailedMessage: + "Falha ao enviar a solicitação de reinício. O Paseo reconecta automaticamente. Tente novamente quando o host aparecer como online.", + dialogFailedMessage: "Não foi possível abrir o diálogo de confirmação de reinício.", + }, + dangerZone: "Zona de perigo", + remove: { + title: "Remover host", + localTitle: "Remover conexão localhost", + hint: "Remove este host e suas conexões salvas deste dispositivo", + localHint: "Remove localhost deste dispositivo e interrompe o daemon integrado", + localConfirmTitle: "Remover conexão localhost e parar daemon?", + confirmMessage: "Remover {{name}}? Isso excluirá suas conexões salvas.", + localConfirmMessage: + "Isso vai remover a conexão localhost, desativar o gerenciamento do daemon integrado e parar o daemon gerenciado. Hosts remotos permanecem conectados.", + errorTitle: "Erro", + errorMessage: "Não foi possível remover host", + localErrorMessage: "Não foi possível remover a conexão localhost", + }, + }, + }, + providers: { + title: "Provedores", + addProvider: "Adicionar provedor", + providerDetails: "Detalhes do provedor {{name}}", + enableProvider: "Ativar {{name}}", + unavailable: "Conecte-se a este host para ver provedores", + loading: "Carregando...", + addErrorTitle: "Não foi possível adicionar provedor", + updateErrorTitle: "Não foi possível atualizar provedor", + statuses: { + disabled: "Desativado", + loading: "Carregando", + error: "Erro", + available: "Disponível", + notInstalled: "Não instalado", + }, + models: { + one: "1 modelo", + many: "{{count}} modelos", + addModel: "Adicionar modelo", + addCustomTitle: "Adicionar modelo personalizado", + modelId: "ID do modelo", + modelIdPlaceholder: "e.g. openai/gpt-5", + add: "Adicionar", + adding: "Adicionando...", + failedToSave: "Falha ao salvar modelo", + removeModel: "Remover {{id}}", + searchPlaceholder: "Buscar modelos", + loading: "Carregando modelos...", + retry: "Tentar novamente", + retrying: "Tentando novamente...", + noSearchMatches: "Nenhum modelo corresponde à sua busca", + noneDetected: "Nenhum modelo detectado", + discovered: "Descobertos", + custom: "Modelos personalizados", + updated: "Atualizado {{time}}", + }, + diagnostic: { + title: "Diagnóstico", + button: "Diagnóstico", + refresh: "Atualizar", + refreshing: "Atualizando...", + copyLabel: "diagnóstico", + copyAccessibility: "Copiar diagnóstico", + copyFailed: "Falha ao copiar diagnóstico", + refreshAccessibility: "Atualizar diagnóstico", + refreshingAccessibility: "Atualizando diagnóstico", + running: "Executando diagnóstico...", + none: "Nenhum diagnóstico disponível", + failedToFetch: "Falha ao buscar diagnóstico", + unknownError: "Erro desconhecido", + }, + }, + project: { + noEditableTarget: "Não há uma cópia editável deste projeto em nenhum host conectado.", + backToProjects: "Voltar para projetos", + switchHost: "Trocar host", + rename: { + renamedToast: "Projeto renomeado", + errorFallback: "Não foi possível renomear projeto", + renameLabel: "Renomear projeto", + resetLabel: "Redefinir nome do projeto para o padrão", + projectNameLabel: "Nome do projeto", + saveLabel: "Salvar nome do projeto", + cancelLabel: "Cancelar renomeação", + reset: "Redefinir", + }, + readFailures: { + invalidTitle: "Não foi possível analisar paseo.json", + invalidDescription: "Corrija o arquivo no disco e recarregue.", + missingTitle: "Este host não tem este projeto", + missingWithHosts: "Troque para outro host acima ou recarregue.", + missingSingleHost: "O host selecionado não tem registro deste projeto.", + transportTitle: "Não foi possível carregar paseo.json", + transportFallback: "O host não respondeu.", + failedTitle: "Não foi possível carregar paseo.json", + failedDescription: "Recarregue para tentar novamente.", + }, + worktree: { + title: "Hooks de ciclo de vida do worktree", + info: "Comandos executados quando um worktree é criado ou desmontado para este projeto", + docs: "Docs", + docsTooltip: + "Veja a documentação para mais detalhes e as variáveis de ambiente disponíveis para estes comandos", + setup: "Configuração", + setupAccessibility: "Comandos de configuração do worktree", + teardown: "Desmontagem", + teardownAccessibility: "Comandos de desmontagem do worktree", + }, + scripts: { + title: "Scripts", + info: "Serviços contínuos e comandos avulsos que você pode iniciar de qualquer agente neste projeto", + empty: "Nenhum script ainda.", + untitled: "Script sem título", + port: "porta {{port}}", + menuAccessibility: "Abrir menu do script", + removeTitle: "Remover script?", + removeMessage: "Remover {{name}}?", + removeFallbackName: "este script", + name: "Nome", + command: "Comando", + nameAccessibility: "Nome do script", + commandAccessibility: "Comando do script", + nameRequired: "O nome é obrigatório", + commandRequired: "O comando é obrigatório", + newScript: "Novo script", + editScript: "Editar {{name}}", + runAsService: "Executar como serviço", + serviceHint: "O Paseo supervisiona o processo e atribui uma porta via $PASEO_PORT", + actions: { + add: "Adicionar script", + edit: "Editar", + remove: "Remover", + }, + }, + metadata: { + title: "Geração de metadados", + info: "Instruções específicas do projeto injetadas nos prompts de IA que o Paseo usa para gerar metadados. Use-as para aplicar convenções da sua equipe, como nomes de branch, estilo de commit ou formato de PR", + branchName: "Nomes de branch", + branchNamePlaceholder: "Prefixe branches com feat/ ou fix/, mb/ para branches pessoais", + commitMessage: "Mensagens de commit", + commitMessagePlaceholder: "Use Conventional Commits com escopo", + pullRequest: "Pull requests", + pullRequestPlaceholder: "Comece com um resumo de um parágrafo e inclua uma seção Test plan", + }, + writeFailures: { + staleTitle: "Configuração alterada no disco", + staleDescription: "Recarregue para buscar o paseo.json mais recente antes de salvar.", + failedTitle: "Não foi possível salvar paseo.json", + failedDescription: "Tente novamente ou recarregue a versão mais recente do disco.", + }, + actions: { + reload: "Recarregar", + tryAgain: "Tentar novamente", + save: "Salvar", + saved: "Projeto salvo", + saving: "Salvando...", + cancel: "Cancelar", + }, + }, + }, +}; diff --git a/packages/app/src/i18n/resources/ru.ts b/packages/app/src/i18n/resources/ru.ts index a9b433d43..48159ebf8 100644 --- a/packages/app/src/i18n/resources/ru.ts +++ b/packages/app/src/i18n/resources/ru.ts @@ -1466,6 +1466,7 @@ export const ru: TranslationResources = { es: "Español", fr: "Français", ja: "日本語", + ptBR: "Português brasileiro", ru: "Русский", zhCN: "中文", }, diff --git a/packages/app/src/i18n/resources/zh-CN.ts b/packages/app/src/i18n/resources/zh-CN.ts index 8d1e51810..7db4b04b1 100644 --- a/packages/app/src/i18n/resources/zh-CN.ts +++ b/packages/app/src/i18n/resources/zh-CN.ts @@ -1421,6 +1421,7 @@ export const zhCN: TranslationResources = { es: "Español", fr: "Français", ja: "日本語", + ptBR: "Português brasileiro", ru: "Русский", zhCN: "简体中文", }, diff --git a/packages/app/src/screens/settings-screen.tsx b/packages/app/src/screens/settings-screen.tsx index ff798940e..bd447bc8e 100644 --- a/packages/app/src/screens/settings-screen.tsx +++ b/packages/app/src/screens/settings-screen.tsx @@ -383,7 +383,7 @@ function GeneralSection({ > {selectedLanguageLabel} - + {LANGUAGE_OPTIONS.map((option) => (