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 + 日本語 + 적용 + 로그인 회원가입