# Localization System > **Relevant source files** > * [src/io/github/samera2022/mouse_macros/Localizer.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java) > * [src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java) > * [src/io/github/samera2022/mouse_macros/util/FileUtil.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java) > * [src/lang/en_us.json](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/lang/en_us.json) > * [src/lang/zh_cn.json](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/lang/zh_cn.json) The **Localization System** provides internationalization (i18n) support for MouseMacros, enabling the application to display user-facing text in multiple languages. The system loads translations from JSON files, automatically detects the system language, supports runtime language switching, and provides a fallback mechanism to ensure all text keys are resolved. For information about configuring language settings and dark mode preferences, see [Configuration System](Configuration-and-Persistence). For details on how UI components apply translations, see [User Interface Components](User-Interface-Components). --- ## System Architecture The localization system is centered around the `Localizer` class, which manages translation files, handles language detection, and provides translation lookup with fallback logic. The system integrates with `ConfigManager` to persist user language preferences and supports both development and production deployment modes. ```mermaid flowchart TD LangFiles["lang/*.json files
en_us.json
zh_cn.json"] SystemLocale["java.util.Locale
System Language"] Localizer["Localizer class
- translations Map
- currentLang String
- isDevMode boolean"] LoadMethod["load(lang) method"] GetMethod["get(key) method"] Config["ConfigManager.Config
- lang field
- followSystemSettings"] GetLangs["getAvailableLangs() method"] MainFrame["MainFrame"] Dialogs["Settings/Macro/Hotkey Dialogs"] Labels["JLabel/JButton components"] SystemLocale --> Localizer LangFiles --> LoadMethod Config --> Localizer GetLangs --> LangFiles GetMethod --> MainFrame GetMethod --> Dialogs GetMethod --> Labels subgraph subGraph3 ["UI Components"] MainFrame Dialogs Labels end subgraph subGraph2 ["Configuration Integration"] Config GetLangs end subgraph subGraph1 ["Core Localization"] Localizer LoadMethod GetMethod LoadMethod --> Localizer Localizer --> GetMethod end subgraph subGraph0 ["Translation Sources"] LangFiles SystemLocale end ``` **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L1-L98](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L1-L98), [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L14-L121](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L14-L121) --- ## Translation Storage and Lookup The `Localizer` class maintains an in-memory `Map` named `translations` that holds all key-value pairs for the currently loaded language. This map is populated during language loading and queried during translation retrieval. ### Key Data Structures | Field | Type | Purpose | | --- | --- | --- | | `translations` | `Map` | Stores translation key-value pairs for current language | | `currentLang` | `String` | Identifier of currently loaded language (e.g., "en_us", "zh_cn") | | `runtimeSwitch` | `boolean` | Flag indicating if runtime language switching is enabled | | `isDevMode` | `boolean` | Determines if running in development environment | ### Key Methods | Method | Return Type | Purpose | | --- | --- | --- | | `load(String lang)` | `void` | Loads translation file for specified language into `translations` map | | `get(String key)` | `String` | Retrieves translation for key, with fallback to English if missing | | `getCurrentLang()` | `String` | Returns identifier of currently loaded language | | `isDevMode()` | `boolean` | Returns true if running from source directory (not JAR) | | `setRuntimeSwitch(boolean)` | `void` | Enables/disables runtime language switching capability | **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L10-L98](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L10-L98) --- ## Language File Discovery The system discovers available languages by scanning the `lang/` directory for JSON files. The `ConfigManager.getAvailableLangs()` method handles discovery differently based on deployment mode: ```mermaid flowchart TD Start["getAvailableLangs() called"] CheckMode["isDevMode()?"] ScanDir["Scan lang/ directory
FileUtil.listFileNames()"] ExtractDev["Extract filename
without .json extension"] GetURL["Get resource URL
lang/ directory"] CheckProtocol["Protocol == 'jar'?"] OpenJar["Open JAR file
JarFile class"] EnumEntries["Enumerate entries
filter 'lang/*.json'"] ExtractJar["Extract filename
without .json extension"] BuildArray["Build String[] array
of language identifiers"] Return["Return language list"] Start --> CheckMode CheckMode --> ScanDir CheckMode --> GetURL ExtractDev --> BuildArray ExtractJar --> BuildArray BuildArray --> Return subgraph subGraph1 ["Production Mode (JAR)"] GetURL CheckProtocol OpenJar EnumEntries ExtractJar GetURL --> CheckProtocol CheckProtocol --> OpenJar OpenJar --> EnumEntries EnumEntries --> ExtractJar end subgraph subGraph0 ["Development Mode"] ScanDir ExtractDev ScanDir --> ExtractDev end ``` **Sources:** [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L66-L120](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L66-L120) ### Development vs Production Mode Detection The system determines its deployment mode by checking for the existence of `lang/zh_cn.json` in the file system: ``` // From Localizer static initializer File devLangFile = new File("lang/zh_cn.json"); isDevMode = devLangFile.exists(); ``` * **Development Mode**: Language files loaded directly from `lang/` directory using `FileReader` * **Production Mode**: Language files loaded from JAR resources using `ClassLoader.getResourceAsStream()` **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L15-L18](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L15-L18) --- ## Translation Loading Flow When a language is loaded via `Localizer.load(String lang)`, the system reads the corresponding JSON file and deserializes it into the `translations` map: ```mermaid sequenceDiagram participant Caller participant Localizer participant File System / JAR participant Gson participant translations Map Caller->>Localizer: load("zh_cn") loop [Development Mode] Localizer->>File System / JAR: new FileReader("lang/zh_cn.json") File System / JAR-->>Localizer: FileReader instance Localizer->>File System / JAR: getResourceAsStream("lang/zh_cn.json") File System / JAR-->>Localizer: InputStream Localizer->>Localizer: new InputStreamReader(stream, UTF-8) Localizer->>Gson: fromJson(reader, Map.class) Gson-->>Localizer: Map Localizer->>translations Map: Replace entire map Localizer->>Localizer: currentLang = "zh_cn" File System / JAR-->>Localizer: Exception Localizer->>translations Map: Set to empty HashMap end Localizer-->>Caller: void (load complete) ``` **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L47-L65](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L47-L65) --- ## Translation Retrieval and Fallback Mechanism The `Localizer.get(String key)` method implements a two-tier fallback strategy to ensure all translation keys are resolved: ```mermaid flowchart TD Start["get(key) called"] LookupCurrent["Look up key in
translations map"] CheckFound["Value found?"] ReturnValue["Return translation value"] CheckLang["currentLang
== 'en_us'?"] LoadEnglish["Load lang/en_us.json"] LookupEnglish["Look up key in
English translations"] CheckEnFound["Value found
in English?"] ReturnEnglish["Return English value"] ReturnKey["Return key itself
(untranslated)"] Start --> LookupCurrent LookupCurrent --> CheckFound CheckFound --> ReturnValue CheckFound --> CheckLang CheckLang --> LoadEnglish CheckLang --> ReturnKey LoadEnglish --> LookupEnglish LookupEnglish --> CheckEnFound CheckEnFound --> ReturnEnglish CheckEnFound --> ReturnKey ``` This fallback mechanism ensures that: 1. If the key exists in the current language, use it 2. If not, and current language is not English, try loading English and check there 3. If still not found, return the key itself (acts as placeholder) **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L67-L93](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L67-L93) --- ## Automatic Language Detection On application startup, the `Localizer` static initializer automatically detects the system's default locale and loads the appropriate language file: ```mermaid flowchart TD GetLocale["java.util.Locale.getDefault()"] Normalize["Normalize locale string
replace '-' with '_'
toLowerCase()"] DetectLang["Starts with?"] SetLang["Set language identifier"] CheckExists["Check if lang file exists
(dev mode only)"] DefaultToEN["Default to 'en_us'"] LoadLang["load(currentLang)"] subgraph subGraph0 ["Static Initialization"] GetLocale Normalize DetectLang SetLang CheckExists DefaultToEN LoadLang GetLocale --> Normalize Normalize --> DetectLang DetectLang --> SetLang DetectLang --> SetLang SetLang --> CheckExists CheckExists --> DefaultToEN CheckExists --> LoadLang DefaultToEN --> LoadLang end ``` The detection logic: * Retrieves system locale via `java.util.Locale.getDefault().toString()` * Normalizes format: replaces hyphens with underscores, converts to lowercase * Maps locale prefixes to language identifiers: * `zh*` → `zh_cn` * `en*` → `en_us` * Falls back to `en_us` if language file doesn't exist (dev mode) or for unrecognized locales **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L20-L33](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L20-L33) --- ## Runtime Language Switching The system supports changing languages at runtime without restarting the application: ```mermaid sequenceDiagram participant User participant SettingsDialog participant Localizer participant ConfigManager participant UI Components User->>SettingsDialog: Select new language SettingsDialog->>ConfigManager: config.lang = newLang SettingsDialog->>ConfigManager: saveConfig() SettingsDialog->>Localizer: setRuntimeSwitch(true) SettingsDialog->>Localizer: load(newLang) note over Localizer: translations map updated SettingsDialog->>UI Components: Trigger UI refresh loop [For each UI component] UI Components->>Localizer: get(translationKey) Localizer-->>UI Components: Translated text UI Components->>UI Components: Update component text end SettingsDialog->>Localizer: setRuntimeSwitch(false) ``` The `runtimeSwitch` flag allows the system to track when a language change is in progress, enabling UI components to respond appropriately to the change. **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L35-L41](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L35-L41), [README.md L30-L31](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/README.md#L30-L31) --- ## Configuration System Integration The localization system integrates tightly with `ConfigManager` to persist user language preferences: ### Configuration Fields | Field in `Config` | Type | Default | Purpose | | --- | --- | --- | --- | | `followSystemSettings` | `boolean` | `true` | If true, sync language with OS locale; if false, use `lang` field | | `lang` | `String` | `"zh_cn"` | User-selected language identifier | ### Configuration Flow When `followSystemSettings` is enabled: 1. Application startup checks `ConfigManager.config.followSystemSettings` 2. If true, system language is detected and loaded automatically (overriding `config.lang`) 3. If false, `config.lang` value is loaded explicitly **Sources:** [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L27-L35](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L27-L35), [README.md L74-L75](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/README.md#L74-L75) --- ## Language File Format Language files are JSON documents stored in the `lang/` directory with the naming convention `{language_identifier}.json`: ### File Naming Convention | File | Language | Description | | --- | --- | --- | | `en_us.json` | English (US) | English translations, serves as fallback | | `zh_cn.json` | Simplified Chinese | Chinese translations | ### JSON Structure Each language file is a flat JSON object mapping translation keys to translated strings: ```json { "key1": "translated text 1", "key2": "translated text 2", "button.save": "Save", "dialog.title": "Settings" } ``` The system uses Gson to deserialize JSON into `Map`: ``` // From Localizer.load() translations = new Gson().fromJson(reader, Map.class); ``` **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L52-L58](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L52-L58), [README.md L30](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/README.md#L30-L30) --- ## Deployment Mode Handling The localization system adapts its file loading strategy based on whether the application is running from source code or a packaged JAR: ### Development Mode **Detection:** `new File("lang/zh_cn.json").exists()` returns `true` **File Loading:** ``` File file = new File("lang/" + lang + ".json"); translations = new Gson().fromJson(new FileReader(file), Map.class); ``` **Language Discovery:** ``` String[] files = FileUtil.listFileNames("lang"); // Process file names to extract language identifiers ``` ### Production Mode (JAR) **Detection:** `new File("lang/zh_cn.json").exists()` returns `false` **File Loading:** ``` InputStream in = Localizer.class.getClassLoader() .getResourceAsStream("lang/" + lang + ".json"); InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); translations = new Gson().fromJson(reader, Map.class); ``` **Language Discovery:** ``` URL dirURL = ConfigManager.class.getClassLoader().getResource("lang/"); JarFile jar = new JarFile(jarPath); Enumeration entries = jar.entries(); // Filter entries starting with "lang/" and ending with ".json" ``` **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L47-L65](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L47-L65), [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L67-L119](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L67-L119) --- ## Integration with UI Components All user-facing UI components retrieve display text through the `Localizer.get()` method. This ensures consistent internationalization across the entire application: ```mermaid flowchart TD MainFrame["MainFrame
buttons, labels, menus"] SettingsDialog["SettingsDialog
settings options"] MacroSettings["MacroSettingsDialog
macro configuration"] HotkeyDialog["HotkeyDialog
key binding interface"] AboutDialog["AboutDialog
author information"] Keys["Translation key strings
'button.record'
'dialog.settings.title'
'label.language'"] GetMethod["Localizer.get(key)"] TransMap["translations Map"] MainFrame --> GetMethod SettingsDialog --> GetMethod MacroSettings --> GetMethod HotkeyDialog --> GetMethod AboutDialog --> GetMethod GetMethod --> MainFrame GetMethod --> SettingsDialog GetMethod --> MacroSettings GetMethod --> HotkeyDialog GetMethod --> AboutDialog Keys --> MainFrame Keys --> SettingsDialog subgraph subGraph2 ["Localization Layer"] GetMethod TransMap GetMethod --> TransMap TransMap --> GetMethod end subgraph subGraph1 ["Translation Keys"] Keys end subgraph subGraph0 ["UI Components"] MainFrame SettingsDialog MacroSettings HotkeyDialog AboutDialog end ``` **Sources:** [README.md L30](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/README.md#L30-L30) Diagram 4 from high-level architecture --- ## Error Handling and Graceful Degradation The localization system implements several safety mechanisms to handle missing translations or file loading failures: ### Translation Key Fallback 1. **Primary Lookup**: Check `translations` map for the requested key 2. **English Fallback**: If not found and current language is not English, load `en_us.json` and check again 3. **Key Return**: If still not found, return the key itself (acts as visible placeholder for missing translations) ### File Loading Failure When a language file fails to load (corrupted JSON, missing file, etc.): ``` catch (Exception e) { translations = new HashMap<>(); } ``` The `translations` map is set to an empty `HashMap`, causing all `get()` calls to fall through to the English fallback or key return logic. ### Language Discovery Failure If `ConfigManager.getAvailableLangs()` encounters an error while scanning language files: ``` catch (Exception e) { e.printStackTrace(); } // Returns empty String[] array return langs.toArray(new String[0]); ``` **Sources:** [src/io/github/samera2022/mouse_macros/Localizer.java L62-L93](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java#L62-L93), [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L115-L117](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L115-L117) --- ## Summary The Localization System provides comprehensive internationalization support through: * **JSON-based translations** stored in `lang/*.json` files * **Automatic language detection** from system locale on startup * **Runtime language switching** without application restart * **Two-tier fallback mechanism** (current language → English → key itself) * **Dual-mode file loading** supporting both development and JAR deployment * **Tight integration** with ConfigManager for persistence and UI components for display * **Graceful degradation** when translations or files are missing The system's architecture ensures that all user-facing text can be translated, with robust fallback mechanisms to prevent display of raw translation keys or application crashes due to missing language resources.