diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4ae7be6f..a5a8cb71 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -109,6 +109,7 @@
+
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/base/BaseActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/base/BaseActivity.kt
index f73d9cc6..ec758594 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/base/BaseActivity.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/base/BaseActivity.kt
@@ -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(
private val inflate: (LayoutInflater) -> T
@@ -43,6 +44,12 @@ abstract class BaseActivity(
}
}
+ 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)
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/common/Utils.kt b/app/src/main/java/kr/co/vividnext/sodalive/common/Utils.kt
index 21cc906b..f2f939a2 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/common/Utils.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/common/Utils.kt
@@ -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)
}
}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt
index 73279c96..6d4c00fb 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt
@@ -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(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)
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LanguageManager.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LanguageManager.kt
new file mode 100644
index 00000000..05f0db84
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LanguageManager.kt
@@ -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) }
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LanguageSettingsActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LanguageSettingsActivity.kt
new file mode 100644
index 00000000..9ee220a7
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LanguageSettingsActivity.kt
@@ -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::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()
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LocaleHelper.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LocaleHelper.kt
new file mode 100644
index 00000000..9bb57177
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/language/LocaleHelper.kt
@@ -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
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_language_settings.xml b/app/src/main/res/layout/activity_language_settings.xml
new file mode 100644
index 00000000..0bfb8880
--- /dev/null
+++ b/app/src/main/res/layout/activity_language_settings.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 6e7de90c..88687271 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -86,6 +86,39 @@
android:contentDescription="@null"
android:src="@drawable/ic_forward" />
+
+
+
+
+
+
+
+
+
OK
Cancel
+
+ Language
+ Korean
+ English
+ Japanese
+ Apply
+
Log in
Sign up
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index a923a0b2..dbcf9ca5 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -87,6 +87,13 @@
OK
キャンセル
+
+ 言語設定
+ 韓国語
+ 英語
+ 日本語
+ 適用
+
ログイン
新規登録
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b2558867..470742ec 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -86,6 +86,13 @@
확인
취소
+
+ 언어 설정
+ 한국어
+ English
+ 日本語
+ 적용
+
로그인
회원가입