# How Voice-Pro Implements Internationalization (i18n): A Complete Technical Guide

> Discover how Voice-Pro implements i18n with a custom I18nAuto class. This guide details its lightweight, dependency-free system for runtime localization of UI strings.

- Repository: [ABUS/voice-pro](https://github.com/abus-aikorea/voice-pro)
- Tags: deep-dive
- Published: 2026-08-03

---

**Voice-Pro implements a lightweight, runtime internationalization system using a custom `I18nAuto` class that loads locale-specific JSON dictionaries and exposes a callable interface for translating UI strings without external dependencies.**

Voice-Pro, an open-source AI voice processing application, handles multilingual support through a custom-built internationalization (i18n) layer that operates entirely at runtime. Unlike frameworks that rely on heavy external libraries such as Babel or gettext, the repository uses a simple JSON-based approach located in `src/i18n/`. This implementation enables rapid addition of new languages while keeping the application footprint minimal.

## Core Architecture of the Voice-Pro i18n System

The internationalization layer consists of three primary components working in concert: a callable loader class, JSON translation bundles, and an AST-based key scanner.

### The I18nAuto Callable Class ([`src/i18n/i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/src/i18n/i18n.py))

At the heart of the system is the **`I18nAuto`** class, which implements Python's `__call__` method to behave like a function. When instantiated, it determines the target locale, loads the corresponding JSON file, and stores the translation mapping in `self.language_map`.

```python
from src.i18n.i18n import I18nAuto

i18n = I18nAuto(language="ko_KR")
print(i18n("Upload media"))  # Returns: "미디어 업로드"

```

The `__call__` method performs a simple dictionary lookup, returning the translated value or the original key if no translation exists:

```python
def __call__(self, key):
    return self.language_map.get(key, key)

```

### Locale Detection and Fallback Strategy

When `I18nAuto()` receives `"Auto"` or `None` as the language parameter, it queries the system locale using `locale.getdefaultlocale()[0]`. If the resolved locale lacks a corresponding JSON file in `src/i18n/locale/`, the system gracefully falls back to `en_US`.

```python

# From src/i18n/i18n.py

if language in ["Auto", None]:
    language = locale.getdefaultlocale()[0]
json_path = os.path.join(Path(__file__).resolve().parent, f"locale/{language}.json")
if not os.path.exists(json_path):
    language = "en_US"

```

### JSON Translation Bundles (`src/i18n/locale/`)

Translations reside in flat JSON files named by locale code (e.g., [`en_US.json`](https://github.com/abus-aikorea/voice-pro/blob/main/en_US.json), [`ko_KR.json`](https://github.com/abus-aikorea/voice-pro/blob/main/ko_KR.json), [`ja_JP.json`](https://github.com/abus-aikorea/voice-pro/blob/main/ja_JP.json)). Each file contains a single dictionary mapping English source strings to localized equivalents:

```json
{
    "Language": "언어",
    "Upload media": "미디어 업로드",
    "Submit": "제출"
}

```

The `load_language_list()` function reads these files with UTF-8 encoding to support multilingual character sets.

## Runtime Translation Workflow

The Voice-Pro i18n system operates through a three-stage pipeline that executes whenever the application launches or switches languages.

1. **Initialization**: `I18nAuto` instantiates and resolves the target locale via `locale.getdefaultlocale()` or a user-specified parameter.

2. **Loading**: The constructor calls `load_language_list(language)`, which constructs a path to `src/i18n/locale/{language}.json` and deserializes the contents into `self.language_map`.

3. **Resolution**: UI modules call `i18n("Key String")`, triggering `__call__` to return the mapped value or the key itself as a fallback.

This approach ensures that adding support for a new language requires only creating a new JSON file and restarting the application.

## Automated Key Discovery with [`scan_i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/scan_i18n.py)

Maintaining translation consistency across a growing codebase requires knowing exactly which strings require translation. The **[`src/i18n/scan_i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/src/i18n/scan_i18n.py)** utility automates this by parsing the abstract syntax tree (AST) of every Python file in the repository.

The scanner walks the source tree, identifies all calls to the `i18n()` function, and extracts the string literals passed as arguments. This produces the canonical list of source-language keys that must exist in every locale file.

```python

# Simplified logic from src/i18n/scan_i18n.py

for filename in python_files:
    tree = ast.parse(open(filename).read())
    i18n_strings = extract_i18n_strings(tree)
    # Collects every string passed to i18n(...)

```

Developers run this script during the build process to generate translation templates or verify that all UI strings are accounted for in the locale files.

## UI Integration in Gradio Components

Voice-Pro uses Gradio for its web interface, and every user-visible string passes through the `i18n` callable. The pattern appears consistently across UI modules such as [`app/tab_tts_kokoro.py`](https://github.com/abus-aikorea/voice-pro/blob/main/app/tab_tts_kokoro.py) and [`app/tab_vsr.py`](https://github.com/abus-aikorea/voice-pro/blob/main/app/tab_vsr.py).

```python

# From app/tab_tts_kokoro.py

from src.i18n.i18n import I18nAuto
i18n = I18nAuto()

gr.Dropdown(
    label=i18n("Language"),
    choices=kokoro_voice.gradio_languages(),
    value=kokoro_voice.selected_language
)

```

Because `i18n` evaluates at render time, switching the locale and reloading the interface immediately reflects the new language without code changes.

## Adding a New Language to Voice-Pro

Extending Voice-Pro to support additional languages follows a straightforward process that leverages the existing scanner utility.

First, run the scanner to collect all current translation keys:

```bash
python src/i18n/scan_i18n.py

```

Next, create a new JSON file in `src/i18n/locale/` using the locale code as the filename (e.g., [`de_DE.json`](https://github.com/abus-aikorea/voice-pro/blob/main/de_DE.json) for German). Copy the collected keys and provide translations:

```json
{
    "Language": "Sprache",
    "Upload media": "Medien hochladen",
    "Submit": "Absenden"
}

```

Finally, instantiate `I18nAuto` with the new locale code or set the system locale accordingly. The application loads the new translations immediately upon restart.

## Summary

- Voice-Pro uses a **custom `I18nAuto` class** in [`src/i18n/i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/src/i18n/i18n.py) that loads JSON dictionaries and acts as a callable translation function.
- **Locale detection** relies on `locale.getdefaultlocale()` with a hard fallback to `en_US` when translation files are missing.
- **Translation bundles** are flat JSON files stored in `src/i18n/locale/`, requiring no compilation or external dependencies.
- **[`scan_i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/scan_i18n.py)** parses the AST to extract all `i18n()` calls, ensuring translation keys remain synchronized with the codebase.
- **UI modules** import a single `i18n` instance and wrap all labels, enabling real-time language switching in Gradio components.

## Frequently Asked Questions

### Does Voice-Pro use external i18n libraries like Babel or gettext?

No. According to the Voice-Pro source code, the implementation avoids external dependencies by using a lightweight, custom-built system. The `I18nAuto` class in [`src/i18n/i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/src/i18n/i18n.py) handles all translation logic through standard library modules (`json`, `locale`, `pathlib`) and simple dictionary lookups.

### How does Voice-Pro handle missing translations?

The system implements a **key fallback mechanism**. When `i18n("Key")` is called, the `__call__` method executes `self.language_map.get(key, key)`, returning the original English key if no translation exists in the loaded JSON file. This ensures the UI remains functional even with incomplete locale files.

### Where are the translation files located in the repository?

All translation bundles reside in the `src/i18n/locale/` directory. Each language has its own JSON file named with the standard locale code (e.g., [`en_US.json`](https://github.com/abus-aikorea/voice-pro/blob/main/en_US.json), [`ko_KR.json`](https://github.com/abus-aikorea/voice-pro/blob/main/ko_KR.json)). The core loader logic is located in [`src/i18n/i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/src/i18n/i18n.py), while the development utility [`src/i18n/scan_i18n.py`](https://github.com/abus-aikorea/voice-pro/blob/main/src/i18n/scan_i18n.py) manages key discovery.

### How can developers add support for a new language?

Developers must create a new JSON file in `src/i18n/locale/` named with the appropriate locale code, then run `python src/i18n/scan_i18n.py` to collect all required translation keys from the source code. After populating the JSON file with translations, the language becomes available by passing the locale code to `I18nAuto(language="xx_XX")` or setting the system locale.