Я хочу поделиться с вами своим решением.
Подтвержденный ответ (с двумя решениями) на текущий вопрос действительно интересен.
Единственная проблема в первом решении - использовать жестко закодированный ключ сообщения (« currentLanguage »), который может исчезнуть из соответствующего файла свойств.
Второй должен жестко закодировать базовое имя (" fr-messages_ ") файла свойств. Но имя файла можно изменить ...
Итак, я последовал примеру проверенного ответа, чтобы расширить свой пользовательский ResourceBundleMessageSource , чтобы сделать это.
Изначально мне нужно было получить содержимое файлов свойств сообщений Spring ( messages_en.properties , messages_fr.properties , ...), поскольку у меня есть полный интерфейс Javascript (используя ExtJs). Итак, мне нужно было загрузить все (интернационализированные) метки приложения на объект JS.
Но его не существует ... По этой причине я разработал пользовательский класс ReloadableResourceBundleMessageSource . Соответствующими методами являются " getAllProperties () ", " getAllPropertiesAsMap () " и " getAllPropertiesAsMessages () ".
Позже мне нужно было получить доступные локали в приложении. И, читая эту страницу, я хотел расширить класс ReloadableResourceBundleMessageSource , чтобы сделать это. Вы можете увидеть методы " getAvailableLocales () " и " isAvailableLocale () " (для проверки только одного Locale).
<code>package fr.ina.archibald.web.support;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.LocaleUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ReflectionUtils;
import fr.ina.archibald.commons.util.StringUtils;
import fr.ina.archibald.entity.MessageEntity;
/**
* Custom {@link org.springframework.context.support.ReloadableResourceBundleMessageSource}.
*
* @author srambeau
*/
public class ReloadableResourceBundleMessageSource extends org.springframework.context.support.ReloadableResourceBundleMessageSource {
private static final Logger LOGGER = LoggerFactory.getLogger(ReloadableResourceBundleMessageSource.class);
private static final String PROPERTIES_SUFFIX = ".properties";
private static final String XML_SUFFIX = ".xml";
private Set<Locale> cacheAvailableLocales;
private Set<Resource> cacheResources;
/**
* Returns all messages for the specified {@code Locale}.
*
* @param locale the {@code Locale}.
*
* @return a {@code Properties} containing all the expected messages or {@code null} if the {@code locale} argument is null or if the properties are empty.
*/
public Properties getAllProperties(final Locale locale) {
if(locale == null) {
LOGGER.debug("Cannot get all properties. 'locale' argument is null.");
return null;
}
return getMergedProperties(locale).getProperties();
}
/**
* Returns all messages for the specified {@code Locale}.
*
* @param locale the {@code Locale}.
*
* @return a {@code Map} containing all the expected messages or {@code null} if the {@code locale} argument is null or if the properties are empty.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Map<String, String> getAllPropertiesAsMap(final Locale locale) {
if(locale == null) {
LOGGER.debug("Cannot get all properties as Map. 'locale' argument is null.");
return null;
}
Properties props = getAllProperties(locale);
if(props == null) {
LOGGER.debug("Cannot get all properties as Map. The properties are missing.");
return null;
}
return new HashMap<String, String>((Map) props);
}
/**
* Returns all messages for the specified {@code Locale}.
*
* @param locale the {@code Locale}.
*
* @return a {@code List<MessageEntity>} containing all the expected messages or {@code null} if the {@code locale} argument is null or if the properties are empty.
*/
public List<MessageEntity> getAllPropertiesAsMessages(final Locale locale) {
if(locale == null) {
LOGGER.debug("Cannot get all properties as MessageEntity. 'locale' argument is null.");
return null;
}
Properties props = getAllProperties(locale);
if(props == null) {
LOGGER.debug("Cannot get all properties as MessageEntity. The properties are missing.");
return null;
}
Set<Entry<Object, Object>> propsSet = props.entrySet();
List<MessageEntity> messages = new ArrayList<MessageEntity>();
for(Entry<Object, Object> prop : propsSet) {
messages.add(new MessageEntity((String) prop.getKey(), (String) prop.getValue()));
}
return messages;
}
/**
* Returns the available {@code Locales} on the specified application context. Calculated from the Spring message files of the application context.
* <p>
* Example of Locales returned corresponding with the messages files defines on the application:
*
* <pre>
* messages_en.properties --> en
* messages_fr.properties --> fr
* messages_en.properties, messages_fr.properties --> en, fr
*
*
*
* @return набор {@code Locales} или ноль, если происходит ошибка.
* /
public Set getAvailableLocales () {
if (cacheAvailableLocales! = null) {
return cacheAvailableLocales;
}
cacheAvailableLocales = getLocales (getAllFileNames (), getMessageFilePrefixes ());
return cacheAvailableLocales;
}
/ **
* Указывает, доступен ли указанный {@code Locale} в приложении.
*
* Примеры результатов, возвращаемых, если приложение содержит файлы «messages_en.properties» и «messages_fr.properties»:
*
*
* en --> true
* fr --> true
* de --> false
* es --> false
*
*
* @param locale {@code Locale}.
*
* @return {@code true}, если локаль доступна, {@code false} в противном случае.
* /
public boolean isAvailableLocale (окончательный языковой стандарт) {
Set locales = getAvailableLocales ();
if (locales == null) {
вернуть ложь;
}
возврат locales.contains (локаль);
}
// ********************** ЧАСТНЫЕ МЕТОДЫ **********************
/ **
* Возвращает {@code Locales}, указанный в именах файлов.
*
* @param fileNames имена файлов.
* @param filePrefixes префиксы базовых имен пакетов ресурсов.
*
* @ вернуть набор {@code Locales}.
* /
приватный набор getLocales (окончательный список fileNames, список filePrefixes) {
if (fileNames == null || fileNames.isEmpty () || filePrefixes == null || filePrefixes.isEmpty ()) {
LOGGER.debug ("Невозможно получить доступные локали. FileNames = [" + StringUtils.toString (fileNames) + "], filePrefixes = [" + StringUtils.toString (filePrefixes) + "]");
вернуть ноль;
}
Set locales = new HashSet ();
for (строковое имя файла: имя файла) {
String fileNameWithoutExtension = FilenameUtils.getBaseName (fileName);
for (строка filePrefixe: filePrefixes) {
Строка localeStr = fileNameWithoutExtension.substring (filePrefixe.length () + 1);
пытаться {
locales.add (LocaleUtils.toLocale (localeStr));
} catch (IllegalArgumentException ex) {
Продолжить;
}
}
}
вернуть локали;
}
/ **
* Возвращает все имена файлов пакетов ресурсов.
*
* @ вернуть список имен файлов или {@code null}, если ресурсы отсутствуют.* /
приватный список getAllFileNames () {
Set resources = getAllResources ();
if (resources == null) {
LOGGER.debug («Недостающие пакеты ресурсов.»);
вернуть ноль;
}
List filenames = new ArrayList (resources.size ());
для (Ресурсный ресурс: ресурсы) {
filenames.add (resource.getFilename ());
}
вернуть имена файлов;
}
/ **
* Получает массив префиксов для файлов сообщений.
*
*
* "WEB-INF/messages" --> "messages"
* "classpath:config/i18n/messages" --> "messages"
* "messages" --> "messages"
*
*
* @ вернуть массив префиксов или ноль, если произошла ошибка.
* /
приватный список getMessageFilePrefixes () {
String [] basenames = getBasenames ();
if (basenames == null) {
LOGGER.debug («Отсутствуют базовые имена пакетов ресурсов.»);
вернуть ноль;
}
List prefixes = new ArrayList (basenames.length);
for (int i = 0; i getAllResources () {
if (cacheResources! = null) {
вернуть cacheResources;
}
String [] basenames = getBasenames ();
if (basenames == null) {
LOGGER.debug («Отсутствуют базовые имена пакетов ресурсов.»);
вернуть ноль;
}
ResourceLoader resourceLoader = getResourceLoader ();
if (resourceLoader == null) {
LOGGER.debug ("Отсутствует ResourceLoader.");
вернуть ноль;
}
Set resources = new HashSet ();
for (Строковое базовое имя: базовые имена) {
for (Locale locale: Locale.getAvailableLocales ()) {
List filenames = CalculateFileNamesForLocale (базовое имя, локаль);
for (строковое имя файла: filenames) {
Resource resource = resourceLoader.getResource (filename + PROPERTIES_SUFFIX);
if (! resource.exists ()) {
resource = resourceLoader.getResource (filename + XML_SUFFIX);
}
if (resource.exists ()) {
resources.add (ресурс);
}
}
}
}
cacheResources = resources;
вернуть ресурсы;
}
/ **
* Получает массив базовых имен, каждое из которых следует базовому соглашению ResourceBundle, в котором не указывается расширение файла или коды языков.
*
* @ вернуть массив базовых имен или ноль, если произошла ошибка.
*
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource # setBasenames
* /
private String [] getBasenames () {
Поле field = ReflectionUtils.findField (org.springframework.context.support.ReloadableResourceBundleMessageSource.class, "basenames");
if (field == null) {
LOGGER.debug («Отсутствует базовое имя поля» из «org.springframework.context.support.ReloadableResourceBundleMessageSource 'class.");
вернуть ноль;
}
ReflectionUtils.makeAccessible (поле);
пытаться {
return (String []) field.get (this);
} catch (Exex ex) {
LOGGER.debug («Невозможно получить значение поля« basenames »из класса org.springframework.context.support.ReloadableResourceBundleMessageSource».);
вернуть ноль;
}
}
/ **
* Получает загрузчик ресурсов.
*
* @ вернуть загрузчик ресурсов.
*
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource # setResourceLoader
* /
private ResourceLoader getResourceLoader () {
Поле field = ReflectionUtils.findField (org.springframework.context.support.ReloadableResourceBundleMessageSource.class, "resourceLoader");if (field == null) {
LOGGER.debug ("Отсутствует поле 'resourceLoader' из 'org.springframework.context.support.ReloadableResourceBundleMessageSource' class.");
вернуть ноль;
}
ReflectionUtils.makeAccessible (поле);
пытаться {
return (ResourceLoader) field.get (this);
} catch (Exex ex) {
LOGGER.debug ("Невозможно получить значение поля 'resourceLoader' из класса 'org.springframework.context.support.ReloadableResourceBundleMessageSource'.);
вернуть ноль;
}
}
}
Если вы хотите использовать две функциональные возможности (получить доступные Locales и получить все сообщения Spring из файлов свойств), то вам нужно получить этот полный класс.
Чтобы использовать это ReloadableResourceBundleMessageSource , это действительно просто.
Вам необходимо объявить комплект ресурсов:
<!-- Custom message source. -->
<bean id="messageSource" class="fr.ina.archibald.web.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:config/i18n/messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
Затем вам просто нужно внедрить комплект ресурсов в класс, в который вы хотите получить доступные локали:
@Inject
private ReloadableResourceBundleMessageSource resourceBundleMessageSource;
Вот пример использования для проверки доступности локали перед автоматическим обновлением локали просмотра пользователя в базе данных, когда Spring LocaleChangeInterceptor обнаруживает изменение (например, через URL => 'http://your.domain? Lang = ан ):
package fr.ina.archibald.web.resolver;
import java.util.Locale;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import fr.ina.archibald.commons.annotation.Log;
import fr.ina.archibald.dao.entity.UserEntity;
import fr.ina.archibald.security.entity.CustomUserDetails;
import fr.ina.archibald.security.util.SecurityUtils;
import fr.ina.archibald.service.UserService;
import fr.ina.archibald.web.support.ReloadableResourceBundleMessageSource;
/**
* Custom SessionLocaleResolver.
*
* @author srambeau
*
* @see org.springframework.web.servlet.i18n.SessionLocaleResolver
*/
public class SessionLocaleResolver extends org.springframework.web.servlet.i18n.SessionLocaleResolver {
@Log
private Logger logger;
@Inject
private UserService userService;
@Inject
private ReloadableResourceBundleMessageSource resourceBundleMessageSource;
@Override
public void setLocale(HttpServletRequest req, HttpServletResponse res, Locale newLocale) {
super.setLocale(req, res, newLocale);
updateUserLocale(newLocale);
}
// /**
// * Returns the default Locale that this resolver is supposed to fall back to, if any.
// */
// @Override
// public Locale getDefaultLocale() {
// return super.getDefaultLocale();
// }
// ********************** PRIVATE METHODES **********************
/**
* Updates the locale of the currently logged in user with the new Locale.
* <p>
* The locale is not updated if the specified locale is {@code null} or the same as the previous, if the user is missing or if an error occurs.
* </p>
*
* @param newLocale the new locale.
*/
private void updateUserLocale(final Locale newLocale) {
if(newLocale == null) {
logger.debug("Cannot update the user's browsing locale. The new locale is null.");
return;
}
CustomUserDetails userDetails = SecurityUtils.getCurrentUser();
if(userDetails == null || userDetails.getUser() == null) {
logger.debug("Cannot update the user's browsing locale. The user is missing.");
return;
}
UserEntity user = userDetails.getUser();
// Updates the user locale if and only if the locale has changed and is available on the application.
if(newLocale.equals(user.getBrowsingLocale()) || ! resourceBundleMessageSource.isAvailableLocale(newLocale)) {
return;
}
user.setBrowsingLocale(newLocale);
try {
userService.update(user);
} catch(Exception ex) {
logger.error("The browsing locale of the user with identifier " + user.getUserId() + " cannot be updated.", ex);
}
}
}
Соответствующее SessionLocaleResolver объявление:
<!-- This custom SessionLocaleResolver allows to update the user Locale when it change. -->
<bean id="localeResolver" class="fr.ina.archibald.web.resolver.SessionLocaleResolver">
<property name="defaultLocale" value="fr" />
</bean>
Надеюсь, это будет вам полезно ...
Наслаждайтесь! : -)