2011-08-02 17 views
14

Mam aplikację internetową, która używa Spring MVC. Chciałbym, aby mój interfejs składał się tylko z jednej strony, która pobiera wszystkie dane dynamicznie jako JSON przez AJAX. Mój problem polega na internacjonalizacji. Kiedy renderowanie treści w JSP, mogę używać znaczników JSTL rozwiązać moje klucze (Super-Easy z wiosennym MVC):Spring - otrzymaj wszystkie rozpoznawalne klucze wiadomości

<fmt:message key="name"/>: ${name} 
<fmt:message key="title"/>: ${title} 
<fmt:message key="group"/>: ${group} 

gdy prawidłowo skonfigurowany, to czyni w fińskich lokalizacji jako

Nimi: yay 
Otsikko: hoopla 
Ryhmä: doo 

Teraz , kiedy używam json, mam tylko to przychodzące z serwera:

{ 
name: "yay", 
title: "hoopla", 
group: "doo" 
} 

Nie ma nazwisk! Ale muszę je jakoś zapewnić. Zastanawiałem się nad zmianą nazw do ich zlokalizowanych formularzy lub dodaniem zlokalizowanych nazw klawiszy do wyjścia json (np. Name_localized = "Nimi"), ale obie te opcje wyglądają na złe. Używam json json, aby automatycznie parsować moje obiekty domeny w json i podoba mi się enkapsulacja, którą zapewnia.

Jedyne możliwe rozwiązanie, które wymyśliłem, to: dynamiczne tworzenie pliku javascript ze zlokalizowanymi nazwami kluczy jako zmiennymi.

<script type="text/javascript"> 
var name="Nimi"; 
var title="Otsikko"; 
var group="Ryhmä"; 
</script> 

Gdy mam ten załadowany, teraz mam wszystkie informacje w moim javascript, aby obsłużyć json! Ale jest gotcha: moja lista nazw pól jest dynamiczna. Tak więc statyczne renderowanie w jsp będzie wyglądać tak:

<c:forEach var="field" values="${fields}"> 
<fmt:message key="${field.key}"/>: ${field.value} 
</c:forEach> 

Potrzebuję znaleźć wszystkie wiadomości określone w pliku moje messages.properties. Wiosenny interfejs MessageSource obsługuje tylko pobieranie wiadomości według klucza. Jak mogę uzyskać listę nazw klawiszy w moim JSP, który renderuje zlokalizowane zmienne javascript?

Odpowiedz

0

Nie uważam, że zwracanie zlokalizowanych wartości nie jest złe. Można powrócić coś takiego od kontrolera (tak po prostu trzeba rozwiązać tam klucze):

{ 
    name: {label: "Nimi", value: "yay"}, 
    title: {label: "Ostikko", value: "hoopla"}, 
    group: {label: "Rhymä", value: "doo"}, 
} 

Można utworzyć pośredni obiekt (najłatwiej byłoby Map<String, Map<String, String>>), który posiada te wartości i Jackson będzie je szeregować w JSON. Myślę, że jest lepsza niż opcja dynamicznego pliku JavaScript. To rozwiązanie również kończy się robieniem tego, co próbujesz zrobić z dynamicznym plikiem JavaScript, czyli powiązaniem klucza z jego wartością rozstrzygniętą.

12

Cóż, "rozwiązałem" mój problem przez rozszerzenie 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(); 
    } 
} 

Teraz mam dostęp do kluczy, ale muszę zrobić brzydki obsadę w moim kontrolera, oprócz konieczności określenia basename źródło wiadomości. Ugh, to dużo sprzężenia.

Set<String> keys = 
    ((ExposedResourceBundleMessageSource) 
    messageSource).getKeys("messages", locale); 
+2

Cóż, możesz usunąć odrobinę brzydoty, ustawiając niestandardowe źródło wiadomości jako domyślne. Trudno to wszystko umieścić w komentarzu, ale '' w twojej konfiguracji, a następnie '@Autowired private ExposedResourceBundleMessageSourcemessageSource;' w twojej klasie. – Patrick

+0

Dla tych, którzy szukają sposobu, w jaki powinieneś uzyskać 'Locale', prawdopodobnie będziesz chciał użyć' LocaleContextHolder.getLocale() '. Spowoduje to przywiązanie ustawień narodowych do wątku, który zwykle jest tym, czego potrzebujesz w środowisku serwlet/wiosna mvc. –

4

Mam rozwiązać ten problem.


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); 
    } 


} 
+1

Jest to dobre rozwiązanie z powodu dodawania buforowania i automatycznego przechwytywania nazw. – JBCP

+1

Jednak użycie opcji locale.getCountry() może być problemem, jeśli obsługujesz wiele języków w tym samym kraju. Samo użycie locale jest prawdopodobnie bezpieczniejsze. – JBCP

1

Moje rozwiązanie:

  1. wykorzystanie wiosna <util:properties />

    <util:properties id="message" location="classpath:messages.properties" /> 
    
  2. Dodaj przechwytujących, z 'wiadomości' ustawioną na życzenie atrybuty

    request.setAttribute("msgKeys", RequestContextUtils.getWebApplicationContext(request).getBean("message")); 
    
  3. W JSP, użyj msgKeys odzyskać wiadomość poprzez <fmt:message /> tagu

    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>}; 
    

(Oczywiście trzeba dodatkowo uciec wyjście dopasować JS łańcuch.)

Tak, dla właściwości

app.name=Application Name 
app.title=Some title 

byłoby wyjście

var msg = { "name":"Application Name", "title":"Some title" }; 

Dobrą rzeczą jest to, że można zrobić więcej logiki w pętli c:forEach.

Powiązane problemy