앱 내 다국어 언어설정 기능 추가

This commit is contained in:
2025-12-12 14:39:00 +09:00
parent ebd557ff71
commit a75a11c9f6
12 changed files with 366 additions and 8 deletions

View File

@@ -18,6 +18,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.viewbinding.ViewBinding
import io.reactivex.rxjava3.disposables.CompositeDisposable
import kotlin.math.max
import kr.co.vividnext.sodalive.settings.language.LocaleHelper
abstract class BaseActivity<T : ViewBinding>(
private val inflate: (LayoutInflater) -> T
@@ -43,6 +44,12 @@ abstract class BaseActivity<T : ViewBinding>(
}
}
override fun attachBaseContext(newBase: Context) {
// 앱 설정 언어가 있으면 해당 Locale을 적용한 Context로 래핑한다.
val wrapped = LocaleHelper.wrap(newBase)
super.attachBaseContext(wrapped)
}
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.os.Build
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import kr.co.vividnext.sodalive.settings.language.LanguageManager
object Utils {
fun convertDurationToString(duration: Int, showHours: Boolean = true): String {
@@ -42,13 +43,7 @@ object Utils {
}
fun getCurrentLanguageCode(context: Context): String {
val config = context.resources.configuration
val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.locales.get(0)
} else {
@Suppress("DEPRECATION")
config.locale
}
return locale.language // "ko", "en" 등
// 효과적 언어 코드(사용자 설정 > 시스템 지원 언어 > ko)를 반환한다.
return LanguageManager.getEffectiveLanguage(context)
}
}

View File

@@ -22,6 +22,7 @@ import kr.co.vividnext.sodalive.databinding.ActivitySettingsBinding
import kr.co.vividnext.sodalive.mypage.alarm.AlarmViewModel
import kr.co.vividnext.sodalive.mypage.recent.RecentContentViewModel
import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsActivity
import kr.co.vividnext.sodalive.settings.language.LanguageSettingsActivity
import kr.co.vividnext.sodalive.settings.signout.SignOutActivity
import kr.co.vividnext.sodalive.settings.terms.TermsActivity
import kr.co.vividnext.sodalive.splash.SplashActivity
@@ -99,6 +100,10 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(ActivitySettingsB
binding.rlContentSettings.visibility = View.GONE
}
binding.rlLanguageSettings.setOnClickListener {
startActivity(Intent(applicationContext, LanguageSettingsActivity::class.java))
}
binding.rlTerms.setOnClickListener {
val intent = Intent(applicationContext, TermsActivity::class.java)
intent.putExtra(Constants.EXTRA_TERMS, Constants.EXTRA_TERMS)

View File

@@ -0,0 +1,69 @@
package kr.co.vividnext.sodalive.settings.language
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import androidx.core.content.edit
import androidx.preference.PreferenceManager
object LanguageManager {
const val LANG_KO = "ko"
const val LANG_EN = "en"
const val LANG_JA = "ja"
private const val PREF_KEY_APP_LANGUAGE = "pref_app_language_code"
private fun prefs(context: Context): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
fun isSupported(code: String): Boolean = when (code) {
LANG_KO, LANG_EN, LANG_JA -> true
else -> false
}
/**
* 사용자가 앱 내에서 명시적으로 선택한 언어 코드를 반환한다. 없으면 null.
*/
fun getUserSelectedLanguageOrNull(context: Context): String? {
val code = prefs(context).getString(PREF_KEY_APP_LANGUAGE, null)
return code?.takeIf { it.isNotBlank() }
}
/**
* 기존 동작 유지를 위해 남겨두지만, 기본값 강제 ko 대신 효과적 언어를 반환하도록 수정한다.
* 가급적 [getEffectiveLanguage] 사용을 권장.
*/
fun getSelectedLanguage(context: Context): String {
return getEffectiveLanguage(context)
}
/**
* 효과적 언어 코드 계산 로직
* 1) 사용자가 앱에서 언어를 선택했다면 그 값을 반환
* 2) 없으면 시스템 언어가 지원 언어면 시스템 언어 반환
* 3) 그 외에는 ko로 폴백
*/
fun getEffectiveLanguage(context: Context): String {
// 1) 사용자 지정 언어 우선
val user = getUserSelectedLanguageOrNull(context)
if (!user.isNullOrBlank() && isSupported(user)) return user
// 2) 시스템 언어가 지원되면 그대로 사용
val config = context.resources.configuration
val systemLang = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.locales.get(0)?.language
} else {
@Suppress("DEPRECATION")
config.locale?.language
}
if (!systemLang.isNullOrBlank() && isSupported(systemLang)) return systemLang
// 3) 폴백
return LANG_KO
}
fun setSelectedLanguage(context: Context, code: String) {
val normalized = if (isSupported(code)) code else LANG_KO
prefs(context).edit { putString(PREF_KEY_APP_LANGUAGE, normalized) }
}
}

View File

@@ -0,0 +1,56 @@
package kr.co.vividnext.sodalive.settings.language
import android.content.Intent
import android.os.Bundle
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.databinding.ActivityLanguageSettingsBinding
import kr.co.vividnext.sodalive.splash.SplashActivity
class LanguageSettingsActivity : BaseActivity<ActivityLanguageSettingsBinding>(
ActivityLanguageSettingsBinding::inflate
) {
private lateinit var selectedCode: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
selectedCode = LanguageManager.getSelectedLanguage(this)
applyCheckedState(selectedCode)
}
override fun setupView() {
binding.toolbar.tvBack.text = binding.root.context.getString(
kr.co.vividnext.sodalive.R.string.screen_settings_language
)
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.rlKo.setOnClickListener { onLanguageSelected(LanguageManager.LANG_KO) }
binding.rlEn.setOnClickListener { onLanguageSelected(LanguageManager.LANG_EN) }
binding.rlJa.setOnClickListener { onLanguageSelected(LanguageManager.LANG_JA) }
binding.tvApply.setOnClickListener { applyAndRestart() }
}
private fun onLanguageSelected(code: String) {
selectedCode = code
applyCheckedState(code)
}
private fun applyCheckedState(code: String) {
val isKo = code == LanguageManager.LANG_KO
val isEn = code == LanguageManager.LANG_EN
val isJa = code == LanguageManager.LANG_JA
binding.rbKo.isChecked = isKo
binding.rbEn.isChecked = isEn
binding.rbJa.isChecked = isJa
}
private fun applyAndRestart() {
LanguageManager.setSelectedLanguage(this, selectedCode)
// 전체 액티비티에 새로운 Locale이 반영되도록 스플래시로 재시작
val intent = Intent(this, SplashActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(intent)
finish()
}
}

View File

@@ -0,0 +1,33 @@
package kr.co.vividnext.sodalive.settings.language
import android.content.Context
import android.os.Build
import android.os.LocaleList
import android.text.TextUtils
import java.util.Locale
object LocaleHelper {
fun wrap(base: Context): Context {
val code = LanguageManager.getEffectiveLanguage(base)
val locale = Locale(code)
Locale.setDefault(locale)
val resources = base.resources
val config = resources.configuration
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(locale)
config.setLocales(LocaleList(locale))
base.createConfigurationContext(config)
} else {
@Suppress("DEPRECATION")
run {
config.setLocale(locale)
@Suppress("DEPRECATION")
resources.updateConfiguration(config, resources.displayMetrics)
base
}
}
}
}