Skip to content

Commit

Permalink
Merge pull request #871 from qixils/component-translator
Browse files Browse the repository at this point in the history
feat(api): allow Translators to create their own Components
  • Loading branch information
kezz committed Mar 1, 2023
2 parents 8cd898d + 3f60e64 commit 7b9205c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ public abstract class TranslatableComponentRenderer<C> extends AbstractComponent
/**
* Creates a {@link TranslatableComponentRenderer} using the {@link Translator} to translate.
*
* <p>Alongside the standard {@link MessageFormat}-based translation, this will also allow the {@link Translator}
* to create a {@link Component} {@link Translator#translate(TranslatableComponent, Locale) directly}.</p>
*
* @param source the translation source
* @return the renderer
* @since 4.0.0
Expand All @@ -73,6 +76,13 @@ public abstract class TranslatableComponentRenderer<C> extends AbstractComponent
protected @Nullable MessageFormat translate(final @NotNull String key, final @NotNull Locale context) {
return source.translate(key, context);
}

@Override
protected @NotNull Component renderTranslatable(final @NotNull TranslatableComponent component, final @NotNull Locale context) {
final @Nullable Component translated = source.translate(component, context);
if (translated != null) return translated;
return super.renderTranslatable(component, context);
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
import net.kyori.examination.ExaminableProperty;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -80,6 +82,17 @@ public boolean removeSource(final @NotNull Translator source) {
return null;
}

@Override
public @Nullable Component translate(final @NotNull TranslatableComponent component, final @NotNull Locale locale) {
requireNonNull(component, "component");
requireNonNull(locale, "locale");
for (final Translator source : this.sources) {
final Component translation = source.translate(component, locale);
if (translation != null) return translation;
}
return null;
}

@Override
public @NotNull Stream<? extends ExaminableProperty> examinableProperties() {
return Stream.of(ExaminableProperty.of("sources", this.sources));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@
import java.util.Locale;
import java.util.ResourceBundle;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* A message format translator.
* A message translator.
*
* <p>To see how to create a {@link Translator} with a {@link ResourceBundle}
* see {@link TranslationRegistry#registerAll(Locale, ResourceBundle, boolean)}</p>
*
* <p>To bypass vanilla's {@link MessageFormat}-based translation system,
* see {@link #translate(TranslatableComponent, Locale)}</p>
*
* <p>After creating a {@link Translator} you can add it to the {@link GlobalTranslator}
* to enable automatic translations by the platforms.</p>
*
Expand Down Expand Up @@ -76,10 +81,25 @@ public interface Translator {
/**
* Gets a message format from a key and locale.
*
* <p>When used in the {@link GlobalTranslator}, this method is called only if
* {@link #translate(TranslatableComponent, Locale)} returns {@code null}.</p>
*
* @param locale a locale
* @param key a translation key
* @return a message format or {@code null} to skip translation
* @since 4.0.0
*/
@Nullable MessageFormat translate(final @NotNull String key, final @NotNull Locale locale);

/**
* Gets a translated component from a translatable component and locale.
*
* @param locale a locale
* @param component a translatable component
* @return a translated component or {@code null} to use {@link #translate(String, Locale)} instead (if available)
* @since 4.13.0
*/
default @Nullable Component translate(final @NotNull TranslatableComponent component, final @NotNull Locale locale) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@
import java.util.Locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -45,16 +48,17 @@ void removeDummySourceBeforeEachTest() {
GlobalTranslator.translator().removeSource(DummyTranslator.INSTANCE);
}

@Test
void testRender() {
@ParameterizedTest
@ValueSource(strings = {"testDummy", "otherDummy"})
void testRender(final @NotNull String key) {
GlobalTranslator.translator().addSource(DummyTranslator.INSTANCE);
assertEquals(
Component.text()
.append(Component.text("Hello "))
.append(Component.text("kashike", NamedTextColor.DARK_PURPLE))
.append(Component.text("!"))
.build(),
GlobalTranslator.render(Component.translatable("testDummy", Component.text("kashike", NamedTextColor.DARK_PURPLE)), Locale.US)
GlobalTranslator.render(Component.translatable(key, Component.text("kashike", NamedTextColor.DARK_PURPLE)), Locale.US)
);
assertEquals(
Component.entityNBT()
Expand All @@ -67,7 +71,7 @@ void testRender() {
.append(Component.text("!"))
)
.build(),
GlobalTranslator.render(Component.entityNBT("ignored", "@p").separator(Component.translatable("testDummy", Component.text("you"))), Locale.US)
GlobalTranslator.render(Component.entityNBT("ignored", "@p").separator(Component.translatable(key, Component.text("you"))), Locale.US)
);
}

Expand All @@ -89,6 +93,14 @@ void testTranslate() {
assertNull(GlobalTranslator.translator().translate("testDummy", Locale.US));
GlobalTranslator.translator().addSource(DummyTranslator.INSTANCE);
assertEquals(new MessageFormat("Hello {0}!"), GlobalTranslator.translator().translate("testDummy", Locale.US));
assertEquals(
Component.text()
.append(Component.text("Hello "))
.append(Component.text("{0}"))
.append(Component.text("!"))
.build(),
GlobalTranslator.translator().translate(Component.translatable("otherDummy"), Locale.US)
);
}

static class DummyTranslator implements Translator {
Expand All @@ -105,5 +117,16 @@ static class DummyTranslator implements Translator {
? new MessageFormat("Hello {0}!")
: null;
}

@Override
public @Nullable Component translate(final @NotNull TranslatableComponent component, final @NotNull Locale locale) {
return (component.key().equals("otherDummy") && locale.equals(Locale.US))
? Component.text()
.append(Component.text("Hello "))
.append(component.args().isEmpty() ? Component.text("{0}") : component.args().get(0))
.append(Component.text("!"))
.build()
: null;
}
}
}

0 comments on commit 7b9205c

Please sign in to comment.