Initial Commit

This commit is contained in:
WhatDidYouExpect 2025-07-06 21:14:47 +02:00
commit 184685fdd8
6 changed files with 135 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__pycache__
.env

25
README.md Normal file
View file

@ -0,0 +1,25 @@
How To Use
Put your translation JSON files inside locales directories anywhere under your project or working directory.
Each JSON file must be named as the language code, e.g., en.json, es.json.
Make sure you have a .env file with a locale variable defining your default language code (eg locale=en).
Start the loader
Just import or run this script — it immediately:
Loads all translation files it finds.
Starts a background thread watching those files for any edits.
Set language
Call set_language("fr") to switch the active locale dynamically.
Fetch translations
Use _('some.key') anywhere to get the localized string.

3
example.py Normal file
View file

@ -0,0 +1,3 @@
from volta.main import _
print(_("hello_key"))

3
locales/en.json Normal file
View file

@ -0,0 +1,3 @@
{
"hello_key": "Hello World!"
}

3
locales/it.json Normal file
View file

@ -0,0 +1,3 @@
{
"hello_key": "Ciao Mondo!"
}

99
volta/main.py Normal file
View file

@ -0,0 +1,99 @@
import os
import json
import pathlib
import threading
import time
from dotenv import load_dotenv
ANSI = "\033["
RED = f"{ANSI}31m"
GREEN = f"{ANSI}32m"
YELLOW = f"{ANSI}33m"
DEBUG = f"{ANSI}1;30m"
RESET = f"{ANSI}0m"
load_dotenv()
LOCALE = os.getenv("locale")
module_dir = pathlib.Path(__file__).parent.parent
working_dir = pathlib.Path.cwd()
EXCLUDE_DIRS = {'.git', '__pycache__'}
locales_dirs = []
def find_locales_dirs(base_path):
found = []
for root, dirs, files in os.walk(base_path):
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS]
if 'locales' in dirs:
locales_path = pathlib.Path(root) / 'locales'
found.append(locales_path)
dirs.remove('locales')
return found
locales_dirs.extend(find_locales_dirs(module_dir))
if working_dir != module_dir:
locales_dirs.extend(find_locales_dirs(working_dir))
translations = {}
_file_mod_times = {}
def load_translations():
global translations, _file_mod_times
translations.clear()
_file_mod_times.clear()
for locales_dir in locales_dirs:
for filename in os.listdir(locales_dir):
if filename.endswith(".json"):
lang_code = filename[:-5]
file_path = locales_dir / filename
try:
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
if lang_code not in translations:
translations[lang_code] = {}
translations[lang_code].update(data)
_file_mod_times[(lang_code, file_path)] = file_path.stat().st_mtime
except Exception as e:
print(f"{RED}Failed loading {file_path}: {e}{RESET}")
def reload_if_changed():
while True:
for (lang_code, file_path), last_mtime in list(_file_mod_times.items()):
try:
current_mtime = file_path.stat().st_mtime
if current_mtime != last_mtime:
print(f"{RED}Translation file changed: {file_path}, reloading...{RESET}")
load_translations()
break
except FileNotFoundError:
print(f"{RED}Translation file removed: {file_path}{RESET}")
_file_mod_times.pop((lang_code, file_path), None)
if lang_code in translations:
translations.pop(lang_code, None)
def set_language(lang: str):
global LOCALE
if lang in translations:
LOCALE = lang
else:
print(f"{RED}Language '{lang}' not found, defaulting to 'en'{RESET}")
LOCALE = "en"
def get_translation(lang: str, key: str):
lang_translations = translations.get(lang, {})
if key in lang_translations:
return lang_translations[key]
fallback = translations.get("en", {}).get(key, key)
print(f"{RED}Missing key: '{key}' in language '{lang}', falling back to: '{fallback}'{RESET}")
return fallback
def _(key: str) -> str:
return get_translation(LOCALE, key)
load_translations()
watchdog_thread = threading.Thread(target=reload_if_changed, daemon=True)
watchdog_thread.start()