All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ro.pippo.core.Messages Maven / Gradle / Ivy

There is a newer version: 1.8.0
Show newest version
/*
 * Copyright (C) 2014 the original author or authors.
 *
 * 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.
 */
package ro.pippo.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.pippo.core.route.RouteContext;
import ro.pippo.core.util.ClasspathUtils;
import ro.pippo.core.util.StringUtils;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;

/**
 * Loads and caches message resource files based on the registered languages in
 * application.properties.
 * 

* This class is based on MessagesImpl.java from the Ninja Web Framework. * * @author James Moger */ public class Messages { private static final Logger log = LoggerFactory.getLogger(Messages.class); private final Map languageMessages; private final Languages languages; public Messages(Languages languages) { this.languages = languages; this.languageMessages = loadRegisteredMessageResources(); } /** * Gets the requested localized message. *

*

* The current Request and Response are used to help determine the messages * resource to use. *

    *
  1. Exact locale match, return the registered locale message *
  2. Language match, but not a locale match, return the registered * language message *
  3. Return the default resource message *
*

* The message can be formatted with optional arguments using the * {@link java.text.MessageFormat} syntax. *

*

* If the key does not exist in the messages resource, then the key name is * returned. *

* * @param key * @param routeContext * @param args * @return the message or the key if the key does not exist */ public String get(String key, RouteContext routeContext, Object... args) { String language = languages.getLanguageOrDefault(routeContext); return get(key, language, args); } /** * Gets the requested localized message. *

*

    *
  1. Exact locale match, return the registered locale message *
  2. Language match but not a locale match, return the registered language * message *
  3. Return the default resource message *
*

* The message can be formatted with optional arguments using the * {@link java.text.MessageFormat} syntax. *

*

* If the key does not exist in the messages resource, then the key name is * returned. *

* * @param key * @param language * @param args * @return the message or the key if the key does not exist */ public String get(String key, String language, Object... args) { Properties messages = getMessagesForLanguage(language); String value = messages.getProperty(key); if (value != null) { String message = formatMessage(value, language, args); return message; } else { log.warn("Failed to find '{}' in Messages", key); return key; } } /** * Gets the requested localized message. *

*

* The current Request and Response are used to help determine the messages * resource to use. *

    *
  1. Exact locale match, return the registered locale message *
  2. Language match, but not a locale match, return the registered * language message *
  3. Return the supplied default message *
*

* The message can be formatted with optional arguments using the * {@link java.text.MessageFormat} syntax. *

*

* If the key does not exist in the messages resource, then the key name is * returned. *

* * @param key * @param defaultMessage * @param routeContext * @param args * @return the message or the key if the key does not exist */ public String getWithDefault(String key, String defaultMessage, RouteContext routeContext, Object... args) { String language = languages.getLanguageOrDefault(routeContext); return getWithDefault(key, defaultMessage, language, args); } /** * Gets the requested localized message. *

*

* The current Request and Response are used to help determine the messages * resource to use. *

    *
  1. Exact locale match, return the registered locale message *
  2. Language match, but not a locale match, return the registered * language message *
  3. Return supplied default message *
*

* The message can be formatted with optional arguments using the * {@link java.text.MessageFormat} syntax. *

* * @param key * @param defaultMessage * @param args * @return the message or the key if the key does not exist */ public String getWithDefault(String key, String defaultMessage, String language, Object... args) { String value = get(key, language, args); if (value.equals(key)) { // key does not exist, format default message value = formatMessage(defaultMessage, language, args); } return value; } /** * Returns all localized messages. *

* The current Request and Response are used to help determine the messages * resource to use. *

    *
  1. Exact locale match, return the registered locale messages *
  2. Language match but not a locale match, return the registered language * messages *
  3. Return the default messages *
*

* * @param routeContext * @return all localized messages */ public Map getAll(RouteContext routeContext) { String language = languages.getLanguageOrDefault(routeContext); return getAll(language); } /** * Returns all localized messages. *
    *
  1. Exact locale match, return the registered locale messages *
  2. Language match but not a locale match, return the registered language * messages *
  3. Return the default messages *
* * @param language * @return all localized messages */ public Map getAll(String language) { Properties messages = getMessagesForLanguage(language); Map map = new TreeMap<>(); for (Map.Entry entry : messages.entrySet()) { map.put(entry.getKey().toString(), entry.getValue().toString()); } return map; } /** * Loads Pippo internal messages & application messages and returns the merger. * * @return all messages */ private Map loadRegisteredMessageResources() { Map internalMessages = loadRegisteredMessageResources("pippo/pippo-messages%s.properties"); Map applicationMessages = loadRegisteredMessageResources("conf/messages%s.properties"); Map allMessages = new TreeMap<>(); Set merged = new HashSet<>(); // create aggregate messages for (Map.Entry entry : internalMessages.entrySet()) { String language = entry.getKey(); Properties messages = entry.getValue(); allMessages.put(language, messages); if (applicationMessages.containsKey(language)) { // override internal messages with application messages messages.putAll(applicationMessages.get(language)); } merged.add(language); } // bring in the application languages which do not have an internal counterpart Set unmerged = new HashSet<>(applicationMessages.keySet()); unmerged.removeAll(merged); for (String language : unmerged) { allMessages.put(language, applicationMessages.get(language)); } return allMessages; } /** * Loads all registered message resources. */ private Map loadRegisteredMessageResources(String name) { Map messageResources = new TreeMap<>(); // Load default messages Properties defaultMessages = loadMessages(String.format(name, "")); if (defaultMessages == null) { log.error("Could not locate the default messages resource '{}', please create it.", String.format(name, "")); } else { messageResources.put("", defaultMessages); } // Load the registered language resources List registeredLanguages = languages.getRegisteredLanguages(); for (String language : registeredLanguages) { // First step: Load complete language eg. en-US Properties messages = loadMessages(String.format(name, "_" + language)); Properties messagesLangOnly = null; // If the language has a country code load the default values for // the language. For example missing keys in en-US will // be filled-in by the default language. String langComponent = languages.getLanguageComponent(language); if (!langComponent.equals(language)) { // see if we have already loaded the language messages messagesLangOnly = messageResources.get(langComponent); if (messagesLangOnly == null) { // load the language messages messagesLangOnly = loadMessages(String.format(name, "_" + langComponent)); } } // If a language is registered in application.properties it should // be there. if (messages == null) { log.error( "Could not locate the '{}' messages resource '{}' specified in '{}'.", language, String.format(name, "_" + language), PippoConstants.SETTING_APPLICATION_LANGUAGES); } else { // add a new language // start with the default messages Properties compositeMessages = new Properties(defaultMessages); // put all the language component messages "en" if (messagesLangOnly != null) { compositeMessages.putAll(messagesLangOnly); // cache language component messages if (!messageResources.containsKey(langComponent)) { Properties langResources = new Properties(); langResources.putAll(compositeMessages); messageResources.put(langComponent, langResources); } } // put all the language specific messages "en-US" compositeMessages.putAll(messages); // and add the composite messages to the hashmap with the // mapping. messageResources.put(language.toLowerCase(), compositeMessages); } } return Collections.unmodifiableMap(messageResources); } /** * Attempts to load a message resource. */ private Properties loadMessages(String fileOrUrl) { URL url = ClasspathUtils.locateOnClasspath(fileOrUrl); if (url != null) { try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { Properties messages = new Properties(); messages.load(reader); return messages; } catch (IOException e) { log.error("Failed to load {}", fileOrUrl, e); } } return null; } /** * Retrieves the messages from an arbitrary one or two component language * String ("en-US", or "en" or "de"...). *

* * @param language A one or two component language code such as "en", "en-US", or * "en-US,en;q=0.8,de;q=0.6". * @return The messages for the requested language or the default messages. */ private Properties getMessagesForLanguage(String language) { if (StringUtils.isNullOrEmpty(language)) { return languageMessages.get(""); } String supportedLanguage = languages.getLanguageOrDefault(language); if (StringUtils.isNullOrEmpty(supportedLanguage)) { log.debug("Messages for '{}' were requested. Using default messages.", language); return languageMessages.get(""); } // try the supported language Properties messages = languageMessages.get(supportedLanguage); if (messages != null) { return messages; } // check the supported language component String langComponent = languages.getLanguageComponent(supportedLanguage); messages = languageMessages.get(langComponent); if (messages != null) { return messages; } // return the default messages resource return languageMessages.get(""); } /** * Optionally formats a message for the requested language with * {@link java.text.MessageFormat}. * * @param message * @param language * @param args * @return the message */ private String formatMessage(String message, String language, Object... args) { if (args != null && args.length > 0) { // only format a message if we have arguments Locale locale = languages.getLocaleOrDefault(language); MessageFormat messageFormat = new MessageFormat(message, locale); return messageFormat.format(args); } return message; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy