├── sample
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── technokratos
│ │ │ │ └── compose
│ │ │ │ └── localization
│ │ │ │ ├── ui
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Shape.kt
│ │ │ │ ├── Type.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Strings.kt
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── technokratos
│ │ │ └── compose
│ │ │ └── localization
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── technokratos
│ │ └── compose
│ │ └── localization
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── localization
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── technokratos
│ │ │ └── compose
│ │ │ └── localization
│ │ │ ├── uid.kt
│ │ │ ├── plurals.kt
│ │ │ ├── Localization.kt
│ │ │ └── plural_rules.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── technokratos
│ │ └── compose
│ │ └── localization
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── media
└── localization.gif
├── settings.gradle
├── .gitmodules
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── LICENSE
├── gradle.properties
├── gradlew.bat
├── README.md
├── tools
└── plural_rules_generator.py
└── gradlew
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/localization/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/localization/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/media/localization.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/media/localization.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = "jetpack-compose-localization-preview"
2 | include ':sample'
3 | include ':localization'
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "cldr"]
2 | path = cldr
3 | url = git@github.com:unicode-org/cldr.git
4 | branch = maint/maint-38-1
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | jetpack-compose-localization-preview
3 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TechnokratosDev/jetpack-compose-localization/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/localization/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Feb 15 21:42:35 MSK 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea
5 | /.idea/caches
6 | /.idea/libraries
7 | /.idea/modules.xml
8 | /.idea/workspace.xml
9 | /.idea/navEditor.xml
10 | /.idea/assetWizardSettings.xml
11 | .DS_Store
12 | /build
13 | /captures
14 | .externalNativeBuild
15 | .cxx
16 | local.properties
17 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/technokratos/compose/localization/ui/Color.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization.ui
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val purple200 = Color(0xFFBB86FC)
6 | val purple500 = Color(0xFF6200EE)
7 | val purple700 = Color(0xFF3700B3)
8 | val teal200 = Color(0xFF03DAC5)
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/technokratos/compose/localization/ui/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization.ui
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/localization/src/main/java/com/technokratos/compose/localization/uid.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization
2 |
3 | import java.util.concurrent.atomic.AtomicInteger
4 |
5 | private val resUID = AtomicInteger(Int.MIN_VALUE)
6 | private val objToUID = mutableMapOf()
7 |
8 | internal fun generateUID(): Int = resUID.incrementAndGet()
9 |
10 | internal fun generateUID(name: Any): Int = resUID.incrementAndGet().also {
11 | objToUID[name] = it
12 | }
13 |
--------------------------------------------------------------------------------
/sample/src/test/java/com/technokratos/compose/localization/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/localization/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/technokratos/compose/localization/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.technokratos.compose.localization", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/localization/src/androidTest/java/com/technokratos/compose/localization/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.technokratos.compose.localization.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/sample/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/technokratos/compose/localization/ui/Type.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization.ui
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | /* Other default text styles to override
17 | button = TextStyle(
18 | fontFamily = FontFamily.Default,
19 | fontWeight = FontWeight.W500,
20 | fontSize = 14.sp
21 | ),
22 | caption = TextStyle(
23 | fontFamily = FontFamily.Default,
24 | fontWeight = FontWeight.Normal,
25 | fontSize = 12.sp
26 | )
27 | */
28 | )
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Technokratos
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
--------------------------------------------------------------------------------
/localization/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdkVersion 30
8 |
9 | defaultConfig {
10 | minSdkVersion 21
11 | targetSdkVersion 30
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | kotlinOptions {
28 | jvmTarget = '1.8'
29 | useIR = true
30 | }
31 | buildFeatures {
32 | compose true
33 | }
34 | composeOptions {
35 | kotlinCompilerExtensionVersion compose_version
36 | }
37 | }
38 |
39 | dependencies {
40 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
41 | implementation "androidx.compose.ui:ui:$compose_version"
42 | }
--------------------------------------------------------------------------------
/sample/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
20 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/technokratos/compose/localization/ui/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization.ui
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material.MaterialTheme
5 | import androidx.compose.material.darkColors
6 | import androidx.compose.material.lightColors
7 | import androidx.compose.runtime.Composable
8 |
9 | private val DarkColorPalette = darkColors(
10 | primary = purple200,
11 | primaryVariant = purple700,
12 | secondary = teal200
13 | )
14 |
15 | private val LightColorPalette = lightColors(
16 | primary = purple500,
17 | primaryVariant = purple700,
18 | secondary = teal200
19 |
20 | /* Other default colors to override
21 | background = Color.White,
22 | surface = Color.White,
23 | onPrimary = Color.White,
24 | onSecondary = Color.Black,
25 | onBackground = Color.Black,
26 | onSurface = Color.Black,
27 | */
28 | )
29 |
30 | @Composable
31 | fun SampleTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
32 | val colors = if (darkTheme) {
33 | DarkColorPalette
34 | } else {
35 | LightColorPalette
36 | }
37 |
38 | MaterialTheme(
39 | colors = colors,
40 | typography = typography,
41 | shapes = shapes,
42 | content = content
43 | )
44 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/technokratos/compose/localization/ui/Strings.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization.ui
2 |
3 | import java.util.Locale
4 | import com.technokratos.compose.localization.NonTranslatable
5 | import com.technokratos.compose.localization.Plural
6 | import com.technokratos.compose.localization.Plurals
7 | import com.technokratos.compose.localization.Translatable
8 | import com.technokratos.compose.localization.registerSupportedLocales
9 |
10 | val RUSSIAN = Locale("ru")
11 | val TATAR = Locale("tt")
12 |
13 | val supportedLocalesNow = registerSupportedLocales(RUSSIAN, TATAR)
14 |
15 | val localesHeader = Translatable(
16 | "Locales",
17 | hashMapOf(
18 | RUSSIAN to "Локали",
19 | TATAR to "Локальләштерүләр"
20 | )
21 | )
22 |
23 | val hello = Translatable(
24 | "Hello!",
25 | hashMapOf(
26 | RUSSIAN to "Привет!",
27 | TATAR to "Исәнме!"
28 | )
29 | )
30 |
31 | val bye = Translatable(
32 | "Goodbye!",
33 | hashMapOf(
34 | RUSSIAN to "Пока!",
35 | TATAR to "Хуш!"
36 | )
37 | )
38 |
39 | val nonTrans = NonTranslatable("%1\$d:%2\$02d")
40 |
41 | val plural = Plurals(
42 | Plural(one = "it's one", other = "it's other"),
43 | hashMapOf(
44 | RUSSIAN to Plural(
45 | one = "это один",
46 | few = "это несколько",
47 | many = "это много",
48 | other = "это другое"
49 | ),
50 | TATAR to Plural(
51 | one = "это не покажется",
52 | other = "бу башка"
53 | )
54 | )
55 | )
56 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdkVersion 30
8 |
9 | defaultConfig {
10 | applicationId "com.technokratos.compose.localization"
11 | minSdkVersion 21
12 | targetSdkVersion 30
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_1_8
27 | targetCompatibility JavaVersion.VERSION_1_8
28 | }
29 | kotlinOptions {
30 | jvmTarget = '1.8'
31 | useIR = true
32 | }
33 | buildFeatures {
34 | compose true
35 | }
36 | composeOptions {
37 | kotlinCompilerExtensionVersion compose_version
38 | }
39 | }
40 |
41 | dependencies {
42 | implementation(project(':localization'))
43 |
44 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
45 | implementation 'androidx.core:core-ktx:1.3.2'
46 | implementation 'androidx.appcompat:appcompat:1.2.0'
47 | implementation 'com.google.android.material:material:1.3.0'
48 |
49 | implementation "androidx.compose.ui:ui:$compose_version"
50 | implementation "androidx.activity:activity-compose:1.3.0-alpha04"
51 | implementation "androidx.compose.material:material:$compose_version"
52 | implementation "androidx.compose.ui:ui-tooling:$compose_version"
53 |
54 | testImplementation 'junit:junit:4.13.2'
55 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
57 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jetpack Compose Localization library
2 |
3 | Android XML-free string resources library with reactive locale changes
4 |
5 | This library
6 | -
7 | - works without `android.content.Context` instance reference
8 | - uses `CompositionLocal` (like `MaterialTheme`)
9 | - changes locale reactively w/o headache of imperative view state management
10 | - falls back to the default locale automatically
11 | - has plurals support out of the box
12 |
13 | TODO
14 | -
15 | - Setting up the default locale
16 | - Preserving the default locale between restarts
17 | - Type-safety formatting (may be)
18 | - Remove 'name' parameter from `(Non)Translatable`
19 | - Interop with old Android XML resources
20 | - String resource overriding in multi-module projects
21 |
22 | Usage
23 | -
24 | **_Strings.kt_**
25 | ```kotlin
26 | // create and register necessary locales
27 | // English locale is registered by default
28 | val RUSSIAN = Locale("ru")
29 | val TATAR = Locale("tt")
30 |
31 | val supportedLocalesNow = registerSupportedLocales(RUSSIAN, TATAR)
32 |
33 | // 1nd parameter: string value for default localization
34 | // 2rd parameter: dictionary of locale to string resource
35 | // hello is ext function that finds string in Localization receiver and returns it
36 | val hello = Translatable(
37 | "Hello!",
38 | hashMapOf(
39 | RUSSIAN to "Привет!",
40 | TATAR to "Исәнме!"
41 | )
42 | )
43 | // non-translatable variant
44 | val nonTrans = NonTranslatable("%1\$d:%2\$02d")
45 | ```
46 |
47 | **_Ui.kt_**
48 | ```kotlin
49 | @Composable
50 | fun Content() {
51 | Column {
52 | // Vocabulary - helper object for statical referencing on current Localization
53 | val localization = Vocabulary.localization
54 | // call the defined function on the retrieved localization
55 | // that returns string in appropriate locale
56 | Text(text = localization.hello())
57 | // this string is always same
58 | Text(text = localization.nonTrans().format(20, 9))
59 | }
60 | }
61 | ```
62 |
63 | **_MainActivity.kt_**
64 | ```kotlin
65 | setContent {
66 | val locale = remember { mutableStateOf(Locale.getDefault()) }
67 | Localization(locale = locale.value) {
68 | Content()
69 | }
70 | }
71 | ```
72 | `locale` change leads on text translating in entire hierarchy
73 |
74 | Sample
75 | -
76 |
77 |
78 | LICENSE
79 | -
80 | ```
81 | MIT License
82 |
83 | Copyright (c) 2021 Technokratos
84 |
85 | Permission is hereby granted, free of charge, to any person obtaining a copy
86 | of this software and associated documentation files (the "Software"), to deal
87 | in the Software without restriction, including without limitation the rights
88 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
89 | copies of the Software, and to permit persons to whom the Software is
90 | furnished to do so, subject to the following conditions:
91 |
92 | The above copyright notice and this permission notice shall be included in all
93 | copies or substantial portions of the Software.
94 |
95 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
96 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
97 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
98 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
99 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
100 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
101 | SOFTWARE.
102 | ```
--------------------------------------------------------------------------------
/localization/src/main/java/com/technokratos/compose/localization/plurals.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization
2 |
3 | import java.util.Locale
4 | import kotlin.math.absoluteValue
5 | import android.util.Log
6 |
7 | class PluralRule(
8 | val zero: (Double, Long, Long, Int, Int) -> Boolean = { _, _, _, _, _ -> false },
9 | val one: (Double, Long, Long, Int, Int) -> Boolean = { _, _, _, _, _ -> false },
10 | val two: (Double, Long, Long, Int, Int) -> Boolean = { _, _, _, _, _ -> false },
11 | val few: (Double, Long, Long, Int, Int) -> Boolean = { _, _, _, _, _ -> false },
12 | val many: (Double, Long, Long, Int, Int) -> Boolean = { _, _, _, _, _ -> false }
13 | )
14 |
15 | class Plural(
16 | val zero: CharSequence? = null,
17 | val one: CharSequence? = null,
18 | val two: CharSequence? = null,
19 | val few: CharSequence? = null,
20 | val many: CharSequence? = null,
21 | val other: CharSequence
22 | )
23 |
24 | fun Plurals(
25 | defaultValue: Plural,
26 | localeToPlural: Map,
27 | name: Int = generateUID()
28 | ): Localization.(Double) -> CharSequence {
29 | defaultLocalization.plurals[name] = defaultValue
30 | for ((locale, value) in localeToPlural.entries) {
31 | if (!isLocaleRegistered(locale)) {
32 | // TODO issue-9
33 | Log.w(
34 | "jcl10n",
35 | "There is no plural rule for $locale currently. Plural's 'other = ${value.other}' field can be used only!"
36 | )
37 | }
38 | val localization =
39 | localizationMap[locale] ?: throw RuntimeException("There is no locale $locale")
40 | localization.plurals[name] = value
41 | }
42 | return fun Localization.(quantity: Double): CharSequence {
43 | return getPlural(name, quantity) ?: defaultLocalization.getPlural(name, quantity)
44 | ?: throw RuntimeException("There is no string called $name in localization $this")
45 | }
46 | }
47 |
48 | fun Plurals(
49 | name: Any,
50 | defaultValue: Plural,
51 | localeToPlural: Map
52 | ): Localization.(Double) -> CharSequence {
53 | return Plurals(
54 | defaultValue,
55 | localeToPlural,
56 | generateUID(name)
57 | )
58 | }
59 |
60 | private fun Localization.getPlural(name: Int, quantity: Double): CharSequence? {
61 | val absQuantity = quantity.absoluteValue
62 | val (int, frac) = absQuantity.toString().split('.')
63 | val integerPart = int.toLong()
64 | val fractionPart = frac.trimStart('0').ifEmpty { "0" }.toLong()
65 | val fractionPartDigitCount = frac.trimEnd('0').count()
66 |
67 | return plurals[name]?.let {
68 | resolveString(
69 | it,
70 | locale,
71 | absQuantity,
72 | integerPart,
73 | fractionPart,
74 | fractionPartDigitCount,
75 | 0 // we don't support String quantity parameter now so exp part is always 0
76 | )
77 | }
78 | }
79 |
80 | private val defaultPluralRule = onlyOther
81 |
82 | private fun isLocaleRegistered(locale: Locale): Boolean {
83 | return localeToPluralRule.containsKey(locale)
84 | }
85 |
86 | private fun resolveString(
87 | plural: Plural,
88 | locale: Locale,
89 | n: Double,
90 | i: Long,
91 | f: Long,
92 | v: Int,
93 | e: Int
94 | ): CharSequence {
95 | val rule = localeToPluralRule[locale] ?: defaultPluralRule
96 |
97 | return when {
98 | rule.zero(n, i, f, v, e) && plural.zero != null -> plural.zero
99 | rule.one(n, i, f, v, e) && plural.one != null -> plural.one
100 | rule.two(n, i, f, v, e) && plural.two != null -> plural.two
101 | rule.few(n, i, f, v, e) && plural.few != null -> plural.few
102 | rule.many(n, i, f, v, e) && plural.many != null -> plural.many
103 | else -> plural.other
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/tools/plural_rules_generator.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | from xml.dom import minidom
3 | import re
4 | import pathlib
5 |
6 | current_dir = str(pathlib.Path(__file__).parent.absolute())
7 | dom = minidom.parse(current_dir + "/../cldr/common/supplemental/plurals.xml")
8 |
9 | firstIterationPattern = re.compile(r"([infve]( % \d+)? (==|!=)) ((\S+,)+\S+)")
10 |
11 | pattern = re.compile(r"([infve])( % \d+)? (==|!=) \d+(\.\.\d+)?")
12 |
13 | def get_suffix(op: str):
14 | if op in ("i", "f"):
15 | return "L"
16 | elif op == "n":
17 | return ".0"
18 | else:
19 | return ""
20 |
21 | def insertBefore(host: str, index: int, insertion: str):
22 | if index < 0 or index > len(host):
23 | print("index {} is out of bounds of '{}' string".format(index, host))
24 | return host
25 | return host[:index] + insertion + host[index:]
26 |
27 | def get_symbol(sym: str, rule: str):
28 | return sym if sym in rule else "_"
29 |
30 | def substitute(substitutions: dict, text: str):
31 | for key, value in substitutions.items():
32 | text = text.replace(key, value)
33 | return text
34 |
35 | with open(current_dir + "/../localization/src/main/java/com/technokratos/compose/localization/plural_rules.kt", "w") as f:
36 | f.write("// this file generated by /tools/plural_rules_generator.py\n\n")
37 | f.write("package com.technokratos.compose.localization\n\n")
38 | f.write("import java.util.Locale\n\n")
39 |
40 | f.write("val onlyOther = PluralRule()\n\n")
41 |
42 | f.write("val localeToPluralRule = mutableMapOf(\n")
43 |
44 | for plural in dom.getElementsByTagName("plurals")[0].getElementsByTagName("pluralRules"):
45 | for locale in plural.getAttribute("locales").split(" "):
46 | if len(plural.getElementsByTagName("pluralRule")) < 2:
47 | f.write(" Locale(\"{}\") to onlyOther,\n".format(locale))
48 | continue
49 | else:
50 | f.write(" Locale(\"{}\") to PluralRule(\n".format(locale))
51 | for rule in plural.getElementsByTagName("pluralRule"):
52 | attr = rule.getAttribute("count")
53 | if attr == "other":
54 | continue
55 | f.write(" {} = ".format(attr))
56 | rule_text = rule.firstChild.data
57 | at = rule_text.find("@")
58 | if at < 0:
59 | rule_text = rule_text[0:len(rule_text)]
60 | else:
61 | rule_text = rule_text[0:(at - 1)]
62 |
63 | rule_text = rule_text.strip()
64 | rule_text = rule_text.replace("t", "f") #we don't support t parameter now
65 | rule_text = rule_text.replace("or", "||")
66 | rule_text = rule_text.replace("and", "&&")
67 | rule_text = rule_text.replace(" = ", " == ")
68 |
69 | f.write("{ " + "{n}: Double, {i}: Long, {f}: Long, {v}: Int, {e}: Int ->\n".format(
70 | n = get_symbol("n", rule_text),
71 | i = get_symbol("i", rule_text),
72 | f = get_symbol("f", rule_text),
73 | v = get_symbol("v", rule_text),
74 | e = get_symbol("e", rule_text)
75 | ))
76 |
77 | substitutions = {}
78 |
79 | for match in firstIterationPattern.finditer(rule_text):
80 | operands = match.group(4).split(',')
81 | substitutions[match.group()] = " && ".join([common + " " + operand for common, operand in zip([match.group(1)] * len(operands), operands)])
82 |
83 | rule_text = substitute(substitutions, rule_text)
84 |
85 | for match in pattern.finditer(rule_text):
86 | suffix = get_suffix(match.group(1))
87 | statement = match.group()
88 | statement = insertBefore(statement, len(statement), suffix)
89 |
90 | if match.group(4) is not None:
91 | statement = insertBefore(statement, statement.index(".."), suffix)
92 | statement = statement.replace("==", "in")
93 | statement = statement.replace("!=", "!in")
94 |
95 | substitutions[match.group()] = statement
96 |
97 | rule_text = substitute(substitutions, rule_text)
98 |
99 | f.write(" {}\n".format(rule_text))
100 | f.write(" },\n")
101 | f.write(" ),\n")
102 | f.write(")")
103 |
--------------------------------------------------------------------------------
/localization/src/main/java/com/technokratos/compose/localization/Localization.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.CompositionLocalProvider
5 | import androidx.compose.runtime.ReadOnlyComposable
6 | import androidx.compose.runtime.compositionLocalOf
7 | import java.util.Locale
8 |
9 | internal val defaultLocalization: Localization = Localization(Locale.ENGLISH)
10 |
11 | private val supportedLocales: MutableSet = mutableSetOf()
12 |
13 | internal val localizationMap = hashMapOf()
14 |
15 | data class Localization(
16 | val locale: Locale,
17 | internal val strings: MutableMap = mutableMapOf(),
18 | internal val plurals: MutableMap = mutableMapOf()
19 | )
20 |
21 | fun registerSupportedLocales(vararg locales: Locale): Set {
22 | locales.filter { it != Locale.ENGLISH }
23 | .forEach {
24 | if (supportedLocales.add(it)) {
25 | registerLocalizationForLocale(it)
26 | }
27 | }
28 | return supportedLocales + Locale.ENGLISH
29 | }
30 |
31 | private fun registerLocalizationForLocale(locale: Locale) {
32 | localizationMap[locale] = Localization(locale)
33 | }
34 |
35 | /**
36 | * Builder function for translatable string resource
37 | *
38 | * Saves given locales into corresponding [Localization] and returns extension function
39 | * that can be used for string resource retrieving
40 | *
41 | * @param defaultValue string value for default localization
42 | * @param localeToValue dictionary of locale to string resource
43 | * @param id integer id of a string resource
44 | * @return ext function that finds string in [Localization] receiver and returns it
45 | */
46 | fun Translatable(
47 | defaultValue: String,
48 | localeToValue: Map,
49 | id: Int = generateUID()
50 | ): Localization.() -> String {
51 | defaultLocalization.strings[id] = defaultValue
52 | for ((locale, value) in localeToValue.entries) {
53 | val localization =
54 | localizationMap[locale] ?: throw RuntimeException("There is no locale $locale")
55 | localization.strings[id] = value
56 | }
57 | return fun Localization.(): String {
58 | return this.strings[id] ?: defaultLocalization.strings[id]
59 | ?: error("There is no string called $id in localization $this")
60 | }
61 | }
62 |
63 | /**
64 | * Builder function for translatable string resource
65 | *
66 | * Saves given locales into corresponding [Localization] and returns extension function
67 | * that can be used for string resource retrieving
68 | *
69 | * @param name id of a string resource, may be any object
70 | * @param defaultValue string value for default localization
71 | * @param localeToValue dictionary of locale to string resource
72 | * @return ext function that finds string in [Localization] receiver and returns it
73 | */
74 | fun Translatable(
75 | name: Any,
76 | defaultValue: String,
77 | localeToValue: Map
78 | ): Localization.() -> String {
79 | return Translatable(
80 | defaultValue,
81 | localeToValue,
82 | generateUID(name)
83 | )
84 | }
85 |
86 | /**
87 | * Builder function for non-translatable string resource
88 | *
89 | * Saves given locales into corresponding [Localization] and returns extension function
90 | * that can be used for string resource retrieving
91 | *
92 | * @param defaultValue string value for default localization
93 | * @param id integer id of a string resource
94 | * @return ext function that finds string in [Localization] receiver and returns it
95 | */
96 | fun NonTranslatable(defaultValue: String, id: Int = generateUID()): Localization.() -> String {
97 | defaultLocalization.strings[id] = defaultValue
98 | return fun Localization.(): String {
99 | return defaultLocalization.strings[id]
100 | ?: error("There is no string called $id in localization default")
101 | }
102 | }
103 |
104 | /**
105 | * Builder function for non-translatable string resource
106 | *
107 | * Saves given locales into corresponding [Localization] and returns extension function
108 | * that can be used for string resource retrieving
109 | *
110 | * @param name id of a string resource, may be any object
111 | * @param defaultValue string value for default localization
112 | * @return ext function that finds string in [Localization] receiver and returns it
113 | */
114 | fun NonTranslatable(name: Any, defaultValue: String): Localization.() -> String {
115 | return NonTranslatable(defaultValue, generateUID(name))
116 | }
117 |
118 | val LocalLocalization = compositionLocalOf { defaultLocalization }
119 |
120 | object Vocabulary {
121 | val localization: Localization
122 | @Composable
123 | @ReadOnlyComposable
124 | get() = LocalLocalization.current
125 | }
126 |
127 | @Composable
128 | fun Localization(locale: Locale, content: @Composable () -> Unit) {
129 | CompositionLocalProvider(
130 | LocalLocalization provides (localizationMap[locale] ?: defaultLocalization),
131 | content = content
132 | )
133 | }
134 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/technokratos/compose/localization/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.technokratos.compose.localization
2 |
3 | import java.util.Locale
4 | import android.os.Bundle
5 | import androidx.activity.compose.setContent
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.compose.foundation.Canvas
8 | import androidx.compose.foundation.background
9 | import androidx.compose.foundation.clickable
10 | import androidx.compose.foundation.layout.Box
11 | import androidx.compose.foundation.layout.Column
12 | import androidx.compose.foundation.layout.Spacer
13 | import androidx.compose.foundation.layout.fillMaxSize
14 | import androidx.compose.foundation.layout.fillMaxWidth
15 | import androidx.compose.foundation.layout.height
16 | import androidx.compose.foundation.layout.padding
17 | import androidx.compose.foundation.layout.requiredHeight
18 | import androidx.compose.foundation.lazy.LazyColumn
19 | import androidx.compose.foundation.lazy.items
20 | import androidx.compose.foundation.rememberScrollState
21 | import androidx.compose.material.MaterialTheme
22 | import androidx.compose.material.Surface
23 | import androidx.compose.material.Text
24 | import androidx.compose.runtime.Composable
25 | import androidx.compose.runtime.derivedStateOf
26 | import androidx.compose.runtime.getValue
27 | import androidx.compose.runtime.mutableStateOf
28 | import androidx.compose.runtime.remember
29 | import androidx.compose.runtime.setValue
30 | import androidx.compose.ui.Modifier
31 | import androidx.compose.ui.graphics.Color
32 | import androidx.compose.ui.tooling.preview.Preview
33 | import androidx.compose.ui.unit.dp
34 | import com.technokratos.compose.localization.ui.SampleTheme
35 | import com.technokratos.compose.localization.ui.bye
36 | import com.technokratos.compose.localization.ui.hello
37 | import com.technokratos.compose.localization.ui.localesHeader
38 | import com.technokratos.compose.localization.ui.nonTrans
39 | import com.technokratos.compose.localization.ui.plural
40 | import com.technokratos.compose.localization.ui.supportedLocalesNow
41 |
42 | class MainActivity : AppCompatActivity() {
43 | override fun onCreate(savedInstanceState: Bundle?) {
44 | super.onCreate(savedInstanceState)
45 | setContent(null) {
46 | MyApp {
47 | Column(modifier = Modifier.fillMaxSize()) {
48 | LanguageChooser(onClick = it)
49 | Spacer(modifier = Modifier.height(8.dp))
50 | Examples(modifier = Modifier.weight(1f))
51 | }
52 | }
53 | }
54 | }
55 | }
56 |
57 | @Composable
58 | fun MyApp(content: @Composable ((Locale) -> Unit) -> Unit) {
59 | SampleTheme {
60 | var locale by remember { mutableStateOf(Locale.getDefault()) }
61 | Localization(locale = locale) {
62 | Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
63 | content {
64 | locale = it
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
71 | @Preview
72 | @Composable
73 | fun DefaultPreview() {
74 | MyApp {
75 | Column(modifier = Modifier.fillMaxSize()) {
76 | LanguageChooser(modifier = Modifier.weight(1f))
77 | Spacer(modifier = Modifier.height(8.dp))
78 | Examples(modifier = Modifier.weight(0.3f))
79 | }
80 | }
81 | }
82 |
83 | @Composable
84 | fun LanguageChooser(modifier: Modifier = Modifier, onClick: (Locale) -> Unit = {}) {
85 | val locales by remember { derivedStateOf { supportedLocalesNow } }
86 | val localization = Vocabulary.localization
87 | rememberScrollState(0)
88 | LazyColumn(modifier = modifier.fillMaxWidth()) {
89 | // use `item` for separate elements like headers
90 | // and `items` for lists of identical elements
91 | item {
92 | Box(modifier = Modifier.padding(8.dp)) {
93 | Text(text = localization.localesHeader(), style = MaterialTheme.typography.h4)
94 | }
95 | }
96 | items(locales.toList()) { locale ->
97 | val background =
98 | if (locale == localization.locale)
99 | Color.Green.copy(alpha = 0.3f)
100 | else
101 | MaterialTheme.colors.surface
102 | Surface(
103 | modifier = Modifier
104 | .fillMaxWidth()
105 | .clickable(onClick = { onClick(locale) })
106 | .background(background)
107 | .padding(8.dp),
108 | color = Color.Transparent
109 | ) {
110 | Text(
111 | text = locale.toString(),
112 | style = MaterialTheme.typography.h6
113 | )
114 | }
115 | }
116 | }
117 | }
118 |
119 | @Composable
120 | fun Examples(modifier: Modifier = Modifier) {
121 | val localization = Vocabulary.localization
122 | LazyColumn(
123 | modifier = modifier
124 | .fillMaxWidth()
125 | .padding(8.dp)
126 | ) {
127 | item {
128 | Text(text = localization.hello(), style = MaterialTheme.typography.h5)
129 | }
130 | item {
131 | Text(text = localization.bye(), style = MaterialTheme.typography.h5)
132 | }
133 | item {
134 | Text(text = localization.nonTrans().format(20, 9), style = MaterialTheme.typography.h5)
135 | }
136 | item {
137 | Column(modifier = Modifier.fillMaxWidth()) {
138 | Text(
139 | modifier = Modifier.fillMaxWidth(),
140 | text = "Plurals",
141 | style = MaterialTheme.typography.h4
142 | )
143 | Canvas(
144 | modifier = Modifier
145 | .fillMaxWidth()
146 | .requiredHeight(1.dp),
147 | onDraw = { drawRect(color = Color.Black) })
148 | }
149 | }
150 | items(arrayOf(0.0, 0.5, 1.0, 81.0, 2.0, 10.0, 11.0)) {
151 | Text(text = "$it ${localization.plural(it)}", style = MaterialTheme.typography.h5)
152 | }
153 | }
154 | }
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/localization/src/main/java/com/technokratos/compose/localization/plural_rules.kt:
--------------------------------------------------------------------------------
1 | // this file generated by /tools/plural_rules_generator.py
2 |
3 | package com.technokratos.compose.localization
4 |
5 | import java.util.Locale
6 |
7 | val onlyOther = PluralRule()
8 |
9 | val localeToPluralRule = mutableMapOf(
10 | Locale("bm") to onlyOther,
11 | Locale("bo") to onlyOther,
12 | Locale("dz") to onlyOther,
13 | Locale("id") to onlyOther,
14 | Locale("ig") to onlyOther,
15 | Locale("ii") to onlyOther,
16 | Locale("in") to onlyOther,
17 | Locale("ja") to onlyOther,
18 | Locale("jbo") to onlyOther,
19 | Locale("jv") to onlyOther,
20 | Locale("jw") to onlyOther,
21 | Locale("kde") to onlyOther,
22 | Locale("kea") to onlyOther,
23 | Locale("km") to onlyOther,
24 | Locale("ko") to onlyOther,
25 | Locale("lkt") to onlyOther,
26 | Locale("lo") to onlyOther,
27 | Locale("ms") to onlyOther,
28 | Locale("my") to onlyOther,
29 | Locale("nqo") to onlyOther,
30 | Locale("osa") to onlyOther,
31 | Locale("root") to onlyOther,
32 | Locale("sah") to onlyOther,
33 | Locale("ses") to onlyOther,
34 | Locale("sg") to onlyOther,
35 | Locale("su") to onlyOther,
36 | Locale("th") to onlyOther,
37 | Locale("to") to onlyOther,
38 | Locale("vi") to onlyOther,
39 | Locale("wo") to onlyOther,
40 | Locale("yo") to onlyOther,
41 | Locale("yue") to onlyOther,
42 | Locale("zh") to onlyOther,
43 | Locale("am") to PluralRule(
44 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
45 | i == 0L || n == 1.0
46 | },
47 | ),
48 | Locale("as") to PluralRule(
49 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
50 | i == 0L || n == 1.0
51 | },
52 | ),
53 | Locale("bn") to PluralRule(
54 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
55 | i == 0L || n == 1.0
56 | },
57 | ),
58 | Locale("doi") to PluralRule(
59 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
60 | i == 0L || n == 1.0
61 | },
62 | ),
63 | Locale("fa") to PluralRule(
64 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
65 | i == 0L || n == 1.0
66 | },
67 | ),
68 | Locale("gu") to PluralRule(
69 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
70 | i == 0L || n == 1.0
71 | },
72 | ),
73 | Locale("hi") to PluralRule(
74 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
75 | i == 0L || n == 1.0
76 | },
77 | ),
78 | Locale("kn") to PluralRule(
79 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
80 | i == 0L || n == 1.0
81 | },
82 | ),
83 | Locale("pcm") to PluralRule(
84 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
85 | i == 0L || n == 1.0
86 | },
87 | ),
88 | Locale("zu") to PluralRule(
89 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
90 | i == 0L || n == 1.0
91 | },
92 | ),
93 | Locale("ff") to PluralRule(
94 | one = { _: Double, i: Long, _: Long, _: Int, _: Int ->
95 | i == 0L && i == 1L
96 | },
97 | ),
98 | Locale("hy") to PluralRule(
99 | one = { _: Double, i: Long, _: Long, _: Int, _: Int ->
100 | i == 0L && i == 1L
101 | },
102 | ),
103 | Locale("kab") to PluralRule(
104 | one = { _: Double, i: Long, _: Long, _: Int, _: Int ->
105 | i == 0L && i == 1L
106 | },
107 | ),
108 | Locale("pt") to PluralRule(
109 | one = { _: Double, i: Long, _: Long, _: Int, _: Int ->
110 | i in 0L..1L
111 | },
112 | ),
113 | Locale("ast") to PluralRule(
114 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
115 | i == 1L && v == 0
116 | },
117 | ),
118 | Locale("ca") to PluralRule(
119 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
120 | i == 1L && v == 0
121 | },
122 | ),
123 | Locale("de") to PluralRule(
124 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
125 | i == 1L && v == 0
126 | },
127 | ),
128 | Locale("en") to PluralRule(
129 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
130 | i == 1L && v == 0
131 | },
132 | ),
133 | Locale("et") to PluralRule(
134 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
135 | i == 1L && v == 0
136 | },
137 | ),
138 | Locale("fi") to PluralRule(
139 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
140 | i == 1L && v == 0
141 | },
142 | ),
143 | Locale("fy") to PluralRule(
144 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
145 | i == 1L && v == 0
146 | },
147 | ),
148 | Locale("gl") to PluralRule(
149 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
150 | i == 1L && v == 0
151 | },
152 | ),
153 | Locale("ia") to PluralRule(
154 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
155 | i == 1L && v == 0
156 | },
157 | ),
158 | Locale("io") to PluralRule(
159 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
160 | i == 1L && v == 0
161 | },
162 | ),
163 | Locale("it") to PluralRule(
164 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
165 | i == 1L && v == 0
166 | },
167 | ),
168 | Locale("ji") to PluralRule(
169 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
170 | i == 1L && v == 0
171 | },
172 | ),
173 | Locale("lij") to PluralRule(
174 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
175 | i == 1L && v == 0
176 | },
177 | ),
178 | Locale("nl") to PluralRule(
179 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
180 | i == 1L && v == 0
181 | },
182 | ),
183 | Locale("pt_PT") to PluralRule(
184 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
185 | i == 1L && v == 0
186 | },
187 | ),
188 | Locale("sc") to PluralRule(
189 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
190 | i == 1L && v == 0
191 | },
192 | ),
193 | Locale("scn") to PluralRule(
194 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
195 | i == 1L && v == 0
196 | },
197 | ),
198 | Locale("sv") to PluralRule(
199 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
200 | i == 1L && v == 0
201 | },
202 | ),
203 | Locale("sw") to PluralRule(
204 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
205 | i == 1L && v == 0
206 | },
207 | ),
208 | Locale("ur") to PluralRule(
209 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
210 | i == 1L && v == 0
211 | },
212 | ),
213 | Locale("yi") to PluralRule(
214 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
215 | i == 1L && v == 0
216 | },
217 | ),
218 | Locale("si") to PluralRule(
219 | one = { n: Double, i: Long, f: Long, _: Int, _: Int ->
220 | n == 0.0 && n == 1.0 || i == 0L && f == 1L
221 | },
222 | ),
223 | Locale("ak") to PluralRule(
224 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
225 | n in 0.0..1.0
226 | },
227 | ),
228 | Locale("bho") to PluralRule(
229 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
230 | n in 0.0..1.0
231 | },
232 | ),
233 | Locale("guw") to PluralRule(
234 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
235 | n in 0.0..1.0
236 | },
237 | ),
238 | Locale("ln") to PluralRule(
239 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
240 | n in 0.0..1.0
241 | },
242 | ),
243 | Locale("mg") to PluralRule(
244 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
245 | n in 0.0..1.0
246 | },
247 | ),
248 | Locale("nso") to PluralRule(
249 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
250 | n in 0.0..1.0
251 | },
252 | ),
253 | Locale("pa") to PluralRule(
254 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
255 | n in 0.0..1.0
256 | },
257 | ),
258 | Locale("ti") to PluralRule(
259 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
260 | n in 0.0..1.0
261 | },
262 | ),
263 | Locale("wa") to PluralRule(
264 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
265 | n in 0.0..1.0
266 | },
267 | ),
268 | Locale("tzm") to PluralRule(
269 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
270 | n in 0.0..1.0 || n in 11.0..99.0
271 | },
272 | ),
273 | Locale("af") to PluralRule(
274 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
275 | n == 1.0
276 | },
277 | ),
278 | Locale("an") to PluralRule(
279 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
280 | n == 1.0
281 | },
282 | ),
283 | Locale("asa") to PluralRule(
284 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
285 | n == 1.0
286 | },
287 | ),
288 | Locale("az") to PluralRule(
289 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
290 | n == 1.0
291 | },
292 | ),
293 | Locale("bem") to PluralRule(
294 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
295 | n == 1.0
296 | },
297 | ),
298 | Locale("bez") to PluralRule(
299 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
300 | n == 1.0
301 | },
302 | ),
303 | Locale("bg") to PluralRule(
304 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
305 | n == 1.0
306 | },
307 | ),
308 | Locale("brx") to PluralRule(
309 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
310 | n == 1.0
311 | },
312 | ),
313 | Locale("ce") to PluralRule(
314 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
315 | n == 1.0
316 | },
317 | ),
318 | Locale("cgg") to PluralRule(
319 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
320 | n == 1.0
321 | },
322 | ),
323 | Locale("chr") to PluralRule(
324 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
325 | n == 1.0
326 | },
327 | ),
328 | Locale("ckb") to PluralRule(
329 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
330 | n == 1.0
331 | },
332 | ),
333 | Locale("dv") to PluralRule(
334 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
335 | n == 1.0
336 | },
337 | ),
338 | Locale("ee") to PluralRule(
339 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
340 | n == 1.0
341 | },
342 | ),
343 | Locale("el") to PluralRule(
344 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
345 | n == 1.0
346 | },
347 | ),
348 | Locale("eo") to PluralRule(
349 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
350 | n == 1.0
351 | },
352 | ),
353 | Locale("es") to PluralRule(
354 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
355 | n == 1.0
356 | },
357 | ),
358 | Locale("eu") to PluralRule(
359 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
360 | n == 1.0
361 | },
362 | ),
363 | Locale("fo") to PluralRule(
364 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
365 | n == 1.0
366 | },
367 | ),
368 | Locale("fur") to PluralRule(
369 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
370 | n == 1.0
371 | },
372 | ),
373 | Locale("gsw") to PluralRule(
374 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
375 | n == 1.0
376 | },
377 | ),
378 | Locale("ha") to PluralRule(
379 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
380 | n == 1.0
381 | },
382 | ),
383 | Locale("haw") to PluralRule(
384 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
385 | n == 1.0
386 | },
387 | ),
388 | Locale("hu") to PluralRule(
389 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
390 | n == 1.0
391 | },
392 | ),
393 | Locale("jgo") to PluralRule(
394 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
395 | n == 1.0
396 | },
397 | ),
398 | Locale("jmc") to PluralRule(
399 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
400 | n == 1.0
401 | },
402 | ),
403 | Locale("ka") to PluralRule(
404 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
405 | n == 1.0
406 | },
407 | ),
408 | Locale("kaj") to PluralRule(
409 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
410 | n == 1.0
411 | },
412 | ),
413 | Locale("kcg") to PluralRule(
414 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
415 | n == 1.0
416 | },
417 | ),
418 | Locale("kk") to PluralRule(
419 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
420 | n == 1.0
421 | },
422 | ),
423 | Locale("kkj") to PluralRule(
424 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
425 | n == 1.0
426 | },
427 | ),
428 | Locale("kl") to PluralRule(
429 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
430 | n == 1.0
431 | },
432 | ),
433 | Locale("ks") to PluralRule(
434 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
435 | n == 1.0
436 | },
437 | ),
438 | Locale("ksb") to PluralRule(
439 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
440 | n == 1.0
441 | },
442 | ),
443 | Locale("ku") to PluralRule(
444 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
445 | n == 1.0
446 | },
447 | ),
448 | Locale("ky") to PluralRule(
449 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
450 | n == 1.0
451 | },
452 | ),
453 | Locale("lb") to PluralRule(
454 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
455 | n == 1.0
456 | },
457 | ),
458 | Locale("lg") to PluralRule(
459 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
460 | n == 1.0
461 | },
462 | ),
463 | Locale("mas") to PluralRule(
464 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
465 | n == 1.0
466 | },
467 | ),
468 | Locale("mgo") to PluralRule(
469 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
470 | n == 1.0
471 | },
472 | ),
473 | Locale("ml") to PluralRule(
474 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
475 | n == 1.0
476 | },
477 | ),
478 | Locale("mn") to PluralRule(
479 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
480 | n == 1.0
481 | },
482 | ),
483 | Locale("mr") to PluralRule(
484 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
485 | n == 1.0
486 | },
487 | ),
488 | Locale("nah") to PluralRule(
489 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
490 | n == 1.0
491 | },
492 | ),
493 | Locale("nb") to PluralRule(
494 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
495 | n == 1.0
496 | },
497 | ),
498 | Locale("nd") to PluralRule(
499 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
500 | n == 1.0
501 | },
502 | ),
503 | Locale("ne") to PluralRule(
504 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
505 | n == 1.0
506 | },
507 | ),
508 | Locale("nn") to PluralRule(
509 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
510 | n == 1.0
511 | },
512 | ),
513 | Locale("nnh") to PluralRule(
514 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
515 | n == 1.0
516 | },
517 | ),
518 | Locale("no") to PluralRule(
519 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
520 | n == 1.0
521 | },
522 | ),
523 | Locale("nr") to PluralRule(
524 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
525 | n == 1.0
526 | },
527 | ),
528 | Locale("ny") to PluralRule(
529 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
530 | n == 1.0
531 | },
532 | ),
533 | Locale("nyn") to PluralRule(
534 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
535 | n == 1.0
536 | },
537 | ),
538 | Locale("om") to PluralRule(
539 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
540 | n == 1.0
541 | },
542 | ),
543 | Locale("or") to PluralRule(
544 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
545 | n == 1.0
546 | },
547 | ),
548 | Locale("os") to PluralRule(
549 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
550 | n == 1.0
551 | },
552 | ),
553 | Locale("pap") to PluralRule(
554 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
555 | n == 1.0
556 | },
557 | ),
558 | Locale("ps") to PluralRule(
559 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
560 | n == 1.0
561 | },
562 | ),
563 | Locale("rm") to PluralRule(
564 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
565 | n == 1.0
566 | },
567 | ),
568 | Locale("rof") to PluralRule(
569 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
570 | n == 1.0
571 | },
572 | ),
573 | Locale("rwk") to PluralRule(
574 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
575 | n == 1.0
576 | },
577 | ),
578 | Locale("saq") to PluralRule(
579 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
580 | n == 1.0
581 | },
582 | ),
583 | Locale("sd") to PluralRule(
584 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
585 | n == 1.0
586 | },
587 | ),
588 | Locale("sdh") to PluralRule(
589 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
590 | n == 1.0
591 | },
592 | ),
593 | Locale("seh") to PluralRule(
594 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
595 | n == 1.0
596 | },
597 | ),
598 | Locale("sn") to PluralRule(
599 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
600 | n == 1.0
601 | },
602 | ),
603 | Locale("so") to PluralRule(
604 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
605 | n == 1.0
606 | },
607 | ),
608 | Locale("sq") to PluralRule(
609 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
610 | n == 1.0
611 | },
612 | ),
613 | Locale("ss") to PluralRule(
614 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
615 | n == 1.0
616 | },
617 | ),
618 | Locale("ssy") to PluralRule(
619 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
620 | n == 1.0
621 | },
622 | ),
623 | Locale("st") to PluralRule(
624 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
625 | n == 1.0
626 | },
627 | ),
628 | Locale("syr") to PluralRule(
629 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
630 | n == 1.0
631 | },
632 | ),
633 | Locale("ta") to PluralRule(
634 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
635 | n == 1.0
636 | },
637 | ),
638 | Locale("te") to PluralRule(
639 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
640 | n == 1.0
641 | },
642 | ),
643 | Locale("teo") to PluralRule(
644 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
645 | n == 1.0
646 | },
647 | ),
648 | Locale("tig") to PluralRule(
649 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
650 | n == 1.0
651 | },
652 | ),
653 | Locale("tk") to PluralRule(
654 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
655 | n == 1.0
656 | },
657 | ),
658 | Locale("tn") to PluralRule(
659 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
660 | n == 1.0
661 | },
662 | ),
663 | Locale("tr") to PluralRule(
664 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
665 | n == 1.0
666 | },
667 | ),
668 | Locale("ts") to PluralRule(
669 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
670 | n == 1.0
671 | },
672 | ),
673 | Locale("ug") to PluralRule(
674 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
675 | n == 1.0
676 | },
677 | ),
678 | Locale("uz") to PluralRule(
679 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
680 | n == 1.0
681 | },
682 | ),
683 | Locale("ve") to PluralRule(
684 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
685 | n == 1.0
686 | },
687 | ),
688 | Locale("vo") to PluralRule(
689 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
690 | n == 1.0
691 | },
692 | ),
693 | Locale("vun") to PluralRule(
694 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
695 | n == 1.0
696 | },
697 | ),
698 | Locale("wae") to PluralRule(
699 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
700 | n == 1.0
701 | },
702 | ),
703 | Locale("xh") to PluralRule(
704 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
705 | n == 1.0
706 | },
707 | ),
708 | Locale("xog") to PluralRule(
709 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
710 | n == 1.0
711 | },
712 | ),
713 | Locale("da") to PluralRule(
714 | one = { n: Double, i: Long, f: Long, _: Int, _: Int ->
715 | n == 1.0 || f != 0L && i == 0L && i == 1L
716 | },
717 | ),
718 | Locale("is") to PluralRule(
719 | one = { _: Double, i: Long, f: Long, _: Int, _: Int ->
720 | f == 0L && i % 10 == 1L && i % 100 != 11L || f != 0L
721 | },
722 | ),
723 | Locale("mk") to PluralRule(
724 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
725 | v == 0 && i % 10 == 1L && i % 100 != 11L || f % 10 == 1L && f % 100 != 11L
726 | },
727 | ),
728 | Locale("ceb") to PluralRule(
729 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
730 | v == 0 && i == 1L && i == 2L && i == 3L || v == 0 && i % 10 != 4L && i % 10 != 6L && i % 10 != 9L || v != 0 && f % 10 != 4L && f % 10 != 6L && f % 10 != 9L
731 | },
732 | ),
733 | Locale("fil") to PluralRule(
734 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
735 | v == 0 && i == 1L && i == 2L && i == 3L || v == 0 && i % 10 != 4L && i % 10 != 6L && i % 10 != 9L || v != 0 && f % 10 != 4L && f % 10 != 6L && f % 10 != 9L
736 | },
737 | ),
738 | Locale("tl") to PluralRule(
739 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
740 | v == 0 && i == 1L && i == 2L && i == 3L || v == 0 && i % 10 != 4L && i % 10 != 6L && i % 10 != 9L || v != 0 && f % 10 != 4L && f % 10 != 6L && f % 10 != 9L
741 | },
742 | ),
743 | Locale("lv") to PluralRule(
744 | zero = { n: Double, _: Long, f: Long, v: Int, _: Int ->
745 | n % 10 == 0.0 || n % 100 in 11.0..19.0 || v == 2 && f % 100 in 11L..19L
746 | },
747 | one = { n: Double, _: Long, f: Long, v: Int, _: Int ->
748 | n % 10 == 1.0 && n % 100 != 11.0 || v == 2 && f % 10 == 1L && f % 100 != 11L || v != 2 && f % 10 == 1L
749 | },
750 | ),
751 | Locale("prg") to PluralRule(
752 | zero = { n: Double, _: Long, f: Long, v: Int, _: Int ->
753 | n % 10 == 0.0 || n % 100 in 11.0..19.0 || v == 2 && f % 100 in 11L..19L
754 | },
755 | one = { n: Double, _: Long, f: Long, v: Int, _: Int ->
756 | n % 10 == 1.0 && n % 100 != 11.0 || v == 2 && f % 10 == 1L && f % 100 != 11L || v != 2 && f % 10 == 1L
757 | },
758 | ),
759 | Locale("lag") to PluralRule(
760 | zero = { n: Double, _: Long, _: Long, _: Int, _: Int ->
761 | n == 0.0
762 | },
763 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
764 | i == 0L && i == 1L && n != 0.0
765 | },
766 | ),
767 | Locale("ksh") to PluralRule(
768 | zero = { n: Double, _: Long, _: Long, _: Int, _: Int ->
769 | n == 0.0
770 | },
771 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
772 | n == 1.0
773 | },
774 | ),
775 | Locale("iu") to PluralRule(
776 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
777 | n == 1.0
778 | },
779 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
780 | n == 2.0
781 | },
782 | ),
783 | Locale("naq") to PluralRule(
784 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
785 | n == 1.0
786 | },
787 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
788 | n == 2.0
789 | },
790 | ),
791 | Locale("sat") to PluralRule(
792 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
793 | n == 1.0
794 | },
795 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
796 | n == 2.0
797 | },
798 | ),
799 | Locale("se") to PluralRule(
800 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
801 | n == 1.0
802 | },
803 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
804 | n == 2.0
805 | },
806 | ),
807 | Locale("sma") to PluralRule(
808 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
809 | n == 1.0
810 | },
811 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
812 | n == 2.0
813 | },
814 | ),
815 | Locale("smi") to PluralRule(
816 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
817 | n == 1.0
818 | },
819 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
820 | n == 2.0
821 | },
822 | ),
823 | Locale("smj") to PluralRule(
824 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
825 | n == 1.0
826 | },
827 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
828 | n == 2.0
829 | },
830 | ),
831 | Locale("smn") to PluralRule(
832 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
833 | n == 1.0
834 | },
835 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
836 | n == 2.0
837 | },
838 | ),
839 | Locale("sms") to PluralRule(
840 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
841 | n == 1.0
842 | },
843 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
844 | n == 2.0
845 | },
846 | ),
847 | Locale("shi") to PluralRule(
848 | one = { n: Double, i: Long, _: Long, _: Int, _: Int ->
849 | i == 0L || n == 1.0
850 | },
851 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
852 | n in 2.0..10.0
853 | },
854 | ),
855 | Locale("mo") to PluralRule(
856 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
857 | i == 1L && v == 0
858 | },
859 | few = { n: Double, _: Long, _: Long, v: Int, _: Int ->
860 | v != 0 || n == 0.0 || n % 100 in 2.0..19.0
861 | },
862 | ),
863 | Locale("ro") to PluralRule(
864 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
865 | i == 1L && v == 0
866 | },
867 | few = { n: Double, _: Long, _: Long, v: Int, _: Int ->
868 | v != 0 || n == 0.0 || n % 100 in 2.0..19.0
869 | },
870 | ),
871 | Locale("bs") to PluralRule(
872 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
873 | v == 0 && i % 10 == 1L && i % 100 != 11L || f % 10 == 1L && f % 100 != 11L
874 | },
875 | few = { _: Double, i: Long, f: Long, v: Int, _: Int ->
876 | v == 0 && i % 10 in 2L..4L && i % 100 !in 12L..14L || f % 10 in 2L..4L && f % 100 !in 12L..14L
877 | },
878 | ),
879 | Locale("hr") to PluralRule(
880 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
881 | v == 0 && i % 10 == 1L && i % 100 != 11L || f % 10 == 1L && f % 100 != 11L
882 | },
883 | few = { _: Double, i: Long, f: Long, v: Int, _: Int ->
884 | v == 0 && i % 10 in 2L..4L && i % 100 !in 12L..14L || f % 10 in 2L..4L && f % 100 !in 12L..14L
885 | },
886 | ),
887 | Locale("sh") to PluralRule(
888 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
889 | v == 0 && i % 10 == 1L && i % 100 != 11L || f % 10 == 1L && f % 100 != 11L
890 | },
891 | few = { _: Double, i: Long, f: Long, v: Int, _: Int ->
892 | v == 0 && i % 10 in 2L..4L && i % 100 !in 12L..14L || f % 10 in 2L..4L && f % 100 !in 12L..14L
893 | },
894 | ),
895 | Locale("sr") to PluralRule(
896 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
897 | v == 0 && i % 10 == 1L && i % 100 != 11L || f % 10 == 1L && f % 100 != 11L
898 | },
899 | few = { _: Double, i: Long, f: Long, v: Int, _: Int ->
900 | v == 0 && i % 10 in 2L..4L && i % 100 !in 12L..14L || f % 10 in 2L..4L && f % 100 !in 12L..14L
901 | },
902 | ),
903 | Locale("fr") to PluralRule(
904 | one = { _: Double, i: Long, _: Long, _: Int, _: Int ->
905 | i == 0L && i == 1L
906 | },
907 | many = { _: Double, i: Long, _: Long, v: Int, e: Int ->
908 | e == 0 && i != 0L && i % 1000000 == 0L && v == 0 || e !in 0..5
909 | },
910 | ),
911 | Locale("gd") to PluralRule(
912 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
913 | n == 1.0 && n == 1.01
914 | },
915 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
916 | n == 2.0 && n == 12.0
917 | },
918 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
919 | n in 3.0..10.0 && n in 13.0..19.0
920 | },
921 | ),
922 | Locale("sl") to PluralRule(
923 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
924 | v == 0 && i % 100 == 1L
925 | },
926 | two = { _: Double, i: Long, _: Long, v: Int, _: Int ->
927 | v == 0 && i % 100 == 2L
928 | },
929 | few = { _: Double, i: Long, _: Long, v: Int, _: Int ->
930 | v == 0 && i % 100 in 3L..4L || v != 0
931 | },
932 | ),
933 | Locale("dsb") to PluralRule(
934 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
935 | v == 0 && i % 100 == 1L || f % 100 == 1L
936 | },
937 | two = { _: Double, i: Long, f: Long, v: Int, _: Int ->
938 | v == 0 && i % 100 == 2L || f % 100 == 2L
939 | },
940 | few = { _: Double, i: Long, f: Long, v: Int, _: Int ->
941 | v == 0 && i % 100 in 3L..4L || f % 100 in 3L..4L
942 | },
943 | ),
944 | Locale("hsb") to PluralRule(
945 | one = { _: Double, i: Long, f: Long, v: Int, _: Int ->
946 | v == 0 && i % 100 == 1L || f % 100 == 1L
947 | },
948 | two = { _: Double, i: Long, f: Long, v: Int, _: Int ->
949 | v == 0 && i % 100 == 2L || f % 100 == 2L
950 | },
951 | few = { _: Double, i: Long, f: Long, v: Int, _: Int ->
952 | v == 0 && i % 100 in 3L..4L || f % 100 in 3L..4L
953 | },
954 | ),
955 | Locale("he") to PluralRule(
956 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
957 | i == 1L && v == 0
958 | },
959 | two = { _: Double, i: Long, _: Long, v: Int, _: Int ->
960 | i == 2L && v == 0
961 | },
962 | many = { n: Double, _: Long, _: Long, v: Int, _: Int ->
963 | v == 0 && n !in 0.0..10.0 && n % 10 == 0.0
964 | },
965 | ),
966 | Locale("iw") to PluralRule(
967 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
968 | i == 1L && v == 0
969 | },
970 | two = { _: Double, i: Long, _: Long, v: Int, _: Int ->
971 | i == 2L && v == 0
972 | },
973 | many = { n: Double, _: Long, _: Long, v: Int, _: Int ->
974 | v == 0 && n !in 0.0..10.0 && n % 10 == 0.0
975 | },
976 | ),
977 | Locale("cs") to PluralRule(
978 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
979 | i == 1L && v == 0
980 | },
981 | few = { _: Double, i: Long, _: Long, v: Int, _: Int ->
982 | i in 2L..4L && v == 0
983 | },
984 | many = { _: Double, _: Long, _: Long, v: Int, _: Int ->
985 | v != 0
986 | },
987 | ),
988 | Locale("sk") to PluralRule(
989 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
990 | i == 1L && v == 0
991 | },
992 | few = { _: Double, i: Long, _: Long, v: Int, _: Int ->
993 | i in 2L..4L && v == 0
994 | },
995 | many = { _: Double, _: Long, _: Long, v: Int, _: Int ->
996 | v != 0
997 | },
998 | ),
999 | Locale("pl") to PluralRule(
1000 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1001 | i == 1L && v == 0
1002 | },
1003 | few = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1004 | v == 0 && i % 10 in 2L..4L && i % 100 !in 12L..14L
1005 | },
1006 | many = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1007 | v == 0 && i != 1L && i % 10 in 0L..1L || v == 0 && i % 10 in 5L..9L || v == 0 && i % 100 in 12L..14L
1008 | },
1009 | ),
1010 | Locale("be") to PluralRule(
1011 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1012 | n % 10 == 1.0 && n % 100 != 11.0
1013 | },
1014 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1015 | n % 10 in 2.0..4.0 && n % 100 !in 12.0..14.0
1016 | },
1017 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1018 | n % 10 == 0.0 || n % 10 in 5.0..9.0 || n % 100 in 11.0..14.0
1019 | },
1020 | ),
1021 | Locale("lt") to PluralRule(
1022 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1023 | n % 10 == 1.0 && n % 100 !in 11.0..19.0
1024 | },
1025 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1026 | n % 10 in 2.0..9.0 && n % 100 !in 11.0..19.0
1027 | },
1028 | many = { _: Double, _: Long, f: Long, _: Int, _: Int ->
1029 | f != 0L
1030 | },
1031 | ),
1032 | Locale("mt") to PluralRule(
1033 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1034 | n == 1.0
1035 | },
1036 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1037 | n == 0.0 || n % 100 in 2.0..10.0
1038 | },
1039 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1040 | n % 100 in 11.0..19.0
1041 | },
1042 | ),
1043 | Locale("ru") to PluralRule(
1044 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1045 | v == 0 && i % 10 == 1L && i % 100 != 11L
1046 | },
1047 | few = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1048 | v == 0 && i % 10 in 2L..4L && i % 100 !in 12L..14L
1049 | },
1050 | many = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1051 | v == 0 && i % 10 == 0L || v == 0 && i % 10 in 5L..9L || v == 0 && i % 100 in 11L..14L
1052 | },
1053 | ),
1054 | Locale("uk") to PluralRule(
1055 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1056 | v == 0 && i % 10 == 1L && i % 100 != 11L
1057 | },
1058 | few = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1059 | v == 0 && i % 10 in 2L..4L && i % 100 !in 12L..14L
1060 | },
1061 | many = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1062 | v == 0 && i % 10 == 0L || v == 0 && i % 10 in 5L..9L || v == 0 && i % 100 in 11L..14L
1063 | },
1064 | ),
1065 | Locale("br") to PluralRule(
1066 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1067 | n % 10 == 1.0 && n % 100 != 11.0 && n % 100 != 71.0 && n % 100 != 91.0
1068 | },
1069 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1070 | n % 10 == 2.0 && n % 100 != 12.0 && n % 100 != 72.0 && n % 100 != 92.0
1071 | },
1072 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1073 | n % 10 in 3.0..4.0 && n % 10 == 9.0 && n % 100 !in 10.0..19.0 && n % 100 !in 70.0..79.0 && n % 100 !in 90.0..99.0
1074 | },
1075 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1076 | n != 0.0 && n % 1000000 == 0.0
1077 | },
1078 | ),
1079 | Locale("ga") to PluralRule(
1080 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1081 | n == 1.0
1082 | },
1083 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1084 | n == 2.0
1085 | },
1086 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1087 | n in 3.0..6.0
1088 | },
1089 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1090 | n in 7.0..10.0
1091 | },
1092 | ),
1093 | Locale("gv") to PluralRule(
1094 | one = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1095 | v == 0 && i % 10 == 1L
1096 | },
1097 | two = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1098 | v == 0 && i % 10 == 2L
1099 | },
1100 | few = { _: Double, i: Long, _: Long, v: Int, _: Int ->
1101 | v == 0 && i % 100 == 0L && i % 100 == 20L && i % 100 == 40L && i % 100 == 60L && i % 100 == 80L
1102 | },
1103 | many = { _: Double, _: Long, _: Long, v: Int, _: Int ->
1104 | v != 0
1105 | },
1106 | ),
1107 | Locale("kw") to PluralRule(
1108 | zero = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1109 | n == 0.0
1110 | },
1111 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1112 | n == 1.0
1113 | },
1114 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1115 | n % 100 == 2.0 && n % 100 == 2.02 && n % 100 == 42.0 && n % 100 == 62.0 && n % 100 == 82.0 || n % 1000 == 0.0 && n % 100000 in 1000.0..20000.0 && n % 100000 == 40000.0 && n % 100000 == 60000.0 && n % 100000 == 80000.0 || n != 0.0 && n % 1000000 == 100000.0
1116 | },
1117 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1118 | n % 100 == 3.0 && n % 100 == 23.0 && n % 100 == 43.0 && n % 100 == 63.0 && n % 100 == 83.0
1119 | },
1120 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1121 | n != 1.0 && n % 100 == 1.0 && n % 100 == 21.0 && n % 100 == 41.0 && n % 100 == 61.0 && n % 100 == 81.0
1122 | },
1123 | ),
1124 | Locale("ar") to PluralRule(
1125 | zero = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1126 | n == 0.0
1127 | },
1128 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1129 | n == 1.0
1130 | },
1131 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1132 | n == 2.0
1133 | },
1134 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1135 | n % 100 in 3.0..10.0
1136 | },
1137 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1138 | n % 100 in 11.0..99.0
1139 | },
1140 | ),
1141 | Locale("ars") to PluralRule(
1142 | zero = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1143 | n == 0.0
1144 | },
1145 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1146 | n == 1.0
1147 | },
1148 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1149 | n == 2.0
1150 | },
1151 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1152 | n % 100 in 3.0..10.0
1153 | },
1154 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1155 | n % 100 in 11.0..99.0
1156 | },
1157 | ),
1158 | Locale("cy") to PluralRule(
1159 | zero = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1160 | n == 0.0
1161 | },
1162 | one = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1163 | n == 1.0
1164 | },
1165 | two = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1166 | n == 2.0
1167 | },
1168 | few = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1169 | n == 3.0
1170 | },
1171 | many = { n: Double, _: Long, _: Long, _: Int, _: Int ->
1172 | n == 6.0
1173 | },
1174 | ),
1175 | )
--------------------------------------------------------------------------------