0
# Plural String Resources
1
2
Plural string resources handle quantity-based pluralization following CLDR (Common Locale Data Repository) plural rules. This enables proper localization for languages with complex pluralization rules, ensuring grammatically correct text across all supported locales.
3
4
## Core Types
5
6
```kotlin { .api }
7
class PluralStringResource(id: String, val key: String, items: Set<ResourceItem>) : Resource(id, items)
8
```
9
10
A plural string resource represents a text entry with multiple plural forms based on quantity. Each resource contains variants for different plural categories (zero, one, two, few, many, other).
11
12
## Composable Functions
13
14
### Basic Plural String Loading
15
16
```kotlin { .api }
17
@Composable
18
fun pluralStringResource(resource: PluralStringResource, quantity: Int): String
19
```
20
21
Loads a pluralized string resource based on the provided quantity. The function automatically selects the appropriate plural form according to CLDR rules for the current locale.
22
23
**Parameters:**
24
- `resource` - The plural string resource
25
- `quantity` - The quantity that determines which plural form to use
26
27
**Usage:**
28
```kotlin
29
@Composable
30
fun ItemCounter(itemCount: Int) {
31
val text = pluralStringResource(Res.plurals.items_count, itemCount)
32
Text(text = text)
33
}
34
35
@Composable
36
fun MessageIndicator(messageCount: Int) {
37
val status = pluralStringResource(
38
resource = Res.plurals.message_status,
39
quantity = messageCount
40
)
41
Text(text = status)
42
}
43
```
44
45
### Formatted Plural String Loading
46
47
```kotlin { .api }
48
@Composable
49
fun pluralStringResource(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String
50
```
51
52
Loads a formatted pluralized string resource with variable arguments. The selected plural form can contain format placeholders that are replaced with the provided arguments.
53
54
**Parameters:**
55
- `resource` - The plural string resource with format placeholders
56
- `quantity` - The quantity for plural form selection
57
- `formatArgs` - Variable arguments to substitute into the string
58
59
**Usage:**
60
```kotlin
61
@Composable
62
fun FileOperationStatus(fileCount: Int, operation: String) {
63
val status = pluralStringResource(
64
resource = Res.plurals.file_operation_message,
65
quantity = fileCount,
66
formatArgs = arrayOf(fileCount, operation)
67
)
68
Text(text = status)
69
}
70
```
71
72
**Plural Resource Example:**
73
```xml
74
<plurals name="file_operation_message">
75
<item quantity="one">%2$s %1$d file</item>
76
<item quantity="other">%2$s %1$d files</item>
77
</plurals>
78
```
79
80
## Suspend Functions
81
82
### Basic Non-Composable Loading
83
84
```kotlin { .api }
85
suspend fun getPluralString(resource: PluralStringResource, quantity: Int): String
86
```
87
88
Loads a pluralized string resource outside of a Composable context using the system's resource environment.
89
90
**Usage:**
91
```kotlin
92
suspend fun createNotificationText(unreadCount: Int): String {
93
return getPluralString(Res.plurals.unread_notifications, unreadCount)
94
}
95
96
class NotificationManager {
97
suspend fun updateBadge(count: Int) {
98
val badgeText = getPluralString(Res.plurals.badge_count, count)
99
updateSystemBadge(badgeText)
100
}
101
}
102
```
103
104
### Formatted Non-Composable Loading
105
106
```kotlin { .api }
107
suspend fun getPluralString(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String
108
```
109
110
Loads a formatted pluralized string resource outside of a Composable context.
111
112
**Usage:**
113
```kotlin
114
suspend fun generateSummaryText(downloadCount: Int, totalSize: String): String {
115
return getPluralString(
116
resource = Res.plurals.download_summary,
117
quantity = downloadCount,
118
formatArgs = arrayOf(downloadCount, totalSize)
119
)
120
}
121
```
122
123
### Environment-Specific Loading
124
125
```kotlin { .api }
126
suspend fun getPluralString(
127
environment: ResourceEnvironment,
128
resource: PluralStringResource,
129
quantity: Int
130
): String
131
132
suspend fun getPluralString(
133
environment: ResourceEnvironment,
134
resource: PluralStringResource,
135
quantity: Int,
136
vararg formatArgs: Any
137
): String
138
```
139
140
Loads pluralized string resources using a specific resource environment. Useful for testing different locales or generating content for specific language contexts.
141
142
**Usage:**
143
```kotlin
144
suspend fun getLocalizedPluralString(
145
locale: Locale,
146
itemCount: Int
147
): String {
148
val environment = ResourceEnvironment(
149
LanguageQualifier(locale.language),
150
RegionQualifier(locale.country),
151
ThemeQualifier.LIGHT,
152
DensityQualifier.MDPI
153
)
154
155
return getPluralString(
156
environment = environment,
157
resource = Res.plurals.item_count,
158
quantity = itemCount,
159
formatArgs = arrayOf(itemCount)
160
)
161
}
162
```
163
164
## CLDR Plural Categories
165
166
The library supports all CLDR plural categories:
167
168
```kotlin { .api }
169
enum class PluralCategory {
170
ZERO, // Exact zero (Arabic, Latvian)
171
ONE, // Singular form (most languages)
172
TWO, // Dual form (Arabic, Welsh)
173
FEW, // Small numbers (Polish, Russian 2-4)
174
MANY, // Large numbers (Polish, Russian 5+)
175
OTHER // Default/fallback (always required)
176
}
177
```
178
179
### Language-Specific Plural Rules
180
181
**English:**
182
- `ONE`: 1
183
- `OTHER`: 0, 2, 3, 4, ... (everything else)
184
185
**Russian:**
186
- `ONE`: 1, 21, 31, 41, ... (ends with 1, not 11)
187
- `FEW`: 2-4, 22-24, 32-34, ... (ends with 2-4, not 12-14)
188
- `MANY`: 0, 5-20, 25-30, ... (everything else)
189
190
**Arabic:**
191
- `ZERO`: 0
192
- `ONE`: 1
193
- `TWO`: 2
194
- `FEW`: 3-10
195
- `MANY`: 11-99
196
- `OTHER`: 100, 200, ... (hundreds)
197
198
## Plural Resource Definition
199
200
### XML Resource Format
201
202
```xml
203
<plurals name="notification_count">
204
<item quantity="zero">No notifications</item>
205
<item quantity="one">1 notification</item>
206
<item quantity="other">%d notifications</item>
207
</plurals>
208
209
<plurals name="download_time">
210
<item quantity="one">%d minute remaining</item>
211
<item quantity="other">%d minutes remaining</item>
212
</plurals>
213
214
<plurals name="complex_example">
215
<item quantity="zero">No items selected</item>
216
<item quantity="one">One item selected</item>
217
<item quantity="two">Both items selected</item>
218
<item quantity="few">A few items selected (%d)</item>
219
<item quantity="many">Many items selected (%d)</item>
220
<item quantity="other">%d items selected</item>
221
</plurals>
222
```
223
224
### Localized Plural Resources
225
226
```
227
res/
228
├── values/plurals.xml # Default (English)
229
├── values-ru/plurals.xml # Russian (complex rules)
230
├── values-ar/plurals.xml # Arabic (6 categories)
231
├── values-zh/plurals.xml # Chinese (simple rules)
232
└── values-pl/plurals.xml # Polish (complex rules)
233
```
234
235
## Plural Rule Management
236
237
```kotlin { .api }
238
class PluralRuleList(private val rules: Array<PluralRule>) {
239
fun getCategory(quantity: Int): PluralCategory
240
241
companion object {
242
suspend fun getInstance(
243
languageQualifier: LanguageQualifier,
244
regionQualifier: RegionQualifier
245
): PluralRuleList
246
247
suspend fun getInstance(cldrLocaleName: String): PluralRuleList
248
}
249
}
250
```
251
252
The library includes built-in CLDR plural rules for all supported locales. Rules are loaded automatically based on the current resource environment.
253
254
## Error Handling
255
256
**Common Exceptions:**
257
- `IllegalArgumentException` - Resource ID not found or no matching plural category
258
- `MissingResourceException` - Plural resource file cannot be read
259
260
**Fallback Behavior:**
261
1. If the exact plural category is not found, falls back to `OTHER`
262
2. If `OTHER` is not defined, throws an exception
263
3. Always define `OTHER` as the fallback category
264
265
**Example Error Handling:**
266
```kotlin
267
@Composable
268
fun SafePluralText(count: Int) {
269
val text = try {
270
pluralStringResource(Res.plurals.item_count, count)
271
} catch (e: IllegalArgumentException) {
272
// Fallback to simple formatting
273
if (count == 1) "1 item" else "$count items"
274
}
275
276
Text(text = text)
277
}
278
```
279
280
## Best Practices
281
282
1. **Always provide the OTHER category**:
283
```xml
284
<plurals name="example">
285
<item quantity="one">1 item</item>
286
<item quantity="other">%d items</item> <!-- Required -->
287
</plurals>
288
```
289
290
2. **Test with multiple locales**:
291
```kotlin
292
// Test plural rules for different languages
293
val testCounts = listOf(0, 1, 2, 3, 4, 5, 11, 21, 101)
294
testCounts.forEach { count ->
295
val text = pluralStringResource(Res.plurals.test_resource, count)
296
println("$count: $text")
297
}
298
```
299
300
3. **Use meaningful resource names**:
301
```kotlin
302
Res.plurals.unread_messages_count // Good
303
Res.plurals.plural1 // Bad
304
```
305
306
4. **Include quantity in formatted strings**:
307
```xml
308
<plurals name="download_progress">
309
<item quantity="one">%d file downloaded</item>
310
<item quantity="other">%d files downloaded</item>
311
</plurals>
312
```
313
314
5. **Handle zero appropriately**:
315
```xml
316
<plurals name="search_results">
317
<item quantity="zero">No results found</item>
318
<item quantity="one">1 result found</item>
319
<item quantity="other">%d results found</item>
320
</plurals>
321
```
322
323
6. **Consider context for better UX**:
324
```xml
325
<!-- Instead of just numbers, provide meaningful context -->
326
<plurals name="friend_activity">
327
<item quantity="zero">No friends are online</item>
328
<item quantity="one">1 friend is online</item>
329
<item quantity="other">%d friends are online</item>
330
</plurals>
331
```
332
333
## Advanced Usage
334
335
### Dynamic Plural Selection
336
337
```kotlin
338
@Composable
339
fun DynamicPluralExample(items: List<Item>) {
340
val selectedCount = items.count { it.isSelected }
341
val totalCount = items.size
342
343
val statusText = pluralStringResource(
344
resource = Res.plurals.selection_status,
345
quantity = selectedCount,
346
formatArgs = arrayOf(selectedCount, totalCount)
347
)
348
349
Text(text = statusText)
350
}
351
```
352
353
### Nested Plural Logic
354
355
```kotlin
356
@Composable
357
fun ComplexPluralExample(users: List<User>, posts: List<Post>) {
358
val userText = pluralStringResource(Res.plurals.user_count, users.size)
359
val postText = pluralStringResource(Res.plurals.post_count, posts.size)
360
361
Text(text = "$userText created $postText")
362
// Examples: "1 user created 5 posts", "3 users created 1 post"
363
}
364
```