Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@

import java.util.Locale;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
import net.kyori.examination.Examinable;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static java.util.Objects.requireNonNull;

/**
* A global source of translations. The global source is the default source used by adventure platforms
Expand Down Expand Up @@ -75,6 +80,25 @@ public interface GlobalTranslator extends Translator, Examinable {
return GlobalTranslatorImpl.INSTANCE.renderer;
}

/**
* Renders a component using the {@link #renderer() global renderer}.
*
* <p>The locale will be determined using the
* {@link #localeOverride(Audience) locale override} or determined using the
* {@link Identity#LOCALE locale pointer}.</p>
*
* @param component the component to render
* @param audience the audience to extract the locale from
* @return the rendered component
* @since 4.22.0
*/
static @NotNull Component render(final @NotNull Component component, final @NotNull Audience audience) {
Locale locale = translator().localeOverride(requireNonNull(audience, "audience"));
if (locale == null) locale = audience.getOrDefault(Identity.LOCALE, null);
if (locale == null) return component;
return render(component, locale);
}

/**
* Renders a component using the {@link #renderer() global renderer}.
*
Expand Down Expand Up @@ -115,4 +139,24 @@ public interface GlobalTranslator extends Translator, Examinable {
* @since 4.0.0
*/
boolean removeSource(final @NotNull Translator source);

/**
* Sets an override for the locale of the audience when fetched using {@link #localeOverride(Audience)}.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably needs a warning in the javadocs or platform implementation that these must be unregistered by passing (null).

* @param audience the audience, may be a {@link ForwardingAudience} to set the override for all members in the audience
* @param locale the locale to set, or {@code null} to remove the override
* @see #localeOverride(Audience)
* @since 4.22.0
*/
void overrideLocale(final @NotNull Audience audience, final @Nullable Locale locale);

/**
* Returns the override locale for an audience that will be used in {@link #render(Component, Audience)}.
*
* @param audience the audience member
* @return the locale, if any
* @see #overrideLocale(Audience, Locale)
* @since 4.22.0
*/
@Nullable Locale localeOverride(final @NotNull Audience audience);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
Expand All @@ -45,6 +50,7 @@ final class GlobalTranslatorImpl implements GlobalTranslator {
static final GlobalTranslatorImpl INSTANCE = new GlobalTranslatorImpl();
final TranslatableComponentRenderer<Locale> renderer = TranslatableComponentRenderer.usingTranslationSource(this);
private final Set<Translator> sources = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final Map<UUID, Locale> localeOverrides = new ConcurrentHashMap<>();

private GlobalTranslatorImpl() {
}
Expand Down Expand Up @@ -80,6 +86,14 @@ public boolean removeSource(final @NotNull Translator source) {
return TriState.FALSE;
}

@Override
public boolean canTranslate(final @NotNull String key, final @NotNull Locale locale) {
for (final Translator source : this.sources) {
if (source.canTranslate(key, locale)) return true;
}
return false;
}

@Override
public @Nullable MessageFormat translate(final @NotNull String key, final @NotNull Locale locale) {
requireNonNull(key, "key");
Expand All @@ -102,6 +116,30 @@ public boolean removeSource(final @NotNull Translator source) {
return null;
}

@Override
public void overrideLocale(final @NotNull Audience audience, final @Nullable Locale locale) {
if (requireNonNull(audience, "audience") instanceof ForwardingAudience) {
for (final Audience single : ((ForwardingAudience) audience).audiences()) {
this.overrideLocale(single, locale);
}
} else {
audience.get(Identity.UUID).ifPresent(uuid -> {
if (locale == null) {
this.localeOverrides.remove(uuid);
} else {
this.localeOverrides.put(uuid, locale);
}
});
}
}

@Override
public @Nullable Locale localeOverride(final @NotNull Audience audience) {
final UUID uuid = requireNonNull(audience, "audience").getOrDefault(Identity.UUID, null);
if (uuid == null) return null;
return this.localeOverrides.get(uuid);
}

@Override
public @NotNull Stream<? extends ExaminableProperty> examinableProperties() {
return Stream.of(ExaminableProperty.of("sources", this.sources));
Expand Down