A Python port of the Invisible Internet Project (I2P)
1"""i18n translation support.
2
3Loads Java .properties files and provides locale-specific message lookup
4with parameter interpolation.
5
6Ported from net.i2p.util.Translate and TranslateReader.
7"""
8
9from __future__ import annotations
10
11import os
12import re
13
14
15class TranslateReader:
16 """Load Java .properties translation files."""
17
18 def load(self, path: str) -> dict[str, str]:
19 """Load a .properties file into a dict.
20
21 Returns empty dict if file not found.
22 """
23 result: dict[str, str] = {}
24 try:
25 with open(path, encoding="utf-8") as f:
26 for line in f:
27 line = line.rstrip("\n\r")
28 if not line or line.startswith("#") or line.startswith("!"):
29 continue
30 # Split on first = or :
31 m = re.match(r"([^=:]+?)\s*[=:]\s*(.*)", line)
32 if m:
33 result[m.group(1).strip()] = m.group(2)
34 except OSError:
35 pass
36 return result
37
38
39class Translate:
40 """Locale-specific message lookup with interpolation."""
41
42 def __init__(self) -> None:
43 self._locale: str = "en"
44 self._strings: dict[str, str] = {}
45 self._reader = TranslateReader()
46
47 def set_locale(self, locale: str) -> None:
48 self._locale = locale
49
50 def get_locale(self) -> str:
51 return self._locale
52
53 def add_strings(self, strings: dict[str, str]) -> None:
54 """Add translations to the current set."""
55 self._strings.update(strings)
56
57 def load_bundle(self, directory: str, bundle_name: str) -> None:
58 """Load a properties file from directory for current locale."""
59 path = os.path.join(directory, f"{bundle_name}_{self._locale}.properties")
60 props = self._reader.load(path)
61 self._strings.update(props)
62
63 def get_string(self, key: str, *params: str) -> str:
64 """Look up a translated string, with optional parameter substitution.
65
66 Returns the key itself if not found (Java convention).
67 Parameters are substituted for {0}, {1}, etc.
68 """
69 value = self._strings.get(key, key)
70 for i, param in enumerate(params):
71 value = value.replace(f"{{{i}}}", str(param))
72 return value