Search K
Посібник з перекладу
SnapOtter постачається з 21 мовою з коробки. Система i18n використовує легкий власний рантайм із контролем повноти локалей через TypeScript і динамічним розбиттям коду.
Підтримувані мови
| 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 |
Як працює визначення мови
SnapOtter використовує трирівневий порядок розвʼязання:
- Уподобання користувача - зберігається в
localStorage("snapotter-locale")і синхронізується з налаштуваннями користувача після автентифікації - Автовизначення браузера - проходить масив
navigator.languagesзі співставленням префіксів за BCP 47 - Стандартне значення інстансу - змінна середовища
DEFAULT_LOCALEадміністратора (отримується зGET /api/v1/config/locale) - Резервна англійська - завжди доступна
Користувачі можуть змінити мову з:
- Селектора-глобуса у футері (десктоп, завжди видимий)
- Селектора мови на сторінці входу (до автентифікації)
- Розділу 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 install2. Скопіюйте еталонний файл (лише для нової мови)
Пропустіть цей крок, якщо ви покращуєте наявний переклад.
bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts3. Перекладіть рядки
Відкрийте свій новий файл і перекладіть кожне рядкове значення. Зберігайте структуру обʼєкта та ключі точно такими самими.
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 correctly6. Надішліть
Відкрийте PR проти main із заголовком на кшталт feat(i18n): add Swedish translation або fix(i18n): correct German typos. Бот CLA попросить вас підписати угоду під час першого внеску.
Додавання нових ключів перекладу
Під час додавання нової функції, якій потрібні нові рядки інтерфейсу:
- Спершу додайте нові ключі до
en.ts(еталонний файл) - Запустіть
pnpm typecheck- кожен файл локалі завершиться помилкою, якщо в ньому бракує нового ключа - Додайте новий ключ до всіх файлів локалей (використовуйте англійську як тимчасовий резервний варіант)
Конфігурація
Задайте стандартну мову інстансу через змінну середовища:
yaml
DEFAULT_LOCALE: "de" # German as the default for all new usersДовідник файлів
| 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
Підтримка 21 мови вище стосується застосунку. Публічний вебсайт (snapotter.com), цей сайт документації та довідник REST API також перекладено всіма 21 мовами через окремий конвеєр із контролем за хешем, який повторно використовує ті самі назви й описи інструментів з packages/shared/src/i18n, тож термінологія лишається узгодженою всюди.
Машинний переклад за замовчуванням
Кожну неанглійську сторінку на вебсайті та в документації перекладено машинно на першому проході (сесією Claude Code, а не стороннім сервісом), і вона має невеликий банер, який можна закрити та який про це повідомляє, з посиланням назад сюди. Це зроблено навмисно: так усі 21 мова постачаються швидко й чесно, після чого спільноті пропонується вдосконалити найважливіші сторінки. Машинний переклад передає зміст; людський перегляд робить його природним для читання.
Як конвеєр вирішує, що перекладати
Кожна перекладна одиниця англійського джерела хешується, і хеш зберігається поруч з її перекладом. На кожному запуску конвеєр:
- перекладає будь-яку одиницю, яка ще не має перекладу,
- пропускає будь-яку одиницю, чий збережений хеш усе ще відповідає англійському джерелу,
- повторно перекладає машинну одиницю, коли її англійське джерело змінюється,
- і позначає одиницю, доопрацьовану людиною, як
stale(потребує перегляду), коли її англійське джерело змінюється, замість того щоб перезаписати вашу роботу.
Доопрацювання вебперекладу через PR
Ви покращуєте переклад вебсайту, документації чи довідника API так само, як ви покращуєте локаль застосунку: редагуючи згенерований файл і відкриваючи PR.
- Знайдіть згенерований переклад для вашої мови:
- рядки інтерфейсу вебсайту:
apps/landing/src/i18n/<locale>.json - сторінка документації:
apps/docs/<locale>/**.md - довідник API:
apps/api/src/openapi.<locale>.yaml
- рядки інтерфейсу вебсайту:
- Відредагуйте текст. Зберігайте код, посилання,
{placeholders}та будь-які маркери⸤I18N…⸥точно такими, як вони є; валідатор конвеєра відхиляє переклад, що видаляє або переставляє їх. - Відкрийте PR. Редагування одиниці змінює її походження з
machineнаhuman, тож конвеєр ніколи не перезапише її на пізнішому запуску. Якщо англійське джерело зміниться згодом, вашу одиницю буде позначеноstaleдля перегляду, а не тихо замінено.
Щоб повідомити про помилку перекладу, не надсилаючи код, відкрийте GitHub Issue із зазначенням URL сторінки, мови, некоректного тексту та вашого запропонованого виправлення.
TIP
Супровідники запускають конвеєр перекладу; вам не потрібен ключ API, щоб зробити внесок. Просто відредагуйте згенерований файл і відкрийте PR. Дивіться scripts/i18n/README.md щодо того, як працює конвеєр.
