Files
upstream-mirror e7da97194a
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
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

145 lines
8.4 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Переводы
description: Выпускайте приложение на нескольких языках — переводите метки манифеста и строки пользовательского интерфейса через единый каталог локалей.
icon: language
---
Приложения Twenty создаются на **английском**: строки в вашем исходном коде и манифесте
являются исходным текстом на английском, `en` — это исходная локаль, с которой вы переводите, а
любая непереведенная локаль будет использовать ее по умолчанию.
В вашем приложении есть два типа переводимого текста, и оба проходят через один и тот же
каталог `locales/`:
* **Метки манифеста** — имена объектов и полей, заголовки представлений, пункты меню и
другие строки, объявленные в метаданных вашего приложения.
* **Строки фронт-компонента** — пользовательский интерфейс для отображения React фронтальных компонентов.
Вы помечаете переводимые строки, извлекаете их в каталоги для каждой локали,
переводите эти каталоги, и сборка отдает правильный язык для
текущего пользователя — без дополнительной настройки.
## Маркировка переднего компонента
Импортировать помощников перевода из `twenty-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>
);
};
```
### Когда использовать, какой
* **`Trans`** — статический текст в JSX. Используйте пропсы `message` и `values` для
интерполяции, как в `Trans message="Hi {name}" values={{ name }}`;
интерполяция напрямую в дочерних элементах не может быть статически извлечена.
* **`useTranslate().t`** — динамические строки внутри компонента. Повторно отображается, когда
пользователь переключает язык. Предпочитать это внутри рендера.
* **`t(...)`** (импортировано напрямую) — перевод можно использовать **где**,
включая обработчики событий, вспомогательные и область модулей — не только внутри рендера.
* **`msg(...)`** — ленивый дескриптор строк, объявленный данными (константы,
config). Разрешите позже с помощью `t(descriptor)`.
### Контекст
Передайте `context` в недвусмысленный перевод идентичных исходных строк, которые переводят
по-разному:
```tsx
t({ message: 'Open', context: 'door' });
t({ message: 'Open', context: 'window' });
<Trans context="card-title">Untitled</Trans>
```
Контекстные строки отображаются в каталогах локалей в виде группы, индексируемой
по их контексту: `"\<context>": { "\<message>": "\<translation>" }`. Манифестные метки являются
контекстными по своей природе — каждая из них несёт метаданные о своей роли
(`objectMetadata.labelSingular`, `fieldMetadata.label`, `view.name`, …), поэтому
одно и то же слово может переводиться по‑разному как имя объекта и как имя представления.
## Извлечение и перевод
Выполнить команду извлечения из вашей папки приложения:
```bash
twenty dev:translations-extract # collect strings into locales/en.json
twenty dev:translations-extract --locale fr-FR # also scaffold a target locale
```
При извлечении собираются как метки манифеста, так и строки `t()`/`msg()`/`Trans`
из исходного кода вашего фронт‑компонента в `locales/\<locale>.json`, где ключом
является исходная строка. Заполните перевод:
```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"
}
}
```
Строки без контекста являются плоскими записями; контекстные строки — включая каждую
метку манифеста — вложены под своим ключом контекста.
Такие держатели, как `{name}`, заменяются во время выполнения — сохраняйте их в переводе
. Любая оставленная строка пуста возвращается к исходному тексту.
## Как он запускается
`twenty dev:build` компилирует каталоги и предоставляет нужный язык для
текущего пользователя: метки манифеста разрешаются на стороне сервера, а каталоги фронтенд-компонентов
встраиваются в каждый бандл компонента. Во время выполнения компонент читает локаль
из контекста выполнения (текущий язык узла) и разрешает
каждую строку в соответствии со своим каталогом, возвращение к источнику, когда перевод
отсутствует. Переключение языка в хосте повторно рендерит строки `Trans` и
`useTranslate().t` в реальном времени.
Метки манифеста компилируются при каждой синхронизации — `twenty dev:build`, `twenty apply`
и в постоянном режиме отслеживания `twenty dev` — поэтому достаточно изменить файл локали и
выполнить синхронизацию, чтобы увидеть новый текст. Каталоги компонентов Front встраиваются в
каждый пакет компонентов во время сборки, поэтому изменение одного из них по-прежнему требует
повторного запуска `twenty dev:build` (и повторного развертывания).
Дочерние текстовые элементы `Trans` могут занимать несколько строк — пробельные символы схлопываются
так же, как их схлопывает JSX, поэтому оба этих варианта извлекаются в ключ `Welcome back`:
```tsx
<Trans>Welcome back</Trans>
<Trans>
Welcome
back
</Trans>
```