feat: move hardcoded UI strings to strings.xml

Replaces hardcoded string literals in composables with string resource
references for proper i18n support. Adds 20 new strings to strings.xml.

Files updated:
- NewUserMetadataScreen: "Social proof" section title
- NoteCompose: "Approve" community post button
- Badge: " and N others" badge awardees text
- Chess: "Decline"/"Accept" challenge buttons
- InteractiveStory: "Restart" story button
- Poll: "Submit" poll button
- ChessGameScreen: "Back", "Relay Settings", "Loading game...",
  "Game Not Found", "This game may have ended...", "Game ID: ...",
  "Go Back"
- ChessLobbyScreen: "Back", "Relay Settings", "New Game",
  "Dismiss", "Connected"
- chess/NewChessGameButton: "New Chess Game" content description
- home/NewChessGameButton: "New Chess Game" content description
- UserSettingsScreen: "Remove <language>" content description

https://claude.ai/code/session_0151CDmy5k2pEWnxhRxet41h
This commit is contained in:
Claude
2026-03-01 20:44:43 +00:00
parent 16088be597
commit ba1d61d8d9
12 changed files with 56 additions and 23 deletions
@@ -291,7 +291,7 @@ fun NewUserMetadataScreen(
// -- Social Proofs -- // -- Social Proofs --
ExpandableSection( ExpandableSection(
title = "Social proof", title = stringRes(R.string.social_proof),
expanded = socialExpanded, expanded = socialExpanded,
) { ) {
OutlinedTextField( OutlinedTextField(
@@ -670,7 +670,7 @@ fun RenderApproveButton(
accountViewModel.approveCommunityPost(post, community) accountViewModel.approveCommunityPost(post, community)
}, },
) { ) {
Text("Approve") Text(stringRes(R.string.approve))
} }
} }
@@ -172,7 +172,7 @@ fun RenderBadgeAward(
} }
if (awardees.size > 100) { if (awardees.size > 100) {
Text(" and ${awardees.size - 100} others", maxLines = 1) Text(stringRes(R.string.badge_and_n_others, awardees.size - 100), maxLines = 1)
} }
} }
@@ -41,6 +41,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.chess.ChessChallenge import com.vitorpamplona.amethyst.commons.chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.chess.ChessGameViewer import com.vitorpamplona.amethyst.commons.chess.ChessGameViewer
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
@@ -48,6 +49,7 @@ import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelFactory import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelFactory
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew
import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent
@@ -178,7 +180,7 @@ fun RenderLiveChessChallenge(
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
) { ) {
OutlinedButton(onClick = { /* TODO: Decline */ }) { OutlinedButton(onClick = { /* TODO: Decline */ }) {
Text("Decline") Text(stringRes(R.string.chess_decline))
} }
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
@@ -207,7 +209,7 @@ fun RenderLiveChessChallenge(
nav.nav(Route.ChessGame(gameId)) nav.nav(Route.ChessGame(gameId))
}, },
) { ) {
Text("Accept") Text(stringRes(R.string.chess_accept))
} }
} }
} }
@@ -46,7 +46,9 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
@@ -183,7 +185,7 @@ fun RenderInteractiveStory(
OutlinedButton( OutlinedButton(
onClick = onRestart, onClick = onRestart,
) { ) {
Text("Restart") Text(stringRes(R.string.restart))
} }
} }
} }
@@ -77,8 +77,10 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
import com.vitorpamplona.amethyst.ui.note.showCount import com.vitorpamplona.amethyst.ui.note.showCount
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.BigPadding import com.vitorpamplona.amethyst.ui.theme.BigPadding
import com.vitorpamplona.amethyst.ui.theme.Size25dp import com.vitorpamplona.amethyst.ui.theme.Size25dp
import com.vitorpamplona.amethyst.ui.theme.SmallishBorder import com.vitorpamplona.amethyst.ui.theme.SmallishBorder
@@ -392,7 +394,7 @@ private fun ColumnScope.RenderMultiChoiceOptions(
modifier = Modifier.align(Alignment.End), modifier = Modifier.align(Alignment.End),
enabled = multichoice.isNotEmpty(), enabled = multichoice.isNotEmpty(),
) { ) {
Text("Submit") Text(stringRes(R.string.poll_submit))
} }
} }
@@ -68,8 +68,10 @@ import com.vitorpamplona.amethyst.commons.chess.ChessBroadcastBanner
import com.vitorpamplona.amethyst.commons.chess.ChessBroadcastStatus import com.vitorpamplona.amethyst.commons.chess.ChessBroadcastStatus
import com.vitorpamplona.amethyst.commons.chess.ChessSyncBanner import com.vitorpamplona.amethyst.commons.chess.ChessSyncBanner
import com.vitorpamplona.amethyst.commons.chess.LiveChessGameScreen import com.vitorpamplona.amethyst.commons.chess.LiveChessGameScreen
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Height4dpModifier import com.vitorpamplona.amethyst.ui.theme.Height4dpModifier
import com.vitorpamplona.quartz.nip64Chess.ChessGameNameGenerator import com.vitorpamplona.quartz.nip64Chess.ChessGameNameGenerator
@@ -186,7 +188,7 @@ fun ChessGameScreen(
IconButton(onClick = { nav.popBack() }) { IconButton(onClick = { nav.popBack() }) {
Icon( Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack, imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back", contentDescription = stringRes(R.string.back),
) )
} }
}, },
@@ -194,7 +196,7 @@ fun ChessGameScreen(
IconButton(onClick = { showRelaySettings = true }) { IconButton(onClick = { showRelaySettings = true }) {
Icon( Icon(
imageVector = Icons.Default.Settings, imageVector = Icons.Default.Settings,
contentDescription = "Relay Settings", contentDescription = stringRes(R.string.relay_settings),
) )
} }
}, },
@@ -239,7 +241,7 @@ fun ChessGameScreen(
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
CircularProgressIndicator() CircularProgressIndicator()
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text("Loading game...") Text(stringRes(R.string.chess_loading_game))
} }
} }
} }
@@ -256,7 +258,7 @@ fun ChessGameScreen(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
) { ) {
Text( Text(
text = "Game Not Found", text = stringRes(R.string.chess_game_not_found),
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
) )
@@ -265,7 +267,7 @@ fun ChessGameScreen(
// Show specific error if available // Show specific error if available
Text( Text(
text = error ?: "This game may have ended or is waiting for opponent.", text = error ?: stringRes(R.string.chess_game_waiting),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = if (error != null) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant, color = if (error != null) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant,
) )
@@ -273,7 +275,7 @@ fun ChessGameScreen(
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = "Game ID: ${gameId.take(16)}...", text = stringRes(R.string.chess_game_id, gameId.take(16)),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
) )
@@ -283,10 +285,10 @@ fun ChessGameScreen(
Button(onClick = { nav.popBack() }) { Button(onClick = { nav.popBack() }) {
Icon( Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack, imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back", contentDescription = stringRes(R.string.back),
modifier = Modifier.padding(end = 8.dp), modifier = Modifier.padding(end = 8.dp),
) )
Text("Go Back") Text(stringRes(R.string.go_back))
} }
} }
} }
@@ -82,6 +82,7 @@ import com.vitorpamplona.amethyst.commons.chess.PublicGame
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.nip64Chess.LiveChessGameState import com.vitorpamplona.quartz.nip64Chess.LiveChessGameState
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -168,7 +169,7 @@ fun ChessLobbyScreen(
IconButton(onClick = { nav.popBack() }) { IconButton(onClick = { nav.popBack() }) {
Icon( Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack, imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back", contentDescription = stringRes(R.string.back),
) )
} }
}, },
@@ -176,7 +177,7 @@ fun ChessLobbyScreen(
IconButton(onClick = { showRelaySettings = true }) { IconButton(onClick = { showRelaySettings = true }) {
Icon( Icon(
imageVector = Icons.Default.Settings, imageVector = Icons.Default.Settings,
contentDescription = "Relay Settings", contentDescription = stringRes(R.string.relay_settings),
) )
} }
}, },
@@ -186,7 +187,7 @@ fun ChessLobbyScreen(
FloatingActionButton( FloatingActionButton(
onClick = { showNewGameDialog = true }, onClick = { showNewGameDialog = true },
) { ) {
Icon(Icons.Default.Add, contentDescription = "New Game") Icon(Icons.Default.Add, contentDescription = stringRes(R.string.chess_new_game))
} }
}, },
) { paddingValues -> ) { paddingValues ->
@@ -238,7 +239,7 @@ fun ChessLobbyScreen(
) { ) {
Text(errorMsg, color = MaterialTheme.colorScheme.onErrorContainer) Text(errorMsg, color = MaterialTheme.colorScheme.onErrorContainer)
OutlinedButton(onClick = { chessViewModel.clearError() }) { OutlinedButton(onClick = { chessViewModel.clearError() }) {
Text("Dismiss") Text(stringRes(R.string.dismiss))
} }
} }
} }
@@ -730,7 +731,7 @@ private fun RelayRow(
) { ) {
Icon( Icon(
imageVector = Icons.Default.CheckCircle, imageVector = Icons.Default.CheckCircle,
contentDescription = "Connected", contentDescription = stringRes(R.string.connected),
tint = tint =
if (isPreferred) { if (isPreferred) {
MaterialTheme.colorScheme.tertiary MaterialTheme.colorScheme.tertiary
@@ -28,6 +28,8 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size26Modifier import com.vitorpamplona.amethyst.ui.theme.Size26Modifier
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
@@ -45,7 +47,7 @@ fun NewChessGameButton(onClick: () -> Unit) {
) { ) {
Icon( Icon(
imageVector = Icons.Default.Add, imageVector = Icons.Default.Add,
contentDescription = "New Chess Game", contentDescription = stringRes(R.string.new_chess_game),
modifier = Size26Modifier, modifier = Size26Modifier,
tint = Color.White, tint = Color.White,
) )
@@ -25,6 +25,8 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.stringRes
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -57,7 +59,7 @@ fun NewChessGameButton(
) { ) {
Icon( Icon(
imageVector = Icons.Default.Add, imageVector = Icons.Default.Add,
contentDescription = "New Chess Game", contentDescription = stringRes(R.string.new_chess_game),
) )
} }
@@ -131,7 +131,7 @@ fun DontTranslateFromSetting(accountViewModel: AccountViewModel) {
trailingIcon = { trailingIcon = {
Icon( Icon(
imageVector = Icons.Default.Close, imageVector = Icons.Default.Close,
contentDescription = "Remove $languageCode", contentDescription = stringRes(R.string.remove_language, languageCode),
tint = Color.Red, tint = Color.Red,
modifier = Modifier.size(16.dp), modifier = Modifier.size(16.dp),
) )
+20
View File
@@ -1525,4 +1525,24 @@
<string name="confirm">Confirm</string> <string name="confirm">Confirm</string>
<string name="next">Next</string> <string name="next">Next</string>
<string name="add_poll_option_button">Add poll option button</string> <string name="add_poll_option_button">Add poll option button</string>
<string name="back">Back</string>
<string name="go_back">Go Back</string>
<string name="dismiss">Dismiss</string>
<string name="approve">Approve</string>
<string name="connected">Connected</string>
<string name="relay_settings">Relay Settings</string>
<string name="social_proof">Social proof</string>
<string name="poll_submit">Submit</string>
<string name="restart">Restart</string>
<string name="chess_accept">Accept</string>
<string name="chess_decline">Decline</string>
<string name="chess_new_game">New Game</string>
<string name="new_chess_game">New Chess Game</string>
<string name="chess_loading_game">Loading game\u2026</string>
<string name="chess_game_not_found">Game Not Found</string>
<string name="chess_game_waiting">This game may have ended or is waiting for opponent.</string>
<string name="chess_game_id">Game ID: %1$s\u2026</string>
<string name="badge_and_n_others"> and %1$d others</string>
<string name="remove_language">Remove %1$s</string>
</resources> </resources>