long1eu / flutter_i18n

This plugin create a binding between your translations from .arb files and your Flutter app.
Apache License 2.0
251 stars 54 forks source link

plurals are generally poorly implemented #45

Open vishna opened 5 years ago

vishna commented 5 years ago
long1eu commented 5 years ago

I see you are a Android dev. Can you show me an example of how you could use the android string resources to obtain this? https://developer.android.com/guide/topics/resources/string-resource#Plurals

vishna commented 5 years ago
<plurals name="message">
    <item quantity="one">%1$s and %2$s other like your photo</item>
    <item quantity="other">%1$s and %2$s others like your photo</item>
 </plurals>

then:

getQuantityString (R.string.message, 1, "John", "one") -> "John and one other like your photo" getQuantityString (R.string.message, 5, "John", "5") -> "John and 5 others like your photo" getQuantityString (R.string.message, 1, "John", "5") -> "John and 5 other like your photo"

you need to map the quantity (the number) to the "zero|one|two|few|many" keyword based on the language you're targeting, here's how you can do it in java using ICU

https://stackoverflow.com/a/14327683/2668982

Unsure if dart/flutter already has already some implementation of PluralRules. I hope it does.

vishna commented 5 years ago

seems there's something: https://github.com/dart-lang/intl/blob/master/lib/src/plural_rules.dart

vishna commented 5 years ago

btw... if strings_en.arb file doesn't specify zero|few etc. keys, those won't get generated for languages that do specify those, so English just having one|other will shadow other languages with more plurals unless you add dummy zero|two|few|many to English

vishna commented 5 years ago

so digging a bit further into this, generating an example output like the one below would be desirable

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:intl/src/plural_rules.dart' as plural_rules;

// ignore_for_file: non_constant_identifier_names
// ignore_for_file: camel_case_types
// ignore_for_file: prefer_single_quotes

//This file is automatically generated. DO NOT EDIT, all your changes would be lost.
class S implements WidgetsLocalizations {
  const S();

  static const GeneratedLocalizationsDelegate delegate =
    GeneratedLocalizationsDelegate();

  static S of(BuildContext context) => Localizations.of<S>(context, S);

  @override
  TextDirection get textDirection => TextDirection.ltr;

  String carrot(int quantity, String count) {
    plural_rules.startRuleEvaluation(quantity);
    final pluralCase = plural_rules.pluralRules["en"]() as plural_rules.PluralCase;
    switch (pluralCase) {
      case plural_rules.PluralCase.ONE: return "${count} carrot";
      case plural_rules.PluralCase.MANY: return "${count} carrots";
      default: return "";
    }
  }
}

class $pl extends S {
  const $pl();

  @override
  TextDirection get textDirection => TextDirection.ltr;

  @override
  String carrot(int quantity, String count) {
    plural_rules.startRuleEvaluation(quantity);
    final pluralCase = plural_rules.pluralRules["pl"]() as plural_rules.PluralCase;
    switch (pluralCase) {
      case plural_rules.PluralCase.ONE: return "${count} marchewka";
      case plural_rules.PluralCase.FEW: return "${count} marchewki";
      case plural_rules.PluralCase.MANY: return "${count} marchewek";
      default: return "";
    }
  }
}
long1eu commented 5 years ago

Can you also share the arboretum file, maybe build an project on GitHub.

On Wed, 27 Feb 2019 at 18:43, Łukasz Wiśniewski notifications@github.com wrote:

so digging a bit further into this, generating an example output like the one below would be desirable

import 'dart:async'; import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';import 'package:intl/src/plural_rules.dart' as plural_rules; // ignore_for_file: non_constant_identifier_names// ignore_for_file: camel_case_types// ignore_for_file: prefer_single_quotes //This file is automatically generated. DO NOT EDIT, all your changes would be lost.class S implements WidgetsLocalizations { const S();

static const GeneratedLocalizationsDelegate delegate = GeneratedLocalizationsDelegate();

static S of(BuildContext context) => Localizations.of(context, S);

@override TextDirection get textDirection => TextDirection.ltr;

String carrot(int quantity, String count) { plural_rules.startRuleEvaluation(quantity); final pluralCase = plural_rules.pluralRules["en"]() as plural_rules.PluralCase; switch (pluralCase) { case plural_rules.PluralCase.ONE: return "${count} carrot"; case plural_rules.PluralCase.MANY: return "${count} carrots"; default: return ""; } } } class $pl extends S { const $pl();

@override TextDirection get textDirection => TextDirection.ltr;

@override String carrot(int quantity, String count) { plural_rules.startRuleEvaluation(quantity); final pluralCase = plural_rules.pluralRules["pl"]() as plural_rules.PluralCase; switch (pluralCase) { case plural_rules.PluralCase.ONE: return "${count} marchewka"; case plural_rules.PluralCase.FEW: return "${count} marchewki"; case plural_rules.PluralCase.MANY: return "${count} marchewek"; default: return ""; } } }

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/long1eu/flutter_i18n/issues/45#issuecomment-467937220, or mute the thread https://github.com/notifications/unsubscribe-auth/AI2nVoMCXU8a9H77b_Aez-vuAuo8Pn2cks5vRrWfgaJpZM4bTHNz .

vishna commented 5 years ago

whoa, a repo project for 2 files that are like less than 10 lines total?

en

{
  "carrotOne": "${count} carrot",
  "carrotMany": "${count} carrots",
}

pl

{
  "carrotOne": "${count} marchewka",
  "carrotFew": "${count} marchewki",
  "carrotMany": "${count} marchewek",
}
wrozwad commented 5 years ago

Also there's a problem with parameter name when you don't specify them in Other suffix, eg.

{
  "carrotOne": "$count carrot",
  "carrotTwo": "$count carrots",
  "carrotOther": "some carrots",
}

Temporary workaround is do use $param name in place of your custom parameter name ($count).

noordawod commented 5 years ago

Good catch @sosite.

ragnor-rs commented 5 years ago

Here's an example of language dependency. A correct definition in Russian would be

  "utilHourZero": "0 часов",
  "utilHourOne": "1 час",
  "utilHourFew": "$count часа",
  "utilHourOther": "$count часов",

The plugin yields

  String utilHour(dynamic count) {
    switch (count.toString()) {
      case "0":
        return "0 часов";
      case "1":
        return "1 час";
      case "few":
        return "$count часа";
      default:
        return "$count часов";
    }
  }

So If I call utilHour(2), I don't get a correct spelling of 2 часа. I get 2 часов because 2 != "few" obviously.

wrozwad commented 5 years ago

I think that the best option will be implement intl Dart package

noordawod commented 5 years ago

@ragnor-rs I believe you need to read how plural rules work, I think you're missing the fundamentals. Also, if "two" has a special meaning in Russian, why not add corresponding "utilHourTwo"?

ragnor-rs commented 5 years ago

@noordawod No, I'm not. The plugin relies on naive switch approach whereas it should be based on pluralization rules which are different for every language. See https://github.com/dart-lang/intl/blob/9d4b477c3314ec203e8f7e65b42ee24d6e5deb40/lib/src/plural_rules.dart#L307, for instance. These rules are used by intl package. (https://api.flutter.dev/flutter/intl/Intl/plural.html).

@sosite is absolutely correct.

noordawod commented 5 years ago

Ok, now I got what you mean @ragnor-rs. Agreed about the current implementation...

Any of you wants to submit a pr to address it and offer a better implementation?