mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-22 18:57:14 +00:00
124 lines
3.6 KiB
Markdown
124 lines
3.6 KiB
Markdown
# langid
|
|||
|
|
|
||
|
|
What language a document is in.
|
||
|
|
|
||
|
|
Part of [catena](../../README.md).
|
||
|
|
|
||
|
|
## What it does
|
||
|
|
|
||
|
|
Two passes, because one will not do. The writing system settles most of the
|
||
|
|
world outright — a text in Hangul is Korean — and for the Latin and Cyrillic
|
||
|
|
alphabets it only narrows the field, so the commonest words decide between
|
||
|
|
what is left.
|
||
|
|
|
||
|
|
No model, no download, no network. Function words make up a third of any
|
||
|
|
running text, and counting them is decisive on a paragraph. On a single line
|
||
|
|
it is not, which is why a close call is refused rather than guessed at.
|
||
|
|
|
||
|
|
## When you would use it
|
||
|
|
|
||
|
|
- **Before OCR**, to know which language to hand it.
|
||
|
|
- **Before embedding**, where a multilingual shelf wants different models.
|
||
|
|
- **Filling the `language` field** on a `doc` record, which nothing else sets.
|
||
|
|
- **Finding the odd book out** in a library you assumed was all one language.
|
||
|
|
|
||
|
|
## Install
|
||
|
|
|
||
|
|
```sh
|
||
|
|
cargo install --git https://git.pumpin.online/funkpower/catena langid
|
||
|
|
```
|
||
|
|
|
||
|
|
## Example
|
||
|
|
|
||
|
|
```
|
||
|
|
$ langid converted/*.md | jq -r '[.name, (.confidence*100|round|tostring + "%"), .path] | @tsv'
|
||
|
|
English 77% converted/enlightenment.md
|
||
|
|
English 76% converted/hagakure.md
|
||
|
|
French 81% converted/meditations.md
|
||
|
|
Latin 68% converted/summa.md
|
||
|
|
```
|
||
|
|
|
||
|
|
Just the code:
|
||
|
|
|
||
|
|
```
|
||
|
|
$ langid --quiet book.md
|
||
|
|
en
|
||
|
|
```
|
||
|
|
|
||
|
|
The whole record, which carries the bibliographic code the `doc` record
|
||
|
|
wants:
|
||
|
|
|
||
|
|
```
|
||
|
|
$ langid book.md
|
||
|
|
{"v":1,"path":"book.md","language":"en","bibliographic":"eng","name":"English","script":"Latin","confidence":0.77}
|
||
|
|
```
|
||
|
|
|
||
|
|
The ones that are not what you assumed:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
langid converted/*.md | jq -r 'select(.language != "en") | [.path, .name] | @tsv'
|
||
|
|
```
|
||
|
|
|
||
|
|
Reading from a pipe:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
pdftotext -l 5 scan.pdf - | langid --quiet
|
||
|
|
```
|
||
|
|
|
||
|
|
## Options
|
||
|
|
|
||
|
|
| Option | Effect |
|
||
|
|
|---|---|
|
||
|
|
| `--quiet` | Print the code alone, one per line |
|
||
|
|
| `--man` | Write this page as a manual page, in roff |
|
||
|
|
|
||
|
|
## Exit codes
|
||
|
|
|
||
|
|
| Code | Meaning |
|
||
|
|
|---|---|
|
||
|
|
| `0` | Every file was placed |
|
||
|
|
| `1` | At least one could not be |
|
||
|
|
| `64` | The command line was wrong |
|
||
|
|
| `74` | A file could not be read |
|
||
|
|
|
||
|
|
## How it decides
|
||
|
|
|
||
|
|
**The script first.** Kana, Hangul, Hebrew, Arabic and Devanagari each belong
|
||
|
|
to one language as far as a library is concerned, and no amount of word
|
||
|
|
counting improves on that. Kana is checked before Han, because Japanese is
|
||
|
|
mostly Han characters with kana between them and it is the kana that tell it
|
||
|
|
from Chinese.
|
||
|
|
|
||
|
|
A quarter of a text's letters agreeing is enough to call it a script: a book
|
||
|
|
carries page numbers, names and quotations in other alphabets, and an English
|
||
|
|
word in a Russian paragraph does not make the paragraph English.
|
||
|
|
|
||
|
|
**Then the words.** Thirteen languages across Latin, Cyrillic and Greek, each
|
||
|
|
with its thirty commonest function words. The score is the share of a text's
|
||
|
|
words found in each list, and the lists are the same length so the scores
|
||
|
|
compare.
|
||
|
|
|
||
|
|
**A close call is no answer.** The winner must be a quarter ahead of the
|
||
|
|
runner-up. `de` is among the commonest words of four languages at once, and on
|
||
|
|
its own settles none of them — so a text that short comes back with the script
|
||
|
|
named, no language, and a note saying which two it could not choose between.
|
||
|
|
|
||
|
|
Only the first twenty thousand characters are read. A book's language does not
|
||
|
|
change after the first few pages.
|
||
|
|
|
||
|
|
## As a library
|
||
|
|
|
||
|
|
```rust
|
||
|
|
let found = langid::detect("book.md", text);
|
||
|
|
assert_eq!(found.language.as_deref(), Some("en"));
|
||
|
|
```
|
||
|
|
|
||
|
|
`langid::script::detect` answers the writing system on its own, and
|
||
|
|
`langid::words::score` ranks every language of a given script.
|
||
|
|
|
||
|
|
## See also
|
||
|
|
|
||
|
|
- [`booksniff`](../booksniff/README.md) — what the file is.
|
||
|
|
- [`bookmeta`](../bookmeta/README.md) — what the file says its language is,
|
||
|
|
which is worth more when it says anything.
|