Files
SnapOtter/apps/docs/uk/guide/translations.md
T
SnapOtterandGitHub 4963ab3bbd feat(docs-i18n): translate all documentation into 20 languages
All 181 docs markdown files translated into 20 languages (apps/docs/<locale>/**). Companion to the i18n code PR; admin-merged because the file count exceeds GitHub's per-PR CI trigger limit. Validated by pnpm i18n:check (all surfaces, 0 stale/missing) and a clean all-locale docs build.
2026-07-11 13:52:47 +08:00

235 lines
15 KiB
Markdown
Raw 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.
---
description: "21 підтримувана мова та як створювати або покращувати переклади для SnapOtter за допомогою системи i18n із контролем через TypeScript."
i18n_source_hash: 55837d9fdaef
i18n_provenance: human
i18n_output_hash: 8795ae2aa060
---
# Посібник з перекладу {#translation-guide}
SnapOtter постачається з 21 мовою з коробки. Система i18n використовує легкий власний рантайм із контролем повноти локалей через TypeScript і динамічним розбиттям коду.
## Підтримувані мови {#supported-languages}
| Code | Language | Native Name | Direction |
|------|----------|-------------|-----------|
| `en` | Англійська | English | LTR |
| `zh-CN` | Китайська (спрощена) | 简体中文 | LTR |
| `zh-TW` | Китайська (традиційна) | 繁體中文 | LTR |
| `ja` | Японська | 日本語 | LTR |
| `ko` | Корейська | 한국어 | LTR |
| `es` | Іспанська | Español | LTR |
| `fr` | Французька | Français | LTR |
| `it` | Італійська | Italiano | LTR |
| `pt-BR` | Португальська (Бразилія) | Português (Brasil) | LTR |
| `de` | Німецька | Deutsch | LTR |
| `nl` | Нідерландська | Nederlands | LTR |
| `sv` | Шведська | Svenska | LTR |
| `ru` | Російська | Русский | LTR |
| `pl` | Польська | Polski | LTR |
| `uk` | Українська | Українська | LTR |
| `ar` | Арабська | العربية | RTL |
| `tr` | Турецька | Türkçe | LTR |
| `hi` | Гінді | हिन्दी | LTR |
| `vi` | В'єтнамська | Tiếng Việt | LTR |
| `id` | Індонезійська | Bahasa Indonesia | LTR |
| `th` | Тайська | ไทย | LTR |
## Як працює визначення мови {#how-language-detection-works}
SnapOtter використовує трирівневий порядок розвʼязання:
1. **Уподобання користувача** - зберігається в `localStorage("snapotter-locale")` і синхронізується з налаштуваннями користувача після автентифікації
2. **Автовизначення браузера** - проходить масив `navigator.languages` зі співставленням префіксів за BCP 47
3. **Стандартне значення інстансу** - змінна середовища `DEFAULT_LOCALE` адміністратора (отримується з `GET /api/v1/config/locale`)
4. **Резервна англійська** - завжди доступна
Користувачі можуть змінити мову з:
- **Селектора-глобуса у футері** (десктоп, завжди видимий)
- Селектора мови на **сторінці входу** (до автентифікації)
- Розділу **Settings > General** (уподобання окремого користувача)
- Випадаючого списку мов у **мобільній бічній панелі**
- Розділ **Settings > System** задає стандартне значення для всього інстансу (лише для адміністратора)
## Як працюють переклади {#how-translations-work}
Усі рядки інтерфейсу зберігаються в `packages/shared/src/i18n/`. Еталонний файл - `en.ts`, який експортує типізований обʼєкт з кожним рядком, що використовує застосунок (~1500 ключів). Інші мови - це окремі файли (наприклад, `de.ts`, `fr.ts`), що експортують ту саму структуру.
Тип `TranslationKeys` використовує `DeepStringRecord`, щоб приймати будь-яке рядкове значення, водночас контролюючи структуру ключів. TypeScript виявляє відсутні ключі в будь-якому файлі перекладу під час компіляції.
Під час виконання завантажується лише активна локаль через динамічний `import()`, завдяки чому основний бандл лишається невеликим.
## Використання перекладів у компонентах {#using-translations-in-components}
```tsx
import { useTranslation } from "@/contexts/i18n-context";
import { format, plural } from "@/lib/format";
function MyComponent() {
const { t, locale, setLocale } = useTranslation();
return (
<div>
<h1>{t.common.settings}</h1>
<p>{format(t.settings.people.deleteConfirm, { username: "admin" })}</p>
<p>{plural(count, t.automate.fileCount, t.automate.fileCountPlural)}</p>
</div>
);
}
```
## Внесок у переклад {#contributing-a-translation}
Ми вітаємо PR з перекладами напряму. Ви можете покращити наявну локаль або додати нову.
Щоб повідомити про помилку перекладу, не надсилаючи код, відкрийте [GitHub Issue](https://github.com/snapotter-hq/SnapOtter/issues) із зазначенням мови, некоректного рядка та запропонованого виправлення.
::: tip
PR з перекладами не потребують попереднього схвалення. Зробіть форк репозиторію, внесіть свої зміни та відкрийте PR. Дивіться [Посібник для контриб'юторів](/uk/guide/contributing) щодо повного процесу PR та вимоги CLA.
:::
## Як створити або оновити переклад {#how-to-create-or-update-a-translation}
### 1. Форк і клонування {#_1-fork-and-clone}
```bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install
```
### 2. Скопіюйте еталонний файл (лише для нової мови) {#_2-copy-the-reference-file-new-language-only}
Пропустіть цей крок, якщо ви покращуєте наявний переклад.
```bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts
```
### 3. Перекладіть рядки {#_3-translate-the-strings}
Відкрийте свій новий файл і перекладіть кожне рядкове значення. Зберігайте структуру обʼєкта та ключі точно такими самими.
```ts
import type { TranslationKeys } from "./en.js";
export const xx: TranslationKeys = {
common: {
upload: "Your translation here",
// ... translate all entries
},
// ... translate all sections
} as const;
```
Правила:
- Не перекладайте ключі обʼєкта, лише рядкові значення
- Залишайте `as const` в кінці
- Імпортуйте `TranslationKeys` з `./en.js` і типізуйте свій експорт
- Залишайте плейсхолдери `{variable}` точно як є
- Масиви (`rotatingPhrases`, `progressMessages`) мають містити однакову кількість елементів
- Не перекладайте: SnapOtter, JPEG, PNG, WebP, EXIF, API та інші технічні терміни
### 4. Зареєструйте локаль (лише для нової мови) {#_4-register-the-locale-new-language-only}
Додайте свою локаль до `SUPPORTED_LOCALES` у `packages/shared/src/i18n/index.ts`:
```ts
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },
```
### 5. Перевірте {#_5-verify}
```bash
pnpm typecheck # catches missing or mistyped keys
pnpm lint # formatting check
pnpm dev # manually verify strings appear correctly
```
### 6. Надішліть {#_6-submit}
Відкрийте PR проти `main` із заголовком на кшталт `feat(i18n): add Swedish translation` або `fix(i18n): correct German typos`. Бот CLA попросить вас підписати угоду під час першого внеску.
## Додавання нових ключів перекладу {#adding-new-translation-keys}
Під час додавання нової функції, якій потрібні нові рядки інтерфейсу:
1. Спершу додайте нові ключі до `en.ts` (еталонний файл)
2. Запустіть `pnpm typecheck` - кожен файл локалі завершиться помилкою, якщо в ньому бракує нового ключа
3. Додайте новий ключ до всіх файлів локалей (використовуйте англійську як тимчасовий резервний варіант)
## Конфігурація {#configuration}
Задайте стандартну мову інстансу через змінну середовища:
```yaml
DEFAULT_LOCALE: "de" # German as the default for all new users
```
## Довідник файлів {#file-reference}
| File | Purpose |
|------|---------|
| `packages/shared/src/i18n/en.ts` | Англійські рядки (еталонна локаль, ~1500 ключів) |
| `packages/shared/src/i18n/index.ts` | `SUPPORTED_LOCALES`, `loadTranslations()`, експорти типів |
| `packages/shared/src/i18n/<locale>.ts` | Файли перекладів для кожної мови |
| `apps/web/src/contexts/i18n-context.tsx` | `I18nProvider`, хук `useTranslation()` |
| `apps/web/src/lib/format.ts` | Допоміжні функції `format()`, `plural()`, `formatFileSize()` |
| `apps/api/src/routes/config.ts` | Публічний ендпоінт `GET /api/v1/config/locale` |
## Переклад вебсайту, документації та довідника API {#translating-the-web-surfaces}
Підтримка 21 мови вище стосується **застосунку**. Публічний вебсайт
(snapotter.com), цей сайт документації та довідник REST API також
перекладено всіма 21 мовами через окремий конвеєр із контролем за хешем, який повторно використовує
ті самі назви й описи інструментів з `packages/shared/src/i18n`, тож
термінологія лишається узгодженою всюди.
### Машинний переклад за замовчуванням {#machine-translated-by-default}
Кожну неанглійську сторінку на вебсайті та в документації **перекладено машинно** на
першому проході (сесією Claude Code, а не стороннім сервісом), і вона має
невеликий банер, який можна закрити та який про це повідомляє, з посиланням назад сюди. Це зроблено навмисно:
так усі 21 мова постачаються швидко й чесно, після чого спільноті пропонується
вдосконалити найважливіші сторінки. Машинний переклад передає зміст;
людський перегляд робить його природним для читання.
### Як конвеєр вирішує, що перекладати {#how-the-web-pipeline-decides}
Кожна перекладна одиниця англійського джерела хешується, і хеш зберігається поруч
з її перекладом. На кожному запуску конвеєр:
- перекладає будь-яку одиницю, яка ще не має перекладу,
- пропускає будь-яку одиницю, чий збережений хеш усе ще відповідає англійському джерелу,
- повторно перекладає **машинну** одиницю, коли її англійське джерело змінюється,
- і позначає одиницю, доопрацьовану **людиною**, як `stale` (потребує перегляду), коли її англійське
джерело змінюється, замість того щоб перезаписати вашу роботу.
### Доопрацювання вебперекладу через PR {#refining-a-web-translation-by-pr}
Ви покращуєте переклад вебсайту, документації чи довідника API так само, як ви
покращуєте локаль застосунку: редагуючи згенерований файл і відкриваючи PR.
1. Знайдіть згенерований переклад для вашої мови:
- рядки інтерфейсу вебсайту: `apps/landing/src/i18n/<locale>.json`
- сторінка документації: `apps/docs/<locale>/**.md`
- довідник API: `apps/api/src/openapi.<locale>.yaml`
2. Відредагуйте текст. Зберігайте код, посилання, `{placeholders}` та будь-які маркери `⸤I18N…⸥`
точно такими, як вони є; валідатор конвеєра відхиляє переклад, що видаляє
або переставляє їх.
3. Відкрийте PR. Редагування одиниці змінює її походження з `machine` на `human`, тож
конвеєр **ніколи не перезапише її** на пізнішому запуску. Якщо англійське джерело
зміниться згодом, вашу одиницю буде позначено `stale` для перегляду, а не
тихо замінено.
Щоб повідомити про помилку перекладу, не надсилаючи код, відкрийте
[GitHub Issue](https://github.com/snapotter-hq/SnapOtter/issues) із зазначенням
URL сторінки, мови, некоректного тексту та вашого запропонованого виправлення.
::: tip
Супровідники запускають конвеєр перекладу; вам не потрібен ключ API, щоб
зробити внесок. Просто відредагуйте згенерований файл і відкрийте PR. Дивіться
[`scripts/i18n/README.md`](https://github.com/snapotter-hq/SnapOtter/blob/main/scripts/i18n/README.md)
щодо того, як працює конвеєр.
:::