Search K
Przewodnik po tłumaczeniach
SnapOtter dostarczany jest z 21 językami od razu po instalacji. System i18n korzysta z lekkiego, własnego środowiska uruchomieniowego z wymuszaną przez TypeScript kompletnością lokalizacji oraz dynamicznym dzieleniem kodu.
Obsługiwane języki
| Code | Language | Native Name | Direction |
|---|---|---|---|
en | angielski | English | LTR |
zh-CN | chiński (uproszczony) | 简体中文 | LTR |
zh-TW | chiński (tradycyjny) | 繁體中文 | LTR |
ja | japoński | 日本語 | LTR |
ko | koreański | 한국어 | LTR |
es | hiszpański | Español | LTR |
fr | francuski | Français | LTR |
it | włoski | Italiano | LTR |
pt-BR | portugalski (Brazylia) | Português (Brasil) | LTR |
de | niemiecki | Deutsch | LTR |
nl | niderlandzki | Nederlands | LTR |
sv | szwedzki | Svenska | LTR |
ru | rosyjski | Русский | LTR |
pl | polski | Polski | LTR |
uk | ukraiński | Українська | LTR |
ar | arabski | العربية | RTL |
tr | turecki | Türkçe | LTR |
hi | hindi | हिन्दी | LTR |
vi | wietnamski | Tiếng Việt | LTR |
id | indonezyjski | Bahasa Indonesia | LTR |
th | tajski | ไทย | LTR |
Jak działa wykrywanie języka
SnapOtter stosuje trzypoziomową kolejność rozstrzygania:
- Preferencja użytkownika - przechowywana w
localStorage("snapotter-locale")i synchronizowana z ustawieniami użytkownika po zalogowaniu - Automatyczne wykrywanie przeglądarki - przechodzi przez tablicę
navigator.languagesz dopasowywaniem prefiksów BCP 47 - Domyślny język instancji - zmienna środowiskowa administratora
DEFAULT_LOCALE(pobierana zGET /api/v1/config/locale) - Awaryjny język angielski - zawsze dostępny
Użytkownicy mogą zmienić język w:
- selektorze Globe w stopce (na komputerze, zawsze widoczny)
- selektorze języka na stronie logowania (przed uwierzytelnieniem)
- sekcji Ustawienia > Ogólne (preferencja dla danego użytkownika)
- rozwijanej liście języków w mobilnym pasku bocznym
- sekcji Ustawienia > System, która ustawia domyślny język dla całej instancji (tylko administrator)
Jak działają tłumaczenia
Wszystkie napisy interfejsu znajdują się w packages/shared/src/i18n/. Plikiem referencyjnym jest en.ts, który eksportuje typowany obiekt zawierający każdy napis używany przez aplikację (~1500 kluczy). Pozostałe języki to osobne pliki (np. de.ts, fr.ts), które eksportują ten sam kształt.
Typ TranslationKeys wykorzystuje DeepStringRecord, aby przyjmować dowolną wartość tekstową, wymuszając jednocześnie strukturę kluczy. TypeScript wychwytuje brakujące klucze w dowolnym pliku tłumaczenia na etapie kompilacji.
W czasie działania ładowana jest tylko aktywna lokalizacja poprzez dynamiczny import(), dzięki czemu główny pakiet pozostaje niewielki.
Używanie tłumaczeń w komponentach
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>
);
}Współtworzenie tłumaczenia
Chętnie przyjmujemy PR-y z tłumaczeniami bezpośrednio. Możesz ulepszyć istniejącą lokalizację lub dodać nową.
Aby zgłosić błędne tłumaczenie bez przesyłania kodu, otwórz zgłoszenie na GitHubie z podaniem języka, nieprawidłowego napisu oraz proponowanej poprawki.
TIP
PR-y z tłumaczeniami nie wymagają wcześniejszej akceptacji. Rozgałęź repozytorium, wprowadź zmiany i otwórz PR. Zajrzyj do przewodnika dla współtwórców, aby poznać pełny proces PR oraz wymóg CLA.
Jak utworzyć lub zaktualizować tłumaczenie
1. Rozgałęź i sklonuj
bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install2. Skopiuj plik referencyjny (tylko nowy język)
Pomiń ten krok, jeśli ulepszasz istniejące tłumaczenie.
bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts3. Przetłumacz napisy
Otwórz swój nowy plik i przetłumacz każdą wartość tekstową. Zachowaj strukturę obiektu i klucze dokładnie takie same.
ts
import type { TranslationKeys } from "./en.js";
export const xx: TranslationKeys = {
common: {
upload: "Your translation here",
// ... translate all entries
},
// ... translate all sections
} as const;Zasady:
- Nie tłumacz kluczy obiektu, tylko wartości tekstowe
- Zachowaj
as constna końcu - Zaimportuj
TranslationKeysz./en.jsi otypuj swój eksport - Zachowaj symbole zastępcze
{variable}dokładnie w takiej samej postaci - Tablice (
rotatingPhrases,progressMessages) muszą mieć tę samą liczbę wpisów - Nie tłumacz: SnapOtter, JPEG, PNG, WebP, EXIF, API oraz innych terminów technicznych
4. Zarejestruj lokalizację (tylko nowy język)
Dodaj swoją lokalizację do SUPPORTED_LOCALES w packages/shared/src/i18n/index.ts:
ts
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },5. Zweryfikuj
bash
pnpm typecheck # catches missing or mistyped keys
pnpm lint # formatting check
pnpm dev # manually verify strings appear correctly6. Prześlij
Otwórz PR wobec main z tytułem w rodzaju feat(i18n): add Swedish translation lub fix(i18n): correct German typos. Bot CLA poprosi Cię o podpis przy pierwszym wkładzie.
Dodawanie nowych kluczy tłumaczeń
Dodając nową funkcję, która wymaga nowych napisów interfejsu:
- Najpierw dodaj nowe klucze do
en.ts(plik referencyjny) - Uruchom
pnpm typecheck- każdy plik lokalizacji zgłosi błąd, jeśli brakuje w nim nowego klucza - Dodaj nowy klucz do wszystkich plików lokalizacji (jako tymczasowego zastępstwa użyj języka angielskiego)
Konfiguracja
Ustaw domyślny język instancji za pomocą zmiennej środowiskowej:
yaml
DEFAULT_LOCALE: "de" # German as the default for all new usersZestawienie plików
| File | Purpose |
|---|---|
packages/shared/src/i18n/en.ts | napisy angielskie (lokalizacja referencyjna, ~1500 kluczy) |
packages/shared/src/i18n/index.ts | SUPPORTED_LOCALES, loadTranslations(), eksporty typów |
packages/shared/src/i18n/<locale>.ts | pliki tłumaczeń dla poszczególnych języków |
apps/web/src/contexts/i18n-context.tsx | I18nProvider, hook useTranslation() |
apps/web/src/lib/format.ts | pomocnicze format(), plural(), formatFileSize() |
apps/api/src/routes/config.ts | publiczny punkt końcowy GET /api/v1/config/locale |
Tłumaczenie witryny, dokumentacji i dokumentacji API
Opisane powyżej wsparcie dla 21 języków obejmuje aplikację. Publiczna witryna (snapotter.com), ta strona z dokumentacją oraz dokumentacja REST API również są tłumaczone na wszystkie 21 języków przez osobny, sterowany hashem potok, który ponownie wykorzystuje te same nazwy i opisy narzędzi z packages/shared/src/i18n, dzięki czemu terminologia pozostaje spójna wszędzie.
Domyślnie tłumaczone maszynowo
Każda nieanglojęzyczna strona witryny i dokumentacji jest tłumaczona maszynowo przy pierwszym przejściu (przez sesję Claude Code, a nie usługę firm trzecich) i zawiera niewielki, możliwy do zamknięcia baner informujący o tym, wraz z odnośnikiem z powrotem tutaj. Jest to celowe: pozwala szybko i uczciwie dostarczyć wszystkie 21 języków, a następnie zaprasza społeczność do dopracowania stron, które mają największe znaczenie. Tłumaczenie maszynowe przekazuje sens; dopiero weryfikacja przez człowieka sprawia, że tekst czyta się naturalnie.
Jak potok decyduje, co przetłumaczyć
Każda tłumaczalna jednostka źródła angielskiego jest hashowana, a hash przechowywany jest obok jej tłumaczenia. Przy każdym uruchomieniu potok:
- tłumaczy każdą jednostkę, która nie ma jeszcze tłumaczenia,
- pomija każdą jednostkę, której przechowywany hash wciąż zgadza się ze źródłem angielskim,
- ponownie tłumaczy jednostkę maszynową, gdy zmieni się jej źródło angielskie,
- oraz oznacza jednostkę dopracowaną przez człowieka jako
stale(wymaga weryfikacji), gdy jej źródło angielskie ulegnie zmianie, zamiast nadpisywać Twoją pracę.
Dopracowanie tłumaczenia strony internetowej przez PR
Tłumaczenie witryny, dokumentacji lub dokumentacji API ulepszasz w ten sam sposób, w jaki ulepszasz lokalizację aplikacji: edytując wygenerowany plik i otwierając PR.
- Znajdź wygenerowane tłumaczenie dla swojego języka:
- napisy interfejsu witryny:
apps/landing/src/i18n/<locale>.json - strona dokumentacji:
apps/docs/<locale>/**.md - dokumentacja API:
apps/api/src/openapi.<locale>.yaml
- napisy interfejsu witryny:
- Zredaguj tekst. Zachowaj kod, odnośniki,
{placeholders}oraz wszelkie znaczniki⸤I18N…⸥dokładnie w takiej postaci, w jakiej są; walidator potoku odrzuca tłumaczenie, które pomija lub zmienia ich kolejność. - Otwórz PR. Edycja jednostki zmienia jej pochodzenie z
machinenahuman, dzięki czemu potok nigdy jej nie nadpisze przy późniejszym uruchomieniu. Jeśli źródło angielskie zmieni się później, Twoja jednostka zostanie oznaczona jakostaledo weryfikacji, zamiast zostać po cichu zastąpiona.
Aby zgłosić błędne tłumaczenie bez przesyłania kodu, otwórz zgłoszenie na GitHubie z podaniem adresu URL strony, języka, nieprawidłowego tekstu oraz proponowanej poprawki.
TIP
Potok tłumaczeń uruchamiają opiekunowie projektu; do wniesienia wkładu nie potrzebujesz klucza API. Wystarczy, że zredagujesz wygenerowany plik i otworzysz PR. Zajrzyj do scripts/i18n/README.md, aby dowiedzieć się, jak działa potok.
