Skip to content

Commit

Permalink
update with named arguments and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
kezz committed Jan 6, 2025
1 parent ca373c0 commit 9380716
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2025 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.text.minimessage.translation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.minimessage.Context;
Expand All @@ -11,29 +38,50 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class ArgumentTag implements TagResolver {
final class ArgumentTag implements TagResolver {
private static final String NAME = "argument";
private static final String NAME_1 = "arg";

private final List<? extends ComponentLike> argumentComponents;
private final Map<String, NamedTranslationArgument> namedArguments;

public ArgumentTag(final @NotNull List<? extends ComponentLike> argumentComponents) {
this.argumentComponents = Objects.requireNonNull(argumentComponents, "argumentComponents");
ArgumentTag(final @NotNull List<? extends ComponentLike> argumentComponents) {
this.argumentComponents = new ArrayList<>(Objects.requireNonNull(argumentComponents, "argumentComponents"));

final Map<String, NamedTranslationArgument> namedArgumentMap = new HashMap<>(this.argumentComponents.size());
for (final ComponentLike argument : this.argumentComponents) {
if (argument instanceof NamedTranslationArgument) {
final NamedTranslationArgument namedArgument = (NamedTranslationArgument) argument;
namedArgumentMap.put(namedArgument.name(), namedArgument);
}
}

this.namedArguments = Collections.unmodifiableMap(namedArgumentMap);
}

@Override
public @Nullable Tag resolve(final @NotNull String name, final @NotNull ArgumentQueue arguments, final @NotNull Context ctx) throws ParsingException {
final int index = arguments.popOr("No argument number provided").asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
if (name.equals(NAME) || name.equals(NAME_1)) {
final int index = arguments.popOr("No argument number provided").asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));

if (index < 0 || index >= argumentComponents.size()) {
throw ctx.newException("Invalid argument number", arguments);
}
if (index < 0 || index >= this.argumentComponents.size()) {
throw ctx.newException("Invalid argument number", arguments);
}

return Tag.inserting(this.argumentComponents.get(index));
} else {
final NamedTranslationArgument namedArgument = this.namedArguments.get(name);

return Tag.inserting(argumentComponents.get(index));
if (namedArgument != null) {
return Tag.inserting(namedArgument);
} else {
return null;
}
}
}

@Override
public boolean has(final @NotNull String name) {
return name.equals(NAME) || name.equals(NAME_1);
return name.equals(NAME) || name.equals(NAME_1) || this.namedArguments.containsKey(name);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2022 KyoriPowered
* Copyright (c) 2017-2025 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -29,22 +29,70 @@
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.translation.Translator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* A {@link Translator} implementation that translates strings using MiniMessage.
*
* <p>To use this feature, you should extend this class, implementing the
* {@link #getMiniMessageString(String, Locale)} method to return the MiniMessage string
* for a given key and locale.
* After that, you can use the translator as-is using
* {@link #translate(TranslatableComponent, Locale)}, or automatically (depending on the
* implementing platform) using the {@link GlobalTranslator}.</p>
*
* <p>This system supports arguments using {@code <arg:0>} tags (or {@code argument},
* where {@code 0} is the index of the argument to use).
* Alternatively, you can use named arguments by creating the translatable component
* with {@link NamedTranslationArgument} as the arguments.
* The provided {@link NamedTranslationArgument#name() name} will be available for use in
* a tag as {@code <name>}, in addition to the index-based {@code arg} tag.</p>
*
* @see Translator
* @see GlobalTranslator
* @see NamedTranslationArgument
* @since 4.19.0
*/
public abstract class MiniMessageTranslator implements Translator {
private final MiniMessage miniMessage;

final MiniMessage miniMessage;

/**
* Constructor for a MiniMessageTranslator using the default MiniMessage instance.
*
* @see MiniMessage#miniMessage()
* @since 4.19.0
*/
public MiniMessageTranslator() {
this(MiniMessage.miniMessage());
}

/**
* Constructor for a MiniMessageTranslator using a specific MiniMessage instance.
*
* @param miniMessage the MiniMessage instance
* @see MiniMessage#miniMessage()
* @since 4.19.0
*/
public MiniMessageTranslator(final @NotNull MiniMessage miniMessage) {
this.miniMessage = Objects.requireNonNull(miniMessage, "miniMessage");
}

/**
* Returns a raw MiniMessage string for the given key.
*
* <p>If no string is found for the given key, returning {@code null} will use the
* {@link TranslatableComponent#fallback() translatable component's fallback} (or the
* key itself).</p>
*
* @param key the key
* @param locale the locale
* @return the resulting MiniMessage string
* @since 4.19.0
*/
@SuppressWarnings("checkstyle:MethodName")
protected abstract @Nullable String getMiniMessageString(final @NotNull String key, final @NotNull Locale locale);

@Override
Expand All @@ -53,19 +101,19 @@ public MiniMessageTranslator(final @NotNull MiniMessage miniMessage) {
}

@Override
public @Nullable Component translate(final @NotNull TranslatableComponent component, final @NotNull Locale locale) {
final String miniMessageString = getMiniMessageString(component.key(), locale);
public final @Nullable Component translate(final @NotNull TranslatableComponent component, final @NotNull Locale locale) {
final String miniMessageString = this.getMiniMessageString(component.key(), locale);

if (miniMessageString == null) {
return null;
}

final Component resultingComponent;

if (component.args().isEmpty()) {
resultingComponent = MiniMessage.miniMessage().deserialize(miniMessageString);
if (component.arguments().isEmpty()) {
resultingComponent = this.miniMessage.deserialize(miniMessageString);
} else {
resultingComponent = MiniMessage.miniMessage().deserialize(miniMessageString, new ArgumentTag(component.args()));
resultingComponent = this.miniMessage.deserialize(miniMessageString, new ArgumentTag(component.arguments()));
}

if (component.children().isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2025 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.text.minimessage.translation;

import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.TranslationArgument;
import net.kyori.adventure.text.TranslationArgumentLike;
import net.kyori.adventure.text.minimessage.tag.TagPattern;
import org.jetbrains.annotations.NotNull;

import static java.util.Objects.requireNonNull;

/**
* A {@link TranslationArgument} with an associated string name.
*
* <p>This is intended for use with {@link TranslatableComponent translatable components}
* used with a {@link MiniMessageTranslator} instance to allow {@code <name>} tags.</p>
*
* @since 4.19.0
*/
public interface NamedTranslationArgument extends TranslationArgumentLike {
/**
* Create a named boolean argument.
*
* @param name the name
* @param value the value
* @return the named argument
* @since 4.19.0
*/
static @NotNull NamedTranslationArgument bool(final @TagPattern @NotNull String name, final boolean value) {
return argument(name, TranslationArgument.bool(value));
}

/**
* Create a named numeric argument.
*
* @param name the name
* @param value the value
* @return the named argument
* @since 4.19.0
*/
static @NotNull NamedTranslationArgument numeric(final @TagPattern @NotNull String name, final @NotNull Number value) {
return argument(name, TranslationArgument.numeric(value));
}

/**
* Create a named component argument.
*
* @param name the name
* @param value the value
* @return the named argument
* @since 4.19.0
*/
static @NotNull NamedTranslationArgument component(final @TagPattern @NotNull String name, final @NotNull ComponentLike value) {
return argument(name, TranslationArgument.component(value));
}

/**
* Create a named translation argument.
*
* @param name the name
* @param argument the translation argument
* @return the named argument
* @since 4.19.0
*/
static @NotNull NamedTranslationArgument argument(final @TagPattern @NotNull String name, final @NotNull TranslationArgumentLike argument) {
return argument(name, requireNonNull(argument, "argument").asTranslationArgument());
}

/**
* Create a named translation argument.
*
* @param name the name
* @param argument the translation argument
* @return the named argument
* @since 4.19.0
*/
static @NotNull NamedTranslationArgument argument(final @TagPattern @NotNull String name, final @NotNull TranslationArgument argument) {
return new NamedTranslationArgumentImpl(name, argument);
}

/**
* The name of this translation argument.
*
* @return the name
* @since 4.19.0
*/
@TagPattern @NotNull String name();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2025 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.text.minimessage.translation;

import java.util.Objects;
import net.kyori.adventure.text.TranslationArgument;
import net.kyori.adventure.text.minimessage.internal.TagInternals;
import net.kyori.adventure.text.minimessage.tag.TagPattern;
import org.jetbrains.annotations.NotNull;

final class NamedTranslationArgumentImpl implements NamedTranslationArgument {

private final @TagPattern @NotNull String name;
private final @NotNull TranslationArgument argument;

NamedTranslationArgumentImpl(final @TagPattern @NotNull String name, final @NotNull TranslationArgument argument) {
Objects.requireNonNull(name, "name");
Objects.requireNonNull(argument, "argument");
TagInternals.assertValidTagName(name);

this.name = name;
this.argument = argument;
}

@Override
public @TagPattern @NotNull String name() {
return this.name;
}

@Override
public @NotNull TranslationArgument asTranslationArgument() {
return this.argument;
}
}

0 comments on commit 9380716

Please sign in to comment.