/*
* Copyright (c) 2000-2007 by Rodney Kinney
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.i18n;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.MissingResourceException;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import VASSAL.build.GameModule;
import VASSAL.configure.SingleChildInstance;
/**
* Singleton class for managing the translation of a module into other languages
* @author rodneykinney
*
*/
public class Localization extends Language {
private static final Logger logger =
LoggerFactory.getLogger(Localization.class);
private static Localization instance;
private Localization() {
moduleBundle = Resources.MODULE_BUNDLE;
languageBundle = moduleBundle + "_" + Resources.getLocale().getLanguage(); //$NON-NLS-1$
countryBundle = languageBundle + "_" + Resources.getLocale().getCountry(); //$NON-NLS-1$
moduleBundle += ".properties"; //$NON-NLS-1$
languageBundle += ".properties"; //$NON-NLS-1$
countryBundle += ".properties"; //$NON-NLS-1$
validator = new SingleChildInstance(GameModule.getGameModule(), getClass());
}
public static Localization getInstance() {
if (instance == null) {
instance = new Localization();
}
return instance;
}
protected String moduleBundle;
protected String languageBundle;
protected String countryBundle;
protected List<Translation> moduleTranslations =
new ArrayList<Translation>();
protected List<Translation> languageTranslations =
new ArrayList<Translation>();
protected List<Translation> countryTranslations =
new ArrayList<Translation>();
protected List<Translation> translations =
new ArrayList<Translation>();
/*
* Master translation property list
*/
protected VassalResourceBundle masterBundle;
/**
* Return a list of translations available for editing.
*
* @return Array of available translations
*/
public String[] getTranslationList() {
Collections.sort(translations);
String[] s = new String[translations.size()];
int idx = 0;
for (Translation t : translations) {
s[idx++] = t.getDescription();
}
return s;
}
/**
* Return a specified translation
*
* @param description
* @return Translation object
*/
public Translation getTranslation(String description) {
for (Translation t : translations) {
if (t.getDescription().equals(description)) {
return t;
}
}
return null;
}
/**
* Record attributes as the module is being built for later translation
*/
protected Set<TranslatableAttribute> translatableItems =
new HashSet<TranslatableAttribute>();
/**
* Record an attribute that may need to be translated.
*
* @param component
* component to be translated
* @param name
* Attribute name to be translated
* @param value
* current value of attribute
*/
public void saveTranslatableAttribute(Translatable component, String name, String value) {
if (GameModule.getGameModule().isLocalizationEnabled()) {
TranslatableAttribute ta = new TranslatableAttribute(component, name, value);
translatableItems.add(ta);
}
}
/**
* Translate the module. The module and all extensions have now been built,
* so all Translations are available and all attributes that need to be
* translated have been recorded. There may be multiple translations that
* match this Locale, merge them in order - Country over-rides Language
* over-rides default. NB - You cannot create a default translation
* (Module.properties) using the VASSAL editor, but a default file can be
* placed into a module or extension manually.
*
* @throws IOException
*/
public void translate() throws IOException {
if (GameModule.getGameModule().isLocalizationEnabled()) {
for (Translation t : moduleTranslations) {
addBundle(t.getBundle());
}
for (Translation t : languageTranslations) {
addBundle(t.getBundle());
}
for (Translation t : countryTranslations) {
addBundle(t.getBundle());
}
if (masterBundle != null) {
translationInProgress = true;
for (TranslatableAttribute attr : translatableItems) {
if (attr.isTranslatable()) {
String key = attr.getKey();
try {
String translation = masterBundle.getString(key);
attr.applyTranslation(translation);
}
catch (MissingResourceException e) {
// Assume that the translated text is the same as the original
}
}
}
translationInProgress = false;
translationComplete = true;
logger.info("Translated");
}
translatableItems.clear();
GameModule.getGameModule().initFrameTitle();
}
}
/**
* Translate an individual attribute.
*
* @param key
* Attribute Key
* @param defaultValue
* Default value if no translation available
* @return translation
*/
public String translate(String key, String defaultValue) {
try {
return masterBundle == null ? defaultValue : masterBundle.getString(key);
}
catch (MissingResourceException e) {
return defaultValue;
}
}
protected void addBundle(VassalResourceBundle child) {
if (masterBundle == null) {
masterBundle = child;
}
else {
child.setParent(masterBundle);
masterBundle = child;
}
}
protected boolean translationInProgress = false;
protected boolean translationComplete = false;
public boolean isTranslationInProgress() {
return translationInProgress;
}
public boolean isTranslationComplete() {
return translationComplete;
}
/**
* Called whenever a Translation is added to a module or extension.
* Check if the translation macthes our locale. If so, add it to the list
* of translations to use. There may multiple matching translations at
* Country, Language and Module level from different extensions.
*
* @param t Translation
*/
public void addTranslation(Translation t) {
/*
* Play and Translate mode - keep a record of all translations that
* match our locale from various extensions. These will be merged
* into one after all are loaded.
*/
if (GameModule.getGameModule().isLocalizationEnabled()) {
Resources.addSupportedLocale(t.getLocale());
if (moduleBundle.equals(t.getBundleFileName())) {
moduleTranslations.add(t);
}
else if (languageBundle.equals(t.getBundleFileName())) {
languageTranslations.add(t);
}
else if (countryBundle.equals(t.getBundleFileName())) {
countryTranslations.add(t);
}
}
/*
* Edit mode, keep a list of all translations available in this
* Module or Extension to use in drop-down lists
*/
else {
translations.add(t);
}
}
public void removeTranslation(Translation t) {
translations.remove(t);
}
}