Skip to content

Commit 66ca0e5

Browse files
Lukas Geigerclaude
andcommitted
Add i18n support and update README (DE+EN)
- Add TranslationSystem (translator.py, manage_translations.py) - Add locales/translations.json with DE+EN translations - Update README.md with bilingual content (DE+EN) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a0ad86e commit 66ca0e5

4 files changed

Lines changed: 307 additions & 0 deletions

File tree

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,30 @@ MIT License - siehe [LICENSE](LICENSE)
9090
## Autor
9191

9292
Lukas Geiger ([@lukisch](https://github.com/lukisch))
93+
94+
---
95+
96+
## English
97+
98+
A lightweight Python IDE with dark theme, debugging integration, and code folding.
99+
100+
### Features
101+
102+
- Syntax highlighting
103+
- Dark/light themes
104+
- Debugging integration
105+
- Code folding
106+
- Auto-completion
107+
108+
### Installation
109+
110+
```bash
111+
git clone https://github.com/lukisch/REL_Editor_PythonBox.git
112+
cd REL_Editor_PythonBox
113+
pip install -r requirements.txt
114+
python "PythonBox_v8.py"
115+
```
116+
117+
### License
118+
119+
See [LICENSE](LICENSE) for details.

locales/translations.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"Einstellungen": {
3+
"de": "Einstellungen",
4+
"en": "Settings"
5+
},
6+
"Keine Änderungen.": {
7+
"de": "Keine Änderungen.",
8+
"en": "No changes."
9+
},
10+
"Schließen": {
11+
"de": "Schließen",
12+
"en": "Close"
13+
},
14+
"⬇️ Einfügen": {
15+
"de": "⬇️ Einfügen",
16+
"en": "⬇️ Insert"
17+
}
18+
}

manage_translations.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
manage_translations.py - Auto-Scanner fuer deutsche GUI-Strings
3+
================================================================
4+
Findet deutsche Strings in .py-Dateien und pflegt locales/translations.json.
5+
6+
Verwendung:
7+
python manage_translations.py [--dir PROJEKTVERZEICHNIS]
8+
"""
9+
10+
import json
11+
import re
12+
import os
13+
import sys
14+
15+
TRANSLATION_FILE = "locales/translations.json"
16+
17+
STRING_PATTERNS = [
18+
re.compile(r'text\s*=\s*"([^"]+)"'),
19+
re.compile(r'setText\s*\(\s*["\']([^"\']+)["\']\s*\)'),
20+
re.compile(r'setWindowTitle\s*\(\s*["\']([^"\']+)["\']\s*\)'),
21+
re.compile(r'QLabel\s*\(\s*["\']([^"\']+)["\']\s*\)'),
22+
re.compile(r'QPushButton\s*\(\s*["\']([^"\']+)["\']\s*\)'),
23+
]
24+
25+
GERMAN_HINTS = [
26+
"datei", "filter", "fehler", "laden", "speichern",
27+
"ansicht", "optionen", "zurueck", "anzeigen", "export",
28+
"import", "einstellungen", "abbrechen", "hilfe", "bearbeiten",
29+
"oeffnen", "schliessen", "start", "aktualisieren",
30+
]
31+
32+
33+
def is_german(text):
34+
if any(ch in text for ch in "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df"):
35+
return True
36+
text_lower = text.lower()
37+
return any(w in text_lower for w in GERMAN_HINTS)
38+
39+
40+
def find_german_strings(source_dir):
41+
german_strings = set()
42+
skip_dirs = {'build', 'dist', 'venv', '.venv', '__pycache__', 'releases'}
43+
44+
for root, dirs, files in os.walk(source_dir):
45+
dirs[:] = [d for d in dirs if d not in skip_dirs]
46+
for file in files:
47+
if file.endswith(".py"):
48+
path = os.path.join(root, file)
49+
try:
50+
with open(path, "r", encoding="utf-8") as f:
51+
content = f.read()
52+
except Exception:
53+
continue
54+
for pattern in STRING_PATTERNS:
55+
for match in pattern.findall(content):
56+
if is_german(match):
57+
german_strings.add(match.strip())
58+
return german_strings
59+
60+
61+
def manage_translations(source_dir="."):
62+
trans_file = os.path.join(source_dir, TRANSLATION_FILE)
63+
64+
if os.path.exists(trans_file):
65+
with open(trans_file, "r", encoding="utf-8") as f:
66+
translations = json.load(f)
67+
else:
68+
translations = {}
69+
70+
found = find_german_strings(source_dir)
71+
72+
added = []
73+
for s in sorted(found):
74+
if s not in translations:
75+
translations[s] = {"de": s, "en": ""}
76+
added.append(s)
77+
78+
os.makedirs(os.path.dirname(trans_file), exist_ok=True)
79+
with open(trans_file, "w", encoding="utf-8") as f:
80+
json.dump(translations, f, indent=2, ensure_ascii=False)
81+
82+
if added:
83+
print(f"[+] {len(added)} neue Eintraege hinzugefuegt:")
84+
for s in added[:20]:
85+
print(f" - {s}")
86+
if len(added) > 20:
87+
print(f" ... und {len(added) - 20} weitere")
88+
else:
89+
print("[i] Keine neuen deutschen Strings gefunden.")
90+
91+
missing = [k for k, v in translations.items() if not v.get("en")]
92+
if missing:
93+
print(f"\n[!] {len(missing)} fehlende englische Uebersetzungen")
94+
else:
95+
print("\n[ok] Alle Strings haben englische Uebersetzungen.")
96+
97+
print(f"\n[i] Gesamt: {len(translations)} Strings in {trans_file}")
98+
99+
100+
if __name__ == "__main__":
101+
target = sys.argv[1] if len(sys.argv) > 1 else "."
102+
manage_translations(target)

translator.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
TranslationSystem - Multi-Language Support fuer Anwendungen
3+
============================================================
4+
Version: 1.0.0 (isoliert aus _LANG)
5+
Quelle: ARC_EntwicklungsschleifeAdvanced/TranslationSystem.py v2.4
6+
7+
Verwendung:
8+
-----------
9+
from translator import TranslationSystem
10+
11+
translator = TranslationSystem('de')
12+
label.setText(translator.t('Datei oeffnen'))
13+
translator.set_language('en')
14+
"""
15+
16+
import json
17+
import re
18+
from pathlib import Path
19+
from typing import Dict, List, Set
20+
21+
22+
class TranslationSystem:
23+
"""Multi-Language Support System v1.0"""
24+
25+
def __init__(self, default_lang: str = 'de', app_dir: Path = None):
26+
"""
27+
Initialisiert Translation-System.
28+
29+
Args:
30+
default_lang: Standard-Sprache ('de' oder 'en')
31+
app_dir: Verzeichnis der Anwendung (default: aktuelles Verzeichnis)
32+
"""
33+
self.current_lang = default_lang
34+
35+
if app_dir is None:
36+
app_dir = Path.cwd()
37+
self.app_dir = Path(app_dir)
38+
39+
self.translations_file = self.app_dir / "locales" / "translations.json"
40+
41+
self.string_patterns = [
42+
re.compile(r'setText\s*\(\s*["\']([^"\']+)["\']\s*\)'),
43+
re.compile(r'setWindowTitle\s*\(\s*["\']([^"\']+)["\']\s*\)'),
44+
re.compile(r'QLabel\s*\(\s*["\']([^"\']+)["\']\s*\)'),
45+
re.compile(r'QPushButton\s*\(\s*["\']([^"\']+)["\']\s*\)'),
46+
re.compile(r'addAction\s*\([^,]*["\']([^"\']+)["\']\s*\)'),
47+
re.compile(r'addTab\s*\([^,]+,\s*["\']([^"\']+)["\']\s*\)'),
48+
re.compile(r'text\s*=\s*"([^"]+)"'),
49+
]
50+
51+
self.german_hints = [
52+
"datei", "bearbeiten", "ansicht", "hilfe", "oeffnen", "speichern",
53+
"schliessen", "einstellungen", "abbrechen", "ok", "ja", "nein",
54+
"start", "stop", "pause", "fortsetzen", "laden", "aktualisieren",
55+
"filter", "fehler", "export", "import", "optionen", "anzeigen",
56+
]
57+
58+
self.translations = {}
59+
self._load_translations()
60+
61+
def _load_translations(self):
62+
if self.translations_file.exists():
63+
try:
64+
with open(self.translations_file, 'r', encoding='utf-8') as f:
65+
self.translations = json.load(f)
66+
except Exception:
67+
self.translations = {}
68+
else:
69+
self.translations = {}
70+
71+
def _save_translations(self):
72+
self.translations_file.parent.mkdir(parents=True, exist_ok=True)
73+
with open(self.translations_file, 'w', encoding='utf-8') as f:
74+
json.dump(self.translations, f, indent=2, ensure_ascii=False)
75+
76+
def t(self, key: str) -> str:
77+
"""
78+
Uebersetzt einen Key in die aktuelle Sprache.
79+
80+
Args:
81+
key: Translation-Key (oft der deutsche Originaltext)
82+
83+
Returns:
84+
Uebersetzter Text oder Key als Fallback
85+
"""
86+
if key in self.translations:
87+
return self.translations[key].get(self.current_lang, key)
88+
89+
if self._is_german(key):
90+
self.translations[key] = {"de": key, "en": ""}
91+
self._save_translations()
92+
93+
return key
94+
95+
def set_language(self, lang: str):
96+
if lang in ['de', 'en']:
97+
self.current_lang = lang
98+
99+
def get_language(self) -> str:
100+
return self.current_lang
101+
102+
def add_translation(self, key: str, de: str, en: str):
103+
self.translations[key] = {"de": de, "en": en}
104+
self._save_translations()
105+
106+
def scan_and_update(self, project_dir: Path = None) -> Dict:
107+
"""Scannt Projekt-Dateien nach deutschen Strings und aktualisiert translations.json."""
108+
if project_dir is None:
109+
project_dir = self.app_dir
110+
111+
found_strings = self._find_german_strings(project_dir)
112+
113+
added = []
114+
for string in sorted(found_strings):
115+
if string not in self.translations:
116+
self.translations[string] = {"de": string, "en": ""}
117+
added.append(string)
118+
119+
if added:
120+
self._save_translations()
121+
122+
missing = [k for k, v in self.translations.items() if not v.get("en")]
123+
124+
return {'added': added, 'missing': missing, 'total': len(self.translations)}
125+
126+
def _find_german_strings(self, directory: Path) -> Set[str]:
127+
german_strings = set()
128+
skip_dirs = {'build', 'dist', 'venv', '.venv', '__pycache__', 'releases'}
129+
130+
for py_file in directory.rglob("*.py"):
131+
if any(folder in py_file.parts for folder in skip_dirs):
132+
continue
133+
try:
134+
with open(py_file, 'r', encoding='utf-8') as f:
135+
content = f.read()
136+
except Exception:
137+
continue
138+
139+
for pattern in self.string_patterns:
140+
for match in pattern.findall(content):
141+
if match and self._is_german(match):
142+
german_strings.add(match.strip())
143+
144+
return german_strings
145+
146+
def _is_german(self, text: str) -> bool:
147+
if any(ch in text for ch in "aeoeueAeOeUess"):
148+
return True
149+
text_lower = text.lower()
150+
return any(hint in text_lower for hint in self.german_hints)
151+
152+
def get_missing_translations(self) -> List[str]:
153+
return [k for k, v in self.translations.items() if not v.get("en")]
154+
155+
156+
if __name__ == "__main__":
157+
tr = TranslationSystem('de')
158+
print(f"Sprache: {tr.get_language()}")
159+
result = tr.scan_and_update()
160+
print(f"Scan: {result['total']} Strings, {len(result['added'])} neu, {len(result['missing'])} ohne EN")

0 commit comments

Comments
 (0)