Huminize

master

Human-friendly formatting library for Swift 6 (numbers, file sizes, time, SI, lists) with EN/RU localization
akvilary/swift-huminize

Huminize

A Swift library that converts machine-readable values into human-friendly strings. Inspired by go-humanize and python-humanize.

Requirements

  • Swift 6.0+
  • macOS 13+ / iOS 16+ / tvOS 16+ / watchOS 9+ / visionOS 1+

Installation

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.

Quick Start

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 дня назад"

API Reference

Numbers

intcomma(_:)

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"

ordinal(_:)

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"

intword(_:)

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 миллиона"

apnumber(_:)

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) // "три"

fractional(_:)

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"

scientific(_:precision:)

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"

metric(_:)

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"

File Sizes

bytes(_:)

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"

ibytes(_:)

Formats a byte count using IEC/binary units (base 1024).

h.ibytes(1024)       // "1.0 KiB"
h.ibytes(82_854_982) // "79.0 MiB"

parseBytes(_:)

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")  // nil

Supports suffixes: B, kB, MB, GB, TB, PB, EB, KiB, MiB, GiB, TiB, PiB, EiB, and their short forms (k, M, ki, mi, etc.).

Time & Dates

naturalTime(_:relativeTo:)

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 минуту"

naturalDelta(_:_:)

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"

naturalDay(_:relativeTo:)

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)           // "вчера"

naturalDate(_:)

Like naturalDay, but falls back to weekday name if within 7 days.

h.naturalDate(now.addingTimeInterval(259200)) // "Wednesday"

precisedDelta(_:_:minimumUnit:)

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.

SI Notation

si(_:unit:) / si(_:unit:decimals:)

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.

parseSI(_:)

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"
}

Lists

naturalList(_:conjunction:)

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"

oxfordList(_:conjunction:)

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"

Localization

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

License

MIT

Description

  • Swift Tools 6.0.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Jun 28 2026 16:19:54 GMT-0900 (Hawaii-Aleutian Daylight Time)