Spring - получить все разрешимые клавиши сообщений
У меня есть веб-приложение, которое использует Spring MVC. Я хотел бы, чтобы мой интерфейс состоял всего из одной страницы, которая динамически извлекает все данные как JSON через AJAX. Моя проблема связана с интернационализацией. Когда я обрабатываю содержимое в jsps, я могу использовать теги JSTL для решения своих ключей (супер-легкий с Spring MVC):
<fmt:message key="name"/>: ${name}
<fmt:message key="title"/>: ${title}
<fmt:message key="group"/>: ${group}
При правильной настройке он отображает в финском языке как
Nimi: yay
Otsikko: hoopla
Ryhmä: doo
Теперь, когда я использую json, у меня есть только это, входящее с сервера:
{
name: "yay",
title: "hoopla",
group: "doo"
}
Нет никаких ключей! Но я должен их каким-то образом предоставить. Я рассматривал изменение ключевых слов в их локализованных формах или добавление локализованных ключевых слов в json-выход (например, name_localized = "Nimi" ), но обе эти опции выглядят как плохая практика. Я использую jackson json для автоматического анализа моих объектов домена в json, и мне нравится инкапсуляция, которую он предоставляет.
Единственное возможное решение, которое я придумал, заключается в следующем: динамически создайте файл javascript с локализованными ключевыми именами как переменными.
<script type="text/javascript">
var name="Nimi";
var title="Otsikko";
var group="Ryhmä";
</script>
Как только я загрузился, теперь у меня есть вся информация в javascript для обработки json! Но есть gotcha: мой список имен полей является динамическим. Так что статический рендеринг в jsp будет выглядеть так:
<c:forEach var="field" values="${fields}">
<fmt:message key="${field.key}"/>: ${field.value}
</c:forEach>
Мне нужно найти все сообщения, указанные в моих сообщениях .properties. Spring Интерфейс MessageSource поддерживает только получение сообщений по ключу. Как я могу получить список ключевых слов в моем JSP, который отображает локализованные переменные javascript?
Ответы
Ответ 1
Ну, я "решил" свою проблему, расширив ResourceBundleMessageSource.
package org.springframework.context.support;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;
public class ExposedResourceBundleMessageSource extends
ResourceBundleMessageSource {
public Set<String> getKeys(String basename, Locale locale) {
ResourceBundle bundle = getResourceBundle(basename, locale);
return bundle.keySet();
}
}
Теперь у меня есть доступ к клавишам, но я должен сделать уродливый бросок в моем контроллере, помимо необходимости указывать базовое имя источника сообщения. Ух, что много сцепления.
Set<String> keys =
((ExposedResourceBundleMessageSource)
messageSource).getKeys("messages", locale);
Ответ 2
Я решил эту проблему.
public class ExposedResourceBundleMessageSource extends
ResourceBundleMessageSource {
public static final String WHOLE = "whole";
private Set baseNames;
private Map> cachedData = new HashMap>();
public Set getKeys(String baseName, Locale locale) {
ResourceBundle bundle = getResourceBundle(baseName, locale);
return bundle.keySet();
}
public Map getKeyValues(String basename, Locale locale) {
String cacheKey = basename + locale.getCountry();
if (cachedData.containsKey(cacheKey)) {
return cachedData.get(cacheKey);
}
ResourceBundle bundle = getResourceBundle(basename, locale);
TreeMap treeMap = new TreeMap();
for (String key : bundle.keySet()) {
treeMap.put(key, getMessage(key, null, locale));
}
cachedData.put(cacheKey, treeMap);
return treeMap;
}
public Map getKeyValues(Locale locale) {
String cacheKey = WHOLE + locale.getCountry();
if (cachedData.containsKey(cacheKey)) {
return cachedData.get(cacheKey);
}
TreeMap treeMap = new TreeMap();
for (String baseName : baseNames) {
treeMap.putAll(getKeyValues(baseName, locale));
}
cachedData.put(cacheKey, treeMap);
return treeMap;
}
public void setBasenames(String[] basenames) {
baseNames = CollectionUtils.arrayAsSet(basenames);
super.setBasenames(basenames);
}
}
Ответ 3
Мое решение:
-
использовать spring <util:properties />
<util:properties id="message" location="classpath:messages.properties" />
-
Добавить перехватчик, с сообщением 'message' для запроса атрибутов
request.setAttribute("msgKeys", RequestContextUtils.getWebApplicationContext(request).getBean("message"));
-
В JSP используйте msgKeys
для получения сообщения через тег <fmt:message />
var msg = {<c:forEach items="${msgKeys}" var="m" varStatus="s">
<c:set var="key" value="${fn:substringBefore(m,'=')}"/>
"${fn:substringAfter(key)}":"<fmt:message key="${key}"/>",
</c:forEach>};
(Конечно, вам нужно еще больше избежать вывода для соответствия строке js.)
Итак, для свойств
app.name=Application Name
app.title=Some title
выводит
var msg = { "name":"Application Name", "title":"Some title" };
Хорошо, что вы можете сделать еще немного логики в цикле c:forEach
.
Ответ 4
Я не считаю плохую практику возвращать локализованные значения. Вы можете вернуть что-то подобное из своего контроллера (так что вам просто нужно разрешить ключи там):
{
name: {label: "Nimi", value: "yay"},
title: {label: "Ostikko", value: "hoopla"},
group: {label: "Rhymä", value: "doo"},
}
Вы можете создать промежуточный объект (самый простой был бы Map<String, Map<String, String>>
), который содержит эти значения, а jackson будет сериализовать его в JSON. Я думаю, что это лучше, чем динамический Javascript файл. Это решение также в конечном итоге делает то, что вы пытались сделать с динамическим Javascript файлом, то есть связывая ключ с его разрешенным значением.