Skip to content

Commit 708690e

Browse files
committed
feat: plurals support Sprintf format + plurals params fixes
1 parent 35ef3ce commit 708690e

27 files changed

Lines changed: 138 additions & 105 deletions

File tree

compose/src/androidMain/kotlin/io/tolgee/ComposeTolgee.android.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,16 @@ fun pluralStringResource(tolgee: Tolgee, @PluralsRes id: Int, quantity: Int): St
103103
val translationFlow = (tolgee as? TolgeeAndroid)?.tPluralFlow(context.resources, id, quantity)
104104
?: (key ?: TolgeeAndroid.getKeyFromResources(context, id))?.let {
105105
tolgee.tFlow(key = it, parameters = TolgeeMessageParams.Indexed(quantity))
106-
} ?: flowOf(androidx.compose.ui.res.pluralStringResource(id, quantity))
106+
} ?: flowOf(androidx.compose.ui.res.pluralStringResource(id, quantity, quantity))
107107

108108
return translationFlow.collectAsState(
109-
initial = androidx.compose.ui.res.pluralStringResource(id, quantity)
109+
initial = androidx.compose.ui.res.pluralStringResource(id, quantity, quantity)
110110
).value
111111
}
112112

113113
@Composable
114114
fun pluralStringResource(@PluralsRes id: Int, quantity: Int): String {
115-
val instance = Tolgee.instanceOrNull ?: return androidx.compose.ui.res.pluralStringResource(id, quantity)
115+
val instance = Tolgee.instanceOrNull ?: return androidx.compose.ui.res.pluralStringResource(id, quantity, quantity)
116116

117117
return pluralStringResource(instance, id, quantity)
118118
}
@@ -127,16 +127,16 @@ fun pluralStringResource(tolgee: Tolgee, @PluralsRes id: Int, quantity: Int, var
127127
val translationFlow = (tolgee as? TolgeeAndroid)?.tPluralFlow(context.resources, id, quantity, *formatArgs)
128128
?: (key ?: TolgeeAndroid.getKeyFromResources(context, id))?.let {
129129
tolgee.tFlow(key = it, parameters = TolgeeMessageParams.Indexed(quantity, *formatArgs))
130-
} ?: flowOf(androidx.compose.ui.res.pluralStringResource(id, quantity, *formatArgs))
130+
} ?: flowOf(androidx.compose.ui.res.pluralStringResource(id, quantity, quantity, *formatArgs))
131131

132132
return translationFlow.collectAsState(
133-
initial = androidx.compose.ui.res.pluralStringResource(id, quantity, *formatArgs)
133+
initial = androidx.compose.ui.res.pluralStringResource(id, quantity, quantity, *formatArgs)
134134
).value
135135
}
136136

137137
@Composable
138138
fun pluralStringResource(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any): String {
139-
val instance = Tolgee.instanceOrNull ?: return androidx.compose.ui.res.pluralStringResource(id, quantity, *formatArgs)
139+
val instance = Tolgee.instanceOrNull ?: return androidx.compose.ui.res.pluralStringResource(id, quantity, quantity, *formatArgs)
140140

141141
return pluralStringResource(instance, id, quantity, *formatArgs)
142142
}

compose/src/commonMain/kotlin/io/tolgee/ComposeTolgee.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,13 @@ fun pluralStringResource(tolgee: Tolgee, resource: PluralStringResource, quantit
9696
key = resource.key,
9797
parameters = TolgeeMessageParams.Indexed(quantity, *formatArgs)
9898
).collectAsState(
99-
initial = org.jetbrains.compose.resources.pluralStringResource(resource, quantity, *formatArgs)
99+
initial = org.jetbrains.compose.resources.pluralStringResource(resource, quantity, quantity, *formatArgs)
100100
).value
101101
}
102102

103103
@Composable
104104
fun pluralStringResource(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String {
105-
val tolgee = Tolgee.instanceOrNull ?: return org.jetbrains.compose.resources.pluralStringResource(resource, quantity, *formatArgs)
105+
val tolgee = Tolgee.instanceOrNull ?: return org.jetbrains.compose.resources.pluralStringResource(resource, quantity, quantity, *formatArgs)
106106
return pluralStringResource(tolgee, resource, quantity, *formatArgs)
107107
}
108108

core/src/androidMain/kotlin/io/tolgee/TolgeeAndroid.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,13 @@ data class TolgeeAndroid internal constructor(
124124
fun tPlural(resources: Resources, @PluralsRes id: Int, quantity: Int): String {
125125
return getKeyFromResources(resources, id)?.let { key ->
126126
t(key, TolgeeMessageParams.Indexed(quantity))
127-
} ?: resources.getQuantityString(id, quantity)
127+
} ?: resources.getQuantityString(id, quantity, quantity)
128128
}
129129

130130
fun tPlural(resources: Resources, @PluralsRes id: Int, quantity: Int, vararg formatArgs: Any): String {
131131
return getKeyFromResources(resources, id)?.let { key ->
132132
t(key, TolgeeMessageParams.Indexed(quantity, *formatArgs))
133-
} ?: resources.getQuantityString(id, quantity, *formatArgs)
133+
} ?: resources.getQuantityString(id, quantity, quantity, *formatArgs)
134134
}
135135

136136
fun tArray(resources: Resources, @ArrayRes id: Int): List<String> {

core/src/commonMain/kotlin/io/tolgee/Tolgee.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ open class Tolgee(
512512
val url: String? = null,
513513
val path: (language: String) -> String = { "$it.json" },
514514
val storage: TolgeeStorageProvider? = platformStorage,
515-
val formatter: Formatter = Formatter.ICU,
515+
val formatter: Formatter = Formatter.Sprintf,
516516
) {
517517
/**
518518
* A builder class for constructing instances of `CDN` with configurable properties.
@@ -566,7 +566,7 @@ open class Tolgee(
566566
* Typically used within the `CDN.Builder` class to configure translation formatting behavior
567567
* for the resulting `CDN` instance.
568568
*/
569-
var formatter: Formatter = Formatter.ICU
569+
var formatter: Formatter = Formatter.Sprintf
570570

571571
/**
572572
* Sets the URL for the CDN configuration.

core/src/commonMain/kotlin/io/tolgee/common/ExtendParser.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ internal fun JsonElement.stringValue(): String = when (this) {
2121

2222
internal fun JsonElement.keyData(): TolgeeKey.Data = when (this) {
2323
is JsonArray -> TolgeeKey.Data.Array(map { it.stringValue() })
24+
is JsonObject -> TolgeeKey.Data.Plural(mapValues { it.value.stringValue() })
2425
else -> TolgeeKey.Data.Text(stringValue())
2526
}
2627

core/src/commonMain/kotlin/io/tolgee/model/TolgeeKey.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ internal data class TolgeeKey(
2121
) {
2222

2323
val isText = translations.values.all { it is Data.Text }
24+
val isPlural = translations.values.any { it is Data.Plural }
25+
val isArray = translations.values.any { it is Data.Array }
2426

2527
/**
2628
* Retrieves a translation for a given language code or the first available non-null translation.
@@ -38,6 +40,9 @@ internal data class TolgeeKey(
3840
@Serializable
3941
data class Text(val text: String) : Data
4042

43+
@Serializable
44+
data class Plural(val plurals: Map<String, String>) : Data
45+
4146
@Serializable
4247
data class Array(val array: List<String>) : Data
4348
}

core/src/commonMain/kotlin/io/tolgee/model/translation/TranslationICU.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ internal data class TranslationICU(
4747
}.groupBy({ it.first }, { it.second })
4848

4949
@Transient
50-
private val stringArrayKeys = keys.filter { !it.isText }
50+
private val stringArrayKeys = keys.filter { it.isArray }
5151

5252
/**
5353
* A collection of message providers grouped by locale. Each provider implements the [MessagesProvider] interface.

core/src/commonMain/kotlin/io/tolgee/model/translation/TranslationSprintf.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package io.tolgee.model.translation
22

33
import de.comahe.i18n4k.Locale
4+
import de.comahe.i18n4k.cldr.plurals.PluralCategory
5+
import de.comahe.i18n4k.cldr.plurals.PluralRule
6+
import de.comahe.i18n4k.cldr.plurals.PluralRuleType
7+
import de.comahe.i18n4k.createLocale
48
import de.comahe.i18n4k.language
59
import io.tolgee.common.sprintf
610
import io.tolgee.model.TolgeeKey
@@ -24,7 +28,7 @@ internal data class TranslationSprintf(
2428
private var usedLocale: Locale?,
2529
) : TolgeeTranslation {
2630

27-
private val stringArrayKeys = keys.filter { !it.isText }
31+
private val stringArrayKeys = keys.filter { it.isArray }
2832

2933
/**
3034
* Retrieves a localized string for the specified key and formatting parameters, considering a given locale.
@@ -52,10 +56,29 @@ internal data class TranslationSprintf(
5256
?: this.usedLocale?.language?.ifBlank { null }
5357
)) {
5458
is TolgeeKey.Data.Text -> return data.text.sprintf(*args)
59+
is TolgeeKey.Data.Plural -> {
60+
val selected = data.plurals[getPluralName(args.getOrNull(0))]
61+
return selected?.sprintf(*args)
62+
}
5563
else -> null
5664
}
5765
}
5866

67+
fun getPluralName(number: Any?): String {
68+
if (number == null) return PluralCategory.OTHER.id
69+
val locale = usedLocale ?: return PluralCategory.OTHER.id
70+
71+
val pluralRule = PluralRule.create(createLocale(locale.language), PluralRuleType.CARDINAL)
72+
73+
val pluralCategory = when (number) {
74+
is Number -> pluralRule?.select(number)
75+
is String -> pluralRule?.select(number)
76+
else -> PluralCategory.OTHER
77+
} ?: PluralCategory.OTHER
78+
79+
return pluralCategory.id
80+
}
81+
5982
/**
6083
* Checks if the specified locale matches the currently used locale or has the same language as the used locale.
6184
*
@@ -70,6 +93,7 @@ internal data class TranslationSprintf(
7093
val foundTolgeeKey = stringArrayKeys.firstOrNull { it.keyName == key } ?: return emptyList()
7194
return when (val data = foundTolgeeKey.translationForOrFirst(locale?.language)) {
7295
is TolgeeKey.Data.Array -> data.array
96+
is TolgeeKey.Data.Plural -> data.plurals.map { it.value }
7397
is TolgeeKey.Data.Text -> listOf(data.text)
7498
else -> emptyList()
7599
}

demo/exampleandroid/src/main/java/io/tolgee/demo/exampleandroid/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class MainActivity : ComponentActivity() {
4949
name.text = getString(R.string.app_name)
5050
basic.text = getString(R.string.description)
5151
parameter.text = getString(R.string.percentage_placeholder, "87")
52-
plural.text = resources.getQuantityString(R.plurals.plr_test_placeholder_2,3, 2, 3, "Plurals")
52+
plural.text = resources.getQuantityString(R.plurals.plr_test_placeholder_2,2, 3, "Plurals")
5353
array.text = resources.getStringArray(R.array.array_test).joinToString()
5454

5555
buttonEn.setOnClickListener {

demo/exampleandroid/src/main/res/values-cs/strings.xml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
<item quantity="other">Množné Ostatní</item>
1616
</plurals>
1717
<plurals name="plr_test_placeholder">
18-
<item quantity="one">%2$s: %d test ok</item>
19-
<item quantity="few">%2$s: %d testy ok</item>
20-
<item quantity="many">%2$s: %d testů ok</item>
21-
<item quantity="other">%2$s: %d testů ok</item>
18+
<item quantity="one">%2$s: %1$d test ok</item>
19+
<item quantity="few">%2$s: %1$d testy ok</item>
20+
<item quantity="many">%2$s: %1$d testů ok</item>
21+
<item quantity="other">%2$s: %1$d testů ok</item>
2222
</plurals>
2323
<plurals name="plr_test_placeholder_2">
24-
<item quantity="one">%3$s: %d test z %2$d ok</item>
25-
<item quantity="few">%3$s: %d testy z %2$d ok</item>
26-
<item quantity="many">%3$s: %d testů z %2$d ok</item>
27-
<item quantity="other">%3$s: %d testů z %2$d ok</item>
24+
<item quantity="one">%3$s: %1$d test z %2$d ok</item>
25+
<item quantity="few">%3$s: %1$d testy z %2$d ok</item>
26+
<item quantity="many">%3$s: %1$d testů z %2$d ok</item>
27+
<item quantity="other">%3$s: %1$d testů z %2$d ok</item>
2828
</plurals>
2929
</resources>

0 commit comments

Comments
 (0)