diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/CatheterizeSheet.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/CatheterizeSheet.kt index d88cad7..1cc0cec 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/CatheterizeSheet.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/CatheterizeSheet.kt @@ -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()) { diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt index 038de4b..80625e2 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt @@ -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)) diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/UrineCameraScreen.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/UrineCameraScreen.kt index 0d5740e..bae1902 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/UrineCameraScreen.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/UrineCameraScreen.kt @@ -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)) diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/VoidingDiaryView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/VoidingDiaryView.kt index 0f710e5..f96c9b3 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/VoidingDiaryView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/VoidingDiaryView.kt @@ -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(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)) } } ) } } diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000..dcfad07 --- /dev/null +++ b/app/src/main/res/values-ko/strings.xml @@ -0,0 +1,71 @@ + + MedilightV2 + + + 확인 + 취소 + 닫기 + 저장 + 삭제 + 다시 시도 + 뒤로 + + + 배뇨 기록 + 입력 방식을 선택하세요 + 수기 + 음성 + 카메라 + 소변량 입력 + 예: 200 + 배뇨일지에 등록되었습니다 + %d ml 등록되었습니다 + 인식 실패: %s + 오류 (%d) + 말씀하세요… + 듣는 중… + 마이크 권한을 확인하세요 + 배뇨일지 + + + 기록이 없습니다 + 기록 삭제 + 이 기록을 삭제하시겠습니까? + 기상 + 취침 + 음성 + 카메라 + 수기 + 기록 + 총 %d건 + 소변량 %d ml + + + 카메라 권한이 필요합니다 + 권한 허용 + 소변량 측정 + 분석 중… + 측정 결과 + 측정 실패 + 밝은 곳에서 다시 촬영해주세요 + 다시 촬영 + 저장하기 + 눈금이 보이는 면을 정면으로 + 밝은 곳에서 촬영해주세요 + + + 저장 완료 + 탭하여 음성 입력 + + + 모델 로딩 중… + 자동 촬영 중… + 유지하세요… %d%% + 소변컵 감지됨 + 소변컵을 찾는 중… + + + 이미지가 너무 어둡습니다 + 눈금 부족 + 감지 실패 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a727e5d..48fad04 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,71 @@ - medilightv2android - \ No newline at end of file + MedilightV2 + + + OK + Cancel + Close + Save + Delete + Retry + Back + + + Voiding Record + Select input method + Manual + Voice + Camera + Enter Volume + e.g. 200 + Registered to voiding diary + %d ml registered + Recognition failed: %s + Error (%d) + Speak now… + Listening… + Check microphone permission + Voiding Diary + + + No records + Delete Record + Delete this record? + Wake up + Bedtime + Voice + Camera + Manual + Record + Total %d + Volume %d ml + + + Camera permission required + Grant Permission + Urine Measurement + Analyzing… + Measurement Result + Measurement Failed + Try again in a brighter area + Retake Photo + Save + Face the graduated side forward + Take photo in a bright area + + + Saved + Tap to speak + + + Loading model… + Auto capturing… + Hold still… %d%% + Cup detected + Searching for cup… + + + Image is too dark + Not enough marks + Detection failed +