/* * FrontlineSMS <http://www.frontlinesms.com> * Copyright 2007, 2008 kiwanja * * This file is part of FrontlineSMS. * * FrontlineSMS is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * FrontlineSMS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with FrontlineSMS. If not, see <http://www.gnu.org/licenses/>. */ /** * */ package net.frontlinesms.ui.i18n; import java.awt.Font; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import org.apache.log4j.Logger; import net.frontlinesms.FrontlineUtils; /** * Bundle of translations for a language, and associated properties. * @author Alex */ public abstract class LanguageBundle { //> CONSTANTS /** Key used to extract the language's ISO 639-1 code. */ public static final String KEY_LANGUAGE_CODE = "bundle.language"; /** Key used to extract the human-readable name of this language, in this language! */ public static final String KEY_LANGUAGE_NAME = "bundle.language.name"; /** Key used to extract the ISO 3166-1 alpha-2 2-letter country code of the flag used for this language. */ public static final String KEY_LANGUAGE_COUNTRY = "bundle.language.country"; /** Key used to extract the font used for this language. */ public static final String KEY_LANGUAGE_FONT = "bundle.language.fonts"; /** Key used to extract whether the language is right-to-left or not from the bundle */ public static final String KEY_RIGHT_TO_LEFT = "language.direction.right.to.left"; /** Key used to extract the name of the font to be used */ /** Logging object for this class */ private static final Logger LOG = FrontlineUtils.getLogger(LanguageBundle.class); //> INSTANCE PROPERTIES /** Map of i18n string keys to internationalised strings. */ private final Map<String, String> properties; //> CONSTRUCTORS /** * Instantiate a new {@link LanguageBundle} with the given properties. * @param filename * @param properties */ public LanguageBundle(Map<String, String> properties) { this.properties = properties; checkRequiredProperty(KEY_LANGUAGE_CODE); checkRequiredProperty(KEY_LANGUAGE_NAME); checkRequiredProperty(KEY_LANGUAGE_COUNTRY); // We don't check the Font property, which is not required } //> PRIVATE HELPER METHODS /** * Checks if a required property is present. A {@link MissingResourceException} will * be thrown if the value is not present. * @param key * @throws MissingResourceException if there are any required property keys missing. */ private void checkRequiredProperty(String key) { if(!this.properties.containsKey(key)) { throw new MissingResourceException("Language bundle missing required property: '" + key + "'", getClass().getName(), key); } } //> ACCESSORS /** @return the locale that this {@link LanguageBundle} is for. */ public Locale getLocale() { return new Locale(this.getLanguageCode(), this.getCountry()); } /** * @return a String identifier uniquely detailing where this {@link LanguageBundle} was loaded from. */ public abstract String getIdentifier(); /** @return the ISO-???? country code relating to the language in this bundle */ public String getCountry() { return getValue(KEY_LANGUAGE_COUNTRY); } /** @return the name of this language bundle */ public String getLanguageName() { return getValue(KEY_LANGUAGE_NAME); } /** @return the ISO-???? code relating to this language */ public String getLanguageCode() { return getValue(KEY_LANGUAGE_CODE); } /** @return the font used for this language bundle */ public String getLanguageFont() { return this.getValue(KEY_LANGUAGE_FONT, null); } /** @return <code>true</code> if this language is displayed right-to-left; <code>false</code> otherwise */ public boolean isRightToLeft() { try { String r2l = getValue(KEY_RIGHT_TO_LEFT); return Boolean.parseBoolean(r2l.trim()); } catch(MissingResourceException ex) { return false; } } /** * @param key name of the property to fetch * @return property value */ public String getValue(String key) { String value = properties.get(key); if(value == null) { throw new MissingResourceException("Requested resource not found in language bundle '" + getIdentifier() + "'", LanguageBundle.class.getName(), key); } return value; } /** * This method iterates through the properties and try to find properties looking like prefix.0, prefix.1 etc. * @param prefix of the properties to fetch * @return list of properties */ public List<String> getValues(String prefix) { List<String> values = getValues(properties, prefix); if(values.size() == 0) { throw new MissingResourceException("Requested resource not found in language bundle '" + getIdentifier() + "'", LanguageBundle.class.getName(), prefix); } return values; } /** * Gets the value of a property. If that property is not set, return the default value. * @param key * @param defaultValue * @return the value of the property, or the default value if the property is not set */ private String getValue(String key, String defaultValue) { String value = properties.get(key); if(value == null) { return defaultValue; } else { return value; } } /** @return the mapping of keys to internationalised text contained in this bundle. */ public Map<String, String> getProperties() { return this.properties; } /** @return an ordered array of font names to try to use for this language, or <code>null</code> if no font is specified */ private String[] getFontNames() { String fontNames = this.getValue(KEY_LANGUAGE_FONT, null); if(fontNames == null) { return null; } else { String[] namesArray = fontNames.split(","); return namesArray; } } /** @return the font that this language should be displayed with, or <code>null</code> if no font is specified or could be found */ public Font getFont() { LOG.trace("Loading font for language: " + this.getLanguageName()); String[] fontNames = getFontNames(); if(fontNames == null) { LOG.trace("No font requested."); } else { for(String fontName : fontNames) { fontName = fontName.trim(); LOG.trace("Attempting to load font: " + fontName); Font font = new Font(fontName, Font.PLAIN, 12); // Make sure that a font was loaded and it was the one we were expecting if(font == null) { LOG.trace("Could not load font."); } else if(!font.getFontName().equals(fontName)) { LOG.trace("Loaded incorrect font: " + font.getFontName()); } else { LOG.trace("Successfully loaded font. Will use: " + fontName); return font; } } } // None of the requested fonts could be found, so return null LOG.trace("No font found. Returning null."); return null; } /** Sets the ISO-???? country code relating to the language in this bundle */ public void setCountry(String country) { this.properties.put(KEY_LANGUAGE_COUNTRY, country); } /** Sets the name of this language bundle */ public void setLanguageName(String languageName) { this.properties.put(KEY_LANGUAGE_NAME, languageName); } /** Sets the ISO-???? code relating to this language */ public void setLanguageCode(String languageCode) { this.properties.put(KEY_LANGUAGE_CODE, languageCode); } /** Sets the font for this language bundle */ public void setLanguageFont(String fontName) { this.properties.put(KEY_LANGUAGE_FONT, fontName); } //> STATIC HELPER METHODS /** * This method iterates through the properties and try to find properties looking like prefix.0, prefix.1 etc. * @param prefix of the properties to fetch * @return list of properties */ public static List<String> getValues(Map<String, String> properties, String prefix) { if (!prefix.endsWith(".")) { prefix += "."; } List<String> propertiesList = new ArrayList<String>(); for (int i = 0; properties.containsKey(prefix + i) ; ++i) { propertiesList.add(properties.get(prefix + i)); } return propertiesList; } //> GENERATED CODE @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getIdentifier() == null) ? 0 : getIdentifier().hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; LanguageBundle other = (LanguageBundle) obj; if (getIdentifier() == null) { if (other.getIdentifier() != null) return false; } else if (!getIdentifier().equals(other.getIdentifier())) return false; return true; } }