dvPluralCategory function

DVPluralCategory dvPluralCategory({
  1. required String locale,
  2. required num count,
})

The category count takes in locale.

locale may be a full tag: the region is ignored, because plural rules are a property of the language. An unknown language falls back to the English rule rather than throwing -- a missing rule should degrade to a plausible plural, not take the application down mid-render.

Implementation

DVPluralCategory dvPluralCategory({
  required String locale,
  required num count,
}) {
  final String language = dvLanguageOf(locale);

  // CLDR operates on the absolute value; -1 file is still singular.
  final num n = count.abs();

  // The CLDR operands this needs: i is the integer part, v the number of
  // visible fraction digits. `1.0` has v=0 once it is a double that prints
  // without a fraction, which is why English calls 1.0 singular and 1.5 not.
  final int i = n.floor();
  final int v = _visibleFractionDigits(n);

  switch (language) {
    // One form for every number. Offering "one" here produces a string no
    // speaker would write.
    case 'ja':
    case 'zh':
    case 'ko':
    case 'vi':
    case 'th':
    case 'id':
    case 'ms':
    case 'my':
    case 'lo':
    case 'km':
      return DVPluralCategory.other;

    case 'fr':
    case 'hy':
    case 'ff':
    case 'kab':
      // 0 and 1 are both singular.
      return i == 0 || i == 1 ? DVPluralCategory.one : DVPluralCategory.other;

    case 'pt':
      return i == 0 || i == 1 ? DVPluralCategory.one : DVPluralCategory.other;

    case 'pl':
      if (v == 0 && i == 1) return DVPluralCategory.one;
      if (v == 0 &&
          i % 10 >= 2 &&
          i % 10 <= 4 &&
          !(i % 100 >= 12 && i % 100 <= 14)) {
        return DVPluralCategory.few;
      }
      if (v == 0) return DVPluralCategory.many;
      return DVPluralCategory.other;

    case 'ru':
    case 'uk':
      if (v == 0 && i % 10 == 1 && i % 100 != 11) return DVPluralCategory.one;
      if (v == 0 &&
          i % 10 >= 2 &&
          i % 10 <= 4 &&
          !(i % 100 >= 12 && i % 100 <= 14)) {
        return DVPluralCategory.few;
      }
      if (v == 0) return DVPluralCategory.many;
      return DVPluralCategory.other;

    case 'cs':
    case 'sk':
      if (i == 1 && v == 0) return DVPluralCategory.one;
      if (i >= 2 && i <= 4 && v == 0) return DVPluralCategory.few;
      // A fraction is its own category here, whatever its value.
      if (v != 0) return DVPluralCategory.many;
      return DVPluralCategory.other;

    case 'ar':
      if (n == 0) return DVPluralCategory.zero;
      if (n == 1) return DVPluralCategory.one;
      if (n == 2) return DVPluralCategory.two;
      final int mod100 = i % 100;
      if (mod100 >= 3 && mod100 <= 10) return DVPluralCategory.few;
      if (mod100 >= 11 && mod100 <= 99) return DVPluralCategory.many;
      return DVPluralCategory.other;

    case 'lt':
      if (i % 10 == 1 && !(i % 100 >= 11 && i % 100 <= 19)) {
        return DVPluralCategory.one;
      }
      if (i % 10 >= 2 &&
          i % 10 <= 9 &&
          !(i % 100 >= 11 && i % 100 <= 19)) {
        return DVPluralCategory.few;
      }
      if (v != 0) return DVPluralCategory.many;
      return DVPluralCategory.other;

    case 'ro':
      if (i == 1 && v == 0) return DVPluralCategory.one;
      if (v != 0 || n == 0 || (n != 1 && i % 100 >= 1 && i % 100 <= 19)) {
        return DVPluralCategory.few;
      }
      return DVPluralCategory.other;

    // en, de, es, it, nl, sv, da, nb, fi, et, and the rest of the
    // one/other languages.
    default:
      return i == 1 && v == 0 ? DVPluralCategory.one : DVPluralCategory.other;
  }
}