A simple and lightweight registry library with flexible identifiers and namespaces for Java.
Localize your application cleanly — with typed labels, dynamic placeholders, and pluggable serializers.
Explore the docs »
·
Report Bug
·
Request Feature
Table of Contents
i18label4j is a modular Java library for managing localizable text labels with a clean, fluent API. It provides:
- Typed labels — distinguish between locale-aware i18n labels and immutable literal labels at compile time.
- Placeholder substitution — register static or dynamic
Mappingobjects on any label and apply them via configurableMappingRulestrategies (supports${key},{key},%key%,<key>, and more out of the box). - Pluggable serializers — convert labels to any target type (plain
String, AdventureComponent, etc.) by registering aLabelSerializer. - Multiple localization sources — load translations from a flat directory (
FileSource), a nested directory tree (DirSource), or implement your ownLocalizationSource. - Format support — JSON, YAML, TOML, and Java
.propertiesfiles are supported out of the box. - Translation caching — the
CommonLabelProvidercaches translations per locale with thread-safeConcurrentHashMapinternals and explicit cache eviction.
- SnakeYAML · toml4j · org.json
- Adventure API (optional serializer target)
- Java 21+
- Gradle 9+ (wrapper included)
Add the repository and dependency to your build.gradle.kts:
repositories {
maven("https://leycm.github.io/repository/")
}
dependencies {
// API only (compile against the interface)
compileOnly("de.leycm:i18label4j-api:1.0")
// Full implementation (includes CommonLabelProvider, FileSource, DirSource, etc.)
implementation("de.leycm:i18label4j-impl:1.0")
}Or with Maven (pom.xml):
<repository>
<id>leycm-repo</id>
<url>https://leycm.github.io/repository/</url>
</repository>
<dependency>
<groupId>de.leycm</groupId>
<artifactId>i18label4j-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.leycm</groupId>
<artifactId>i18label4j-impl</artifactId>
<version>1.0</version>
</dependency>// Load translations from a flat directory of JSON files:
// resources/lang/en.json, de.json, ...
LocalizationSource source = FileSource.json(
URI.create("resource://lang")
);
LabelProvider provider = CommonLabelProvider.builder()
.locale(Locale.ENGLISH) // default locale
.defaultMappingRule(MappingRule.DOLLAR_CURLY) // ${placeholder} syntax
.withSerializer(String.class, new MyStringSerializer())
.buildWarm(source, Locale.ENGLISH, Locale.GERMAN); // pre-load cache
// Register as singleton
Instanceable.register(provider, LabelProvider.class);// Translatable label — looks up "greeting" in the active locale
Label hello = Label.of("greeting");
System.out.println(hello.in(Locale.ENGLISH)); // -> "Hello!"
System.out.println(hello.in(Locale.GERMAN)); // -> "Hallo!"
// Literal label — always returns the fixed string
Label version = Label.literal("v1.0.0");
System.out.println(version.in(Locale.JAPANESE)); // -> "v1.0.0"Label message = Label.of("welcome.message")
.mapTo("name", () -> currentUser.getDisplayName())
.mapTo("count", itemCount);
// Translation: "Welcome, ${name}! You have ${count} items."
System.out.println(message.mapped(Locale.ENGLISH));
// -> "Welcome, Alice! You have 3 items."// Assuming an Adventure Component serializer is registered:
Component component = Label.of("server.motd").serialize(Component.class);resources/lang/
en.json -> {"greeting": "Hello!", "welcome.message": "Welcome, ${name}!"}
de.json -> {"greeting": "Hallo!", "welcome.message": "Willkommen, ${name}!"}
resources/lang/
en/
messages.json -> {"greeting": "Hello!"}
errors.json -> {"not_found": "Not found."}
de/
messages.json -> {"greeting": "Hallo!"}
Keys are prefixed with the filename stem: messages.greeting, errors.not_found.
| Constant | Example |
|---|---|
MappingRule.DOLLAR_CURLY |
${variable} |
MappingRule.CURLY |
{variable} |
MappingRule.DOUBLE_CURLY |
{{variable}} |
MappingRule.PERCENT |
%variable% |
MappingRule.TAG |
<variable> |
MappingRule.SHELL |
$variable |
MappingRule.MINI_MESSAGE |
<var:variable> |
| ... | ... |
i18label4j
├── i18-api/ # Public API — Label, LabelProvider, Mapping, MappingRule, LabelSerializer, LocalizationSource
└── i18-impl/ # Implementation — CommonLabelProvider, LiteralLabel, LocaleLabel,
# FileSource, DirSource, FileParser, FileUtils
The library is split into two modules so downstream projects can depend only on the API and swap implementations at runtime via Instanceable.register(...).
- Core
LabelAPI withLiteralLabelandLocaleLabel -
CommonLabelProviderwith thread-safe translation cache -
MappingRulewith 10+ built-in placeholder styles -
FileSourceandDirSourcewith JSON, YAML, TOML,.propertiessupport - Classpath (
resource://), filesystem (file://), and remote (http(s)://) URI schemes - Nested/hierarchical key support in
DirSource - Hot-reload support for translation files
- Maven / Gradle plugin for compile-time key validation
- Additional serializer modules (Adventure, MiniMessage)
See the open issues for the full list of proposed features and known bugs.
Contributions are what make open source such a great place to learn and build. Any contributions you make are greatly appreciated.
- Fork the project
- Create your feature branch (
git checkout -b feat/amazing-feature) - Commit your changes (
git commit -m 'feat: add some amazing Features') - Push to the branch (
git push origin feat/amazing-feature) - Open a Pull Request
Distributed under the GNU Lesser General Public License v3.0. See LICENSE.LGPL for more information.
Lennard — leycm@proton.me
Project Link: https://github.com/leycm/i18label4j
- Lombok — boilerplate-free Java
- SnakeYAML — YAML parsing
- toml4j — TOML parsing
- org.json — JSON parsing
- Adventure API — Minecraft text component library
- Best-README-Template — README structure inspiration
- Choose an Open Source License
- Shields.io