/** * Copyright 2011 multibit.org * * Licensed under the MIT license (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://opensource.org/licenses/mit-license.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.multibit; import com.google.bitcoin.core.Utils; import org.joda.money.BigMoney; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.math.BigDecimal; import java.math.BigInteger; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.*; /** * Class used for producing localised messages it contains a resource bundle and * has some helper functions to do message formatting. * * It also pulls in a file language.properties which lists the number of * languages and language codes. * * @author jim * */ public class Localiser { private static final Logger log = LoggerFactory.getLogger(Localiser.class); public static final String MULTIBIT_RESOURCE_BUNDLE_DIRECTORY = "/i18n"; public static final String MULTIBIT_RESOURCE_BUNDLE_NAME = "viewer"; public static final String SEPARATOR = "_"; public static final String PROPERTY_NAME_SUFFIX = ".properties"; public static final String VERSION_PROPERTY_KEY_NAME = "version"; public static final String VERSION_PROPERTIES_FILENAME = "/version.properties"; public static final String LANGUAGE_PROPERTIES_FILENAME = MULTIBIT_RESOURCE_BUNDLE_DIRECTORY + "/language.properties"; public static final String FALLBACK_LANGUAGE_CODE = "en"; private ResourceBundle resourceBundle; private MessageFormat formatter; private Properties versionProperties; private Properties languageProperties; private Locale locale; private final static String MISSING_RESOURCE_TEXT = "Missing resource : "; private NumberFormat numberFormat; private NumberFormat numberFormatNotLocalised; public static final int NUMBER_OF_FRACTION_DIGITS_FOR_BITCOIN = 8; private java.text.DecimalFormatSymbols decimalFormatSymbols; /** * Localiser hardwired to English - mainly for testing */ public Localiser() { this(new Locale("en")); } /** * Create a Localiser using a ResourceBundle based on the specified * 'bundleName' with Locale 'locale'. * * @param locale */ public Localiser(Locale locale) { formatter = new MessageFormat(""); languageProperties = new Properties(); try { java.net.URL languagePropertiesURL = Localiser.class.getResource(LANGUAGE_PROPERTIES_FILENAME); if (languagePropertiesURL != null) { languageProperties.load(languagePropertiesURL.openStream()); } } catch (IOException ioe) { log.error(ioe.getMessage(), ioe); } setLocale(locale); numberFormat = NumberFormat.getInstance(locale); numberFormat.setMaximumFractionDigits(NUMBER_OF_FRACTION_DIGITS_FOR_BITCOIN); numberFormatNotLocalised = NumberFormat.getInstance(Locale.ENGLISH); numberFormatNotLocalised.setMaximumFractionDigits(NUMBER_OF_FRACTION_DIGITS_FOR_BITCOIN); numberFormatNotLocalised.setGroupingUsed(false); decimalFormatSymbols = new java.text.DecimalFormatSymbols(locale); } synchronized public String getString(String key) { if (key == null) { return ""; } // See if it is the number of languages or a language code. if (languageProperties != null) { String toReturn = (String) languageProperties.get(key); if (toReturn != null) { return toReturn; } } if (resourceBundle != null) { try { return resourceBundle.getString(key); } catch (NullPointerException npe) { return MISSING_RESOURCE_TEXT + key + " (npe)"; } catch (ClassCastException cce) { return MISSING_RESOURCE_TEXT + key + " (cce)"; } catch (MissingResourceException mre) { return MISSING_RESOURCE_TEXT + key + " (mre)"; } } else { return MISSING_RESOURCE_TEXT + key; } } synchronized public String getString(String key, Object[] parameters) { if (key == null) { return ""; } if (resourceBundle != null) { try { String pattern = resourceBundle.getString(key); // Change any apostrophes to \u2032 as MessageFormatter swallows them pattern = pattern.replaceAll("\u0027", "\u2032"); formatter.applyPattern(pattern); return formatter.format(parameters); } catch (NullPointerException npe) { return MISSING_RESOURCE_TEXT + key + " (npe)"; } catch (IllegalArgumentException iae) { return MISSING_RESOURCE_TEXT + key + " (iae)"; } catch (ClassCastException cce) { return MISSING_RESOURCE_TEXT + key + " (cce)"; } catch (MissingResourceException mre) { return MISSING_RESOURCE_TEXT + key + " (mre)"; } catch (StringIndexOutOfBoundsException sioobe) { return MISSING_RESOURCE_TEXT + key + " (sioobe)"; } } else { return MISSING_RESOURCE_TEXT + key; } } public Locale getLocale() { return locale; } public void setLocale(Locale locale) { String propertyFilename = MULTIBIT_RESOURCE_BUNDLE_DIRECTORY + "/" + locale.getLanguage() + "/" + MULTIBIT_RESOURCE_BUNDLE_NAME + PROPERTY_NAME_SUFFIX; String propertyFilenameBase = MULTIBIT_RESOURCE_BUNDLE_DIRECTORY + "/" + FALLBACK_LANGUAGE_CODE + "/" + MULTIBIT_RESOURCE_BUNDLE_NAME + PROPERTY_NAME_SUFFIX; if ("he".equals(locale.getLanguage()) || "iw".equals(locale.getLanguage())) { // Hebrew can be he or iw this.locale = new Locale("iw"); propertyFilename = MULTIBIT_RESOURCE_BUNDLE_DIRECTORY + "/he/" + MULTIBIT_RESOURCE_BUNDLE_NAME + PROPERTY_NAME_SUFFIX; } if ("id".equals(locale.getLanguage()) || "in".equals(locale.getLanguage())) { // Indonesian can be id or in this.locale = new Locale("in"); propertyFilename = MULTIBIT_RESOURCE_BUNDLE_DIRECTORY + "/id/" + MULTIBIT_RESOURCE_BUNDLE_NAME + PROPERTY_NAME_SUFFIX; } else { this.locale = locale; } formatter.setLocale(locale); numberFormat = NumberFormat.getInstance(locale); numberFormat.setMaximumFractionDigits(NUMBER_OF_FRACTION_DIGITS_FOR_BITCOIN); decimalFormatSymbols = new java.text.DecimalFormatSymbols(locale); boolean foundIt = false; try { InputStream inputStream = Localiser.class.getResourceAsStream(propertyFilename); if (inputStream != null) { resourceBundle = new PropertyResourceBundle(new InputStreamReader(inputStream, "UTF8")); foundIt = true; } } catch (FileNotFoundException e) { log.error(e.getMessage(), e); } catch (IOException e) { log.error(e.getMessage(), e); } if (!foundIt) { // just get the base version i.e. English try { InputStream inputStream = Localiser.class.getResourceAsStream(propertyFilenameBase); if (inputStream != null) { resourceBundle = new PropertyResourceBundle(new InputStreamReader(inputStream, "UTF8")); } } catch (FileNotFoundException e) { log.error(e.getMessage(), e); } catch (IOException e) { log.error(e.getMessage(), e); } } } /** * Get the version number specified in the version.properties file. * * @return */ public String getVersionNumber() { String version = ""; if (versionProperties == null) { versionProperties = new Properties(); try { java.net.URL versionPropertiesURL = Localiser.class.getResource(VERSION_PROPERTIES_FILENAME); if (versionPropertiesURL != null) { versionProperties.load(versionPropertiesURL.openStream()); } } catch (IOException ioe) { log.error(ioe.getMessage(), ioe); } } if (versionProperties != null) { version = versionProperties.getProperty(VERSION_PROPERTY_KEY_NAME); if (version == null) { version = ""; } } return version; } /** * Returns the given value in nanocoins as a 0.12345678 type string. * This function is localised. **/ public String bitcoinValueToString(BigInteger value, boolean addUnit, boolean blankZero) { if (blankZero && value.compareTo(BigInteger.ZERO) == 0) { return ""; } boolean negative = value.compareTo(BigInteger.ZERO) < 0; if (negative) { value = value.negate(); } String toReturn = ""; if (negative) { toReturn = "-"; } if (value == null) { throw new IllegalArgumentException("Value cannot be null"); } BigDecimal valueInBTC = new BigDecimal(value).divide(new BigDecimal(Utils.COIN)); toReturn = toReturn + numberFormat.format(valueInBTC.doubleValue()); if (addUnit) { toReturn = toReturn + " " + getString("sendBitcoinPanel.amountUnitLabel"); } return toReturn; } /** * Returns the given value in nanocoins as a 0.12345678 type string. * This function is NOT localised. **/ public String bitcoinValueToStringNotLocalised(BigInteger value, boolean addUnit, boolean blankZero) { if (blankZero && value.compareTo(BigInteger.ZERO) == 0) { return ""; } boolean negative = value.compareTo(BigInteger.ZERO) < 0; if (negative) { value = value.negate(); } String toReturn = ""; if (negative) { toReturn = "-"; } if (value == null) { throw new IllegalArgumentException("Value cannot be null"); } BigDecimal valueInBTC = new BigDecimal(value).divide(new BigDecimal(Utils.COIN)); toReturn = toReturn + numberFormatNotLocalised.format(valueInBTC.doubleValue()); if (addUnit) { toReturn = toReturn + " " + getString("sendBitcoinPanel.amountUnitLabel"); } return toReturn; } /** * Returns a formatted money onject **/ public String bigMoneyValueToString(BigMoney value) { if (value == null) { throw new IllegalArgumentException("Value cannot be null"); } String toReturn = ""; toReturn = numberFormat.format(value.getAmount().doubleValue()); return toReturn; } public java.text.DecimalFormatSymbols getDecimalFormatSymbols() { return decimalFormatSymbols; } }