16
16
17
17
package com.bq.poeditor.gradle.xml
18
18
19
+ import com.bq.poeditor.gradle.ktx.dumpToString
20
+ import com.bq.poeditor.gradle.ktx.toDocument
19
21
import com.bq.poeditor.gradle.utils.ALL_REGEX_STRING
20
22
import org.w3c.dom.Document
21
23
import org.w3c.dom.Element
@@ -31,6 +33,13 @@ class XmlPostProcessor {
31
33
companion object {
32
34
private val DEFAULT_ENCODING = Charsets .UTF_8
33
35
private val VARIABLE_REGEX = Regex (""" \{\d?\{(.*?)\}\}""" )
36
+
37
+ private const val TAG_RESOURCES = " resources"
38
+ private const val TAG_STRING = " string"
39
+ private const val TAG_PLURALS = " plurals"
40
+ private const val TAG_ITEM = " item"
41
+
42
+ private const val ATTR_NAME = " name"
34
43
}
35
44
36
45
/* *
@@ -40,17 +49,29 @@ class XmlPostProcessor {
40
49
* - Format variables and texts to conform to Android strings.xml format
41
50
* - Split to multiple XML files depending on regex matching
42
51
*/
43
- fun postProcessTranslationXml (translationXmlString : String ,
52
+ fun postProcessTranslationXml (translationFileXmlString : String ,
44
53
fileSplitRegexStringList : List <String >): Map <String , Document > =
45
- splitTranslationXml(formatTranslationXml(translationXmlString), fileSplitRegexStringList)
54
+ splitTranslationXml(formatTranslationXml(translationFileXmlString), fileSplitRegexStringList)
55
+
56
+ /* *
57
+ * Formats a given translations XML string to conform to Android strings.xml format.
58
+ */
59
+ fun formatTranslationXml (translationFileXmlString : String ): String {
60
+ // Parse line by line by traversing the original file using DOM
61
+ val translationFileXmlDocument = translationFileXmlString.toDocument()
62
+
63
+ formatTranslationXmlDocument(translationFileXmlDocument, translationFileXmlDocument.childNodes)
64
+
65
+ return translationFileXmlDocument.dumpToString()
66
+ }
46
67
47
68
/* *
48
- * Format variables and texts to conform to Android strings.xml format.
69
+ * Formats a given string to conform to Android strings.xml format.
49
70
*/
50
- fun formatTranslationXml ( translationXmlString : String ): String {
51
- // We need to check for variables to see if we have to escape percent symbols: if we find variables, we have to
52
- // escape them
53
- val containsVariables = translationXmlString .contains(VARIABLE_REGEX )
71
+ fun formatTranslationString ( translationString : String ): String {
72
+ // We need to check for variables to see if we have to escape percent symbols: if we find variables, we have
73
+ // to escape them
74
+ val containsVariables = translationString .contains(VARIABLE_REGEX )
54
75
55
76
val placeholderTransform: (MatchResult ) -> CharSequence = { matchResult ->
56
77
// TODO: if the string has multiple variables but any of them has no order number,
@@ -64,7 +85,7 @@ class XmlPostProcessor {
64
85
}
65
86
}
66
87
67
- return translationXmlString
88
+ return translationString
68
89
// Replace % with %% if variables are found
69
90
.let { if (containsVariables) it.replace(" %" , " %%" ) else it }
70
91
// Replace < with < and > with >
@@ -98,9 +119,9 @@ class XmlPostProcessor {
98
119
nodes.forEach { node ->
99
120
node.parentNode.removeChild(node)
100
121
val copiedNode = (node.cloneNode(true ) as Element ).apply {
101
- val name = getAttribute(" name " )
122
+ val name = getAttribute(ATTR_NAME )
102
123
val nameWithoutRegex = regex.find(name)?.groups?.get(1 )?.value ? : " "
103
- setAttribute(" name " , nameWithoutRegex)
124
+ setAttribute(ATTR_NAME , nameWithoutRegex)
104
125
}
105
126
xmlRecords.adoptNode(copiedNode)
106
127
xmlRecords.firstChild.appendChild(copiedNode)
@@ -113,25 +134,65 @@ class XmlPostProcessor {
113
134
.plus(ALL_REGEX_STRING to translationFileRecords)
114
135
}
115
136
137
+ private fun formatTranslationXmlDocument (document : Document , nodeList : NodeList , rootNode : Node ? = null) {
138
+ for (i in 0 until nodeList.length) {
139
+ if (nodeList.item(i).nodeType == Node .ELEMENT_NODE ) {
140
+ val nodeElement = nodeList.item(i) as Element
141
+ when (nodeElement.tagName) {
142
+ TAG_RESOURCES -> {
143
+ // Main node, traverse its children
144
+ formatTranslationXmlDocument(document, nodeElement.childNodes, nodeElement)
145
+ }
146
+ TAG_PLURALS -> {
147
+ // Plurals node, process its children
148
+ formatTranslationXmlDocument(document, nodeElement.childNodes, nodeElement)
149
+ }
150
+ TAG_STRING -> {
151
+ // String node, apply transformation to the content
152
+ processTextAndReplaceNodeContent(document, nodeElement, rootNode)
153
+ }
154
+ TAG_ITEM -> {
155
+ // Plurals item node, apply transformation to the content
156
+ processTextAndReplaceNodeContent(document, nodeElement, rootNode)
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ private fun processTextAndReplaceNodeContent (document : Document , nodeElement : Element , rootNode : Node ? ) {
164
+ val content = nodeElement.textContent
165
+ val processedContent = formatTranslationString(content)
166
+ val copiedNodeElement = (nodeElement.cloneNode(true ) as Element ).apply {
167
+ textContent = processedContent
168
+ }
169
+ document.adoptNode(copiedNodeElement)
170
+ rootNode?.replaceChild(copiedNodeElement, nodeElement)
171
+ }
172
+
116
173
private fun extractMatchingNodes (nodeList : NodeList , regexString : String ): List <Node > {
117
174
val matchedNodes = mutableListOf<Node >()
118
175
val regex = Regex (regexString)
119
176
120
177
for (i in 0 until nodeList.length) {
121
178
if (nodeList.item(i).nodeType == Node .ELEMENT_NODE ) {
122
179
val nodeElement = nodeList.item(i) as Element
123
- when {
124
- // Main XML node, process children
125
- nodeElement.tagName == " resources " -> {
180
+ when (nodeElement.tagName) {
181
+ TAG_RESOURCES -> {
182
+ // Main XML node, process children
126
183
matchedNodes.addAll(extractMatchingNodes(nodeElement.childNodes, regexString))
127
184
}
128
- // String node, add if name matches regex
129
- nodeElement.tagName == " string" && nodeElement.getAttribute(" name" ).matches(regex) -> {
130
- matchedNodes.add(nodeElement)
185
+ TAG_STRING -> {
186
+ // String node, add node if name matches regex
187
+ if (nodeElement.getAttribute(ATTR_NAME ).matches(regex)) {
188
+ matchedNodes.add(nodeElement)
189
+ }
131
190
}
132
- // Plurals node, add node and children if name matches regex
133
- nodeElement.tagName == " plurals" && nodeElement.getAttribute(" name" ).matches(regex) -> {
134
- matchedNodes.add(nodeElement)
191
+ TAG_PLURALS -> {
192
+ // Plurals node, add node and children if name matches regex
193
+ if (nodeElement.getAttribute(ATTR_NAME ).matches(regex)) {
194
+ matchedNodes.add(nodeElement)
195
+ }
135
196
}
136
197
}
137
198
}
0 commit comments