#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PluralCategory {
Zero,
One,
Two,
Few,
Many,
Other,
}
impl PluralCategory {
pub fn suffix(&self) -> &'static str {
match self {
Self::Zero => "zero",
Self::One => "one",
Self::Two => "two",
Self::Few => "few",
Self::Many => "many",
Self::Other => "other",
}
}
}
pub fn plural_category(locale: &str, count: i64) -> PluralCategory {
let lang = locale
.split('-')
.next()
.unwrap_or(locale)
.to_ascii_lowercase();
match lang.as_str() {
"zh" | "ja" | "ko" | "vi" | "th" | "id" | "lo" => PluralCategory::Other,
"ar" => arabic_plural(count),
"pl" => polish_plural(count),
"ru" | "uk" | "cs" | "sk" | "hr" | "sr" | "bg" | "sl" => slavic_plural(count),
"he" => hebrew_plural(count),
"en" | "de" | "es" | "it" | "pt" | "nl" | "sv" | "da" | "no" | "nb" | "nn" | "fi"
| "el" | "hi" | "tr" => {
if count == 1 {
PluralCategory::One
} else {
PluralCategory::Other
}
}
_ => {
if count == 1 {
PluralCategory::One
} else {
PluralCategory::Other
}
}
}
}
fn slavic_plural(count: i64) -> PluralCategory {
let abs = count.unsigned_abs();
let mod10 = abs % 10;
let mod100 = abs % 100;
if mod10 == 1 && mod100 != 11 {
PluralCategory::One
} else if (2..=4).contains(&mod10) && !(12..=14).contains(&mod100) {
PluralCategory::Few
} else if mod10 == 0 || (5..=20).contains(&mod100) || (5..=9).contains(&mod10) {
PluralCategory::Many
} else {
PluralCategory::Other
}
}
fn polish_plural(count: i64) -> PluralCategory {
let abs = count.unsigned_abs();
let mod10 = abs % 10;
let mod100 = abs % 100;
if abs == 1 {
PluralCategory::One
} else if (2..=4).contains(&mod10) && !(12..=14).contains(&mod100) {
PluralCategory::Few
} else if mod10 == 0 || mod10 == 1 || (5..=9).contains(&mod10) || (11..=14).contains(&mod100) {
PluralCategory::Many
} else {
PluralCategory::Other
}
}
fn hebrew_plural(count: i64) -> PluralCategory {
let abs = count.unsigned_abs();
let mod10 = abs % 10;
if abs == 1 {
PluralCategory::One
} else if abs == 2 {
PluralCategory::Two
} else if (10..=19).contains(&abs) || (abs != 0 && mod10 == 0) {
PluralCategory::Many
} else {
PluralCategory::Other
}
}
fn arabic_plural(count: i64) -> PluralCategory {
let abs = count.unsigned_abs();
match abs {
0 => PluralCategory::Zero,
1 => PluralCategory::One,
2 => PluralCategory::Two,
3..=10 => PluralCategory::Few,
11..=99 => PluralCategory::Many,
_ => PluralCategory::Other,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_english() {
assert_eq!(plural_category("en", 0), PluralCategory::Other);
assert_eq!(plural_category("en", 1), PluralCategory::One);
assert_eq!(plural_category("en", 2), PluralCategory::Other);
assert_eq!(plural_category("en", 5), PluralCategory::Other);
}
#[test]
fn test_chinese() {
assert_eq!(plural_category("zh-CN", 0), PluralCategory::Other);
assert_eq!(plural_category("zh-CN", 1), PluralCategory::Other);
assert_eq!(plural_category("zh-CN", 5), PluralCategory::Other);
}
#[test]
fn test_japanese() {
assert_eq!(plural_category("ja", 1), PluralCategory::Other);
}
#[test]
fn test_russian() {
assert_eq!(plural_category("ru", 1), PluralCategory::One);
assert_eq!(plural_category("ru", 21), PluralCategory::One);
assert_eq!(plural_category("ru", 11), PluralCategory::Many);
assert_eq!(plural_category("ru", 2), PluralCategory::Few);
assert_eq!(plural_category("ru", 5), PluralCategory::Many);
assert_eq!(plural_category("ru", 0), PluralCategory::Many);
}
#[test]
fn test_arabic() {
assert_eq!(plural_category("ar", 0), PluralCategory::Zero);
assert_eq!(plural_category("ar", 1), PluralCategory::One);
assert_eq!(plural_category("ar", 2), PluralCategory::Two);
assert_eq!(plural_category("ar", 5), PluralCategory::Few);
assert_eq!(plural_category("ar", 50), PluralCategory::Many);
assert_eq!(plural_category("ar", 100), PluralCategory::Other);
}
#[test]
fn test_locale_with_region() {
assert_eq!(plural_category("en-US", 1), PluralCategory::One);
assert_eq!(plural_category("pt-BR", 1), PluralCategory::One);
}
#[test]
fn test_polish() {
assert_eq!(plural_category("pl", 1), PluralCategory::One);
assert_eq!(plural_category("pl", 2), PluralCategory::Few);
assert_eq!(plural_category("pl", 5), PluralCategory::Many);
assert_eq!(plural_category("pl", 22), PluralCategory::Few);
assert_eq!(plural_category("pl", 100), PluralCategory::Many);
}
#[test]
fn test_hebrew() {
assert_eq!(plural_category("he", 1), PluralCategory::One);
assert_eq!(plural_category("he", 2), PluralCategory::Two);
assert_eq!(plural_category("he", 15), PluralCategory::Many);
assert_eq!(plural_category("he", 20), PluralCategory::Many);
assert_eq!(plural_category("he", 3), PluralCategory::Other);
}
}