Proposal: Standardize OCR Language Codes Using BCP-47 #3915
nikos-livathinos
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Updated after feedback from @Ahmetcanyvz
Docling supports multiple OCR engines (RapidOCR, nemotron-ocr, EasyOCR, Tesseract, ocrmac), each using a different notation for the same languages. Users must learn which notation applies to each engine/backend combination, code cannot be written portably across engines, and it silently breaks when engine or model versions change.
Current Issues
1. The same language, five notations. Simplified Chinese:
chi_simchiplus a suffixch_simchorchinesezh-Hanslanguage-REGIONmultilingualAny standard we adopt has to accommodate both ends of that range. Docling's current normalization layer is two entries (
rapid_ocr_model.py):2. Notation varies within an engine, by backend and model version. RapidOCR alone:
ch,en,de,fr,japan, …arabic,ch,cyrillic,devanagari,el,en,eslav,korean,latin,ta,te,tharabic,cyrillic,devanagari,ka,korean,latin,ta,te3. Script-based vs. language-based systems. PP-OCRv5's
cyrilliccovers 34 languages andlatincovers many more, while PP-OCRv6 has per-language codes (de,fr). The user must know which kind of code an engine expects.4. Codes change with model versions. v4→v5 added
el,eslav,th; v5→v6 went from ~12 to ~52 codes and introduced aliases (zh→ch,ja→japan,ko→korean). Users track this across dependencies.5. Language alone does not identify an OCR model. OCR consumes a language rendered in a writing system, and the two vary independently: Serbian is written in both Cyrillic and Latin; Chinese Simplified vs. Traditional is not a language distinction at all, so no ISO 639 code expresses it; Punjabi is Gurmukhi in India and Shahmukhi in Pakistan (likewise Uzbek, Kazakh, Kurdish). Plain ISO 639 cannot say "Serbian, Cyrillic", so it is not sufficient on its own.
Proposed Solution
Decouple Docling's language notation from engine-specific implementations by adopting BCP-47 as the standard.
BCP-47 (RFC 5646) composes the ISO registries into one tag —
language[-script][-region], e.g.en,zh-Hans,sr-Cyrl,yue. The primary subtag is ISO 639-1 where a two-letter code exists and ISO 639-3 otherwise, giving full 639-3 coverage while avoiding ISO 639-2's bibliographic/terminological duplicates (ger/deu,chi/zho).1. User-facing standardization. The CLI and API accept BCP-47 tags. Script variants use the script subtag (
zh-Hans,sr-Latn), which states what is meant; region tags (zh-CN,zh-TW) are also accepted as lossy-but-valid, since the region implies the script (zh-SG→zh-Hanstoo) — so ocrmac's existing["en-US", "fr-FR"]style keeps working unchanged.2. Internal mapping layer. Per-engine adapters translate to native notation, versioned per model release. Incoming tags are canonicalized (
deu,geranddeare all registry-valid, so only canonicalization collapses them) and keyed on(language, script)rather than the tag string — the region is dropped once it has inferred the script:BCP-47 also reserves three non-language subtags we can adopt directly:
und(undetermined) for script auto-detection, replacing today's"auto"sentinel;mul(multiple languages) for an engine's broadest multilingual model, which is what nemotron-ocr's"multilingual"already means; andzxx(no linguistic content) to skip OCR.3. Script handling. Engines that expose script families rather than languages (RapidOCR PP-OCRv5's
cyrillic, Tesseract'sscript/Cyrillic) need no separate vocabulary from the user: the script is derived from the tag, so"ru"resolves to the Cyrillic model on its own. Macrolanguages fall out the same way —cmnandyueresolve underzh, sozhmeans "any Chinese language in this script", which is the right granularity for OCR.4. Implementation.
langcodesprovides parsing, canonicalization, likely-subtags and RFC 4647 matching in 1.0 MB with zero dependencies (the 16 MBlangcodes[data]extra is only for human-readable names, which we don't need). Legacy engine-native codes (ch_sim,chi_sim,chinese) keep working with aDeprecationWarningthrough a transition period.Example Usage
Only the options class changes. The language tag no longer does.
Question for Discussion
Multi-language behavior: should we standardize how engines handle multi-language requests — warn when an engine uses only the first language, error when it has no multi-language support, or auto-select the best engine for the combination?
BCP-47 gives us
mulfor "explicitly multilingual" but does not decide the policy, and engines diverge sharply: ocrmac takes an ordered preference list, Tesseract joins with+, nemotron-ocr collapses everything into one"multilingual"model. This is the main open question — community input most valuable here.Looking forward to community feedback on this proposal! 🚀
References
langcodes(Python implementation)All reactions