package act.i18n; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. * #L% */ import act.Act; import act.util.ActContext; import org.osgl.$; import org.osgl.Osgl; import org.osgl.exception.NotAppliedException; import org.osgl.logging.LogManager; import org.osgl.logging.Logger; import org.osgl.util.C; import org.osgl.util.S; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class I18n { private static final Logger logger = LogManager.get(I18n.class); public static final String DEF_RESOURCE_BUNDLE_NAME = "messages"; public static Locale locale() { ActContext context = ActContext.Base.currentContext(); return null != context ? context.locale(true) : Act.appConfig().locale(); } public static String i18n(String msgId, Object ... args) { return i18n(locale(), msgId, args); } public static String i18n(Locale locale, String msgId, Object ... args) { return i18n(locale, DEF_RESOURCE_BUNDLE_NAME, msgId, args); } public static String i18n(Class<?> bundleSpec, String msgId, Object... args) { return i18n(locale(), bundleSpec.getName(), msgId, args); } public static String i18n(Locale locale, Class<?> bundleSpec, String msgId, Object... args) { return i18n(locale, bundleSpec.getName(), msgId, args); } public static String i18n(Locale locale, String bundleName, String msgId, Object... args) { return i18n(false, locale, bundleName, msgId, args); } public static String i18n(boolean ignoreError, Locale locale, String bundleName, String msgId, Object... args) { if (null == msgId) { return ""; } ResourceBundle bundle; try { bundle = ResourceBundle.getBundle(bundleName, $.notNull(locale), Act.app().classLoader()); } catch (MissingResourceException e) { return msgId; } String msg = msgId; if (ignoreError) { if (bundle.containsKey(msgId)) { msg = bundle.getString(msgId); } } else { try { msg = bundle.getString(msgId); } catch (MissingResourceException e) { logger.warn("Cannot find i18n message key: %s", msgId); } } int len = args.length; if (len > 0) { Object[] resolvedArgs = new Object[len]; for (int i = 0; i < len; ++i) { Object arg = args[i]; if (arg instanceof String) { resolvedArgs[i] = i18n(true, locale, bundleName, (String) arg); } else { resolvedArgs[i] = arg; } } msg = S.fmt(msg, resolvedArgs); } return msg; } public static String i18n(Enum<?> msgId) { return i18n(locale(), msgId); } public static String i18n(Locale locale, Enum<?> msgId) { return i18n(locale, DEF_RESOURCE_BUNDLE_NAME, msgId); } public static String i18n(Class<?> bundleSpec, Enum<?> msgId) { return i18n(locale(), bundleSpec, msgId); } public static String i18n(Locale locale, Class<?> bundleSpec, Enum<?> msgId) { return i18n(locale, bundleSpec.getName(), msgId); } public static String i18n(Locale locale, String bundleName, Enum<?> msgId) { String key = S.newBuffer("enum.").append(msgId.getDeclaringClass().getSimpleName().toLowerCase()).append(".").append(msgId.name().toLowerCase()).toString(); return i18n(locale, bundleName, key); } public static Map<String, Object> i18n(Class<? extends Enum> enumClass) { return i18n(locale(), enumClass); } public static Map<String, Object> i18n(Locale locale, Class<? extends Enum> enumClass) { return i18n(locale, DEF_RESOURCE_BUNDLE_NAME, enumClass); } public static Map<String, Object> i18n(Class<?> bundleSpec, Class<? extends Enum> enumClass) { return i18n(locale(), bundleSpec, enumClass); } public static Map<String, Object> i18n(Locale locale, Class<?> bundleSpec, Class<? extends Enum> enumClass) { return i18n(locale, bundleSpec.getName(), enumClass); } public static Map<String, Object> i18n(Locale locale, String bundleName, Class<? extends Enum> enumClass) { return i18n(locale, bundleName, enumClass, false); } public static Map<String, Object> i18n(Class<? extends Enum> enumClass, boolean outputProperties) { return i18n(locale(), enumClass, outputProperties); } public static Map<String, Object> i18n(Locale locale, Class<? extends Enum> enumClass, boolean outputProperties) { return i18n(locale, DEF_RESOURCE_BUNDLE_NAME, enumClass, outputProperties); } public static Map<String, Object> i18n(Class<?> bundleSpec, Class<? extends Enum> enumClass, boolean outputProperties) { return i18n(locale(), bundleSpec, enumClass, outputProperties); } public static Map<String, Object> i18n(Locale locale, Class<?> bundleSpec, Class<? extends Enum> enumClass, boolean outputProperties) { return i18n(locale, bundleSpec.getName(), enumClass, outputProperties); } public static Map<String, Object> i18n(Locale locale, String bundleName, Class<? extends Enum> enumClass, boolean outputProperties) { LinkedHashMap<String, Object> map = new LinkedHashMap<>(); for (Enum<?> enumInstance : enumClass.getEnumConstants()) { String name = enumInstance.name(); String val = i18n(locale, bundleName, enumInstance); if (outputProperties) { Map<String, Object> values = new HashMap<>(); map.put(name, values); values.put("message", val); Map<String, $.Function<Object, Object>> getters = enumPropertyGetters(enumClass); for (Map.Entry<String, $.Function<Object, Object>> entry : getters.entrySet()) { values.put(entry.getKey(), entry.getValue().apply(enumInstance)); } } else { map.put(name, val); } } return map; } private static Set<String> standardsAnnotationMethods = C.newSet(C.list("declaringClass", "hashCode", "toString", "ordinal", "name", "class")); private static ConcurrentMap<Class<? extends Enum>, Map<String, $.Function<Object, Object>>> enumPropertyGetterCache = new ConcurrentHashMap<>(); private static Map<String, $.Function<Object, Object>> enumPropertyGetters(Class<? extends Enum> enumClass) { Map<String, $.Function<Object, Object>> map = enumPropertyGetterCache.get(enumClass); if (null == map) { map = buildEnumPropertyGetters(enumClass); enumPropertyGetterCache.putIfAbsent(enumClass, map); } return map; } private static Map<String, $.Function<Object, Object>> buildEnumPropertyGetters(Class<? extends Enum> enumClass) { Map<String, $.Function<Object, Object>> map = new HashMap<>(); for (final Method method : enumClass.getMethods()) { if (void.class == method.getReturnType() || Void.class == method.getReturnType() || Modifier.isStatic(method.getModifiers()) || method.getParameterTypes().length > 0) { continue; } String name = propertyName(method); if (standardsAnnotationMethods.contains(name)) { continue; } map.put(method.getName(), new Osgl.Function<Object, Object>() { @Override public Object apply(Object o) throws NotAppliedException, Osgl.Break { return $.invokeVirtual(o, method); } }); } return map; } private static String propertyName(Method method) { String name = method.getName(); int len = name.length(); if (len > 3 && name.startsWith("get")) { name = S.lowerFirst(name.substring(3)); } else if (len > 2 && name.startsWith("is")) { name = S.lowerFirst(name.substring(2)); } return name; } }