diff --git a/adapter/firebase/build.gradle.kts b/adapter/firebase/build.gradle.kts index e80b41d5..566adef9 100644 --- a/adapter/firebase/build.gradle.kts +++ b/adapter/firebase/build.gradle.kts @@ -6,6 +6,7 @@ plugins { dependencies { implementation(project(":domain")) + implementation(project(":app:support:i18n")) implementation(project(":support:metric")) implementation("org.springframework.boot:spring-boot-starter:${Versions.SPRING_BOOT}") diff --git a/adapter/firebase/src/main/kotlin/com/xorker/draw/firebase/FcmService.kt b/adapter/firebase/src/main/kotlin/com/xorker/draw/firebase/FcmService.kt index 6e63ffb5..cb2a332b 100644 --- a/adapter/firebase/src/main/kotlin/com/xorker/draw/firebase/FcmService.kt +++ b/adapter/firebase/src/main/kotlin/com/xorker/draw/firebase/FcmService.kt @@ -3,22 +3,22 @@ package com.xorker.draw.firebase import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.Message import com.google.firebase.messaging.Notification +import com.xorker.draw.i18n.MessageService import com.xorker.draw.notification.PushMessageUseCase import java.util.Locale -import org.springframework.context.MessageSource import org.springframework.stereotype.Service @Service class FcmService( - private val messageSource: MessageSource, + private val messageService: MessageService, ) : PushMessageUseCase { override fun quickStart(localeStr: String, nickname: String) { val locale = Locale(localeStr) val notification = Notification.builder() - .setTitle(messageSource.getMessage("fcm.quickstart.title", null, locale)) - .setBody(String.format(messageSource.getMessage("fcm.quickstart.body", null, locale), nickname)) + .setTitle(messageService.getMessage("fcm.quickstart.title", locale)) + .setBody(String.format(messageService.getMessage("fcm.quickstart.body", locale), nickname)) .build() val message = Message.builder() diff --git a/adapter/firebase/src/main/resources/i18n/messages_en.properties b/adapter/firebase/src/main/resources/i18n/messages_en.properties deleted file mode 100644 index c5ad0cf9..00000000 --- a/adapter/firebase/src/main/resources/i18n/messages_en.properties +++ /dev/null @@ -1,2 +0,0 @@ -fcm.quickstart.title=šŸ•¹ļø Quick Start Alarm -fcm.quickstart.body=%s is waiting in a quick match. diff --git a/adapter/firebase/src/main/resources/i18n/messages_ko.properties b/adapter/firebase/src/main/resources/i18n/messages_ko.properties deleted file mode 100644 index c080164b..00000000 --- a/adapter/firebase/src/main/resources/i18n/messages_ko.properties +++ /dev/null @@ -1,2 +0,0 @@ -fcm.quickstart.title=šŸ•¹ļø ė¹ ė„ø ź²Œģž„ ģ•Œė¦¼ -fcm.quickstart.body=%sė‹˜ģ“ ė¹ ė„ø ź²Œģž„ģ—ģ„œ źø°ė‹¤ė¦¬ź³  ģžˆģ–“ģš”. diff --git a/app/support/exception/build.gradle.kts b/app/support/exception/build.gradle.kts index e455aa4d..4d4bbdbe 100644 --- a/app/support/exception/build.gradle.kts +++ b/app/support/exception/build.gradle.kts @@ -7,6 +7,7 @@ plugins { dependencies { implementation(project(":domain")) + implementation(project(":app:support:i18n")) implementation("org.springframework.boot:spring-boot-starter-web:${Versions.SPRING_BOOT}") } diff --git a/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionButtonType.kt b/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionButtonType.kt index 022f9fcf..360d9b2d 100644 --- a/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionButtonType.kt +++ b/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionButtonType.kt @@ -2,8 +2,8 @@ package com.xorker.draw.exception import com.xorker.draw.exception.ExceptionButtonAction.CloseDialog import com.xorker.draw.exception.ExceptionButtonAction.ForceUpdate +import com.xorker.draw.i18n.MessageService import java.util.* -import org.springframework.context.MessageSource enum class ExceptionButtonType( private val i18nCode: String, @@ -17,10 +17,10 @@ enum class ExceptionButtonType( private val responseMap = HashMap() - fun toResponse(source: MessageSource, locale: Locale): ExceptionButtonResponse { + fun toResponse(source: MessageService, locale: Locale): ExceptionButtonResponse { if (responseMap.containsKey(locale).not()) { val response = ExceptionButtonResponse( - text = source.getMessage(this.i18nCode, null, locale), + text = source.getMessage(this.i18nCode, locale), action = this.action, ) @@ -62,6 +62,7 @@ fun XorkerException.getButtons(): List { InvalidRequestOtherPlayingException, AlreadyPlayingRoomException, InvalidWebSocketStatusException, + is NotDefinedMessageCodeException, -> buttonOk } } diff --git a/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionDesignFactory.kt b/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionDesignFactory.kt index 42cc1803..f5ae55d8 100644 --- a/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionDesignFactory.kt +++ b/app/support/exception/src/main/kotlin/com/xorker/draw/exception/ExceptionDesignFactory.kt @@ -1,12 +1,12 @@ package com.xorker.draw.exception -import org.springframework.context.MessageSource +import com.xorker.draw.i18n.MessageService import org.springframework.context.i18n.LocaleContextHolder import org.springframework.stereotype.Component @Component class ExceptionDesignFactory( - private val messageSource: MessageSource, + private val messageService: MessageService, ) { fun generate(ex: XorkerException): ExceptionDesign { return when (ex) { @@ -20,8 +20,8 @@ class ExceptionDesignFactory( private fun generateToast(ex: XorkerException): ToastResponse { val locale = LocaleContextHolder.getLocale() - val text = messageSource.getMessage("exception.${ex.code}.text", null, "ERROR", locale) - ?: messageSource.getMessage("exception.default.description", null, locale) + val text = messageService.getMessageOrNull("exception.${ex.code}.text", locale) + ?: messageService.getMessage("exception.default.description", locale) return ToastResponse(text) } @@ -29,10 +29,11 @@ class ExceptionDesignFactory( private fun generateDialog(ex: XorkerException): DialogResponse { val locale = LocaleContextHolder.getLocale() - val title = messageSource.getMessage("exception.${ex.code}.title", null, "ERROR", locale) // TODO źø°ė³ø ģ—ėŸ¬ ģ²˜ė¦¬ ķ•˜źø° - val description = messageSource.getMessage("exception.${ex.code}.description", null, null, locale) - ?: messageSource.getMessage("exception.default.description", null, locale) - val buttons = ex.getButtons().map { it.toResponse(messageSource, locale) } + val title = messageService.getMessageOrNull("exception.${ex.code}.title", locale) ?: "ERROR" // TODO źø°ė³ø ģ—ėŸ¬ ģ²˜ė¦¬ ķ•˜źø° + val description = + messageService.getMessageOrNull("exception.${ex.code}.description", locale) + ?: messageService.getMessage("exception.default.description", locale) + val buttons = ex.getButtons().map { it.toResponse(messageService, locale) } return DialogResponse(title, description, buttons) } diff --git a/app/support/exception/src/main/resources/application-exception.yml b/app/support/exception/src/main/resources/application-exception.yml deleted file mode 100644 index 83c5e46a..00000000 --- a/app/support/exception/src/main/resources/application-exception.yml +++ /dev/null @@ -1,4 +0,0 @@ -spring: - messages: - basename: i18n/messages - encoding: UTF-8 diff --git a/app/support/i18n/build.gradle.kts b/app/support/i18n/build.gradle.kts new file mode 100644 index 00000000..ec2946db --- /dev/null +++ b/app/support/i18n/build.gradle.kts @@ -0,0 +1,18 @@ +import org.springframework.boot.gradle.tasks.bundling.BootJar + +plugins { + id("org.springframework.boot") + kotlin("plugin.spring") +} + +dependencies { + implementation(project(":domain")) + implementation(project(":support:logging")) + + implementation("org.springframework.boot:spring-boot-starter:${Versions.SPRING_BOOT}") +} + +tasks { + withType { enabled = true } + withType { enabled = false } +} diff --git a/app/support/i18n/src/main/kotlin/com/xorker/draw/i18n/MessageConfig.kt b/app/support/i18n/src/main/kotlin/com/xorker/draw/i18n/MessageConfig.kt new file mode 100644 index 00000000..f853dca3 --- /dev/null +++ b/app/support/i18n/src/main/kotlin/com/xorker/draw/i18n/MessageConfig.kt @@ -0,0 +1,17 @@ +package com.xorker.draw.i18n + +import org.springframework.context.MessageSource +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.ResourceBundleMessageSource + +@Configuration +class MessageConfig { + @Bean + fun messageSource(): MessageSource { + val messageSource = ResourceBundleMessageSource() + messageSource.setDefaultEncoding("UTF-8") + messageSource.setBasename("i18n/messages") + return messageSource + } +} diff --git a/app/support/i18n/src/main/kotlin/com/xorker/draw/i18n/MessageService.kt b/app/support/i18n/src/main/kotlin/com/xorker/draw/i18n/MessageService.kt new file mode 100644 index 00000000..41f482d8 --- /dev/null +++ b/app/support/i18n/src/main/kotlin/com/xorker/draw/i18n/MessageService.kt @@ -0,0 +1,33 @@ +package com.xorker.draw.i18n + +import com.xorker.draw.exception.NotDefinedMessageCodeException +import com.xorker.draw.support.logging.logger +import java.util.Locale +import org.springframework.context.MessageSource +import org.springframework.context.NoSuchMessageException +import org.springframework.stereotype.Component + +@Component +class MessageService( + private val messageSource: MessageSource, +) { + private val logger = logger() + + fun getMessage(code: String, locale: Locale): String { + try { + return messageSource.getMessage(code, null, locale) + } catch (e: NoSuchMessageException) { + logger.error("i18n Error - $code", e) + throw NotDefinedMessageCodeException(code, locale, e) + } + } + + fun getMessageOrNull(code: String, locale: Locale): String? { + try { + return messageSource.getMessage(code, null, locale) + } catch (e: NoSuchMessageException) { + logger.warn("i18n Error - $code", e) + return null + } + } +} diff --git a/app/support/exception/src/main/resources/i18n/messages_en.properties b/app/support/i18n/src/main/resources/i18n/messages_en.properties similarity index 81% rename from app/support/exception/src/main/resources/i18n/messages_en.properties rename to app/support/i18n/src/main/resources/i18n/messages_en.properties index e8edf359..c31fb51a 100644 --- a/app/support/exception/src/main/resources/i18n/messages_en.properties +++ b/app/support/i18n/src/main/resources/i18n/messages_en.properties @@ -1,3 +1,6 @@ +fcm.quickstart.title=šŸ•¹ļø Quick Start Alarm +fcm.quickstart.body=%s is waiting in a quick match. + exception.default.description='ģ˜ˆģƒģ¹˜ ėŖ»ķ•œ ģ˜ˆģ™øź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤.' exception.button.ok='OK' exception.button.cancel='Cancel' diff --git a/app/support/exception/src/main/resources/i18n/messages_ko.properties b/app/support/i18n/src/main/resources/i18n/messages_ko.properties similarity index 83% rename from app/support/exception/src/main/resources/i18n/messages_ko.properties rename to app/support/i18n/src/main/resources/i18n/messages_ko.properties index 6cccc39f..56a5b674 100644 --- a/app/support/exception/src/main/resources/i18n/messages_ko.properties +++ b/app/support/i18n/src/main/resources/i18n/messages_ko.properties @@ -1,3 +1,7 @@ +fcm.quickstart.title=šŸ•¹ļø ė¹ ė„ø ź²Œģž„ ģ•Œė¦¼ +fcm.quickstart.body=%sė‹˜ģ“ ė¹ ė„ø ź²Œģž„ģ—ģ„œ źø°ė‹¤ė¦¬ź³  ģžˆģ–“ģš”. + + exception.default.description='ģ˜ˆģƒģ¹˜ ėŖ»ķ•œ ģ˜ˆģ™øź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤.' exception.button.ok='ķ™•ģø' exception.button.cancel='ģ·Øģ†Œ' diff --git a/domain/src/main/kotlin/com/xorker/draw/exception/XorkerException.kt b/domain/src/main/kotlin/com/xorker/draw/exception/XorkerException.kt index 8ed48b08..32be3692 100644 --- a/domain/src/main/kotlin/com/xorker/draw/exception/XorkerException.kt +++ b/domain/src/main/kotlin/com/xorker/draw/exception/XorkerException.kt @@ -1,5 +1,8 @@ package com.xorker.draw.exception +import java.util.Locale +import org.springframework.context.NoSuchMessageException + sealed class XorkerException(val code: String, message: String, cause: Throwable? = null) : RuntimeException(message, cause) //region Client @@ -36,4 +39,5 @@ data object UnSupportedException : CriticalException("crt003", "ģ •ģ˜ķ•˜ģ§€ ģ•Š data object InvalidBroadcastException : CriticalException("crt004", "ģœ ķšØķ•˜ģ§€ ģ•Šģ€ ėøŒė”œė“œģŗģŠ¤ķŠø ģƒķƒœ") { private fun readResolve(): Any = InvalidBroadcastException } class InvalidMafiaPhaseException(message: String) : CriticalException("crt005", message) data object InvalidWebSocketStatusException : CriticalException("crt006", "ģ›¹ ģ†Œģ¼“ ģ„øģ…˜ ģƒķƒœź°€ ģœ ķšØķ•˜ģ§€ ģ•ŠģŒ") { private fun readResolve(): Any = InvalidWebSocketStatusException } +class NotDefinedMessageCodeException(messageCode: String, locale: Locale, cause: NoSuchMessageException) : CriticalException("crt007", "ģ •ģ˜ė˜ģ§€ ģ•Šģ€ ė©”ģ‹œģ§€ ģ½”ė“œ $locale $messageCode", cause) //endregion diff --git a/settings.gradle.kts b/settings.gradle.kts index c656c69d..842a6492 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,6 +7,7 @@ include( "app:api", "app:support:auth", "app:support:exception", + "app:support:i18n", "app:websocket", "domain", "core",