Skip to content

Commit

Permalink
Fix HTML tags being escaped when parsing. Dotenv support for local de…
Browse files Browse the repository at this point in the history
…velopment
  • Loading branch information
adriangl committed Dec 29, 2020
1 parent 6ef22c8 commit 15ee7a6
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 56 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,7 @@ gradle-app.setting
# End of https://www.gitignore.io/api/java,gradle,intellij+iml

# Output directory for tests
/output
/output

# Donenv configuration file
.env
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- No security issues fixed!
-->

## [Unreleased]
### Added
- No new features!
### Changed
- No changed features!
### Deprecated
- No deprecated features!
### Removed
- No removed features!
### Fixed
- No fixed issues!
### Security
- No security issues fixed!

## [1.4.2] - 2020-12-29
### Added
- [Dotenv support](https://github.com/cdimascio/dotenv-kotlin) for the `Main.kt` file for easier local development.
### Fixed
- Fix HTML tags being escaped when parsing.

## [1.4.1] - 2020-12-28
### Fixed
- Fix percent symbols not being properly escaped (again) by processing the XML file line by line.
Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ dependencies {
implementation("com.squareup.okhttp3:logging-interceptor:4.7.2")
implementation("com.squareup.okhttp3:okhttp:4.7.2")

implementation("io.github.cdimascio:dotenv-kotlin:6.2.2")

testImplementation(gradleTestKit())
testImplementation(kotlin("test"))
testImplementation("junit:junit:4.13")
Expand Down
14 changes: 10 additions & 4 deletions src/main/kotlin/com/bq/poeditor/gradle/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@

package com.bq.poeditor.gradle

import io.github.cdimascio.dotenv.Dotenv

/**
* Only for testing purposes.
*
* Declare the variables API_TOKEN, PROJECT_ID, RES_DIR_PATH and DEFAULT_LANGUAGE in /.env
*/
@Suppress("MagicNumber")
fun main() {
val apiToken = "your_api_token"
val projectId = 1234567890
val resDirPath = "output"
val defaultLanguage = "en"
val dotenv: Dotenv = Dotenv.load()

val apiToken = dotenv.get("API_TOKEN", "")
val projectId = dotenv.get("PROJECT_ID", "-1").toInt()
val resDirPath = dotenv.get("RES_DIR_PATH", "")
val defaultLanguage = dotenv.get("DEFAULT_LANGUAGE", "")

PoEditorStringsImporter.importPoEditorStrings(apiToken, projectId, defaultLanguage, resDirPath)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ fun String.toDocument(): Document =
.parse(this.byteInputStream(DEFAULT_ENCODING))

/**
* Convers a [Document] into a formatted [String].
* Converts a [Document] into a formatted [String].
*/
fun Document.dumpToString(): String {
fun Document.toAndroidXmlString(): String {
val registry = DOMImplementationRegistry.newInstance()
val impl = registry.getDOMImplementation("LS") as DOMImplementationLS
val output = impl.createLSOutput().apply { encoding = "UTF-8" }
Expand All @@ -51,5 +51,5 @@ fun Document.dumpToString(): String {

serializer.write(this, output)

return writer.toString()
return writer.toString().unescapeHtmlTags()
}
24 changes: 24 additions & 0 deletions src/main/kotlin/com/bq/poeditor/gradle/ktx/StringExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2020 BQ
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.bq.poeditor.gradle.ktx

private val UNESCAPED_HTML_TAGS_REGEX = Regex("""<([^.]*?)>""")

/**
* Unescapes HTML tags from string.
*/
fun String.unescapeHtmlTags() = this.replace(UNESCAPED_HTML_TAGS_REGEX, "<$1>")
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.bq.poeditor.gradle.xml

import com.bq.poeditor.gradle.ktx.dumpToString
import com.bq.poeditor.gradle.ktx.toAndroidXmlString
import com.bq.poeditor.gradle.utils.TABLET_REGEX_STRING
import com.bq.poeditor.gradle.utils.TABLET_RES_FOLDER_SUFFIX
import com.bq.poeditor.gradle.utils.logger
Expand Down Expand Up @@ -68,6 +68,6 @@ class AndroidXmlWriter {
}
}

File(stringsFolderFile, "strings.xml").writeText(document.dumpToString())
File(stringsFolderFile, "strings.xml").writeText(document.toAndroidXmlString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

package com.bq.poeditor.gradle.xml

import com.bq.poeditor.gradle.ktx.dumpToString
import com.bq.poeditor.gradle.ktx.toAndroidXmlString
import com.bq.poeditor.gradle.ktx.toDocument
import com.bq.poeditor.gradle.ktx.unescapeHtmlTags
import com.bq.poeditor.gradle.utils.ALL_REGEX_STRING
import org.w3c.dom.Document
import org.w3c.dom.Element
Expand Down Expand Up @@ -62,7 +63,7 @@ class XmlPostProcessor {

formatTranslationXmlDocument(translationFileXmlDocument, translationFileXmlDocument.childNodes)

return translationFileXmlDocument.dumpToString()
return translationFileXmlDocument.toAndroidXmlString()
}

/**
Expand All @@ -88,8 +89,7 @@ class XmlPostProcessor {
return translationString
// Replace % with %% if variables are found
.let { if (containsVariables) it.replace("%", "%%") else it }
// Replace &lt; with < and &gt; with >
.replace("&lt;", "<").replace("&gt;", ">")
.unescapeHtmlTags()
// Replace placeholders from {{variable}} to %1$s format.
.replace(VARIABLE_REGEX, placeholderTransform)
}
Expand Down
106 changes: 64 additions & 42 deletions src/test/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.bq.poeditor.gradle.xml

import com.bq.poeditor.gradle.ktx.dumpToString
import com.bq.poeditor.gradle.ktx.toAndroidXmlString
import com.bq.poeditor.gradle.utils.ALL_REGEX_STRING
import com.bq.poeditor.gradle.utils.TABLET_REGEX_STRING
import org.junit.Assert
Expand Down Expand Up @@ -217,46 +217,6 @@ class XmlPostProcessorTest {
Assert.assertEquals(expectedResult, xmlPostProcessor.formatTranslationXml(inputXmlString))
}

@Test
fun `Splitting tablet translation strings works`() {
// Test complete Xml
val expectedKey = "general_button_goTop"
val inputXmlString = """
<resources>
<string name="$expectedKey">
"$expectedKey"
</string>
<string name="${expectedKey}_tablet">
"${expectedKey}_tablet"
</string>
</resources>
"""

val allRegexString = ALL_REGEX_STRING
val tabletRegexString = TABLET_REGEX_STRING

val splitTranslationXmlMap = xmlPostProcessor.splitTranslationXml(inputXmlString, listOf(tabletRegexString))

// Check XML documents and see if the first string node has the proper name and the proper text with XPath
val xpNamePath = "//resources/string[position()=1]/@name"
val xpTextPath = "//resources/string[position()=1]/text()"

Assert.assertEquals(
expectedKey,
xp.evaluate(xpNamePath, splitTranslationXmlMap.getValue(allRegexString)).trim())
Assert.assertEquals(
expectedKey,
xp.evaluate(xpNamePath, splitTranslationXmlMap.getValue(tabletRegexString)).trim())

Assert.assertEquals(
"\"$expectedKey\"",
xp.evaluate(xpTextPath, splitTranslationXmlMap.getValue(allRegexString)).trim())
Assert.assertEquals(
"\"${expectedKey}_tablet\"",
xp.evaluate(xpTextPath, splitTranslationXmlMap.getValue(tabletRegexString)).trim())

}

@Test
fun `Postprocessing XML with plurals works`() {
// Test complete Xml
Expand Down Expand Up @@ -333,6 +293,68 @@ class XmlPostProcessorTest {
Assert.assertEquals(expectedResult, xmlPostProcessor.formatTranslationXml(inputXmlString))
}

@Test
fun `Postprocessing XML with string HTML symbols works`() {
// Test complete Xml
val inputXmlString = """
<resources>
<string name="hello_friend_bold">
"Hello &lt;b&gt;{{name}}&lt;/b&gt;"
</string>
</resources>
"""

val expectedResult = """
<resources>
<string name="hello_friend_bold">
"Hello <b>%1${'$'}s</b>"
</string>
</resources>
""".formatXml()

Assert.assertEquals(expectedResult, xmlPostProcessor.formatTranslationXml(inputXmlString))
}

@Test
fun `Splitting tablet translation strings works`() {
// Test complete Xml
val expectedKey = "general_button_goTop"
val inputXmlString = """
<resources>
<string name="$expectedKey">
"$expectedKey"
</string>
<string name="${expectedKey}_tablet">
"${expectedKey}_tablet"
</string>
</resources>
"""

val allRegexString = ALL_REGEX_STRING
val tabletRegexString = TABLET_REGEX_STRING

val splitTranslationXmlMap = xmlPostProcessor.splitTranslationXml(inputXmlString, listOf(tabletRegexString))

// Check XML documents and see if the first string node has the proper name and the proper text with XPath
val xpNamePath = "//resources/string[position()=1]/@name"
val xpTextPath = "//resources/string[position()=1]/text()"

Assert.assertEquals(
expectedKey,
xp.evaluate(xpNamePath, splitTranslationXmlMap.getValue(allRegexString)).trim())
Assert.assertEquals(
expectedKey,
xp.evaluate(xpNamePath, splitTranslationXmlMap.getValue(tabletRegexString)).trim())

Assert.assertEquals(
"\"$expectedKey\"",
xp.evaluate(xpTextPath, splitTranslationXmlMap.getValue(allRegexString)).trim())
Assert.assertEquals(
"\"${expectedKey}_tablet\"",
xp.evaluate(xpTextPath, splitTranslationXmlMap.getValue(tabletRegexString)).trim())

}

@Test
fun `Splitting tablet translation strings with plurals works`() {
// Test complete Xml
Expand Down Expand Up @@ -370,5 +392,5 @@ class XmlPostProcessorTest {
DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(this.byteInputStream(Charsets.UTF_8))
.dumpToString()
.toAndroidXmlString()
}

0 comments on commit 15ee7a6

Please sign in to comment.