This repository has been archived on 2026-05-27. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
rustypipe/src/timeago.rs
2022-09-01 20:16:00 +02:00

1164 lines
47 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::{borrow::Cow, str::FromStr, vec};
use anyhow::Result;
use fancy_regex::Regex;
use crate::{model::Language, util};
pub const LANGUAGES: [Language; 83] = [
Language::Af,
Language::Am,
Language::Ar,
Language::As,
Language::Az,
Language::Be,
Language::Bg,
Language::Bn,
Language::Bs,
Language::Ca,
Language::Cs,
Language::Da,
Language::De,
Language::El,
Language::En,
Language::EnGb,
Language::EnIn,
Language::Es,
Language::Es419,
Language::EsUs,
Language::Et,
Language::Eu,
Language::Fa,
Language::Fi,
Language::Fil,
Language::Fr,
Language::FrCa,
Language::Gl,
Language::Gu,
Language::Hi,
Language::Hr,
Language::Hu,
Language::Hy,
Language::Id,
Language::Is,
Language::It,
Language::Iw,
Language::Ja,
Language::Ka,
Language::Kk,
Language::Km,
Language::Kn,
Language::Ko,
Language::Ky,
Language::Lo,
Language::Lt,
Language::Lv,
Language::Mk,
Language::Ml,
Language::Mn,
Language::Mr,
Language::Ms,
Language::My,
Language::Ne,
Language::Nl,
Language::No,
Language::Or,
Language::Pa,
Language::Pl,
Language::Pt,
Language::PtPt,
Language::Ro,
Language::Ru,
Language::Si,
Language::Sk,
Language::Sl,
Language::Sq,
Language::Sr,
Language::SrLatn,
Language::Sv,
Language::Sw,
Language::Ta,
Language::Te,
Language::Th,
Language::Tr,
Language::Uk,
Language::Ur,
Language::Uz,
Language::Vi,
Language::ZhCn,
Language::ZhHk,
Language::ZhTw,
Language::Zu,
];
#[derive(Debug)]
pub struct TimeagoPattern<'a> {
word_separator: &'a str,
seconds: Vec<&'a str>,
minutes: Vec<&'a str>,
hours: Vec<&'a str>,
days: Vec<&'a str>,
weeks: Vec<&'a str>,
months: Vec<&'a str>,
years: Vec<&'a str>,
special_cases: Vec<(&'a str, u64)>,
}
impl From<Language> for TimeagoPattern<'_> {
fn from(language: Language) -> Self {
match language {
Language::Af => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekonde", "sekondes"],
minutes: vec!["minute", "minuut"],
hours: vec!["ure", "uur"],
days: vec!["dae", "dag"],
weeks: vec!["week", "weke"],
months: vec!["maand", "maande"],
years: vec!["jaar"],
special_cases: vec![],
},
Language::Am => TimeagoPattern {
word_separator: " ",
seconds: vec!["ሰኮንዶች", "ሴኮንድ"],
minutes: vec!["ደቂቃ", "ደቂቃዎች"],
hours: vec!["ሰዓት", "ሰዓቶች"],
// INFO: add days[0]
days: vec!["ቀናት", "ቀን", "ቀኖች"],
weeks: vec!["ሳምንታት", "ሳምንት"],
months: vec!["ወራት", "ወር"],
years: vec!["ዓመታት", "ዓመት"],
special_cases: vec![],
},
Language::Ar => TimeagoPattern {
word_separator: " ",
seconds: vec!["ثانية", "ثانيتين", "ثوانٍ"],
minutes: vec!["دقائق", "دقيقة", "دقيقتين"],
hours: vec!["ساعات", "ساعة", "ساعتين"],
days: vec!["أيام", "يوم", "يومين", "يومًا"],
weeks: vec!["أسابيع", "أسبوع", "أسبوعين"],
months: vec!["أشهر", "شهر", "شهرين", "شهرًا"],
years: vec!["سنة", "سنتين", "سنوات"],
special_cases: vec![
("ساعتين", 2 * 3600),
("يومين", 2 * 3600 * 24),
("أسبوعين", 2 * 3600 * 24 * 7),
("شهرين", 2 * 3600 * 24 * 30),
("سنتين", 2 * 3600 * 24 * 365),
],
},
// INFO: newly added
Language::As => TimeagoPattern {
word_separator: " ",
seconds: vec!["ছেকেণ্ড"],
minutes: vec!["মিনিট"],
hours: vec!["ঘণ্টা"],
days: vec!["দিন"],
weeks: vec!["সপ্তাহ"],
months: vec!["মাহ"],
years: vec!["বছৰৰ"],
special_cases: vec![],
},
Language::Az => TimeagoPattern {
word_separator: " ",
seconds: vec!["saniyə"],
minutes: vec!["dəqiqə"],
hours: vec!["saat"],
days: vec!["gün"],
weeks: vec!["həftə"],
months: vec!["ay"],
years: vec!["il"],
special_cases: vec![],
},
Language::Be => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунд", "секунду", "секунды"],
minutes: vec!["хвілін", "хвіліну", "хвіліны"],
hours: vec!["гадзін", "гадзіну", "гадзіны"],
days: vec!["дзень", "дзён", "дня", "дні"],
weeks: vec!["тыдзень", "тыдня", "тыдні"],
months: vec!["месяц", "месяца", "месяцы", "месяцаў"],
years: vec!["год", "года", "гады", "гадоў"],
special_cases: vec![],
},
Language::Bg => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунда", "секунди"],
minutes: vec!["минута", "минути"],
hours: vec!["час", "часа"],
days: vec!["ден", "дни"],
weeks: vec!["седмица", "седмици"],
months: vec!["месец", "месеца"],
years: vec!["година", "години"],
special_cases: vec![],
},
Language::Bn => TimeagoPattern {
word_separator: " ",
// INFO: hours fixed
seconds: vec!["সেকেন্ড"],
minutes: vec!["মিনিট"],
hours: vec!["ঘন্টা"],
days: vec!["দিন"],
weeks: vec!["সপ্তাহ"],
months: vec!["মাস"],
years: vec!["বছর"],
special_cases: vec![],
},
Language::Bs => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekundi", "sekunde", "sekundu"],
minutes: vec!["minuta", "minute", "minutu"],
hours: vec!["h", "sat", "sata", "sati"],
days: vec!["dan", "dana"],
// INFO: fix sedmice (week plural)
weeks: vec!["sedm.", "sedmice", "sedmicu"],
months: vec!["mj.", "mjesec", "mjeseca", "mjeseci"],
years: vec!["godina", "godine", "godinu"],
special_cases: vec![],
},
Language::Ca => TimeagoPattern {
word_separator: " ",
seconds: vec!["segon", "segons"],
minutes: vec!["minut", "minuts"],
hours: vec!["hora", "hores"],
days: vec!["dia", "dies"],
weeks: vec!["setmana", "setmanes"],
months: vec!["mes", "mesos"],
years: vec!["any", "anys"],
special_cases: vec![],
},
Language::Cs => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekundami", "sekundou"],
minutes: vec!["minutami", "minutou"],
hours: vec!["hodinami", "hodinou"],
days: vec!["dny", "včera"],
weeks: vec!["týdnem", "týdny"],
months: vec!["měsícem", "měsíci"],
years: vec!["rokem", "roky", "lety"],
special_cases: vec![],
},
Language::Da => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekund", "sekunder"],
minutes: vec!["minut", "minutter"],
hours: vec!["time", "timer"],
days: vec!["dag", "dage"],
weeks: vec!["uge", "uger"],
months: vec!["måned", "måneder"],
years: vec!["år"],
special_cases: vec![],
},
Language::De => TimeagoPattern {
word_separator: " ",
seconds: vec!["Sekunde", "Sekunden"],
minutes: vec!["Minute", "Minuten"],
hours: vec!["Stunde", "Stunden"],
days: vec!["Tag", "Tagen"],
weeks: vec!["Woche", "Wochen"],
months: vec!["Monat", "Monaten"],
years: vec!["Jahr", "Jahren"],
special_cases: vec![],
},
Language::El => TimeagoPattern {
word_separator: " ",
seconds: vec!["δευτερόλεπτα", "δευτερόλεπτο"],
minutes: vec!["λεπτά", "λεπτό"],
hours: vec!["ώρα", "ώρες"],
days: vec!["ημέρα", "ημέρες"],
weeks: vec!["εβδομάδα", "εβδομάδες"],
months: vec!["μήνα", "μήνες"],
// INFO: fixed years
years: vec!["έτος", "έτη"],
special_cases: vec![],
},
Language::En | Language::EnGb | Language::EnIn => TimeagoPattern {
word_separator: " ",
seconds: vec!["second", "seconds"],
minutes: vec!["minute", "minutes"],
hours: vec!["hour", "hours"],
days: vec!["day", "days"],
weeks: vec!["week", "weeks"],
months: vec!["month", "months"],
years: vec!["year", "years"],
special_cases: vec![],
},
Language::Es | Language::EsUs | Language::Es419 => TimeagoPattern {
word_separator: " ",
seconds: vec!["segundo", "segundos"],
minutes: vec!["minuto", "minutos"],
hours: vec!["hora", "horas"],
days: vec!["día", "días"],
weeks: vec!["semana", "semanas"],
months: vec!["mes", "meses"],
years: vec!["año", "años"],
special_cases: vec![],
},
Language::Et => TimeagoPattern {
word_separator: " ",
// INFO: corrected secs/min/weeks
seconds: vec!["sekund", "sekundi", "sekundit"],
minutes: vec!["minut", "minuti", "minutit"],
hours: vec!["tunni"],
days: vec!["päev", "päeva"],
weeks: vec!["nädal", "nädala", "nädalat"],
months: vec!["kuu", "kuud"],
years: vec!["aasta", "aastat"],
special_cases: vec![],
},
Language::Eu => TimeagoPattern {
word_separator: " ",
seconds: vec!["segundo"],
minutes: vec!["minutu"],
hours: vec!["ordu", "ordubete"],
days: vec!["egun"],
weeks: vec!["aste", "astebete"],
months: vec!["hilabete"],
years: vec!["urte", "urtebete"],
special_cases: vec![],
},
Language::Fa => TimeagoPattern {
word_separator: " ",
seconds: vec!["ثانیه"],
minutes: vec!["دقیقه"],
hours: vec!["ساعت"],
days: vec!["روز"],
weeks: vec!["هفته"],
months: vec!["ماه"],
years: vec!["سال"],
special_cases: vec![],
},
Language::Fi => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekunti", "sekuntia"],
minutes: vec!["minuutti", "minuuttia"],
hours: vec!["tunti", "tuntia"],
days: vec!["päivä", "päivää"],
weeks: vec!["viikko", "viikkoa"],
months: vec!["kuukausi", "kuukautta"],
years: vec!["vuosi", "vuotta"],
special_cases: vec![],
},
Language::Fil => TimeagoPattern {
word_separator: " ",
seconds: vec!["segundo"],
minutes: vec!["minuto"],
hours: vec!["oras"],
days: vec!["araw"],
weeks: vec!["linggo"],
months: vec!["buwan"],
years: vec!["taon"],
special_cases: vec![],
},
Language::Fr | Language::FrCa => TimeagoPattern {
word_separator: " ",
seconds: vec!["seconde", "secondes"],
minutes: vec!["minute", "minutes"],
hours: vec!["heure", "heures"],
days: vec!["jour", "jours"],
weeks: vec!["semaine", "semaines"],
months: vec!["mois"],
years: vec!["an", "ans"],
special_cases: vec![],
},
Language::Gl => TimeagoPattern {
word_separator: " ",
seconds: vec!["segundo", "segundos"],
minutes: vec!["minuto", "minutos"],
hours: vec!["hora", "horas"],
days: vec!["día", "días"],
weeks: vec!["semana", "semanas"],
months: vec!["mes", "meses"],
years: vec!["ano", "anos"],
special_cases: vec![],
},
Language::Gu => TimeagoPattern {
word_separator: " ",
seconds: vec!["સેકંડ"],
minutes: vec!["મિનિટ"],
hours: vec!["કલાક"],
days: vec!["દિવસ"],
weeks: vec!["અઠવાડિયા"],
months: vec!["મહિના"],
years: vec!["વર્ષ"],
special_cases: vec![],
},
Language::Hi => TimeagoPattern {
word_separator: " ",
seconds: vec!["सेकंड"],
minutes: vec!["मिनट"],
hours: vec!["घंटा", "घंटे"],
days: vec!["दिन"],
weeks: vec!["सप्ताह", "हफ़्ते"],
// INFO: fix months
months: vec!["माह", "महीना", "महीने"],
years: vec!["वर्ष"],
special_cases: vec![],
},
Language::Hr => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekunde", "sekundi", "sekundu"],
minutes: vec!["minuta", "minute", "minutu"],
hours: vec!["sat", "sata", "sati"],
days: vec!["dan", "dana"],
weeks: vec!["tjedan", "tjedna"],
months: vec!["mjesec", "mjeseca", "mjeseci"],
years: vec!["godina", "godine", "godinu"],
special_cases: vec![],
},
Language::Hu => TimeagoPattern {
word_separator: " ",
// INFO: updated
seconds: vec!["másodperce", "másodperccel"],
minutes: vec!["perce", "perccel"],
hours: vec!["órája", "órával"],
days: vec!["napja", "nappal"],
weeks: vec!["hete", "héttel"],
months: vec!["hónapja", "hónappal"],
years: vec!["éve", "évvel"],
special_cases: vec![],
},
Language::Hy => TimeagoPattern {
word_separator: " ",
seconds: vec!["վայրկյան"],
minutes: vec!["րոպե"],
hours: vec!["ժամ"],
days: vec!["օր"],
weeks: vec!["շաբաթ"],
months: vec!["ամիս"],
years: vec!["տարի"],
special_cases: vec![],
},
Language::Id => TimeagoPattern {
word_separator: " ",
seconds: vec!["detik"],
minutes: vec!["menit"],
hours: vec!["jam"],
days: vec!["hari"],
weeks: vec!["minggu"],
months: vec!["bulan"],
years: vec!["tahun"],
special_cases: vec![],
},
Language::Is => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekúndu", "sekúndum"],
minutes: vec!["mínútu", "mínútum"],
hours: vec!["klukkustund", "klukkustundum"],
days: vec!["degi", "dögum"],
weeks: vec!["viku", "vikum"],
months: vec!["mánuði", "mánuðum"],
years: vec!["ári", "árum"],
special_cases: vec![],
},
Language::It => TimeagoPattern {
word_separator: " ",
seconds: vec!["secondi", "secondo"],
minutes: vec!["minuti", "minuto"],
hours: vec!["ora", "ore"],
days: vec!["giorni", "giorno"],
weeks: vec!["settimana", "settimane"],
months: vec!["mese", "mesi"],
years: vec!["anni", "anno"],
special_cases: vec![],
},
Language::Iw => TimeagoPattern {
word_separator: " ",
seconds: vec!["שניות", "שנייה"],
minutes: vec!["דקה", "דקות"],
hours: vec!["שעה", "שעות"],
days: vec!["יום", "ימים"],
weeks: vec!["שבוע", "שבועות"],
months: vec!["חודש", "חודשים"],
years: vec!["שנה", "שנים"],
special_cases: vec![
("שעתיים", 2 * 3600),
("יומיים", 2 * 3600 * 24),
("שבועיים", 2 * 3600 * 24 * 7),
("חודשיים", 2 * 3600 * 24 * 30),
("שנתיים", 2 * 3600 * 24 * 365),
],
},
Language::Ja => TimeagoPattern {
word_separator: "",
seconds: vec![""],
minutes: vec![""],
hours: vec!["時間"],
days: vec![""],
weeks: vec!["週間"],
months: vec!["か月"],
years: vec![""],
special_cases: vec![],
},
Language::Ka => TimeagoPattern {
word_separator: " ",
seconds: vec!["წამის"],
minutes: vec!["წუთის"],
hours: vec!["საათის"],
days: vec!["დღის"],
weeks: vec!["კვირის"],
months: vec!["თვის"],
years: vec!["წლის"],
special_cases: vec![],
},
Language::Kk => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунд"],
minutes: vec!["минут"],
hours: vec!["сағат"],
days: vec!["күн"],
weeks: vec!["апта"],
months: vec!["ай"],
years: vec!["жыл"],
special_cases: vec![],
},
Language::Km => TimeagoPattern {
word_separator: "",
seconds: vec!["វិនាទីមុន"],
minutes: vec!["នាទីមុន"],
hours: vec!["ម៉ោងមុន"],
days: vec!["ថ្ងៃមុន"],
weeks: vec!["សប្ដាហ៍មុន"],
months: vec!["ខែមុន"],
years: vec!["ឆ្នាំមុន"],
special_cases: vec![],
},
Language::Kn => TimeagoPattern {
word_separator: " ",
// INFO: fix hours
seconds: vec!["ಸೆಕೆಂಡುಗಳ", "ಸೆಕೆಂಡ್"],
minutes: vec!["ನಿಮಿಷಗಳ", "ನಿಮಿಷದ"],
hours: vec!["ಗಂಟೆ", "ಗಂಟೆಗಳ", "ಗಂಟೆಯ"],
days: vec!["ದಿನಗಳ", "ದಿನದ"],
weeks: vec!["ವಾರಗಳ", "ವಾರದ"],
months: vec!["ತಿಂಗಳ", "ತಿಂಗಳುಗಳ"],
years: vec!["ವರ್ಷಗಳ", "ವರ್ಷದ"],
special_cases: vec![],
},
Language::Ko => TimeagoPattern {
word_separator: "",
seconds: vec![""],
minutes: vec![""],
hours: vec!["시간"],
days: vec![""],
weeks: vec![""],
months: vec!["개월"],
years: vec![""],
special_cases: vec![],
},
Language::Ky => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунд"],
minutes: vec!["мүнөт"],
hours: vec!["саат"],
days: vec!["күн"],
weeks: vec!["апта"],
months: vec!["ай"],
years: vec!["жыл"],
special_cases: vec![],
},
Language::Lo => TimeagoPattern {
word_separator: "",
seconds: vec!["ວິນາທີກ່ອນ"],
minutes: vec!["ນາທີກ່ອນ"],
hours: vec!["ຊົ່ວໂມງກ່ອນ"],
days: vec!["ມື້ກ່ອນ"],
weeks: vec!["ອາທິດກ່ອນ"],
months: vec!["ເດືອນກ່ອນ"],
years: vec!["ປີກ່ອນ"],
special_cases: vec![],
},
Language::Lt => TimeagoPattern {
// INFO: fix weeks
word_separator: " ",
seconds: vec!["sekundes", "sekundę", "sekundžių"],
minutes: vec!["minutes", "minutę", "minučių"],
hours: vec!["valandas", "valandą", "valandų"],
days: vec!["dienas", "dieną", "dienų"],
weeks: vec!["savaites", "savaitę", "savaičių"],
months: vec!["mėnesius", "mėnesių", "mėnesį"],
years: vec!["metus", "metų"],
special_cases: vec![],
},
Language::Lv => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekundes", "sekundēm"],
minutes: vec!["minūtes", "minūtēm", "minūtes"],
hours: vec!["stundas", "stundām"],
days: vec!["dienas", "dienām"],
weeks: vec!["nedēļas", "nedēļām"],
months: vec!["mēneša", "mēnešiem"],
years: vec!["gada", "gadiem"],
special_cases: vec![],
},
Language::Mk => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунда", "секунди"],
minutes: vec!["минута", "минути"],
hours: vec!["час", "часа"],
days: vec!["ден", "дена"],
// INFO: fix weeks
weeks: vec!["недела", "недели", "седмици"],
months: vec!["месец", "месеци"],
years: vec!["година", "години"],
special_cases: vec![],
},
Language::Ml => TimeagoPattern {
word_separator: "",
seconds: vec!["സെക്കന്റ്", "സെക്കൻഡ്"],
minutes: vec!["മിനിറ്റ്"],
hours: vec!["മണിക്കൂർ"],
days: vec!["ദിവസം"],
// weeks: vec!["ആഴ്ച", "ആഴ്\u{200c}ച"],
weeks: vec!["ആഴ്ച"],
months: vec!["മാസം"],
years: vec!["വർഷം"],
special_cases: vec![],
},
Language::Mn => TimeagoPattern {
word_separator: " ",
seconds: vec!["секундын"],
minutes: vec!["минутын"],
hours: vec!["цагийн"],
days: vec!["өдрийн"],
weeks: vec!["долоо", "хоногийн"],
months: vec!["сарын"],
years: vec!["жилийн"],
special_cases: vec![],
},
Language::Mr => TimeagoPattern {
word_separator: "",
seconds: vec!["सेकंदांपूर्वी", "सेकंदापूर्वी"],
minutes: vec!["मिनिटांपूर्वी", "मिनिटापूर्वी"],
hours: vec!["तासांपूर्वी", "तासापूर्वी"],
days: vec!["दिवसांपूर्वी", "दिवसापूर्वी"],
weeks: vec!["आठवड्यांपूर्वी", "आठवड्यापूर्वी"],
months: vec!["महिन्यांपूर्वी", "महिन्यापूर्वी"],
years: vec!["वर्षांपूर्वी", "वर्षापूर्वी"],
special_cases: vec![],
},
Language::Ms => TimeagoPattern {
word_separator: " ",
seconds: vec!["saat"],
minutes: vec!["minit"],
hours: vec!["jam"],
days: vec!["hari"],
weeks: vec!["minggu"],
months: vec!["bulan"],
years: vec!["tahun"],
special_cases: vec![],
},
Language::My => TimeagoPattern {
word_separator: " ",
seconds: vec!["စက္ကန့်"],
minutes: vec!["မိနစ်"],
hours: vec!["နာရီ"],
days: vec!["ရက်"],
weeks: vec!["ပတ်"],
months: vec![""],
years: vec!["နှစ်"],
special_cases: vec![],
},
Language::Ne => TimeagoPattern {
word_separator: " ",
// INFO: fix hours
seconds: vec!["सेकेन्ड"],
minutes: vec!["मिनेट"],
hours: vec!["घण्टा"],
days: vec!["दिन"],
weeks: vec!["हप्ता"],
months: vec!["महिना"],
years: vec!["वर्ष"],
special_cases: vec![],
},
Language::Nl => TimeagoPattern {
word_separator: " ",
seconds: vec!["seconde", "seconden"],
minutes: vec!["minuten", "minuut"],
hours: vec!["uur"],
days: vec!["dag", "dagen"],
weeks: vec!["week", "weken"],
months: vec!["maand", "maanden"],
years: vec!["jaar"],
special_cases: vec![],
},
Language::No => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekund", "sekunder"],
minutes: vec!["minutt", "minutter"],
hours: vec!["time", "timer"],
days: vec!["dag", "dager", "døgn"],
weeks: vec!["uke", "uker"],
// INFO: fixed months, days
months: vec!["måned", "måneder"],
years: vec!["år"],
special_cases: vec![],
},
// INFO: newly added
Language::Or => TimeagoPattern {
word_separator: " ",
seconds: vec!["ସେକେଣ୍ଡ"],
minutes: vec!["ମିନିଟ୍"],
hours: vec!["ଘଣ୍ଟା"],
days: vec!["ଦିନ"],
weeks: vec!["ସପ୍ତାହ"],
months: vec!["ମାସ"],
years: vec!["ବର୍ଷ"],
special_cases: vec![],
},
Language::Pa => TimeagoPattern {
word_separator: " ",
seconds: vec!["ਸਕਿੰਟ"],
minutes: vec!["ਮਿੰਟ"],
hours: vec!["ਘੰਟਾ", "ਘੰਟੇ"],
days: vec!["ਦਿਨ"],
weeks: vec!["ਹਫ਼ਤਾ", "ਹਫ਼ਤੇ"],
months: vec!["ਮਹੀਨਾ", "ਮਹੀਨੇ"],
years: vec!["ਸਾਲ"],
special_cases: vec![],
},
Language::Pl => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekund", "sekundy", "sekundę"],
minutes: vec!["minut", "minuty", "minutę"],
hours: vec!["godzin", "godziny", "godzinę"],
days: vec!["dni", "dzień"],
weeks: vec!["tydzień", "tygodnie"],
months: vec!["miesiąc", "miesiące", "miesięcy"],
years: vec!["lat", "lata", "rok"],
special_cases: vec![],
},
Language::Pt | Language::PtPt => TimeagoPattern {
word_separator: " ",
seconds: vec!["segundo", "segundos"],
minutes: vec!["minuto", "minutos"],
hours: vec!["hora", "horas"],
days: vec!["dia", "dias"],
weeks: vec!["semana", "semanas"],
months: vec!["meses", "mês"],
years: vec!["ano", "anos"],
special_cases: vec![],
},
Language::Ro => TimeagoPattern {
word_separator: " ",
seconds: vec!["secunde", "secundă"],
minutes: vec!["minut", "minute"],
hours: vec!["ore", "oră"],
days: vec!["zi", "zile"],
weeks: vec!["săptămâni", "săptămână"],
months: vec!["luni", "lună"],
years: vec!["an", "ani"],
special_cases: vec![],
},
Language::Ru => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунд", "секунду", "секунды", "только что"],
minutes: vec!["минут", "минуту", "минуты"],
hours: vec!["час", "часа", "часов"],
days: vec!["день", "дней", "дня"],
weeks: vec!["Неделю", "недели"],
months: vec!["месяц", "месяца", "месяцев"],
years: vec!["Год", "года", "лет"],
special_cases: vec![],
},
Language::Si => TimeagoPattern {
word_separator: " ",
seconds: vec!["තත්පර"],
minutes: vec!["මිනිත්තු"],
hours: vec!["පැය"],
days: vec!["දින"],
weeks: vec!["සති"],
months: vec!["මාස"],
years: vec!["වසර"],
special_cases: vec![],
},
Language::Sk => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekundami", "sekundou"],
minutes: vec!["minútami", "minútou"],
hours: vec!["hodinami", "hodinou"],
days: vec!["dňami", "dňom"],
weeks: vec!["týždňami", "týždňom"],
months: vec!["mesiacmi", "mesiacom"],
years: vec!["rokmi", "rokom"],
special_cases: vec![],
},
Language::Sl => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekundama", "sekundami", "sekundo"],
minutes: vec!["minutama", "minutami", "minuto"],
hours: vec!["urama", "urami", "uro"],
days: vec!["dnem", "dnevi", "dnevoma"],
weeks: vec!["tedni", "tednom", "tednoma"],
months: vec!["mesecem", "mesecema", "meseci"],
years: vec!["leti", "letom", "letoma"],
special_cases: vec![],
},
Language::Sq => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekonda", "sekondë"],
minutes: vec!["minuta", "minutë"],
hours: vec!["orë"],
days: vec!["ditë"],
weeks: vec!["javë"],
months: vec!["muaj"],
years: vec!["vit", "vjet"],
special_cases: vec![],
},
Language::Sr => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунде", "секунди"],
minutes: vec!["минута"],
hours: vec!["сат", "сата", "сати"],
// INFO: simplified days
days: vec!["дан", "дана"],
weeks: vec!["недеље", "недељу"],
months: vec!["месец", "месеца", "месеци"],
years: vec!["година", "године", "годину"],
special_cases: vec![],
},
Language::SrLatn => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekunde", "sekundi"],
minutes: vec!["minuta"],
hours: vec!["sat", "sati", "sata"],
// INFO: simplified days
days: vec!["dan", "dana"],
weeks: vec!["nedelja", "nedelje", "nedelju"],
months: vec!["mesec", "meseci", "meseca"],
years: vec!["godine", "godina", "godinu"],
special_cases: vec![],
},
Language::Sv => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekund", "sekunder"],
minutes: vec!["minut", "minuter"],
hours: vec!["timmar", "timme"],
days: vec!["dag", "dagar"],
weeks: vec!["vecka", "veckor"],
months: vec!["månad", "månader"],
years: vec!["år"],
special_cases: vec![],
},
Language::Sw => TimeagoPattern {
word_separator: " ",
seconds: vec!["sekunde"],
minutes: vec!["dakika"],
hours: vec!["saa"],
days: vec!["siku"],
weeks: vec!["wiki"],
months: vec!["Mwezi", "miezi"],
years: vec!["Miaka", "Mwaka"],
special_cases: vec![],
},
Language::Ta => TimeagoPattern {
word_separator: " ",
// INFO: fixed minutes hours months, TODO: 1 second
// 2 விநாடிகளுக்கு முன்
seconds: vec!["வினாடி", "வினாடிகளுக்கு"],
// 1 நிமிடத்திற்கு முன் 2 நிமிடங்களுக்கு முன்
minutes: vec!["நிமிடங்களுக்கு", "நிமிடத்திற்கு", "நிமிடங்கள்", "நிமிடம்"],
hours: vec!["மணிநேரம்"],
days: vec!["நாட்களுக்கு", "நாளுக்கு"],
weeks: vec!["வாரங்களுக்கு", "வாரம்"],
months: vec!["மாதத்துக்கு", "மாதங்களுக்கு"],
years: vec!["ஆண்டிற்கு", "ஆண்டுகளுக்கு"],
special_cases: vec![],
},
Language::Te => TimeagoPattern {
word_separator: " ",
seconds: vec!["సెకను", "సెకన్ల"],
minutes: vec!["నిమిషం", "నిమిషాల"],
hours: vec!["గంట", "గంటల"],
days: vec!["రోజు", "రోజుల"],
weeks: vec!["వారం", "వారాల"],
months: vec!["నెల", "నెలల"],
years: vec!["సంవత్సరం", "సంవత్సరాల"],
special_cases: vec![],
},
Language::Th => TimeagoPattern {
word_separator: " ",
seconds: vec!["วินาทีที่ผ่านมา"],
minutes: vec!["นาทีที่ผ่านมา"],
hours: vec!["ชั่วโมงที่ผ่านมา"],
days: vec!["วันที่ผ่านมา"],
weeks: vec!["สัปดาห์ที่ผ่านมา"],
months: vec!["เดือนที่ผ่านมา"],
// INFO: fixed years
years: vec!["ปีที่แล้ว"],
special_cases: vec![],
},
Language::Tr => TimeagoPattern {
word_separator: " ",
seconds: vec!["saniye"],
minutes: vec!["dakika"],
hours: vec!["saat"],
days: vec!["gün"],
weeks: vec!["hafta"],
months: vec!["ay"],
years: vec!["yıl"],
special_cases: vec![],
},
Language::Uk => TimeagoPattern {
word_separator: " ",
seconds: vec!["секунд", "секунди", "секунду"],
minutes: vec!["хвилин", "хвилини", "хвилину"],
hours: vec!["годин", "години", "годину"],
days: vec!["день", "дні", "днів"],
weeks: vec!["тиждень", "тижні"],
months: vec!["місяць", "місяці", "місяців"],
years: vec!["роки", "років", "рік"],
special_cases: vec![],
},
Language::Ur => TimeagoPattern {
word_separator: " ",
// INFO: fix days, months
seconds: vec!["سیکنڈ", "سیکنڈز"],
minutes: vec!["منٹ", "منٹس"],
hours: vec!["گھنٹہ", "گھنٹے"],
days: vec!["دن", "دنوں"],
weeks: vec!["ہفتہ", "ہفتے"],
months: vec!["مہینہ", "مہینے"],
years: vec!["سال"],
special_cases: vec![],
},
Language::Uz => TimeagoPattern {
word_separator: " ",
seconds: vec!["soniya"],
minutes: vec!["daqiqa"],
hours: vec!["soat"],
days: vec!["kun"],
weeks: vec!["hafta"],
months: vec!["oy"],
years: vec!["yil"],
special_cases: vec![],
},
Language::Vi => TimeagoPattern {
word_separator: " ",
seconds: vec!["giây"],
minutes: vec!["phút"],
hours: vec!["giờ", "tiếng"],
days: vec!["ngày"],
weeks: vec!["tuần"],
months: vec!["tháng"],
years: vec!["năm"],
special_cases: vec![],
},
Language::ZhCn => TimeagoPattern {
// INFO: remove 'ago' character
word_separator: "",
seconds: vec![""],
minutes: vec!["分钟"],
hours: vec!["小时"],
days: vec![""],
weeks: vec![""],
months: vec!["个月"],
years: vec![""],
special_cases: vec![],
},
Language::ZhHk => TimeagoPattern {
// INFO: fix days, remove 'ago' character
word_separator: "",
seconds: vec![""],
minutes: vec!["分鐘"],
hours: vec!["小時"],
days: vec![""],
weeks: vec!["星期"],
months: vec!["個月"],
years: vec![""],
special_cases: vec![],
},
Language::ZhTw => TimeagoPattern {
// INFO: fix days, remove 'ago' character
word_separator: "",
seconds: vec![""],
minutes: vec!["分鐘"],
hours: vec!["小時"],
days: vec![""],
weeks: vec![""],
months: vec!["個月"],
years: vec![""],
special_cases: vec![],
},
Language::Zu => TimeagoPattern {
word_separator: " ",
// INFO: fix hours, days
seconds: vec!["amasekhondi", "isekhondi"],
minutes: vec!["amaminithi", "iminithi"],
hours: vec!["emahoreni", "amahora", "ihora"],
days: vec!["ezinsukwini", "izinsuku", "usuku"],
weeks: vec!["amaviki", "iviki"],
months: vec!["inyanga", "izinyanga"],
years: vec!["iminyaka", "unyaka"],
special_cases: vec![],
},
}
}
}
impl TryFrom<&str> for TimeagoPattern<'_> {
type Error = serde_json::Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Language::from_str(s).map(|l| Self::from(l))
}
}
impl TimeagoPattern<'_> {
pub fn parse(&self, textual_date: &str) -> Option<u64> {
self.special_cases
.iter()
.find_map(|case| {
if self.textual_date_matches(textual_date, case.0) {
Some(case.1)
} else {
None
}
})
.or_else(|| match self.parse_time_unit(textual_date) {
Some(tu) => Some(util::parse_numeric::<u64>(textual_date).unwrap_or(1) * tu),
None => None,
})
}
fn parse_time_unit(&self, textual_date: &str) -> Option<u64> {
match self.is_time_unit(textual_date, &self.seconds) {
true => Some(1),
false => match self.is_time_unit(textual_date, &self.minutes) {
true => Some(60),
false => match self.is_time_unit(textual_date, &self.hours) {
true => Some(3600),
false => match self.is_time_unit(textual_date, &self.days) {
true => Some(24 * 3600),
false => match self.is_time_unit(textual_date, &self.weeks) {
true => Some(7 * 24 * 3600),
false => match self.is_time_unit(textual_date, &self.months) {
true => Some(30 * 24 * 3600),
false => match self.is_time_unit(textual_date, &self.years) {
true => Some(365 * 24 * 3600),
false => None,
},
},
},
},
},
},
}
}
fn is_time_unit(&self, textual_date: &str, phrases: &Vec<&str>) -> bool {
phrases
.iter()
.any(|p| self.textual_date_matches(textual_date, p))
}
fn textual_date_matches(&self, textual_date: &str, ago_phrase: &str) -> bool {
if textual_date == ago_phrase {
return true;
}
let text_lower = textual_date.to_lowercase().replace('\u{200b}', "");
let ago_lower = ago_phrase.to_lowercase();
if self.word_separator.is_empty() {
return text_lower.contains(&ago_lower);
}
let escaped_phrase = fancy_regex::escape(&ago_lower);
let escaped_separator = match self.word_separator {
" " => Cow::Borrowed("[ \\t\\xA0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000]"),
_ => fancy_regex::escape(self.word_separator),
};
let pattern = format!(
"(^|{}){}($|{})",
escaped_separator, escaped_phrase, escaped_separator
);
ok_or_bail!(Regex::new(&pattern), false)
.is_match(&text_lower)
.unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path};
use rstest::rstest;
use super::*;
#[rstest]
#[case(Language::De, "vor 1 Sekunde", Some(1))]
#[case(Language::Ar, "قبل ساعة واحدة", Some(3600))]
fn t_parse(#[case] lang: Language, #[case] textual_date: &str, #[case] expect: Option<u64>) {
let pat = TimeagoPattern::try_from(lang).unwrap();
let secs_ago = pat.parse(textual_date);
assert_eq!(secs_ago, expect);
}
#[test]
fn t_testfile() {
let json_path = Path::new("testfiles/date/timeago.json");
let expect = [
10 * 60,
20 * 60,
1 * 3600,
2 * 3600,
7 * 3600,
8 * 3600,
9 * 3600,
10 * 3600,
11 * 3600,
12 * 3600,
13 * 3600,
14 * 3600,
15 * 3600,
3 * 3600,
4 * 3600,
4 * 3600,
5 * 3600,
6 * 3600,
6 * 3600,
20 * 3600,
2 * 3600 * 24,
3 * 3600 * 24,
5 * 3600 * 24,
6 * 3600 * 24,
8 * 3600 * 24,
10 * 3600 * 24,
12 * 3600 * 24,
2 * 3600 * 24 * 7,
3 * 3600 * 24 * 7,
4 * 3600 * 24 * 7,
1 * 3600 * 24 * 30,
8 * 3600 * 24 * 30,
11 * 3600 * 24 * 30,
1 * 3600 * 24 * 365,
2 * 3600 * 24 * 365,
3 * 3600 * 24 * 365,
4 * 3600 * 24 * 365,
];
let json_file = File::open(json_path).unwrap();
let strings_map: BTreeMap<String, Vec<String>> =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
strings_map.iter().for_each(|(lang, strings)| {
let pat = TimeagoPattern::try_from(lang.as_str()).unwrap();
assert_eq!(strings.len(), expect.len());
strings.iter().enumerate().for_each(|(n, s)| {
assert_eq!(
pat.parse(s),
Some(expect[n]),
"Language: {}, n: {}",
lang,
n
);
});
})
}
}