diff --git a/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResourcesUtils.kt b/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResourcesUtils.kt index 5013468f5bf..9cc55391d54 100644 --- a/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResourcesUtils.kt +++ b/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResourcesUtils.kt @@ -4,9 +4,30 @@ import org.jetbrains.compose.resources.plural.PluralCategory import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi -private val SimpleStringFormatRegex = Regex("""%(\d+)\$[ds]""") +private val SimpleStringFormatRegex = Regex("""%(\d+)\$(0?)(\d+)?([ds])""") + internal fun String.replaceWithArgs(args: List) = SimpleStringFormatRegex.replace(this) { matchResult -> - args[matchResult.groupValues[1].toInt() - 1] + val index = matchResult.groupValues[1].toInt() - 1 + val flag = matchResult.groupValues[2] + val width = matchResult.groupValues[3].takeIf { it.isNotEmpty() }?.toInt() ?: 0 + val type = matchResult.groupValues[4] + + val value = args[index] + + when (type) { + "d" -> { + when(flag) { + "0" -> { + value.padStart(width, '0') + } + else -> { + value.padStart(width) + } + } + } + "s" -> value.padStart(width) + else -> value + } } internal sealed interface StringItem { diff --git a/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/StringFormatTest.kt b/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/StringFormatTest.kt index a326e84e7b8..87c5197b858 100644 --- a/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/StringFormatTest.kt +++ b/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/StringFormatTest.kt @@ -180,4 +180,86 @@ class StringFormatTest { // Only the first argument should be used, ignoring the rest assertEquals("Hello Alice!", result) } + + @Test + fun `replaceWithArgs work with width flag for d`() { + val template = "%1\$5d" + val args = listOf("42", "65") + + val result = template.replaceWithArgs(args) + + assertEquals(" 42", result) + } + + @Test + fun `replaceWithArgs work with over width flag for d`() { + val template = "%1\$5d" + val args = listOf("999999") + + val result = template.replaceWithArgs(args) + + assertEquals("999999", result) + } + + @Test + fun `replaceWithArgs work with width flag for s`() { + val template = "%1\$5s" + val args = listOf("qwer") + + val result = template.replaceWithArgs(args) + + assertEquals(" qwer", result) + } + + @Test + fun `replaceWithArgs work with over width flag for s`() { + val template = "%1\$5s" + val args = listOf("qwerasdf") + + val result = template.replaceWithArgs(args) + + assertEquals("qwerasdf", result) + } + + @Test + fun `replaceWithArgs work with width and zero flag for d`() { + val template = "%1\$02d : %2\$02d" + val args = listOf("6", "9") + + val result = template.replaceWithArgs(args) + + assertEquals("06 : 09", result) + } + + @Test + fun `replaceWithArgs work with over width and zero flag for d`() { + val template = "%1\$02d : %2\$02d" + val args = listOf("666", "999") + + val result = template.replaceWithArgs(args) + + assertEquals("666 : 999", result) + } + + @Test + fun `replaceWithArgs work with width and zero flag for s`() { + val template = "%1\$02s : %2\$02s" + val args = listOf("6", "9") + + val result = template.replaceWithArgs(args) + + //Not Effect Zero flag on s + assertEquals(" 6 : 9", result) + } + + @Test + fun `replaceWithArgs work with over width and zero flag for s`() { + val template = "%1\$02s : %2\$02s" + val args = listOf("666", "999") + + val result = template.replaceWithArgs(args) + + //Not Effect Zero flag on s + assertEquals("666 : 999", result) + } } \ No newline at end of file