Übersetzungssystem: Drei-Technologien-Hybridansatz

Dieser Artikel erklärt unser Übersetzungssystem, das drei Technologien kombiniert – Flask-Babel für Templates, TranslationManager für Phrasentabellen und DeepSeek AI für Inhaltsübersetzung – um umfassende mehrsprachige Unterstützung zu bieten.

Das Problem: Übersetzung einer komplexen Website

Unsere Website hat mehrere Arten von Inhalten, die übersetzt werden müssen:

  • Statische Templates: Navigation, Buttons, Beschriftungen (500+ Zeichenketten)

  • Dynamische Inhalte: Produktnamen, Beschreibungen, Funktionen (10.000+ Zeichenketten)

  • Nutzererstellte Inhalte: Bewertungen, Kommentare, Support-Tickets

  • Technische Begriffe: Markennamen, SKUs, URLs (dürfen NICHT übersetzt werden)

Ein einzelner Übersetzungsansatz funktioniert nicht:

  • Maschinenübersetzung: Schnell, aber ungenau für technische Begriffe

  • Manuelle Übersetzung: Genau, aber langsam und teuer

  • Nur Templates: Handhabt keine dynamischen Inhalte

Wir brauchen einen Hybridansatz, der das Beste aus allen dreien kombiniert.

Übersetzungsarchitektur

graph TD
    Request[User Request
lang=hi] subgraph Templates Babel[Flask-Babel
.po files] Static[Static strings
buttons, labels] end subgraph Dynamic TM[TranslationManager
phrase tables] Phrases[Product names
features, terms] end subgraph AI DS[DeepSeek API
AI translation] Content[Descriptions
articles, long text] end Request --> Babel Request --> TM Request --> DS Babel --> Static TM --> Phrases DS --> Content Static --> Response[Translated Page] Phrases --> Response Content --> Response

Drei Technologien

1. Flask-Babel: Template-Übersetzungen

Flask-Babel handhabt statische Template-Zeichenketten mithilfe von gettext .po-Dateien.

  • Anwendungsfall: Navigation, Buttons, Beschriftungen, Fehlermeldungen

  • Beispiel:

# Template
{{ _('Add to Cart') }}

# .po file (Hindi)
msgid "Add to Cart"
msgstr "कार्ट में जोड़ें"

Vorteile:

  • ✅ Standard-i18n-Ansatz

  • ✅ Unterstützung durch Übersetzungstools (Poedit, Weblate)

  • ✅ Validierung zur Kompilierzeit

  • ✅ Schneller Zugriff (kompilierte .mo-Dateien)

Einschränkungen:

  • ❌ Funktioniert nur für statische Zeichenketten

  • ❌ Erfordert Codeänderungen, um neue Zeichenketten hinzuzufügen

  • ❌ Keine Unterstützung für dynamische Inhalte

2. TranslationManager: Phrasentabellen

Unsere benutzerdefinierte TranslationManager-Klasse handhabt dynamische Inhalte mithilfe von JSON-Phrasentabellen.

  • Anwendungsfall: Produktnamen, Funktionen, Spezifikationen, Beschreibungen

  • Beispiel:

{
  "Mini PC": "मिनी पीसी",
  "Intel N100": "Intel N100",
  "8GB RAM": "8GB RAM",
  "256GB SSD": "256GB SSD"
}

Vorteile:

  • ✅ Unterstützung für dynamische Inhalte

  • ✅ Updates zur Laufzeit (kein Neustart nötig)

  • ✅ Bewahrungsregeln (Markennamen, SKUs)

  • ✅ Regionale Formatierung (Dezimaltrennzeichen)

  • ✅ Übersetzungswarteschlange (fehlende Zeichenketten)

Einschränkungen:

  • ❌ Manuelle Phrasenverwaltung

  • ❌ Kein Kontext für Übersetzer

  • ❌ Erfordert anfängliche Übersetzung

3. DeepSeek AI: Inhaltsübersetzung

Das DeepSeek-AI-Modell handhabt die Übersetzung von langen Inhalten.

  • Anwendungsfall: Blog-Artikel, Produktbeschreibungen, Marketingtexte

  • Beispiel:

prompt = f"Translate to {lang}: {text}"
response = deepseek.generate(prompt)

Vorteile:

  • ✅ Handhabt lange Inhalte (1000+ Wörter)

  • ✅ Kontextbewusste Übersetzung

  • ✅ Natürliche Sprachausgabe

  • ✅ Stapelverarbeitung

Einschränkungen:

  • ❌ API-Kosten pro Übersetzung

  • ❌ Erfordert Internetverbindung

  • ❌ Kann technische Begriffe falsch übersetzen

TranslationManager-Architektur

Phrasentabellen-Struktur

Phrasentabellen werden als JSON-Dateien pro Sprache gespeichert:

app/shared/translation/phrase_tables/

Jede Datei bildet englische Phrasen auf Übersetzungen ab:

{
  "Mini PC": "मिनी पीसी",
  "Thin Client": "थिन क्लाइंट",
  "Industrial PC": "औद्योगिक पीसी",
  "All-in-One": "ऑल-इन-वन"
}

Übersetzungsabfrage

Die get()-Methode führt eine mehrstufige Abfrage durch:

1. Bewahrungsprüfung:

if self._should_skip_translation(text, lang):
    return text  # Markennamen, SKUs, URLs nicht übersetzen

2. Komma-Aufteilung (für lange Zeichenketten):

if len(text) > 150 and "," in text:
    parts = text.split(",")
    return ", ".join(self.get(p, lang) for p in parts)

3. Phrasentabellen-Abfrage:

if text in self.phrase_tables[lang]:
    return self.phrase_tables[lang][text]

4. Legacy-Cache-Promotion:

if text_hash in self.legacy_caches[lang]:
    translation = self.legacy_caches[lang][text_hash]
    self.set(text, lang, translation)  # In Phrasentabelle befördern
    return translation

5. In Warteschlange, falls fehlend:

if queue_if_missing:
    self.add_to_queue(text, lang)
return None  # Keine Übersetzung gefunden

Bewahrungsregeln

Wir bewahren bestimmte Inhaltstypen:

Markennamen:

BRAND_NAMES_PRESERVE = [
    "Thinvent", "Intel", "AMD", "Microsoft", "Ubuntu",
    "Windows", "Linux", "WiFi", "Bluetooth"
]

Technische Begriffe:

TECHNICAL_TERMS_PRESERVE = [
    "HDMI", "DisplayPort", "VGA", "USB", "Ethernet",
    "DDR4", "SSD", "NVMe", "PCIe", "SATA"
]

SKUs (musterbasiert):

if not " " in text and text.count("-") >= 2:
    return True  # Bewahre "Treo-N100-8-256-2H-W6-11P"

URLs:

if text.startswith(("/", "http://", "https://")):
    return True

Regionale Formatierung

Für europäische Gebietsschemata (Deutsch, Französisch, Spanisch) wenden wir regionale Formatierung an:

Dezimaltrennzeichen:

# Englisch: 34.00
# Deutsch:  34,00
text = text.replace(".", ",")

Spannung/Stromstärke:

# Englisch: 12.5V
# Deutsch:  12,5V
text = re.sub(r"(\d+)\.(\d+)V", r"\1,\2V", text)

Dies stellt sicher, dass Zahlen für jedes Gebietsschema korrekt angezeigt werden.

Übersetzungswarteschlange

Fehlende Übersetzungen werden zur Stapelverarbeitung in die Warteschlange gestellt:

[
  {
    "text": "Compact Desktop",
    "lang": "hi",
    "hash": "a1b2c3d4e5f6"
  },
  {
    "text": "Fanless Design",
    "lang": "hi",
    "hash": "f6e5d4c3b2a1"
  }
]

Ein Hintergrundskript verarbeitet die Warteschlange mithilfe von DeepSeek AI:

for item in queue:
    translation = deepseek.translate(item["text"], item["lang"])
    translation_manager.set(item["text"], item["lang"], translation)

Dateisperren

Wir verwenden fcntl-Dateisperren, um gleichzeitige Schreibvorgänge zu verhindern:

with open(lock_path, "w") as lock_file:
    fcntl.flock(lock_file, fcntl.LOCK_EX)
    try:
        # Phrasentabelle lesen, modifizieren, schreiben
        with open(table_path, "r+") as f:
            data = json.load(f)
            data.update(new_translations)
            f.seek(0)
            json.dump(data, f)
            f.truncate()
    finally:
        fcntl.flock(lock_file, fcntl.LOCK_UN)

Dies stellt sicher, dass mehrere Prozesse die Phrasentabellen nicht beschädigen.

Spracherkennung

Wir erkennen die Abfragesprache mithilfe von Unicode-Bereichen und Stoppwörtern:

Unicode-Bereichserkennung

Devanagari (Hindi, Marathi):

if 0x0900 <= ord(char) <= 0x097F:
    return "hi"

Bengalisch:

if 0x0980 <= ord(char) <= 0x09FF:
    return "bn"

Arabisch:

if 0x0600 <= ord(char) <= 0x06FF:
    return "ar"

Kyrillisch (Russisch):

if 0x0400 <= ord(char) <= 0x04FF:
    return "ru"

CJK (Chinesisch, Japanisch, Koreanisch):

if 0x4E00 <= ord(char) <= 0x9FFF:  # Kanji/Hanzi
    has_cjk = True
if 0x3040 <= ord(char) <= 0x30FF:  # Hiragana/Katakana
    return "ja"
if 0xAC00 <= ord(char) <= 0xD7AF:  # Hangul
    return "ko"

Stoppworterkennung

Für lateinbasierte Sprachen verwenden wir Stoppwörter:

Spanisch:

SPANISH_STOPWORDS = {
    "el", "la", "los", "las", "un", "una", "para", "con",
    "en", "por", "que", "es", "su", "y", "del", "al"
}

Französisch:

FRENCH_STOPWORDS = {
    "le", "la", "les", "des", "du", "un", "une", "pour",
    "avec", "en", "est", "sur", "et", "au", "dans"
}

Deutsch:

GERMAN_STOPWORDS = {
    "der", "die", "das", "ein", "eine", "für", "mit",
    "ist", "und", "auf", "den", "dem", "bei", "von"
}

Wenn ein Stoppwort übereinstimmt, geben wir diese Sprache zurück.

Dynamische Spracherkennung

Benutzer können die Sprache über folgende Wege angeben:

1. URL-Parameter

/p/Treo-N100-8-256-2H-W6-11P?lang=hi

2. Cookie

response.set_cookie("lang", "hi", max_age=31536000)

3. Browser Accept-Language-Header

lang = request.accept_languages.best_match(["en", "hi", "es", "fr", "de"])

Priorität: URL-Parameter > Cookie > Accept-Language > Standard (en)

Rekursive Übersetzung

Wir übersetzen rekursiv verschachtelte Datenstrukturen:

def translate_data(obj, lang):
    if isinstance(obj, dict):
        return {k: translate_data(v, lang) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [translate_data(item, lang) for item in obj]
    elif isinstance(obj, str):
        return self.get(obj, lang) or obj
    return obj

Beispiel:

data = {
    "title": "Mini PC",
    "features": {
        "RAM": "8GB",
        "Storage": "256GB SSD"
    },
    "price": 25000
}

translated = translate_data(data, "hi")
# Ergebnis:
# {
#     "title": "मिनी पीसी",
#     "features": {
#         "RAM": "8GB",
#         "Storage": "256GB SSD"
#     },
#     "price": 25000
# }

Funktionsübersetzung

Produktfunktionen erfordern spezielle Handhabung:

def translate_features(features, lang):
    translated = {}
    for heading, feature_dict in features.items():
        # Überschrift übersetzen
        translated_heading = self.get(heading, lang) or heading

        # Funktionsnamen und -werte übersetzen
        translated_features = {}
        for name, value in feature_dict.items():
            translated_name = self.get(name, lang) or name
            translated_value = self.get(value, lang) or value
            translated_features[translated_name] = translated_value

        translated[translated_heading] = translated_features

    return translated

Beispiel:

features = {
    "Processing": {
        "Processor": "Intel N100",
        "Cores": "4",
        "RAM": "8GB"
    }
}

translated = translate_features(features, "hi")
# Ergebnis:
# {
#     "प्रोसेसिंग": {
#         "प्रोसेसर": "Intel N100",
#         "कोर": "4",
#         "RAM": "8GB"
#     }
# }

Integration in die SEO-Pipeline

Das Übersetzungssystem integriert sich in die SEO-Pipeline:

Abfragesprachen-Propagation

Wenn ein Benutzer auf Hindi sucht, propagieren wir die Sprache zu verwandten Suchen:

if SEO_ENABLE_LANGUAGE_PROPAGATION:
    lang = detect_query_language(query)
    if lang != "en":
        url = f"{url}?lang={lang}"

Dies stellt sicher, dass Benutzer bei Klicks auf verwandte Suchen in ihrer bevorzugten Sprache bleiben.

Übersetzte Abfrageseiten

Abfrageseiten werden in mehreren Sprachen generiert:

/q/mini-pc          (Englisch)
/q/mini-pc?lang=hi  (Hindi)
/q/mini-pc?lang=es  (Spanisch)

Der Inhalt wird mithilfe von TranslationManager übersetzt.

Leistungsmerkmale

Phrasentabellen-Abfrage:

  • Zeit: O(1) Dictionary-Abfrage (~1μs)

  • Speicher: ~5 MB pro Sprache (10.000 Phrasen)

Spracherkennung:

  • Zeit: O(n) wobei n = Zeichenkettenlänge (~10μs für 100 Zeichen)

  • Speicher: Vernachlässigbar

Rekursive Übersetzung:

  • Zeit: O(n) wobei n = Anzahl der Zeichenketten (~1ms für 100 Zeichenketten)

  • Speicher: Proportional zur Größe der Datenstruktur

Dateisperren:

  • Zeit: ~1ms pro Sperrerwerb

  • Konflikte: Selten (Schreibvorgänge sind selten)

Referenzen

Technische Konzepte