/*
GanttProject is an opensource project management tool. License: GPL3
Copyright (C) 2011 Dmitry Barashev, GanttProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package net.sourceforge.ganttproject.language;
import java.awt.ComponentOrientation;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EventListener;
import java.util.EventObject;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TimeZone;
import javax.swing.UIManager;
import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.util.PropertiesUtil;
import biz.ganttproject.core.option.GPAbstractOption;
import biz.ganttproject.core.time.CalendarFactory;
/**
* Class for the language
*/
public class GanttLanguage {
public class Event extends EventObject {
public Event(GanttLanguage language) {
super(language);
}
public GanttLanguage getLanguage() {
return (GanttLanguage) getSource();
}
}
public interface Listener extends EventListener {
public void languageChanged(Event event);
}
public static Comparator<Locale> LEXICOGRAPHICAL_LOCALE_COMPARATOR = new Comparator<Locale>() {
@Override
public int compare(Locale o1, Locale o2) {
return (o1.getDisplayLanguage(Locale.US) + o1.getDisplayCountry(Locale.US)).compareTo(o2.getDisplayLanguage(Locale.US)
+ o2.getDisplayCountry(Locale.US));
}
};
private static class CalendarFactoryImpl extends CalendarFactory implements CalendarFactory.LocaleApi {
static void setLocaleImpl() {
CalendarFactory.setLocaleApi(new CalendarFactoryImpl());
}
@Override
public Locale getLocale() {
return GanttLanguage.getInstance().getLocale();
}
@Override
public DateFormat getShortDateFormat() {
return GanttLanguage.getInstance().getShortDateFormat();
}
}
private static final GanttLanguage ganttLanguage = new GanttLanguage();
private final SimpleDateFormat myRecurringDateFormat = new SimpleDateFormat("MMM dd");
private ArrayList<Listener> myListeners = new ArrayList<Listener>();
private Locale currentLocale = null;
private final CharSetMap myCharSetMap;
private ResourceBundle i18n = null;
private SimpleDateFormat currentDateFormat = null;
private SimpleDateFormat shortCurrentDateFormat = null;
private SimpleDateFormat myLongFormat;
private DateFormat currentTimeFormat = null;
private List<String> myDayShortNames;
private Locale myDateFormatLocale;
private Properties myExtraLocales = new Properties();
private GanttLanguage() {
new GPAbstractOption.I18N() {
{
setI18N(this);
}
@Override
protected String i18n(String key) {
return getText(key);
}
};
Properties charsets = new Properties();
PropertiesUtil.loadProperties(charsets, "/charsets.properties");
myCharSetMap = new CharSetMap(charsets);
setLocale(Locale.getDefault());
PropertiesUtil.loadProperties(myExtraLocales, "/language/extra.properties");
}
public static GanttLanguage getInstance() {
return ganttLanguage;
}
public SimpleDateFormat getMediumDateFormat() {
return currentDateFormat;
}
public SimpleDateFormat getShortDateFormat() {
return shortCurrentDateFormat;
}
public SimpleDateFormat getRecurringDateFormat() {
return myRecurringDateFormat;
}
public SimpleDateFormat getLongDateFormat() {
return myLongFormat;
}
public Locale getDateFormatLocale() {
return myDateFormatLocale;
}
private void applyDateFormatLocale(Locale locale) {
myDateFormatLocale = locale;
setShortDateFormat((SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, locale));
currentDateFormat = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
currentTimeFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
myLongFormat = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.LONG, locale);
UIManager.put("JXDatePicker.longFormat", myLongFormat.toPattern());
UIManager.put("JXDatePicker.mediumFormat", currentDateFormat.toPattern());
UIManager.put("JXDatePicker.numColumns", new Integer(10));
myDayShortNames = getShortDayNames(locale);
UIManager.put("JXMonthView.daysOfTheWeek", myDayShortNames.toArray(new String[7]));
}
public void setShortDateFormat(SimpleDateFormat dateFormat) {
shortCurrentDateFormat = dateFormat;
UIManager.put("JXDatePicker.shortFormat", shortCurrentDateFormat.toPattern());
fireLanguageChanged();
}
public void setLocale(Locale locale) {
currentLocale = locale;
CalendarFactoryImpl.setLocaleImpl();
Locale.setDefault(locale);
int defaultTimezoneOffset = TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings();
TimeZone utc = TimeZone.getTimeZone("UTC");
utc.setRawOffset(defaultTimezoneOffset);
TimeZone.setDefault(utc);
applyDateFormatLocale(getDateFormatLocale(locale));
i18n = getResourceBundle(locale);
fireLanguageChanged();
}
private static ResourceBundle getResourceBundle(Locale locale) {
String resourceBase = System.getProperty("org.ganttproject.resourcebase", "language/i18n");
return ResourceBundle.getBundle(resourceBase, locale);
}
private Locale getDateFormatLocale(Locale baseLocale) {
String dateFormatLocale = myExtraLocales.getProperty(baseLocale.getLanguage() + ".dateFormatLocale", null);
if (dateFormatLocale == null) {
return baseLocale;
}
return new Locale(dateFormatLocale);
}
public List<Locale> getAvailableLocales() {
Set<Locale> removeLangOnly = new HashSet<Locale>();
Set<Locale> result = new HashSet<Locale>();
for (Locale l : Locale.getAvailableLocales()) {
if (GanttLanguage.class.getResource("/language/i18n_" + l.getLanguage() + "_" + l.getCountry() + ".properties") != null) {
removeLangOnly.add(new Locale(l.getLanguage()));
result.add(new Locale(l.getLanguage(), l.getCountry()));
} else if (GanttLanguage.class.getResource("/language/i18n_" + l.getLanguage() + ".properties") != null) {
result.add(new Locale(l.getLanguage()));
}
}
String[] locales = myExtraLocales.getProperty("_").split(",");
for (String l : locales) {
if (!myExtraLocales.containsKey(l + ".lang")) {
continue;
}
String langCode = myExtraLocales.getProperty(l + ".lang");
String countryCode = myExtraLocales.getProperty(l + ".country", "");
String regionCode = myExtraLocales.getProperty(l + ".region", "");
Locale locale = new Locale(langCode, countryCode, regionCode);
result.add(locale);
}
result.removeAll(removeLangOnly);
result.add(Locale.ENGLISH);
List<Locale> result1 = new ArrayList<Locale>(result);
Collections.sort(result1, LEXICOGRAPHICAL_LOCALE_COMPARATOR);
return result1;
}
public String formatLanguageAndCountry(Locale locale) {
String englishName = locale.getDisplayLanguage(Locale.US);
String localName = locale.getDisplayLanguage(locale);
String currentLocaleName = locale.getDisplayLanguage(getLocale());
if ("en".equals(locale.getLanguage()) || "zh".equals(locale.getLanguage()) || "pt".equals(locale.getLanguage())) {
if (!locale.getCountry().isEmpty()) {
englishName += " - " + locale.getDisplayCountry(Locale.US);
localName += " - " + locale.getDisplayCountry(locale);
}
}
if (localName.equals(englishName) && currentLocaleName.equals(englishName)) {
return englishName;
}
StringBuilder builder = new StringBuilder(englishName);
builder.append(" (");
boolean hasLocal = false;
if (!localName.equals(englishName)) {
builder.append(localName);
hasLocal = true;
}
if (!currentLocaleName.equals(localName) && !currentLocaleName.equals(englishName)) {
if (hasLocal) {
builder.append(", ");
}
builder.append(currentLocaleName);
}
builder.append(")");
return builder.toString();
}
/** @return The current Locale */
public Locale getLocale() {
return currentLocale;
}
public String getCharSet() {
return myCharSetMap.getCharSet(getLocale());
}
public String getDay(int day) {
return myDayShortNames.get(day);
}
/** @return The current DateFormat */
public DateFormat getDateFormat() {
return currentDateFormat;
}
public String formatDate(Calendar date) {
return currentDateFormat.format(date.getTime());
}
public String formatShortDate(Calendar date) {
return shortCurrentDateFormat.format(date.getTime());
}
public String formatTime(Calendar date) {
return currentTimeFormat.format(date.getTime());
}
public Date parseDate(String dateString) {
if (dateString == null) {
return null;
}
try {
Date parsed = getShortDateFormat().parse(dateString);
if (getShortDateFormat().format(parsed).equals(dateString)) {
return parsed;
}
} catch (ParseException e) {
GPLogger.logToLogger(e);
}
return null;
}
public String getMonth(int m) {
GregorianCalendar month = new GregorianCalendar(2000, m, 1);
SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM", myDateFormatLocale);
StringBuffer result = new StringBuffer();
result = dateFormat.format(month.getTime(), result, new FieldPosition(DateFormat.MONTH_FIELD));
return result.toString();
}
private static List<String> getShortDayNames(Locale locale) {
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE", locale);
List<String> result = new ArrayList<String>();
for (int i = 0; i < 7; i++) {
GregorianCalendar day = new GregorianCalendar(2000, 1, 1);
while (day.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
day.add(Calendar.DATE, 1);
}
day.add(Calendar.DATE, i);
StringBuffer formattedDay = new StringBuffer();
formattedDay = dateFormat.format(day.getTime(), formattedDay, new FieldPosition(DateFormat.DAY_OF_WEEK_FIELD));
result.add(formattedDay.toString());
}
return result;
}
/** @return the text in the current language for the given key */
public String getText(String key) {
try {
return i18n.getString(key);
} catch (MissingResourceException e) {
return null;
}
}
public String getText(String key, Locale locale) {
try {
return getResourceBundle(locale).getString(key);
} catch (MissingResourceException e) {
return getText(key);
}
}
/**
* @return the text suitable for labels in the current language for the given
* key (all $ characters are removed from the original text)
* @see #GanttLagetText()
* @see #correctLabel()
*/
public String getCorrectedLabel(String key) {
String label = getText(key);
return label == null ? null : correctLabel(label);
}
public ComponentOrientation getComponentOrientation() {
return ComponentOrientation.getOrientation(currentLocale);
}
public void addListener(Listener listener) {
myListeners.add(listener);
}
public void removeListener(Listener listener) {
myListeners.remove(listener);
}
private void fireLanguageChanged() {
Event event = new Event(this);
for (int i = 0; i < myListeners.size(); i++) {
Listener next = myListeners.get(i);
next.languageChanged(event);
}
}
public SimpleDateFormat createDateFormat(String string) {
return new SimpleDateFormat(string, myDateFormatLocale);
}
/** @return label with the $ removed from it (if it was included) */
public String correctLabel(String label) {
if (label == null) {
return null;
}
int index = label.indexOf('$');
if (index != -1 && label.length() - index > 1) {
label = label.substring(0, index).concat(label.substring(++index));
}
return label;
}
public String formatText(String key, Object... values) {
String message = getText(key);
return message == null ? null : MessageFormat.format(message, values);
}
}