Nim library for human-readable formatting of numbers, file sizes, times, durations, and lists. Inspired by Python humanize and Go go-humanize.
- No external dependencies (only Nim stdlib)
- Explicit locale parameter on every function (no global state)
- 8 built-in locales: English, Arabic, German, Spanish, French, Italian, Russian, Chinese
- Nim >= 2.0.0
nimble install humanizeimport humanize
echo numComma(1_000_000) # "1,000,000"
echo ordinal(3) # "3rd"
echo naturalSize(1_500_000'i64) # "1.5 MB"
echo naturalList(["a", "b", "c"]) # "a, b and c"Convert an integer to its ordinal string representation.
import humanize
echo ordinal(1) # "1st"
echo ordinal(2) # "2nd"
echo ordinal(3) # "3rd"
echo ordinal(11) # "11th"
echo ordinal(21) # "21st"
echo ordinal(111) # "111th"With locales and gender:
import humanize
import humanize/lang/fr
echo ordinal(1, gMasc, LangFr) # "1er"
echo ordinal(1, gFem, LangFr) # "1re"
echo ordinal(2, locale = LangFr) # "2e"Format a number with thousands separators.
import humanize
echo numComma(1000) # "1,000"
echo numComma(1_000_000) # "1,000,000"
echo numComma(-1234) # "-1,234"
# Float overload with decimal places
echo numComma(1234567.89, 2) # "1,234,567.89"With a German locale:
import humanize
import humanize/lang/de
echo numComma(1_000_000, LangDe) # "1.000.000"Convert large integers to a readable word form.
import humanize
echo numWord(1_000_000'i64) # "1.0 million"
echo numWord(1_200_000'i64) # "1.2 million"
echo numWord(1_000_000_000'i64) # "1.0 billion"
echo numWord(999_999'i64) # "999,999" (below threshold)Associated Press style: numbers 1-9 as words, others as digits.
import humanize
echo numName(1) # "one"
echo numName(5) # "five"
echo numName(9) # "nine"
echo numName(10) # "10"
echo numName(0) # "0"Format byte counts as human-readable file sizes. Always uses "." as decimal separator (international units, no locale dependency).
import humanize
# SI (decimal, powers of 1000)
echo naturalSize(0'i64) # "0 Bytes"
echo naturalSize(1'i64) # "1 Byte"
echo naturalSize(1_000'i64) # "1.0 KB"
echo naturalSize(1_000_000'i64) # "1.0 MB"
echo naturalSize(1_000_000_000'i64) # "1.0 GB"
# Binary (powers of 1024)
echo naturalSize(1024'i64, binary = true) # "1.0 KiB"
echo naturalSize(1_048_576'i64, binary = true) # "1.0 MiB"
# GNU style (single-letter, powers of 1024)
echo naturalSize(1_048_576'i64, gnu = true) # "1.0M"
# Custom format
echo naturalSize(1_234_567'i64, format = "%.2f") # "1.23 MB"
# Negative values
echo naturalSize(-1000'i64) # "-1.0 KB"Parse a human-readable size string back to bytes.
import humanize
echo parseSize("1 MB") # 1000000
echo parseSize("1 MiB") # 1048576
echo parseSize("1.5 GB") # 1500000000
echo parseSize("1M", binary = true) # 1048576
echo parseSize("100 bytes") # 100Raises ValueError on invalid input.
Convenient constants for size calculations:
import humanize
# SI (powers of 1000)
const size = 5 * GigaByte # 5_000_000_000
# Binary (powers of 1024)
const mem = 2 * GibiByte # 2_147_483_648Available: Byte, KiloByte, MegaByte, GigaByte, TeraByte, PetaByte, ExaByte, KibiByte, MebiByte, GibiByte, TebiByte, PebiByte, ExbiByte.
Express a duration as a human-readable delta.
import std/times
import humanize
echo naturalDelta(initDuration(seconds = 1)) # "1 second"
echo naturalDelta(initDuration(seconds = 45)) # "45 seconds"
echo naturalDelta(initDuration(seconds = 120)) # "2 minutes"
echo naturalDelta(initDuration(hours = 3)) # "3 hours"
echo naturalDelta(initDuration(days = 15)) # "15 days"
echo naturalDelta(initDuration(days = 400)) # "1 year, 1 month"
# Without month grouping
echo naturalDelta(initDuration(days = 400), months = false)
# "1 year, 35 days"
# Int overload (seconds)
echo naturalDelta(3600) # "1 hour"Express a DateTime relative to now.
import std/times
import humanize
let base = now()
let past = base - initDuration(hours = 2)
let future = base + initDuration(days = 3)
echo naturalTime(base, base) # "just now"
echo naturalTime(past, base) # "2 hours ago"
echo naturalTime(future, base) # "3 days from now"Relative day names, with optional year.
import std/times
import humanize
let today = now()
let yesterday = today - initDuration(days = 1)
let tomorrow = today + initDuration(days = 1)
echo naturalDay(today, today) # "today"
echo naturalDay(yesterday, today) # "yesterday"
echo naturalDay(tomorrow, today) # "tomorrow"
# naturalDate includes year for other years
let oldDate = dateTime(2023, mMar, 15, 0, 0, 0, 0, utc())
echo naturalDate(oldDate, today) # "Mar 15, 2023"Multi-unit, precise duration formatting.
import std/times
import humanize
echo preciseDelta(initDuration(seconds = 90))
# "1 minute and 30 seconds"
echo preciseDelta(initDuration(seconds = 3633))
# "1 hour and 33 seconds"
echo preciseDelta(initDuration(days = 1, seconds = 30))
# "1 day and 30 seconds"
# Control minimum unit
echo preciseDelta(initDuration(seconds = 3633), minimumUnit = duMinutes)
# "1 hour and 60.55 minutes"
# Suppress specific units
echo preciseDelta(initDuration(days = 10), suppress = @[duWeeks])
# "10 days"
# Int overload (seconds)
echo preciseDelta(90) # "1 minute and 30 seconds"Join strings in a human-readable way.
import humanize
echo naturalList(newSeq[string]()) # ""
echo naturalList(["a"]) # "a"
echo naturalList(["a", "b"]) # "a and b"
echo naturalList(["a", "b", "c"]) # "a, b and c"Every function accepts a locale parameter (always the last argument, defaults to English). Locales are imported explicitly:
import humanize
import humanize/lang/de
import humanize/lang/ru
echo numComma(1_000_000, LangDe) # "1.000.000"
echo naturalDelta(initDuration(seconds = 30), locale = LangRu)
# "30 секунд"
echo naturalList(["a", "b", "c"], LangDe)
# "a, b und c"| Import | Constant | Language |
|---|---|---|
| (built-in) | LangEn |
English |
humanize/lang/ar |
LangAr |
Arabic |
humanize/lang/de |
LangDe |
German |
humanize/lang/es |
LangEs |
Spanish |
humanize/lang/fr |
LangFr |
French |
humanize/lang/it |
LangIt |
Italian |
humanize/lang/ru |
LangRu |
Russian |
humanize/lang/zh |
LangZh |
Chinese |
You can define your own locale by constructing a Locale object:
import humanize
const MyLocale = Locale(
name: "custom",
pluralRule: prGermanic,
ordinalRule: orEnglish,
timeUnits: TimeUnits(
years: Plurals(one: "year", few: "years", many: "years"),
# ... fill all fields
),
conjunction: "and",
serialComma: true, # Oxford numComma
# ... other fields
)
echo naturalList(["a", "b", "c"], MyLocale)
# "a, b, and c" (note the Oxford numComma)Import only what you need:
# Only number formatting
import humanize/number
# Only file sizes (no locale dependency)
import humanize/filesize
# Only list formatting
import humanize/list| Proc | Description |
|---|---|
ordinal(n, gender?, locale?) |
ordinal(3) -> "3rd" |
numComma(n, locale?) |
numComma(1000) -> "1,000" |
numComma(n, ndigits?, locale?) |
numComma(1234.5, 2) -> "1,234.50" |
numWord(n, locale?) |
numWord(1_000_000) -> "1.0 million" |
numName(n, locale?) |
numName(5) -> "five" |
| Proc | Description |
|---|---|
naturalSize(bytes, binary?, gnu?, format?) |
naturalSize(1_000_000) -> "1.0 MB" |
parseSize(text, binary?) |
parseSize("1.5 GB") -> 1_500_000_000 |
| Proc | Description |
|---|---|
naturalDelta(duration, months?, locale?) |
naturalDelta(initDuration(seconds=120)) -> "2 minutes" |
naturalTime(dt, now?, months?, locale?) |
naturalTime(past) -> "2 hours ago" |
naturalDay(dt, now?, locale?) |
naturalDay(yesterday) -> "yesterday" |
naturalDate(dt, now?, locale?) |
naturalDate(oldDate) -> "Mar 15, 2023" |
| Proc | Description |
|---|---|
preciseDelta(duration, minimumUnit?, suppress?, format?, locale?) |
preciseDelta(initDuration(seconds=3633)) -> "1 hour and 33 seconds" |
| Proc | Description |
|---|---|
naturalList(items, locale?) |
naturalList(["a","b","c"]) -> "a, b and c" |
MIT