-
Notifications
You must be signed in to change notification settings - Fork 0
Language Files and Translation Keys
Relevant source files
This document describes the language file format, structure, naming conventions, and discovery mechanisms used by the MouseMacros localization system. Language files are JSON-formatted translation dictionaries that enable multi-language support in the application.
For information about the Localizer class that loads and uses these files, see Localizer. For overall configuration system architecture, see Configuration System.
Language files use JSON format with UTF-8 encoding. Each file consists of a flat key-value map where keys are translation identifiers and values are localized strings.
{
"key.identifier": "Translated String",
"another.key": "Another Translation",
"button.start": "Start Recording"
}The file format is loaded using Google's Gson library, which deserializes the JSON into a Map<String, String> structure stored in the Localizer class.
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L52, src/io/github/samera2022/mouse_macros/Localizer.java L58
Language files are stored in the lang/ directory relative to the application root. The location differs based on execution context:
| Context | Location |
|---|---|
| Development mode |
lang/ directory in project root |
| JAR execution |
lang/ directory inside JAR resources |
Language files follow the pattern: <language_code>.json
| Language | File Name | Example |
|---|---|---|
| Chinese (Simplified) | zh_cn.json |
Default language in config |
| English (US) | en_us.json |
Fallback language |
| Other languages | <code>.json |
Format: <lang>_<region>.json
|
Language codes use lowercase with underscore separators (e.g., zh_cn, en_us).
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L17-L18, src/io/github/samera2022/mouse_macros/Localizer.java L23-L24, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L29
The application discovers available language files at runtime using ConfigManager.getAvailableLangs(). The discovery mechanism differs based on execution context.
flowchart TD
Start["ConfigManager.getAvailableLangs()"]
CheckDevMode["Localizer.isDevMode()"]
DevPath["FileUtil.listFileNames('lang')"]
DevFilter["Filter files ending with .json<br>Strip .json extension"]
JarCheck["dirURL.getProtocol()"]
JarPath["Read JAR file entries<br>Filter 'lang/*.json'"]
FilePath["Read filesystem directory<br>Filter *.json files"]
Return["Return String[] of language codes"]
Start --> CheckDevMode
CheckDevMode --> DevPath
CheckDevMode --> JarCheck
DevPath --> DevFilter
DevFilter --> Return
JarCheck --> JarPath
JarCheck --> FilePath
JarPath --> Return
FilePath --> Return
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L67-L120
In development mode (when lang/zh_cn.json exists in the filesystem), the discovery process:
- Calls
FileUtil.listFileNames("lang")to list all files in thelang/directory - Filters files for
.jsonextension - Strips the
.jsonextension to get language codes - Returns array of language codes
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L69-L79, src/io/github/samera2022/mouse_macros/Localizer.java L16-L18
In JAR execution mode, the discovery process uses two approaches:
When running from a JAR file:
- Gets URL to
lang/resource directory viaConfigManager.class.getClassLoader().getResource("lang/") - Extracts JAR file path from the URL
- Opens the JAR file using
java.util.jar.JarFile - Enumerates all JAR entries
- Filters entries matching pattern
lang/*.json - Strips
lang/prefix and.jsonsuffix to extract language codes
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L81-L101
When running from extracted class files:
- Converts URL to filesystem path using
dirURL.toURI() - Lists files in directory with
.jsonextension - Strips
.jsonextension to get language codes
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L102-L114
The Localizer.load(String lang) method loads language files into memory. The loading mechanism varies based on execution context.
sequenceDiagram
participant Application Code
participant Localizer
participant File System
participant JAR Resources
participant Gson Parser
Application Code->>Localizer: load("zh_cn")
loop [isDevMode == true]
Localizer->>File System: new File("lang/zh_cn.json")
File System-->>Localizer: File object
Localizer->>Gson Parser: fromJson(FileReader)
Gson Parser-->>Localizer: Map<String, String>
Localizer->>JAR Resources: getResourceAsStream("lang/zh_cn.json")
JAR Resources-->>Localizer: InputStream
Localizer->>Gson Parser: fromJson(InputStreamReader)
Gson Parser-->>Localizer: Map<String, String>
end
Localizer->>Localizer: translations = parsedMap
Localizer->>Localizer: currentLang = "zh_cn"
Localizer-->>Application Code: Load complete
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L47-L65
In development mode:
- Constructs
Fileobject pointing tolang/<lang>.json - Creates
FileReaderfor the file - Uses Gson to parse JSON into
Map<String, String> - Stores result in static
translationsfield
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L49-L52
In JAR mode:
- Constructs resource path:
lang/<lang>.json - Opens
InputStreamviaLocalizer.class.getClassLoader().getResourceAsStream(path) - Wraps stream in
InputStreamReaderwithStandardCharsets.UTF_8 - Uses Gson to parse JSON into
Map<String, String> - Stores result in static
translationsfield
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L54-L59
The Localizer.get(String key) method retrieves translated strings with a fallback mechanism.
flowchart TD
Start["Localizer.get(key)"]
CheckCurrent["Key exists in<br>current language?"]
ReturnCurrent["Return translation from<br>current language"]
CheckFallback["Current language<br>is 'en_us'?"]
LoadEnglish["Load en_us.json"]
CheckEnglish["Key exists in<br>en_us.json?"]
ReturnEnglish["Return translation from<br>en_us.json"]
ReturnKey["Return key as-is<br>(untranslated)"]
Start --> CheckCurrent
CheckCurrent --> ReturnCurrent
CheckCurrent --> CheckFallback
CheckFallback --> ReturnKey
CheckFallback --> LoadEnglish
LoadEnglish --> CheckEnglish
CheckEnglish --> ReturnEnglish
CheckEnglish --> ReturnKey
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L67-L93
The localization system implements a three-tier fallback mechanism:
| Priority | Source | Condition |
|---|---|---|
| 1 | Current language file | Key exists in loaded translations map |
| 2 | en_us.json |
Key missing and current language is not en_us
|
| 3 | Key itself | Key missing in both current language and English |
This ensures that missing translations gracefully degrade to English or display the translation key itself.
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L68-L92
The Localizer class automatically detects the system language at initialization.
- Calls
java.util.Locale.getDefault().toString().toLowerCase() - Replaces hyphens with underscores (e.g.,
zh-CN→zh_cn) - Normalizes common locales: * Languages starting with
zh→zh_cn* Languages starting withen→en_us - Verifies language file exists (in dev mode)
- Falls back to
en_usif file not found - Loads the determined language file
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L20-L32
The language selection is persisted in config.cfg and can be overridden by user settings.
The selected language is stored in ConfigManager.Config.lang field:
| Field | Type | Default | Description |
|---|---|---|---|
lang |
String |
"zh_cn" |
Current language code |
followSystemSettings |
boolean |
true |
Whether to sync with OS language |
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L28-L29
flowchart TD
Start["Application Start"]
CheckFollow["config.followSystemSettings"]
UseSystem["Use system locale<br>java.util.Locale.getDefault()"]
UseConfig["Use config.lang value"]
Load["Localizer.load(lang)"]
Start --> CheckFollow
CheckFollow --> UseSystem
CheckFollow --> UseConfig
UseSystem --> Load
UseConfig --> Load
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L28, src/io/github/samera2022/mouse_macros/Localizer.java L20-L32
While the codebase does not enforce strict naming conventions, examining the code reveals common patterns for translation keys:
| Pattern | Usage Example | Purpose |
|---|---|---|
window.* |
window.title |
Window titles |
button.* |
button.start, button.stop
|
Button labels |
menu.* |
menu.file, menu.settings
|
Menu items |
message.* |
message.success, message.error
|
Status messages |
dialog.* |
dialog.about, dialog.hotkey
|
Dialog-specific strings |
label.* |
label.language, label.theme
|
Form labels |
Translation keys are retrieved throughout the UI code using Localizer.get(String key). The method returns the localized string or the key itself if no translation exists.
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L67-L93
The localization system detects whether it's running in development mode to determine file loading strategy.
// From Localizer static initializer
File devLangFile = new File("lang/zh_cn.json");
isDevMode = devLangFile.exists();
If lang/zh_cn.json exists as a filesystem file (not in JAR), the application assumes development mode and loads files from the filesystem rather than JAR resources.
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L16-L18
| Aspect | Development Mode | JAR Mode |
|---|---|---|
| Detection |
lang/zh_cn.json file exists |
lang/zh_cn.json file doesn't exist |
| File Loading |
File + FileReader
|
getResourceAsStream() |
| Directory Listing | FileUtil.listFileNames() |
JAR entry enumeration |
| File System Access | Direct filesystem | Via ClassLoader resources |
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L16-L18, src/io/github/samera2022/mouse_macros/Localizer.java L49-L59, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L69-L114
All language files must use UTF-8 encoding without BOM (Byte Order Mark). This is enforced during both loading and discovery operations.
- File Encoding: UTF-8
-
Reader Encoding:
StandardCharsets.UTF_8specified explicitly -
JAR Resource Encoding: UTF-8 via
InputStreamReader(in, StandardCharsets.UTF_8)
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L57, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L88
The language file system implements graceful error handling to ensure application stability even when language files are missing or malformed.
| Scenario | Handling | Result |
|---|---|---|
| Language file not found | Catch FileNotFoundException
|
Empty translations map |
| Malformed JSON | Catch Exception in load()
|
Empty translations map |
| Missing translation key | Check for null in get()
|
Fall back to English, then key |
| JAR enumeration failure | Catch Exception in getAvailableLangs()
|
Return empty array |
flowchart TD
LoadAttempt["Localizer.load(lang)"]
TryLoad["Try to load<br>language file"]
Success["Parse JSON into Map<br>Store in translations"]
Failure["Exception caught"]
EmptyMap["translations = new HashMap()"]
Continue["Application continues<br>with empty translations"]
LoadAttempt --> TryLoad
TryLoad --> Success
TryLoad --> Failure
Failure --> EmptyMap
EmptyMap --> Continue
Sources: src/io/github/samera2022/mouse_macros/Localizer.java L62-L64, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L115-L117
Language files in MouseMacros follow these key characteristics:
- Format: UTF-8 encoded JSON files with flat key-value structure
-
Location:
lang/directory in project root or JAR resources -
Naming:
<language_code>.json(e.g.,zh_cn.json,en_us.json) -
Discovery: Automatic enumeration via
ConfigManager.getAvailableLangs() - Loading: Dual-mode support for development filesystem and JAR resources
- Fallback: Three-tier fallback (current language → English → key itself)
- Error Handling: Graceful degradation with empty translation maps
The system is designed to work seamlessly in both development and production environments, with robust error handling ensuring the application remains functional even when language files are missing or malformed.