ovos-lang-parser
July 31, 2026 · View on GitHub
Map spoken and written language names to standard IETF/BCP-47 language codes, and back, in many languages, offline, with a two-function API.
"Brazilian Portuguese" -> "pt-br"
"alemão" (Portuguese) -> "de"
"pt-br" -> "Português do Brasil" (rendered in Portuguese)
"pt-br" -> "Brazilian Portuguese" (rendered in English)
The library understands language names written in 21 languages (see
Coverage). A user can say "French" in English, "français" in French, or
"Französisch" in German, and each resolves to the code fr. This is the piece you need
whenever a human names a language in free text and your code needs a canonical code to act
on: routing to a translation/TTS/STT engine, tagging an entity, or normalizing a messy
label.
It ships as part of OpenVoiceOS, but has no OVOS runtime dependency and is useful in any plain-Python project.
Install
pip install ovos-lang-parser
# or
uv add ovos-lang-parser
Runtime dependencies are small: langcodes for tag
normalization and ovos-utils for the fuzzy matcher. There are no models and no network
calls: the wordlists are bundled.
30-second quickstart
from ovos_lang_parser import extract_langcode, pronounce_lang
# name -> code (second arg is the language the text is written in)
print(extract_langcode("Brazilian Portuguese", "en")) # -> ('pt-br', 1.0)
print(extract_langcode("translate this to German", "en")) # -> ('de', 1.0)
# code -> name, rendered in a chosen language
print(pronounce_lang("de", "en")) # -> German
print(pronounce_lang("de", "pt")) # -> Alemão
That is the whole surface for most callers: extract_langcode reads a name out of text and
returns (code, confidence). pronounce_lang turns a code back into a human name.
The API in one screen
| Function | Purpose | Returns |
|---|---|---|
extract_langcode(text, lang) | Find the language named in text (written in lang) | (langcode, confidence). Confidence is 0.0 to 1.0, and an exact name match is 1.0 |
pronounce_lang(langcode, lang) | Human name of langcode, rendered in lang | str (falls back to the base tag, then to langcode unchanged) |
get_lang_data(lang) | The full {name: code} table for lang | dict[str, str] |
LANGS | Codes of the languages a name can be written in | list[str] |
In both functions lang is the language the names are written in, not the language being
named. extract_langcode("français", "fr") and extract_langcode("French", "en") both give
fr. See docs/api.md for full signatures, the confidence model, and edge
behavior.
Use it outside OVOS
The same two functions cover a range of standalone jobs. Each example below is a runnable
script under examples/: run pip install ovos-lang-parser, then run the script.
No OVOS stack is required.
Entity extraction / NER: pull a language out of free text
You have a sentence and want to know which language it mentions.
from ovos_lang_parser import extract_langcode
for text in ["translate this to Brazilian Portuguese",
"can you say it in Mandarin Chinese?",
"I'd like the subtitles in Greek"]:
code, conf = extract_langcode(text, "en")
if conf >= 0.7:
print(f"{text!r} -> {code} ({conf:.2f})")
Because matching is fuzzy, apply a confidence threshold to decide whether a language was
really mentioned. Full script:
examples/ner_language_mentions.py.
Routing: pick a translation / TTS / STT engine by name
A user names a target language. You resolve it to a code and hand that to whatever engine your pipeline drives.
from ovos_lang_parser import extract_langcode
def resolve_target(user_request, spoken_in="en"):
code, conf = extract_langcode(user_request, spoken_in)
return code if conf >= 0.7 else None
print(resolve_target("read it back to me in German")) # -> de (feed to your TTS)
Full script (with a mock engine table): examples/routing.py.
Normalization: canonicalize messy names and autonyms to one code
Aliases, autonyms, and localized spellings all collapse to a single canonical code, so you can deduplicate and standardize labels regardless of how they were written.
from ovos_lang_parser import extract_langcode
labels = [("Deutsch", "de"), ("alemão", "pt"), ("German", "en"), ("allemand", "fr")]
for name, written_in in labels:
code, _ = extract_langcode(name, written_in)
print(f"{name:>10} -> {code}") # all -> de
Full script: examples/normalization.py.
In an OVOS skill vs. standalone
The API is identical. Only where you get text and lang differs.
# Standalone language-routing utility
code, conf = extract_langcode(user_input, "en")
# Inside an OVOS skill, the utterance and its language come from the session
class MySkill(OVOSSkill):
def handle_translate(self, message):
utterance = message.data["utterance"]
code, conf = extract_langcode(utterance, self.lang)
...
Coverage
Names can be written in 21 languages. Each carries a table of a few hundred target languages keyed by ISO 639 code.
an Aragonese | ar Arabic | ast Asturian | bg Bulgarian |
ca Catalan | da Danish | de German | en English |
es Spanish | eu Basque | fr French | fy Frisian |
gl Galician | hr Croatian | it Italian | kab Kabyle |
nl Dutch | oc Occitan | pt Portuguese | ro Romanian |
sk Slovak |
The live list is always ovos_lang_parser.LANGS. Adding a language means dropping in one
JSON file. See docs/coverage.md and docs/extending.md.
Documentation
- docs/api.md: full reference, confidence model, edge behavior
- docs/coverage.md: supported languages and the data model
- docs/extending.md: add a language wordlist
- examples/: runnable scripts for each use case
Related projects
- ovos-number-parser: numbers
- ovos-date-parser: dates and times
- ovos-color-parser: colors
License
Apache 2.0. See LICENSE.