Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement MiniMessage Velocity translations #1108

Open
wants to merge 7 commits into
base: dev/3.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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 @@ -63,6 +63,7 @@
import com.velocitypowered.proxy.util.bossbar.AdventureBossBarManager;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiter;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiters;
import com.velocitypowered.proxy.util.translation.VelocityTranslationRegistry;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
Expand Down Expand Up @@ -217,8 +218,6 @@ void start() {
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
console.setupStreams();

registerTranslations();

serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);

cm.logChannelInformation();
Expand All @@ -233,6 +232,8 @@ void start() {

this.doStartupConfigLoad();

registerTranslations();

for (Map.Entry<String, String> entry : configuration.getServers().entrySet()) {
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
}
Expand Down Expand Up @@ -263,8 +264,8 @@ void start() {
}

private void registerTranslations() {
final TranslationRegistry translationRegistry = TranslationRegistry
.create(Key.key("velocity", "translations"));
final TranslationRegistry translationRegistry = new VelocityTranslationRegistry(
TranslationRegistry.create(Key.key("velocity", "translations")));
translationRegistry.defaultLocale(Locale.US);
try {
ResourceUtils.visitResources(VelocityServer.class, path -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.velocitypowered.proxy.config.migration.ConfigurationMigration;
import com.velocitypowered.proxy.config.migration.ForwardingMigration;
import com.velocitypowered.proxy.config.migration.KeyAuthenticationMigration;
import com.velocitypowered.proxy.config.migration.MiniMessageTranslationsMigration;
import com.velocitypowered.proxy.config.migration.MotdMigration;
import com.velocitypowered.proxy.util.AddressUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
Expand Down Expand Up @@ -452,7 +453,8 @@ public static VelocityConfiguration read(Path path) throws IOException {
final ConfigurationMigration[] migrations = {
new ForwardingMigration(),
new KeyAuthenticationMigration(),
new MotdMigration()
new MotdMigration(),
new MiniMessageTranslationsMigration()
};

for (final ConfigurationMigration migration : migrations) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
* Configuration Migration interface.
*/
public sealed interface ConfigurationMigration
permits ForwardingMigration, KeyAuthenticationMigration, MotdMigration {
permits ForwardingMigration,
KeyAuthenticationMigration,
MiniMessageTranslationsMigration,
MotdMigration {
boolean shouldMigrate(CommentedFileConfig config);

void migrate(CommentedFileConfig config, Logger logger) throws IOException;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2024 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.velocitypowered.proxy.config.migration;

import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.apache.logging.log4j.Logger;

/**
* Migration from old to modern language argument format with MiniMessage.
* Also migrates possible use of legacy colors to MiniMessage format.
*/
public final class MiniMessageTranslationsMigration implements ConfigurationMigration {
@Override
public boolean shouldMigrate(final CommentedFileConfig config) {
return configVersion(config) < 2.7;
}

@Override
public void migrate(final CommentedFileConfig config, final Logger logger) throws IOException {
final Path langFolder = Path.of("lang");
if (Files.notExists(langFolder)) {
return;
}
try (final DirectoryStream<Path> stream
= Files.newDirectoryStream(langFolder, Files::isRegularFile)) {
for (final Path path : stream) {
String content = Files.readString(path, StandardCharsets.UTF_8);
// Migrate old arguments
content = content.replace("{0}", "<arg:0>")
.replace("{1}", "<arg:1>");
// Some setups use legacy color codes, this format is migrated to MiniMessage
content = MiniMessage.miniMessage().serialize(
LegacyComponentSerializer.legacySection().deserialize(content));
Files.writeString(path, content, StandardCharsets.UTF_8);
}
}
config.set("config-version", "2.7");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
import com.velocitypowered.proxy.tablist.VelocityTabListLegacy;
import com.velocitypowered.proxy.util.ClosestLocaleMatcher;
import com.velocitypowered.proxy.util.DurationUtils;
import com.velocitypowered.proxy.util.TranslatableMapper;
import com.velocitypowered.proxy.util.translation.TranslatableMapper;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import java.net.InetSocketAddress;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

package com.velocitypowered.proxy.provider;

import com.velocitypowered.proxy.util.TranslatableMapper;
import com.velocitypowered.proxy.util.translation.TranslatableMapper;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
import net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider;
import net.kyori.adventure.text.serializer.ansi.ANSIComponentSerializer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.velocitypowered.proxy.util;
package com.velocitypowered.proxy.util.translation;

import com.velocitypowered.proxy.util.ClosestLocaleMatcher;
import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright (C) 2023 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.velocitypowered.proxy.util.translation;

import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.minimessage.Context;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.ParsingException;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.translation.TranslationRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Velocity Translation Registry.
* Based on <a href="https://github.com/KyoriPowered/adventure/pull/972">Adventure PR</a>.
* MIT Licenced.
*/
public final class VelocityTranslationRegistry implements TranslationRegistry {
private final TranslationRegistry backedRegistry;

public VelocityTranslationRegistry(final TranslationRegistry backed) {
this.backedRegistry = backed;
}

@Override
public boolean contains(@NotNull String key) {
return backedRegistry.contains(key);
}

@Override
public @NotNull Key name() {
return backedRegistry.name();
}

@Override
public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) {
return null;
}

@Override
public @Nullable Component translate(
@NotNull TranslatableComponent component,
@NotNull Locale locale
) {
final MessageFormat translationFormat = backedRegistry.translate(component.key(), locale);

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

final String miniMessageString = translationFormat.toPattern();

final Component resultingComponent;

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

if (component.children().isEmpty()) {
return resultingComponent;
} else {
return resultingComponent.children(component.children());
}
}

@Override
public void defaultLocale(@NotNull Locale locale) {
backedRegistry.defaultLocale(locale);
}

@Override
public void register(@NotNull String key, @NotNull Locale locale, @NotNull MessageFormat format) {
backedRegistry.register(key, locale, format);
}

@Override
public void unregister(@NotNull String key) {
backedRegistry.unregister(key);
}

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

private final List<? extends ComponentLike> argumentComponents;

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

@Override
public 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 (index < 0 || index >= argumentComponents.size()) {
throw ctx.newException("Invalid argument number", arguments);
}

return Tag.inserting(argumentComponents.get(index));
}

@Override
public boolean has(final @NotNull String name) {
return name.equals(NAME) || name.equals(NAME_1);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,42 @@
velocity.error.already-connected=You are already connected to this server!
velocity.error.already-connected-proxy=You are already connected to this proxy!
velocity.error.already-connecting=You are already trying to connect to a server!
velocity.error.cant-connect=Unable to connect to {0}: {1}
velocity.error.connecting-server-error=Unable to connect you to {0}. Please try again later.
velocity.error.connected-server-error=Your connection to {0} encountered a problem.
velocity.error.cant-connect=Unable to connect to <arg:0>: <arg:1>
velocity.error.connecting-server-error=Unable to connect you to <arg:0>. Please try again later.
velocity.error.connected-server-error=Your connection to <arg:0> encountered a problem.
velocity.error.internal-server-connection-error=An internal server connection error occurred.
velocity.error.logging-in-too-fast=You are logging in too fast, try again later.
velocity.error.online-mode-only=You are not logged into your Minecraft account. If you are logged into your Minecraft account, try restarting your Minecraft client.
velocity.error.player-connection-error=An internal error occurred in your connection.
velocity.error.modern-forwarding-needs-new-client=This server is only compatible with Minecraft 1.13 and above.
velocity.error.modern-forwarding-failed=Your server did not send a forwarding request to the proxy. Make sure the server is configured for Velocity forwarding.
velocity.error.moved-to-new-server=You were kicked from {0}: {1}
velocity.error.moved-to-new-server=You were kicked from <arg:0>: <arg:1>
velocity.error.no-available-servers=There are no available servers to connect you to. Try again later or contact an admin.
velocity.error.illegal-chat-characters=Illegal characters in chat
# Commands
velocity.command.generic-error=An error occurred while running this command.
velocity.command.command-does-not-exist=This command does not exist.
velocity.command.players-only=Only players can run this command.
velocity.command.server-does-not-exist=The specified server {0} does not exist.
velocity.command.player-not-found=The specified player {0} does not exist.
velocity.command.server-current-server=You are currently connected to {0}.
velocity.command.server-does-not-exist=The specified server <arg:0> does not exist.
velocity.command.player-not-found=The specified player <arg:0> does not exist.
velocity.command.server-current-server=You are currently connected to <arg:0>.
velocity.command.server-too-many=There are too many servers set up. Use tab completion to view all servers available.
velocity.command.server-available=Available servers:
velocity.command.server-tooltip-player-online={0} player online
velocity.command.server-tooltip-players-online={0} players online
velocity.command.server-tooltip-player-online=<arg:0> player online
velocity.command.server-tooltip-players-online=<arg:0> players online
velocity.command.server-tooltip-current-server=Currently connected to this server
velocity.command.server-tooltip-offer-connect-server=Click to connect to this server
velocity.command.glist-player-singular={0} player is currently connected to the proxy.
velocity.command.glist-player-plural={0} players are currently connected to the proxy.
velocity.command.glist-player-singular=<arg:0> player is currently connected to the proxy.
velocity.command.glist-player-plural=<arg:0> players are currently connected to the proxy.
velocity.command.glist-view-all=To view all players on servers, use /glist all.
velocity.command.reload-success=Velocity configuration successfully reloaded.
velocity.command.reload-failure=Unable to reload your Velocity configuration. Check the console for more details.
velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is licensed under the terms of the GNU General Public License v3.
velocity.command.version-copyright=Copyright 2018-2024 <arg:0>. <arg:1> is licensed under the terms of the GNU General Public License v3.
velocity.command.no-plugins=There are no plugins currently installed.
velocity.command.plugins-list=Plugins: {0}
velocity.command.plugin-tooltip-website=Website: {0}
velocity.command.plugin-tooltip-author=Author: {0}
velocity.command.plugin-tooltip-authors=Authors: {0}
velocity.command.plugins-list=Plugins: <arg:0>
velocity.command.plugin-tooltip-website=Website: <arg:0>
velocity.command.plugin-tooltip-author=Author: <arg:0>
velocity.command.plugin-tooltip-authors=Authors: <arg:0>
velocity.command.dump-uploading=Uploading gathered information...
velocity.command.dump-send-error=An error occurred while communicating with the Velocity servers. The servers may be temporarily unavailable or there is an issue with your network settings. You can find more information in the log or console of your Velocity server.
velocity.command.dump-success=Created an anonymised report containing useful information about this proxy. If a developer requested it, you may share the following link with them:
Expand Down
Loading