Metadata-Version: 2.4
Name: auto-i18n-lib
Version: 2.0.8
Summary: Post-render HTML and frontend UI dictionary translation for Python projects with OpenAI-backed caching
Author-email: Andrey Bondarenko <bona.plus2030@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://bona-plus.ru
Project-URL: Source, https://github.com/Aalam2000/autoi18n
Project-URL: Issues, https://github.com/Aalam2000/autoi18n/issues
Keywords: i18n,l10n,translation,html,fastapi,flask,jinja2,openai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: openai>=1.0.0
Requires-Dist: python-dotenv>=1.0.0
Dynamic: license-file

# auto-i18n-lib

**Автоматический перевод интерфейса проекта без единой правки кода самого проекта.**

Библиотека сама находит тексты в исходниках (HTML-шаблоны и JS/JSX/TSX),
переводит их через ИИ и на лету подменяет их в уже отрисованной странице.
Чтобы подключить её к проекту, код проекта дорабатывать не нужно — ни
`t('key')`, ни `data-i18n`, ни любые другие вызовы в компонентах не
требуются.

---

## Требования

- Python >= 3.9
- **Node.js (обязательно)** — используется для разбора JS/JSX/TSX через
  настоящий AST-парсер (`@babel/parser` + `@babel/traverse`), а не через
  регулярные выражения. Без Node.js извлечение строк из JS/JSX-файлов
  работать не будет. Node нужен только на этапе сканирования проекта
  (`autoi18n scan` / воркер), в рантайме отдачи страниц он не требуется.
- Перед первым использованием — установить зависимости JS-моста:
  ```
  cd <папка_библиотеки>/src/autoi18n/extractor/js_bridge
  npm install
  ```

---

## Архитектура в двух словах

- **Ключ хранения — это хэш текста на исходном языке**, а не
  семантический ID, который придумывает разработчик. Совпадающий текст в
  разных местах проекта — это одна и та же фраза.
- **Файл исходного языка (`translations/<source_lang>.json`) — сам по
  себе реестр всех известных фраз проекта.** Отдельного файла-словаря
  ключей нет.
- **Сканирование (`extract`) сравнивает найденные в файлах фразы только с
  этим реестром.** Новые фразы добавляются в реестр и ставятся в очередь
  на перевод на все активные целевые языки. Уже известные фразы повторно
  никуда не ставятся.
- Библиотека сканирует **только файлы проекта** (HTML-шаблоны,
  JS/JSX/TSX). Контент, который заполняется в момент рендера — данные из
  БД, ответы пользователя, вопросы/ответы квиза и т.п. — она никогда не
  видит и не трогает.
- **Исходный язык и целевые языки — из `.env`**, это единственный
  источник истины (`SOURCE_LANG`, `AUTO_I18N_TARGET_LANGS`). Новый
  целевой язык можно добавить в любой момент — `add_target_lang()`
  (например, из админки проекта) сразу дописывает `.env` (переживает
  рестарт) и переводит на него весь текущий реестр, не дожидаясь
  следующего цикла воркера.
- **Обязательный отладочный этап до подключения ИИ**: `autoi18n scan
  --dry-run` показывает, что именно нашла библиотека — без записи
  чего-либо и без обращения к ИИ (ключ API для этой команды не нужен).
  Проверяете список руками на мусор/пропуски и только после этого
  запускаете реальное сканирование и перевод.
- **Клиентский рантайм ничего не требует от кода компонентов.** После
  отрисовки страницы он обходит уже готовый DOM (`TreeWalker`) и
  подменяет видимый текст на перевод; изменения DOM после первой отрисовки
  (React-перерисовка, обновление счётчика) подхватываются через
  `MutationObserver`. Параметризованные фразы («Вопрос {{0}} / {{1}}»)
  сопоставляются с живым текстом на странице по маске, скомпилированной
  из перевода с плейсхолдерами.

---

## Быстрый старт

```python
from autoi18n import Translator

t = Translator(env_path=".env")
```

`.env` проекта:
```
SOURCE_LANG=ru
AUTO_I18N_TARGET_LANGS=en,az
OPENAI_API_KEY=sk-...
```

### 1. Отладочный этап (обязательно перед подключением ИИ)

```bash
autoi18n scan --dry-run
```

Выводит список найденных фраз (файл, строка, текст, число параметров) —
ничего не пишет на диск. Проверьте на мусор и пропуски.

### 2. Реальное сканирование

```bash
autoi18n scan
```

Новые фразы уходят в реестр исходного языка и в очередь на перевод для
всех целевых языков из `.env`.

### 3. Перевод очереди

```bash
autoi18n translate
```

Требует настроенного ИИ (по умолчанию — OpenAI, `OPENAI_API_KEY`).

### 4. Добавить язык «на лету» (например, из админки)

```bash
autoi18n add-lang en
```
или из кода:
```python
t.add_target_lang("en")
```
Дописывает `.env` и сразу переводит весь реестр на новый язык.

### Фоновый цикл (сканирование + перевод по расписанию)

```python
t.run_translation_loop(interval=300)  # раз в 5 минут: extract() -> process_queue()
```

---

## Какие файлы сканируются

Сканируются только исходники проекта — стандартный набор путей
(`config.py`, `DEFAULT_SCAN_PATHS`):

| Папка | Расширения | Тип |
|---|---|---|
| `frontend/src` | `.js .jsx .ts .tsx` | JS/JSX (AST) |
| `src` | `.js .jsx .ts .tsx` | JS/JSX (AST) |
| `app` | `.js .jsx .ts .tsx` | JS/JSX (AST) |
| `templates` | `.html` | HTML |
| `app/templates` | `.html` | HTML |
| `backend/templates` | `.html` | HTML |

Поиск файлов — явный обход папок (`os.walk`) по списку расширений, без
glob-паттернов и без brace-expansion (`*.{js,jsx}`) — именно такие
паттерны в v1 молча находили ноль файлов, так как `glob` в Python их не
поддерживает. Служебные папки (`node_modules`, `.git`, `dist`, `build`,
`.venv` и т.п.) пропускаются автоматически.

### Что именно считается «текстом интерфейса»

- **JSX** — текст между тегами и переводимые атрибуты
  (`label`, `placeholder`, `title`, `aria-label`).
- **HTML** — видимый текст и переводимые атрибуты; содержимое
  `<script>` внутри HTML разбирается тем же JS/AST-парсером.
- **Императивные изменения текста в JS** — только там, где строка
  структурно и есть видимый текст страницы: `el.textContent = ...`,
  `el.innerText = ...`, `el.innerHTML = ...`, а также аргументы
  `alert()`/`confirm()`.

Обычные строковые литералы вне этих мест (CSS-в-JS значения, id,
классы, URL и т.п.) намеренно не трогаются — иначе в реестр попадал бы
технический мусор.

---

## Методы `Translator`

| Метод | Назначение |
|---|---|
| `extract(dry_run=False)` | Сканирует проект; `dry_run=True` — только отчёт, ничего не пишет. |
| `process_queue(batch_size=50)` | Переводит накопленную очередь. |
| `run_translation_loop(interval, batch_size)` | Фоновый цикл: `extract()` → `process_queue()`. |
| `get_target_langs()` | Текущий список целевых языков из `.env`. |
| `add_target_lang(lang)` | Добавляет язык в `.env` и сразу переводит на него весь реестр. |
| `apply_to_html(html, lang)` | Подставляет перевод в уже отрисованный HTML. |
| `apply_to_dict(source_dict, lang, filter_keys=None)` | Переводит значения вложенного словаря (например, JSON-ответ API). |
| `build_runtime(lang, dynamic_dom_enabled=False)` | Генерирует клиентский JS-рантайм для языка. |
| `register_keys(items, dict_name="bot")` | Регистрирует бэкенд-фразы, которых нет в файлах проекта (например, генерируемые сообщения). |
| `translate_key(default, lang, dict_name="bot")` | Перевод бэкенд-фразы по её тексту (не по ключу — ключей больше нет). |
| `get_translation_coverage(lang)` | Процент готовых переводов для языка. |

`translate_key` больше не принимает семантический `key` — только текст на
исходном языке (`default`) и язык. Если перевода ещё нет — ставит фразу в
очередь и возвращает исходный текст.

---

## Интеграция с проектом (без правок кода компонентов)

1. На бэкенде — один хук в месте рендера страницы:
   ```python
   html = t.apply_to_html(rendered_html, lang=current_lang)
   ```
   Для JSON-ответов API — аналогично `apply_to_dict(...)`.
2. На фронтенде — один `<script>` с рантаймом, подключённый один раз в
   точке входа:
   ```html
   <script>
   // t.build_runtime(lang, dynamic_dom_enabled=True)
   </script>
   ```
3. Переключение языка на клиенте:
   ```js
   window.autoI18n.setLanguage('en');
   ```
   Рантайм сам подгрузит словарь нового языка и пройдёт по DOM — код
   компонентов трогать не нужно.

---

## Переменные окружения

| Переменная | Описание |
|---|---|
| `OPENAI_API_KEY` | Ключ ИИ-провайдера — нужен только для `translate`/`add-lang`, не для `scan --dry-run`. |
| `SOURCE_LANG` | Исходный язык (по умолчанию `ru`). |
| `AUTO_I18N_TARGET_LANGS` | Целевые языки через запятую — источник истины, редактируется через `add_target_lang()`. |
| `AUTO_I18N_CACHE_DIR` | Папка для файлов переводов (по умолчанию `./translations`). |

---

## CLI

```
autoi18n scan --dry-run [--json]   отладочный этап: показать найденное, ничего не писать
autoi18n scan                      реальное сканирование
autoi18n translate [--batch-size]  перевести очередь
autoi18n add-lang <lang>           добавить целевой язык и перевести на него реестр
autoi18n coverage <lang>           процент покрытия перевода
autoi18n langs                     исходный и текущие целевые языки
```

---

## Лицензия

MIT
