mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-27 13:15:44 +01:00
Add /lp translations command
This commit is contained in:
parent
c5841b58a7
commit
6e7c49488d
@ -60,6 +60,9 @@ luckperms {
|
||||
migration {
|
||||
plugin brigadier:string single_word;
|
||||
}
|
||||
translations {
|
||||
install;
|
||||
}
|
||||
creategroup {
|
||||
name brigadier:string single_word;
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import me.lucko.luckperms.common.commands.misc.NetworkSyncCommand;
|
||||
import me.lucko.luckperms.common.commands.misc.ReloadConfigCommand;
|
||||
import me.lucko.luckperms.common.commands.misc.SearchCommand;
|
||||
import me.lucko.luckperms.common.commands.misc.SyncCommand;
|
||||
import me.lucko.luckperms.common.commands.misc.TranslationsCommand;
|
||||
import me.lucko.luckperms.common.commands.misc.TreeCommand;
|
||||
import me.lucko.luckperms.common.commands.misc.VerboseCommand;
|
||||
import me.lucko.luckperms.common.commands.track.CreateTrack;
|
||||
@ -114,6 +115,7 @@ public class CommandManager {
|
||||
.add(new ReloadConfigCommand())
|
||||
.add(new BulkUpdateCommand())
|
||||
.add(new MigrationParentCommand())
|
||||
.add(new TranslationsCommand())
|
||||
.add(new ApplyEditsCommand())
|
||||
.add(new CreateGroup())
|
||||
.add(new DeleteGroup())
|
||||
|
@ -47,6 +47,7 @@ public enum CommandPermission {
|
||||
BULK_UPDATE("bulkupdate", Type.NONE),
|
||||
APPLY_EDITS("applyedits", Type.NONE),
|
||||
MIGRATION("migration", Type.NONE),
|
||||
TRANSLATIONS("translations", Type.NONE),
|
||||
|
||||
CREATE_GROUP("creategroup", Type.NONE),
|
||||
DELETE_GROUP("deletegroup", Type.NONE),
|
||||
|
@ -85,6 +85,9 @@ public enum CommandSpec {
|
||||
arg("constraint...", false)
|
||||
),
|
||||
MIGRATION("/%s migration"),
|
||||
TRANSLATIONS("/%s translations",
|
||||
arg("install", false)
|
||||
),
|
||||
APPLY_EDITS("/%s applyedits <code> [target]",
|
||||
arg("code", true),
|
||||
arg("target", false)
|
||||
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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 me.lucko.luckperms.common.commands.misc;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.http.UnsuccessfulRequestException;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.locale.TranslationManager;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.MoreFiles;
|
||||
import me.lucko.luckperms.common.util.Predicates;
|
||||
import me.lucko.luckperms.common.util.gson.GsonProvider;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TranslationsCommand extends SingleCommand {
|
||||
private static final String TRANSLATIONS_INFO_ENDPOINT = "https://metadata.luckperms.net/data/translations";
|
||||
private static final String TRANSLATIONS_DOWNLOAD_ENDPOINT = "https://metadata.luckperms.net/translation/";
|
||||
|
||||
public TranslationsCommand() {
|
||||
super(CommandSpec.TRANSLATIONS, "Translations", CommandPermission.TRANSLATIONS, Predicates.notInRange(0, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) {
|
||||
Message.TRANSLATIONS_SEARCHING.send(sender);
|
||||
|
||||
List<LanguageInfo> availableTranslations;
|
||||
try {
|
||||
availableTranslations = getAvailableTranslations(plugin);
|
||||
} catch (IOException | UnsuccessfulRequestException e) {
|
||||
Message.TRANSLATIONS_SEARCHING_ERROR.send(sender);
|
||||
e.printStackTrace();
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
if (args.size() >= 1 && args.get(0).equalsIgnoreCase("install")) {
|
||||
Message.TRANSLATIONS_DOWNLOADING.send(sender);
|
||||
|
||||
downloadTranslations(plugin, availableTranslations, sender);
|
||||
plugin.getTranslationManager().reload();
|
||||
|
||||
Message.TRANSLATIONS_INSTALL_COMPLETE.send(sender);
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
Message.INSTALLED_TRANSLATIONS.send(sender, plugin.getTranslationManager().getInstalledLocales().stream().map(Locale::toString).collect(Collectors.toList()));
|
||||
|
||||
Message.AVAILABLE_TRANSLATIONS_HEADER.send(sender);
|
||||
for (LanguageInfo language : availableTranslations) {
|
||||
Message.AVAILABLE_TRANSLATIONS_ENTRY.send(sender, language.locale.toString(), language.locale.getDisplayLanguage(language.locale), language.progress, language.contributors);
|
||||
}
|
||||
sender.sendMessage(Message.prefixed(Component.empty()));
|
||||
Message.TRANSLATIONS_DOWNLOAD_PROMPT.send(sender, label);
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
private static void downloadTranslations(LuckPermsPlugin plugin, List<LanguageInfo> languages, Sender sender) {
|
||||
try {
|
||||
MoreFiles.createDirectoriesIfNotExists(plugin.getTranslationManager().getTranslationsDirectory());
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
for (LanguageInfo language : languages) {
|
||||
Message.TRANSLATIONS_INSTALLING.send(sender, language.locale.toString());
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.header("User-Agent", plugin.getBytebin().getUserAgent())
|
||||
.url(TRANSLATIONS_DOWNLOAD_ENDPOINT + language.id)
|
||||
.build();
|
||||
|
||||
Path file = plugin.getTranslationManager().getTranslationsDirectory().resolve(language.locale.toString() + ".properties");
|
||||
|
||||
try (Response response = plugin.getBytebin().makeHttpRequest(request)) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
if (responseBody == null) {
|
||||
throw new RuntimeException("No response");
|
||||
}
|
||||
|
||||
try (InputStream inputStream = responseBody.byteStream()) {
|
||||
Files.copy(inputStream, file, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
} catch (UnsuccessfulRequestException | IOException e) {
|
||||
Message.TRANSLATIONS_DOWNLOAD_ERROR.send(sender, language.locale.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<LanguageInfo> getAvailableTranslations(LuckPermsPlugin plugin) throws IOException, UnsuccessfulRequestException {
|
||||
Request request = new Request.Builder()
|
||||
.header("User-Agent", plugin.getBytebin().getUserAgent())
|
||||
.url(TRANSLATIONS_INFO_ENDPOINT)
|
||||
.build();
|
||||
|
||||
JsonObject jsonResponse;
|
||||
try (Response response = plugin.getBytebin().makeHttpRequest(request)) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
if (responseBody == null) {
|
||||
throw new RuntimeException("No response");
|
||||
}
|
||||
|
||||
try (InputStream inputStream = responseBody.byteStream()) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||
jsonResponse = GsonProvider.normal().fromJson(reader, JsonObject.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<LanguageInfo> languages = new ArrayList<>();
|
||||
for (Map.Entry<String, JsonElement> language : jsonResponse.get("languages").getAsJsonObject().entrySet()) {
|
||||
languages.add(new LanguageInfo(language.getKey(), language.getValue().getAsJsonObject()));
|
||||
}
|
||||
languages.removeIf(language -> language.progress <= 0);
|
||||
return languages;
|
||||
}
|
||||
|
||||
private static final class LanguageInfo {
|
||||
private final String id;
|
||||
private final String name;
|
||||
private final Locale locale;
|
||||
private final int progress;
|
||||
private final List<String> contributors;
|
||||
|
||||
LanguageInfo(String id, JsonObject data) {
|
||||
this.id = id;
|
||||
this.name = data.get("name").getAsString();
|
||||
this.locale = Objects.requireNonNull(TranslationManager.parseLocale(data.get("localeTag").getAsString(), null));
|
||||
this.progress = data.get("progress").getAsInt();
|
||||
this.contributors = new ArrayList<>();
|
||||
for (JsonElement contributor : data.get("contributors").getAsJsonArray()) {
|
||||
this.contributors.add(contributor.getAsJsonObject().get("name").getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2570,6 +2570,104 @@ public interface Message {
|
||||
.append(FULL_STOP)
|
||||
);
|
||||
|
||||
Args0 TRANSLATIONS_SEARCHING = () -> prefixed(translatable()
|
||||
// "&7Searching for available translations, please wait..."
|
||||
.key("luckperms.command.translations.searching")
|
||||
.color(GRAY)
|
||||
);
|
||||
|
||||
Args0 TRANSLATIONS_SEARCHING_ERROR = () -> prefixed(text()
|
||||
// "&cUnable to obtain a list of available translations. Check the console for errors."
|
||||
.color(RED)
|
||||
.append(translatable("luckperms.command.translations.searching-error"))
|
||||
.append(FULL_STOP)
|
||||
.append(space())
|
||||
.append(translatable("luckperms.command.misc.check-console-for-errors"))
|
||||
.append(FULL_STOP)
|
||||
);
|
||||
|
||||
Args1<Collection<String>> INSTALLED_TRANSLATIONS = locales -> prefixed(translatable()
|
||||
// "&aInstalled Translations:"
|
||||
.key("luckperms.command.translations.installed-translations")
|
||||
.color(GREEN)
|
||||
.append(text(':'))
|
||||
.append(space())
|
||||
.append(formatStringList(locales))
|
||||
);
|
||||
|
||||
Args0 AVAILABLE_TRANSLATIONS_HEADER = () -> prefixed(translatable()
|
||||
// "&aAvailable Translations:"
|
||||
.key("luckperms.command.translations.available-translations")
|
||||
.color(GREEN)
|
||||
.append(text(':'))
|
||||
);
|
||||
|
||||
Args4<String, String, Integer, List<String>> AVAILABLE_TRANSLATIONS_ENTRY = (tag, name, percentComplete, contributors) -> prefixed(text()
|
||||
// - {} ({}) - {}% translated - by {}
|
||||
.color(GRAY)
|
||||
.append(text('-'))
|
||||
.append(space())
|
||||
.append(text(tag, AQUA))
|
||||
.append(space())
|
||||
.append(OPEN_BRACKET)
|
||||
.append(text(name, WHITE))
|
||||
.append(CLOSE_BRACKET)
|
||||
.append(text(" - "))
|
||||
.append(translatable("luckperms.command.translations.percent-translated", text(percentComplete, GREEN)))
|
||||
.apply(builder -> {
|
||||
if (!contributors.isEmpty()) {
|
||||
builder.append(text(" - "));
|
||||
builder.append(translatable("luckperms.command.translations.translations-by"));
|
||||
builder.append(space());
|
||||
builder.append(formatStringList(contributors));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
Args1<String> TRANSLATIONS_DOWNLOAD_PROMPT = label -> join(newline(),
|
||||
// "Use /lp translations install to download and install up-to-date versions of these translations provided by the community."
|
||||
// "Please note that this will override any changes you've made for these languages."
|
||||
prefixed(translatable()
|
||||
.key("luckperms.command.translations.download-prompt")
|
||||
.color(AQUA)
|
||||
.args(text("/" + label + " translations install", GREEN))
|
||||
.append(FULL_STOP)),
|
||||
prefixed(translatable()
|
||||
.key("luckperms.command.translations.download-override-warning")
|
||||
.color(GRAY)
|
||||
.append(FULL_STOP))
|
||||
);
|
||||
|
||||
Args0 TRANSLATIONS_DOWNLOADING = () -> prefixed(translatable()
|
||||
// "&bDownloading translations, please wait..."
|
||||
.key("luckperms.command.translations.downloading")
|
||||
.color(AQUA)
|
||||
);
|
||||
|
||||
Args1<String> TRANSLATIONS_INSTALLING = name -> prefixed(translatable()
|
||||
// "&aInstalling language {}..."
|
||||
.key("luckperms.command.translations.installing")
|
||||
.color(GREEN)
|
||||
.args(text((name)))
|
||||
);
|
||||
|
||||
Args0 TRANSLATIONS_INSTALL_COMPLETE = () -> prefixed(translatable()
|
||||
// "&bInstallation complete."
|
||||
.key("luckperms.command.translations.install-complete")
|
||||
.color(AQUA)
|
||||
.append(FULL_STOP)
|
||||
);
|
||||
|
||||
Args1<String> TRANSLATIONS_DOWNLOAD_ERROR = name -> prefixed(text()
|
||||
// "&cUnable download translation for {}. Check the console for errors."
|
||||
.color(RED)
|
||||
.append(translatable("luckperms.command.translations.download-error", text(name, DARK_RED)))
|
||||
.append(FULL_STOP)
|
||||
.append(space())
|
||||
.append(translatable("luckperms.command.misc.check-console-for-errors"))
|
||||
.append(FULL_STOP)
|
||||
);
|
||||
|
||||
Args4<String, String, Boolean, Boolean> USER_INFO_GENERAL = (username, uuid, mojang, online) -> join(newline(),
|
||||
// "&b&l> &bUser Info: &f{}"
|
||||
// "&f- &3UUID: &f{}"
|
||||
|
@ -40,6 +40,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -48,23 +50,40 @@ public class TranslationManager {
|
||||
public static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
|
||||
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final TranslationRegistry registry;
|
||||
private final Path translationsDirectory;
|
||||
private final Set<Locale> installed = ConcurrentHashMap.newKeySet();
|
||||
private TranslationRegistry registry;
|
||||
|
||||
public TranslationManager(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.translationsDirectory = this.plugin.getBootstrap().getConfigDirectory().resolve("translations");
|
||||
}
|
||||
|
||||
// create a translation registry for luckperms
|
||||
public Path getTranslationsDirectory() {
|
||||
return this.translationsDirectory;
|
||||
}
|
||||
|
||||
public Set<Locale> getInstalledLocales() {
|
||||
return Collections.unmodifiableSet(this.installed);
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
// remove any previous registry
|
||||
if (this.registry != null) {
|
||||
GlobalTranslator.get().removeSource(this.registry);
|
||||
this.installed.clear();
|
||||
}
|
||||
|
||||
// create a translation registry
|
||||
this.registry = TranslationRegistry.create(Key.key("luckperms", "main"));
|
||||
this.registry.defaultLocale(DEFAULT_LOCALE);
|
||||
|
||||
// register it to the global source, so our translations can be picked up by adventure-platform
|
||||
GlobalTranslator.get().addSource(this.registry);
|
||||
}
|
||||
|
||||
public void load() {
|
||||
// load custom translations first, then the base (built-in) translations after.
|
||||
loadCustom();
|
||||
loadBase();
|
||||
|
||||
// register it to the global source, so our translations can be picked up by adventure-platform
|
||||
GlobalTranslator.get().addSource(this.registry);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,7 +99,7 @@ public class TranslationManager {
|
||||
*/
|
||||
public void loadCustom() {
|
||||
List<Path> translationFiles;
|
||||
try (Stream<Path> stream = Files.list(this.plugin.getBootstrap().getConfigDirectory().resolve("translations"))) {
|
||||
try (Stream<Path> stream = Files.list(this.translationsDirectory)) {
|
||||
translationFiles = stream.filter(path -> path.getFileName().toString().endsWith(".properties")).collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
translationFiles = Collections.emptyList();
|
||||
@ -106,7 +125,8 @@ public class TranslationManager {
|
||||
}
|
||||
|
||||
this.registry.registerAll(locale, translationFile, true);
|
||||
this.plugin.getLogger().info("Registered additional translations for " + locale.toLanguageTag());
|
||||
this.plugin.getLogger().info("Registered additional translations for " + locale.toString());
|
||||
this.installed.add(locale);
|
||||
}
|
||||
|
||||
public static Locale parseLocale(String locale, Locale defaultLocale) {
|
||||
|
@ -78,6 +78,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
private PermissionRegistry permissionRegistry;
|
||||
private LogDispatcher logDispatcher;
|
||||
private LuckPermsConfiguration configuration;
|
||||
private OkHttpClient httpClient;
|
||||
private BytebinClient bytebin;
|
||||
private FileWatcher fileWatcher = null;
|
||||
private Storage storage;
|
||||
@ -98,7 +99,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
this.dependencyManager.loadDependencies(getGlobalDependencies());
|
||||
|
||||
this.translationManager = new TranslationManager(this);
|
||||
this.translationManager.load();
|
||||
this.translationManager.reload();
|
||||
}
|
||||
|
||||
public final void enable() {
|
||||
@ -118,11 +119,11 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
this.configuration = new LuckPermsConfiguration(this, provideConfigurationAdapter());
|
||||
|
||||
// setup a bytebin instance
|
||||
OkHttpClient httpClient = new OkHttpClient.Builder()
|
||||
this.httpClient = new OkHttpClient.Builder()
|
||||
.callTimeout(15, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
this.bytebin = new BytebinClient(httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms");
|
||||
this.bytebin = new BytebinClient(this.httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms");
|
||||
|
||||
// now the configuration is loaded, we can create a storage factory and load initial dependencies
|
||||
StorageFactory storageFactory = new StorageFactory(this);
|
||||
@ -316,6 +317,11 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OkHttpClient getHttpClient() {
|
||||
return this.httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BytebinClient getBytebin() {
|
||||
return this.bytebin;
|
||||
|
@ -57,6 +57,8 @@ import me.lucko.luckperms.common.verbose.VerboseHandler;
|
||||
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -232,6 +234,13 @@ public interface LuckPermsPlugin {
|
||||
*/
|
||||
Optional<FileWatcher> getFileWatcher();
|
||||
|
||||
/**
|
||||
* Gets the http client used by the plugin.
|
||||
*
|
||||
* @return the http client
|
||||
*/
|
||||
OkHttpClient getHttpClient();
|
||||
|
||||
/**
|
||||
* Gets the bytebin instance in use by platform.
|
||||
*
|
||||
|
@ -302,6 +302,18 @@ luckperms.command.update-task.push.error=Error whilst pushing changes to other s
|
||||
luckperms.command.update-task.push.error-not-setup=Cannot push changes to other servers as a messaging service has not been configured
|
||||
luckperms.command.reload-config.success=The configuration file was reloaded
|
||||
luckperms.command.reload-config.restart-note=some options will only apply after the server has restarted
|
||||
luckperms.command.translations.searching=Searching for available translations, please wait...
|
||||
luckperms.command.translations.searching-error=Unable to obtain a list of available translations
|
||||
luckperms.command.translations.installed-translations=Installed Translations
|
||||
luckperms.command.translations.available-translations=Available Translations
|
||||
luckperms.command.translations.percent-translated={0}% translated
|
||||
luckperms.command.translations.translations-by=by
|
||||
luckperms.command.translations.downloading=Downloading translations, please wait...
|
||||
luckperms.command.translations.download-error=Unable download translation for {0}
|
||||
luckperms.command.translations.installing=Installing language {0}...
|
||||
luckperms.command.translations.install-complete=Installation complete
|
||||
luckperms.command.translations.download-prompt=Use {0} to download and install up-to-date versions of these translations provided by the community
|
||||
luckperms.command.translations.download-override-warning=Please note that this will override any changes you''ve made for these languages
|
||||
luckperms.usage.user.description=A set of commands for managing users within LuckPerms. (A ''user'' in LuckPerms is just a player, and can refer to a UUID or username)
|
||||
luckperms.usage.group.description=A set of commands for managing groups within LuckPerms. Groups are just collections of permission assignments that can be given to users. New groups are made using the ''creategroup'' command.
|
||||
luckperms.usage.track.description=A set of commands for managing tracks within LuckPerms. Tracks are a ordered collection of groups which can be used for defining promotions and demotions.
|
||||
@ -340,6 +352,8 @@ luckperms.usage.bulk-update.argument.action-field=the field to act upon. only re
|
||||
luckperms.usage.bulk-update.argument.action-value=the value to replace with. only required for ''update''.
|
||||
luckperms.usage.bulk-update.argument.constraint=the constraints required for the update
|
||||
luckperms.usage.migration.description=Migration commands
|
||||
luckperms.usage.translations.description=Manage translations
|
||||
luckperms.usage.translations.argument.install=subcommand to install translations
|
||||
luckperms.usage.apply-edits.description=Applies permission changes made from the web editor
|
||||
luckperms.usage.apply-edits.argument.code=the unique code for the data
|
||||
luckperms.usage.apply-edits.argument.target=who to apply the data to
|
||||
|
Loading…
Reference in New Issue
Block a user