From aa4e247904a5e4691c700abd242d02c0cc8de003 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Sun, 28 Dec 2025 21:00:57 +0900 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20Context=20=EC=97=86=EC=9D=B4=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=97=B4=EC=9D=84=20=EB=8B=A4=EB=A3=B0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8A=94=20UiText=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/presentation/util/UiTextUtil.kt | 14 +++++ .../src/main/java/com/eatssu/common/UiText.kt | 57 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 app/src/main/java/com/eatssu/android/presentation/util/UiTextUtil.kt create mode 100644 core/common/src/main/java/com/eatssu/common/UiText.kt diff --git a/app/src/main/java/com/eatssu/android/presentation/util/UiTextUtil.kt b/app/src/main/java/com/eatssu/android/presentation/util/UiTextUtil.kt new file mode 100644 index 00000000..f7a7ce16 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/util/UiTextUtil.kt @@ -0,0 +1,14 @@ +package com.eatssu.android.presentation.util + +import androidx.compose.runtime.Composable +import com.eatssu.common.UiText +import androidx.compose.ui.platform.LocalContext + +/** + * Composable에서 UiText를 쉽게 Resolve할 수 있게 해주는 확장 함수 + */ +@Composable +fun UiText.asString(): String { + val context = LocalContext.current + return asString(context) +} diff --git a/core/common/src/main/java/com/eatssu/common/UiText.kt b/core/common/src/main/java/com/eatssu/common/UiText.kt new file mode 100644 index 00000000..f25e073b --- /dev/null +++ b/core/common/src/main/java/com/eatssu/common/UiText.kt @@ -0,0 +1,57 @@ +package com.eatssu.common + +import android.content.Context +import androidx.annotation.StringRes + +/** + * UI 텍스트를 표현하는 sealed class + * ViewModel에서 Context 없이 문자열을 다룰 수 있게 함 + */ +sealed class UiText { + + /** + * 문자열 리소스 + * @param resId 문자열 리소스 ID + * @param args 포맷 인자 + */ + data class StringResource( + @StringRes val resId: Int, + val args: List = emptyList() + ) : UiText() { + // vararg를 사용한 편의 생성자 + constructor(@StringRes resId: Int, vararg args: Any) : this(resId, args.toList()) + } + + /** + * 동적 문자열 ex) 사용자 입력 + * UI 텍스트는 StringResource 사용 권장 + */ + data class DynamicString(val value: String) : UiText() + + /** + * 빈 텍스트 플레이스홀더 + */ + data object Empty : UiText() + + /** + * Context를 사용하여 실제 문자열로 변환 + */ + fun asString(context: Context): String = when (this) { + is StringResource -> { + if (args.isEmpty()) { + context.getString(resId) + } else { + // 중첩된 UiText 인자를 재귀적으로 변환 + val resolvedArgs = args.map { arg -> + when (arg) { + is UiText -> arg.asString(context) + else -> arg + } + }.toTypedArray() + context.getString(resId, *resolvedArgs) + } + } + is DynamicString -> value + is Empty -> "" + } +} From f97e1f87ce5ed5e8ce7d53e72018ac5344de8a02 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Sun, 28 Dec 2025 21:03:27 +0900 Subject: [PATCH 02/20] =?UTF-8?q?refactor:=20=ED=95=98=EB=93=9C=EC=BD=94?= =?UTF-8?q?=EB=94=A9=EB=90=9C=20=EB=AC=B8=EC=9E=90=EC=97=B4=EC=9D=B4?= =?UTF-8?q?=EB=82=98=20context=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8D=98=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC=20UiText=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/presentation/MainViewModel.kt | 12 +- .../cafeteria/info/InfoBottomSheetFragment.kt | 2 +- .../cafeteria/menu/MenuAdapter.kt | 2 +- .../review/list/ReviewListViewModel.kt | 23 +- .../review/modify/ModifyViewModel.kt | 16 +- .../cafeteria/review/report/ReportActivity.kt | 4 +- .../review/report/ReportViewModel.kt | 8 +- .../review/write/WriteReviewViewModel.kt | 14 +- .../presentation/intro/IntroViewModel.kt | 12 +- .../presentation/login/LoginViewModel.kt | 8 +- .../presentation/map/MapFragmentView.kt | 2 +- .../presentation/mypage/MyPageViewModel.kt | 8 +- .../presentation/mypage/SignOutViewModel.kt | 10 +- .../mypage/myreview/MyReviewViewModel.kt | 15 +- .../mypage/userinfo/UserInfoViewModel.kt | 21 +- .../android/presentation/util/ToastUtil.kt | 4 +- .../presentation/widget/ui/MealWidget.kt | 18 +- .../widget/ui/WidgetSettingActivity.kt | 2 +- .../widget/ui/WidgetSettingScreen.kt | 17 +- app/src/main/res/values/strings.xml | 280 ++++++++++++------ .../main/java/com/eatssu/common/UiEvent.kt | 2 +- .../com/eatssu/common/enums/ReportType.kt | 22 +- .../com/eatssu/common/enums/Restaurant.kt | 36 ++- core/common/src/main/res/values/strings.xml | 22 ++ 24 files changed, 374 insertions(+), 186 deletions(-) create mode 100644 core/common/src/main/res/values/strings.xml diff --git a/app/src/main/java/com/eatssu/android/presentation/MainViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/MainViewModel.kt index 6a22eab0..02a1b036 100644 --- a/app/src/main/java/com/eatssu/android/presentation/MainViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/MainViewModel.kt @@ -1,6 +1,5 @@ package com.eatssu.android.presentation -import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -13,9 +12,9 @@ import com.eatssu.android.domain.usecase.user.GetUserNickNameUseCase import com.eatssu.android.domain.usecase.user.SetUserCollegeDepartmentUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -32,8 +31,7 @@ class MainViewModel @Inject constructor( private val getUserNickNameUseCase: GetUserNickNameUseCase, private val setUserCollegeDepartmentUseCase: SetUserCollegeDepartmentUseCase, private val userRepository: UserRepository, - private val getUserCollegeDepartmentUseCase: GetUserCollegeDepartmentUseCase, - @ApplicationContext private val context: Context + private val getUserCollegeDepartmentUseCase: GetUserCollegeDepartmentUseCase ) : ViewModel() { private val _uiState: MutableStateFlow> = MutableStateFlow(UiState.Init) @@ -68,7 +66,7 @@ class MainViewModel @Inject constructor( // 1) 닉네임 없음 if (nickname.isBlank()) { _uiState.value = UiState.Success(MainState.NicknameNull) - _uiEvent.emit(UiEvent.ShowToast(context.getString(R.string.set_nickname), ToastType.ERROR)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.set_nickname), ToastType.ERROR)) return@launch } @@ -83,7 +81,7 @@ class MainViewModel @Inject constructor( _uiState.value = UiState.Success(MainState.LoggedOut) _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.toast_logout_success), ToastType.SUCCESS + UiText.StringResource(R.string.toast_logout_success), ToastType.SUCCESS ) ) } @@ -120,7 +118,7 @@ class MainViewModel @Inject constructor( _uiState.value = UiState.Error _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.not_found), + UiText.StringResource(R.string.not_found), ToastType.ERROR ) ) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/info/InfoBottomSheetFragment.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/info/InfoBottomSheetFragment.kt index a661266b..29c68469 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/info/InfoBottomSheetFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/info/InfoBottomSheetFragment.kt @@ -40,7 +40,7 @@ class InfoBottomSheetFragment : BottomSheetDialogFragment() { EventLogger.clickRestaurantInfo(restaurantType) - binding.tvName.text = restaurantType.korean + binding.tvName.text = getString(restaurantType.displayNameResId) CoroutineScope(Dispatchers.Main).launch { val restaurantInfo = infoViewModel.getRestaurantInfo(restaurantType) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuAdapter.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuAdapter.kt index b571f981..ca07e3e2 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuAdapter.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuAdapter.kt @@ -38,7 +38,7 @@ class MenuAdapter( Log.d("MenuAdapter", "bind: ${sectionModel.cafeteria}") } - binding.tvCafeteria.text = sectionModel.cafeteria.korean + binding.tvCafeteria.text = binding.root.context.getString(sectionModel.cafeteria.displayNameResId) binding.tvCafeteriaLocation.text = sectionModel.cafeteriaLocation binding.rvMenu.apply { diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt index 3a880c76..c5cc865e 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt @@ -2,6 +2,7 @@ package com.eatssu.android.presentation.cafeteria.review.list import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.R import com.eatssu.android.domain.model.Review import com.eatssu.android.domain.model.ReviewInfo import com.eatssu.android.domain.usecase.review.DeleteReviewUseCase @@ -9,6 +10,7 @@ import com.eatssu.android.domain.usecase.review.GetReviewInfoUseCase import com.eatssu.android.domain.usecase.review.GetReviewListUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.MenuType import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel @@ -54,7 +56,12 @@ class ReviewListViewModel @Inject constructor( _uiState.value = UiState.Success(ReviewListState(reviewInfo, reviewList)) } catch (e: Exception) { _uiState.value = UiState.Error - _uiEvent.emit(UiEvent.ShowToast("리뷰를 불러오지 못했습니다.", ToastType.ERROR)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_review_load_failed), + ToastType.ERROR + ) + ) } } @@ -64,12 +71,22 @@ class ReviewListViewModel @Inject constructor( val success = deleteReviewUseCase(reviewId) if (!success) { - _uiEvent.emit(UiEvent.ShowToast("리뷰 삭제에 실패했습니다.", ToastType.ERROR)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_review_delete_failed), + ToastType.ERROR + ) + ) return@launch } // 삭제 성공 시 - _uiEvent.emit(UiEvent.ShowToast("리뷰를 삭제했습니다.", ToastType.SUCCESS)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_review_delete_success), + ToastType.SUCCESS + ) + ) val type = lastMenuType val id = lastItemId if (type != null && id != null) { diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyViewModel.kt index fcb7a272..626a8c35 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyViewModel.kt @@ -2,10 +2,12 @@ package com.eatssu.android.presentation.cafeteria.review.modify import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.R import com.eatssu.android.domain.model.Review import com.eatssu.android.domain.usecase.review.ModifyReviewUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -66,11 +68,21 @@ class ModifyViewModel @Inject constructor( ) if (!success) { _uiState.value = UiState.Success(editing) - _uiEvent.emit(UiEvent.ShowToast("리뷰 수정이 실패했습니다.", ToastType.ERROR)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_review_modify_failed), + ToastType.ERROR + ) + ) } _uiEvent.emit(UiEvent.NavigateBack) - _uiEvent.emit(UiEvent.ShowToast("리뷰를 수정했습니다.", ToastType.SUCCESS)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_review_modify_success), + ToastType.SUCCESS + ) + ) } } } diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt index ab5d3874..39312142 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt @@ -62,7 +62,7 @@ class ReportActivity : BaseActivity( content = if (selectedReportType == ReportType.EXTRA) { inputText } else { - selectedReportType.description + getString(selectedReportType.descriptionResId) } reportViewModel.postData(reviewId, reportType, content) @@ -70,7 +70,7 @@ class ReportActivity : BaseActivity( lifecycleScope.launch { reportViewModel.uiState.collectLatest { showToast( - it.toastMessage, + it.toastMessage.asString(this@ReportActivity), if (it.isDone) ToastType.SUCCESS else ToastType.ERROR ) if (it.isDone) { diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportViewModel.kt index d948f9f1..c5d8f3f5 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportViewModel.kt @@ -2,8 +2,10 @@ package com.eatssu.android.presentation.cafeteria.review.report import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.R import com.eatssu.android.data.remote.dto.request.ReportRequest import com.eatssu.android.domain.usecase.review.PostReportUseCase +import com.eatssu.common.UiText import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -33,7 +35,7 @@ class ReportViewModel it.copy( loading = false, error = true, - toastMessage = "신고가 실패하였습니다." + toastMessage = UiText.StringResource(R.string.toast_report_failed) ) } return@launch @@ -44,7 +46,7 @@ class ReportViewModel loading = false, error = false, isDone = true, - toastMessage = "신고가 완료되었습니다." + toastMessage = UiText.StringResource(R.string.toast_report_success) ) } } @@ -55,6 +57,6 @@ data class ReportUiState( var loading: Boolean = true, var error: Boolean = false, - var toastMessage: String = "", + var toastMessage: UiText = UiText.Empty, var isDone: Boolean = false, ) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewViewModel.kt index ef99e227..d8c0070f 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewViewModel.kt @@ -4,12 +4,14 @@ import android.content.Context import android.net.Uri import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.R import com.eatssu.android.domain.model.MenuMini import com.eatssu.android.domain.usecase.menu.GetValidMenusOfMealUseCase import com.eatssu.android.domain.usecase.review.GetImageUrlUseCase import com.eatssu.android.domain.usecase.review.WriteReviewUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.MenuType import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel @@ -112,24 +114,24 @@ class WriteReviewViewModel @Inject constructor( val compressedFile = compressImage(context, originalFile) if (compressedFile != null && compressedFile.exists()) { imageUrl = getImageUrlUseCase(compressedFile) - _uiEvent.emit(UiEvent.ShowToast("이미지가 업로드되었습니다.", ToastType.SUCCESS)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_image_upload_success), ToastType.SUCCESS)) // 원본 파일 삭제 (압축된 파일만 유지) originalFile.delete() } else { _uiState.value = UiState.Success(editing) // 되돌림 - _uiEvent.emit(UiEvent.ShowToast("이미지 압축에 실패하였습니다.", ToastType.ERROR)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_image_compress_failed), ToastType.ERROR)) return@launch } } else { _uiState.value = UiState.Success(editing) // 되돌림 - _uiEvent.emit(UiEvent.ShowToast("이미지 파일을 찾을 수 없습니다.", ToastType.ERROR)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_image_not_found), ToastType.ERROR)) return@launch } } catch (e: Exception) { Timber.e(e, "이미지 업로드 실패") _uiState.value = UiState.Success(editing) // 되돌림 - _uiEvent.emit(UiEvent.ShowToast("이미지 업로드에 실패하였습니다.", ToastType.ERROR)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_image_upload_failed), ToastType.ERROR)) return@launch } } @@ -146,11 +148,11 @@ class WriteReviewViewModel @Inject constructor( if (!success) { _uiState.value = UiState.Success(editing) // 되돌림 - _uiEvent.emit(UiEvent.ShowToast("리뷰 작성에 실패하였습니다.", ToastType.ERROR)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_review_write_failed), ToastType.ERROR)) return@launch } - _uiEvent.emit(UiEvent.ShowToast("리뷰가 작성되었습니다.", ToastType.SUCCESS)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_review_write_success), ToastType.SUCCESS)) _uiEvent.emit(UiEvent.NavigateBack) } } diff --git a/app/src/main/java/com/eatssu/android/presentation/intro/IntroViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/intro/IntroViewModel.kt index dc0304fc..f32a1b39 100644 --- a/app/src/main/java/com/eatssu/android/presentation/intro/IntroViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/intro/IntroViewModel.kt @@ -1,6 +1,5 @@ package com.eatssu.android.presentation.intro -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.R @@ -11,9 +10,9 @@ import com.eatssu.android.domain.usecase.auth.GetIsAccessTokenValidUseCase import com.eatssu.android.domain.usecase.health.HealthCheckUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -25,7 +24,6 @@ import javax.inject.Inject @HiltViewModel class IntroViewModel @Inject constructor( - @ApplicationContext private val context: Context, private val healthCheckUseCase: HealthCheckUseCase, private val getAccessTokenUseCase: GetAccessTokenUseCase, private val getIsAccessTokenValidUseCase: GetIsAccessTokenValidUseCase, @@ -59,7 +57,7 @@ class IntroViewModel @Inject constructor( } catch (e: Exception) { Timber.e(e, "앱 초기화 중 오류 발생") _uiState.value = UiState.Error - _uiEvent.emit(UiEvent.ShowToast("앱 초기화 중 오류가 발생했습니다", ToastType.ERROR)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_app_init_error), ToastType.ERROR)) } } } @@ -83,7 +81,7 @@ class IntroViewModel @Inject constructor( when (result) { is VersionCheckResult.ForceUpdateRequired -> { Timber.d("강제 업데이트 필요: 최신 버전 ${result.minimumVersionCode}") - _uiEvent.emit(UiEvent.ShowToast("앱을 업데이트해주세요", ToastType.INFO)) + _uiEvent.emit(UiEvent.ShowToast(UiText.StringResource(R.string.toast_app_update_required), ToastType.INFO)) } VersionCheckResult.UpdateNotRequired -> { @@ -110,7 +108,7 @@ class IntroViewModel @Inject constructor( _uiState.value = UiState.Error _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.toast_token_invalid), + UiText.StringResource(R.string.toast_token_invalid), ToastType.INFO ) ) @@ -122,7 +120,7 @@ class IntroViewModel @Inject constructor( _uiState.value = UiState.Error _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.toast_token_invalid), + UiText.StringResource(R.string.toast_token_invalid), ToastType.INFO ) ) diff --git a/app/src/main/java/com/eatssu/android/presentation/login/LoginViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/login/LoginViewModel.kt index 662be558..e9c6b95b 100644 --- a/app/src/main/java/com/eatssu/android/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/login/LoginViewModel.kt @@ -1,6 +1,5 @@ package com.eatssu.android.presentation.login -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.R @@ -12,9 +11,9 @@ import com.eatssu.android.domain.usecase.auth.SetRefreshTokenUseCase import com.eatssu.android.domain.usecase.user.SetUserEmailUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -30,8 +29,7 @@ class LoginViewModel @Inject constructor( private val loginUseCase: LoginUseCase, private val setAccessTokenUseCase: SetAccessTokenUseCase, private val setRefreshTokenUseCase: SetRefreshTokenUseCase, - private val setUserEmailUseCase: SetUserEmailUseCase, - @ApplicationContext private val context: Context + private val setUserEmailUseCase: SetUserEmailUseCase ) : ViewModel() { private val _uiState = MutableStateFlow>(UiState.Init) @@ -48,7 +46,7 @@ class LoginViewModel @Inject constructor( _uiState.value = UiState.Error _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.toast_login_failed), + UiText.StringResource(R.string.toast_login_failed), ToastType.ERROR ) ) diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt index 0d0dbf05..fc2188f5 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt @@ -142,7 +142,7 @@ fun MapRoute( LaunchedEffect(Unit) { viewModel.uiEvent.collectLatest { event -> when (event) { - is UiEvent.ShowToast -> Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show() + is UiEvent.ShowToast -> Toast.makeText(context, event.message.asString(context), Toast.LENGTH_SHORT).show() } } } diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageViewModel.kt index 50e1c6cf..6ada9e76 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageViewModel.kt @@ -1,6 +1,5 @@ package com.eatssu.android.presentation.mypage -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.BuildConfig @@ -11,9 +10,9 @@ import com.eatssu.android.domain.usecase.alarm.SetDailyNotificationStatusUseCase import com.eatssu.android.domain.usecase.user.GetUserNickNameUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -28,11 +27,10 @@ import javax.inject.Inject @HiltViewModel class MyPageViewModel @Inject constructor( - @ApplicationContext private val context: Context, private val getUserNickNameUseCase: GetUserNickNameUseCase, private val setNotificationStatusUseCase: SetDailyNotificationStatusUseCase, private val alarmUseCase: AlarmUseCase, - private val settingDataStore: SettingDataStore, + private val settingDataStore: SettingDataStore ) : ViewModel() { // 내부는 항상 "값 그 자체"만 들고 있고, @@ -76,7 +74,7 @@ class MyPageViewModel @Inject constructor( _state.update { it.copy(nickname = null) } _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.toast_require_nickname), + UiText.StringResource(R.string.toast_require_nickname), ToastType.INFO ) ) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutViewModel.kt index 74e4edb4..2b816e0d 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutViewModel.kt @@ -1,6 +1,5 @@ package com.eatssu.android.presentation.mypage -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.R @@ -8,9 +7,9 @@ import com.eatssu.android.domain.usecase.auth.LogoutUseCase import com.eatssu.android.domain.usecase.auth.SignOutUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -21,9 +20,8 @@ import javax.inject.Inject @HiltViewModel class SignOutViewModel @Inject constructor( - @ApplicationContext private val context: Context, private val logoutUseCase: LogoutUseCase, - private val signOutUseCase: SignOutUseCase, + private val signOutUseCase: SignOutUseCase ) : ViewModel() { private val _uiState: MutableStateFlow> = MutableStateFlow(UiState.Init) @@ -41,7 +39,7 @@ class SignOutViewModel @Inject constructor( _uiState.value = UiState.Error _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.toast_sign_out_fail), + UiText.StringResource(R.string.toast_sign_out_fail), ToastType.ERROR ) ) @@ -51,7 +49,7 @@ class SignOutViewModel @Inject constructor( _uiState.value = UiState.Success(SignOutState(isSignOuted = true)) _uiEvent.emit( UiEvent.ShowToast( - context.getString(R.string.toast_sign_out_success), + UiText.StringResource(R.string.toast_sign_out_success), ToastType.SUCCESS ) ) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewViewModel.kt index 53cfe2a6..7a249960 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewViewModel.kt @@ -2,12 +2,14 @@ package com.eatssu.android.presentation.mypage.myreview import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.R import com.eatssu.android.domain.model.Review import com.eatssu.android.domain.usecase.review.DeleteReviewUseCase import com.eatssu.android.domain.usecase.review.GetMyReviewsUseCase import com.eatssu.android.domain.usecase.user.GetUserNickNameUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -65,10 +67,19 @@ class MyReviewViewModel @Inject constructor( viewModelScope.launch { val success = deleteReviewUseCase(reviewId) if (!success) { - _uiEvent.emit(UiEvent.ShowToast("리뷰 삭제에 실패했습니다.", ToastType.ERROR)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_review_delete_failed), + ToastType.ERROR + ) + ) return@launch } - _uiEvent.emit(UiEvent.ShowToast("리뷰를 삭제했습니다.", ToastType.SUCCESS)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_review_delete_success), ToastType.SUCCESS + ) + ) // 삭제 성공 시 내 리뷰 목록 재조회 getMyReviewList() } diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt index 482979fa..bca27350 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt @@ -2,6 +2,7 @@ package com.eatssu.android.presentation.mypage.userinfo import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.R import com.eatssu.android.domain.model.College import com.eatssu.android.domain.model.Department import com.eatssu.android.domain.repository.UserRepository @@ -13,6 +14,7 @@ import com.eatssu.android.domain.usecase.user.ValidateNicknameLocalUseCase import com.eatssu.android.domain.usecase.user.ValidateNicknameServerUseCase import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.UiText import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -156,6 +158,7 @@ class UserInfoViewModel @Inject constructor( selectedCollege = college, isCollegeChanged = isCollegeChanged, // 단과대가 변경되면 학과 초기화 + // TODO: Consider resolving from com.eatssu.common.R.string.department_placeholder at UI layer selectedDepartment = Department(-1, "학과"), departmentList = emptyList() ) @@ -206,8 +209,12 @@ class UserInfoViewModel @Inject constructor( if (data.isNicknameChanged) { val result = setUserNicknameUseCase(data.nickname) result.onFailure { error -> - val errorMessage = error.message ?: "닉네임 변경에 실패했어요." - _uiEvent.emit(UiEvent.ShowToast(errorMessage, ToastType.ERROR)) + _uiEvent.emit( + UiEvent.ShowToast( + UiText.StringResource(R.string.toast_nickname_change_failed), + ToastType.ERROR + ) + ) _uiState.value = UiState.Error return@launch } @@ -231,10 +238,10 @@ class UserInfoViewModel @Inject constructor( // 성공 메시지 val message = when { - nicknameUpdated && departmentUpdated -> "정보가 업데이트되었습니다." - nicknameUpdated -> "닉네임이 변경되었습니다." - departmentUpdated -> "학과 정보가 업데이트되었습니다." - else -> "변경사항이 없습니다." + nicknameUpdated && departmentUpdated -> UiText.StringResource(R.string.toast_info_updated) + nicknameUpdated -> UiText.StringResource(R.string.toast_nickname_changed) + departmentUpdated -> UiText.StringResource(R.string.toast_department_updated) + else -> UiText.StringResource(R.string.toast_no_changes) } _uiEvent.emit(UiEvent.ShowToast(message, ToastType.INFO)) @@ -259,6 +266,8 @@ data class UserInfoData( val isDuplicationChecked: Boolean = false, // 중복 확인 완료 여부 // 단과대/학과 + // Note: Placeholder strings correspond to com.eatssu.common.R.string.college_placeholder + // and com.eatssu.common.R.string.department_placeholder val selectedCollege: College = College(-1, "단과대"), val originalCollege: College = College(-1, "단과대"), val isCollegeChanged: Boolean = false, diff --git a/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt b/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt index e0886b4b..701506a0 100644 --- a/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt +++ b/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt @@ -59,10 +59,10 @@ fun Context.showToast( } fun Context.showToast(event: UiEvent.ShowToast) = - showToast(event.message, event.type) + showToast(event.message.asString(this), event.type) fun Fragment.showToast(event: UiEvent.ShowToast) = - requireContext().showToast(event.message, event.type) + requireContext().showToast(event.message.asString(requireContext()), event.type) fun Fragment.showToast(message: String, type: ToastType) = requireContext().showToast(message, type) diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt index 0ebba124..aacb062c 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt @@ -42,6 +42,7 @@ import androidx.glance.text.TextStyle import com.eatssu.android.R import com.eatssu.android.domain.model.WidgetMealInfo import com.eatssu.android.domain.usecase.widget.LoadRestaurantByFileKeyUseCase +import com.eatssu.android.presentation.util.asString import com.eatssu.android.presentation.widget.MealInfoStateDefinition import com.eatssu.android.presentation.widget.MealWorker import com.eatssu.android.presentation.widget.theme.EATSSUWidgetColorScheme @@ -116,13 +117,13 @@ class MealWidget : GlanceAppWidget() { MealWidgetContent( mealTime = mealTime, mealList = mealList, - restaurant = restaurant?.korean ?: "", + restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "", glanceId = id, ) } else { MealWidgetError( mealTime = mealTime, - restaurant = restaurant?.korean ?: "", + restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "", text = "오늘의 메뉴가 없습니다.", glanceId = id, ) @@ -132,7 +133,7 @@ class MealWidget : GlanceAppWidget() { is WidgetMealInfo.Loading -> { // Loading 상태일 때도 저장된 식당 정보 표시 MealWidgetError( - restaurant = restaurant?.korean ?: "", + restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "", mealTime = "점심", text = "로딩 중", glanceId = id, @@ -141,7 +142,7 @@ class MealWidget : GlanceAppWidget() { is WidgetMealInfo.Unavailable -> { MealWidgetError( - restaurant = restaurant?.korean ?: "", + restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "", mealTime = "점심", text = "네트워크 연결 상태를 확인해주세요.", glanceId = id, @@ -296,13 +297,18 @@ class MealWidget : GlanceAppWidget() { @Preview @Composable fun MealWidgetPreview() { - MealWidgetContent("저녁", listOf(listOf("밥", "국", "반찬", "음료")), Restaurant.DODAM.korean) + MealWidgetContent( + "저녁", + listOf(listOf("밥", "국", "반찬", "음료")), + Restaurant.DODAM.toUiText().asString() + ) } @OptIn(ExperimentalGlancePreviewApi::class) @Preview @Composable fun MealWidgetPreviewError() { - MealWidgetError("저녁", Restaurant.DODAM.korean, "에러임") + val context = LocalContext.current + MealWidgetError("저녁", context.getString(Restaurant.DODAM.displayNameResId), "에러임") } } diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingActivity.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingActivity.kt index fa77a9a0..2cb2d5fa 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingActivity.kt @@ -36,7 +36,7 @@ class WidgetSettingActivity : ComponentActivity() { EatssuTheme { val restaurantOptions = Restaurant.getVariableRestaurantList().map { - it.korean + getString(it.displayNameResId) } // 변동 식당만 불러옵니다. 하드코딩 x var selectedRestaurant by rememberSaveable { mutableStateOf(restaurantOptions[0]) } diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt index 8908c5e9..203bf39e 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt @@ -14,9 +14,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.eatssu.android.presentation.util.asString import com.eatssu.common.EventLogger import com.eatssu.common.enums.Restaurant -import com.eatssu.common.enums.Restaurant.Companion.fromKorean import com.eatssu.design_system.component.EatSsuButton import com.eatssu.design_system.component.EatSsuRadioButtonGroup import com.eatssu.design_system.component.EatSsuTopBar @@ -29,8 +29,12 @@ fun WidgetSettingScreen( selectedRestaurant: String, onSelectRestaurant: (String) -> Unit, onConfirm: (Restaurant) -> Unit = {}, - onBack: () -> Unit = {} // 뒤로가기 동작을 위한 람다 추가 + onBack: () -> Unit = {} ) { + // onClick 람다에서 LocalContext 접근이 불가하므로 Composable 레벨에서 미리 매핑 생성 + val restaurantDisplayNameMap = Restaurant.getVariableRestaurantList() + .associateBy { it.toUiText().asString() } + Scaffold( modifier = modifier.fillMaxSize(), topBar = { @@ -64,10 +68,11 @@ fun WidgetSettingScreen( modifier = Modifier.padding(bottom = 74.dp), text = "선택하기", onClick = { - onConfirm( - fromKorean(selectedRestaurant) - ) - EventLogger.addWidget(Restaurant.fromKorean(selectedRestaurant)) + val selectedRestaurantEnum = restaurantDisplayNameMap[selectedRestaurant] + ?: Restaurant.HAKSIK + + onConfirm(selectedRestaurantEnum) + EventLogger.addWidget(selectedRestaurantEnum) } ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6ed75299..f7ee5aad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,8 @@ + EAT-SSU + + @@ -24,6 +27,7 @@ 11 12 + HAKSIK DODAM @@ -32,6 +36,7 @@ FOOD_COURT SNACK_CORNER + rvBreakfastHaksik rvBreakfastDodam @@ -41,80 +46,176 @@ rvBreakfastSnack + + 리뷰가 작성되었습니다. + 리뷰 작성에 실패하였습니다. + 리뷰를 수정했습니다. + 리뷰 수정이 실패했습니다. + 리뷰를 삭제했습니다. + 리뷰 삭제에 실패했습니다. + 리뷰를 불러오지 못했습니다. - 내 리뷰 - 비밀번호 변경 - 공지사항 - 로그아웃 - 회원탈퇴 - 앱 버전 - 영문자과 숫자를 포함하여 8자 이상을 입력해주세요. + + 이미지가 업로드되었습니다. + 이미지 업로드에 실패하였습니다. + 이미지 압축에 실패하였습니다. + 이미지 파일을 찾을 수 없습니다. + + + 신고가 완료되었습니다. + 신고가 실패하였습니다. + + + 올바르지 않은 닉네임이에요. + 닉네임 변경에 실패했어요. + 정보가 업데이트되었습니다. + 닉네임이 변경되었습니다. + 학과 정보가 업데이트되었습니다. + 변경사항이 없습니다. + 단과대를 먼저 선택해 주세요. + + + 앱 초기화 중 오류가 발생했습니다 + 앱을 업데이트해주세요 + 제휴 정보가 없습니다. + 로그인에 실패했어요. + 로그아웃했어요. + 다시 로그인해주세요. + 세션이 만료되어 다시 로그인해주세요. + 시스템 오류로 다시 로그인해주세요. + 닉네임을 설정해주세요. + EAT-SSU 수신 동의 (%1$s) + EAT-SSU 수신 거절 (%1$s) + 오픈소스 라이브러리를 불러올 수 없어요. + 제휴 정보를 불러오지 못했어요. + 내 제휴 정보를 불러오지 못했어요. + 회원탈퇴되었어요. + 회원탈퇴에 실패했어요. + + + 리뷰 작성하기 + 리뷰 수정하기 + 탈퇴하기 + 내 정보 + 신고하기 + 위젯 설정 + 제휴 지도 + 강제 업데이트 + + + 오늘의 식사는 어땠나요? + 추천하고 싶은 메뉴가 있나요? + 메뉴에 대한 상세한 리뷰를 작성해주세요 + 화면을 준비하는 중입니다. + 작성 중... + 수정 중... + 사진 클릭 시, 삭제됩니다. + 사진 %1$d/%2$d + 리뷰 설정 + 에러가 발생했습니다. + + + 확인하고 싶은 식당을 선택하세요. + 선택하기 + 아침 + 점심 + 저녁 + 오늘의 메뉴가 없습니다. + 로딩 중 + 네트워크 연결 상태를 확인해주세요. + 설정 필요 + 위젯 설정에서 식당을 선택해주세요. + + + 로그아웃 + 로그아웃 하시겠습니까? + 새 버전의 앱을 설치해야 합니다. + 내 위치를 바로 확인하며 제휴 식당을 찾아볼 수 있도록 위치 권한을 허용해 주세요. + 알림 권한 필요 + 알림을 받으려면 알림 권한을 활성화해야 합니다.\n설정에서 알림 권한을 허용해주세요. + 설정으로 이동 + 리뷰를 수정하시겠습니까? + 리뷰를 삭제하시겠습니까? + 리뷰 수정을 취소하시겠습니까? + 리뷰 삭제를 취소하시겠습니까? + + 완료하기 - 스낵코너 + 수정하기 + 리뷰삭제 + 저장하기 + 취소 + 확인 + 전송하기 + + 오늘의 메뉴 - 푸드코트 - THE KITCHEN - 학생식당 가격 평점 + + + 학생식당 + 스낵코너 + 푸드코트 + THE KITCHEN 도담식당 기숙사 식당 기숙사 식당 - 메뉴와 관련없는 내용 - 음란성, 욕설 등 부적절한 내용 - 부적절한 홍보 또는 광고 - 리뷰 작성 취지에 맞지 않은 내용 (복사글 등) - 저작권 도용 의심 (사진 등) - 기타 (하단 내용 작성) - 리뷰 작성하기 - - - 신고 - - 수정하기 - 리뷰를 수정하시겠습니까? - 리뷰가 수정되었습니다. - 리뷰 수정이 실패하였습니다. - 리뷰 수정을 취소하시겠습니까? - 리뷰삭제 - 리뷰를 삭제하시겠습니까? - 리뷰가 삭제 되었습니다. - 리뷰 삭제가 실패 하였습니다. - 리뷰 삭제를 취소하시겠습니까? + + 내 리뷰 + 내 정보 + 비밀번호 변경 + 공지사항 + 로그아웃 + 회원탈퇴 + 앱 버전 + 마이 + 마이페이지 + 작성한 리뷰 아직 작성한 리뷰가 없어요 첫 리뷰를 남겨 주세요! 가장 먼저 리뷰를 남겨 주세요 - back - 카카오 - 연결된 계정 + 리뷰 작성하기 + 리뷰 수정하기 + 신고 + 리뷰 + 총 리뷰 수 + 환영합니다! 서비스 이용을 위해 로그인 해주세요 + 카카오 + 연결된 계정 - 정보를 불러올 수 없어요. - + 닉네임 닉네임을 설정해주세요 %1$d~%2$d글자를 입력해주세요. 사용 가능한 닉네임입니다! 이미 사용 중인 닉네임입니다. + + 정보를 불러올 수 없어요. + 통신 오류 + 서버와 통신할 수 없습니다.\n잠시 후 다시 시도해주세요. + + EAT SSU에게 보내기 - 식당 위치 - 비고 - 영업 시간 - 리뷰 수정하기 + 리뷰를 신고하는 이유를 선택해주세요. + 하나의 리뷰에 대해 24시간 내 한 번만 신고 가능합니다. + 리뷰 신고 사유를 작성해 주세요 + 메뉴와 관련없는 내용 + 음란성, 욕설 등 부적절한 내용 + 부적절한 홍보 또는 광고 + 리뷰 작성 취지에 맞지 않은 내용 (복사글 등) + 저작권 도용 의심 (사진 등) + 기타 (하단 내용 작성) + + 다음 단계로 - 5점 - 4점 - 총 리뷰 수 - 3점 - 1점 - 2점 - 리뷰 한 줄 평 작성하기 리뷰 등록 후, 사진은 수정할 수 없습니다 리뷰 등록하기 @@ -122,88 +223,83 @@ 식사는 맛있게 하셨나요? 어떤 음식에 대한 리뷰인가요? - 리뷰를 신고하는 이유를 선택해주세요. - 하나의 리뷰에 대해 24시간 내 한 번만 신고 가능합니다. - 리뷰 신고 사유를 작성해 주세요 + + 5점 + 4점 + 3점 + 2점 + 1점 + 메뉴 + EAT SSU 숭실대에서 먹자! \'를 추천하시겠어요? + + 최대 300자 최대 150 최대 500자 + 영문자과 숫자를 포함하여 8자 이상을 입력해주세요. + + 스토어 앱 버전 + + 문의하기 서비스 이용약관 개인정보 처리방침 + 개인정보 처리방침 + 서비스 이용약관 + 문의하기 + 만든 사람들 + TEAM EAT\-SSU + 오픈소스 라이브러리 + 문의할 내용을 남겨주세요. 이메일 답변받을 이메일 주소를 남겨주세요. 문의 내용 - 전송하기 문의 전송이 완료되었습니다. 답변은 영업일 기준 2~3일 소요됩니다. 문의 전송이 실패하였습니다. 다시 시도 해주세요. - 개인정보 처리방침 - 서비스 이용약관 - 문의하기 - + 정말 탈퇴하시겠습니까? 작성한 리뷰는 삭제되지 않으며, (알수없음)으로 표시됩니다.\n자세한 내용은 서비스 이용약관 및 개인정보처리방침을 확인해 주세요. 닉네임을 입력해주세요 - 만든 사람들 - TEAM EAT\-SSU - + 🤔 오늘 밥 뭐 먹지 오늘의 학식을 확인해보세요! + EAT-SSU 알림 수신을 동의하였습니다. + EAT-SSU 알림 수신을 거부하였습니다. - + https://github.com/EAT-SSU/Docs/wiki/EAT%E2%80%90SSU-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EB%B0%A9%EC%B9%A8 https://github.com/EAT-SSU/Docs/wiki/EAT%E2%80%90SSU-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9D%B4%EC%9A%A9%EC%95%BD%EA%B4%80 http://pf.kakao.com/_ZlVAn + eatssu.official + https://eat-ssu.notion.site/1d2eeef75a1681ae800cf6ffa6faa37d?pvs=74 - 오픈소스 라이브러리 + 학식 지도 - 마이 - 마이페이지 + back - 내 정보 - 저장하기 + 학과 입력하기 학과를 입력하고\n나만의 제휴를 확인해보세요! + 단과대/학과 정보를 알 수 없음 + + + 식당 위치 + 비고 + 영업 시간 찜한 제휴 카페 음식점 주점 - - eatssu.official - https://eat-ssu.notion.site/1d2eeef75a1681ae800cf6ffa6faa37d?pvs=74 - - 취소 - 확인 - - 로그인에 실패했어요. - 다시 로그인해주세요. - 세션이 만료되어 다시 로그인해주세요. - 시스템 오류로 다시 로그인해주세요. - 로그아웃했어요. - - 닉네임을 설정해주세요. - EAT-SSU 수신 동의 (%1$s) - EAT-SSU 수신 거절 (%1$s) - 오픈소스 라이브러리를 불러올 수 없어요. - - 제휴 정보를 불러오지 못했어요. - 내 제휴 정보를 불러오지 못했어요. - - 회원탈퇴되었어요. - 회원탈퇴에 실패했어요. - - 통신 오류 - 서버와 통신할 수 없습니다.\n잠시 후 다시 시도해주세요. - \ No newline at end of file + diff --git a/core/common/src/main/java/com/eatssu/common/UiEvent.kt b/core/common/src/main/java/com/eatssu/common/UiEvent.kt index f421957c..0c4e3380 100644 --- a/core/common/src/main/java/com/eatssu/common/UiEvent.kt +++ b/core/common/src/main/java/com/eatssu/common/UiEvent.kt @@ -8,7 +8,7 @@ import com.eatssu.common.enums.ToastType */ interface UiEvent { data class ShowToast( - val message: String, + val message: UiText, val type: ToastType ) : UiEvent object NavigateBack : UiEvent diff --git a/core/common/src/main/java/com/eatssu/common/enums/ReportType.kt b/core/common/src/main/java/com/eatssu/common/enums/ReportType.kt index f5580f6f..56cbc08d 100644 --- a/core/common/src/main/java/com/eatssu/common/enums/ReportType.kt +++ b/core/common/src/main/java/com/eatssu/common/enums/ReportType.kt @@ -1,11 +1,19 @@ package com.eatssu.common.enums +import androidx.annotation.StringRes +import com.eatssu.common.R +import com.eatssu.common.UiText -enum class ReportType(val description: String) { - NO_ASSOCIATE_CONTENT("메뉴와 관련없는 내용"), - IMPROPER_CONTENT("음란성, 욕설 등 부적절한 내용"), - IMPROPER_ADVERTISEMENT("부적절한 홍보 또는 광고"), - COPY("리뷰 작성 취지에 맞지 않은 내용 (복사글 등)"), - COPYRIGHT("저작권 도용 의심 (사진 등)"), - EXTRA("기타 (하단 내용 작성)") +enum class ReportType( + @StringRes val descriptionResId: Int +) { + NO_ASSOCIATE_CONTENT(R.string.report_type_no_associate_content), + IMPROPER_CONTENT(R.string.report_type_improper_content), + IMPROPER_ADVERTISEMENT(R.string.report_type_improper_advertisement), + COPY(R.string.report_type_copy), + COPYRIGHT(R.string.report_type_copyright), + EXTRA(R.string.report_type_extra); + + /** ViewModel에서 Context 없이 사용하기 위한 UiText 변환 */ + fun toUiText(): UiText = UiText.StringResource(descriptionResId) } \ No newline at end of file diff --git a/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt b/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt index 0cbe90e6..0e037e7d 100644 --- a/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt +++ b/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt @@ -1,26 +1,34 @@ package com.eatssu.common.enums +import androidx.annotation.StringRes +import com.eatssu.common.R +import com.eatssu.common.UiText -enum class Restaurant(val value: String, val korean: String, val menuType: MenuType) { - HAKSIK("haksik", "학생 식당", MenuType.VARIABLE), - DODAM("dodam", "도담 식당", MenuType.VARIABLE), - DORMITORY("dormitory", "기숙사 식당", MenuType.VARIABLE), - FACULTY("faculty", "FACULTY (교직원 전용)", MenuType.VARIABLE), - FOOD_COURT("food_court", "푸드 코트", MenuType.FIXED), - SNACK_CORNER("snack_corner", "스낵 코너", MenuType.FIXED), - THE_KITCHEN("the_kitchen", "더 키친", MenuType.FIXED); +enum class Restaurant( + val value: String, + @field:StringRes val displayNameResId: Int, + val menuType: MenuType +) { + HAKSIK("haksik", R.string.restaurant_haksik, MenuType.VARIABLE), + DODAM("dodam", R.string.restaurant_dodam, MenuType.VARIABLE), + DORMITORY("dormitory", R.string.restaurant_dormitory, MenuType.VARIABLE), + FACULTY("faculty", R.string.restaurant_faculty, MenuType.VARIABLE), + FOOD_COURT("food_court", R.string.restaurant_food_court, MenuType.FIXED), + SNACK_CORNER("snack_corner", R.string.restaurant_snack_corner, MenuType.FIXED), + THE_KITCHEN("the_kitchen", R.string.restaurant_the_kitchen, MenuType.FIXED); + + /** ViewModel에서 Context 없이 사용하기 위한 UiText 변환 */ + fun toUiText(): UiText = UiText.StringResource(displayNameResId) companion object { fun getVariableRestaurantList(): List { return entries.filter { it.menuType == MenuType.VARIABLE } } - - fun fromRestaurantEnumName(enumName: String): String { - return entries.find { it.name == enumName }?.korean ?: "" - } - fun fromKorean(name: String): Restaurant = - entries.find { it.korean == name } ?: error("Unknown display name: $name") + @Deprecated("다국어 지원을 위해 displayNameResId 사용", ReplaceWith("entries.find { it.name == enumName }?.displayNameResId")) + fun fromRestaurantEnumName(enumName: String): String { + return "" + } } } \ No newline at end of file diff --git a/core/common/src/main/res/values/strings.xml b/core/common/src/main/res/values/strings.xml new file mode 100644 index 00000000..bd2a6d6a --- /dev/null +++ b/core/common/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ + + + 학생 식당 + 도담 식당 + 기숙사 식당 + FACULTY (교직원 전용) + 푸드 코트 + 스낵 코너 + 더 키친 + + + 메뉴와 관련없는 내용 + 음란성, 욕설 등 부적절한 내용 + 부적절한 홍보 또는 광고 + 리뷰 작성 취지에 맞지 않은 내용 (복사글 등) + 저작권 도용 의심 (사진 등) + 기타 (하단 내용 작성) + + + 단과대 + 학과 + From ec43552321ee3348b072a2deea43f9b012c97d5e Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 13:39:58 +0900 Subject: [PATCH 03/20] =?UTF-8?q?chore:=20=EC=9E=84=EC=8B=9C=20=EB=B2=88?= =?UTF-8?q?=EC=97=AD=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values-en/strings.xml | 5 +++++ app/src/main/res/values-ja/strings.xml | 5 +++++ app/src/main/res/values-vi/strings.xml | 5 +++++ app/src/main/res/values-zh/strings.xml | 5 +++++ core/common/src/main/res/values-en/strings.xml | 4 ++++ core/common/src/main/res/values-ja/strings.xml | 4 ++++ core/common/src/main/res/values-vi/strings.xml | 4 ++++ core/common/src/main/res/values-zh/strings.xml | 4 ++++ 8 files changed, 36 insertions(+) create mode 100644 app/src/main/res/values-en/strings.xml create mode 100644 app/src/main/res/values-ja/strings.xml create mode 100644 app/src/main/res/values-vi/strings.xml create mode 100644 app/src/main/res/values-zh/strings.xml create mode 100644 core/common/src/main/res/values-en/strings.xml create mode 100644 core/common/src/main/res/values-ja/strings.xml create mode 100644 core/common/src/main/res/values-vi/strings.xml create mode 100644 core/common/src/main/res/values-zh/strings.xml diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml new file mode 100644 index 00000000..06e0827b --- /dev/null +++ b/app/src/main/res/values-en/strings.xml @@ -0,0 +1,5 @@ + + + + EAT-SSU + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml new file mode 100644 index 00000000..ff2e0aca --- /dev/null +++ b/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,5 @@ + + + + EAT-SSU + diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml new file mode 100644 index 00000000..e70c5362 --- /dev/null +++ b/app/src/main/res/values-vi/strings.xml @@ -0,0 +1,5 @@ + + + + EAT-SSU + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml new file mode 100644 index 00000000..b1ceedd4 --- /dev/null +++ b/app/src/main/res/values-zh/strings.xml @@ -0,0 +1,5 @@ + + + + EAT-SSU + diff --git a/core/common/src/main/res/values-en/strings.xml b/core/common/src/main/res/values-en/strings.xml new file mode 100644 index 00000000..e228fcf4 --- /dev/null +++ b/core/common/src/main/res/values-en/strings.xml @@ -0,0 +1,4 @@ + + + + diff --git a/core/common/src/main/res/values-ja/strings.xml b/core/common/src/main/res/values-ja/strings.xml new file mode 100644 index 00000000..3c92fbfc --- /dev/null +++ b/core/common/src/main/res/values-ja/strings.xml @@ -0,0 +1,4 @@ + + + + diff --git a/core/common/src/main/res/values-vi/strings.xml b/core/common/src/main/res/values-vi/strings.xml new file mode 100644 index 00000000..5bade1eb --- /dev/null +++ b/core/common/src/main/res/values-vi/strings.xml @@ -0,0 +1,4 @@ + + + + diff --git a/core/common/src/main/res/values-zh/strings.xml b/core/common/src/main/res/values-zh/strings.xml new file mode 100644 index 00000000..acd844ed --- /dev/null +++ b/core/common/src/main/res/values-zh/strings.xml @@ -0,0 +1,4 @@ + + + + From c5eab8a1da900c4a9319252634ec5efb69cef1b0 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 18:47:49 +0900 Subject: [PATCH 04/20] =?UTF-8?q?refactor:=20Presentation=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EC=9D=98=20=ED=95=98=EB=93=9C=20=EC=BD=94?= =?UTF-8?q?=EB=94=A9=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alarm/EatSsuFirebaseMessagingService.kt | 4 +- .../android/alarm/NotificationReceiver.kt | 4 +- .../cafeteria/CafeteriaFragment.kt | 3 +- .../cafeteria/review/list/ReviewListScreen.kt | 15 +- .../list/component/MyReviewBottomSheet.kt | 7 +- .../list/component/OthersReviewBottomSheet.kt | 5 +- .../list/component/ReviewProgressBar.kt | 10 +- .../review/modify/ModifyReviewScreen.kt | 16 +- .../review/write/WriteReviewScreen.kt | 19 +- .../common/ForceUpdateActivity.kt | 5 +- .../presentation/common/NetworkConnection.kt | 3 +- .../presentation/map/MapFragmentView.kt | 8 +- .../map/component/DepartmentBottomSheet.kt | 4 +- .../map/component/MapRestaurantBottomSheet.kt | 4 +- .../presentation/mypage/MyPageFragment.kt | 12 +- .../presentation/mypage/SignOutActivity.kt | 3 +- .../mypage/myreview/MyReviewListScreen.kt | 7 +- .../android/presentation/util/DialogUtil.kt | 4 +- .../presentation/widget/ui/MealWidget.kt | 22 +- .../widget/ui/WidgetSettingScreen.kt | 8 +- app/src/main/res/layout/activity_base.xml | 2 +- app/src/main/res/layout/activity_fix_menu.xml | 6 +- app/src/main/res/layout/activity_report.xml | 12 +- .../main/res/layout/activity_user_info.xml | 12 +- app/src/main/res/layout/dialog_default.xml | 8 +- .../main/res/layout/dialog_destructive.xml | 8 +- app/src/main/res/layout/fragment_my_page.xml | 40 +- .../main/res/menu/menu_bottom_navigation.xml | 6 +- app/src/main/res/menu/menu_my_review.xml | 4 +- app/src/main/res/values-en/strings.xml | 344 +++++++++++++++++- app/src/main/res/values-ja/strings.xml | 344 +++++++++++++++++- app/src/main/res/values-vi/strings.xml | 342 ++++++++++++++++- app/src/main/res/values-zh/strings.xml | 344 +++++++++++++++++- app/src/main/res/values/strings.xml | 294 +++++++++------ 34 files changed, 1706 insertions(+), 223 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt b/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt index e973b2ea..32fab8b6 100644 --- a/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt +++ b/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt @@ -34,10 +34,10 @@ class EatSsuFirebaseMessagingService : FirebaseMessagingService() { val channel = NotificationChannel( CHANNEL_ID, - "서버가 보낸 알림", + getString(R.string.notification_channel_server_name), NotificationManager.IMPORTANCE_HIGH ).apply { - description = "잇슈 서버가 보낸 알림을 표시합니다." + description = getString(R.string.notification_channel_server_description) enableLights(true) enableVibration(true) // 진동도 활성화 lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC // 잠금 화면에서도 표시 diff --git a/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt b/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt index d153249f..e9f659dc 100644 --- a/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt +++ b/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt @@ -28,10 +28,10 @@ class NotificationReceiver : BroadcastReceiver() { val channel = NotificationChannel( CHANNEL_ID, - "점심시간 전 알림", + context.getString(R.string.notification_channel_lunch_name), NotificationManager.IMPORTANCE_HIGH // 중요도를 높게 설정 ).apply { - description = "점심시간 전, 푸시알림을 발송합니다." + description = context.getString(R.string.notification_channel_lunch_description) enableLights(true) enableVibration(true) // 진동도 활성화 lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC // 잠금 화면에서도 표시 diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/CafeteriaFragment.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/CafeteriaFragment.kt index f42e7166..76972676 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/CafeteriaFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/CafeteriaFragment.kt @@ -8,6 +8,7 @@ import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 +import com.eatssu.android.R import com.eatssu.android.databinding.FragmentCafeteriaBinding import com.eatssu.android.presentation.MainViewModel import com.eatssu.android.presentation.base.BaseFragment @@ -49,7 +50,7 @@ class CafeteriaFragment : BaseFragment( viewPager.adapter = viewpagerFragmentAdapter viewPager.setCurrentItem(viewpagerFragmentAdapter.getDefaultFragmentPosition(), false) - val tabTitles = listOf("아침", "점심", "저녁") + val tabTitles = listOf(getString(R.string.widget_morning), getString(R.string.widget_lunch), getString(R.string.widget_dinner)) TabLayoutMediator(tabLayout, viewPager) { tab, position -> tab.text = tabTitles[position] }.attach() // ViewPager 페이지 변경 감지 diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt index 985210e2..05c45939 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -144,13 +145,13 @@ internal fun ReviewListScreen( Scaffold( topBar = { EatSsuTopBar( - title = "리뷰", + title = stringResource(R.string.review), onBack = onBack ) }, bottomBar = { // 하단에 버튼을 고정하기 위함 EatSsuButton( - text = "리뷰 작성하기", + text = stringResource(R.string.review_write), onClick = { onReviewWriteButtonClick() }, @@ -235,7 +236,7 @@ internal fun ReviewListScreen( Row(Modifier.padding(horizontal = 24.dp)) { Text( - "리뷰", + stringResource(R.string.review), style = EatssuTheme.typography.h2, ) Spacer(modifier = Modifier.width(6.dp)) @@ -320,7 +321,7 @@ internal fun ReviewListScreen( .padding(top = 100.dp) ) { Text( - "에러가 발생했습니다.", + stringResource(R.string.review_error_occurred), style = EatssuTheme.typography.body1, modifier = Modifier.align(Alignment.Center) ) @@ -365,7 +366,7 @@ fun ReviewInfoContent( ) Spacer(Modifier.width(4.dp)) Text( - "오늘의 메뉴", + stringResource(R.string.today_menu), style = EatssuTheme.typography.subtitle1 ) } @@ -441,13 +442,13 @@ fun EmptyReviewContent(modifier: Modifier) { ) Spacer(Modifier.height(16.dp)) Text( - "아직 작성된 리뷰가 없어요", + stringResource(R.string.none_review), style = EatssuTheme.typography.subtitle2, color = Gray600 ) Spacer(Modifier.height(8.dp)) Text( - "메뉴에 가장 먼저 리뷰를 남겨주세요!", + stringResource(R.string.none_review_list_detail), style = EatssuTheme.typography.caption2, color = Gray600 ) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt index 832c502c..7a53dcb9 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color.Companion.White import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -77,7 +78,7 @@ fun MyReviewBottomSheet( ) Text( - text = "리뷰 설정", + text = stringResource(R.string.review_settings), style = EatssuTheme.typography.body2, color = Gray600, modifier = Modifier.padding(horizontal = 20.dp, vertical = 22.dp) @@ -98,7 +99,7 @@ fun MyReviewBottomSheet( ) Spacer(modifier = Modifier.width(20.dp)) Text( - text = "수정하기", + text = stringResource(R.string.button_modify), style = EatssuTheme.typography.body2, color = Black ) @@ -119,7 +120,7 @@ fun MyReviewBottomSheet( ) Spacer(modifier = Modifier.width(20.dp)) Text( - text = "삭제하기", + text = stringResource(R.string.button_delete_action), style = EatssuTheme.typography.body2, color = Black ) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/OthersReviewBottomSheet.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/OthersReviewBottomSheet.kt index d8f8c74f..16f0c58c 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/OthersReviewBottomSheet.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/OthersReviewBottomSheet.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color.Companion.White import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -76,7 +77,7 @@ fun OthersReviewBottomSheet( ) Text( - text = "리뷰 설정", + text = stringResource(R.string.review_settings), style = EatssuTheme.typography.body2, color = Gray600, modifier = Modifier.padding(horizontal = 20.dp, vertical = 22.dp) @@ -97,7 +98,7 @@ fun OthersReviewBottomSheet( ) Spacer(modifier = Modifier.width(20.dp)) Text( - text = "신고하기", + text = stringResource(R.string.title_report), style = EatssuTheme.typography.body2, color = Black ) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/ReviewProgressBar.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/ReviewProgressBar.kt index 83f8f9fb..ad8cea0a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/ReviewProgressBar.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/ReviewProgressBar.kt @@ -50,15 +50,7 @@ fun ReviewProgressBar( .padding(vertical = 2.dp) ) { Text( - text = stringResource( - id = when (rating) { - 5 -> R.string.rate_5 - 4 -> R.string.rate_4 - 3 -> R.string.rate_3 - 2 -> R.string.rate_2 - else -> R.string.rate_1 - } - ), + text = stringResource(R.string.rate_n, rating), style = EatssuTheme.typography.caption2, ) Spacer(modifier = Modifier.width(8.dp)) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewScreen.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewScreen.kt index 0818002a..40652c4a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewScreen.kt @@ -21,10 +21,12 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.eatssu.android.R import com.eatssu.android.domain.model.Review import com.eatssu.android.presentation.cafeteria.review.write.component.MenuLikeButtonItem import com.eatssu.android.presentation.util.showToast @@ -75,7 +77,7 @@ fun ModifyReviewScreen( is ModifyState.Editing -> { ModifyReviewScreen( modifier = modifier, - title = "리뷰 수정하기", + title = stringResource(R.string.title_review_modify), rating = data.rating, content = data.content, menuLikeInfos = data.menuLikeInfos, @@ -95,7 +97,7 @@ fun ModifyReviewScreen( // 통신 중에도 폼은 유지, 버튼/입력 제한만 ModifyReviewScreen( modifier = modifier, - title = "리뷰 수정하기", + title = stringResource(R.string.title_review_modify), rating = data.rating, content = data.content, menuLikeInfos = data.menuLikeInfos, @@ -121,7 +123,7 @@ fun ModifyReviewScreen( Spacer(Modifier.height(24.dp)) CircularProgressIndicator() Spacer(Modifier.height(8.dp)) - Text("화면을 준비하는 중입니다.", style = EatssuTheme.typography.body2) + Text(stringResource(R.string.review_preparing), style = EatssuTheme.typography.body2) } } } @@ -147,7 +149,7 @@ internal fun ModifyReviewScreen( topBar = { CloseTopBar(title, onClose = onBack) }, bottomBar = { EatSsuButton( - text = if (isSubmitting) "수정 중..." else "완료하기", + text = if (isSubmitting) stringResource(R.string.review_modifying) else stringResource(R.string.button_complete), enabled = canSubmit && rating > 0 && !isSubmitting, onClick = onSubmit, modifier = Modifier.padding(24.dp) @@ -165,7 +167,7 @@ internal fun ModifyReviewScreen( .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text("오늘의 식사는 어땠나요?", style = EatssuTheme.typography.subtitle1) + Text(stringResource(R.string.review_how_was_meal), style = EatssuTheme.typography.subtitle1) RatingBarMedium( modifier = Modifier.padding(top = 16.dp, bottom = 12.dp), @@ -173,7 +175,7 @@ internal fun ModifyReviewScreen( onRatingChanged = { if (!isSubmitting) onRatingChanged(it) } ) - Text("추천하고 싶은 메뉴가 있나요?", style = EatssuTheme.typography.subtitle1) + Text(stringResource(R.string.review_recommend_menu), style = EatssuTheme.typography.subtitle1) Spacer(modifier = Modifier.height(16.dp)) LazyColumn( @@ -207,7 +209,7 @@ internal fun ModifyReviewScreen( }, placeholder = { Text( - "메뉴에 대한 상세한 리뷰를 작성해주세요", + stringResource(R.string.review_placeholder), style = EatssuTheme.typography.body2, color = Gray400 ) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewScreen.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewScreen.kt index 554983cc..112b0250 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewScreen.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -91,7 +92,7 @@ fun WriteReviewScreen( is WriteReviewState.Editing -> { WriteReviewScreen( modifier = modifier, - title = "리뷰 작성하기", + title = stringResource(R.string.title_review_write), menuList = data.menuList, rating = data.rating, content = data.content, @@ -113,7 +114,7 @@ fun WriteReviewScreen( is WriteReviewState.Posting -> { WriteReviewScreen( modifier = modifier, - title = "리뷰 작성하기", + title = stringResource(R.string.title_review_write), menuList = data.menuList, rating = data.rating, content = data.content, @@ -139,7 +140,7 @@ fun WriteReviewScreen( horizontalAlignment = Alignment.CenterHorizontally ) { Spacer(Modifier.height(24.dp)) - Text("화면을 준비하는 중입니다.", style = EatssuTheme.typography.body2) + Text(stringResource(R.string.review_preparing), style = EatssuTheme.typography.body2) } } } @@ -168,7 +169,7 @@ internal fun WriteReviewScreen( topBar = { CloseTopBar(title, onClose = onBack) }, bottomBar = { EatSsuButton( - text = if (isPosting) "작성 중..." else "완료하기", + text = if (isPosting) stringResource(R.string.review_posting) else stringResource(R.string.button_complete), enabled = rating > 0 && !isPosting, onClick = onSubmit, modifier = Modifier.padding(24.dp) @@ -186,7 +187,7 @@ internal fun WriteReviewScreen( .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text("오늘의 식사는 어땠나요?", style = EatssuTheme.typography.subtitle1) + Text(stringResource(R.string.review_how_was_meal), style = EatssuTheme.typography.subtitle1) RatingBarMedium( modifier = Modifier.padding(top = 16.dp, bottom = 12.dp), @@ -194,7 +195,7 @@ internal fun WriteReviewScreen( onRatingChanged = { if (!isPosting) onRatingChanged(it) } ) - Text("추천하고 싶은 메뉴가 있나요?", style = EatssuTheme.typography.subtitle1) + Text(stringResource(R.string.review_recommend_menu), style = EatssuTheme.typography.subtitle1) Spacer(modifier = Modifier.height(16.dp)) LazyColumn( @@ -229,7 +230,7 @@ internal fun WriteReviewScreen( }, placeholder = { Text( - "메뉴에 대한 상세한 리뷰를 작성해주세요", + stringResource(R.string.review_placeholder), style = EatssuTheme.typography.body2, color = Gray400 ) @@ -276,7 +277,7 @@ internal fun WriteReviewScreen( } Text( modifier = Modifier.padding(top = 8.dp), - text = "사진 클릭 시, 삭제됩니다.", + text = stringResource(R.string.review_photo_delete_hint), color = Gray500, style = EatssuTheme.typography.caption3 ) @@ -297,7 +298,7 @@ internal fun WriteReviewScreen( tint = Gray300 ) Text( - "사진 0/1", + stringResource(R.string.review_photo_count, 0, 1), color = Gray400, style = EatssuTheme.typography.caption3 ) diff --git a/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt b/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt index 09ec3616..b2bd575f 100644 --- a/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.net.toUri +import com.eatssu.android.R import com.eatssu.android.presentation.util.showDialog @@ -16,8 +17,8 @@ class ForceUpdateDialogActivity : AppCompatActivity() { } private fun showForceUpdateDialog() { - showDialog("강제 업데이트", "새 버전의 앱을 설치해야 합니다.") { - confirmText = "업데이트" + showDialog(getString(R.string.title_force_update), getString(R.string.dialog_force_update_message)) { + confirmText = getString(R.string.button_update) cancellable = false showCancelButton = false diff --git a/app/src/main/java/com/eatssu/android/presentation/common/NetworkConnection.kt b/app/src/main/java/com/eatssu/android/presentation/common/NetworkConnection.kt index af30eeae..3b89c647 100644 --- a/app/src/main/java/com/eatssu/android/presentation/common/NetworkConnection.kt +++ b/app/src/main/java/com/eatssu/android/presentation/common/NetworkConnection.kt @@ -8,6 +8,7 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.provider.Settings +import com.eatssu.android.R import com.eatssu.android.presentation.util.showDialog // 네트워크 연결 확인을 위해 네트워크 변경 시 알람에 사용하는 클래스 NetworkCallback 을 커스터마이징 @@ -23,7 +24,7 @@ class NetworkConnection(private val context: Context) : // 네트워크 연결 안 되어있을 때 보여줄 다이얼로그 private val dialog: Dialog by lazy { - context.showDialog("네트워크 연결 안 됨", "Wi-Fi, 모바일 데이터를 확인해주세요") { + context.showDialog(context.getString(R.string.dialog_network_error_title), context.getString(R.string.dialog_network_error_message)) { cancellable = false showCancelButton = false showWhenStart = false diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt index fc2188f5..29b2e9cb 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -123,7 +124,7 @@ fun MapRoute( ) { permissions -> val granted = permissions.values.all { it } if (!granted) { - Toast.makeText(context, "내 위치를 바로 확인하며 제휴 식당을 찾아볼 수 있도록 위치 권한을 허용해 주세요.", Toast.LENGTH_SHORT).show() + Toast.makeText(context, context.getString(R.string.dialog_location_permission_description), Toast.LENGTH_SHORT).show() } } @@ -256,12 +257,13 @@ internal fun MapScreen( departmentName: String?, selectedFilter: FilterType, ) { + val context = LocalContext.current Scaffold( topBar = { CenterAlignedTopAppBar( title = { Text( - text = "제휴 지도", + text = stringResource(R.string.title_partnership_map), style = EatssuTheme.typography.subtitle1 ) }, @@ -357,7 +359,7 @@ internal fun MapScreen( captionTextSize = 10.sp, onClick = { if (partnership.partnershipInfos.isEmpty()) { - showToast("제휴 정보가 없습니다.") + showToast(context.getString(R.string.toast_partnership_info_not_found)) true } else { // 제휴 정보가 있을 때만 바텀시트 띄움 diff --git a/app/src/main/java/com/eatssu/android/presentation/map/component/DepartmentBottomSheet.kt b/app/src/main/java/com/eatssu/android/presentation/map/component/DepartmentBottomSheet.kt index ec14a05e..03ac0fab 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/component/DepartmentBottomSheet.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/component/DepartmentBottomSheet.kt @@ -68,7 +68,7 @@ fun DepartmentBottomSheet( Spacer(modifier = Modifier.height(36.dp)) Text( - text = stringResource(R.string.Input_string_description), + text = stringResource(R.string.input_string_description), style = EatssuTheme.typography.h2, textAlign = TextAlign.Start, modifier = Modifier.padding(start = 28.dp) @@ -86,7 +86,7 @@ fun DepartmentBottomSheet( .height(52.dp) ) { - Text(stringResource(R.string.inpur_department), color = White, style = EatssuTheme.typography.button1) + Text(stringResource(R.string.input_department), color = White, style = EatssuTheme.typography.button1) } } } diff --git a/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt b/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt index 8d4d978a..179eeee2 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt @@ -28,11 +28,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color.Companion.White import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.eatssu.android.R import com.eatssu.android.presentation.map.model.PlaceType import com.eatssu.android.presentation.map.model.RestaurantInfo import com.eatssu.android.presentation.util.TrackScreenViewEvent @@ -167,7 +169,7 @@ fun MapRestaurantBottomSheet( append("${item.collegeName}${item.departmentName}") } else -> { - append("단과대/학과 정보를 알 수 없음") + append(stringResource(R.string.map_unknown_college_department)) } } diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt index 120f3ac7..5ce9c1ba 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt @@ -99,7 +99,7 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) binding.tvNickname.text = state.nickname } else { // 필요 시 미설정 안내 문구 - binding.tvNickname.text = "닉네임을 설정해주세요" + binding.tvNickname.text = getString(R.string.set_nickname) } // 알람 스위치 (리스너 잠시 해제 후 값 반영) @@ -191,11 +191,11 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) private fun showNotificationPermissionDialog() { requireContext().run { showDialog( - title = "알림 권한 필요", - description = "알림을 받으려면 알림 권한을 활성화해야 합니다. 설정 화면으로 이동하시겠습니까?" + title = getString(R.string.dialog_notification_permission_title), + description = getString(R.string.dialog_notification_permission_description) ) { - confirmText = "설정으로 이동" - cancelText = "취소" + confirmText = getString(R.string.dialog_settings) + cancelText = getString(R.string.button_cancel) onConfirm { dialog -> openAppNotificationSettings(this@run) dialog.dismiss() @@ -215,7 +215,7 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) private fun showLogoutDialog() { requireContext().run { - showDialog("로그아웃", "로그아웃 하시겠습니까?") { + showDialog(getString(R.string.dialog_logout_title), getString(R.string.dialog_logout_message)) { isDestructive = true onConfirm { mainViewModel.logOut() // 로그아웃은 메인 액티비티에서 처리하도록 수정 diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt index 1d50cfbc..e687941a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt @@ -5,6 +5,7 @@ import android.os.Bundle import androidx.activity.viewModels import androidx.core.widget.doAfterTextChanged import androidx.lifecycle.lifecycleScope +import com.eatssu.android.R import com.eatssu.android.databinding.ActivitySignOutBinding import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.login.LoginActivity @@ -30,7 +31,7 @@ class SignOutActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - toolbarTitle.text = "탈퇴하기" // 툴바 제목 설정 + toolbarTitle.text = getString(R.string.title_sign_out) // 툴바 제목 설정 val nickname = intent.getStringExtra("nickname")?.trim() ?: "" diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListScreen.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListScreen.kt index 3fbb9d2f..c493b965 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListScreen.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -108,7 +109,7 @@ internal fun MyReviewListScreen( Scaffold( topBar = { EatSsuTopBar( - title = "내 리뷰", + title = stringResource(R.string.my_review), onBack = onBack ) }, @@ -167,13 +168,13 @@ internal fun MyReviewListScreen( ) Spacer(Modifier.height(16.dp)) Text( - "아직 작성된 리뷰가 없어요", + stringResource(R.string.none_review), style = EatssuTheme.typography.subtitle2, color = Gray600 ) Spacer(Modifier.height(8.dp)) Text( - "첫 리뷰를 남겨 주세요!", + stringResource(R.string.none_review_my), style = EatssuTheme.typography.caption2, color = Gray600 ) diff --git a/app/src/main/java/com/eatssu/android/presentation/util/DialogUtil.kt b/app/src/main/java/com/eatssu/android/presentation/util/DialogUtil.kt index 9b9cf72e..1c4163b0 100644 --- a/app/src/main/java/com/eatssu/android/presentation/util/DialogUtil.kt +++ b/app/src/main/java/com/eatssu/android/presentation/util/DialogUtil.kt @@ -32,10 +32,10 @@ class DialogBuilder( var cancellable: Boolean = true // 확인 버튼 텍스트 - var confirmText: String = context.getString(R.string.confirm) + var confirmText: String = context.getString(R.string.button_confirm) // 취소 버튼 텍스트 - var cancelText: String = context.getString(R.string.cancel) + var cancelText: String = context.getString(R.string.button_cancel) // 취소 버튼 표시 여부 var showCancelButton: Boolean = true diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt index aacb062c..51b4382a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt @@ -108,9 +108,9 @@ class MealWidget : GlanceAppWidget() { val currentMealTime = WidgetDataDisplayManager.getCurrentMealTime() val (mealTime, mealList) = when (currentMealTime) { - MealTime.Morning -> "아침" to state.breakfast - MealTime.Lunch -> "점심" to state.lunch - MealTime.Dinner -> "저녁" to state.dinner + MealTime.Morning -> context.getString(R.string.widget_morning) to state.breakfast + MealTime.Lunch -> context.getString(R.string.widget_lunch) to state.lunch + MealTime.Dinner -> context.getString(R.string.widget_dinner) to state.dinner } if (mealList.isNotEmpty()) { @@ -124,7 +124,7 @@ class MealWidget : GlanceAppWidget() { MealWidgetError( mealTime = mealTime, restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "", - text = "오늘의 메뉴가 없습니다.", + text = context.getString(R.string.widget_no_menu), glanceId = id, ) } @@ -134,8 +134,8 @@ class MealWidget : GlanceAppWidget() { // Loading 상태일 때도 저장된 식당 정보 표시 MealWidgetError( restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "", - mealTime = "점심", - text = "로딩 중", + mealTime = context.getString(R.string.widget_lunch), + text = context.getString(R.string.widget_loading), glanceId = id, ) } @@ -143,8 +143,8 @@ class MealWidget : GlanceAppWidget() { is WidgetMealInfo.Unavailable -> { MealWidgetError( restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "", - mealTime = "점심", - text = "네트워크 연결 상태를 확인해주세요.", + mealTime = context.getString(R.string.widget_lunch), + text = context.getString(R.string.widget_network_error), glanceId = id, ) } @@ -152,9 +152,9 @@ class MealWidget : GlanceAppWidget() { } else { // 저장된 식당 정보가 없으면 설정 필요 메시지 표시 MealWidgetError( - restaurant = "설정 필요", - mealTime = "점심", - text = "위젯 설정에서 식당을 선택해주세요.", + restaurant = context.getString(R.string.widget_setup_required), + mealTime = context.getString(R.string.widget_lunch), + text = context.getString(R.string.widget_select_prompt), glanceId = id, ) } diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt index 203bf39e..7b16eb0a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt @@ -12,8 +12,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.eatssu.android.R import com.eatssu.android.presentation.util.asString import com.eatssu.common.EventLogger import com.eatssu.common.enums.Restaurant @@ -39,7 +41,7 @@ fun WidgetSettingScreen( modifier = modifier.fillMaxSize(), topBar = { EatSsuTopBar( - title = "위젯 설정", + title = stringResource(R.string.title_widget_setting), onBack = onBack ) }, @@ -51,7 +53,7 @@ fun WidgetSettingScreen( .padding(horizontal = 24.dp) // 이후에 추가적인 패딩 적용 ) { Text( - text = "확인하고 싶은 식당을 선택하세요.", + text = stringResource(R.string.widget_select_restaurant), style = EatssuTheme.typography.body2, modifier = Modifier.padding(bottom = 20.dp) ) @@ -66,7 +68,7 @@ fun WidgetSettingScreen( EatSsuButton( modifier = Modifier.padding(bottom = 74.dp), - text = "선택하기", + text = stringResource(R.string.widget_select), onClick = { val selectedRestaurantEnum = restaurantDisplayNameMap[selectedRestaurant] ?: Restaurant.HAKSIK diff --git a/app/src/main/res/layout/activity_base.xml b/app/src/main/res/layout/activity_base.xml index 81e5d018..5ea36354 100644 --- a/app/src/main/res/layout/activity_base.xml +++ b/app/src/main/res/layout/activity_base.xml @@ -35,7 +35,7 @@ android:layout_height="20dp" android:layout_gravity="center" android:background="@android:color/transparent" - android:contentDescription="@string/back_btn" + android:contentDescription="@string/nav_back" android:scaleType="fitCenter" android:src="@drawable/ic_arrow_left" /> diff --git a/app/src/main/res/layout/activity_fix_menu.xml b/app/src/main/res/layout/activity_fix_menu.xml index 3e4321d1..5352c055 100644 --- a/app/src/main/res/layout/activity_fix_menu.xml +++ b/app/src/main/res/layout/activity_fix_menu.xml @@ -160,7 +160,7 @@ android:layout_marginRight="15dp" android:background="@drawable/shape_text_field_small" android:gravity="top" - android:hint="@string/write_text_review" + android:hint="@string/review_write_text" android:inputType="textMultiLine" android:lines="10" android:maxLength="300" @@ -186,7 +186,7 @@ style="@style/Caption3" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/pic_not_patch" + android:text="@string/review_pic_not_patch" android:textColor="@color/gray500" app:layout_constraintStart_toStartOf="@+id/et_review2_comment" app:layout_constraintTop_toBottomOf="@+id/et_review2_comment" /> @@ -200,7 +200,7 @@ android:layout_marginBottom="30dp" android:background="@drawable/shape_button_duplicate" android:includeFontPadding="false" - android:text="@string/review_patch_done" + android:text="@string/button_modify" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" /> diff --git a/app/src/main/res/layout/activity_report.xml b/app/src/main/res/layout/activity_report.xml index f07288eb..bcaf47a6 100644 --- a/app/src/main/res/layout/activity_report.xml +++ b/app/src/main/res/layout/activity_report.xml @@ -59,7 +59,7 @@ android:layout_height="wrap_content" android:background="@drawable/selector_report" android:button="@null" - android:text="@string/report1" + android:text="@string/report_type_no_associate_content" android:textColor="@color/black" /> diff --git a/app/src/main/res/layout/activity_user_info.xml b/app/src/main/res/layout/activity_user_info.xml index 54649309..aea47a66 100644 --- a/app/src/main/res/layout/activity_user_info.xml +++ b/app/src/main/res/layout/activity_user_info.xml @@ -13,7 +13,7 @@ android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="24dp" - android:text="닉네임 설정" + android:text="@string/userinfo_nickname_setting" android:textColor="@color/black" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -53,7 +53,7 @@ android:layout_marginStart="5dp" android:background="@drawable/shape_button_duplicate" android:stateListAnimator="@null" - android:text="중복확인" + android:text="@string/button_check_duplicate" app:layout_constraintStart_toEndOf="@+id/et_ch_nickname" /> @@ -75,7 +75,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="40dp" - android:text="소속 설정" + android:text="@string/userinfo_affiliation_setting" android:textColor="@color/black" app:layout_constraintTop_toBottomOf="@id/tv_nickname_status" app:layout_constraintStart_toStartOf="parent" @@ -101,7 +101,7 @@ android:layout_height="match_parent" android:gravity="center_vertical" android:paddingEnd="40dp" - android:text="단과대" + android:text="@string/college_placeholder" android:textColor="@color/gray400" /> diff --git a/app/src/main/res/layout/dialog_default.xml b/app/src/main/res/layout/dialog_default.xml index 8891aba9..73825ad3 100644 --- a/app/src/main/res/layout/dialog_default.xml +++ b/app/src/main/res/layout/dialog_default.xml @@ -24,7 +24,7 @@ android:textColor="@color/black" android:textStyle="bold" android:gravity="center" - android:text="제목을 입력해주세요" /> + android:text="@string/dialog_placeholder_title" /> + android:text="@string/dialog_placeholder_body" /> @@ -51,7 +51,7 @@ + android:text="@string/dialog_placeholder_title" /> + android:text="@string/dialog_placeholder_body" /> @@ -51,7 +51,7 @@ @@ -89,14 +89,14 @@ style="@style/Body1" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="푸시 알림 설정" + android:text="@string/mypage_push_notification_title" android:textColor="@color/black" /> @@ -188,6 +188,40 @@ app:tint="@color/gray300" /> + + + + + + + + + + + android:title="@string/nav_cafeteria_menu"/> + android:title="@string/nav_map"/> + android:title="@string/nav_mypage"/> \ No newline at end of file diff --git a/app/src/main/res/menu/menu_my_review.xml b/app/src/main/res/menu/menu_my_review.xml index 59e10a1d..9c59f77b 100644 --- a/app/src/main/res/menu/menu_my_review.xml +++ b/app/src/main/res/menu/menu_my_review.xml @@ -2,8 +2,8 @@ + android:title="@string/button_modify" /> + android:title="@string/button_delete" /> \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 06e0827b..5f12a08b 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -1,5 +1,345 @@ - - + + + EAT-SSU + + + + + + Mon + Tue + Wed + Thu + Fri + Sat + Sun + + + + + + Write Review + Edit Review + Delete Account + My Info + Report + Widget Settings + Partnership Map + Update Required + + + + + Cafeteria + Map + My + back + + + + + Complete + Edit + Delete Review + Delete + Save + Cancel + Confirm + Send + Next + Update + Check + + + + + Logout + Are you sure you want to logout? + You need to install a new version of the app. + A new version is available. Update is required. + Please allow location permission to find nearby partner restaurants. + Notification Permission Required + To receive notifications, you need to enable notification permissions.\nPlease enable it in Settings. + Go to Settings + Do you want to edit this review? + Do you want to delete this review? + Do you want to cancel editing? + Do you want to cancel deletion? + Enter title + Enter content + No Network Connection + Please check your Wi-Fi or mobile data + Notice + + + + + Review has been posted. + Failed to post review. + Review has been updated. + Failed to update review. + Review has been deleted. + Failed to delete review. + Failed to load reviews. + + + + + Image has been uploaded. + Failed to upload image. + Failed to compress image. + Image file not found. + + + + + Report has been submitted. + Failed to submit report. + + + + + Invalid nickname. + Failed to change nickname. + Information has been updated. + Nickname has been changed. + Department information has been updated. + No changes made. + Please select a college first. + + + + + An error occurred during app initialization + Please update the app + No partnership information found. + Login failed. + Logged out successfully. + Please login again. + Session expired. Please login again. + System error. Please login again. + Please set your nickname. + EAT-SSU notifications enabled (%1$s) + EAT-SSU notifications disabled (%1$s) + Failed to load open source libraries. + Failed to load partnership information. + Failed to load your partnership information. + Account deleted successfully. + Failed to delete account. + + + + + How was your meal today? + Any menu you want to recommend? + Write a detailed review about the menu + Preparing screen... + Posting... + Updating... + Click on photo to delete. + Photo %1$d/%2$d + Review Settings + An error occurred. + Write Review + Edit Review + Post Review + Update Review + Write a short review + Photos cannot be edited after posting + Did you enjoy your meal? + Which food is this review about? + Write a detailed review about this menu + \' - Would you recommend it? + + + + + Review + My Reviews + My Reviews + No reviews yet + Write your first review! + Be the first to write a review + Be the first to leave a review for this menu! + Total Reviews + + + + + %1$d stars + + + + + Today\'s Menu + Menu + Price + Rating + Taste + Portion + Student Cafeteria + Snack Corner + Food Court + THE KITCHEN + Dodam Cafeteria + Dormitory Cafeteria + + + + + Select a restaurant to check. + Select + Breakfast + Lunch + Dinner + No menu available today. + Loading + Please check your network connection. + Setup Required + Please select a restaurant in widget settings. + + + + + My Page + My Info + Change Password + Announcements + Logout + Delete Account + App Version + Store App Version + Push Notification Settings + We\'ll send notifications every day at 11 AM + + + Language Settings + Select your preferred language. + System Language (Default) + Language has been changed. + + + + + Welcome! + Please login to use the service + Eat at + SSU! + Kakao + Connected Account + + + + + Nickname + Please set your nickname + Please enter %1$d to %2$d characters. + This nickname is available! + This nickname is already in use. + Whitespace characters (except spaces) are not allowed. + Consecutive spaces are not allowed. + Only Korean, English, and numbers are allowed. + Consecutive special characters are not allowed. + Nickname cannot consist of only numbers. + Nickname cannot start or end with special characters. + Nicknames containing profanity are not allowed. + + + + + Nickname Settings + Affiliation Settings + + + + + Could not load information. + Connection Error + Unable to connect to server.\nPlease try again later. + Unknown error + + + + + Report + Send to EAT SSU + Please select a reason for reporting this review. + You can only report a review once within 24 hours. + Please write the reason for the report + + + + + Max 300 characters + Max 150 + Max 500 characters + Please enter at least 8 characters including letters and numbers. + + + + + EAT SSU + Eat at Soongsil! + TEAM EAT\-SSU + + + + + Contact Us + Terms of Service + Privacy Policy + Privacy Policy + Terms of Service + Contact Us + About Us + Open Source Libraries + + + + + Please leave your inquiry. + Email + Please enter your email to receive a response. + Inquiry Content + Your inquiry has been sent. Response will take 2–3 business days. + Failed to send inquiry. Please try again. + + + + + Are you sure you want to delete your account? + Your reviews will not be deleted and will be displayed as (Unknown).\nPlease check the Terms of Service and Privacy Policy for details. + Please enter your nickname + + + + + 🤔 What should I eat today? + Check out today\'s cafeteria menu! + You have agreed to receive EAT-SSU notifications. + You have declined to receive EAT-SSU notifications. + Pre-lunch Notification + Sends push notifications before lunchtime. + Server Notifications + Displays notifications sent by the EAT-SSU server. + + + + + Enter Department + Enter your department\nand check your partnerships! + College/Department information unknown + + + + + Location + Note + Hours + Favorite Partnerships + Cafe + Restaurant + Bar diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ff2e0aca..d399de5d 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1,5 +1,345 @@ - - + + + EAT-SSU + + + + + + + + + + + + + + + + + + レビューを書く + レビューを編集 + 退会する + マイ情報 + 報告する + ウィジェット設定 + 提携マップ + アップデートが必要 + + + + + 学食 + マップ + マイ + 戻る + + + + + 完了 + 編集 + レビュー削除 + 削除する + 保存 + キャンセル + 確認 + 送信 + 次へ + アップデート + 確認 + + + + + ログアウト + ログアウトしますか? + 新しいバージョンのアプリをインストールする必要があります。 + 新しいバージョンがリリースされました。アップデートが必要です。 + 近くの提携レストランを見つけるために位置情報へのアクセスを許可してください。 + 通知許可が必要 + 通知を受け取るには、通知許可を有効にする必要があります。\n設定で有効にしてください。 + 設定へ移動 + このレビューを編集しますか? + このレビューを削除しますか? + 編集をキャンセルしますか? + 削除をキャンセルしますか? + タイトルを入力してください + 内容を入力してください + ネットワーク接続なし + Wi-Fiまたはモバイルデータを確認してください + お知らせ + + + + + レビューが投稿されました。 + レビューの投稿に失敗しました。 + レビューが更新されました。 + レビューの更新に失敗しました。 + レビューが削除されました。 + レビューの削除に失敗しました。 + レビューの読み込みに失敗しました。 + + + + + 画像がアップロードされました。 + 画像のアップロードに失敗しました。 + 画像の圧縮に失敗しました。 + 画像ファイルが見つかりません。 + + + + + 報告が送信されました。 + 報告の送信に失敗しました。 + + + + + 無効なニックネームです。 + ニックネームの変更に失敗しました。 + 情報が更新されました。 + ニックネームが変更されました。 + 学科情報が更新されました。 + 変更はありません。 + まず学部を選択してください。 + + + + + アプリの初期化中にエラーが発生しました + アプリをアップデートしてください + 提携情報がありません。 + ログインに失敗しました。 + ログアウトしました。 + 再度ログインしてください。 + セッションが期限切れです。再度ログインしてください。 + システムエラーです。再度ログインしてください。 + ニックネームを設定してください。 + EAT-SSU通知が有効になりました (%1$s) + EAT-SSU通知が無効になりました (%1$s) + オープンソースライブラリを読み込めませんでした。 + 提携情報を読み込めませんでした。 + あなたの提携情報を読み込めませんでした。 + 退会が完了しました。 + 退会に失敗しました。 + + + + + 今日の食事はいかがでしたか? + おすすめのメニューはありますか? + メニューについての詳細なレビューを書いてください + 画面を準備中... + 投稿中... + 更新中... + 写真をクリックして削除。 + 写真 %1$d/%2$d + レビュー設定 + エラーが発生しました。 + レビューを書く + レビューを編集 + レビューを投稿 + レビューを更新 + 一行レビューを書く + 投稿後は写真を編集できません + 食事は楽しめましたか? + どの料理についてのレビューですか? + このメニューについての詳細なレビューを書いてください + \' - おすすめしますか? + + + + + レビュー + マイレビュー + マイレビュー + まだレビューがありません + 最初のレビューを書いてください! + 最初にレビューを書いてください + このメニューに最初のレビューを残してください! + レビュー総数 + + + + + %1$d点 + + + + + 今日のメニュー + メニュー + 価格 + 評価 + + + 学生食堂 + スナックコーナー + フードコート + THE KITCHEN + ドダム食堂 + 寮食堂 + + + + + 確認したいレストランを選択してください。 + 選択 + 朝食 + 昼食 + 夕食 + 今日のメニューはありません。 + 読み込み中 + ネットワーク接続を確認してください。 + 設定が必要 + ウィジェット設定でレストランを選択してください。 + + + + + マイページ + マイ情報 + パスワード変更 + お知らせ + ログアウト + 退会 + アプリバージョン + ストアアプリバージョン + プッシュ通知設定 + 毎日午前11時に通知をお届けします + + + 言語設定 + 使用する言語を選択してください。 + システム言語 (デフォルト) + 言語が変更されました。 + + + + + ようこそ! + サービスを利用するにはログインしてください + 崇實大で + 食べよう! + Kakao + 連携アカウント + + + + + ニックネーム + ニックネームを設定してください + %1$d〜%2$d文字で入力してください。 + このニックネームは使用可能です! + このニックネームは既に使用されています。 + 空白文字(スペース除く)は使用できません。 + 連続したスペースは使用できません。 + 韓国語、英語、数字のみ使用可能です。 + 連続した特殊文字は使用できません。 + 数字のみのニックネームは使用できません。 + 特殊文字で始まるまたは終わるニックネームは使用できません。 + 不適切な表現を含むニックネームは使用できません。 + + + + + ニックネーム設定 + 所属設定 + + + + + 情報を読み込めませんでした。 + 接続エラー + サーバーに接続できません。\nしばらくしてからもう一度お試しください。 + 不明なエラー + + + + + 報告 + EAT SSUに送信 + このレビューを報告する理由を選択してください。 + 24時間以内にレビューを1回のみ報告できます。 + 報告理由を記入してください + + + + + 最大300文字 + 最大150 + 最大500文字 + 英字と数字を含む8文字以上を入力してください。 + + + + + EAT SSU + 崇實大で食べよう! + TEAM EAT\-SSU + + + + + お問い合わせ + 利用規約 + プライバシーポリシー + プライバシーポリシー + 利用規約 + お問い合わせ + 制作者 + オープンソースライブラリ + + + + + お問い合わせ内容を入力してください。 + メール + 返信用のメールアドレスを入力してください。 + お問い合わせ内容 + お問い合わせが送信されました。回答には2〜3営業日かかります。 + 送信に失敗しました。もう一度お試しください。 + + + + + 本当に退会しますか? + 投稿したレビューは削除されず、(不明)と表示されます。\n詳細は利用規約およびプライバシーポリシーをご確認ください。 + ニックネームを入力してください + + + + + 🤔 今日は何を食べよう? + 今日の学食メニューをチェック! + EAT-SSU通知の受信に同意しました。 + EAT-SSU通知の受信を拒否しました。 + 昼食前通知 + 昼食時間前にプッシュ通知を送信します。 + サーバー通知 + EAT-SSUサーバーからの通知を表示します。 + + + + + 学科を入力 + 学科を入力して\nあなたの提携を確認! + 学部/学科情報不明 + + + + + 場所 + 備考 + 営業時間 + お気に入りの提携 + カフェ + レストラン + 居酒屋 diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index e70c5362..92f9998e 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1,5 +1,343 @@ - - + + + EAT-SSU + + + + + + T2 + T3 + T4 + T5 + T6 + T7 + CN + + + + + + Viết đánh giá + Sửa đánh giá + Xóa tài khoản + Thông tin của tôi + Báo cáo + Cài đặt Widget + Bản đồ đối tác + Cần cập nhật + + + + + Căng tin + Bản đồ + Tôi + Quay lại + + + + + Hoàn thành + Chỉnh sửa + Xóa đánh giá + Lưu + Hủy + Xác nhận + Gửi + Tiếp theo + Cập nhật + Kiểm tra + + + + + Đăng xuất + Bạn có chắc muốn đăng xuất không? + Bạn cần cài đặt phiên bản mới của ứng dụng. + Phiên bản mới đã được phát hành. Cần cập nhật. + Vui lòng cho phép quyền vị trí để tìm nhà hàng đối tác gần đây. + Cần quyền thông báo + Để nhận thông báo, bạn cần bật quyền thông báo.\nVui lòng bật trong Cài đặt. + Đi đến Cài đặt + Bạn có muốn sửa đánh giá này không? + Bạn có muốn xóa đánh giá này không? + Bạn có muốn hủy chỉnh sửa không? + Bạn có muốn hủy xóa không? + Nhập tiêu đề + Nhập nội dung + Không có kết nối mạng + Vui lòng kiểm tra Wi-Fi hoặc dữ liệu di động + Thông báo + + + + + Đánh giá đã được đăng. + Không thể đăng đánh giá. + Đánh giá đã được cập nhật. + Không thể cập nhật đánh giá. + Đánh giá đã được xóa. + Không thể xóa đánh giá. + Không thể tải đánh giá. + + + + + Hình ảnh đã được tải lên. + Không thể tải lên hình ảnh. + Không thể nén hình ảnh. + Không tìm thấy tệp hình ảnh. + + + + + Báo cáo đã được gửi. + Không thể gửi báo cáo. + + + + + Biệt danh không hợp lệ. + Không thể đổi biệt danh. + Thông tin đã được cập nhật. + Biệt danh đã được thay đổi. + Thông tin khoa đã được cập nhật. + Không có thay đổi. + Vui lòng chọn trường trước. + + + + + Đã xảy ra lỗi khi khởi tạo ứng dụng + Vui lòng cập nhật ứng dụng + Không tìm thấy thông tin đối tác. + Đăng nhập thất bại. + Đã đăng xuất thành công. + Vui lòng đăng nhập lại. + Phiên đã hết hạn. Vui lòng đăng nhập lại. + Lỗi hệ thống. Vui lòng đăng nhập lại. + Vui lòng đặt biệt danh. + Thông báo EAT-SSU đã được bật (%1$s) + Thông báo EAT-SSU đã bị tắt (%1$s) + Không thể tải thư viện mã nguồn mở. + Không thể tải thông tin đối tác. + Không thể tải thông tin đối tác của bạn. + Tài khoản đã được xóa thành công. + Không thể xóa tài khoản. + + + + + Bữa ăn hôm nay thế nào? + Có món nào bạn muốn giới thiệu không? + Viết đánh giá chi tiết về món ăn + Đang chuẩn bị màn hình... + Đang đăng... + Đang cập nhật... + Nhấn vào ảnh để xóa. + Ảnh %1$d/%2$d + Cài đặt đánh giá + Đã xảy ra lỗi. + Viết đánh giá + Sửa đánh giá + Đăng đánh giá + Cập nhật đánh giá + Viết đánh giá ngắn + Không thể chỉnh sửa ảnh sau khi đăng + Bạn có thích bữa ăn không? + Đánh giá này về món ăn nào? + Viết đánh giá chi tiết về món ăn này + \' - Bạn có muốn giới thiệu không? + + + + + Đánh giá + Đánh giá của tôi + Đánh giá của tôi + Chưa có đánh giá + Viết đánh giá đầu tiên của bạn! + Hãy là người đầu tiên viết đánh giá + Tổng số đánh giá + + + + + %1$d sao + + + + + Menu hôm nay + Menu + Giá + Đánh giá + Hương vị + Khẩu phần + Căng tin sinh viên + Góc ăn vặt + Khu ẩm thực + THE KITCHEN + Căng tin Dodam + Căng tin ký túc xá + + + + + Chọn nhà hàng để xem. + Chọn + Bữa sáng + Bữa trưa + Bữa tối + Không có menu hôm nay. + Đang tải + Vui lòng kiểm tra kết nối mạng. + Cần cài đặt + Vui lòng chọn nhà hàng trong cài đặt widget. + + + + + Trang của tôi + Thông tin của tôi + Đổi mật khẩu + Thông báo + Đăng xuất + Xóa tài khoản + Phiên bản ứng dụng + Phiên bản trên cửa hàng + Cài đặt thông báo đẩy + Chúng tôi sẽ gửi thông báo mỗi ngày lúc 11 giờ sáng + + + Cài đặt ngôn ngữ + Chọn ngôn ngữ bạn muốn sử dụng. + Ngôn ngữ hệ thống (Mặc định) + Ngôn ngữ đã được thay đổi. + + + + + Chào mừng! + Vui lòng đăng nhập để sử dụng dịch vụ + Ăn tại + SSU! + Kakao + Tài khoản đã kết nối + + + + + Biệt danh + Vui lòng đặt biệt danh + Vui lòng nhập từ %1$d đến %2$d ký tự. + Biệt danh này có thể sử dụng! + Biệt danh này đã được sử dụng. + Không được sử dụng ký tự khoảng trắng (trừ dấu cách). + Không được sử dụng dấu cách liên tiếp. + Chỉ được sử dụng tiếng Hàn, tiếng Anh và số. + Không được sử dụng ký tự đặc biệt liên tiếp. + Biệt danh không thể chỉ bao gồm số. + Biệt danh không thể bắt đầu hoặc kết thúc bằng ký tự đặc biệt. + Không được sử dụng biệt danh chứa ngôn từ thô tục. + + + + + Cài đặt biệt danh + Cài đặt đơn vị + + + + + Không thể tải thông tin. + Lỗi kết nối + Không thể kết nối với máy chủ.\nVui lòng thử lại sau. + Lỗi không xác định + + + + + Báo cáo + Gửi đến EAT SSU + Vui lòng chọn lý do báo cáo đánh giá này. + Bạn chỉ có thể báo cáo một đánh giá một lần trong vòng 24 giờ. + Vui lòng viết lý do báo cáo + + + + + Tối đa 300 ký tự + Tối đa 150 + Tối đa 500 ký tự + Vui lòng nhập ít nhất 8 ký tự bao gồm chữ cái và số. + + + + + EAT SSU + Ăn tại Soongsil! + TEAM EAT\-SSU + + + + + Liên hệ + Điều khoản dịch vụ + Chính sách bảo mật + Chính sách bảo mật + Điều khoản dịch vụ + Liên hệ + Về chúng tôi + Thư viện mã nguồn mở + + + + + Vui lòng để lại câu hỏi. + Email + Vui lòng nhập email để nhận phản hồi. + Nội dung câu hỏi + Câu hỏi đã được gửi. Phản hồi sẽ mất 2-3 ngày làm việc. + Không thể gửi câu hỏi. Vui lòng thử lại. + + + + + Bạn có chắc chắn muốn xóa tài khoản không? + Đánh giá của bạn sẽ không bị xóa và sẽ hiển thị là (Không xác định).\nVui lòng xem Điều khoản dịch vụ và Chính sách bảo mật để biết chi tiết. + Vui lòng nhập biệt danh của bạn + + + + + 🤔 Hôm nay ăn gì? + Xem menu căng tin hôm nay! + Bạn đã đồng ý nhận thông báo EAT-SSU. + Bạn đã từ chối nhận thông báo EAT-SSU. + Thông báo trước bữa trưa + Gửi thông báo đẩy trước giờ ăn trưa. + Thông báo từ máy chủ + Hiển thị thông báo từ máy chủ EAT-SSU. + + + + + Nhập Khoa + Nhập khoa của bạn\nvà xem các đối tác! + Không xác định thông tin Trường/Khoa + + + + + Vị trí + Ghi chú + Giờ mở cửa + Đối tác yêu thích + Quán cà phê + Nhà hàng + Quán bar diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index b1ceedd4..23495e56 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1,5 +1,345 @@ - - + + + EAT-SSU + + + + + + 周一 + 周二 + 周三 + 周四 + 周五 + 周六 + 周日 + + + + + + 撰写评价 + 修改评价 + 注销账户 + 我的信息 + 举报 + 小组件设置 + 合作地图 + 需要更新 + + + + + 食堂 + 地图 + 我的 + 返回 + + + + + 完成 + 修改 + 删除评价 + 删除 + 保存 + 取消 + 确认 + 发送 + 下一步 + 更新 + 检查 + + + + + 退出登录 + 确定要退出登录吗? + 需要安装新版本的应用程序。 + 新版本已发布,需要更新。 + 请允许位置权限以查找附近的合作餐厅。 + 需要通知权限 + 要接收通知,需要启用通知权限。\n请在设置中启用。 + 前往设置 + 要修改这条评价吗? + 要删除这条评价吗? + 要取消修改吗? + 要取消删除吗? + 请输入标题 + 请输入内容 + 网络连接失败 + 请检查Wi-Fi或移动数据 + 公告 + + + + + 评价已发布。 + 发布评价失败。 + 评价已更新。 + 更新评价失败。 + 评价已删除。 + 删除评价失败。 + 加载评价失败。 + + + + + 图片已上传。 + 上传图片失败。 + 压缩图片失败。 + 找不到图片文件。 + + + + + 举报已提交。 + 提交举报失败。 + + + + + 昵称无效。 + 更改昵称失败。 + 信息已更新。 + 昵称已更改。 + 院系信息已更新。 + 没有更改。 + 请先选择学院。 + + + + + 应用初始化时发生错误 + 请更新应用 + 没有合作信息。 + 登录失败。 + 已成功退出。 + 请重新登录。 + 会话已过期,请重新登录。 + 系统错误,请重新登录。 + 请设置您的昵称。 + EAT-SSU通知已启用 (%1$s) + EAT-SSU通知已禁用 (%1$s) + 无法加载开源库。 + 无法加载合作信息。 + 无法加载您的合作信息。 + 账户已成功删除。 + 删除账户失败。 + + + + + 今天的餐食怎么样? + 有想推荐的菜品吗? + 请写一份详细的菜品评价 + 正在准备页面... + 发布中... + 更新中... + 点击照片删除。 + 照片 %1$d/%2$d + 评价设置 + 发生错误。 + 撰写评价 + 修改评价 + 发布评价 + 更新评价 + 写一句简短评价 + 发布后无法修改照片 + 用餐愉快吗? + 这是关于哪道菜的评价? + 请写一份详细的菜品评价 + \' - 您会推荐吗? + + + + + 评价 + 我的评价 + 我的评价 + 还没有评价 + 写下您的第一条评价! + 成为第一个写评价的人 + 成为第一个为这道菜留下评价的人吧! + 评价总数 + + + + + %1$d星 + + + + + 今日菜单 + 菜单 + 价格 + 评分 + 口味 + 分量 + 学生食堂 + 小吃角 + 美食广场 + THE KITCHEN + 道担食堂 + 宿舍食堂 + + + + + 选择要查看的餐厅。 + 选择 + 早餐 + 午餐 + 晚餐 + 今天没有菜单。 + 加载中 + 请检查网络连接。 + 需要设置 + 请在小组件设置中选择餐厅。 + + + + + 我的页面 + 我的信息 + 更改密码 + 公告 + 退出登录 + 注销账户 + 应用版本 + 商店应用版本 + 推送通知设置 + 我们每天上午11点发送通知 + + + 语言设置 + 选择您要使用的语言。 + 系统语言 (默认) + 语言已更改。 + + + + + 欢迎! + 请登录使用服务 + 在崇实 + 用餐! + Kakao + 已连接的账户 + + + + + 昵称 + 请设置您的昵称 + 请输入%1$d到%2$d个字符。 + 此昵称可用! + 此昵称已被使用。 + 不允许使用空白字符(空格除外)。 + 不允许连续空格。 + 只允许使用韩文、英文和数字。 + 不允许连续特殊字符。 + 昵称不能只由数字组成。 + 昵称不能以特殊字符开头或结尾。 + 不允许包含不当言论的昵称。 + + + + + 昵称设置 + 所属设置 + + + + + 无法加载信息。 + 连接错误 + 无法连接到服务器。\n请稍后再试。 + 未知错误 + + + + + 举报 + 发送给 EAT SSU + 请选择举报此评价的原因。 + 24小时内只能举报一条评价一次。 + 请写明举报原因 + + + + + 最多300字符 + 最多150 + 最多500字符 + 请输入至少8个字符,包括字母和数字。 + + + + + EAT SSU + 在崇实用餐! + TEAM EAT\-SSU + + + + + 联系我们 + 服务条款 + 隐私政策 + 隐私政策 + 服务条款 + 联系我们 + 关于我们 + 开源库 + + + + + 请留下您的问题。 + 电子邮件 + 请输入您的电子邮件以接收回复。 + 问题内容 + 您的问题已发送。回复需要2-3个工作日。 + 发送问题失败,请重试。 + + + + + 确定要注销账户吗? + 您的评价不会被删除,将显示为(未知)。\n详情请查看服务条款和隐私政策。 + 请输入您的昵称 + + + + + 🤔 今天吃什么? + 查看今天的食堂菜单! + 您已同意接收EAT-SSU通知。 + 您已拒绝接收EAT-SSU通知。 + 午餐前通知 + 在午餐时间前发送推送通知。 + 服务器通知 + 显示EAT-SSU服务器发送的通知。 + + + + + 输入院系 + 输入您的院系\n查看您的合作! + 学院/院系信息未知 + + + + + 位置 + 备注 + 营业时间 + 收藏的合作 + 咖啡厅 + 餐厅 + 酒吧 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f7ee5aad..0d955948 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,8 +1,12 @@ + + EAT-SSU + + @@ -46,7 +50,65 @@ rvBreakfastSnack + + + + 리뷰 작성하기 + 리뷰 수정하기 + 탈퇴하기 + 내 정보 + 신고하기 + 위젯 설정 + 제휴 지도 + 강제 업데이트 + + + + + 학식 + 지도 + 마이 + back + + + + + 완료하기 + 수정하기 + 리뷰삭제 + 삭제하기 + 저장하기 + 취소 + 확인 + 전송하기 + 다음 단계로 + 업데이트 + 중복확인 + + + + + 로그아웃 + 로그아웃 하시겠습니까? + 새 버전의 앱을 설치해야 합니다. + 새 버전이 출시되었습니다. 업데이트가 필요합니다. + 내 위치를 바로 확인하며 제휴 식당을 찾아볼 수 있도록 위치 권한을 허용해 주세요. + 알림 권한 필요 + 알림을 받으려면 알림 권한을 활성화해야 합니다.\n설정에서 알림 권한을 허용해주세요. + 설정으로 이동 + 리뷰를 수정하시겠습니까? + 리뷰를 삭제하시겠습니까? + 리뷰 수정을 취소하시겠습니까? + 리뷰 삭제를 취소하시겠습니까? + 제목을 입력해주세요 + 본문을 입력해주세요 + 네트워크 연결 안 됨 + Wi-Fi, 모바일 데이터를 확인해주세요 + 공지 + + + 리뷰가 작성되었습니다. 리뷰 작성에 실패하였습니다. 리뷰를 수정했습니다. @@ -55,17 +117,23 @@ 리뷰 삭제에 실패했습니다. 리뷰를 불러오지 못했습니다. + + 이미지가 업로드되었습니다. 이미지 업로드에 실패하였습니다. 이미지 압축에 실패하였습니다. 이미지 파일을 찾을 수 없습니다. + + 신고가 완료되었습니다. 신고가 실패하였습니다. + + 올바르지 않은 닉네임이에요. 닉네임 변경에 실패했어요. 정보가 업데이트되었습니다. @@ -74,7 +142,9 @@ 변경사항이 없습니다. 단과대를 먼저 선택해 주세요. + + 앱 초기화 중 오류가 발생했습니다 앱을 업데이트해주세요 제휴 정보가 없습니다. @@ -92,17 +162,9 @@ 회원탈퇴되었어요. 회원탈퇴에 실패했어요. - - 리뷰 작성하기 - 리뷰 수정하기 - 탈퇴하기 - 내 정보 - 신고하기 - 위젯 설정 - 제휴 지도 - 강제 업데이트 - + + 오늘의 식사는 어땠나요? 추천하고 싶은 메뉴가 있나요? 메뉴에 대한 상세한 리뷰를 작성해주세요 @@ -113,43 +175,39 @@ 사진 %1$d/%2$d 리뷰 설정 에러가 발생했습니다. + 리뷰 작성하기 + 리뷰 수정하기 + 리뷰 등록하기 + 리뷰 수정하기 + 한 줄 평 작성하기 + 리뷰 등록 후, 사진은 수정할 수 없습니다 + 식사는 맛있게 하셨나요? + 어떤 음식에 대한 리뷰인가요? + 해당 메뉴에 대한 상세한 평가를 남겨주세요 + \'를 추천하시겠어요? + + + + + 리뷰 + 내 리뷰 + 작성한 리뷰 + 아직 작성한 리뷰가 없어요 + 첫 리뷰를 남겨 주세요! + 가장 먼저 리뷰를 남겨 주세요 + 메뉴에 가장 먼저 리뷰를 남겨주세요! + 총 리뷰 수 - - 확인하고 싶은 식당을 선택하세요. - 선택하기 - 아침 - 점심 - 저녁 - 오늘의 메뉴가 없습니다. - 로딩 중 - 네트워크 연결 상태를 확인해주세요. - 설정 필요 - 위젯 설정에서 식당을 선택해주세요. - - - 로그아웃 - 로그아웃 하시겠습니까? - 새 버전의 앱을 설치해야 합니다. - 내 위치를 바로 확인하며 제휴 식당을 찾아볼 수 있도록 위치 권한을 허용해 주세요. - 알림 권한 필요 - 알림을 받으려면 알림 권한을 활성화해야 합니다.\n설정에서 알림 권한을 허용해주세요. - 설정으로 이동 - 리뷰를 수정하시겠습니까? - 리뷰를 삭제하시겠습니까? - 리뷰 수정을 취소하시겠습니까? - 리뷰 삭제를 취소하시겠습니까? - - - 완료하기 - 수정하기 - 리뷰삭제 - 저장하기 - 취소 - 확인 - 전송하기 + + + + %1$d점 + + 오늘의 메뉴 + 메뉴 가격 평점 @@ -160,94 +218,108 @@ THE KITCHEN 도담식당 기숙사 식당 - 기숙사 식당 + + + + 확인하고 싶은 식당을 선택하세요. + 선택하기 + 아침 + 점심 + 저녁 + 오늘의 메뉴가 없습니다. + 로딩 중 + 네트워크 연결 상태를 확인해주세요. + 설정 필요 + 위젯 설정에서 식당을 선택해주세요. + + - 내 리뷰 + + 마이페이지 내 정보 비밀번호 변경 공지사항 로그아웃 회원탈퇴 앱 버전 - 마이 - 마이페이지 + 스토어 앱 버전 + 푸시 알림 설정 + 매일 오전 11시에 알림을 보내드려요 - - 작성한 리뷰 - 아직 작성한 리뷰가 없어요 - 첫 리뷰를 남겨 주세요! - 가장 먼저 리뷰를 남겨 주세요 - 리뷰 작성하기 - 리뷰 수정하기 - 신고 - 리뷰 - 총 리뷰 수 + + 언어 설정 + 사용할 언어를 선택하세요. + 시스템 언어 (기본값) + 언어가 변경되었습니다. + + 환영합니다! 서비스 이용을 위해 로그인 해주세요 + 숭실대에서 + 먹자 카카오 연결된 계정 + + 닉네임 닉네임을 설정해주세요 %1$d~%2$d글자를 입력해주세요. 사용 가능한 닉네임입니다! 이미 사용 중인 닉네임입니다. - + 띄어쓰기를 제외한 공백 문자는 사용할 수 없어요. + 연속된 띄어쓰기는 사용할 수 없어요. + 허용 문자(한글/영문/숫자)만 사용할 수 있어요. + 연속된 특수문자(--, __)는 사용할 수 없어요. + 숫자만으로 된 닉네임은 사용할 수 없어요. + 특수문자로 시작/끝나는 닉네임은 사용할 수 없어요. + 욕설, 비속어 등의 표현이 포함된 닉네임은 사용할 수 없어요. + + + + + 닉네임 설정 + 소속 설정 + + + 정보를 불러올 수 없어요. 통신 오류 서버와 통신할 수 없습니다.\n잠시 후 다시 시도해주세요. + 알 수 없는 에러 + + + 신고 EAT SSU에게 보내기 리뷰를 신고하는 이유를 선택해주세요. 하나의 리뷰에 대해 24시간 내 한 번만 신고 가능합니다. 리뷰 신고 사유를 작성해 주세요 - 메뉴와 관련없는 내용 - 음란성, 욕설 등 부적절한 내용 - 부적절한 홍보 또는 광고 - 리뷰 작성 취지에 맞지 않은 내용 (복사글 등) - 저작권 도용 의심 (사진 등) - 기타 (하단 내용 작성) - - - 다음 단계로 - 한 줄 평 작성하기 - 리뷰 등록 후, 사진은 수정할 수 없습니다 - 리뷰 등록하기 - 리뷰 수정하기 - 식사는 맛있게 하셨나요? - 어떤 음식에 대한 리뷰인가요? - - - 5점 - 4점 - 3점 - 2점 - 1점 - - - 메뉴 - - - EAT SSU - 숭실대에서 먹자! - \'를 추천하시겠어요? + + 최대 300자 최대 150 최대 500자 영문자과 숫자를 포함하여 8자 이상을 입력해주세요. - - 스토어 앱 버전 + + + + EAT SSU + 숭실대에서 먹자! + TEAM EAT\-SSU + + 문의하기 서비스 이용약관 개인정보 처리방침 @@ -255,10 +327,11 @@ 서비스 이용약관 문의하기 만든 사람들 - TEAM EAT\-SSU 오픈소스 라이브러리 + + 문의할 내용을 남겨주세요. 이메일 답변받을 이메일 주소를 남겨주세요. @@ -266,40 +339,49 @@ 문의 전송이 완료되었습니다. 답변은 영업일 기준 2~3일 소요됩니다. 문의 전송이 실패하였습니다. 다시 시도 해주세요. + + 정말 탈퇴하시겠습니까? 작성한 리뷰는 삭제되지 않으며, (알수없음)으로 표시됩니다.\n자세한 내용은 서비스 이용약관 및 개인정보처리방침을 확인해 주세요. 닉네임을 입력해주세요 + + 🤔 오늘 밥 뭐 먹지 오늘의 학식을 확인해보세요! EAT-SSU 알림 수신을 동의하였습니다. EAT-SSU 알림 수신을 거부하였습니다. + 점심시간 전 알림 + 점심시간 전, 푸시알림을 발송합니다. + 서버가 보낸 알림 + 잇슈 서버가 보낸 알림을 표시합니다. - - https://github.com/EAT-SSU/Docs/wiki/EAT%E2%80%90SSU-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EB%B0%A9%EC%B9%A8 - https://github.com/EAT-SSU/Docs/wiki/EAT%E2%80%90SSU-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9D%B4%EC%9A%A9%EC%95%BD%EA%B4%80 - http://pf.kakao.com/_ZlVAn - eatssu.official - https://eat-ssu.notion.site/1d2eeef75a1681ae800cf6ffa6faa37d?pvs=74 - - - 학식 - 지도 - back - + - 학과 입력하기 - 학과를 입력하고\n나만의 제휴를 확인해보세요! + + 학과 입력하기 + 학과를 입력하고\n나만의 제휴를 확인해보세요! 단과대/학과 정보를 알 수 없음 + + 식당 위치 비고 영업 시간 찜한 제휴 카페 - 음식점 + 음식점 주점 + + + + + https://github.com/EAT-SSU/Docs/wiki/EAT%E2%80%90SSU-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EB%B0%A9%EC%B9%A8 + https://github.com/EAT-SSU/Docs/wiki/EAT%E2%80%90SSU-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9D%B4%EC%9A%A9%EC%95%BD%EA%B4%80 + http://pf.kakao.com/_ZlVAn + eatssu.official + https://eat-ssu.notion.site/1d2eeef75a1681ae800cf6ffa6faa37d?pvs=74 From 91402efeda406f415d268b2caeeb36757b6ece56 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 18:48:06 +0900 Subject: [PATCH 05/20] =?UTF-8?q?feat:=20=EC=96=B8=EC=96=B4=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 3 + .../android/data/local/SettingDataStore.kt | 15 +++ .../android/domain/model/AppLanguage.kt | 30 +++++ .../presentation/mypage/MyPageFragment.kt | 5 + .../language/LanguageSelectorActivity.kt | 22 ++++ .../mypage/language/LanguageSelectorScreen.kt | 106 ++++++++++++++++++ .../language/LanguageSelectorViewModel.kt | 48 ++++++++ .../common/src/main/res/values-en/strings.xml | 28 ++++- .../common/src/main/res/values-ja/strings.xml | 28 ++++- .../common/src/main/res/values-vi/strings.xml | 28 ++++- .../common/src/main/res/values-zh/strings.xml | 28 ++++- core/common/src/main/res/values/strings.xml | 8 +- 12 files changed, 340 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/eatssu/android/domain/model/AppLanguage.kt create mode 100644 app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorActivity.kt create mode 100644 app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt create mode 100644 app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 92a9c382..82503081 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -134,6 +134,9 @@ + diff --git a/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt b/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt index 5d65d520..8d8867d3 100644 --- a/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt +++ b/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt @@ -5,7 +5,9 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore +import com.eatssu.android.domain.model.AppLanguage import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -19,6 +21,7 @@ class SettingDataStore @Inject constructor( companion object { private val DAILY_NOTIFICATION_KEY = booleanPreferencesKey("daily_notification") + private val LANGUAGE_KEY = stringPreferencesKey("app_language") } val dailyNotificationStatus: Flow = context.settingDataStore.data @@ -32,5 +35,17 @@ class SettingDataStore @Inject constructor( } } + val appLanguage: Flow = context.settingDataStore.data + .map { preferences -> + val code = preferences[LANGUAGE_KEY] ?: "" + AppLanguage.fromCode(code) + } + + suspend fun setAppLanguage(language: AppLanguage) { + context.settingDataStore.edit { preferences -> + preferences[LANGUAGE_KEY] = language.code + } + } + suspend fun clear() = context.settingDataStore.edit { it.clear() } } diff --git a/app/src/main/java/com/eatssu/android/domain/model/AppLanguage.kt b/app/src/main/java/com/eatssu/android/domain/model/AppLanguage.kt new file mode 100644 index 00000000..00f26ec7 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/domain/model/AppLanguage.kt @@ -0,0 +1,30 @@ +package com.eatssu.android.domain.model + +import java.util.Locale + +/** + * 앱에서 지원하는 언어 목록 + * SYSTEM은 기기 언어를 따르며, 다른 옵션은 사용자가 직접 선택한 언어 + */ +enum class AppLanguage( + val code: String, + val displayName: String, + val nativeDisplayName: String +) { + SYSTEM("", "System Default", "시스템 언어"), + KOREAN("ko", "Korean", "한국어"), + ENGLISH("en", "English", "English"), + JAPANESE("ja", "Japanese", "日本語"), + CHINESE("zh", "Chinese", "中文"), + VIETNAMESE("vi", "Vietnamese", "Tiếng Việt"); + + companion object { + fun fromCode(code: String): AppLanguage { + return entries.find { it.code == code } ?: SYSTEM + } + } + + fun toLocale(): Locale? { + return if (code.isEmpty()) null else Locale(code) + } +} diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt index 5ce9c1ba..efc811fd 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt @@ -24,6 +24,7 @@ import com.eatssu.android.presentation.login.LoginActivity import com.eatssu.android.presentation.mypage.myreview.MyReviewListComposeActivity import com.eatssu.android.presentation.mypage.terms.WebViewActivity import com.eatssu.android.presentation.mypage.userinfo.UserInfoActivity +import com.eatssu.android.presentation.mypage.language.LanguageSelectorActivity import com.eatssu.android.presentation.util.showDialog import com.eatssu.android.presentation.util.showErrorToast import com.eatssu.android.presentation.util.showInfoToast @@ -150,6 +151,10 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) startActivity(Intent(requireContext(), MyReviewListComposeActivity::class.java)) } + binding.llLanguage.setOnClickListener { + startActivity(Intent(requireContext(), LanguageSelectorActivity::class.java)) + } + binding.tvLogout.setOnClickListener { showLogoutDialog() } diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorActivity.kt new file mode 100644 index 00000000..6781df64 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorActivity.kt @@ -0,0 +1,22 @@ +package com.eatssu.android.presentation.mypage.language + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import com.eatssu.design_system.theme.EatssuTheme +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class LanguageSelectorActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + EatssuTheme { + LanguageSelectorScreen( + onBack = { finish() } + ) + } + } + } +} diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt new file mode 100644 index 00000000..57fa3ac1 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt @@ -0,0 +1,106 @@ +package com.eatssu.android.presentation.mypage.language + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.eatssu.android.R +import com.eatssu.android.domain.model.AppLanguage +import com.eatssu.design_system.component.EatSsuRadioButtonGroup +import com.eatssu.design_system.component.EatSsuTopBar +import com.eatssu.design_system.theme.EatssuTheme + +@Composable +fun LanguageSelectorScreen( + modifier: Modifier = Modifier, + viewModel: LanguageSelectorViewModel = hiltViewModel(), + onBack: () -> Unit = {} +) { + val selectedLanguage by viewModel.selectedLanguage.collectAsStateWithLifecycle() + + LanguageSelectorContent( + modifier = modifier, + selectedLanguage = selectedLanguage, + onLanguageSelected = { viewModel.selectLanguage(it) }, + onBack = onBack + ) +} + +@Composable +fun LanguageSelectorContent( + modifier: Modifier = Modifier, + selectedLanguage: AppLanguage, + onLanguageSelected: (AppLanguage) -> Unit, + onBack: () -> Unit = {} +) { + val languageOptions = AppLanguage.entries.map { language -> + when (language) { + AppLanguage.SYSTEM -> stringResource(R.string.language_system_default) + else -> language.nativeDisplayName + } + } + + val selectedOption = when (selectedLanguage) { + AppLanguage.SYSTEM -> stringResource(R.string.language_system_default) + else -> selectedLanguage.nativeDisplayName + } + + Scaffold( + modifier = modifier.fillMaxSize(), + topBar = { + EatSsuTopBar( + title = stringResource(R.string.language_setting), + onBack = onBack + ) + } + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + .padding(horizontal = 24.dp) + ) { + Text( + text = stringResource(R.string.language_select_description), + style = EatssuTheme.typography.body2, + modifier = Modifier.padding(vertical = 20.dp) + ) + + EatSsuRadioButtonGroup( + options = languageOptions, + selectedOption = selectedOption, + onOptionSelected = { selected -> + val language = AppLanguage.entries.find { lang -> + if (lang == AppLanguage.SYSTEM) { + selected == languageOptions.first() + } else { + lang.nativeDisplayName == selected + } + } ?: AppLanguage.SYSTEM + onLanguageSelected(language) + } + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun LanguageSelectorScreenPreview() { + EatssuTheme { + LanguageSelectorContent( + selectedLanguage = AppLanguage.KOREAN, + onLanguageSelected = {}, + onBack = {} + ) + } +} diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt new file mode 100644 index 00000000..ecffdc7b --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt @@ -0,0 +1,48 @@ +package com.eatssu.android.presentation.mypage.language + +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.os.LocaleListCompat +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.eatssu.android.data.local.SettingDataStore +import com.eatssu.android.domain.model.AppLanguage +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class LanguageSelectorViewModel @Inject constructor( + private val settingDataStore: SettingDataStore +) : ViewModel() { + + private val _selectedLanguage = MutableStateFlow(AppLanguage.SYSTEM) + val selectedLanguage: StateFlow = _selectedLanguage.asStateFlow() + + init { + viewModelScope.launch { + settingDataStore.appLanguage.collect { language -> + _selectedLanguage.value = language + } + } + } + + fun selectLanguage(language: AppLanguage) { + viewModelScope.launch { + settingDataStore.setAppLanguage(language) + _selectedLanguage.value = language + applyLanguage(language) + } + } + + private fun applyLanguage(language: AppLanguage) { + val localeList = if (language == AppLanguage.SYSTEM) { + LocaleListCompat.getEmptyLocaleList() + } else { + LocaleListCompat.forLanguageTags(language.code) + } + AppCompatDelegate.setApplicationLocales(localeList) + } +} diff --git a/core/common/src/main/res/values-en/strings.xml b/core/common/src/main/res/values-en/strings.xml index e228fcf4..13b01ee6 100644 --- a/core/common/src/main/res/values-en/strings.xml +++ b/core/common/src/main/res/values-en/strings.xml @@ -1,4 +1,28 @@ - - + + + + Student Cafeteria + Dodam Cafeteria + Dormitory Cafeteria + FACULTY (Staff Only) + Food Court + Snack Corner + The Kitchen + + + + + Content unrelated to the menu + Obscene, profane or inappropriate content + Inappropriate promotion or advertising + Content not aligned with review purpose (e.g., copied text) + Suspected copyright infringement (e.g., photos) + Other (please describe below) + + + + + College + Department diff --git a/core/common/src/main/res/values-ja/strings.xml b/core/common/src/main/res/values-ja/strings.xml index 3c92fbfc..22e0ab1b 100644 --- a/core/common/src/main/res/values-ja/strings.xml +++ b/core/common/src/main/res/values-ja/strings.xml @@ -1,4 +1,28 @@ - - + + + + 学生食堂 + ドダム食堂 + 寮食堂 + FACULTY (教職員専用) + フードコート + スナックコーナー + ザ・キッチン + + + + + メニューに関係のない内容 + わいせつ、罵倒等の不適切な内容 + 不適切な宣伝または広告 + レビュー目的に合わない内容(コピー文等) + 著作権侵害の疑い(写真等) + その他(下記に記載) + + + + + 学部 + 学科 diff --git a/core/common/src/main/res/values-vi/strings.xml b/core/common/src/main/res/values-vi/strings.xml index 5bade1eb..55ad5f55 100644 --- a/core/common/src/main/res/values-vi/strings.xml +++ b/core/common/src/main/res/values-vi/strings.xml @@ -1,4 +1,28 @@ - - + + + + Căng tin sinh viên + Căng tin Dodam + Căng tin ký túc xá + FACULTY (Chỉ dành cho nhân viên) + Khu ẩm thực + Góc ăn vặt + Bếp + + + + + Nội dung không liên quan đến menu + Nội dung khiêu dâm, thô tục hoặc không phù hợp + Quảng cáo hoặc khuyến mãi không phù hợp + Nội dung không phù hợp với mục đích đánh giá (ví dụ: văn bản sao chép) + Nghi ngờ vi phạm bản quyền (ví dụ: hình ảnh) + Khác (vui lòng mô tả bên dưới) + + + + + Trường + Khoa diff --git a/core/common/src/main/res/values-zh/strings.xml b/core/common/src/main/res/values-zh/strings.xml index acd844ed..582ae5ba 100644 --- a/core/common/src/main/res/values-zh/strings.xml +++ b/core/common/src/main/res/values-zh/strings.xml @@ -1,4 +1,28 @@ - - + + + + 学生食堂 + 道担食堂 + 宿舍食堂 + FACULTY (教职工专用) + 美食广场 + 小吃角 + 厨房 + + + + + 与菜单无关的内容 + 淫秽、粗俗或不当内容 + 不当推广或广告 + 与评价目的不符的内容(如复制文本) + 疑似侵犯版权(如照片) + 其他(请在下方说明) + + + + + 学院 + 院系 diff --git a/core/common/src/main/res/values/strings.xml b/core/common/src/main/res/values/strings.xml index bd2a6d6a..0ccbadf8 100644 --- a/core/common/src/main/res/values/strings.xml +++ b/core/common/src/main/res/values/strings.xml @@ -1,5 +1,7 @@ + + 학생 식당 도담 식당 기숙사 식당 @@ -8,7 +10,9 @@ 스낵 코너 더 키친 + + 메뉴와 관련없는 내용 음란성, 욕설 등 부적절한 내용 부적절한 홍보 또는 광고 @@ -16,7 +20,9 @@ 저작권 도용 의심 (사진 등) 기타 (하단 내용 작성) - + + + 단과대 학과 From 8e12f0977ef7bc1ec868d16faacc30be24f77549 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 18:52:36 +0900 Subject: [PATCH 06/20] =?UTF-8?q?chore:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EB=B2=A0=ED=8A=B8=EB=82=A8=EC=96=B4=20=EB=B2=88=EC=97=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values-vi/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 92f9998e..3bf2e28b 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -43,6 +43,7 @@ Hoàn thành Chỉnh sửa Xóa đánh giá + Xoá đi Lưu Hủy Xác nhận @@ -161,6 +162,7 @@ Chưa có đánh giá Viết đánh giá đầu tiên của bạn! Hãy là người đầu tiên viết đánh giá + Hãy để lại review đầu tiên trên menu nhé! Tổng số đánh giá From bbb364f92865c2b2bca625a4f310d0d573b962f9 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 19:01:31 +0900 Subject: [PATCH 07/20] =?UTF-8?q?refactor:=20fallback=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8D=98=20=ED=95=9C=EA=B5=AD?= =?UTF-8?q?=EC=96=B4=20=EB=8B=A8=EC=96=B4=EB=A5=BC=20nullable=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EB=B0=98=ED=99=98=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=ED=9B=84=20=ED=99=94=EB=A9=B4=20=ED=91=9C=EA=B8=B0?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=A0=9C=EC=99=B8=EB=90=98=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/dto/response/CollegeResponse.kt | 9 ++++---- .../remote/dto/response/DepartmentResponse.kt | 9 ++++---- .../response/UserCollegeDepartmentResponse.kt | 21 +++++++++---------- .../remote/repository/UserRepositoryImpl.kt | 6 +++--- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt index 11b2a5a7..8b2a202c 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt @@ -10,7 +10,8 @@ data class CollegeResponse( val collegeName: String? ) -fun CollegeResponse.toDomain() = College( - collegeId = this.collegeId ?: -1, - collegeName = this.collegeName ?: "단과대", -) \ No newline at end of file +fun CollegeResponse.toDomain(): College? { + val id = collegeId ?: return null + val name = collegeName ?: return null + return College(collegeId = id, collegeName = name) +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt index 108a9bcf..e7964968 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt @@ -10,7 +10,8 @@ data class DepartmentResponse( val departmentName: String?, ) -fun DepartmentResponse.toDomain() = Department( - departmentId = this.departmentId ?: -1, - departmentName = this.departmentName ?: "학과", -) \ No newline at end of file +fun DepartmentResponse.toDomain(): Department? { + val id = departmentId ?: return null + val name = departmentName ?: return null + return Department(departmentId = id, departmentName = name) +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt index dcb51d5a..93fd5a8c 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt @@ -15,14 +15,13 @@ data class UserCollegeDepartmentResponse( val collegeName: String?, ) -fun UserCollegeDepartmentResponse.toDomain(): Pair = - Pair( - College( - collegeId = this.collegeId ?: -1, - collegeName = this.collegeName ?: "단과대" - ), - Department( - departmentId = this.departmentId ?: -1, - departmentName = this.departmentName ?: "학과" - ) - ) \ No newline at end of file +fun UserCollegeDepartmentResponse.toDomain(): Pair? { + val colId = collegeId ?: return null + val colName = collegeName ?: return null + val deptId = departmentId ?: return null + val deptName = departmentName ?: return null + return Pair( + College(collegeId = colId, collegeName = colName), + Department(departmentId = deptId, departmentName = deptName) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/remote/repository/UserRepositoryImpl.kt b/app/src/main/java/com/eatssu/android/data/remote/repository/UserRepositoryImpl.kt index 37e13413..23f2012b 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/repository/UserRepositoryImpl.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/repository/UserRepositoryImpl.kt @@ -51,16 +51,16 @@ class UserRepositoryImpl @Inject constructor( override suspend fun getTotalColleges(): List = userService.getCollegeList() - .map { list -> list.map { it.toDomain() } } + .map { list -> list.mapNotNull { it.toDomain() } } .orEmptyList() override suspend fun getTotalDepartments(collegeId: Int): List = userService.getDepartmentsByCollege(collegeId) - .map { list -> list.map { it.toDomain() } } + .map { list -> list.mapNotNull { it.toDomain() } } .orEmptyList() override suspend fun getUserCollegeDepartment(): Pair? = - userService.getUserCollegeDepartment().map { it.toDomain() }.orNull() + userService.getUserCollegeDepartment().orNull()?.toDomain() override suspend fun setUserDepartment(departmentId: Int): Boolean { return userService.setUserDepartment(UserDepartmentRequest(departmentId)).isSuccess() From 7ed3c7a8432afad04382979789c00e1c803135df Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 19:47:03 +0900 Subject: [PATCH 08/20] =?UTF-8?q?refactor:=20=EC=95=88=EC=93=B0=EB=8A=94?= =?UTF-8?q?=20string=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../list/component/MyReviewBottomSheet.kt | 2 +- app/src/main/res/layout/activity_report.xml | 2 +- app/src/main/res/values-en/strings.xml | 75 +------------ app/src/main/res/values-ja/strings.xml | 61 +--------- app/src/main/res/values-vi/strings.xml | 61 +--------- app/src/main/res/values-zh/strings.xml | 75 +------------ app/src/main/res/values/strings.xml | 106 +----------------- 7 files changed, 11 insertions(+), 371 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt index 7a53dcb9..0627b857 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt @@ -120,7 +120,7 @@ fun MyReviewBottomSheet( ) Spacer(modifier = Modifier.width(20.dp)) Text( - text = stringResource(R.string.button_delete_action), + text = stringResource(R.string.button_delete), style = EatssuTheme.typography.body2, color = Black ) diff --git a/app/src/main/res/layout/activity_report.xml b/app/src/main/res/layout/activity_report.xml index bcaf47a6..a653ef40 100644 --- a/app/src/main/res/layout/activity_report.xml +++ b/app/src/main/res/layout/activity_report.xml @@ -160,7 +160,7 @@ android:background="@drawable/shape_button_duplicate" android:includeFontPadding="false" android:stateListAnimator="@null" - android:text="@string/report_btn" + android:text="@string/button_report" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 5f12a08b..02ac4caf 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -23,7 +23,6 @@ Write Review Edit Review Delete Account - My Info Report Widget Settings Partnership Map @@ -43,12 +42,10 @@ Complete Edit Delete Review - Delete Save + Report Cancel Confirm - Send - Next Update Check @@ -58,20 +55,14 @@ Logout Are you sure you want to logout? You need to install a new version of the app. - A new version is available. Update is required. Please allow location permission to find nearby partner restaurants. Notification Permission Required To receive notifications, you need to enable notification permissions.\nPlease enable it in Settings. Go to Settings - Do you want to edit this review? - Do you want to delete this review? - Do you want to cancel editing? - Do you want to cancel deletion? Enter title Enter content No Network Connection Please check your Wi-Fi or mobile data - Notice @@ -101,13 +92,11 @@ - Invalid nickname. Failed to change nickname. Information has been updated. Nickname has been changed. Department information has been updated. No changes made. - Please select a college first. @@ -124,8 +113,6 @@ EAT-SSU notifications enabled (%1$s) EAT-SSU notifications disabled (%1$s) Failed to load open source libraries. - Failed to load partnership information. - Failed to load your partnership information. Account deleted successfully. Failed to delete account. @@ -143,22 +130,16 @@ Review Settings An error occurred. Write Review - Edit Review Post Review Update Review Write a short review Photos cannot be edited after posting - Did you enjoy your meal? - Which food is this review about? - Write a detailed review about this menu - \' - Would you recommend it? Review My Reviews - My Reviews No reviews yet Write your first review! Be the first to write a review @@ -180,11 +161,6 @@ Taste Portion Student Cafeteria - Snack Corner - Food Court - THE KITCHEN - Dodam Cafeteria - Dormitory Cafeteria @@ -205,12 +181,9 @@ My Page My Info - Change Password - Announcements Logout Delete Account App Version - Store App Version Push Notification Settings We\'ll send notifications every day at 11 AM @@ -218,15 +191,10 @@ Language Settings Select your preferred language. System Language (Default) - Language has been changed. - Welcome! - Please login to use the service - Eat at - SSU! Kakao Connected Account @@ -237,14 +205,6 @@ Please set your nickname Please enter %1$d to %2$d characters. This nickname is available! - This nickname is already in use. - Whitespace characters (except spaces) are not allowed. - Consecutive spaces are not allowed. - Only Korean, English, and numbers are allowed. - Consecutive special characters are not allowed. - Nickname cannot consist of only numbers. - Nickname cannot start or end with special characters. - Nicknames containing profanity are not allowed. @@ -258,35 +218,19 @@ Could not load information. Connection Error Unable to connect to server.\nPlease try again later. - Unknown error Report - Send to EAT SSU Please select a reason for reporting this review. You can only report a review once within 24 hours. Please write the reason for the report - - - - Max 300 characters - Max 150 - Max 500 characters - Please enter at least 8 characters including letters and numbers. - - - - - EAT SSU - Eat at Soongsil! - TEAM EAT\-SSU - + Up to 150 characters Contact Us Terms of Service Privacy Policy @@ -296,16 +240,6 @@ About Us Open Source Libraries - - - - Please leave your inquiry. - Email - Please enter your email to receive a response. - Inquiry Content - Your inquiry has been sent. Response will take 2–3 business days. - Failed to send inquiry. Please try again. - @@ -318,8 +252,6 @@ 🤔 What should I eat today? Check out today\'s cafeteria menu! - You have agreed to receive EAT-SSU notifications. - You have declined to receive EAT-SSU notifications. Pre-lunch Notification Sends push notifications before lunchtime. Server Notifications @@ -339,7 +271,4 @@ Note Hours Favorite Partnerships - Cafe - Restaurant - Bar diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index d399de5d..face0929 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -23,7 +23,6 @@ レビューを書く レビューを編集 退会する - マイ情報 報告する ウィジェット設定 提携マップ @@ -43,12 +42,10 @@ 完了 編集 レビュー削除 - 削除する 保存 + 申告する キャンセル 確認 - 送信 - 次へ アップデート 確認 @@ -58,20 +55,14 @@ ログアウト ログアウトしますか? 新しいバージョンのアプリをインストールする必要があります。 - 新しいバージョンがリリースされました。アップデートが必要です。 近くの提携レストランを見つけるために位置情報へのアクセスを許可してください。 通知許可が必要 通知を受け取るには、通知許可を有効にする必要があります。\n設定で有効にしてください。 設定へ移動 - このレビューを編集しますか? - このレビューを削除しますか? - 編集をキャンセルしますか? - 削除をキャンセルしますか? タイトルを入力してください 内容を入力してください ネットワーク接続なし Wi-Fiまたはモバイルデータを確認してください - お知らせ @@ -101,13 +92,11 @@ - 無効なニックネームです。 ニックネームの変更に失敗しました。 情報が更新されました。 ニックネームが変更されました。 学科情報が更新されました。 変更はありません。 - まず学部を選択してください。 @@ -124,8 +113,6 @@ EAT-SSU通知が有効になりました (%1$s) EAT-SSU通知が無効になりました (%1$s) オープンソースライブラリを読み込めませんでした。 - 提携情報を読み込めませんでした。 - あなたの提携情報を読み込めませんでした。 退会が完了しました。 退会に失敗しました。 @@ -143,22 +130,16 @@ レビュー設定 エラーが発生しました。 レビューを書く - レビューを編集 レビューを投稿 レビューを更新 一行レビューを書く 投稿後は写真を編集できません - 食事は楽しめましたか? - どの料理についてのレビューですか? - このメニューについての詳細なレビューを書いてください - \' - おすすめしますか? レビュー マイレビュー - マイレビュー まだレビューがありません 最初のレビューを書いてください! 最初にレビューを書いてください @@ -180,11 +161,6 @@ 学生食堂 - スナックコーナー - フードコート - THE KITCHEN - ドダム食堂 - 寮食堂 @@ -205,12 +181,9 @@ マイページ マイ情報 - パスワード変更 - お知らせ ログアウト 退会 アプリバージョン - ストアアプリバージョン プッシュ通知設定 毎日午前11時に通知をお届けします @@ -218,15 +191,10 @@ 言語設定 使用する言語を選択してください。 システム言語 (デフォルト) - 言語が変更されました。 - ようこそ! - サービスを利用するにはログインしてください - 崇實大で - 食べよう! Kakao 連携アカウント @@ -237,14 +205,6 @@ ニックネームを設定してください %1$d〜%2$d文字で入力してください。 このニックネームは使用可能です! - このニックネームは既に使用されています。 - 空白文字(スペース除く)は使用できません。 - 連続したスペースは使用できません。 - 韓国語、英語、数字のみ使用可能です。 - 連続した特殊文字は使用できません。 - 数字のみのニックネームは使用できません。 - 特殊文字で始まるまたは終わるニックネームは使用できません。 - 不適切な表現を含むニックネームは使用できません。 @@ -258,13 +218,11 @@ 情報を読み込めませんでした。 接続エラー サーバーに接続できません。\nしばらくしてからもう一度お試しください。 - 不明なエラー 報告 - EAT SSUに送信 このレビューを報告する理由を選択してください。 24時間以内にレビューを1回のみ報告できます。 報告理由を記入してください @@ -272,17 +230,11 @@ - 最大300文字 最大150 - 最大500文字 - 英字と数字を含む8文字以上を入力してください。 - EAT SSU - 崇實大で食べよう! - TEAM EAT\-SSU @@ -299,12 +251,6 @@ - お問い合わせ内容を入力してください。 - メール - 返信用のメールアドレスを入力してください。 - お問い合わせ内容 - お問い合わせが送信されました。回答には2〜3営業日かかります。 - 送信に失敗しました。もう一度お試しください。 @@ -318,8 +264,6 @@ 🤔 今日は何を食べよう? 今日の学食メニューをチェック! - EAT-SSU通知の受信に同意しました。 - EAT-SSU通知の受信を拒否しました。 昼食前通知 昼食時間前にプッシュ通知を送信します。 サーバー通知 @@ -339,7 +283,4 @@ 備考 営業時間 お気に入りの提携 - カフェ - レストラン - 居酒屋 diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 3bf2e28b..b25fe47b 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -23,7 +23,6 @@ Viết đánh giá Sửa đánh giá Xóa tài khoản - Thông tin của tôi Báo cáo Cài đặt Widget Bản đồ đối tác @@ -43,12 +42,10 @@ Hoàn thành Chỉnh sửa Xóa đánh giá - Xoá đi Lưu + Báo cáo Hủy Xác nhận - Gửi - Tiếp theo Cập nhật Kiểm tra @@ -58,20 +55,14 @@ Đăng xuất Bạn có chắc muốn đăng xuất không? Bạn cần cài đặt phiên bản mới của ứng dụng. - Phiên bản mới đã được phát hành. Cần cập nhật. Vui lòng cho phép quyền vị trí để tìm nhà hàng đối tác gần đây. Cần quyền thông báo Để nhận thông báo, bạn cần bật quyền thông báo.\nVui lòng bật trong Cài đặt. Đi đến Cài đặt - Bạn có muốn sửa đánh giá này không? - Bạn có muốn xóa đánh giá này không? - Bạn có muốn hủy chỉnh sửa không? - Bạn có muốn hủy xóa không? Nhập tiêu đề Nhập nội dung Không có kết nối mạng Vui lòng kiểm tra Wi-Fi hoặc dữ liệu di động - Thông báo @@ -101,13 +92,11 @@ - Biệt danh không hợp lệ. Không thể đổi biệt danh. Thông tin đã được cập nhật. Biệt danh đã được thay đổi. Thông tin khoa đã được cập nhật. Không có thay đổi. - Vui lòng chọn trường trước. @@ -124,8 +113,6 @@ Thông báo EAT-SSU đã được bật (%1$s) Thông báo EAT-SSU đã bị tắt (%1$s) Không thể tải thư viện mã nguồn mở. - Không thể tải thông tin đối tác. - Không thể tải thông tin đối tác của bạn. Tài khoản đã được xóa thành công. Không thể xóa tài khoản. @@ -143,22 +130,16 @@ Cài đặt đánh giá Đã xảy ra lỗi. Viết đánh giá - Sửa đánh giá Đăng đánh giá Cập nhật đánh giá Viết đánh giá ngắn Không thể chỉnh sửa ảnh sau khi đăng - Bạn có thích bữa ăn không? - Đánh giá này về món ăn nào? - Viết đánh giá chi tiết về món ăn này - \' - Bạn có muốn giới thiệu không? Đánh giá Đánh giá của tôi - Đánh giá của tôi Chưa có đánh giá Viết đánh giá đầu tiên của bạn! Hãy là người đầu tiên viết đánh giá @@ -180,11 +161,6 @@ Hương vị Khẩu phần Căng tin sinh viên - Góc ăn vặt - Khu ẩm thực - THE KITCHEN - Căng tin Dodam - Căng tin ký túc xá @@ -205,12 +181,9 @@ Trang của tôi Thông tin của tôi - Đổi mật khẩu - Thông báo Đăng xuất Xóa tài khoản Phiên bản ứng dụng - Phiên bản trên cửa hàng Cài đặt thông báo đẩy Chúng tôi sẽ gửi thông báo mỗi ngày lúc 11 giờ sáng @@ -218,15 +191,10 @@ Cài đặt ngôn ngữ Chọn ngôn ngữ bạn muốn sử dụng. Ngôn ngữ hệ thống (Mặc định) - Ngôn ngữ đã được thay đổi. - Chào mừng! - Vui lòng đăng nhập để sử dụng dịch vụ - Ăn tại - SSU! Kakao Tài khoản đã kết nối @@ -237,14 +205,6 @@ Vui lòng đặt biệt danh Vui lòng nhập từ %1$d đến %2$d ký tự. Biệt danh này có thể sử dụng! - Biệt danh này đã được sử dụng. - Không được sử dụng ký tự khoảng trắng (trừ dấu cách). - Không được sử dụng dấu cách liên tiếp. - Chỉ được sử dụng tiếng Hàn, tiếng Anh và số. - Không được sử dụng ký tự đặc biệt liên tiếp. - Biệt danh không thể chỉ bao gồm số. - Biệt danh không thể bắt đầu hoặc kết thúc bằng ký tự đặc biệt. - Không được sử dụng biệt danh chứa ngôn từ thô tục. @@ -258,13 +218,11 @@ Không thể tải thông tin. Lỗi kết nối Không thể kết nối với máy chủ.\nVui lòng thử lại sau. - Lỗi không xác định Báo cáo - Gửi đến EAT SSU Vui lòng chọn lý do báo cáo đánh giá này. Bạn chỉ có thể báo cáo một đánh giá một lần trong vòng 24 giờ. Vui lòng viết lý do báo cáo @@ -272,17 +230,11 @@ - Tối đa 300 ký tự Tối đa 150 - Tối đa 500 ký tự - Vui lòng nhập ít nhất 8 ký tự bao gồm chữ cái và số. - EAT SSU - Ăn tại Soongsil! - TEAM EAT\-SSU @@ -299,12 +251,6 @@ - Vui lòng để lại câu hỏi. - Email - Vui lòng nhập email để nhận phản hồi. - Nội dung câu hỏi - Câu hỏi đã được gửi. Phản hồi sẽ mất 2-3 ngày làm việc. - Không thể gửi câu hỏi. Vui lòng thử lại. @@ -318,8 +264,6 @@ 🤔 Hôm nay ăn gì? Xem menu căng tin hôm nay! - Bạn đã đồng ý nhận thông báo EAT-SSU. - Bạn đã từ chối nhận thông báo EAT-SSU. Thông báo trước bữa trưa Gửi thông báo đẩy trước giờ ăn trưa. Thông báo từ máy chủ @@ -339,7 +283,4 @@ Ghi chú Giờ mở cửa Đối tác yêu thích - Quán cà phê - Nhà hàng - Quán bar diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 23495e56..a5765eb9 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -23,7 +23,6 @@ 撰写评价 修改评价 注销账户 - 我的信息 举报 小组件设置 合作地图 @@ -43,12 +42,10 @@ 完成 修改 删除评价 - 删除 保存 + 举报 取消 确认 - 发送 - 下一步 更新 检查 @@ -58,20 +55,14 @@ 退出登录 确定要退出登录吗? 需要安装新版本的应用程序。 - 新版本已发布,需要更新。 请允许位置权限以查找附近的合作餐厅。 需要通知权限 要接收通知,需要启用通知权限。\n请在设置中启用。 前往设置 - 要修改这条评价吗? - 要删除这条评价吗? - 要取消修改吗? - 要取消删除吗? 请输入标题 请输入内容 网络连接失败 请检查Wi-Fi或移动数据 - 公告 @@ -101,13 +92,11 @@ - 昵称无效。 更改昵称失败。 信息已更新。 昵称已更改。 院系信息已更新。 没有更改。 - 请先选择学院。 @@ -124,8 +113,6 @@ EAT-SSU通知已启用 (%1$s) EAT-SSU通知已禁用 (%1$s) 无法加载开源库。 - 无法加载合作信息。 - 无法加载您的合作信息。 账户已成功删除。 删除账户失败。 @@ -143,22 +130,16 @@ 评价设置 发生错误。 撰写评价 - 修改评价 发布评价 更新评价 写一句简短评价 发布后无法修改照片 - 用餐愉快吗? - 这是关于哪道菜的评价? - 请写一份详细的菜品评价 - \' - 您会推荐吗? 评价 我的评价 - 我的评价 还没有评价 写下您的第一条评价! 成为第一个写评价的人 @@ -180,11 +161,6 @@ 口味 分量 学生食堂 - 小吃角 - 美食广场 - THE KITCHEN - 道担食堂 - 宿舍食堂 @@ -205,12 +181,9 @@ 我的页面 我的信息 - 更改密码 - 公告 退出登录 注销账户 应用版本 - 商店应用版本 推送通知设置 我们每天上午11点发送通知 @@ -218,15 +191,10 @@ 语言设置 选择您要使用的语言。 系统语言 (默认) - 语言已更改。 - 欢迎! - 请登录使用服务 - 在崇实 - 用餐! Kakao 已连接的账户 @@ -237,14 +205,6 @@ 请设置您的昵称 请输入%1$d到%2$d个字符。 此昵称可用! - 此昵称已被使用。 - 不允许使用空白字符(空格除外)。 - 不允许连续空格。 - 只允许使用韩文、英文和数字。 - 不允许连续特殊字符。 - 昵称不能只由数字组成。 - 昵称不能以特殊字符开头或结尾。 - 不允许包含不当言论的昵称。 @@ -258,35 +218,19 @@ 无法加载信息。 连接错误 无法连接到服务器。\n请稍后再试。 - 未知错误 举报 - 发送给 EAT SSU 请选择举报此评价的原因。 24小时内只能举报一条评价一次。 请写明举报原因 - - - - 最多300字符 - 最多150 - 最多500字符 - 请输入至少8个字符,包括字母和数字。 - - - - - EAT SSU - 在崇实用餐! - TEAM EAT\-SSU - + 最多150个字 联系我们 服务条款 隐私政策 @@ -296,16 +240,6 @@ 关于我们 开源库 - - - - 请留下您的问题。 - 电子邮件 - 请输入您的电子邮件以接收回复。 - 问题内容 - 您的问题已发送。回复需要2-3个工作日。 - 发送问题失败,请重试。 - @@ -318,8 +252,6 @@ 🤔 今天吃什么? 查看今天的食堂菜单! - 您已同意接收EAT-SSU通知。 - 您已拒绝接收EAT-SSU通知。 午餐前通知 在午餐时间前发送推送通知。 服务器通知 @@ -339,7 +271,4 @@ 备注 营业时间 收藏的合作 - 咖啡厅 - 餐厅 - 酒吧 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0d955948..7d48d028 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,46 +17,12 @@ - - 01 - 02 - 03 - 04 - 05 - 06 - 07 - 08 - 09 - 10 - 11 - 12 - - - - HAKSIK - DODAM - DOMITORY - THE_KITCHEN - FOOD_COURT - SNACK_CORNER - - - - rvBreakfastHaksik - rvBreakfastDodam - rvBreakfastGisik - rvBreakfastKitchen - rvBreakfastFood - rvBreakfastSnack - - 리뷰 작성하기 리뷰 수정하기 탈퇴하기 - 내 정보 신고하기 위젯 설정 제휴 지도 @@ -75,13 +41,11 @@ 완료하기 수정하기 - 리뷰삭제 - 삭제하기 + 삭제하기 저장하기 + 신고하기 취소 확인 - 전송하기 - 다음 단계로 업데이트 중복확인 @@ -91,20 +55,14 @@ 로그아웃 로그아웃 하시겠습니까? 새 버전의 앱을 설치해야 합니다. - 새 버전이 출시되었습니다. 업데이트가 필요합니다. 내 위치를 바로 확인하며 제휴 식당을 찾아볼 수 있도록 위치 권한을 허용해 주세요. 알림 권한 필요 알림을 받으려면 알림 권한을 활성화해야 합니다.\n설정에서 알림 권한을 허용해주세요. 설정으로 이동 - 리뷰를 수정하시겠습니까? - 리뷰를 삭제하시겠습니까? - 리뷰 수정을 취소하시겠습니까? - 리뷰 삭제를 취소하시겠습니까? 제목을 입력해주세요 본문을 입력해주세요 네트워크 연결 안 됨 Wi-Fi, 모바일 데이터를 확인해주세요 - 공지 @@ -134,13 +92,11 @@ - 올바르지 않은 닉네임이에요. 닉네임 변경에 실패했어요. 정보가 업데이트되었습니다. 닉네임이 변경되었습니다. 학과 정보가 업데이트되었습니다. 변경사항이 없습니다. - 단과대를 먼저 선택해 주세요. @@ -157,8 +113,6 @@ EAT-SSU 수신 동의 (%1$s) EAT-SSU 수신 거절 (%1$s) 오픈소스 라이브러리를 불러올 수 없어요. - 제휴 정보를 불러오지 못했어요. - 내 제휴 정보를 불러오지 못했어요. 회원탈퇴되었어요. 회원탈퇴에 실패했어요. @@ -176,22 +130,16 @@ 리뷰 설정 에러가 발생했습니다. 리뷰 작성하기 - 리뷰 수정하기 리뷰 등록하기 리뷰 수정하기 한 줄 평 작성하기 리뷰 등록 후, 사진은 수정할 수 없습니다 - 식사는 맛있게 하셨나요? - 어떤 음식에 대한 리뷰인가요? - 해당 메뉴에 대한 상세한 평가를 남겨주세요 - \'를 추천하시겠어요? 리뷰 내 리뷰 - 작성한 리뷰 아직 작성한 리뷰가 없어요 첫 리뷰를 남겨 주세요! 가장 먼저 리뷰를 남겨 주세요 @@ -213,11 +161,6 @@ 학생식당 - 스낵코너 - 푸드코트 - THE KITCHEN - 도담식당 - 기숙사 식당 @@ -238,12 +181,9 @@ 마이페이지 내 정보 - 비밀번호 변경 - 공지사항 로그아웃 회원탈퇴 앱 버전 - 스토어 앱 버전 푸시 알림 설정 매일 오전 11시에 알림을 보내드려요 @@ -251,15 +191,10 @@ 언어 설정 사용할 언어를 선택하세요. 시스템 언어 (기본값) - 언어가 변경되었습니다. - 환영합니다! - 서비스 이용을 위해 로그인 해주세요 - 숭실대에서 - 먹자 카카오 연결된 계정 @@ -270,14 +205,6 @@ 닉네임을 설정해주세요 %1$d~%2$d글자를 입력해주세요. 사용 가능한 닉네임입니다! - 이미 사용 중인 닉네임입니다. - 띄어쓰기를 제외한 공백 문자는 사용할 수 없어요. - 연속된 띄어쓰기는 사용할 수 없어요. - 허용 문자(한글/영문/숫자)만 사용할 수 있어요. - 연속된 특수문자(--, __)는 사용할 수 없어요. - 숫자만으로 된 닉네임은 사용할 수 없어요. - 특수문자로 시작/끝나는 닉네임은 사용할 수 없어요. - 욕설, 비속어 등의 표현이 포함된 닉네임은 사용할 수 없어요. @@ -291,13 +218,11 @@ 정보를 불러올 수 없어요. 통신 오류 서버와 통신할 수 없습니다.\n잠시 후 다시 시도해주세요. - 알 수 없는 에러 신고 - EAT SSU에게 보내기 리뷰를 신고하는 이유를 선택해주세요. 하나의 리뷰에 대해 24시간 내 한 번만 신고 가능합니다. 리뷰 신고 사유를 작성해 주세요 @@ -305,17 +230,7 @@ - 최대 300자 - 최대 150 - 최대 500자 - 영문자과 숫자를 포함하여 8자 이상을 입력해주세요. - - - - - EAT SSU - 숭실대에서 먹자! - TEAM EAT\-SSU + 최대 150자 @@ -329,16 +244,6 @@ 만든 사람들 오픈소스 라이브러리 - - - - 문의할 내용을 남겨주세요. - 이메일 - 답변받을 이메일 주소를 남겨주세요. - 문의 내용 - 문의 전송이 완료되었습니다. 답변은 영업일 기준 2~3일 소요됩니다. - 문의 전송이 실패하였습니다. 다시 시도 해주세요. - @@ -351,8 +256,6 @@ 🤔 오늘 밥 뭐 먹지 오늘의 학식을 확인해보세요! - EAT-SSU 알림 수신을 동의하였습니다. - EAT-SSU 알림 수신을 거부하였습니다. 점심시간 전 알림 점심시간 전, 푸시알림을 발송합니다. 서버가 보낸 알림 @@ -372,9 +275,6 @@ 비고 영업 시간 찜한 제휴 - 카페 - 음식점 - 주점 From fb6a28427cb5b69b615c47257ecb4d2d9a800d73 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 19:47:13 +0900 Subject: [PATCH 09/20] =?UTF-8?q?fix:=20=EB=8B=AC=EB=A0=A5=EC=9D=98=20week?= =?UTF-8?q?day=EA=B0=80=20=ED=95=9C=EA=B5=AD=EC=96=B4=EB=A1=9C=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95=EB=90=98=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cafeteria/calendar/CalendarAdapter.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/calendar/CalendarAdapter.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/calendar/CalendarAdapter.kt index 44d21d2a..69db323a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/calendar/CalendarAdapter.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/calendar/CalendarAdapter.kt @@ -8,8 +8,6 @@ import com.eatssu.android.R import com.eatssu.android.databinding.ItemCalendarListBinding import com.eatssu.android.presentation.util.CalendarUtil import java.time.LocalDate -import java.time.format.TextStyle -import java.util.Locale internal class CalendarAdapter( @@ -33,10 +31,14 @@ internal class CalendarAdapter( override fun onBindViewHolder(holder: CalendarViewHolder, position: Int) { + val context = holder.itemView.context + val date = days[position] holder.dayOfMonth.text = date.dayOfMonth.toString() - holder.dayText.text = - date.dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.KOREAN).toString() + + // custom_weekdays 사용해 weekday 표기 + val weekdayNames = context.resources.getStringArray(R.array.custom_weekdays) + holder.dayText.text = weekdayNames[date.dayOfWeek.value - 1] /** * iOS의 FSCalendar를 Custom으로 만들었습니다. @@ -48,7 +50,7 @@ internal class CalendarAdapter( holder.dayOfMonth.setBackgroundResource(R.drawable.selector_background_blue) holder.dayOfMonth.setTextColor( ContextCompat.getColor( - holder.itemView.context, + context, R.color.selector_calendar_colortext ) ) @@ -56,7 +58,7 @@ internal class CalendarAdapter( //오늘 날짜가 선택 되지 않았을 때, 오늘 날 text 색 지정 holder.dayOfMonth.setTextColor( ContextCompat.getColor( - holder.itemView.context, + context, R.color.primary ) ) From cbdc4d15517ff94eb8d3ecb6e4682c3ff779ec36 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 19:49:35 +0900 Subject: [PATCH 10/20] =?UTF-8?q?refactor:=20=ED=95=98=EB=93=9C=EC=BD=94?= =?UTF-8?q?=EB=94=A9=EB=90=9C=20toast=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/presentation/mypage/userinfo/UserInfoActivity.kt | 2 +- .../java/com/eatssu/android/presentation/util/ToastUtil.kt | 3 +++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt index e6a672a4..735c5095 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt @@ -178,7 +178,7 @@ class UserInfoActivity : // 단과대를 먼저 선택하도록 유도 if (data.selectedCollege.collegeId == -1) { - showToast("단과대를 먼저 선택해 주세요.", ToastType.ERROR) + showToast(R.string.toast_college_required, ToastType.ERROR) return } diff --git a/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt b/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt index 701506a0..96919fa2 100644 --- a/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt +++ b/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt @@ -58,6 +58,9 @@ fun Context.showToast( snackbar.show() } +fun Context.showToast(@StringRes messageId: Int, type: ToastType) = + showToast(getString(messageId), type) + fun Context.showToast(event: UiEvent.ShowToast) = showToast(event.message.asString(this), event.type) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d48d028..e9174e49 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,6 +93,7 @@ 닉네임 변경에 실패했어요. + 단과대를 먼저 선택해주세요. 정보가 업데이트되었습니다. 닉네임이 변경되었습니다. 학과 정보가 업데이트되었습니다. From 425cd47f055559420acb9da2862664aa37a92467 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 19:53:18 +0900 Subject: [PATCH 11/20] =?UTF-8?q?refactor:=20=ED=95=98=EB=93=9C=EC=BD=94?= =?UTF-8?q?=EB=94=A9=EB=90=9C=20User=20Info=20Title=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/presentation/mypage/userinfo/UserInfoActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt index 735c5095..ff444699 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt @@ -38,7 +38,7 @@ class UserInfoActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - toolbarTitle.text = "내 정보" + toolbarTitle.text = getString(R.string.my_info) setupListeners() observeUiState() From 49bd387880057e83665f28f49c39c530c014db28 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 20:13:04 +0900 Subject: [PATCH 12/20] =?UTF-8?q?docs:=20nullable=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=9C=20=ED=95=A8=EC=88=98=EC=9D=98=20=EC=9D=98?= =?UTF-8?q?=EB=8F=84=20=EC=84=A4=EB=AA=85=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatssu/android/data/remote/dto/response/CollegeResponse.kt | 1 + .../android/data/remote/dto/response/DepartmentResponse.kt | 1 + .../data/remote/dto/response/UserCollegeDepartmentResponse.kt | 1 + 3 files changed, 3 insertions(+) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt index 8b2a202c..8614cb69 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt @@ -10,6 +10,7 @@ data class CollegeResponse( val collegeName: String? ) +// 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐 fun CollegeResponse.toDomain(): College? { val id = collegeId ?: return null val name = collegeName ?: return null diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt index e7964968..976ac558 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt @@ -10,6 +10,7 @@ data class DepartmentResponse( val departmentName: String?, ) +// 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐 fun DepartmentResponse.toDomain(): Department? { val id = departmentId ?: return null val name = departmentName ?: return null diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt index 93fd5a8c..5b9867b4 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt @@ -15,6 +15,7 @@ data class UserCollegeDepartmentResponse( val collegeName: String?, ) +// 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐 fun UserCollegeDepartmentResponse.toDomain(): Pair? { val colId = collegeId ?: return null val colName = collegeName ?: return null From d182d9f696c753cdb52eb014f44d0385a7e2744f Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 20:27:28 +0900 Subject: [PATCH 13/20] =?UTF-8?q?chore:=20TODO=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/mypage/userinfo/UserInfoViewModel.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt index bca27350..08346e29 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt @@ -158,7 +158,7 @@ class UserInfoViewModel @Inject constructor( selectedCollege = college, isCollegeChanged = isCollegeChanged, // 단과대가 변경되면 학과 초기화 - // TODO: Consider resolving from com.eatssu.common.R.string.department_placeholder at UI layer + // TODO: 기본값 대신에 Department? 로 변경 selectedDepartment = Department(-1, "학과"), departmentList = emptyList() ) @@ -266,8 +266,7 @@ data class UserInfoData( val isDuplicationChecked: Boolean = false, // 중복 확인 완료 여부 // 단과대/학과 - // Note: Placeholder strings correspond to com.eatssu.common.R.string.college_placeholder - // and com.eatssu.common.R.string.department_placeholder + // TODO: 기본값 대신에 Department?, College? 로 변경 val selectedCollege: College = College(-1, "단과대"), val originalCollege: College = College(-1, "단과대"), val isCollegeChanged: Boolean = false, From f1ef2ec9288d19247251c0101eb93b41175bc368 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 20:31:37 +0900 Subject: [PATCH 14/20] =?UTF-8?q?refactor:=20Context=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eatssu/android/presentation/widget/ui/MealWidget.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt index 51b4382a..64c562e3 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt @@ -308,7 +308,6 @@ class MealWidget : GlanceAppWidget() { @Preview @Composable fun MealWidgetPreviewError() { - val context = LocalContext.current - MealWidgetError("저녁", context.getString(Restaurant.DODAM.displayNameResId), "에러임") + MealWidgetError("저녁", Restaurant.DODAM.toUiText().asString(), "에러임") } } From 0fd9bb163f268cc6209ebae073bbc701ff820c72 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Mon, 29 Dec 2025 20:33:19 +0900 Subject: [PATCH 15/20] =?UTF-8?q?chore:=20=EC=82=AC=EB=9D=BC=EC=A7=84=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/presentation/widget/ui/WidgetSettingScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt index 7b16eb0a..afeafc46 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/WidgetSettingScreen.kt @@ -31,7 +31,7 @@ fun WidgetSettingScreen( selectedRestaurant: String, onSelectRestaurant: (String) -> Unit, onConfirm: (Restaurant) -> Unit = {}, - onBack: () -> Unit = {} + onBack: () -> Unit = {} // 뒤로가기 동작을 위한 람다 추가 ) { // onClick 람다에서 LocalContext 접근이 불가하므로 Composable 레벨에서 미리 매핑 생성 val restaurantDisplayNameMap = Restaurant.getVariableRestaurantList() From 5e700adf43ef1f902ae3a76fb16c291f59daf68b Mon Sep 17 00:00:00 2001 From: PeraSite Date: Wed, 31 Dec 2025 16:01:47 +0900 Subject: [PATCH 16/20] =?UTF-8?q?feat:=20=EC=98=81=EC=96=B4=20=EC=B9=B4?= =?UTF-8?q?=EC=B9=B4=EC=98=A4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/res/drawable-en/img_kakao_login_btn.png | Bin 0 -> 8758 bytes .../main/res/drawable-ja/img_kakao_login_btn.png | Bin 0 -> 8758 bytes .../main/res/drawable-vi/img_kakao_login_btn.png | Bin 0 -> 8758 bytes .../main/res/drawable-zh/img_kakao_login_btn.png | Bin 0 -> 8758 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/main/res/drawable-en/img_kakao_login_btn.png create mode 100644 app/src/main/res/drawable-ja/img_kakao_login_btn.png create mode 100644 app/src/main/res/drawable-vi/img_kakao_login_btn.png create mode 100644 app/src/main/res/drawable-zh/img_kakao_login_btn.png diff --git a/app/src/main/res/drawable-en/img_kakao_login_btn.png b/app/src/main/res/drawable-en/img_kakao_login_btn.png new file mode 100644 index 0000000000000000000000000000000000000000..91e2061cf2416fcfe28912499f3b9716b75d6fc4 GIT binary patch literal 8758 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 Date: Wed, 31 Dec 2025 16:07:45 +0900 Subject: [PATCH 17/20] =?UTF-8?q?refactor:=20Department=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EA=B0=92=20=EC=82=AD=EC=A0=9C(=ED=95=98=EB=93=9C?= =?UTF-8?q?=EC=BD=94=EB=94=A9=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mypage/userinfo/UserInfoViewModel.kt | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt index 08346e29..041e6903 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoViewModel.kt @@ -158,8 +158,7 @@ class UserInfoViewModel @Inject constructor( selectedCollege = college, isCollegeChanged = isCollegeChanged, // 단과대가 변경되면 학과 초기화 - // TODO: 기본값 대신에 Department? 로 변경 - selectedDepartment = Department(-1, "학과"), + selectedDepartment = null, departmentList = emptyList() ) ) @@ -223,16 +222,16 @@ class UserInfoViewModel @Inject constructor( // 학과/단과대 변경이 있는 경우 if (data.isCollegeChanged || data.isDepartmentChanged) { - val success = userRepository.setUserDepartment(data.selectedDepartment.departmentId) + val department = data.selectedDepartment ?: return@launch + val college = data.selectedCollege ?: return@launch + + val success = userRepository.setUserDepartment(department.departmentId) if (!success) { _uiState.value = UiState.Error return@launch } - setUserCollegeDepartmentUseCase( - data.selectedCollege, - data.selectedDepartment - ) + setUserCollegeDepartmentUseCase(college, department) departmentUpdated = true } @@ -266,13 +265,12 @@ data class UserInfoData( val isDuplicationChecked: Boolean = false, // 중복 확인 완료 여부 // 단과대/학과 - // TODO: 기본값 대신에 Department?, College? 로 변경 - val selectedCollege: College = College(-1, "단과대"), - val originalCollege: College = College(-1, "단과대"), + val selectedCollege: College? = null, + val originalCollege: College? = null, val isCollegeChanged: Boolean = false, - val selectedDepartment: Department = Department(-1, "학과"), - val originalDepartment: Department = Department(-1, "학과"), + val selectedDepartment: Department? = null, + val originalDepartment: Department? = null, val isDepartmentChanged: Boolean = false, // 목록 @@ -294,7 +292,7 @@ data class UserInfoData( val isNicknameValid = isDuplicationChecked && nicknameValidationError == null val hasDepartmentChange = isCollegeChanged || isDepartmentChanged - val isDepartmentSelected = selectedDepartment.departmentId != -1 + val isDepartmentSelected = selectedDepartment != null return when { // 닉네임 변경: 닉네임 유효성 필수 From 0830f97ae12bbce40f160b9a351a850c5641aecd Mon Sep 17 00:00:00 2001 From: PeraSite Date: Wed, 31 Dec 2025 16:13:13 +0900 Subject: [PATCH 18/20] =?UTF-8?q?feat:=20=EC=88=AD=EC=8B=A4=EB=8C=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=A8=B9=EC=9E=90=20=EC=8A=AC=EB=A1=9C?= =?UTF-8?q?=EA=B1=B4=20=EB=B2=88=EC=97=AD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/activity_login.xml | 18 ++++++++++++++---- app/src/main/res/values-en/strings.xml | 14 +++++++++++++- app/src/main/res/values-ja/strings.xml | 8 ++++++++ app/src/main/res/values-vi/strings.xml | 8 ++++++++ app/src/main/res/values-zh/strings.xml | 14 +++++++++++++- app/src/main/res/values/strings.xml | 8 ++++++++ 6 files changed, 64 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 8d63a8c5..a915ce43 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -40,21 +40,31 @@ app:layout_constraintTop_toBottomOf="@+id/iv_logo"> + + Please write the reason for the report - + Up to 150 characters + + + + + Let\'s Eat at Soongsil! + Let\'s + Eat + at Soongsil! + + + + Contact Us Terms of Service Privacy Policy diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index face0929..c6ad6ea0 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -232,6 +232,14 @@ 最大150 + + + + 崇実大学で食べよう! + 崇実大学で + 食べよう! + + diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index b25fe47b..0f2d84f6 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -232,6 +232,14 @@ Tối đa 150 + + + + Hãy Ăn tại Soongsil! + Hãy + Ăn + tại Soongsil! + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index a5765eb9..df0057ac 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -228,9 +228,21 @@ 请写明举报原因 - + 最多150个字 + + + + + 在崇实大学吃吧! + 在崇实大学 + 吃吧! + + + + + 联系我们 服务条款 隐私政策 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e9174e49..6bc716f0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -233,6 +233,14 @@ 최대 150자 + + + + 숭실대에서 먹자! + 숭실대에서 + 먹자 + + From 990bdb036f1d427bca6b85024b0ebf6c2fafa133 Mon Sep 17 00:00:00 2001 From: PeraSite Date: Wed, 31 Dec 2025 16:14:22 +0900 Subject: [PATCH 19/20] =?UTF-8?q?fix:=20=ED=83=80=EC=9E=85=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BB=B4=ED=8C=8C=EC=9D=BC=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/mypage/userinfo/UserInfoActivity.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt index ff444699..df054b43 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt @@ -133,17 +133,17 @@ class UserInfoActivity : private fun updateCollegeDepartmentUI(data: UserInfoData) { with(binding) { - tvCollege.text = data.selectedCollege.collegeName + tvCollege.text = data.selectedCollege?.collegeName ?: "단과대" tvCollege.setTextColor( getColor( - if (data.selectedCollege.collegeId != -1) R.color.gray700 else R.color.gray400 + if (data.selectedCollege != null) R.color.gray700 else R.color.gray400 ) ) - tvDepartment.text = data.selectedDepartment.departmentName + tvDepartment.text = data.selectedDepartment?.departmentName ?: "학과" tvDepartment.setTextColor( getColor( - if (data.selectedDepartment.departmentId != -1) R.color.gray700 else R.color.gray400 + if (data.selectedDepartment != null) R.color.gray700 else R.color.gray400 ) ) } @@ -177,7 +177,7 @@ class UserInfoActivity : val data = state.data // 단과대를 먼저 선택하도록 유도 - if (data.selectedCollege.collegeId == -1) { + if (data.selectedCollege == null) { showToast(R.string.toast_college_required, ToastType.ERROR) return } From 3e017ad4c28f82d91de3e54fb8411c8878a9861f Mon Sep 17 00:00:00 2001 From: PeraSite Date: Wed, 31 Dec 2025 16:27:02 +0900 Subject: [PATCH 20/20] =?UTF-8?q?refactor:=20AppLanguage=20enum=EC=9D=84?= =?UTF-8?q?=20common=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/eatssu/android/data/local/SettingDataStore.kt | 2 +- .../presentation/mypage/language/LanguageSelectorScreen.kt | 2 +- .../presentation/mypage/language/LanguageSelectorViewModel.kt | 2 +- .../src/main/java/com/eatssu/common/enums}/AppLanguage.kt | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename {app/src/main/java/com/eatssu/android/domain/model => core/common/src/main/java/com/eatssu/common/enums}/AppLanguage.kt (95%) diff --git a/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt b/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt index 8d8867d3..0cfbac1a 100644 --- a/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt +++ b/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt @@ -7,7 +7,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore -import com.eatssu.android.domain.model.AppLanguage +import com.eatssu.common.enums.AppLanguage import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt index 57fa3ac1..467acc52 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorScreen.kt @@ -14,7 +14,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.eatssu.android.R -import com.eatssu.android.domain.model.AppLanguage +import com.eatssu.common.enums.AppLanguage import com.eatssu.design_system.component.EatSsuRadioButtonGroup import com.eatssu.design_system.component.EatSsuTopBar import com.eatssu.design_system.theme.EatssuTheme diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt index ecffdc7b..90690cb2 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorViewModel.kt @@ -5,7 +5,7 @@ import androidx.core.os.LocaleListCompat import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.data.local.SettingDataStore -import com.eatssu.android.domain.model.AppLanguage +import com.eatssu.common.enums.AppLanguage import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/app/src/main/java/com/eatssu/android/domain/model/AppLanguage.kt b/core/common/src/main/java/com/eatssu/common/enums/AppLanguage.kt similarity index 95% rename from app/src/main/java/com/eatssu/android/domain/model/AppLanguage.kt rename to core/common/src/main/java/com/eatssu/common/enums/AppLanguage.kt index 00f26ec7..efe6975b 100644 --- a/app/src/main/java/com/eatssu/android/domain/model/AppLanguage.kt +++ b/core/common/src/main/java/com/eatssu/common/enums/AppLanguage.kt @@ -1,4 +1,4 @@ -package com.eatssu.android.domain.model +package com.eatssu.common.enums import java.util.Locale @@ -27,4 +27,4 @@ enum class AppLanguage( fun toLocale(): Locale? { return if (code.isEmpty()) null else Locale(code) } -} +} \ No newline at end of file