Skip to content

Commit

Permalink
Added support for ignoring placeholders order for specific languages
Browse files Browse the repository at this point in the history
  • Loading branch information
marcin-adamczewski committed Dec 6, 2024
1 parent f428c94 commit ead3b90
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,27 @@ class PlaceholdersValidator {

fun validatePlaceholders(
mainFilePlaceholders: PlaceholdersForFile,
translatedFilesPlaceholders: List<PlaceholdersForFile>
translatedFilesPlaceholders: List<PlaceholdersForFile>,
ignoredOrderLanguages: Set<String> = emptySet()
): List<ValidationError> {
val errors = mutableListOf<ValidationError>()

translatedFilesPlaceholders.forEach { placeholdersForFile ->
val ignoreOrder = ignoredOrderLanguages.any {
placeholdersForFile.filePath.endsWith(it)
}

mainFilePlaceholders.placeholders.forEach { (stringKey, referencePlaceholders) ->
if (placeholdersForFile.placeholders.containsKey(stringKey)) { // We don't want to validate not translated strings
val filePlaceholders: List<String> = placeholdersForFile.placeholders[stringKey].orEmpty()
val filePlaceholders: List<String> = placeholdersForFile
.placeholders[stringKey]
.orEmpty()

val placeholdersMatches = translatedPlaceholderMatchWithReference(
filePlaceholders, referencePlaceholders, ignoreOrder
)

if (referencePlaceholders != filePlaceholders) {
if (!placeholdersMatches) {
errors.add(
ValidationError(
placeholders = filePlaceholders,
Expand Down Expand Up @@ -55,6 +66,18 @@ class PlaceholdersValidator {
return stringKeyToPlaceholders
}

private fun translatedPlaceholderMatchWithReference(
translatedPlaceholders: List<String>,
referencePlaceholders: List<String>,
ignoreOrder: Boolean
): Boolean {
return translatedPlaceholders.sortIfNeeded(ignoreOrder) ==
referencePlaceholders.sortIfNeeded(ignoreOrder)
}

private fun List<String>.sortIfNeeded(sort: Boolean): List<String> =
if (sort) toMutableList().sorted() else this

/**
* For plurals we only take the first string from each language as each language
* can have various amount of plurals.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileTree
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty

const val EXTENSION_NAME = "placeholdersValidator"
const val TASK_NAME = "placeholdersValidatorTask"
Expand All @@ -23,12 +23,16 @@ abstract class PlaceholdersValidatorPlugin : Plugin<Project> {
project.tasks.register(TASK_NAME, PlaceholdersValidatorTask::class.java) { task ->
task.resourcesDir.set(extension.resourcesDir)
task.ignorePlurals.set(extension.ignorePlurals)
task.ignoredOrderLanguages.set(extension.ignoredOrderLanguages)
}
}

}

abstract class PlaceholdersValidatorExtension(objects: ObjectFactory) {
val resourcesDir: Property<FileTree> = objects.property(FileTree::class.java)
val ignorePlurals: Property<Boolean> = objects.property(Boolean::class.java)
lateinit var resourcesDir: FileTree
var ignorePlurals: Boolean = false
var ignoredOrderLanguages: SetProperty<String> = objects.setProperty(String::class.java).apply {
set(emptySet<String>())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.FileTree
import org.gradle.api.internal.file.CompositeFileTree
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import java.io.File
Expand All @@ -20,6 +21,8 @@ abstract class PlaceholdersValidatorTask : DefaultTask() {
abstract val resourcesDir: Property<FileTree>
@get:Input
abstract val ignorePlurals: Property<Boolean>
@get:Input
abstract val ignoredOrderLanguages: SetProperty<String>

init {
description = "Validates placeholders from translated strings.xml files"
Expand Down Expand Up @@ -49,7 +52,12 @@ abstract class PlaceholdersValidatorTask : DefaultTask() {
createStringPlaceholdersMap(it, ignorePlurals)
}

val errors = validator.validatePlaceholders(mainFilePlaceholders, translatedFilesPlaceholders)
val errors = validator.validatePlaceholders(
mainFilePlaceholders,
translatedFilesPlaceholders,
ignoredOrderLanguages.get()
)

if (errors.isNotEmpty()) {
val errorMessage = errors.fold("", { acc, error -> acc + error.message })
throw GradleScriptException(errorMessage, Throwable(errorMessage))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ class PlaceholdersValidatorTest {
create()
val parsedXml: Node = XmlParser().parse(StringReader(xmlWithPlaceholders))

val result: Map<String, List<String>> = validator.extractPlaceholdersFromXml(parsedXml, ignorePluralsNode = false)
val result: Map<String, List<String>> = validator.extractPlaceholdersFromXml(
parsedXml, ignorePluralsNode = false
)

val expected = mutableMapOf<String, List<String>>()
expected["1"] = listOf("%1${d}s")
Expand Down Expand Up @@ -74,6 +76,44 @@ class PlaceholdersValidatorTest {
)
}

@Test
fun `given ignoring order for german, when wrong order for portuguese and german, then ignore order only for german`() {
create()

val mainXml = """<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="1">"Two placeholders %1${d}s %2${d}s"</string>
<string name="2">"String: %s"</string>
</resources>
"""

val wrongOrderFileXml = """<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="1">"Two placeholders %2${d}s %1${d}s"</string>
<string name="2">"String: %s"</string>
</resources>
"""
val mainFilePlaceholders = extractPlaceholdersFrom(mainXml)
val wrongOrderFilePlaceholders = extractPlaceholdersFrom(wrongOrderFileXml)

val errors = validator.validatePlaceholders(
PlaceholdersForFile(mainFilePlaceholders, "/values/strings.xml"),
listOf(
PlaceholdersForFile(mainFilePlaceholders, "/values-es/strings.xml"),
PlaceholdersForFile(wrongOrderFilePlaceholders, "/values-de/strings.xml"),
PlaceholdersForFile(wrongOrderFilePlaceholders, "/values-pt/strings.xml")
),
ignoredOrderLanguages = setOf("values-de/strings.xml")
)

errors.first().assertPlaceholderError(
key = "1",
placeholders = listOf("%2${d}s", "%1${d}s"),
shouldBePlaceholders = listOf("%1${d}s", "%2${d}s"),
file = "/values-pt/strings.xml"
)
}

@Test
fun `when plurals ignored, then do not parse plurals`() {
create()
Expand Down

0 comments on commit ead3b90

Please sign in to comment.