From 656872aadb757c08ffb8a2ff6e815acdd8f7cf91 Mon Sep 17 00:00:00 2001 From: Chanjung Kim Date: Wed, 20 Mar 2024 20:07:05 +0900 Subject: [PATCH] Generate Kotlin arrays containing pluralization rules during build --- .../kotlin/GeneratePluralRuleListsTask.kt | 98 +++++++++++++++++++ components/resources/library/build.gradle.kts | 17 +++- 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 components/buildSrc/src/main/kotlin/GeneratePluralRuleListsTask.kt diff --git a/components/buildSrc/src/main/kotlin/GeneratePluralRuleListsTask.kt b/components/buildSrc/src/main/kotlin/GeneratePluralRuleListsTask.kt new file mode 100644 index 0000000000..1357d8d99a --- /dev/null +++ b/components/buildSrc/src/main/kotlin/GeneratePluralRuleListsTask.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2020-2024 JetBrains s.r.o. and respective authors and developers. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +import groovy.util.Node +import groovy.xml.XmlParser +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.* + +/** + * Reads a pluralization rules XML file from Unicode's CLDR and generates a Kotlin file that holds the XML content as + * arrays. This Task is required for quantity string resource support. + */ +@CacheableTask +abstract class GeneratePluralRuleListsTask : DefaultTask() { + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val pluralsFile: RegularFileProperty + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @TaskAction + fun generatePluralRuleLists() { + val outputDir = outputDir.get().asFile + if (outputDir.exists()) { + outputDir.deleteRecursively() + } + outputDir.mkdirs() + + val pluralRuleLists = parsePluralRuleLists() + val pluralRuleListIndexByLocale = pluralRuleLists.flatMapIndexed { idx, pluralRuleList -> + pluralRuleList.locales.map { locale -> + locale to idx + } + } + + val fileContent = """ + package org.jetbrains.compose.resources.intl + + internal val cldrPluralRuleListIndexByLocale = mapOf( + ${pluralRuleListIndexByLocale.joinToString { (locale, idx) -> "\"$locale\" to $idx" }} + ) + + internal val cldrPluralRuleLists = arrayOf( + ${ + pluralRuleLists.joinToString { pluralRuleList -> + """ + arrayOf(${ + pluralRuleList.rules.joinToString { rule -> + "PluralCategory.${rule.count.uppercase()} to \"${rule.rule}\"" + } + }) + """.trimIndent() + } + } + ) + """.trimIndent() + + outputDir.resolve("generated.kt").writeText(fileContent) + } + + private fun parsePluralRuleLists(): List { + val parser = XmlParser(false, false).apply { + setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + setFeature("http://apache.org/xml/features/disallow-doctype-decl", false) + } + val supplementalData = parser.parse(pluralsFile.get().asFile) + val pluralRuleLists = supplementalData.children().filterIsInstance().first { it.name() == "plurals" } + + return pluralRuleLists.children().filterIsInstance().map { pluralRules -> + val locales = pluralRules.attribute("locales").toString().split(' ') + PluralRuleList( + locales, + pluralRules.children().filterIsInstance().map { pluralRule -> + PluralRule( + pluralRule.attribute("count").toString(), + // trim samples as not needed + pluralRule.text().split('@')[0].trim(), + ) + } + ) + } + } +} + +private data class PluralRuleList( + val locales: List, + val rules: List, +) + +private data class PluralRule( + val count: String, + val rule: String, +) \ No newline at end of file diff --git a/components/resources/library/build.gradle.kts b/components/resources/library/build.gradle.kts index 1e563934c5..fe51526074 100644 --- a/components/resources/library/build.gradle.kts +++ b/components/resources/library/build.gradle.kts @@ -1,5 +1,5 @@ import org.jetbrains.compose.ExperimentalComposeLibrary -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl plugins { @@ -65,12 +65,27 @@ kotlin { // │ ┌───┴───┐ │ ┌───┴───┐ // web ios macos desktop android + // For plurals support + val generatePluralRuleListsTask = tasks.register("generatePluralRuleLists") { + pluralsFile = project.layout.projectDirectory.file( + "src/commonMain/kotlin/org/jetbrains/compose/resources/intl/plurals.xml" + ) + outputDir = project.layout.buildDirectory.dir( + "generated/intl/kotlin" + ) + } + + tasks.withType> { + dependsOn(generatePluralRuleListsTask) + } + val commonMain by getting { dependencies { implementation(compose.runtime) implementation(compose.foundation) implementation(libs.kotlinx.coroutines.core) } + kotlin.srcDir(generatePluralRuleListsTask.flatMap { it.outputDir }) } val commonTest by getting { dependencies {