Skip to content
Merged
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 @@ -8,6 +8,7 @@
import com.rexcantor64.triton.language.item.LanguageSign;
import com.rexcantor64.triton.language.item.LanguageText;
import com.rexcantor64.triton.storage.LocalStorage;
import com.rexcantor64.triton.utils.ComponentUtils;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -151,6 +152,10 @@ public synchronized void setup() {

setupMiniMessage();

if (!this.matches.isEmpty() && !this.triton.getConfig().getParser().equalsIgnoreCase("legacy")) {
this.triton.getLogger().logWarning("Some of your translations have patterns, but you are currently using the Adventure parser, which does not support those. If you need patterns, please use the legacy parser instead.");
}

this.triton.getLogger()
.logInfo(
"Successfully setup the Translation Manager! %1 text translations and %2 sign translations loaded!",
Expand Down Expand Up @@ -377,22 +382,16 @@ public Component[] formatLines(@NonNull Language language,
}

public String matchPattern(String input, @NotNull Localized localized) {
return matchPattern(input, localized.getLanguageId());
}

public String matchPattern(String input, String language) {
// TODO this needs to be rethought to work with Adventure
for (Map.Entry<Pattern, LanguageText> entry : matches.entrySet()) {
String replacement = entry.getValue().getMessageRegex(language);
if (replacement == null) {
replacement = entry.getValue().getMessageRegex(triton.getLanguageManager().getMainLanguage().getName());
}
String replacement = getPatternReplacement(entry.getValue(), localized);
if (replacement == null) {
continue;
}
try {
Matcher matcher = entry.getKey().matcher(input);
input = matcher.replaceAll(replacement);
// patterns only support legacy formatting for now
input = matcher.replaceAll(ComponentUtils.translateAlternateColorCodes(replacement));
} catch (IndexOutOfBoundsException e) {
this.triton.getLogger().logError(
"Failed to translate using patterns: translation has more placeholders than regex groups. Translation key: %1",
Expand All @@ -402,6 +401,24 @@ public String matchPattern(String input, String language) {
return input;
}

private @Nullable String getPatternReplacement(@NotNull LanguageText languageText, @NotNull Localized localized) {
String replacement = languageText.getMessageRegex(localized.getLanguageId());
if (replacement != null) {
return replacement;
}
for (String fallbackLanguageName : localized.getLanguage().getFallbackLanguages()) {
val fallbackLanguage = triton.getLanguageManager().getLanguageByName(fallbackLanguageName);
if (!fallbackLanguage.isPresent()) {
continue;
}
replacement = languageText.getMessageRegex(fallbackLanguage.get().getLanguageId());
if (replacement != null) {
return replacement;
}
}
return languageText.getMessageRegex(triton.getLanguageManager().getMainLanguage().getName());
}

/**
* @see com.rexcantor64.triton.language.parser.AdventureParser#replaceArguments(Component, List)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -79,7 +80,8 @@ public class AdventureParser extends MessageParser {
syntax,
Triton.get().getConfig().getDisabledLine(),
// TODO properly integrate this
(key, arguments) -> Triton.get().getTranslationManager().getTextComponentOr404(language, key, arguments)
(key, arguments) -> Triton.get().getTranslationManager().getTextComponentOr404(language, key, arguments),
Function.identity()
);

Triton.get().getDumpManager().dump(component, language, syntax);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.rexcantor64.triton.api.language.Localized;
import com.rexcantor64.triton.utils.ComponentUtils;
import com.rexcantor64.triton.utils.ParserUtils;
import com.rexcantor64.triton.utils.StringUtils;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -39,6 +40,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
Expand All @@ -65,12 +67,10 @@ public class LegacyParser extends MessageParser {
private static final char FONT_MID_DELIM = FONT_START_DELIM + 2;
private static final char FONT_END_DELIM = FONT_START_DELIM + 1;

private static final char AMPERSAND_CHAR = '&';
private static final char SECTION_CHAR = '§';
private static final char HEX_PREFIX = '#';
private static final char HEX_CODE = 'x';
private static final char SHADOW_COLOR_CODE = 's';
private static final String VALID_COLOR_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx";
private static final Pattern FORMATTING_STRIP_PATTERN = Pattern.compile(
CLICK_DELIM + "\\d[\\w-]{36}|" + HOVER_DELIM + "[\\w-]{36}|" + CLICK_END_DELIM + "|" + HOVER_END_DELIM
+ "|" + SECTION_CHAR + "[0-9A-Fa-fK-Ok-oRrXx]"
Expand Down Expand Up @@ -128,7 +128,8 @@ public class LegacyParser extends MessageParser {
}

return replaceArguments(notFoundComponent, new SerializedComponent(key), argsConcatenationComp);
})
}),
prevText -> Triton.get().getTranslationManager().matchPattern(prevText, language)
);

return translateComponent(component, configuration);
Expand All @@ -141,9 +142,21 @@ TranslationResult<SerializedComponent> translateComponent(
@NotNull TranslationConfiguration<SerializedComponent> configuration
) {
String text = component.getText();
String newText = configuration.getMatchSupplier().apply(text);
val changedByPatterns = !Objects.equals(text, newText);
if (changedByPatterns) {
component.setText(newText);
text = newText;
}
val indexes = ParserUtils.getPatternIndexArray(text, configuration.getFeatureSyntax().getLang());

if (indexes.isEmpty()) {
if (changedByPatterns) {
component = handleNonContentText(component, configuration)
.getResult()
.orElse(component);
return TranslationResult.changed(component);
}
return handleNonContentText(component, configuration);
}

Expand Down Expand Up @@ -205,6 +218,9 @@ TranslationResult<SerializedComponent> translateComponent(
}
key = ParserUtils.normalizeTranslationKey(key, configuration);
key = stripFormatting(key);
if (!StringUtils.isEmptyOrNull(configuration.getDisabledLine()) && configuration.getDisabledLine().equals(key)) {
return Optional.empty();
}

val result = configuration.translationSupplier.apply(key, arguments);

Expand Down Expand Up @@ -344,31 +360,8 @@ SerializedComponent replaceArguments(@NotNull SerializedComponent comp, @NotNull
} else if (message.startsWith(JSON_TYPE_TAG)) {
return new SerializedComponent(GsonComponentSerializer.gson().deserialize(message.substring(JSON_TYPE_TAG.length())));
} else {
return new SerializedComponent(translateAlternateColorCodes(message));
}
}

/**
* Convert color codes with ampersand (&) into section characters (§).
* The character is only converted if followed by a valid color code.
* Inspired by md5's ChatColor#translateAlternateColorCodes.
*
* @param text The text to convert the color code characters from.
* @return The input text with the color codes replaced.
*/
@Contract("null -> null; !null -> !null")
private @Nullable String translateAlternateColorCodes(@Nullable String text) {
if (text == null) {
return null;
}

char[] chars = text.toCharArray();
for (int i = 0; i < chars.length - 1; i++) {
if (chars[i] == AMPERSAND_CHAR && VALID_COLOR_CODES.indexOf(chars[i + 1]) != -1) {
chars[i] = SECTION_CHAR;
}
return new SerializedComponent(ComponentUtils.translateAlternateColorCodes(message));
}
return new String(chars);
}

/**
Expand Down Expand Up @@ -401,7 +394,8 @@ static class SerializedComponent {
@Getter
private final HashMap<UUID, @Nullable HoverEvent<?>> hoverEvents = new HashMap<>();
@Getter
@Setter(AccessLevel.PRIVATE)
@Setter(AccessLevel.PACKAGE)
@VisibleForTesting
private String text;

public SerializedComponent(Component component) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

import com.rexcantor64.triton.api.config.FeatureSyntax;
import lombok.Data;
import org.jetbrains.annotations.NotNull;

import java.util.function.BiFunction;
import java.util.function.Function;

@Data
public class TranslationConfiguration<T> {
final FeatureSyntax featureSyntax;
final String disabledLine;
final BiFunction<String, T[], T> translationSupplier;
/**
* For legacy parser only.
*/
final Function<@NotNull String, @NotNull String> matchSupplier;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import net.kyori.adventure.text.serializer.json.JSONOptions;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -28,6 +30,8 @@
public class ComponentUtils {

public final static char SECTION_CHAR = '§';
private final static char AMPERSAND_CHAR = '&';
private final static String VALID_COLOR_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx";
private final static GsonComponentSerializer GSON_SERIALIZER;
private final static LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection()
.toBuilder()
Expand Down Expand Up @@ -242,4 +246,27 @@ public static boolean isValidClickEvent(ClickEvent clickEvent) {
|| URL_REGEX.matcher(clickEvent.value()).find();
}

/**
* Convert color codes with ampersand (&) into section characters (§).
* The character is only converted if followed by a valid color code.
* Inspired by md5's ChatColor#translateAlternateColorCodes.
*
* @param text The text to convert the color code characters from.
* @return The input text with the color codes replaced.
*/
@Contract("null -> null; !null -> !null")
public static @Nullable String translateAlternateColorCodes(@Nullable String text) {
if (text == null) {
return null;
}

char[] chars = text.toCharArray();
for (int i = 0; i < chars.length - 1; i++) {
if (chars[i] == AMPERSAND_CHAR && VALID_COLOR_CODES.indexOf(chars[i + 1]) != -1) {
chars[i] = SECTION_CHAR;
}
}
return new String(chars);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ public class AdventureParserTest {
private final TranslationConfiguration<Component> configuration = new TranslationConfiguration<>(
defaultSyntax,
"disabled.line",
(key, args) -> parser.replaceArguments(messageResolver.apply(key), Arrays.asList(args))
(key, args) -> parser.replaceArguments(messageResolver.apply(key), Arrays.asList(args)),
Function.identity()
);

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public class LegacyParserTest {
private final TranslationConfiguration<SerializedComponent> configuration = new TranslationConfiguration<>(
defaultSyntax,
"disabled.line",
(key, args) -> parser.replaceArguments(messageResolver.apply(key), args)
(key, args) -> parser.replaceArguments(messageResolver.apply(key), args),
text -> text.replace("abababab", "this has been replaced").replace("babababa", "[lang]without.formatting[/lang]")
);

private final Component ALL_IN_ONE_COMPONENT = Component.text("Lorem ")
Expand Down Expand Up @@ -385,4 +386,59 @@ public void testParseComponentWithPlaceholdersInShowEntityHoverAction() {
assertNotNull(result.getResultRaw());
assertEquals(expected.compact(), result.getResultRaw().compact());
}

@Test
public void testParseComponentWithPatternsNoPlaceholders() {
Component comp = Component.text()
.content("test abababab lorem")
.asComponent();

TranslationResult<Component> result = parser.translateComponent(new SerializedComponent(comp), configuration)
.map(SerializedComponent::toComponent);

Component expected = Component.text()
.content("test this has been replaced lorem")
.asComponent();

assertEquals(TranslationResult.ResultState.CHANGED, result.getState());
assertNotNull(result.getResultRaw());
assertEquals(expected.compact(), result.getResultRaw().compact());
}

@Test
public void testParseComponentWithPatterns() {
Component comp = Component.text()
.content("test abababab lorem")
.hoverEvent(
HoverEvent.showText(
Component.text("ipsum babababa")
)
)
.asComponent();

TranslationResult<Component> result = parser.translateComponent(new SerializedComponent(comp), configuration)
.map(SerializedComponent::toComponent);

Component expected = Component.text()
.content("test this has been replaced lorem")
.hoverEvent(
HoverEvent.showText(
Component.text("ipsum This is text without formatting")
)
)
.asComponent();

assertEquals(TranslationResult.ResultState.CHANGED, result.getState());
assertNotNull(result.getResultRaw());
assertEquals(expected.compact(), result.getResultRaw().compact());
}

@Test
public void testParseComponentWithDisabledLine() {
Component comp = Component.text("[lang]disabled.line[/lang]");

TranslationResult<SerializedComponent> result = parser.translateComponent(new SerializedComponent(comp), configuration);

assertEquals(TranslationResult.ResultState.TO_REMOVE, result.getState());
}
}