feat(i18n): add Português (Brasil) (pt-BR) locale (#3347)
## Summary Adds **Português (Brasil)** (`pt-BR`) as a supported locale: - Selectable as **Translation target language** (project settings) and as the dashboard / terminal UI language. - Full machine-drafted catalogs (`app`, `cli`, `common`), disclosed in `packages/i18n/locales/TRANSLATION_STATUS.md` following the pattern #1352 established — reviewed for glossary/register consistency (0.18% untranslated, matching only keys that are empty in `en`), but native-speaker corrections are welcome. - Brazilian Portuguese content-language detection (accent-stripped stopword list — the scorer strips diacritics before matching, so accented entries never match; `com`/`mais` deliberately omitted to avoid bare-domain `.com` and French collisions, with regression tests for both directions). - `pt`/`pt-PT` browser and environment locales resolve to `pt-BR` on all three detection paths (`FALLBACK_LNG` routing plus a `pt` branch in `normalizeToSupportedLocale`, mirroring the existing `zh` handling). - `README.pt-BR.md` + switcher links in all READMEs, docs updates (`settings-reference`, `cli-reference`, `i18n-contributing`, `--lang` help text), changeset (`minor`). Drive-by fixes bundled: `TRANSLATION_STATUS.md` was missing the `ko` row; the LanguageSelector endonym test was missing `한국어`; `docs/i18n-contributing.md` now names the two compile-enforced display maps (`LOCALE_LABELS`, `localeDisplayName`) a new locale must update; the `--lang` CLI help text no longer drifts from its validator. ## Test plan - `pnpm i18n:status` (key parity gate) green; catalogs are `i18n:sync`-idempotent. - Updated/extended suites: core `locale-settings`, i18n `config`/`parity`/`db-banner-catalog`/`i18n-gate-coverage`, dashboard `useLanguage`/`LanguageSelector`/`GeneralSection.importTranslate`/`detectContentLanguage` (incl. new pt-BR detection + bare-domain regression tests), CLI `settings`. - `pnpm verify:fast` (typecheck, build, boot smoke), `pnpm lint`, `pnpm check:changesets`, and the bounded `pnpm test` lane all green locally (the three `test:pg-gate` files fail locally only for lack of a Postgres instance; they fail identically on clean `main`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Brazilian Portuguese (Português (Brasil)) across the dashboard, terminal interface, settings, and translation tools. * Added Portuguese translations for common interface and CLI content. * Added automatic Portuguese language detection, locale normalization, and fallback support. * Added a Portuguese (Brazil) README with product, setup, and usage documentation. * **Documentation** * Updated language selectors, CLI references, settings documentation, and translation guidance. * Added Portuguese README links to translated documentation. * Added French to the documented dashboard language options. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
This commit is contained in:
7
.changeset/pt-br-locale.md
Normal file
7
.changeset/pt-br-locale.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
summary: Add Português (Brasil) (pt-BR) as a dashboard, terminal UI, and translation target language.
|
||||
category: feature
|
||||
dev: Adds `pt-BR` to `SUPPORTED_LOCALES` with machine-drafted catalogs (flagged in packages/i18n/locales/TRANSLATION_STATUS.md) and Portuguese content-language detection stopwords.
|
||||
@@ -10,7 +10,7 @@ Describe lo que quieres — un equipo de agentes de IA lo **planifica, construye
|
||||
|
||||
[**runfusion.ai →**](https://runfusion.ai) · [Docs](./docs/README.md) · [GitHub](https://github.com/Runfusion/Fusion) · [npm](https://www.npmjs.com/package/@runfusion/fusion) · [Discord](https://discord.gg/ksrfuy7WYR)
|
||||
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · **Español** · [한국어](./README.ko.md)
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · **Español** · [한국어](./README.ko.md) · [Português (Brasil)](./README.pt-BR.md)
|
||||
|
||||
*Esta es una traducción automática; el README en inglés es el documento canónico.*
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Décrivez ce que vous voulez — une équipe d'agents IA le **planifie, le const
|
||||
|
||||
[**runfusion.ai →**](https://runfusion.ai) · [Docs](./docs/README.md) · [GitHub](https://github.com/Runfusion/Fusion) · [npm](https://www.npmjs.com/package/@runfusion/fusion) · [Discord](https://discord.gg/ksrfuy7WYR)
|
||||
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · **Français** · [Español](./README.es.md) · [한국어](./README.ko.md)
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · **Français** · [Español](./README.es.md) · [한국어](./README.ko.md) · [Português (Brasil)](./README.pt-BR.md)
|
||||
|
||||
*Traduction automatique — le README anglais fait foi.*
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
[**runfusion.ai →**](https://runfusion.ai) · [문서](./docs/README.md) · [GitHub](https://github.com/Runfusion/Fusion) · [npm](https://www.npmjs.com/package/@runfusion/fusion) · [Discord](https://discord.gg/ksrfuy7WYR)
|
||||
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · [Español](./README.es.md) · **한국어**
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · [Español](./README.es.md) · **한국어** · [Português (Brasil)](./README.pt-BR.md)
|
||||
|
||||
*이 문서는 기계 번역본입니다. 공식 원본은 [영문 README](./README.md)를 참조하세요.*
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Describe what you want — a team of AI agents **plans, builds, reviews, and shi
|
||||
|
||||
[**runfusion.ai →**](https://runfusion.ai) · [Docs](./docs/README.md) · [GitHub](https://github.com/Runfusion/Fusion) · [npm](https://www.npmjs.com/package/@runfusion/fusion) · [Discord](https://discord.gg/ksrfuy7WYR)
|
||||
|
||||
**English** · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · [Español](./README.es.md) · [한국어](./README.ko.md)
|
||||
**English** · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · [Español](./README.es.md) · [한국어](./README.ko.md) · [Português (Brasil)](./README.pt-BR.md)
|
||||
|
||||
[](./LICENSE)
|
||||
[](https://www.npmjs.com/package/@runfusion/fusion)
|
||||
|
||||
697
README.pt-BR.md
Normal file
697
README.pt-BR.md
Normal file
@@ -0,0 +1,697 @@
|
||||
<div align="center">
|
||||
|
||||
# <img src="./demo/assets/fusion-logo-orange.svg" alt="" width="34" align="center" /> Fusion
|
||||
|
||||
### De ideia bruta a código em produção — automaticamente.
|
||||
|
||||
### 🏭 Uma fábrica de software, operada por um orquestrador multiagente.
|
||||
|
||||
Descreva o que você quer — uma equipe de agentes de IA **planeja, constrói, revisa e entrega** para você. O Fusion é a sua fábrica de software: uma linha de montagem para código que opera entre tarefas, agentes, missões, git, arquivos e worktrees, com qualquer modelo, local ou na nuvem.
|
||||
|
||||
[**runfusion.ai →**](https://runfusion.ai) · [Docs](./docs/README.md) · [GitHub](https://github.com/Runfusion/Fusion) · [npm](https://www.npmjs.com/package/@runfusion/fusion) · [Discord](https://discord.gg/ksrfuy7WYR)
|
||||
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · [Español](./README.es.md) · [한국어](./README.ko.md) · **Português (Brasil)**
|
||||
|
||||
*Esta é uma tradução automática; o README em inglês é o documento canônico.*
|
||||
|
||||
[](./LICENSE)
|
||||
[](https://www.npmjs.com/package/@runfusion/fusion)
|
||||
[](https://discord.gg/ksrfuy7WYR)
|
||||

|
||||

|
||||
|
||||
<br />
|
||||
|
||||
<img src="./demo/assets/fusion-reel.gif" alt="Demonstração do Fusion: de ideia bruta a código em produção" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<a href="https://runfusion.ai">
|
||||
<img src="https://runfusion.ai/fusion-dashboard.png" alt="Painel do Fusion: colunas kanban Planejamento, A Fazer, Em Andamento, Em Revisão e Concluído com cartões de tarefas ativas" width="900" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Todo o seu ambiente de desenvolvimento. Em uma única tela.
|
||||
|
||||
Descreva uma tarefa em linguagem natural. Um agente de planejamento lê seu projeto, entende o contexto e escreve um plano completo em `PROMPT.md` — etapas, escopo de arquivos, critérios de aceitação. Depois o Fusion planeja, revisa, executa e revisa novamente, em um worktree de git isolado, com um gate de aprovação humana onde você quiser.
|
||||
|
||||
Um quadro. Controlado de qualquer lugar. Laptop, Mac mini, servidor Linux, VM na nuvem, celular — tudo conectado.
|
||||
|
||||
> Como o Trello, mas suas tarefas são especificadas, executadas e entregues por IA. Construído sobre o excelente trabalho de [dustinbyrne/kb](https://github.com/dustinbyrne/kb).
|
||||
|
||||
---
|
||||
|
||||
## Início rápido
|
||||
|
||||
**Zero instalação, direto do npm:**
|
||||
|
||||
```bash
|
||||
npx runfusion.ai
|
||||
```
|
||||
|
||||
Isso abre o painel. Os subcomandos são encaminhados diretamente: `npx runfusion.ai task create "fix X"`, `npx runfusion.ai --help`, etc. (Ou de forma explícita: `npx @runfusion/fusion dashboard`.)
|
||||
|
||||
**Instalador em uma linha** (macOS e Linux — escolhe o Homebrew automaticamente, com fallback para npm):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://runfusion.ai/install.sh | sh
|
||||
fusion dashboard
|
||||
```
|
||||
|
||||
**Homebrew** (macOS e Linux):
|
||||
|
||||
```bash
|
||||
brew install runfusion/fusion/fusion
|
||||
fusion dashboard # ou: fn dashboard
|
||||
```
|
||||
|
||||
A instalação totalmente qualificada faz auto-tap e, no Homebrew 6.0+, confia somente na fórmula do Fusion. Se você já executou `brew tap runfusion/fusion` e a instalação pelo nome curto falhar com "untrusted tap", execute `brew trust --formula runfusion/fusion/fusion` e depois `brew install fusion`.
|
||||
|
||||
**npm global**:
|
||||
|
||||
```bash
|
||||
npm install -g @runfusion/fusion
|
||||
fn dashboard # ou: fusion dashboard
|
||||
```
|
||||
|
||||
**A partir de um clone** (para desenvolvimento):
|
||||
|
||||
```bash
|
||||
pnpm dev dashboard
|
||||
```
|
||||
|
||||
Depois, clique na URL `Open:` impressa no terminal. Ela embute um token de
|
||||
portador (`http://localhost:4040/?token=fn_...`) que o navegador captura no
|
||||
`localStorage` na primeira visita e reutiliza automaticamente depois disso.
|
||||
No lado do servidor, o Fusion agora persiste o token do painel/daemon em
|
||||
`~/.fusion/settings.json` na primeira execução autenticada e o reutiliza nas
|
||||
próximas inicializações, a menos que você o substitua (`--token`,
|
||||
`FUSION_DASHBOARD_TOKEN`, `FUSION_DAEMON_TOKEN`) ou desabilite a autenticação
|
||||
com `--no-auth`. Veja
|
||||
[Referência da CLI → fn dashboard → Autenticação](./docs/cli-reference.md#fn-dashboard)
|
||||
para a precedência completa e as opções de redefinição/revogação.
|
||||
|
||||
### Configuração inicial
|
||||
|
||||
No primeiro lançamento, o Fusion abre o **assistente de onboarding** com três etapas guiadas:
|
||||
|
||||
1. **Configuração de IA** — Use uma lista simplificada de provedores para início rápido (provedores recomendados mais quaisquer provedores já conectados), depois expanda **Configurações avançadas de provedor** somente se precisar de provedores adicionais ou detalhes de configuração. Você só precisa de um provedor para começar. As entradas de provedor obsoletas Google Gemini CLI / Antigravity ficam intencionalmente ocultas; os caminhos de chave de API do Google/Gemini, Google Generative AI, Vertex e Cloud Code continuam suportados.
|
||||
2. **GitHub (Opcional)** — Conecte o GitHub para importar issues e gerenciar PRs
|
||||
3. **Primeira Tarefa** — Crie sua primeira tarefa ou importe do GitHub (se nenhum projeto estiver ativo, o onboarding primeiro pede que você registre/selecione um diretório de projeto)
|
||||
|
||||
O assistente pode ser **dispensado e não é bloqueante** — clique em **Pular por agora** para usar o painel imediatamente. Ative-o novamente depois em **Configurações → Autenticação → Reabrir guia de onboarding**.
|
||||
|
||||
### Mobile
|
||||
|
||||
Para o fluxo de trabalho com Capacitor + PWA, veja [MOBILE.md](./MOBILE.md).
|
||||
|
||||
---
|
||||
|
||||
## O fluxo
|
||||
|
||||
```
|
||||
① Describe ② Planning ③ The board ④ Isolated worktree
|
||||
───────────── ───────────── ───────────── ─────────────────────
|
||||
"Add dark mode → Agent writes → Plan → Review → → fusion/FN-123 branch
|
||||
toggle to PROMPT.md Execute → Review concurrent, zero
|
||||
settings panel" (steps, scope, (per step, until file conflicts
|
||||
acceptance) done)
|
||||
```
|
||||
|
||||
### Veja cada etapa, antes do merge
|
||||
|
||||
<div align="center">
|
||||
<img src="https://runfusion.ai/screenshot-task-detail.png" alt="Detalhe de tarefa do Fusion: etapas do fluxo de trabalho visíveis em uma tarefa em andamento, com diffs e alterações de arquivos" width="820" />
|
||||
</div>
|
||||
|
||||
Cada tarefa mostra seu plano, suas revisões, seus diffs e suas alterações de arquivo em tempo real. Entre em uma tarefa ativa e ajuste a direção, reforce as restrições, pause ou reformule o prompt.
|
||||
|
||||
---
|
||||
|
||||
## O que o torna diferente
|
||||
|
||||
<!--
|
||||
FNXC:Docs 2026-07-15-00:00:
|
||||
README must surface PostgreSQL-default storage and recently shipped operator surfaces so the public front door matches the shipped product. Legacy SQLite is a one-time migration input only; feature claims below remain grounded in release notes and linked product docs rather than new showcase media.
|
||||
-->
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 🧠 **Planejamento com IA** | Descreva uma tarefa em linguagem natural. Os agentes de planejamento a transformam em um plano `PROMPT.md` com etapas, escopo de arquivos e critérios de aceitação. |
|
||||
| 🔁 **Fluxos de trabalho selecionáveis** | Os fluxos integrados cobrem codificação, correções rápidas, trabalho com revisão intensa, execução passo a passo, Compound Engineering habilitado por plugin e fragmentos de ciclo de vida de PR. Escolha um fluxo de trabalho por tarefa ou crie fluxos personalizados no [Editor de Fluxo de Trabalho](./docs/workflow-editor.md). |
|
||||
| 🛡️ **Supervisão do planejador** | O nível de supervisão por tarefa ou por fluxo de trabalho (`off` / `observe` / `steer` / `autonomous`) determina o quanto um supervisor de planejamento acompanha e intervém — ações de merge/PR e ações destrutivas sempre exigem confirmação humana explícita. Veja a [Referência de Configurações](./docs/settings-reference.md#workflow-settings) e o [Guia do Painel](./docs/dashboard-guide.md). |
|
||||
| 🌳 **Isolamento de worktree** | Cada tarefa roda em sua própria branch e worktree (`fusion/{task-id}`). Tarefas em paralelo. Zero conflitos. Delegação opcional ao [worktrunk](https://github.com/max-sixty/worktrunk) via [`worktrunk.enabled`](./docs/settings-reference.md#worktree-backend-settings) (veja a [abstração WorktreeBackend](./docs/architecture.md#worktreebackend-abstraction)). |
|
||||
| 🗄️ **PostgreSQL por padrão** | O Fusion usa PostgreSQL incorporado, sem configuração, para os metadados de execução locais. Arquivos SQLite legados servem apenas como entrada de migração única; use um banco de dados externo compartilhado para configurações de [múltiplos projetos e nós](./docs/multi-project.md). ([Armazenamento](./docs/storage.md)) |
|
||||
| ⚡ **Controles inteligentes de merge** | Passou por todos os gates? O Fusion faz squash-merge e segue em frente. Ative aprovação manual onde quiser, herde o padrão global de auto-merge ao vivo, ou defina substituições explícitas de auto/manual por tarefa. |
|
||||
| 🛰️ **Malha multi-nó** | Laptop, Mac mini, servidor Linux, VM na nuvem, celular — tudo sincronizado. Desktop, mobile, web. |
|
||||
| 🧩 **Qualquer modelo** | Anthropic, OpenAI, Ollama, Google Generative AI, Z.ai, Kimi K3, runtimes locais e [provedores personalizados](./docs/dashboard-guide.md#custom-providers) definidos pelo usuário. Local e nuvem coexistem, com faixas de modelo/fallback do fluxo de trabalho configuráveis por projeto. |
|
||||
| 🏢 **Empresas de agentes** | Importe equipes prontas — mais de 440 agentes em 16 empresas — e execute-as de forma autônoma por semanas. |
|
||||
| 📬 **Mensagens entre agentes** | Caixa de mensagens integrada entre agentes. Delegue, esclareça, coordene; agentes com papel de engenheiro podem optar pela captura automática de backlog quando você quiser ajuda de implementação além da captação apenas por executores. |
|
||||
| 🗨️ **Chat com agentes** | Chat direto, chat de tarefa que narra proativamente o progresso das etapas, falhas e resultados de revisão, anexos, cartões de pergunta no chat, streams retomáveis, e Salas de Chat multiagente experimentais em que membros mencionados respondem diretamente e membros ambientes podem entrar até um limite. ([Documentação de chat](./docs/dashboard-guide.md#chat-view)) |
|
||||
| 🗺️ **Missões** | Planejamento hierárquico (Missão → Marco → Fatia → Funcionalidade → Tarefa) com piloto automático e contratos de validação. |
|
||||
| 🔬 **Pesquisa** | Execuções de pesquisa limitadas com busca na web, GitHub, documentação local e síntese por LLM (além do suporte nativo do runtime a WebSearch/WebFetch nos fluxos de planejamento e síntese, quando disponível). Transforme descobertas em tarefas. ([Docs](./docs/research.md)) |
|
||||
| 🧪 **Autoaperfeiçoamento** | Os agentes refletem sobre o próprio resultado e atualizam seus prompts à medida que aprendem sua base de código. |
|
||||
| 🔓 **Código aberto. MIT.** | Sem dependência de fornecedor. Rode no seu próprio hardware. Lançamentos semanais. |
|
||||
|
||||
---
|
||||
|
||||
## Veja em ação
|
||||
|
||||
<!--
|
||||
FNXC:Docs 2026-06-21-19:55:
|
||||
README must lead with a smaller wordmark and a visual showcase of the latest surfaces (Command Center, selectable workflows, agent chat, multi-agent chat rooms, agent mail) so the value lands fast.
|
||||
Each feature pairs a short looping GIF with value copy; Command Center additionally carries real fleet stats, the token/productivity/team graph trio, and the 70+-theme grid (incl. shadcn light/mono/orange/black) to make the data pop.
|
||||
Media lives in demo/assets/ (committed, GitHub-inline GIFs); stat numbers are sourced from a live seeded fleet — refresh them if the captures are re-shot.
|
||||
Each feature keeps its original Tokyo Night capture and adds a Shadcn Light + Shadcn Dark Gray + Ember trio; the theme showcase is split into a light-themes grid and a dark-themes grid. Workflow GIFs feature the Stepwise coding graph with node-level zoom/pan.
|
||||
-->
|
||||
|
||||
As superfícies mais recentes do Fusion, em resumo — o quadro ao vivo, sua equipe de agentes, controle de missão, fluxos de trabalho visuais, chat com agentes, salas multiagente e correio entre agentes.
|
||||
|
||||
### 📋 O quadro e sua equipe de agentes — ao vivo, de uma frota real
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/dashboard-tokyo.png" alt="Quadro do Fusion: colunas Planejamento, A Fazer, Em Andamento, Em Revisão e Concluído com cartões de tarefas ao vivo no tema Tokyo Night" width="900" />
|
||||
</div>
|
||||
|
||||
Cada tarefa, cada coluna, cada etapa — ao vivo. Os cartões trazem links do GitHub, contagem de etapas, níveis de revisão e ações de promover/mover/arquivar. Mude para a visualização em **Grafo** para ver as dependências entre tarefas como um grafo de nós interativo:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%"><img src="./demo/assets/dashboard-tokyo.png" alt="Quadro do Fusion: colunas Planejamento, A Fazer, Em Andamento, Em Revisão e Concluído com cartões de tarefas ao vivo" /><br/><sub><b>Quadro</b> — colunas kanban</sub></td>
|
||||
<td width="50%"><img src="./demo/assets/dashboard-graph.png" alt="Visualização em grafo do Fusion: grafo de dependências entre tarefas com nós conectados" /><br/><sub><b>Grafo</b> — mapa de dependências</sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Aqui está a mesma frota com uma nova aparência no tema **Ember** (grafite escuro com um destaque laranja), ao lado da lista de **Agentes**:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%"><img src="./demo/assets/dashboard-ember.png" alt="Quadro do Fusion no tema Ember" /><br/><sub><b>Quadro — Ember</b></sub></td>
|
||||
<td width="50%"><img src="./demo/assets/agents-tokyo.png" alt="Visualização de Agentes do Fusion: CEO, Product Manager, CTO e engenheiros com papéis e status de heartbeat" /><br/><sub><b>Agentes — Tokyo Night</b></sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Importe uma equipe e cada agente aparece aqui — papel, cadeia de subordinação, heartbeat e participação de tokens. O menu suspenso de heartbeat de cada cartão de agente mostra **Desativado** quando o agendamento está persistido como desligado; escolha Desativado para pausar os heartbeats mantendo a cadência configurada, ou selecione um intervalo para reativá-los. A lista de **Agentes** no tema Ember:
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/agents-ember.png" alt="Visualização de Agentes do Fusion no tema Ember" width="900" />
|
||||
</div>
|
||||
|
||||
### 🛰️ Command Center — controle de missão para sua frota de agentes
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/command-center.gif" alt="Command Center do Fusion: medidores de concorrência ao vivo, gráficos de tokens e telemetria da frota em várias abas" width="900" />
|
||||
</div>
|
||||
|
||||
Uma única tela para tudo o que seus agentes estão fazendo. Ajuste a capacidade do agendador em tempo real, acompanhe o gasto de tokens por modelo ao vivo e comprove o valor com números concretos. A aba **Visão geral** abre com medidores e gráficos ao vivo:
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/command-center-overview.png" alt="Visão geral do Command Center do Fusion: medidores de concorrência, status do engine e gráficos da frota" width="900" />
|
||||
</div>
|
||||
|
||||
Cada aba é uma lente diferente sobre a mesma frota ao vivo:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/command-center-tokens.png" alt="Gráficos de tokens por modelo, tendência de tokens e tokens ao longo do tempo" /><br/><sub><b>Tokens</b> — gasto por modelo, cache vs. entrada vs. saída, ao longo do tempo.</sub></td>
|
||||
<td width="33%"><img src="./demo/assets/command-center-productivity.png" alt="Produtividade: commits, horas humanas economizadas, percentis de duração das tarefas e arquivos por linguagem" /><br/><sub><b>Produtividade</b> — resultados, percentis de duração, mix de linguagens.</sub></td>
|
||||
<td width="33%"><img src="./demo/assets/command-center-team.png" alt="Organograma de agentes com participação de tokens e detalhamento de tokens por agente" /><br/><sub><b>Equipe</b> — organograma de agentes e participação de tokens por agente.</sub></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/command-center-activity.png" alt="Atividade: gráficos de throughput de tarefas e linha do tempo de eventos" /><br/><sub><b>Atividade</b> — throughput de tarefas e linhas do tempo de eventos.</sub></td>
|
||||
<td width="33%"><img src="./demo/assets/command-center-signals.png" alt="Sinais: detecção de anomalias e gráficos de sinais da frota" /><br/><sub><b>Sinais</b> — detecção de anomalias e saúde da frota.</sub></td>
|
||||
<td width="33%"><img src="./demo/assets/command-center-tokens.png" alt="Aba do Command Center" /><br/><sub><b>Mais</b> — Ferramentas · Ecossistema · GitHub · Sistema · Confiabilidade.</sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
> Tokens · Ferramentas · Atividade · Produtividade · Equipe · Ecossistema · GitHub · Sinais · Sistema · Confiabilidade · Controle de missões — cada aba é uma lente diferente sobre a mesma frota ao vivo.
|
||||
|
||||
**A mesma frota, do seu jeito** — o Command Center (e todo o painel) muda de aparência ao vivo entre **mais de 70 temas de cor**, incluindo Cobalt, Clay e Moss. Aqui está nos temas Shadcn Light, Shadcn Dark Gray e Ember:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/command-center-light.gif" alt="Command Center no tema Shadcn Light" /><br/><sub><b>Shadcn Light</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/command-center-gray.gif" alt="Command Center no tema Shadcn Dark Gray" /><br/><sub><b>Shadcn Dark Gray</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/command-center-ember.gif" alt="Command Center no tema Ember" /><br/><sub><b>Ember</b></sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<details>
|
||||
<summary><b>Uma dúzia de temas claros e uma dúzia de temas escuros</b> (clique para expandir)</summary>
|
||||
|
||||
<br/>
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/command-center-themes-light.png" alt="Command Center em 12 temas de cor claros" width="900" />
|
||||
<br/><br/>
|
||||
<img src="./demo/assets/command-center-themes-dark.png" alt="Command Center em 12 temas de cor escuros" width="900" />
|
||||
</div>
|
||||
|
||||
</details>
|
||||
|
||||
### 🔁 Fluxos de trabalho selecionáveis, criados visualmente
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/workflows.gif" alt="Editor de Fluxo de Trabalho do Fusion: alternando entre grafos de fluxo de trabalho integrados" width="820" />
|
||||
</div>
|
||||
|
||||
A jornada de uma tarefa da ideia até o merge é um **fluxo de trabalho** — e é você quem escolhe e molda. Escolha um integrado (Coding, Quick fix, Review-heavy, Stepwise, PR lifecycle, Compound engineering e mais), inspecione seu grafo, depois duplique e personalize colunas, gates, faixas de modelo e política de revisão no [Editor de Fluxo de Trabalho](./docs/workflow-editor.md) visual. Sem necessidade de fazer fork do engine.
|
||||
|
||||
Aqui está o grafo **Stepwise coding** — planeje, execute e revise cada etapa antes da próxima — explorado nó a nó em Shadcn Light, Dark Gray e Ember:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/workflows-light.gif" alt="Grafo do fluxo de trabalho Stepwise coding em Shadcn Light, percorrendo os nós" /><br/><sub><b>Shadcn Light</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/workflows-gray.gif" alt="Grafo do fluxo de trabalho Stepwise coding em Shadcn Dark Gray, percorrendo os nós" /><br/><sub><b>Shadcn Dark Gray</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/workflows-ember.gif" alt="Grafo do fluxo de trabalho Stepwise coding em Ember, percorrendo os nós" /><br/><sub><b>Ember</b></sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 🗨️ Chat com agentes — converse com seus agentes, em pleno voo
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/agent-chat.gif" alt="Chat com agentes do Fusion: uma conversa em thread com um agente diagnosticando uma tarefa que falhou" width="900" />
|
||||
</div>
|
||||
|
||||
Chat direto e chat por tarefa com qualquer agente, em qualquer modelo. Pergunte por que uma tarefa falhou, oriente uma abordagem, solte anexos, responda cartões de pergunta no chat e retome streams de onde parou — com renderização completa de markdown e código o tempo todo.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%"><img src="./demo/assets/agent-chat-light.png" alt="Thread de chat com agente em Shadcn Light" /><br/><sub><b>Shadcn Light</b></sub></td>
|
||||
<td width="50%"><img src="./demo/assets/agent-chat-gray.png" alt="Thread de chat com agente em Shadcn Dark Gray" /><br/><sub><b>Shadcn Dark Gray</b></sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 👥 Salas de chat multiagente
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/chat-rooms.gif" alt="Sala de chat do Fusion: agentes CEO, Product Manager e CTO se coordenando em #leads" width="900" />
|
||||
</div>
|
||||
|
||||
Coloque vários agentes em uma sala e deixe que se coordenem. Mencione um membro e ele responde diretamente; membros ambientes podem entrar na conversa até um limite. Aqui os agentes **CEO**, **Product Manager** e **CTO** alinham a responsabilidade pelas tarefas em `#leads` — sem nenhum humano no loop. ([Documentação de chat](./docs/dashboard-guide.md#chat-view))
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/chat-rooms-light.gif" alt="Sala de chat multiagente em Shadcn Light" /><br/><sub><b>Shadcn Light</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/chat-rooms-gray.gif" alt="Sala de chat multiagente em Shadcn Dark Gray" /><br/><sub><b>Shadcn Dark Gray</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/chat-rooms-ember.gif" alt="Sala de chat multiagente em Ember" /><br/><sub><b>Ember</b></sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 📬 Correio dos agentes — uma caixa de entrada entre seus agentes
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/agent-mail.gif" alt="Caixa de mensagens do Fusion: mensagens entre agentes com resumos de triagem e aprovações" width="900" />
|
||||
</div>
|
||||
|
||||
Uma caixa de mensagens integrada para delegação, esclarecimentos e transferências. Os agentes registram resumos de triagem, solicitam aprovações e coordenam o trabalho em toda a frota — com visualizações de Caixa de Entrada, Caixa de Saída, Agentes e Aprovações, para que você possa auditar cada troca.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/agent-mail-light.gif" alt="Caixa de mensagens de agentes em Shadcn Light" /><br/><sub><b>Shadcn Light</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/agent-mail-gray.gif" alt="Caixa de mensagens de agentes em Shadcn Dark Gray" /><br/><sub><b>Shadcn Dark Gray</b></sub></td>
|
||||
<td width="33%"><img src="./demo/assets/agent-mail-ember.gif" alt="Caixa de mensagens de agentes em Ember" /><br/><sub><b>Ember</b></sub></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 📱 O Fusion é uma fábrica de IA no seu bolso
|
||||
|
||||
O quadro completo, o Command Center, missões, agentes e chat viajam com você — aplicativos nativos para **iOS** e **Android** (Capacitor), além de um PWA instalável. Inicie uma execução no seu laptop e guie-a a partir do seu celular.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/mobile-board.png" alt="Fusion mobile: quadro" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-command-center.png" alt="Fusion mobile: Command Center" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-missions.png" alt="Fusion mobile: missões" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/mobile-agents.png" alt="Fusion mobile: agentes" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-chat.png" alt="Fusion mobile: chat com agentes" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-chat-list.png" alt="Fusion mobile: lista de chats" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Veja [MOBILE.md](./MOBILE.md) para o fluxo de trabalho com Capacitor + PWA.</sub>
|
||||
|
||||
---
|
||||
|
||||
## Como funciona
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
H((You)) -->|rough idea| T["Planning<br/><i>auto-planning</i>"]
|
||||
T --> TD["Todo<br/><i>scheduled for execution</i>"]
|
||||
TD --> IP["In Progress<br/><i>for each step:<br/>plan, review, execute, review</i>"]
|
||||
|
||||
subgraph IP["In Progress"]
|
||||
direction TD
|
||||
NS([Begin step]) --> P[Plan]
|
||||
P --> R1{Review}
|
||||
R1 -->|revise| P
|
||||
R1 -->|approve| E[Execute]
|
||||
E --> R2{Review}
|
||||
R2 -->|revise| E
|
||||
R2 -->|next step| NS
|
||||
R2 -->|rethink| P
|
||||
end
|
||||
|
||||
R2 -->|done| IR["In Review<br/><i>ready to merge,<br/>or auto-complete</i>"]
|
||||
IR -->|direct squash merge<br/>or merged PR| D["Done"]
|
||||
|
||||
style H fill:#161b22,stroke:#8b949e,color:#e6edf3
|
||||
style T fill:#2d2006,stroke:#d29922,color:#d29922
|
||||
style TD fill:#0d2044,stroke:#58a6ff,color:#58a6ff
|
||||
style IP fill:#1a0d2e,stroke:#bc8cff,color:#bc8cff
|
||||
style P fill:#1a0d2e,stroke:#bc8cff,color:#e6edf3
|
||||
style R1 fill:#1a0d2e,stroke:#bc8cff,color:#e6edf3
|
||||
style E fill:#1a0d2e,stroke:#bc8cff,color:#e6edf3
|
||||
style R2 fill:#1a0d2e,stroke:#bc8cff,color:#e6edf3
|
||||
style NS fill:#1a0d2e,stroke:#bc8cff,color:#bc8cff
|
||||
style IR fill:#0d2d16,stroke:#3fb950,color:#3fb950
|
||||
style D fill:#1a1a1a,stroke:#8b949e,color:#8b949e
|
||||
```
|
||||
|
||||
Tarefas com dependências são processadas sequencialmente. Tarefas independentes rodam em paralelo. Opcionalmente, exija aprovação manual antes que as tarefas passem de Planejamento para A Fazer (configuração `requirePlanApproval`).
|
||||
|
||||
---
|
||||
|
||||
## Visão geral do fluxo de trabalho
|
||||
|
||||
<!--
|
||||
FNXC:Docs 2026-06-16-23:10:
|
||||
Fusion now exposes workflow selection and authoring as public product surfaces, so the README must explain the high-level lifecycle and link to the canonical Workflow Steps and Workflow Editor docs instead of duplicating editor internals here.
|
||||
-->
|
||||
|
||||
Os fluxos de trabalho do Fusion definem como uma tarefa avança da ideia até a entrega. O caminho de codificação padrão ainda é o já conhecido ciclo **Plan/Triage → Execute → Workflow steps → Review → Merge**, mas agora a política vive em um fluxo de trabalho selecionável, em vez de ser apenas um comportamento fixo no engine.
|
||||
|
||||
- **Selecione por tarefa:** escolha um fluxo de trabalho nos controles de fluxo de trabalho da tarefa/quadro no painel, ou atribua um via `fn_workflow_select` / `workflow_id` ao criar tarefas.
|
||||
- **Catálogo integrado:** Coding (`builtin:coding`), Quick fix (`builtin:quick-fix`), Review-heavy (`builtin:review-heavy`), Compound engineering (`builtin:compound-engineering`, habilitado por plugin), Stepwise coding (`builtin:stepwise-coding`) e o PR lifecycle (`builtin:pr-workflow`, um fragmento de grafo de PR reutilizável).
|
||||
- **Personalize com segurança:** inspecione os integrados, duplique-os, ou crie fluxos de trabalho personalizados no [Editor de Fluxo de Trabalho](./docs/workflow-editor.md) visual. As configurações específicas de cada fluxo de trabalho cobrem faixas de modelo, política de revisão/aprovação, ajustes de execução de etapas, campos de tarefa e colunas.
|
||||
|
||||
Leia [Etapas do Fluxo de Trabalho](./docs/workflow-steps.md) para a semântica de execução, o comportamento dos fluxos de trabalho integrados e os templates de etapas de fluxo de trabalho; leia [Editor de Fluxo de Trabalho](./docs/workflow-editor.md) para o guia de criação no painel.
|
||||
|
||||
<!--
|
||||
FNXC:PlannerOversight 2026-07-05-00:00:
|
||||
The planner-oversight feature (FN-7508 → FN-7583) shipped fully documented in internal/reference docs (architecture.md, settings-reference.md, dashboard-guide.md, workflow-steps.md) but had no front-door entry point in README.md, so a new operator could not discover it. Add a short overview here that links out to the canonical docs instead of duplicating engine internals (FN-7598).
|
||||
-->
|
||||
|
||||
### Supervisão do planejador
|
||||
|
||||
Cada fluxo de trabalho (e, opcionalmente, cada tarefa) pode definir um nível de **supervisão do planejador** — `off`, `observe`, `steer` ou `autonomous` (padrão) — que controla o quanto um supervisor de planejamento acompanha e intervém na execução daquela tarefa. Mesmo em `autonomous`, o avanço de merge/PR e qualquer efeito colateral destrutivo ou que envolva serviço externo sempre exigem uma confirmação humana explícita e registrada antes de serem executados. A verbosidade das notificações é controlada separadamente. Defina o padrão na aba **Editor de Fluxo de Trabalho → Valores**, ou substitua por tarefa a partir do diálogo Nova Tarefa / formulário de edição em Detalhe da Tarefa. Leia a [Referência de Configurações](./docs/settings-reference.md#workflow-settings) para a semântica completa das configurações e o [Guia do Painel](./docs/dashboard-guide.md) para os controles de UI.
|
||||
|
||||
---
|
||||
|
||||
## Multi-nó. Um único quadro. Todas as plataformas.
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="./demo/assets/fusion-mesh.gif" alt="Malha do Fusion: laptop, Mac mini, servidor Linux, VM na nuvem, celular — tudo sincronizado" width="820" />
|
||||
|
||||
<br />
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
Laptop, Mac mini, servidor Linux, VM na nuvem, celular — cada nó é um par. O estado das suas tarefas, agentes, logs e diffs permanecem sincronizados em toda a malha. O mesmo Fusion é distribuído como:
|
||||
|
||||
- 🖥️ **Aplicativo desktop** — Electron para **macOS** (Intel + Apple Silicon), **Windows** 10/11 e **Linux**
|
||||
- 📱 **Aplicativo mobile** — Capacitor para **iOS/iPadOS** e **Android** ([MOBILE.md](./MOBILE.md))
|
||||
- 🌐 **Painel web** — qualquer navegador moderno, servido pelo daemon `fn dashboard`
|
||||
- 🔌 **CLI** — binário `fn` + extensão para fluxos de trabalho focados em terminal
|
||||
|
||||
Inicie o daemon em qualquer nó, conecte seus outros dispositivos, e o quadro te acompanha para todo lugar.
|
||||
|
||||
---
|
||||
|
||||
## Execute uma empresa de agentes
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="./demo/assets/fusion-company-reel.gif" alt="Empresa de agentes do Fusion: importe uma equipe, execute-a de forma autônoma por semanas" width="820" />
|
||||
|
||||
</div>
|
||||
|
||||
Importe uma equipe. Execute-a de forma autônoma por semanas. **Mais de 440 agentes em 16 empresas**, prontos para missões, caixas de mensagens e delegação entre agentes.
|
||||
|
||||
```bash
|
||||
npx companies.sh add paperclipai/companies/gstack
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Compatível com as ferramentas que você já usa.
|
||||
|
||||
O Fusion se integra com as ferramentas que você já ama. **Hermes**, **Paperclip** e **OpenClaw** vêm todos como plugins de primeira classe — encaminhe qualquer workspace para o runtime que melhor se encaixa na tarefa. E qualquer empresa de agentes do Paperclip é importada com um único comando.
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/hermes-logo.svg" alt="Hermes" height="56" />
|
||||
</div>
|
||||
|
||||
### [Hermes](https://hermes-agent.nousresearch.com) <sub>`experimental`</sub>
|
||||
|
||||
<sub>Nous Research</sub>
|
||||
|
||||
O agente autônomo de código aberto da **Nous Research**. Instale o plugin do Hermes e execute agentes através do Hermes para trabalhos de longa duração com contexto crescente — encaminhe qualquer workspace do Fusion para ele.
|
||||
|
||||
### OpenClaw <sub>`experimental`</sub>
|
||||
|
||||
O suporte ao runtime OpenClaw está disponível como um plugin experimental (`fusion-plugin-openclaw-runtime`) para paridade de descoberta/configuração de runtime. Configure os agentes com `runtimeConfig.runtimeHint: "openclaw"` depois de instalar o plugin.
|
||||
|
||||
<br />
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/paperclip-logo.svg" alt="Paperclip" height="56" />
|
||||
</div>
|
||||
|
||||
### [Paperclip](https://paperclip.ing) <sub>`experimental`</sub>
|
||||
|
||||
<sub>paperclip.ing</sub>
|
||||
|
||||
O plano de controle humano para o trabalho de IA. Instale o plugin do Paperclip para executar agentes através do Paperclip dentro do Fusion.
|
||||
|
||||
O Fusion também suporta nativamente o padrão de empresa de agentes **[`companies.sh`](https://github.com/paperclipai/companies)**: importe uma equipe pronta — **mais de 440 agentes em 16 empresas** — e deixe que se coordenem através da caixa de mensagens, missões e gates de fluxo de trabalho do Fusion durante semanas de trabalho autônomo. Mesmo formato de empresa, mesmos agentes, mesmas habilidades do Paperclip.
|
||||
|
||||
```bash
|
||||
npx companies.sh add paperclipai/companies/gstack
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
> **Hermes**, **Paperclip** e **OpenClaw** são plugins de runtime **experimentais** — APIs e formatos de comunicação podem mudar entre releases menores.
|
||||
|
||||
---
|
||||
|
||||
## Documentação
|
||||
|
||||
| Guia | O que cobre |
|
||||
|---|---|
|
||||
| [Primeiros passos](./docs/getting-started.md) | Instalação, onboarding, primeira tarefa e noções básicas de seleção de fluxo de trabalho |
|
||||
| [Guia do Painel](./docs/dashboard-guide.md) | Visualizações de quadro/lista, chat, editor de fluxo de trabalho, gerenciador de git, configurações e ferramentas de UI |
|
||||
| [Gestão de Tarefas](./docs/task-management.md) | Ciclo de vida da tarefa, especificações de prompt, comentários, arquivamento e integração com GitHub |
|
||||
| [Referência da CLI](./docs/cli-reference.md) | Referência completa dos comandos `fn` e do daemon |
|
||||
| [Referência de Configurações](./docs/settings-reference.md) | Configurações globais/de projeto, hierarquia de modelos, configurações de fluxo de trabalho e provedores personalizados |
|
||||
| [Etapas do Fluxo de Trabalho](./docs/workflow-steps.md) | Execução de fluxos de trabalho, fluxos integrados, gates, templates e fases |
|
||||
| [Editor de Fluxo de Trabalho](./docs/workflow-editor.md) | Criação visual, importação/exportação, campos/colunas/configurações personalizados e editor mobile |
|
||||
| [Pesquisa](./docs/research.md) | Execuções de pesquisa limitadas, descobertas, exportações e integração com tarefas |
|
||||
| [Agentes](./docs/agents.md) | Gestão de agentes, criação, heartbeat e fluxos de caixa de mensagens |
|
||||
| [Missões](./docs/missions.md) | Hierarquia de missões, planejamento, piloto automático e contratos de validação |
|
||||
| [Gestão de Plugins](./docs/plugin-management.md) | Descoberta, instalação, habilitação, configuração e solução de problemas de plugins |
|
||||
| [Criação de Plugins](./docs/PLUGIN_AUTHORING.md) | Construção de plugins com hooks de ciclo de vida, rotas, ferramentas, runtimes e superfícies do painel |
|
||||
| [Acesso Remoto](./docs/remote-access.md) | Acesso remoto ao painel com token, configuração do Tailscale/Cloudflare e solução de problemas |
|
||||
| [Multi-Projeto](./docs/multi-project.md) | Registro central, modos de isolamento e caminhos de migração |
|
||||
| [Armazenamento](./docs/storage.md) | Armazenamento de execução PostgreSQL, compatibilidade de migração e payloads baseados em arquivo |
|
||||
| [Docker](./docs/docker.md) | Deploy em containers |
|
||||
|
||||
---
|
||||
|
||||
## Funcionalidades principais
|
||||
|
||||
- **Planejamento com IA** — O agente de planejamento gera um `PROMPT.md` detalhado com etapas, escopo de arquivos e critérios de aceitação
|
||||
- **Execução passo a passo** — Ciclo Plan → Review → Execute → Review para cada etapa da tarefa, com fluxos de trabalho em modo grafo capazes de modelar explicitamente parse/execute/review/rework por etapa
|
||||
- **Isolamento de Worktree do Git** — Cada tarefa roda em seu próprio worktree (branch `fusion/{task-id}`)
|
||||
- **Fluxos de trabalho selecionáveis** — Escolha Coding, Quick fix, Review-heavy, Stepwise coding, Compound Engineering habilitado por plugin, fluxos de trabalho personalizados, ou fragmentos de PR lifecycle quando apropriado ([visão geral](#visão-geral-do-fluxo-de-trabalho); [Etapas do Fluxo de Trabalho](./docs/workflow-steps.md#workflow-overview))
|
||||
- **Editor de Fluxo de Trabalho Visual** — Inspecione integrados somente leitura, duplique/personalize fluxos de trabalho, e edite nós do grafo, colunas, campos de tarefa, configurações tipadas e valores por projeto ([Editor de Fluxo de Trabalho](./docs/workflow-editor.md))
|
||||
- **Etapas do Fluxo de Trabalho** — Gates de qualidade configuráveis (pré-merge: bloqueia o merge; pós-merge: informativo), além de etapas opcionais declaradas pelo fluxo de trabalho, como a [Verificação em Navegador](./docs/workflow-steps.md#workflow-declared-optional-steps-optional-group-nodes) opcional
|
||||
- **Política nativa do fluxo de trabalho** — Planejamento em modo rápido (`leanPlanning` / `autoApproveSpec`), limites de triagem tipados, revisão/aprovação, execução de etapas e faixas de modelo/fallback são configurações do fluxo de trabalho, não constantes fixas no engine ([Referência de Configurações](./docs/settings-reference.md#workflow-native-triage-policy-settings); [configurações de fluxo de trabalho](./docs/settings-reference.md#workflow-settings))
|
||||
- **Supervisão do planejador** — `plannerOversightLevel` nativo do fluxo de trabalho (`off`/`observe`/`steer`/`autonomous`), com uma substituição opcional por tarefa e uma configuração separada de verbosidade de notificações; o avanço de merge/PR e ações destrutivas sempre exigem confirmação humana explícita, mesmo em `autonomous` ([visão geral](#supervisão-do-planejador); [Referência de Configurações](./docs/settings-reference.md#workflow-settings))
|
||||
- **GitHub + ciclo de vida de PR** — Importe issues com tradução opcional e anexos de captura de tela, pule issues já importadas mesmo após edições ou mudanças de capitalização no repositório, crie PRs, exiba badges de PR/issue em tempo real, e use fragmentos de grafo de PR lifecycle em modo fluxo de trabalho onde habilitado
|
||||
- **Painel** — Visualizações kanban/lista/grafo em tempo real, uma Visão geral do projeto com estimativa de tokens da base de código local e tamanho em disco, gestão de agentes, terminal, gerenciador de git, planejador de missões, chat, editor de fluxo de trabalho, configuração de provedores personalizados, e ação de atualização em um clique
|
||||
- **Missões** — Planejamento hierárquico (Missão → Marco → Fatia → Funcionalidade → Tarefa) com piloto automático, contratos de validação, retentativas de correção de funcionalidade, vinculação de meta de missão e semântica de transferência bloqueada
|
||||
- **Multi-Projeto** — Gerencie múltiplos projetos a partir de uma única instalação com isolamento entre projetos
|
||||
- **Provedores Personalizados** — Adicione provedores compatíveis com OpenAI, OpenAI Responses, compatíveis com Anthropic, ou Google Generative AI; os modelos salvos aparecem nos menus de Modelos do Projeto e de modelo do fluxo de trabalho ([Guia do Painel](./docs/dashboard-guide.md#custom-providers); [formato das configurações](./docs/settings-reference.md#customproviders))
|
||||
- **Controles inteligentes de merge** — O auto-merge global permanece ativo para tarefas padrão, enquanto substituições explícitas por tarefa podem forçar o comportamento auto/manual ([Referência de Configurações](./docs/settings-reference.md#project-settings))
|
||||
- **Mensagens Entre Agentes** — Mensagens integradas para coordenação entre agentes e usuários; agentes com papel de engenheiro podem optar pela captura automática de backlog para tarefas de implementação ([Referência de Configurações](./docs/settings-reference.md#project-settings))
|
||||
- **Chat com Agentes + Salas de Chat** — O chat direto/de tarefa suporta anexos, streams retomáveis, cartões de resposta a perguntas e conversas renomeáveis; salas experimentais encaminham membros mencionados como respondentes diretos, com respostas ambientes opcionais ([Guia do Painel → Visualização de Chat](./docs/dashboard-guide.md#chat-view))
|
||||
|
||||
### Autenticação de provedores
|
||||
|
||||
O Fusion suporta autenticação baseada em OAuth para provedores de IA configurados em **Configurações → Autenticação**. Para a maioria dos provedores OAuth, quando o painel é acessado por um host que não é localhost (nó remoto, host/IP de LAN, ou proxy reverso), as URLs de login do provedor são reescritas para encaminhar os callbacks de OAuth através de um endpoint ponte (`/api/auth/oauth-callback`) para que os redirecionamentos cheguem à sessão de navegador ativa.
|
||||
|
||||
- **Anthropic (Claude)** — Usa um fluxo de código de autorização colado em Configurações/onboarding: faça login e depois cole a URL de redirecionamento final (ou o código) de volta no Fusion para concluir o login
|
||||
- **OpenAI Codex** — Usa o mesmo fluxo de código de autorização colado, com validação segura de estado
|
||||
- **Factory AI — via Droid CLI** *(opcional)* — requer instalação local da Droid CLI + `droid auth login`; a detecção segue o caminho efetivo do binário em tempo de execução (padrão `droid`, ou `droidBinaryPath` do plugin quando configurado), depois habilite em **Configurações → Autenticação** e reinicie o Fusion
|
||||
- **llama.cpp — via servidor HTTP** *(opcional)* — configure a URL do seu servidor llama.cpp (padrão `http://127.0.0.1:8080`) e a chave de API opcional, depois habilite em **Configurações → Autenticação**
|
||||
- **Outros provedores** — Autentique-se inserindo a chave de API em Configurações (incluindo chave de API do Google/Gemini, Google Generative AI, Vertex e os aliases do Cloud Code)
|
||||
- **Provedores personalizados** — Adicione endpoints compatíveis com OpenAI, OpenAI Responses, compatíveis com Anthropic, ou Google Generative AI definidos pelo usuário em **Configurações → Autenticação → Provedores Personalizados**; os IDs de modelo salvos ficam selecionáveis nas faixas de modelo do projeto e do fluxo de trabalho ([Guia do Painel](./docs/dashboard-guide.md#custom-providers))
|
||||
|
||||
### Sistema de modelos
|
||||
|
||||
O Fusion usa uma hierarquia de modelos de escopo duplo com faixas independentes. As configurações globais definem os padrões de referência; as configurações de projeto fornecem substituições por projeto.
|
||||
|
||||
| Faixa | Propósito | Chaves de Referência Global | Chaves de Substituição por Projeto |
|
||||
|------|---------|---------------------|----------------------|
|
||||
| Executor | Agente de execução de tarefas | `executionGlobalProvider` + `executionGlobalModelId` | `executionProvider` + `executionModelId` |
|
||||
| Planning | Agente de planejamento de tarefas | `planningGlobalProvider` + `planningGlobalModelId` | `planningProvider` + `planningModelId` |
|
||||
| Validator | Revisor de plano/código | `validatorGlobalProvider` + `validatorGlobalModelId` | `validatorProvider` + `validatorModelId` |
|
||||
| Merger | Agente de conflitos de merge / merge em clean-room | `mergerGlobalProvider` + `mergerGlobalModelId` | `mergerProvider` + `mergerModelId` |
|
||||
| Title Summarization | Geração automática de título | `titleSummarizerGlobalProvider` + `titleSummarizerGlobalModelId` | `titleSummarizerProvider` + `titleSummarizerModelId` |
|
||||
| Workflow Step Refinement | Refinamento de prompt por IA | (usa `defaultProvider`/`defaultModelId`) | (usa `modelProvider`/`modelId` em WorkflowStep) |
|
||||
|
||||
**Faixas de fluxo de trabalho:** O fluxo de trabalho padrão expõe as faixas de modelo Plan/Triage, Executor, Reviewer e fallback em **Configurações → Modelos do Projeto**, e configurações avançadas de fluxo de trabalho podem declarar valores tipados adicionais de modelo/política ([Referência de Configurações](./docs/settings-reference.md#workflow-settings)).
|
||||
|
||||
**Substituições por Tarefa:** Quick Add e Inline Create permitem que tarefas substituam as faixas de planning, executor, validator e merger; as seleções de planning, validator e merger também suportam níveis de raciocínio específicos por tarefa. (`modelProvider`/`modelId`, `validatorModelProvider`/`validatorModelId`, `planningModelProvider`/`planningModelId`, e substituições de modelo/raciocínio do merger.)
|
||||
|
||||
**Precedência:** Por tarefa → Substituição do projeto → Faixa global → `defaultProvider`/`defaultModelId` → Resolução automática.
|
||||
|
||||
Para a documentação completa de configurações, veja a [Referência de Configurações](./docs/settings-reference.md).
|
||||
|
||||
### Tarefas agendadas / automações
|
||||
|
||||
O Fusion suporta automação de tarefas agendadas através dos endpoints `/api/automations`. Automações podem executar comandos de shell ou fluxos de trabalho de múltiplas etapas em um agendamento configurável.
|
||||
|
||||
#### Escopo de agendamento
|
||||
|
||||
Automações e rotinas podem rodar em dois escopos:
|
||||
|
||||
- **Global** — Roda em todos os projetos. Use para manutenção entre projetos, backups ou relatórios unificados.
|
||||
- **Projeto** — Roda apenas dentro de um projeto específico. Use para CI, testes ou tarefas de deploy específicas do projeto.
|
||||
|
||||
Quando você cria um agendamento sem escolher um escopo, o Fusion usa por padrão o **escopo de projeto** com o ID de projeto `default`, para compatibilidade com versões anteriores.
|
||||
|
||||
Para direcionar um escopo explicitamente:
|
||||
- No modal **Scheduled Tasks** do painel, use o alternador **Global / Projeto**.
|
||||
- Pela API, passe `?scope=global` ou `?scope=project&projectId=<id>` nos endpoints de automação/rotina.
|
||||
|
||||
**Regras de resolução de escopo:**
|
||||
- `scope=global` sempre resolve para a faixa global de automação/rotina, independentemente do projeto ativo.
|
||||
- `scope=project` requer um `projectId`. Se omitido, recai para `"default"`.
|
||||
- Operações de CRUD, execução, alternância e webhook são estritamente isoladas por escopo: um agendamento global não pode ser modificado a partir de uma requisição com escopo de projeto, e vice-versa.
|
||||
|
||||
**Orientação operacional para configurações multi-projeto:**
|
||||
- Prefira agendamentos **globais** para infraestrutura compartilhada (por exemplo, backups noturnos, extração de insights de memória).
|
||||
- Prefira agendamentos de **projeto** para automação por repositório (por exemplo, executores de teste por projeto, hooks de deploy).
|
||||
- As faixas global e de projeto são verificadas de forma independente pelo engine, então execuções pendentes em uma faixa não bloqueiam a outra.
|
||||
|
||||
#### Automações
|
||||
|
||||
| Endpoint | Método | Descrição |
|
||||
|---------|--------|-------------|
|
||||
| `/api/automations` | GET | Lista todas as automações (filtradas por escopo, se especificado) |
|
||||
| `/api/automations` | POST | Cria uma automação (o escopo padrão é `project`) |
|
||||
| `/api/automations/:id` | GET | Obtém uma automação pelo ID |
|
||||
| `/api/automations/:id` | PATCH | Atualiza uma automação |
|
||||
| `/api/automations/:id` | DELETE | Exclui uma automação |
|
||||
| `/api/automations/:id/run` | POST | Dispara uma execução manual |
|
||||
| `/api/automations/:id/toggle` | POST | Alterna entre habilitado/desabilitado |
|
||||
| `/api/automations/:id/steps/reorder` | POST | Reordena as etapas da automação |
|
||||
|
||||
#### Rotinas
|
||||
|
||||
Rotinas são tarefas de agente de IA disparadas por agendamentos cron, webhooks ou execução manual. As rotinas compartilham o mesmo modelo de escopo global/projeto das automações.
|
||||
|
||||
| Endpoint | Método | Descrição |
|
||||
|---------|--------|-------------|
|
||||
| `/api/routines` | GET | Lista todas as rotinas (filtradas por escopo, se especificado) |
|
||||
| `/api/routines` | POST | Cria uma rotina (o escopo padrão é `project`) |
|
||||
| `/api/routines/:id` | GET | Obtém uma rotina pelo ID |
|
||||
| `/api/routines/:id` | PATCH | Atualiza uma rotina |
|
||||
| `/api/routines/:id` | DELETE | Exclui uma rotina |
|
||||
| `/api/routines/:id/run` | POST | Disparo manual |
|
||||
| `/api/routines/:id/trigger` | POST | Disparo manual canônico |
|
||||
| `/api/routines/:id/runs` | GET | Obtém o histórico de execuções |
|
||||
| `/api/routines/:id/webhook` | POST | Disparo via webhook (com suporte a verificação de assinatura) |
|
||||
|
||||
---
|
||||
|
||||
## Exemplos rápidos da CLI
|
||||
|
||||
```bash
|
||||
fn task create "Fix the login bug" # Entrada rápida → planejamento
|
||||
fn task plan "Build auth system" # Planejamento guiado por IA
|
||||
fn task import owner/repo --labels bug # Importar issues do GitHub
|
||||
fn task show FN-001 # Ver detalhes da tarefa
|
||||
fn task logs FN-001 --follow # Transmitir logs de execução
|
||||
fn task steer FN-001 "Use TypeScript" # Guiar o agente durante a execução
|
||||
|
||||
fn project add my-app /path/to/app # Registrar um projeto
|
||||
fn project list # Listar todos os projetos
|
||||
|
||||
fn settings set maxConcurrent 4 # Definir configurações
|
||||
fn settings export # Exportar configuração
|
||||
|
||||
fn mission create "Auth System" "Build auth" # Criar missão
|
||||
fn mission activate-slice <slice-id> # Ativar uma fatia
|
||||
|
||||
fn skills search react # Buscar no skills.sh
|
||||
fn skills install firebase/agent-skills # Instalar habilidades de agente
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pacotes
|
||||
|
||||
| Pacote | Descrição |
|
||||
|---------|-------------|
|
||||
| `@fusion/core` | Modelo de domínio — tarefas, colunas do quadro, armazenamento PostgreSQL |
|
||||
| `@fusion/dashboard` | Interface web — servidor Express + quadro kanban com SSE |
|
||||
| `@fusion/engine` | Engine de IA — planejamento, execução, agendamento, etapas de fluxo de trabalho |
|
||||
| `@runfusion/fusion` | CLI + extensão — publicado no npm |
|
||||
|
||||
---
|
||||
|
||||
## Desenvolvimento
|
||||
|
||||
```bash
|
||||
pnpm install # Instalar dependências
|
||||
pnpm local # Iniciar painel/API local + motor de IA em uma porta diferente da 4040
|
||||
pnpm local --no-engine # Iniciar apenas o painel/API local
|
||||
pnpm build # Compilar pacotes padrão do workspace (exclui desktop/mobile)
|
||||
pnpm build:all # Compilar todos os pacotes (incluindo desktop/mobile)
|
||||
pnpm dev dashboard # Executar painel + motor de IA
|
||||
pnpm dev:watch # Painel + motor de IA; reinicia em edições de código-fonte após o esvaziamento dos agentes
|
||||
pnpm dev:ui # Apenas o painel (sem motor de IA)
|
||||
pnpm lint # Executar lint em todos os pacotes
|
||||
pnpm typecheck # Verificar tipos em todos os pacotes
|
||||
pnpm test # Executar todos os testes
|
||||
```
|
||||
|
||||
### Compilar um executável independente
|
||||
|
||||
Compile um único binário `fn` autocontido usando o [Bun](https://bun.sh/):
|
||||
|
||||
```bash
|
||||
pnpm build:exe # Compilar para a plataforma atual
|
||||
pnpm build:exe:all # Compilação cruzada para todas as plataformas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Licença
|
||||
|
||||
MIT — código aberto, sem dependência de fornecedor. Veja [LICENSE](./LICENSE).
|
||||
|
||||
<div align="center">
|
||||
|
||||
**[runfusion.ai →](https://runfusion.ai)**
|
||||
|
||||
</div>
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
[**runfusion.ai →**](https://runfusion.ai) · [文档](./docs/README.md) · [GitHub](https://github.com/Runfusion/Fusion) · [npm](https://www.npmjs.com/package/@runfusion/fusion) · [Discord](https://discord.gg/ksrfuy7WYR)
|
||||
|
||||
[English](./README.md) · **简体中文** · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · [Español](./README.es.md) · [한국어](./README.ko.md)
|
||||
[English](./README.md) · **简体中文** · [繁體中文](./README.zh-TW.md) · [Français](./README.fr.md) · [Español](./README.es.md) · [한국어](./README.ko.md) · [Português (Brasil)](./README.pt-BR.md)
|
||||
|
||||
*本文档为社区机器翻译版本,英文 README 为权威原版。*
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
[**runfusion.ai →**](https://runfusion.ai) · [文件](./docs/README.md) · [GitHub](https://github.com/Runfusion/Fusion) · [npm](https://www.npmjs.com/package/@runfusion/fusion) · [Discord](https://discord.gg/ksrfuy7WYR)
|
||||
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · **繁體中文** · [Français](./README.fr.md) · [Español](./README.es.md) · [한국어](./README.ko.md)
|
||||
[English](./README.md) · [简体中文](./README.zh-CN.md) · **繁體中文** · [Français](./README.fr.md) · [Español](./README.es.md) · [한국어](./README.ko.md) · [Português (Brasil)](./README.pt-BR.md)
|
||||
|
||||
*本文件為機器翻譯,英文版 README 為權威版本。*
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ fn dashboard --lang zh-TW # force a UI locale for this run
|
||||
```
|
||||
|
||||
The terminal UI is localized. `--lang <code>` (one of `en`, `zh-CN`, `zh-TW`,
|
||||
`fr`, `es`, `ko`) takes precedence over the saved dashboard language setting and the
|
||||
`fr`, `es`, `ko`, `pt-BR`) takes precedence over the saved dashboard language setting and the
|
||||
`LC_ALL`/`LC_MESSAGES`/`LANG`/`LANGUAGE` environment. See
|
||||
[Localization contributor guide](./i18n-contributing.md).
|
||||
|
||||
|
||||
@@ -86,7 +86,12 @@ No feature code changes are required: the dashboard discovers the locale through
|
||||
the generated `app/locales/` tree, and the CLI through the regenerated import
|
||||
map. Add the language's endonym to `ENDONYMS` in
|
||||
`packages/dashboard/app/components/LanguageSelector.tsx` so it appears in the
|
||||
Settings switcher.
|
||||
Settings switcher, add a prompt label to `LOCALE_LABELS` in
|
||||
`packages/dashboard/src/ai-translate.ts` (compile-enforced), and add a display
|
||||
name to `localeDisplayName` in
|
||||
`packages/core/src/i18n/detect-content-language.ts`. For Latin-script languages,
|
||||
also consider a stopword list in `LATIN_STOPWORDS` there so content-language
|
||||
detection can recognize the language (entries must be accent-stripped).
|
||||
|
||||
## Using a non-English locale
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ Defaults from `DEFAULT_GLOBAL_SETTINGS`; key scope from `GLOBAL_SETTINGS_KEYS`.
|
||||
| `themeMode` | `"dark" \| "light" \| "system"` | `"system"` | Dashboard theme mode. Fresh installs follow the operating system light/dark preference until the user chooses Light, Dark, or System. |
|
||||
| `colorTheme` | `ColorTheme` | `"shadcn-ember"` | Dashboard color theme preset. Aurora is a built-in navy/teal/violet palette with a misty light counterpart; Calm is a low-stimulation slate/sage palette with a misty light counterpart; Dawn uses indigo/plum dark surfaces, amber accents, and dawn-white light surfaces; Midnight uses deep-navy surfaces with restrained violet/indigo accents and a cool pale light counterpart; Factory Dark uses graphite/steel surfaces, cool gunmetal borders, and restrained safety-orange accents with a concrete-gray light counterpart; Factory Light uses bright concrete/warm-white surfaces, steel-gray borders, a darkened safety-orange accent, and a warm-graphite dark counterpart. Use `"shadcn-custom"` to show the separate custom shadcn color picker in Settings → Appearance and the Command Center theme card. |
|
||||
| `shadcnCustomColors` | `Record<string, string>` | `undefined` | Optional shadcn design-token override map for `"shadcn-custom"` only. Keys are CSS token names such as `--accent`, `--bg`, `--surface`, `--card`, `--border`, `--text`, `--text-muted`, workflow status tokens, and `--color-success`/`--color-warning`/`--color-error`; values must be sanitized `#RGB` or `#RRGGBB` hex colors. Missing or invalid entries fall back to the `shadcn-custom` base defaults and are not applied to other themes. |
|
||||
| `language` | `"en" \| "zh-CN" \| "zh-TW" \| "fr" \| "es" \| "ko"` | `undefined` | UI language for the dashboard and TUI. When unset, the dashboard detects from localStorage → browser language and the CLI from `--lang` flag → environment locale, falling back to `en`. Validated at the store write boundary (`validateLocale`); invalid values are dropped. Reset to auto-detect via the dashboard's "Auto" language option or `fn settings set language auto` (clears the persisted key). |
|
||||
| `language` | `"en" \| "zh-CN" \| "zh-TW" \| "fr" \| "es" \| "ko" \| "pt-BR"` | `undefined` | UI language for the dashboard and TUI. When unset, the dashboard detects from localStorage → browser language and the CLI from `--lang` flag → environment locale, falling back to `en`. Validated at the store write boundary (`validateLocale`); invalid values are dropped. Reset to auto-detect via the dashboard's "Auto" language option or `fn settings set language auto` (clears the persisted key). |
|
||||
| `dashboardFontScalePct` | `number` | `100` | Dashboard font scale percentage used by Appearance settings. Valid range: `85` to `125`; applied pre-hydration via document root font-size so board typography (column headers/counts, task cards, and quick-entry text) scales with the setting from first paint. |
|
||||
| `dismissModalsOnOutsideClick` | `boolean` | `false` | Global dashboard preference for closing fixed modal overlays by clicking/tapping the backdrop. Off by default to prevent accidental modal dismissal; explicit close, cancel, and Escape paths remain available. |
|
||||
| `skipConfirmationDialogs` | `boolean` | `false` | Global-only operator preference that skips centralized confirmation dialogs for critical actions. When enabled, destructive actions such as deleting a task or resetting progress immediately take the dialog's primary/default action; project settings cannot enable it for shared-project collaborators. |
|
||||
|
||||
@@ -29,7 +29,7 @@ const DEFERRED_I18N_LINT_FILES = [
|
||||
* source, not by file path. `common` is the default namespace.
|
||||
*/
|
||||
export default defineConfig({
|
||||
locales: ["en", "zh-CN", "zh-TW", "fr", "es", "ko"],
|
||||
locales: ["en", "zh-CN", "zh-TW", "fr", "es", "ko", "pt-BR"],
|
||||
extract: {
|
||||
input: [
|
||||
"packages/dashboard/app/**/*.{ts,tsx}",
|
||||
|
||||
@@ -502,7 +502,7 @@ Options:
|
||||
--no-engine Start dashboard only (no AI engine)
|
||||
--supervise (default) Run with auto-restart on crash and System-panel restart support
|
||||
--no-supervise Run the dashboard without the supervising parent process
|
||||
--lang <locale> Terminal-UI locale for this run (en, zh-CN, zh-TW, fr, es, ko); the browser dashboard resolves its own language
|
||||
--lang <locale> Terminal-UI locale for this run (en, zh-CN, zh-TW, fr, es, ko, pt-BR); the browser dashboard resolves its own language
|
||||
--attach <file> Attach file(s) on task create (repeatable)
|
||||
--depends <id> Declare dependency on task create (repeatable)
|
||||
--no-dedup Bypass deterministic duplicate guard on task create
|
||||
|
||||
@@ -40,7 +40,7 @@ vi.mock("@fusion/core", () => {
|
||||
return {
|
||||
GlobalSettingsStore: makeConstructibleMock(),
|
||||
DEFAULT_SETTINGS,
|
||||
SUPPORTED_LOCALES: ["en", "zh-CN", "zh-TW", "fr", "es", "ko"],
|
||||
SUPPORTED_LOCALES: ["en", "zh-CN", "zh-TW", "fr", "es", "ko", "pt-BR"],
|
||||
resolveWorktrunkSettings: (globalValue: any, projectValue: any) => ({
|
||||
enabled: projectValue?.enabled ?? globalValue?.enabled ?? false,
|
||||
...(projectValue?.binaryPath ?? globalValue?.binaryPath ? { binaryPath: projectValue?.binaryPath ?? globalValue?.binaryPath } : {}),
|
||||
|
||||
@@ -12,13 +12,13 @@ import {
|
||||
} from "../index.js";
|
||||
|
||||
describe("locale primitives", () => {
|
||||
it("exposes exactly the six supported locale codes", () => {
|
||||
expect([...SUPPORTED_LOCALES]).toEqual(["en", "zh-CN", "zh-TW", "fr", "es", "ko"]);
|
||||
it("exposes exactly the seven supported locale codes", () => {
|
||||
expect([...SUPPORTED_LOCALES]).toEqual(["en", "zh-CN", "zh-TW", "fr", "es", "ko", "pt-BR"]);
|
||||
});
|
||||
|
||||
it("uses en as the default/source locale", () => {
|
||||
expect(DEFAULT_LOCALE).toBe("en");
|
||||
expectTypeOf<Locale>().toEqualTypeOf<"en" | "zh-CN" | "zh-TW" | "fr" | "es" | "ko">();
|
||||
expectTypeOf<Locale>().toEqualTypeOf<"en" | "zh-CN" | "zh-TW" | "fr" | "es" | "ko" | "pt-BR">();
|
||||
});
|
||||
|
||||
it("narrows supported codes and rejects everything else via isLocale", () => {
|
||||
@@ -26,6 +26,7 @@ describe("locale primitives", () => {
|
||||
expect(isLocale(code)).toBe(true);
|
||||
}
|
||||
expect(isLocale("zh")).toBe(false);
|
||||
expect(isLocale("pt")).toBe(false);
|
||||
expect(isLocale("de")).toBe(false);
|
||||
expect(isLocale("")).toBe(false);
|
||||
expect(isLocale(undefined)).toBe(false);
|
||||
@@ -50,6 +51,7 @@ describe("language global setting", () => {
|
||||
expect(validateLocale("zh-TW")).toBe("zh-TW");
|
||||
expect(validateLocale("fr")).toBe("fr");
|
||||
expect(validateLocale("es")).toBe("es");
|
||||
expect(validateLocale("pt-BR")).toBe("pt-BR");
|
||||
expect(validateLocale(undefined)).toBeUndefined();
|
||||
expect(validateLocale("zh")).toBeUndefined();
|
||||
expect(validateLocale("de")).toBeUndefined();
|
||||
|
||||
@@ -28,7 +28,7 @@ export type DetectedContentLanguage = {
|
||||
confidence: "high" | "medium" | "low";
|
||||
};
|
||||
|
||||
const LATIN_STOPWORDS: Record<"en" | "fr" | "es" | "cs", readonly string[]> = {
|
||||
const LATIN_STOPWORDS: Record<"en" | "fr" | "es" | "cs" | "pt-BR", readonly string[]> = {
|
||||
en: [
|
||||
"the", "and", "for", "that", "with", "this", "from", "have", "will", "are",
|
||||
"not", "but", "you", "all", "can", "has", "was", "were", "been", "their",
|
||||
@@ -55,6 +55,23 @@ const LATIN_STOPWORDS: Record<"en" | "fr" | "es" | "cs", readonly string[]> = {
|
||||
"kdy", "ktere", "ktery", "na", "nebo", "nema", "neni", "po", "pro", "pri", "se", "si",
|
||||
"tak", "to", "uz", "ve", "vse", "z", "ze",
|
||||
],
|
||||
/*
|
||||
FNXC:GitHubImportTranslate 2026-08-07-22:55:
|
||||
pt-BR stopwords are written accent-stripped because scoreLatinLocale strips diacritics
|
||||
before matching ("não"→"nao"); accented entries would never match. "mais" is deliberately
|
||||
omitted (collides with the French "mais" list entry).
|
||||
|
||||
FNXC:GitHubImportTranslate 2026-08-07-23:07:
|
||||
"com" is deliberately omitted too (review finding, Task 3): the tokenizer only strips
|
||||
protocol-prefixed URLs, so bare domains like "github.com, gitlab.com, npmjs.com" tokenize
|
||||
to repeated "com" tokens, pushing ordinary English issue bodies over the high-confidence
|
||||
pt-BR threshold and triggering false translate offers / wasted auto-translate model calls.
|
||||
*/
|
||||
"pt-BR": [
|
||||
"nao", "uma", "para", "dos", "das", "pelo", "pela", "sao", "esta",
|
||||
"tambem", "quando", "voce", "isso", "essa", "esse", "seu", "sua", "ate",
|
||||
"apos", "ja", "fazer", "pode", "deve", "usar", "entao", "muito", "tem", "foi", "ser",
|
||||
],
|
||||
};
|
||||
|
||||
function countMatches(text: string, re: RegExp): number {
|
||||
@@ -88,16 +105,16 @@ function scoreLatinLocale(text: string): { locale: Locale | "unknown"; confidenc
|
||||
return { locale: "en", confidence: "low" };
|
||||
}
|
||||
|
||||
const scores: Record<"en" | "fr" | "es" | "cs", number> = { en: 0, fr: 0, es: 0, cs: 0 };
|
||||
const scores: Record<"en" | "fr" | "es" | "cs" | "pt-BR", number> = { en: 0, fr: 0, es: 0, cs: 0, "pt-BR": 0 };
|
||||
for (const token of tokens) {
|
||||
for (const locale of ["en", "fr", "es", "cs"] as const) {
|
||||
for (const locale of ["en", "fr", "es", "cs", "pt-BR"] as const) {
|
||||
if (LATIN_STOPWORDS[locale].includes(token)) {
|
||||
scores[locale] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ranked = (Object.entries(scores) as Array<["en" | "fr" | "es" | "cs", number]>).sort(
|
||||
const ranked = (Object.entries(scores) as Array<["en" | "fr" | "es" | "cs" | "pt-BR", number]>).sort(
|
||||
(a, b) => b[1] - a[1],
|
||||
);
|
||||
const [best, second] = ranked;
|
||||
@@ -280,6 +297,8 @@ export function localeDisplayName(locale: Locale | "unknown"): string {
|
||||
return "Español";
|
||||
case "ko":
|
||||
return "한국어";
|
||||
case "pt-BR":
|
||||
return "Português (Brasil)";
|
||||
default:
|
||||
return locale;
|
||||
}
|
||||
|
||||
@@ -186,8 +186,11 @@ export type ColorTheme = (typeof COLOR_THEMES)[number];
|
||||
* source-of-truth language and the fallback for all others. Adding a locale
|
||||
* here (plus translated catalogs) is the only code change a new language
|
||||
* needs — see `@fusion/i18n`. zh-CN and zh-TW are independent catalogs and
|
||||
* are never auto-converted between scripts. */
|
||||
export const SUPPORTED_LOCALES = ["en", "zh-CN", "zh-TW", "fr", "es", "ko"] as const;
|
||||
* are never auto-converted between scripts.
|
||||
*
|
||||
* FNXC:I18nLocales 2026-08-07-22:00:
|
||||
* Added pt-BR as a dashboard/translation-target locale; catalogs are machine-drafted pending human review. */
|
||||
export const SUPPORTED_LOCALES = ["en", "zh-CN", "zh-TW", "fr", "es", "ko", "pt-BR"] as const;
|
||||
export type Locale = (typeof SUPPORTED_LOCALES)[number];
|
||||
/** Source-of-truth language and the fallback for all locales. */
|
||||
export const DEFAULT_LOCALE: Locale = "en";
|
||||
|
||||
@@ -11,6 +11,7 @@ const ENDONYMS: Record<Locale, string> = {
|
||||
fr: "Français",
|
||||
es: "Español",
|
||||
ko: "한국어",
|
||||
"pt-BR": "Português (Brasil)",
|
||||
};
|
||||
|
||||
/** Settings control for choosing the UI language. Applies in place (no reload). */
|
||||
|
||||
@@ -26,9 +26,9 @@ describe("LanguageSelector", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("renders all five language endonyms", () => {
|
||||
it("renders all seven language endonyms", () => {
|
||||
render(<LanguageSelector />);
|
||||
for (const name of ["English", "简体中文", "繁體中文", "Français", "Español"]) {
|
||||
for (const name of ["English", "简体中文", "繁體中文", "Français", "Español", "한국어", "Português (Brasil)"]) {
|
||||
expect(screen.getByText(name)).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -147,6 +147,7 @@ describe("GeneralSection - import auto-translate controls", () => {
|
||||
expect(select.className).toContain("select");
|
||||
expect(select.value).toBe("");
|
||||
expect([...select.options].map((o) => o.textContent)).toContain("Follow dashboard language");
|
||||
expect([...select.options].map((o) => o.textContent)).toContain("Português (Brasil)");
|
||||
});
|
||||
|
||||
it("reflects and stores an explicit target locale", () => {
|
||||
|
||||
@@ -83,7 +83,7 @@ describe("useLanguage", () => {
|
||||
|
||||
it("exposes the supported locale list", () => {
|
||||
const { result } = renderHook(() => useLanguage());
|
||||
expect([...result.current.supportedLocales]).toEqual(["en", "zh-CN", "zh-TW", "fr", "es", "ko"]);
|
||||
expect([...result.current.supportedLocales]).toEqual(["en", "zh-CN", "zh-TW", "fr", "es", "ko", "pt-BR"]);
|
||||
});
|
||||
|
||||
it("hydrates from server settings when no local choice exists", async () => {
|
||||
|
||||
@@ -54,6 +54,21 @@ describe("detectContentLanguage", () => {
|
||||
expect(detected.family).toBe("latin");
|
||||
});
|
||||
|
||||
it("detects Brazilian Portuguese stopword-heavy prose", () => {
|
||||
const text =
|
||||
"Não podemos usar essa configuração quando o painel já está aberto, então você deve fazer uma revisão das tarefas para que tudo também funcione como esperado.";
|
||||
const detected = detectContentLanguage(text);
|
||||
expect(detected.locale).toBe("pt-BR");
|
||||
expect(detected.family).toBe("latin");
|
||||
});
|
||||
|
||||
it("does not mistake bare .com domains for Portuguese", () => {
|
||||
const text =
|
||||
"Please check github.com, gitlab.com, bitbucket.com, npmjs.com and registry.com to see if the package is published.";
|
||||
const detected = detectContentLanguage(text);
|
||||
expect(detected.locale).toBe("en");
|
||||
});
|
||||
|
||||
it("ignores fenced code and URLs when scoring", () => {
|
||||
const text = `
|
||||
## Bug
|
||||
@@ -227,5 +242,6 @@ describe("localeDisplayName", () => {
|
||||
expect(localeDisplayName("en")).toBe("English");
|
||||
expect(localeDisplayName("ko")).toBe("한국어");
|
||||
expect(localeDisplayName("fr")).toBe("Français");
|
||||
expect(localeDisplayName("pt-BR")).toBe("Português (Brasil)");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -144,12 +144,18 @@ export function createSmokeHtml() {
|
||||
The fixture must include all five production icon controls before Save, including the session
|
||||
advisor toggle. Omitting it understates the primary group's minimum width and could conceal a
|
||||
300px overflow or wrap regression on either Board or List.
|
||||
|
||||
FNXC:QuickAddActionRow 2026-08-07-23:56:
|
||||
pt-BR joined the supported translations (the scaffold was previously empty and now carries
|
||||
machine-drafted translations) — add it here so the smoke keeps measuring the widest
|
||||
emitted-font label across every shipped locale.
|
||||
*/
|
||||
const localizedSaveLabels = [
|
||||
["en", "Save"],
|
||||
["es", "Guardar"],
|
||||
["fr", "Enregistrer"],
|
||||
["ko", "저장"],
|
||||
["pt-BR", "Salvar"],
|
||||
["zh-CN", "保存"],
|
||||
["zh-TW", "儲存"],
|
||||
];
|
||||
|
||||
@@ -136,6 +136,7 @@ const LOCALE_LABELS: Record<Locale, string> = {
|
||||
fr: "French (Français)",
|
||||
es: "Spanish (Español)",
|
||||
ko: "Korean (한국어)",
|
||||
"pt-BR": "Brazilian Portuguese (Português do Brasil)",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Translation status
|
||||
|
||||
`en` is the authored source-of-truth. The four non-English locales below were
|
||||
`en` is the authored source-of-truth. The non-English locales below were
|
||||
**machine-drafted and have NOT been reviewed by a human translator.** They are
|
||||
shipped so the feature is end-to-end exercisable, but each string should be
|
||||
reviewed (and corrected where needed) by a fluent speaker before being treated
|
||||
@@ -13,6 +13,8 @@ as production-quality.
|
||||
| `zh-TW`| Traditional Chinese (TW) | Machine-drafted ⚠️ |
|
||||
| `fr` | French | Machine-drafted ⚠️ |
|
||||
| `es` | Spanish | Machine-drafted ⚠️ |
|
||||
| `ko` | Korean | Machine-drafted ⚠️ |
|
||||
| `pt-BR`| Brazilian Portuguese | Machine-drafted ⚠️ |
|
||||
|
||||
Notes for reviewers:
|
||||
|
||||
@@ -24,3 +26,6 @@ Notes for reviewers:
|
||||
sweep the product name without touching catalogs.
|
||||
- Keybinding accelerators inside hints (e.g. `[{{key}}]`) must stay as the
|
||||
literal key — translate only the surrounding words.
|
||||
- `pt-BR` is Brazilian Portuguese specifically. European Portuguese (pt-PT)
|
||||
differs in vocabulary, orthography, and register — do not machine-convert one
|
||||
into the other.
|
||||
|
||||
9284
packages/i18n/locales/pt-BR/app.json
Normal file
9284
packages/i18n/locales/pt-BR/app.json
Normal file
File diff suppressed because it is too large
Load Diff
226
packages/i18n/locales/pt-BR/cli.json
Normal file
226
packages/i18n/locales/pt-BR/cli.json
Normal file
@@ -0,0 +1,226 @@
|
||||
{
|
||||
"tui": {
|
||||
"agentDeleteConfirmHints": "[y] confirmar exclusão [qualquer outra tecla] cancelar",
|
||||
"agentDeleteConfirmId": "ID:",
|
||||
"agentDeleteConfirmName": "Agente:",
|
||||
"agentDeleteConfirmTitle": "Excluir agente?",
|
||||
"agentDeleted": "Agente {{name}} excluído",
|
||||
"agentDetailCaps": "Caps:",
|
||||
"agentDetailLoadError": "Não foi possível carregar o detalhe do agente.",
|
||||
"agentDetailRole": "Função:",
|
||||
"agentDetailSelectHint": "Selecione um agente da lista.",
|
||||
"agentDetailState": "Estado:",
|
||||
"agentDetailTask": "Tarefa:",
|
||||
"agentDetailTitle": "Detalhe do Agente",
|
||||
"agentDetailTitle2": "Título:",
|
||||
"agentNarrowDetail": "detalhe",
|
||||
"agentNarrowList": "lista",
|
||||
"agentOpenLogsHint": "[Enter] abrir registros",
|
||||
"agentRunHistory": "Histórico de execuções (mais recente primeiro):",
|
||||
"agentRunId": "ID:",
|
||||
"agentRunLogsBackHint": "[Esc/q] voltar às execuções",
|
||||
"agentRunLogsTitle": "Registros de execução ({{index}})",
|
||||
"agentsFooterHints": "[s] iniciar [x] parar [D] excluir [r] atualizar [Tab] foco ↑↓ selecionar",
|
||||
"agentsListTitle_one": "Agente ({{count}})",
|
||||
"agentsListTitle_other": "Agentes ({{count}})",
|
||||
"agentsNoAgents": "Nenhum agente encontrado.",
|
||||
"agentStarted": "Agente iniciado",
|
||||
"agentStopped": "Agente parado",
|
||||
"boardCreateTaskHints": "Enter para criar · Esc para cancelar",
|
||||
"boardCreateTaskNoProject": "Nenhum projeto selecionado",
|
||||
"boardCreateTaskTitleEmpty": "O título não pode estar vazio",
|
||||
"boardCreatingTask": "Criando…",
|
||||
"boardNewTaskPlaceholder": "O que precisa ser feito?",
|
||||
"boardNewTaskProject": "Projeto: {{name}}",
|
||||
"boardNewTaskTitle": "Nova Tarefa",
|
||||
"boardNewTaskTitleLabel": "Título",
|
||||
"boardOtherReadOnlyHint": "coluna personalizada — mover desativado aqui",
|
||||
"copiedSuccess": "✓ Copiado!",
|
||||
"copyFailed": "✗ Falha ao copiar",
|
||||
"expandedLogHeader": "Entrada {{index}}/{{total}} · [Enter/Esc] fechar · [c] copiar",
|
||||
"expandedLogLevel": "Nível:",
|
||||
"expandedLogPrefix": "Prefixo:",
|
||||
"expandedLogTime": "Hora:",
|
||||
"filesBinary": "[arquivo binário, {{size}}]",
|
||||
"filesEmpty": "(vazio)",
|
||||
"filesEmptyFile": "(arquivo vazio)",
|
||||
"filesFooterHints": "[Tab] trocar painel [↑↓/jk] mover [Enter] abrir [←/→] recolher/expandir [.] ocultos [w] quebra de linha [p] projeto [r] recarregar",
|
||||
"filesMoreLines_one": "… mais {{count}} linha",
|
||||
"filesMoreLines_other": "… mais {{count}} linhas",
|
||||
"filesSelectProject": "Selecionar Projeto",
|
||||
"filesSelectToPreview": "Selecione um arquivo para pré-visualizar",
|
||||
"filesTooLarge": "{{size}} — [muito grande para pré-visualizar]",
|
||||
"filesUnableToRead": "Não foi possível ler o arquivo",
|
||||
"gitFetched": "Buscado",
|
||||
"gitFetchFailed": "Falha ao buscar: {{output}}",
|
||||
"gitFetching": "Buscando…",
|
||||
"gitFooterHints": "[r] atualizar {{push}}[F] buscar [↑↓] linhas [←→] status▸branches{{worktrees}}▸commits▸alterações [p] projeto [Esc/s] voltar",
|
||||
"gitNoCommits": "Nenhum commit",
|
||||
"gitNoProject": "Nenhum projeto",
|
||||
"gitPushDismissHint": "[Esc] descartar",
|
||||
"gitPushFailed": "Falha no push",
|
||||
"gitPushingToOrigin": "Fazendo push para origin/{{branch}}",
|
||||
"gitPushModalAhead": "à frente",
|
||||
"gitPushModalBranch": "Branch:",
|
||||
"gitPushModalCommits": "Commits para push (mais antigo→mais recente):",
|
||||
"gitPushModalHints": "[Enter] push [Esc] cancelar",
|
||||
"gitPushModalTitle": "Push para o remoto",
|
||||
"gitPushSuccessful": "Push concluído com sucesso",
|
||||
"gitRefreshing": "atualizando",
|
||||
"gitWorkingTreeClean": "Árvore de trabalho limpa",
|
||||
"headerHelpQuitHint": "[?] ajuda [q] sair",
|
||||
"headerTunnelQrHint": " [^Q] QR",
|
||||
"headerTunnelRunning": "túnel",
|
||||
"headerTunnelStarting": "túnel iniciando…",
|
||||
"helpShortcutAdjustNumber": "Ajustar número (Configurações)",
|
||||
"helpShortcutAdjustThreshold": "Ajustar limite de memória de encerramento do vitest (Utilitários)",
|
||||
"helpShortcutAgents": "Visão de agentes",
|
||||
"helpShortcutBoard": "Visão do quadro",
|
||||
"helpShortcutClearLogs": "Limpar registros (Utilitários)",
|
||||
"helpShortcutCopyEntry": "Copiar entrada de registro selecionada para a área de transferência (Registros)",
|
||||
"helpShortcutCopyToken": "Copiar token de autenticação para a área de transferência (Sistema)",
|
||||
"helpShortcutDeleteAgent": "Excluir agente — requer confirmação (Agentes)",
|
||||
"helpShortcutExpandEntry": "Expandir entrada de registro (Registros)",
|
||||
"helpShortcutExpandLog": "Expandir registro + liberar o mouse para seleção de texto (Registros)",
|
||||
"helpShortcutFiles": "Arquivos (quando não estiver em Registros); alterna o filtro de severidade de registro em Registros",
|
||||
"helpShortcutFirstLast": "Primeira / última entrada de registro (Registros)",
|
||||
"helpShortcutForceQuit": "Forçar saída",
|
||||
"helpShortcutGit": "Visão do Git",
|
||||
"helpShortcutHiddenFiles": "Alternar arquivos ocultos (Arquivos)",
|
||||
"helpShortcutJumpPanel": "Ir para o painel (Principal: Sistema/Registros/Estatísticas/Utilitários/Configurações)",
|
||||
"helpShortcutKillVitest": "Encerrar todos os processos do vitest (Utilitários)",
|
||||
"helpShortcutMain": "Principal (modo status)",
|
||||
"helpShortcutMouseToggle": "Alternância manual do modo mouse (automático: ativado para Registros/Arquivos/Git/Quadro, desativado nos demais)",
|
||||
"helpShortcutNavigate": "Navegar lista / entradas de registro",
|
||||
"helpShortcutNewTask": "Nova tarefa (Quadro)",
|
||||
"helpShortcutNextPanel": "Próximo painel (Principal; ↑/↓ rolar em Registros)",
|
||||
"helpShortcutOpenUrl": "Abrir URL do painel no navegador (Sistema)",
|
||||
"helpShortcutPrevPanel": "Painel anterior (Principal; ↑/↓ rolar em Registros)",
|
||||
"helpShortcutProjectPicker": "Seletor de projeto (Quadro, Arquivos)",
|
||||
"helpShortcutPushFetch": "Push / buscar (Git)",
|
||||
"helpShortcutQuit": "Sair",
|
||||
"helpShortcutRefreshStats": "Atualizar estatísticas (Utilitários)",
|
||||
"helpShortcutSettings": "Visão de configurações",
|
||||
"helpShortcutSwitchPane": "Trocar painel (Agentes, Configurações, Arquivos, Git)",
|
||||
"helpShortcutTabBackward": "Ciclar painel / painel em foco para trás",
|
||||
"helpShortcutTabForward": "Ciclar painel / painel em foco para frente",
|
||||
"helpShortcutToggleAutoKill": "Alternar encerramento automático do vitest sob pressão de memória (Utilitários)",
|
||||
"helpShortcutToggleBool": "Alternar booleano (Configurações)",
|
||||
"helpShortcutToggleHelp": "Alternar ajuda",
|
||||
"helpShortcutWordWrap": "Alternar quebra de linha (Registros / Arquivos)",
|
||||
"helpTitle": "ATALHOS DE TECLADO",
|
||||
"interactiveModeUnavailable": "Modo interativo indisponível — sem fonte de dados",
|
||||
"loading": "Carregando…",
|
||||
"loadingTasks": "Carregando tarefas…",
|
||||
"logsPanelTitle": "Registros",
|
||||
"narrowModeIndicator": "[estreito]",
|
||||
"noEntriesMatchFilter": "Nenhuma entrada corresponde ao filtro {{filter}}.",
|
||||
"noLogEntries": "Ainda não há entradas de registro.",
|
||||
"noTasks": "Não há tarefas neste projeto.",
|
||||
"projectSelectorChangeHint": "[p] trocar",
|
||||
"projectSelectorLabel": "Projeto:",
|
||||
"projectSelectorNavHints": "↑↓ navegar · Enter selecionar · Esc cancelar",
|
||||
"projectSelectorNone": "(nenhum)",
|
||||
"projectSelectorNoProjects": "(nenhum projeto registrado)",
|
||||
"projectSelectorPickTitle": "Escolha um projeto",
|
||||
"qrCloseHint": "[Esc] fechar",
|
||||
"qrGenerating": "Gerando QR…",
|
||||
"qrNoTunnelRunning": "Nenhum túnel remoto está em execução. Inicie um em Configurações (g).",
|
||||
"qrOverlayTitle": "Acesso Remoto — Escaneie para conectar",
|
||||
"readyIn": "Pronto em {{secs}}s",
|
||||
"runLogNone": "Nenhum registro capturado para esta execução.",
|
||||
"runLogResult": "resultado:",
|
||||
"runLogSource": "origem",
|
||||
"runLogStderr": "stderr:",
|
||||
"runLogStdout": "stdout:",
|
||||
"runLogTrigger": "gatilho",
|
||||
"runStatusActive": "Ativo",
|
||||
"runStatusCompleted": "Concluído",
|
||||
"runStatusFailed": "Falhou",
|
||||
"runStatusTerminated": "Encerrado",
|
||||
"runStatusUnknown": "Desconhecido",
|
||||
"settingsActivatedProvider": "Provedor ativado: {{provider}}",
|
||||
"settingsAdjust1": "[+/-] ajustar em 1",
|
||||
"settingsAdjust5000ms": "[+/-] ajustar em 5000ms",
|
||||
"settingsAvailableModelsHeader": "──── Modelos Disponíveis ────",
|
||||
"settingsBoolToggleHint": "[Espaço] alternar",
|
||||
"settingsCancelledTokenInput": "Entrada de token de curta duração cancelada",
|
||||
"settingsConfigureModelInDashboard": "Configure o modelo padrão no painel web",
|
||||
"settingsCurrentLabel": "Atual:",
|
||||
"settingsEditModelsTitle": "Editar / Modelos",
|
||||
"settingsEnterTtl": "Digite o TTL em milissegundos e pressione Enter",
|
||||
"settingsEnumCycleHint": "[←/→] alternar opções:",
|
||||
"settingsFooterHints": "[Tab] trocar painel ↑↓ selecionar configuração [Espaço] alternar bool [+/-] ajustar núm [←/→] alternar enum [C/V/X/P/L/U/K/R] ações remotas",
|
||||
"settingsInteractivePanelTitle": "Configurações",
|
||||
"settingsLoadingSettings": "Carregando configurações…",
|
||||
"settingsMoreModels_one": "… e mais {{count}}",
|
||||
"settingsMoreModels_other": "… e mais {{count}}",
|
||||
"settingsPanelTitle": "Configurações",
|
||||
"settingsPersistentTokenRegenerated": "Token persistente regenerado",
|
||||
"settingsQrFetched": "Payload do QR obtido",
|
||||
"settingsRemoteActions1": "[C] ativar provedor [V] iniciar [X] parar [P] token persistente [L] token de curta duração",
|
||||
"settingsRemoteActions2": "[U] transferir URL [K] transferir QR [R] atualizar",
|
||||
"settingsRemoteAuthUrl": "URL de autenticação:",
|
||||
"settingsRemoteHeader": "──── Remoto ────",
|
||||
"settingsRemoteOff": "desativado",
|
||||
"settingsRemoteOn": "ativado",
|
||||
"settingsRemotePersistentToken": "Token persistente:",
|
||||
"settingsRemoteProvider": "Provedor:",
|
||||
"settingsRemoteProviderNone": "nenhum",
|
||||
"settingsRemoteShortLived": "Curta duração:",
|
||||
"settingsRemoteShortLivedExpires": "Expiração de curta duração:",
|
||||
"settingsRemoteState": "Estado:",
|
||||
"settingsRemoteStateUnknown": "desconhecido",
|
||||
"settingsRemoteStatusRefreshed": "Status remoto atualizado",
|
||||
"settingsRemoteToken": "Token:",
|
||||
"settingsRemoteTunnelUrl": "URL do túnel:",
|
||||
"settingsRemoteUrlFetched": "URL remota obtida",
|
||||
"settingsSaved": "Salvo",
|
||||
"settingsSavingInProgress": "Salvando…",
|
||||
"settingsSelectProviderFirst": "Selecione primeiro um provedor remoto",
|
||||
"settingsShortLivedTokenGenerated": "Token de curta duração gerado",
|
||||
"settingsTtlHints": "[Enter] gerar [Esc] cancelar",
|
||||
"settingsTtlLabel": "TTL ms:",
|
||||
"settingsTtlMustBePositive": "O TTL deve ser um número positivo (ms)",
|
||||
"settingsTunnelStarting": "Túnel remoto iniciando",
|
||||
"settingsTunnelStopped": "Túnel remoto parado",
|
||||
"settingsUnavailable": "Configurações não disponíveis.",
|
||||
"statsPanelTitle": "Estatísticas",
|
||||
"statsUnavailable": "Estatísticas não disponíveis.",
|
||||
"statusBarHelp": "Tab alternar painel · 1-5 saltar",
|
||||
"stepDurationDone": " ({{status}} — {{duration}})",
|
||||
"stepDurationRunning": " (em execução — {{duration}})",
|
||||
"switchProjectsHint": "Pressione [{{key}}] para trocar de projeto.",
|
||||
"systemCopyTokenHint": "copiar token · selecione o texto do token para copiar manualmente",
|
||||
"systemDragToSelect": "arraste para selecionar",
|
||||
"systemInfoUnavailable": "Informações do sistema não disponíveis.",
|
||||
"systemOpenUrl": "abrir URL",
|
||||
"systemPanelTitle": "Sistema",
|
||||
"tabAgents": "Agentes",
|
||||
"tabBoard": "Quadro",
|
||||
"tabFiles": "Arquivos",
|
||||
"tabGit": "Git",
|
||||
"tabMain": "Principal",
|
||||
"tabSettings": "Configurações",
|
||||
"taskCardUntitled": "(sem título)",
|
||||
"taskDetailBack": "[Esc] voltar",
|
||||
"taskDetailLoading": "Carregando detalhes da tarefa…",
|
||||
"taskDetailLogsLive": "[ao vivo]",
|
||||
"taskDetailLogsPaused": "[pausado]",
|
||||
"taskDetailLogsSectionHeader": "── Registros ───────────────────────────────────────",
|
||||
"taskDetailNoLogEntries": "(ainda sem entradas de registro)",
|
||||
"taskDetailNoSteps": "(ainda sem etapas)",
|
||||
"taskDetailScrollHints": "↑↓/j/k rolar · PgUp/PgDn meia página · g topo · G fim · Esc voltar",
|
||||
"taskDetailStepsSectionHeader": "── Etapas ──────────────────────────────────────",
|
||||
"taskDetailUnavailable": "Tarefa não está mais disponível — Esc para voltar",
|
||||
"updateAvailable": "Atualização disponível: v{{currentVersion}} → v{{latestVersion}}. Execute `fn update` para um CLI instalado, ou dê pull neste checkout de código-fonte.",
|
||||
"utilitiesAdjustThreshold": "Ajustar Limite ({{pct}}%)",
|
||||
"utilitiesAutoKillVitest": "Auto-Encerrar Vitest >{{pct}}% Mem: {{state}}",
|
||||
"utilitiesClearLogs": "Limpar Registros",
|
||||
"utilitiesHelp": "Ajuda",
|
||||
"utilitiesKillVitest": "Encerrar Processos Vitest",
|
||||
"utilitiesPanelTitle": "Utilitários",
|
||||
"utilitiesRefreshStats": "Atualizar Estatísticas",
|
||||
"utilitiesToggleEnginePause": "Alternar Pausa do Motor"
|
||||
}
|
||||
}
|
||||
244
packages/i18n/locales/pt-BR/common.json
Normal file
244
packages/i18n/locales/pt-BR/common.json
Normal file
@@ -0,0 +1,244 @@
|
||||
{
|
||||
"actions": {
|
||||
"send": "Enviar"
|
||||
},
|
||||
"agents": {
|
||||
"ratings": {
|
||||
"trendDeclining": "↓ Em declínio",
|
||||
"trendImproving": "↑ Melhorando",
|
||||
"trendInsufficient": "Dados insuficientes",
|
||||
"trendStable": "→ Estável"
|
||||
},
|
||||
"reflections": {
|
||||
"triggerManual": "Manual",
|
||||
"triggerPeriodic": "Periódico",
|
||||
"triggerPostTask": "Pós-tarefa",
|
||||
"triggerUserRequested": "Solicitado pelo usuário"
|
||||
},
|
||||
"time": {
|
||||
"daysAgo_one": "há {{count}}d",
|
||||
"daysAgo_other": "há {{count}}d",
|
||||
"hoursAgo_one": "há {{count}}h",
|
||||
"hoursAgo_other": "há {{count}}h",
|
||||
"inAMoment": "em instantes",
|
||||
"inDays_one": "em {{count}}d",
|
||||
"inDays_other": "em {{count}}d",
|
||||
"inHours_one": "em {{count}}h",
|
||||
"inHours_other": "em {{count}}h",
|
||||
"inMinutes_one": "em {{count}}m",
|
||||
"inMinutes_other": "em {{count}}m",
|
||||
"justNow": "agora mesmo",
|
||||
"minutesAgo_one": "há {{count}}m",
|
||||
"minutesAgo_other": "há {{count}}m"
|
||||
}
|
||||
},
|
||||
"board": {
|
||||
"rejection": {
|
||||
"capacityExhausted": "Essa coluna está com capacidade esgotada. Tente novamente quando uma vaga se abrir.",
|
||||
"guardRejected": "Este movimento não é permitido pelo fluxo de trabalho.",
|
||||
"mergeBlocked": "Esta tarefa está bloqueada para conclusão até que sua etapa de merge termine.",
|
||||
"unknownColumn": "Essa coluna não existe no fluxo de trabalho desta tarefa.",
|
||||
"unplannedForExecution": "Esta tarefa ainda não está pronta para execução — o planejamento ou a revisão do plano ainda está pendente.",
|
||||
"workflowMismatch": "Arrastar não pode mover um cartão entre fluxos de trabalho. Use o seletor de fluxo de trabalho."
|
||||
}
|
||||
},
|
||||
"chat": {
|
||||
"failedToGetResponse": "Falha ao obter resposta",
|
||||
"failureReferenceId": "ID",
|
||||
"failureReferenceKind": "Tipo",
|
||||
"failureReferenceLabel": "Referência",
|
||||
"failureReferenceMetaLabel": "Rótulo",
|
||||
"openMailboxMessage": "Abrir mensagem da caixa de entrada",
|
||||
"toolCallArgsPrefix": "args",
|
||||
"toolCallResultPrefix": "resultado",
|
||||
"toolCallsCount_one": "{{count}} chamada de ferramenta",
|
||||
"toolCallsCount_other": "{{count}} chamadas de ferramenta",
|
||||
"toolCallsHeader": "Chamadas de ferramenta",
|
||||
"toolCallStatusCompleted": "concluído",
|
||||
"toolCallStatusError": "erro",
|
||||
"toolCallStatusErrors": "erros",
|
||||
"toolCallStatusRunning": "em execução",
|
||||
"viewFailureDetails": "Ver detalhes da falha"
|
||||
},
|
||||
"commandCenter": {
|
||||
"range": {
|
||||
"allTime": "Todo o período",
|
||||
"last24h": "Últimas 24h",
|
||||
"last30d": "Últimos 30 dias",
|
||||
"last7d": "Últimos 7 dias"
|
||||
}
|
||||
},
|
||||
"health": {
|
||||
"anomaly": {
|
||||
"duplicateActiveId": "ID de tarefa ativa duplicado",
|
||||
"idInBothStorages": "ID de tarefa presente no armazenamento ativo e arquivado",
|
||||
"sequenceOverlap": "A próxima sequência do alocador sobrepõe um ID de tarefa existente",
|
||||
"unknownPrefix": "Linha de tarefa usa um prefixo fora do estado do alocador"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"connecting": "Conectando",
|
||||
"error": "Erro",
|
||||
"offline": "Offline",
|
||||
"online": "Online"
|
||||
},
|
||||
"labels": {
|
||||
"name": "Nome"
|
||||
},
|
||||
"merge": {
|
||||
"unknown": "Desconhecido"
|
||||
},
|
||||
"missions": {
|
||||
"autopilotStateActivating": "Ativando fatia",
|
||||
"autopilotStateCompleting": "Concluindo",
|
||||
"autopilotStateInactive": "Inativo",
|
||||
"autopilotStateUnknown": "Desconhecido",
|
||||
"autopilotStateWatching": "Observando",
|
||||
"interviewStatusAwaitingInput": "Aguardando entrada",
|
||||
"interviewStatusComplete": "Plano pronto",
|
||||
"interviewStatusError": "Precisa de nova tentativa",
|
||||
"interviewStatusGenerating": "Gerando plano",
|
||||
"runHelperActive": "Ao parar, as tarefas vinculadas são pausadas e a missão é marcada como bloqueada.",
|
||||
"runHelperBlocked": "Ao retomar, a missão é reativada e a execução continua.",
|
||||
"runHelperPlanning": "Ao iniciar, a primeira fatia é ativada para que o trabalho possa começar."
|
||||
},
|
||||
"models": {
|
||||
"messages": {
|
||||
"modelSetTo": "Modelo de {{label}} definido para {{provider}}/{{modelId}}",
|
||||
"modelSetToDefault": "Modelo de {{label}} definido para o padrão"
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
"auth": {
|
||||
"differ": "As credenciais de autenticação são diferentes",
|
||||
"differProviders": "As credenciais de autenticação são diferentes: {{providers}}",
|
||||
"match": "As credenciais de autenticação coincidem",
|
||||
"notSynced": "Autenticação não sincronizada"
|
||||
},
|
||||
"status": {
|
||||
"connecting": "Conectando",
|
||||
"creating": "Criando",
|
||||
"deleting": "Excluindo",
|
||||
"error": "Erro",
|
||||
"exited": "Encerrado",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"recreating": "Recriando",
|
||||
"running": "Em execução",
|
||||
"stopped": "Parado"
|
||||
}
|
||||
},
|
||||
"nodeStatus": {
|
||||
"connecting": "Conectando",
|
||||
"error": "Erro",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"unknown": "Desconhecido"
|
||||
},
|
||||
"research": {
|
||||
"providerGitHub": "GitHub",
|
||||
"providerLlmSynthesis": "Síntese por LLM",
|
||||
"providerLocalDocs": "Documentos locais",
|
||||
"providerPageFetch": "Busca de página",
|
||||
"providerWebSearch": "Pesquisa na Web"
|
||||
},
|
||||
"routing": {
|
||||
"policyLabel": {
|
||||
"block": "Bloquear execução",
|
||||
"fallback": "Recorrer ao local",
|
||||
"notConfigured": "Não configurado"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"nodeRouting": {
|
||||
"statusConnecting": "Conectando",
|
||||
"statusError": "Erro",
|
||||
"statusOffline": "Offline",
|
||||
"statusOnline": "Online"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"apiKeyFormatError": "As chaves da {{providerName}} devem seguir este formato: {{hint}} (ex.: {{example}})",
|
||||
"apiKeyLabel": {
|
||||
"fallback": "Chave de API",
|
||||
"kimiCoding": "Chave de API do Kimi",
|
||||
"minimax": "Chave de API do MiniMax",
|
||||
"ollama": "Endpoint do Ollama",
|
||||
"openai": "Chave de API da OpenAI",
|
||||
"openrouter": "Chave de API do OpenRouter",
|
||||
"zai": "Chave de API da Zhipu AI"
|
||||
},
|
||||
"apiKeyPlaceholder": {
|
||||
"fallback": "Digite a chave de API",
|
||||
"kimiCoding": "Digite sua chave de API do Kimi",
|
||||
"minimax": "Digite sua chave de API do MiniMax",
|
||||
"zai": "Digite sua chave de API da Zhipu AI"
|
||||
},
|
||||
"apiKeyRequired": "A chave de API é obrigatória",
|
||||
"apiKeySetup": {
|
||||
"fallback": "Digite sua chave de API para este provedor.",
|
||||
"kimiCoding": "Crie sua chave de API nas configurações de conta da plataforma Moonshot.",
|
||||
"minimax": "Gere uma chave de API no console de desenvolvedor da plataforma MiniMax.",
|
||||
"ollama": "Digite a URL do endpoint do Ollama (por exemplo, http://localhost:11434).",
|
||||
"openai": "Crie uma chave de API no painel da OpenAI, em API keys.",
|
||||
"openrouter": "Crie uma chave de API na página de gerenciamento de chaves da sua conta OpenRouter.",
|
||||
"zai": "Crie uma chave de API nas configurações de conta da plataforma aberta da Zhipu AI."
|
||||
},
|
||||
"apiKeyUsage": {
|
||||
"fallback": "Usada pelo Fusion para autenticar solicitações a este provedor",
|
||||
"kimiCoding": "Usada para modelos Kimi/Moonshot AI na execução e no planejamento de tarefas",
|
||||
"minimax": "Usada para modelos MiniMax na execução de tarefas",
|
||||
"ollama": "Conecta à sua instância local do Ollama",
|
||||
"openai": "Usada para modelos GPT na execução e no planejamento de tarefas",
|
||||
"openrouter": "Roteia para múltiplos provedores de modelos de IA através de uma única chave",
|
||||
"zai": "Usada para modelos GLM na execução de tarefas"
|
||||
},
|
||||
"providerDesc": {
|
||||
"anthropic": "Modelos Claude — fortes em raciocínio, análise e código",
|
||||
"fallback": "Provedor de IA — conecte-se para começar a usar modelos de IA",
|
||||
"gemini": "Modelos Gemini — multimodais com forte raciocínio",
|
||||
"google": "Modelos Gemini — multimodais com forte raciocínio",
|
||||
"kimi": "Kimi da Moonshot AI — capacidades de contexto longo",
|
||||
"kimiCoding": "Kimi da Moonshot AI — capacidades de contexto longo",
|
||||
"minimax": "Modelos MiniMax — custo-benefício para uso de alto volume",
|
||||
"moonshot": "Kimi da Moonshot AI — capacidades de contexto longo",
|
||||
"ollama": "Execute modelos de código aberto localmente na sua máquina",
|
||||
"openai": "Modelos GPT — versáteis para uma ampla variedade de tarefas",
|
||||
"openaiCodex": "Modelos Codex da OpenAI — otimizados para tarefas de programação",
|
||||
"openrouter": "OpenRouter — roteia solicitações entre múltiplos provedores de IA",
|
||||
"zai": "Modelos GLM da Zhipu AI — forte suporte multilíngue"
|
||||
}
|
||||
},
|
||||
"taskForm": {
|
||||
"nodeStatusConnecting": "Conectando",
|
||||
"nodeStatusError": "Erro",
|
||||
"nodeStatusOffline": "Offline",
|
||||
"nodeStatusOnline": "Online"
|
||||
},
|
||||
"taskReview": {
|
||||
"never": "Nunca",
|
||||
"refreshSourceBackground": "Segundo plano",
|
||||
"refreshSourceInitialLoad": "Carregamento inicial",
|
||||
"refreshSourceManual": "Manual"
|
||||
},
|
||||
"workflowNodes": {
|
||||
"summaryAwaitInput": "Aguarda entrada do usuário",
|
||||
"summaryCodeDefault": "TypeScript",
|
||||
"summaryDefaultModel": "Modelo padrão",
|
||||
"summaryGateAdvisory": "Consultivo",
|
||||
"summaryGateBlocks": "Gate (bloqueia)",
|
||||
"summaryHoldRelease": "Liberação: {{release}}",
|
||||
"summaryLoopIterations_one": "{{count}}x",
|
||||
"summaryLoopIterations_other": "{{count}}x",
|
||||
"summaryLoopUntilContains": "até conter \"{{value}}\"",
|
||||
"summaryLoopUntilMatches": "até corresponder a /{{pattern}}/",
|
||||
"summaryNotConfigured": "Não configurado",
|
||||
"summaryReviewType": "revisão de {{type}}",
|
||||
"summarySeamExecute": "Executar (motor)",
|
||||
"summarySeamMerge": "Limite de merge",
|
||||
"summarySeamPlanning": "Planejar (motor)",
|
||||
"summarySeamReview": "Revisar (motor)",
|
||||
"summarySeamStepExecute": "Executar etapa (motor)",
|
||||
"summarySeamUnknown": "Seam: {{seam}}"
|
||||
}
|
||||
}
|
||||
1
packages/i18n/locales/pt-BR/errors.json
Normal file
1
packages/i18n/locales/pt-BR/errors.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -33,6 +33,8 @@ describe("@fusion/i18n config", () => {
|
||||
expect(FALLBACK_LNG.zh).toEqual(["zh-CN"]);
|
||||
expect(FALLBACK_LNG["zh-Hans"]).toEqual(["zh-CN"]);
|
||||
expect(FALLBACK_LNG["zh-Hant"]).toEqual(["zh-TW"]);
|
||||
expect(FALLBACK_LNG.pt).toEqual(["pt-BR"]);
|
||||
expect(FALLBACK_LNG["pt-PT"]).toEqual(["pt-BR"]);
|
||||
expect(FALLBACK_LNG.default).toEqual(["en"]);
|
||||
});
|
||||
|
||||
@@ -94,4 +96,11 @@ describe("normalizeToSupportedLocale", () => {
|
||||
expect(normalizeToSupportedLocale("de-DE")).toBeUndefined();
|
||||
expect(normalizeToSupportedLocale("")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("resolves any-region Portuguese to pt-BR", () => {
|
||||
expect(normalizeToSupportedLocale("pt")).toBe("pt-BR");
|
||||
expect(normalizeToSupportedLocale("pt-PT")).toBe("pt-BR");
|
||||
expect(normalizeToSupportedLocale("pt_PT.UTF-8")).toBe("pt-BR");
|
||||
expect(normalizeToSupportedLocale("ptx")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const locales = ["en", "es", "fr", "ko", "zh-CN", "zh-TW"] as const;
|
||||
const locales = ["en", "es", "fr", "ko", "pt-BR", "zh-CN", "zh-TW"] as const;
|
||||
|
||||
describe("database health banner catalogs", () => {
|
||||
it.each(locales)("uses renderable rich-text guidance in %s", (locale) => {
|
||||
|
||||
@@ -25,7 +25,7 @@ function readCatalogs(locale: string, namespaces = listNamespaces()): NamespaceC
|
||||
|
||||
describe("i18n key parity", () => {
|
||||
it("passes for the live catalogs across all supported locales and en namespaces", () => {
|
||||
expect([...SUPPORTED_LOCALES]).toEqual(["en", "zh-CN", "zh-TW", "fr", "es", "ko"]);
|
||||
expect([...SUPPORTED_LOCALES]).toEqual(["en", "zh-CN", "zh-TW", "fr", "es", "ko", "pt-BR"]);
|
||||
expect(listNamespaces()).toEqual(["app", "cli", "common", "errors"]);
|
||||
|
||||
const enCatalogs = readCatalogs("en");
|
||||
|
||||
@@ -12,6 +12,9 @@ import fr_errors from "../locales/fr/errors.json";
|
||||
import ko_common from "../locales/ko/common.json";
|
||||
import ko_cli from "../locales/ko/cli.json";
|
||||
import ko_errors from "../locales/ko/errors.json";
|
||||
import pt_BR_common from "../locales/pt-BR/common.json";
|
||||
import pt_BR_cli from "../locales/pt-BR/cli.json";
|
||||
import pt_BR_errors from "../locales/pt-BR/errors.json";
|
||||
import zh_CN_common from "../locales/zh-CN/common.json";
|
||||
import zh_CN_cli from "../locales/zh-CN/cli.json";
|
||||
import zh_CN_errors from "../locales/zh-CN/errors.json";
|
||||
@@ -40,6 +43,11 @@ export const cliResources = {
|
||||
cli: ko_cli,
|
||||
errors: ko_errors,
|
||||
},
|
||||
"pt-BR": {
|
||||
common: pt_BR_common,
|
||||
cli: pt_BR_cli,
|
||||
errors: pt_BR_errors,
|
||||
},
|
||||
"zh-CN": {
|
||||
common: zh_CN_common,
|
||||
cli: zh_CN_cli,
|
||||
|
||||
@@ -35,6 +35,8 @@ export const FALLBACK_LNG: FallbackLngObjList = {
|
||||
"zh-Hans": ["zh-CN"],
|
||||
"zh-Hant": ["zh-TW"],
|
||||
zh: ["zh-CN"],
|
||||
pt: ["pt-BR"],
|
||||
"pt-PT": ["pt-BR"],
|
||||
default: [DEFAULT_LOCALE],
|
||||
};
|
||||
|
||||
@@ -57,6 +59,16 @@ export function normalizeToSupportedLocale(tag: string): Locale | undefined {
|
||||
if (isLocale(norm)) return norm;
|
||||
|
||||
const lower = norm.toLowerCase();
|
||||
/*
|
||||
FNXC:I18nLocales 2026-08-07-22:55:
|
||||
normalizeToSupportedLocale had an explicit zh branch but no pt one, so the FALLBACK_LNG
|
||||
pt/pt-PT entries were dead on both the CLI env-detection and dashboard browser-detection
|
||||
paths (both funnel through this function, which returned undefined for base "pt"). Any
|
||||
Portuguese region tag (pt, pt-PT, pt_PT.UTF-8) resolves to pt-BR, the only Portuguese
|
||||
catalog Fusion ships; exact "pt-BR" already matches via the isLocale(norm) check above.
|
||||
*/
|
||||
const base = lower.split("-")[0];
|
||||
if (base === "pt") return "pt-BR";
|
||||
if (lower.startsWith("zh")) {
|
||||
// An explicit Simplified script subtag wins over region: zh-Hans-HK /
|
||||
// zh-Hans-MO are valid BCP-47 for Simplified Chinese used in HK/Macau.
|
||||
@@ -72,7 +84,6 @@ export function normalizeToSupportedLocale(tag: string): Locale | undefined {
|
||||
return "zh-CN";
|
||||
}
|
||||
|
||||
const base = lower.split("-")[0];
|
||||
return isLocale(base) ? base : undefined;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user