Skip to content

Translation Adapter (Code Level)

C4 Level 4 detail of the PSI translation system. Shows the adapter interface, factory pattern, per-silo configuration, and content translation flow.

Adapter Class Hierarchy

classDiagram
    class TranslationAdapter {
        <<interface>>
        +translate(text: string, fromLang: string, toLang: string) Promise~string~
    }

    class LLMTranslationAdapter {
        -llmAdapter: LLMAdapter
        +translate(text, fromLang, toLang)
    }

    class DeepLAdapter {
        -apiKey: string
        -apiUrl: string
        +translate(text, fromLang, toLang)
    }

    class TextShuttleAdapter {
        -apiKey: string
        -apiUrl: string
        +translate(text, fromLang, toLang)
    }

    class SRGSSRAdapter {
        -apiUrl: string
        -apiKey: string
        +translate(text, fromLang, toLang)
    }

    TranslationAdapter <|.. LLMTranslationAdapter : TEXT_TRANSLATION_PROVIDER=llm (default)
    TranslationAdapter <|.. DeepLAdapter : TEXT_TRANSLATION_PROVIDER=deepl
    TranslationAdapter <|.. TextShuttleAdapter : TEXT_TRANSLATION_PROVIDER=textshuttle
    TranslationAdapter <|.. SRGSSRAdapter : TEXT_TRANSLATION_PROVIDER=srgssr-crknews-translation-api

    LLMTranslationAdapter --> LLMAdapter : uses for GPT translation

    note for LLMTranslationAdapter "Uses OpenAI GPT with server/prompts/translate.txt.\nDefault adapter. No extra API key needed\n(uses existing OPENAI_KEY)."

    note for DeepLAdapter "DeepL API for high-quality machine translation.\nRequires DEEPL_API_KEY."

    note for TextShuttleAdapter "TextShuttle API for specialized translation.\nRequires TEXT_SHUTTLE_API_KEY."

    note for SRGSSRAdapter "SRG SSR enterprise translation API.\nUsed by the SRG silo for Swiss multilingual content."

Factory Pattern

graph TD
    A[Server Initialization] --> B[Read TEXT_TRANSLATION_PROVIDER env var]
    B --> C{Provider value?}
    C -->|"llm" or undefined| D[createLLMTranslationAdapter<br/>Uses existing LLM Adapter]
    C -->|"deepl"| E[createDeepLAdapter<br/>Requires DEEPL_API_KEY]
    C -->|"textshuttle"| F[createTextShuttleAdapter<br/>Requires TEXT_SHUTTLE_API_KEY]
    C -->|"srgssr-crknews-translation-api"| G[createSRGSSRAdapter<br/>Requires SRG_TRANSLATION_API_URL]

    D --> H[TranslationAdapter instance]
    E --> H
    F --> H
    G --> H

    H --> I[Injected into contentTranslation module]

Content Translation Flow

sequenceDiagram
    participant User
    participant Frontend as PSI Frontend
    participant Backend as PSI Backend (Hono)
    participant Module as contentTranslation Module
    participant Store as ServerStore
    participant Adapter as Translation Adapter
    participant Service as Translation Service

    User->>Frontend: Post comment in German
    Frontend->>Backend: POST /api/contentTranslation/translateContent

    Backend->>Module: translateContent(store, params)
    Module->>Store: Check silo config: multiLanguage settings
    Store-->>Module: {enabled: true, languages: ["de", "fr", "it", "rm"]}

    loop For each target language != source
        Module->>Adapter: translate(commentText, "de", "fr")
        Adapter->>Service: API call (DeepL/TextShuttle/SRG SSR/OpenAI)
        Service-->>Adapter: Translated text
        Adapter-->>Module: "French translation"

        Module->>Store: setModuleData(PUBLIC, {translation: "fr"}, translatedText)
    end

    Module-->>Backend: {translations: {fr: "...", it: "...", rm: "..."}}
    Backend-->>Frontend: Translation results
    Frontend->>User: Display comment with language toggle

Per-Silo Configuration

Different silos use different translation providers and language sets:

graph LR
    subgraph "Silo Configuration (psi.config.ts)"
        SRG["SRG Silo<br/>multiLanguage: true<br/>languages: de, fr, it, rm<br/>provider: srgssr-crknews-translation-api"]
        Global["Global Silo<br/>multiLanguage: false<br/>Single language: en"]
        ZDF["ZDF Silo<br/>multiLanguage: false<br/>Single language: de"]
        RC["RC Silo<br/>multiLanguage: false<br/>Single language: fr-CA"]
    end

    subgraph "Server Config (env vars)"
        LLM["TEXT_TRANSLATION_PROVIDER=llm<br/>Default for most silos"]
        SRGAPI["TEXT_TRANSLATION_PROVIDER=<br/>srgssr-crknews-translation-api<br/>For SRG silo"]
    end

    SRG --> SRGAPI
    Global --> LLM
    ZDF --> LLM
    RC --> LLM

Supported Languages

Language Code Silos
English en Global, CBC
French (Standard) fr RTBF
French (Canadian) fr-CA Radio-Canada
German de ZDF, SRG
Italian it SRG
Dutch nl KRO-NCRV/NPO
Romansh rm SRG

LLM Translation Prompt

The LLM adapter uses server/prompts/translate.txt which instructs GPT to:

  1. Translate the provided text to the target language
  2. Preserve the tone and register of the original
  3. Handle user-generated content idioms and colloquialisms
  4. Return only the translated text (no explanations)

Further Reading