Repository navigation
Development
The project is small, deliberately — two source files and a test suite — but the conventions around them are strict.
kaman-ai-translator/
├── translator_app_v3.py # PyQt6 UI & QThread workers (entry point)
│ # batch engine, live view, dialogs, themes
├── kaman_core.py # Qt-free core: config + DPAPI encryption,
│ # prompts, chunking, structure analysis,
│ # engines (Gemini / OpenAI-compatible),
│ # job store, exporters
├── tests/test_v3.py # 302-check offline test-suite
├── icon.png / icon.ico
├── requirements.txt
├── LICENSE
├── CONTRIBUTING.md
└── README.md
Ten tracked files. The commit history goes back to the v3.0.0 rewrite
(b1c004a), and the current head is 3feb372.
kaman_core.py must not import Qt.
The core holds everything that is worth testing — configuration and encryption, the prompts, chunking, structure analysis, the engines, the job store, the exporters. Keeping it GUI-free is what allows the entire pipeline to be tested headlessly, with no display and no event loop.
The reverse is not true: translator_app_v3.py imports the core freely, and
imports PyQt6 heavily. It is the entry point — python translator_app_v3.py starts
the application.
If a change needs a GUI, it goes in translator_app_v3.py. If it does not, it goes
in kaman_core.py. A pull request that adds a PyQt6 import to the core breaks
the test suite's ability to run without a display, and will be sent back.
git clone https://github.com/Mohammad-Hasan-Kaman/kaman-ai-translator.git
cd kaman-ai-translator
pip install -r requirements.txt
python translator_app_v3.pyPython 3.11+. The dependencies are PyQt6, Pillow, google-genai and
pymupdf.
python tests/test_v3.py
# expected: ALL V3 TESTS PASSED (302 checks)The suite is fully offline — fake engines, a temporary APPDATA, no network, no
API key. See Testing for what it covers and how it is structured.
A contribution is not complete until the suite passes. The rule from
CONTRIBUTING.md is explicit: the test suite must pass, and it must pass offline —
a test that requires a real API key or a network call is not acceptable, because
then it cannot run for anyone else.
The suite exits non-zero on any failure, so it works as a CI gate.
CONTRIBUTING.md covers three areas.
New behaviour comes with a test. The suite must keep passing with no network access and no credentials.
The interface is 100% right-to-left Persian, and this is a maintained property, not an accident:
| Rule | Implementation |
|---|---|
| Persian numerals |
fa_number(value) maps ASCII digits to ۰۱۲۳۴۵۶۷۸۹. |
| Spin-box alignment |
align_spin_value_right plus _SpinAlignFilter, re-applied on Show, StyleChange, Polish, FontChange and ApplicationPaletteChange — Qt re-lays-out spin boxes on a theme change and loses the alignment otherwise. |
| Progress direction | The percentage label is right-aligned; the progress bar's internal text is hidden because Qt drew it on the left. |
| Arrow direction | "Next" points left (◀), "previous" points right (▶). |
| Panel order | The source panel is added first, so in an RTL layout it lands on the right. |
| Copy button state | Blue when the current page has a translation, grey when it does not. |
These are asserted by the test suite — sections K, S, U and V exist to check the geometry of the interface. A change that breaks RTL alignment breaks a test.
- The API key is never to be committed, hard-coded, logged, or included in a release asset.
- It is entered in the settings dialog and stored encrypted with Windows DPAPI.
- A contribution that adds a network endpoint, an update checker, or a telemetry call will be rejected — the "no background network activity beyond the provider you configured" property is a promise the README makes, and the test suite and the contribution guide both protect it.
The GUI never blocks. Everything long-running runs on a QThread worker:
translation, structure analysis, and exports. A new feature that could take more
than a fraction of a second belongs on a worker thread, not on the main thread.
The state is saved per page. JobStore writes the job file atomically after
every completed page, guarded by a lock. A new code path that mutates job state
should go through the store, not write the file directly.
Cancellation is cooperative. Workers poll a threading.Event between chunks and
raise CancelledError; the app never kills a thread. New long operations should
follow the same pattern so that ⏸ توقف and ■ لغو keep working.
The config is an allow-list. CONFIG_DEFAULTS defines both the defaults and
the set of keys that are read from disk. A new setting must be added there, or it
will be silently dropped on load.
The history is small and readable, and the messages describe why:
3feb372 chore: remove the unused duplicate icon..ico asset
946bdb2 docs: translate the star section to English and drop the stale icon from the project tree
522e64f Docs: advertise bidirectional EN⇄FA translation in the README tagline
b57a6c2 Docs: fix stale test badge (303) and document encrypt_fallback.log
9734867 Log the reason when DPAPI encryption falls back to base64
7cd883c Show the exact local data folder in Settings with a one-click open button
20ac318 Docs: professional README, CONTRIBUTING and LICENSE for the v3.0.0 release
3466b04 Docs: fix executable build command (verified PyInstaller flags with bundled icon) and list icon.ico
1d7c6fb Add multi-resolution icon.ico for the Windows executable
b1c004a kaman-ai-translator v3.0.0 — complete rewrite
The convention is a conventional-commit prefix (feat:, docs:, chore:, fix:)
followed by a description of the effect.
- Testing — the suite.
- Architecture — how the two files divide the work.
- Build and Release — packaging and the published releases.