diff --git a/Core/src/main/java/com/plotsquared/core/PlotSquared.java b/Core/src/main/java/com/plotsquared/core/PlotSquared.java index 30d738afc..3d7ff56c0 100644 --- a/Core/src/main/java/com/plotsquared/core/PlotSquared.java +++ b/Core/src/main/java/com/plotsquared/core/PlotSquared.java @@ -28,13 +28,16 @@ package com.plotsquared.core; import com.plotsquared.core.command.WE_Anywhere; import com.plotsquared.core.components.ComponentPresetManager; import com.plotsquared.core.configuration.Caption; +import com.plotsquared.core.configuration.caption.CaptionMap; import com.plotsquared.core.configuration.CaptionUtility; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.configuration.ConfigurationSection; import com.plotsquared.core.configuration.ConfigurationUtil; +import com.plotsquared.core.configuration.caption.LegacyCaptionMap; import com.plotsquared.core.configuration.MemorySection; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.Storage; +import com.plotsquared.core.configuration.caption.CaptionLoader; import com.plotsquared.core.configuration.file.YamlConfiguration; import com.plotsquared.core.configuration.serialization.ConfigurationSerialization; import com.plotsquared.core.database.DBFunc; @@ -107,6 +110,7 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.sql.SQLException; import java.util.ArrayDeque; @@ -164,6 +168,8 @@ public class PlotSquared { public HashMap> clusters_tmp; public HashMap> plots_tmp; private YamlConfiguration config; + // Localization + @Getter private CaptionMap captionMap; // Implementation logger @Setter @Getter private ILogger logger; // Platform / Version / Update URL @@ -219,10 +225,23 @@ public class PlotSquared { if (!setupConfigs()) { return; } - this.translationFile = MainUtil.getFile(this.IMP.getDirectory(), - Settings.Paths.TRANSLATIONS + File.separator + IMP.getPluginName() - + ".use_THIS.yml"); - Captions.load(this.translationFile); + + // Setup localization + CaptionMap captionMap; + if (Settings.Enabled_Components.PER_USER_LOCALE) { + captionMap = CaptionLoader.loadAll(Paths.get("lang")); + } else { + String fileName = "messages_" + Settings.Enabled_Components.DEFAULT_LOCALE + ".json"; + captionMap = CaptionLoader.loadSingle(Paths.get("lang", fileName)); + } + if (Settings.Enabled_Components.LEGACY_MESSAGES) { + this.translationFile = MainUtil.getFile(this.IMP.getDirectory(), + Settings.Paths.TRANSLATIONS + File.separator + IMP.getPluginName() + + ".use_THIS.yml"); + Captions.load(this.translationFile); + captionMap = new LegacyCaptionMap(captionMap); + } + this.captionMap = captionMap; // Setup plotAreaManager if (Settings.Enabled_Components.WORLDS) { diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java index 80d103882..c2e2c4f68 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -573,6 +573,10 @@ public class Settings extends Config { PERSISTENT_ROAD_REGEN = false; @Comment({"Enable the `/plot component` preset GUI", "Read more about components here: https://wiki.intellectualsites.com/en/plotsquared/installation/plot-components"}) public static boolean COMPONENT_PRESETS = true; + @Comment({"Use legacy messages (will be removed in future)", + "It is recommended to use the new message system"}) public static boolean LEGACY_MESSAGES = true; + @Comment("Enable per user locale") public static boolean PER_USER_LOCALE = false; + @Comment("The default locale") public static String DEFAULT_LOCALE = "en"; @Comment("Use UUID cache to complete usernames") public static boolean EXTENDED_USERNAME_COMPLETION = true; @Comment("Command aliases that will be tab completed") diff --git a/Core/src/main/java/com/plotsquared/core/configuration/caption/CaptionLoader.java b/Core/src/main/java/com/plotsquared/core/configuration/caption/CaptionLoader.java new file mode 100644 index 000000000..8af62e840 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/configuration/caption/CaptionLoader.java @@ -0,0 +1,60 @@ +package com.plotsquared.core.configuration.caption; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public final class CaptionLoader { + private static final Gson GSON = new GsonBuilder().create(); + private static final Pattern FILE_NAME_PATTERN = Pattern.compile("messages_(.*)\\.json"); + + private CaptionLoader() { + } + + public static CaptionMap loadAll(@NotNull final Path directory) throws IOException { + final Map localeMaps = new HashMap<>(); + try (Stream files = Files.list(directory)) { + List captionFiles = files.filter(Files::isRegularFile).collect(Collectors.toList()); + for (Path file : captionFiles) { + CaptionMap localeMap = loadSingle(file); + localeMaps.put(localeMap.getLocale(), localeMap); + } + return new PerUserLocaleCaptionMap(localeMaps); + } + } + + public static CaptionMap loadSingle(@NotNull final Path file) throws IOException { + final String fileName = file.getFileName().toString(); + final Matcher matcher = FILE_NAME_PATTERN.matcher(fileName); + final Locale locale; + if (matcher.matches()) { + locale = Locale.forLanguageTag(matcher.group(1)); + } else { + throw new IllegalArgumentException(fileName + " is an invalid message file (cannot extract locale)"); + } + JsonObject object = GSON.fromJson( + Files.newBufferedReader(file, StandardCharsets.UTF_16), + JsonObject.class); + Map captions = new HashMap<>(); + for (Map.Entry entry : object.entrySet()) { + TranslatableCaption key = TranslatableCaption.of(entry.getKey()); + captions.put(key, entry.getValue().getAsString()); + } + return new LocalizedCaptionMap(locale, captions); + } +} diff --git a/Core/src/main/java/com/plotsquared/core/configuration/caption/CaptionMap.java b/Core/src/main/java/com/plotsquared/core/configuration/caption/CaptionMap.java new file mode 100644 index 000000000..29061d414 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/configuration/caption/CaptionMap.java @@ -0,0 +1,16 @@ +package com.plotsquared.core.configuration.caption; + +import com.plotsquared.core.player.PlotPlayer; + +import java.util.Locale; + +public interface CaptionMap { + + String getMessage(TranslatableCaption caption); + + String getMessage(TranslatableCaption caption, PlotPlayer context); + + boolean supportsLocale(Locale locale); + + Locale getLocale(); +} diff --git a/Core/src/main/java/com/plotsquared/core/configuration/caption/LegacyCaptionMap.java b/Core/src/main/java/com/plotsquared/core/configuration/caption/LegacyCaptionMap.java new file mode 100644 index 000000000..f2d502ebb --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/configuration/caption/LegacyCaptionMap.java @@ -0,0 +1,52 @@ +package com.plotsquared.core.configuration.caption; + +import com.plotsquared.core.configuration.Caption; +import com.plotsquared.core.configuration.Captions; +import com.plotsquared.core.player.PlotPlayer; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +@Deprecated +public class LegacyCaptionMap implements CaptionMap { + private final CaptionMap parent; + private final Map legacyMap; + + public LegacyCaptionMap(CaptionMap parent) { + this.parent = parent; + this.legacyMap = new HashMap<>(); + // add here custom mappings + } + + @Override + public String getMessage(TranslatableCaption caption) { + Caption legacy = this.legacyMap.computeIfAbsent(caption.getKey(), key -> { + String captionsName = key.substring(key.indexOf('.') + 1).toUpperCase(Locale.ROOT); + try { + return Captions.valueOf(captionsName); + } catch (IllegalArgumentException ignored) { + return null; + } + }); + if (legacy == null) { + return this.parent.getMessage(caption); + } + return legacy.getTranslated(); + } + + @Override + public String getMessage(TranslatableCaption caption, PlotPlayer context) { + return getMessage(caption); // Captions does not allow per player locale + } + + @Override + public boolean supportsLocale(Locale locale) { + return false; + } + + @Override + public Locale getLocale() { + return Locale.ROOT; + } +} diff --git a/Core/src/main/java/com/plotsquared/core/configuration/caption/LocalizedCaptionMap.java b/Core/src/main/java/com/plotsquared/core/configuration/caption/LocalizedCaptionMap.java new file mode 100644 index 000000000..483525a2e --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/configuration/caption/LocalizedCaptionMap.java @@ -0,0 +1,32 @@ +package com.plotsquared.core.configuration.caption; + +import com.plotsquared.core.player.PlotPlayer; + +import java.util.Locale; +import java.util.Map; + +public class LocalizedCaptionMap implements CaptionMap { + private final Locale locale; + private final Map captions; + + public LocalizedCaptionMap(Locale locale, Map captions) { + this.locale = locale; + this.captions = captions; + } + + @Override public String getMessage(TranslatableCaption caption) { + return this.captions.get(caption); + } + + @Override public String getMessage(TranslatableCaption caption, PlotPlayer context) { + return getMessage(caption); // use the translation of this locale + } + + @Override public boolean supportsLocale(Locale locale) { + return this.locale.equals(locale); + } + + @Override public Locale getLocale() { + return this.locale; + } +} diff --git a/Core/src/main/java/com/plotsquared/core/configuration/caption/PerUserLocaleCaptionMap.java b/Core/src/main/java/com/plotsquared/core/configuration/caption/PerUserLocaleCaptionMap.java new file mode 100644 index 000000000..43a14045a --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/configuration/caption/PerUserLocaleCaptionMap.java @@ -0,0 +1,26 @@ +package com.plotsquared.core.configuration.caption; + +import com.plotsquared.core.player.PlotPlayer; + +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + +public class PerUserLocaleCaptionMap extends LocalizedCaptionMap { + private final Map localeMap; + + public PerUserLocaleCaptionMap(Map localeMap) { + super(Locale.ROOT, Collections.emptyMap()); + this.localeMap = localeMap; + } + + @Override + public String getMessage(TranslatableCaption caption, PlotPlayer context) { + return this.localeMap.get(context.getLocale()).getMessage(caption); + } + + @Override + public boolean supportsLocale(Locale locale) { + return this.localeMap.containsKey(locale); + } +} diff --git a/Core/src/main/java/com/plotsquared/core/configuration/caption/TranslatableCaption.java b/Core/src/main/java/com/plotsquared/core/configuration/caption/TranslatableCaption.java new file mode 100644 index 000000000..714829e7c --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/configuration/caption/TranslatableCaption.java @@ -0,0 +1,28 @@ +package com.plotsquared.core.configuration.caption; + +import com.plotsquared.core.configuration.Caption; +import org.jetbrains.annotations.NotNull; + +public final class TranslatableCaption implements Caption { + @NotNull private final String key; + + private TranslatableCaption(@NotNull String key) { + this.key = key; + } + + public static TranslatableCaption of(@NotNull final String key) { + return new TranslatableCaption(key); + } + + @Override public String getTranslated() { + return null; + } + + @Override public boolean usePrefix() { + return false; + } + + @NotNull public String getKey() { + return this.key; + } +} diff --git a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java index 13efa6bfb..20d3f16b9 100644 --- a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java +++ b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java @@ -61,6 +61,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -86,6 +87,7 @@ public abstract class PlotPlayer

implements CommandCaller, OfflinePlotPlayer */ private ConcurrentHashMap meta; private int hash; + private Locale locale; public static PlotPlayer from(@NonNull final T object) { if (!converters.containsKey(object.getClass())) { @@ -737,6 +739,21 @@ public abstract class PlotPlayer

implements CommandCaller, OfflinePlotPlayer return this.getAttribute("debug"); } + @NotNull public Locale getLocale() { + if (this.locale == null) { + this.locale = Locale.forLanguageTag(Settings.Enabled_Components.DEFAULT_LOCALE); + } + return this.locale; + } + + public void setLocale(@NotNull final Locale locale) { + if (!PlotSquared.get().getCaptionMap().supportsLocale(locale)) { + this.locale = Locale.forLanguageTag(Settings.Enabled_Components.DEFAULT_LOCALE); + } else { + this.locale = locale; + } + } + @Override public int hashCode() { if (this.hash == 0 || this.hash == 485) { this.hash = 485 + this.getUUID().hashCode();