Skip to content

Incorrect plural string returned when device language is set to Latvian.  #133276

Open

Description

@ReinisSprogis

Is there an existing issue for this?

Steps to reproduce

https://github.com/ReinisSprogis/flutter-plural-bug

  1. Set the device language to Latvian.
  2. Tap on Floating action button to increment value.

Actual results

When the device language is set to Latvian, the plural strings are not handled correctly. For example, the string for the count "10" should be "10 punkti," but it incorrectly returns "Nav pievienotu punktu."

In English
"count": "{count, plural, =0{No points added} =1{1 point} other{{count} points}}",
In Latvian would be
"count": "{count, plural, =0{Nav pievienotu punktu} =1{1 punkts} other{{count} punkti}}",

0 returning = Nav pievienotu punktu
1 returning = 1 punkts
2 to 9 returning correct string

However when it hits 10 showing "Nav pievienotu punktu" so all the way up to 20.
21 showing = 1 punkts
22 showing = 22 -29 correct result
30 showing = Nav pievienotu punktu
31 showing = 1 punkts
33 showing = 33 punkti

And so on.

Logs

Logs
No logs

Flutter Doctor output

Doctor output
[√] Flutter (Channel stable, 3.10.6, on Microsoft Windows [Version 10.0.19045.3324], locale en-GB)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Build Tools 2022 17.5.5)
[√] Android Studio (version 2022.2)
[√] VS Code (version 1.81.1)
[√] Connected device (4 available)
[√] Network resources

Activity

  1. ReinisSprogis commented on Aug 24, 2023

    @ReinisSprogis
    Author

    I dug little bit around and seems like bug is in the PluralCase _lv_rule plural_rules.dart .
    I'm not exactly sure what is happening there, but seems like it could be it.

    PluralCase _lv_rule() {
      if (_n % 10 == 0 ||
          _n % 100 >= 11 && _n % 100 <= 19 ||
          _v == 2 && _f % 100 >= 11 && _f % 100 <= 19) {
        return ZERO;
      }
      if (_n % 10 == 1 && _n % 100 != 11 ||
          _v == 2 && _f % 10 == 1 && _f % 100 != 11 ||
          _v != 2 && _f % 10 == 1) {
        return ONE;
      }
      return OTHER;
    }
  2. darshankawar commented on Aug 25, 2023

    @darshankawar
    Member

    Thanks for the report. I was able to replicate this using latest stable and master versions, whererin, from 1 to 10, it shows correctly as punktu, but from 10 onwards, it shows as below:

    Screenshot 2023-08-25 at 3 26 13 PM

    The text above 10 in English means no points added.

    stable, master flutter doctor -v
    [!] Flutter (Channel stable, 3.13.1, on macOS 12.2.1 21D62 darwin-x64, locale
        en-GB)
        • Flutter version 3.13.1 on channel stable at
          /Users/dhs/documents/fluttersdk/flutter
        ! Warning: `flutter` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
          your current Flutter SDK checkout at
          /Users/dhs/documents/fluttersdk/flutter. Consider adding
          /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
        ! Warning: `dart` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
          current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
          Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
          of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision e1e47221e8 (2 days ago), 2023-08-22 21:43:18 -0700
        • Engine revision b20183e040
        • Dart version 3.1.0
        • DevTools version 2.25.0
        • If those were intentional, you can disregard the above warnings; however
          it is recommended to use "git" directly to perform update checks and
          upgrades.
    
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    [!] Flutter (Channel master, 3.14.0-9.0.pre.88, on macOS 12.2.1 21D62
        darwin-x64, locale en-GB)
        • Flutter version 3.14.0-9.0.pre.88 on channel master at
          /Users/dhs/documents/fluttersdk/flutter
        ! Warning: `flutter` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
          your current Flutter SDK checkout at
          /Users/dhs/documents/fluttersdk/flutter. Consider adding
          /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
        ! Warning: `dart` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
          current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
          Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
          of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 9ab8d27a2c (33 minutes ago), 2023-08-25 02:27:12 -0400
        • Engine revision 09e620d268
        • Dart version 3.2.0 (build 3.2.0-97.0.dev)
        • DevTools version 2.26.2
        • If those were intentional, you can disregard the above warnings; however
          it is recommended to use "git" directly to perform update checks and
          upgrades.
    
    [!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
        • Android SDK at /Users/dhs/Library/Android/sdk
        ✗ cmdline-tools component is missing
          Run `path/to/sdkmanager --install "cmdline-tools;latest"`
          See https://developer.android.com/studio/command-line for more details.
        ✗ Android license status unknown.
          Run `flutter doctor --android-licenses` to accept the SDK licenses.
          See https://flutter.dev/docs/get-started/install/macos#android-setup for
          more details.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Build 13C100
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] IntelliJ IDEA Ultimate Edition (version 2021.3.2)
        • IntelliJ at /Applications/IntelliJ IDEA.app
        • Flutter plugin version 65.1.4
        • Dart plugin version 213.7228
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.29.0
    
    [✓] Connected device (3 available)
        • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios
          • iOS 15.3.1 19D52
        • macOS (desktop)           • macos                                    •
          darwin-x64     • macOS 12.2.1 21D62 darwin-x64
        • Chrome (web)              • chrome                                   •
          web-javascript • Google Chrome 109.0.5414.119
    
    [✓] Network resources
        • All expected network resources are available.
    
    ! Doctor found issues in 1 category.
          
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    
    
    
  3. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    a: internationalizationSupporting other languages or locales. (aka i18n)
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    in triagePresently being triaged by the triage team
    on Aug 25, 2023
  4. gampixi commented on Aug 28, 2023

    @gampixi

    Someone who's more experienced with doing Latvian l10n should probably chime in, but as a native speaker I can't imagine why the condition for PluralCase.ZERO needs to be so complicated.

    The noun changes between singular and plural - generally singular if the count ends with 1 (1 punkts, 21 punkts, 201 punkts, 22 punkti) (except in the case of 11 (11 punkti, 211 punkti)), and plural for the rest (0 punkti, 10 punkti, 20 punkti).

    As far as I'm aware, we'd need the zero case only for zero - and only because it would be preferable to provide a custom string for 0.

    The PluralCase.ONE condition (for specifying when singular noun must be used) seems correct at a glance, though.

  5. gampixi commented on Aug 28, 2023

    @gampixi

    And @ReinisSprogis , you should use the {count} placeholder for the =1 case, as for Latvian it appears to refer to any case where the singular noun must be used. As a workaround, you can have the =0 case match the plural, which would be grammatically correct ("0 punkti", "10 punkti" and so on).

  6. added
    P2Important issues not at the top of the work list
    on Aug 30, 2023
  7. volo commented on Dec 5, 2023

    @volo

    That's unfortunately how plural category names for the Latvian language are defined in the Unicode Common Locale Data Repository (CLDR).

    As stated by a Latvian native speaker in a different issue php-gettext/Languages#45 (comment) :

    Lithuanian is a similar language to Latvian. Their similar plural form that is called "zero" in Latvian, is called "other" in Lithuanian, and what is called "other" in Latvian, is called "few" in Lithuanian.

    The Latvian plural form rules are defined by CLDR here, and the Lithuanian are here.

    So there's a discrepancy, and while Latvian follows the plural category names recommendations defined by the CLDR:

    In some sense, the names for the categories are somewhat arbitrary. Yet for consistency across languages, the following guidelines should be used when selecting the plural category names.

    1. If no forms change, then stop (there are no plural rules — everything gets 'other')

    2. 'one': Use the category 'one' for the form used with 1.

    3. 'other': Use the category 'other' for the form used with the most integers.

    4. 'two': Use the category 'two' for the form used with 2, if it is limited to numbers whose integer values end with '2'.

    • If everything else has the same form, stop (everything else gets 'other')
    1. 'zero': Use the category 'zero' for the form used with 0, if it is limited to numbers whose integer values end with '0'.
    • If everything else has the same form, stop (everything else gets 'other')
    1. 'few': Use the category 'few' for the form used with the least remaining number (such as '4')
    • If everything else has the same form, stop (everything else gets 'other')
    1. 'many': Use the category 'many' for the form used with the least remaining number (such as '10')
    • If everything else has the same form, stop (everything else gets 'other')
    • If there needs to be a category for items only have fractional values, use 'many'
    1. If there are more categories needed for the language, describe what those categories need to cover in the bug report.

    but it makes Latvian the only modernly spoken language that uses zero plural category for cardinals other then 0.

    And that would be just okay, but then in 2010s Apple entered the stage with a "brilliant" (not!) idea of using the zero category for cases with a zero quantity values to facilitate negative forms of sentences (i.e.: "You have no emails" instead of "You have 0 emails"). So, instead of introducing a custom quantity category like none or =0 they messed up the CLDR categories and plural rules, completely disregarding the fact that it won't work properly for the Latvian language. (More detailed info about the Apple's implementation can be found here: https://stackoverflow.com/a/61426762). Android didn't follow that slippery slope, but now and then I see Android devs complaining online that zero plural category doesn't work as it's supposed to work 🤷🏻‍♂️

    The solution is to not use the plural strings for something they were never intended for (I'm looking at you Apple), but instead handle negative/absence form in the code (Kotlin+Android example below, but the code is straightforward):

    val text = 
        if (count == 0) getString(R.string.no_emails)             // "You have no emails"
        else getQuantityString(R.plurals.x_emails, count, count)  // "You have %d email", "You have %d emails"

    Or handle it with ICU MessageFormat:

    val msgFmt = MessageFormat(
        "{count, plural, =0{You have no emails} =1{You have one email} other{You have # emails}}",
        ULocale.ENGLISH,
    )
    val text = msgFmt.format(mutableMapOf("count" to 10))

    @ReinisSprogis to sum it up, for Latvian you should add both zero plural category and =0 pattern if you'd like to explicitly handle the case for the absence of points:

    {count, plural, =0{Nav pievienotu punktu} =1{1 punkts} zero{{count} punkti} other{{count} punkti}}
    

    And I guess, you also need the one plural category to handle proper plural forms of quantities like 21, 31, …

    {count, plural, =0{Nav pievienotu punktu} =1{1 punkts} zero{{count} punkti} one{{count} punkts} other{{count} punkti}}
    

    FWIW, native Android implementation with the absence of zero part in the message patterns above fallbacks to other. If that's not the case for Flutter then there's indeed a bug in the ICU message format implementation.

    val msgFmt = MessageFormat(
        "{count, plural, =0{Nav pievienotu punktu} =1{1 punkts} zero{{count} punkti} one{{count} punkts} other{{count} punkti}}",
        ULocale.forLocale(Locale("lv", "LV"))
    )
    val text = msgFmt.format(mutableMapOf("count" to 20))  // "20 punkti"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: internationalizationSupporting other languages or locales. (aka i18n)found in release: 3.13Found to occur in 3.13found in release: 3.14Found to occur in 3.14frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions