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""")
+ )
}
}