updates to translations
This commit is contained in:
1 parent
1f5b06daa2
commit
1e39f6caa8
5 files changed
+152
-145
No files matched your search
@@ -4,6 +4,7 @@ import carpet.settings.ParsedRule;
|
||||
import carpet.settings.Rule;
|
||||
import carpet.settings.SettingsManager;
|
||||
import carpet.settings.Validator;
|
||||
import carpet.utils.Translations;
|
||||
import carpet.utils.Messenger;
|
||||
import carpet.utils.SpawnChunks;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -41,13 +42,30 @@ public class CarpetSettings
|
||||
public static Vec3d fixedPosition = null;
|
||||
public static int runPermissionLevel = 2;
|
||||
|
||||
private static class LanguageValidator extends Validator<String> {
|
||||
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
|
||||
if (currentRule.get().equals(newValue))
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
if (!Translations.isValidLanguage(newValue))
|
||||
{
|
||||
Messenger.m(source, "r "+newValue+" is not a valid language");
|
||||
return null;
|
||||
}
|
||||
CarpetSettings.language = newValue;
|
||||
Translations.updateLanguage(source);
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
@Rule(
|
||||
desc = "sets the language of CarpetMod",
|
||||
category = {CREATIVE, SURVIVAL},
|
||||
options = {"none"},
|
||||
strict = false
|
||||
desc = "sets the language for carpet",
|
||||
category = FEATURE,
|
||||
options = {"none", "zh_cn"},
|
||||
strict = false,
|
||||
validate = LanguageValidator.class
|
||||
)
|
||||
public static String carpetLanguage = "none";
|
||||
public static String language = "none";
|
||||
|
||||
@Rule(
|
||||
desc = "Nether portals correctly place entities going through",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package carpet.settings;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.utils.Translations;
|
||||
import carpet.utils.Messenger;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
@@ -12,6 +13,8 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static carpet.utils.Translations.tr;
|
||||
|
||||
public final class ParsedRule<T> implements Comparable<ParsedRule> {
|
||||
public final Field field;
|
||||
public final String name;
|
||||
@@ -225,4 +228,32 @@ public final class ParsedRule<T> implements Comparable<ParsedRule> {
|
||||
{
|
||||
return this.name + ": " + getAsString();
|
||||
}
|
||||
|
||||
private String translationKey()
|
||||
{
|
||||
return String.format("carpet.rule.%s.name", name);
|
||||
}
|
||||
|
||||
public String translatedName(){
|
||||
String key = translationKey();
|
||||
return Translations.hasTranslation(key) ? tr(key) + String.format(" (%s)", name): name;
|
||||
}
|
||||
|
||||
public String translatedDescription()
|
||||
{
|
||||
return tr(String.format("carpet.rule.%s.desc", (name)), description);
|
||||
}
|
||||
|
||||
public List<String> translatedExtras()
|
||||
{
|
||||
if (!Translations.hasTranslations()) return extraInfo;
|
||||
String keyBase = String.format("carpet.rule.%s.extra.", name);
|
||||
List<String> extras = new ArrayList<>();
|
||||
int i = 0;
|
||||
while (Translations.hasTranslation(keyBase+i))
|
||||
{
|
||||
extras.add(Translations.tr(keyBase+i));
|
||||
}
|
||||
return (extras.isEmpty()) ? extraInfo : extras;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package carpet.settings;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.utils.LanguageUtils;
|
||||
import carpet.utils.Translations;
|
||||
import carpet.utils.Messenger;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -40,6 +40,7 @@ import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static carpet.utils.Translations.tr;
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
import static net.minecraft.server.command.CommandSource.suggestMatching;
|
||||
@@ -351,7 +352,7 @@ public class SettingsManager
|
||||
String ruleName = StringArgumentType.getString(ctx, "rule");
|
||||
ParsedRule<?> rule = getRule(ruleName);
|
||||
if (rule == null)
|
||||
throw new SimpleCommandExceptionType(Messenger.c("rb "+ LanguageUtils.translateOrDefault("carpet.ui.statement.unknown_rule","Unknown rule")+": "+ruleName)).create();
|
||||
throw new SimpleCommandExceptionType(Messenger.c("rb "+ tr("carpet.ui.statement.unknown_rule","Unknown rule")+": "+ruleName)).create();
|
||||
return rule;
|
||||
}
|
||||
|
||||
@@ -367,22 +368,18 @@ public class SettingsManager
|
||||
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal(identifier).requires((player) ->
|
||||
player.hasPermissionLevel(2) && !locked);
|
||||
|
||||
literalargumentbuilder.executes((context)->
|
||||
{
|
||||
LanguageUtils.setLanguage(CarpetSettings.carpetLanguage);
|
||||
return listAllSettings(context.getSource());
|
||||
}).
|
||||
literalargumentbuilder.executes((context)-> listAllSettings(context.getSource())).
|
||||
then(literal("list").
|
||||
executes( (c) -> listSettings(c.getSource(), String.format(LanguageUtils.translateOrDefault("carpet.ui.statement.all_%s_settings","All %s Settings"), fancyName),
|
||||
executes( (c) -> listSettings(c.getSource(), String.format(tr("carpet.ui.statement.all_%s_settings","All %s Settings"), fancyName),
|
||||
getRules())).
|
||||
then(literal("defaults").
|
||||
executes( (c)-> listSettings(c.getSource(),
|
||||
String.format(LanguageUtils.translateOrDefault("carpet.ui.statement.current_%s_startup_settings_from_%s","Current %s Startup Settings from %s"), fancyName, (identifier+".conf")),
|
||||
String.format(tr("carpet.ui.statement.current_%s_startup_settings_from_%s","Current %s Startup Settings from %s"), fancyName, (identifier+".conf")),
|
||||
findStartupOverrides()))).
|
||||
then(argument("tag",StringArgumentType.word()).
|
||||
suggests( (c, b)->suggestMatching(getCategories(), b)).
|
||||
executes( (c) -> listSettings(c.getSource(),
|
||||
String.format(LanguageUtils.translateOrDefault("carpet.ui.statement.%s_settings_matching_'%s'","%s Settings matching \"%s\""), fancyName, LanguageUtils.translateOrDefault("carpet.ui.category." + StringArgumentType.getString(c, "tag"),StringArgumentType.getString(c, "tag"))),
|
||||
String.format(tr("carpet.ui.statement.%s_settings_matching_'%s'","%s Settings matching \"%s\""), fancyName, tr("carpet.ui.category." + StringArgumentType.getString(c, "tag"),StringArgumentType.getString(c, "tag"))),
|
||||
getRulesMatching(StringArgumentType.getString(c, "tag")))))).
|
||||
then(literal("removeDefault").
|
||||
requires(s -> !locked).
|
||||
@@ -410,36 +407,35 @@ public class SettingsManager
|
||||
private int displayRuleMenu(ServerCommandSource source, ParsedRule<?> rule)
|
||||
{
|
||||
PlayerEntity player;
|
||||
String translatedPlusName = LanguageUtils.translatePlusDefault(LanguageUtils.getRuleNameKey(rule.name), rule.name);
|
||||
String displayName = rule.translatedName();
|
||||
try
|
||||
{
|
||||
player = source.getPlayer();
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
Messenger.m(source, "w "+ translatedPlusName +" "+LanguageUtils.translateOrDefault( "carpet.ui.statement.is_set_to","is set to")+": ","wb "+rule.getAsString());
|
||||
Messenger.m(source, "w "+ displayName +" "+ tr( "carpet.ui.statement.is_set_to","is set to")+": ","wb "+rule.getAsString());
|
||||
return 1;
|
||||
}
|
||||
|
||||
Messenger.m(player, "");
|
||||
Messenger.m(player, "wb "+ translatedPlusName ,"!/"+identifier+" "+rule.name,"^g refresh");
|
||||
Messenger.m(player, "w "+ LanguageUtils.translateOrDefault(LanguageUtils.getRuleDescKey(rule.name), rule.description));
|
||||
Messenger.m(player, "wb "+ displayName ,"!/"+identifier+" "+rule.name,"^g refresh");
|
||||
Messenger.m(player, "w "+ rule.translatedDescription());
|
||||
|
||||
ImmutableList<String> translatedExtraInfo = LanguageUtils.getRuleExtras(rule.name, rule.extraInfo);
|
||||
translatedExtraInfo.forEach(s -> Messenger.m(player, "g "+s));
|
||||
rule.translatedExtras().forEach(s -> Messenger.m(player, "g "+s));
|
||||
|
||||
List<BaseText> tags = new ArrayList<>();
|
||||
tags.add(Messenger.c("w "+LanguageUtils.translateOrDefault("carpet.ui.statement.tags", "Tags")+": "));
|
||||
tags.add(Messenger.c("w "+ tr("carpet.ui.statement.tags", "Tags")+": "));
|
||||
for (String t: rule.categories)
|
||||
{
|
||||
String translated = LanguageUtils.translateOrDefault("carpet.ui.category." + t, t);
|
||||
tags.add(Messenger.c("c ["+ translated +"]", "^g "+ String.format(LanguageUtils.translateOrDefault("list_all_%s_settings","list all %s settings"), translated),"!/"+identifier+" list "+t));
|
||||
String translated = tr("carpet.ui.category." + t, t);
|
||||
tags.add(Messenger.c("c ["+ translated +"]", "^g "+ String.format(tr("list_all_%s_settings","list all %s settings"), translated),"!/"+identifier+" list "+t));
|
||||
tags.add(Messenger.c("w , "));
|
||||
}
|
||||
tags.remove(tags.size()-1);
|
||||
Messenger.m(player, tags.toArray(new Object[0]));
|
||||
|
||||
Messenger.m(player, "w "+LanguageUtils.translateOrDefault("carpet.ui.statement.current_value", "Current value")+": ",String.format("%s %s (%s value)",rule.getBoolValue()?"lb":"nb", rule.getAsString(),rule.isDefault()?"default":"modified"));
|
||||
Messenger.m(player, "w "+ tr("carpet.ui.statement.current_value", "Current value")+": ",String.format("%s %s (%s value)",rule.getBoolValue()?"lb":"nb", rule.getAsString(),rule.isDefault()?"default":"modified"));
|
||||
List<BaseText> options = new ArrayList<>();
|
||||
options.add(Messenger.c("w Options: ", "y [ "));
|
||||
for (String o: rule.options)
|
||||
@@ -457,8 +453,8 @@ public class SettingsManager
|
||||
private int setRule(ServerCommandSource source, ParsedRule<?> rule, String newValue)
|
||||
{
|
||||
if (rule.set(source, newValue) != null)
|
||||
Messenger.m(source, "w "+rule.toString()+", ", "c ["+LanguageUtils.translateOrDefault("carpet.ui.statement.change_permanently","change permanently")+"?]",
|
||||
"^w "+String.format(LanguageUtils.translateOrDefault("carpet.ui.statement.click_to_keep_the_settings_in_%s_to_save_across_restarts","Click to keep the settings in %s to save across restarts"), identifier+".conf"),
|
||||
Messenger.m(source, "w "+rule.toString()+", ", "c ["+ tr("carpet.ui.statement.change_permanently","change permanently")+"?]",
|
||||
"^w "+String.format(tr("carpet.ui.statement.click_to_keep_the_settings_in_%s_to_save_across_restarts","Click to keep the settings in %s to save across restarts"), identifier+".conf"),
|
||||
"?/"+identifier+" setDefault "+rule.name+" "+rule.getAsString());
|
||||
return 1;
|
||||
}
|
||||
@@ -473,7 +469,7 @@ public class SettingsManager
|
||||
writeSettingsToConf(conf.getLeft()); // this may feels weird, but if conf
|
||||
// is locked, it will never reach this point.
|
||||
rule.set(source,stringValue);
|
||||
Messenger.m(source ,"gi "+String.format(LanguageUtils.translateOrDefault("carpet.ui.statement.rule_%s_will_now_default_to_%s","rule %s will now default to %s"), LanguageUtils.translateOrDefault(LanguageUtils.getRuleNameKey(rule.name), rule.name), stringValue));
|
||||
Messenger.m(source ,"gi "+String.format(tr("carpet.ui.statement.rule_%s_will_now_default_to_%s","rule %s will now default to %s"), rule.translatedName(), stringValue));
|
||||
return 1;
|
||||
}
|
||||
// removes overrides of the default values in the file
|
||||
@@ -490,11 +486,11 @@ public class SettingsManager
|
||||
|
||||
private BaseText displayInteractiveSetting(ParsedRule<?> rule)
|
||||
{
|
||||
String translatedPlusName = LanguageUtils.translatePlusDefault(LanguageUtils.getRuleNameKey(rule.name), rule.name);
|
||||
String displayName = rule.translatedName();
|
||||
List<Object> args = new ArrayList<>();
|
||||
args.add("w - "+ translatedPlusName +" ");
|
||||
args.add("w - "+ displayName +" ");
|
||||
args.add("!/"+identifier+" "+rule.name);
|
||||
args.add("^y "+LanguageUtils.translateOrDefault(LanguageUtils.getRuleDescKey(rule.name), rule.description));
|
||||
args.add("^y "+rule.translatedDescription());
|
||||
for (String option: rule.options)
|
||||
{
|
||||
args.add(makeSetRuleButton(rule, option, true));
|
||||
@@ -520,10 +516,10 @@ public class SettingsManager
|
||||
}
|
||||
String baseText = style + (brackets ? " [" : " ") + option + (brackets ? "]" : "");
|
||||
if (locked)
|
||||
return Messenger.c(baseText, "^g "+fancyName+" " + LanguageUtils.translateOrDefault("carpet.ui.statement.settings_are_locked","settings are locked"));
|
||||
return Messenger.c(baseText, "^g "+fancyName+" " + tr("carpet.ui.statement.settings_are_locked","settings are locked"));
|
||||
if (option.equalsIgnoreCase(rule.getAsString()))
|
||||
return Messenger.c(baseText);
|
||||
return Messenger.c(baseText, "^g "+ LanguageUtils.translateOrDefault("carpet.ui.statement.switch_to","Switch to") +" " + option+(option.equalsIgnoreCase(rule.defaultAsString)?" (default)":""), "?/"+identifier+" " + rule.name + " " + option);
|
||||
return Messenger.c(baseText, "^g "+ tr("carpet.ui.statement.switch_to","Switch to") +" " + option+(option.equalsIgnoreCase(rule.defaultAsString)?" (default)":""), "?/"+identifier+" " + rule.name + " " + option);
|
||||
}
|
||||
|
||||
private int listSettings(ServerCommandSource source, String title, Collection<ParsedRule<?>> settings_list)
|
||||
@@ -545,24 +541,24 @@ public class SettingsManager
|
||||
private int listAllSettings(ServerCommandSource source)
|
||||
{
|
||||
//listSettings(source, "Current "+fancyName+" Settings", getNonDefault());
|
||||
listSettings(source, String.format(LanguageUtils.translateOrDefault("carpet.ui.statement.current_%s_settings","Current %s Settings"), fancyName), getNonDefault());
|
||||
listSettings(source, String.format(tr("carpet.ui.statement.current_%s_settings","Current %s Settings"), fancyName), getNonDefault());
|
||||
|
||||
if (version != null)
|
||||
//Messenger.m(source, "g "+fancyName+" version: "+ version);
|
||||
Messenger.m(source, "g "+fancyName+" "+ LanguageUtils.translateOrDefault("carpet.ui.statement.version", "version") + ": "+ version);
|
||||
Messenger.m(source, "g "+fancyName+" "+ tr("carpet.ui.statement.version", "version") + ": "+ version);
|
||||
try
|
||||
{
|
||||
PlayerEntity player = source.getPlayer();
|
||||
List<Object> tags = new ArrayList<>();
|
||||
//tags.add("w Browse Categories:\n");
|
||||
tags.add("w " + LanguageUtils.translateOrDefault("carpet.ui.statement.browse_categories", "Browse Categories") + ":\n");
|
||||
tags.add("w " + tr("carpet.ui.statement.browse_categories", "Browse Categories") + ":\n");
|
||||
for (String t : getCategories())
|
||||
{
|
||||
String translated = LanguageUtils.translateOrDefault("carpet.ui.category." + t, t);
|
||||
String translatedPlus = LanguageUtils.translatePlusDefault("carpet.ui.category." + t, t);
|
||||
String translated = tr("carpet.ui.category." + t, t);
|
||||
String translatedPlus = Translations.hasTranslation("carpet.ui.category." + t) ? tr("carpet.ui.category." + t, t) + String.format(" (%s)", t) : t;
|
||||
tags.add("c [" + translated +"]");
|
||||
//tags.add("^g list all " + translated + " settings");
|
||||
tags.add("^g " + String.format(LanguageUtils.translateOrDefault("carpet.ui.statement.list_all_%s_settings","list all %s settings"), translatedPlus));
|
||||
tags.add("^g " + String.format(tr("carpet.ui.statement.list_all_%s_settings","list all %s settings"), translatedPlus));
|
||||
tags.add("!/"+identifier+" list " + t);
|
||||
tags.add("w ");
|
||||
}
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
package carpet.utils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class LanguageUtils
|
||||
{
|
||||
private static Map<String, String> translationMap;
|
||||
|
||||
public static String currentLanguage;
|
||||
|
||||
public static String translate(String translationKey)
|
||||
{
|
||||
if (translationMap == null)
|
||||
{
|
||||
return translationKey;
|
||||
}
|
||||
return translationMap.getOrDefault(translationKey, translationKey);
|
||||
}
|
||||
|
||||
public static String translateOrDefault(String translationKey, String defaultValue)
|
||||
{
|
||||
if (translationMap == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
return translationMap.getOrDefault(translationKey, defaultValue);
|
||||
}
|
||||
|
||||
public static String translatePlusDefault(String translationKey, String defaultValue){
|
||||
return translationMap.containsKey(translationKey) ? translateOrDefault(translationKey, defaultValue) + String.format(" (%s)", defaultValue): defaultValue;
|
||||
}
|
||||
|
||||
public static String getRuleNameKey(String rule)
|
||||
{
|
||||
return String.format("carpet.rule.%s.name", rule);
|
||||
}
|
||||
|
||||
public static String getRuleDescKey(String rule)
|
||||
{
|
||||
return String.format("carpet.rule.%s.desc", rule);
|
||||
}
|
||||
|
||||
public static ImmutableList<String> getRuleExtras(String rule, ImmutableList<String> defaultList)
|
||||
{
|
||||
if (translationMap == null) return defaultList;
|
||||
String keyBase = String.format("carpet.rule.%s.extra.", rule);
|
||||
List<String> extras = new ArrayList<>();
|
||||
int i = 0;
|
||||
do {
|
||||
String keyExtra = keyBase + i;
|
||||
String value = translationMap.get(keyExtra);
|
||||
System.out.println(keyExtra);
|
||||
System.out.println(value);
|
||||
if (value != null && !value.equals(""))
|
||||
{
|
||||
extras.add(value);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while (translationMap.containsKey(keyBase + (i - 1)));
|
||||
return (extras.isEmpty()) ? defaultList : ImmutableList.copyOf(extras);
|
||||
}
|
||||
|
||||
public static void setLanguage(String language)
|
||||
{
|
||||
if (language.equals("none"))
|
||||
{
|
||||
currentLanguage = language;
|
||||
translationMap = null;
|
||||
return;
|
||||
}
|
||||
|
||||
String langJs;
|
||||
try{
|
||||
langJs = IOUtils.toString(
|
||||
Objects.requireNonNull(LanguageUtils.class.getClassLoader().getResourceAsStream(String.format("assets/carpet/lang/%s.json", language))),
|
||||
StandardCharsets.UTF_8);
|
||||
} catch (NullPointerException | IOException e) {
|
||||
return;
|
||||
}
|
||||
Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
|
||||
Type type = new TypeToken<Map<String, String>>() {}.getType();
|
||||
Map<String, String> map1 = gson.fromJson(langJs, type);
|
||||
currentLanguage = language;
|
||||
translationMap = map1;
|
||||
}
|
||||
|
||||
public static void loadConfiguration()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package carpet.utils;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Translations
|
||||
{
|
||||
private static Map<String, String> translationMap;
|
||||
|
||||
public static String tr(String key)
|
||||
{
|
||||
return translationMap == null ? key : translationMap.getOrDefault(key, key);
|
||||
}
|
||||
|
||||
public static String tr(String key, String str)
|
||||
{
|
||||
return translationMap == null ? str : translationMap.getOrDefault(key, str);
|
||||
}
|
||||
|
||||
public static boolean hasTranslations()
|
||||
{
|
||||
return translationMap != null;
|
||||
}
|
||||
|
||||
public static boolean hasTranslation(String key)
|
||||
{
|
||||
return translationMap != null && translationMap.containsKey(key);
|
||||
}
|
||||
|
||||
public static void updateLanguage(ServerCommandSource source)
|
||||
{
|
||||
if (CarpetSettings.language.equalsIgnoreCase("none"))
|
||||
{
|
||||
translationMap = null;
|
||||
return;
|
||||
}
|
||||
String langJs;
|
||||
try
|
||||
{
|
||||
langJs = IOUtils.toString(
|
||||
Objects.requireNonNull(Translations.class.getClassLoader().getResourceAsStream(String.format("assets/carpet/lang/%s.json", CarpetSettings.language))),
|
||||
StandardCharsets.UTF_8);
|
||||
} catch (NullPointerException | IOException e) {
|
||||
Messenger.m(source, "r Failed to update language");
|
||||
return;
|
||||
}
|
||||
Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
|
||||
Type type = new TypeToken<Map<String, String>>() {}.getType();
|
||||
translationMap = gson.fromJson(langJs, type);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidLanguage(String newValue)
|
||||
{
|
||||
// will put some validations for availble languages at some point
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user