A Swift library that converts machine-readable values into human-friendly strings. Inspired by go-humanize and python-humanize.
- Swift 6.0+
- macOS 13+ / iOS 16+ / tvOS 16+ / watchOS 9+ / visionOS 1+
Add the dependency in your Package.swift:
dependencies: [
.package(url: "https://github.com/your-org/huminize-swift.git", from: "1.0.0"),
]Then add "Huminize" to your target's dependencies.
import Huminize
// English (default)
let h = Huminize.default
h.intcomma(1_234_567) // "1,234,567"
h.ordinal(193) // "193rd"
h.bytes(82_854_982) // "82.9 MB"
h.naturalTime(someDate) // "3 days ago"
// Russian
let ru = Huminize(locale: .ru)
ru.intcomma(1_234_567) // "1 234 567" (non-breaking space)
ru.ordinal(1) // "1-й"
ru.bytes(82_854_982) // "82.9 MB"
ru.naturalTime(someDate) // "3 дня назад"Adds thousand separators to numbers. Uses , for English and non-breaking space for Russian.
h.intcomma(1000) // "1,000"
h.intcomma(12_345_678) // "12,345,678"
h.intcomma(-1_234_567) // "-1,234,567"
// Also accepts floating-point:
h.intcomma(834_142.32) // "834,142.32"Converts a number to its ordinal form.
h.ordinal(1) // "1st"
h.ordinal(2) // "2nd"
h.ordinal(3) // "3rd"
h.ordinal(4) // "4th"
h.ordinal(11) // "11th"
h.ordinal(21) // "21st"
h.ordinal(193) // "193rd"Converts large integers into human-readable words.
h.intword(100) // "1 hundred"
h.intword(1_000) // "1 thousand"
h.intword(1_500_000) // "1.5 million"
h.intword(1_000_000_000) // "1 billion"
h.intword(1_000_000_000_000) // "1 trillion"Russian with proper pluralization:
ru.intword(1_000) // "1 тысяча"
ru.intword(2_000) // "2 тысячи"
ru.intword(5_000) // "5 тысяч"
ru.intword(1_000_000) // "1 миллион"
ru.intword(2_000_000) // "2 миллиона"Converts numbers 0-9 to their textual representation.
h.apnumber(0) // "zero"
h.apnumber(3) // "three"
h.apnumber(9) // "nine"
h.apnumber(10) // "10"
ru.apnumber(0) // "ноль"
ru.apnumber(3) // "три"Converts a float to a fractional representation.
h.fractional(0.5) // "1/2"
h.fractional(1.5) // "1 1/2"
h.fractional(0.25) // "1/4"
h.fractional(0.75) // "3/4"Formats a number in scientific notation.
h.scientific(1000) // "1.00×10^3"
h.scientific(1500, precision: 1) // "1.5×10^3"
h.scientific(0) // "0"Formats a number with metric/SI prefixes.
h.metric(1500) // "1.5 k"
h.metric(1_500_000) // "1.5 M"
h.metric(1_500_000_000) // "1.5 G"Formats a byte count using SI units (base 1000).
h.bytes(0) // "0 B"
h.bytes(1) // "1 B"
h.bytes(1000) // "1.0 kB"
h.bytes(82_854_982) // "82.9 MB"
h.bytes(1_000_000_000) // "1.0 GB"Formats a byte count using IEC/binary units (base 1024).
h.ibytes(1024) // "1.0 KiB"
h.ibytes(82_854_982) // "79.0 MiB"Parses a human-readable byte string back into a number. Returns nil on failure.
h.parseBytes("42 MB") // 42_000_000
h.parseBytes("1.5 GB") // 1_500_000_000
h.parseBytes("1 KiB") // 1_024
h.parseBytes("invalid") // nilSupports suffixes: B, kB, MB, GB, TB, PB, EB, KiB, MiB, GiB, TiB, PiB, EiB, and their short forms (k, M, ki, mi, etc.).
Expresses a Date as a human-friendly relative time string.
let now = Date()
h.naturalTime(now.addingTimeInterval(-30), relativeTo: now) // "30 seconds ago"
h.naturalTime(now.addingTimeInterval(-3600), relativeTo: now) // "1 hour ago"
h.naturalTime(now.addingTimeInterval(86400), relativeTo: now) // "1 day from now"
h.naturalTime(now, relativeTo: now) // "now"
ru.naturalTime(now.addingTimeInterval(-30), relativeTo: now) // "30 секунд назад"
ru.naturalTime(now.addingTimeInterval(60), relativeTo: now) // "через 1 минуту"Returns the absolute time difference between two dates as a human-readable string (no "ago"/"from now").
h.naturalDelta(now, now.addingTimeInterval(90)) // "1 minute"
h.naturalDelta(now, now.addingTimeInterval(3600)) // "1 hour"
h.naturalDelta(now, now.addingTimeInterval(86400)) // "1 day"
h.naturalDelta(now, now) // "now"Converts a date to "today", "yesterday", "tomorrow", or a formatted date.
h.naturalDay(now) // "today"
h.naturalDay(now - 86400) // "yesterday"
h.naturalDay(now + 86400) // "tomorrow"
h.naturalDay(someOtherDate) // "May 22, 2026"
ru.naturalDay(now) // "сегодня"
ru.naturalDay(now - 86400) // "вчера"Like naturalDay, but falls back to weekday name if within 7 days.
h.naturalDate(now.addingTimeInterval(259200)) // "Wednesday"Returns a precise time delta with up to two significant units.
h.precisedDelta(now, now.addingTimeInterval(3661)) // "1 hour, 1 minute"
h.precisedDelta(now, now.addingTimeInterval(3661), minimumUnit: .days) // "1 hour" (skips smaller units)TimeUnit values: .seconds, .minutes, .hours, .days, .months, .years.
Formats a number with SI prefixes.
h.si(1_000_000, unit: "B") // "1 MB"
h.si(2.2345e-12, unit: "F") // "2.2345 pF"
h.si(1_000_000, unit: "B", decimals: 0) // "1 MB"Supported prefixes: q, r, y, z, a, f, p, n, µ, m, (none), k, M, G, T, P, E, Z, Y, R, Q.
Parses an SI-formatted string back into a value and unit. Returns nil on failure.
if let (value, unit) = h.parseSI("2.2345 pF") {
// value ≈ 2.2345e-12, unit = "F"
}Joins a list of strings with a conjunction.
h.naturalList([]) // ""
h.naturalList(["foo"]) // "foo"
h.naturalList(["foo", "bar"]) // "foo and bar"
h.naturalList(["foo", "bar", "baz"]) // "foo, bar and baz"
h.naturalList(["foo", "bar"], conjunction: .or) // "foo or bar"
ru.naturalList(["foo", "bar", "baz"]) // "foo, bar и baz"Like naturalList, but uses the Oxford comma (serial comma).
h.oxfordList(["foo", "bar", "baz"]) // "foo, bar, and baz"
h.oxfordList(["foo", "bar", "baz"], conjunction: .or) // "foo, bar, or baz"Two locales are supported:
| Locale | Enum Value |
|---|---|
| English (default) | .en |
| Russian | .ru |
let en = Huminize() // or Huminize.default
let ru = Huminize(locale: .ru)Russian locale features:
- Non-breaking space (
\u{00A0}) as thousands separator - Proper Russian plural forms (1 минуту, 2 минуты, 5 минут)
- Correct word order for future time ("через 5 минут" vs "5 минут назад")
- Russian weekday and month names
MIT