2012-09-16 10:47:41 +02:00
|
|
|
package net.citizensnpcs.util;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.FileNotFoundException;
|
2012-10-08 04:16:19 +02:00
|
|
|
import java.io.FileOutputStream;
|
2012-10-04 12:58:27 +02:00
|
|
|
import java.io.IOException;
|
2012-09-16 10:47:41 +02:00
|
|
|
import java.io.InputStream;
|
2012-10-08 04:16:19 +02:00
|
|
|
import java.io.OutputStream;
|
2012-09-16 10:47:41 +02:00
|
|
|
import java.net.MalformedURLException;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.text.MessageFormat;
|
2012-10-08 04:16:19 +02:00
|
|
|
import java.util.ListResourceBundle;
|
2012-09-16 10:47:41 +02:00
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Map;
|
2012-10-08 04:16:19 +02:00
|
|
|
import java.util.Map.Entry;
|
2012-09-16 10:47:41 +02:00
|
|
|
import java.util.MissingResourceException;
|
2012-10-08 04:16:19 +02:00
|
|
|
import java.util.Properties;
|
|
|
|
import java.util.PropertyResourceBundle;
|
2012-09-16 10:47:41 +02:00
|
|
|
import java.util.ResourceBundle;
|
|
|
|
|
|
|
|
import com.google.common.collect.Maps;
|
2012-10-08 04:16:19 +02:00
|
|
|
import com.google.common.io.Closeables;
|
2012-10-04 12:58:27 +02:00
|
|
|
import com.google.common.io.Files;
|
|
|
|
import com.google.common.io.InputSupplier;
|
2012-09-16 10:47:41 +02:00
|
|
|
|
|
|
|
public class Translator {
|
2012-10-04 11:28:42 +02:00
|
|
|
private final Locale defaultLocale;
|
2012-09-16 10:47:41 +02:00
|
|
|
private final Map<String, MessageFormat> messageFormatCache = Maps.newHashMap();
|
2012-10-04 11:28:42 +02:00
|
|
|
private ResourceBundle preferredBundle;
|
2012-09-16 13:20:01 +02:00
|
|
|
private final File resourceFile;
|
2012-09-16 10:47:41 +02:00
|
|
|
|
2012-09-16 13:20:01 +02:00
|
|
|
private Translator(File resourceFile, Locale locale) {
|
|
|
|
this.resourceFile = resourceFile;
|
2012-10-04 11:28:42 +02:00
|
|
|
this.defaultLocale = locale;
|
2012-09-16 10:47:41 +02:00
|
|
|
try {
|
2012-10-04 11:28:42 +02:00
|
|
|
preferredBundle = ResourceBundle.getBundle(PREFIX, defaultLocale, new FileClassLoader(
|
|
|
|
Translator.class.getClassLoader(), resourceFile));
|
2012-09-16 10:47:41 +02:00
|
|
|
} catch (MissingResourceException e) {
|
2012-10-04 11:28:42 +02:00
|
|
|
preferredBundle = getDefaultBundle();
|
2012-10-04 12:28:23 +02:00
|
|
|
Messaging.logTr(Messages.MISSING_TRANSLATIONS, locale);
|
2012-09-16 10:47:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-04 11:28:42 +02:00
|
|
|
private String format(String key, Locale locale, Object... msg) {
|
|
|
|
String unreplaced = translate(key, locale);
|
2012-09-16 10:47:41 +02:00
|
|
|
MessageFormat formatter = getFormatter(unreplaced);
|
|
|
|
return formatter.format(msg);
|
|
|
|
}
|
|
|
|
|
2012-10-04 11:28:42 +02:00
|
|
|
private ResourceBundle getBundle(Locale locale) {
|
|
|
|
try {
|
|
|
|
ResourceBundle bundle = ResourceBundle.getBundle(PREFIX, locale, new FileClassLoader(
|
|
|
|
Translator.class.getClassLoader(), resourceFile));
|
|
|
|
return bundle == null ? preferredBundle : bundle;
|
|
|
|
} catch (MissingResourceException e) {
|
|
|
|
return preferredBundle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-16 10:47:41 +02:00
|
|
|
private ResourceBundle getDefaultBundle() {
|
2012-10-08 04:16:19 +02:00
|
|
|
return getDefaultResourceBundle(resourceFile, PREFIX + "_en.properties");
|
2012-09-16 10:47:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private MessageFormat getFormatter(String unreplaced) {
|
|
|
|
MessageFormat formatter = messageFormatCache.get(unreplaced);
|
|
|
|
if (formatter == null)
|
|
|
|
messageFormatCache.put(unreplaced, formatter = new MessageFormat(unreplaced));
|
|
|
|
return formatter;
|
|
|
|
}
|
|
|
|
|
2012-10-04 11:28:42 +02:00
|
|
|
private String translate(String key, Locale locale) {
|
|
|
|
ResourceBundle bundle = preferredBundle;
|
|
|
|
if (locale != defaultLocale)
|
|
|
|
bundle = getBundle(locale);
|
2012-09-16 10:47:41 +02:00
|
|
|
try {
|
|
|
|
return bundle.getString(key);
|
|
|
|
} catch (MissingResourceException e) {
|
|
|
|
try {
|
|
|
|
return getDefaultBundle().getString(key);
|
|
|
|
} catch (MissingResourceException ex) {
|
|
|
|
return "?" + key + "?";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static class FileClassLoader extends ClassLoader {
|
|
|
|
private final File folder;
|
|
|
|
|
|
|
|
public FileClassLoader(ClassLoader classLoader, File folder) {
|
|
|
|
super(classLoader);
|
|
|
|
this.folder = folder;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public URL getResource(String string) {
|
|
|
|
File file = new File(folder, string);
|
|
|
|
if (file.exists()) {
|
|
|
|
try {
|
|
|
|
return file.toURI().toURL();
|
|
|
|
} catch (MalformedURLException ex) {
|
|
|
|
}
|
2012-10-04 12:28:23 +02:00
|
|
|
} else {
|
|
|
|
string = string.replaceFirst("/", "");
|
|
|
|
URL test = Translator.class.getResource('/' + string);
|
|
|
|
if (test != null)
|
|
|
|
return test;
|
2012-09-16 10:47:41 +02:00
|
|
|
}
|
|
|
|
return super.getResource(string);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public InputStream getResourceAsStream(String string) {
|
|
|
|
File file = new File(folder, string);
|
|
|
|
if (file.exists()) {
|
|
|
|
try {
|
|
|
|
return new FileInputStream(file);
|
|
|
|
} catch (FileNotFoundException ex) {
|
|
|
|
}
|
2012-10-04 12:28:23 +02:00
|
|
|
} else {
|
|
|
|
string = string.replaceFirst("/", "");
|
|
|
|
InputStream stream = Translator.class.getResourceAsStream('/' + string);
|
2012-10-04 12:58:27 +02:00
|
|
|
if (stream != null) {
|
|
|
|
new Thread(new SaveResourceThread(folder, string)).start();
|
2012-10-04 12:28:23 +02:00
|
|
|
return stream;
|
2012-10-04 12:58:27 +02:00
|
|
|
}
|
2012-09-16 10:47:41 +02:00
|
|
|
}
|
|
|
|
return super.getResourceAsStream(string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-04 12:58:27 +02:00
|
|
|
private static class SaveResourceThread implements Runnable {
|
|
|
|
private final String fileName;
|
|
|
|
private final File rootFolder;
|
|
|
|
|
|
|
|
private SaveResourceThread(File rootFolder, String fileName) {
|
|
|
|
this.rootFolder = rootFolder;
|
|
|
|
this.fileName = fileName;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
File file = new File(rootFolder, fileName);
|
|
|
|
if (file.exists())
|
|
|
|
return;
|
|
|
|
final InputStream stream = Translator.class.getResourceAsStream('/' + fileName);
|
|
|
|
if (stream == null)
|
|
|
|
return;
|
|
|
|
InputSupplier<InputStream> in = new InputSupplier<InputStream>() {
|
|
|
|
@Override
|
|
|
|
public InputStream getInput() throws IOException {
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
try {
|
2012-10-05 14:15:06 +02:00
|
|
|
rootFolder.mkdirs();
|
2012-10-04 12:58:27 +02:00
|
|
|
File to = File.createTempFile(fileName, null, rootFolder);
|
|
|
|
to.deleteOnExit();
|
|
|
|
Files.copy(in, to);
|
|
|
|
if (!file.exists())
|
|
|
|
to.renameTo(file);
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-08 04:16:19 +02:00
|
|
|
private static ResourceBundle defaultBundle;
|
|
|
|
|
2012-09-16 10:47:41 +02:00
|
|
|
private static Translator instance;
|
2012-10-08 04:16:19 +02:00
|
|
|
|
2012-09-16 10:47:41 +02:00
|
|
|
public static final String PREFIX = "messages";
|
2012-09-30 11:55:51 +02:00
|
|
|
|
2012-10-08 04:16:19 +02:00
|
|
|
private static Properties getDefaultBundleProperties() {
|
|
|
|
Properties defaults = new Properties();
|
|
|
|
InputStream in = null;
|
|
|
|
try {
|
|
|
|
in = Messages.class.getResourceAsStream("/" + PREFIX + "_en.properties");
|
|
|
|
defaults.load(in);
|
|
|
|
} catch (IOException e) {
|
|
|
|
} finally {
|
|
|
|
Closeables.closeQuietly(in);
|
|
|
|
}
|
|
|
|
return defaults;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static ResourceBundle getDefaultResourceBundle(File resourceDirectory, String fileName) {
|
|
|
|
if (Translator.defaultBundle != null)
|
|
|
|
return Translator.defaultBundle;
|
|
|
|
resourceDirectory.mkdirs();
|
|
|
|
|
|
|
|
File bundleFile = new File(resourceDirectory, fileName);
|
|
|
|
if (!bundleFile.exists()) {
|
|
|
|
try {
|
|
|
|
bundleFile.createNewFile();
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Translator.populateDefaults(bundleFile);
|
|
|
|
FileInputStream stream = null;
|
|
|
|
try {
|
|
|
|
stream = new FileInputStream(bundleFile);
|
|
|
|
Translator.defaultBundle = new PropertyResourceBundle(stream);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
Translator.defaultBundle = Translator.getFallbackResourceBundle();
|
|
|
|
} finally {
|
|
|
|
Closeables.closeQuietly(stream);
|
|
|
|
}
|
|
|
|
return Translator.defaultBundle;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static ResourceBundle getFallbackResourceBundle() {
|
|
|
|
return new ListResourceBundle() {
|
|
|
|
@Override
|
|
|
|
protected Object[][] getContents() {
|
|
|
|
return new Object[0][0];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void populateDefaults(File bundleFile) {
|
|
|
|
Properties properties = new Properties();
|
|
|
|
InputStream in = null;
|
|
|
|
try {
|
|
|
|
in = new FileInputStream(bundleFile);
|
|
|
|
properties.load(in);
|
|
|
|
} catch (IOException e) {
|
|
|
|
} finally {
|
|
|
|
Closeables.closeQuietly(in);
|
|
|
|
}
|
|
|
|
Properties defaults = getDefaultBundleProperties();
|
|
|
|
for (Entry<Object, Object> entry : defaults.entrySet()) {
|
|
|
|
if (!properties.containsKey(entry.getKey()))
|
|
|
|
properties.put(entry.getKey(), entry.getValue());
|
|
|
|
}
|
|
|
|
OutputStream stream = null;
|
|
|
|
try {
|
|
|
|
stream = new FileOutputStream(bundleFile);
|
|
|
|
properties.store(stream, "");
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} finally {
|
|
|
|
Closeables.closeQuietly(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-16 13:20:01 +02:00
|
|
|
public static void setInstance(File resourceFile, Locale locale) {
|
|
|
|
instance = new Translator(resourceFile, locale);
|
|
|
|
}
|
|
|
|
|
2012-10-08 04:16:19 +02:00
|
|
|
public static String translate(String key, Locale preferredLocale, Object... msg) {
|
2012-10-04 11:28:42 +02:00
|
|
|
return StringHelper.parseColors(msg.length == 0 ? instance.translate(key, preferredLocale) : instance
|
|
|
|
.format(key, preferredLocale, msg));
|
|
|
|
}
|
|
|
|
|
2012-10-08 04:16:19 +02:00
|
|
|
public static String translate(String key, Object... msg) {
|
|
|
|
return translate(key, instance.defaultLocale, msg);
|
2012-09-16 10:47:41 +02:00
|
|
|
}
|
|
|
|
}
|