Metadata-Version: 2.4
Name: ovos-lang-parser
Version: 0.7.1a3
Summary: OpenVoiceOS's multilingual language-name parsing and formatting library
Author-email: JarbasAI <jarbasai@mailfence.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/OpenVoiceOS/ovos-lang-parser
Project-URL: Source, https://github.com/OpenVoiceOS/ovos-lang-parser
Project-URL: Issues, https://github.com/OpenVoiceOS/ovos-lang-parser/issues
Keywords: languages,parsing,nlp,multilingual,ovos
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: ovos-utils<1.0.0,>=0.0.38
Requires-Dist: ovos-spec-tools>=1.5.0a1
Requires-Dist: langcodes
Dynamic: license-file

# ovos-lang-parser

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](#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

```bash
pip install ovos-lang-parser
# or
uv add ovos-lang-parser
```

Runtime dependencies are small: [`langcodes`](https://github.com/rspeer/langcodes) for tag
normalization and `ovos-utils` for the fuzzy matcher. No models, no network calls — the
wordlists are bundled.

## 30-second quickstart

```python
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 `0.0`–`1.0`; 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](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/`](examples/) — `pip install ovos-lang-parser` and run it, no OVOS
stack required.

### Entity extraction / NER — pull a language out of free text

You have a sentence and want to know which language it mentions.

```python
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`](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.

```python
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`](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.

```python
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`](examples/normalization.py).

### In an OVOS skill vs. standalone

The API is identical; only where you get `text` and `lang` differs.

```python
# 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 is a matter of dropping
in one JSON file — see [docs/coverage.md](docs/coverage.md) and
[docs/extending.md](docs/extending.md).

## Documentation

- [docs/api.md](docs/api.md) — full reference, confidence model, edge behavior
- [docs/coverage.md](docs/coverage.md) — supported languages and the data model
- [docs/extending.md](docs/extending.md) — add a language wordlist
- [examples/](examples/) — runnable scripts for each use case

## Related projects

- [ovos-number-parser](https://github.com/OpenVoiceOS/ovos-number-parser) — numbers
- [ovos-date-parser](https://github.com/OpenVoiceOS/ovos-date-parser) — dates and times
- [ovos-color-parser](https://github.com/OVOSHatchery/ovos-color-parser) — colors

## License

Apache 2.0 — see [LICENSE](LICENSE).
