Multilingual Search Tokenization#
RAGFlow's BM25 index uses language-aware tokenization to produce accurate stems and tokens for non-English knowledge bases. Without this, the default English Porter stemmer leaves non-English words un-stemmed or incorrectly reduced, degrading BM25 recall. The system threads a language parameter from the KB configuration through the entire chunking pipeline into the core tokenizer.
Architecture#
The tokenization stack has two layers:
-
rag/nlp/rag_tokenizer.py— A thin Python wrapper aroundinfinity.rag_tokenizer.RagTokenizer. When the Infinity document engine is active,tokenize()andfine_grained_tokenize()pass through to the Infinity SDK; otherwise they delegate to the parent class. Theset_language()method — which selects the appropriate Snowball stemmer — is provided by the Infinity SDK parent class, not defined in this file . -
rag/nlp/__init__.py— Higher-level functions that callset_language()before tokenizing and propagate thelanguagekeyword argument throughout:tokenize(d, txt, eng, language="English")— sets language on the tokenizer singleton, then populatescontent_ltks(coarse) andcontent_sm_ltks(fine-grained).tokenize_chunks(..., language="English")— iterates chunks and delegates totokenize().tokenize_table(..., language="English")— also adjusts row delimiters: Chinese and Japanese use";"instead of";".tokenize_chunks_with_images()anddoc_tokenize_chunks_with_images()mirror the same pattern.
All defaults are "English", preserving backward compatibility for existing KBs .
Language Parameter Threading#
rag/svr/task_executor.py is the entry point where the KB language field is picked up and propagated:
build_chunks()callsrag_tokenizer.tokenizer.set_language(task["language"])at line 422 before tokenizing chunks, and also passeslang=task_languageto the chunker .run_dataflow()callsset_language(task.get("language", "English"))at line 771.run_raptor_for_kb()callsset_language(row.get("language", "English"))at line 1042.do_handle_task()callsset_language(task_language)at line 1406.
All 14 parser modules under rag/app/ (audio, book, email, laws, manual, naive, one, paper, picture, presentation, qa, resume, table, tag) were updated to pass language=lang into the tokenization calls as part of PR #14140 .
Supported Languages#
The KB language selector (LanguageList in web/src/constants/common.ts) exposes 16 languages:
| Display Name | Native Name |
|---|---|
| English | English |
| Chinese | 简体中文 |
| Traditional Chinese | 繁體中文 |
| Russian | Русский |
| Indonesian | Bahasa Indonesia |
| Spanish | Español |
| Vietnamese | Tiếng việt |
| Japanese | 日本語 |
| Portuguese BR | Português BR |
| German | Deutsch |
| French | Français |
| Italian | Italiano |
| Bulgarian | Български |
| Arabic | العربية |
| Turkish | Türkçe |
| Dutch | Nederlands |
The NLTK Snowball stemmer (bundled with NLTK, no new dependency) is selected by set_language() for the 16 languages that have a Snowball mapping. Languages without a mapping (Chinese, Japanese, Korean, etc.) fall back to the Porter stemmer — this is acceptable because those languages use character-based tokenization anyway .
The cross-language search form (web/src/components/cross-language-form-field.tsx) exposes a curated subset of 11 languages: English, Chinese, Spanish, French, German, Japanese, Korean, Vietnamese, Arabic, Turkish, and Dutch.
RTL Text Support (Arabic, Hebrew, etc.)#
RTL language support has a nuanced history tracked in issue #4675. The core problem was in DeepDoc OCR: it did not correctly handle RTL text extraction from PDFs, causing Arabic/Hebrew chunks to appear in LTR order — breaking both chunk display and downstream BM25 retrieval .
- Workaround (still valid for OCR-heavy PDFs): Convert the PDF to
.docxfirst (e.g., via Microsoft Word), then upload the.docx. The DOCX parser preserves Arabic character order correctly . - Current status: Issue #4675 was closed as RTL support was added. If problems persist with the latest version, open a new issue with a sample file and logs.
Arabic is included in LanguageList and LanguageMap and the cross-language form field . The BM25 tokenizer path is configured for Arabic; the historical limitation was specific to OCR-based PDF extraction.
Key Source Files#
| File | Purpose |
|---|---|
rag/nlp/rag_tokenizer.py | Tokenizer wrapper; delegates set_language() to Infinity SDK |
rag/nlp/__init__.py | tokenize(), tokenize_chunks(), tokenize_table() — language parameter entrypoints |
rag/svr/task_executor.py | Reads KB language, calls set_language(), drives chunking pipeline |
web/src/constants/common.ts | LanguageList / LanguageMap — 16 supported languages in UI |
web/src/components/cross-language-form-field.tsx | Cross-language search language picker (11 languages) |
| PR #14140 | Introduced language-aware Snowball stemmer and Dutch support |