-
Notifications
You must be signed in to change notification settings - Fork 1
CatFrame Localization System
CatFrame ships a namespace-based JSON localization system — similar to how higher Minecraft versions handle translations. Instead of .lang files, you write JSON key-value maps under assets/{modid}/lang/, and the system auto-detects the current Minecraft language at runtime.
-
During preInit: each mod registers its language domain with a
modidand a classpath-relative path to its JSON lang directory. -
LocalizationManager.load()scans all registered domains, loadsen_us.jsonas fallback, then loads the current language's JSON. Missing keys fall back toen_usautomatically. -
At runtime: you request translations via
Textwrappers or by callingLocalizationManager.Translation.translate()directly.
JSON files are plain key-value maps — no domain: prefix needed in the keys. The system prepends the domain automatically when loading.
File naming is always lowercase:
en_us.json,zh_cn.json,ja_jp.json, etc. — matching high-version Minecraft convention.
assets/catframe/lang/en_us.json:
{
"menu.paused": "Paused",
"item.example.count": "%d / %d items",
"ui.layouts.default": "Default"
}assets/catframe/lang/zh_cn.json:
{
"menu.paused": "暂停",
"item.example.count": "%d / %d 个物品",
"ui.layouts.default": "默认"
}If the player's language is zh_cn, keys resolve to Chinese. Otherwise they fall back to en_us. If a key is missing in both, the raw domain:key string is returned instead.
Package: decok.dfcdvadstf.catframe.langguage.LanguageRegister
Register your mod's language domain before calling LocalizationManager.load():
import decok.dfcdvadstf.catframe.langguage.LanguageRegister;
import decok.dfcdvadstf.catframe.langguage.LocalizationManager;
// In preInit, client side:
LanguageRegister.domain(Tags.MODID, "assets/catframe/lang");
LanguageRegister.domain("mymod", "assets/mymod/lang");
LocalizationManager.Loader.load();| Method | Description |
|---|---|
domain(String modid, String basePath) |
Register a language domain. basePath is the classpath-relative directory containing en_us.json, zh_cn.json, etc. |
getDomains() |
Returns an unmodifiable Map<String, String> of all registered domains. |
Package: decok.dfcdvadstf.catframe.langguage.LocalizationManager
The singleton that resolves translation keys. Keys are always scoped by domain — internally stored as domain:key.
Two forms, just like ResourceLocation:
// Colon format
String text = LocalizationManager.Translation.translate("catframe:menu.paused");
String text2 = LocalizationManager.Translation.translate("catframe:item.count", 5, 10);
// Explicit domain + key
String text3 = LocalizationManager.Translation.translate("catframe", "menu.paused");
String text4 = LocalizationManager.Translation.translate("catframe", "item.count", 5, 10);Format arguments use String.format semantics (%d, %s, etc.). If formatting fails or the key is missing, the raw key is returned with a log warning.
LocalizationManager.DescriptionIds.make() generates translation keys in high-version Minecraft's type.namespace.name format — the same pattern used by vanilla Block.getName() and Item.getName():
// generate description IDs
String blockKey = LocalizationManager.DescriptionIds.make("block", "minecraft", "stone");
// → "block.minecraft.stone"
String itemKey = LocalizationManager.DescriptionIds.make("item", "catframe", "meat_raw");
// → "item.catframe.meat_raw"Then put the corresponding entry in your lang JSON:
{
"stone": "Stone",
"meat_raw": "Raw Meat"
}The stone key is loaded under domain minecraft, and meat_raw under catframe — no prefix needed in the JSON.
For a familiar 1.7.10 API, use translateToLocal() — it accepts both domain:key and description IDs:
// domain:key — pulled apart at the colon
String s1 = LocalizationManager.Translation.translateToLocal("catframe:menu.paused");
// description ID — parsed as type.namespace.key
String s2 = LocalizationManager.Translation.translateToLocal("block.minecraft.stone");
String s3 = LocalizationManager.Translation.translateToLocal("item.catframe.meat_raw");
// explicit domain + key (no guessing required)
String s4 = LocalizationManager.Translation.translateToLocal("catframe", "menu.paused");translateToLocal("block.minecraft.stone") walks the dots: first dot is the type prefix (block), second dot marks the namespace boundary (minecraft), everything after is the key (stone). So it resolves to translate("minecraft", "stone").
Every time a Text is created with setTranslatable() or Text.translatable(), the key is automatically marked as "enabled". You can query this: check which keys your UI actually uses, verify coverage, or log untranslated keys at dev time:
LocalizationManager.KeyTracking.mark("mymod", "some.key"); // (called automatically by Text)
boolean used = LocalizationManager.KeyTracking.isEnabled("mymod", "some.key");
Set<String> allUsed = LocalizationManager.KeyTracking.all();| Method | Description |
|---|---|
Loader.load() |
Load translations from all registered domains (client-side only). |
Loader.reload() |
Reload after runtime language switch. |
Translation.translate(String resourceKey, Object... args) |
Resolve domain:key → translated string. |
Translation.translate(String domain, String key, Object... args) |
Resolve explicit domain + key. |
Translation.translateToLocal(String key, Object... args) |
StatCollector-style: accepts domain:key or type.namespace.name description IDs. |
Translation.translateToLocal(String domain, String key, Object... args) |
StatCollector-style with explicit domain + key. |
DescriptionIds.make(String type, String domain, String name) |
Generate a description ID like "block.minecraft.stone". |
Translation.hasKey(String resourceKey) |
Check if a domain:key exists. |
Translation.hasKey(String domain, String key) |
Check if a domain+key pair exists. |
KeyTracking.mark(String domain, String key) |
Mark a key as used (called automatically by Text). |
KeyTracking.isEnabled(String domain, String key) |
Check if a key has been marked. |
KeyTracking.all() |
Get all enabled domain:key strings. |
Tip: Call
KeyTracking.all()at the end of dev and diff it against your JSON files to find missing translations. Keys present in JSON but never marked as enabled are unused dead entries.
Package: decok.dfcdvadstf.catframe.ui.Text
A text wrapper that supports both literal strings and namespace-based translatable keys — similar to modern Minecraft's Component system. Use it anywhere you'd normally pass a String for display text.
// Literal — always renders the same text
Text label = Text.literal("Hello World");
// Translatable — resolved at render time via LocalizationManager
private static final Text PAUSED_TEXT = Text.translatable("catframe:menu.paused");Like ResourceLocation, you can specify keys in two ways:
// Colon format: "domain:key"
Text title = Text.translatable("catframe:ui.layouts.default");
// Explicit domain + key
Text title2 = Text.translatable("catframe", "ui.layouts.default");
// With format arguments
Text count = Text.translatable("catframe:item.example.count", 5, 10);
Text count2 = Text.translatable("catframe", "item.example.count", 5, 10);Text.translatable() automatically calls LocalizationManager.KeyTracking.mark() — the key is tracked as "enabled" the moment you create it.
Already have a Text instance and want to make it translatable later?
Text dyn = new Text();
dyn.setTranslatable("catframe:menu.paused"); // colon format
dyn.setTranslatable("catframe", "menu.paused"); // domain+key format
dyn.setTranslatable("catframe:item.count", 5, 10); // with argsCall getString() when you need the final display text. The Text instance is safe to hold as a static final field — translation lookup happens lazily at render time:
private static final Text PAUSED_TEXT = Text.translatable("catframe:menu.paused");
// In drawScreen():
this.drawString(fontRendererObj, PAUSED_TEXT.getString(), x, y, 0xFFFFFF);
// toString() delegates to getString() for convenience
this.drawString(fontRendererObj, PAUSED_TEXT.toString(), x, y, 0xFFFFFF);Constructors:
| Method | Description |
|---|---|
new Text() |
Create an empty Text |
new Text(String literal) |
Create a literal Text |
Static factories:
| Method | Returns | Description |
|---|---|---|
Text.literal(String text) |
Text |
Create a non-translatable literal text. |
Text.literal(String text, Style style) |
Text |
Create a literal text with style. |
Text.translatable(String resourceKey, Object... args) |
Text |
Create translatable text from domain:key. |
Text.translatable(Style style, String resourceKey, Object... args) |
Text |
Create translatable text with style. |
Text.translatable(String domain, String key, Object... args) |
Text |
Create translatable text from explicit domain + key. |
Text.translatable(String domain, String key, Style style, Object... args) |
Text |
Create translatable text with explicit domain, key, and style. |
Text.translatableString(String resourceKey, Object... args) |
String |
Translate domain:key directly to String. |
Text.translatableString(String domain, String key, Object... args) |
String |
Translate domain+key directly to String. |
Text.literalString(String text) |
String |
Identity helper (returns text as-is). |
Instance methods:
| Method | Returns | Description |
|---|---|---|
setTranslatable(String resourceKey, Object... args) |
void |
Mark this instance as translatable (domain:key). |
setTranslatable(String domain, String key, Object... args) |
void |
Mark this instance as translatable (explicit). |
setLiteral(String text) |
void |
Revert to literal text. |
getString() |
String |
Resolve to the final display string. |
getRaw() |
String |
The raw domain:key (if translatable) or literal text. |
getDomain() |
String |
Domain portion (empty for literals). |
getKey() |
String |
Key portion or literal text. |
getArgs() |
Object[] |
Format arguments. |
isTranslatable() |
boolean |
Whether this Text uses translation. |
getStyle() |
Style |
Get the associated style (or null). |
setStyle(Style style) |
void |
Set the style for this Text. |
withStyle(Style style) |
Text |
Return new Text with specified style. |
withStyleApplied(Style style) |
Text |
Return new Text with style merged on top of existing. |
Note:
Textoverridesequals(),hashCode(), andtoString(). TwoTextinstances are equal if they share the same domain, key, translatable flag, args, and style — so you can use them inMaplookups orSetdeduplication.
Package: decok.dfcdvadstf.catframe.ui.LoadingDotsText
A tiny utility that produces an animated three-frame loading indicator — "o O o" → "o o O" → "O o o" — cycling every 200 ms.
// In drawScreen(), pass the current time in milliseconds:
String dots = LoadingDotsText.get(System.currentTimeMillis());
this.drawString(fontRendererObj, "Loading" + dots, x, y, 0xFFFFFF);| Method | Returns | Description |
|---|---|---|
get(long timeMs) |
String |
Get the current animation frame. Cycles every 200 ms. |