refactor: make the 40 interpolated UI strings format strings, and assert the argument order
Phase 4, third step, of docs/material-design-conformance.md. `Text("Add chapter to
${uiState.artifact.name}")` becomes a resource holding `Add chapter to %1$s` and a call
passing the expression. 49 call sites. Literals in composables go 76 -> 39;
`stringResource` goes 374 -> 424.
**A silent bug in the previous commit's extractor, found by this one.** Imports were
tested with `statement in source`, and the generated accessors are named after their
strings -- so `import mantra.composeapp.generated.resources.translate` is a *prefix* of
`...resources.translate_into_which_dialect`. The substring test decided the import was
already there, and the compiler reported "Unresolved reference 'translate'" in a file
whose imports looked complete. Both extractors now match whole lines, and the helper
carries the explanation.
**Four filters, each earned by something the dry run got wrong.**
*A template that is only interpolation has nothing to translate.* `Text("$name")` would
have become a resource holding `%1$s` -- longer, slower, and no more localisable than the
code it replaced.
*A leading or trailing space means it is being glued to a neighbour.* " \\u00b7 %1$s" is a
separator. The test has to be on the format string rather than on the literal halves: a
template opening with an interpolation leaves the first part empty and the second starting
with the separating space, which makes "%1$s Key packages" look like a fragment when it is
a whole label.
*`\\uXXXX` and `\\"` are Kotlin syntax, not XML.* Left alone they would have shipped as the
six visible characters of the escape. They are decoded into the resource, which is UTF-8
and can hold `·` directly. `\\n` is **not** decoded, because
StringCatalogueJvmTest shows Compose Resources processes that one and a real newline in an
XML value would be reflowed by the parser.
*A term of a `+` concatenation is still not a string.* Same rule as the plain extractor.
**Three copy problems surfaced only here, because interpolated strings had never been
checked.** `m3-title-case.py` excludes anything containing `$` -- an interpolation is not a
literal -- so `"$count Key Packages"` had been invisible to every pass so far, as had
`"replying To ${…}"`. And a third instance of the old product name, in
`"...once they're on Torch."`. All three fixed. Worth noting as a gap in the checker rather
than a one-off: title case inside a template is still unchecked, and there are 83
concatenation fragments left where it could hide.
**Two new assertions, on the two things a compiler cannot see.** Argument *order* is
decided by where each `${…}` sat, and a transposition compiles and reads plausibly --
"Recovered 3 of 12" against "Recovered 12 of 3" -- so a two-argument and a three-argument
string are asserted end to end. The three-argument one doubles as the check that `·`
was decoded rather than passed through.
**What is deliberately left.** 83 literals that are terms of a `+` concatenation.
Reassembling `"a " + x + " b"` into one format string means deciding what the whole
sentence is, and several are pluralisations -- `(if (n == 2) "event" else "events")` --
which want a real plural resource rather than a format argument, and that is an API choice
rather than a rewrite. `m3-extract-formatted.py --remaining` lists them.
**Tests.** 949 pass, 600 jvm over 73 classes and 349 android over 44, up from 947/598/349.
`:composeApp:compileDebugKotlinAndroid` builds; the debug apk installs and runs on
emulator-5554 through onboarding, the message list and a chat room with its text intact.
`m3-audit.sh --check` exits 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,7 @@ import mantra.composeapp.generated.resources.original_text_markdown
|
||||
import mantra.composeapp.generated.resources.paste_the_chapter_s_markdown_blank_lines
|
||||
import mantra.composeapp.generated.resources.propose_chapter
|
||||
import mantra.composeapp.generated.resources.this_artifact_has_no_version_for_a_chapter
|
||||
import mantra.composeapp.generated.resources.add_chapter_to
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -138,7 +139,7 @@ fun AddChapterScreen(
|
||||
modifier = Modifier.imePadding(),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Add chapter to ${addChapterUIState.artifact.name}") },
|
||||
title = { Text(stringResource(Res.string.add_chapter_to, addChapterUIState.artifact.name)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(
|
||||
|
||||
@@ -43,6 +43,9 @@ import org.jetbrains.compose.resources.stringResource
|
||||
import mantra.composeapp.generated.resources.direct_message_detail
|
||||
import mantra.composeapp.generated.resources.direct_message_functionality_will_be_here
|
||||
import mantra.composeapp.generated.resources.invite
|
||||
import mantra.composeapp.generated.resources.invite_2
|
||||
import mantra.composeapp.generated.resources.once_invited_will_be_able_to_receive_and
|
||||
import mantra.composeapp.generated.resources.to_join_the_chat_room
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -119,7 +122,7 @@ fun AddMemberToChatRoomConfirmationScreen(
|
||||
contentDescription = "Invite new member"
|
||||
)
|
||||
|
||||
Text("Invite ${addMemberToChatRoomConfirmationUIState.profile.humanReadableNameOrPubkey()}")
|
||||
Text(stringResource(Res.string.invite_2, addMemberToChatRoomConfirmationUIState.profile.humanReadableNameOrPubkey()))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
@@ -147,11 +150,11 @@ fun AddMemberToChatRoomConfirmationScreen(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "to join the ${addMemberToChatRoomConfirmationUIState.localChatRoom.chatRoom.subject ?: ""} chat room."
|
||||
text = stringResource(Res.string.to_join_the_chat_room, addMemberToChatRoomConfirmationUIState.localChatRoom.chatRoom.subject ?: "")
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Once invited will be able to receive and send private message sent to all the ${addMemberToChatRoomConfirmationUIState.localChatRoom.localParticipants.size} members in the chat room.",
|
||||
text = stringResource(Res.string.once_invited_will_be_able_to_receive_and, addMemberToChatRoomConfirmationUIState.localChatRoom.localParticipants.size),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ import mantra.composeapp.generated.resources.add_translation
|
||||
import mantra.composeapp.generated.resources.propose_translation
|
||||
import mantra.composeapp.generated.resources.this_artifact_has_no_version_for_a
|
||||
import mantra.composeapp.generated.resources.translate_into_which_dialect
|
||||
import mantra.composeapp.generated.resources.translate
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
@@ -137,7 +138,7 @@ fun AddTranslationArtifactVersionScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Translate ${addTranslationUIState.artifact.name}") },
|
||||
title = { Text(stringResource(Res.string.translate, addTranslationUIState.artifact.name)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(
|
||||
|
||||
@@ -62,6 +62,8 @@ import mantra.composeapp.generated.resources.no_versions
|
||||
import mantra.composeapp.generated.resources.translations
|
||||
import mantra.composeapp.generated.resources.version
|
||||
import mantra.composeapp.generated.resources.versions
|
||||
import mantra.composeapp.generated.resources.chapter
|
||||
import mantra.composeapp.generated.resources.words_characters
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -151,10 +153,10 @@ fun ArtifactDetailScreen(
|
||||
contentDescription = "Chapter"
|
||||
)
|
||||
},
|
||||
overlineContent = { Text("Chapter ${chapter.index}") },
|
||||
overlineContent = { Text(stringResource(Res.string.chapter, chapter.index)) },
|
||||
headlineContent = { Text(chapter.name) },
|
||||
supportingContent = {
|
||||
Text("${chapter.wordCount} words · ${chapter.characterCount} characters")
|
||||
Text(stringResource(Res.string.words_characters, chapter.wordCount, chapter.characterCount))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ import org.jetbrains.compose.resources.stringResource
|
||||
import mantra.composeapp.generated.resources.chapter_detail
|
||||
import mantra.composeapp.generated.resources.no_chunks
|
||||
import mantra.composeapp.generated.resources.original_text
|
||||
import mantra.composeapp.generated.resources.chapter_words_characters
|
||||
import mantra.composeapp.generated.resources.chunk
|
||||
import mantra.composeapp.generated.resources.chunks
|
||||
import mantra.composeapp.generated.resources.words_characters
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -101,7 +105,7 @@ fun ChapterDetailScreen(
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = "Chapter ${chapter.index} · ${chapter.wordCount} words · ${chapter.characterCount} characters",
|
||||
text = stringResource(Res.string.chapter_words_characters, chapter.index, chapter.wordCount, chapter.characterCount),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
@@ -128,7 +132,7 @@ fun ChapterDetailScreen(
|
||||
// Chunks
|
||||
item {
|
||||
Text(
|
||||
text = "Chunks (${chapterDetailUIState.chunks.size})",
|
||||
text = stringResource(Res.string.chunks, chapterDetailUIState.chunks.size),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
@@ -147,10 +151,10 @@ fun ChapterDetailScreen(
|
||||
contentDescription = "Chunk"
|
||||
)
|
||||
},
|
||||
overlineContent = { Text("Chunk ${chunk.index}") },
|
||||
overlineContent = { Text(stringResource(Res.string.chunk, chunk.index)) },
|
||||
headlineContent = { Text(chunk.text) },
|
||||
supportingContent = {
|
||||
Text("${chunk.wordCount} words · ${chunk.characterCount} characters")
|
||||
Text(stringResource(Res.string.words_characters, chunk.wordCount, chunk.characterCount))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ import mantra.composeapp.generated.resources.projects
|
||||
import mantra.composeapp.generated.resources.proposals
|
||||
import mantra.composeapp.generated.resources.reindex_events
|
||||
import mantra.composeapp.generated.resources.shared_key
|
||||
import mantra.composeapp.generated.resources.event_s_still_unreadable
|
||||
import mantra.composeapp.generated.resources.recovered_of_event_s
|
||||
import mantra.composeapp.generated.resources.recovered_of_still_unreadable
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -706,10 +709,10 @@ private fun ReindexMarmotGroupEventsButton(
|
||||
"Nothing to reindex · ${report.stored - report.predatingMembership} " +
|
||||
"event(s) all read$beforeJoining"
|
||||
report.recovered > 0 && report.failed > 0 ->
|
||||
"Recovered ${report.recovered} of ${report.unresolved} · ${report.failed} still unreadable$beforeJoining"
|
||||
stringResource(Res.string.recovered_of_still_unreadable, report.recovered, report.unresolved, report.failed, beforeJoining)
|
||||
report.recovered > 0 ->
|
||||
"Recovered ${report.recovered} of ${report.unresolved} event(s)$beforeJoining"
|
||||
else -> "${report.failed} event(s) still unreadable$beforeJoining"
|
||||
stringResource(Res.string.recovered_of_event_s, report.recovered, report.unresolved, beforeJoining)
|
||||
else -> stringResource(Res.string.event_s_still_unreadable, report.failed, beforeJoining)
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Center
|
||||
|
||||
@@ -64,6 +64,8 @@ import mantra.composeapp.generated.resources.creating_new_chat
|
||||
import mantra.composeapp.generated.resources.direct_message_detail
|
||||
import mantra.composeapp.generated.resources.direct_message_functionality_will_be_here
|
||||
import mantra.composeapp.generated.resources.say_what_now
|
||||
import mantra.composeapp.generated.resources.private_message_to
|
||||
import mantra.composeapp.generated.resources.private_to
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -242,7 +244,7 @@ fun ChatRoomMessagingScreen(
|
||||
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "Private to ${chatMessageListViewModel.nameFor(directMessageRecipient.participantPublicKey)}",
|
||||
text = stringResource(Res.string.private_to, chatMessageListViewModel.nameFor(directMessageRecipient.participantPublicKey)),
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
@@ -284,7 +286,7 @@ fun ChatRoomMessagingScreen(
|
||||
placeholder = {
|
||||
Text(
|
||||
text = if (directMessageRecipient != null) {
|
||||
"Private message to ${chatMessageListViewModel.nameFor(directMessageRecipient.participantPublicKey)}"
|
||||
stringResource(Res.string.private_message_to, chatMessageListViewModel.nameFor(directMessageRecipient.participantPublicKey))
|
||||
} else {
|
||||
stringResource(Res.string.say_what_now)
|
||||
}
|
||||
|
||||
@@ -95,6 +95,10 @@ import mantra.composeapp.generated.resources.the_group_has_a_shared_key
|
||||
import mantra.composeapp.generated.resources.this_is_fixed_once_the_ceremony_runs
|
||||
import mantra.composeapp.generated.resources.try_again
|
||||
import mantra.composeapp.generated.resources.your_share_of_it_is_on_this_device_only_your
|
||||
import mantra.composeapp.generated.resources.everyone_has_to_be_online_at_the_same_time
|
||||
import mantra.composeapp.generated.resources.of
|
||||
import mantra.composeapp.generated.resources.of_members_will_be_needed_to_sign_with_this
|
||||
import mantra.composeapp.generated.resources.shared_key_for
|
||||
|
||||
/**
|
||||
* The shared-key ceremony: a ChillDKG ritual run across the group's NIP-17
|
||||
@@ -148,7 +152,7 @@ fun DkgRitualScreen(
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = "Shared key for ${dkgRitualUIState.localChatRoom.chatRoom.subject ?: "this group"}",
|
||||
text = stringResource(Res.string.shared_key_for, dkgRitualUIState.localChatRoom.chatRoom.subject ?: "this group"),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
@@ -222,7 +226,7 @@ fun DkgRitualScreen(
|
||||
actions = {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = MaterialTheme.spacing.space200),
|
||||
text = "${dkgRitualViewModel.threshold.value} of $participantCount",
|
||||
text = stringResource(Res.string.of, dkgRitualViewModel.threshold.value, participantCount),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
@@ -245,7 +249,7 @@ fun DkgRitualScreen(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Everyone has to be online at the same time — the ceremony can only finish once all $participantCount of you have taken part.",
|
||||
text = stringResource(Res.string.everyone_has_to_be_online_at_the_same_time, participantCount),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
||||
@@ -369,7 +373,7 @@ private fun RitualProgress(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "${session.threshold} of $participantCount members will be needed to sign with this key.",
|
||||
text = stringResource(Res.string.of_members_will_be_needed_to_sign_with_this, session.threshold, participantCount),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
||||
@@ -606,7 +610,7 @@ private fun RitualStep(
|
||||
Text(text = title, style = MaterialTheme.typography.titleSmall)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = "$count of $total",
|
||||
text = stringResource(Res.string.of, count, total),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
@@ -651,7 +655,7 @@ private fun QuorumStepper(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "$threshold of $participantCount",
|
||||
text = stringResource(Res.string.of, threshold, participantCount),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ import mantra.composeapp.generated.resources.ready_to_sign
|
||||
import mantra.composeapp.generated.resources.sign
|
||||
import mantra.composeapp.generated.resources.sign_with_the_group_s_key
|
||||
import mantra.composeapp.generated.resources.signed_their_part
|
||||
import mantra.composeapp.generated.resources.events_signed_together
|
||||
|
||||
/**
|
||||
* One signing session, and the member's decision about it.
|
||||
@@ -352,7 +353,7 @@ private fun WhatIsBeingSigned(events: List<Event>, expected: Int) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.space200)) {
|
||||
if (events.size > 1) {
|
||||
Text(
|
||||
text = "${events.size} events, signed together",
|
||||
text = stringResource(Res.string.events_signed_together, events.size),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import press.mantra.compose.ui.theme.spacing
|
||||
import mantra.composeapp.generated.resources.Res
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import mantra.composeapp.generated.resources.functionality_coming_soon_2
|
||||
|
||||
@Composable
|
||||
fun ImplementationPendingScreen(
|
||||
@@ -30,7 +33,7 @@ fun ImplementationPendingScreen(
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
"\"$text\" functionality coming soon",
|
||||
stringResource(Res.string.functionality_coming_soon_2, text),
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import mantra.composeapp.generated.resources.key_package_management
|
||||
import mantra.composeapp.generated.resources.publish_new_key_package
|
||||
import mantra.composeapp.generated.resources.something_went_wrong
|
||||
import mantra.composeapp.generated.resources.we_couldn_t_find_the_local_profile_please
|
||||
import mantra.composeapp.generated.resources.key_packages
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -135,7 +136,7 @@ fun KeyPackageManagementScreen(
|
||||
}
|
||||
item {
|
||||
Text(
|
||||
"${keyPackageManagementUIState.keyPackageBundles.size} Key Packages"
|
||||
stringResource(Res.string.key_packages, keyPackageManagementUIState.keyPackageBundles.size)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ import mantra.composeapp.generated.resources.proposals
|
||||
import mantra.composeapp.generated.resources.review
|
||||
import mantra.composeapp.generated.resources.this_group_has_not_been_asked_to_sign
|
||||
import mantra.composeapp.generated.resources.waiting_for_you
|
||||
import mantra.composeapp.generated.resources.of_them_could_not_be_read
|
||||
|
||||
/**
|
||||
* Everything the group has asked its shared key to sign.
|
||||
@@ -340,7 +341,7 @@ private fun ProposalCard(
|
||||
// rather than left for the screen behind it.
|
||||
if (proposal.unreadable > 0) {
|
||||
Text(
|
||||
text = "${proposal.unreadable} of them could not be read",
|
||||
text = stringResource(Res.string.of_them_could_not_be_read, proposal.unreadable),
|
||||
color = if (proposal.awaitsYou) {
|
||||
cardContentColor
|
||||
} else {
|
||||
|
||||
@@ -61,6 +61,7 @@ import mantra.composeapp.generated.resources.no_events_were_found
|
||||
import mantra.composeapp.generated.resources.profiles
|
||||
import mantra.composeapp.generated.resources.search
|
||||
import mantra.composeapp.generated.resources.something_went_wrong
|
||||
import mantra.composeapp.generated.resources.searching_for_on
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
@@ -273,7 +274,7 @@ fun SearchResultScreen(
|
||||
modifier = Modifier.height(MaterialTheme.spacing.space600)
|
||||
)
|
||||
Text(
|
||||
text = "Searching for ${searchViewModel.searchResultType.name.lowercase()} on \"${searchQuery.lowercase()}\"",
|
||||
text = stringResource(Res.string.searching_for_on, searchViewModel.searchResultType.name.lowercase(), searchQuery.lowercase()),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(
|
||||
|
||||
@@ -50,6 +50,9 @@ import mantra.composeapp.generated.resources.no_one_selected_yet
|
||||
import mantra.composeapp.generated.resources.no_one_to_add_yet
|
||||
import mantra.composeapp.generated.resources.search_for_people_and_chat_with_them_first
|
||||
import mantra.composeapp.generated.resources.you_can_still_carry_on_and_invite_people
|
||||
import mantra.composeapp.generated.resources.add_people_to
|
||||
import mantra.composeapp.generated.resources.next_with
|
||||
import mantra.composeapp.generated.resources.selected
|
||||
|
||||
/**
|
||||
* Second step of group creation: pick who is in the group.
|
||||
@@ -101,7 +104,7 @@ fun SelectChatRoomMembersScreen(
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = "Add people to $name",
|
||||
text = stringResource(Res.string.add_people_to, name),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
@@ -125,7 +128,7 @@ fun SelectChatRoomMembersScreen(
|
||||
|
||||
Text(
|
||||
text = if (selectedCount > 0) {
|
||||
"Next with $selectedCount"
|
||||
stringResource(Res.string.next_with, selectedCount)
|
||||
} else {
|
||||
stringResource(Res.string.next)
|
||||
}
|
||||
@@ -136,7 +139,7 @@ fun SelectChatRoomMembersScreen(
|
||||
Text(
|
||||
modifier = Modifier.padding(start = MaterialTheme.spacing.space200),
|
||||
text = if (selectedCount > 0) {
|
||||
"$selectedCount selected"
|
||||
stringResource(Res.string.selected, selectedCount)
|
||||
} else {
|
||||
stringResource(Res.string.no_one_selected_yet)
|
||||
},
|
||||
@@ -247,7 +250,7 @@ fun SelectChatRoomMembersScreen(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Add people to $name",
|
||||
text = stringResource(Res.string.add_people_to, name),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
@@ -65,6 +65,11 @@ import mantra.composeapp.generated.resources.just_you_for_now
|
||||
import mantra.composeapp.generated.resources.open_chat
|
||||
import mantra.composeapp.generated.resources.this_decides_who_can_change_the_group_later
|
||||
import mantra.composeapp.generated.resources.you_and_1_other
|
||||
import mantra.composeapp.generated.resources.how_should_be_run
|
||||
import mantra.composeapp.generated.resources.of
|
||||
import mantra.composeapp.generated.resources.was_created_but_couldn_t_be_added_yet_invite
|
||||
import mantra.composeapp.generated.resources.was_created_but_its_shared_key_ceremony
|
||||
import mantra.composeapp.generated.resources.you_and_others
|
||||
|
||||
/**
|
||||
* Last step of group creation: convenient (one admin) or robust (everyone
|
||||
@@ -127,7 +132,7 @@ fun SelectChatRoomTypeScreen(
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = "How should $name be run?",
|
||||
text = stringResource(Res.string.how_should_be_run, name),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
@@ -170,7 +175,7 @@ fun SelectChatRoomTypeScreen(
|
||||
text = when (selectChatRoomTypeUIState.members.size) {
|
||||
0 -> stringResource(Res.string.just_you_for_now)
|
||||
1 -> stringResource(Res.string.you_and_1_other)
|
||||
else -> "You and ${selectChatRoomTypeUIState.members.size} others"
|
||||
else -> stringResource(Res.string.you_and_others, selectChatRoomTypeUIState.members.size)
|
||||
},
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
@@ -196,7 +201,7 @@ fun SelectChatRoomTypeScreen(
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(MaterialTheme.spacing.space200),
|
||||
text = "$name was created, but ${membersNotAdded.joinToString { selectChatRoomTypeViewModel.displayNameFor(it) }} couldn't be added yet. Invite them again from the chat once they're on Torch.",
|
||||
text = stringResource(Res.string.was_created_but_couldn_t_be_added_yet_invite, name, membersNotAdded.joinToString { selectChatRoomTypeViewModel.displayNameFor(it) }),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
@@ -212,7 +217,7 @@ fun SelectChatRoomTypeScreen(
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(MaterialTheme.spacing.space200),
|
||||
text = "$name was created, but its shared key ceremony couldn't be started. Open the chat and start it from the group's details — until then the group has no key of its own.",
|
||||
text = stringResource(Res.string.was_created_but_its_shared_key_ceremony, name),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
@@ -287,7 +292,7 @@ fun SelectChatRoomTypeScreen(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "How should $name be run?",
|
||||
text = stringResource(Res.string.how_should_be_run, name),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
@@ -428,7 +433,7 @@ private fun QuorumPicker(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "$quorum of $adminCount",
|
||||
text = stringResource(Res.string.of, quorum, adminCount),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ import mantra.composeapp.generated.resources.no_chapters
|
||||
import mantra.composeapp.generated.resources.propose
|
||||
import mantra.composeapp.generated.resources.this_group_has_no_shared_key_so_it_cannot
|
||||
import mantra.composeapp.generated.resources.translation_detail
|
||||
import mantra.composeapp.generated.resources.chapter
|
||||
import mantra.composeapp.generated.resources.chunks_translated
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -217,9 +219,9 @@ fun TranslationArtifactVersionDetailScreen(
|
||||
contentDescription = "Chapter"
|
||||
)
|
||||
},
|
||||
headlineContent = { Text("Chapter ${progress.chapter.index}") },
|
||||
headlineContent = { Text(stringResource(Res.string.chapter, progress.chapter.index)) },
|
||||
supportingContent = {
|
||||
Text("${progress.translatedChunks}/${progress.totalChunks} chunks translated")
|
||||
Text(stringResource(Res.string.chunks_translated, progress.translatedChunks, progress.totalChunks))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import mantra.composeapp.generated.resources.type_out_what_you_would_like_to_pub
|
||||
import mantra.composeapp.generated.resources.what_s_your_comment_on_the_below
|
||||
import mantra.composeapp.generated.resources.what_s_your_reply_to_the_above
|
||||
import mantra.composeapp.generated.resources.what_vibrations_do_you_want_to_send_out
|
||||
import mantra.composeapp.generated.resources.replying_to
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@@ -130,7 +131,7 @@ fun WriteNewNoteScreen(
|
||||
bottom = MaterialTheme.spacing.space125,
|
||||
|
||||
),
|
||||
text = "replying To ${writeNewNoteUIState.inReplyToNostrEvent.profile?.humanReadableNameOrPubkey() ?: "note"}",
|
||||
text = stringResource(Res.string.replying_to, writeNewNoteUIState.inReplyToNostrEvent.profile?.humanReadableNameOrPubkey() ?: "note"),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -346,7 +347,7 @@ fun WriteNewNoteScreen(
|
||||
|
||||
if (writeNewNoteUIState.inReplyToNostrEvent != null) {
|
||||
Text(
|
||||
text = "replying To ${writeNewNoteUIState.inReplyToNostrEvent.profile?.humanReadableNameOrPubkey() ?: "note"}",
|
||||
text = stringResource(Res.string.replying_to, writeNewNoteUIState.inReplyToNostrEvent.profile?.humanReadableNameOrPubkey() ?: "note"),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
@@ -32,6 +32,10 @@ import co.touchlab.kermit.Logger
|
||||
import coil3.compose.AsyncImage
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import press.mantra.compose.ui.theme.spacing
|
||||
import mantra.composeapp.generated.resources.Res
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import mantra.composeapp.generated.resources.nostr
|
||||
import mantra.composeapp.generated.resources.nostr_2
|
||||
|
||||
@Composable
|
||||
fun RichContent(
|
||||
@@ -284,7 +288,7 @@ fun RichContent(
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "nostr:${segment.eventId.take(8)}..",
|
||||
text = stringResource(Res.string.nostr, segment.eventId.take(8)),
|
||||
style = style,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
@@ -308,7 +312,7 @@ fun RichContent(
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "nostr:${segment.dTag.take(12)}...",
|
||||
text = stringResource(Res.string.nostr_2, segment.dTag.take(12)),
|
||||
style = style,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ import press.mantra.compose.ui.theme.spacing
|
||||
import mantra.composeapp.generated.resources.Res
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import mantra.composeapp.generated.resources.unsupported_event_kind
|
||||
import mantra.composeapp.generated.resources.unsupported_event_kind_2
|
||||
|
||||
@Composable
|
||||
internal fun UnsupportedKindBadge(kind: Int?, style: TextStyle) {
|
||||
@@ -27,7 +28,7 @@ internal fun UnsupportedKindBadge(kind: Int?, style: TextStyle) {
|
||||
.padding(vertical = MaterialTheme.spacing.space75)
|
||||
) {
|
||||
Text(
|
||||
text = if (kind != null) "Unsupported event kind: $kind" else stringResource(Res.string.unsupported_event_kind),
|
||||
text = if (kind != null) stringResource(Res.string.unsupported_event_kind_2, kind) else stringResource(Res.string.unsupported_event_kind),
|
||||
style = style,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(MaterialTheme.spacing.space175)
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.jetbrains.compose.resources.stringResource
|
||||
import mantra.composeapp.generated.resources.be_the_first_to_comment
|
||||
import mantra.composeapp.generated.resources.post
|
||||
import mantra.composeapp.generated.resources.something_went_wrong
|
||||
import mantra.composeapp.generated.resources.reply_to
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
@@ -138,7 +139,7 @@ fun TextNoteEventDetail(
|
||||
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "Reply to ${localNostrEvent.profile?.humanReadableNameOrPubkey() ?: "the above"}",
|
||||
text = stringResource(Res.string.reply_to, localNostrEvent.profile?.humanReadableNameOrPubkey() ?: "the above"),
|
||||
maxLines = 1,
|
||||
)
|
||||
|
||||
|
||||
@@ -104,6 +104,10 @@ import mantra.composeapp.generated.resources.review
|
||||
import mantra.composeapp.generated.resources.something_went_wrong
|
||||
import mantra.composeapp.generated.resources.waiting_for_your_signature
|
||||
import mantra.composeapp.generated.resources.you
|
||||
import mantra.composeapp.generated.resources.private_to
|
||||
import mantra.composeapp.generated.resources.proposals_are_waiting_for_your_signature
|
||||
import mantra.composeapp.generated.resources.reply_privately_to
|
||||
import mantra.composeapp.generated.resources.sent_a_private_message_to
|
||||
|
||||
class ChatMessageListViewModel(
|
||||
initialChatMessageListUIState: ChatMessageListUIState,
|
||||
@@ -668,7 +672,7 @@ class ChatMessageListViewModel(
|
||||
)
|
||||
Text(
|
||||
text = if (localChatMessage.chatMessage.isUserMessage) {
|
||||
"Private to ${nameFor(localChatMessage.chatMessage.directMessageRecipientPublicKey)}"
|
||||
stringResource(Res.string.private_to, nameFor(localChatMessage.chatMessage.directMessageRecipientPublicKey))
|
||||
} else {
|
||||
stringResource(Res.string.private_to_you)
|
||||
},
|
||||
@@ -733,7 +737,7 @@ class ChatMessageListViewModel(
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text("Reply privately to ${nameFor(localChatMessage.chatMessage.senderPublicKey)}")
|
||||
Text(stringResource(Res.string.reply_privately_to, nameFor(localChatMessage.chatMessage.senderPublicKey)))
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Lock, contentDescription = Decorative)
|
||||
@@ -887,7 +891,7 @@ private fun ProposalsAwaitingYouNotice(
|
||||
text = if (single != null) {
|
||||
stringResource(Res.string.waiting_for_your_signature)
|
||||
} else {
|
||||
"${proposals.size} proposals are waiting for your signature"
|
||||
stringResource(Res.string.proposals_are_waiting_for_your_signature, proposals.size)
|
||||
},
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
@@ -948,7 +952,7 @@ private fun PrivateMessageNotice(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$sender sent a private message to $recipient",
|
||||
text = stringResource(Res.string.sent_a_private_message_to, sender, recipient),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user