diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 92a9c382c..82503081b 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/alarm/EatSsuFirebaseMessagingService.kt b/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt
index e973b2ea5..32fab8b6b 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 d153249fa..e9f659dca 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/data/local/SettingDataStore.kt b/app/src/main/java/com/eatssu/android/data/local/SettingDataStore.kt
index 5d65d520f..0cfbac1a2 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.common.enums.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/data/remote/dto/response/CollegeResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt
index 11b2a5a7d..8614cb69c 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,9 @@ data class CollegeResponse(
val collegeName: String?
)
-fun CollegeResponse.toDomain() = College(
- collegeId = this.collegeId ?: -1,
- collegeName = this.collegeName ?: "단과대",
-)
\ No newline at end of file
+// 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐
+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 108a9bcf8..976ac558c 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,9 @@ data class DepartmentResponse(
val departmentName: String?,
)
-fun DepartmentResponse.toDomain() = Department(
- departmentId = this.departmentId ?: -1,
- departmentName = this.departmentName ?: "학과",
-)
\ No newline at end of file
+// 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐
+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 dcb51d5a7..5b9867b47 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,14 @@ 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
+// 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐
+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 37e134131..23f2012be 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()
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 6a22eab00..02a1b0360 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/CafeteriaFragment.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/CafeteriaFragment.kt
index f42e71665..769726765 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/calendar/CalendarAdapter.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/calendar/CalendarAdapter.kt
index 44d21d2a0..69db323ab 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
)
)
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 a661266b8..29c684692 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 b571f9819..ca07e3e29 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/ReviewListScreen.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt
index 985210e23..05c459391 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/ReviewListViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt
index 3a880c762..c5cc865e1 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/list/component/MyReviewBottomSheet.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/component/MyReviewBottomSheet.kt
index 832c502c0..0627b8575 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),
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 d8f8c74f3..16f0c58c6 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 83f8f9fb1..ad8cea0a0 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 0818002a0..40652c4a6 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/modify/ModifyViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyViewModel.kt
index fcb7a2722..626a8c35b 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 ab5d3874a..393121420 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 d948f9f17..c5d8f3f5a 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/WriteReviewScreen.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewScreen.kt
index 554983cc8..112b0250d 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/cafeteria/review/write/WriteReviewViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/WriteReviewViewModel.kt
index ef99e2278..d8c0070f7 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/common/ForceUpdateActivity.kt b/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt
index 09ec36167..b2bd575f5 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 3a11de92a..3d7ab7bfa 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
@@ -10,6 +10,7 @@ import android.net.NetworkRequest
import android.provider.Settings
import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.launch
+import com.eatssu.android.R
import com.eatssu.android.presentation.util.showDialog
// 네트워크 연결 확인을 위해 네트워크 변경 시 알람에 사용하는 클래스 NetworkCallback 을 커스터마이징
@@ -27,7 +28,7 @@ class NetworkConnection(
// 네트워크 연결 안 되어있을 때 보여줄 다이얼로그
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/intro/IntroViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/intro/IntroViewModel.kt
index dc0304fce..f32a1b39c 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 662be5580..e9c6b95b1 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 0d0dbf053..29b2e9cb2 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()
}
}
@@ -142,7 +143,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()
}
}
}
@@ -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 ec14a05ef..03ac0fab5 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 8d4d978a3..179eeee2a 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 120f3ac70..efc811fdd 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
@@ -99,7 +100,7 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN)
binding.tvNickname.text = state.nickname
} else {
// 필요 시 미설정 안내 문구
- binding.tvNickname.text = "닉네임을 설정해주세요"
+ binding.tvNickname.text = getString(R.string.set_nickname)
}
// 알람 스위치 (리스너 잠시 해제 후 값 반영)
@@ -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()
}
@@ -191,11 +196,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 +220,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/MyPageViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageViewModel.kt
index 50e1c6cf0..6ada9e763 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/SignOutActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt
index 1d50cfbc6..e687941ad 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/SignOutViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutViewModel.kt
index 74e4edb45..2b816e0d5 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/language/LanguageSelectorActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/language/LanguageSelectorActivity.kt
new file mode 100644
index 000000000..6781df64c
--- /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 000000000..467acc52d
--- /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.common.enums.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 000000000..90690cb2c
--- /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.common.enums.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/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 3fbb9d2ff..c493b9653 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/mypage/myreview/MyReviewViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewViewModel.kt
index 53cfe2a60..7a2499602 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/UserInfoActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt
index e6a672a41..df054b431 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()
@@ -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,8 +177,8 @@ class UserInfoActivity :
val data = state.data
// 단과대를 먼저 선택하도록 유도
- if (data.selectedCollege.collegeId == -1) {
- showToast("단과대를 먼저 선택해 주세요.", ToastType.ERROR)
+ if (data.selectedCollege == null) {
+ showToast(R.string.toast_college_required, ToastType.ERROR)
return
}
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 482979fa5..041e6903c 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,7 +158,7 @@ class UserInfoViewModel @Inject constructor(
selectedCollege = college,
isCollegeChanged = isCollegeChanged,
// 단과대가 변경되면 학과 초기화
- selectedDepartment = Department(-1, "학과"),
+ selectedDepartment = null,
departmentList = emptyList()
)
)
@@ -206,8 +208,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
}
@@ -216,25 +222,25 @@ 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
}
// 성공 메시지
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,12 +265,12 @@ data class UserInfoData(
val isDuplicationChecked: Boolean = false, // 중복 확인 완료 여부
// 단과대/학과
- 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,
// 목록
@@ -286,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 {
// 닉네임 변경: 닉네임 유효성 필수
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 9b9cf72e7..1c4163b01 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/util/ToastUtil.kt b/app/src/main/java/com/eatssu/android/presentation/util/ToastUtil.kt
index e0886b4b7..96919fa26 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,11 +58,14 @@ 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, 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/util/UiTextUtil.kt b/app/src/main/java/com/eatssu/android/presentation/util/UiTextUtil.kt
new file mode 100644
index 000000000..f7a7ce169
--- /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/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 0ebba1249..64c562e39 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
@@ -107,23 +108,23 @@ 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()) {
MealWidgetContent(
mealTime = mealTime,
mealList = mealList,
- restaurant = restaurant?.korean ?: "",
+ restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "",
glanceId = id,
)
} else {
MealWidgetError(
mealTime = mealTime,
- restaurant = restaurant?.korean ?: "",
- text = "오늘의 메뉴가 없습니다.",
+ restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "",
+ text = context.getString(R.string.widget_no_menu),
glanceId = id,
)
}
@@ -132,18 +133,18 @@ class MealWidget : GlanceAppWidget() {
is WidgetMealInfo.Loading -> {
// Loading 상태일 때도 저장된 식당 정보 표시
MealWidgetError(
- restaurant = restaurant?.korean ?: "",
- mealTime = "점심",
- text = "로딩 중",
+ restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "",
+ mealTime = context.getString(R.string.widget_lunch),
+ text = context.getString(R.string.widget_loading),
glanceId = id,
)
}
is WidgetMealInfo.Unavailable -> {
MealWidgetError(
- restaurant = restaurant?.korean ?: "",
- mealTime = "점심",
- text = "네트워크 연결 상태를 확인해주세요.",
+ restaurant = restaurant?.let { context.getString(it.displayNameResId) } ?: "",
+ mealTime = context.getString(R.string.widget_lunch),
+ text = context.getString(R.string.widget_network_error),
glanceId = id,
)
}
@@ -151,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,
)
}
@@ -296,13 +297,17 @@ 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, "에러임")
+ MealWidgetError("저녁", Restaurant.DODAM.toUiText().asString(), "에러임")
}
}
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 fa77a9a06..2cb2d5fad 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 8908c5e93..afeafc461 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,11 +12,13 @@ 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
-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,13 +31,17 @@ 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 = {
EatSsuTopBar(
- title = "위젯 설정",
+ title = stringResource(R.string.title_widget_setting),
onBack = onBack
)
},
@@ -47,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)
)
@@ -62,12 +68,13 @@ fun WidgetSettingScreen(
EatSsuButton(
modifier = Modifier.padding(bottom = 74.dp),
- text = "선택하기",
+ text = stringResource(R.string.widget_select),
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/drawable-en/img_kakao_login_btn.png b/app/src/main/res/drawable-en/img_kakao_login_btn.png
new file mode 100644
index 000000000..91e2061cf
Binary files /dev/null and b/app/src/main/res/drawable-en/img_kakao_login_btn.png differ
diff --git a/app/src/main/res/drawable-ja/img_kakao_login_btn.png b/app/src/main/res/drawable-ja/img_kakao_login_btn.png
new file mode 100644
index 000000000..91e2061cf
Binary files /dev/null and b/app/src/main/res/drawable-ja/img_kakao_login_btn.png differ
diff --git a/app/src/main/res/drawable-vi/img_kakao_login_btn.png b/app/src/main/res/drawable-vi/img_kakao_login_btn.png
new file mode 100644
index 000000000..91e2061cf
Binary files /dev/null and b/app/src/main/res/drawable-vi/img_kakao_login_btn.png differ
diff --git a/app/src/main/res/drawable-zh/img_kakao_login_btn.png b/app/src/main/res/drawable-zh/img_kakao_login_btn.png
new file mode 100644
index 000000000..91e2061cf
Binary files /dev/null and b/app/src/main/res/drawable-zh/img_kakao_login_btn.png differ
diff --git a/app/src/main/res/layout/activity_base.xml b/app/src/main/res/layout/activity_base.xml
index 81e5d0180..5ea36354b 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 3e4321d1b..5352c055b 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_login.xml b/app/src/main/res/layout/activity_login.xml
index 8d63a8c5e..a915ce436 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">
+
+
@@ -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/layout/activity_user_info.xml b/app/src/main/res/layout/activity_user_info.xml
index 54649309e..aea47a667 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 8891aba92..73825ad39 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 59e10a1da..9c59f77b7 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 @@
\ 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
new file mode 100644
index 000000000..afd60113b
--- /dev/null
+++ b/app/src/main/res/values-en/strings.xml
@@ -0,0 +1,286 @@
+
+
+
+
+ EAT-SSU
+
+
+
+
+
+ - Mon
+ - Tue
+ - Wed
+ - Thu
+ - Fri
+ - Sat
+ - Sun
+
+
+
+
+
+ Write Review
+ Edit Review
+ Delete Account
+ Report
+ Widget Settings
+ Partnership Map
+ Update Required
+
+
+
+
+ Cafeteria
+ Map
+ My
+ back
+
+
+
+
+ Complete
+ Edit
+ Delete Review
+ Save
+ Report
+ Cancel
+ Confirm
+ Update
+ Check
+
+
+
+
+ Logout
+ Are you sure you want to logout?
+ You need to install a new version of the app.
+ 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
+ Enter title
+ Enter content
+ No Network Connection
+ Please check your Wi-Fi or mobile data
+
+
+
+
+ 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.
+
+
+
+
+ Failed to change nickname.
+ Information has been updated.
+ Nickname has been changed.
+ Department information has been updated.
+ No changes made.
+
+
+
+
+ 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.
+ 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
+ Post Review
+ Update Review
+ Write a short review
+ Photos cannot be edited after posting
+
+
+
+
+ Review
+ 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
+
+
+
+
+ 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
+ Logout
+ Delete Account
+ App Version
+ Push Notification Settings
+ We\'ll send notifications every day at 11 AM
+
+
+ Language Settings
+ Select your preferred language.
+ System Language (Default)
+
+
+
+
+ Kakao
+ Connected Account
+
+
+
+
+ Nickname
+ Please set your nickname
+ Please enter %1$d to %2$d characters.
+ This nickname is available!
+
+
+
+
+ Nickname Settings
+ Affiliation Settings
+
+
+
+
+ Could not load information.
+ Connection Error
+ Unable to connect to server.\nPlease try again later.
+
+
+
+
+ Report
+ 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
+
+
+
+
+ Up to 150 characters
+
+
+
+
+ Let\'s Eat at Soongsil!
+ Let\'s
+ Eat
+ at Soongsil!
+
+
+
+
+ Contact Us
+ Terms of Service
+ Privacy Policy
+ Privacy Policy
+ Terms of Service
+ Contact Us
+ About Us
+ Open Source Libraries
+
+
+
+
+ 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!
+ 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
+
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 000000000..c6ad6ea0d
--- /dev/null
+++ b/app/src/main/res/values-ja/strings.xml
@@ -0,0 +1,294 @@
+
+
+
+
+ EAT-SSU
+
+
+
+
+
+ - 月
+ - 火
+ - 水
+ - 木
+ - 金
+ - 土
+ - 日
+
+
+
+
+
+ レビューを書く
+ レビューを編集
+ 退会する
+ 報告する
+ ウィジェット設定
+ 提携マップ
+ アップデートが必要
+
+
+
+
+ 学食
+ マップ
+ マイ
+ 戻る
+
+
+
+
+ 完了
+ 編集
+ レビュー削除
+ 保存
+ 申告する
+ キャンセル
+ 確認
+ アップデート
+ 確認
+
+
+
+
+ ログアウト
+ ログアウトしますか?
+ 新しいバージョンのアプリをインストールする必要があります。
+ 近くの提携レストランを見つけるために位置情報へのアクセスを許可してください。
+ 通知許可が必要
+ 通知を受け取るには、通知許可を有効にする必要があります。\n設定で有効にしてください。
+ 設定へ移動
+ タイトルを入力してください
+ 内容を入力してください
+ ネットワーク接続なし
+ Wi-Fiまたはモバイルデータを確認してください
+
+
+
+
+ レビューが投稿されました。
+ レビューの投稿に失敗しました。
+ レビューが更新されました。
+ レビューの更新に失敗しました。
+ レビューが削除されました。
+ レビューの削除に失敗しました。
+ レビューの読み込みに失敗しました。
+
+
+
+
+ 画像がアップロードされました。
+ 画像のアップロードに失敗しました。
+ 画像の圧縮に失敗しました。
+ 画像ファイルが見つかりません。
+
+
+
+
+ 報告が送信されました。
+ 報告の送信に失敗しました。
+
+
+
+
+ ニックネームの変更に失敗しました。
+ 情報が更新されました。
+ ニックネームが変更されました。
+ 学科情報が更新されました。
+ 変更はありません。
+
+
+
+
+ アプリの初期化中にエラーが発生しました
+ アプリをアップデートしてください
+ 提携情報がありません。
+ ログインに失敗しました。
+ ログアウトしました。
+ 再度ログインしてください。
+ セッションが期限切れです。再度ログインしてください。
+ システムエラーです。再度ログインしてください。
+ ニックネームを設定してください。
+ EAT-SSU通知が有効になりました (%1$s)
+ EAT-SSU通知が無効になりました (%1$s)
+ オープンソースライブラリを読み込めませんでした。
+ 退会が完了しました。
+ 退会に失敗しました。
+
+
+
+
+ 今日の食事はいかがでしたか?
+ おすすめのメニューはありますか?
+ メニューについての詳細なレビューを書いてください
+ 画面を準備中...
+ 投稿中...
+ 更新中...
+ 写真をクリックして削除。
+ 写真 %1$d/%2$d
+ レビュー設定
+ エラーが発生しました。
+ レビューを書く
+ レビューを投稿
+ レビューを更新
+ 一行レビューを書く
+ 投稿後は写真を編集できません
+
+
+
+
+ レビュー
+ マイレビュー
+ まだレビューがありません
+ 最初のレビューを書いてください!
+ 最初にレビューを書いてください
+ このメニューに最初のレビューを残してください!
+ レビュー総数
+
+
+
+
+ %1$d点
+
+
+
+
+ 今日のメニュー
+ メニュー
+ 価格
+ 評価
+ 味
+ 量
+ 学生食堂
+
+
+
+
+ 確認したいレストランを選択してください。
+ 選択
+ 朝食
+ 昼食
+ 夕食
+ 今日のメニューはありません。
+ 読み込み中
+ ネットワーク接続を確認してください。
+ 設定が必要
+ ウィジェット設定でレストランを選択してください。
+
+
+
+
+ マイページ
+ マイ情報
+ ログアウト
+ 退会
+ アプリバージョン
+ プッシュ通知設定
+ 毎日午前11時に通知をお届けします
+
+
+ 言語設定
+ 使用する言語を選択してください。
+ システム言語 (デフォルト)
+
+
+
+
+ Kakao
+ 連携アカウント
+
+
+
+
+ ニックネーム
+ ニックネームを設定してください
+ %1$d〜%2$d文字で入力してください。
+ このニックネームは使用可能です!
+
+
+
+
+ ニックネーム設定
+ 所属設定
+
+
+
+
+ 情報を読み込めませんでした。
+ 接続エラー
+ サーバーに接続できません。\nしばらくしてからもう一度お試しください。
+
+
+
+
+ 報告
+ このレビューを報告する理由を選択してください。
+ 24時間以内にレビューを1回のみ報告できます。
+ 報告理由を記入してください
+
+
+
+
+ 最大150
+
+
+
+
+ 崇実大学で食べよう!
+ 崇実大学で
+ 食べよう!
+
+
+
+
+
+
+
+
+
+ お問い合わせ
+ 利用規約
+ プライバシーポリシー
+ プライバシーポリシー
+ 利用規約
+ お問い合わせ
+ 制作者
+ オープンソースライブラリ
+
+
+
+
+
+
+
+
+ 本当に退会しますか?
+ 投稿したレビューは削除されず、(不明)と表示されます。\n詳細は利用規約およびプライバシーポリシーをご確認ください。
+ ニックネームを入力してください
+
+
+
+
+ 🤔 今日は何を食べよう?
+ 今日の学食メニューをチェック!
+ 昼食前通知
+ 昼食時間前にプッシュ通知を送信します。
+ サーバー通知
+ EAT-SSUサーバーからの通知を表示します。
+
+
+
+
+ 学科を入力
+ 学科を入力して\nあなたの提携を確認!
+ 学部/学科情報不明
+
+
+
+
+ 場所
+ 備考
+ 営業時間
+ お気に入りの提携
+
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 000000000..0f2d84f6f
--- /dev/null
+++ b/app/src/main/res/values-vi/strings.xml
@@ -0,0 +1,294 @@
+
+
+
+
+ EAT-SSU
+
+
+
+
+
+ - T2
+ - T3
+ - T4
+ - T5
+ - T6
+ - T7
+ - CN
+
+
+
+
+
+ Viết đánh giá
+ Sửa đánh giá
+ Xóa tài khoản
+ 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
+ Báo cáo
+ Hủy
+ Xác nhận
+ 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.
+ 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
+ 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
+
+
+
+
+ Đá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.
+
+
+
+
+ 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.
+
+
+
+
+ Đã 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ở.
+ 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á
+ Đă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
+
+
+
+
+ Đánh giá
+ Đá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á
+ Hãy để lại review đầu tiên trên menu nhé!
+ 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
+
+
+
+
+ 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
+ Đăng xuất
+ Xóa tài khoản
+ Phiên bản ứng dụ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)
+
+
+
+
+ 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!
+
+
+
+
+ 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.
+
+
+
+
+ Báo cáo
+ 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 150
+
+
+
+
+ Hãy Ăn tại Soongsil!
+ Hãy
+ Ăn
+ tại Soongsil!
+
+
+
+
+
+
+
+
+ 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ở
+
+
+
+
+
+
+
+
+ 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!
+ 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
+
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 000000000..df0057acf
--- /dev/null
+++ b/app/src/main/res/values-zh/strings.xml
@@ -0,0 +1,286 @@
+
+
+
+
+ EAT-SSU
+
+
+
+
+
+ - 周一
+ - 周二
+ - 周三
+ - 周四
+ - 周五
+ - 周六
+ - 周日
+
+
+
+
+
+ 撰写评价
+ 修改评价
+ 注销账户
+ 举报
+ 小组件设置
+ 合作地图
+ 需要更新
+
+
+
+
+ 食堂
+ 地图
+ 我的
+ 返回
+
+
+
+
+ 完成
+ 修改
+ 删除评价
+ 保存
+ 举报
+ 取消
+ 确认
+ 更新
+ 检查
+
+
+
+
+ 退出登录
+ 确定要退出登录吗?
+ 需要安装新版本的应用程序。
+ 请允许位置权限以查找附近的合作餐厅。
+ 需要通知权限
+ 要接收通知,需要启用通知权限。\n请在设置中启用。
+ 前往设置
+ 请输入标题
+ 请输入内容
+ 网络连接失败
+ 请检查Wi-Fi或移动数据
+
+
+
+
+ 评价已发布。
+ 发布评价失败。
+ 评价已更新。
+ 更新评价失败。
+ 评价已删除。
+ 删除评价失败。
+ 加载评价失败。
+
+
+
+
+ 图片已上传。
+ 上传图片失败。
+ 压缩图片失败。
+ 找不到图片文件。
+
+
+
+
+ 举报已提交。
+ 提交举报失败。
+
+
+
+
+ 更改昵称失败。
+ 信息已更新。
+ 昵称已更改。
+ 院系信息已更新。
+ 没有更改。
+
+
+
+
+ 应用初始化时发生错误
+ 请更新应用
+ 没有合作信息。
+ 登录失败。
+ 已成功退出。
+ 请重新登录。
+ 会话已过期,请重新登录。
+ 系统错误,请重新登录。
+ 请设置您的昵称。
+ EAT-SSU通知已启用 (%1$s)
+ EAT-SSU通知已禁用 (%1$s)
+ 无法加载开源库。
+ 账户已成功删除。
+ 删除账户失败。
+
+
+
+
+ 今天的餐食怎么样?
+ 有想推荐的菜品吗?
+ 请写一份详细的菜品评价
+ 正在准备页面...
+ 发布中...
+ 更新中...
+ 点击照片删除。
+ 照片 %1$d/%2$d
+ 评价设置
+ 发生错误。
+ 撰写评价
+ 发布评价
+ 更新评价
+ 写一句简短评价
+ 发布后无法修改照片
+
+
+
+
+ 评价
+ 我的评价
+ 还没有评价
+ 写下您的第一条评价!
+ 成为第一个写评价的人
+ 成为第一个为这道菜留下评价的人吧!
+ 评价总数
+
+
+
+
+ %1$d星
+
+
+
+
+ 今日菜单
+ 菜单
+ 价格
+ 评分
+ 口味
+ 分量
+ 学生食堂
+
+
+
+
+ 选择要查看的餐厅。
+ 选择
+ 早餐
+ 午餐
+ 晚餐
+ 今天没有菜单。
+ 加载中
+ 请检查网络连接。
+ 需要设置
+ 请在小组件设置中选择餐厅。
+
+
+
+
+ 我的页面
+ 我的信息
+ 退出登录
+ 注销账户
+ 应用版本
+ 推送通知设置
+ 我们每天上午11点发送通知
+
+
+ 语言设置
+ 选择您要使用的语言。
+ 系统语言 (默认)
+
+
+
+
+ Kakao
+ 已连接的账户
+
+
+
+
+ 昵称
+ 请设置您的昵称
+ 请输入%1$d到%2$d个字符。
+ 此昵称可用!
+
+
+
+
+ 昵称设置
+ 所属设置
+
+
+
+
+ 无法加载信息。
+ 连接错误
+ 无法连接到服务器。\n请稍后再试。
+
+
+
+
+ 举报
+ 请选择举报此评价的原因。
+ 24小时内只能举报一条评价一次。
+ 请写明举报原因
+
+
+
+
+ 最多150个字
+
+
+
+
+ 在崇实大学吃吧!
+ 在崇实大学
+ 吃吧!
+
+
+
+
+
+ 联系我们
+ 服务条款
+ 隐私政策
+ 隐私政策
+ 服务条款
+ 联系我们
+ 关于我们
+ 开源库
+
+
+
+
+ 确定要注销账户吗?
+ 您的评价不会被删除,将显示为(未知)。\n详情请查看服务条款和隐私政策。
+ 请输入您的昵称
+
+
+
+
+ 🤔 今天吃什么?
+ 查看今天的食堂菜单!
+ 午餐前通知
+ 在午餐时间前发送推送通知。
+ 服务器通知
+ 显示EAT-SSU服务器发送的通知。
+
+
+
+
+ 输入院系
+ 输入您的院系\n查看您的合作!
+ 学院/院系信息未知
+
+
+
+
+ 位置
+ 备注
+ 营业时间
+ 收藏的合作
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6ed752996..6bc716f0a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,5 +1,12 @@
+
+
+
EAT-SSU
+
+
+
+
- 월
- 화
@@ -10,200 +17,280 @@
- 일
-
- - 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
-
+
+
+
+ 리뷰 작성하기
+ 리뷰 수정하기
+ 탈퇴하기
+ 신고하기
+ 위젯 설정
+ 제휴 지도
+ 강제 업데이트
+
+
+
+
+ 학식
+ 지도
+ 마이
+ back
+
+
+
+
+ 완료하기
+ 수정하기
+ 삭제하기
+ 저장하기
+ 신고하기
+ 취소
+ 확인
+ 업데이트
+ 중복확인
+
+
+
+
+ 로그아웃
+ 로그아웃 하시겠습니까?
+ 새 버전의 앱을 설치해야 합니다.
+ 내 위치를 바로 확인하며 제휴 식당을 찾아볼 수 있도록 위치 권한을 허용해 주세요.
+ 알림 권한 필요
+ 알림을 받으려면 알림 권한을 활성화해야 합니다.\n설정에서 알림 권한을 허용해주세요.
+ 설정으로 이동
+ 제목을 입력해주세요
+ 본문을 입력해주세요
+ 네트워크 연결 안 됨
+ Wi-Fi, 모바일 데이터를 확인해주세요
+
+
+
+
+ 리뷰가 작성되었습니다.
+ 리뷰 작성에 실패하였습니다.
+ 리뷰를 수정했습니다.
+ 리뷰 수정이 실패했습니다.
+ 리뷰를 삭제했습니다.
+ 리뷰 삭제에 실패했습니다.
+ 리뷰를 불러오지 못했습니다.
+
+
+
+
+ 이미지가 업로드되었습니다.
+ 이미지 업로드에 실패하였습니다.
+ 이미지 압축에 실패하였습니다.
+ 이미지 파일을 찾을 수 없습니다.
+
+
+
+
+ 신고가 완료되었습니다.
+ 신고가 실패하였습니다.
+
+
+
+
+ 닉네임 변경에 실패했어요.
+ 단과대를 먼저 선택해주세요.
+ 정보가 업데이트되었습니다.
+ 닉네임이 변경되었습니다.
+ 학과 정보가 업데이트되었습니다.
+ 변경사항이 없습니다.
+
+
+
+
+ 앱 초기화 중 오류가 발생했습니다
+ 앱을 업데이트해주세요
+ 제휴 정보가 없습니다.
+ 로그인에 실패했어요.
+ 로그아웃했어요.
+ 다시 로그인해주세요.
+ 세션이 만료되어 다시 로그인해주세요.
+ 시스템 오류로 다시 로그인해주세요.
+ 닉네임을 설정해주세요.
+ EAT-SSU 수신 동의 (%1$s)
+ EAT-SSU 수신 거절 (%1$s)
+ 오픈소스 라이브러리를 불러올 수 없어요.
+ 회원탈퇴되었어요.
+ 회원탈퇴에 실패했어요.
+
+
+
+ 오늘의 식사는 어땠나요?
+ 추천하고 싶은 메뉴가 있나요?
+ 메뉴에 대한 상세한 리뷰를 작성해주세요
+ 화면을 준비하는 중입니다.
+ 작성 중...
+ 수정 중...
+ 사진 클릭 시, 삭제됩니다.
+ 사진 %1$d/%2$d
+ 리뷰 설정
+ 에러가 발생했습니다.
+ 리뷰 작성하기
+ 리뷰 등록하기
+ 리뷰 수정하기
+ 한 줄 평 작성하기
+ 리뷰 등록 후, 사진은 수정할 수 없습니다
+
+
+
+ 리뷰
내 리뷰
- 비밀번호 변경
- 공지사항
- 로그아웃
- 회원탈퇴
- 앱 버전
- 영문자과 숫자를 포함하여 8자 이상을 입력해주세요.
- 완료하기
- 스낵코너
+ 아직 작성한 리뷰가 없어요
+ 첫 리뷰를 남겨 주세요!
+ 가장 먼저 리뷰를 남겨 주세요
+ 메뉴에 가장 먼저 리뷰를 남겨주세요!
+ 총 리뷰 수
+
+
+
+
+ %1$d점
+
+
+
+
오늘의 메뉴
- 푸드코트
- THE KITCHEN
- 학생식당
+ 메뉴
가격
평점
- 도담식당
- 기숙사 식당
- 기숙사 식당
- 메뉴와 관련없는 내용
- 음란성, 욕설 등 부적절한 내용
- 부적절한 홍보 또는 광고
- 리뷰 작성 취지에 맞지 않은 내용 (복사글 등)
- 저작권 도용 의심 (사진 등)
- 기타 (하단 내용 작성)
- 리뷰 작성하기
맛
양
- 신고
+ 학생식당
- 수정하기
- 리뷰를 수정하시겠습니까?
- 리뷰가 수정되었습니다.
- 리뷰 수정이 실패하였습니다.
- 리뷰 수정을 취소하시겠습니까?
+
+
+
+ 확인하고 싶은 식당을 선택하세요.
+ 선택하기
+ 아침
+ 점심
+ 저녁
+ 오늘의 메뉴가 없습니다.
+ 로딩 중
+ 네트워크 연결 상태를 확인해주세요.
+ 설정 필요
+ 위젯 설정에서 식당을 선택해주세요.
+
+
+
+
+ 마이페이지
+ 내 정보
+ 로그아웃
+ 회원탈퇴
+ 앱 버전
+ 푸시 알림 설정
+ 매일 오전 11시에 알림을 보내드려요
- 리뷰삭제
- 리뷰를 삭제하시겠습니까?
- 리뷰가 삭제 되었습니다.
- 리뷰 삭제가 실패 하였습니다.
- 리뷰 삭제를 취소하시겠습니까?
+
+ 언어 설정
+ 사용할 언어를 선택하세요.
+ 시스템 언어 (기본값)
- 작성한 리뷰
- 아직 작성한 리뷰가 없어요
- 첫 리뷰를 남겨 주세요!
- 가장 먼저 리뷰를 남겨 주세요
- back
+
+
+
카카오
연결된 계정
- 환영합니다!
- 서비스 이용을 위해 로그인 해주세요
-
- 정보를 불러올 수 없어요.
-
+
+
+
닉네임
닉네임을 설정해주세요
%1$d~%2$d글자를 입력해주세요.
사용 가능한 닉네임입니다!
- 이미 사용 중인 닉네임입니다.
- EAT SSU에게 보내기
- 식당 위치
- 비고
- 영업 시간
- 리뷰 수정하기
- 다음 단계로
- 5점
- 4점
- 총 리뷰 수
- 3점
- 1점
- 2점
- 리뷰
- 한 줄 평 작성하기
- 리뷰 등록 후, 사진은 수정할 수 없습니다
- 리뷰 등록하기
- 리뷰 수정하기
- 식사는 맛있게 하셨나요?
- 어떤 음식에 대한 리뷰인가요?
+
+
+
+ 닉네임 설정
+ 소속 설정
+
+
+
+
+ 정보를 불러올 수 없어요.
+ 통신 오류
+ 서버와 통신할 수 없습니다.\n잠시 후 다시 시도해주세요.
+
+
+
+ 신고
리뷰를 신고하는 이유를 선택해주세요.
하나의 리뷰에 대해 24시간 내 한 번만 신고 가능합니다.
리뷰 신고 사유를 작성해 주세요
- 메뉴
+
+
+
+ 최대 150자
- EAT SSU
- 숭실대에서 먹자!
- \'를 추천하시겠어요?
- 최대 300자
- 최대 150
- 최대 500자
- 스토어 앱 버전
+
+
+
+ 숭실대에서 먹자!
+ 숭실대에서
+ 먹자
+
+
+
+
+
문의하기
서비스 이용약관
개인정보 처리방침
-
- 문의할 내용을 남겨주세요.
- 이메일
- 답변받을 이메일 주소를 남겨주세요.
- 문의 내용
- 전송하기
- 문의 전송이 완료되었습니다. 답변은 영업일 기준 2~3일 소요됩니다.
- 문의 전송이 실패하였습니다. 다시 시도 해주세요.
-
개인정보 처리방침
서비스 이용약관
문의하기
+ 만든 사람들
+ 오픈소스 라이브러리
+
+
+
정말 탈퇴하시겠습니까?
작성한 리뷰는 삭제되지 않으며, (알수없음)으로 표시됩니다.\n자세한 내용은 서비스 이용약관 및 개인정보처리방침을 확인해 주세요.
닉네임을 입력해주세요
- 만든 사람들
- TEAM EAT\-SSU
-
+
+
+
🤔 오늘 밥 뭐 먹지
오늘의 학식을 확인해보세요!
+ 점심시간 전 알림
+ 점심시간 전, 푸시알림을 발송합니다.
+ 서버가 보낸 알림
+ 잇슈 서버가 보낸 알림을 표시합니다.
+
+
+
+ 학과 입력하기
+ 학과를 입력하고\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
-
- 오픈소스 라이브러리
- 학식
- 지도
- 마이
- 마이페이지
-
- 내 정보
- 저장하기
- 학과 입력하기
- 학과를 입력하고\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
+
+
+
+ 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
+
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 f421957c0..0c4e3380e 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/UiText.kt b/core/common/src/main/java/com/eatssu/common/UiText.kt
new file mode 100644
index 000000000..f25e073b9
--- /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 -> ""
+ }
+}
diff --git a/core/common/src/main/java/com/eatssu/common/enums/AppLanguage.kt b/core/common/src/main/java/com/eatssu/common/enums/AppLanguage.kt
new file mode 100644
index 000000000..efe6975b7
--- /dev/null
+++ b/core/common/src/main/java/com/eatssu/common/enums/AppLanguage.kt
@@ -0,0 +1,30 @@
+package com.eatssu.common.enums
+
+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)
+ }
+}
\ No newline at end of file
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 f5580f6fa..56cbc08da 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 0cbe90e64..0e037e7d0 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-en/strings.xml b/core/common/src/main/res/values-en/strings.xml
new file mode 100644
index 000000000..13b01ee62
--- /dev/null
+++ b/core/common/src/main/res/values-en/strings.xml
@@ -0,0 +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
new file mode 100644
index 000000000..22e0ab1b9
--- /dev/null
+++ b/core/common/src/main/res/values-ja/strings.xml
@@ -0,0 +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
new file mode 100644
index 000000000..55ad5f55b
--- /dev/null
+++ b/core/common/src/main/res/values-vi/strings.xml
@@ -0,0 +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
new file mode 100644
index 000000000..582ae5bac
--- /dev/null
+++ b/core/common/src/main/res/values-zh/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ 学生食堂
+ 道担食堂
+ 宿舍食堂
+ FACULTY (教职工专用)
+ 美食广场
+ 小吃角
+ 厨房
+
+
+
+
+ 与菜单无关的内容
+ 淫秽、粗俗或不当内容
+ 不当推广或广告
+ 与评价目的不符的内容(如复制文本)
+ 疑似侵犯版权(如照片)
+ 其他(请在下方说明)
+
+
+
+
+ 学院
+ 院系
+
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 000000000..0ccbadf80
--- /dev/null
+++ b/core/common/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ 학생 식당
+ 도담 식당
+ 기숙사 식당
+ FACULTY (교직원 전용)
+ 푸드 코트
+ 스낵 코너
+ 더 키친
+
+
+
+
+ 메뉴와 관련없는 내용
+ 음란성, 욕설 등 부적절한 내용
+ 부적절한 홍보 또는 광고
+ 리뷰 작성 취지에 맞지 않은 내용 (복사글 등)
+ 저작권 도용 의심 (사진 등)
+ 기타 (하단 내용 작성)
+
+
+
+
+ 단과대
+ 학과
+