/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * 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 2 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 org.andork.i18n; import java.text.DateFormat; import java.text.MessageFormat; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.andork.bind2.BiFunctionBinder; import org.andork.bind2.Binder; import org.andork.bind2.BinderHolder; import org.andork.bind2.DefaultBinder; import org.andork.bind2.FunctionBinder; import org.andork.bind2.ListBinder; import org.andork.bind2.OpaqueBinderHolder; public class I18n { public static class Localizer { public final String name; private Binder<ResourceBundle> bundleBinder; private final Set<String> missingKeys = new HashSet<String>(); private Localizer(String name, Binder<Locale> localeBinder) { super(); this.name = name; bundleBinder = new FunctionBinder<>(localeBinder, locale -> { if (disableBundleLoading || locale == null) { return null; } try { return ResourceBundle.getBundle(name, locale); } catch (MissingResourceException ex) { ex.printStackTrace(); } return null; }).convertToWeakReferencing(); } public Binder<String> formattedStringBinder(Binder<String> keyBinder, Binder<?>... argBinders) { return new BiFunctionBinder<String, List<Object>, String>( stringBinder(keyBinder), new ListBinder<Object>(Arrays.asList(argBinders)), (pattern, args) -> pattern == null || args == null ? null : MessageFormat.format(pattern, args.toArray())) .convertToWeakReferencing(); } public Binder<String> formattedStringBinder(String key, Object... args) { return new FunctionBinder<ResourceBundle, String>( bundleBinder, bundle -> MessageFormat.format(getString(bundle, key), args)) .convertToWeakReferencing(); } public ResourceBundle getBundle() { return bundleBinder.get(); } public String getFormattedString(String key, Object... args) { return MessageFormat.format(getString(key), args); } private String getString(ResourceBundle bundle, String key) { try { return bundle == null || key == null ? key : bundle.getString(key); } catch (MissingResourceException ex) { if (missingKeys.add(key)) { logger.log(Level.SEVERE, "Missing I18n key: \"" + key + '"', ex); } return key; } } public String getString(String key) { return getString(bundleBinder.get(), key); } public Binder<String> stringBinder(Binder<String> keyBinder) { return new BiFunctionBinder<ResourceBundle, String, String>( bundleBinder, keyBinder, (bundle, key) -> getString(bundle, key)) .convertToWeakReferencing(); } public Binder<String> stringBinder(String key) { return new FunctionBinder<ResourceBundle, String>( bundleBinder, bundle -> getString(bundle, key)) .convertToWeakReferencing(); } } private static final Logger logger = Logger.getLogger(I18n.class.getName()); private static BinderHolder<Locale> localeBinderHolder = new BinderHolder<>(); private static OpaqueBinderHolder<Locale> opaqueLocaleBinderHolder = new OpaqueBinderHolder<>(localeBinderHolder); private static final Map<String, Localizer> localizers = new HashMap<String, Localizer>(); private static boolean disableBundleLoading = System.getProperties().containsKey( "disableBundleLoading"); static { setLocale(Locale.getDefault()); } public static Localizer forClass(Class<?> cls) { return forName(cls.getName()); } public static Binder<String> formattedDateTimeBinder(Binder<Date> dateBinder, int dateStyle, int timeStyle) { Binder<DateFormat> formatBinder = new FunctionBinder<>(localeBinderHolder, locale -> locale == null ? DateFormat.getDateTimeInstance(dateStyle, timeStyle) : DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale)); return new BiFunctionBinder<>(dateBinder, formatBinder, (date, format) -> date == null || format == null ? null : format.format(date)); } public static Localizer forName(String name) { Localizer result = localizers.get(name); if (result == null) { result = new Localizer(name, localeBinderHolder); localizers.put(name, result); } return result; } public static Locale getLocale() { return localeBinderHolder.get(); } public static Binder<Locale> getLocaleBinder() { return opaqueLocaleBinderHolder; } public static boolean isDisableBundleLoading() { return disableBundleLoading; } public static void setDisableBundleLoading(boolean disableBundleLoading) { I18n.disableBundleLoading = disableBundleLoading; } public static void setLocale(Locale locale) { localeBinderHolder.binderLink.bind(new DefaultBinder<>(locale)); } public static void setLocaleBinder(Binder<? extends Locale> binder) { localeBinderHolder.binderLink.bind(binder); } }