DEV Community

Cover image for Your browser speaks 200+ languages.
Kirill Chernenko
Kirill Chernenko

Posted on

Your browser speaks 200+ languages.

Let's say you're building a chat app. International. You're launching in Germany, Japan, and Brazil next quarter — because apparently that's the plan now, and you found out on a Tuesday afternoon.
You have a timestamp. A user was online 5 hours ago. You want to show that.
Simple.

In English: "5 hours ago"
In German: "vor 5 Stunden"
In Japanese: "5時間前"
In Brazilian Portuguese: "há 5 horas"
Enter fullscreen mode Exit fullscreen mode

Or maybe you have an event. It's in two days. You want to say that.

In English: "in 2 days"
In Arabic: "خلال يومين"
In Turkish: "2 gün içinde"
Enter fullscreen mode Exit fullscreen mode

Or just a date. March 25, 2026. Simple.

In English: "Mar 25, 2026"
In Russian: "25 мар. 2026 г."
In Chinese: "2026年3月25日"
Enter fullscreen mode Exit fullscreen mode

None of this is hard to display. It's hard to display correctly, in the right language, without shipping a dictionary to every user.

The obvious solution that isn't great

You open dayjs. You find the locale system. You import German. Portugal... and over and over more.

import 'dayjs/locale/de'
import 'dayjs/locale/ja'
import 'dayjs/locale/pt-br' 
Enter fullscreen mode Exit fullscreen mode

It works. Three locales down, forty-seven to go — and you're not sure which forty-seven, because that depends on where the business decides to expand next.

Then someone asks for Arabic. Arabic has six plural forms — "1 hour ago", "2 hours ago", "3 hours ago" are all different grammatical structures. You import the locale file. Then Serbian. Then Korean.

Then there's a meeting where someone says "we should support all major languages" and you smile and nod and quietly go update your webpack config.

The locale bundle grows. The import list grows. Any better ideas?

My solution

Three functions. That's it. No classes, no configuration objects, no "please read the 40-page migration guide". Just install.

npm install anywhen
Enter fullscreen mode Exit fullscreen mode

And use.

import { anydate, anywhen, anyago } from 'anywhen'
Enter fullscreen mode Exit fullscreen mode

Each function answers one question you already ask yourself when writing UI.

anywhen — just make it readable

This is the one that thinks for you. You give it a date, it figures out what a human would actually say. Recent things get relative time. Yesterday gets "yesterday". Older things get a proper date. Future things get "in 2 weeks". You stop making these decisions in every component.

anywhen(date, 'en')   // "just now"
anywhen(date, 'en')   // "10 minutes ago"
anywhen(date, 'en')   // "today, 2:35 PM"
anywhen(date, 'en')   // "yesterday, 9:00 AM"
anywhen(date, 'en')   // "Wednesday, 11:20 AM"
anywhen(date, 'en')   // "Mar 5, 2016"
anywhen(date, 'en')   // "in 2 weeks"
Enter fullscreen mode Exit fullscreen mode

The switching logic:

< 45 seconds   →  "just now"
< 1 hour       →  "10 minutes ago"
future > 1h    →  "in 2 weeks"
same day       →  "today, 14:35"
yesterday      →  "yesterday, 09:00"
< 7 days       →  "Wednesday, 11:20"
older          →  "Mar 5, 2016"
Enter fullscreen mode Exit fullscreen mode

anydate — what's the exact date, just a formatter?

Maybe, but smart. For when you need a clean, localized date string. A post timestamp. An invoice date. A birthday. Something absolute that doesn't change based on when the user is reading it.

anydate(date, 'en')   // "Mar 25, 2026"
anydate(date, 'de')   // "25. März 2026"
anydate(date, 'ja')   // "2026年3月25日"
anydate(date, 'ar')   // "٢٥ مارس ٢٠٢٦"
Enter fullscreen mode Exit fullscreen mode

Need more control? Pass any Intl.DateTimeFormat options:

anydate(date, 'en', { weekday: 'long', month: 'long', day: 'numeric' })
// "Wednesday, March 25"

anydate(date, 'en', { hour: '2-digit', minute: '2-digit' })
// "2:35 PM"
Enter fullscreen mode Exit fullscreen mode

anyago — how long ago? how far ahead?

For when the exact date doesn't matter — the distance does. A comment was posted. An event is coming. A subscription expires. You want to say "3 hours ago" or "in 2 days", not "March 25, 2026 at 14:35:00 UTC".

anyago(date, 'en')   // "5 hours ago"
anyago(date, 'de')   // "vor 5 Stunden"
anyago(date, 'tr')   // "5 saat önce"
anyago(date, 'ru')   // "5 часов назад"

// works for the future too
anyago(date, 'en')   // "in 2 days"
anyago(date, 'ar')   // "خلال يومين"
Enter fullscreen mode Exit fullscreen mode

Sometimes you don't want "yesterday" — you want "1 day ago". Just add flag true:

anyago(date, 'en', true)   // "1 day ago" instead of "yesterday"
anyago(date, 'en', true)   // "1 week ago" instead of "last week"
Enter fullscreen mode Exit fullscreen mode

anywhere — same locale everywhere

If you're formatting multiple dates in one component, passing the locale every time gets old fast. anywhere binds it once.

import { anywhere } from 'anywhen'

const d = anywhere('de')

d.anydate(date)   // "25. März 2026"
d.anywhen(date)   // "heute, 09:00"
d.anyago(date)    // "vor 5 Stunden"
Enter fullscreen mode Exit fullscreen mode

Locale set once and forgotten.

800 bytes. That includes every language.

What it doesn't do

Honest section. I like these.
No custom format strings like DD/MM/YYYY. No timezone handling (at least now).
No date arithmetic. No parsing of whatever creative format your backend decided to use in 2013.

The 7-day cutoff in anywhen — after which it shows an absolute date instead of a weekday — is fixed. If you need custom logic, use anyago and anydate directly and wire it yourself.

This library is for one specific thing:

displaying dates to humans in their language.

Chat timestamps. Event countdowns. Comment dates. Notification feeds. If that's what you need, it's 800 bytes and you're done. If you need to parse timezones or do date math — use date-fns. No shame in that.

Try it

Live demo where you can pick any method, any date, any locale and see the output: Demo page

Source is on GitHub. The whole thing is small enough to read in one sitting. If something is broken or missing — open an issue. I built this because I needed it, and I'm really curious whether other people do too.

Top comments (0)