humanize-time-tool
Version:
A simple and lightweight Node.js library for human-friendly time formatting and localization.
702 lines (682 loc) • 22.4 kB
JavaScript
/**
* Collection of translations for multiple languages.
* Each language object contains strings for various time intervals.
*/
const translations = {
"sv": {
"seconds": "för några sekunder sedan",
"minute_one": "för 1 minut sedan",
"minutes": "för {n} minuter sedan",
"hour_one": "för 1 timme sedan",
"hours": "för {n} timmar sedan",
"day_one": "för 1 dag sedan",
"days": "för {n} dagar sedan",
"week_one": "för 1 vecka sedan",
"weeks": "för {n} veckor sedan",
"month_one": "för 1 månad sedan",
"months": "för {n} månader sedan",
"year_one": "för 1 år sedan",
"years": "för {n} år sedan"
},
"en": {
"seconds": "a few seconds ago",
"minute_one": "1 minute ago",
"minutes": "{n} minutes ago",
"hour_one": "1 hour ago",
"hours": "{n} hours ago",
"day_one": "1 day ago",
"days": "{n} days ago",
"week_one": "1 week ago",
"weeks": "{n} weeks ago",
"month_one": "1 month ago",
"months": "{n} months ago",
"year_one": "1 year ago",
"years": "{n} years ago"
},
"fr": {
"seconds": "il y a quelques secondes",
"minute_one": "il y a 1 minute",
"minutes": "il y a {n} minutes",
"hour_one": "il y a 1 heure",
"hours": "il y a {n} heures",
"day_one": "il y a 1 jour",
"days": "il y a {n} jours",
"week_one": "il y a 1 semaine",
"weeks": "il y a {n} semaines",
"month_one": "il y a 1 mois",
"months": "il y a {n} mois",
"year_one": "il y a 1 an",
"years": "il y a {n} ans"
},
"de": {
"seconds": "vor ein paar Sekunden",
"minute_one": "vor 1 Minute",
"minutes": "vor {n} Minuten",
"hour_one": "vor 1 Stunde",
"hours": "vor {n} Stunden",
"day_one": "vor 1 Tag",
"days": "vor {n} Tagen",
"week_one": "vor 1 Woche",
"weeks": "vor {n} Wochen",
"month_one": "vor 1 Monat",
"months": "vor {n} Monaten",
"year_one": "vor 1 Jahr",
"years": "vor {n} Jahren"
},
"es": {
"seconds": "hace unos segundos",
"minute_one": "hace 1 minuto",
"minutes": "hace {n} minutos",
"hour_one": "hace 1 hora",
"hours": "hace {n} horas",
"day_one": "hace 1 día",
"days": "hace {n} días",
"week_one": "hace 1 semana",
"weeks": "hace {n} semanas",
"month_one": "hace 1 mes",
"months": "hace {n} meses",
"year_one": "hace 1 año",
"years": "hace {n} años"
},
"it": {
"seconds": "qualche secondo fa",
"minute_one": "1 minuto fa",
"minutes": "{n} minuti fa",
"hour_one": "1 ora fa",
"hours": "{n} ore fa",
"day_one": "1 giorno fa",
"days": "{n} giorni fa",
"week_one": "1 settimana fa",
"weeks": "{n} settimane fa",
"month_one": "1 mese fa",
"months": "{n} mesi fa",
"year_one": "1 anno fa",
"years": "{n} anni fa"
},
"pt": {
"seconds": "há poucos segundos",
"minute_one": "há 1 minuto",
"minutes": "há {n} minutos",
"hour_one": "há 1 hora",
"hours": "há {n} horas",
"day_one": "há 1 dia",
"days": "há {n} dias",
"week_one": "há 1 semana",
"weeks": "há {n} semanas",
"month_one": "há 1 mês",
"months": "há {n} meses",
"year_one": "há 1 ano",
"years": "há {n} anos"
},
"nl": {
"seconds": "een paar seconden geleden",
"minute_one": "1 minuut geleden",
"minutes": "{n} minuten geleden",
"hour_one": "1 uur geleden",
"hours": "{n} uur geleden",
"day_one": "1 dag geleden",
"days": "{n} dagen geleden",
"week_one": "1 week geleden",
"weeks": "{n} weken geleden",
"month_one": "1 maand geleden",
"months": "{n} maanden geleden",
"year_one": "1 jaar geleden",
"years": "{n} jaar geleden"
},
"pl": {
"seconds": "kilka sekund temu",
"minute_one": "1 minuta temu",
"minutes": "{n} minut temu",
"hour_one": "1 godzina temu",
"hours": "{n} godzin temu",
"day_one": "1 dzień temu",
"days": "{n} dni temu",
"week_one": "1 tydzień temu",
"weeks": "{n} tygodni temu",
"month_one": "1 miesiąc temu",
"months": "{n} miesięcy temu",
"year_one": "1 rok temu",
"years": "{n} lat temu"
},
"fi": {
"seconds": "muutama sekunti sitten",
"minute_one": "1 minuutti sitten",
"minutes": "{n} minuuttia sitten",
"hour_one": "1 tunti sitten",
"hours": "{n} tuntia sitten",
"day_one": "1 päivä sitten",
"days": "{n} päivää sitten",
"week_one": "1 viikko sitten",
"weeks": "{n} viikkoa sitten",
"month_one": "1 kuukausi sitten",
"months": "{n} kuukautta sitten",
"year_one": "1 vuosi sitten",
"years": "{n} vuotta sitten"
},
"no": {
"seconds": "for noen sekunder siden",
"minute_one": "for 1 minutt siden",
"minutes": "for {n} minutter siden",
"hour_one": "for 1 time siden",
"hours": "for {n} timer siden",
"day_one": "for 1 dag siden",
"days": "for {n} dager siden",
"week_one": "for 1 uke siden",
"weeks": "for {n} uker siden",
"month_one": "for 1 måned siden",
"months": "for {n} måneder siden",
"year_one": "for 1 år siden",
"years": "for {n} år siden"
},
"da": {
"seconds": "for få sekunder siden",
"minute_one": "for 1 minut siden",
"minutes": "for {n} minutter siden",
"hour_one": "for 1 time siden",
"hours": "for {n} timer siden",
"day_one": "for 1 dag siden",
"days": "for {n} dage siden",
"week_one": "for 1 uge siden",
"weeks": "for {n} uger siden",
"month_one": "for 1 måned siden",
"months": "for {n} måneder siden",
"year_one": "for 1 år siden",
"years": "for {n} år siden"
},
"ru": {
"seconds": "несколько секунд назад",
"minute_one": "1 минуту назад",
"minutes": "{n} минут назад",
"hour_one": "1 час назад",
"hours": "{n} часов назад",
"day_one": "1 день назад",
"days": "{n} дней назад",
"week_one": "1 неделю назад",
"weeks": "{n} недель назад",
"month_one": "1 месяц назад",
"months": "{n} месяцев назад",
"year_one": "1 год назад",
"years": "{n} лет назад"
},
"zh": {
"seconds": "几秒前",
"minute_one": "1 分钟前",
"minutes": "{n} 分钟前",
"hour_one": "1 小时前",
"hours": "{n} 小时前",
"day_one": "1 天前",
"days": "{n} 天前",
"week_one": "1 周前",
"weeks": "{n} 周前",
"month_one": "1 个月前",
"months": "{n} 个月前",
"year_one": "1 年前",
"years": "{n} 年前"
},
"ja": {
"seconds": "数秒前",
"minute_one": "1 分前",
"minutes": "{n} 分前",
"hour_one": "1 時間前",
"hours": "{n} 時間前",
"day_one": "1 日前",
"days": "{n} 日前",
"week_one": "1 週間前",
"weeks": "{n} 週間前",
"month_one": "1 か月前",
"months": "{n} か月前",
"year_one": "1 年前",
"years": "{n} 年前"
},
"ko": {
"seconds": "몇 초 전",
"minute_one": "1분 전",
"minutes": "{n}분 전",
"hour_one": "1시간 전",
"hours": "{n}시간 전",
"day_one": "1일 전",
"days": "{n}일 전",
"week_one": "1주 전",
"weeks": "{n}주 전",
"month_one": "1개월 전",
"months": "{n}개월 전",
"year_one": "1년 전",
"years": "{n}년 전"
},
"ar": {
"seconds": "منذ ثوانٍ قليلة",
"minute_one": "منذ دقيقة واحدة",
"minutes": "منذ {n} دقائق",
"hour_one": "منذ ساعة واحدة",
"hours": "منذ {n} ساعات",
"day_one": "منذ يوم واحد",
"days": "منذ {n} أيام",
"week_one": "منذ أسبوع واحد",
"weeks": "منذ {n} أسابيع",
"month_one": "منذ شهر واحد",
"months": "منذ {n} أشهر",
"year_one": "منذ سنة واحدة",
"years": "منذ {n} سنوات"
},
"hi": {
"seconds": "कुछ सेकंड पहले",
"minute_one": "1 मिनट पहले",
"minutes": "{n} मिनट पहले",
"hour_one": "1 घंटे पहले",
"hours": "{n} घंटे पहले",
"day_one": "1 दिन पहले",
"days": "{n} दिन पहले",
"week_one": "1 सप्ताह पहले",
"weeks": "{n} सप्ताह पहले",
"month_one": "1 महीने पहले",
"months": "{n} महीने पहले",
"year_one": "1 साल पहले",
"years": "{n} साल पहले"
},
"he": {
"seconds": "לפני כמה שניות",
"minute_one": "לפני דקה אחת",
"minutes": "לפני {n} דקות",
"hour_one": "לפני שעה אחת",
"hours": "לפני {n} שעות",
"day_one": "לפני יום אחד",
"days": "לפני {n} ימים",
"week_one": "לפני שבוע אחד",
"weeks": "לפני {n} שבועות",
"month_one": "לפני חודש אחד",
"months": "לפני {n} חודשים",
"year_one": "לפני שנה אחת",
"years": "לפני {n} שנים"
},
"tr": {
"seconds": "birkaç saniye önce",
"minute_one": "1 dakika önce",
"minutes": "{n} dakika önce",
"hour_one": "1 saat önce",
"hours": "{n} saat önce",
"day_one": "1 gün önce",
"days": "{n} gün önce",
"week_one": "1 hafta önce",
"weeks": "{n} hafta önce",
"month_one": "1 ay önce",
"months": "{n} ay önce",
"year_one": "1 yıl önce",
"years": "{n} yıl önce"
},
"el": {
"seconds": "πριν από μερικά δευτερόλεπτα",
"minute_one": "πριν από 1 λεπτό",
"minutes": "πριν από {n} λεπτά",
"hour_one": "πριν από 1 ώρα",
"hours": "πριν από {n} ώρες",
"day_one": "πριν από 1 μέρα",
"days": "πριν από {n} μέρες",
"week_one": "πριν από 1 εβδομάδα",
"weeks": "πριν από {n} εβδομάδες",
"month_one": "πριν από 1 μήνα",
"months": "πριν από {n} μήνες",
"year_one": "πριν από 1 χρόνο",
"years": "πριν από {n} χρόνια"
},
"cs": {
"seconds": "před několika sekundami",
"minute_one": "před 1 minutou",
"minutes": "před {n} minutami",
"hour_one": "před 1 hodinou",
"hours": "před {n} hodinami",
"day_one": "před 1 dnem",
"days": "před {n} dny",
"week_one": "před 1 týdnem",
"weeks": "před {n} týdny",
"month_one": "před 1 měsícem",
"months": "před {n} měsíci",
"year_one": "před 1 rokem",
"years": "před {n} lety"
},
"vi": {
"seconds": "vài giây trước",
"minute_one": "1 phút trước",
"minutes": "{n} phút trước",
"hour_one": "1 giờ trước",
"hours": "{n} giờ trước",
"day_one": "1 ngày trước",
"days": "{n} ngày trước",
"week_one": "1 tuần trước",
"weeks": "{n} tuần trước",
"month_one": "1 tháng trước",
"months": "{n} tháng trước",
"year_one": "1 năm trước",
"years": "{n} năm trước"
},
"et": {
"seconds": "paar sekundit tagasi",
"minute_one": "1 minut tagasi",
"minutes": "{n} minutit tagasi",
"hour_one": "1 tund tagasi",
"hours": "{n} tundi tagasi",
"day_one": "1 päev tagasi",
"days": "{n} päeva tagasi",
"week_one": "1 nädal tagasi",
"weeks": "{n} nädalat tagasi",
"month_one": "1 kuu tagasi",
"months": "{n} kuud tagasi",
"year_one": "1 aasta tagasi",
"years": "{n} aastat tagasi"
},
"lv": {
"seconds": "dažas sekundes atpakaļ",
"minute_one": "1 minūti atpakaļ",
"minutes": "{n} minūtes atpakaļ",
"hour_one": "1 stundu atpakaļ",
"hours": "{n} stundas atpakaļ",
"day_one": "1 dienu atpakaļ",
"days": "{n} dienas atpakaļ",
"week_one": "1 nedēļu atpakaļ",
"weeks": "{n} nedēļas atpakaļ",
"month_one": "1 mēnesi atpakaļ",
"months": "{n} mēneši atpakaļ",
"year_one": "1 gadu atpakaļ",
"years": "{n} gadus atpakaļ"
},
"lt": {
"seconds": "prieš kelias sekundes",
"minute_one": "prieš 1 minutę",
"minutes": "prieš {n} minutes",
"hour_one": "prieš 1 valandą",
"hours": "prieš {n} valandas",
"day_one": "prieš 1 dieną",
"days": "prieš {n} dienas",
"week_one": "prieš 1 savaitę",
"weeks": "prieš {n} savaites",
"month_one": "prieš 1 mėnesį",
"months": "prieš {n} mėnesius",
"year_one": "prieš 1 metus",
"years": "prieš {n} metus"
},
"sk": {
"seconds": "pred niekoľkými sekundami",
"minute_one": "pred 1 minútou",
"minutes": "pred {n} minútami",
"hour_one": "pred 1 hodinou",
"hours": "pred {n} hodinami",
"day_one": "pred 1 dňom",
"days": "pred {n} dňami",
"week_one": "pred 1 týždňom",
"weeks": "pred {n} týždňami",
"month_one": "pred 1 mesiacom",
"months": "pred {n} mesiacmi",
"year_one": "pred 1 rokom",
"years": "pred {n} rokmi"
},
"hu": {
"seconds": "néhány másodperce",
"minute_one": "1 perce",
"minutes": "{n} perce",
"hour_one": "1 órája",
"hours": "{n} órája",
"day_one": "1 napja",
"days": "{n} napja",
"week_one": "1 hete",
"weeks": "{n} hete",
"month_one": "1 hónapja",
"months": "{n} hónapja",
"year_one": "1 éve",
"years": "{n} éve"
},
"sr": {
"seconds": "пре неколико секунди",
"minute_one": "пре 1 минут",
"minutes": "пре {n} минута",
"hour_one": "пре 1 сат",
"hours": "пре {n} сати",
"day_one": "пре 1 дана",
"days": "пре {n} дана",
"week_one": "пре 1 недеље",
"weeks": "пре {n} недеља",
"month_one": "пре 1 месеца",
"months": "пре {n} месеци",
"year_one": "пре 1 године",
"years": "пре {n} година"
},
"hr": {
"seconds": "prije nekoliko sekundi",
"minute_one": "prije 1 minute",
"minutes": "prije {n} minuta",
"hour_one": "prije 1 sat",
"hours": "prije {n} sati",
"day_one": "prije 1 dan",
"days": "prije {n} dana",
"week_one": "prije 1 tjedan",
"weeks": "prije {n} tjedana",
"month_one": "prije 1 mjesec",
"months": "prije {n} mjeseci",
"year_one": "prije 1 godinu",
"years": "prije {n} godina"
},
"uk": {
"seconds": "кілька секунд тому",
"minute_one": "1 хвилину тому",
"minutes": "{n} хвилин тому",
"hour_one": "1 годину тому",
"hours": "{n} годин тому",
"day_one": "1 день тому",
"days": "{n} днів тому",
"week_one": "1 тиждень тому",
"weeks": "{n} тижнів тому",
"month_one": "1 місяць тому",
"months": "{n} місяців тому",
"year_one": "1 рік тому",
"years": "{n} років тому"
},
"be": {
"seconds": "некалькі секунд таму",
"minute_one": "1 хвіліну таму",
"minutes": "{n} хвілін таму",
"hour_one": "1 гадзіну таму",
"hours": "{n} гадзін таму",
"day_one": "1 дзень таму",
"days": "{n} дзён таму",
"week_one": "1 тыдзень таму",
"weeks": "{n} тыдняў таму",
"month_one": "1 месяц таму",
"months": "{n} месяцаў таму",
"year_one": "1 год таму",
"years": "{n} гадоў таму"
},
"bg": {
"seconds": "преди няколко секунди",
"minute_one": "преди 1 минута",
"minutes": "преди {n} минути",
"hour_one": "преди 1 час",
"hours": "преди {n} часа",
"day_one": "преди 1 ден",
"days": "преди {n} дни",
"week_one": "преди 1 седмица",
"weeks": "преди {n} седмици",
"month_one": "преди 1 месец",
"months": "преди {n} месеца",
"year_one": "преди 1 година",
"years": "преди {n} години"
},
"ro": {
"seconds": "cu câteva secunde în urmă",
"minute_one": "acum 1 minut",
"minutes": "acum {n} minute",
"hour_one": "acum 1 oră",
"hours": "acum {n} ore",
"day_one": "acum 1 zi",
"days": "acum {n} zile",
"week_one": "acum 1 săptămână",
"weeks": "acum {n} săptămâni",
"month_one": "acum 1 lună",
"months": "acum {n} luni",
"year_one": "acum 1 an",
"years": "acum {n} ani"
},
"th": {
"seconds": "เมื่อไม่กี่วินาทีที่แล้ว",
"minute_one": "1 นาทีที่แล้ว",
"minutes": "{n} นาทีที่แล้ว",
"hour_one": "1 ชั่วโมงที่แล้ว",
"hours": "{n} ชั่วโมงที่แล้ว",
"day_one": "1 วันที่แล้ว",
"days": "{n} วันที่แล้ว",
"week_one": "1 สัปดาห์ที่แล้ว",
"weeks": "{n} สัปดาห์ที่แล้ว",
"month_one": "1 เดือนที่แล้ว",
"months": "{n} เดือนที่แล้ว",
"year_one": "1 ปีที่แล้ว",
"years": "{n} ปีที่แล้ว"
},
"sl": {
"seconds": "pred nekaj sekundami",
"minute_one": "pred 1 minuto",
"minutes": "pred {n} minutami",
"hour_one": "pred 1 uro",
"hours": "pred {n} urami",
"day_one": "pred 1 dnem",
"days": "pred {n} dnevi",
"week_one": "pred 1 tednom",
"weeks": "pred {n} tedni",
"month_one": "pred 1 mesecem",
"months": "pred {n} meseci",
"year_one": "pred 1 letom",
"years": "pred {n} leti"
},
"bs": {
"seconds": "prije nekoliko sekundi",
"minute_one": "prije 1 minut",
"minutes": "prije {n} minuta",
"hour_one": "prije 1 sat",
"hours": "prije {n} sati",
"day_one": "prije 1 dan",
"days": "prije {n} dana",
"week_one": "prije 1 sedmicu",
"weeks": "prije {n} sedmica",
"month_one": "prije 1 mjesec",
"months": "prije {n} mjeseci",
"year_one": "prije 1 godinu",
"years": "prije {n} godina"
},
"fa": {
"seconds": "چند ثانیه پیش",
"minute_one": "1 دقیقه پیش",
"minutes": "{n} دقیقه پیش",
"hour_one": "1 ساعت پیش",
"hours": "{n} ساعت پیش",
"day_one": "1 روز پیش",
"days": "{n} روز پیش",
"week_one": "1 هفته پیش",
"weeks": "{n} هفته پیش",
"month_one": "1 ماه پیش",
"months": "{n} ماه پیش",
"year_one": "1 سال پیش",
"years": "{n} سال پیش"
},
"is": {
"seconds": "fyrir nokkrum sekúndum",
"minute_one": "fyrir 1 mínútu",
"minutes": "fyrir {n} mínútum",
"hour_one": "fyrir 1 klukkustund",
"hours": "fyrir {n} klukkustundum",
"day_one": "fyrir 1 degi",
"days": "fyrir {n} dögum",
"week_one": "fyrir 1 viku",
"weeks": "fyrir {n} vikum",
"month_one": "fyrir 1 mánuði",
"months": "fyrir {n} mánuðum",
"year_one": "fyrir 1 ári",
"years": "fyrir {n} árum"
}
};
// Internal storage for registered languages
const registered = new Map();
/**
* Register a language (activate it)
* @param {string} lang - language code
*/
function register(lang) {
if (translations[lang]) {
registered.set(lang, translations[lang]);
} else {
console.warn(`Language '${lang}' not found in translations`);
}
}
/**
* Clear all registered languages
*/
function clearRegistered() {
registered.clear();
}
/**
* Middleware to "allow" certain languages
* @param {'*'|string[]} allowedLanguages - '*' or an array of allowed language codes
* @returns middleware function for e.g. Express
*/
function languageMiddleware(allowedLanguages) {
return function (req, res, next) {
clearRegistered();
const isWildcard =
allowedLanguages === '*' ||
(Array.isArray(allowedLanguages) && allowedLanguages.length === 1 && allowedLanguages[0] === '*');
if (isWildcard) {
Object.keys(translations).forEach(register);
} else if (Array.isArray(allowedLanguages)) {
allowedLanguages.forEach(register);
} else {
console.warn('⚠️ allowedLanguages must be "*" or an array of language codes.');
}
next();
};
}
/**
* Get translations for a language if it's registered
* @param {string} lang
* @returns {object|null}
*/
function getTranslations(lang) {
return registered.get(lang) || null;
}
/**
* Formats a time difference as "time ago" according to selected language
* @param {Date|string|number} dateInput
* @param {string} lang - language code
* @param {object} custom - optional custom translations
* @returns {string}
*/
function timeAgo(dateInput, lang = 'en', custom = {}) {
const date = new Date(dateInput);
if (isNaN(date)) throw new TypeError('Invalid date');
const diff = Math.floor((Date.now() - date.getTime()) / 1000);
const base = translations.en;
const locale = getTranslations(lang) || base;
const t = { ...base, ...locale, ...custom };
const fmt = (str, n) => (typeof str === 'string' ? str.replace('{n}', n) : '');
if (diff < 60) return t.seconds || 'a few seconds ago';
const min = Math.floor(diff / 60);
if (min === 1) return t.minute_one || '1 minute ago';
if (min < 60) return fmt(t.minutes, min);
const hr = Math.floor(min / 60);
if (hr === 1) return t.hour_one || '1 hour ago';
if (hr < 24) return fmt(t.hours, hr);
const day = Math.floor(hr / 24);
if (day === 1) return t.day_one || '1 day ago';
if (day < 7) return fmt(t.days, day);
const wk = Math.floor(day / 7);
if (wk === 1) return t.week_one || '1 week ago';
if (wk < 5) return fmt(t.weeks, wk);
const mo = Math.floor(day / 30.44);
if (mo === 1) return t.month_one || '1 month ago';
if (mo < 12) return fmt(t.months, mo);
const yr = Math.floor(day / 365.25);
if (yr === 1) return t.year_one || '1 year ago';
return fmt(t.years, yr) || `${yr} years ago`;
}
export {
languageMiddleware,
timeAgo,
register,
clearRegistered,
getTranslations,
translations,
};