├── .editorconfig ├── .github ├── funding.yml └── workflows │ ├── build.yml │ ├── publish-release.yml │ └── publish-snapshot.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── MultiplatformLocale.podspec ├── MultiplatformLocaleAll.podspec ├── MultiplatformLocaleGeo.podspec ├── MultiplatformLocaleGooglePlayStore.podspec ├── README.md ├── build.gradle.kts ├── generator ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── com │ └── vanniktech │ └── locale │ └── generator │ └── main.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lint.xml ├── multiplatform-locale-all ├── MultiplatformLocaleAll.podspec ├── api │ └── current.txt ├── build.gradle.kts ├── gradle.properties └── src │ └── commonMain │ └── kotlin │ └── com │ └── vanniktech │ └── locale │ └── all │ └── iosworkaround.kt ├── multiplatform-locale-apple-app-store ├── MultiplatformLocaleAppleAppStore.podspec ├── api │ └── current.txt ├── build.gradle.kts ├── gradle.properties └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── locale │ │ └── apple │ │ └── app │ │ └── store │ │ └── AppleAppStoreLocale.kt │ └── commonTest │ └── kotlin │ └── com │ └── vanniktech │ └── locale │ └── apple │ └── app │ └── store │ └── AppleAppStoreLocaleTest.kt ├── multiplatform-locale-geo ├── MultiplatformLocaleGeo.podspec ├── api │ └── current.txt ├── build.gradle.kts ├── gradle.properties └── src │ └── commonMain │ └── kotlin │ └── com │ └── vanniktech │ └── locale │ └── geo │ ├── CountryGeo.kt │ └── Geo.kt ├── multiplatform-locale-google-play-store ├── MultiplatformLocaleGooglePlayStore.podspec ├── api │ └── current.txt ├── build.gradle.kts ├── gradle.properties └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── locale │ │ └── google │ │ └── play │ │ └── store │ │ └── GooglePlayStoreLocale.kt │ └── commonTest │ └── kotlin │ └── com │ └── vanniktech │ └── locale │ └── google │ └── play │ └── store │ └── GooglePlayStoreLocaleTest.kt ├── multiplatform-locale ├── MultiplatformLocale.podspec ├── api │ └── current.txt ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidMain │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── locale │ │ ├── Countries.kt │ │ ├── Languages.kt │ │ ├── Locales.kt │ │ └── Multiplatform.android.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── locale │ │ ├── Continent.kt │ │ ├── Countries.kt │ │ ├── Country.kt │ │ ├── Language.kt │ │ ├── Languages.kt │ │ ├── Locale.kt │ │ ├── Locales.kt │ │ ├── Region.kt │ │ ├── Territory.kt │ │ └── capitalize.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── locale │ │ ├── CountriesTest.kt │ │ ├── CountryTest.kt │ │ ├── LanguageTest.kt │ │ ├── LanguagesTest.kt │ │ ├── LocaleTest.kt │ │ ├── LocalesTest.kt │ │ └── RegionTest.kt │ ├── iosMain │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── locale │ │ ├── Countries.kt │ │ ├── Languages.kt │ │ ├── Locales.kt │ │ └── Multiplatform.ios.kt │ └── jvmMain │ └── kotlin │ └── com │ └── vanniktech │ └── locale │ ├── Countries.kt │ ├── Languages.kt │ ├── Locales.kt │ └── Multiplatform.jvm.kt ├── renovate.json ├── sample-android.png ├── sample-android ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── vanniktech │ │ └── locale │ │ └── sample │ │ └── android │ │ ├── LocaleApplication.kt │ │ └── LocaleMainActivity.kt │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v23 │ └── themes.xml │ ├── values-v27 │ └── themes.xml │ └── values │ ├── strings.xml │ └── styles.xml ├── sample-ios.png ├── sample-ios ├── LICENSE ├── Podfile ├── Podfile.lock ├── ios.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── ios.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── ios │ ├── App.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ └── Preview Content │ └── Preview Assets.xcassets │ └── Contents.json ├── sample-jvm.png ├── sample-jvm ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── vanniktech │ └── locale │ └── sample │ └── jvm │ └── LocaleJvm.kt └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | ktlint_code_style=intellij_idea 3 | indent_size=2 4 | continuation_indent_size=2 5 | ij_kotlin_allow_trailing_comma=true 6 | ij_kotlin_allow_trailing_comma_on_call_site=true 7 | insert_final_newline=true 8 | ktlint_standard_annotation=disabled 9 | ktlint_standard_max-line-length=disabled 10 | ktlint_standard_filename=disabled 11 | ktlint_standard_spacing-between-declarations-with-annotations=disabled 12 | ktlint_standard_blank-line-between-when-conditions=disabled 13 | ktlint_standard_backing-property-naming=disabled 14 | ktlint_standard_kdoc=disabled 15 | ktlint_standard_condition-wrapping=disabled 16 | ktlint_experimental=enabled -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: [vanniktech] -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request, merge_group] 4 | 5 | jobs: 6 | build: 7 | name: JDK ${{ matrix.java_version }} 8 | runs-on: macOS-latest 9 | 10 | strategy: 11 | matrix: 12 | java_version: [17] 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Gradle Wrapper Validation 19 | uses: gradle/actions/wrapper-validation@v4 20 | 21 | - name: Setup gradle 22 | uses: gradle/gradle-build-action@v3 23 | 24 | - name: Install JDK ${{ matrix.java_version }} 25 | uses: actions/setup-java@v4 26 | with: 27 | distribution: 'zulu' 28 | java-version: ${{ matrix.java_version }} 29 | 30 | - name: Build with Gradle 31 | run: ./gradlew licensee jvmTest ktlint testDebug build --stacktrace 32 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish: 10 | 11 | runs-on: macOS-latest 12 | if: github.repository == 'vanniktech/multiplatform-locale' 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Install JDK 17 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: 'zulu' 22 | java-version: 17 23 | 24 | - name: Setup gradle 25 | uses: gradle/gradle-build-action@v3.5.0 26 | 27 | - name: Publish release 28 | run: ./gradlew publishAllPublicationsToMavenCentralRepository 29 | env: 30 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 31 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 32 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_PRIVATE_KEY }} 33 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} 34 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Publish Snapshot 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | publish: 10 | 11 | runs-on: macOS-latest 12 | if: github.repository == 'vanniktech/multiplatform-locale' 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Install JDK 17 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: 'zulu' 22 | java-version: 17 23 | 24 | - name: Setup gradle 25 | uses: gradle/gradle-build-action@v3.5.0 26 | 27 | - name: Retrieve version 28 | run: | 29 | echo "VERSION_NAME=$(cat gradle.properties | grep -w "VERSION_NAME" | cut -d'=' -f2)" >> $GITHUB_ENV 30 | 31 | - name: Publish snapshot 32 | run: ./gradlew publishAllPublicationsToMavenCentralRepository 33 | if: endsWith(env.VERSION_NAME, '-SNAPSHOT') 34 | env: 35 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 36 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Android Studio 26 | .idea 27 | .gradle 28 | build/ 29 | *.iml 30 | 31 | # iOS 32 | xcuserdata/ 33 | *.ipa 34 | Pods/ 35 | 36 | ios/fastlane/report.xml 37 | ios/fastlane/README.md 38 | *.dSYM.zip 39 | 40 | # Windows thumbnail db 41 | Thumbs.db 42 | 43 | # OSX files 44 | .DS_Store 45 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | Version 0.10.0 *(In development)* 4 | --------------------------------- 5 | 6 | Version 0.9.0 *(2025-01-19)* 7 | ---------------------------- 8 | 9 | - API: Country Add BOUVET_ISLAND [\#243](https://github.com/vanniktech/multiplatform-locale/pull/243) ([vanniktech](https://github.com/vanniktech)) 10 | - API: Country Add UNITED_STATES_MINOR_OUTLYING_ISLANDS [\#244](https://github.com/vanniktech/multiplatform-locale/pull/244) ([vanniktech](https://github.com/vanniktech)) 11 | - API: Country.ARUBA Change Continent from SOUTH_AMERICA to NORTH_AMERICA [\#225](https://github.com/vanniktech/multiplatform-locale/pull/225) ([vanniktech](https://github.com/vanniktech)) 12 | - API: Country.AZERBAIJAN remove EUROPE continent. [\#226](https://github.com/vanniktech/multiplatform-locale/pull/226) ([vanniktech](https://github.com/vanniktech)) 13 | - API: Country.BONAIRE Change Continent from SOUTH_AMERICA to NORTH_AMERICA [\#227](https://github.com/vanniktech/multiplatform-locale/pull/227) ([vanniktech](https://github.com/vanniktech)) 14 | - API: Country.CAYMAN_ISLANDS fix Calling Code. [\#228](https://github.com/vanniktech/multiplatform-locale/pull/228) ([vanniktech](https://github.com/vanniktech)) 15 | - API: Country.CURACAO Change Continent from SOUTH_AMERICA to NORTH_AMERICA [\#232](https://github.com/vanniktech/multiplatform-locale/pull/232) ([vanniktech](https://github.com/vanniktech)) 16 | - API: Country.CYPRUS remove EUROPE continent. [\#242](https://github.com/vanniktech/multiplatform-locale/pull/242) ([vanniktech](https://github.com/vanniktech)) 17 | - API: Country.DOMINICAN_REPUBLIC fix Calling Code. [\#229](https://github.com/vanniktech/multiplatform-locale/pull/229) ([vanniktech](https://github.com/vanniktech)) 18 | - API: Country.FRENCH_SOUTHERN_AND_ANTARCTIC_LANDS fix Calling Code. [\#241](https://github.com/vanniktech/multiplatform-locale/pull/241) ([vanniktech](https://github.com/vanniktech)) 19 | - API: Country.FRENCH_SOUTHERN_AND_ANTARCTIC_LANDS remove ANTARCTICA continent. [\#246](https://github.com/vanniktech/multiplatform-locale/pull/246) ([vanniktech](https://github.com/vanniktech)) 20 | - API: Country.GEORGIA remove EUROPE continent. [\#230](https://github.com/vanniktech/multiplatform-locale/pull/230) ([vanniktech](https://github.com/vanniktech)) 21 | - API: Country.JAMAICA fix Calling Code. [\#231](https://github.com/vanniktech/multiplatform-locale/pull/231) ([vanniktech](https://github.com/vanniktech)) 22 | - API: Country.KAZAKHSTAN fix Calling Code. [\#240](https://github.com/vanniktech/multiplatform-locale/pull/240) ([vanniktech](https://github.com/vanniktech)) 23 | - API: Country.KAZAKHSTAN remove EUROPE continent. [\#245](https://github.com/vanniktech/multiplatform-locale/pull/245) ([vanniktech](https://github.com/vanniktech)) 24 | - API: Country.KOSOVO fix code3. [\#248](https://github.com/vanniktech/multiplatform-locale/pull/248) ([vanniktech](https://github.com/vanniktech)) 25 | - API: Country.MAYOTTE Change Continent from ASIA to AFRICA. [\#233](https://github.com/vanniktech/multiplatform-locale/pull/233) ([vanniktech](https://github.com/vanniktech)) 26 | - API: Country.PITCAIRN_ISLANDS fix Calling Code. [\#234](https://github.com/vanniktech/multiplatform-locale/pull/234) ([vanniktech](https://github.com/vanniktech)) 27 | - API: Country.PUERTO_RICO fix Calling Code. [\#235](https://github.com/vanniktech/multiplatform-locale/pull/235) ([vanniktech](https://github.com/vanniktech)) 28 | - API: Country.RUSSIA remove ASIA continent. [\#236](https://github.com/vanniktech/multiplatform-locale/pull/236) ([vanniktech](https://github.com/vanniktech)) 29 | - API: Country.SINT_MAARTEN fix Calling Code. [\#237](https://github.com/vanniktech/multiplatform-locale/pull/237) ([vanniktech](https://github.com/vanniktech)) 30 | - API: Country.TRINIDAD_AND_TOBAGO Change Continent from SOUTH_AMERICA to NORTH_AMERICA [\#239](https://github.com/vanniktech/multiplatform-locale/pull/239) ([vanniktech](https://github.com/vanniktech)) 31 | - API: Country.TURKEY remove ASIA continent. [\#238](https://github.com/vanniktech/multiplatform-locale/pull/238) ([vanniktech](https://github.com/vanniktech)) 32 | - Breaking: Continent is only available on Country and limited to one. [\#256](https://github.com/vanniktech/multiplatform-locale/pull/256) ([vanniktech](https://github.com/vanniktech)) 33 | - Breaking: Extract AppleAppStoreLocale into its own multiplatform-locale-apple-app-store module. [\#220](https://github.com/vanniktech/multiplatform-locale/pull/220) ([vanniktech](https://github.com/vanniktech)) 34 | - Breaking: Extract GooglePlayStoreLocale into its own multiplatform-locale-google-play-store module. [\#218](https://github.com/vanniktech/multiplatform-locale/pull/218) ([vanniktech](https://github.com/vanniktech)) 35 | - Breaking: Move displayName() extension function to Language/Territory type. [\#217](https://github.com/vanniktech/multiplatform-locale/pull/217) ([vanniktech](https://github.com/vanniktech)) 36 | - Feature: New multiplatform-locale-all module that has all the modules bundled. [\#219](https://github.com/vanniktech/multiplatform-locale/pull/219) ([vanniktech](https://github.com/vanniktech)) 37 | - Technical: Generate Country Enum entries based on https://github.com/countries/countries [\#249](https://github.com/vanniktech/multiplatform-locale/pull/249) ([vanniktech](https://github.com/vanniktech)) 38 | - Technical: Kotlin remove deprecated Binary MemoryModel flags. [\#250](https://github.com/vanniktech/multiplatform-locale/pull/250) ([vanniktech](https://github.com/vanniktech)) 39 | - Technical: Remove deprecated embedBitcode option. [\#247](https://github.com/vanniktech/multiplatform-locale/pull/247) ([vanniktech](https://github.com/vanniktech)) 40 | - Technical: Update Kotlin to 2.1.0 [\#222](https://github.com/vanniktech/multiplatform-locale/pull/222) ([vanniktech](https://github.com/vanniktech)) 41 | 42 | Version 0.8.0 *(2024-08-02)* 43 | ---------------------------- 44 | 45 | - API: Language add Filipino. [\#178](https://github.com/vanniktech/multiplatform-locale/pull/178) ([vanniktech](https://github.com/vanniktech)) 46 | - API: Language add Icelandic. [\#191](https://github.com/vanniktech/multiplatform-locale/pull/191) ([vanniktech](https://github.com/vanniktech)) 47 | - Breaking: Sealed Territory interface with a Country and Region implementation to support for instance European Union and have a better separation. [\#197](https://github.com/vanniktech/multiplatform-locale/pull/197) ([vanniktech](https://github.com/vanniktech)) 48 | - Bug fix: In Macao Chinese is also spoken. [\#174](https://github.com/vanniktech/multiplatform-locale/pull/174) ([vanniktech](https://github.com/vanniktech)) 49 | - Technical: Automatically generate Country\#emoji based on the country code. [\#193](https://github.com/vanniktech/multiplatform-locale/pull/193) ([vanniktech](https://github.com/vanniktech)) 50 | - Technical: Use Kotlin's entries property on Enums. [\#192](https://github.com/vanniktech/multiplatform-locale/pull/192) ([vanniktech](https://github.com/vanniktech)) 51 | - Update kotlin monorepo to v2 \(major\) [\#179](https://github.com/vanniktech/multiplatform-locale/pull/179) ([renovate[bot]](https://github.com/apps/renovate)) 52 | 53 | Version 0.7.0 *(2024-03-29)* 54 | ---------------------------- 55 | 56 | - API: Language add Telugu. [\#145](https://github.com/vanniktech/multiplatform-locale/pull/145) ([vanniktech](https://github.com/vanniktech)) 57 | - Fix Emoji flag for Country.COTE\_DIVOIRE. [\#142](https://github.com/vanniktech/multiplatform-locale/pull/142) ([vanniktech](https://github.com/vanniktech)) 58 | - Fix Emoji flag for Country.PERU [\#159](https://github.com/vanniktech/multiplatform-locale/pull/159) ([vanniktech](https://github.com/vanniktech)) 59 | - Update kotlin monorepo to v1.9.23 [\#155](https://github.com/vanniktech/multiplatform-locale/pull/155) ([renovate[bot]](https://github.com/apps/renovate)) 60 | 61 | Version 0.6.0 *(2023-12-11)* 62 | ---------------------------- 63 | 64 | - API: Add list of Continent\(s\) to each Country. [\#134](https://github.com/vanniktech/multiplatform-locale/pull/134) ([vanniktech](https://github.com/vanniktech)) 65 | - API: Language add Amharic. [\#129](https://github.com/vanniktech/multiplatform-locale/pull/129) ([vanniktech](https://github.com/vanniktech)) 66 | - API: Language add Central Kmher. [\#128](https://github.com/vanniktech/multiplatform-locale/pull/128) ([vanniktech](https://github.com/vanniktech)) 67 | - API: Language add Marathi. [\#116](https://github.com/vanniktech/multiplatform-locale/pull/116) ([vanniktech](https://github.com/vanniktech)) 68 | - API: Language add Tamil. [\#112](https://github.com/vanniktech/multiplatform-locale/pull/112) ([vanniktech](https://github.com/vanniktech)) 69 | - API: Language add Urdu. [\#110](https://github.com/vanniktech/multiplatform-locale/pull/110) ([vanniktech](https://github.com/vanniktech)) 70 | - Technical: Target Android 34. [\#133](https://github.com/vanniktech/multiplatform-locale/pull/133) ([vanniktech](https://github.com/vanniktech)) 71 | - Technical: Update Kotlin to 1.9.20 & ktlint to 0.50.0 [\#103](https://github.com/vanniktech/multiplatform-locale/pull/103) ([vanniktech](https://github.com/vanniktech)) 72 | 73 | Version 0.5.0 *(2023-08-28)* 74 | ---------------------------- 75 | 76 | - API: Country add French Southern and Antartic Islands. [\#100](https://github.com/vanniktech/multiplatform-locale/pull/100) ([vanniktech](https://github.com/vanniktech)) 77 | - API: Country add Heard Island and McDonald Islands. [\#101](https://github.com/vanniktech/multiplatform-locale/pull/101) ([vanniktech](https://github.com/vanniktech)) 78 | - API: Country gets code3 property \(ISO 3166-1 alpha-3 code\) [\#99](https://github.com/vanniktech/multiplatform-locale/pull/99) ([vanniktech](https://github.com/vanniktech)) 79 | - API: Language add Belarusian. [\#76](https://github.com/vanniktech/multiplatform-locale/pull/76) ([vanniktech](https://github.com/vanniktech)) 80 | - API: Language add Malayalam. [\#79](https://github.com/vanniktech/multiplatform-locale/pull/79) ([vanniktech](https://github.com/vanniktech)) 81 | - API: Language add Tagalog. [\#102](https://github.com/vanniktech/multiplatform-locale/pull/102) ([vanniktech](https://github.com/vanniktech)) 82 | 83 | Version 0.4.0 *(2022-05-14)* 84 | ---------------------------- 85 | 86 | - API: Add missing countries \(SH, MF, PM, GS & SJ\) [\#71](https://github.com/vanniktech/multiplatform-locale/pull/71) ([vanniktech](https://github.com/vanniktech)) 87 | - API: Country - add calling codes list. [\#72](https://github.com/vanniktech/multiplatform-locale/pull/72) ([vanniktech](https://github.com/vanniktech)) 88 | - Kotlin: Use Android Source Set Layout Version 2. [\#63](https://github.com/vanniktech/multiplatform-locale/pull/63) ([vanniktech](https://github.com/vanniktech)) 89 | - Language: Add Armenian. [\#30](https://github.com/vanniktech/multiplatform-locale/pull/30) ([vanniktech](https://github.com/vanniktech)) 90 | - Language: Add Azerbaijani. [\#39](https://github.com/vanniktech/multiplatform-locale/pull/39) ([vanniktech](https://github.com/vanniktech)) 91 | - Language: Add Bokmal. [\#68](https://github.com/vanniktech/multiplatform-locale/pull/68) ([vanniktech](https://github.com/vanniktech)) 92 | - Language: Add Burmese. [\#49](https://github.com/vanniktech/multiplatform-locale/pull/49) ([vanniktech](https://github.com/vanniktech)) 93 | - Language: Add Catalan. [\#62](https://github.com/vanniktech/multiplatform-locale/pull/62) ([vanniktech](https://github.com/vanniktech)) 94 | - Language: Add Croatian. [\#32](https://github.com/vanniktech/multiplatform-locale/pull/32) ([vanniktech](https://github.com/vanniktech)) 95 | - Language: Add Irish. [\#53](https://github.com/vanniktech/multiplatform-locale/pull/53) ([vanniktech](https://github.com/vanniktech)) 96 | - Language: Add Uzbek. [\#58](https://github.com/vanniktech/multiplatform-locale/pull/58) ([vanniktech](https://github.com/vanniktech)) 97 | 98 | Version 0.3.0 *(2022-12-20)* 99 | ---------------------------- 100 | 101 | - Language: Add Farsi. [\#29](https://github.com/vanniktech/multiplatform-locale/pull/29) ([vanniktech](https://github.com/vanniktech)) 102 | - Gradle: Fix POM License. [\#28](https://github.com/vanniktech/multiplatform-locale/pull/28) ([vanniktech](https://github.com/vanniktech)) 103 | - Fix broken Podspec symlink. [\#27](https://github.com/vanniktech/multiplatform-locale/pull/27) ([vanniktech](https://github.com/vanniktech)) 104 | - Language: Add Malay. [\#26](https://github.com/vanniktech/multiplatform-locale/pull/26) ([vanniktech](https://github.com/vanniktech)) 105 | - Language: Add Thai. [\#25](https://github.com/vanniktech/multiplatform-locale/pull/25) ([vanniktech](https://github.com/vanniktech)) 106 | - Language: Add Hindi & Gujarati. [\#22](https://github.com/vanniktech/multiplatform-locale/pull/22) ([vanniktech](https://github.com/vanniktech)) 107 | - Gradle: Set Jvm Toolchain to 11. [\#21](https://github.com/vanniktech/multiplatform-locale/pull/21) ([vanniktech](https://github.com/vanniktech)) 108 | - API: Languages\#currentLanguageCode which returns the current ISO 639-1 language code. [\#19](https://github.com/vanniktech/multiplatform-locale/pull/19) ([vanniktech](https://github.com/vanniktech)) 109 | - API: Countries\#currentCountryCode which returns the current optional ISO 3166-1 alpha-2 country code. [\#18](https://github.com/vanniktech/multiplatform-locale/pull/18) ([vanniktech](https://github.com/vanniktech)) 110 | - Locale: Fix parsing of strings like 'ar\_EG\_\#u-nu-latn' [\#14](https://github.com/vanniktech/multiplatform-locale/pull/14) ([vanniktech](https://github.com/vanniktech)) 111 | - Behavior Change: Locale\#fromAndroidValuesDirectoryNameOrNull don't require values prefix. [\#12](https://github.com/vanniktech/multiplatform-locale/pull/12) ([vanniktech](https://github.com/vanniktech)) 112 | 113 | Version 0.2.0 *(2022-11-08)* 114 | ---------------------------- 115 | 116 | - API: Rename Country\#identifier to Country\#code. [\#11](https://github.com/vanniktech/multiplatform-locale/pull/11) ([vanniktech](https://github.com/vanniktech)) 117 | - API: Rename Language\#identifier to Language\#code. [\#10](https://github.com/vanniktech/multiplatform-locale/pull/10) ([vanniktech](https://github.com/vanniktech)) 118 | - API: Remove inferDefaultCountry overload in Locale from methods. [\#9](https://github.com/vanniktech/multiplatform-locale/pull/9) ([vanniktech](https://github.com/vanniktech)) 119 | - API: Locale\#fromAndroidValuesDirectoryName function. [\#8](https://github.com/vanniktech/multiplatform-locale/pull/8) ([vanniktech](https://github.com/vanniktech)) 120 | - API: Language rename FINISH to FINNISH \(fixing a typo\) [\#7](https://github.com/vanniktech/multiplatform-locale/pull/7) ([vanniktech](https://github.com/vanniktech)) 121 | - Behavior Change: Language uses ISO 639-1 code with backwards compatible support for Hebrew & Indonesian. [\#6](https://github.com/vanniktech/multiplatform-locale/pull/6) ([vanniktech](https://github.com/vanniktech)) 122 | - API: Language\#from & Language\#fromLocale. [\#5](https://github.com/vanniktech/multiplatform-locale/pull/5) ([vanniktech](https://github.com/vanniktech)) 123 | - Behavior Change: Locale\#googlePlayStoreLocale & Locale\#appleAppStoreLocale prefer Locale from Language\#defaultCountry in case there are multiple options. [\#4](https://github.com/vanniktech/multiplatform-locale/pull/4) ([vanniktech](https://github.com/vanniktech)) 124 | - Behavior Change: Locale\#googlePlayStoreLocale & Locale\#appleAppStoreLocale will return a Locale if countries mismatch but language is the same. [\#3](https://github.com/vanniktech/multiplatform-locale/pull/3) ([vanniktech](https://github.com/vanniktech)) 125 | - API: Locale is now Comparable. [\#2](https://github.com/vanniktech/multiplatform-locale/pull/2) ([vanniktech](https://github.com/vanniktech)) 126 | - API: Locale.fromAndroidValuesDirectoryNameOrNull [\#1](https://github.com/vanniktech/multiplatform-locale/pull/1) ([vanniktech](https://github.com/vanniktech)) 127 | 128 | Version 0.1.0 *(2022-11-07)* 129 | ---------------------------- 130 | 131 | - Initial release 132 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MultiplatformLocale.podspec: -------------------------------------------------------------------------------- 1 | multiplatform-locale/MultiplatformLocale.podspec -------------------------------------------------------------------------------- /MultiplatformLocaleAll.podspec: -------------------------------------------------------------------------------- 1 | multiplatform-locale-all/MultiplatformLocaleAll.podspec -------------------------------------------------------------------------------- /MultiplatformLocaleGeo.podspec: -------------------------------------------------------------------------------- 1 | multiplatform-locale-geo/MultiplatformLocaleGeo.podspec -------------------------------------------------------------------------------- /MultiplatformLocaleGooglePlayStore.podspec: -------------------------------------------------------------------------------- 1 | multiplatform-locale-google-play-store/MultiplatformLocaleGooglePlayStore.podspec -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Multiplatform Locale 2 | ==================== 3 | 4 | A Type Safe Kotlin Multiplatform Locale implementation for your Android App, iOS / Mac App & JVM Backend. 5 | 6 | | `Android` | `iOS` | `JVM` | 7 | |:--------------------------------------|:------------------------------|:------------------------------| 8 | | ![Sample Android](sample-android.png) | ![Sample iOS](sample-ios.png) | ![Sample JVM](sample-jvm.png) | 9 | 10 | # Usage 11 | 12 | From Kotlin Multiplatform: 13 | 14 | ```groovy 15 | kotlin { 16 | sourceSets { 17 | val commonMain by getting { 18 | dependencies { 19 | implementation("com.vanniktech:multiplatform-locale:0.9.0") 20 | } 21 | } 22 | } 23 | } 24 | ``` 25 | 26 | From Android / JVM Multiplatform: 27 | 28 | ```groovy 29 | dependencies { 30 | implementation("com.vanniktech:multiplatform-locale:0.9.0") 31 | } 32 | ``` 33 | 34 | From iOS: 35 | 36 | ```ruby 37 | pod 'MultiplatformLocale', :git => 'https://github.com/vanniktech/multiplatform-locale', :tag => "0.9.0" 38 | ``` 39 | 40 | # API 41 | 42 | Use `com.vanniktech.locale.Locales`, `com.vanniktech.locale.Country`, `com.vanniktech.locale.Language` & `com.vanniktech.locale.Locale` directly in your platform specific code: 43 | 44 | - [sample-android](./sample-android/src/main/kotlin/com/vanniktech/locale/sample/android/LocaleMainActivity.kt) 45 | - [sample-ios](./sample-ios/ios/App.swift) 46 | - [sample-jvm](sample-jvm/src/main/java/com/vanniktech/locale/sample/jvm/LocaleJvm.kt) 47 | 48 | # License 49 | 50 | Copyright (C) 2022 - Niklas Baudy 51 | 52 | Licensed under the Apache License, Version 2.0 53 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | google() 5 | gradlePluginPortal() 6 | } 7 | dependencies { 8 | classpath(libs.plugin.android.cache.fix) 9 | classpath(libs.plugin.androidgradleplugin) 10 | classpath(libs.plugin.dokka) 11 | classpath(libs.plugin.kotlin) 12 | classpath(libs.plugin.licensee) 13 | classpath(libs.plugin.metalava) 14 | classpath(libs.plugin.publish) 15 | } 16 | } 17 | 18 | plugins { 19 | alias(libs.plugins.codequalitytools) 20 | alias(libs.plugins.dependencygraphgenerator) 21 | alias(libs.plugins.kotlinSerialization) apply false 22 | } 23 | 24 | codeQualityTools { 25 | checkstyle { 26 | enabled = false // Kotlin only. 27 | } 28 | pmd { 29 | enabled = false // Kotlin only. 30 | } 31 | ktlint { 32 | toolVersion = libs.versions.ktlint.get() 33 | } 34 | detekt { 35 | enabled = false // Don"t want. 36 | } 37 | cpd { 38 | enabled = false // Kotlin only. 39 | } 40 | lint { 41 | lintConfig = rootProject.file("lint.xml") 42 | checkAllWarnings = true 43 | } 44 | kotlin { 45 | allWarningsAsErrors = true 46 | } 47 | } 48 | 49 | allprojects { 50 | repositories { 51 | google() 52 | mavenCentral() 53 | gradlePluginPortal() 54 | } 55 | } 56 | 57 | subprojects { 58 | plugins.withType { 59 | apply(plugin = "org.gradle.android.cache-fix") 60 | } 61 | 62 | tasks.withType(Test::class.java).all { 63 | testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /generator/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.jvm") 3 | application 4 | alias(libs.plugins.kotlinSerialization) 5 | } 6 | 7 | application { 8 | mainClass.set("com.vanniktech.locale.generator.MainKt") 9 | } 10 | 11 | dependencies { 12 | implementation(libs.okio) 13 | implementation(libs.kaml) 14 | } 15 | -------------------------------------------------------------------------------- /generator/src/main/kotlin/com/vanniktech/locale/generator/main.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.generator 2 | 3 | import com.charleskorn.kaml.Yaml 4 | import com.charleskorn.kaml.YamlConfiguration 5 | import kotlinx.serialization.SerialName 6 | import kotlinx.serialization.Serializable 7 | import kotlinx.serialization.builtins.MapSerializer 8 | import kotlinx.serialization.builtins.serializer 9 | import okio.FileSystem 10 | import okio.Path.Companion.toPath 11 | 12 | fun main() { 13 | // Adjust the path of the https://github.com/countries/countries repository as needed. 14 | val githubDirectory = "/Users/niklas/dev/GitHub/".toPath() 15 | val countriesPath = githubDirectory / "countries/lib/countries/data/countries".toPath() 16 | val fileSystem = FileSystem.SYSTEM 17 | 18 | val overwrittenNames = mutableMapOf( 19 | // Names that we want to keep. 20 | "BOLIVIA_PLURINATIONAL_STATE_OF" to "BOLIVIA", 21 | "BONAIRE_SINT_EUSTATIUS_AND_SABA" to "BONAIRE", 22 | "BRUNEI_DARUSSALAM" to "BRUNEI", 23 | "CURAÇAO" to "CURACAO", 24 | "CÔTE_DIVOIRE" to "COTE_DIVOIRE", 25 | "HOLY_SEE" to "VATICAN_CITY", 26 | "IRAN_ISLAMIC_REPUBLIC_OF" to "IRAN", 27 | "KOREA_DEMOCRATIC_PEOPLES_REPUBLIC_OF" to "NORTH_KOREA", 28 | "KOREA_REPUBLIC_OF" to "SOUTH_KOREA", 29 | "LAO_PEOPLES_DEMOCRATIC_REPUBLIC" to "LAOS", 30 | "MICRONESIA_FEDERATED_STATES_OF" to "MICRONESIA", 31 | "MOLDOVA_REPUBLIC_OF" to "MOLDOVA", 32 | "PALESTINE_STATE_OF" to "PALESTINE", 33 | "RUSSIAN_FEDERATION" to "RUSSIA", 34 | "RÉUNION" to "REUNION", 35 | "SAINT_MARTIN_FRENCH_PART" to "SAINT_MARTIN", 36 | "SINT_MAARTEN_DUTCH_PART" to "SINT_MAARTEN", 37 | "SYRIAN_ARAB_REPUBLIC" to "SYRIA", 38 | "TAIWAN_PROVINCE_OF_CHINA" to "TAIWAN", 39 | "TANZANIA_UNITED_REPUBLIC_OF" to "TANZANIA", 40 | "TÜRKIYE" to "TURKEY", 41 | "UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND" to "ENGLAND", 42 | "UNITED_STATES_OF_AMERICA" to "USA", 43 | "VENEZUELA_BOLIVARIAN_REPUBLIC_OF" to "VENEZUELA", 44 | "VIET_NAM" to "VIETNAM", 45 | "ÅLAND_ISLANDS" to "ALAND_ISLANDS", 46 | 47 | // Congo. 48 | "CONGO" to "REPUBLIC_OF_THE_CONGO", // This might be fine. 49 | "CONGO_DEMOCRATIC_REPUBLIC_OF_THE" to "DEMOCRATIC_REPUBLIC_OF_CONGO", // Should be DEMOCRATIC_REPUBLIC_OF_THE_CONGO. 50 | 51 | // Names that we might align at some point. 52 | "CABO_VERDE" to "CAPE_VERDE", 53 | "COCOS_KEELING_ISLANDS" to "COCOS_ISLANDS", 54 | "FALKLAND_ISLANDS_MALVINAS" to "FALKLAND_ISLANDS", 55 | "FRENCH_SOUTHERN_TERRITORIES" to "FRENCH_SOUTHERN_AND_ANTARCTIC_LANDS", 56 | "MARSHALL_ISLANDS" to "MARSHALL_ISLAND", 57 | "PITCAIRN" to "PITCAIRN_ISLANDS", 58 | "SAINT_BARTHÉLEMY" to "SAINT_BARTS", // Should be SAINT_BARTHELEMY. 59 | "SAO_TOME_AND_PRINCIPE" to "SAO_TOME_AND_PRINCE", 60 | "TURKS_AND_CAICOS_ISLANDS" to "TURKS_AND_CAICOS", 61 | "VIRGIN_ISLANDS_BRITISH" to "BRITISH_VIRGIN_ISLANDS", 62 | "VIRGIN_ISLANDS_US" to "US_VIRGIN_ISLANDS", 63 | "WESTERN_SAHARA" to "SAHRAWI_ARAB_DEMOCRATIC_REPUBLIC", 64 | ) 65 | 66 | val yaml = Yaml(configuration = YamlConfiguration(strictMode = false)) 67 | val serializer = MapSerializer(String.serializer(), CountryDto.serializer()) 68 | val parsedCountries = fileSystem.list(countriesPath) 69 | .flatMap { yaml.decodeFromSource(serializer, fileSystem.source(countriesPath / it)).values } 70 | .map { 71 | val fallback = it.isoShortName.uppercase() 72 | .replace(" ", "_") 73 | .replace(",", "") 74 | .replace("-", "_") 75 | .replace(".", "") 76 | .replace("'", "") 77 | .replace("(", "") 78 | .replace(")", "") 79 | 80 | it.copy( 81 | isoShortName = overwrittenNames.remove(fallback) ?: fallback, 82 | ) 83 | } 84 | 85 | val otherCountries = listOf( 86 | // https://github.com/countries/countries/issues/793#issuecomment-2027531814 87 | CountryDto( 88 | alpha2 = "XK", 89 | alpha3 = "XKX", 90 | countryCode = "383", 91 | isoShortName = "KOSOVO", 92 | continent = "EUROPE", 93 | geo = Geo( 94 | latitude = 42.5833, 95 | longitude = 21.0001, 96 | maxLatitude = 43.139, 97 | maxLongitude = 21.835, 98 | minLatitude = 41.877, 99 | minLongitude = 19.949, 100 | ), 101 | ), 102 | ) 103 | 104 | val countries = (parsedCountries + otherCountries).sortedBy { it.isoShortName } 105 | val enumOutput = countries 106 | .map { 107 | val callingCode = it.nanpPrefix ?: it.countryCode 108 | val continent = it.continent.uppercase() 109 | .replace(" ", "_") 110 | .replace("AUSTRALIA", "OCEANIA") 111 | 112 | "${it.isoShortName}(code = \"${it.alpha2}\", code3 = \"${it.alpha3}\", callingCodes = listOf(\"+$callingCode\"), continent = Continent.$continent)," 113 | } 114 | 115 | require(overwrittenNames.isEmpty()) { "Unmatched overwritten names: $overwrittenNames" } 116 | println(enumOutput.joinToString(separator = "\n")) 117 | 118 | fileSystem.write(githubDirectory / "multiplatform-locale/multiplatform-locale-geo/src/commonMain/kotlin/com/vanniktech/locale/geo/CountryGeo.kt") { 119 | writeUtf8( 120 | """ 121 | |package com.vanniktech.locale.geo 122 | | 123 | |import com.vanniktech.locale.Country 124 | | 125 | | 126 | """.trimMargin(), 127 | ) 128 | 129 | countries.forEach { 130 | writeUtf8( 131 | """ 132 | |private val GEO_${it.isoShortName} = Geo( 133 | | latitude = ${it.geo.latitude}, 134 | | longitude = ${it.geo.longitude}, 135 | | maxLatitude = ${it.geo.maxLatitude}, 136 | | maxLongitude = ${it.geo.maxLongitude}, 137 | | minLatitude = ${it.geo.minLatitude}, 138 | | minLongitude = ${it.geo.minLongitude}, 139 | |) 140 | | 141 | | 142 | """.trimMargin(), 143 | ) 144 | } 145 | 146 | writeUtf8( 147 | """ 148 | |val Country.geo get() = when (this) { 149 | | 150 | """.trimMargin(), 151 | ) 152 | 153 | countries.forEach { 154 | writeUtf8( 155 | """ 156 | | Country.${it.isoShortName} -> GEO_${it.isoShortName} 157 | | 158 | """.trimMargin(), 159 | ) 160 | } 161 | 162 | writeUtf8( 163 | """ 164 | |} 165 | | 166 | """.trimMargin(), 167 | ) 168 | } 169 | } 170 | 171 | // https://github.com/countries/countries/blob/master/lib/countries/data/countries/TL.yaml 172 | @Serializable data class CountryDto( 173 | @SerialName("alpha2") val alpha2: String, 174 | @SerialName("alpha3") val alpha3: String, 175 | @SerialName("country_code") val countryCode: String, 176 | @SerialName("nanp_prefix") val nanpPrefix: String? = null, 177 | @SerialName("iso_short_name") val isoShortName: String, 178 | @SerialName("continent") val continent: String, 179 | @SerialName("geo") val geo: Geo, 180 | ) 181 | 182 | @Serializable data class Geo( 183 | @SerialName("latitude") val latitude: Double, 184 | @SerialName("longitude") val longitude: Double, 185 | @SerialName("max_latitude") val maxLatitude: Double, 186 | @SerialName("max_longitude") val maxLongitude: Double, 187 | @SerialName("min_latitude") val minLatitude: Double, 188 | @SerialName("min_longitude") val minLongitude: Double, 189 | ) 190 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | GROUP=com.vanniktech 2 | VERSION_NAME=0.10.0-SNAPSHOT 3 | 4 | POM_DESCRIPTION=Type Safe Kotlin Multiplatform Locale implementation 5 | 6 | POM_URL=https://github.com/vanniktech/multiplatform-locale 7 | POM_SCM_URL=https://github.com/vanniktech/multiplatform-locale 8 | POM_SCM_CONNECTION=scm:git:git://github.com/vanniktech/multiplatform-locale.git 9 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/vanniktech/multiplatform-locale.git 10 | 11 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 12 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 13 | POM_LICENCE_DIST=repo 14 | 15 | POM_DEVELOPER_ID=vanniktech 16 | POM_DEVELOPER_NAME=Niklas Baudy 17 | 18 | android.useAndroidX=true 19 | # We are solely relying on AndroidX. 20 | android.enableJetifier=false 21 | 22 | android.experimental.enableSourceSetPathsMap=true 23 | android.experimental.cacheCompileLibResources=true 24 | android.suppressUnsupportedOptionWarnings=android.suppressUnsupportedOptionWarnings,android.experimental.enableSourceSetPathsMap,android.experimental.cacheCompileLibResources 25 | 26 | SONATYPE_HOST=DEFAULT 27 | SONATYPE_AUTOMATIC_RELEASE=true 28 | RELEASE_SIGNING_ENABLED=true 29 | 30 | org.gradle.jvmargs=-Xmx2048m 31 | 32 | android.defaults.buildfeatures.resvalues=false 33 | android.defaults.buildfeatures.shaders=false 34 | 35 | kotlin.mpp.stability.nowarn=true 36 | kotlin.mpp.androidSourceSetLayoutVersion=2 37 | 38 | # https://youtrack.jetbrains.com/issue/KT-42254 39 | kotlin.native.cacheKind=none 40 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | minSdk = "21" 3 | compileSdk = "34" 4 | targetSdk = "34" 5 | 6 | androidgradleplugin = "8.9.2" 7 | kotlin = "2.1.21" 8 | ktlint = "1.4.1" 9 | 10 | [libraries] 11 | kaml = "com.charleskorn.kaml:kaml:0.80.1" 12 | kotlin-test-annotations-common = { module = "org.jetbrains.kotlin:kotlin-test-annotations-common", version.ref = "kotlin" } 13 | kotlin-test-common = { module = "org.jetbrains.kotlin:kotlin-test-common", version.ref = "kotlin" } 14 | kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } 15 | leakcanary-android = "com.squareup.leakcanary:leakcanary-android:2.14" 16 | material = "com.google.android.material:material:1.12.0" 17 | okio = "com.squareup.okio:okio:3.12.0" 18 | plugin-android-cache-fix = "org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin:3.0.1" 19 | plugin-androidgradleplugin = { module = "com.android.tools.build:gradle", version.ref = "androidgradleplugin" } 20 | plugin-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0" 21 | plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } 22 | plugin-licensee = "app.cash.licensee:licensee-gradle-plugin:1.13.0" 23 | plugin-metalava = "me.tylerbwong.gradle.metalava:plugin:0.4.0-alpha03" 24 | plugin-publish = "com.vanniktech:gradle-maven-publish-plugin:0.32.0" 25 | timber = "com.jakewharton.timber:timber:5.0.1" 26 | 27 | [plugins] 28 | codequalitytools = { id = "com.vanniktech.code.quality.tools", version = "0.24.0" } 29 | dependencygraphgenerator = { id = "com.vanniktech.dependency.graph.generator", version = "0.8.0" } 30 | kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } 31 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /multiplatform-locale-all/MultiplatformLocaleAll.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'MultiplatformLocaleAll' 3 | spec.version = '0.10.0-SNAPSHOT' 4 | spec.homepage = 'https://github.com/vanniktech/multiplatform-locale' 5 | spec.source = { :http=> ''} 6 | spec.authors = 'Niklas Baudy' 7 | spec.license = 'MIT' 8 | spec.summary = 'Multiplatform Locale All for iOS, Android and JVM via Kotlin Multiplatform' 9 | spec.vendored_frameworks = 'build/cocoapods/framework/multiplatform_locale_all.framework' 10 | spec.libraries = 'c++' 11 | 12 | 13 | 14 | if !Dir.exist?('build/cocoapods/framework/multiplatform_locale_all.framework') || Dir.empty?('build/cocoapods/framework/multiplatform_locale_all.framework') 15 | raise " 16 | 17 | Kotlin framework 'multiplatform_locale_all' doesn't exist yet, so a proper Xcode project can't be generated. 18 | 'pod install' should be executed after running ':generateDummyFramework' Gradle task: 19 | 20 | ./gradlew :multiplatform-locale-all:generateDummyFramework 21 | 22 | Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" 23 | end 24 | 25 | spec.xcconfig = { 26 | 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', 27 | } 28 | 29 | spec.pod_target_xcconfig = { 30 | 'KOTLIN_PROJECT_PATH' => ':multiplatform-locale-all', 31 | 'PRODUCT_MODULE_NAME' => 'multiplatform_locale_all', 32 | } 33 | 34 | spec.script_phases = [ 35 | { 36 | :name => 'Build MultiplatformLocaleAll', 37 | :execution_position => :before_compile, 38 | :shell_path => '/bin/sh', 39 | :script => <<-SCRIPT 40 | if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then 41 | echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" 42 | exit 0 43 | fi 44 | set -ev 45 | REPO_ROOT="$PODS_TARGET_SRCROOT" 46 | "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ 47 | -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ 48 | -Pkotlin.native.cocoapods.archs="$ARCHS" \ 49 | -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" 50 | SCRIPT 51 | } 52 | ] 53 | 54 | end -------------------------------------------------------------------------------- /multiplatform-locale-all/api/current.txt: -------------------------------------------------------------------------------- 1 | // Signature format: 4.0 2 | -------------------------------------------------------------------------------- /multiplatform-locale-all/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.dokka") 3 | id("org.jetbrains.kotlin.multiplatform") 4 | id("org.jetbrains.kotlin.native.cocoapods") 5 | id("com.android.library") 6 | id("me.tylerbwong.gradle.metalava") 7 | id("com.vanniktech.maven.publish") 8 | id("app.cash.licensee") 9 | } 10 | 11 | licensee { 12 | allow("Apache-2.0") 13 | } 14 | 15 | metalava { 16 | filename.set("api/current.txt") 17 | } 18 | 19 | kotlin { 20 | applyDefaultHierarchyTemplate() 21 | 22 | androidTarget { 23 | publishLibraryVariants("release") 24 | } 25 | jvm() 26 | jvmToolchain(11) 27 | iosX64() 28 | iosArm64() 29 | iosSimulatorArm64() 30 | 31 | sourceSets { 32 | val commonMain by getting { 33 | dependencies { 34 | api(project(":multiplatform-locale")) 35 | api(project(":multiplatform-locale-apple-app-store")) 36 | api(project(":multiplatform-locale-geo")) 37 | api(project(":multiplatform-locale-google-play-store")) 38 | } 39 | } 40 | } 41 | 42 | cocoapods { 43 | summary = "Multiplatform Locale All for iOS, Android and JVM via Kotlin Multiplatform" 44 | homepage = "https://github.com/vanniktech/multiplatform-locale" 45 | license = "MIT" 46 | name = "MultiplatformLocaleAll" 47 | authors = "Niklas Baudy" 48 | version = project.property("VERSION_NAME").toString() 49 | 50 | framework { 51 | isStatic = true 52 | export(project(":multiplatform-locale")) 53 | export(project(":multiplatform-locale-apple-app-store")) 54 | export(project(":multiplatform-locale-geo")) 55 | export(project(":multiplatform-locale-google-play-store")) 56 | } 57 | } 58 | } 59 | 60 | android { 61 | namespace = "com.vanniktech.locale.all" 62 | 63 | compileSdk = libs.versions.compileSdk.get().toInt() 64 | 65 | defaultConfig { 66 | minSdk = libs.versions.minSdk.get().toInt() 67 | } 68 | 69 | compileOptions { 70 | sourceCompatibility = JavaVersion.VERSION_11 71 | targetCompatibility = JavaVersion.VERSION_11 72 | } 73 | 74 | resourcePrefix = "locale_all_" 75 | } 76 | -------------------------------------------------------------------------------- /multiplatform-locale-all/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Locale All 2 | POM_ARTIFACT_ID=multiplatform-locale-all 3 | -------------------------------------------------------------------------------- /multiplatform-locale-all/src/commonMain/kotlin/com/vanniktech/locale/all/iosworkaround.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("standard:no-empty-file") 2 | 3 | package com.vanniktech.locale.all 4 | 5 | // Empty file is required for iOS, otherwise it fails. 6 | -------------------------------------------------------------------------------- /multiplatform-locale-apple-app-store/MultiplatformLocaleAppleAppStore.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'MultiplatformLocaleAppleAppStore' 3 | spec.version = '0.10.0-SNAPSHOT' 4 | spec.homepage = 'https://github.com/vanniktech/multiplatform-locale' 5 | spec.source = { :http=> ''} 6 | spec.authors = 'Niklas Baudy' 7 | spec.license = 'MIT' 8 | spec.summary = 'Multiplatform Locale Apple App Store for iOS, Android and JVM via Kotlin Multiplatform' 9 | spec.vendored_frameworks = 'build/cocoapods/framework/multiplatform_locale_apple_app_store.framework' 10 | spec.libraries = 'c++' 11 | 12 | 13 | 14 | if !Dir.exist?('build/cocoapods/framework/multiplatform_locale_apple_app_store.framework') || Dir.empty?('build/cocoapods/framework/multiplatform_locale_apple_app_store.framework') 15 | raise " 16 | 17 | Kotlin framework 'multiplatform_locale_apple_app_store' doesn't exist yet, so a proper Xcode project can't be generated. 18 | 'pod install' should be executed after running ':generateDummyFramework' Gradle task: 19 | 20 | ./gradlew :multiplatform-locale-apple-app-store:generateDummyFramework 21 | 22 | Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" 23 | end 24 | 25 | spec.xcconfig = { 26 | 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', 27 | } 28 | 29 | spec.pod_target_xcconfig = { 30 | 'KOTLIN_PROJECT_PATH' => ':multiplatform-locale-apple-app-store', 31 | 'PRODUCT_MODULE_NAME' => 'multiplatform_locale_apple_app_store', 32 | } 33 | 34 | spec.script_phases = [ 35 | { 36 | :name => 'Build MultiplatformLocaleAppleAppStore', 37 | :execution_position => :before_compile, 38 | :shell_path => '/bin/sh', 39 | :script => <<-SCRIPT 40 | if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then 41 | echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" 42 | exit 0 43 | fi 44 | set -ev 45 | REPO_ROOT="$PODS_TARGET_SRCROOT" 46 | "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ 47 | -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ 48 | -Pkotlin.native.cocoapods.archs="$ARCHS" \ 49 | -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" 50 | SCRIPT 51 | } 52 | ] 53 | 54 | end -------------------------------------------------------------------------------- /multiplatform-locale-apple-app-store/api/current.txt: -------------------------------------------------------------------------------- 1 | // Signature format: 4.0 2 | package com.vanniktech.locale.apple.app.store { 3 | 4 | public enum AppleAppStoreLocale { 5 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale ar_SA; 6 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale ca; 7 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale cs; 8 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale da; 9 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale de_DE; 10 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale el; 11 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale en_AU; 12 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale en_CA; 13 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale en_GB; 14 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale en_US; 15 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale es_ES; 16 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale es_MX; 17 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale fi; 18 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale fr_CA; 19 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale fr_FR; 20 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale he; 21 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale hi; 22 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale hr; 23 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale hu; 24 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale id; 25 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale it; 26 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale ja; 27 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale ko; 28 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale ms; 29 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale nl_NL; 30 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale no; 31 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale pl; 32 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale pt_BR; 33 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale pt_PT; 34 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale ro; 35 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale ru; 36 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale sk; 37 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale sv; 38 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale th; 39 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale tr; 40 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale uk; 41 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale vi; 42 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale zh_Hans; 43 | enum_constant public static final com.vanniktech.locale.apple.app.store.AppleAppStoreLocale zh_Hant; 44 | } 45 | 46 | public final class AppleAppStoreLocaleKt { 47 | method public static com.vanniktech.locale.apple.app.store.AppleAppStoreLocale? appleAppStoreLocale(com.vanniktech.locale.Locale); 48 | } 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /multiplatform-locale-apple-app-store/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.dokka") 3 | id("org.jetbrains.kotlin.multiplatform") 4 | id("org.jetbrains.kotlin.native.cocoapods") 5 | id("com.android.library") 6 | id("me.tylerbwong.gradle.metalava") 7 | id("com.vanniktech.maven.publish") 8 | id("app.cash.licensee") 9 | } 10 | 11 | licensee { 12 | allow("Apache-2.0") 13 | } 14 | 15 | metalava { 16 | filename.set("api/current.txt") 17 | } 18 | 19 | kotlin { 20 | applyDefaultHierarchyTemplate() 21 | 22 | androidTarget { 23 | publishLibraryVariants("release") 24 | } 25 | jvm() 26 | jvmToolchain(11) 27 | iosX64() 28 | iosArm64() 29 | iosSimulatorArm64() 30 | 31 | targets.withType { 32 | compilations["main"].kotlinOptions.freeCompilerArgs += "-Xexport-kdoc" 33 | } 34 | 35 | sourceSets { 36 | val commonMain by getting { 37 | dependencies { 38 | api(project(":multiplatform-locale")) 39 | } 40 | } 41 | 42 | val commonTest by getting { 43 | dependencies { 44 | implementation(libs.kotlin.test.common) 45 | implementation(libs.kotlin.test.annotations.common) 46 | } 47 | } 48 | 49 | val androidUnitTest by getting { 50 | dependencies { 51 | implementation(libs.kotlin.test.junit) 52 | } 53 | } 54 | 55 | val jvmTest by getting { 56 | dependencies { 57 | implementation(libs.kotlin.test.junit) 58 | } 59 | } 60 | } 61 | 62 | cocoapods { 63 | summary = "Multiplatform Locale Apple App Store for iOS, Android and JVM via Kotlin Multiplatform" 64 | homepage = "https://github.com/vanniktech/multiplatform-locale" 65 | license = "MIT" 66 | name = "MultiplatformLocaleAppleAppStore" 67 | authors = "Niklas Baudy" 68 | version = project.property("VERSION_NAME").toString() 69 | 70 | framework { 71 | isStatic = true 72 | export(project(":multiplatform-locale")) 73 | } 74 | } 75 | } 76 | 77 | android { 78 | namespace = "com.vanniktech.locale.apple.app.store" 79 | 80 | compileSdk = libs.versions.compileSdk.get().toInt() 81 | 82 | defaultConfig { 83 | minSdk = libs.versions.minSdk.get().toInt() 84 | } 85 | 86 | compileOptions { 87 | sourceCompatibility = JavaVersion.VERSION_11 88 | targetCompatibility = JavaVersion.VERSION_11 89 | } 90 | 91 | resourcePrefix = "locale_apple_app_store_" 92 | } 93 | -------------------------------------------------------------------------------- /multiplatform-locale-apple-app-store/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Locale Apple App Store 2 | POM_ARTIFACT_ID=multiplatform-locale-apple-app-store 3 | -------------------------------------------------------------------------------- /multiplatform-locale-apple-app-store/src/commonMain/kotlin/com/vanniktech/locale/apple/app/store/AppleAppStoreLocale.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.apple.app.store 2 | 3 | import com.vanniktech.locale.Country 4 | import com.vanniktech.locale.Language 5 | import com.vanniktech.locale.Locale 6 | import com.vanniktech.locale.Locale.Companion.fromOrNull 7 | 8 | /** All the locales supported by the Apple App Store. */ 9 | @Suppress("ktlint:standard:enum-entry-name-case") enum class AppleAppStoreLocale { 10 | ar_SA, 11 | ca, 12 | cs, 13 | da, 14 | de_DE, 15 | el, 16 | en_AU, 17 | en_CA, 18 | en_GB, 19 | en_US, 20 | es_ES, 21 | es_MX, 22 | fi, 23 | fr_CA, 24 | fr_FR, 25 | he, 26 | hi, 27 | hr, 28 | hu, 29 | id, 30 | it, 31 | ja, 32 | ko, 33 | ms, 34 | nl_NL, 35 | no, 36 | pl, 37 | pt_BR, 38 | pt_PT, 39 | ro, 40 | ru, 41 | sk, 42 | sv, 43 | th, 44 | tr, 45 | uk, 46 | vi, 47 | zh_Hans, 48 | zh_Hant, 49 | ; 50 | 51 | override fun toString() = name.replace("_", "-") 52 | } 53 | 54 | /** Returns the optional [AppleAppStoreLocale] that can be used for localizing the Apple App Store. */ 55 | fun Locale.appleAppStoreLocale(): AppleAppStoreLocale? { 56 | val optimized = Locale( 57 | language = language, 58 | territory = territory ?: language.defaultCountry, 59 | ) 60 | return AppleAppStoreLocale.entries 61 | .groupBy { Language.fromLocaleOrNull(it.toString()) } 62 | .firstNotNullOfOrNull { (key, locales) -> 63 | locales.firstNotNullOfOrNull { locale -> 64 | locale.takeIf { 65 | val isChineseTaiwan = it.name == "zh_Hant" && language == Language.CHINESE && territory == Country.TAIWAN 66 | fromOrNull(it.name) == optimized || isChineseTaiwan 67 | } 68 | } ?: locales.firstNotNullOfOrNull { locale -> locale.takeIf { language == key } } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /multiplatform-locale-apple-app-store/src/commonTest/kotlin/com/vanniktech/locale/apple/app/store/AppleAppStoreLocaleTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.apple.app.store 2 | 3 | import com.vanniktech.locale.Country 4 | import com.vanniktech.locale.Language 5 | import com.vanniktech.locale.Locale 6 | import kotlin.test.Test 7 | import kotlin.test.assertEquals 8 | 9 | class AppleAppStoreLocaleTest { 10 | @Test fun appleAppStoreLocaleStringAllPossibilities() { 11 | assertEquals( 12 | expected = listOf( 13 | AppleAppStoreLocale.ar_SA, 14 | AppleAppStoreLocale.zh_Hans, 15 | AppleAppStoreLocale.zh_Hant, 16 | AppleAppStoreLocale.ca, 17 | AppleAppStoreLocale.cs, 18 | AppleAppStoreLocale.hr, 19 | AppleAppStoreLocale.da, 20 | AppleAppStoreLocale.nl_NL, 21 | AppleAppStoreLocale.en_US, 22 | AppleAppStoreLocale.en_CA, 23 | AppleAppStoreLocale.en_AU, 24 | AppleAppStoreLocale.en_GB, 25 | AppleAppStoreLocale.fi, 26 | AppleAppStoreLocale.fr_FR, 27 | AppleAppStoreLocale.fr_CA, 28 | AppleAppStoreLocale.de_DE, 29 | AppleAppStoreLocale.el, 30 | AppleAppStoreLocale.he, 31 | AppleAppStoreLocale.hi, 32 | AppleAppStoreLocale.hu, 33 | AppleAppStoreLocale.id, 34 | AppleAppStoreLocale.it, 35 | AppleAppStoreLocale.ja, 36 | AppleAppStoreLocale.ko, 37 | AppleAppStoreLocale.ms, 38 | AppleAppStoreLocale.no, 39 | AppleAppStoreLocale.pl, 40 | AppleAppStoreLocale.pt_PT, 41 | AppleAppStoreLocale.pt_BR, 42 | AppleAppStoreLocale.ro, 43 | AppleAppStoreLocale.ru, 44 | AppleAppStoreLocale.sk, 45 | AppleAppStoreLocale.es_ES, 46 | AppleAppStoreLocale.es_MX, 47 | AppleAppStoreLocale.sv, 48 | AppleAppStoreLocale.th, 49 | AppleAppStoreLocale.tr, 50 | AppleAppStoreLocale.uk, 51 | AppleAppStoreLocale.vi, 52 | ), 53 | actual = Language.entries.flatMap { language -> (language.officialTerritories + language.otherTerritories).mapNotNull { Locale(language, it).appleAppStoreLocale() } }.distinct(), 54 | ) 55 | } 56 | 57 | @Test fun appleAppStoreLocaleStringLenient() { 58 | // Swedish only has one GooglePlayStoreLocale, so we'll match regardless of Country. 59 | assertEquals( 60 | expected = AppleAppStoreLocale.it, 61 | actual = Locale(Language.ITALIAN, null).appleAppStoreLocale(), 62 | ) 63 | 64 | assertEquals( 65 | expected = AppleAppStoreLocale.it, 66 | actual = Locale(Language.ITALIAN, Country.AUSTRIA).appleAppStoreLocale(), 67 | ) 68 | 69 | assertEquals( 70 | expected = AppleAppStoreLocale.fr_FR, 71 | actual = Locale(Language.FRENCH, null).appleAppStoreLocale(), 72 | ) 73 | 74 | // English has many, so we'll try to be as close as possible. 75 | assertEquals( 76 | expected = AppleAppStoreLocale.en_AU, 77 | actual = Locale(Language.ENGLISH, Country.AUSTRIA).appleAppStoreLocale(), 78 | ) 79 | 80 | assertEquals( 81 | expected = AppleAppStoreLocale.en_US, 82 | actual = Locale(Language.ENGLISH, Country.USA).appleAppStoreLocale(), 83 | ) 84 | 85 | assertEquals( 86 | expected = AppleAppStoreLocale.en_CA, 87 | actual = Locale(Language.ENGLISH, Country.CANADA).appleAppStoreLocale(), 88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /multiplatform-locale-geo/MultiplatformLocaleGeo.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'MultiplatformLocaleGeo' 3 | spec.version = '0.10.0-SNAPSHOT' 4 | spec.homepage = 'https://github.com/vanniktech/multiplatform-locale' 5 | spec.source = { :http=> ''} 6 | spec.authors = 'Niklas Baudy' 7 | spec.license = 'MIT' 8 | spec.summary = 'Multiplatform Locale Geo for iOS, Android and JVM via Kotlin Multiplatform' 9 | spec.vendored_frameworks = 'build/cocoapods/framework/multiplatform_locale_geo.framework' 10 | spec.libraries = 'c++' 11 | 12 | 13 | 14 | if !Dir.exist?('build/cocoapods/framework/multiplatform_locale_geo.framework') || Dir.empty?('build/cocoapods/framework/multiplatform_locale_geo.framework') 15 | raise " 16 | 17 | Kotlin framework 'multiplatform_locale_geo' doesn't exist yet, so a proper Xcode project can't be generated. 18 | 'pod install' should be executed after running ':generateDummyFramework' Gradle task: 19 | 20 | ./gradlew :multiplatform-locale-geo:generateDummyFramework 21 | 22 | Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" 23 | end 24 | 25 | spec.xcconfig = { 26 | 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', 27 | } 28 | 29 | spec.pod_target_xcconfig = { 30 | 'KOTLIN_PROJECT_PATH' => ':multiplatform-locale-geo', 31 | 'PRODUCT_MODULE_NAME' => 'multiplatform_locale_geo', 32 | } 33 | 34 | spec.script_phases = [ 35 | { 36 | :name => 'Build MultiplatformLocaleGeo', 37 | :execution_position => :before_compile, 38 | :shell_path => '/bin/sh', 39 | :script => <<-SCRIPT 40 | if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then 41 | echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" 42 | exit 0 43 | fi 44 | set -ev 45 | REPO_ROOT="$PODS_TARGET_SRCROOT" 46 | "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ 47 | -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ 48 | -Pkotlin.native.cocoapods.archs="$ARCHS" \ 49 | -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" 50 | SCRIPT 51 | } 52 | ] 53 | 54 | end -------------------------------------------------------------------------------- /multiplatform-locale-geo/api/current.txt: -------------------------------------------------------------------------------- 1 | // Signature format: 4.0 2 | package com.vanniktech.locale.geo { 3 | 4 | public final class CountryGeoKt { 5 | method public static com.vanniktech.locale.geo.Geo getGeo(com.vanniktech.locale.Country); 6 | } 7 | 8 | public final class Geo { 9 | method public double component1(); 10 | method public double component2(); 11 | method public double component3(); 12 | method public double component4(); 13 | method public double component5(); 14 | method public double component6(); 15 | method public com.vanniktech.locale.geo.Geo copy(double latitude, double longitude, double maxLatitude, double maxLongitude, double minLatitude, double minLongitude); 16 | method public double getLatitude(); 17 | method public double getLongitude(); 18 | method public double getMaxLatitude(); 19 | method public double getMaxLongitude(); 20 | method public double getMinLatitude(); 21 | method public double getMinLongitude(); 22 | property public final double latitude; 23 | property public final double longitude; 24 | property public final double maxLatitude; 25 | property public final double maxLongitude; 26 | property public final double minLatitude; 27 | property public final double minLongitude; 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /multiplatform-locale-geo/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.dokka") 3 | id("org.jetbrains.kotlin.multiplatform") 4 | id("org.jetbrains.kotlin.native.cocoapods") 5 | id("com.android.library") 6 | id("me.tylerbwong.gradle.metalava") 7 | id("com.vanniktech.maven.publish") 8 | id("app.cash.licensee") 9 | } 10 | 11 | licensee { 12 | allow("Apache-2.0") 13 | } 14 | 15 | metalava { 16 | filename.set("api/current.txt") 17 | } 18 | 19 | kotlin { 20 | applyDefaultHierarchyTemplate() 21 | 22 | androidTarget { 23 | publishLibraryVariants("release") 24 | } 25 | jvm() 26 | jvmToolchain(11) 27 | iosX64() 28 | iosArm64() 29 | iosSimulatorArm64() 30 | 31 | targets.withType { 32 | compilations["main"].kotlinOptions.freeCompilerArgs += "-Xexport-kdoc" 33 | } 34 | 35 | sourceSets { 36 | val commonMain by getting { 37 | dependencies { 38 | api(project(":multiplatform-locale")) 39 | } 40 | } 41 | 42 | val commonTest by getting { 43 | dependencies { 44 | implementation(libs.kotlin.test.common) 45 | implementation(libs.kotlin.test.annotations.common) 46 | } 47 | } 48 | 49 | val androidUnitTest by getting { 50 | dependencies { 51 | implementation(libs.kotlin.test.junit) 52 | } 53 | } 54 | 55 | val jvmTest by getting { 56 | dependencies { 57 | implementation(libs.kotlin.test.junit) 58 | } 59 | } 60 | } 61 | 62 | cocoapods { 63 | summary = "Multiplatform Locale Geo for iOS, Android and JVM via Kotlin Multiplatform" 64 | homepage = "https://github.com/vanniktech/multiplatform-locale" 65 | license = "MIT" 66 | name = "MultiplatformLocaleGeo" 67 | authors = "Niklas Baudy" 68 | version = project.property("VERSION_NAME").toString() 69 | 70 | framework { 71 | isStatic = true 72 | export(project(":multiplatform-locale")) 73 | } 74 | } 75 | } 76 | 77 | android { 78 | namespace = "com.vanniktech.locale.geo" 79 | 80 | compileSdk = libs.versions.compileSdk.get().toInt() 81 | 82 | defaultConfig { 83 | minSdk = libs.versions.minSdk.get().toInt() 84 | } 85 | 86 | compileOptions { 87 | sourceCompatibility = JavaVersion.VERSION_11 88 | targetCompatibility = JavaVersion.VERSION_11 89 | } 90 | 91 | resourcePrefix = "locale_geo" 92 | } 93 | -------------------------------------------------------------------------------- /multiplatform-locale-geo/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Locale Geo 2 | POM_ARTIFACT_ID=multiplatform-locale-geo 3 | -------------------------------------------------------------------------------- /multiplatform-locale-geo/src/commonMain/kotlin/com/vanniktech/locale/geo/Geo.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.geo 2 | 3 | @ConsistentCopyVisibility data class Geo internal constructor( 4 | val latitude: Double, 5 | val longitude: Double, 6 | val maxLatitude: Double, 7 | val maxLongitude: Double, 8 | val minLatitude: Double, 9 | val minLongitude: Double, 10 | ) 11 | -------------------------------------------------------------------------------- /multiplatform-locale-google-play-store/MultiplatformLocaleGooglePlayStore.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'MultiplatformLocaleGooglePlayStore' 3 | spec.version = '0.10.0-SNAPSHOT' 4 | spec.homepage = 'https://github.com/vanniktech/multiplatform-locale' 5 | spec.source = { :http=> ''} 6 | spec.authors = 'Niklas Baudy' 7 | spec.license = 'MIT' 8 | spec.summary = 'Multiplatform Locale Google Play Store for iOS, Android and JVM via Kotlin Multiplatform' 9 | spec.vendored_frameworks = 'build/cocoapods/framework/multiplatform_locale_google_play_store.framework' 10 | spec.libraries = 'c++' 11 | 12 | 13 | 14 | if !Dir.exist?('build/cocoapods/framework/multiplatform_locale_google_play_store.framework') || Dir.empty?('build/cocoapods/framework/multiplatform_locale_google_play_store.framework') 15 | raise " 16 | 17 | Kotlin framework 'multiplatform_locale_google_play_store' doesn't exist yet, so a proper Xcode project can't be generated. 18 | 'pod install' should be executed after running ':generateDummyFramework' Gradle task: 19 | 20 | ./gradlew :multiplatform-locale-google-play-store:generateDummyFramework 21 | 22 | Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" 23 | end 24 | 25 | spec.xcconfig = { 26 | 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', 27 | } 28 | 29 | spec.pod_target_xcconfig = { 30 | 'KOTLIN_PROJECT_PATH' => ':multiplatform-locale-google-play-store', 31 | 'PRODUCT_MODULE_NAME' => 'multiplatform_locale_google_play_store', 32 | } 33 | 34 | spec.script_phases = [ 35 | { 36 | :name => 'Build MultiplatformLocaleGooglePlayStore', 37 | :execution_position => :before_compile, 38 | :shell_path => '/bin/sh', 39 | :script => <<-SCRIPT 40 | if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then 41 | echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" 42 | exit 0 43 | fi 44 | set -ev 45 | REPO_ROOT="$PODS_TARGET_SRCROOT" 46 | "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ 47 | -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ 48 | -Pkotlin.native.cocoapods.archs="$ARCHS" \ 49 | -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" 50 | SCRIPT 51 | } 52 | ] 53 | 54 | end -------------------------------------------------------------------------------- /multiplatform-locale-google-play-store/api/current.txt: -------------------------------------------------------------------------------- 1 | // Signature format: 4.0 2 | package com.vanniktech.locale.google.play.store { 3 | 4 | public enum GooglePlayStoreLocale { 5 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale af; 6 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale am; 7 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ar; 8 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale az_AZ; 9 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale be; 10 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale bg; 11 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale bn_BD; 12 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ca; 13 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale cs_CZ; 14 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale da_DK; 15 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale de_DE; 16 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale el_GR; 17 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale en_AU; 18 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale en_CA; 19 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale en_GB; 20 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale en_IN; 21 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale en_SG; 22 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale en_US; 23 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale en_ZA; 24 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale es_419; 25 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale es_ES; 26 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale es_US; 27 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale et; 28 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale eu_ES; 29 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fa; 30 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fa_AE; 31 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fa_AF; 32 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fa_IR; 33 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fi_FI; 34 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fil; 35 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fr_CA; 36 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale fr_FR; 37 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale gl_ES; 38 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale gu; 39 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale hi_IN; 40 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale hr; 41 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale hu_HU; 42 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale hy_AM; 43 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale id; 44 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale is_IS; 45 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale it_IT; 46 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale iw_IL; 47 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ja_JP; 48 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ka_GE; 49 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale kk; 50 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale km_KH; 51 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale kn_IN; 52 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ko_KR; 53 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ky_KG; 54 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale lo_LA; 55 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale lt; 56 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale lv; 57 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale mk_MK; 58 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ml_IN; 59 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale mn_MN; 60 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale mr_IN; 61 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ms; 62 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ms_MY; 63 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale my_MM; 64 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ne_NP; 65 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale nl_NL; 66 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale no_NO; 67 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale pa; 68 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale pl_PL; 69 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale pt_BR; 70 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale pt_PT; 71 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale rm; 72 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ro; 73 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ru_RU; 74 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale si_LK; 75 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale sk; 76 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale sl; 77 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale sq; 78 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale sr; 79 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale sv_SE; 80 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale sw; 81 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ta_IN; 82 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale te_IN; 83 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale th; 84 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale tr_TR; 85 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale uk; 86 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale ur; 87 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale vi; 88 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale zh_CN; 89 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale zh_HK; 90 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale zh_TW; 91 | enum_constant public static final com.vanniktech.locale.google.play.store.GooglePlayStoreLocale zu; 92 | } 93 | 94 | public final class GooglePlayStoreLocaleKt { 95 | method public static com.vanniktech.locale.google.play.store.GooglePlayStoreLocale? googlePlayStoreLocale(com.vanniktech.locale.Locale); 96 | } 97 | 98 | } 99 | 100 | -------------------------------------------------------------------------------- /multiplatform-locale-google-play-store/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.dokka") 3 | id("org.jetbrains.kotlin.multiplatform") 4 | id("org.jetbrains.kotlin.native.cocoapods") 5 | id("com.android.library") 6 | id("me.tylerbwong.gradle.metalava") 7 | id("com.vanniktech.maven.publish") 8 | id("app.cash.licensee") 9 | } 10 | 11 | licensee { 12 | allow("Apache-2.0") 13 | } 14 | 15 | metalava { 16 | filename.set("api/current.txt") 17 | } 18 | 19 | kotlin { 20 | applyDefaultHierarchyTemplate() 21 | 22 | androidTarget { 23 | publishLibraryVariants("release") 24 | } 25 | jvm() 26 | jvmToolchain(11) 27 | iosX64() 28 | iosArm64() 29 | iosSimulatorArm64() 30 | 31 | targets.withType { 32 | compilations["main"].kotlinOptions.freeCompilerArgs += "-Xexport-kdoc" 33 | } 34 | 35 | sourceSets { 36 | val commonMain by getting { 37 | dependencies { 38 | api(project(":multiplatform-locale")) 39 | } 40 | } 41 | 42 | val commonTest by getting { 43 | dependencies { 44 | implementation(libs.kotlin.test.common) 45 | implementation(libs.kotlin.test.annotations.common) 46 | } 47 | } 48 | 49 | val androidUnitTest by getting { 50 | dependencies { 51 | implementation(libs.kotlin.test.junit) 52 | } 53 | } 54 | 55 | val jvmTest by getting { 56 | dependencies { 57 | implementation(libs.kotlin.test.junit) 58 | } 59 | } 60 | } 61 | 62 | cocoapods { 63 | summary = "Multiplatform Locale Google Play Store for iOS, Android and JVM via Kotlin Multiplatform" 64 | homepage = "https://github.com/vanniktech/multiplatform-locale" 65 | license = "MIT" 66 | name = "MultiplatformLocaleGooglePlayStore" 67 | authors = "Niklas Baudy" 68 | version = project.property("VERSION_NAME").toString() 69 | 70 | framework { 71 | isStatic = true 72 | export(project(":multiplatform-locale")) 73 | } 74 | } 75 | } 76 | 77 | android { 78 | namespace = "com.vanniktech.locale.google.play.store" 79 | 80 | compileSdk = libs.versions.compileSdk.get().toInt() 81 | 82 | defaultConfig { 83 | minSdk = libs.versions.minSdk.get().toInt() 84 | } 85 | 86 | compileOptions { 87 | sourceCompatibility = JavaVersion.VERSION_11 88 | targetCompatibility = JavaVersion.VERSION_11 89 | } 90 | 91 | resourcePrefix = "locale_google_play_store_" 92 | } 93 | -------------------------------------------------------------------------------- /multiplatform-locale-google-play-store/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Locale Google Play Store 2 | POM_ARTIFACT_ID=multiplatform-locale-google-play-store 3 | -------------------------------------------------------------------------------- /multiplatform-locale-google-play-store/src/commonMain/kotlin/com/vanniktech/locale/google/play/store/GooglePlayStoreLocale.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.google.play.store 2 | 3 | import com.vanniktech.locale.Language 4 | import com.vanniktech.locale.Locale 5 | import com.vanniktech.locale.Locale.Companion.fromOrNull 6 | 7 | /** All the locales supported by the Google Play Store. */ 8 | @Suppress("ktlint:standard:enum-entry-name-case") enum class GooglePlayStoreLocale { 9 | af, 10 | sq, 11 | am, 12 | ar, 13 | hy_AM, 14 | az_AZ, 15 | bn_BD, 16 | eu_ES, 17 | be, 18 | bg, 19 | my_MM, 20 | ca, 21 | zh_HK, 22 | zh_CN, 23 | zh_TW, 24 | hr, 25 | cs_CZ, 26 | da_DK, 27 | nl_NL, 28 | en_AU, 29 | en_US, 30 | en_CA, 31 | en_GB, 32 | en_IN, 33 | en_SG, 34 | en_ZA, 35 | et, 36 | fil, 37 | fi_FI, 38 | fr_CA, 39 | fr_FR, 40 | gl_ES, 41 | ka_GE, 42 | de_DE, 43 | el_GR, 44 | gu, 45 | iw_IL, 46 | hi_IN, 47 | hu_HU, 48 | is_IS, 49 | id, 50 | it_IT, 51 | ja_JP, 52 | kn_IN, 53 | kk, 54 | km_KH, 55 | ko_KR, 56 | ky_KG, 57 | lo_LA, 58 | lv, 59 | lt, 60 | mk_MK, 61 | ms_MY, 62 | ms, 63 | ml_IN, 64 | mr_IN, 65 | mn_MN, 66 | ne_NP, 67 | no_NO, 68 | fa, 69 | fa_AE, 70 | fa_AF, 71 | fa_IR, 72 | pl_PL, 73 | pt_BR, 74 | pt_PT, 75 | pa, 76 | ro, 77 | rm, 78 | ru_RU, 79 | sr, 80 | si_LK, 81 | sk, 82 | sl, 83 | es_ES, 84 | es_US, 85 | es_419, 86 | sw, 87 | sv_SE, 88 | ta_IN, 89 | te_IN, 90 | th, 91 | tr_TR, 92 | uk, 93 | ur, 94 | vi, 95 | zu, 96 | ; 97 | 98 | override fun toString() = name.replace("_", "-") 99 | } 100 | 101 | /** Returns the optional [GooglePlayStoreLocale] that can be used for localizing the Google Play Store. */ 102 | fun Locale.googlePlayStoreLocale(): GooglePlayStoreLocale? { 103 | val optimized = Locale( 104 | language = language, 105 | territory = territory ?: language.defaultCountry, 106 | ) 107 | return GooglePlayStoreLocale.entries 108 | .groupBy { Language.fromLocaleOrNull(it.toString()) } 109 | .firstNotNullOfOrNull { (key, locales) -> 110 | locales.firstNotNullOfOrNull { locale -> 111 | locale.takeIf { 112 | fromOrNull(it.name) == optimized 113 | } 114 | } ?: locales.firstNotNullOfOrNull { locale -> locale.takeIf { language == key } } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /multiplatform-locale-google-play-store/src/commonTest/kotlin/com/vanniktech/locale/google/play/store/GooglePlayStoreLocaleTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.google.play.store 2 | 3 | import com.vanniktech.locale.Country 4 | import com.vanniktech.locale.Language 5 | import com.vanniktech.locale.Locale 6 | import kotlin.test.Test 7 | import kotlin.test.assertEquals 8 | 9 | class GooglePlayStoreLocaleTest { 10 | @Test fun googlePlayStoreLocaleAllPossibilities() { 11 | assertEquals( 12 | expected = listOf( 13 | GooglePlayStoreLocale.ar, 14 | GooglePlayStoreLocale.af, 15 | GooglePlayStoreLocale.sq, 16 | GooglePlayStoreLocale.am, 17 | GooglePlayStoreLocale.hy_AM, 18 | GooglePlayStoreLocale.az_AZ, 19 | GooglePlayStoreLocale.be, 20 | GooglePlayStoreLocale.bn_BD, 21 | GooglePlayStoreLocale.bg, 22 | GooglePlayStoreLocale.my_MM, 23 | GooglePlayStoreLocale.km_KH, 24 | GooglePlayStoreLocale.zh_CN, 25 | GooglePlayStoreLocale.zh_TW, 26 | GooglePlayStoreLocale.zh_HK, 27 | GooglePlayStoreLocale.ca, 28 | GooglePlayStoreLocale.cs_CZ, 29 | GooglePlayStoreLocale.hr, 30 | GooglePlayStoreLocale.da_DK, 31 | GooglePlayStoreLocale.nl_NL, 32 | GooglePlayStoreLocale.en_US, 33 | GooglePlayStoreLocale.en_CA, 34 | GooglePlayStoreLocale.en_AU, 35 | GooglePlayStoreLocale.en_GB, 36 | GooglePlayStoreLocale.et, 37 | GooglePlayStoreLocale.fa_IR, 38 | GooglePlayStoreLocale.fa_AF, 39 | GooglePlayStoreLocale.fa, 40 | GooglePlayStoreLocale.fa_AE, 41 | GooglePlayStoreLocale.fil, 42 | GooglePlayStoreLocale.fi_FI, 43 | GooglePlayStoreLocale.fr_FR, 44 | GooglePlayStoreLocale.fr_CA, 45 | GooglePlayStoreLocale.ka_GE, 46 | GooglePlayStoreLocale.de_DE, 47 | GooglePlayStoreLocale.el_GR, 48 | GooglePlayStoreLocale.gu, 49 | GooglePlayStoreLocale.iw_IL, 50 | GooglePlayStoreLocale.is_IS, 51 | GooglePlayStoreLocale.hi_IN, 52 | GooglePlayStoreLocale.hu_HU, 53 | GooglePlayStoreLocale.id, 54 | GooglePlayStoreLocale.it_IT, 55 | GooglePlayStoreLocale.ja_JP, 56 | GooglePlayStoreLocale.kn_IN, 57 | GooglePlayStoreLocale.ko_KR, 58 | GooglePlayStoreLocale.lv, 59 | GooglePlayStoreLocale.lt, 60 | GooglePlayStoreLocale.mk_MK, 61 | GooglePlayStoreLocale.ms_MY, 62 | GooglePlayStoreLocale.ml_IN, 63 | GooglePlayStoreLocale.mr_IN, 64 | GooglePlayStoreLocale.no_NO, 65 | GooglePlayStoreLocale.pl_PL, 66 | GooglePlayStoreLocale.pt_PT, 67 | GooglePlayStoreLocale.pt_BR, 68 | GooglePlayStoreLocale.ro, 69 | GooglePlayStoreLocale.ru_RU, 70 | GooglePlayStoreLocale.sr, 71 | GooglePlayStoreLocale.sk, 72 | GooglePlayStoreLocale.sl, 73 | GooglePlayStoreLocale.es_ES, 74 | GooglePlayStoreLocale.es_419, 75 | GooglePlayStoreLocale.es_US, 76 | GooglePlayStoreLocale.sw, 77 | GooglePlayStoreLocale.sv_SE, 78 | GooglePlayStoreLocale.ta_IN, 79 | GooglePlayStoreLocale.te_IN, 80 | GooglePlayStoreLocale.th, 81 | GooglePlayStoreLocale.tr_TR, 82 | GooglePlayStoreLocale.uk, 83 | GooglePlayStoreLocale.ur, 84 | GooglePlayStoreLocale.vi, 85 | ), 86 | actual = Language.entries.flatMap { language -> (language.officialTerritories + language.otherTerritories).mapNotNull { Locale(language, it).googlePlayStoreLocale() } }.distinct(), 87 | ) 88 | } 89 | 90 | @Test fun googlePlayStoreLocaleLenient() { 91 | // Italian only has one GooglePlayStoreLocale, so we'll match regardless of Country. 92 | assertEquals( 93 | expected = GooglePlayStoreLocale.it_IT, 94 | actual = Locale(Language.ITALIAN, null).googlePlayStoreLocale(), 95 | ) 96 | 97 | assertEquals( 98 | expected = GooglePlayStoreLocale.it_IT, 99 | actual = Locale(Language.ITALIAN, Country.AUSTRIA).googlePlayStoreLocale(), 100 | ) 101 | 102 | assertEquals( 103 | expected = GooglePlayStoreLocale.fr_FR, 104 | actual = Locale(Language.FRENCH, null).googlePlayStoreLocale(), 105 | ) 106 | 107 | // English has many, so we'll try to be as close as possible. 108 | assertEquals( 109 | expected = GooglePlayStoreLocale.en_AU, 110 | actual = Locale(Language.ENGLISH, Country.AUSTRIA).googlePlayStoreLocale(), 111 | ) 112 | 113 | assertEquals( 114 | expected = GooglePlayStoreLocale.en_US, 115 | actual = Locale(Language.ENGLISH, Country.USA).googlePlayStoreLocale(), 116 | ) 117 | 118 | assertEquals( 119 | expected = GooglePlayStoreLocale.en_CA, 120 | actual = Locale(Language.ENGLISH, Country.CANADA).googlePlayStoreLocale(), 121 | ) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /multiplatform-locale/MultiplatformLocale.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'MultiplatformLocale' 3 | spec.version = '0.10.0-SNAPSHOT' 4 | spec.homepage = 'https://github.com/vanniktech/multiplatform-locale' 5 | spec.source = { :http=> ''} 6 | spec.authors = 'Niklas Baudy' 7 | spec.license = 'MIT' 8 | spec.summary = 'Multiplatform Locale for iOS, Android and JVM via Kotlin Multiplatform' 9 | spec.vendored_frameworks = 'build/cocoapods/framework/multiplatform_locale.framework' 10 | spec.libraries = 'c++' 11 | 12 | 13 | 14 | if !Dir.exist?('build/cocoapods/framework/multiplatform_locale.framework') || Dir.empty?('build/cocoapods/framework/multiplatform_locale.framework') 15 | raise " 16 | 17 | Kotlin framework 'multiplatform_locale' doesn't exist yet, so a proper Xcode project can't be generated. 18 | 'pod install' should be executed after running ':generateDummyFramework' Gradle task: 19 | 20 | ./gradlew :multiplatform-locale:generateDummyFramework 21 | 22 | Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" 23 | end 24 | 25 | spec.xcconfig = { 26 | 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', 27 | } 28 | 29 | spec.pod_target_xcconfig = { 30 | 'KOTLIN_PROJECT_PATH' => ':multiplatform-locale', 31 | 'PRODUCT_MODULE_NAME' => 'multiplatform_locale', 32 | } 33 | 34 | spec.script_phases = [ 35 | { 36 | :name => 'Build MultiplatformLocale', 37 | :execution_position => :before_compile, 38 | :shell_path => '/bin/sh', 39 | :script => <<-SCRIPT 40 | if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then 41 | echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" 42 | exit 0 43 | fi 44 | set -ev 45 | REPO_ROOT="$PODS_TARGET_SRCROOT" 46 | "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ 47 | -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ 48 | -Pkotlin.native.cocoapods.archs="$ARCHS" \ 49 | -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" 50 | SCRIPT 51 | } 52 | ] 53 | 54 | end -------------------------------------------------------------------------------- /multiplatform-locale/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.dokka") 3 | id("org.jetbrains.kotlin.multiplatform") 4 | id("org.jetbrains.kotlin.native.cocoapods") 5 | id("com.android.library") 6 | id("me.tylerbwong.gradle.metalava") 7 | id("com.vanniktech.maven.publish") 8 | id("app.cash.licensee") 9 | } 10 | 11 | licensee { 12 | allow("Apache-2.0") 13 | } 14 | 15 | metalava { 16 | filename.set("api/current.txt") 17 | } 18 | 19 | kotlin { 20 | applyDefaultHierarchyTemplate() 21 | 22 | androidTarget { 23 | publishLibraryVariants("release") 24 | } 25 | jvm() 26 | jvmToolchain(11) 27 | iosX64() 28 | iosArm64() 29 | iosSimulatorArm64() 30 | 31 | targets.withType { 32 | compilations["main"].kotlinOptions.freeCompilerArgs += "-Xexport-kdoc" 33 | } 34 | 35 | sourceSets { 36 | val commonTest by getting { 37 | dependencies { 38 | implementation(libs.kotlin.test.common) 39 | implementation(libs.kotlin.test.annotations.common) 40 | } 41 | } 42 | 43 | val androidUnitTest by getting { 44 | dependencies { 45 | implementation(libs.kotlin.test.junit) 46 | } 47 | } 48 | 49 | val jvmTest by getting { 50 | dependencies { 51 | implementation(libs.kotlin.test.junit) 52 | } 53 | } 54 | } 55 | 56 | cocoapods { 57 | summary = "Multiplatform Locale for iOS, Android and JVM via Kotlin Multiplatform" 58 | homepage = "https://github.com/vanniktech/multiplatform-locale" 59 | license = "MIT" 60 | name = "MultiplatformLocale" 61 | authors = "Niklas Baudy" 62 | version = project.property("VERSION_NAME").toString() 63 | 64 | framework { 65 | isStatic = true 66 | } 67 | } 68 | } 69 | 70 | android { 71 | namespace = "com.vanniktech.locale" 72 | 73 | compileSdk = libs.versions.compileSdk.get().toInt() 74 | 75 | defaultConfig { 76 | minSdk = libs.versions.minSdk.get().toInt() 77 | } 78 | 79 | compileOptions { 80 | sourceCompatibility = JavaVersion.VERSION_11 81 | targetCompatibility = JavaVersion.VERSION_11 82 | } 83 | 84 | resourcePrefix = "locale_" 85 | } 86 | -------------------------------------------------------------------------------- /multiplatform-locale/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Locale 2 | POM_ARTIFACT_ID=multiplatform-locale 3 | -------------------------------------------------------------------------------- /multiplatform-locale/src/androidMain/kotlin/com/vanniktech/locale/Countries.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import java.util.Locale 4 | 5 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 6 | actual object Countries { 7 | actual fun currentCountryCode(): String? = Locale.getDefault().country 8 | } 9 | -------------------------------------------------------------------------------- /multiplatform-locale/src/androidMain/kotlin/com/vanniktech/locale/Languages.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import java.util.Locale 4 | 5 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 6 | actual object Languages { 7 | actual fun currentLanguageCode(): String = Locale.getDefault().language 8 | } 9 | -------------------------------------------------------------------------------- /multiplatform-locale/src/androidMain/kotlin/com/vanniktech/locale/Locales.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import android.os.Build.VERSION.SDK_INT 4 | import android.os.LocaleList 5 | import java.util.Locale 6 | 7 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 8 | actual object Locales { 9 | actual fun currentLocaleString(): String = Locale.getDefault().toString() 10 | 11 | actual fun currentLocaleStrings() = when { 12 | SDK_INT >= 24 -> { 13 | val locales = LocaleList.getDefault() 14 | (0 until locales.size()).map { locales[it] } 15 | } 16 | else -> { 17 | listOf(Locale.getDefault()) 18 | } 19 | }.map { it.toString() } 20 | } 21 | -------------------------------------------------------------------------------- /multiplatform-locale/src/androidMain/kotlin/com/vanniktech/locale/Multiplatform.android.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | internal actual fun Language.commonDisplayName() = java.util.Locale( 4 | code, 5 | "", 6 | ).displayLanguage.capitalized() 7 | 8 | internal actual fun Territory.commonDisplayName() = when (this) { 9 | Region.INTERNATIONAL_WATERS -> "International Waters" 10 | else -> java.util.Locale( 11 | java.util.Locale.getDefault().language, 12 | code, 13 | ).displayCountry.capitalized() 14 | } 15 | 16 | fun Locale.toJavaLocale() = java.util.Locale( 17 | language.code, 18 | territory?.code.orEmpty(), 19 | ) 20 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Continent.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | enum class Continent { 4 | ASIA, 5 | AFRICA, 6 | NORTH_AMERICA, 7 | SOUTH_AMERICA, 8 | ANTARCTICA, 9 | EUROPE, 10 | OCEANIA, 11 | } 12 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Countries.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 4 | expect object Countries { 5 | /** Returns the current optional ISO 3166-1 alpha-2 [Country.code] - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 */ 6 | fun currentCountryCode(): String? 7 | } 8 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Language.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | enum class Language( 4 | /** ISO 639-1 code - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes */ 5 | val code: String, 6 | /** [HEBREW] & [INDONESIAN] changed their identifier. We are backwards compatible and support both. */ 7 | val legacyCode: String? = null, 8 | /** Territories, where this [Language] is officially spoken in. */ 9 | val officialTerritories: List, 10 | /** Territories, where this [Language] is spoken in. */ 11 | val otherTerritories: List, 12 | ) { 13 | ARABIC( 14 | code = "ar", 15 | officialTerritories = listOf(Country.SAUDI_ARABIA, Country.ALGERIA, Country.BAHRAIN, Country.MALI, Country.NIGER, Country.SENEGAL, Country.CHAD, Country.COMOROS, Country.DJIBOUTI, Country.EGYPT, Country.IRAQ, Country.JORDAN, Country.KUWAIT, Country.LEBANON, Country.LIBYA, Country.MAURITANIA, Country.MOROCCO, Country.OMAN, Country.QATAR, Country.SOMALIA, Country.SUDAN, Country.SYRIA, Country.TUNISIA, Country.UNITED_ARAB_EMIRATES, Country.YEMEN), 16 | otherTerritories = listOf(Country.PALESTINE, Country.SOMALIA, Country.SAHRAWI_ARAB_DEMOCRATIC_REPUBLIC, Country.TANZANIA), 17 | ), 18 | AFRIKAANS( 19 | code = "af", 20 | officialTerritories = listOf(Country.SOUTH_AFRICA), 21 | otherTerritories = listOf(Country.NAMIBIA), 22 | ), 23 | ALBANIAN( 24 | code = "sq", 25 | officialTerritories = listOf(Country.ALBANIA, Country.KOSOVO), 26 | otherTerritories = listOf(Country.NORTH_MACEDONIA, Country.MONTENEGRO), 27 | ), 28 | AMHARIC( 29 | code = "am", 30 | officialTerritories = listOf(Country.ETHIOPIA), 31 | otherTerritories = emptyList(), 32 | ), 33 | ARMENIAN( 34 | code = "hy", 35 | officialTerritories = listOf(Country.ARMENIA), 36 | otherTerritories = emptyList(), 37 | ), 38 | AZERBAIJANI( 39 | code = "az", 40 | officialTerritories = listOf(Country.AZERBAIJAN), 41 | otherTerritories = emptyList(), 42 | ), 43 | BELARUSIAN( 44 | code = "be", 45 | officialTerritories = listOf(Country.BELARUS), 46 | otherTerritories = listOf(Country.RUSSIA, Country.LITHUANIA, Country.LATVIA, Country.POLAND, Country.UKRAINE), 47 | ), 48 | BENGALI( 49 | code = "bn", 50 | officialTerritories = listOf(Country.BANGLADESH), 51 | otherTerritories = listOf(Country.INDIA), 52 | ), 53 | BOKMAL( 54 | code = "nb", 55 | officialTerritories = listOf(Country.NORWAY), 56 | otherTerritories = listOf(), 57 | ), 58 | BOSNIAN( 59 | code = "bs", 60 | officialTerritories = listOf(Country.BOSNIA_AND_HERZEGOVINA), 61 | otherTerritories = listOf(), 62 | ), 63 | BULGARIAN( 64 | code = "bg", 65 | officialTerritories = listOf(Country.BULGARIA), 66 | otherTerritories = listOf(Country.GREECE, Country.ROMANIA, Country.MOLDOVA, Country.UKRAINE), 67 | ), 68 | BURMESE( 69 | code = "my", 70 | officialTerritories = listOf(Country.MYANMAR), 71 | otherTerritories = listOf(), 72 | ), 73 | CENTRAL_KMHER( 74 | code = "km", 75 | officialTerritories = listOf(Country.CAMBODIA), 76 | otherTerritories = emptyList(), 77 | ), 78 | CHINESE( 79 | code = "zh", 80 | officialTerritories = listOf(Country.CHINA, Country.TAIWAN, Country.HONG_KONG, Country.SINGAPORE, Country.MACAO), 81 | otherTerritories = emptyList(), 82 | ), 83 | CATALAN( 84 | code = "ca", 85 | officialTerritories = listOf(Country.SPAIN), 86 | otherTerritories = listOf(Country.ANDORRA), 87 | ), 88 | CZECH( 89 | code = "cs", 90 | officialTerritories = listOf(Country.CZECHIA), 91 | otherTerritories = listOf(Country.POLAND, Country.SERBIA, Country.UKRAINE, Country.SLOVAKIA, Country.BULGARIA, Country.AUSTRIA), 92 | ), 93 | CROATIAN( 94 | code = "hr", 95 | officialTerritories = listOf(Country.CROATIA), 96 | otherTerritories = listOf(Country.AUSTRIA, Country.ITALY, Country.SERBIA), 97 | ), 98 | DANISH( 99 | code = "da", 100 | officialTerritories = listOf(Country.DENMARK, Country.GREENLAND, Country.FAROE_ISLANDS), 101 | otherTerritories = emptyList(), 102 | ), 103 | DUTCH( 104 | code = "nl", 105 | officialTerritories = listOf(Country.NETHERLANDS, Country.BELGIUM, Country.SURINAME, Country.ARUBA, Country.CURACAO, Country.SINT_MAARTEN), 106 | otherTerritories = emptyList(), 107 | ), 108 | ENGLISH( 109 | code = "en", 110 | officialTerritories = listOf(Country.USA, Country.CANADA, Country.AUSTRALIA, Country.ENGLAND, Country.NEW_ZEALAND, Country.CHRISTMAS_ISLAND, Country.COCOS_ISLANDS, Country.TOKELAU, Country.BRITISH_INDIAN_OCEAN_TERRITORY, Country.GUERNSEY, Country.MONTSERRAT), 111 | otherTerritories = listOf(Country.BAHRAIN, Country.BANGLADESH, Country.BRUNEI, Country.CYPRUS, Country.ERITREA, Country.ETHIOPIA, Country.GREENLAND, Country.ICELAND, Country.ISRAEL, Country.JORDAN, Country.KUWAIT, Country.MALAYSIA, Country.MALDIVES, Country.MYANMAR, Country.QATAR, Country.SRI_LANKA, Country.UNITED_ARAB_EMIRATES), 112 | ), 113 | ESTONIAN( 114 | code = "et", 115 | officialTerritories = listOf(Country.ESTONIA), 116 | otherTerritories = emptyList(), 117 | ), 118 | FARSI( 119 | code = "fa", 120 | officialTerritories = listOf(Country.IRAN), 121 | otherTerritories = listOf(Country.AFGHANISTAN, Country.BAHRAIN, Country.IRAQ, Country.OMAN, Country.YEMEN, Country.UNITED_ARAB_EMIRATES), 122 | ), 123 | FILIPINO( 124 | code = "fil", 125 | officialTerritories = listOf(Country.PHILIPPINES), 126 | otherTerritories = emptyList(), 127 | ), 128 | FINNISH( 129 | code = "fi", 130 | officialTerritories = listOf(Country.FINLAND, Country.SWEDEN), 131 | otherTerritories = emptyList(), 132 | ), 133 | FRENCH( 134 | code = "fr", 135 | officialTerritories = listOf(Country.FRANCE, Country.CANADA, Country.BELGIUM, Country.BENIN, Country.BURKINA_FASO, Country.BURUNDI, Country.CAMEROON, Country.CHAD, Country.COTE_DIVOIRE, Country.DEMOCRATIC_REPUBLIC_OF_CONGO, Country.DJIBOUTI, Country.EQUATORIAL_GUINEA, Country.HAITI, Country.LUXEMBOURG, Country.MADAGASCAR, Country.MALI, Country.MONACO, Country.NIGER, Country.RWANDA, Country.SENEGAL, Country.SEYCHELLES, Country.SWITZERLAND, Country.TOGO, Country.VANUATU), 136 | otherTerritories = emptyList(), 137 | ), 138 | GEORGIAN( 139 | code = "ka", 140 | officialTerritories = listOf(Country.GEORGIA), 141 | otherTerritories = listOf(Country.TURKEY, Country.IRAN, Country.RUSSIA), 142 | ), 143 | GERMAN( 144 | code = "de", 145 | officialTerritories = listOf(Country.GERMANY, Country.AUSTRIA, Country.BELGIUM, Country.LUXEMBOURG, Country.SWITZERLAND, Country.LIECHTENSTEIN), 146 | otherTerritories = emptyList(), 147 | ), 148 | GREEK( 149 | code = "el", 150 | officialTerritories = listOf(Country.GREECE, Country.CYPRUS), 151 | otherTerritories = emptyList(), 152 | ), 153 | GUJARATI( 154 | code = "gu", 155 | officialTerritories = listOf(Country.INDIA, Country.SRI_LANKA, Country.SINGAPORE), 156 | otherTerritories = listOf(Country.MALAYSIA, Country.MAURITIUS, Country.FIJI, Country.SOUTH_AFRICA), 157 | ), 158 | HEBREW( 159 | code = "he", 160 | legacyCode = "iw", 161 | officialTerritories = listOf(Country.ISRAEL), 162 | otherTerritories = emptyList(), 163 | ), 164 | ICELANDIC( 165 | code = "is", 166 | officialTerritories = listOf(Country.ICELAND), 167 | otherTerritories = emptyList(), 168 | ), 169 | HINDI( 170 | code = "hi", 171 | officialTerritories = listOf(Country.INDIA), 172 | otherTerritories = listOf(Country.MAURITIUS, Country.FIJI, Country.SURINAME, Country.GUYANA, Country.TRINIDAD_AND_TOBAGO, Country.NEPAL), 173 | ), 174 | HUNGARIAN( 175 | code = "hu", 176 | officialTerritories = listOf(Country.HUNGARY), 177 | otherTerritories = listOf(Country.ROMANIA, Country.SLOVAKIA, Country.SERBIA, Country.AUSTRIA, Country.SLOVENIA), 178 | ), 179 | INDONESIAN( 180 | code = "id", 181 | legacyCode = "in", 182 | officialTerritories = listOf(Country.INDONESIA), 183 | otherTerritories = emptyList(), 184 | ), 185 | IRISH( 186 | code = "ga", 187 | officialTerritories = listOf(Country.IRELAND), 188 | otherTerritories = listOf(), 189 | ), 190 | ITALIAN( 191 | code = "it", 192 | officialTerritories = listOf(Country.ITALY, Country.SAN_MARINO, Country.SWITZERLAND, Country.VATICAN_CITY), 193 | otherTerritories = listOf(Country.CROATIA, Country.SLOVENIA, Country.AUSTRIA), 194 | ), 195 | JAPANESE( 196 | code = "ja", 197 | officialTerritories = listOf(Country.JAPAN), 198 | otherTerritories = emptyList(), 199 | ), 200 | KANNADA( 201 | code = "kn", 202 | officialTerritories = listOf(Country.INDIA), 203 | otherTerritories = emptyList(), 204 | ), 205 | KOREAN( 206 | code = "ko", 207 | officialTerritories = listOf(Country.SOUTH_KOREA, Country.NORTH_KOREA), 208 | otherTerritories = emptyList(), 209 | ), 210 | LATVIAN( 211 | code = "lv", 212 | officialTerritories = listOf(Country.LATVIA), 213 | otherTerritories = emptyList(), 214 | ), 215 | LITHUANIAN( 216 | code = "lt", 217 | officialTerritories = listOf(Country.LITHUANIA), 218 | otherTerritories = emptyList(), 219 | ), 220 | MACEDONIAN( 221 | code = "mk", 222 | officialTerritories = listOf(Country.NORTH_MACEDONIA), 223 | otherTerritories = emptyList(), 224 | ), 225 | MALAY( 226 | code = "ms", 227 | officialTerritories = listOf(Country.MALAYSIA, Country.BRUNEI, Country.INDONESIA, Country.SINGAPORE), 228 | otherTerritories = emptyList(), 229 | ), 230 | MALAYALAM( 231 | code = "ml", 232 | officialTerritories = listOf(Country.INDIA), 233 | otherTerritories = emptyList(), 234 | ), 235 | MARATHI( 236 | code = "mr", 237 | officialTerritories = listOf(Country.INDIA), 238 | otherTerritories = emptyList(), 239 | ), 240 | NORWEGIAN( 241 | code = "no", 242 | officialTerritories = listOf(Country.NORWAY), 243 | otherTerritories = emptyList(), 244 | ), 245 | POLISH( 246 | code = "pl", 247 | officialTerritories = listOf(Country.POLAND), 248 | otherTerritories = emptyList(), 249 | ), 250 | PORTUGUESE( 251 | code = "pt", 252 | officialTerritories = listOf(Country.PORTUGAL, Country.BRAZIL, Country.ANGOLA, Country.MOZAMBIQUE), 253 | otherTerritories = listOf(Country.GUINEA_BISSAU, Country.CAPE_VERDE, Country.SAO_TOME_AND_PRINCE, Country.MACAO), 254 | ), 255 | ROMANIAN( 256 | code = "ro", 257 | officialTerritories = listOf(Country.ROMANIA, Country.MOLDOVA), 258 | otherTerritories = emptyList(), 259 | ), 260 | RUSSIAN( 261 | code = "ru", 262 | officialTerritories = listOf(Country.RUSSIA, Country.BELARUS, Country.KAZAKHSTAN, Country.KYRGYZSTAN), 263 | otherTerritories = listOf(Country.AZERBAIJAN, Country.ESTONIA, Country.GEORGIA, Country.LATVIA, Country.UKRAINE, Country.LITHUANIA, Country.MOLDOVA, Country.TAJIKISTAN, Country.TURKMENISTAN, Country.UZBEKISTAN, Country.ISRAEL), 264 | ), 265 | SERBIAN( 266 | code = "sr", 267 | officialTerritories = listOf(Country.SERBIA), 268 | otherTerritories = listOf(Country.CROATIA, Country.BOSNIA_AND_HERZEGOVINA, Country.MONTENEGRO), 269 | ), 270 | SLOVAK( 271 | code = "sk", 272 | officialTerritories = listOf(Country.SLOVAKIA), 273 | otherTerritories = listOf(Country.SERBIA, Country.CZECHIA, Country.HUNGARY, Country.POLAND, Country.ROMANIA, Country.UKRAINE), 274 | ), 275 | SLOVENIAN( 276 | code = "sl", 277 | officialTerritories = listOf(Country.SLOVENIA), 278 | otherTerritories = listOf(Country.AUSTRIA, Country.ITALY), 279 | ), 280 | SPANISH( 281 | code = "es", 282 | officialTerritories = listOf(Country.SPAIN, Country.MEXICO, Country.COLOMBIA, Country.ARGENTINA, Country.CHILE, Country.NICARAGUA, Country.URUGUAY, Country.EQUATORIAL_GUINEA, Country.PANAMA, Country.COSTA_RICA, Country.EL_SALVADOR, Country.PARAGUAY, Country.HONDURAS, Country.DOMINICAN_REPUBLIC, Country.CUBA, Country.BOLIVIA, Country.ECUADOR, Country.GUATEMALA, Country.VENEZUELA, Country.PERU, Country.PARAGUAY, Country.BOLIVIA, Region.CANARY_ISLANDS, Region.LATIN_AMERICA), 283 | otherTerritories = listOf(Country.USA, Country.BELIZE, Country.ANDORRA, Country.GIBRALTAR), 284 | ), 285 | SWAHILI( 286 | code = "sw", 287 | officialTerritories = listOf(Country.TANZANIA, Country.KENYA, Country.UGANDA, Country.DEMOCRATIC_REPUBLIC_OF_CONGO), 288 | otherTerritories = listOf(Country.RWANDA, Country.BURUNDI, Country.SOMALIA, Country.MOZAMBIQUE, Country.MALAWI, Country.ZAMBIA, Country.COMOROS, Country.SOUTH_SUDAN), 289 | ), 290 | SWEDISH( 291 | code = "sv", 292 | officialTerritories = listOf(Country.SWEDEN, Country.FINLAND), 293 | otherTerritories = listOf(Country.ESTONIA, Country.LATVIA), 294 | ), 295 | TAMIL( 296 | code = "ta", 297 | officialTerritories = listOf(Country.INDIA), 298 | otherTerritories = emptyList(), 299 | ), 300 | TAGALOG( 301 | code = "tl", 302 | officialTerritories = listOf(Country.PHILIPPINES), 303 | otherTerritories = emptyList(), 304 | ), 305 | TELUGU( 306 | code = "te", 307 | officialTerritories = listOf(Country.INDIA), 308 | otherTerritories = emptyList(), 309 | ), 310 | THAI( 311 | code = "th", 312 | officialTerritories = listOf(Country.THAILAND), 313 | otherTerritories = emptyList(), 314 | ), 315 | TURKISH( 316 | code = "tr", 317 | officialTerritories = listOf(Country.TURKEY), 318 | otherTerritories = emptyList(), 319 | ), 320 | UKRAINIAN( 321 | code = "uk", 322 | officialTerritories = listOf(Country.UKRAINE), 323 | otherTerritories = listOf(Country.KAZAKHSTAN, Country.MOLDOVA, Country.POLAND, Country.ROMANIA, Country.LITHUANIA, Country.SLOVAKIA), 324 | ), 325 | URDU( 326 | code = "ur", 327 | officialTerritories = listOf(Country.PAKISTAN), 328 | otherTerritories = listOf(), 329 | ), 330 | UZBEK( 331 | code = "uz", 332 | officialTerritories = listOf(Country.UZBEKISTAN), 333 | otherTerritories = listOf(Country.KAZAKHSTAN, Country.KYRGYZSTAN, Country.TAJIKISTAN, Country.TURKMENISTAN), 334 | ), 335 | VIETNAMESE( 336 | code = "vi", 337 | officialTerritories = listOf(Country.VIETNAM), 338 | otherTerritories = emptyList(), 339 | ), 340 | ; 341 | 342 | val defaultCountry = officialTerritories.first() 343 | 344 | fun displayName() = commonDisplayName() 345 | 346 | companion object { 347 | fun from(identifier: String): Language = requireNotNull(fromOrNull(identifier)) { "Can't get language from $identifier" } 348 | 349 | fun fromOrNull(identifier: String?): Language? = entries.firstOrNull { it.code.equals(identifier, ignoreCase = true) || it.legacyCode?.equals(identifier, ignoreCase = true) == true } 350 | 351 | fun fromLocale(locale: String): Language = requireNotNull(fromLocaleOrNull(locale)) { "Can't get language from $locale" } 352 | 353 | fun fromLocaleOrNull(locale: String?): Language? = fromOrNull(localeSplit(locale).getOrNull(0)) 354 | } 355 | } 356 | 357 | /** Returns the display name of [Language] in the current language. */ 358 | internal expect fun Language.commonDisplayName(): String 359 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Languages.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 4 | expect object Languages { 5 | /** Returns the current ISO 639-1 [Language.code] - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes. */ 6 | fun currentLanguageCode(): String 7 | } 8 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Locale.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | data class Locale( 4 | val language: Language, 5 | val territory: Territory?, 6 | ) : Comparable { 7 | override fun toString() = listOfNotNull( 8 | language.code, 9 | territory?.code, 10 | ).joinToString(separator = "-") 11 | 12 | override fun compareTo(other: Locale): Int = compareValuesBy(this, other, { it.language }, { it.territory as Comparable<*> }) 13 | 14 | companion object { 15 | fun from(locale: String) = requireNotNull(fromOrNull(locale)) { "Can't get locale for $locale" } 16 | 17 | fun fromOrNull(locale: String?): Locale? { 18 | val language = Language.fromLocaleOrNull(locale) 19 | 20 | return if (language != null) { 21 | Locale( 22 | language = language, 23 | territory = Country.fromLocaleOrNull(locale) ?: Region.fromLocaleOrNull(locale), 24 | ) 25 | } else { 26 | null 27 | } 28 | } 29 | 30 | fun fromAndroidValuesDirectoryName(androidValuesDirectoryName: String) = requireNotNull(fromAndroidValuesDirectoryNameOrNull(androidValuesDirectoryName)) { "Can't get locale for $androidValuesDirectoryName" } 31 | 32 | fun fromAndroidValuesDirectoryNameOrNull(androidValuesDirectoryName: String): Locale? { 33 | val name = androidValuesDirectoryName 34 | .removePrefix("values") 35 | .removePrefix("-") 36 | 37 | return when (androidValuesDirectoryName) { 38 | "values" -> Locale(Language.ENGLISH, null) 39 | else -> fromOrNull(name.replace("-r", "-")) 40 | } 41 | } 42 | } 43 | } 44 | 45 | internal fun localeSplit(locale: String?) = when { 46 | locale == null -> emptyList() 47 | locale.contains("_") -> locale.split("_") 48 | locale.contains("-") -> locale.split("-") 49 | else -> listOf(locale) 50 | } 51 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Locales.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 4 | expect object Locales { 5 | /** Returns the current locale. */ 6 | fun currentLocaleString(): String 7 | 8 | /** 9 | * On Android / JVM, it returns the supported locales. 10 | * On iOS, it returns the preferred languages. 11 | */ 12 | fun currentLocaleStrings(): List 13 | } 14 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Region.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | enum class Region( 4 | /** ISO 3166-1 alpha-2 code - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 */ 5 | override val code: String, 6 | /** ISO 3166-1 alpha-3 code - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3 */ 7 | override val code3: String, 8 | ) : Territory { 9 | EUROPEAN_UNION(code = "EU", code3 = "EUE"), 10 | LATIN_AMERICA(code = "419", code3 = "419"), 11 | CANARY_ISLANDS(code = "IC", code3 = ""), 12 | INTERNATIONAL_WATERS(code = "XZ", code3 = "XZZ"), // https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#User-assigned_code_elemen 13 | ; 14 | 15 | override val emoji: String? get() = 16 | when (this) { 17 | EUROPEAN_UNION -> """🇪🇺""" 18 | LATIN_AMERICA -> null 19 | CANARY_ISLANDS -> """🇮🇨""" 20 | INTERNATIONAL_WATERS -> """🌊""" 21 | } 22 | 23 | companion object { 24 | fun fromOrNull(identifier: String?) = when { 25 | identifier.isNullOrBlank() -> null 26 | else -> entries.firstOrNull { it.code.equals(identifier, ignoreCase = true) || it.code3.equals(identifier, ignoreCase = true) } 27 | } 28 | 29 | fun fromLocaleOrNull(locale: String?): Region? { 30 | val splits = localeSplit(locale) 31 | return fromOrNull(splits.getOrNull(1)) 32 | } 33 | 34 | /** Tries every possible combination that I know to somehow get a meaningful country. */ 35 | fun fromLenientOrNull(string: String?) = entries.firstOrNull { it.name.equals(string, ignoreCase = true) } 36 | ?: fromOrNull(string) 37 | ?: fromLocaleOrNull(string) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/Territory.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | sealed interface Territory { 4 | /** ISO 3166-1 alpha-2 code - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 */ 5 | val code: String 6 | 7 | /** ISO 3166-1 alpha-3 code - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3 */ 8 | val code3: String 9 | 10 | /** Optional emoji. */ 11 | val emoji: String? 12 | 13 | fun displayName() = commonDisplayName() 14 | } 15 | 16 | /** Returns the display name of [Territory] in the current language. */ 17 | internal expect fun Territory.commonDisplayName(): String 18 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonMain/kotlin/com/vanniktech/locale/capitalize.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | internal fun String.capitalized() = replaceFirstChar { 4 | when { 5 | it.isLowerCase() -> it.titlecase() 6 | else -> it.toString() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonTest/kotlin/com/vanniktech/locale/CountriesTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class CountriesTest { 7 | @Test fun currentCountryCode() { 8 | assertEquals(expected = "US", actual = Countries.currentCountryCode()) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonTest/kotlin/com/vanniktech/locale/CountryTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class CountryTest { 7 | @Test fun uniqueEmojis() { 8 | val emojis = Country.entries.groupBy { it.emoji } 9 | emojis.forEach { (emoji, countries) -> 10 | assertEquals(message = "Emoji $emoji found multiple times: ${countries.joinToString { it.displayName() }}", expected = 1, actual = countries.size) 11 | } 12 | } 13 | 14 | @Test fun callingCodesAreSorted() { 15 | Country.entries 16 | .forEach { 17 | assertEquals(expected = it.callingCodes.sorted(), actual = it.callingCodes, message = "Dial codes not sorted for $it") 18 | } 19 | } 20 | 21 | @Test fun displayName() { 22 | assertEquals(expected = "United Kingdom", actual = Country.ENGLAND.displayName()) 23 | assertEquals(expected = "Germany", actual = Country.GERMANY.displayName()) 24 | } 25 | 26 | @Test fun fromOrNull() { 27 | assertEquals(expected = null, actual = Country.fromOrNull(null)) 28 | assertEquals(expected = null, actual = Country.fromOrNull("")) 29 | assertEquals(expected = null, actual = Country.fromOrNull("xxx")) 30 | assertEquals(expected = Country.ARMENIA, actual = Country.fromOrNull("AM")) 31 | assertEquals(expected = Country.ARMENIA, actual = Country.fromOrNull("am")) 32 | assertEquals(expected = Country.USA, actual = Country.fromOrNull("US")) 33 | assertEquals(expected = Country.USA, actual = Country.fromOrNull("USA")) 34 | } 35 | 36 | @Test fun fromLocaleOrNull() { 37 | assertEquals(expected = Country.USA, actual = Country.fromLocaleOrNull("de-US")) 38 | assertEquals(expected = Country.USA, actual = Country.fromLocaleOrNull("en-US")) 39 | assertEquals(expected = Country.USA, actual = Country.fromLocaleOrNull("de_US")) 40 | assertEquals(expected = Country.USA, actual = Country.fromLocaleOrNull("en_US")) 41 | assertEquals(expected = null, actual = Country.fromLocaleOrNull("en")) 42 | assertEquals(expected = null, actual = Country.fromLocaleOrNull("de")) 43 | assertEquals(expected = null, actual = Country.fromLocaleOrNull("")) 44 | assertEquals(expected = null, actual = Country.fromLocaleOrNull(null)) 45 | } 46 | 47 | @Test fun fromLenientOrNull() { 48 | assertEquals(expected = null, actual = Country.fromLenientOrNull("xxx")) 49 | assertEquals(expected = null, actual = Country.fromLenientOrNull(null)) 50 | assertEquals(expected = Country.USA, actual = Country.fromLenientOrNull("US")) 51 | assertEquals(expected = Country.USA, actual = Country.fromLenientOrNull("us")) 52 | assertEquals(expected = Country.USA, actual = Country.fromLenientOrNull("USA")) 53 | assertEquals(expected = Country.USA, actual = Country.fromLenientOrNull("usa")) 54 | assertEquals(expected = Country.USA, actual = Country.fromLenientOrNull("en-US")) 55 | assertEquals(expected = Country.GERMANY, actual = Country.fromLenientOrNull("en-DE")) 56 | assertEquals(expected = Country.VIETNAM, actual = Country.fromLenientOrNull("VIETNAM")) 57 | assertEquals(expected = Country.ZAMBIA, actual = Country.fromLenientOrNull("zambia")) 58 | } 59 | 60 | @Test fun country() { 61 | assertEquals( 62 | expected = """ 63 | AD-ANDORRA 🇦🇩 64 | AE-UNITED_ARAB_EMIRATES 🇦🇪 65 | AF-AFGHANISTAN 🇦🇫 66 | AG-ANTIGUA_AND_BARBUDA 🇦🇬 67 | AI-ANGUILLA 🇦🇮 68 | AL-ALBANIA 🇦🇱 69 | AM-ARMENIA 🇦🇲 70 | AO-ANGOLA 🇦🇴 71 | AQ-ANTARCTICA 🇦🇶 72 | AR-ARGENTINA 🇦🇷 73 | AS-AMERICAN_SAMOA 🇦🇸 74 | AT-AUSTRIA 🇦🇹 75 | AU-AUSTRALIA 🇦🇺 76 | AW-ARUBA 🇦🇼 77 | AX-ALAND_ISLANDS 🇦🇽 78 | AZ-AZERBAIJAN 🇦🇿 79 | BA-BOSNIA_AND_HERZEGOVINA 🇧🇦 80 | BB-BARBADOS 🇧🇧 81 | BD-BANGLADESH 🇧🇩 82 | BE-BELGIUM 🇧🇪 83 | BF-BURKINA_FASO 🇧🇫 84 | BG-BULGARIA 🇧🇬 85 | BH-BAHRAIN 🇧🇭 86 | BI-BURUNDI 🇧🇮 87 | BJ-BENIN 🇧🇯 88 | BL-SAINT_BARTS 🇧🇱 89 | BM-BERMUDA 🇧🇲 90 | BN-BRUNEI 🇧🇳 91 | BO-BOLIVIA 🇧🇴 92 | BQ-BONAIRE 🇧🇶 93 | BR-BRAZIL 🇧🇷 94 | BS-BAHAMAS 🇧🇸 95 | BT-BHUTAN 🇧🇹 96 | BV-BOUVET_ISLAND 🇧🇻 97 | BW-BOTSWANA 🇧🇼 98 | BY-BELARUS 🇧🇾 99 | BZ-BELIZE 🇧🇿 100 | CA-CANADA 🇨🇦 101 | CC-COCOS_ISLANDS 🇨🇨 102 | CD-DEMOCRATIC_REPUBLIC_OF_CONGO 🇨🇩 103 | CF-CENTRAL_AFRICAN_REPUBLIC 🇨🇫 104 | CG-REPUBLIC_OF_THE_CONGO 🇨🇬 105 | CH-SWITZERLAND 🇨🇭 106 | CI-COTE_DIVOIRE 🇨🇮 107 | CK-COOK_ISLANDS 🇨🇰 108 | CL-CHILE 🇨🇱 109 | CM-CAMEROON 🇨🇲 110 | CN-CHINA 🇨🇳 111 | CO-COLOMBIA 🇨🇴 112 | CR-COSTA_RICA 🇨🇷 113 | CU-CUBA 🇨🇺 114 | CV-CAPE_VERDE 🇨🇻 115 | CW-CURACAO 🇨🇼 116 | CX-CHRISTMAS_ISLAND 🇨🇽 117 | CY-CYPRUS 🇨🇾 118 | CZ-CZECHIA 🇨🇿 119 | DE-GERMANY 🇩🇪 120 | DJ-DJIBOUTI 🇩🇯 121 | DK-DENMARK 🇩🇰 122 | DM-DOMINICA 🇩🇲 123 | DO-DOMINICAN_REPUBLIC 🇩🇴 124 | DZ-ALGERIA 🇩🇿 125 | EC-ECUADOR 🇪🇨 126 | EE-ESTONIA 🇪🇪 127 | EG-EGYPT 🇪🇬 128 | EH-SAHRAWI_ARAB_DEMOCRATIC_REPUBLIC 🇪🇭 129 | ER-ERITREA 🇪🇷 130 | ES-SPAIN 🇪🇸 131 | ET-ETHIOPIA 🇪🇹 132 | FI-FINLAND 🇫🇮 133 | FJ-FIJI 🇫🇯 134 | FK-FALKLAND_ISLANDS 🇫🇰 135 | FM-MICRONESIA 🇫🇲 136 | FO-FAROE_ISLANDS 🇫🇴 137 | FR-FRANCE 🇫🇷 138 | GA-GABON 🇬🇦 139 | GB-ENGLAND 🇬🇧 140 | GD-GRENADA 🇬🇩 141 | GE-GEORGIA 🇬🇪 142 | GF-FRENCH_GUIANA 🇬🇫 143 | GG-GUERNSEY 🇬🇬 144 | GH-GHANA 🇬🇭 145 | GI-GIBRALTAR 🇬🇮 146 | GL-GREENLAND 🇬🇱 147 | GM-GAMBIA 🇬🇲 148 | GN-GUINEA 🇬🇳 149 | GP-GUADELOUPE 🇬🇵 150 | GQ-EQUATORIAL_GUINEA 🇬🇶 151 | GR-GREECE 🇬🇷 152 | GS-SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS 🇬🇸 153 | GT-GUATEMALA 🇬🇹 154 | GU-GUAM 🇬🇺 155 | GW-GUINEA_BISSAU 🇬🇼 156 | GY-GUYANA 🇬🇾 157 | HK-HONG_KONG 🇭🇰 158 | HM-HEARD_ISLAND_AND_MCDONALD_ISLANDS 🇭🇲 159 | HN-HONDURAS 🇭🇳 160 | HR-CROATIA 🇭🇷 161 | HT-HAITI 🇭🇹 162 | HU-HUNGARY 🇭🇺 163 | ID-INDONESIA 🇮🇩 164 | IE-IRELAND 🇮🇪 165 | IL-ISRAEL 🇮🇱 166 | IM-ISLE_OF_MAN 🇮🇲 167 | IN-INDIA 🇮🇳 168 | IO-BRITISH_INDIAN_OCEAN_TERRITORY 🇮🇴 169 | IQ-IRAQ 🇮🇶 170 | IR-IRAN 🇮🇷 171 | IS-ICELAND 🇮🇸 172 | IT-ITALY 🇮🇹 173 | JE-JERSEY 🇯🇪 174 | JM-JAMAICA 🇯🇲 175 | JO-JORDAN 🇯🇴 176 | JP-JAPAN 🇯🇵 177 | KE-KENYA 🇰🇪 178 | KG-KYRGYZSTAN 🇰🇬 179 | KH-CAMBODIA 🇰🇭 180 | KI-KIRIBATI 🇰🇮 181 | KM-COMOROS 🇰🇲 182 | KN-SAINT_KITTS_AND_NEVIS 🇰🇳 183 | KP-NORTH_KOREA 🇰🇵 184 | KR-SOUTH_KOREA 🇰🇷 185 | KW-KUWAIT 🇰🇼 186 | KY-CAYMAN_ISLANDS 🇰🇾 187 | KZ-KAZAKHSTAN 🇰🇿 188 | LA-LAOS 🇱🇦 189 | LB-LEBANON 🇱🇧 190 | LC-SAINT_LUCIA 🇱🇨 191 | LI-LIECHTENSTEIN 🇱🇮 192 | LK-SRI_LANKA 🇱🇰 193 | LR-LIBERIA 🇱🇷 194 | LS-LESOTHO 🇱🇸 195 | LT-LITHUANIA 🇱🇹 196 | LU-LUXEMBOURG 🇱🇺 197 | LV-LATVIA 🇱🇻 198 | LY-LIBYA 🇱🇾 199 | MA-MOROCCO 🇲🇦 200 | MC-MONACO 🇲🇨 201 | MD-MOLDOVA 🇲🇩 202 | ME-MONTENEGRO 🇲🇪 203 | MF-SAINT_MARTIN 🇲🇫 204 | MG-MADAGASCAR 🇲🇬 205 | MH-MARSHALL_ISLAND 🇲🇭 206 | MK-NORTH_MACEDONIA 🇲🇰 207 | ML-MALI 🇲🇱 208 | MM-MYANMAR 🇲🇲 209 | MN-MONGOLIA 🇲🇳 210 | MO-MACAO 🇲🇴 211 | MP-NORTHERN_MARIANA_ISLANDS 🇲🇵 212 | MQ-MARTINIQUE 🇲🇶 213 | MR-MAURITANIA 🇲🇷 214 | MS-MONTSERRAT 🇲🇸 215 | MT-MALTA 🇲🇹 216 | MU-MAURITIUS 🇲🇺 217 | MV-MALDIVES 🇲🇻 218 | MW-MALAWI 🇲🇼 219 | MX-MEXICO 🇲🇽 220 | MY-MALAYSIA 🇲🇾 221 | MZ-MOZAMBIQUE 🇲🇿 222 | NA-NAMIBIA 🇳🇦 223 | NC-NEW_CALEDONIA 🇳🇨 224 | NE-NIGER 🇳🇪 225 | NF-NORFOLK_ISLAND 🇳🇫 226 | NG-NIGERIA 🇳🇬 227 | NI-NICARAGUA 🇳🇮 228 | NL-NETHERLANDS 🇳🇱 229 | NO-NORWAY 🇳🇴 230 | NP-NEPAL 🇳🇵 231 | NR-NAURU 🇳🇷 232 | NU-NIUE 🇳🇺 233 | NZ-NEW_ZEALAND 🇳🇿 234 | OM-OMAN 🇴🇲 235 | PA-PANAMA 🇵🇦 236 | PE-PERU 🇵🇪 237 | PF-FRENCH_POLYNESIA 🇵🇫 238 | PG-PAPUA_NEW_GUINEA 🇵🇬 239 | PH-PHILIPPINES 🇵🇭 240 | PK-PAKISTAN 🇵🇰 241 | PL-POLAND 🇵🇱 242 | PM-SAINT_PIERRE_AND_MIQUELON 🇵🇲 243 | PN-PITCAIRN_ISLANDS 🇵🇳 244 | PR-PUERTO_RICO 🇵🇷 245 | PS-PALESTINE 🇵🇸 246 | PT-PORTUGAL 🇵🇹 247 | PW-PALAU 🇵🇼 248 | PY-PARAGUAY 🇵🇾 249 | QA-QATAR 🇶🇦 250 | RE-REUNION 🇷🇪 251 | RO-ROMANIA 🇷🇴 252 | RS-SERBIA 🇷🇸 253 | RU-RUSSIA 🇷🇺 254 | RW-RWANDA 🇷🇼 255 | SA-SAUDI_ARABIA 🇸🇦 256 | SB-SOLOMON_ISLANDS 🇸🇧 257 | SC-SEYCHELLES 🇸🇨 258 | SD-SUDAN 🇸🇩 259 | SE-SWEDEN 🇸🇪 260 | SG-SINGAPORE 🇸🇬 261 | SH-SAINT_HELENA_ASCENSION_AND_TRISTAN_DA_CUNHA 🇸🇭 262 | SI-SLOVENIA 🇸🇮 263 | SJ-SVALBARD_AND_JAN_MAYEN 🇸🇯 264 | SK-SLOVAKIA 🇸🇰 265 | SL-SIERRA_LEONE 🇸🇱 266 | SM-SAN_MARINO 🇸🇲 267 | SN-SENEGAL 🇸🇳 268 | SO-SOMALIA 🇸🇴 269 | SR-SURINAME 🇸🇷 270 | SS-SOUTH_SUDAN 🇸🇸 271 | ST-SAO_TOME_AND_PRINCE 🇸🇹 272 | SV-EL_SALVADOR 🇸🇻 273 | SX-SINT_MAARTEN 🇸🇽 274 | SY-SYRIA 🇸🇾 275 | SZ-ESWATINI 🇸🇿 276 | TC-TURKS_AND_CAICOS 🇹🇨 277 | TD-CHAD 🇹🇩 278 | TF-FRENCH_SOUTHERN_AND_ANTARCTIC_LANDS 🇹🇫 279 | TG-TOGO 🇹🇬 280 | TH-THAILAND 🇹🇭 281 | TJ-TAJIKISTAN 🇹🇯 282 | TK-TOKELAU 🇹🇰 283 | TL-TIMOR_LESTE 🇹🇱 284 | TM-TURKMENISTAN 🇹🇲 285 | TN-TUNISIA 🇹🇳 286 | TO-TONGA 🇹🇴 287 | TR-TURKEY 🇹🇷 288 | TT-TRINIDAD_AND_TOBAGO 🇹🇹 289 | TV-TUVALU 🇹🇻 290 | TW-TAIWAN 🇹🇼 291 | TZ-TANZANIA 🇹🇿 292 | UA-UKRAINE 🇺🇦 293 | UG-UGANDA 🇺🇬 294 | UM-UNITED_STATES_MINOR_OUTLYING_ISLANDS 🇺🇲 295 | US-USA 🇺🇸 296 | UY-URUGUAY 🇺🇾 297 | UZ-UZBEKISTAN 🇺🇿 298 | VA-VATICAN_CITY 🇻🇦 299 | VC-SAINT_VINCENT_AND_THE_GRENADINES 🇻🇨 300 | VE-VENEZUELA 🇻🇪 301 | VG-BRITISH_VIRGIN_ISLANDS 🇻🇬 302 | VI-US_VIRGIN_ISLANDS 🇻🇮 303 | VN-VIETNAM 🇻🇳 304 | VU-VANUATU 🇻🇺 305 | WF-WALLIS_AND_FUTUNA 🇼🇫 306 | WS-SAMOA 🇼🇸 307 | XK-KOSOVO 🇽🇰 308 | YE-YEMEN 🇾🇪 309 | YT-MAYOTTE 🇾🇹 310 | ZA-SOUTH_AFRICA 🇿🇦 311 | ZM-ZAMBIA 🇿🇲 312 | ZW-ZIMBABWE 🇿🇼 313 | """.trimIndent(), 314 | actual = Country.entries.sortedBy { it.code }.joinToString(separator = "\n") { "${it.code}-${it.name} ${it.emoji}" }, 315 | ) 316 | } 317 | 318 | @Test fun noDuplicates() { 319 | val unique = Country.entries.map { it.code }.toSet() 320 | assertEquals(expected = Country.entries.size, actual = unique.size) 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonTest/kotlin/com/vanniktech/locale/LanguageTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | import kotlin.test.assertFailsWith 6 | 7 | class LanguageTest { 8 | @Test fun from() { 9 | assertEquals(expected = Language.UKRAINIAN, actual = Language.from("uk")) 10 | 11 | assertFailsWith { 12 | Language.from("asdfa") 13 | } 14 | } 15 | 16 | @Test fun fromOrNull() { 17 | assertEquals(expected = Language.UKRAINIAN, actual = Language.fromOrNull("uk")) 18 | 19 | assertEquals(expected = null, actual = Language.fromOrNull("asdfa")) 20 | } 21 | 22 | @Test fun fromLocale() { 23 | assertEquals(expected = Language.UKRAINIAN, actual = Language.fromLocale("uk")) 24 | 25 | assertFailsWith { 26 | Language.fromLocale("asdfa") 27 | } 28 | } 29 | 30 | @Test fun fromLocaleOrNull() { 31 | assertEquals(expected = Language.UKRAINIAN, actual = Language.fromLocaleOrNull("uk-UA")) 32 | 33 | assertEquals(expected = null, actual = Language.fromLocaleOrNull("asdfa")) 34 | } 35 | 36 | @Test fun legacyIdentifier() { 37 | assertEquals(expected = Language.HEBREW, actual = Language.from("he")) 38 | assertEquals(expected = Language.HEBREW, actual = Language.from("iw")) 39 | 40 | assertEquals(expected = Language.INDONESIAN, actual = Language.from("id")) 41 | assertEquals(expected = Language.INDONESIAN, actual = Language.from("in")) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonTest/kotlin/com/vanniktech/locale/LanguagesTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class LanguagesTest { 7 | @Test fun currentLanguageCode() { 8 | assertEquals(expected = "en", actual = Languages.currentLanguageCode()) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonTest/kotlin/com/vanniktech/locale/LocaleTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class LocaleTest { 7 | @Test fun sorted() { 8 | assertEquals( 9 | expected = listOf( 10 | Locale(Language.ENGLISH, territory = Country.USA), 11 | Locale(Language.GERMAN, territory = Country.GERMANY), 12 | ), 13 | actual = listOf( 14 | Locale(Language.GERMAN, territory = Country.GERMANY), 15 | Locale(Language.ENGLISH, territory = Country.USA), 16 | ).sorted(), 17 | ) 18 | } 19 | 20 | @Test fun fromOrNull() { 21 | listOf( 22 | Pair(Locale(Language.ENGLISH, territory = null), "en"), 23 | Pair(Locale(Language.GERMAN, territory = null), "de"), 24 | Pair(Locale(Language.ENGLISH, Country.ENGLAND), "en-GB"), 25 | Pair(Locale(Language.ENGLISH, Country.USA), "en-US"), 26 | Pair(Locale(Language.ENGLISH, Country.USA), "en_US"), 27 | Pair(Locale(Language.DUTCH, Country.NETHERLANDS), "nl-NL"), 28 | Pair(Locale(Language.DUTCH, Country.NETHERLANDS), "nl_NL"), 29 | Pair(Locale(Language.ITALIAN, Country.ITALY), "it-IT"), 30 | Pair(Locale(Language.ITALIAN, Country.ITALY), "it_IT"), 31 | Pair(Locale(Language.GERMAN, Country.USA), "de-US"), 32 | Pair(Locale(Language.GERMAN, Country.USA), "de_US"), 33 | Pair(Locale(Language.DANISH, Country.DENMARK), "da-DK"), 34 | Pair(Locale(Language.DANISH, Country.DENMARK), "da_DK"), 35 | Pair(Locale(Language.KOREAN, Country.SOUTH_KOREA), "ko_KR"), 36 | Pair(Locale(Language.KOREAN, Country.SOUTH_KOREA), "ko-KR"), 37 | Pair(Locale(Language.KOREAN, Country.NORTH_KOREA), "ko_KP"), 38 | Pair(Locale(Language.KOREAN, Country.NORTH_KOREA), "ko-KP"), 39 | Pair(Locale(Language.JAPANESE, Country.JAPAN), "ja-JP"), 40 | Pair(Locale(Language.JAPANESE, Country.JAPAN), "ja_JP"), 41 | Pair(Locale(Language.PORTUGUESE, Country.PORTUGAL), "pt-PT"), 42 | Pair(Locale(Language.PORTUGUESE, Country.PORTUGAL), "pt_PT"), 43 | Pair(Locale(Language.PORTUGUESE, Country.BRAZIL), "pt-BR"), 44 | Pair(Locale(Language.PORTUGUESE, Country.BRAZIL), "pt_BR"), 45 | Pair(Locale(Language.CHINESE, Country.CHINA), "zh-CN"), 46 | Pair(Locale(Language.CHINESE, Country.CHINA), "zh_CN_#Hans"), 47 | Pair(Locale(Language.SERBIAN, Country.SERBIA), "sr-RS"), 48 | Pair(Locale(Language.SERBIAN, Country.SERBIA), "sr_RS_#Latn"), 49 | Pair(Locale(Language.SLOVAK, Country.SLOVAKIA), "sk-SK"), 50 | Pair(Locale(Language.SLOVAK, Country.SLOVAKIA), "sk_SK"), 51 | Pair(Locale(Language.THAI, Country.THAILAND), "th-TH"), 52 | Pair(Locale(Language.ARABIC, Country.EGYPT), "ar_EG_#u-nu-latn"), 53 | Pair(Locale(Language.HINDI, Country.INDIA), "hi-IN"), 54 | Pair(Locale(Language.GUJARATI, Country.INDIA), "gu-IN"), 55 | Pair(Locale(Language.MALAY, Country.MALAYSIA), "ms_MY"), 56 | Pair(Locale(Language.FARSI, Country.IRAN), "fa-IR"), 57 | Pair(Locale(Language.ARMENIAN, Country.ARMENIA), "hy-AM"), 58 | Pair(Locale(Language.CROATIAN, Country.CROATIA), "hr-HR"), 59 | Pair(Locale(Language.AZERBAIJANI, Country.AZERBAIJAN), "az-AZ"), 60 | Pair(Locale(Language.BURMESE, Country.MYANMAR), "my-MM"), 61 | Pair(Locale(Language.IRISH, Country.IRELAND), "ga-IE"), 62 | Pair(Locale(Language.UZBEK, Country.UZBEKISTAN), "uz_UZ"), 63 | Pair(Locale(Language.CATALAN, Country.SPAIN), "ca_ES"), 64 | Pair(Locale(Language.BOKMAL, Country.NORWAY), "nb_NO"), 65 | Pair(Locale(Language.MALAYALAM, Country.INDIA), "ml-IN"), 66 | Pair(Locale(Language.URDU, Country.PAKISTAN), "ur_PK"), 67 | Pair(Locale(Language.CENTRAL_KMHER, Country.ENGLAND), "km-GB"), 68 | Pair(Locale(Language.AMHARIC, Country.ETHIOPIA), "am-ET"), 69 | Pair(Locale(Language.FILIPINO, Country.PHILIPPINES), "fil-PH"), 70 | Pair(Locale(Language.ICELANDIC, Country.ICELAND), "is_IS"), 71 | Pair(Locale(Language.SPANISH, Region.LATIN_AMERICA), "es-419"), 72 | Pair(Locale(Language.SPANISH, Region.CANARY_ISLANDS), "es-IC"), 73 | Pair(Locale(Language.BOSNIAN, Country.BOSNIA_AND_HERZEGOVINA), "bs-BA"), 74 | ).forEach { (locale, localeString) -> 75 | assertEquals( 76 | message = localeString, 77 | expected = locale, 78 | actual = Locale.fromOrNull(localeString), 79 | ) 80 | } 81 | } 82 | 83 | @Test fun fromAndroidValuesDirectoryName() { 84 | assertEquals(expected = Locale(Language.ENGLISH, null), actual = Locale.fromAndroidValuesDirectoryName("values")) 85 | } 86 | 87 | @Test fun fromAndroidValuesDirectoryNameOrNull() { 88 | listOf( 89 | Pair(null, ""), 90 | Pair(null, "values-foo"), 91 | Pair(Locale(Language.ENGLISH, null), "values"), 92 | Pair(Locale(Language.ARABIC, null), "values-ar"), 93 | Pair(Locale(Language.BULGARIAN, null), "values-bg"), 94 | Pair(Locale(Language.CZECH, null), "values-cs"), 95 | Pair(Locale(Language.GERMAN, null), "values-de"), 96 | Pair(Locale(Language.GREEK, null), "values-el"), 97 | Pair(Locale(Language.SPANISH, null), "values-es"), 98 | Pair(Locale(Language.FINNISH, null), "values-fi"), 99 | Pair(Locale(Language.FRENCH, null), "values-fr"), 100 | Pair(Locale(Language.HUNGARIAN, null), "values-hu"), 101 | Pair(Locale(Language.INDONESIAN, null), "values-in"), 102 | Pair(Locale(Language.ITALIAN, null), "values-it"), 103 | Pair(Locale(Language.HEBREW, null), "values-iw"), 104 | Pair(Locale(Language.DUTCH, null), "values-nl"), 105 | Pair(Locale(Language.NORWEGIAN, null), "values-no"), 106 | Pair(Locale(Language.POLISH, null), "values-pl"), 107 | Pair(Locale(Language.PORTUGUESE, null), "values-pt"), 108 | Pair(Locale(Language.PORTUGUESE, Country.BRAZIL), "values-pt-rBR"), 109 | Pair(Locale(Language.ROMANIAN, null), "values-ro"), 110 | Pair(Locale(Language.RUSSIAN, null), "values-ru"), 111 | Pair(Locale(Language.SWEDISH, null), "values-sv"), 112 | Pair(Locale(Language.TURKISH, null), "values-tr"), 113 | Pair(Locale(Language.UKRAINIAN, null), "values-uk"), 114 | Pair(Locale(Language.VIETNAMESE, null), "values-vi"), 115 | Pair(Locale(Language.CHINESE, Country.CHINA), "values-zh-rCN"), 116 | Pair(Locale(Language.CHINESE, Country.TAIWAN), "values-zh-rTW"), 117 | Pair(Locale(Language.CHINESE, Country.TAIWAN), "zh-rTW"), 118 | Pair(Locale(Language.AZERBAIJANI, Country.AZERBAIJAN), "az_AZ_#Latn"), 119 | Pair(Locale(Language.BELARUSIAN, Country.BELARUS), "be-BY"), 120 | Pair(Locale(Language.TAMIL, Country.INDIA), "ta-IN"), 121 | Pair(Locale(Language.MARATHI, Country.INDIA), "mr-IN"), 122 | ).forEach { (locale, androidValuesDirectoryName) -> 123 | assertEquals(message = androidValuesDirectoryName, expected = locale, actual = Locale.fromAndroidValuesDirectoryNameOrNull(androidValuesDirectoryName)) 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonTest/kotlin/com/vanniktech/locale/LocalesTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class LocalesTest { 7 | @Test fun currentLocaleString() { 8 | assertEquals(expected = "en_US", actual = Locales.currentLocaleString()) 9 | } 10 | 11 | @Test fun currentLocaleStrings() { 12 | val currentLocaleStrings = Locales.currentLocaleStrings() 13 | 14 | assertEquals( 15 | message = currentLocaleStrings.joinToString(), 16 | expected = true, 17 | actual = currentLocaleStrings.contains("en_US") || currentLocaleStrings.contains("en-US"), 18 | ) 19 | 20 | assertEquals( 21 | message = currentLocaleStrings.joinToString(), 22 | expected = true, 23 | actual = currentLocaleStrings.size >= 1, 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /multiplatform-locale/src/commonTest/kotlin/com/vanniktech/locale/RegionTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class RegionTest { 7 | @Test fun uniqueEmojis() { 8 | val emojis = Region.entries.groupBy { it.emoji } 9 | emojis.forEach { (emoji, countries) -> 10 | assertEquals(message = "Emoji $emoji found multiple times: ${countries.joinToString { it.displayName() }}", expected = 1, actual = countries.size) 11 | } 12 | } 13 | 14 | @Test fun displayName() { 15 | assertEquals(expected = "European Union", actual = Region.EUROPEAN_UNION.displayName()) 16 | assertEquals(expected = "Latin America", actual = Region.LATIN_AMERICA.displayName()) 17 | assertEquals(expected = "Canary Islands", actual = Region.CANARY_ISLANDS.displayName()) 18 | assertEquals(expected = "International Waters", actual = Region.INTERNATIONAL_WATERS.displayName()) 19 | } 20 | 21 | @Test fun fromOrNull() { 22 | assertEquals(expected = null, actual = Region.fromOrNull(null)) 23 | assertEquals(expected = null, actual = Region.fromOrNull("")) 24 | assertEquals(expected = null, actual = Region.fromOrNull("xxx")) 25 | assertEquals(expected = Region.EUROPEAN_UNION, actual = Region.fromOrNull("EU")) 26 | assertEquals(expected = Region.EUROPEAN_UNION, actual = Region.fromOrNull("eu")) 27 | assertEquals(expected = Region.INTERNATIONAL_WATERS, actual = Region.fromOrNull("XZZ")) 28 | assertEquals(expected = Region.INTERNATIONAL_WATERS, actual = Region.fromOrNull("xzz")) 29 | } 30 | 31 | @Test fun fromLocaleOrNull() { 32 | assertEquals(expected = Region.EUROPEAN_UNION, actual = Region.fromLocaleOrNull("de-EU")) 33 | assertEquals(expected = Region.LATIN_AMERICA, actual = Region.fromLocaleOrNull("es-419")) 34 | assertEquals(expected = Region.INTERNATIONAL_WATERS, actual = Region.fromLocaleOrNull("es-XZZ")) 35 | assertEquals(expected = null, actual = Region.fromLocaleOrNull("en")) 36 | assertEquals(expected = null, actual = Region.fromLocaleOrNull("de")) 37 | assertEquals(expected = null, actual = Region.fromLocaleOrNull("")) 38 | assertEquals(expected = null, actual = Region.fromLocaleOrNull(null)) 39 | } 40 | 41 | @Test fun fromLenientOrNull() { 42 | assertEquals(expected = null, actual = Region.fromLenientOrNull("xxx")) 43 | assertEquals(expected = null, actual = Region.fromLenientOrNull(null)) 44 | assertEquals(expected = Region.EUROPEAN_UNION, actual = Region.fromLenientOrNull("EU")) 45 | assertEquals(expected = Region.EUROPEAN_UNION, actual = Region.fromLenientOrNull("eu")) 46 | assertEquals(expected = Region.EUROPEAN_UNION, actual = Region.fromLenientOrNull("de-EU")) 47 | assertEquals(expected = Region.LATIN_AMERICA, actual = Region.fromLenientOrNull("es-419")) 48 | assertEquals(expected = Region.INTERNATIONAL_WATERS, actual = Region.fromLenientOrNull("es-XZZ")) 49 | } 50 | 51 | @Test fun region() { 52 | assertEquals( 53 | expected = """ 54 | 419-LATIN_AMERICA-null 55 | EU-EUROPEAN_UNION-🇪🇺 56 | IC-CANARY_ISLANDS-🇮🇨 57 | XZ-INTERNATIONAL_WATERS-🌊 58 | """.trimIndent(), 59 | actual = Region.entries.sortedBy { it.code }.joinToString(separator = "\n") { "${it.code}-${it.name}-${it.emoji}" }, 60 | ) 61 | } 62 | 63 | @Test fun noDuplicates() { 64 | val unique = Region.entries.map { it.code }.toSet() 65 | assertEquals(expected = Region.entries.size, actual = unique.size) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /multiplatform-locale/src/iosMain/kotlin/com/vanniktech/locale/Countries.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import platform.Foundation.NSLocale 4 | import platform.Foundation.countryCode 5 | import platform.Foundation.currentLocale 6 | 7 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 8 | actual object Countries { 9 | actual fun currentCountryCode(): String? = NSLocale.currentLocale.countryCode 10 | } 11 | -------------------------------------------------------------------------------- /multiplatform-locale/src/iosMain/kotlin/com/vanniktech/locale/Languages.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import platform.Foundation.NSLocale 4 | import platform.Foundation.currentLocale 5 | import platform.Foundation.languageCode 6 | 7 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 8 | actual object Languages { 9 | actual fun currentLanguageCode() = NSLocale.currentLocale.languageCode 10 | } 11 | -------------------------------------------------------------------------------- /multiplatform-locale/src/iosMain/kotlin/com/vanniktech/locale/Locales.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import platform.Foundation.NSLocale 4 | import platform.Foundation.currentLocale 5 | import platform.Foundation.localeIdentifier 6 | import platform.Foundation.preferredLanguages 7 | 8 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 9 | actual object Locales { 10 | actual fun currentLocaleString() = NSLocale.currentLocale.localeIdentifier 11 | 12 | actual fun currentLocaleStrings() = NSLocale.preferredLanguages.mapNotNull { it.toString() } 13 | } 14 | -------------------------------------------------------------------------------- /multiplatform-locale/src/iosMain/kotlin/com/vanniktech/locale/Multiplatform.ios.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import platform.Foundation.NSLocale 4 | import platform.Foundation.currentLocale 5 | import platform.Foundation.localizedStringForCountryCode 6 | import platform.Foundation.localizedStringForLanguageCode 7 | 8 | internal actual fun Language.commonDisplayName(): String = NSLocale.currentLocale.localizedStringForLanguageCode(code).orEmpty().capitalized() 9 | 10 | internal actual fun Territory.commonDisplayName() = when (this) { 11 | Region.INTERNATIONAL_WATERS -> "International Waters" 12 | else -> NSLocale.currentLocale.localizedStringForCountryCode(code).orEmpty().capitalized() 13 | } 14 | -------------------------------------------------------------------------------- /multiplatform-locale/src/jvmMain/kotlin/com/vanniktech/locale/Countries.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import java.util.Locale 4 | 5 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 6 | actual object Countries { 7 | actual fun currentCountryCode(): String? = Locale.getDefault().country 8 | } 9 | -------------------------------------------------------------------------------- /multiplatform-locale/src/jvmMain/kotlin/com/vanniktech/locale/Languages.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import java.util.Locale 4 | 5 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 6 | actual object Languages { 7 | actual fun currentLanguageCode(): String = Locale.getDefault().language 8 | } 9 | -------------------------------------------------------------------------------- /multiplatform-locale/src/jvmMain/kotlin/com/vanniktech/locale/Locales.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | import java.util.Locale 4 | 5 | @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") 6 | actual object Locales { 7 | actual fun currentLocaleString(): String = Locale.getDefault().toString() 8 | 9 | actual fun currentLocaleStrings() = listOf(currentLocaleString()) 10 | } 11 | -------------------------------------------------------------------------------- /multiplatform-locale/src/jvmMain/kotlin/com/vanniktech/locale/Multiplatform.jvm.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale 2 | 3 | internal actual fun Language.commonDisplayName() = java.util.Locale( 4 | code, 5 | "", 6 | ).displayLanguage.capitalized() 7 | 8 | internal actual fun Territory.commonDisplayName() = when (this) { 9 | Region.INTERNATIONAL_WATERS -> "International Waters" 10 | else -> java.util.Locale( 11 | java.util.Locale.getDefault().language, 12 | code, 13 | ).displayCountry.capitalized() 14 | } 15 | 16 | fun Locale.toJavaLocale() = java.util.Locale( 17 | language.code, 18 | territory?.code.orEmpty(), 19 | ) 20 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | ":disableDependencyDashboard" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /sample-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/sample-android.png -------------------------------------------------------------------------------- /sample-android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("org.jetbrains.kotlin.android") 4 | } 5 | 6 | kotlin { 7 | jvmToolchain(11) 8 | } 9 | 10 | android { 11 | namespace = "com.vanniktech.locale.sample.android" 12 | 13 | compileSdk = libs.versions.compileSdk.get().toInt() 14 | 15 | defaultConfig { 16 | applicationId = "com.vanniktech.locale.sample.android" 17 | vectorDrawables.useSupportLibrary = true 18 | minSdk = libs.versions.minSdk.get().toInt() 19 | targetSdk = libs.versions.targetSdk.get().toInt() 20 | versionCode = 1 21 | versionName = project.property("VERSION_NAME").toString() 22 | 23 | vectorDrawables.useSupportLibrary = true 24 | 25 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 26 | } 27 | 28 | compileOptions { 29 | sourceCompatibility = JavaVersion.VERSION_11 30 | targetCompatibility = JavaVersion.VERSION_11 31 | } 32 | 33 | buildFeatures { 34 | viewBinding = true 35 | } 36 | 37 | buildTypes { 38 | release { 39 | isMinifyEnabled = false 40 | isShrinkResources = false 41 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt")) 42 | } 43 | } 44 | } 45 | 46 | dependencies { 47 | implementation(project(":multiplatform-locale-all")) 48 | implementation(libs.timber) 49 | implementation(libs.material) 50 | } 51 | 52 | dependencies { 53 | debugImplementation(libs.leakcanary.android) 54 | } 55 | -------------------------------------------------------------------------------- /sample-android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /sample-android/src/main/kotlin/com/vanniktech/locale/sample/android/LocaleApplication.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.sample.android 2 | 3 | import android.app.Application 4 | import timber.log.Timber 5 | 6 | class LocaleApplication : Application() { 7 | override fun onCreate() { 8 | super.onCreate() 9 | Timber.plant(Timber.DebugTree()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sample-android/src/main/kotlin/com/vanniktech/locale/sample/android/LocaleMainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.locale.sample.android 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.vanniktech.locale.Country 7 | import com.vanniktech.locale.Language 8 | import com.vanniktech.locale.Locale 9 | import com.vanniktech.locale.Locales 10 | import com.vanniktech.locale.apple.app.store.appleAppStoreLocale 11 | import com.vanniktech.locale.google.play.store.googlePlayStoreLocale 12 | import com.vanniktech.locale.sample.android.databinding.ActivityMainBinding 13 | 14 | class LocaleMainActivity : AppCompatActivity() { 15 | @SuppressLint("SetTextI18n") 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | 19 | val binding = ActivityMainBinding.inflate(layoutInflater) 20 | setContentView(binding.root) 21 | 22 | val currentLocaleString = Locales.currentLocaleString() 23 | binding.currentLocaleString.text = "Current Locale: $currentLocaleString" 24 | binding.googlePlayStoreLocale.text = "Google Play Store Locale: ${Locale.fromOrNull(currentLocaleString)?.googlePlayStoreLocale()?.toString() ?: "/"}" 25 | binding.appleAppStoreLocale.text = "Apple App Store Locale: ${Locale.fromOrNull(currentLocaleString)?.appleAppStoreLocale()?.toString() ?: "/"}" 26 | binding.currentLocaleStrings.text = "Current Locale Strings: ${Locales.currentLocaleStrings().joinToString(separator = ", ")}" 27 | binding.allCountries.text = "All countries: ${Country.entries.joinToString { it.displayName() }}" 28 | binding.allLanguages.text = "All languages: ${Language.entries.joinToString { it.displayName() }}" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sample-android/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 18 | 25 | 32 | 40 | 48 | 56 | 57 | -------------------------------------------------------------------------------- /sample-android/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/sample-android/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-android/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/sample-android/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-android/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/sample-android/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-android/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/sample-android/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/sample-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample-android/src/main/res/values-v23/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /sample-android/src/main/res/values-v27/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /sample-android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Locale 4 | false 5 | 6 | -------------------------------------------------------------------------------- /sample-android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /sample-ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/multiplatform-locale/1483cc3065c65d04c23927cb0b08607d7e630228/sample-ios.png -------------------------------------------------------------------------------- /sample-ios/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /sample-ios/Podfile: -------------------------------------------------------------------------------- 1 | source "https://cdn.cocoapods.org/" 2 | platform :ios, '14.0' 3 | workspace "ios" 4 | 5 | # Comment this line if you're not using Swift and don't want to use dynamic frameworks. 6 | use_frameworks! 7 | 8 | target 'ios' do 9 | project 'ios' 10 | 11 | pod "MultiplatformLocaleAll", :path => "../multiplatform-locale-all" 12 | end 13 | -------------------------------------------------------------------------------- /sample-ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MultiplatformLocaleAll (0.10.0-SNAPSHOT) 3 | 4 | DEPENDENCIES: 5 | - MultiplatformLocaleAll (from `../multiplatform-locale-all`) 6 | 7 | EXTERNAL SOURCES: 8 | MultiplatformLocaleAll: 9 | :path: "../multiplatform-locale-all" 10 | 11 | SPEC CHECKSUMS: 12 | MultiplatformLocaleAll: 7a8aa6addcfc6e2bcd5dc38d97dcade45fc67a54 13 | 14 | PODFILE CHECKSUM: 9ed168d98ea160d8c0c0865fb29f7c761bf8bdfe 15 | 16 | COCOAPODS: 1.16.2 17 | -------------------------------------------------------------------------------- /sample-ios/ios.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 850D4FB228CCEBA100714308 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850D4FB128CCEBA100714308 /* App.swift */; }; 11 | 850D4FB628CCEBA100714308 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 850D4FB528CCEBA100714308 /* Assets.xcassets */; }; 12 | 850D4FB928CCEBA100714308 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 850D4FB828CCEBA100714308 /* Preview Assets.xcassets */; }; 13 | D65BB7D28364C04EC850180D /* Pods_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93C0936F297D4B91B3ABA725 /* Pods_ios.framework */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | 208331B5D6E6A7716D6C289D /* Pods-BlurHash.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BlurHash.release.xcconfig"; path = "Target Support Files/Pods-BlurHash/Pods-BlurHash.release.xcconfig"; sourceTree = ""; }; 18 | 6AFCE7532F78DBD821C8E78D /* Pods-BlurHash.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BlurHash.debug.xcconfig"; path = "Target Support Files/Pods-BlurHash/Pods-BlurHash.debug.xcconfig"; sourceTree = ""; }; 19 | 850D4FAE28CCEBA100714308 /* ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ios.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 850D4FB128CCEBA100714308 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; 21 | 850D4FB528CCEBA100714308 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 22 | 850D4FB828CCEBA100714308 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 23 | 8F886CA92368476133371299 /* Pods-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.debug.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.debug.xcconfig"; sourceTree = ""; }; 24 | 93C0936F297D4B91B3ABA725 /* Pods_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | E8CBC299A962EF4A7EF2E9D2 /* Pods-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.release.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.release.xcconfig"; sourceTree = ""; }; 26 | /* End PBXFileReference section */ 27 | 28 | /* Begin PBXFrameworksBuildPhase section */ 29 | 850D4FAB28CCEBA100714308 /* Frameworks */ = { 30 | isa = PBXFrameworksBuildPhase; 31 | buildActionMask = 2147483647; 32 | files = ( 33 | D65BB7D28364C04EC850180D /* Pods_ios.framework in Frameworks */, 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | 2D6FE175FA70558B6EE8AE2B /* Frameworks */ = { 41 | isa = PBXGroup; 42 | children = ( 43 | 93C0936F297D4B91B3ABA725 /* Pods_ios.framework */, 44 | ); 45 | name = Frameworks; 46 | sourceTree = ""; 47 | }; 48 | 850D4FA528CCEBA100714308 = { 49 | isa = PBXGroup; 50 | children = ( 51 | 850D4FB028CCEBA100714308 /* ios */, 52 | 850D4FAF28CCEBA100714308 /* Products */, 53 | 9BDC6B73A6A1A714F2D6297C /* Pods */, 54 | 2D6FE175FA70558B6EE8AE2B /* Frameworks */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 850D4FAF28CCEBA100714308 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 850D4FAE28CCEBA100714308 /* ios.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 850D4FB028CCEBA100714308 /* ios */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 850D4FB128CCEBA100714308 /* App.swift */, 70 | 850D4FB528CCEBA100714308 /* Assets.xcassets */, 71 | 850D4FB728CCEBA100714308 /* Preview Content */, 72 | ); 73 | path = ios; 74 | sourceTree = ""; 75 | }; 76 | 850D4FB728CCEBA100714308 /* Preview Content */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 850D4FB828CCEBA100714308 /* Preview Assets.xcassets */, 80 | ); 81 | path = "Preview Content"; 82 | sourceTree = ""; 83 | }; 84 | 9BDC6B73A6A1A714F2D6297C /* Pods */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 6AFCE7532F78DBD821C8E78D /* Pods-BlurHash.debug.xcconfig */, 88 | 208331B5D6E6A7716D6C289D /* Pods-BlurHash.release.xcconfig */, 89 | 8F886CA92368476133371299 /* Pods-ios.debug.xcconfig */, 90 | E8CBC299A962EF4A7EF2E9D2 /* Pods-ios.release.xcconfig */, 91 | ); 92 | path = Pods; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | 850D4FAD28CCEBA100714308 /* ios */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = 850D4FBC28CCEBA100714308 /* Build configuration list for PBXNativeTarget "ios" */; 101 | buildPhases = ( 102 | 7BF1B2F561E1594EC096416E /* [CP] Check Pods Manifest.lock */, 103 | 850D4FAA28CCEBA100714308 /* Sources */, 104 | 850D4FAB28CCEBA100714308 /* Frameworks */, 105 | 850D4FAC28CCEBA100714308 /* Resources */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = ios; 112 | productName = Locale; 113 | productReference = 850D4FAE28CCEBA100714308 /* ios.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | 850D4FA628CCEBA100714308 /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | BuildIndependentTargetsInParallel = 1; 123 | LastSwiftUpdateCheck = 1340; 124 | LastUpgradeCheck = 1340; 125 | TargetAttributes = { 126 | 850D4FAD28CCEBA100714308 = { 127 | CreatedOnToolsVersion = 13.4.1; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = 850D4FA928CCEBA100714308 /* Build configuration list for PBXProject "ios" */; 132 | compatibilityVersion = "Xcode 13.0"; 133 | developmentRegion = en; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = 850D4FA528CCEBA100714308; 140 | productRefGroup = 850D4FAF28CCEBA100714308 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | 850D4FAD28CCEBA100714308 /* ios */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | 850D4FAC28CCEBA100714308 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 850D4FB928CCEBA100714308 /* Preview Assets.xcassets in Resources */, 155 | 850D4FB628CCEBA100714308 /* Assets.xcassets in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXShellScriptBuildPhase section */ 162 | 7BF1B2F561E1594EC096416E /* [CP] Check Pods Manifest.lock */ = { 163 | isa = PBXShellScriptBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | inputFileListPaths = ( 168 | ); 169 | inputPaths = ( 170 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 171 | "${PODS_ROOT}/Manifest.lock", 172 | ); 173 | name = "[CP] Check Pods Manifest.lock"; 174 | outputFileListPaths = ( 175 | ); 176 | outputPaths = ( 177 | "$(DERIVED_FILE_DIR)/Pods-ios-checkManifestLockResult.txt", 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | shellPath = /bin/sh; 181 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 182 | showEnvVarsInLog = 0; 183 | }; 184 | /* End PBXShellScriptBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | 850D4FAA28CCEBA100714308 /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 850D4FB228CCEBA100714308 /* App.swift in Sources */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXSourcesBuildPhase section */ 196 | 197 | /* Begin XCBuildConfiguration section */ 198 | 850D4FBA28CCEBA100714308 /* Debug */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ALWAYS_SEARCH_USER_PATHS = NO; 202 | CLANG_ANALYZER_NONNULL = YES; 203 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 204 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 205 | CLANG_ENABLE_MODULES = YES; 206 | CLANG_ENABLE_OBJC_ARC = YES; 207 | CLANG_ENABLE_OBJC_WEAK = YES; 208 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_COMMA = YES; 211 | CLANG_WARN_CONSTANT_CONVERSION = YES; 212 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 213 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 214 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INFINITE_RECURSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 220 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 221 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 224 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 225 | CLANG_WARN_STRICT_PROTOTYPES = YES; 226 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 227 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 228 | CLANG_WARN_UNREACHABLE_CODE = YES; 229 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 230 | COPY_PHASE_STRIP = NO; 231 | DEBUG_INFORMATION_FORMAT = dwarf; 232 | ENABLE_STRICT_OBJC_MSGSEND = YES; 233 | ENABLE_TESTABILITY = YES; 234 | GCC_C_LANGUAGE_STANDARD = gnu11; 235 | GCC_DYNAMIC_NO_PIC = NO; 236 | GCC_NO_COMMON_BLOCKS = YES; 237 | GCC_OPTIMIZATION_LEVEL = 0; 238 | GCC_PREPROCESSOR_DEFINITIONS = ( 239 | "DEBUG=1", 240 | "$(inherited)", 241 | ); 242 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 243 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 244 | GCC_WARN_UNDECLARED_SELECTOR = YES; 245 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 246 | GCC_WARN_UNUSED_FUNCTION = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 249 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 250 | MTL_FAST_MATH = YES; 251 | ONLY_ACTIVE_ARCH = YES; 252 | SDKROOT = iphoneos; 253 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 254 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 255 | }; 256 | name = Debug; 257 | }; 258 | 850D4FBB28CCEBA100714308 /* Release */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | ALWAYS_SEARCH_USER_PATHS = NO; 262 | CLANG_ANALYZER_NONNULL = YES; 263 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_ENABLE_OBJC_WEAK = YES; 268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_COMMA = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 284 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 285 | CLANG_WARN_STRICT_PROTOTYPES = YES; 286 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 287 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 288 | CLANG_WARN_UNREACHABLE_CODE = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | ENABLE_NS_ASSERTIONS = NO; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu11; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | MTL_FAST_MATH = YES; 305 | SDKROOT = iphoneos; 306 | SWIFT_COMPILATION_MODE = wholemodule; 307 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 308 | VALIDATE_PRODUCT = YES; 309 | }; 310 | name = Release; 311 | }; 312 | 850D4FBD28CCEBA100714308 /* Debug */ = { 313 | isa = XCBuildConfiguration; 314 | baseConfigurationReference = 8F886CA92368476133371299 /* Pods-ios.debug.xcconfig */; 315 | buildSettings = { 316 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 317 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 318 | CODE_SIGN_STYLE = Automatic; 319 | CURRENT_PROJECT_VERSION = 1; 320 | DEVELOPMENT_ASSET_PATHS = "\"ios/Preview Content\""; 321 | DEVELOPMENT_TEAM = 779A4D7K9R; 322 | ENABLE_PREVIEWS = YES; 323 | GENERATE_INFOPLIST_FILE = YES; 324 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 325 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 326 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 327 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 328 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 329 | LD_RUNPATH_SEARCH_PATHS = ( 330 | "$(inherited)", 331 | "@executable_path/Frameworks", 332 | ); 333 | MARKETING_VERSION = 1.0; 334 | PRODUCT_BUNDLE_IDENTIFIER = com.vanniktech.locale.sample.ios; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | SWIFT_EMIT_LOC_STRINGS = YES; 337 | SWIFT_VERSION = 5.0; 338 | TARGETED_DEVICE_FAMILY = "1,2"; 339 | }; 340 | name = Debug; 341 | }; 342 | 850D4FBE28CCEBA100714308 /* Release */ = { 343 | isa = XCBuildConfiguration; 344 | baseConfigurationReference = E8CBC299A962EF4A7EF2E9D2 /* Pods-ios.release.xcconfig */; 345 | buildSettings = { 346 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 347 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 348 | CODE_SIGN_STYLE = Automatic; 349 | CURRENT_PROJECT_VERSION = 1; 350 | DEVELOPMENT_ASSET_PATHS = "\"ios/Preview Content\""; 351 | DEVELOPMENT_TEAM = 779A4D7K9R; 352 | ENABLE_PREVIEWS = YES; 353 | GENERATE_INFOPLIST_FILE = YES; 354 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 355 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 356 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 357 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 358 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 359 | LD_RUNPATH_SEARCH_PATHS = ( 360 | "$(inherited)", 361 | "@executable_path/Frameworks", 362 | ); 363 | MARKETING_VERSION = 1.0; 364 | PRODUCT_BUNDLE_IDENTIFIER = com.vanniktech.locale.sample.ios; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | SWIFT_EMIT_LOC_STRINGS = YES; 367 | SWIFT_VERSION = 5.0; 368 | TARGETED_DEVICE_FAMILY = "1,2"; 369 | }; 370 | name = Release; 371 | }; 372 | /* End XCBuildConfiguration section */ 373 | 374 | /* Begin XCConfigurationList section */ 375 | 850D4FA928CCEBA100714308 /* Build configuration list for PBXProject "ios" */ = { 376 | isa = XCConfigurationList; 377 | buildConfigurations = ( 378 | 850D4FBA28CCEBA100714308 /* Debug */, 379 | 850D4FBB28CCEBA100714308 /* Release */, 380 | ); 381 | defaultConfigurationIsVisible = 0; 382 | defaultConfigurationName = Release; 383 | }; 384 | 850D4FBC28CCEBA100714308 /* Build configuration list for PBXNativeTarget "ios" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 850D4FBD28CCEBA100714308 /* Debug */, 388 | 850D4FBE28CCEBA100714308 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | /* End XCConfigurationList section */ 394 | }; 395 | rootObject = 850D4FA628CCEBA100714308 /* Project object */; 396 | } 397 | -------------------------------------------------------------------------------- /sample-ios/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample-ios/ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample-ios/ios.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sample-ios/ios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample-ios/ios/App.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import multiplatform_locale_all 3 | 4 | @main 5 | struct LocaleApp : App { 6 | var body: some Scene { 7 | WindowGroup { 8 | ContentView() 9 | } 10 | } 11 | } 12 | 13 | struct ContentView: View { 14 | var body: some View { 15 | VStack { 16 | let currentLocaleString = Locales.shared.currentLocaleString() 17 | Text("Current Locale: \(currentLocaleString)") 18 | 19 | if let locale = multiplatform_locale_all.Locale.Companion.shared.fromOrNull(locale: currentLocaleString) { 20 | Text("Google Play Store Locale: \(locale.googlePlayStoreLocale()?.description() ?? "/")") 21 | Text("Apple App Store Locale: \(locale.appleAppStoreLocale()?.description() ?? "/")") 22 | } 23 | 24 | Text("Current Locale Strings: \(Locales.shared.currentLocaleStrings().joined(separator: ", "))") 25 | .padding() 26 | 27 | let countries = Country.values() 28 | 29 | Text("All countries: \((0..