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

Übersetzungsleitfaden

SnapOtter wird von Haus aus mit 21 Sprachen ausgeliefert. Das i18n-System verwendet eine leichtgewichtige, eigene Laufzeitumgebung mit TypeScript-erzwungener Vollständigkeit der Locales und dynamischem Code-Splitting.

Unterstützte Sprachen

CodeSpracheNative NameDirection
enEnglischEnglishLTR
zh-CNChinesisch (Vereinfacht)简体中文LTR
zh-TWChinesisch (Traditionell)繁體中文LTR
jaJapanisch日本語LTR
koKoreanisch한국어LTR
esSpanischEspañolLTR
frFranzösischFrançaisLTR
itItalienischItalianoLTR
pt-BRPortugiesisch (Brasilien)Português (Brasil)LTR
deDeutschDeutschLTR
nlNiederländischNederlandsLTR
svSchwedischSvenskaLTR
ruRussischРусскийLTR
plPolnischPolskiLTR
ukUkrainischУкраїнськаLTR
arArabischالعربيةRTL
trTürkischTürkçeLTR
hiHindiहिन्दीLTR
viVietnamesischTiếng ViệtLTR
idIndonesischBahasa IndonesiaLTR
thThailändischไทยLTR

Wie die Spracherkennung funktioniert

SnapOtter verwendet eine dreistufige Auflösungsreihenfolge:

  1. Benutzereinstellung - gespeichert in localStorage("snapotter-locale") und mit den Benutzereinstellungen synchronisiert, sobald angemeldet
  2. Automatische Browsererkennung - durchläuft das navigator.languages-Array mit BCP 47-Präfixabgleich
  3. Instanzstandard - die DEFAULT_LOCALE-Umgebungsvariable des Admins (abgerufen von GET /api/v1/config/locale)
  4. Englischer Fallback - immer verfügbar

Benutzer können die Sprache ändern über:

  • Den Globus-Auswähler in der Fußzeile (Desktop, immer sichtbar)
  • Den Sprachauswähler auf der Anmeldeseite (vor der Anmeldung)
  • Den Abschnitt Einstellungen > Allgemein (Einstellung pro Benutzer)
  • Das Sprach-Dropdown in der mobilen Seitenleiste
  • Der Abschnitt Einstellungen > System legt den instanzweiten Standard fest (nur Admin)

Wie Übersetzungen funktionieren

Alle UI-Zeichenketten befinden sich in packages/shared/src/i18n/. Die Referenzdatei ist en.ts, die ein typisiertes Objekt mit jeder von der App verwendeten Zeichenkette exportiert (~1500 Schlüssel). Andere Sprachen sind separate Dateien (z. B. de.ts, fr.ts), die dieselbe Struktur exportieren.

Der Typ TranslationKeys verwendet DeepStringRecord, um jeden Zeichenkettenwert zu akzeptieren und gleichzeitig die Schlüsselstruktur zu erzwingen. TypeScript erkennt fehlende Schlüssel in jeder Übersetzungsdatei zur Kompilierzeit.

Zur Laufzeit wird nur die aktive Locale über dynamisches import() geladen, wodurch das Haupt-Bundle klein bleibt.

Übersetzungen in Komponenten verwenden

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

Eine Übersetzung beitragen

Wir begrüßen Übersetzungs-PRs direkt. Du kannst eine bestehende Locale verbessern oder eine neue hinzufügen.

Um eine Fehlübersetzung zu melden, ohne Code einzureichen, öffne ein GitHub Issue mit der Sprache, der fehlerhaften Zeichenkette und dem vorgeschlagenen Fix.

TIP

Übersetzungs-PRs erfordern keine vorherige Genehmigung. Forke das Repository, nimm deine Änderungen vor und öffne einen PR. Siehe den Contributing Guide für den vollständigen PR-Prozess und die CLA-Anforderung.

Wie man eine Übersetzung erstellt oder aktualisiert

1. Forken und klonen

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

2. Die Referenzdatei kopieren (nur bei neuer Sprache)

Überspringe diesen Schritt, wenn du eine bestehende Übersetzung verbesserst.

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

3. Die Zeichenketten übersetzen

Öffne deine neue Datei und übersetze jeden Zeichenkettenwert. Behalte die Objektstruktur und die Schlüssel exakt gleich bei.

ts
import type { TranslationKeys } from "./en.js";

export const xx: TranslationKeys = {
  common: {
    upload: "Your translation here",
    // ... translate all entries
  },
  // ... translate all sections
} as const;

Regeln:

  • Übersetze keine Objektschlüssel, nur Zeichenkettenwerte
  • Behalte as const am Ende bei
  • Importiere TranslationKeys aus ./en.js und typisiere deinen Export
  • Behalte {variable}-Platzhalter exakt so bei, wie sie sind
  • Arrays (rotatingPhrases, progressMessages) müssen dieselbe Anzahl von Einträgen haben
  • Nicht übersetzen: SnapOtter, JPEG, PNG, WebP, EXIF, API und andere Fachbegriffe

4. Die Locale registrieren (nur bei neuer Sprache)

Füge deine Locale zu SUPPORTED_LOCALES in packages/shared/src/i18n/index.ts hinzu:

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

5. Überprüfen

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

6. Einreichen

Öffne einen PR gegen main mit einem Titel wie feat(i18n): add Swedish translation oder fix(i18n): correct German typos. Der CLA-Bot wird dich bei deinem ersten Beitrag um eine Unterschrift bitten.

Neue Übersetzungsschlüssel hinzufügen

Wenn du ein neues Feature hinzufügst, das neue UI-Zeichenketten benötigt:

  1. Füge die neuen Schlüssel zuerst zu en.ts hinzu (der Referenzdatei)
  2. Führe pnpm typecheck aus - jede Locale-Datei schlägt fehl, wenn der neue Schlüssel fehlt
  3. Füge den neuen Schlüssel zu allen Locale-Dateien hinzu (verwende Englisch als vorübergehenden Fallback)

Konfiguration

Lege die Standardsprache der Instanz über eine Umgebungsvariable fest:

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

Dateireferenz

DateiZweck
packages/shared/src/i18n/en.tsEnglische Zeichenketten (Referenz-Locale, ~1500 Schlüssel)
packages/shared/src/i18n/index.tsSUPPORTED_LOCALES, loadTranslations(), Typ-Exporte
packages/shared/src/i18n/<locale>.tsÜbersetzungsdateien pro Sprache
apps/web/src/contexts/i18n-context.tsxI18nProvider, useTranslation()-Hook
apps/web/src/lib/format.tsformat()-, plural()-, formatFileSize()-Helfer
apps/api/src/routes/config.tsÖffentlicher GET /api/v1/config/locale-Endpunkt

Website, Docs und API-Referenz übersetzen

Die 21-Sprachen-Unterstützung oben deckt die App ab. Die öffentliche Website (snapotter.com), diese Dokumentationsseite und die REST-API-Referenz werden ebenfalls in alle 21 Sprachen übersetzt, durch eine separate, hash-gesteuerte Pipeline, die dieselben Tool-Namen und Beschreibungen aus packages/shared/src/i18n wiederverwendet, sodass die Terminologie überall konsistent bleibt.

Standardmäßig maschinell übersetzt

Jede nicht-englische Seite auf der Website und in den Docs wird im ersten Durchlauf maschinell übersetzt (durch eine Claude-Code-Sitzung, nicht durch einen Drittanbieterdienst) und trägt ein kleines, ausblendbares Banner, das darauf hinweist, mit einem Link zurück hierher. Das ist Absicht: So werden alle 21 Sprachen schnell und ehrlich ausgeliefert, und die Community wird eingeladen, die Seiten zu verfeinern, die am wichtigsten sind. Maschinelle Übersetzung vermittelt die Bedeutung; menschliche Überprüfung sorgt dafür, dass sie sich natürlich liest.

Wie die Pipeline entscheidet, was übersetzt wird

Jede übersetzbare Einheit des englischen Quelltexts wird gehasht, und der Hash wird neben ihrer Übersetzung gespeichert. Bei jedem Durchlauf:

  • übersetzt die Pipeline jede Einheit, die noch keine Übersetzung hat,
  • überspringt jede Einheit, deren gespeicherter Hash noch mit dem englischen Quelltext übereinstimmt,
  • übersetzt eine maschinelle Einheit neu, wenn sich ihr englischer Quelltext ändert,
  • und markiert eine von einem Menschen verfeinerte Einheit als stale (benötigt Überprüfung), wenn sich ihr englischer Quelltext ändert, anstatt deine Arbeit zu überschreiben.

Eine Web-Übersetzung per PR verfeinern

Du verbesserst eine Übersetzung der Website, der Docs oder der API-Referenz auf dieselbe Weise, wie du eine App-Locale verbesserst: indem du die generierte Datei bearbeitest und einen PR öffnest.

  1. Finde die generierte Übersetzung für deine Sprache:
    • Website-UI-Zeichenketten: apps/landing/src/i18n/<locale>.json
    • eine Docs-Seite: apps/docs/<locale>/**.md
    • die API-Referenz: apps/api/src/openapi.<locale>.yaml
  2. Bearbeite den Text. Behalte Code, Links, {placeholders} und alle ⸤I18N…⸥-Markierungen exakt so bei, wie sie sind; der Validator der Pipeline weist eine Übersetzung zurück, die sie auslässt oder umsortiert.
  3. Öffne einen PR. Das Bearbeiten einer Einheit ändert ihre Herkunft von machine zu human, sodass die Pipeline sie bei einem späteren Durchlauf niemals überschreibt. Wenn sich der englische Quelltext danach ändert, wird deine Einheit als stale zur Überprüfung markiert, anstatt stillschweigend ersetzt zu werden.

Um eine Fehlübersetzung zu melden, ohne Code einzureichen, öffne ein GitHub Issue mit der Seiten-URL, der Sprache, dem fehlerhaften Text und deinem vorgeschlagenen Fix.

TIP

Maintainer führen die Übersetzungs-Pipeline aus; du benötigst keinen API-Schlüssel, um beizutragen. Bearbeite einfach die generierte Datei und öffne einen PR. Siehe scripts/i18n/README.md für Informationen dazu, wie die Pipeline läuft.