diff --git a/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt b/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt index 15b08af..1cadcf7 100644 --- a/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt +++ b/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt @@ -52,6 +52,8 @@ import dev.kaizen.app.net.SessionViewModel import dev.kaizen.app.ui.theme.Amber import dev.kaizen.app.ui.theme.AmberDeep import dev.kaizen.app.ui.theme.OnAmber +import androidx.compose.ui.res.stringResource +import dev.kaizen.app.R import kotlinx.coroutines.launch /** @@ -64,6 +66,7 @@ import kotlinx.coroutines.launch fun LoginScreen(session: SessionViewModel, modifier: Modifier = Modifier) { val cs = MaterialTheme.colorScheme val scope = rememberCoroutineScope() + val ctx = androidx.compose.ui.platform.LocalContext.current var baseUrl by remember { mutableStateOf(DEFAULT_BASE_URL) } var email by remember { mutableStateOf("") } @@ -81,9 +84,9 @@ fun LoginScreen(session: SessionViewModel, modifier: Modifier = Modifier) { scope.launch { when (val result = session.login(baseUrl, email, password, deviceName)) { is LoginResult.Success -> Unit // session.config flips → MainActivity routes to chat - LoginResult.InvalidCredentials -> error = "E-Mail oder Passwort ist falsch." - LoginResult.RateLimited -> error = "Zu viele Versuche. Bitte kurz warten." - is LoginResult.Error -> error = "Verbindung fehlgeschlagen. Server-Adresse prüfen." + LoginResult.InvalidCredentials -> error = ctx.getString(R.string.login_error_credentials) + LoginResult.RateLimited -> error = ctx.getString(R.string.login_error_rate_limited) + is LoginResult.Error -> error = ctx.getString(R.string.login_error_connection) } loading = false } @@ -102,10 +105,10 @@ fun LoginScreen(session: SessionViewModel, modifier: Modifier = Modifier) { Spacer(Modifier.size(64.dp)) KaizenOrb(96.dp) Spacer(Modifier.size(24.dp)) - Text("Willkommen", color = cs.onBackground, fontSize = 30.sp, fontWeight = FontWeight.Bold) + Text(stringResource(R.string.login_welcome), color = cs.onBackground, fontSize = 30.sp, fontWeight = FontWeight.Bold) Spacer(Modifier.size(6.dp)) Text( - "Melde dich bei deiner Kaizen-Instanz an", + stringResource(R.string.login_subtitle), color = cs.onSurfaceVariant.copy(alpha = 0.8f), fontSize = 14.sp, ) @@ -115,7 +118,7 @@ fun LoginScreen(session: SessionViewModel, modifier: Modifier = Modifier) { GlassField( value = baseUrl, onValueChange = { baseUrl = it; error = null }, - placeholder = "https://deine-instanz.de", + placeholder = stringResource(R.string.login_url_placeholder), keyboardType = KeyboardType.Uri, imeAction = ImeAction.Next, ) @@ -123,7 +126,7 @@ fun LoginScreen(session: SessionViewModel, modifier: Modifier = Modifier) { GlassField( value = email, onValueChange = { email = it; error = null }, - placeholder = "E-Mail", + placeholder = stringResource(R.string.login_email_placeholder), keyboardType = KeyboardType.Email, imeAction = ImeAction.Next, ) @@ -131,7 +134,7 @@ fun LoginScreen(session: SessionViewModel, modifier: Modifier = Modifier) { GlassField( value = password, onValueChange = { password = it; error = null }, - placeholder = "Passwort", + placeholder = stringResource(R.string.login_password_placeholder), keyboardType = KeyboardType.Password, imeAction = ImeAction.Done, password = true, @@ -227,7 +230,7 @@ private fun SignInButton(enabled: Boolean, loading: Boolean, onClick: () -> Unit CircularProgressIndicator(color = OnAmber, strokeWidth = 2.4.dp, modifier = Modifier.size(22.dp)) } else { Text( - "Anmelden", + stringResource(R.string.login_submit), color = if (enabled) OnAmber else cs.onSurfaceVariant, fontSize = 16.sp, fontWeight = FontWeight.SemiBold, diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt index 01c70a7..99467f3 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt @@ -67,6 +67,8 @@ import dev.kaizen.app.ui.shape.KaizenShapes import dev.kaizen.app.ui.theme.Amber import dev.kaizen.app.ui.theme.AmberDeep import dev.kaizen.app.ui.theme.OnAmber +import dev.kaizen.app.R +import androidx.compose.ui.res.stringResource import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.Locale @@ -122,7 +124,6 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M now.format(DateTimeFormatter.ofPattern("EEE, d. MMMM · HH:mm", Locale.GERMAN)) } - // DESIGN.md §11.1: no suggestion chips on mobile — redundant with mode pills Column( modifier = modifier .fillMaxSize() @@ -135,7 +136,7 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M Spacer(Modifier.height(24.dp)) Text("${greeting(now.hour)}, $userName", color = cs.onSurfaceVariant, fontSize = 17.sp) Spacer(Modifier.height(6.dp)) - Text("Was liegt an?", color = cs.onBackground, fontSize = 32.sp, fontWeight = FontWeight.Bold) + Text(stringResource(R.string.hero_headline), color = cs.onBackground, fontSize = 32.sp, fontWeight = FontWeight.Bold) Spacer(Modifier.height(8.dp)) Text(dateLine, color = cs.onSurfaceVariant.copy(alpha = 0.7f), fontSize = 13.sp) Spacer(Modifier.height(142.dp)) @@ -143,7 +144,7 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M } @Composable -fun ModePillsRow(selected: String, onSelect: (String) -> Unit, modifier: Modifier = Modifier) { +fun ModePillsRow(selected: Int, onSelect: (Int) -> Unit, modifier: Modifier = Modifier) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() @@ -163,7 +164,8 @@ fun ModePillsRow(selected: String, onSelect: (String) -> Unit, modifier: Modifie horizontalArrangement = Arrangement.spacedBy(8.dp), ) { chatModes.forEach { mode -> - val active = mode.label == selected + val label = stringResource(mode.labelRes) + val active = mode.labelRes == selected val backgroundFill = if (active) Amber.copy(alpha = 0.16f) else if (isDark) Color(0x12FFFFFF) else Color(0x82FFFFFF) val borderBrush = if (active) glassBorderActive else glassBorderInactive @@ -173,13 +175,13 @@ fun ModePillsRow(selected: String, onSelect: (String) -> Unit, modifier: Modifie .clip(KaizenShapes.pill) .background(backgroundFill) .border(1.2.dp, borderBrush, KaizenShapes.pill) - .clickable { onSelect(mode.label) } + .clickable { onSelect(mode.labelRes) } .padding(horizontal = 13.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { Icon(mode.icon, null, tint = if (active) Amber else cs.onSurfaceVariant, modifier = Modifier.size(16.dp)) Spacer(Modifier.width(6.dp)) - Text(mode.label, color = if (active) cs.onBackground else cs.onSurfaceVariant, fontSize = 13.sp, fontWeight = FontWeight.Medium) + Text(label, color = if (active) cs.onBackground else cs.onSurfaceVariant, fontSize = 13.sp, fontWeight = FontWeight.Medium) } } } @@ -296,10 +298,10 @@ fun ChatInput( } Row(Modifier.padding(horizontal = 8.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically) { Box(Modifier.size(40.dp).clip(KaizenShapes.circle).clickable(onClick = onAddClick), contentAlignment = Alignment.Center) { - Icon(Icons.Rounded.Add, "Anhang", tint = cs.onSurfaceVariant, modifier = Modifier.size(22.dp)) + Icon(Icons.Rounded.Add, stringResource(R.string.chat_attachment), tint = cs.onSurfaceVariant, modifier = Modifier.size(22.dp)) } Box(Modifier.weight(1f).heightIn(min = 36.dp).padding(horizontal = 4.dp), contentAlignment = Alignment.CenterStart) { - if (value.isEmpty()) Text("Nachricht eingeben …", color = cs.onSurfaceVariant, fontSize = 16.sp) + if (value.isEmpty()) Text(stringResource(R.string.chat_input_placeholder), color = cs.onSurfaceVariant, fontSize = 16.sp) BasicTextField( value = value, onValueChange = onValueChange, textStyle = TextStyle(color = cs.onBackground, fontSize = 16.sp, lineHeight = 22.sp), diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt b/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt index 8a94a3e..6ad8e15 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt @@ -8,7 +8,10 @@ import androidx.compose.material.icons.rounded.Language import androidx.compose.material.icons.rounded.Movie import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.Tune +import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import dev.kaizen.app.R import dev.kaizen.app.net.Attachment enum class Role { User, Assistant } @@ -23,31 +26,30 @@ data class Message( val attachments: List = emptyList(), ) -// Empty-state suggestion pills (mirrors the web hero) -data class Suggestion(val label: String, val prompt: String, val icon: ImageVector) +data class Suggestion(val labelRes: Int, val promptRes: Int, val icon: ImageVector) val suggestions = listOf( - Suggestion("Brainstormen", "Lass uns zu einem Thema brainstormen.", Icons.Rounded.AutoAwesome), - Suggestion("Bild generieren", "Generiere ein Bild von einem Bergsee bei Sonnenaufgang.", Icons.Rounded.Image), - Suggestion("Video erstellen", "Erstelle ein kurzes Video von einer Stadt bei Nacht.", Icons.Rounded.Movie), - Suggestion("Web durchsuchen", "Durchsuche das Web nach den neuesten KI-Nachrichten.", Icons.Rounded.Language), + Suggestion(R.string.suggest_brainstorm, R.string.suggest_brainstorm_prompt, Icons.Rounded.AutoAwesome), + Suggestion(R.string.suggest_image, R.string.suggest_image_prompt, Icons.Rounded.Image), + Suggestion(R.string.suggest_video, R.string.suggest_video_prompt, Icons.Rounded.Movie), + Suggestion(R.string.suggest_web, R.string.suggest_web_prompt, Icons.Rounded.Language), ) -// Mode pills above the input (visual only in Phase 0) -data class ChatMode(val label: String, val icon: ImageVector) +data class ChatMode(val labelRes: Int, val icon: ImageVector) val chatModes = listOf( - ChatMode("Standard", Icons.Rounded.Adjust), - ChatMode("Sampling", Icons.Rounded.Tune), - ChatMode("Bild", Icons.Rounded.Image), - ChatMode("Suche", Icons.Rounded.Language), - ChatMode("Video", Icons.Rounded.Movie), - ChatMode("Audio", Icons.Rounded.MusicNote), + ChatMode(R.string.mode_standard, Icons.Rounded.Adjust), + ChatMode(R.string.mode_sampling, Icons.Rounded.Tune), + ChatMode(R.string.mode_image, Icons.Rounded.Image), + ChatMode(R.string.mode_search, Icons.Rounded.Language), + ChatMode(R.string.mode_video, Icons.Rounded.Movie), + ChatMode(R.string.mode_audio, Icons.Rounded.MusicNote), ) +@Composable fun greeting(hour: Int): String = when (hour) { - in 5..11 -> "Guten Morgen" - in 12..17 -> "Guten Tag" - in 18..22 -> "Guten Abend" - else -> "Hallo" + in 5..11 -> stringResource(R.string.greeting_morning) + in 12..17 -> stringResource(R.string.greeting_day) + in 18..22 -> stringResource(R.string.greeting_evening) + else -> stringResource(R.string.greeting_night) } diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt index 980741c..9146d0f 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt @@ -54,6 +54,7 @@ import dev.kaizen.app.ui.effect.GlassSurface import dev.kaizen.app.ui.effect.GlassTiers import dev.kaizen.app.ui.effect.KaizenShadows import dev.kaizen.app.ui.shape.KaizenShapes +import dev.kaizen.app.R import dev.kaizen.app.db.MessageEntity import dev.kaizen.app.db.toEntity import dev.kaizen.app.haptics.rememberHaptics @@ -93,7 +94,7 @@ fun ChatScreen( var input by remember { mutableStateOf("") } var isStreaming by remember { mutableStateOf(false) } var nextId by remember { mutableStateOf(0L) } - var selectedMode by remember { mutableStateOf("Standard") } + var selectedMode by remember { mutableStateOf(R.string.mode_standard) } // Navigation State var currentScreen by remember { mutableStateOf(AppScreen.Chat) } @@ -315,11 +316,11 @@ fun ChatScreen( val idx = messages.indexOfLast { it.id == assistantId } if (idx >= 0) { val existing = messages[idx].content - val errorSuffix = "\n\n⚠ ${chatErrorText(e)}" + val errorSuffix = "\n\n⚠ ${chatErrorText(e, context)}" messages[idx] = messages[idx].copy( streaming = false, thinking = false, - content = if (existing.isNotBlank()) existing + errorSuffix else chatErrorText(e), + content = if (existing.isNotBlank()) existing + errorSuffix else chatErrorText(e, context), ) } } finally { @@ -437,7 +438,7 @@ fun ChatScreen( Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Icon( imageVector = Icons.Rounded.Menu, - contentDescription = "Menü öffnen", + contentDescription = context.getString(R.string.chat_open_menu), tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp) ) @@ -511,11 +512,10 @@ fun ChatScreen( } } -/** Maps a streaming failure to a short, human German message shown in the bubble. */ -private fun chatErrorText(e: Throwable): String = when { - e is ChatHttpException && e.code == 401 -> "Sitzung abgelaufen. Bitte erneut anmelden." - e is ChatHttpException && e.code == 402 -> "Guthaben aufgebraucht." - e is ChatHttpException && e.code == 429 -> "Zu viele Anfragen. Kurz warten." - e is ChatHttpException -> "Fehler vom Server (${e.code})." - else -> "Keine Verbindung zum Server." +private fun chatErrorText(e: Throwable, ctx: android.content.Context): String = when { + e is ChatHttpException && e.code == 401 -> ctx.getString(R.string.error_session_expired) + e is ChatHttpException && e.code == 402 -> ctx.getString(R.string.error_credits_exhausted) + e is ChatHttpException && e.code == 429 -> ctx.getString(R.string.error_rate_limited) + e is ChatHttpException -> ctx.getString(R.string.error_server, e.code) + else -> ctx.getString(R.string.error_no_connection) } diff --git a/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt b/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt index fa83f15..4b9dd5d 100644 --- a/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt +++ b/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt @@ -45,29 +45,33 @@ import dev.kaizen.app.ui.effect.GlassSurface import dev.kaizen.app.ui.effect.GlassTiers import dev.kaizen.app.ui.effect.KaizenShadows import dev.kaizen.app.ui.shape.KaizenShapes +import dev.kaizen.app.R import dev.kaizen.app.ui.theme.Amber +import androidx.compose.ui.res.stringResource import java.time.Instant import java.time.LocalDate import java.time.ZoneId import java.time.format.DateTimeFormatter import java.util.Locale -private fun dateLabel(updatedAt: String?): String { - val instant = runCatching { Instant.parse(updatedAt) }.getOrNull() ?: return "FRÜHER" +private data class DateLabels(val today: String, val yesterday: String, val earlier: String, val pinned: String) + +private fun dateLabel(updatedAt: String?, labels: DateLabels): String { + val instant = runCatching { Instant.parse(updatedAt) }.getOrNull() ?: return labels.earlier val date = instant.atZone(ZoneId.systemDefault()).toLocalDate() val today = LocalDate.now() return when (date) { - today -> "HEUTE" - today.minusDays(1) -> "GESTERN" - else -> date.format(DateTimeFormatter.ofPattern("dd. MMM yyyy", Locale.GERMAN)).uppercase() + today -> labels.today + today.minusDays(1) -> labels.yesterday + else -> date.format(DateTimeFormatter.ofPattern("dd. MMM yyyy", Locale.getDefault())).uppercase() } } -private fun groupConversations(conversations: List): Map> { +private fun groupConversations(conversations: List, labels: DateLabels): Map> { val groups = LinkedHashMap>() - conversations.filter { it.pinned }.takeIf { it.isNotEmpty() }?.let { groups["★ ANGEHEFTET"] = it.toMutableList() } + conversations.filter { it.pinned }.takeIf { it.isNotEmpty() }?.let { groups[labels.pinned] = it.toMutableList() } conversations.filterNot { it.pinned }.forEach { - groups.getOrPut(dateLabel(it.updatedAt)) { mutableListOf() }.add(it) + groups.getOrPut(dateLabel(it.updatedAt, labels)) { mutableListOf() }.add(it) } return groups } @@ -109,7 +113,7 @@ fun KaizenSidebar( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - Text("kaizen", color = cs.onBackground, fontSize = 20.sp, fontWeight = FontWeight.Bold) + Text(stringResource(R.string.sidebar_brand), color = cs.onBackground, fontSize = 20.sp, fontWeight = FontWeight.Bold) Box( modifier = Modifier .size(36.dp) @@ -118,7 +122,7 @@ fun KaizenSidebar( .clickable { onNewChat() }, contentAlignment = Alignment.Center ) { - Icon(Icons.Rounded.Edit, "Neuer Chat", tint = cs.onBackground, modifier = Modifier.size(18.dp)) + Icon(Icons.Rounded.Edit, stringResource(R.string.chat_new), tint = cs.onBackground, modifier = Modifier.size(18.dp)) } } @@ -136,7 +140,7 @@ fun KaizenSidebar( ) { Icon(Icons.Rounded.Search, null, tint = cs.onSurfaceVariant.copy(alpha = 0.6f), modifier = Modifier.size(18.dp)) Spacer(Modifier.width(8.dp)) - Text("Suchen...", color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 14.sp) + Text(stringResource(R.string.chat_search), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 14.sp) } Spacer(Modifier.height(24.dp)) @@ -145,13 +149,19 @@ fun KaizenSidebar( Box(Modifier.weight(1f).fillMaxWidth()) { if (conversations.isEmpty()) { Text( - "Noch keine Chats", + stringResource(R.string.chat_no_conversations), color = cs.onSurfaceVariant.copy(alpha = 0.5f), fontSize = 14.sp, modifier = Modifier.padding(start = 12.dp, top = 12.dp) ) } else { - val grouped = remember(conversations) { groupConversations(conversations) } + val labels = DateLabels( + today = stringResource(R.string.sidebar_today), + yesterday = stringResource(R.string.sidebar_yesterday), + earlier = stringResource(R.string.sidebar_earlier), + pinned = stringResource(R.string.sidebar_pinned), + ) + val grouped = remember(conversations, labels) { groupConversations(conversations, labels) } LazyColumn( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(8.dp) @@ -207,7 +217,7 @@ fun KaizenSidebar( Column(Modifier.weight(1f)) { Text(userName, color = cs.onBackground, fontSize = 15.sp, fontWeight = FontWeight.SemiBold) } - Icon(Icons.Rounded.Settings, "Einstellungen", tint = cs.onSurfaceVariant.copy(alpha = 0.7f), modifier = Modifier.size(20.dp)) + Icon(Icons.Rounded.Settings, stringResource(R.string.sidebar_settings), tint = cs.onSurfaceVariant.copy(alpha = 0.7f), modifier = Modifier.size(20.dp)) } Spacer(Modifier.height(10.dp)) @@ -221,9 +231,9 @@ fun KaizenSidebar( .padding(horizontal = 12.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.AutoMirrored.Rounded.Logout, "Abmelden", tint = cs.onSurfaceVariant.copy(alpha = 0.7f), modifier = Modifier.size(18.dp)) + Icon(Icons.AutoMirrored.Rounded.Logout, stringResource(R.string.sidebar_logout), tint = cs.onSurfaceVariant.copy(alpha = 0.7f), modifier = Modifier.size(18.dp)) Spacer(Modifier.width(10.dp)) - Text("Abmelden", color = cs.onSurfaceVariant.copy(alpha = 0.85f), fontSize = 14.sp, fontWeight = FontWeight.Medium) + Text(stringResource(R.string.sidebar_logout), color = cs.onSurfaceVariant.copy(alpha = 0.85f), fontSize = 14.sp, fontWeight = FontWeight.Medium) } } } @@ -260,7 +270,7 @@ private fun HistoryItemRow(item: ConversationSummary, selected: Boolean, onClick Spacer(Modifier.width(8.dp)) } Text( - text = if (item.locked) "Gesperrter Chat" else item.title, + text = if (item.locked) stringResource(R.string.chat_locked) else item.title, color = if (item.locked) cs.onSurfaceVariant.copy(alpha = 0.5f) else cs.onBackground, fontSize = 14.sp, fontWeight = FontWeight.Medium, @@ -269,7 +279,7 @@ private fun HistoryItemRow(item: ConversationSummary, selected: Boolean, onClick ) if (item.locked) { Spacer(Modifier.width(8.dp)) - Icon(Icons.Rounded.Lock, "Gesperrt", tint = Amber, modifier = Modifier.size(15.dp)) + Icon(Icons.Rounded.Lock, stringResource(R.string.chat_locked_badge), tint = Amber, modifier = Modifier.size(15.dp)) } } } 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 0000000..224869a --- /dev/null +++ b/app/src/main/res/values-en/strings.xml @@ -0,0 +1,94 @@ + + + Good Morning + Good Afternoon + Good Evening + Hello + What\'s up? + + + Type a message … + No chats yet + Locked Chat + Locked + New Chat + Search… + Attachment + Call + Remove + Error + Open menu + + + Session expired. Please sign in again. + Credits exhausted. + Too many requests. Please wait. + Server error (%1$d). + No connection to server. + + + Standard + Sampling + Image + Search + Video + Audio + + + Brainstorm + Let\'s brainstorm on a topic. + Generate Image + Generate an image of a mountain lake at sunrise. + Create Video + Create a short video of a city at night. + Search the Web + Search the web for the latest AI news. + + + kaizen + ★ PINNED + TODAY + YESTERDAY + EARLIER + Sign Out + Settings + + + Select Model + Search models… + Clear + Connecting… + No matches + Favorite + Selected + ★ Favorites + + + Welcome + Sign in to your Kaizen instance + https://your-instance.com + Email + Password + Sign In + Invalid email or password. + Too many attempts. Please wait. + Connection failed. Check server address. + + + Settings + Back + APPEARANCE + PROFILE & ACCOUNT + AI PROFILE & CONTEXT + Accent Color + Choose the app color scheme + Appearance + Set light/dark appearance + Display Name + Change your display name + Email Address + System Prompt & Facts + Light + Dark + System + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a679dc3..b0df167 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,96 @@ Kaizen - \ No newline at end of file + + + Guten Morgen + Guten Tag + Guten Abend + Hallo + Was liegt an? + + + Nachricht eingeben … + Noch keine Chats + Gesperrter Chat + Gesperrt + Neuer Chat + Suchen… + Anhang + Anruf + Entfernen + Fehler + Menü öffnen + + + Sitzung abgelaufen. Bitte erneut anmelden. + Guthaben aufgebraucht. + Zu viele Anfragen. Kurz warten. + Fehler vom Server (%1$d). + Keine Verbindung zum Server. + + + Standard + Sampling + Bild + Suche + Video + Audio + + + Brainstormen + Lass uns zu einem Thema brainstormen. + Bild generieren + Generiere ein Bild von einem Bergsee bei Sonnenaufgang. + Video erstellen + Erstelle ein kurzes Video von einer Stadt bei Nacht. + Web durchsuchen + Durchsuche das Web nach den neuesten KI-Nachrichten. + + + kaizen + ★ ANGEHEFTET + HEUTE + GESTERN + FRÜHER + Abmelden + Einstellungen + + + Modell auswählen + Modell suchen… + Löschen + Verbindung wird hergestellt… + Keine Treffer + Favorit + Ausgewählt + ★ Favoriten + + + Willkommen + Melde dich bei deiner Kaizen-Instanz an + https://deine-instanz.de + E-Mail + Passwort + Anmelden + E-Mail oder Passwort ist falsch. + Zu viele Versuche. Bitte kurz warten. + Verbindung fehlgeschlagen. Server-Adresse prüfen. + + + Einstellungen + Zurück + ERSCHEINUNGSBILD + PROFIL & KONTO + KI-PROFIL & KONTEXT + Akzentfarbe + Wähle das Farbschema der App + Helligkeitsmodus + Passe das Hell/Dunkel-Erscheinungsbild an + Benutzername + Ändere deinen Anzeigenamen + E-Mail-Adresse + System-Prompt & Fakten + Hell + Dunkel + System +