--- 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 (

{t.common.settings}

{format(t.settings.people.deleteConfirm, { username: "admin" })}

{plural(count, t.automate.fileCount, t.automate.fileCountPlural)}

); } ``` ## Внесок у переклад {#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//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/.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/.json` - сторінка документації: `apps/docs//**.md` - довідник API: `apps/api/src/openapi..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) щодо того, як працює конвеєр. :::