diff --git a/.gitignore b/.gitignore
index d926477..4b1554c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,4 +127,7 @@ gradle-app.setting
# End of https://www.gitignore.io/api/java,gradle,intellij+iml
# Output directory for tests
-/output
\ No newline at end of file
+/output
+
+# Donenv configuration file
+.env
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 705f48a..1b5df93 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/build.gradle.kts b/build.gradle.kts
index dbdc9d8..4b8b331 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -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")
diff --git a/src/main/kotlin/com/bq/poeditor/gradle/Main.kt b/src/main/kotlin/com/bq/poeditor/gradle/Main.kt
index 3b75ee9..6a4d80f 100644
--- a/src/main/kotlin/com/bq/poeditor/gradle/Main.kt
+++ b/src/main/kotlin/com/bq/poeditor/gradle/Main.kt
@@ -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)
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/bq/poeditor/gradle/ktx/DocumentExtensions.kt b/src/main/kotlin/com/bq/poeditor/gradle/ktx/DocumentExtensions.kt
index 8a6632d..d15e30b 100644
--- a/src/main/kotlin/com/bq/poeditor/gradle/ktx/DocumentExtensions.kt
+++ b/src/main/kotlin/com/bq/poeditor/gradle/ktx/DocumentExtensions.kt
@@ -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" }
@@ -51,5 +51,5 @@ fun Document.dumpToString(): String {
serializer.write(this, output)
- return writer.toString()
+ return writer.toString().unescapeHtmlTags()
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/bq/poeditor/gradle/ktx/StringExtensions.kt b/src/main/kotlin/com/bq/poeditor/gradle/ktx/StringExtensions.kt
new file mode 100644
index 0000000..53f07ed
--- /dev/null
+++ b/src/main/kotlin/com/bq/poeditor/gradle/ktx/StringExtensions.kt
@@ -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>")
diff --git a/src/main/kotlin/com/bq/poeditor/gradle/xml/AndroidXmlWriter.kt b/src/main/kotlin/com/bq/poeditor/gradle/xml/AndroidXmlWriter.kt
index db8275f..5ac73db 100644
--- a/src/main/kotlin/com/bq/poeditor/gradle/xml/AndroidXmlWriter.kt
+++ b/src/main/kotlin/com/bq/poeditor/gradle/xml/AndroidXmlWriter.kt
@@ -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
@@ -68,6 +68,6 @@ class AndroidXmlWriter {
}
}
- File(stringsFolderFile, "strings.xml").writeText(document.dumpToString())
+ File(stringsFolderFile, "strings.xml").writeText(document.toAndroidXmlString())
}
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessor.kt b/src/main/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessor.kt
index 260ed88..1d59532 100644
--- a/src/main/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessor.kt
+++ b/src/main/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessor.kt
@@ -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
@@ -62,7 +63,7 @@ class XmlPostProcessor {
formatTranslationXmlDocument(translationFileXmlDocument, translationFileXmlDocument.childNodes)
- return translationFileXmlDocument.dumpToString()
+ return translationFileXmlDocument.toAndroidXmlString()
}
/**
@@ -88,8 +89,7 @@ class XmlPostProcessor {
return translationString
// Replace % with %% if variables are found
.let { if (containsVariables) it.replace("%", "%%") else it }
- // Replace < with < and > with >
- .replace("<", "<").replace(">", ">")
+ .unescapeHtmlTags()
// Replace placeholders from {{variable}} to %1$s format.
.replace(VARIABLE_REGEX, placeholderTransform)
}
diff --git a/src/test/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessorTest.kt b/src/test/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessorTest.kt
index cec3394..722f1b6 100644
--- a/src/test/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessorTest.kt
+++ b/src/test/kotlin/com/bq/poeditor/gradle/xml/XmlPostProcessorTest.kt
@@ -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
@@ -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 = """
-
-
- "$expectedKey"
-
-
- "${expectedKey}_tablet"
-
-
- """
-
- 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
@@ -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 = """
+
+
+ "Hello <b>{{name}}</b>"
+
+
+ """
+
+ val expectedResult = """
+
+
+ "Hello %1${'$'}s"
+
+
+ """.formatXml()
+
+ Assert.assertEquals(expectedResult, xmlPostProcessor.formatTranslationXml(inputXmlString))
+ }
+
+ @Test
+ fun `Splitting tablet translation strings works`() {
+ // Test complete Xml
+ val expectedKey = "general_button_goTop"
+ val inputXmlString = """
+
+
+ "$expectedKey"
+
+
+ "${expectedKey}_tablet"
+
+
+ """
+
+ 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
@@ -370,5 +392,5 @@ class XmlPostProcessorTest {
DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(this.byteInputStream(Charsets.UTF_8))
- .dumpToString()
+ .toAndroidXmlString()
}
\ No newline at end of file