snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
---
title: CLI
description: comandos `yarn twenty` para executar funções, transmitir logs, gerenciar instalações de aplicativos e alternar remotos.
icon: terminal
---
A CLI `yarn twenty` é a sua interface para tudo relacionado a aplicativos. Lista completa de comandos:
| Comando | O que faz | Documentado em |
| ----------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `dev` | Monitora arquivos-fonte e sincroniza alterações em tempo real | [Início rápido](/l/pt/developers/extend/apps/getting-started/quick-start) |
| `plan` | Visualize as alterações de metadados sem aplicá-las | [Sincronização e recuperação](/l/pt/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
| `apply` | Aplicar alterações de metadados após exibir o plano | [Sincronização e recuperação](/l/pt/developers/extend/apps/operations/sync-and-recovery) |
| `dev:build` | Compile o aplicativo e gere o cliente da API (`--tarball` para empacotar um `.tgz`) | [Publicação](/l/pt/developers/extend/apps/operations/publishing) |
| `dev:typecheck` | Executar a verificação de tipos do TypeScript | [Testes](/l/pt/developers/extend/apps/operations/testing) |
| `dev:add` | Criar a estrutura inicial de uma nova entidade | [Scaffolding](/l/pt/developers/extend/apps/getting-started/scaffolding) |
| `dev:generate-client` | Regenerar o cliente da API tipado | esta página |
| `dev:function:exec` / `dev:function:logs` | Executar funções e transmitir seus logs | esta página |
| `dev:translations-extract` | Extrair strings traduzíveis para catálogos em `locales/` | [Traduções](/l/pt/developers/extend/apps/operations/translations) |
| `dev:catalog-sync` | Acionar a sincronização do catálogo do marketplace | [Publicação](/l/pt/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
| `app:publish` / `app:install` / `app:uninstall` | Ciclo de vida de versões | [Publicação](/l/pt/developers/extend/apps/operations/publishing) e esta página |
| `docker:*` | Gerenciar o contêiner do servidor local do Twenty | [Servidor local](/l/pt/developers/extend/apps/getting-started/local-server) |
| `remote:*` | Gerenciar conexões de servidor | esta página |
Todo comando aceita `-r, --remote \<name>` para direcionar a um remoto específico em vez do padrão.
## Executando funções (`yarn twenty dev:function:exec`)
Execute manualmente uma função de lógica sem acioná-la via HTTP, cron ou evento de banco de dados:
```bash filename="Terminal"
# Execute by function name
yarn twenty dev:function:exec -n create-new-post-card
# Execute by universalIdentifier
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
# Execute the install and uninstall hooks
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
yarn twenty dev:function:exec --uninstall
```
## Visualizando logs de funções (`yarn twenty dev:function:logs`)
Transmita os logs de execução das funções de lógica do seu aplicativo:
```bash filename="Terminal"
# Stream all function logs
yarn twenty dev:function:logs
# Filter by function name
yarn twenty dev:function:logs -n create-new-post-card
# Filter by universalIdentifier
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
<Note>
Isso é diferente de `yarn twenty docker:logs`, que mostra os logs do contêiner Docker. `yarn twenty dev:function:logs` mostra os logs de execução de funções do seu aplicativo a partir do servidor Twenty.
</Note>
## Gerando o cliente tipado (`yarn twenty dev:generate-client`)
Regenerar o cliente de API tipado (`twenty-client-sdk`) a partir do esquema do remoto ativo, sem compilar nem sincronizar um aplicativo. Use-o para obter um cliente tipado em qualquer projeto — como um serviço de backend em um repositório separado — que se comunica com a sua instância do Twenty:
```bash filename="Terminal"
# In your project (no Twenty app definition required)
yarn add twenty-sdk twenty-client-sdk
# Connect to the Twenty instance to generate the client from
yarn twenty remote:add
# Generate the typed client into node_modules/twenty-client-sdk
yarn twenty dev:generate-client
```
Em seguida, importe o cliente no seu código:
```typescript
import { CoreApiClient } from 'twenty-client-sdk/core';
```
Execute novamente o comando sempre que o seu modelo de dados for alterado para atualizar os tipos gerados.
<Note>
O cliente é gerado dentro de `node_modules`, portanto não é incluído no commit junto com o seu código. Execute `yarn twenty dev:generate-client` após cada instalação (por exemplo, em um script `postinstall` ou no CI).
</Note>
## Desinstalando um aplicativo (`yarn twenty app:uninstall`)
Remova seu aplicativo do espaço de trabalho ativo:
```bash filename="Terminal"
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty app:uninstall --yes
```
## Gerenciando remotos
Um **remoto** é um servidor Twenty ao qual seu aplicativo se conecta. Durante a configuração, o gerador de scaffold cria um para você automaticamente. Você pode adicionar mais remotos ou alternar entre eles a qualquer momento.
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote:add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote:add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote:list
# Set the active remote
yarn twenty remote:use <name>
# Check that the active remote's authentication is still valid
yarn twenty remote:status
# Remove a remote
yarn twenty remote:remove <name>
```
Suas credenciais são armazenadas em `~/.twenty/config.json`.

View File

@@ -0,0 +1,32 @@
---
title: Visão Geral
description: Crie, teste e envie seu app — comandos de CLI, testes de integração, CI e publicação em um servidor ou no npm.
icon: rocket
---
A **camada de operações** é tudo o que você faz *para* o seu app em vez de *com* ele: invocar comandos de CLI, executar testes de integração contra um servidor Twenty real, configurar CI e enviar versões — seja como um tarball implantado em um único servidor ou como um pacote npm listado no marketplace.
```text
develop ─▶ test ─▶ build ─▶ deploy / publish
─────── ──── ───── ─────────────────
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
twenty test twenty
dev dev:build yarn twenty app:publish (npm → marketplace)
```
## Nesta secção
<CardGroup cols={2}>
<Card title="CLI" icon="terminal" href="/l/pt/developers/extend/apps/operations/cli">
Referência do `yarn twenty` — exec, logs, uninstall, remotes.
</Card>
<Card title="Sincronização e recuperação" icon="compass" href="/l/pt/developers/extend/apps/operations/sync-and-recovery">
Qual comando usar e quando, leitura do diff de sincronização e etapas de recuperação.
</Card>
<Card title="Testes" icon="flask" href="/l/pt/developers/extend/apps/operations/testing">
Configuração do Vitest, testes de integração, verificação de tipos, fluxo de trabalho de CI.
</Card>
<Card title="Publicação" icon="upload" href="/l/pt/developers/extend/apps/operations/publishing">
Criar, implantar um tarball, publicar no npm, instalar.
</Card>
</CardGroup>

View File

@@ -0,0 +1,293 @@
---
title: Publicação
icon: upload
description: Distribua seu aplicativo Twenty no Marketplace ou implante-o internamente.
---
## Visão Geral
Depois que seu aplicativo estiver [compilado e testado localmente](/l/pt/developers/extend/apps/getting-started/concepts), você tem dois caminhos para distribuí-lo:
* **Implantar um tarball** — envie seu aplicativo diretamente para um servidor Twenty específico para uso interno ou privado.
* **Publicar no npm** — liste seu aplicativo no Marketplace da Twenty para que qualquer espaço de trabalho possa descobrir e instalar.
Ambos os caminhos começam na mesma etapa de **build**.
## Compilando seu app
Execute o comando build para compilar seu app e gerar um `manifest.json` pronto para distribuição:
```bash filename="Terminal"
yarn twenty dev:build
```
Isso compila seu código-fonte em TypeScript, transpila funções de lógica e componentes de front-end e grava tudo em `.twenty/output/`. Adicione `--tarball` para também gerar um pacote `.tgz` para distribuição manual ou para o comando de publish.
## Implantando em um servidor (tarball)
Para aplicativos que você não quer disponibilizar publicamente — ferramentas proprietárias, integrações apenas para empresas ou builds experimentais — você pode implantar um tarball diretamente em um servidor Twenty.
### Pré-requisitos
Antes de implantar, você precisa de um remote configurado apontando para o servidor de destino. Os remotes armazenam a URL do servidor e as credenciais de autenticação localmente em `~/.twenty/config.json`.
Adicionar um remote:
```bash filename="Terminal"
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### Implantando
Compile e envie seu aplicativo para o servidor em uma única etapa:
```bash filename="Terminal"
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty app:publish --private --remote production
```
### Compartilhando um aplicativo implantado
Aplicativos em tarball não são listados no marketplace público, então outros espaços de trabalho no mesmo servidor não os descobrirão ao navegar. Para compartilhar um aplicativo implantado:
1. Vá para **Configurações > Aplicações > Registros** e abra seu aplicativo
2. Na guia **Distribuição**, clique em **Copiar link de compartilhamento**
3. Compartilhe esse link com usuários de outros espaços de trabalho — ele os leva diretamente para a página de instalação do aplicativo
O link de compartilhamento usa a URL base do servidor (sem qualquer subdomínio de espaço de trabalho), para funcionar em qualquer espaço de trabalho no servidor.
### Gerenciamento de versões
Ao atualizar um aplicativo empacotado como tarball já implantado, o servidor exige que o `version` no `package.json` seja **estritamente maior** (de acordo com a ordenação do [semver](https://semver.org)) do que a versão atualmente implantada. Reimplantar a mesma versão, ou enviar uma inferior, é rejeitado antes que o tarball seja armazenado — você verá um erro `VERSION_ALREADY_EXISTS` na CLI.
Para lançar uma atualização:
1. Atualize o campo `version` no seu `package.json` (por exemplo, `1.2.3` → `1.2.4`, `1.3.0` ou `2.0.0`)
2. Execute `yarn twenty app:publish --private` (ou `yarn twenty app:publish --private --remote production`)
3. Espaços de trabalho que têm o app instalado e ativaram a atualização automática para o app (na aba Configurações do app) são atualizados automaticamente em segundo plano; os demais verão a atualização disponível em suas configurações
<Note>
Tags de pré-lançamento funcionam como esperado: incrementar `1.0.0-rc.1` → `1.0.0-rc.2` é permitido, e uma versão final como `1.0.0` é corretamente reconhecida como superior a `1.0.0-rc.5`. A versão em `package.json` deve ser, ela própria, uma string semver válida.
</Note>
{/* TODO: add screenshot of the Upgrade button */}
### Compatibilidade da versão do servidor
Se o seu aplicativo usar um recurso introduzido em uma versão específica do servidor Twenty (por exemplo, provedores OAuth adicionados na v2.3.0), você deve declarar a versão mínima do servidor que seu aplicativo requer usando o campo `engines.twenty` em `package.json`:
```json filename="package.json"
{
"name": "twenty-my-app",
"version": "1.0.0",
"engines": {
"node": "^24.5.0",
"twenty": ">=2.3.0"
}
}
```
O valor é um [intervalo semver](https://github.com/npm/node-semver#ranges) padrão. Padrões comuns:
| Intervalo | Significado |
| ---------------------------------- | ---------------------------------------------------------- |
| `>=2.3.0` | Qualquer servidor a partir de 2.3.0 |
| `>=2.3.0 \<3.0.0` | 2.3.0 ou posterior, mas abaixo da próxima versão principal |
| `^2.3.0` | O mesmo que `>=2.3.0 \<3.0.0` |
**O que acontece no momento da implantação e da instalação:**
* Se `engines.twenty` estiver definido e a versão do servidor de destino não satisfizer o intervalo, a implantação (upload do tarball) ou a instalação será rejeitada com o erro `SERVER_VERSION_INCOMPATIBLE` e uma mensagem indicando tanto o intervalo exigido quanto a versão real do servidor.
* Se `engines.twenty` **não estiver definido**, o aplicativo é aceito em qualquer versão do servidor (retrocompatível com os aplicativos existentes).
* Se o servidor não tiver `APP_VERSION` configurado, a verificação será ignorada.
<Note>
O servidor realiza a verificação definitiva — ele valida `engines.twenty` tanto no upload do tarball quanto na instalação no espaço de trabalho. Se você implantar um tarball fora de banda ou instalar a partir do marketplace, o servidor ainda impõe a compatibilidade.
</Note>
## CI/CD automatizado (fluxos de trabalho pré-configurados)
Os apps gerados com `create-twenty-app` já vêm com três fluxos de trabalho do GitHub Actions prontos, em `.github/workflows/`. A CI é executada sem nenhuma configuração, a CD requer um único segredo e a publicação no npm exige uma configuração única de trusted-publisher do npm.
### CI — `ci.yml`
Executa testes de integração a cada push para `main` e a cada pull request.
**O que faz:**
1. Faz checkout do código-fonte do seu app.
2. Inicia uma instância de teste do Twenty isolada usando a ação composta `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (o equivalente em CI de `yarn twenty docker:start --test`).
3. Habilita o Corepack, configura o Node.js a partir do seu `.nvmrc` e instala as dependências com `yarn install --immutable`.
4. Executa `yarn test`, passando `TWENTY_API_URL` e `TWENTY_API_KEY` da instância iniciada para que seus testes possam se comunicar com um servidor real.
**Opções de configuração:**
* `TWENTY_VERSION` (env, padrão `latest`) — fixe a versão do servidor Twenty usada no CI editando isto em `ci.yml`.
* A concorrência é agrupada por `github.ref` e cancela execuções em andamento quando há novos pushes.
Nenhum segredo é necessário — a instância de teste é efêmera e existe apenas durante a execução do job.
### CD — `cd.yml`
Faz o deploy do seu app para um servidor Twenty configurado a cada push para `main` e, opcionalmente, a partir de um pull request quando o rótulo `deploy` é aplicado.
**O que faz:**
1. Faz checkout do head do PR (para PRs rotulados) ou do commit enviado.
2. Executa `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — o equivalente em CI de `yarn twenty app:publish --private`.
3. Executa `twentyhq/twenty/.github/actions/install-twenty-app@main` para que a versão recém-implantada seja instalada no espaço de trabalho de destino.
**Configuração obrigatória:**
| Configuração | Onde | Finalidade |
| ----------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `TWENTY_DEPLOY_URL` | `env` em `cd.yml` (padrão `http://localhost:3000`) | O servidor Twenty para o qual fazer o deploy. Altere isto para a URL real do seu servidor antes do primeiro uso. |
| `TWENTY_DEPLOY_API_KEY` | Repositório do GitHub **Settings → Secrets and variables → Actions** | Chave de API com permissão de deploy no servidor de destino. |
<Note>
O `TWENTY_DEPLOY_URL` padrão de `http://localhost:3000` é um placeholder — ele não alcançará nada a partir de um runner hospedado pelo GitHub. Atualize-o para a URL pública do seu servidor (ou use um runner self-hosted com acesso à rede) antes de habilitar o CD.
</Note>
**Acionando um deploy de pré-visualização a partir de um PR:**
Adicione o rótulo `deploy` a um pull request. A condição `if:` em `cd.yml` executará o job para esse PR usando o commit HEAD do PR, permitindo que você valide uma alteração no servidor de destino antes de fazer o merge.
### Publicar — `publish.yml`
Publica seu app no npm com procedência quando você envia uma tag de versão (por exemplo, `v1.0.0`), ou quando executa o fluxo de trabalho manualmente na aba Actions.
**O que faz:**
1. Faz o checkout do seu app, configura o Node.js e atualiza o npm (a publicação confiável requer o npm 11.5.1 ou posterior).
2. Executa `yarn twenty app:publish`, que compila o app e publica `.twenty/output` no npm. Em CI, adiciona automaticamente `--provenance` e `--access public`, portanto não são necessárias flags no fluxo de trabalho.
**Configuração única:**
Em npmjs.com, abra o seu pacote > **Settings → Trusted Publisher** e registre este repositório com o fluxo de trabalho `publish.yml` (consulte a [documentação de publicação confiável do npm](https://docs.npmjs.com/trusted-publishers)). Publicar com proveniência certifica qual repositório do GitHub compilou o pacote, o que também é como você reivindica a propriedade do seu app em um marketplace da Twenty.
<Note>
npm só aceita proveniência de repositórios de código-fonte **públicos**. Se você publicar a partir de um repositório privado, o npm rejeita o pacote de proveniência OIDC com um `E422 ... Erro: `Unsupported GitHub Actions source repository visibility: "private"`. Para publicar a partir de um repositório privado, desative a proveniência definindo `TWENTY_APP_PUBLISH_DISABLE_PROVENANCE: 'true'`no`env`da etapa de publicação (uma dica comentada está incluída no`publish.yml\` gerado pelo scaffold):
```yaml filename=".github/workflows/publish.yml"
- name: Publish to npm
env:
TWENTY_APP_PUBLISH_DISABLE_PROVENANCE: 'true'
run: yarn twenty app:publish
```
</Note>
### Fixando as ações reutilizáveis
Os fluxos de trabalho `ci.yml` e `cd.yml` fazem referência a ações reutilizáveis em `@main`, portanto as atualizações de ações no repositório `twentyhq/twenty` são aplicadas automaticamente. Se você quiser builds determinísticos, substitua `@main` por um SHA de commit ou uma tag de release em cada linha `uses:`.
## Publicação no npm
Publicar no npm torna seu aplicativo descobrível no Marketplace da Twenty. Qualquer espaço de trabalho da Twenty pode navegar, instalar e atualizar aplicativos do Marketplace diretamente pela UI.
### Requisitos
* Uma conta no [npm](https://www.npmjs.com)
* A palavra-chave `twenty-app` no array `keywords` do seu `package.json` (adicione-a manualmente — não é incluída por padrão no template `create-twenty-app`)
```json filename="package.json"
{
"name": "twenty-app-postcard-sender",
"version": "1.0.0",
"keywords": ["twenty-app"]
}
```
### Metadados do Marketplace
A configuração `defineApplication()` oferece suporte a campos opcionais que controlam como seu app aparece no marketplace. Use `logo` e `galleryImages` para referenciar imagens da pasta `public/`:
```ts src/application-config.ts
export default defineApplication({
universalIdentifier: '...',
displayName: 'My App',
description: 'A great app',
logo: 'public/logo.png',
galleryImages: [
'public/screenshot-1.png',
'public/screenshot-2.png',
],
});
```
Veja o [acordeão de defineApplication](/l/pt/developers/extend/apps/config/application#marketplace-metadata) na página Building Apps para a lista completa de campos do marketplace (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.).
#### Dimensões recomendadas para imagens da galeria
O marketplace renderiza `galleryImages` em um contêiner fixo de `8:5` (por exemplo, `1600×1000 px`).
<Note>
Imagens da galeria de qualquer proporção são exibidas por completo e nunca são cortadas, mas qualquer coisa significativamente mais alta ou mais estreita que `8:5` exibirá faixas vazias nas laterais.
</Note>
#### Limite de tamanho de imagem
O arquivo `logo` e cada arquivo em `galleryImages` não devem exceder **10 MB**. Arquivos maiores são ignorados quando o marketplace volta a hospedar os seus recursos publicados, portanto eles não serão exibidos.
### Publicar
```bash filename="Terminal"
yarn twenty app:publish
```
Para publicar sob uma dist-tag específica (por exemplo, `beta` ou `next`):
```bash filename="Terminal"
yarn twenty app:publish --tag beta
```
### Como funciona a descoberta no marketplace
O servidor Twenty sincroniza seu catálogo do marketplace a partir do registro do npm **a cada hora**.
Você pode acionar a sincronização imediatamente em vez de esperar:
```bash filename="Terminal"
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty dev:catalog-sync --remote production
```
Os metadados exibidos no marketplace vêm da sua configuração de `defineApplication()` — consulte [Metadados do marketplace](#marketplace-metadata) acima.
<Note>
Se o seu aplicativo não definir um `aboutDescription` em `defineApplication()`, o marketplace usará automaticamente o `README.md` do seu pacote no npm como conteúdo da página Sobre. Isso significa que você pode manter um único README tanto para o npm quanto para o marketplace da Twenty. Se quiser uma descrição diferente no marketplace, defina explicitamente `aboutDescription`.
</Note>
### Publicação via CI
O fluxo de trabalho `publish.yml` estruturado conforme descrito acima publica automaticamente no npm em tags de versão, com proveniência. Como `yarn twenty app:publish` adiciona `--provenance` e `--access public` para você quando é executado em CI, o fluxo de trabalho não precisa de flags do npm — apenas da configuração única de trusted publisher.
Para outros sistemas de CI (GitLab CI, CircleCI, etc.), execute `yarn install` e depois `yarn twenty app:publish`. A proveniência é emitida quando o ambiente pode gerar um token OIDC e é ignorada automaticamente caso contrário.
<Note>
A **proveniência do npm** adiciona um selo de confiança à sua listagem no npm, permitindo que os usuários verifiquem que o pacote foi construído a partir de um commit específico em um pipeline de CI público. É também o que permite que você reivindique a propriedade do seu app em um marketplace da Twenty. Consulte a [documentação de proveniência do npm](https://docs.npmjs.com/generating-provenance-statements) para mais detalhes.
</Note>
## Instalando aplicativos
Depois que um app é publicado (npm) ou implantado (tarball), os espaços de trabalho podem instalá-lo pela interface do usuário.
Vá para a página **Configurações > Aplicações** no Twenty, onde é possível navegar e instalar tanto apps do marketplace quanto apps implantados por tarball.
{/* TODO: add screenshot of the UI when the app is registered */}
Você também pode instalar apps pela linha de comando:
```bash filename="Terminal"
yarn twenty app:install
```
<Note>
O servidor impõe o versionamento semver na instalação, espelhando as regras da implantação:
* Instalar a mesma versão que já está instalada no seu espaço de trabalho é rejeitado com um erro `APP_ALREADY_INSTALLED`.
* Instalar uma versão inferior à atualmente instalada é rejeitado com um erro `CANNOT_DOWNGRADE_APPLICATION`.
Para instalar uma versão mais recente, implante ou publique-a primeiro e, em seguida, execute novamente `yarn twenty app:install`.
</Note>

View File

@@ -0,0 +1,126 @@
---
title: Sincronização e recuperação
description: Qual comando usar em cada caso, como ler a saída da sincronização e uma escada de recuperação para quando os metadados locais se desalinham — antes de chegar a um reset completo.
icon: compass
---
O desenvolvimento local de apps gira em torno da **sincronização**: a CLI reconstrói seu manifesto e o servidor aplica apenas a diferença entre ele e os metadados que já estão no seu workspace. Esta página cobre qual comando usar, como ler o que uma sincronização mudou e o que fazer — em ordem — quando o estado local parece inconsistente.
## Qual comando usar e quando
<Note>
Para a iteração local do dia a dia, quase sempre você vai querer `yarn twenty dev`. Fazer deploy e publicar servem para entregar releases, **não** para o ciclo local.
</Note>
| Você quer… | Comando | Notas |
| ------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Iterar localmente com sincronização em tempo real | `yarn twenty dev` | Monitora seus arquivos e sincroniza a cada alteração. |
| Sincronizar uma vez e sair (CI, scripts, hooks) | `yarn twenty apply` | Um build + sincronização, depois encerra. Adicione `--force` para pular a confirmação de mudança destrutiva. |
| Prever mudanças **sem aplicá-las** | `yarn twenty plan` | Calcula e imprime o diff; não grava nada. |
| Remover o app do workspace | `yarn twenty app:uninstall` | Adicione `--yes` para pular o prompt. |
| Enviar um tarball para um servidor | `yarn twenty app:publish --private` | Requer uma versão **estritamente maior** em `package.json` — veja [Publicação](/l/pt/developers/extend/apps/operations/publishing). |
| Publicar no marketplace (npm) | `yarn twenty app:publish` | — |
| Instalar / atualizar uma versão implantada | `yarn twenty app:install` | Instala a versão atualmente implantada. |
| Limpar o servidor local e começar do zero | `yarn twenty docker:reset` | Exclui **todos** os dados locais — último recurso. |
<Note>
`yarn twenty dev --once` e `yarn twenty dev --once --dry-run` ainda funcionam como aliases obsoletos para `yarn twenty apply` e `yarn twenty plan`.
</Note>
### A sincronização local não precisa de incremento de versão
A regra de `version` estritamente crescente (`VERSION_ALREADY_EXISTS` no deploy, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` na instalação) se aplica a **`app:publish` / `app:install`** — o caminho de release. `yarn twenty dev` sincroniza seu manifesto no lugar e nunca exige mudança de versão, então você não precisa mexer em `package.json` para iterar. Se você se pegar aumentando a versão para testar uma mudança local, está usando o caminho de release quando o que quer é o ciclo de desenvolvimento.
## Lendo a saída da sincronização
Cada sincronização imprime as alterações de metadados que aplicou (ou aplicaria, com `plan`), no estilo do Terraform — um bloco por entidade com seus atributos, depois uma linha de resumo:
```text filename="Terminal"
# objectMetadata "rocket" will be created
+ icon = "IconRocket"
+ labelSingular = "Rocket"
+ ...
# fieldMetadata "launchedAt" will be updated
~ isNullable = false -> true
Plan: 2 to add, 1 to change, 1 to destroy.
✓ Synced My App (4 files)
```
Este é seu primeiro diagnóstico: ele mostra exatamente quais objetos, campos e layouts mudaram, para que você possa confirmar que uma sincronização fez o que esperava antes de conferir na interface.
Mudanças destrutivas (`to destroy`) são listadas com o que elas removem (por exemplo, `objectMetadata "auditNote" — drops the table and all its rows`) e exigem confirmação interativa, ou `--force` em scripts.
Quando uma sincronização falha em uma única entidade, o erro nomeia a entidade com problema e seu `universalIdentifier`, por exemplo:
```text
Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) failed
```
Use esse identificador para encontrar a entidade no seu manifesto (e, se necessário, no workspace), em vez de adivinhar qual está em conflito.
## Visualizando mudanças (plan)
`yarn twenty plan` compila seu manifesto, pede ao servidor o plano de migração e o imprime — **sem aplicar nada**. É a forma segura de responder "o que esta sincronização mudaria?" antes de se comprometer com ela.
```bash filename="Terminal"
yarn twenty plan
```
```text filename="Terminal"
Building manifest...
Computing metadata plan (read-only, nothing will be applied)...
# fieldMetadata "crewCapacity" will be created
+ ...
Plan: 1 to add, 1 to change, 0 to destroy.
✓ Plan complete for My App — no changes were applied
```
Um plano:
* **Não grava nada** — nenhuma migração de metadados, nenhuma atualização de registro de aplicativo, nenhuma mudança de função/aba padrão e nenhuma geração de cliente de API.
* Retorna o **mesmo diff** que uma sincronização real aplicaria, para que você possa revisar previamente as entidades criadas/atualizadas/excluídas.
* É útil antes de uma mudança arriscada, ao revisar uma mudança gerada por IA ou em um script que deve falhar se uma mudança inesperada estiver prestes a ser aplicada.
<Note>
Um plano só antevê mudanças de **metadados**, e exige que o app tenha sido sincronizado ao menos uma vez (para que o workspace o conheça). Se você rodar isso em um app que nunca foi sincronizado, o servidor informa que o app não está instalado — rode `yarn twenty dev` uma vez antes.
</Note>
## Escada de recuperação
Quando os metadados locais parecerem errados, aumente o nível nesta ordem e pare assim que estiver desbloqueado. Cada etapa é mais disruptiva que a anterior.
1. **Ressincronizar.** Rode `yarn twenty apply` novamente. Sincronizações são idempotentes — rodar novamente um manifesto limpo é seguro e frequentemente resolve um problema transitório.
2. **Prever o plano.** Rode `yarn twenty plan` para ver exatamente o que a próxima sincronização pretende mudar, sem aplicá-la.
3. **Leia o erro nomeado.** Se uma sincronização falhar, anote o tipo de metadado e o `universalIdentifier` na mensagem (veja acima) e localize essa entidade no seu manifesto. Um conflito geralmente aponta para um identificador duplicado ou reutilizado.
4. **Desinstalar e reinstalar.** `yarn twenty app:uninstall`, depois sincronize novamente (`yarn twenty dev`). Isso reconstrói os metadados do app a partir do zero, mantendo o restante do seu workspace intacto.
5. **Reset completo (último recurso).** `yarn twenty docker:reset`, depois faça o seeding e a sincronização novamente.
<Warning>
`yarn twenty docker:reset` exclui **todos** os dados da sua instância local — todos os workspaces, registros e apps. Use isso somente depois que as etapas anteriores tiverem falhado.
</Warning>
<Note>
Encontrou um erro de metadados? Por favor, [abra uma issue](https://github.com/twentyhq/twenty/issues/new/choose) e inclua a mensagem de migração que falhou (com seu tipo de metadado e `universalIdentifier`), a saída de `Metadata changes` da sincronização e os comandos que você rodou.
</Note>
## Evite sincronizações concorrentes em um único workspace
Sincronizar aplica migrações de metadados. Executar várias operações de sincronização, deploy ou instalação contra o **mesmo workspace ao mesmo tempo** — por exemplo, múltiplos terminais ou agentes de IA iterando em paralelo — pode intercalar essas migrações e deixar os metadados em um estado parcialmente aplicado.
O servidor serializa sincronizações por workspace para evitar isso, mas você ainda deve direcionar operações sensíveis de metadados por um **único** processo em vez de dispará-las concorrentemente. Se você orquestra o desenvolvimento com múltiplos agentes, encaminhe as chamadas de sincronização/deploy/instalação deles por uma única fila, para que apenas uma rode por vez.
## Diferenciando tipos de falha
Quando algo dá errado, o diff de metadados e os erros nomeados permitem localizar a falha:
* **Erro de build do manifesto** — a CLI falha antes de sincronizar (`MANIFEST_BUILD_FAILED`, `TYPECHECK_FAILED`); corrija o código-fonte do seu app.
* **Erro de sincronização / migração** — o build é bem-sucedido, mas aplicar o diff falha, nomeando a entidade e o `universalIdentifier`; corrija os metadados em conflito.
* **Erro no tamanho das dependências** — a sincronização ou instalação falha porque as `dependencies` de produção do app são grandes demais para serem instaladas como uma camada de runtime (`LOGIC_FUNCTION_DEPENDENCIES_SIZE_EXCEEDED`); mova para `devDependencies` os pacotes que suas funções de lógica não importam em tempo de execução (bibliotecas de UI, ferramentas de desenvolvimento).
* **Erro de tempo de execução no código do app** — a sincronização é concluída com êxito, mas suas funções de lógica ou componentes se comportam de forma incorreta em tempo de execução; verifique os [logs de função](/l/pt/developers/extend/apps/operations/cli).
* **Estado da instância local** — nenhuma das opções acima e o espaço de trabalho ainda parece incorreto; prossiga descendo na escada de recuperação.

View File

@@ -0,0 +1,264 @@
---
title: Testes
description: Configuração do Vitest, testes de integração contra um servidor Twenty real, verificação de tipos e CI com GitHub Actions.
icon: flask
---
O SDK fornece APIs programáticas que permitem compilar, implantar, instalar e desinstalar seu aplicativo a partir de código de teste. Em conjunto com [Vitest](https://vitest.dev/) e os clientes de API tipados, você pode escrever testes de integração que verificam que seu aplicativo funciona de ponta a ponta em um servidor Twenty real.
## Usando pacotes npm
Você pode instalar e usar qualquer pacote npm no seu app. Tanto funções lógicas quanto componentes de front-end são empacotados com [esbuild](https://esbuild.github.io/), que incorpora suas dependências na saída — nenhum `node_modules` é necessário em tempo de execução.
### Instalando um pacote
```bash filename="Terminal"
yarn add axios
```
Em seguida, importe-o no seu código:
```ts src/logic-functions/fetch-data.ts
import { defineLogicFunction } from 'twenty-sdk/define';
import axios from 'axios';
const handler = async (): Promise<any> => {
const { data } = await axios.get('https://api.example.com/data');
return { data };
};
export default defineLogicFunction({
universalIdentifier: '...',
name: 'fetch-data',
description: 'Fetches data from an external API',
timeoutSeconds: 10,
handler,
});
```
O mesmo vale para componentes de front-end:
```tsx src/front-components/chart.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import { format } from 'date-fns';
const DateWidget = () => {
return <p>Today is {format(new Date(), 'MMMM do, yyyy')}</p>;
};
export default defineFrontComponent({
universalIdentifier: '...',
name: 'date-widget',
component: DateWidget,
});
```
### Como o empacotamento funciona
A etapa de build usa o esbuild para produzir um único arquivo independente por função lógica e por componente de front-end. Pacotes importados são incorporados ao bundle, exceto aqueles listados abaixo.
**Funções lógicas** são executadas em um ambiente Node.js. Módulos nativos do Node (`fs`, `path`, `crypto`, `http`, etc.) estão disponíveis e não precisam ser instalados.
**Componentes de front-end** são executados em um Web Worker. Módulos internos do nó **não** estão disponíveis — apenas pacotes npm que funcionam em um ambiente de navegador. Note que a sandbox implementa um *parcial* DOM, para que um pacote possa ser construído de forma limpa e ainda falhar no tempo de execução; veja [Limitação atual](/l/pt/developers/extend/apps/layout/front-components#current-limitations).
Ambos os ambientes têm `twenty-client-sdk/core` e `twenty-client-sdk/metadata` disponíveis como módulos pré-fornecidos — eles não são empacotados, mas resolvidos em tempo de execução pelo servidor.
Componentes de front-end também podem compartilhar dependências por meio de um [bundle de dependências compartilhadas](/l/pt/developers/extend/apps/layout/front-components#sharing-dependencies-across-front-components). Os pacotes listados no campo `frontComponentSharedDependencies` do package.json do app são compilados uma vez em um arquivo separado e carregados a partir dele em tempo de execução, em vez de serem incorporados em cada componente. Assim, um componente que importa apenas pacotes compartilhados praticamente não carrega código de dependência próprio.
## Configuração
O aplicativo gerado pelo scaffolder já inclui o Vitest. Se você configurá-lo manualmente, instale as dependências:
```bash filename="Terminal"
yarn add -D vitest vite-tsconfig-paths
```
Crie um `vitest.config.ts` na raiz do seu aplicativo:
```ts vitest.config.ts
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
const TWENTY_API_KEY = process.env.TWENTY_API_KEY ?? '<the pre-seeded local dev key>';
// Make env vars available to globalSetup (test.env only applies to workers)
process.env.TWENTY_API_URL = TWENTY_API_URL;
process.env.TWENTY_API_KEY = TWENTY_API_KEY;
export default defineConfig({
plugins: [
tsconfigPaths({
projects: ['tsconfig.spec.json'],
ignoreConfigErrors: true,
}),
],
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
globalSetup: ['src/__tests__/global-setup.ts'],
env: {
TWENTY_API_URL,
TWENTY_API_KEY,
},
},
});
```
Crie um arquivo de configuração global que verifique se o servidor está acessível, escreva uma configuração de teste para o SDK (`~/.twenty/config.test.json`) e sincronize o app antes da execução dos testes:
```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
const APP_PATH = process.cwd();
const CONFIG_DIR = path.join(os.homedir(), '.twenty');
export async function setup() {
const apiUrl = process.env.TWENTY_API_URL!;
const apiKey = process.env.TWENTY_API_KEY!;
// Verify the server is running
const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
// Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
// Start from a clean slate, then sync the app
await appUninstall({ appPath: APP_PATH }).catch(() => {});
const result = await appDevOnce({ appPath: APP_PATH });
if (!result.success) {
throw new Error(`Dev sync failed: ${result.error?.message}`);
}
}
export async function teardown() {
await appUninstall({ appPath: APP_PATH });
}
```
## APIs programáticas do SDK
O subcaminho `twenty-sdk/cli` exporta funções que você pode chamar diretamente a partir do código de teste:
| Função | Descrição |
| -------------- | ---------------------------------------------------------------- |
| `appBuild` | Compilar o aplicativo e, opcionalmente, empacotar um tarball |
| `appDeploy` | Enviar um tarball para o servidor |
| `appDevOnce` | Compila e sincroniza o app uma vez (igual a `yarn twenty apply`) |
| `appInstall` | Instalar o aplicativo no espaço de trabalho ativo |
| `appUninstall` | Desinstalar o aplicativo do espaço de trabalho ativo |
Cada função retorna um objeto de resultado com `success: boolean` e `data` ou `error`.
## Escrevendo um teste de integração
Aqui está um exemplo completo que compila, implanta e instala o aplicativo e, em seguida, verifica se ele aparece no espaço de trabalho:
```ts src/__tests__/app-install.integration-test.ts
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const APP_PATH = process.cwd();
describe('App installation', () => {
beforeAll(async () => {
const buildResult = await appBuild({
appPath: APP_PATH,
tarball: true,
onProgress: (message: string) => console.log(`[build] ${message}`),
});
if (!buildResult.success) {
throw new Error(`Build failed: ${buildResult.error?.message}`);
}
const deployResult = await appDeploy({
tarballPath: buildResult.data.tarballPath!,
onProgress: (message: string) => console.log(`[deploy] ${message}`),
});
if (!deployResult.success) {
throw new Error(`Deploy failed: ${deployResult.error?.message}`);
}
const installResult = await appInstall({ appPath: APP_PATH });
if (!installResult.success) {
throw new Error(`Install failed: ${installResult.error?.message}`);
}
});
afterAll(async () => {
await appUninstall({ appPath: APP_PATH });
});
it('should find the installed app in the workspace', async () => {
const metadataClient = new MetadataApiClient();
const result = await metadataClient.query({
findManyApplications: {
id: true,
name: true,
universalIdentifier: true,
},
});
const installedApp = result.findManyApplications.find(
(app: { universalIdentifier: string }) =>
app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
);
expect(installedApp).toBeDefined();
});
});
```
## Executando testes
Certifique-se de que seu servidor Twenty local esteja em execução e, em seguida:
```bash filename="Terminal"
yarn test
```
Ou no modo watch durante o desenvolvimento:
```bash filename="Terminal"
yarn test:watch
```
## Verificação de tipos
Você também pode executar a verificação de tipos no seu aplicativo sem executar os testes:
```bash filename="Terminal"
yarn twenty dev:typecheck
```
Isso executa `tsc --noEmit` no `tsconfig.json` do seu app e informa quaisquer erros de tipo. Os apps criados pelo scaffold também incluem um script `yarn typecheck` que também cobre arquivos de teste (`tsconfig.spec.json`).
## CI com GitHub Actions
O gerador de scaffold cria um workflow pronto para uso em `.github/workflows/ci.yml`. A cada push para `main` e a cada pull request, ele inicia um servidor Twenty efêmero no runner (por meio da action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`) e então executa `yarn lint`, `yarn typecheck`, `yarn test:unit` e `yarn test` com `TWENTY_API_URL` / `TWENTY_API_KEY` apontando para esse servidor. Nenhum secret é necessário e você pode fixar a versão do servidor por meio da variável de ambiente `TWENTY_VERSION` no topo do workflow.
Consulte [Publicação → CI/CD automatizado](/l/pt/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) para um passo a passo completo dos três workflows criados pelo scaffold (`ci.yml`, o pipeline de deploy `cd.yml` e `publish.yml` para publicação no npm).

View File

@@ -0,0 +1,143 @@
---
title: Traduções
description: Publique seu aplicativo em vários idiomas — traduza rótulos do manifesto e strings da interface do usuário por meio de um único catálogo de localidades.
icon: language
---
Os aplicativos Twenty são escritos em **inglês**: as strings no seu código-fonte e no manifesto
são o texto-fonte em inglês, `en` é a localidade de origem a partir da qual você traduz, e
qualquer localidade deixada sem tradução reverte para essa localidade.
Seu aplicativo tem dois tipos de texto traduzível, e ambos passam pelo mesmo
catálogo `locales/`:
* **Rótulos do manifesto** — nomes de objetos e campos, títulos de exibição, itens de menu e
outras strings declaradas nos metadados do seu aplicativo.
* **Front-component strings** — A interface do usuário que processa seus componentes frontais do React.
Você marca as strings traduzíveis, extraa-as em catálogos por localidade,
traduza os catálogos, e a compilação serve a linguagem certa para o usuário atual
— sem asas extras.
## Marcando strings front-component
Importe os auxiliares de tradução de `vinte e sdk/front-component`:
```tsx
import { Trans, t, msg, useTranslate } from 'twenty-sdk/front-component';
const STATUSES = [
{ id: 'draft', label: msg('Draft') },
{ id: 'sent', label: msg('Sent') },
];
const Card = ({ count, name }: { count: number; name: string }) => {
const { t } = useTranslate();
return (
<section>
{/* Static text — reactive to the user's locale */}
<Trans>Loading postcard…</Trans>
{/* Disambiguate identical sources with a context */}
<Trans context="card-title">Untitled</Trans>
{/* Interpolation: pass values explicitly */}
<p>{t('Hi {name}', { name })}</p>
<p>{t('Saved {count} cards', { count })}</p>
{/* Resolve a lazily-declared descriptor */}
<ul>{STATUSES.map((s) => <li key={s.id}>{t(s.label)}</li>)}</ul>
</section>
);
};
```
### Quando usar qual
* **`Trans`** — texto estático em JSX. Use as props `message` e `values` para
interpolação, como em `Trans message="Hi {name}" values={{ name }}`;
a interpolação diretamente em children não pode ser extraída estaticamente.
* **`useTranslate().t`** — strings dinâmicas dentro de um componente. Rerenderiza quando o usuário
muda de linguagem. Prefere isso dentro de renderização.
* **`t(...)`** (importado diretamente) — tradução ávida utilizável **em qualquer lugar**,
incluindo manipuladores de eventos, auxiliares e escopo de módulo — não apenas dentro de renderização.
* **`msg(...)`** — um descritor preguiçoso para frases declaradas como dados (constantes,
config). Resolva-o mais tarde com `t(descriptor)`.
### Contexto
Passe `context` para desambiguar sequências de caracteres de origem idênticas que traduzem
de forma diferente:
```tsx
t({ message: 'Open', context: 'door' });
t({ message: 'Open', context: 'window' });
<Trans context="card-title">Untitled</Trans>
```
Strings contextuais aparecem nos catálogos de configurações regionais como um grupo indexado pelo seu
contexto: `"\<context>": { "\<message>": "\<translation>" }`. Os rótulos do manifesto são
contextuais por definição — cada um traz o papel de metadados que desempenha
(`objectMetadata.labelSingular`, `fieldMetadata.label`, `view.name`, …), por isso
a mesma palavra pode ser traduzida de forma diferente como nome de objeto e como nome de visualização.
## Extraindo e traduzindo
Execute o comando extracto a partir do diretório do seu aplicativo:
```bash
twenty dev:translations-extract # collect strings into locales/en.json
twenty dev:translations-extract --locale fr-FR # also scaffold a target locale
```
A extração coleta tanto os rótulos do seu manifesto quanto as strings de `t()`/`msg()`/`Trans`
no código-fonte do seu front-component em `locales/\<locale>.json`, indexadas pela
string de origem. Preencha as traduções:
```json
// locales/fr-FR.json
{
"Loading postcard…": "Chargement de la carte…",
"Hi {name}": "Bonjour {name}",
"Saved {count} cards": "{count} cartes enregistrées",
"door": {
"Open": "Ouvrir"
},
"objectMetadata.labelSingular": {
"Postcard": "Carte postale"
}
}
```
Strings sem contexto são entradas simples; strings contextuais — incluindo cada rótulo de manifesto — são aninhadas sob sua chave de contexto.
Espaços reservados como `{name}` são substituídos no tempo de execução — mantenha-os na tradução
. Qualquer cadeia de caracteres deixada vazia retorna ao texto de origem.
## Como ele funciona
`twenty dev:build` compila os catálogos e fornece o idioma correto para o
usuário atual: os rótulos do manifesto são resolvidos no lado do servidor e os catálogos de componentes de front-end
são incorporados em cada pacote de componente. No tempo de execução, um componente lê a localidade
de seu contexto de execução (o idioma atual do host) e resolve
cada seqüência de caracteres contra seu catálogo, voltando à fonte quando uma tradução
estiver faltando. Mudar o idioma no host faz com que as strings de `Trans` e
`useTranslate().t` sejam renderizadas novamente em tempo real.
Os rótulos do manifesto são compilados a cada sincronização — `twenty dev:build`, `twenty apply`
e também durante a observação contínua de `twenty dev` — portanto, editar um arquivo de localidade e
sincronizar é suficiente para ver o novo texto. Os catálogos de componentes do front-end são incorporados a
cada pacote de componentes no momento da compilação, portanto, alterar um deles ainda exige
executar novamente `twenty dev:build` (e reimplantar).
O conteúdo de texto (children) de `Trans` pode se estender por várias linhas — os espaços em branco são colapsados da
mesma forma que o JSX os colapsa, portanto, ambos são extraídos para a chave `Welcome back`:
```tsx
<Trans>Welcome back</Trans>
<Trans>
Welcome
back
</Trans>
```