i18n: add Korean/English localization via strings.xml

Replace hardcoded Korean UI strings in Piezo, CatheterizeSheet,
VoidingDiary, and UrineCameraScreen with stringResource() calls.
Add values/strings.xml (English) and values-ko/strings.xml (Korean).
System language setting now controls app language.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dwjang
2026-04-13 11:23:58 +09:00
parent 0e186234d2
commit dc61e1d52e
6 changed files with 219 additions and 74 deletions
@@ -14,12 +14,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import com.example.medilightv2android.R
import com.example.medilightv2android.measure.VoidingRecord
import com.example.medilightv2android.measure.VoidingRecordStore
import com.example.medilightv2android.speech.SpeechRecognizerManager
@@ -56,13 +58,13 @@ fun CatheterizeSheet(
VoidingRecordStore.add(context, VoidingRecord(volume = volume, method = method))
onCatheterized()
isSaved = true
Toast.makeText(context, "배뇨일지에 등록되었습니다", Toast.LENGTH_SHORT).show()
Toast.makeText(context, context.getString(R.string.registered_diary), Toast.LENGTH_SHORT).show()
}
fun startVoice() {
if (!hasAudioPerm) { audioPermLauncher.launch(android.Manifest.permission.RECORD_AUDIO); return }
speechManager?.destroy()
isListening = true; voiceStatus = "듣는 중..."
isListening = true; voiceStatus = context.getString(R.string.listening)
speechManager = SpeechRecognizerManager(
context = context,
onResult = { candidates ->
@@ -85,14 +87,14 @@ fun CatheterizeSheet(
volumeText = it.volumeMl.toString()
voiceStatus = "\"${it.rawText}\" → ${it.volumeMl} ml"
saveRecord(it.volumeMl, "voice")
} ?: run { voiceStatus = "인식 실패: ${candidates.firstOrNull() ?: ""}" }
} ?: run { voiceStatus = context.getString(R.string.recognition_fail, candidates.firstOrNull() ?: "") }
},
onError = { code -> isListening = false; voiceStatus = when(code) {
android.speech.SpeechRecognizer.ERROR_NO_MATCH -> "인식된 음성 없음"
android.speech.SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> "시간 초과"
else -> "오류 ($code)"
android.speech.SpeechRecognizer.ERROR_NO_MATCH -> context.getString(R.string.recognition_fail, "")
android.speech.SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> context.getString(R.string.error_code, code)
else -> context.getString(R.string.error_code, code)
}},
onReady = { voiceStatus = "말씀하세요..." }
onReady = { voiceStatus = context.getString(R.string.speak_now) }
)
speechManager?.startListening()
}
@@ -106,7 +108,7 @@ fun CatheterizeSheet(
// 저장 완료 화면
Icon(Icons.Default.CheckCircle, null, Modifier.size(64.dp), tint = Color(0xFF4CAF50))
Spacer(Modifier.height(16.dp))
Text("배뇨일지에 등록되었습니다", fontSize = 18.sp, fontWeight = FontWeight.SemiBold)
Text(stringResource(R.string.registered_diary), fontSize = 18.sp, fontWeight = FontWeight.SemiBold)
if (volumeText.isNotEmpty()) {
Spacer(Modifier.height(8.dp))
Text("$volumeText ml", fontSize = 32.sp, fontWeight = FontWeight.Bold, color = Color(0xFF007AFF))
@@ -116,10 +118,10 @@ fun CatheterizeSheet(
onClick = onDismiss,
Modifier.fillMaxWidth().height(48.dp),
shape = RoundedCornerShape(12.dp)
) { Text("확인") }
) { Text(stringResource(R.string.confirm)) }
} else {
// 입력 모드 선택
Text("배뇨 기록", fontSize = 20.sp, fontWeight = FontWeight.Bold)
Text(stringResource(R.string.voiding_record), fontSize = 20.sp, fontWeight = FontWeight.Bold)
Spacer(Modifier.height(16.dp))
// 3 탭 버튼
@@ -135,7 +137,7 @@ fun CatheterizeSheet(
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(Icons.Default.Edit, null, Modifier.size(20.dp), tint = if (selectedTab == 0) Color.White else Color(0xFFFF9800))
Text("수기", fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = if (selectedTab == 0) Color.White else Color(0xFFFF9800))
Text(stringResource(R.string.manual_input), fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = if (selectedTab == 0) Color.White else Color(0xFFFF9800))
}
}
// 음성
@@ -149,7 +151,7 @@ fun CatheterizeSheet(
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(Icons.Default.SettingsVoice, null, Modifier.size(20.dp), tint = if (selectedTab == 1) Color.White else Color(0xFF007AFF))
Text("음성", fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = if (selectedTab == 1) Color.White else Color(0xFF007AFF))
Text(stringResource(R.string.voice_input), fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = if (selectedTab == 1) Color.White else Color(0xFF007AFF))
}
}
// 카메라
@@ -161,7 +163,7 @@ fun CatheterizeSheet(
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(Icons.Default.CameraAlt, null, Modifier.size(20.dp), tint = Color(0xFF4CAF50))
Text("카메라", fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = Color(0xFF4CAF50))
Text(stringResource(R.string.camera_input), fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = Color(0xFF4CAF50))
}
}
}
@@ -175,7 +177,7 @@ fun CatheterizeSheet(
OutlinedTextField(
value = volumeText,
onValueChange = { volumeText = it.filter { c -> c.isDigit() }.take(4) },
placeholder = { Text("예: 200", color = Color.Gray) },
placeholder = { Text(stringResource(R.string.volume_placeholder), color = Color.Gray) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
singleLine = true,
textStyle = androidx.compose.ui.text.TextStyle(fontSize = 32.sp, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center),
@@ -193,7 +195,7 @@ fun CatheterizeSheet(
modifier = Modifier.fillMaxWidth().height(48.dp),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFFF9800))
) { Text("저장하기", fontWeight = FontWeight.SemiBold) }
) { Text(stringResource(R.string.save_to_diary), fontWeight = FontWeight.SemiBold) }
}
1 -> {
// 음성 입력
@@ -209,7 +211,7 @@ fun CatheterizeSheet(
}
Spacer(Modifier.height(12.dp))
Text(
if (isListening) "듣는 중..." else "탭하여 음성 입력",
if (isListening) stringResource(R.string.listening) else stringResource(R.string.tap_to_speak),
color = Color.Gray, fontSize = 14.sp
)
if (voiceStatus.isNotEmpty()) {
@@ -35,7 +35,9 @@ import com.example.medilightv2android.ui.components.BladdyRiveView
import com.example.medilightv2android.ui.components.BleDisconnectionBanner
import com.example.medilightv2android.ui.components.BleReconnectionState
import com.example.medilightv2android.ui.components.DonutChartView
import com.example.medilightv2android.R
import com.example.medilightv2android.ui.theme.*
import androidx.compose.ui.res.stringResource
@Composable
fun PiezoMonitoringView(appState: AppState) {
@@ -139,23 +141,23 @@ fun PiezoMonitoringView(appState: AppState) {
if (showCatheterizeSheet) {
AlertDialog(
onDismissRequest = { showCatheterizeSheet = false },
title = { Text("배뇨 기록", fontWeight = FontWeight.Bold) },
text = { Text("입력 방식을 선택하세요") },
title = { Text(stringResource(R.string.voiding_record), fontWeight = FontWeight.Bold) },
text = { Text(stringResource(R.string.select_input_method)) },
confirmButton = {},
dismissButton = {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
TextButton(onClick = {
showCatheterizeSheet = false
showManualInput = true
}) { Text("수기") }
}) { Text(stringResource(R.string.manual_input)) }
TextButton(onClick = {
showCatheterizeSheet = false
showVoiceInput = true
}) { Text("음성") }
}) { Text(stringResource(R.string.voice_input)) }
TextButton(onClick = {
showCatheterizeSheet = false
showUrineCamera = true
}) { Text("카메라") }
}) { Text(stringResource(R.string.camera_input)) }
}
}
)
@@ -166,13 +168,13 @@ fun PiezoMonitoringView(appState: AppState) {
var volumeText by remember { mutableStateOf(appState.estimatedVolume.toString()) }
AlertDialog(
onDismissRequest = { showManualInput = false },
title = { Text("소변량 입력") },
title = { Text(stringResource(R.string.volume_input_title)) },
text = {
androidx.compose.foundation.layout.Row(verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = volumeText,
onValueChange = { volumeText = it.filter { c -> c.isDigit() }.take(4) },
placeholder = { Text("예: ${appState.estimatedVolume}") },
placeholder = { Text(stringResource(R.string.volume_placeholder)) },
keyboardOptions = androidx.compose.foundation.text.KeyboardOptions(keyboardType = androidx.compose.ui.text.input.KeyboardType.Number),
singleLine = true,
modifier = Modifier.weight(1f)
@@ -189,12 +191,12 @@ fun PiezoMonitoringView(appState: AppState) {
context, com.example.medilightv2android.measure.VoidingRecord(volume = vol, method = "manual")
)
appState.resetLevel()
android.widget.Toast.makeText(context, "배뇨일지에 등록되었습니다", android.widget.Toast.LENGTH_SHORT).show()
android.widget.Toast.makeText(context, context.getString(R.string.registered_diary), android.widget.Toast.LENGTH_SHORT).show()
showManualInput = false
}
}) { Text("저장") }
}) { Text(stringResource(R.string.save)) }
},
dismissButton = { TextButton(onClick = { showManualInput = false }) { Text("취소") } }
dismissButton = { TextButton(onClick = { showManualInput = false }) { Text(stringResource(R.string.cancel)) } }
)
}
@@ -221,14 +223,14 @@ fun PiezoMonitoringView(appState: AppState) {
context, com.example.medilightv2android.measure.VoidingRecord(volume = parsed.volumeMl, method = "voice")
)
appState.resetLevel()
android.widget.Toast.makeText(context, "${parsed.volumeMl} ml 등록되었습니다", android.widget.Toast.LENGTH_SHORT).show()
android.widget.Toast.makeText(context, context.getString(R.string.registered_ml, parsed.volumeMl), android.widget.Toast.LENGTH_SHORT).show()
showVoiceInput = false
} else {
voiceResult = "인식 실패: ${candidates.firstOrNull() ?: ""}"
voiceResult = context.getString(R.string.recognition_fail, candidates.firstOrNull() ?: "")
}
},
onError = { code -> isListening = false; voiceResult = "오류 ($code)" },
onReady = { voiceResult = "말씀하세요..." }
onError = { code -> isListening = false; voiceResult = context.getString(R.string.error_code, code) },
onReady = { voiceResult = context.getString(R.string.speak_now) }
)
speechMgr?.startListening()
}
@@ -236,17 +238,17 @@ fun PiezoMonitoringView(appState: AppState) {
AlertDialog(
onDismissRequest = { speechMgr?.destroy(); showVoiceInput = false },
title = { Text(if (isListening) "듣는 중..." else "음성 입력") },
text = { Text(voiceResult ?: "마이크 권한을 확인하세요") },
title = { Text(stringResource(if (isListening) R.string.listening else R.string.voice_input)) },
text = { Text(voiceResult ?: stringResource(R.string.check_mic_permission)) },
confirmButton = {
if (!isListening && voiceResult != null) {
TextButton(onClick = {
isListening = true; voiceResult = null
speechMgr?.startListening()
}) { Text("다시 시도") }
}) { Text(stringResource(R.string.retry)) }
}
},
dismissButton = { TextButton(onClick = { speechMgr?.destroy(); showVoiceInput = false }) { Text("취소") } }
dismissButton = { TextButton(onClick = { speechMgr?.destroy(); showVoiceInput = false }) { Text(stringResource(R.string.cancel)) } }
)
}
@@ -275,10 +277,10 @@ fun PiezoMonitoringView(appState: AppState) {
Icon(Icons.Default.Home, "Home", tint = MlPrimary, modifier = Modifier.size(if (isCompact) 20.dp else 24.dp))
}
IconButton(onClick = { appState.currentScreen = AppScreen.VOIDING_DIARY }, modifier = Modifier.size(48.dp)) {
Icon(Icons.Default.Description, contentDescription = "배뇨일지", tint = MlPrimary, modifier = Modifier.size(if (isCompact) 20.dp else 24.dp))
Icon(Icons.Default.Description, stringResource(R.string.voiding_diary), tint = MlPrimary, modifier = Modifier.size(if (isCompact) 20.dp else 24.dp))
}
IconButton(onClick = { appState.currentScreen = AppScreen.REMINDER_SETTINGS }, modifier = Modifier.size(48.dp)) {
Icon(Icons.Default.Notifications, contentDescription = "배뇨 알리미", tint = MlPrimary, modifier = Modifier.size(if (isCompact) 20.dp else 24.dp))
Icon(Icons.Default.Notifications, contentDescription = "Reminder", tint = MlPrimary, modifier = Modifier.size(if (isCompact) 20.dp else 24.dp))
}
if (!isCompact) {
Text("Bladder Monitor", fontSize = 20.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(start = 8.dp))
@@ -32,8 +32,10 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import com.example.medilightv2android.R
import com.example.medilightv2android.measure.SimpleMeasureService
import com.example.medilightv2android.measure.VoidingRecord
import com.example.medilightv2android.measure.VoidingRecordStore
@@ -149,9 +151,9 @@ fun UrineCameraScreen(
if (!hasCameraPerm) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("카메라 권한이 필요합니다")
Text(stringResource(R.string.camera_permission_needed))
Spacer(Modifier.height(16.dp))
Button(onClick = { permLauncher.launch(Manifest.permission.CAMERA) }) { Text("권한 허용") }
Button(onClick = { permLauncher.launch(Manifest.permission.CAMERA) }) { Text(stringResource(R.string.grant_permission)) }
}
}
} else if (capturedBitmap != null) {
@@ -161,8 +163,8 @@ fun UrineCameraScreen(
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(Modifier.fillMaxWidth(), Arrangement.SpaceBetween, Alignment.CenterVertically) {
IconButton(onClick = onCancel) { Icon(Icons.Default.ArrowBack, "뒤로") }
Text("소변량 측정", fontWeight = FontWeight.SemiBold)
IconButton(onClick = onCancel) { Icon(Icons.Default.ArrowBack, stringResource(R.string.back)) }
Text(stringResource(R.string.urine_measurement), fontWeight = FontWeight.SemiBold)
Spacer(Modifier.size(48.dp))
}
Spacer(Modifier.height(16.dp))
@@ -173,37 +175,37 @@ fun UrineCameraScreen(
Row(verticalAlignment = Alignment.CenterVertically) {
CircularProgressIndicator(Modifier.size(24.dp))
Spacer(Modifier.width(8.dp))
Text("분석 중...", color = Color.Gray)
Text(stringResource(R.string.analyzing), color = Color.Gray)
}
} else if (analysisComplete) {
if (resultVolume != null && resultVolume!! > 0) {
Text("측정 결과", color = Color.Gray, fontSize = 14.sp)
Text(stringResource(R.string.measurement_result), color = Color.Gray, fontSize = 14.sp)
Row(verticalAlignment = Alignment.Bottom) {
Text("$resultVolume", fontSize = 48.sp, fontWeight = FontWeight.Bold, color = Color(0xFF007AFF))
Text(" ml", fontSize = 20.sp, color = Color.Gray, modifier = Modifier.padding(bottom = 8.dp))
}
} else {
Text("측정 실패", fontSize = 20.sp, color = Color.Red)
Text("밝은 곳에서 다시 촬영해주세요", color = Color.Gray, fontSize = 14.sp)
Text(stringResource(R.string.measurement_fail), fontSize = 20.sp, color = Color.Red)
Text(stringResource(R.string.retry_in_bright), color = Color.Gray, fontSize = 14.sp)
}
Spacer(Modifier.height(16.dp))
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp)) {
OutlinedButton(
onClick = { capturedBitmap = null; originalBitmap = null; analysisComplete = false; resultVolume = null; isAutoCapturing = false; detectionStartTime = null; autoCaptureProgress = 0f },
Modifier.weight(1f).height(48.dp), shape = RoundedCornerShape(12.dp)
) { Text("다시 촬영") }
) { Text(stringResource(R.string.retake_photo)) }
Button(
onClick = {
val vol = resultVolume
if (vol != null && vol > 0) {
VoidingRecordStore.add(context, VoidingRecord(volume = vol, method = "camera"))
Toast.makeText(context, "배뇨일지에 등록되었습니다", Toast.LENGTH_SHORT).show()
Toast.makeText(context, context.getString(R.string.registered_diary), Toast.LENGTH_SHORT).show()
}
onDone(vol)
},
Modifier.weight(1f).height(48.dp), shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF4CAF50))
) { Text("저장하기", fontWeight = FontWeight.SemiBold) }
) { Text(stringResource(R.string.save_to_diary), fontWeight = FontWeight.SemiBold) }
}
}
Spacer(Modifier.height(16.dp))
@@ -242,14 +244,14 @@ fun UrineCameraScreen(
// 상단
Row(Modifier.fillMaxWidth().statusBarsPadding().padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onCancel) { Icon(Icons.Default.Close, "닫기", tint = Color.White) }
IconButton(onClick = onCancel) { Icon(Icons.Default.Close, stringResource(R.string.close), tint = Color.White) }
Spacer(Modifier.weight(1f))
val statusText = when {
!isModelLoaded -> "모델 로딩 중..."
isAutoCapturing -> "자동 촬영 중..."
autoCaptureProgress > 0 -> "유지하세요... ${(autoCaptureProgress * 100).toInt()}%"
currentDetection != null -> "소변컵 감지됨"
else -> "소변컵을 찾는 중..."
!isModelLoaded -> stringResource(R.string.model_loading)
isAutoCapturing -> stringResource(R.string.auto_capturing)
autoCaptureProgress > 0 -> stringResource(R.string.hold_still, (autoCaptureProgress * 100).toInt())
currentDetection != null -> stringResource(R.string.cup_detected)
else -> stringResource(R.string.searching_cup)
}
Text(statusText, color = Color.White, fontSize = 12.sp, fontWeight = FontWeight.SemiBold,
modifier = Modifier.background(Color.Black.copy(alpha = 0.6f), RoundedCornerShape(20.dp)).padding(horizontal = 12.dp, vertical = 6.dp))
@@ -258,8 +260,8 @@ fun UrineCameraScreen(
// 가이드
Column(Modifier.align(Alignment.Center).offset(y = 120.dp).background(Color.Black.copy(alpha = 0.5f), RoundedCornerShape(10.dp)).padding(horizontal = 16.dp, vertical = 8.dp), horizontalAlignment = Alignment.CenterHorizontally) {
Text("눈금이 보이는 면을 정면으로", color = Color.White, fontSize = 13.sp, fontWeight = FontWeight.SemiBold)
Text("밝은 곳에서 촬영해주세요", color = Color(0xFFFFD54F), fontSize = 12.sp)
Text(stringResource(R.string.camera_guide_1), color = Color.White, fontSize = 13.sp, fontWeight = FontWeight.SemiBold)
Text(stringResource(R.string.camera_guide_2), color = Color(0xFFFFD54F), fontSize = 12.sp)
if (autoCaptureProgress > 0) {
Spacer(Modifier.height(8.dp))
LinearProgressIndicator(progress = { autoCaptureProgress }, Modifier.fillMaxWidth(0.6f).height(4.dp).clip(RoundedCornerShape(2.dp)), color = Color.Green, trackColor = Color.White.copy(alpha = 0.3f))
@@ -15,10 +15,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.medilightv2android.R
import com.example.medilightv2android.measure.VoidingRecord
import com.example.medilightv2android.measure.VoidingRecordStore
import com.example.medilightv2android.ui.theme.MlCardBackground
@@ -38,8 +40,6 @@ fun VoidingDiaryView(onBack: () -> Unit) {
var showDeleteConfirm by remember { mutableStateOf<String?>(null) }
val dateFormat = remember { SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) }
val displayDateFormat = remember { SimpleDateFormat("M월 d일 (E)", Locale.KOREA) }
val grouped = remember(records) {
records.groupBy { dateFormat.format(Date(it.timestamp)) }
.toSortedMap(compareByDescending { it })
@@ -51,9 +51,9 @@ fun VoidingDiaryView(onBack: () -> Unit) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("배뇨일지", fontWeight = FontWeight.SemiBold) },
title = { Text(stringResource(R.string.voiding_diary), fontWeight = FontWeight.SemiBold) },
navigationIcon = {
IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "뒤로") }
IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back)) }
}
)
}
@@ -63,7 +63,7 @@ fun VoidingDiaryView(onBack: () -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(Icons.Default.Description, contentDescription = null, Modifier.size(48.dp), Color.Gray.copy(alpha = 0.5f))
Spacer(Modifier.height(8.dp))
Text("기록이 없습니다", color = Color.Gray)
Text(stringResource(R.string.no_records), color = Color.Gray)
}
}
} else {
@@ -79,7 +79,7 @@ fun VoidingDiaryView(onBack: () -> Unit) {
item {
val displayDate = try {
val d = dateFormat.parse(date)
displayDateFormat.format(d!!)
SimpleDateFormat("M/d (E)", Locale.getDefault()).format(d!!)
} catch (_: Exception) { date }
Text(displayDate, fontWeight = FontWeight.Bold, fontSize = 15.sp,
@@ -106,21 +106,21 @@ fun VoidingDiaryView(onBack: () -> Unit) {
record.activity == "awake" -> {
Icon(Icons.Default.WbSunny, contentDescription = null, Modifier.size(16.dp), Color(0xFFFF9800))
Spacer(Modifier.width(4.dp))
Text("기상")
Text(stringResource(R.string.awake))
}
record.activity == "sleep" -> {
Icon(Icons.Default.DarkMode, contentDescription = null, Modifier.size(16.dp), Color(0xFF9C27B0))
Spacer(Modifier.width(4.dp))
Text("취침")
Text(stringResource(R.string.sleep))
}
}
Spacer(Modifier.weight(1f))
val methodLabel = when (record.method) {
"voice" -> "음성"
"camera" -> "카메라"
else -> "수기"
"voice" -> stringResource(R.string.method_voice)
"camera" -> stringResource(R.string.method_camera)
else -> stringResource(R.string.method_manual)
}
Text(methodLabel, fontSize = 10.sp, color = Color.Gray,
modifier = Modifier.background(Color.Gray.copy(alpha = 0.15f), RoundedCornerShape(4.dp))
@@ -132,7 +132,7 @@ fun VoidingDiaryView(onBack: () -> Unit) {
onClick = { showDeleteConfirm = record.id },
modifier = Modifier.size(48.dp)
) {
Icon(Icons.Default.Delete, contentDescription = "삭제", Modifier.size(20.dp), Color.Gray)
Icon(Icons.Default.Delete, contentDescription = stringResource(R.string.delete), Modifier.size(20.dp), Color.Gray)
}
}
}
@@ -140,7 +140,7 @@ fun VoidingDiaryView(onBack: () -> Unit) {
item {
Spacer(Modifier.height(16.dp))
Text("총 ${records.size}건", color = Color.Gray, fontSize = 12.sp,
Text(stringResource(R.string.total_records, records.size), color = Color.Gray, fontSize = 12.sp,
modifier = Modifier.fillMaxWidth(), textAlign = androidx.compose.ui.text.style.TextAlign.Center)
}
}
@@ -151,16 +151,16 @@ fun VoidingDiaryView(onBack: () -> Unit) {
showDeleteConfirm?.let { id ->
AlertDialog(
onDismissRequest = { showDeleteConfirm = null },
title = { Text("기록 삭제") },
text = { Text("이 기록을 삭제하시겠습니까?") },
title = { Text(stringResource(R.string.delete_record)) },
text = { Text(stringResource(R.string.delete_confirm_msg)) },
confirmButton = {
TextButton(onClick = {
VoidingRecordStore.delete(context, id)
records = VoidingRecordStore.getAll(context).sortedByDescending { it.timestamp }
showDeleteConfirm = null
}) { Text("삭제", color = Color.Red) }
}) { Text(stringResource(R.string.delete), color = Color.Red) }
},
dismissButton = { TextButton(onClick = { showDeleteConfirm = null }) { Text("취소") } }
dismissButton = { TextButton(onClick = { showDeleteConfirm = null }) { Text(stringResource(R.string.cancel)) } }
)
}
}