This page was machine-translated. Spotted a mistake?Help improve it.
Skip to content

Посібник з перекладу

SnapOtter постачається з 21 мовою з коробки. Система i18n використовує легкий власний рантайм із контролем повноти локалей через TypeScript і динамічним розбиттям коду.

Підтримувані мови

CodeLanguageNative NameDirection
enАнглійськаEnglishLTR
zh-CNКитайська (спрощена)简体中文LTR
zh-TWКитайська (традиційна)繁體中文LTR
jaЯпонська日本語LTR
koКорейська한국어LTR
esІспанськаEspañolLTR
frФранцузькаFrançaisLTR
itІталійськаItalianoLTR
pt-BRПортугальська (Бразилія)Português (Brasil)LTR
deНімецькаDeutschLTR
nlНідерландськаNederlandsLTR
svШведськаSvenskaLTR
ruРосійськаРусскийLTR
plПольськаPolskiLTR
ukУкраїнськаУкраїнськаLTR
arАрабськаالعربيةRTL
trТурецькаTürkçeLTR
hiГіндіहिन्दीLTR
viВ'єтнамськаTiếng ViệtLTR
idІндонезійськаBahasa IndonesiaLTR
thТайськаไทยLTR

Як працює визначення мови

SnapOtter використовує трирівневий порядок розвʼязання:

  1. Уподобання користувача - зберігається в localStorage("snapotter-locale") і синхронізується з налаштуваннями користувача після автентифікації
  2. Автовизначення браузера - проходить масив navigator.languages зі співставленням префіксів за BCP 47
  3. Стандартне значення інстансу - змінна середовища DEFAULT_LOCALE адміністратора (отримується з GET /api/v1/config/locale)
  4. Резервна англійська - завжди доступна

Користувачі можуть змінити мову з:

  • Селектора-глобуса у футері (десктоп, завжди видимий)
  • Селектора мови на сторінці входу (до автентифікації)
  • Розділу Settings > General (уподобання окремого користувача)
  • Випадаючого списку мов у мобільній бічній панелі
  • Розділ Settings > System задає стандартне значення для всього інстансу (лише для адміністратора)

Як працюють переклади

Усі рядки інтерфейсу зберігаються в packages/shared/src/i18n/. Еталонний файл - en.ts, який експортує типізований обʼєкт з кожним рядком, що використовує застосунок (~1500 ключів). Інші мови - це окремі файли (наприклад, de.ts, fr.ts), що експортують ту саму структуру.

Тип TranslationKeys використовує DeepStringRecord, щоб приймати будь-яке рядкове значення, водночас контролюючи структуру ключів. TypeScript виявляє відсутні ключі в будь-якому файлі перекладу під час компіляції.

Під час виконання завантажується лише активна локаль через динамічний import(), завдяки чому основний бандл лишається невеликим.

Використання перекладів у компонентах

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>
  );
}

Внесок у переклад

Ми вітаємо PR з перекладами напряму. Ви можете покращити наявну локаль або додати нову.

Щоб повідомити про помилку перекладу, не надсилаючи код, відкрийте GitHub Issue із зазначенням мови, некоректного рядка та запропонованого виправлення.

TIP

PR з перекладами не потребують попереднього схвалення. Зробіть форк репозиторію, внесіть свої зміни та відкрийте PR. Дивіться Посібник для контриб'юторів щодо повного процесу PR та вимоги CLA.

Як створити або оновити переклад

1. Форк і клонування

bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install

2. Скопіюйте еталонний файл (лише для нової мови)

Пропустіть цей крок, якщо ви покращуєте наявний переклад.

bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts

3. Перекладіть рядки

Відкрийте свій новий файл і перекладіть кожне рядкове значення. Зберігайте структуру обʼєкта та ключі точно такими самими.

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. Зареєструйте локаль (лише для нової мови)

Додайте свою локаль до SUPPORTED_LOCALES у packages/shared/src/i18n/index.ts:

ts
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },

5. Перевірте

bash
pnpm typecheck    # catches missing or mistyped keys
pnpm lint         # formatting check
pnpm dev          # manually verify strings appear correctly

6. Надішліть

Відкрийте PR проти main із заголовком на кшталт feat(i18n): add Swedish translation або fix(i18n): correct German typos. Бот CLA попросить вас підписати угоду під час першого внеску.

Додавання нових ключів перекладу

Під час додавання нової функції, якій потрібні нові рядки інтерфейсу:

  1. Спершу додайте нові ключі до en.ts (еталонний файл)
  2. Запустіть pnpm typecheck - кожен файл локалі завершиться помилкою, якщо в ньому бракує нового ключа
  3. Додайте новий ключ до всіх файлів локалей (використовуйте англійську як тимчасовий резервний варіант)

Конфігурація

Задайте стандартну мову інстансу через змінну середовища:

yaml
DEFAULT_LOCALE: "de"  # German as the default for all new users

Довідник файлів

FilePurpose
packages/shared/src/i18n/en.tsАнглійські рядки (еталонна локаль, ~1500 ключів)
packages/shared/src/i18n/index.tsSUPPORTED_LOCALES, loadTranslations(), експорти типів
packages/shared/src/i18n/<locale>.tsФайли перекладів для кожної мови
apps/web/src/contexts/i18n-context.tsxI18nProvider, хук useTranslation()
apps/web/src/lib/format.tsДопоміжні функції format(), plural(), formatFileSize()
apps/api/src/routes/config.tsПублічний ендпоінт GET /api/v1/config/locale

Переклад вебсайту, документації та довідника API

Підтримка 21 мови вище стосується застосунку. Публічний вебсайт (snapotter.com), цей сайт документації та довідник REST API також перекладено всіма 21 мовами через окремий конвеєр із контролем за хешем, який повторно використовує ті самі назви й описи інструментів з packages/shared/src/i18n, тож термінологія лишається узгодженою всюди.

Машинний переклад за замовчуванням

Кожну неанглійську сторінку на вебсайті та в документації перекладено машинно на першому проході (сесією Claude Code, а не стороннім сервісом), і вона має невеликий банер, який можна закрити та який про це повідомляє, з посиланням назад сюди. Це зроблено навмисно: так усі 21 мова постачаються швидко й чесно, після чого спільноті пропонується вдосконалити найважливіші сторінки. Машинний переклад передає зміст; людський перегляд робить його природним для читання.

Як конвеєр вирішує, що перекладати

Кожна перекладна одиниця англійського джерела хешується, і хеш зберігається поруч з її перекладом. На кожному запуску конвеєр:

  • перекладає будь-яку одиницю, яка ще не має перекладу,
  • пропускає будь-яку одиницю, чий збережений хеш усе ще відповідає англійському джерелу,
  • повторно перекладає машинну одиницю, коли її англійське джерело змінюється,
  • і позначає одиницю, доопрацьовану людиною, як stale (потребує перегляду), коли її англійське джерело змінюється, замість того щоб перезаписати вашу роботу.

Доопрацювання вебперекладу через 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 із зазначенням URL сторінки, мови, некоректного тексту та вашого запропонованого виправлення.

TIP

Супровідники запускають конвеєр перекладу; вам не потрібен ключ API, щоб зробити внесок. Просто відредагуйте згенерований файл і відкрийте PR. Дивіться scripts/i18n/README.md щодо того, як працює конвеєр.