-
Notifications
You must be signed in to change notification settings - Fork 0
Localizer
Relevant source files
The Localizer utility class provides internationalization (i18n) functionality for the MouseMacros application. It manages loading, caching, and retrieval of localized strings from JSON language files. This system supports seven languages (English, Chinese, Japanese, Korean, Russian, Spanish, and French) and provides automatic system language detection with English fallback.
For information about the language file structure and supported translation keys, see Language Files and Translation Keys. For details on how appearance settings control language selection, see Configuration Files and Settings.
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L1-L98
The Localizer operates as a singleton utility with static methods, loading translations at application startup and providing runtime access to localized strings throughout the UI layer.
flowchart TD
StaticInit["Localizer static block"]
EnvDetect["Environment Detection"]
SysLangDetect["System Language Detection"]
DevFiles["Development Mode<br>lang/*.json files"]
JarFiles["Production Mode<br>JAR resources"]
LocalizerClass["Localizer"]
TranslationMap["translations Map"]
CurrentLang["currentLang String"]
DevModeFlag["isDevMode boolean"]
RuntimeSwitchFlag["runtimeSwitch boolean"]
LoadMethod["load(String lang)"]
GetMethod["get(String key)"]
IsDevMode["isDevMode()"]
GetCurrentLang["getCurrentLang()"]
SetRuntimeSwitch["setRuntimeSwitch(boolean)"]
ConfigManager["ConfigManager.getAvailableLangs()"]
UIComponents["MainFrame, Dialogs, Listeners"]
SettingsDialog["SettingsDialog<br>language selection"]
PrimaryLookup["Primary Language Lookup"]
EnglishFallback["English Fallback<br>en_us.json"]
KeyReturn["Return key if not found"]
EnvDetect --> DevModeFlag
SysLangDetect --> LoadMethod
DevModeFlag --> DevFiles
DevModeFlag --> JarFiles
DevFiles --> LoadMethod
JarFiles --> LoadMethod
LoadMethod --> TranslationMap
LoadMethod --> CurrentLang
GetMethod --> PrimaryLookup
LocalizerClass --> LoadMethod
LocalizerClass --> GetMethod
LocalizerClass --> IsDevMode
LocalizerClass --> GetCurrentLang
LocalizerClass --> SetRuntimeSwitch
ConfigManager --> IsDevMode
ConfigManager --> JarFiles
UIComponents --> GetMethod
SettingsDialog --> LoadMethod
TranslationMap --> GetMethod
CurrentLang --> GetMethod
subgraph subGraph5 ["Fallback Mechanism"]
PrimaryLookup
EnglishFallback
KeyReturn
PrimaryLookup --> EnglishFallback
EnglishFallback --> KeyReturn
end
subgraph subGraph4 ["Integration Points"]
ConfigManager
UIComponents
SettingsDialog
end
subgraph subGraph3 ["API Methods"]
LoadMethod
GetMethod
IsDevMode
GetCurrentLang
SetRuntimeSwitch
end
subgraph subGraph2 ["Localizer Core"]
LocalizerClass
TranslationMap
CurrentLang
DevModeFlag
RuntimeSwitchFlag
end
subgraph subGraph1 ["Language File Sources"]
DevFiles
JarFiles
end
subgraph subGraph0 ["Application Initialization"]
StaticInit
EnvDetect
SysLangDetect
StaticInit --> EnvDetect
StaticInit --> SysLangDetect
end
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L1-L98, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L79-L132
The Localizer determines its operational mode (development or production) during static initialization by checking for the presence of language files in the file system.
| Mode | Detection Method | Language File Source | Use Case |
|---|---|---|---|
| Development | Checks if lang/zh_cn.json file exists |
Reads from lang/ directory in project |
IDE/development environment |
| Production | File check fails | Reads from JAR resources lang/*.json
|
Packaged application |
flowchart TD
Start["Static Initialization"]
CheckFile["Check File:<br>new File('lang/zh_cn.json').exists()"]
SetDevMode["isDevMode = true"]
SetProdMode["isDevMode = false"]
DetectSysLang["Detect System Language<br>Locale.getDefault()"]
NormalizeLang["Normalize language code<br>replace '-' with '_'"]
CheckLangFile["Language file<br>exists?"]
UseSysLang["Use system language"]
FallbackEnglish["Fallback to en_us"]
LoadLang["load(currentLang)"]
Start --> CheckFile
CheckFile --> SetDevMode
CheckFile --> SetProdMode
SetDevMode --> DetectSysLang
SetProdMode --> DetectSysLang
DetectSysLang --> NormalizeLang
NormalizeLang --> CheckLangFile
CheckLangFile --> UseSysLang
CheckLangFile --> FallbackEnglish
UseSysLang --> LoadLang
FallbackEnglish --> LoadLang
Code Implementation:
- Environment detection: src/io/github/samera2022/mouse_macros/Localizer.java L15-L18
- System language detection: src/io/github/samera2022/mouse_macros/Localizer.java L20-L32
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L15-L33
The load(String lang) method is the core mechanism for loading translation files into memory. It handles both development and production environments differently.
sequenceDiagram
participant Caller
participant load(lang)
participant isDevMode check
participant File System
participant ClassLoader
participant Gson Parser
participant translations Map
Caller->>load(lang): load("zh_cn")
load(lang)->>isDevMode check: Check isDevMode
loop [Development Mode]
isDevMode check->>File System: new File("lang/zh_cn.json")
File System->>File System: Check existence
File System-->>load(lang): FileReader
load(lang)->>Gson Parser: fromJson(reader, Map.class)
isDevMode check->>ClassLoader: getResourceAsStream("lang/zh_cn.json")
ClassLoader-->>load(lang): InputStream
load(lang)->>Gson Parser: fromJson(InputStreamReader, Map.class)
end
Gson Parser-->>load(lang): Map<String, String>
load(lang)->>translations Map: Update translations
load(lang)->>load(lang): currentLang = "zh_cn"
load(lang)-->>Caller: Success
note over load(lang): On exception: translations = empty HashMap
| Environment | File Path | Access Method |
|---|---|---|
| Development |
/lang/{lang}.json or lang/{lang}.json
|
new FileReader(file) |
| Production |
lang/{lang}.json (JAR resource) |
ClassLoader.getResourceAsStream(path) |
Code Implementation:
- Load method signature: src/io/github/samera2022/mouse_macros/Localizer.java L47
- Development mode loading: src/io/github/samera2022/mouse_macros/Localizer.java L49-L52
- Production mode loading: src/io/github/samera2022/mouse_macros/Localizer.java L53-L59
- Error handling: src/io/github/samera2022/mouse_macros/Localizer.java L62-L64
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L47-L65
The get(String key) method implements a two-tier fallback strategy to ensure that UI text is always available, even when translations are incomplete.
flowchart TD
GetCall["get(key)"]
PrimaryLookup["translations.get(key)"]
CheckValue["value != null?"]
ReturnValue["Return value"]
CheckCurrent["currentLang == 'en_us'?"]
LoadEnglish["Load en_us.json"]
EnglishLookup["enMap.get(key)"]
CheckEnValue["enValue != null?"]
ReturnEnValue["Return enValue"]
ReturnKey["Return key<br>(untranslated)"]
GetCall --> PrimaryLookup
PrimaryLookup --> CheckValue
CheckValue --> ReturnValue
CheckValue --> CheckCurrent
CheckCurrent --> ReturnKey
CheckCurrent --> LoadEnglish
LoadEnglish --> EnglishLookup
EnglishLookup --> CheckEnValue
CheckEnValue --> ReturnEnValue
CheckEnValue --> ReturnKey
| Scenario | Primary Lookup | English Fallback | Final Result |
|---|---|---|---|
| Key exists in current language | ✓ Found | Not attempted | Localized string |
| Key missing, current is English | ✗ Not found | Not applicable | Key string |
| Key missing, current is not English | ✗ Not found | Attempted | English string or key |
| All lookups fail | ✗ Not found | ✗ Not found | Key string |
Code Implementation:
- Primary lookup: src/io/github/samera2022/mouse_macros/Localizer.java L67-L69
- English fallback check: src/io/github/samera2022/mouse_macros/Localizer.java L71
- Development mode fallback: src/io/github/samera2022/mouse_macros/Localizer.java L73-L79
- Production mode fallback: src/io/github/samera2022/mouse_macros/Localizer.java L80-L89
- Final fallback: src/io/github/samera2022/mouse_macros/Localizer.java L92
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L67-L93
The Localizer supports runtime language switching through a control flag and manual reload mechanism.
flowchart TD
RuntimeSwitch["runtimeSwitch boolean"]
SetMethod["setRuntimeSwitch(boolean)"]
IsMethod["isRuntimeSwitch()"]
UserAction["User selects new language"]
ConfigUpdate["ConfigManager updates config.lang"]
LoadCall["Localizer.load(newLang)"]
MapUpdate["translations Map updated"]
CurrentLangUpdate["currentLang updated"]
ComponentUtil["ComponentUtil triggers resize"]
FrameAdjust["Frame adjustments based on<br>readjustFrameMode"]
TextRefresh["UI components re-fetch strings"]
MapUpdate --> ComponentUtil
subgraph subGraph2 ["UI Update"]
ComponentUtil
FrameAdjust
TextRefresh
ComponentUtil --> FrameAdjust
FrameAdjust --> TextRefresh
end
subgraph subGraph1 ["Language Change Process"]
UserAction
ConfigUpdate
LoadCall
MapUpdate
CurrentLangUpdate
UserAction --> ConfigUpdate
ConfigUpdate --> LoadCall
LoadCall --> MapUpdate
LoadCall --> CurrentLangUpdate
end
subgraph subGraph0 ["Control Flag"]
RuntimeSwitch
SetMethod
IsMethod
SetMethod --> RuntimeSwitch
RuntimeSwitch --> IsMethod
end
| Method | Purpose | Usage |
|---|---|---|
setRuntimeSwitch(boolean enable) |
Enable/disable runtime switching flag | Called by settings dialog before language change |
isRuntimeSwitch() |
Check if runtime switch is active | Used by ComponentUtil to determine resize behavior |
load(String lang) |
Reload translations for new language | Called after config update |
getCurrentLang() |
Get active language code | Used to check current state |
Code Implementation:
- Runtime switch setter: src/io/github/samera2022/mouse_macros/Localizer.java L35-L37
- Runtime switch getter: src/io/github/samera2022/mouse_macros/Localizer.java L39-L41
- Current language getter: src/io/github/samera2022/mouse_macros/Localizer.java L95-L97
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L35-L41, src/io/github/samera2022/mouse_macros/Localizer.java L95-L97
The ConfigManager works with Localizer to discover available languages and manage language selection preferences.
The ConfigManager.getAvailableLangs() method enumerates language files using the same environment detection logic as Localizer.
flowchart TD
GetLangs["getAvailableLangs()"]
CheckMode["isDevMode()?"]
ListFiles["FileUtil.listFileNames('lang')"]
StripExt1["Strip .json extension"]
GetResURL["getResource('lang/')"]
CheckProtocol["protocol == 'jar'?"]
ParsePath["Parse JAR path"]
OpenJar["new JarFile(jarPath)"]
EnumEntries["Enumerate entries"]
FilterLang["Filter 'lang/*.json'"]
ExtractName["Extract filename"]
ListDir["List directory files"]
FilterJson["Filter .json files"]
StripExt2["Strip extension"]
BuildArray["Build String[] array"]
ReturnLangs["Return available languages"]
GetLangs --> CheckMode
CheckMode --> ListFiles
CheckMode --> GetResURL
StripExt1 --> BuildArray
ExtractName --> BuildArray
StripExt2 --> BuildArray
BuildArray --> ReturnLangs
subgraph subGraph3 ["Production Mode"]
GetResURL
CheckProtocol
GetResURL --> CheckProtocol
CheckProtocol --> ParsePath
CheckProtocol --> ListDir
subgraph subGraph2 ["File Protocol"]
ListDir
FilterJson
StripExt2
ListDir --> FilterJson
FilterJson --> StripExt2
end
subgraph subGraph1 ["JAR Processing"]
ParsePath
OpenJar
EnumEntries
FilterLang
ExtractName
ParsePath --> OpenJar
OpenJar --> EnumEntries
EnumEntries --> FilterLang
FilterLang --> ExtractName
end
end
subgraph subGraph0 ["Development Mode"]
ListFiles
StripExt1
ListFiles --> StripExt1
end
| Environment | Discovery Method | File Pattern |
|---|---|---|
| Development | FileUtil.listFileNames("lang") |
lang/*.json in filesystem |
| Production (JAR) | JAR entry enumeration | Entries matching lang/*.json
|
| Production (File) | Directory listing |
.json files in lang/ directory |
Code Implementation:
- getAvailableLangs() method: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L79-L132
- isDevMode() check: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L81
- Development mode listing: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L82-L91
- JAR entry enumeration: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L94-L113
- File protocol handling: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L114-L126
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L79-L132, src/io/github/samera2022/mouse_macros/Localizer.java L43-L45
The Localizer.get(key) method is called extensively throughout the UI layer to retrieve localized strings.
flowchart TD
MainFrame["MainFrame"]
Dialogs["Various Dialogs"]
Listeners["Event Listeners"]
GetButton["Localizer.get('button.record')"]
GetLog["Localizer.get('log.recording_mouse_pressed')"]
GetTooltip["Localizer.get('tooltip.enable_quick_mode')"]
GetLabel["Localizer.get('label.language')"]
UIKeys["UI Element Labels"]
LogKeys["Log Messages"]
TooltipKeys["Tooltip Text"]
DialogKeys["Dialog Messages"]
MainFrame --> GetButton
MainFrame --> GetLabel
Dialogs --> GetTooltip
Listeners --> GetLog
GetButton --> UIKeys
GetLog --> LogKeys
GetTooltip --> TooltipKeys
GetLabel --> DialogKeys
subgraph subGraph2 ["Translation Keys"]
UIKeys
LogKeys
TooltipKeys
DialogKeys
end
subgraph subGraph1 ["Localizer Usage"]
GetButton
GetLog
GetTooltip
GetLabel
end
subgraph subGraph0 ["UI Components"]
MainFrame
Dialogs
Listeners
end
| Component | Key Example | Purpose |
|---|---|---|
GlobalMouseListener |
"recording_key_pressed" |
Log recording events |
GlobalMouseListener |
"log.mouse_left" |
Button name display |
GlobalMouseListener |
"log.recording_scroll_msg1" |
Scroll event logging |
MainFrame |
Button labels, menu items | UI text |
| Dialogs | Dialog titles, labels | Settings interface |
Code Examples:
- Key press recording: src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L47
- Mouse button names: src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L73
- Scroll event logging: src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L125
Sources: src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L9, src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L47-L48, src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L73, src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L87, src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java L125
| Field | Type | Purpose | Access |
|---|---|---|---|
translations |
Map<String, String> |
Stores current language key-value pairs | Private |
currentLang |
String |
Currently loaded language code | Private |
runtimeSwitch |
boolean |
Flag for runtime language switching | Private |
isDevMode |
boolean |
Environment detection flag | Private |
| Method Signature | Return Type | Description |
|---|---|---|
load(String lang) |
void |
Loads translations for specified language |
get(String key) |
String |
Retrieves translation for key with fallback |
getCurrentLang() |
String |
Returns current language code |
isDevMode() |
boolean |
Returns true if in development mode |
setRuntimeSwitch(boolean enable) |
void |
Sets runtime switching flag |
isRuntimeSwitch() |
boolean |
Returns runtime switching flag state |
Loads a language file and populates the translations map.
-
Parameters: *
lang- Language code (e.g., "en_us", "zh_cn") -
Behavior: * In dev mode: reads from
lang/{lang}.jsonfile * In production: reads from JAR resourcelang/{lang}.json* UpdatescurrentLangon success * Setstranslationsto empty map on error - Thread Safety: Not thread-safe; typically called during initialization or from UI thread
Retrieves a localized string for the given key.
-
Parameters: *
key- Translation key (e.g., "button.record") - Returns: * Localized string if found in current language * English translation if current language missing key * Key itself if no translation found
- Thread Safety: Thread-safe for reading (map is replaced atomically on load)
Checks if running in development environment.
-
Returns:
trueiflang/zh_cn.jsonfile exists,falseotherwise -
Usage: Primarily by
ConfigManagerfor language discovery - Initialized: During static initialization
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L1-L98
The Localizer uses static initialization to ensure translations are available before any UI components are created. The static block:
- Detects environment (dev vs production)
- Auto-detects system language via
Locale.getDefault() - Normalizes language codes (replacing
-with_) - Checks for language file existence
- Loads the appropriate language file
This ensures that even early initialization code can safely call Localizer.get().
The two-tier fallback (current language → English → key) ensures:
- Graceful degradation: Missing translations don't break UI
- Developer visibility: Untranslated keys appear as-is for easy identification
-
English baseline: English (
en_us) serves as complete reference
The dual-mode (dev/production) design allows:
- Easy development: Work with editable JSON files in IDE
- Proper packaging: Read from JAR resources in production
- Consistent API: Same method calls work in both modes
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L15-L33, src/io/github/samera2022/mouse_macros/Localizer.java L47-L65, src/io/github/samera2022/mouse_macros/Localizer.java L67-L93