From 827351bdf5973799af843bb0ea486ecb2ad8521f Mon Sep 17 00:00:00 2001 From: Elex Date: Mon, 16 Aug 2021 13:47:25 +0900 Subject: [PATCH] 2021-08-16 --- i18n-mustache/build.gradle.kts | 27 +++++++ .../java/kr/pe/elex/examples/Application.java | 20 +++++ .../elex/examples/CustomLocaleResolver.java | 81 +++++++++++++++++++ .../java/kr/pe/elex/examples/I18nAdvice.java | 1 + .../kr/pe/elex/examples/MyController.java | 36 +++++++++ .../java/kr/pe/elex/examples/WebConfig.java | 60 ++++++++++++++ .../kr/pe/elex/examples/package-info.java | 8 ++ .../src/main/resources/application.yaml | 9 +++ i18n-mustache/src/main/resources/banner.txt | 10 +++ .../src/main/resources/i18n.properties | 2 + .../src/main/resources/i18n_ko.properties | 1 + .../src/main/resources/logback-spring.xml | 48 +++++++++++ .../main/resources/templates/main.mustache | 1 + i18n/build.gradle.kts | 4 +- .../elex/examples/CustomLocaleResolver.java | 33 ++++++-- .../kr/pe/elex/examples/MyController.java | 4 +- .../java/kr/pe/elex/examples/WebConfig.java | 13 ++- i18n/src/main/resources/application.yaml | 9 ++- i18n/src/main/resources/i18n.properties | 2 + i18n/src/main/resources/i18n_en.properties | 1 - i18n/src/main/resources/templates/home.html | 13 +++ settings.gradle.kts | 2 +- 22 files changed, 365 insertions(+), 20 deletions(-) create mode 100644 i18n-mustache/build.gradle.kts create mode 100644 i18n-mustache/src/main/java/kr/pe/elex/examples/Application.java create mode 100644 i18n-mustache/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java rename {i18n => i18n-mustache}/src/main/java/kr/pe/elex/examples/I18nAdvice.java (95%) create mode 100644 i18n-mustache/src/main/java/kr/pe/elex/examples/MyController.java create mode 100644 i18n-mustache/src/main/java/kr/pe/elex/examples/WebConfig.java create mode 100644 i18n-mustache/src/main/java/kr/pe/elex/examples/package-info.java create mode 100644 i18n-mustache/src/main/resources/application.yaml create mode 100644 i18n-mustache/src/main/resources/banner.txt create mode 100644 i18n-mustache/src/main/resources/i18n.properties create mode 100644 i18n-mustache/src/main/resources/i18n_ko.properties create mode 100644 i18n-mustache/src/main/resources/logback-spring.xml rename {i18n => i18n-mustache}/src/main/resources/templates/main.mustache (58%) create mode 100644 i18n/src/main/resources/i18n.properties delete mode 100644 i18n/src/main/resources/i18n_en.properties create mode 100644 i18n/src/main/resources/templates/home.html diff --git a/i18n-mustache/build.gradle.kts b/i18n-mustache/build.gradle.kts new file mode 100644 index 0000000..b6071fc --- /dev/null +++ b/i18n-mustache/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +plugins { + id("elex-spring-boot") + + id("org.springframework.boot") version "2.5.3" + id("io.spring.dependency-management") version "1.0.11.RELEASE" +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-mustache") + + compileOnly("org.projectlombok:lombok") + developmentOnly("org.springframework.boot:spring-boot-devtools") + + annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + annotationProcessor("org.projectlombok:lombok") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + +} diff --git a/i18n-mustache/src/main/java/kr/pe/elex/examples/Application.java b/i18n-mustache/src/main/java/kr/pe/elex/examples/Application.java new file mode 100644 index 0000000..7ff4bbf --- /dev/null +++ b/i18n-mustache/src/main/java/kr/pe/elex/examples/Application.java @@ -0,0 +1,20 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/i18n-mustache/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java b/i18n-mustache/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java new file mode 100644 index 0000000..4a68154 --- /dev/null +++ b/i18n-mustache/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java @@ -0,0 +1,81 @@ +package kr.pe.elex.examples; + +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.jni.Local; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * 로케일 리졸버 + * AcceptHeaderLocaleResolver를 기본 정책으로하되, GET 파라미터로 로캐일 변환이 가능하도록 한다. + * @author Elex + */ +@Slf4j +public class CustomLocaleResolver implements LocaleResolver { + + /** + * 지원되는 로캐일 목록. 첫 번째 로캐일은 기본 로캐일로 사용된다. + */ + private static final List supportedLocales = List + .of(Locale.KOREAN, Locale.ENGLISH); + + //private Locale locale = null; + private final AcceptHeaderLocaleResolver acceptHeaderLocaleResolver; + + public CustomLocaleResolver() { + super(); + + acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver(); + acceptHeaderLocaleResolver.setSupportedLocales(supportedLocales); + acceptHeaderLocaleResolver.setDefaultLocale(Locale.ENGLISH); + } + + @Override + public @NotNull Locale resolveLocale(@NotNull HttpServletRequest request) { + String lang = request.getParameter("language"); + if (null == lang) { + return acceptHeaderLocaleResolver.resolveLocale(request); + + } else { + Locale loc = new Locale(lang); + List priorityList = new ArrayList<>(); + priorityList.add(new Locale.LanguageRange(loc.toLanguageTag(), 1.0)); + priorityList.add(new Locale.LanguageRange(loc.getLanguage(), 0.9)); + priorityList.add(new Locale.LanguageRange(Locale.ENGLISH.getLanguage(),0.3)); + //priorityList.add(new Locale.LanguageRange(Locale.ROOT.getLanguage(),0.1)); + log.debug("PriorityList: {}", priorityList); + Locale pick = Locale.lookup( + priorityList, + supportedLocales); + log.debug("Picked locale: {}", pick); + return pick; + /*for (Locale loc : supportedLocales) { + if (locale.equals(loc)) return loc; + } + for (Locale loc : supportedLocales) { + if (locale.getLanguage().equals(loc.getLanguage())) return loc; + } + return supportedLocales.get(0);*/ + } + } + + /** + * 세션 등에 저장하고 싶을 때 사용한다. + * @param request + * @param response + * @param locale + */ + @Override + public void setLocale(@NotNull HttpServletRequest request, HttpServletResponse response, + Locale locale) { + //this.locale = locale; + + } +} diff --git a/i18n/src/main/java/kr/pe/elex/examples/I18nAdvice.java b/i18n-mustache/src/main/java/kr/pe/elex/examples/I18nAdvice.java similarity index 95% rename from i18n/src/main/java/kr/pe/elex/examples/I18nAdvice.java rename to i18n-mustache/src/main/java/kr/pe/elex/examples/I18nAdvice.java index 1e6f4b1..5d6f0cd 100644 --- a/i18n/src/main/java/kr/pe/elex/examples/I18nAdvice.java +++ b/i18n-mustache/src/main/java/kr/pe/elex/examples/I18nAdvice.java @@ -1,5 +1,6 @@ package kr.pe.elex.examples; +//import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Mustache; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/i18n-mustache/src/main/java/kr/pe/elex/examples/MyController.java b/i18n-mustache/src/main/java/kr/pe/elex/examples/MyController.java new file mode 100644 index 0000000..56cfac8 --- /dev/null +++ b/i18n-mustache/src/main/java/kr/pe/elex/examples/MyController.java @@ -0,0 +1,36 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.util.HashMap; + +@Slf4j +@Controller +public class MyController { + + @GetMapping(path = {"/"}) + public String index(ModelAndView modelAndView) throws Exception { + //log.info("Data: {}", modelAndView); + return "main"; + } + +} diff --git a/i18n-mustache/src/main/java/kr/pe/elex/examples/WebConfig.java b/i18n-mustache/src/main/java/kr/pe/elex/examples/WebConfig.java new file mode 100644 index 0000000..9db02fc --- /dev/null +++ b/i18n-mustache/src/main/java/kr/pe/elex/examples/WebConfig.java @@ -0,0 +1,60 @@ +package kr.pe.elex.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Description; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.context.support.ResourceBundleMessageSource; +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.LocaleChangeInterceptor; + +import java.util.Locale; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Value("${spring.messages.basename:i18n}") + String messagesBasename = null; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } + + /** + * Locale resolver, 디폴트 로캐일을 지정한다. + */ + @Bean + public LocaleResolver localeResolver() { + /* + SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver(); + sessionLocaleResolver.setDefaultLocale(Locale.US); + return sessionLocaleResolver; + */ + return new CustomLocaleResolver(); + } + + /** + * Locale change interceptor, 요청 파라미터에 따른 로케일 변경 + */ + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); + localeChangeInterceptor.setParamName("language"); + return localeChangeInterceptor; + } + + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource messageSource + = new ReloadableResourceBundleMessageSource(); + messageSource.setBasename("classpath:/" + messagesBasename); + messageSource.setDefaultEncoding("UTF-8"); + return messageSource; + } + + +} diff --git a/i18n-mustache/src/main/java/kr/pe/elex/examples/package-info.java b/i18n-mustache/src/main/java/kr/pe/elex/examples/package-info.java new file mode 100644 index 0000000..ce9cc62 --- /dev/null +++ b/i18n-mustache/src/main/java/kr/pe/elex/examples/package-info.java @@ -0,0 +1,8 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; diff --git a/i18n-mustache/src/main/resources/application.yaml b/i18n-mustache/src/main/resources/application.yaml new file mode 100644 index 0000000..b65dcf3 --- /dev/null +++ b/i18n-mustache/src/main/resources/application.yaml @@ -0,0 +1,9 @@ +spring: + application: + name: My spring-boot project + mustache: + expose-request-attributes: true + messages: + basename: i18n +server: + port: 8080 diff --git a/i18n-mustache/src/main/resources/banner.txt b/i18n-mustache/src/main/resources/banner.txt new file mode 100644 index 0000000..f7a35db --- /dev/null +++ b/i18n-mustache/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + ('-. ('-. ) (`-. + _( OO) _( OO) ( OO ). +(,------.,--. (,------.(_/. \_)-. + | .---'| |.-') | .---' \ `.' / + | | | | OO ) | | \ /\ +(| '--. | |`-' |(| '--. \ \ | + | .--'(| '---.' | .--' .' \_) + | `---.| | | `---. / .'. \ + `------'`------' `------''--' '--' +powered by ELEX diff --git a/i18n-mustache/src/main/resources/i18n.properties b/i18n-mustache/src/main/resources/i18n.properties new file mode 100644 index 0000000..a0c79d4 --- /dev/null +++ b/i18n-mustache/src/main/resources/i18n.properties @@ -0,0 +1,2 @@ +hello.text = Hello +only.text = Root Only diff --git a/i18n-mustache/src/main/resources/i18n_ko.properties b/i18n-mustache/src/main/resources/i18n_ko.properties new file mode 100644 index 0000000..7a98a2d --- /dev/null +++ b/i18n-mustache/src/main/resources/i18n_ko.properties @@ -0,0 +1 @@ +hello.text = 안녕 diff --git a/i18n-mustache/src/main/resources/logback-spring.xml b/i18n-mustache/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..19f023b --- /dev/null +++ b/i18n-mustache/src/main/resources/logback-spring.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + UTF-8 + ${FILE_LOG_PATTERN} + + ${LOG_PATH} + + ${LOG_DIR}/sebastian_%d{yyyy-MM-dd}_%i.log.gz + + 10MB + + 60 + + + + + + + + + + + + + diff --git a/i18n/src/main/resources/templates/main.mustache b/i18n-mustache/src/main/resources/templates/main.mustache similarity index 58% rename from i18n/src/main/resources/templates/main.mustache rename to i18n-mustache/src/main/resources/templates/main.mustache index f622c0e..9f8ad38 100644 --- a/i18n/src/main/resources/templates/main.mustache +++ b/i18n-mustache/src/main/resources/templates/main.mustache @@ -1,2 +1,3 @@

i18n

{{#i18n}}hello.text{{/i18n}}

+

{{#i18n}}only.text{{/i18n}}

diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index b6071fc..5caba55 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -14,8 +14,8 @@ plugins { dependencies { implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-mustache") - + //implementation("org.springframework.boot:spring-boot-starter-mustache") + implementation("org.springframework.boot:spring-boot-starter-thymeleaf") compileOnly("org.projectlombok:lombok") developmentOnly("org.springframework.boot:spring-boot-devtools") diff --git a/i18n/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java b/i18n/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java index f09ac52..4a68154 100644 --- a/i18n/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java +++ b/i18n/src/main/java/kr/pe/elex/examples/CustomLocaleResolver.java @@ -1,12 +1,14 @@ package kr.pe.elex.examples; import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.jni.Local; import org.jetbrains.annotations.NotNull; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -22,9 +24,9 @@ public class CustomLocaleResolver implements LocaleResolver { * 지원되는 로캐일 목록. 첫 번째 로캐일은 기본 로캐일로 사용된다. */ private static final List supportedLocales = List - .of(Locale.ENGLISH, Locale.KOREAN); + .of(Locale.KOREAN, Locale.ENGLISH); - private Locale locale = null; + //private Locale locale = null; private final AcceptHeaderLocaleResolver acceptHeaderLocaleResolver; public CustomLocaleResolver() { @@ -32,26 +34,40 @@ public class CustomLocaleResolver implements LocaleResolver { acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver(); acceptHeaderLocaleResolver.setSupportedLocales(supportedLocales); - acceptHeaderLocaleResolver.setDefaultLocale(supportedLocales.get(0)); + acceptHeaderLocaleResolver.setDefaultLocale(Locale.ENGLISH); } @Override public @NotNull Locale resolveLocale(@NotNull HttpServletRequest request) { - if (null == locale) { + String lang = request.getParameter("language"); + if (null == lang) { return acceptHeaderLocaleResolver.resolveLocale(request); + } else { - for (Locale loc : supportedLocales) { + Locale loc = new Locale(lang); + List priorityList = new ArrayList<>(); + priorityList.add(new Locale.LanguageRange(loc.toLanguageTag(), 1.0)); + priorityList.add(new Locale.LanguageRange(loc.getLanguage(), 0.9)); + priorityList.add(new Locale.LanguageRange(Locale.ENGLISH.getLanguage(),0.3)); + //priorityList.add(new Locale.LanguageRange(Locale.ROOT.getLanguage(),0.1)); + log.debug("PriorityList: {}", priorityList); + Locale pick = Locale.lookup( + priorityList, + supportedLocales); + log.debug("Picked locale: {}", pick); + return pick; + /*for (Locale loc : supportedLocales) { if (locale.equals(loc)) return loc; } for (Locale loc : supportedLocales) { if (locale.getLanguage().equals(loc.getLanguage())) return loc; } - return supportedLocales.get(0); + return supportedLocales.get(0);*/ } } /** - * 안터셉터에서 처리될껄? + * 세션 등에 저장하고 싶을 때 사용한다. * @param request * @param response * @param locale @@ -59,6 +75,7 @@ public class CustomLocaleResolver implements LocaleResolver { @Override public void setLocale(@NotNull HttpServletRequest request, HttpServletResponse response, Locale locale) { - this.locale = locale; + //this.locale = locale; + } } diff --git a/i18n/src/main/java/kr/pe/elex/examples/MyController.java b/i18n/src/main/java/kr/pe/elex/examples/MyController.java index bcab714..1e54aa7 100644 --- a/i18n/src/main/java/kr/pe/elex/examples/MyController.java +++ b/i18n/src/main/java/kr/pe/elex/examples/MyController.java @@ -18,6 +18,7 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import java.util.HashMap; @@ -28,8 +29,7 @@ public class MyController { @GetMapping(path = {"/"}) public String index() throws Exception { - - return "main"; + return "home"; } } diff --git a/i18n/src/main/java/kr/pe/elex/examples/WebConfig.java b/i18n/src/main/java/kr/pe/elex/examples/WebConfig.java index b7ced2b..6238097 100644 --- a/i18n/src/main/java/kr/pe/elex/examples/WebConfig.java +++ b/i18n/src/main/java/kr/pe/elex/examples/WebConfig.java @@ -4,12 +4,16 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Description; import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.context.support.ResourceBundleMessageSource; 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.LocaleChangeInterceptor; +import java.util.Locale; + @Configuration public class WebConfig implements WebMvcConfigurer { @Value("${spring.messages.basename:i18n}") @@ -44,11 +48,12 @@ public class WebConfig implements WebMvcConfigurer { } @Bean - public MessageSource messageSource() { - ReloadableResourceBundleMessageSource messageSource - = new ReloadableResourceBundleMessageSource(); - messageSource.setBasename("classpath:/" + messagesBasename); + public ResourceBundleMessageSource messageSource() { + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + messageSource.setBasename(messagesBasename); messageSource.setDefaultEncoding("UTF-8"); + messageSource.setDefaultLocale(Locale.ENGLISH); return messageSource; } + } diff --git a/i18n/src/main/resources/application.yaml b/i18n/src/main/resources/application.yaml index b65dcf3..25cbbfa 100644 --- a/i18n/src/main/resources/application.yaml +++ b/i18n/src/main/resources/application.yaml @@ -1,9 +1,14 @@ spring: application: name: My spring-boot project - mustache: - expose-request-attributes: true messages: basename: i18n + thymeleaf: + enabled: true + cache: false + encoding: UTF-8 + mode: HTML + prefix: classpath:/templates/ + suffix: .html server: port: 8080 diff --git a/i18n/src/main/resources/i18n.properties b/i18n/src/main/resources/i18n.properties new file mode 100644 index 0000000..a0c79d4 --- /dev/null +++ b/i18n/src/main/resources/i18n.properties @@ -0,0 +1,2 @@ +hello.text = Hello +only.text = Root Only diff --git a/i18n/src/main/resources/i18n_en.properties b/i18n/src/main/resources/i18n_en.properties deleted file mode 100644 index cdca0dc..0000000 --- a/i18n/src/main/resources/i18n_en.properties +++ /dev/null @@ -1 +0,0 @@ -hello.text = Hello diff --git a/i18n/src/main/resources/templates/home.html b/i18n/src/main/resources/templates/home.html new file mode 100644 index 0000000..8163eb1 --- /dev/null +++ b/i18n/src/main/resources/templates/home.html @@ -0,0 +1,13 @@ + + + + + Sample + + +

i18n

+

HELLO_TEXT

+

Mmm... 뭥

+ + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 65f7d21..0a8b5cd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,5 +9,5 @@ rootProject.name = "spring-boot-examples" include( "file-upload", "security", "security-with-jpa", "validation", "testing", "mqtt", "websocket", "restful", "swing", "rest-doc", - "cache", "security-with-jwt", "exception", "i18n", "mvc" + "cache", "security-with-jwt", "exception", "i18n", "i18n-mustache","mvc" )