package net.krazyweb.starmodmanager.data;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import com.ibm.icu.text.MessageFormat;
public class Localizer implements LocalizerModelInterface, Observer {
private static final Logger log = LogManager.getLogger(Localizer.class);
private static boolean overrideLanguage = false;
private static String languageFile = "";
private static String languageLocale = "";
private List<Language> languages;
private Locale locale;
private ResourceBundle bundle;
private SettingsModelInterface settings;
private SettingsModelFactory settingsFactory;
private Set<Observer> observers;
protected Localizer(final SettingsModelFactory settingsFactory) {
observers = new HashSet<>();
this.settingsFactory = settingsFactory;
}
@Override
public Task<Void> getInitializerTask() {
settings = settingsFactory.getInstance();
settings.addObserver(this);
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
this.updateMessage("Loading Localizer");
this.updateProgress(0.0, 1.0);
setLocale(settings.getPropertyString("locale"));
languages = new ArrayList<>();
Collections.addAll(languages,
new Language("en-US", "English"),
new Language("de-DE", "Deutsch")
//new Language("fl-SB", "Floran")
);
Collections.sort(languages);
this.updateProgress(1.0, 1.0);
return null;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(final WorkerStateEvent event) {
notifyObservers("localizerloaded");
}
});
return task;
}
@Override
public String getMessage(final String key, final boolean suppressLogging) {
String output = "";
try {
output = bundle.getString(key.toLowerCase());
} catch (final NullPointerException e) {
if (!suppressLogging) {
log.warn("Localization key is null.");
}
return "Localization key is null.";
} catch (final MissingResourceException e) {
if (!suppressLogging) {
log.warn("Key '{}' not found.", key);
}
return "Key '" + key + "' not found.";
} catch (final ClassCastException e) {
if (!suppressLogging) {
log.warn("Value found for key '{}' is not a String.", key);
}
return "Value found for key '" + key + "' is not a String.";
}
String formatted = "CHAR ENCODING ERROR";
try {
formatted = new String(output.getBytes("ISO-8859-1"), "UTF-8");
} catch (final UnsupportedEncodingException e) {
if (!suppressLogging) {
log.error(new ParameterizedMessage("Could not encode '{}' to UTF-8.", output), e);
}
}
if (!suppressLogging) {
log.debug("String '{}' converted to '{}'.", output, formatted);
}
return formatted;
}
@Override
public String getMessage(final String key) {
return getMessage(key, false);
}
@Override
public String formatMessage(final boolean suppressLogging, final String key, final Object... messageArguments) {
MessageFormat formatter = null;
try {
formatter = new MessageFormat(bundle.getString(key.toLowerCase()), locale);
} catch (final IllegalArgumentException | MissingResourceException e) {
if (!suppressLogging) {
log.error(new ParameterizedMessage("Could not parse pattern for '{}'", key.toLowerCase()), e);
}
}
if (formatter == null) {
return "INVALID PROPERTY: " + key;
}
formatter.setLocale(locale);
String formatted = "CHAR ENCODING ERROR";
try {
formatted = new String(formatter.format(messageArguments).getBytes("ISO-8859-1"), "UTF-8");
} catch (final UnsupportedEncodingException e) {
if (!suppressLogging) {
log.error(new ParameterizedMessage("Could not encode '{}' to UTF-8.", formatter.format(messageArguments)), e);
}
}
if (!suppressLogging) {
log.debug("String '{}' encoded to '{}'.", formatter.format(messageArguments), formatted);
}
return formatted;
}
@Override
public String formatMessage(final String key, final Object... messageArguments) {
return formatMessage(false, key, messageArguments);
}
@Override
public List<Language> getLanguages() {
return new ArrayList<>(languages);
}
@Override
public Language getCurrentLanguage() {
String loc = settings.getPropertyString("locale");
for (Language l : languages) {
if (l.getLocale().equals(loc)) {
return l;
}
}
return null;
}
private void setLocale(final String loc) {
if (overrideLanguage) {
log.debug("Language Overridden!");
try {
File file = Paths.get(languageFile).toAbsolutePath().getParent().toFile();
URL[] urls = {file.toURI().toURL()};
ClassLoader loader = new URLClassLoader(urls);
log.debug("{}", file);
String[] splitLocale = languageLocale.split("-");
locale = new Locale(splitLocale[0], splitLocale[1]);
bundle = ResourceBundle.getBundle("strings", locale, loader);
notifyObservers("localechanged");
} catch (final MalformedURLException e) {
log.error("Could not load provided language file '{}'", languageFile);
}
} else {
String[] splitLocale = loc.split("-");
Locale oldLocale = locale;
locale = new Locale(splitLocale[0], splitLocale[1]);
//Don't reload the language if nothing changed
if (oldLocale == null || !oldLocale.equals(locale)) {
bundle = ResourceBundle.getBundle("strings", locale);
log.debug("Locale set to: {}", locale);
notifyObservers("localechanged");
}
}
}
public static void overrideLanguage(final String file, final String locale) {
overrideLanguage = true;
languageFile = file;
languageLocale = locale;
}
@Override
public void update(final Observable observable, final Object message) {
if (observable instanceof Settings && message.equals("propertychanged:locale")) {
log.debug("Locale changed message received.");
setLocale(settings.getPropertyString("locale"));
}
}
@Override
public void addObserver(final Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(final Observer observer) {
observers.remove(observer);
}
private final void notifyObservers(final String message) {
for (final Observer o : observers) {
o.update(this, (Object) message);
}
}
}