Skip to content

Commit

Permalink
Tweak api a little, release a first beta
Browse files Browse the repository at this point in the history
  • Loading branch information
Patbox committed Feb 28, 2023
1 parent 8485744 commit c904fff
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 65 deletions.
54 changes: 52 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,53 @@
# Server-Translations
# Server Translation API
It's a library for handling translations server side. It supports
per player language, by using one provided by client on join/language change.

Translators can translate the components here: https://hosted.weblate.org/projects/nucleoid/
## Adding as dependency:
Add it to your dependencies like this:

```groovy
repositories {
maven { url 'https://maven.nucleoid.xyz' }
}
dependencies {
modImplementation include("xyz.nucleoid:server-translations-api:[TAG]")
}
```

For `[TAG]`/translations api version I recommend you checking [this maven](https://maven.nucleoid.xyz/xyz/nucleoid/server-translations-api/).

### For versions before 1.19.4:
```groovy
repositories {
maven { url 'https://maven.nucleoid.xyz' }
}
dependencies {
modImplementation include("fr.catcore:server-translations-api:[TAG]")
}
```

For `[TAG]`/translations api version I recommend you checking [this maven](https://maven.nucleoid.xyz/fr/catcore/server-translations-api/).

## Usage
To use i, you just need to use vanilla `Text.translation(...)` with key specified by you in your code.

Then you just need to create `data/modid/lang` folder in your mod's resources.
Then you can create there `en_us.json` for default translation and similar files for other languages (same format as vanilla translations).

Example valid language file looks like this:
```
{
"block.honeytech.pipe": "Pipe",
"block.honeytech.item_extractor": "Item Extractor",
"block.honeytech.trashcan": "Trash Can",
"block.honeytech.cable": "Cable",
"item.honeytech.diamond_dust": "Diamond Dust",
"item.honeytech.raw_aluminium": "Raw Aluminium Ore",
"item.honeytech.aluminium_ingot": "Aluminium Ingot",
"item.honeytech.copper_wire": "Copper Wire",
"item.honeytech.motor": "Motor",
"gui.honeytech.show_recipes": "Show Recipes"
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@ public static Text text(Text text, LocalizationTarget target) {
}

public static Text text(Text text, ServerLanguage language) {
return text(text, true, language);
return text(text, language, true);
}

public static Text text(Text text, boolean full, ServerPlayerEntity target) {
return text(text, full, (LocalizationTarget) target);
public static Text text(Text text, ServerPlayerEntity target, boolean deep) {
return text(text, (LocalizationTarget) target, deep);
}

public static Text text(Text text, boolean full, LocalizationTarget target) {
return text(text, full, target.getLanguage());
public static Text text(Text text, LocalizationTarget target, boolean deep) {
return text(text, target.getLanguage(), deep);
}

public static Text text(Text text, boolean full, ServerLanguage language) {
return LocalizableText.asLocalizedFor(text, language, full);
public static Text text(Text text, ServerLanguage language, boolean deep) {
return LocalizableText.asLocalizedFor(text, language, deep);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
import xyz.nucleoid.server.translations.impl.ServerTranslations;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;

public record ServerLanguage(ServerLanguageDefinition definition,
TranslationAccess serverTranslations) {


public static ServerLanguage getLanguage(@Nullable String code) {
return ServerTranslations.INSTANCE.getLanguage(code);
}

public static ServerLanguage getLanguage(ServerLanguageDefinition languageDefinition) {
return ServerTranslations.INSTANCE.getLanguage(languageDefinition.code());
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package xyz.nucleoid.server.translations.api.language;


import xyz.nucleoid.server.translations.impl.ServerTranslations;

public record ServerLanguageDefinition(String code, String region, String name,
boolean rightToLeft) implements Comparable<ServerLanguageDefinition> {
public static final String DEFAULT_CODE = "en_us";
public static final ServerLanguageDefinition DEFAULT = new ServerLanguageDefinition(DEFAULT_CODE, "US", "English", false);

public static Iterable<ServerLanguageDefinition> getAllLanguages() {
return ServerTranslations.INSTANCE.getAllLanguages();
}

@Override
public String toString() {
return String.format("%s (%s)", this.region, this.name);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,34 @@
package xyz.nucleoid.server.translations.impl;

import xyz.nucleoid.server.translations.api.LocalizationTarget;
import xyz.nucleoid.server.translations.api.language.ServerLanguage;
import xyz.nucleoid.server.translations.impl.nbt.StackNbtLocalizer;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableTextContent;

import java.util.List;


public interface LocalizableText extends Text {
boolean stapi$isLocalized();

void stapi$setLocalized(boolean value);

static Text asLocalizedFor(Text text, LocalizationTarget target) {
return asLocalizedFor(text, target.getLanguage());
}

static Text asLocalizedFor(Text text, ServerLanguage language) {
return asLocalizedFor(text, language, true);
}

static Text asLocalizedFor(Text text, ServerLanguage language, boolean localizeSiblings) {
if (isTranslatable(text) || (localizeSiblings && (isTranslatableAny(text.getSiblings())))) {
static Text asLocalizedFor(final Text text, final ServerLanguage language, final boolean translateDeeply) {
if (isTranslatable(text, translateDeeply)) {
var content = text.getContent();

if (content instanceof TranslatableTextContent translatableContent) {
var args = new Object[translatableContent.getArgs().length];

for (int i = 0; i < args.length; i++) {
var arg = translatableContent.getArgs()[i];
if (arg instanceof Text argText) {
args[i] = asLocalizedFor(argText, language);
} else {
args[i] = arg;
var args = translatableContent.getArgs();

if (translateDeeply) {
args = new Object[translatableContent.getArgs().length];
for (int i = 0; i < args.length; i++) {
var arg = translatableContent.getArgs()[i];
if (arg instanceof Text argText) {
args[i] = asLocalizedFor(argText, language, true);
} else {
args[i] = arg;
}
}
}

Expand All @@ -47,7 +39,7 @@ static Text asLocalizedFor(Text text, ServerLanguage language, boolean localizeS
}

var out = MutableText.of(content);
if (localizeSiblings) {
if (translateDeeply) {
for (var sibling : out.getSiblings()) {
out.append(asLocalizedFor(sibling, language, true));
}
Expand All @@ -58,17 +50,17 @@ static Text asLocalizedFor(Text text, ServerLanguage language, boolean localizeS
var style = text.getStyle();

if (style.getHoverEvent() != null) {
if (localizeSiblings && style.getHoverEvent().getAction() == HoverEvent.Action.SHOW_TEXT) {
style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, asLocalizedFor(style.getHoverEvent().getValue(HoverEvent.Action.SHOW_TEXT), language)));
if (translateDeeply && style.getHoverEvent().getAction() == HoverEvent.Action.SHOW_TEXT) {
style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, asLocalizedFor(style.getHoverEvent().getValue(HoverEvent.Action.SHOW_TEXT), language, true)));
} else if (style.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ITEM) {
var value = style.getHoverEvent().getValue(HoverEvent.Action.SHOW_ITEM);
var stack = value.asStack();
stack.setNbt(StackNbtLocalizer.localize(stack, stack.getNbt(), language));
style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ITEM, new HoverEvent.ItemStackContent(stack)));
} else if (localizeSiblings && style.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ENTITY) {
} else if (translateDeeply && style.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ENTITY) {
var value = style.getHoverEvent().getValue(HoverEvent.Action.SHOW_ENTITY);
if (value.name != null) {
style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ENTITY, new HoverEvent.EntityContent(value.entityType, value.uuid, asLocalizedFor(value.name, language))));
style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ENTITY, new HoverEvent.EntityContent(value.entityType, value.uuid, asLocalizedFor(value.name, language, true))));
}
}
}
Expand All @@ -80,16 +72,21 @@ static Text asLocalizedFor(Text text, ServerLanguage language, boolean localizeS
return text;
}

static boolean isTranslatable(Text text) {
return (text instanceof LocalizableText localizableText && !localizableText.stapi$isLocalized()) && (text.getContent() instanceof TranslatableTextContent || text.getStyle().getHoverEvent() != null);
}
static boolean isTranslatable(Text text, boolean deepLocalization) {
if (deepLocalization) {
for (var x : text.getSiblings()) {
if (isTranslatable(x, true)) {
return true;
}
}

private static boolean isTranslatableAny(List<Text> siblings) {
for (var x : siblings) {
if (isTranslatable(x)) {
if (text.getStyle().getHoverEvent() != null) {
return true;
}
}
return false;

return (text instanceof LocalizableText localizableText && !localizableText.stapi$isLocalized())
&& (text.getContent() instanceof TranslatableTextContent
|| (text.getStyle().getHoverEvent() != null && text.getStyle().getHoverEvent().getAction() == HoverEvent.Action.SHOW_ITEM));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ private int getTranslationKeyCount() {
public CompletableFuture<Void> reload(Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor) {
CompletableFuture<Multimap<String, Supplier<TranslationMap>>> future = CompletableFuture.supplyAsync(() -> {
this.reload();
return LanguageReader.collectTranslationSuppliers(manager);
return LanguageReader.collectDataPackTranslations(manager);
});

return future.thenCompose(synchronizer::whenPrepared)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static TranslationMap read(InputStream stream) {
return map;
}

public static TranslationMap loadVanillaTranslations() {
public static TranslationMap loadBuiltInTranslation() {
try (InputStream input = Language.class.getResourceAsStream("/assets/minecraft/lang/" + ServerLanguageDefinition.DEFAULT_CODE + ".json")) {
return LanguageReader.read(input);
} catch (IOException e) {
Expand All @@ -29,7 +29,7 @@ public static TranslationMap loadVanillaTranslations() {
}
}

public static Multimap<String, Supplier<TranslationMap>> collectTranslationSuppliers(ResourceManager manager) {
public static Multimap<String, Supplier<TranslationMap>> collectDataPackTranslations(ResourceManager manager) {
Multimap<String, Supplier<TranslationMap>> translationSuppliers = HashMultimap.create();

for (Identifier path : manager.findResources("lang", path -> path.getPath().endsWith(".json")).keySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class BlockEntityUpdateS2CPacketMixin {
private NbtCompound translateNbt(NbtCompound nbtCompound) {
var target = LocalizationTarget.forPacket();

if ((this.blockEntityType == BlockEntityType.SIGN || this.blockEntityType == BlockEntityType.HANGING_SIGN)&& target != null) {
if (target != null && (this.blockEntityType == BlockEntityType.SIGN || this.blockEntityType == BlockEntityType.HANGING_SIGN)) {
var nbt = nbtCompound.copy();
nbt.putString("Text1", this.parseText(nbt.getString("Text1"), target));
nbt.putString("Text2", this.parseText(nbt.getString("Text2"), target));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
@Mixin(PacketByteBuf.class)
public class PacketByteBufMixin {
@Unique
private ItemStack stapi_cachedStack;
private ItemStack stapi$cachedStack;

@Inject(
method = "writeItemStack",
Expand All @@ -27,8 +27,8 @@ public class PacketByteBufMixin {
shift = At.Shift.BEFORE
)
)
private void cacheStack(ItemStack stack, CallbackInfoReturnable<PacketByteBuf> cir) {
this.stapi_cachedStack = stack;
private void stapi$cacheStack(ItemStack stack, CallbackInfoReturnable<PacketByteBuf> cir) {
this.stapi$cachedStack = stack;
}

@ModifyArg(
Expand All @@ -38,18 +38,18 @@ private void cacheStack(ItemStack stack, CallbackInfoReturnable<PacketByteBuf> c
target = "Lnet/minecraft/network/PacketByteBuf;writeNbt(Lnet/minecraft/nbt/NbtCompound;)Lnet/minecraft/network/PacketByteBuf;"
)
)
private NbtCompound writeItemStackTag(NbtCompound tag) {
private NbtCompound stapi$writeItemStackTag(NbtCompound tag) {
LocalizationTarget target = LocalizationTarget.forPacket();
if (target != null) {
tag = StackNbtLocalizer.localize(this.stapi_cachedStack, tag, target);
tag = StackNbtLocalizer.localize(this.stapi$cachedStack, tag, target);
}
this.stapi_cachedStack = null;
this.stapi$cachedStack = null;

return tag;
}

@Inject(method = "readItemStack", at = @At(value = "RETURN", ordinal = 1), locals = LocalCapture.CAPTURE_FAILHARD)
private void readItemStack(CallbackInfoReturnable<ItemStack> ci, Item item, int count, ItemStack stack) {
private void stapi$readItemStack(CallbackInfoReturnable<ItemStack> ci, Item item, int count, ItemStack stack) {
NbtCompound tag = StackNbtLocalizer.unlocalize(stack.getNbt());
stack.setNbt(tag);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
package xyz.nucleoid.server.translations.mixin.packet;

import com.google.gson.JsonElement;
import com.google.gson.JsonSerializationContext;
import xyz.nucleoid.server.translations.api.LocalizationTarget;
import xyz.nucleoid.server.translations.impl.LocalizableText;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

import java.lang.reflect.Type;

@Mixin(Text.Serializer.class)
public abstract class TextSerializerMixin {
@Shadow
public abstract JsonElement serialize(Text text, Type type, JsonSerializationContext ctx);

@ModifyVariable(method = "serialize(Lnet/minecraft/text/Text;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;", at = @At("HEAD"), ordinal = 0)
private Text stapi$serializeTranslatableText(Text text) {
LocalizationTarget target = LocalizationTarget.forPacket();
if (target != null) {
return LocalizableText.asLocalizedFor(text, target);
return LocalizableText.asLocalizedFor(text, target.getLanguage(), false);
}
return text;
}
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {

class Globals {
static def baseVersion = "2.0.0-beta.1"
static def mcVersion = "1.19.4-pre1"
static def mcVersion = "1.19.4-pre2"
static def yarnVersion = "+build.1"
}

Expand All @@ -22,7 +22,7 @@ allprojects {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17

group = "fr.catcore"
group = "xyz.nucleoid"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G

maven_group=fr.catcore
maven_group=xyz.nucleoid

0 comments on commit c904fff

Please sign in to comment.