Language Code Normalisation

June 16, 2026 · View on GitHub

Each vendor API has its own convention for language code format. ovos-translate-server normalises codes at the router boundary so the underlying OVOS plugin always receives BCP-47 lowercase codes (e.g. en, de, fr, en-us).


Per-Vendor Conventions

VendorInbound formatExample inboundOutbound formatExample outbound
LibreTranslatelowercase BCP-47en, delowercase BCP-47en, de
DeepLuppercase BCP-47EN, EN-US, DEuppercase BCP-47EN, EN-US
DeepLXcase-insensitive BCP-47EN, de, autono code echoed
Googlelowercase BCP-47en, de, en-uslowercase BCP-47en, de
Azuremixed BCP-47en, de, en-UStarget code echoed unchangedde
Amazonlowercase BCP-47en, de, autolowercase BCP-47en, de
Lingvacase-insensitive BCP-47en, DE, autono code echoed

DeepL Normalisation — routers/deepl.py

DeepL clients send uppercase codes such as EN-US or DE. The router converts inbound codes to lowercase before passing them to the plugin, and converts detected/source codes back to uppercase in the response.

# deepl.py — make_deepl_router / translate handler
source = request.source_lang.lower() if request.source_lang else None
target = request.target_lang.lower()
# ...
detected_source = engine.detect.detect(item).upper()
  • source_lang (e.g. EN) → lowercased to en before calling engine.tx.translate()
  • target_lang (e.g. DE) → lowercased to de
  • detected_source_language in the response is always uppercased (e.g. EN)

Source: ovos_translate_server/routers/deepl.py


DeepLX Normalisation — routers/deeplx.py

DeepLX clients conventionally send uppercase codes (EN, DE) but the format is not enforced. The router lowercases both codes before calling the plugin and treats the source_lang: "auto" sentinel as automatic detection. The DeepLX response schema ({code, data}) carries no language code, so there is nothing to echo back.

# deeplx.py — translate handler
source = None if request.source_lang.lower() == "auto" else request.source_lang.lower()
target = request.target_lang.lower()
  • source_lang (e.g. EN, or auto) → lowercased to en, or None for auto-detect
  • target_lang (e.g. DE) → lowercased to de

Source: ovos_translate_server/routers/deeplx.py


Azure Normalisation — routers/azure_translator.py

Azure uses the to query parameter and an optional from parameter. Target codes are echoed back in the to field of each translation result. The router uppercases the to value in the response to match Azure's behaviour:

# azure_translator.py — translate handler
translations.append(AzureTranslation(text=translated or "", to=tgt.upper()))

Source: ovos_translate_server/routers/azure_translator.py


LibreTranslate — No Normalisation

LibreTranslate clients already use lowercase codes. The router passes them through to the plugin unchanged. The source="auto" sentinel is translated to source=None before calling the plugin:

source = None if request.source == "auto" else request.source

Source: ovos_translate_server/routers/libretranslate.py


Amazon — auto Sentinel

Amazon Translate uses SourceLanguageCode: "auto" to request automatic source language detection. The router translates this to source=None before calling the plugin, then performs detection to fill in the actual source code returned in the response:

source = None if request.SourceLanguageCode == "auto" else request.SourceLanguageCode

Source: ovos_translate_server/routers/amazon_translate.py


Lingva Normalisation — routers/lingva.py

Lingva passes the source and target languages as URL path parameters. The router lowercases both before calling the plugin and treats the source path segment auto as automatic detection. The Lingva response schema ({translation}) carries no language code, so there is nothing to echo back.

# lingva.py — translate handler
src = None if source.lower() == "auto" else source.lower()
translated = engine.tx.translate(query, target=target.lower(), source=src)
  • source path segment (e.g. en, or auto) → lowercased to en, or None for auto-detect
  • target path segment (e.g. de) → lowercased to de

Source: ovos_translate_server/routers/lingva.py


Plugin Expectation

The underlying OVOS LanguageTranslator.translate() method always receives lowercase BCP-47 codes for both target and source. Returning normalised output is the responsibility of each router.