package com.constellio.app.ui.i18n;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.app.ui.application.ConstellioUI;
import com.constellio.model.conf.FoldersLocator;
import com.constellio.model.entities.EnumWithSmallCode;
import com.constellio.model.entities.Language;
import com.constellio.model.frameworks.validation.ValidationError;
import com.constellio.model.frameworks.validation.ValidationErrors;
import com.constellio.model.utils.i18n.Utf8ResourceBundles;
public class i18n {
private static Logger LOGGER = LoggerFactory.getLogger(i18n.class);
// TODO Use a languageCode->Locale Map instead?
private static Locale locale;
private static Utf8ResourceBundles defaultBundle = null;
private static List<Utf8ResourceBundles> registeredBundles = new ArrayList<>();
public static Locale getLocale() {
try {
return ConstellioUI.getCurrentSessionContext().getCurrentLocale();
} catch (Throwable e) {
//LOGGER.warn("error when trying to get session locale", e);
}
return locale;
}
public static void setLocale(Locale locale) {
i18n.locale = locale;
try {
ConstellioUI.getCurrentSessionContext().setCurrentLocale(locale);
} catch (Throwable e) {
//LOGGER.warn("error when trying to set session locale", e);
}
}
public static String $(String key) {
return $(key, (Object) null);
}
public static String $(String key, Object... args) {
String message = null;
if (key == null) {
return "";
}
for (Utf8ResourceBundles bundle : getBundles()) {
ResourceBundle messages = bundle.getBundle(getLocale());
if (messages.containsKey(key)) {
message = messages.getString(key);
if (args != null) {
try {
message = MessageFormat.format(message, args);
} catch (Exception e) {
// Ignore, return the original message before the formatting attempt
}
}
}
}
if (message == null) {
message = key;
}
return message;
}
public static String $(String key, Locale locale) {
String message = null;
if (key == null) {
return "";
}
for (Utf8ResourceBundles bundle : getBundles()) {
ResourceBundle messages = bundle.getBundle(locale);
if (messages.containsKey(key)) {
message = messages.getString(key);
}
}
if (message == null) {
message = key;
}
return message;
}
public static String $(String key, Map<String, Object> args) {
String message = null;
if (key == null) {
return "";
}
for (Utf8ResourceBundles bundle : getBundles()) {
ResourceBundle messages = bundle.getBundle(getLocale());
if (messages.containsKey(key)) {
message = messages.getString(key);
if (args.get("prefix") != null) {
message = args.get("prefix") + message;
}
if (args.get("suffix") != null) {
message = message + args.get("suffix");
}
if (args != null) {
for (String argName : args.keySet()) {
Object argValue = args.get(argName);
if (argValue instanceof String) {
message = message.replace("{" + argName + "}", (String) argValue);
} else if (argValue instanceof Number) {
message = message.replace("{" + argName + "}", "" + argValue);
} else if (argValue instanceof Map) {
/* TODO Manage Map value here:
- Must fetch the entry for the current language.
*/
Map<String, String> labelsMap = (Map<String, String>) argValue;
String language = getLocale().getLanguage();
message = message.replace("{" + argName + "}", labelsMap.get(language));
} else if (argValue instanceof EnumWithSmallCode) {
EnumWithSmallCode enumWithSmallCode = (EnumWithSmallCode) argValue;
message = message.replace("{" + argName + "}",
$(enumWithSmallCode.getClass().getSimpleName() + "." + enumWithSmallCode.getCode()));
} else if (argValue instanceof Boolean) {
message = message.replace("{" + argName + "}",
$(argValue.toString()));
} else if (argValue instanceof Enum) {
Enum anEnum = (Enum) argValue;
message = message.replace("{" + argName + "}",
$(anEnum.getClass().getSimpleName() + "." + anEnum.name()));
} else {
message = message.replace("{" + argName + "}", "");
}
}
}
}
}
if (message != null && message.toLowerCase().startsWith("jexl:")) {
try {
message = callJexlScript(message.substring(5), args);
} catch (Exception e) {
LOGGER.warn("Script failure '" + message.substring(5) + "'", e);
message = null;
}
}
if (message == null) {
if (args == null || args.isEmpty()) {
message = key;
} else {
message = key + " " + args.toString();
}
}
return message;
}
private static String callJexlScript(String expression, Map<String, Object> args)
throws Exception {
JexlEngine jexl = new JexlBuilder().create();
JexlScript jexlScript = jexl.createScript(expression);
JexlContext jc = prepareJexlContext(args);
Object calculatedValue = jexlScript.execute(jc);
return "null".equals(calculatedValue) ? null : (String) calculatedValue;
}
private static JexlContext prepareJexlContext(Map<String, Object> args) {
JexlContext jc = new MapContext();
for (Map.Entry<String, Object> entry : args.entrySet()) {
jc.set(entry.getKey(), entry.getValue());
}
jc.set("i18n", new Map<String, String>() {
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
return get(key) != null;
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public String get(Object key) {
return $((String) key);
}
@Override
public String put(String key, String value) {
throw new UnsupportedOperationException();
}
@Override
public String remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map m) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> keySet() {
throw new UnsupportedOperationException();
}
@Override
public Collection<String> values() {
throw new UnsupportedOperationException();
}
@Override
public Set<Entry<String, String>> entrySet() {
throw new UnsupportedOperationException();
}
});
return jc;
}
public static String $(ValidationErrors errors) {
StringBuilder sb = new StringBuilder();
for (ValidationError error : errors.getValidationErrors()) {
sb.append(" - " + $(error) + "<br/>");
}
return sb.toString();
}
public static List<String> asListOfMessages(ValidationErrors errors) {
List<String> messages = new ArrayList<>();
for (ValidationError error : errors.getValidationErrors()) {
messages.add($(error));
}
for (ValidationError error : errors.getValidationWarnings()) {
messages.add($(error));
}
return messages;
}
public static List<String> asListOfMessages(List<ValidationError> errors) {
List<String> messages = new ArrayList<>();
for (ValidationError error : errors) {
messages.add($(error));
}
return messages;
}
public static List<String> asListOfMessages(ValidationErrors errors, Object... params) {
List<String> messages = new ArrayList<>();
for (ValidationError error : errors.getValidationErrors()) {
messages.add($(error.getCode(), params));
}
return messages;
}
public static String $(ValidationError error) {
String key = error.getCode();
Map<String, Object> args = error.getParameters();
return $(key, args);
}
public static String $(Throwable throwable) {
return $(throwable, (Object) null);
}
public static String $(Throwable throwable, Object... args) {
return $(throwable.getMessage(), args);
}
private static List<Utf8ResourceBundles> getBundles() {
List<Utf8ResourceBundles> bundles = new ArrayList<>();
bundles.add(getDefaultBundle());
bundles.addAll(registeredBundles);
return bundles;
}
public static Utf8ResourceBundles getDefaultBundle() {
if (defaultBundle == null) {
defaultBundle = Utf8ResourceBundles.forPropertiesFile(new FoldersLocator().getI18nFolder(), "i18n");
}
return defaultBundle;
}
public static void registerBundle(File bundleFolder, String bundleName) {
File bundleProperties = new File(bundleFolder, bundleName + ".properties");
if (!bundleProperties.exists()) {
throw new RuntimeException("No such file '" + bundleProperties.getAbsolutePath() + "'");
}
registeredBundles.add(Utf8ResourceBundles.forPropertiesFile(bundleFolder, bundleName));
}
public static void registerBundle(Utf8ResourceBundles bundle) {
if (bundle != null) {
registeredBundles.add(bundle);
}
}
public static List<String> getSupportedLanguages() {
List<String> localeCodes = new ArrayList<>();
Language[] languages = Language.values();
for (Language language : languages) {
if (language != Language.UNKNOWN) {
String localeCode = language.getCode();
localeCodes.add(localeCode);
}
}
return localeCodes;
}
public static void clearBundles() {
registeredBundles.clear();
}
public static Language getLanguage() {
Locale loc = getLocale();
Language[] languages = Language.values();
for (Language language : languages) {
if (loc.getLanguage().equals(language.getCode())) {
return language;
}
}
throw new RuntimeException(
"Current locale" + loc + " does not correspond to any language" + StringUtils.join(languages, ","));
}
}