Translations (i18n)
Dokumentace k překladovému systému v Hytale.
---
Přehled
Hytale používá I18nModule pro správu překladů. Překlady jsou uloženy v .lang souborech a podporují:
- Parametry (
{name},{count}) - Formátování (
{value, number},{text, upper}) - Pluralizaci (
{count, plural, one {...} other {...}}) - Fallback jazyky
---
Struktura Souborů
Umístění
Assets/
└── Server/
└── Languages/
├── fallback.lang # Mapování variant jazyků
├── en-US/ # Výchozí jazyk
│ ├── server.lang
│ ├── commands.lang
│ └── subfolder/
│ └── module.lang
├── cs-CZ/ # Čeština
│ ├── server.lang
│ └── ...
└── de-DE/ # Němčina
└── ...
Formát .lang Souborů
Komentáře začínají #
Jednoduchý překlad
server.welcome = Welcome to the server!S parametry
server.commands.spawn.teleported = Teleported to {x}, {y}, {z}Víceřádkový (escaped)
server.help.description = This is a long description \
that spans multiple linesS formátováním
server.stats.kills = You have {count, number, integer} kills
Příklad z Assets/Server/Languages/en-US/server.lang
=== assetEditor ===
assetEditor.messages.unknownItem = Unknown Item "{id}"
assetEditor.messages.failedToDecodeAsset = Failed to decode asset: {message}=== barter ===
barter.customUI.barterPage.restocksToday = Restocks today at {restockTime}
barter.customUI.barterPage.restocksInDays = Restocks in {days} {days, plural, one {day} other {days}}
barter.customUI.barterPage.noStock = OUT OF STOCK
barter.customUI.barterPage.inStock = Stock: {count}=== benchCategories ===
benchCategories.workbench.crafting = Crafting
benchCategories.workbench.survival = Survival
---
Fallback Systém
fallback.lang
Mapuje varianty jazyků na hlavní verze:
fallback.lang
en-GB = en-US
en-CA = en-US
en-AU = en-UScs-SK = cs-CZ
de-AT = de-DE
de-CH = de-DE
es-MX = es-ES
es-AR = es-ES
Fallback Řetězec
1. Hledá klíč v požadovaném jazyce (např. cs-CZ)
2. Pokud nenalezen, hledá ve fallback jazyce z fallback.lang
3. Pokud stále nenalezen, hledá v en-US (výchozí)
---
Parametry
Základní Parametry
// Definice v .lang
// server.greeting = Hello, {name}!Message msg = Message.translation("server.greeting")
.param("name", playerName);
Typy Parametrů
// String
.param("name", "Player")// Integer
.param("count", 42)
// Long
.param("id", 123456789L)
// Double/Float
.param("ratio", 3.14)
// Boolean
.param("enabled", true)
// Message (vnořená)
.param("item", Message.translation("items.sword.name"))
Z Dekompilovaného Kódu - ParamValue
// Podporované typy (Message.java - ParamValueCodec)
switch (bsonValue.getBsonType()) {
case DOUBLE -> DoubleParamValue
case STRING -> StringParamValue
case BOOLEAN -> BoolParamValue
case INT32 -> IntParamValue
case INT64 -> LongParamValue
}
---
Formátování
Číselné Formátování
V .lang souboru
server.stats.score = Score: {score, number}
server.stats.kills = Kills: {kills, number, integer}
server.stats.ratio = K/D: {ratio, number, decimal}
Textové Formátování
Uppercase
server.alert = ALERT: {message, upper}Lowercase
server.whisper = {sender, lower} whispers: {text}
Z MessageUtil.java
// Formátovací logika
formattedReplacement = switch (format) {
case "upper" -> {
if (replacement instanceof StringParamValue s) {
yield s.value.toUpperCase();
}
}
case "lower" -> {
if (replacement instanceof StringParamValue s) {
yield s.value.toLowerCase();
}
}
case "number" -> {
// decimal nebo integer
switch (options) {
case "integer" -> Integer.toString((int)d.value);
default -> Double.toString(d.value);
}
}
// ...
};
---
Pluralizace
Syntaxe
V .lang souboru
server.items.count = You have {count, plural, one {{count} item} other {{count} items}}
barter.customUI.barterPage.restocksInDays = Restocks in {days} {days, plural, one {day} other {days}}
Části
one {...} - singulár (count == 1)other {...} - plurál (count != 1){count} odkazuje na stejný parametrPříklad Použití
Message msg = Message.translation("server.items.count")
.param("count", itemCount);
// count=1: "You have 1 item"
// count=5: "You have 5 items"
Z MessageUtil.java - Plural Logika
case "plural" -> {
if (options != null) {
String oneText = null;
String otherText = null;
int oneIdx = options.indexOf("one {");
int otherIdx = options.indexOf("other {"); // Parse one {...}
if (oneIdx >= 0) {
int oneStart = oneIdx + "one {".length();
int oneEnd = findMatchingBrace(options, oneStart - 1);
if (oneEnd > oneStart) {
oneText = options.substring(oneStart, oneEnd);
}
}
// Parse other {...}
if (otherIdx >= 0) {
int otherStart = otherIdx + "other {".length();
int otherEnd = findMatchingBrace(options, otherStart - 1);
if (otherEnd > otherStart) {
otherText = options.substring(otherStart, otherEnd);
}
}
int value = Integer.parseInt(replacement.toString());
String selected;
if (value == 1 && oneText != null) {
selected = oneText;
} else if (otherText != null) {
selected = otherText;
} else if (oneText != null) {
selected = oneText;
} else {
selected = "";
}
yield formatText(selected, params, messageParams);
}
}
---
I18nModule API
Získání Instance
I18nModule i18n = I18nModule.get();
Získání Překladu
// Získání textu překladu
String text = I18nModule.get().getMessage("en-US", "server.welcome");// Všechny překlady pro jazyk
Map messages = I18nModule.get().getMessages("cs-CZ");
Odeslání Překladů Hráči
// Interně používáno při připojení hráče
I18nModule.get().sendTranslations(packetHandler, playerLanguage);
---
Automatické Překlady z Assets
I18nModule automaticky generuje překlady z některých assets:
BlockType Benches
// Z I18nModule.java
event.getLoadedAssets().values().forEach(item -> {
Bench bench = item.getBench();
if (bench != null) {
String id = item.getId();
if (bench instanceof CraftingBench craftingBench) {
for (CraftingBench.BenchCategory category : craftingBench.getCategories()) {
addedMessages.put(
"server.items." + id + ".bench.categories." + category.getId() + ".name",
category.getName()
);
}
}
}
});
FieldcraftCategory
event.getLoadedAssets().values().forEach(category -> {
if (category.getName() != null) {
addedMessages.put("fieldcraftCategories." + category.getId() + ".name", category.getName());
}
});
ResourceType
event.getLoadedAssets().values().forEach(resourceType -> {
if (resourceType.getName() != null) {
addedMessages.put("resourceTypes." + resourceType.getId() + ".name", resourceType.getName());
}
if (resourceType.getDescription() != null) {
addedMessages.put("resourceTypes." + resourceType.getId() + ".description", resourceType.getDescription());
}
});
---
Konvence Klíčů
Struktura
{namespace}.{category}.{subcategory}.{key}
Příklady
Server příkazy
server.commands.spawn.teleported
server.commands.errors.playerNotFound
server.commands.errors.invalidArgumentUI
server.customUI.respawnPointClaimed
server.customUI.portalDevice.timeLimitInterakce
server.interactions.sleep.sleepAtTheseHours
server.interactions.didNotMountFormátování
server.formatting.list.header
server.formatting.list.empty
server.formatting.list.itemSeparatorObecné
server.general.enabled
server.general.disabled
server.general.assetstore.reloadAssets
---
Hot Reload
I18nModule podporuje hot reload .lang souborů:
// Z I18nModule.java - AssetMonitorHandler
if (assetMonitor != null && !pack.isImmutable()) {
assetMonitor.monitorDirectoryFiles(languagesPath, new I18nAssetMonitorHandler(languagesPath));
}// Při změně souboru:
// 1. Načte změněné překlady
// 2. Odešle UpdateTranslations paket všem hráčům
// 3. Vyvolá MessagesUpdated event
---
MessagesUpdated Event
// Pro reakci na změny překladů
getEventRegistry().register(MessagesUpdated.class, event -> {
Map> changed = event.getChanged();
Map> removed = event.getRemoved(); // Reaguj na změny
});
---
Vytvoření Vlastních Překladů
1. Vytvoř .lang Soubor
YourPlugin/
└── assets/
└── Server/
└── Languages/
└── en-US/
└── yourplugin.lang
2. Definuj Překlady
yourplugin.lang
yourplugin.commands.hello.success = Hello, {name}!
yourplugin.commands.hello.error = Player {name} not found
yourplugin.messages.welcome = Welcome to our server!
yourplugin.messages.goodbye = Goodbye, {name}. See you soon!
3. Použij v Kódu
// Jednoduché
player.sendMessage(Message.translation("yourplugin.messages.welcome"));// S parametry
player.sendMessage(
Message.translation("yourplugin.commands.hello.success")
.param("name", playerName)
);
---
Escape Sekvence
Nový řádek
server.multiline = First line\nSecond lineTab
server.tabbed = Column1\tColumn2Escaped braces
server.literal = Use {{param}} for parametersPokračování na další řádek
server.long = This is a very long message \
that continues on the next line
---
Shrnutí
| Funkce | Syntaxe |
|--------|---------|
| Parametr | {name} |
| Číslo | {count, number} |
| Integer | {value, number, integer} |
| Uppercase | {text, upper} |
| Lowercase | {text, lower} |
| Plurál | {count, plural, one {...} other {...}} |
| Escaped brace | {{ nebo }} |
| Soubor | Účel |
|--------|------|
| *.lang | Překlady |
| fallback.lang | Mapování variant jazyků |
| API | Použití |
|-----|---------|
| Message.translation(key) | Vytvoření zprávy s překladem |
| .param(key, value) | Přidání parametru |
| I18nModule.get().getMessage(lang, key) | Přímé získání textu |