001/* 002 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining 005 * a copy of this software and associated documentation files (the 006 * "Software"), to deal in the Software without restriction, including 007 * without limitation the rights to use, copy, modify, merge, publish, 008 * distribute, sublicense, and/or sell copies of the Software, and to 009 * permit persons to whom the Software is furnished to do so, subject to 010 * the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be 013 * included in all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 016 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 017 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 018 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 019 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 020 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 021 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 022 */ 023 024package co.aikar.commands; 025 026import co.aikar.locales.LocaleManager; 027import co.aikar.locales.MessageKey; 028import co.aikar.locales.MessageKeyProvider; 029import com.google.common.collect.HashMultimap; 030import com.google.common.collect.SetMultimap; 031import org.jetbrains.annotations.NotNull; 032 033import java.util.ArrayList; 034import java.util.HashMap; 035import java.util.HashSet; 036import java.util.LinkedHashMap; 037import java.util.List; 038import java.util.Locale; 039import java.util.Map; 040import java.util.Set; 041import java.util.regex.Matcher; 042 043@SuppressWarnings("WeakerAccess") 044public class Locales { 045 // Locales for reference since Locale doesn't have as many, add our own here for ease of use. 046 public static final Locale ENGLISH = Locale.ENGLISH; 047 public static final Locale GERMAN = Locale.GERMAN; 048 public static final Locale FRENCH = Locale.FRENCH; 049 public static final Locale JAPANESE = Locale.JAPANESE; 050 public static final Locale ITALIAN = Locale.ITALIAN; 051 public static final Locale KOREAN = Locale.KOREAN; 052 public static final Locale CHINESE = Locale.CHINESE; 053 public static final Locale SIMPLIFIED_CHINESE = Locale.SIMPLIFIED_CHINESE; 054 public static final Locale TRADITIONAL_CHINESE = Locale.TRADITIONAL_CHINESE; 055 public static final Locale SPANISH = new Locale("es"); 056 public static final Locale DUTCH = new Locale("nl"); 057 public static final Locale DANISH = new Locale("da"); 058 public static final Locale CZECH = new Locale("cs"); 059 public static final Locale GREEK = new Locale("el"); 060 public static final Locale LATIN = new Locale("la"); 061 public static final Locale BULGARIAN = new Locale("bg"); 062 public static final Locale AFRIKAANS = new Locale("af"); 063 public static final Locale HINDI = new Locale("hi"); 064 public static final Locale HEBREW = new Locale("he"); 065 public static final Locale POLISH = new Locale("pl"); 066 public static final Locale PORTUGUESE = new Locale("pt"); 067 public static final Locale FINNISH = new Locale("fi"); 068 public static final Locale SWEDISH = new Locale("sv"); 069 public static final Locale RUSSIAN = new Locale("ru"); 070 public static final Locale ROMANIAN = new Locale("ro"); 071 public static final Locale VIETNAMESE = new Locale("vi"); 072 public static final Locale THAI = new Locale("th"); 073 public static final Locale TURKISH = new Locale("tr"); 074 public static final Locale UKRANIAN = new Locale("uk"); 075 public static final Locale ARABIC = new Locale("ar"); 076 public static final Locale WELSH = new Locale("cy"); 077 public static final Locale NORWEGIAN_BOKMAAL = new Locale("nb"); 078 public static final Locale NORWEGIAN_NYNORSK = new Locale("nn"); 079 public static final Locale HUNGARIAN = new Locale("hu"); 080 081 private final CommandManager manager; 082 private final LocaleManager<CommandIssuer> localeManager; 083 private final Map<ClassLoader, SetMultimap<String, Locale>> loadedBundles = new HashMap<>(); 084 private final List<ClassLoader> registeredClassLoaders = new ArrayList<>(); 085 086 public Locales(CommandManager manager) { 087 this.manager = manager; 088 this.localeManager = LocaleManager.create(manager::getIssuerLocale); 089 this.addBundleClassLoader(this.getClass().getClassLoader()); 090 } 091 092 public void loadLanguages() { 093 addMessageBundles("acf-core"); 094 } 095 096 public Locale getDefaultLocale() { 097 return this.localeManager.getDefaultLocale(); 098 } 099 100 public Locale setDefaultLocale(Locale locale) { 101 return this.localeManager.setDefaultLocale(locale); 102 } 103 104 /** 105 * Looks for all previously loaded bundles, and if any new Supported Languages have been added, load them. 106 */ 107 public void loadMissingBundles() { 108 //noinspection unchecked 109 Set<Locale> supportedLanguages = manager.getSupportedLanguages(); 110 for (Locale locale : supportedLanguages) { 111 for(SetMultimap<String, Locale> localeData: this.loadedBundles.values()) { 112 for (String bundleName : new HashSet<>(localeData.keys())) { 113 addMessageBundle(bundleName, locale); 114 } 115 } 116 117 } 118 } 119 120 public void addMessageBundles(String... bundleNames) { 121 for (String bundleName : bundleNames) { 122 //noinspection unchecked 123 Set<Locale> supportedLanguages = manager.getSupportedLanguages(); 124 for (Locale locale : supportedLanguages) { 125 addMessageBundle(bundleName, locale); 126 } 127 } 128 } 129 130 public boolean addMessageBundle(String bundleName, Locale locale) { 131 boolean found = false; 132 for(ClassLoader classLoader: this.registeredClassLoaders) { 133 if(this.addMessageBundle(classLoader, bundleName, locale)) { 134 found = true; 135 } 136 } 137 138 return found; 139 } 140 141 public boolean addMessageBundle(ClassLoader classLoader, String bundleName, Locale locale) { 142 SetMultimap<String, Locale> classLoadersLocales = this.loadedBundles.getOrDefault(classLoader, HashMultimap.create()); 143 if(!classLoadersLocales.containsEntry(bundleName, locale)) { 144 if(this.localeManager.addMessageBundle(classLoader, bundleName, locale)) { 145 classLoadersLocales.put(bundleName, locale); 146 this.loadedBundles.put(classLoader, classLoadersLocales); 147 return true; 148 } 149 } 150 151 return false; 152 } 153 154 public void addMessageStrings(Locale locale, @NotNull Map<String, String> messages) { 155 Map<MessageKey, String> map = new HashMap<>(messages.size()); 156 messages.forEach((key, value) -> map.put(MessageKey.of(key), value)); 157 this.localeManager.addMessages(locale, map); 158 } 159 160 public void addMessages(Locale locale, @NotNull Map<? extends MessageKeyProvider, String> messages) { 161 Map<MessageKey, String> messagesMap = new LinkedHashMap<>(); 162 for (Map.Entry<? extends MessageKeyProvider, String> entry : messages.entrySet()) { 163 messagesMap.put(entry.getKey().getMessageKey(), entry.getValue()); 164 } 165 166 this.localeManager.addMessages(locale, messagesMap); 167 } 168 169 public String addMessage(Locale locale, MessageKeyProvider key, String message) { 170 return this.localeManager.addMessage(locale, key.getMessageKey(), message); 171 } 172 173 public String getMessage(CommandIssuer issuer, MessageKeyProvider key) { 174 final MessageKey msgKey = key.getMessageKey(); 175 String message = this.localeManager.getMessage(issuer, msgKey); 176 if (message == null) { 177 manager.log(LogLevel.ERROR, "Missing Language Key: " + msgKey.getKey()); 178 message = "<MISSING_LANGUAGE_KEY:" + msgKey.getKey() + ">"; 179 } 180 return message; 181 } 182 183 public String getOptionalMessage(CommandIssuer issuer, MessageKey key) { 184 if (issuer == null) { 185 return this.localeManager.getTable(getDefaultLocale()).getMessage(key); 186 } 187 return this.localeManager.getMessage(issuer, key); 188 } 189 190 public String replaceI18NStrings(String message) { 191 if (message == null) { 192 return null; 193 } 194 Matcher matcher = ACFPatterns.I18N_STRING.matcher(message); 195 if (!matcher.find()) { 196 return message; 197 } 198 199 CommandIssuer issuer = CommandManager.getCurrentCommandIssuer(); 200 201 matcher.reset(); 202 StringBuffer sb = new StringBuffer(message.length()); 203 while (matcher.find()) { 204 MessageKey key = MessageKey.of(matcher.group("key")); 205 matcher.appendReplacement(sb, Matcher.quoteReplacement(getMessage(issuer, key))); 206 } 207 matcher.appendTail(sb); 208 return sb.toString(); 209 } 210 211 public boolean addBundleClassLoader(ClassLoader classLoader) { 212 return !this.registeredClassLoaders.contains(classLoader) && this.registeredClassLoaders.add(classLoader); 213 214 } 215}