Outerstellar Framework
Headless, framework-agnostic Kotlin utilities for modern JVM development — hot-reloading i18n, JSON-based theming, a plugin engine, and build-time translation validation.
Outerstellar Framework
Headless Kotlin utility modules for i18n, theming, and plugin discovery. No web framework or UI toolkit dependencies — each module is a standalone artifact you can add to any JVM project.
Published to GitHub Packages at maven.pkg.github.com/rygel/outerstellar-framework.
What modules are included?
outerstellar-i18n
Translation service backed by ResourceBundle with parameter substitution and locale fallback. Bundles are cached per locale and can be reloaded at runtime without a restart.
val i18n = I18nService.create("messages") // loads messages.properties
// Simple lookup
val greeting = i18n.translate("welcome.title")
// → "Welcome to Outerstellar"
// Parameter substitution ({0}, {1} style)
val personalized = i18n.translate("welcome.message", userName)
// → "Hello, Alex! Welcome back."
// Locale-specific
val german = i18n.translate("welcome.title", Locale.GERMAN)
// → "Willkommen bei Outerstellar"
Thread-safe. Zero dependencies beyond the Kotlin stdlib.
outerstellar-theme
Reads color definitions from JSON files and produces CSS custom properties or raw color values for programmatic use. Supports shade generation (lighter/darker variants from a base color).
val theme = ThemeService.create().loadFromClasspath("themes/dark.json")
// CSS output
val css = theme.toCssForSelector(":root")
// → ":root { --color-primary: #f97316; --color-bg: #0f172a; ... }"
// Programmatic access
val primary = theme.color("primary")
val lighter = theme.shade("primary", 0.2) // 20% lighter
Theme file:
{
"name": "dark",
"colors": {
"primary": "#f97316",
"background": "#0f172a",
"surface": "#1e293b",
"text": "#f1f5f9",
"muted": "#64748b"
}
}
Themes can be loaded from the classpath, file system, or built programmatically. Hot reload is supported in development.
outerstellar-plugin
ServiceLoader-based plugin discovery with lifecycle hooks and caching.
interface AnalyticsPlugin {
fun trackEvent(name: String, properties: Map<String, Any>)
}
// Discovers all implementations on the classpath
val plugins = PluginEngine.discover<AnalyticsPlugin>()
plugins.forEach { it.trackEvent("page_view", mapOf("path" to "/")) }
Plugins are found automatically when their JAR is on the classpath. The engine caches discovery results and provides onLoad/onUnload lifecycle hooks.
How does build-time translation validation work?
The framework includes tools to detect translation problems at build time rather than in production.
Checks
| Check | What it catches |
|---|---|
| Missing translations | Keys in the default bundle that are absent in a locale |
| Unused keys | Keys defined in bundles but never referenced in source |
| Undefined references | Keys used in source code but missing from all bundles |
| Parameter mismatches | Placeholder differences ({0}, {1}) between locales |
Maven Plugin
Runs during mvn verify and fails the build on issues:
<plugin>
<groupId>io.github.rygel</groupId>
<artifactId>outerstellar-i18n-validator-maven-plugin</artifactId>
<version>1.0.14</version>
<executions>
<execution>
<goals><goal>validate</goal></goals>
<configuration>
<bundleName>messages</bundleName>
<sourceDirectories>
<directory>src/main/kotlin</directory>
</sourceDirectories>
<locales>
<locale>en</locale>
<locale>de</locale>
<locale>fr</locale>
</locales>
</configuration>
</execution>
</executions>
</plugin>
CLI
For use outside Maven or in CI pipelines:
java -jar outerstellar-i18n-validator-cli.jar \
--bundle messages \
--source src/main/kotlin \
--locales en,de,fr
GUI
A Swing application for browsing translation files and seeing coverage at a glance. Intended for translators and content editors.
How do I add the framework to my project?
Add the GitHub Packages repository:
<repositories>
<repository>
<id>github-rygel</id>
<url>https://maven.pkg.github.com/rygel/outerstellar-framework</url>
</repository>
</repositories>
Then add modules as needed:
<dependency>
<groupId>io.github.rygel</groupId>
<artifactId>outerstellar-i18n</artifactId>
<version>1.0.14</version>
</dependency>
<dependency>
<groupId>io.github.rygel</groupId>
<artifactId>outerstellar-theme</artifactId>
<version>1.0.14</version>
</dependency>
<dependency>
<groupId>io.github.rygel</groupId>
<artifactId>outerstellar-plugin</artifactId>
<version>1.0.14</version>
</dependency>
Project Structure
outerstellar-framework/
├── outerstellar-i18n/ Translation service
├── outerstellar-theme/ Theming engine
├── outerstellar-plugin/ Plugin discovery
├── outerstellar-i18n-validator/ Validation core
├── outerstellar-i18n-validator-maven-plugin/ Maven plugin
├── outerstellar-i18n-validator-cli/ CLI
└── outerstellar-i18n-validator-gui/ Swing GUI
Requirements
- JDK 21+
- Kotlin 2.0+ (compiles against 2.0 for consumer compatibility; works with 2.3+)
FAQ
What is the Outerstellar Framework?
A set of headless, framework-agnostic Kotlin utility modules for internationalisation (i18n), theming, and plugin discovery. Each module is a standalone Maven artifact with zero web framework dependencies — add only what you need.
What does the i18n module do?
It provides a ResourceBundle-backed translation service with parameter substitution ({0}, {1} style), locale fallback, and hot reload at runtime. A Maven plugin and CLI tool validate translations at build time, catching missing keys, unused keys, undefined references, and parameter mismatches between locales.
Does the theming module require a specific CSS framework?
No. It reads color definitions from JSON files and outputs CSS custom properties (for web) or raw color values (for desktop or programmatic use). Shade generation produces lighter/darker variants from a base color. It works with Tailwind CSS, plain CSS, or no CSS at all.
What is the plugin engine?
A ServiceLoader-based plugin discovery system with lifecycle hooks (onLoad/onUnload) and caching. Plugins are discovered automatically when their JAR is on the classpath. No annotation processing or code generation required.
What Java version is required?
JDK 21 or later. The framework compiles against Kotlin 2.0 for consumer compatibility and works with Kotlin 2.3+.