ตัวอย่างการทำ Spring-boot Reactive ให้รองรับหลายภาษา (i18n)
- มีความเข้าใจเรื่อง Internationalization (i18n) และ Localization (L10n)
- ถ้าไม่เข้าใจสามารถเรียนรู้ได้จาก สรุปสั้น ๆ เรื่อง Internationalization (i18n) และ Localization (L10n)
pom.xml
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>build-info</id>
<goals>
<goal>build-info</goal>
</goals>
<configuration>
<additionalProperties>
<java.version>${java.version}</java.version>
</additionalProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
@SpringBootApplication
@ComponentScan(basePackages = {"me.jittagornp"})
public class AppStarter {
public static void main(String[] args) {
SpringApplication.run(AppStarter.class, args);
}
}
สร้าง Java Class Config ขึ้นมา แล้วกำหนด Locale ตามที่ต้องการ
@Slf4j
@Configuration
public class LocaleConfig {
@PostConstruct
public void setDefaultLocale(){
Locale.setDefault(Locale.forLanguageTag("th-TH"));
log.info("set Default Locale => {}", Locale.getDefault());
}
}
เป็น Properties File เพื่อกำหนดเป็นข้อความแปลสำหรับแต่ละภาษา โดยเราจะเอาไว้ที่ /src/main/resources/i18n/
ภาษาไทย (Default) - messages.properties
hello=สวัสดี {0}
ภาษาอังกฤษ - messages_en.properties
hello=Hello {0}
ภาษาจีน - messages_zh.properties
hello=你好 {0}
ข้อสังเกต
- สังเกตว่า นอกจาก Default (ภาษาไทย) แล้ว File messages อื่น ๆ จะตามด้วย
_
แล้วเป็น Language Code - ขอย้ำว่าให้ใช้เป็น Language Code
เพื่อทำการเปลี่ยนภาษา (Switch Locale) ตามเงื่อนไขที่ต้องการ
public static class CustomLocaleContextResolver implements LocaleContextResolver {
private static final String LANGUAGE_PARAMETER_NAME = "lang";
private Locale getRequestLocale(final ServerWebExchange exchange) {
final ServerHttpRequest request = exchange.getRequest();
final String lang = request.getQueryParams().getFirst(LANGUAGE_PARAMETER_NAME);
if (hasText(lang)) {
return Locale.forLanguageTag(lang);
}
return request.getHeaders()
.getAcceptLanguageAsLocales()
.stream()
.findFirst()
.orElse(Locale.getDefault());
}
@Override
public LocaleContext resolveLocaleContext(final ServerWebExchange exchange) {
return new SimpleLocaleContext(getRequestLocale(exchange));
}
@Override
public void setLocaleContext(final ServerWebExchange exchange, final LocaleContext localeContext) {
//Unused
}
}
คำอธิบาย
จาก Code ด้านบน ใช้การตัดสินใจเพื่อเปลี่ยนภาษาดังนี้
- ใช้ Query string parameter
lang
ในการกำหนดภาษา ซึ่งสามารถส่งมาเป็น Language Code หรือ Language Tag ก็ได้ - ถ้าไม่ได้ส่ง Params
lang
มา ให้อ่านค่าภาษาจาก Http HeaderAccept-Language
ซึ่งเป็น Header ที่เอาไว้บอกว่า ณ ตอนนั้น Client (Browser) Support หรือใช้ Locale อะไร - ถ้าไม่มี Params
lang
หรือ HeaderAccept-Language
ให้ใช้ Default Locale (Language) ตามที่กำหนดไว้
เพื่อกำหนดที่อยู่ของ messages และเรียกใช้ LocaleContextResolver
ที่เขียนขึ้นมา
@Configuration
public class i18nConfig {
@Bean
public MessageSource messageSource() {
final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
@Bean
@Primary
public LocaleContextResolver customLocaleContextResolver() {
return new CustomLocaleContextResolver();
}
...
}
เพื่อให้ใช้ MessageSource ง่ายขึ้น
ประกาศ interface
public interface MessageService {
String getMessage(final ServerWebExchange exchange, final String propertyName, final Object...params);
String getMessage(final Locale locale, final String propertyName, final Object...params);
}
implement interface
@Component
@RequiredArgsConstructor
public class MessageServiceImpl implements MessageService {
private final MessageSource messageSource;
private final LocaleContextResolver localeContextResolver;
@Override
public String getMessage(final ServerWebExchange exchange, final String propertyName, final Object... params) {
final LocaleContext localeContext = localeContextResolver.resolveLocaleContext(exchange);
final Locale locale = localeContext.getLocale();
return getMessage(locale, propertyName, params);
}
@Override
public String getMessage(final Locale locale, final String propertyName, final Object... params) {
return messageSource.getMessage(propertyName, params, locale);
}
}
หมายเหตุ
- เรียกใช้
MessageSource
และLocaleContextResolver
@RestController
@RequestMapping("/messages")
@RequiredArgsConstructor
public class MessageController {
private final MessageService messageService;
@GetMapping
public Mono<Message> getMessage(final ServerWebExchange exchange) {
return Mono.fromCallable(() -> {
return Message.builder()
.hello(messageService.getMessage(exchange, "hello", "jittagornp"))
.build();
});
}
@Data
@Builder
public static class Message {
private String hello;
}
}
cd ไปที่ root ของ project จากนั้น
$ mvn clean package
$ mvn spring-boot:run
เปิด browser แล้วเข้า http://localhost:8080/messages
ภาษาไทย (Default)
ภาษาอังกฤษ (lang=en
)
ภาษาจีน (lang=zh
)
ภาษาอื่น ๆ (lang=other
)
จะเข้าภาษาไทย (Default) เพราะไม่ตรงกับ messages_xx.properties ใด ๆ