forked from I2P_Developers/i2p.i2p
Translations:
- Add support for country variants (ticket #1133) - Refactor data in ConfigUIHelper Config files: Allow empty values
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -49,15 +51,30 @@ public class CSSHelper extends HelperBase {
|
||||
return url;
|
||||
}
|
||||
|
||||
/** change default language for the router AND save it */
|
||||
/**
|
||||
* change default language for the router AND save it
|
||||
* @param lang xx OR xx_XX
|
||||
*/
|
||||
public void setLang(String lang) {
|
||||
// Protected with nonce in css.jsi
|
||||
if (lang != null && lang.length() == 2 && !lang.equals(_context.getProperty(Messages.PROP_LANG))) {
|
||||
_context.router().saveConfig(Messages.PROP_LANG, lang);
|
||||
if (lang != null) {
|
||||
Map m = new HashMap(2);
|
||||
if (lang.length() == 2) {
|
||||
m.put(Messages.PROP_LANG, lang.toLowerCase(Locale.US));
|
||||
m.put(Messages.PROP_COUNTRY, "");
|
||||
_context.router().saveConfig(m, null);
|
||||
} else if (lang.length() == 5) {
|
||||
m.put(Messages.PROP_LANG, lang.substring(0, 2).toLowerCase(Locale.US));
|
||||
m.put(Messages.PROP_COUNTRY, lang.substring(3, 5).toUpperCase(Locale.US));
|
||||
_context.router().saveConfig(m, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** needed for conditional css loads for zh */
|
||||
/**
|
||||
* needed for conditional css loads for zh
|
||||
* @return two-letter only, lower-case
|
||||
*/
|
||||
public String getLang() {
|
||||
return Messages.getLanguage(_context);
|
||||
}
|
||||
|
@ -67,36 +67,65 @@ public class ConfigUIHelper extends HelperBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Each language has the ISO code, the flag, and the name.
|
||||
* Each language has the ISO code, the flag, the name, and the optional country name.
|
||||
* Alphabetical by the ISO code please.
|
||||
* See http://en.wikipedia.org/wiki/ISO_639-1 .
|
||||
* Any language-specific flag added to the icon set must be
|
||||
* added to the top-level build.xml for the updater.
|
||||
*/
|
||||
private static final String langs[] = {"ar", "cs", "da", "de", "et", "el", "en", "es", "fi",
|
||||
"fr", "hu", "it", "nb", "nl", "pl", "pt", "ro", "ru",
|
||||
"sv", "tr", "uk", "vi", "zh"};
|
||||
private static final String flags[] = {"lang_ar", "cz", "dk", "de", "ee", "gr", "us", "es", "fi",
|
||||
"fr", "hu", "it", "nl", "no", "pl", "pt", "ro", "ru",
|
||||
"se", "tr", "ua", "vn", "cn"};
|
||||
private static final String xlangs[] = {_x("Arabic"), _x("Czech"), _x("Danish"),
|
||||
_x("German"), _x("Estonian"), _x("Greek"), _x("English"), _x("Spanish"), _x("Finnish"),
|
||||
_x("French"), _x("Hungarian"), _x("Italian"), _x("Dutch"), _x("Norwegian Bokmaal"), _x("Polish"),
|
||||
_x("Portuguese"), _x("Romanian"), _x("Russian"), _x("Swedish"),
|
||||
_x("Turkish"), _x("Ukrainian"), _x("Vietnamese"), _x("Chinese")};
|
||||
private static final String langs[][] = {
|
||||
{ "ar", "lang_ar", _x("Arabic"), null },
|
||||
{ "cs", "cz", _x("Czech"), null },
|
||||
{ "da", "dk", _x("Danish"), null },
|
||||
{ "de", "de", _x("German"), null },
|
||||
{ "et", "ee", _x("Estonian"), null },
|
||||
{ "el", "gr", _x("Greek"), null },
|
||||
{ "en", "us", _x("English"), null },
|
||||
{ "es", "es", _x("Spanish"), null },
|
||||
{ "fi", "fi", _x("Finnish"), null },
|
||||
{ "fr", "fr", _x("French"), null },
|
||||
{ "hu", "hu", _x("Hungarian"), null },
|
||||
{ "it", "it", _x("Italian"), null },
|
||||
{ "nb", "nl", _x("Dutch"), null },
|
||||
{ "nl", "no", _x("Norwewgian Bokmaal"), null },
|
||||
{ "pl", "pl", _x("Polish"), null },
|
||||
{ "pt", "pt", _x("Portuguese"), null },
|
||||
// { "pt_BR", "br", _x("Portuguese"), "Brazil" },
|
||||
{ "ro", "ro", _x("Romainian"), null },
|
||||
{ "ru", "ru", _x("Russian"), null },
|
||||
{ "sv", "se", _x("Swedish"), null },
|
||||
{ "tr", "tr", _x("Turkish"), null },
|
||||
{ "uk", "ua", _x("Ukrainian"), null },
|
||||
{ "vi", "vn", _x("Vietnamese"), null },
|
||||
{ "zh", "cn", _x("Chinese"), null }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** todo sort by translated string */
|
||||
public String getLangSettings() {
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
String current = Messages.getLanguage(_context);
|
||||
String country = Messages.getCountry(_context);
|
||||
if (country != null && country.length() > 0)
|
||||
current += '_' + country;
|
||||
for (int i = 0; i < langs.length; i++) {
|
||||
// we use "lang" so it is set automagically in CSSHelper
|
||||
buf.append("<input type=\"radio\" class=\"optbox\" name=\"lang\" ");
|
||||
if (langs[i].equals(current))
|
||||
String lang = langs[i][0];
|
||||
if (lang.equals(current))
|
||||
buf.append("checked=\"checked\" ");
|
||||
buf.append("value=\"").append(langs[i]).append("\">")
|
||||
.append("<img height=\"11\" width=\"16\" alt=\"\" src=\"/flags.jsp?c=").append(flags[i]).append("\"> ")
|
||||
.append(Messages.getDisplayLanguage(langs[i], xlangs[i], _context)).append("<br>\n");
|
||||
buf.append("value=\"").append(lang).append("\">")
|
||||
.append("<img height=\"11\" width=\"16\" alt=\"\" src=\"/flags.jsp?c=").append(langs[i][1]).append("\"> ");
|
||||
String slang = lang.length() > 2 ? lang.substring(0, 2) : lang;
|
||||
buf.append(Messages.getDisplayLanguage(slang, langs[i][2], _context));
|
||||
String name = langs[i][3];
|
||||
if (name != null) {
|
||||
buf.append(" (")
|
||||
.append(Messages.getString(name, _context, Messages.COUNTRY_BUNDLE_NAME))
|
||||
.append(')');
|
||||
}
|
||||
buf.append("<br>\n");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import net.i2p.util.Translate;
|
||||
public class Messages extends Translate {
|
||||
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
|
||||
|
||||
static final String COUNTRY_BUNDLE_NAME = "net.i2p.router.countries.messages";
|
||||
|
||||
/** lang in routerconsole.lang property, else current locale */
|
||||
public static String getString(String key, I2PAppContext ctx) {
|
||||
return Translate.getString(key, ctx, BUNDLE_NAME);
|
||||
|
@ -350,8 +350,6 @@ public class NetDbRenderer {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private static final String COUNTRY_BUNDLE_NAME = "net.i2p.router.countries.messages";
|
||||
|
||||
/**
|
||||
* Countries now in a separate bundle
|
||||
* @param code two-letter country code
|
||||
@ -359,7 +357,7 @@ public class NetDbRenderer {
|
||||
*/
|
||||
private String getTranslatedCountry(String code) {
|
||||
String name = _context.commSystem().getCountryName(code);
|
||||
return Translate.getString(name, _context, COUNTRY_BUNDLE_NAME);
|
||||
return Translate.getString(name, _context, Messages.COUNTRY_BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/** sort by translated country name using rules for the current language setting */
|
||||
|
@ -399,6 +399,7 @@ public class DataHelper {
|
||||
* - Leading whitespace is not trimmed
|
||||
* - '=' is the only key-termination character (not ':' or whitespace)
|
||||
*
|
||||
* As of 0.9.10, an empty value is allowed.
|
||||
*/
|
||||
public static void loadProps(Properties props, File file) throws IOException {
|
||||
loadProps(props, file, false);
|
||||
@ -442,7 +443,8 @@ public class DataHelper {
|
||||
// it was a horrible idea anyway
|
||||
//val = val.replaceAll("\\\\r","\r");
|
||||
//val = val.replaceAll("\\\\n","\n");
|
||||
if ( (key.length() > 0) && (val.length() > 0) )
|
||||
|
||||
// as of 0.9.10, an empty value is allowed
|
||||
if (forceLowerCase)
|
||||
props.setProperty(key.toLowerCase(Locale.US), val);
|
||||
else
|
||||
|
@ -25,7 +25,12 @@ import net.i2p.util.ConcurrentHashSet;
|
||||
*/
|
||||
public abstract class Translate {
|
||||
public static final String PROP_LANG = "routerconsole.lang";
|
||||
/** @since 0.9.10 */
|
||||
public static final String PROP_COUNTRY = "routerconsole.country";
|
||||
/** non-null, two-letter lower case, may be "" */
|
||||
private static final String _localeLang = Locale.getDefault().getLanguage();
|
||||
/** non-null, two-letter upper case, may be "" */
|
||||
private static final String _localeCountry = Locale.getDefault().getCountry();
|
||||
private static final Map<String, ResourceBundle> _bundles = new ConcurrentHashMap(16);
|
||||
private static final Set<String> _missing = new ConcurrentHashSet(16);
|
||||
/** use to look for untagged strings */
|
||||
@ -42,7 +47,7 @@ public abstract class Translate {
|
||||
// shouldnt happen but dont dump the po headers if it does
|
||||
if (key.equals(""))
|
||||
return key;
|
||||
ResourceBundle bundle = findBundle(bun, lang);
|
||||
ResourceBundle bundle = findBundle(bun, lang, getCountry(ctx));
|
||||
if (bundle == null)
|
||||
return key;
|
||||
try {
|
||||
@ -110,7 +115,7 @@ public abstract class Translate {
|
||||
return TEST_STRING + '(' + n + ')' + TEST_STRING;
|
||||
ResourceBundle bundle = null;
|
||||
if (!lang.equals("en"))
|
||||
bundle = findBundle(bun, lang);
|
||||
bundle = findBundle(bun, lang, getCountry(ctx));
|
||||
String x;
|
||||
if (bundle == null)
|
||||
x = n == 1 ? s : p;
|
||||
@ -129,7 +134,10 @@ public abstract class Translate {
|
||||
}
|
||||
}
|
||||
|
||||
/** @return lang in routerconsole.lang property, else current locale */
|
||||
/**
|
||||
* Two-letter lower case
|
||||
* @return lang in routerconsole.lang property, else current locale
|
||||
*/
|
||||
public static String getLanguage(I2PAppContext ctx) {
|
||||
String lang = ctx.getProperty(PROP_LANG);
|
||||
if (lang == null || lang.length() <= 0)
|
||||
@ -137,14 +145,39 @@ public abstract class Translate {
|
||||
return lang;
|
||||
}
|
||||
|
||||
/** cache both found and not found for speed */
|
||||
private static ResourceBundle findBundle(String bun, String lang) {
|
||||
String key = bun + '-' + lang;
|
||||
/**
|
||||
* Two-letter upper case or ""
|
||||
* @return country in routerconsole.country property, else current locale
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public static String getCountry(I2PAppContext ctx) {
|
||||
// property may be empty so we don't have a non-default
|
||||
// language and a default country
|
||||
return ctx.getProperty(PROP_COUNTRY, _localeCountry);
|
||||
}
|
||||
|
||||
/**
|
||||
* cache both found and not found for speed
|
||||
* @param lang non-null, if "" returns null
|
||||
* @param country non-null, may be ""
|
||||
* @return null if not found
|
||||
*/
|
||||
private static ResourceBundle findBundle(String bun, String lang, String country) {
|
||||
String key = bun + '-' + lang + '-' + country;
|
||||
ResourceBundle rv = _bundles.get(key);
|
||||
if (rv == null && !_missing.contains(key)) {
|
||||
if ("".equals(lang)) {
|
||||
_missing.add(key);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Locale loc;
|
||||
if ("".equals(country))
|
||||
loc = new Locale(lang);
|
||||
else
|
||||
loc = new Locale(lang, country);
|
||||
// We must specify the class loader so that a webapp can find the bundle in the .war
|
||||
rv = ResourceBundle.getBundle(bun, new Locale(lang), Thread.currentThread().getContextClassLoader());
|
||||
rv = ResourceBundle.getBundle(bun, loc, Thread.currentThread().getContextClassLoader());
|
||||
if (rv != null)
|
||||
_bundles.put(key, rv);
|
||||
} catch (MissingResourceException e) {
|
||||
|
Reference in New Issue
Block a user