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: comenzi `yarn twenty` pentru executarea funcțiilor, transmiterea în flux a jurnalelor, gestionarea instalărilor de aplicații și comutarea între remote-uri.
icon: terminal
---
Interfața CLI `yarn twenty` este punctul tău de acces pentru tot ce ține de aplicație. Lista completă de comenzi:
| Comandă | Ce face | Documentat în |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `dev` | Monitorizează fișierele sursă și sincronizează în timp real modificările | [Ghid de pornire rapidă](/l/ro/developers/extend/apps/getting-started/quick-start) |
| `plan` | Previzualizează modificările metadatelor fără a le aplica | [Sincronizare și recuperare](/l/ro/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
| `aplică` | Aplică modificările metadatelor după afișarea planului | [Sincronizare și recuperare](/l/ro/developers/extend/apps/operations/sync-and-recovery) |
| `dev:build` | Compilează aplicația și generează clientul API (`--tarball` pentru a împacheta un `.tgz`) | [Publicare](/l/ro/developers/extend/apps/operations/publishing) |
| `dev:typecheck` | Rulează verificarea tipurilor TypeScript | [Testare](/l/ro/developers/extend/apps/operations/testing) |
| `dev:add` | Creează scheletul unei entități noi | [Generarea scheletului](/l/ro/developers/extend/apps/getting-started/scaffolding) |
| `dev:generate-client` | Regenerează clientul API tipizat | această pagină |
| `dev:function:exec` / `dev:function:logs` | Execută funcții și transmite în flux jurnalele acestora | această pagină |
| `dev:translations-extract` | Extrage șirurile traducibile în cataloagele din `locales/` | [Traduceri](/l/ro/developers/extend/apps/operations/translations) |
| `dev:catalog-sync` | Declanșează o sincronizare a catalogului marketplace-ului | [Publicare](/l/ro/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
| `app:publish` / `app:install` / `app:uninstall` | Ciclul de viață al lansărilor | [Publicare](/l/ro/developers/extend/apps/operations/publishing) și această pagină |
| `docker:*` | Administrează containerul serverului Twenty local | [Server local](/l/ro/developers/extend/apps/getting-started/local-server) |
| `remote:*` | Administrează conexiunile la server | această pagină |
Fiecare comandă acceptă `-r, --remote \<name>` pentru a viza un anumit server la distanță în locul celui implicit.
## Executarea funcțiilor (`yarn twenty dev:function:exec`)
Rulați manual o funcție logică fără a o declanșa prin HTTP, cron sau eveniment de bază de date:
```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
```
## Vizualizarea jurnalelor funcțiilor (`yarn twenty dev:function:logs`)
Transmiteți în flux jurnalele de execuție pentru funcțiile logice ale aplicației:
```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>
Acest lucru este diferit de `yarn twenty docker:logs`, care afișează jurnalele containerului Docker. `yarn twenty dev:function:logs` afișează jurnalele de execuție ale funcțiilor aplicației de pe serverul Twenty.
</Note>
## Generarea clientului tipizat (`yarn twenty dev:generate-client`)
Regenerați clientul tipizat al API-ului (`twenty-client-sdk`) din schema remote-ului activ, fără a construi sau sincroniza o aplicație. Folosiți-l pentru a obține un client tipizat în orice proiect — cum ar fi un serviciu backend aflat într-un repository separat — care comunică cu instanța dvs. 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
```
Apoi importați clientul în codul dvs.:
```typescript
import { CoreApiClient } from 'twenty-client-sdk/core';
```
Rulați din nou comanda ori de câte ori se schimbă modelul de date pentru a reîmprospăta tipurile generate.
<Note>
Clientul este generat în interiorul `node_modules`, astfel încât nu este inclus în commit împreună cu codul dvs. Rulați `yarn twenty dev:generate-client` după fiecare instalare (de exemplu, într-un script `postinstall` sau în CI).
</Note>
## Dezinstalarea unei aplicații (`yarn twenty app:uninstall`)
Eliminați aplicația din spațiul de lucru activ:
```bash filename="Terminal"
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty app:uninstall --yes
```
## Gestionarea remote-urilor
Un „remote” este un server Twenty la care se conectează aplicația. În timpul configurării, Scaffolderul creează automat unul pentru dvs. Puteți adăuga mai multe remote-uri sau comuta între ele oricând.
```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>
```
Acreditările dvs. sunt stocate în `~/.twenty/config.json`.

View File

@@ -0,0 +1,32 @@
---
title: Prezentare generală
description: Construiește, testează și livrează aplicația ta — comenzi CLI, teste de integrare, CI și publicare pe un server sau pe npm.
icon: rocket
---
**Stratul de operațiuni** este tot ceea ce faci *asupra* aplicației tale, mai degrabă decât *cu* ea: invocarea comenzilor CLI, rularea testelor de integrare pe un server Twenty real, configurarea CI și livrarea versiunilor — fie ca un tarball implementat pe un singur server, fie ca un pachet npm listat în 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)
```
## În această secțiune
<CardGroup cols={2}>
<Card title="CLI" icon="terminal" href="/l/ro/developers/extend/apps/operations/cli">
Referință pentru `yarn twenty` — exec, logs, uninstall, remotes.
</Card>
<Card title="Sincronizare și recuperare" icon="compass" href="/l/ro/developers/extend/apps/operations/sync-and-recovery">
Ce comandă și când, citirea diferențelor de sincronizare și o schemă de recuperare.
</Card>
<Card title="Testare" icon="flask" href="/l/ro/developers/extend/apps/operations/testing">
Configurare Vitest, teste de integrare, verificare a tipurilor, flux de lucru CI.
</Card>
<Card title="Publicare" icon="upload" href="/l/ro/developers/extend/apps/operations/publishing">
Construire, implementare a unui tarball, publicare pe npm, instalare.
</Card>
</CardGroup>

View File

@@ -0,0 +1,293 @@
---
title: Publicare
icon: upload
description: Distribuie aplicația ta Twenty în marketplace sau implementeaz-o intern.
---
## Prezentare generală
După ce aplicația ta este [construită și testată local](/l/ro/developers/extend/apps/getting-started/concepts), ai două căi pentru distribuire:
* **Implementează un tarball** — încarcă aplicația direct pe un server Twenty anume pentru uz intern sau privat.
* **Publică pe npm** — listează aplicația ta în marketplace-ul Twenty pentru ca orice spațiu de lucru să o poată descoperi și instala.
Ambele căi pornesc din aceeași etapă de **build**.
## Construirea aplicației
Rulează comanda `build` pentru a compila aplicația și a genera un `manifest.json` pregătit pentru distribuire:
```bash filename="Terminal"
yarn twenty dev:build
```
Aceasta compilează sursele TypeScript, transpilează funcțiile de logică și componentele de front-end și scrie totul în `.twenty/output/`. Adaugă `--tarball` pentru a produce și un pachet `.tgz` pentru distribuire manuală sau pentru comanda de publish.
## Implementare pe un server (tarball)
Pentru aplicațiile pe care nu le dorești disponibile public — instrumente proprietare, integrări doar pentru enterprise sau build-uri experimentale — poți implementa un tarball direct pe un server Twenty.
### Cerințe
Înainte de implementare, ai nevoie de un remote configurat care să indice serverul țintă. Remote-urile stochează local URL-ul serverului și credențialele de autentificare în `~/.twenty/config.json`.
Adaugă un remote:
```bash filename="Terminal"
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### Implementare
Construiește și încarcă aplicația ta pe server într-un singur pas:
```bash filename="Terminal"
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty app:publish --private --remote production
```
### Partajarea unei aplicații implementate
Aplicațiile tarball nu sunt listate în marketplace-ul public, astfel încât alte spații de lucru de pe același server nu le vor descoperi prin navigare. Pentru a partaja o aplicație implementată:
1. Mergi la **Setări > Aplicații > Înregistrări** și deschide aplicația ta
2. În fila **Distribuție**, fă clic pe **Copiază linkul de partajare**
3. Partajează acest link cu utilizatori din alte spații de lucru — îi duce direct la pagina de instalare a aplicației
Linkul de partajare folosește URL-ul de bază al serverului (fără niciun subdomeniu de spațiu de lucru), astfel încât funcționează pentru orice spațiu de lucru de pe server.
### Gestionarea versiunilor
Când actualizezi o aplicație tarball deja implementată, serverul solicită ca `version` din `package.json` să fie **strict mai mare** (conform ordonării [semver](https://semver.org)) decât versiunea implementată în prezent. Redeployarea aceleiași versiuni sau trimiterea uneia inferioare este respinsă înainte ca tarball-ul să fie stocat — vei vedea o eroare `VERSION_ALREADY_EXISTS` de la CLI.
Pentru a lansa o actualizare:
1. Incrementează câmpul `version` din `package.json` (de ex. `1.2.3` → `1.2.4`, `1.3.0` sau `2.0.0`)
2. Rulează `yarn twenty app:publish --private` (sau `yarn twenty app:publish --private --remote production`)
3. Spațiile de lucru care au aplicația instalată și au activat actualizarea automată pentru aceasta (în fila Setări a aplicației) sunt actualizate automat în fundal; celelalte vor vedea actualizarea disponibilă în setările lor
<Note>
Etichetele de pre-lansare funcționează conform așteptărilor: incrementarea de la `1.0.0-rc.1` la `1.0.0-rc.2` este permisă, iar o lansare finală precum `1.0.0` este recunoscută corect ca fiind mai mare decât `1.0.0-rc.5`. Versiunea din `package.json` trebuie să fie ea însăși un șir semver valid.
</Note>
{/* TODO: add screenshot of the Upgrade button */}
### Compatibilitatea versiunii serverului
Dacă aplicația ta folosește o funcționalitate introdusă într-o anumită versiune de server Twenty (de exemplu, furnizori OAuth adăugați în v2.3.0), ar trebui să declari versiunea minimă de server necesară aplicației folosind câmpul `engines.twenty` din `package.json`:
```json filename="package.json"
{
"name": "twenty-my-app",
"version": "1.0.0",
"engines": {
"node": "^24.5.0",
"twenty": ">=2.3.0"
}
}
```
Valoarea este un [interval semver](https://github.com/npm/node-semver#ranges) standard. Tipare comune:
| Interval | Semnificație |
| ---------------------------------- | ------------------------------------------------------ |
| `>=2.3.0` | Orice server de la 2.3.0 încolo |
| `>=2.3.0 \<3.0.0` | 2.3.0 sau ulterior, dar sub următoarea versiune majoră |
| `^2.3.0` | La fel ca `>=2.3.0 \<3.0.0` |
**Ce se întâmplă în timpul implementării și instalării:**
* Dacă `engines.twenty` este setat și versiunea serverului țintă nu respectă intervalul, implementarea (încărcarea arhivei tarball) sau instalarea este respinsă cu eroarea `SERVER_VERSION_INCOMPATIBLE` și cu un mesaj care indică atât intervalul necesar, cât și versiunea efectivă a serverului.
* Dacă `engines.twenty` nu este setat, aplicația este acceptată pe orice versiune de server (retrocompatibilă cu aplicațiile existente).
* Dacă serverul nu are nicio `APP_VERSION` configurată, verificarea este omisă.
<Note>
Serverul este verificarea autoritativă — validează `engines.twenty` atât la încărcarea arhivei tarball, cât și la instalarea în spațiul de lucru. Dacă implementezi un tarball în afara fluxului standard sau instalezi din marketplace, serverul impune în continuare compatibilitatea.
</Note>
## CI/CD automatizat (fluxuri de lucru preconfigurate)
Aplicațiile generate cu `create-twenty-app` vin, gata de utilizare, cu trei fluxuri de lucru GitHub Actions, în `.github/workflows/`. CI rulează fără nicio configurare, CD necesită un singur secret, iar publicarea pe npm necesită o configurare unică pentru npm trusted publisher.
### CI — `ci.yml`
Rulează testele de integrare la fiecare push pe `main` și la fiecare pull request.
**Ce face:**
1. Preia codul sursă al aplicației.
2. Pornește o instanță de test Twenty izolată folosind acțiunea compozită `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (echivalentul din CI al `yarn twenty docker:start --test`).
3. Activează Corepack, configurează Node.js pe baza fișierului `.nvmrc` și instalează dependențele cu `yarn install --immutable`.
4. Rulează `yarn test`, transmitând `TWENTY_API_URL` și `TWENTY_API_KEY` din instanța pornită, astfel încât testele să poată comunica cu un server real.
**Opțiuni de configurare:**
* `TWENTY_VERSION` (variabilă de mediu, implicit `latest`) — fixează versiunea serverului Twenty folosită în CI editând acest parametru în `ci.yml`.
* Concurența este grupată după `github.ref` și anulează execuțiile în desfășurare la noile push-uri.
Nu sunt necesare secrete — instanța de test este efemeră și există doar pe durata jobului.
### CD — `cd.yml`
Implementează aplicația pe un server Twenty configurat la fiecare push pe `main` și, opțional, dintr-un pull request când se aplică eticheta `deploy`.
**Ce face:**
1. Preia head-ul PR-ului (pentru PR-urile etichetate) sau commitul împins.
2. Rulează `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — echivalentul din CI al `yarn twenty app:publish --private`.
3. Rulează `twentyhq/twenty/.github/actions/install-twenty-app@main` astfel încât versiunea nou implementată să fie instalată în spațiul de lucru țintă.
**Configurare necesară:**
| Setare | Unde | Scop |
| ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `TWENTY_DEPLOY_URL` | `env` în `cd.yml` (implicit `http://localhost:3000`) | Serverul Twenty la care se face implementarea. Modifică-l la URL-ul real al serverului înainte de prima utilizare. |
| `TWENTY_DEPLOY_API_KEY` | GitHub repo **Settings → Secrets and variables → Actions** | Cheie API cu permisiune de implementare pe serverul țintă. |
<Note>
Valoarea implicită a `TWENTY_DEPLOY_URL`, `http://localhost:3000`, este un placeholder — nu va putea accesa nimic dintr-un runner găzduit de GitHub. Actualizează-l la URL-ul public al serverului tău (sau folosește un runner self-hosted cu acces la rețea) înainte de a activa CD.
</Note>
**Declanșarea unei implementări de previzualizare dintr-un PR:**
Adaugă eticheta `deploy` la un pull request. Condiția `if:` din `cd.yml` va rula jobul pentru acel PR folosind commitul head al PR-ului, permițându-ți să validezi o modificare pe serverul țintă înainte de a face merge.
### Publicare — `publish.yml`
Publică aplicația pe npm, cu proveniență, când împingi un tag de versiune (de ex. `v1.0.0`) sau când rulezi manual fluxul de lucru din fila Actions.
**Ce face:**
1. Face checkout al aplicației tale, configurează Node.js și actualizează npm (publicarea de încredere necesită npm 11.5.1 sau o versiune ulterioară).
2. Rulează `yarn twenty app:publish`, care construiește aplicația și publică `.twenty/output` în npm. În CI adaugă automat `--provenance` și `--access public`, astfel încât nu sunt necesare flaguri în fluxul de lucru.
**Configurare unică:**
Pe npmjs.com deschide pachetul tău > **Settings → Trusted Publisher** și înregistrează acest repozitoriu cu fluxul de lucru `publish.yml` (vezi [documentația npm trusted publishing](https://docs.npmjs.com/trusted-publishers)). Publicarea cu provenance certifică ce repozitoriu GitHub a construit pachetul, ceea ce este, de asemenea, modul în care îți revendici proprietatea asupra aplicației tale într-un marketplace Twenty.
<Note>
npm acceptă doar proveniență din depozite sursă **publice**. Dacă publici dintr-un depozit privat, npm respinge pachetul de proveniență OIDC cu un `E422 ... Eroare „Unsupported GitHub Actions source repository visibility: "private"`.” Pentru a publica dintr-un depozit privat, renunță la proveniență setând `TWENTY_APP_PUBLISH_DISABLE_PROVENANCE: 'true'` în `env` al etapei de publicare (un indiciu comentat este inclus în fișierul generat `publish.yml`):
```yaml filename=".github/workflows/publish.yml"
- name: Publish to npm
env:
TWENTY_APP_PUBLISH_DISABLE_PROVENANCE: 'true'
run: yarn twenty app:publish
```
</Note>
### Fixarea acțiunilor reutilizabile
Fluxurile de lucru `ci.yml` și `cd.yml` fac referire la acțiuni reutilizabile la `@main`, astfel încât actualizările acțiunilor din repo-ul `twentyhq/twenty` sunt preluate automat. Dacă dorești builduri deterministe, înlocuiește `@main` cu un SHA de commit sau cu un tag de release pe fiecare linie `uses:`.
## Publicarea pe npm
Publicarea pe npm face ca aplicația ta să poată fi descoperită în marketplace-ul Twenty. Orice spațiu de lucru Twenty poate răsfoi, instala și actualiza aplicațiile din marketplace direct din interfață.
### Cerințe
* Un cont [npm](https://www.npmjs.com)
* Cuvântul cheie `twenty-app` din array-ul `keywords` al fișierului `package.json` (adaugă-l manual — nu este inclus în mod implicit în șablonul `create-twenty-app`)
```json filename="package.json"
{
"name": "twenty-app-postcard-sender",
"version": "1.0.0",
"keywords": ["twenty-app"]
}
```
### Metadate pentru marketplace
Configurația `defineApplication()` acceptă câmpuri opționale care controlează modul în care aplicația ta apare în marketplace. Folosește `logo` și `galleryImages` pentru a face referire la imaginile din folderul `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',
],
});
```
Vezi [acordeonul defineApplication](/l/ro/developers/extend/apps/config/application#marketplace-metadata) din pagina Building Apps pentru lista completă de câmpuri ale marketplace-ului (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.).
#### Dimensiuni recomandate pentru imaginile din galerie
Marketplace-ul redă `galleryImages` într-un container fix cu raport `8:5` (de exemplu, `1600×1000 px`).
<Note>
Imaginile din galerie cu orice raport de aspect sunt afișate integral și nu sunt niciodată decupate, însă orice imagine semnificativ mai înaltă sau mai îngustă decât `8:5` va afișa benzi goale pe laterale.
</Note>
#### Limită de dimensiune pentru imagini
Fișierul `logo` și fiecare fișier din `galleryImages` nu trebuie să depășească **10 MB**. Fișierele mai mari sunt omise atunci când marketplace-ul re-găzduiește resursele tale publicate, astfel că nu vor fi afișate.
### Publicare
```bash filename="Terminal"
yarn twenty app:publish
```
Pentru a publica sub un dist-tag specific (de ex., `beta` sau `next`):
```bash filename="Terminal"
yarn twenty app:publish --tag beta
```
### Cum funcționează descoperirea în marketplace
Serverul Twenty sincronizează catalogul marketplace-ului din registrul npm **la fiecare oră**.
Poți declanșa sincronizarea imediat, în loc să aștepți:
```bash filename="Terminal"
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty dev:catalog-sync --remote production
```
Metadatele afișate în marketplace provin din configurația `defineApplication()` — vezi secțiunea [Metadate pentru marketplace](#marketplace-metadata) de mai sus.
<Note>
Dacă aplicația ta nu definește un `aboutDescription` în `defineApplication()`, piața va folosi automat fișierul `README.md` al pachetului tău de pe npm drept conținut pentru pagina Despre. Acest lucru înseamnă că poți menține un singur README atât pentru npm, cât și pentru piața Twenty. Dacă vrei o descriere diferită în piață, setează explicit `aboutDescription`.
</Note>
### Publicare CI
Fluxul de lucru generat `publish.yml` descris mai sus publică automat pe npm la etichetele de versiune, cu provenance. Deoarece `yarn twenty app:publish` adaugă `--provenance` și `--access public` pentru tine când rulează în CI, fluxul de lucru nu are nevoie de flaguri npm — doar de configurarea unică a Trusted Publisher.
Pentru alte sisteme CI (GitLab CI, CircleCI etc.), rulează `yarn install` apoi `yarn twenty app:publish`. Provenance este emisă atunci când mediul poate genera un token OIDC și este omisă automat în caz contrar.
<Note>
**npm provenance** adaugă un badge de încredere la listarea ta în npm, permițând utilizatorilor să verifice că pachetul a fost construit dintr-un commit specific într-un pipeline CI public. Este, de asemenea, ceea ce îți permite să revendici proprietatea asupra aplicației tale într-un marketplace Twenty. Vezi [documentația npm provenance](https://docs.npmjs.com/generating-provenance-statements) pentru detalii.
</Note>
## Instalarea aplicațiilor
După ce o aplicație este publicată (npm) sau implementată (tarball), spațiile de lucru o pot instala prin interfața utilizatorului (UI).
Mergi la pagina **Setări > Aplicații** din Twenty, unde pot fi parcurse și instalate atât aplicațiile din marketplace, cât și cele implementate prin tarball.
{/* TODO: add screenshot of the UI when the app is registered */}
Poți instala aplicații și din linia de comandă:
```bash filename="Terminal"
yarn twenty app:install
```
<Note>
Serverul impune versionarea semver la instalare, reflectând regulile de la deploy:
* Instalarea aceleiași versiuni care este deja instalată în spațiul tău de lucru este respinsă cu o eroare `APP_ALREADY_INSTALLED`.
* Instalarea unei versiuni mai mici decât cea instalată în prezent este respinsă cu o eroare `CANNOT_DOWNGRADE_APPLICATION`.
Pentru a instala o versiune mai nouă, fă mai întâi deploy sau public-o, apoi rulează din nou `yarn twenty app:install`.
</Note>

View File

@@ -0,0 +1,126 @@
---
title: Sincronizare și recuperare
description: Ce comandă să folosești și când, cum să citești rezultatul sincronizării și un plan în trepte de recuperare pentru situațiile în care metadatele locale deviază — înainte de a ajunge la o resetare completă.
icon: compass
---
Dezvoltarea locală de aplicații se învârte în jurul **sincronizării**: CLI-ul reconstruiește manifestul și serverul aplică doar diferența dintre acesta și metadatele deja existente în spațiul tău de lucru. Această pagină explică ce comandă să folosești, cum să citești ce a modificat o sincronizare și ce să faci — în ordine — atunci când starea locală pare inconsistentă.
## Ce comandă și când
<Note>
Pentru iterațiile locale de zi cu zi vei dori aproape întotdeauna `yarn twenty dev`. Publicarea și deploy-ul sunt pentru lansarea de versiuni, **nu** pentru bucla locală.
</Note>
| Vrei să… | Comandă | Notițe |
| -------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Iterează local cu sincronizare în timp real | `yarn twenty dev` | Monitorizează fișierele și sincronizează la fiecare modificare. |
| Sincronizează o singură dată și iese (CI, scripturi, hook-uri) | `yarn twenty apply` | O singură compilare + sincronizare, apoi iese. Adaugă `--force` pentru a omite confirmarea schimbărilor distructive. |
| Previzualizează modificările **fără a le aplica** | `yarn twenty plan` | Calculează și afișează diff-ul; nu scrie nimic. |
| Elimină aplicația din spațiul de lucru | `yarn twenty app:uninstall` | Adaugă `--yes` pentru a sări peste prompt. |
| Trimite un tarball către un server | `yarn twenty app:publish --private` | Necesită o versiune `package.json` **strict mai mare** — vezi [Publicare](/l/ro/developers/extend/apps/operations/publishing). |
| Publică în marketplace (npm) | `yarn twenty app:publish` | — |
| Instalează / actualizează o versiune deja implementată | `yarn twenty app:install` | Instalează versiunea implementată în prezent. |
| Șterge serverul local și pornește de la zero | `yarn twenty docker:reset` | Șterge **toate** datele locale — ultimă soluție. |
<Note>
`yarn twenty dev --once` și `yarn twenty dev --once --dry-run` funcționează în continuare ca aliasuri depreciate pentru `yarn twenty apply` și `yarn twenty plan`.
</Note>
### Sincronizarea locală nu are nevoie de incrementarea versiunii
Regula de `version` strict crescătoare (`VERSION_ALREADY_EXISTS` la deploy, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` la instalare) se aplică pentru **`app:publish` / `app:install`** — calea de release. `yarn twenty dev` sincronizează manifestul pe loc și nu necesită niciodată schimbarea versiunii, astfel încât nu trebuie să atingi `package.json` pentru a itera. Dacă ajungi să crești versiunea ca să testezi o modificare locală, folosești calea de release atunci când ai nevoie de bucla de dezvoltare.
## Citirea rezultatului sincronizării
Fiecare sincronizare afișează modificările de metadate pe care le-a aplicat (sau le-ar aplica, cu `plan`), în stil Terraform — un bloc per entitate cu atributele sale, apoi o linie de rezumat:
```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)
```
Acesta este primul tău instrument de diagnostic: îți spune exact ce obiecte, câmpuri și layout-uri s-au schimbat, astfel încât să poți confirma că o sincronizare a făcut ce te așteptai înainte să verifici interfața.
Schimbările distructive (`to destroy`) sunt listate împreună cu ceea ce elimină (de ex. `objectMetadata "auditNote" — drops the table and all its rows`) și necesită confirmare interactivă sau `--force` în scripturi.
Când o sincronizare eșuează pe o singură entitate, eroarea numește entitatea problematică și `universalIdentifier`-ul acesteia, de exemplu:
```text
Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) failed
```
Folosește acel identificator pentru a găsi entitatea în manifest (și, dacă este nevoie, în spațiul de lucru) în loc să ghicești care intră în conflict.
## Previzualizarea modificărilor (plan)
`yarn twenty plan` construiește manifestul, cere serverului planul de migrare și îl afișează — **fără a aplica nimic**. Este modalitatea sigură de a răspunde la întrebarea „ce ar schimba această sincronizare?” înainte de a te angaja la ea.
```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
```
Un plan:
* **Nu scrie nimic** — fără migrare de metadate, fără actualizare a înregistrării aplicației, fără modificări ale rolului sau filei implicite și fără generare de client API.
* Returnează **același diff** pe care l-ar aplica o sincronizare reală, astfel încât poți revizui dinainte entitățile create/actualizate/șterse.
* Este util înaintea unei modificări riscante, când revizuiești o modificare generată de AI sau într-un script care ar trebui să eșueze dacă o modificare neașteptată este pe cale să fie aplicată.
<Note>
Un plan previzualizează doar modificările de **metadate** și necesită ca aplicația să fi fost sincronizată cel puțin o dată (astfel încât spațiul de lucru să știe de ea). Dacă îl rulezi pentru o aplicație care nu a fost niciodată sincronizată, serverul va raporta că aplicația nu este instalată — rulează mai întâi o dată `yarn twenty dev`.
</Note>
## Plan de recuperare în trepte
Când metadatele locale par greșite, escaladează în această ordine și oprește-te de îndată ce ești deblocat. Fiecare pas este mai disruptiv decât precedentul.
1. **Resincronizează.** Rulează din nou `yarn twenty apply`. Sincronizările sunt idempotente — rularea din nou a unui manifest curat este sigură și rezolvă adesea o problemă temporară.
2. **Previzualizează planul.** Rulează `yarn twenty plan` pentru a vedea exact ce intenționează să schimbe următoarea sincronizare, fără a o aplica.
3. **Citește eroarea nominalizată.** Dacă o sincronizare eșuează, notează tipul de metadate și `universalIdentifier`-ul din mesaj (vezi mai sus) și localizează acea entitate în manifest. Un conflict indică de obicei un identificator duplicat sau reutilizat.
4. **Dezinstalează și reinstalează.** `yarn twenty app:uninstall`, apoi sincronizează din nou (`yarn twenty dev`). Acest lucru reconstruiește metadatele aplicației de la zero, păstrând în același timp restul spațiului de lucru intact.
5. **Resetare completă (ultimă soluție).** `yarn twenty docker:reset`, apoi reinițializează datele și resincronizează.
<Warning>
`yarn twenty docker:reset` șterge **toate** datele din instanța ta locală — fiecare spațiu de lucru, înregistrare și aplicație. Folosește această comandă doar după ce pașii anteriori au eșuat.
</Warning>
<Note>
Ai întâlnit o eroare de metadate? Te rugăm să [deschizi un issue](https://github.com/twentyhq/twenty/issues/new/choose) și să incluzi mesajul de migrare eșuată (cu tipul de metadate și `universalIdentifier`-ul), rezultatul `Metadata changes` din sincronizare și comenzile pe care le-ai rulat.
</Note>
## Evită sincronizările concurente pe același spațiu de lucru
Sincronizarea aplică migrațiile de metadate. Rularea mai multor operațiuni de sincronizare, deploy sau instalare împotriva aceluiași spațiu de lucru în același timp — de exemplu, mai multe terminale sau agenți AI care iterează în paralel — poate intercala acele migrații și poate lăsa metadatele într-o stare aplicată parțial.
Serverul serializează sincronizările per spațiu de lucru pentru a preveni acest lucru, dar ar trebui totuși să treci operațiunile sensibile de metadate printr-un proces **unic**, în loc să le declanșezi concurent. Dacă orchestrezi dezvoltarea cu mai mulți agenți, rutează apelurile lor de sync/deploy/install printr-o singură coadă, astfel încât să ruleze doar unul la un moment dat.
## Deosebirea tipurilor de erori
Când ceva nu merge bine, diff-ul de metadate și erorile nominalizate îți permit să localizezi eșecul:
* **Eroare de construire a manifestului** — CLI-ul eșuează înainte de sincronizare (`MANIFEST_BUILD_FAILED`, `TYPECHECK_FAILED`); repară sursa aplicației.
* **Eroare de sincronizare / migrare** — build-ul reușește, dar aplicarea diff-ului eșuează, menționând entitatea și `universalIdentifier`-ul; repară metadatele care intră în conflict.
* **Eroare de dimensiune a dependențelor** — sincronizarea sau instalarea eșuează deoarece `dependencies` de producție ale aplicației sunt prea mari pentru a fi instalate ca strat de rulare (`LOGIC_FUNCTION_DEPENDENCIES_SIZE_EXCEEDED`); mută pachetele pe care funcțiile tale logice nu le importă la rulare (biblioteci UI, unelte de dezvoltare) în `devDependencies`.
* **Eroare de execuție în codul aplicației** — sincronizarea reușește, dar funcțiile sau componentele tale logice se comportă incorect la execuție; verifică [jurnalele funcțiilor](/l/ro/developers/extend/apps/operations/cli).
* **Stare locală a instanței** — niciuna dintre cele de mai sus și spațiul de lucru tot arată greșit; coboară pe scara de recuperare.

View File

@@ -0,0 +1,264 @@
---
title: Testare
description: Configurare Vitest, teste de integrare pe un server Twenty real, verificare a tipurilor și CI cu GitHub Actions.
icon: flask
---
SDK-ul oferă API-uri programatice care vă permit să construiți, să distribuiți, să instalați și să dezinstalați aplicația din codul de test. Combinat cu [Vitest](https://vitest.dev/) și clienții API tipizați, puteți scrie teste de integrare care verifică faptul că aplicația funcționează de la un cap la altul pe un server Twenty real.
## Utilizarea pachetelor npm
Puteți instala și utiliza orice pachet npm în aplicația dvs. Atât funcţiile logice cât şi componentele frontale sunt grupate cu [esbuild](https://esbuild.github.io/), care înglobează dependențele lor la ieșire - nu sunt necesare `node_modules` în timpul rulării.
### Instalarea unui pachet
```bash filename="Terminal"
yarn add axios
```
Apoi importați-l în codul dvs.:
```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,
});
```
Același lucru funcționează și pentru componentele frontend:
```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,
});
```
### Cum funcționează împachetarea
Pasul de build folosește esbuild pentru a produce un singur fișier autonom pentru fiecare funcție logică și pentru fiecare componentă frontend. Ambalajele importate sunt încorporate în pachet, cu excepţia celor enumerate mai jos.
**Funcțiile logice** rulează într-un mediu Node.js. Modulele built-in Node (`fs`, `path`, `crypto`, `http` etc.) sunt disponibile și nu trebuie instalate.
**Componentele frontend** rulează într-un Web Worker. Modulele built-in Node **nu** sunt disponibile — doar pachetele npm care funcționează într-un mediu de browser. Reține că sandbox-ul implementează un DOM *parțial*, astfel încât un pachet se poate construi fără erori, dar totuși poate eșua la execuție; vezi [Limitări curente](/l/ro/developers/extend/apps/layout/front-components#current-limitations).
Ambele medii au `twenty-client-sdk/core` și `twenty-client-sdk/metadata` disponibile ca module pre-furnizate — acestea nu sunt incluse în bundle, ci sunt rezolvate la rulare de către server.
Componentele frontului pot de asemenea partaja dependențe printr-un [pachet de dependențe partajate](/l/ro/developers/extend/apps/layout/front-components#sharing-dependencies-across-front-components). Pachete listate în câmpul `frontComponentSharedDependencies` din pachetul aplicației. Fiul este construit o dată într-un fișier separat și încărcat din el la rulare în loc să fie înglobat în fiecare componentă, astfel încât o componentă care importă numai pachete comune să nu aibă aproape niciun cod propriu de dependență.
## Configurare
Aplicația generată (scaffolded) include deja Vitest. Dacă o configurați manual, instalați dependențele:
```bash filename="Terminal"
yarn add -D vitest vite-tsconfig-paths
```
Creați un `vitest.config.ts` în rădăcina aplicației:
```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,
},
},
});
```
Creează un fișier global de configurare inițială care verifică faptul că serverul este accesibil, scrie un fișier de configurare de test pentru SDK (`~/.twenty/config.test.json`) și sincronizează aplicația înainte ca testele să ruleze:
```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 });
}
```
## API-uri SDK programatice
Subcalea `twenty-sdk/cli` exportă funcții pe care le puteți apela direct din codul de test:
| Funcție | Descriere |
| -------------- | -------------------------------------------------------------------------------------- |
| `appBuild` | Construiți aplicația și, opțional, împachetați un tarball |
| `appDeploy` | Încărcați un tarball pe server |
| `appDevOnce` | Construiește și sincronizează aplicația o singură dată (la fel ca `yarn twenty apply`) |
| `appInstall` | Instalați aplicația în spațiul de lucru activ |
| `appUninstall` | Dezinstalați aplicația din spațiul de lucru activ |
Fiecare funcție returnează un obiect rezultat cu `success: boolean` și fie `data`, fie `error`.
## Scrierea unui test de integrare
Iată un exemplu complet care construiește, distribuie și instalează aplicația, apoi verifică faptul că aceasta apare în spațiul de lucru:
```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();
});
});
```
## Rularea testelor
Asigurați-vă că serverul Twenty local rulează, apoi:
```bash filename="Terminal"
yarn test
```
Sau în modul watch în timpul dezvoltării:
```bash filename="Terminal"
yarn test:watch
```
## Verificarea tipurilor
Puteți rula și verificarea tipurilor pe aplicație fără a rula testele:
```bash filename="Terminal"
yarn twenty dev:typecheck
```
Aceasta rulează `tsc --noEmit` pe fișierul `tsconfig.json` al aplicației și raportează orice erori de tip. Aplicațiile create cu scaffolderul includ, de asemenea, un script `yarn typecheck` care acoperă și fișierele de test (`tsconfig.spec.json`).
## CI cu GitHub Actions
Scaffolderul generează un workflow gata de utilizare la `.github/workflows/ci.yml`. La fiecare push pe `main` și la fiecare pull request, acesta pornește un server Twenty efemer în runner (prin acțiunea `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), apoi rulează `yarn lint`, `yarn typecheck`, `yarn test:unit` și `yarn test` cu `TWENTY_API_URL` / `TWENTY_API_KEY` îndreptate către acel server. Nu sunt necesare secrete și poți fixa versiunea serverului prin variabila de mediu `TWENTY_VERSION` din partea de sus a workflow-ului.
Vezi [Publicare → CI/CD automatizat](/l/ro/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) pentru un ghid complet al celor trei workflow-uri create cu scaffolderul (`ci.yml`, pipeline-ul de deploy `cd.yml` și `publish.yml` pentru publicare pe npm).

View File

@@ -0,0 +1,142 @@
---
title: Traduceri
description: Lansează-ți aplicația în mai multe limbi — tradu etichetele din manifest și șirurile UI printr-un singur catalog de locale.
icon: language
---
Aplicațiile Twenty sunt create în **engleză**: șirurile din sursă și din manifest
sunt textul sursă în engleză, `en` este localizarea sursă din care traduci, iar
orice localizare lăsată netradusă revine implicit la aceasta.
Aplicația ta are două tipuri de text traducibil și ambele trec prin același
catalog `locales/`:
* **Etichete de manifest** — nume de obiecte și câmpuri, titluri de vizualizări, elemente de meniu și
alte șiruri declarate în metadatele aplicației tale.
* **Șiruri componentă-față** - textul UI al componentelor dvs. React din față.
Marchează șirurile traduse, le extrag în cataloage per-localizare,
traduce acele cataloage, iar clădirea servește limba corectă pentru utilizatorul curent* fără cableri suplimentare.
## Marcarea sirurilor componentei frontale
Importă ajutătoarele de traducere din `douăzeci de aplicații/front-componente`:
```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>
);
};
```
### Când să utilizaţi care
* **`Trans`** — text static în JSX. Folosește proprietățile `message` și `values` pentru
interpolare, ca în `Trans message="Hi {name}" values={{ name }}`;
interpolarea direct în elementele copil nu poate fi extrasă static.
* **`useTranslate().t`** - șiruri dinamice în interiorul unei componente. Re-randează atunci când utilizatorul
schimbă limba. Preferă acest lucru în redare.
* **`t(...)`** (importat direct) - traducerea mai ușoară **oriunde**,
inclusiv manipulatorii de evenimente, ajutoarele și domeniul de aplicare al modulului - nu numai în randare.
* **`msg(...)`** — un descriptor leneș pentru șiruri declarate ca date (constante,
config). Rezolvați-l mai târziu cu `t(descriptor)`.
### Context
Treceți `context` pentru a disambigua șirurile sursă identice care traduc
în mod diferit:
```tsx
t({ message: 'Open', context: 'door' });
t({ message: 'Open', context: 'window' });
<Trans context="card-title">Untitled</Trans>
```
Șirurile contextuale apar în cataloagele de localizare ca un grup indexat după
contextul lor: `"\<context>": { "\<message>": "\<translation>" }`. Etichetele din manifest sunt
contextuale prin construcție — fiecare poartă rolul de metadată pe care îl joacă
(`objectMetadata.labelSingular`, `fieldMetadata.label`, `view.name`, …), astfel încât
același cuvânt poate fi tradus diferit ca nume de obiect și ca nume de vizualizare.
## Extragerea și traducerea
Rulează comanda de extragere din directorul aplicației:
```bash
twenty dev:translations-extract # collect strings into locales/en.json
twenty dev:translations-extract --locale fr-FR # also scaffold a target locale
```
Procesul de extragere colectează atât etichetele manifestului, cât și șirurile `t()`/`msg()`/`Trans`
din sursa front-component în `locales/\<locale>.json`, indexate după
șirul sursă. Completează traducerile:
```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"
}
}
```
Șirurile fără context sunt intrări simple; șirurile contextuale — inclusiv fiecare etichetă din manifest — sunt îmbricate sub cheia lor de context.
Localnicii ca `{name}` sunt substituiți la timp - țineți-le în traducerea
. Orice şir rămas gol se întoarce la textul sursă.
## Cum rulează
`douăzeci de dev:build` compilează cataloagele şi serveşte limba corectă pentru utilizatorul curent
: etichetele manifest sunt rezolvate pe server, iar cataloagele de tip front-component
sunt copite în fiecare pachet de componente. La runtime, o componentă citește localizarea
din contextul de execuție (limba curentă a gazdei) și rezolvă
fiecare șir din catalog, revenind la sursă atunci când lipsește o traducere
. Schimbarea limbii în gazdă reafișează în timp real șirurile `Trans` și
`useTranslate().t`.
Etichetele manifestului sunt compilate la fiecare sincronizare — `twenty dev:build`, `twenty apply`
și monitorizarea continuă `twenty dev` — astfel că editarea unui fișier de localizare și
sincronizarea sunt suficiente pentru a vedea textul nou. Cataloagele componentelor Front sunt integrate în
fiecare pachet de componente la momentul compilării, astfel că modificarea unuia dintre ele înseamnă totuși
rularea din nou a `twenty dev:build` (și redistribuirea).
Elementele text copil ale `Trans` pot acoperi mai multe linii — spațiile albe sunt comprimate în
același mod în care JSX le comprimă, astfel încât ambele variante sunt extrase cu cheia `Welcome back`:
```tsx
<Trans>Welcome back</Trans>
<Trans>
Welcome
back
</Trans>
```