Commit 57ce374d authored by Ubbo Veentjer's avatar Ubbo Veentjer
Browse files

Feature/i18n

parent 7a609031
......@@ -6,11 +6,15 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
// config contains all the int-stuff now, compare
// https://www.baeldung.com/spring-boot-internationalization
// https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/session-locale-resolver.html
// https://phrase.com/blog/posts/spring-boot-internationalization/
@Configuration
public class TgRepConfiguration implements WebMvcConfigurer {
......@@ -29,7 +33,7 @@ public class TgRepConfiguration implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
//slr.setDefaultLocale(Locale.ENGLISH);
return slr;
}
......
package info.textgrid.rep.advancedsearch;
import java.util.HashMap;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import info.textgrid.rep.shared.I18NUtils;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
@Controller
public class AdvancedSearchController {
private static final Log log = LogFactory.getLog(AdvancedSearchController.class);
private I18NProvider i18nProvider;
@Autowired
public AdvancedSearchController(I18NProvider i18nProvider) {
this.i18nProvider = i18nProvider;
}
@GetMapping("/advanced-search")
public String render(
Locale locale,
Model model) {
HashMap<String, String> i18n = I18NUtils.i18n(locale);
model.addAttribute("i18n", i18n);
model.addAttribute("language", locale.getLanguage());
I18N i18n = i18nProvider.getI18N(locale);
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
return "advancedsearch";
}
......
......@@ -4,7 +4,6 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import javax.ws.rs.client.ClientBuilder;
......@@ -18,10 +17,11 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import info.textgrid.namespaces.middleware.tgsearch.ResultType;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
import info.textgrid.rep.service.tgsearch.AggregatorClientService;
import info.textgrid.rep.service.tgsearch.TgrepConfigurationService;
import info.textgrid.rep.service.tgsearch.TgsearchClientService;
import info.textgrid.rep.shared.I18NUtils;
import info.textgrid.rep.shared.Utils;
import info.textgrid.rep.shared.ViewMode;
import io.jsonwebtoken.Claims;
......@@ -35,15 +35,20 @@ public class BrowseController {
private TgsearchClientService tgsearchClientService;
private TgrepConfigurationService tgrepConfig;
private AggregatorClientService aggregatorClient;
private I18NProvider i18nProvider;
private static final Log log = LogFactory.getLog(BrowseController.class);
@Autowired
public BrowseController(TgsearchClientService tgsearchClientService,
AggregatorClientService aggregatorClient, TgrepConfigurationService tgrepConfig) {
public BrowseController(
TgsearchClientService tgsearchClientService,
AggregatorClientService aggregatorClient,
TgrepConfigurationService tgrepConfig,
I18NProvider i18nProvider) {
this.tgsearchClientService = tgsearchClientService;
this.aggregatorClient = aggregatorClient;
this.tgrepConfig = tgrepConfig;
this.i18nProvider = i18nProvider;
}
@GetMapping("/browse/{id}")
......@@ -54,13 +59,14 @@ public class BrowseController {
@RequestParam(value = "mode", defaultValue = "list") String mode,
@RequestParam(value = "fragment", required = false) String fragment) {
HashMap<String, String> i18n = I18NUtils.i18n(locale);
I18N i18n = i18nProvider.getI18N(locale);
// common variables for browse-root aggregations and browse single items
model.addAttribute("textgridHost", this.tgrepConfig.getTextgridHost());
model.addAttribute("mode", mode);
model.addAttribute("i18n", i18n);
model.addAttribute("language", locale.getLanguage());
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
// if no id, browse all root aggregations
......
......@@ -12,22 +12,25 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import info.textgrid.namespaces.middleware.tgsearch.FacetType;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
import info.textgrid.rep.service.tgsearch.TgsearchClientService;
import info.textgrid.rep.shared.I18NUtils;
@Controller
public class BrowseFacetController {
// injected service
private TgsearchClientService tgsearchClientService;
private I18NProvider i18nProvider;
private static final Log log = LogFactory.getLog(BrowseFacetController.class);
@Autowired
public BrowseFacetController(TgsearchClientService tgsearchClientService) {
public BrowseFacetController(
TgsearchClientService tgsearchClientService, I18NProvider i18nProvider) {
this.tgsearchClientService = tgsearchClientService;
this.i18nProvider = i18nProvider;
}
@GetMapping("/facet/{facet}")
......@@ -38,6 +41,7 @@ public class BrowseFacetController {
@RequestParam(value="limit", required=false, defaultValue="0") int limit,
@RequestParam(value="order", required=false, defaultValue="count.desc") String order) {
I18N i18n = i18nProvider.getI18N(locale);
// boolean sandbox = Utils.userWantsSandbox(PortalUtil.getUserId(renderRequest));
boolean sandbox = true;
......@@ -48,8 +52,8 @@ public class BrowseFacetController {
List<FacetType> facets = tgsearchClientService.listFacet(facetList, limit, order, sandbox)
.getFacetGroup().get(0).getFacet();
model.addAttribute("i18n", I18NUtils.i18n(locale));
model.addAttribute("language", locale.getLanguage());
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
model.addAttribute("facets", facets);
model.addAttribute("facet", facet);
return "browsefacet";
......
package info.textgrid.rep.i18n;
import java.util.HashMap;
/**
* Keep track of language map and its name string for use with jsp controllers.
*
* This class keeps the compatibility to jsp code accessing an i18n hashmap,
* which originated in porting JavaServerFaces el-i18n functionality.
*
* @author Ubbo Veentjer
*/
public class I18N {
private String language;
private HashMap<String, String> messages;
public String getLanguage() {
return language;
}
public I18N(String language, HashMap<String, String> messages) {
this.language = language;
this.messages = messages;
}
/**
* convenience function for accessing single entry of the messages hashmap
* @param key
* @return
*/
public String get(String key) {
return this.messages.get(key);
}
public HashMap<String, String> getTranslationMap() {
return messages;
}
}
package info.textgrid.rep.i18n;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* Utility class to keep track of available and requested language.
*
* Available and default language are configured by lang.available
* and lang.default spring configuration properties. The translations
* are loaded from i18n/Language_xx.properties
*
* This class keeps the compatibility to jsp code accessing an
* i18n hashmap, which originated in porting
* JavaServerFaces el-i18n functionality.
*
* @author Ubbo Veentjer
*/
@Component
public class I18NProvider {
@Value("${lang.available}")
private List<String> langAvailable;
@Value("${lang.default}")
private String langDefault;
private HashMap<String, I18N> i18nMap = new HashMap<String, I18N>();
@PostConstruct
public void postConstruct() {
for (String lang : langAvailable) {
I18N i18n = new I18N(lang, I18NUtils.getTranslationMap(new Locale(lang)));
i18nMap.put(lang, i18n);
}
}
/**
* Get matching i18n object for a locale
*
* @param locale
* @return i18n object for a locale
*/
public I18N getI18N(Locale locale) {
Locale selectedLocale;
if(langAvailable.contains(locale.getLanguage())) {
selectedLocale = locale;
} else {
selectedLocale = new Locale(langDefault);
}
return i18nMap.get(selectedLocale.getLanguage());
}
public List<String> getLangAvailable() {
return langAvailable;
}
public void setLangAvailable(List<String> langAvailable) {
this.langAvailable = langAvailable;
}
public String getLangDefault() {
return langDefault;
}
public void setLangDefault(String langDefault) {
this.langDefault = langDefault;
}
}
package info.textgrid.rep.i18n;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
public class I18NUtils {
public static HashMap<String, String> getTranslationMap(Locale locale) {
HashMap<String, String> messages = new HashMap<String, String>();
ResourceBundle rb = ResourceBundle.getBundle("i18n.Language", locale, new UTF8Control());
for (String key : rb.keySet()) {
messages.put(key, rb.getString(key));
}
return messages;
}
// read resource bundle in utf8: http://stackoverflow.com/a/4660195
private static class UTF8Control extends Control {
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format,
ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
// The below is a copy of the default implementation.
String bundleName = toBundleName(baseName, locale);
String resourceName = toResourceName(bundleName, "properties");
ResourceBundle bundle = null;
InputStream stream = null;
if (reload) {
URL url = loader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
} else {
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null) {
try {
// Only this line is changed to make it to read properties files as UTF-8.
bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
} finally {
stream.close();
}
}
return bundle;
}
}
}
package info.textgrid.rep.markdown;
import java.util.HashMap;
import java.util.Locale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import info.textgrid.rep.shared.I18NUtils;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
@Controller
public class DocController {
private I18NProvider i18nProvider;
@Autowired
public DocController(I18NProvider i18nService) {
this.i18nProvider = i18nService;
}
@GetMapping("/")
public String renderIndex(
Model model,
......@@ -18,7 +26,6 @@ public class DocController {
) {
return renderMarkdown(model, "index", locale);
}
@GetMapping("/doc/{doc}")
......@@ -27,14 +34,16 @@ public class DocController {
@PathVariable("doc") String doc,
Locale locale) {
String dloc = "docs/" + doc + "." + locale.getLanguage() + ".md";
I18N i18n = i18nProvider.getI18N(locale);
String dloc = "docs/" + doc + "." + i18n.getLanguage() + ".md";
String content = MarkdownRenderer.renderDoc(dloc);
model.addAttribute("content", content);
// translation array
HashMap<String, String> i18n = I18NUtils.i18n(locale);
model.addAttribute("i18n", i18n);
model.addAttribute("language", locale.getLanguage());
//HashMap<String, String> i18n = I18NUtils.i18n(locale);
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
return "markdown";
......
package info.textgrid.rep.search;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import org.slf4j.Logger;
......@@ -13,25 +12,31 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.HtmlUtils;
import info.textgrid.namespaces.middleware.tgsearch.Response;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
import info.textgrid.rep.service.tgsearch.TgrepConfigurationService;
import info.textgrid.rep.service.tgsearch.TgsearchClientService;
import info.textgrid.rep.shared.Pager;
import info.textgrid.rep.shared.Utils;
import info.textgrid.rep.shared.ViewMode;
import info.textgrid.rep.shared.I18NUtils;
@Controller
public class SearchController {
private TgsearchClientService tgsearchClientService;
private TgrepConfigurationService tgrepConfig;
private I18NProvider i18nProvider;
private static final Logger logger = LoggerFactory.getLogger(SearchController.class);
@Autowired
public SearchController(TgsearchClientService tgsearchClientService, TgrepConfigurationService tgrepConfig) {
this.tgsearchClientService = tgsearchClientService;
this.tgrepConfig = tgrepConfig;
public SearchController(
TgsearchClientService tgsearchClientService,
TgrepConfigurationService tgrepConfig,
I18NProvider i18nProvider) {
this.tgsearchClientService = tgsearchClientService;
this.tgrepConfig = tgrepConfig;
this.i18nProvider = i18nProvider;
}
@GetMapping("/search")
......@@ -45,12 +50,7 @@ public class SearchController {
Locale locale,
Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
System.out.println("Welcome home! The client locale is: " + locale);
//HashMap<String, String> i18n = I18NUtils.i18n(new Locale("de"));
// only do this if locales are restricted to en & de!
HashMap<String, String> i18n = I18NUtils.i18n(locale);
I18N i18n = i18nProvider.getI18N(locale);
//model.addAttribute("query", query);
//return "search";
......@@ -105,13 +105,13 @@ public class SearchController {
model.addAttribute("textgridHost", this.tgrepConfig.getTextgridHost());
// translation array
model.addAttribute("i18n", i18n);
model.addAttribute("language", locale.getLanguage());
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
// which .jsp to render
return "search";
}
}
package info.textgrid.rep.shared;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
public class I18NUtils {
public static HashMap<String, String> i18n (Locale locale) {
HashMap<String, String> messages = new HashMap<String, String>();
ResourceBundle rb = ResourceBundle.getBundle("i18n.Language", locale, new UTF8Control());
for(String key : rb.keySet()) {
messages.put(key, rb.getString(key));
}
return messages;
}
// read resource bundle in utf8: http://stackoverflow.com/a/4660195
private static class UTF8Control extends Control {
@Override
public ResourceBundle newBundle
(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException
{
// The below is a copy of the default implementation.
String bundleName = toBundleName(baseName, locale);
String resourceName = toResourceName(bundleName, "properties");
ResourceBundle bundle = null;
InputStream stream = null;
if (reload) {
URL url = loader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
} else {
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null) {
try {
// Only this line is changed to make it to read properties files as UTF-8.
bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
} finally {
stream.close();
}
}
return bundle;
}
}
}
......@@ -3,10 +3,13 @@ spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
# textgrid defaults
textgrid.host: https://textgridlab.org
textgrid.host: https://dev.textgridlab.org
# tool defaults
tool.voyant.host: https://voyant-tools.org
tool.annotate.host: https://annotation.de.dariah.eu/AnnotationViewer
tool.mirador.host: https://textgridlab.org/1.0/iiif/mirador
tool.digilib.host: https://textgridlab.org/1.0/tgrep/digilib
lang.available: en, de
lang.default: en
......@@ -117,5 +117,5 @@ search-term=Suchbegriff
settings=Einstellungen
shelf=Regal
imprint=Impressum
privpol=Datenschutzerklrung
help=Hilfe
\ No newline at end of file
privpol=Datenschutzerklärung
help=Hilfe
......@@ -4,10 +4,10 @@
<div class="tg footer_left">
<ul>
<li>
<a class="tg footer_contact" href="https://textgrid.de/en/kontakt/">Contact</a>
<a class="tg footer_contact" href="https://textgrid.de/en/kontakt/">${i18n['contact']}</a>
</li>
<li><a href="https://de.dariah.eu/impressum">Imprint</a></li>
<li><a href="https://de.dariah.eu/privacy-policy">Privacy Policy</a></li>
<li><a href="https://de.dariah.eu/impressum">${i18n['imprint']}</a></li>
<li><a href="https://de.dariah.eu/privacy-policy">${i18n['privpol']}</a></li>
<li><a href="https://creativecommons.org/licenses/by/4.0/deed.de">CC BY 4.0</a></li>
</ul>
<ul>
......
......@@ -10,6 +10,7 @@ import java.util.Locale;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;