From 870b2d3aec7778bcf821303e7ad8e4811b825fe6 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 2 Feb 2024 16:25:51 +0100 Subject: [PATCH] Improve handling of special characters in string resources (#4220) Introduced a function to process and replace certain escaped symbols like '\n', '\t', and '\uXXXX' in the strings extracted from compose string resources. --- .../composeResources/values/strings.xml | 13 +++--- .../compose/resources/StringResources.kt | 40 +++++++++++++++++-- .../compose/resources/ResourceTest.kt | 7 ++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/components/resources/demo/shared/src/commonMain/composeResources/values/strings.xml b/components/resources/demo/shared/src/commonMain/composeResources/values/strings.xml index bdfed65b61..2fa6bad2bc 100644 --- a/components/resources/demo/shared/src/commonMain/composeResources/values/strings.xml +++ b/components/resources/demo/shared/src/commonMain/composeResources/values/strings.xml @@ -1,13 +1,12 @@ Compose Resources App 😊 Hello world! - Lorem ipsum dolor sit amet, - consectetur adipiscing elit. - Donec eget turpis ac sem ultricies consequat. - Hello, %1$s! You have %2$d new messages. + Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit. +Donec eget turpis ac sem ultricies consequat. + Hello, %1$s!\nYou have %2$d new messages. - item 1 - item 2 - item 3 + item \u2605 + item \u2318 + item \u00BD diff --git a/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt b/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt index 1dcebfa830..da3d53eb6c 100644 --- a/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt +++ b/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt @@ -20,7 +20,7 @@ private val SimpleStringFormatRegex = Regex("""%(\d)\$[ds]""") @ExperimentalResourceApi @Immutable class StringResource -@InternalResourceApi constructor(id: String, val key: String, items: Set): Resource(id, items) +@InternalResourceApi constructor(id: String, val key: String, items: Set) : Resource(id, items) private sealed interface StringItem { data class Value(val text: String) : StringItem @@ -53,11 +53,13 @@ private suspend fun getParsedStrings( private suspend fun parseStringXml(path: String, resourceReader: ResourceReader): Map { val nodes = resourceReader.read(path).toXmlElement().childNodes val strings = nodes.getElementsWithName("string").associate { element -> - element.getAttribute("name") to StringItem.Value(element.textContent.orEmpty()) + val rawString = element.textContent.orEmpty() + element.getAttribute("name") to StringItem.Value(handleSpecialCharacters(rawString)) } val arrays = nodes.getElementsWithName("string-array").associate { arrayElement -> val items = arrayElement.childNodes.getElementsWithName("item").map { element -> - element.textContent.orEmpty() + val rawString = element.textContent.orEmpty() + handleSpecialCharacters(rawString) } arrayElement.getAttribute("name") to StringItem.Array(items) } @@ -203,4 +205,34 @@ private suspend fun loadStringArray( private fun NodeList.getElementsWithName(name: String): List = List(length) { item(it) } .filterIsInstance() - .filter { it.localName == name } \ No newline at end of file + .filter { it.localName == name } + +//https://developer.android.com/guide/topics/resources/string-resource#escaping_quotes +/** + * Replaces + * + * '\n' -> new line + * + * '\t' -> tab + * + * '\uXXXX' -> unicode symbol + * + * '\\' -> '\' + * + * @param string The input string to handle. + * @return The string with special characters replaced according to the logic. + */ +internal fun handleSpecialCharacters(string: String): String { + val unicodeNewLineTabRegex = Regex("""\\u[a-fA-F\d]{4}|\\n|\\t""") + val doubleSlashRegex = Regex("""\\\\""") + val doubleSlashIndexes = doubleSlashRegex.findAll(string).map { it.range.first } + val handledString = unicodeNewLineTabRegex.replace(string) { matchResult -> + if (doubleSlashIndexes.contains(matchResult.range.first - 1)) matchResult.value + else when (matchResult.value) { + "\\n" -> "\n" + "\\t" -> "\t" + else -> matchResult.value.substring(2).toInt(16).toChar().toString() + } + }.replace("""\\""", """\""") + return handledString +} \ No newline at end of file diff --git a/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ResourceTest.kt b/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ResourceTest.kt index f2a516618a..0ea746a0b2 100644 --- a/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ResourceTest.kt +++ b/components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ResourceTest.kt @@ -98,6 +98,13 @@ class ResourceTest { }.message.let { msg -> assertEquals("Resource with ID='ImageResource:test3' has more than one file: en1, en2", msg) } + } + @Test + fun testEscapedSymbols() { + assertEquals( + "abc \n \\n \t \\t \u1234 \ua45f \\u1234 \\ \\u355g", + handleSpecialCharacters("""abc \n \\n \t \\t \u1234 \ua45f \\u1234 \\ \u355g""") + ) } }