Post-render HTML and frontend UI dictionary translation for Python projects with OpenAI-backed caching
Project description
auto-i18n-lib
Lightweight file-based translation library for Python applications.
auto-i18n-lib translates:
- rendered HTML
- frontend UI dictionaries
- backend phrases by stable keys
The library works in two modes:
- render mode — only reads ready local files
- background mode — finds missing translations and fills caches
This allows pages, UI, PDFs, and backend bot/system messages to work without synchronous translation requests during runtime.
Features
- post-render HTML translation
- frontend UI dictionary translation
- backend phrase translation by keys
- separate backend dictionaries for:
botsystem
- separate pending queues for backend dictionaries
- per-language JSON file caches
- no synchronous translation calls during render
- fallback to source text when translation is missing
- background translation worker
- nested
dict/listsupport for UI dictionaries - language normalization
- fallback language chain
- legacy page cache compatibility
Installation
pip install auto-i18n-lib
Requirements
- Python 3.9+
- OpenAI API key
Environment
You can configure the library through constructor arguments or environment variables.
Linux / macOS
export OPENAI_API_KEY="your_key"
export SOURCE_LANG="ru"
export AUTO_I18N_TARGET_LANGS="en,az,tr"
export AUTO_I18N_JS_SCAN_ENABLED="true"
export AUTO_I18N_JS_GLOBS='["web/static/js/**/*.js"]'
export AUTO_I18N_UI_NAMESPACE="ui"
export AUTO_I18N_DYNAMIC_DOM_ENABLED="true"
export AUTO_I18N_FALLBACK_LANG="ru"
Windows PowerShell
$env:OPENAI_API_KEY="your_key"
$env:SOURCE_LANG="ru"
$env:AUTO_I18N_TARGET_LANGS="en,az,tr"
$env:AUTO_I18N_JS_SCAN_ENABLED="true"
$env:AUTO_I18N_JS_GLOBS='["web/static/js/**/*.js"]'
$env:AUTO_I18N_UI_NAMESPACE="ui"
$env:AUTO_I18N_DYNAMIC_DOM_ENABLED="true"
$env:AUTO_I18N_FALLBACK_LANG="ru"
Core idea
Render mode
During normal application work the library:
- reads translations only from ready JSON files
- does not call OpenAI during render
- returns original text if translation is missing
This keeps rendering fast and stable.
Background mode
In the background the library:
- scans registered pages
- finds missing HTML strings
- finds missing UI dictionary strings
- finds missing backend key phrases
- puts missing items into pending files
- translates them in batches
- writes ready translations into local JSON files
Quick start
from autoi18n import Translator
translator = Translator(
api_key="YOUR_OPENAI_API_KEY",
cache_dir="./translations",
source_lang="ru",
target_langs=["en", "az", "tr"],
)
HTML translation
Use translate_html() for already rendered HTML.
html = """
<h1>Главная</h1>
<p>Добро пожаловать в систему</p>
<button>Сохранить</button>
"""
translated_html = translator.translate_html(
html=html,
target_lang="en",
page_name="home",
)
print(translated_html)
Important
If translation is missing:
- original text is returned immediately
- missing text is added to pending queue
- background worker translates it later
Frontend UI dictionary translation
Use translate_dict() for JS tables, forms, modals, filters, buttons, and other frontend dictionaries.
ui = {
"title": "Грузы",
"filters": {
"search": "Поиск",
"reset": "Сбросить"
},
"modal": {
"save": "Сохранить",
"cancel": "Отмена"
}
}
translated_ui = translator.translate_dict(
page_name="cargo",
dict_name="table",
source_dict=ui,
target_lang="en",
)
You can restrict translation to specific keys:
translated_ui = translator.translate_dict(
page_name="cargo",
dict_name="table",
source_dict=ui,
target_lang="en",
filter_keys=["title", "search", "save", "cancel"],
)
Backend phrase translation by keys
This block is предназначен for bot messages, system messages, service replies, validation texts, and other backend phrases that must not be hardcoded by language in Python code.
Main idea
You register source phrases once by keys:
translator.register_keys(
{
"bot.welcome": "Добро пожаловать в систему",
"bot.ask_email": "Введите email администратора",
},
dict_name="bot",
)
Then you read translations locally by key:
text = translator.translate_key(
key="bot.welcome",
lang="en",
default="Добро пожаловать в систему",
dict_name="bot",
)
If translation is missing:
defaultis returned immediately- the key remains registered
- missing translation is added to backend pending file
- worker translates it later
Supported backend dictionaries
botsystem
These dictionaries are stored separately and do not mix with HTML or UI translation storage.
register_keys()
Registers source backend phrases by stable keys.
Example
translator.register_keys(
{
"bot.welcome": "Добро пожаловать в систему",
"bot.ask_email": "Введите email администратора",
"bot.done": "Готово",
},
dict_name="bot",
)
For system phrases
translator.register_keys(
{
"system.user_not_found": "Пользователь не найден",
"system.access_denied": "У вас нет доступа",
"system.validation_error": "Ошибка проверки данных",
},
dict_name="system",
)
Behavior
- source phrases are saved into source-language backend dictionary
- missing target-language translations are added into backend pending file
- already translated keys are not re-added
- storage is separate for
botandsystem
translate_key()
Returns local backend translation by key.
Example
text = translator.translate_key(
key="system.access_denied",
lang="az",
default="У вас нет доступа",
dict_name="system",
)
Behavior
- reads translation only from local backend files
- checks fallback language chain
- if translation is missing, returns
default - also ensures the key is registered and queued for background translation
Django example
View
import json
from django.shortcuts import render
from autoi18n import Translator
translator = Translator(
api_key="YOUR_OPENAI_API_KEY",
cache_dir="./translations",
source_lang="ru",
target_langs=["en", "az", "tr"],
)
translator.register_keys(
{
"bot.welcome": "Добро пожаловать в систему",
"system.access_denied": "У вас нет доступа",
},
dict_name="bot",
)
translator.register_keys(
{
"system.access_denied": "У вас нет доступа",
},
dict_name="system",
)
def cargo_page(request):
target_lang = translator.detect_browser_lang(
request.headers.get("Accept-Language", "")
)
html = """
<h1>Грузы</h1>
<button>Сохранить</button>
"""
translated_html = translator.translate_html(
html=html,
target_lang=target_lang,
page_name="cargo_page",
)
ui = {
"title": "Грузы",
"search": "Поиск",
"empty": "Нет данных",
"save": "Сохранить",
"cancel": "Отмена",
}
translated_ui = translator.translate_dict(
page_name="cargo_page",
dict_name="table_ui",
source_dict=ui,
target_lang=target_lang,
)
bot_welcome = translator.translate_key(
key="bot.welcome",
lang=target_lang,
default="Добро пожаловать в систему",
dict_name="bot",
)
return render(request, "cargo/page.html", {
"translated_html": translated_html,
"ui_json": json.dumps(translated_ui, ensure_ascii=False),
"bot_welcome": bot_welcome,
})
Template
<div>
{{ translated_html|safe }}
</div>
<div>
{{ bot_welcome }}
</div>
<script>
window.CARGO_I18N = {{ ui_json|safe }};
</script>
Background page registration
Register pages for automatic background HTML scanning.
def render_dashboard():
return """
<h1>Панель управления</h1>
<button>Продолжить</button>
"""
translator.register_page(
page_name="dashboard",
html_getter=render_dashboard,
target_langs=["en", "az", "tr"],
)
Background processing
Process all translations once
report = translator.process_all_translations(batch_size=50)
print(report)
This processes:
- registered HTML pages
- frontend UI pending queue
- backend key pending queues for
bot - backend key pending queues for
system
Run continuous background loop
translator.run_translation_loop(interval=300, batch_size=50)
Run in a separate thread
import threading
threading.Thread(
target=translator.run_translation_loop,
kwargs={"interval": 300, "batch_size": 50},
daemon=True,
).start()
Public API
Translator(...)
Translator(
cache_dir="./translations",
api_key=None,
source_lang=None,
model="gpt-4o-mini",
target_langs=None,
)
Parameters
cache_dir— root directory for translation filesapi_key— OpenAI API keysource_lang— source language codemodel— OpenAI model nametarget_langs— list of target languages for background translation
If target_langs is not passed, the library tries to read them from:
AUTO_I18N_TARGET_LANGS
HTML / UI API
register_page(page_name, html_getter, target_langs, context=None)
Registers a page for background scanning.
scan_page(page_name, target_lang)
Scans one registered page and returns missing translatable HTML items.
scan_all_pages()
Scans all registered pages and all configured target languages.
process_page_translations(page_name, target_lang, batch_size=50)
Processes missing HTML translations for one page and one language.
process_all_translations(batch_size=50)
Processes all registered HTML pages and backend pending queues.
When AUTO_I18N_JS_SCAN_ENABLED=true, this method also scans JS files and adds _js_keys statistics into the report.
extract_js_keys(js_globs=None, namespace=None)
Extracts stable UI translation keys from JS sources and enqueues missing target-language translations.
Supports:
- explicit calls:
t("ui.key", "Текст") - explicit calls:
translateKey("ui.key", "Текст") - dictionary-like entries:
field: "Заголовок"
run_translation_loop(interval=300, target_lang=None, page_name="page", stop_event=None, batch_size=50)
Runs continuous background translation loop.
translate_text(text, target_lang, page_name="page", prompt_type="normal")
Returns translated plain text from cache if available. If translation is missing, returns original text and adds it to pending queue.
translate_html(html, target_lang, page_name="page")
Translates rendered HTML using local cache. Missing strings remain unchanged until background processing fills them.
translate_dict(page_name, dict_name, source_dict, target_lang=None, filter_keys=None)
Translates frontend UI dictionaries.
Rules:
- supports nested
dict - supports nested
list - stores keys as
ui::page_name::dict_name::path - returns original values if translation is missing
- uses shared HTML/UI cache and pending queue
get_frontend_translations(lang, namespace=None)
Returns frontend translation dictionary for runtime usage.
build_frontend_runtime(lang, namespace=None, dynamic_dom_enabled=None)
Returns ready-to-inline JavaScript runtime with:
window.autoI18n.translateKey(key, fallback)window.autoI18n.translateDom(root?)- optional
MutationObserversupport when dynamic DOM mode is enabled
Backend key API
register_keys(items, dict_name="bot", target_langs=None)
Registers backend phrases by stable keys.
Parameters:
items— dict like{key: source_text}dict_name—"bot"or"system"target_langs— optional override for target languages
translate_key(key, lang, default, dict_name="bot")
Returns backend phrase translation from local files.
Parameters:
key— stable backend phrase keylang— target languagedefault— source/fallback textdict_name—"bot"or"system"
enqueue_backend_missing_keys(items, target_lang, dict_name="bot")
Adds backend keys to backend pending file manually.
get_backend_pending_entries(dict_name="bot", target_lang=None)
Returns backend pending items.
process_backend_key_translations(dict_name="bot", target_lang=None, batch_size=50)
Processes backend pending queue for one backend dictionary.
process_all_backend_key_translations(batch_size=50)
Processes both backend dictionaries:
botsystem
get_backend_translation_coverage(lang, dict_name="bot")
Returns translation coverage for backend dictionary.
Example response:
{
"dict_name": "bot",
"lang": "en",
"translated": 120,
"pending": 8,
"total": 128,
"percent": 93.75
}
General helper API
enqueue_missing_texts(items, target_lang, page_name="page")
Adds missing HTML/UI items to shared pending queue.
get_pending_entries(target_lang=None)
Returns current shared HTML/UI pending items.
process_pending_translations(target_lang=None, page_name="page", batch_size=50)
Processes shared HTML/UI pending queue.
get_translation_coverage(lang)
Returns translation statistics for shared HTML/UI storage.
detect_browser_lang(accept_language)
Extracts and normalizes browser language from Accept-Language.
get_alternative_lang(current_lang, browser_lang)
Returns helper language for language switch logic.
Language normalization
The library normalizes language codes before using them in cache files.
Examples:
EN->enen_US->en-usen-US->en-us
Fallback chain
When translation is missing, the library checks fallback languages.
Example:
en-us -> en -> source_lang
For HTML / UI
If nothing is found:
- original text is returned
For backend keys
If nothing is found:
defaultis returned
Cache structure
Translations are stored as JSON files inside the cache directory.
translations/
en.json
az.json
tr.json
_pending.json
backend/
bot.ru.json
bot.en.json
bot.az.json
bot.tr.json
system.ru.json
system.en.json
system.az.json
system.tr.json
_pending_bot.json
_pending_system.json
Shared HTML / UI cache format
{
"Сохранить": "Save",
"ui::cargo::table::title": "Cargo",
"ui::cargo::table::filters.search": "Search"
}
Backend cache format
backend/bot.ru.json
{
"bk::bot.welcome": "Добро пожаловать в систему",
"bk::bot.ask_email": "Введите email администратора"
}
backend/bot.en.json
{
"bk::bot.welcome": "Welcome to the system",
"bk::bot.ask_email": "Enter administrator email"
}
backend/system.en.json
{
"bk::system.access_denied": "Access denied",
"bk::system.user_not_found": "User not found"
}
What is translated
The library translates:
HTML
-
visible HTML text nodes
-
button labels
-
selected HTML attributes:
placeholdertitlealtaria-labelvaluefor button-like inputs
Frontend UI
- frontend dictionaries
- nested dict/list structures
- table labels
- modal texts
- form labels
- buttons
- filters
- empty-state texts
Backend keys
- bot replies
- system replies
- validation texts
- service messages
- backend-generated phrases by stable keys
What is skipped
The parser skips:
scriptstylenoscript
It also skips:
- elements with
translate="no" - elements with
data-translate="no" - element with
id="langSwitch" - pure numbers
- number-like strings
- hashes / UUID-like strings
- many technical values
PDF usage
Do not translate a generated PDF file directly.
Correct flow:
render HTML -> translate_html -> generate PDF
If PDF contains backend phrases inserted by Python code:
translate_key / translate_dict / translate_html -> build final translated HTML -> generate PDF
Recommended integration pattern
Use the library in this order:
- render HTML as usual
- translate rendered HTML through
translate_html() - pass JS/UI dictionaries through
translate_dict() - register backend phrases through
register_keys() - read backend phrase translations through
translate_key() - run background worker to complete missing translations
- generate PDF only from already translated HTML
Recommended bot integration pattern
For bots and backend services:
- define stable backend keys
- register source phrases once during startup
- request translated text only through
translate_key() - never hardcode per-language backend phrase dictionaries in code
- let background worker fill missing target translations
Example
translator.register_keys(
{
"bot.greeting": "Привет! Чем помочь?",
"bot.ask_phone": "Введите номер телефона",
"system.error_generic": "Произошла ошибка",
},
dict_name="bot",
)
translator.register_keys(
{
"system.error_generic": "Произошла ошибка",
"system.not_allowed": "Действие недоступно",
},
dict_name="system",
)
reply = translator.translate_key(
key="bot.greeting",
lang=user_lang,
default="Привет! Чем помочь?",
dict_name="bot",
)
Limitations
- the library works on rendered HTML, not templates
- translation quality depends on source text quality
- highly unstable HTML reduces cache reuse efficiency
- JS string extraction is manual: developer passes UI dictionaries explicitly
- backend phrase keys must be registered with stable source texts
- target languages must be configured explicitly if background translation should cover them automatically
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file auto_i18n_lib-0.5.1.tar.gz.
File metadata
- Download URL: auto_i18n_lib-0.5.1.tar.gz
- Upload date:
- Size: 21.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f4090dfa897c8f965bd3d364cb5b2d5b74f3ce742ac567dc03e8f07f88e0754
|
|
| MD5 |
7f4c49e821261eef5dbf1e0be2a3fdb0
|
|
| BLAKE2b-256 |
54be332a407e5a4ffe6260f40482745e557ff0c98974ae2db24ab839ef39197b
|
File details
Details for the file auto_i18n_lib-0.5.1-py3-none-any.whl.
File metadata
- Download URL: auto_i18n_lib-0.5.1-py3-none-any.whl
- Upload date:
- Size: 22.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
081f8b97e71d409f8de8a8ce0867889bcff2dbe15a0be108e6affa9a0fe968b9
|
|
| MD5 |
b83ecbf6f073aa6a4a46ed815451e1db
|
|
| BLAKE2b-256 |
97ab1394e0dfbf1fc8c8c0133e458d959eded2ff7f6d6a68fe650b9c2cb56a75
|