Updated on 2026-08-14
This commit is contained in:
commit
233d76d9f4
2 changed files with 69 additions and 6 deletions
|
|
@ -304,14 +304,24 @@ fun TextReference.resolveReference(resources: Resources): String {
|
|||
@Composable
|
||||
fun TextReference.resolveAnnotatedReference(): AnnotatedString {
|
||||
return when (this) {
|
||||
is TextReference.Res -> {
|
||||
is TextReference.Res -> if (formatArgs.hasAnnotatedArgs()) {
|
||||
resolveWithAnnotatedArgs(formatArgs = formatArgs, shouldDecapitalize = shouldDecapitalize) { args ->
|
||||
stringResourceSafe(id = id, *args)
|
||||
}
|
||||
} else {
|
||||
val args = formatArgs.map { if (it is TextReference) it.resolveReference() else it }.toTypedArray()
|
||||
|
||||
formatAnnotated(stringResourceSafe(id = id, *args))
|
||||
}
|
||||
is TextReference.PluralRes -> formatAnnotated(
|
||||
pluralStringResourceSafe(id, count, *formatArgs.toTypedArray()),
|
||||
)
|
||||
is TextReference.PluralRes -> if (formatArgs.hasAnnotatedArgs()) {
|
||||
resolveWithAnnotatedArgs(formatArgs = formatArgs) { args ->
|
||||
pluralStringResourceSafe(id, count, *args)
|
||||
}
|
||||
} else {
|
||||
formatAnnotated(
|
||||
pluralStringResourceSafe(id, count, *formatArgs.toTypedArray()),
|
||||
)
|
||||
}
|
||||
is TextReference.Str -> formatAnnotated(value)
|
||||
is TextReference.Annotated -> value
|
||||
is TextReference.Combined -> buildAnnotatedString {
|
||||
|
|
@ -361,6 +371,60 @@ inline fun TextReference?.isNullOrEmpty(): Boolean {
|
|||
return this == null || this == TextReference.EMPTY
|
||||
}
|
||||
|
||||
private fun WrappedList<Any>.hasAnnotatedArgs(): Boolean {
|
||||
return any { it is AnnotatedString || it is TextReference.Annotated }
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a formatted resource whose [formatArgs] may carry annotations (span styles, inline content).
|
||||
*
|
||||
* Annotated args are substituted with sentinel tokens for the locale-aware formatting pass performed by
|
||||
* [formatTemplate] (so `%d`, positional `%1$s` reordering and `%%` escapes still work) and then spliced back
|
||||
* with their annotations preserved. Literal template text is appended verbatim — markdown is not parsed on
|
||||
* this path.
|
||||
*/
|
||||
@Composable
|
||||
private fun resolveWithAnnotatedArgs(
|
||||
formatArgs: WrappedList<Any>,
|
||||
shouldDecapitalize: Boolean = false,
|
||||
formatTemplate: @Composable (args: Array<Any>) -> String,
|
||||
): AnnotatedString {
|
||||
val annotatedArgs = mutableMapOf<Int, AnnotatedString>()
|
||||
val plainArgs = formatArgs.mapIndexed { index, arg ->
|
||||
val annotated = when (arg) {
|
||||
is AnnotatedString -> arg
|
||||
is TextReference.Annotated -> arg.value
|
||||
else -> null
|
||||
}
|
||||
when {
|
||||
annotated != null -> {
|
||||
annotatedArgs[index] = annotated
|
||||
"$ANNOTATED_ARG_SENTINEL$index$ANNOTATED_ARG_SENTINEL"
|
||||
}
|
||||
arg is TextReference -> arg.resolveReference()
|
||||
else -> arg
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
val formatted = formatTemplate(plainArgs).let { resolved ->
|
||||
if (shouldDecapitalize) resolved.replaceFirstChar { char -> char.lowercase() } else resolved
|
||||
}
|
||||
|
||||
return buildAnnotatedString {
|
||||
var literalStart = 0
|
||||
annotatedArgSentinelRegex.findAll(formatted).forEach { match ->
|
||||
append(formatted.substring(literalStart, match.range.first))
|
||||
annotatedArgs[match.groupValues[1].toInt()]?.let(::append)
|
||||
literalStart = match.range.last + 1
|
||||
}
|
||||
append(formatted.substring(literalStart))
|
||||
}
|
||||
}
|
||||
|
||||
// OBJECT REPLACEMENT CHARACTER — never occurs in resource strings, so it can't clash with formatted output
|
||||
private const val ANNOTATED_ARG_SENTINEL = ''
|
||||
private val annotatedArgSentinelRegex = Regex("(\\d+)")
|
||||
|
||||
@Composable
|
||||
private fun formatAnnotated(rawString: String): AnnotatedString {
|
||||
val markdownDescriptor = rememberMarkdownParser()
|
||||
|
|
|
|||
|
|
@ -111,9 +111,8 @@ private fun FeedListContent(
|
|||
}
|
||||
DateBlock(state.currentDate)
|
||||
|
||||
SpacerH(16.dp)
|
||||
|
||||
if (state.forYouBannerUM is ForYouBannerUM.Content && LocalRedesignEnabled.current) {
|
||||
SpacerH(16.dp)
|
||||
TangemMessageBanner(
|
||||
title = resourceReference(R.string.for_you_title),
|
||||
description = resourceReference(R.string.for_you_description),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue