Socket
Book a DemoInstallSign in
Socket

human_number

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

human_number

0.2.1
bundlerRubygems
Version published
Maintainers
1
Created
Source

HumanNumber

HumanNumber is a Ruby gem that implements accurate number formatting based on international standards. It references Microsoft Globalization documentation and Unicode CLDR standards to provide human-readable number formats that respect the unique formatting conventions of each country and region.

Features

  • 🌍 International Standards Compliance: Based on Microsoft Globalization and Unicode CLDR standards
  • 🔢 Cultural Number Systems: Automatic selection of Western (K/M/B/T), East Asian (만/억/조), or Indian (lakh/crore) systems
  • 💰 Currency Formatting: ISO 4217 currency codes with native locale precision rules
  • 🏗️ Intelligent Abbreviations: Culturally-appropriate large number simplification
  • 🔧 Rails Integration: Complete compatibility with Rails I18n infrastructure
  • 📚 rails-i18n Based: Leverages verified locale data

Quick Start

Add to your Gemfile and install:

gem 'human_number'
bundle install

Start formatting numbers:

require 'human_number'

# Human-readable numbers
HumanNumber.human_number(1_234_567)           #=> "1.2M"
HumanNumber.human_number(50_000, locale: :ko) #=> "5만"

# Currency formatting
HumanNumber.currency(1234.56, currency_code: 'USD', locale: :en) #=> "$1,234.56"
HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en) #=> "$1M"

Installation

Add this line to your application's Gemfile:

gem 'human_number'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install human_number

API Reference

Core Methods

HumanNumber.human_number(number, **options)

Formats numbers with intelligent, culturally-appropriate abbreviations.

# Basic usage
HumanNumber.human_number(1_234_567)           #=> "1.2M"
HumanNumber.human_number(50_000, locale: :ko) #=> "5만"
HumanNumber.human_number(50_000, locale: :ja) #=> "5万"

# Significant digits control (default: max_digits: 2)  
HumanNumber.human_number(1_234_567, max_digits: 1)   #=> "1M"     # 1 significant digit
HumanNumber.human_number(1_234_567, max_digits: 3)   #=> "1.23M"  # 3 significant digits  
HumanNumber.human_number(1_234_567, max_digits: nil) #=> "1M 234K 567" # Complete breakdown

# Unit preferences (Western locales only)
HumanNumber.human_number(1_000_000, abbr_units: true)  #=> "1M"
HumanNumber.human_number(1_000_000, abbr_units: false) #=> "1 million"

# Minimum thresholds
HumanNumber.human_number(5_000, min_unit: 10_000)  #=> "5,000"
HumanNumber.human_number(50_000, min_unit: 10_000) #=> "50K"

# Zero trimming (default: true)
HumanNumber.human_number(1_000_000, trim_zeros: true)  #=> "1M"
HumanNumber.human_number(1_000_000, trim_zeros: false) #=> "1.0M"

Parameters:

  • locale (Symbol): Target locale for cultural number systems
  • max_digits (Integer|nil): Maximum significant digits (default: 2, nil for complete mode)
  • abbr_units (Boolean): Use abbreviated vs full units (default: true)
  • min_unit (Integer): Minimum unit threshold for abbreviation (default: nil)
  • trim_zeros (Boolean): Remove trailing decimal zeros (default: true)

HumanNumber.currency(number, currency_code:, locale:)

Standard currency formatting with native locale precision rules.

HumanNumber.currency(1234.56, currency_code: 'USD', locale: :en) #=> "$1,234.56"
HumanNumber.currency(50_000, currency_code: 'KRW', locale: :ko)  #=> "50,000원"
HumanNumber.currency(1234.99, currency_code: 'JPY', locale: :ja) #=> "1,235円"

# Cross-locale consistency (USD always 2 decimals, JPY always 0)
HumanNumber.currency(1234.56, currency_code: 'USD', locale: :ko) #=> "$1,234.56"  
HumanNumber.currency(1234.56, currency_code: 'JPY', locale: :en) #=> "1,235円"

HumanNumber.human_currency(number, currency_code:, locale:, **options)

Human-readable currency formatting with cultural abbreviations.

HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en) #=> "$1M"
HumanNumber.human_currency(50_000, currency_code: 'KRW', locale: :ko)    #=> "5만원"

# Combined options
HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en, max_digits: 3) #=> "$1.23M"
HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en, max_digits: nil) #=> "$1M 234K 567"

System Detection Methods

HumanNumber.number_system(locale:)

Determines which number system is used for a given locale. Returns a symbol indicating the cultural number formatting system.

# Western/Default system (K/M/B/T)
HumanNumber.number_system(locale: :en)    #=> :default
HumanNumber.number_system(locale: :fr)    #=> :default
HumanNumber.number_system(locale: :de)    #=> :default

# East Asian system (만/억/조 or 万/億/兆)  
HumanNumber.number_system(locale: :ko)    #=> :east_asian
HumanNumber.number_system(locale: :ja)    #=> :east_asian
HumanNumber.number_system(locale: :zh)    #=> :east_asian

# Indian system (thousand/lakh/crore)
HumanNumber.number_system(locale: :hi)    #=> :indian
HumanNumber.number_system(locale: :'en-IN') #=> :indian

# Defaults to current I18n.locale when not specified
HumanNumber.number_system #=> (uses I18n.locale)

Return Values:

  • :default - Western system using K/M/B/T (thousand/million/billion/trillion)
  • :east_asian - Asian system using 만/억/조 or 万/億/兆 patterns
  • :indian - South Asian system using thousand/lakh/crore

HumanNumber.currency_number_system(currency_code:)

Determines which number system is used for a given currency code. Returns a symbol indicating the culturally appropriate formatting system for that currency's region.

# Default system currencies
HumanNumber.currency_number_system(currency_code: 'USD') #=> :default
HumanNumber.currency_number_system(currency_code: 'EUR') #=> :default
HumanNumber.currency_number_system(currency_code: 'GBP') #=> :default

# East Asian system currencies
HumanNumber.currency_number_system(currency_code: 'KRW') #=> :east_asian  # Korean Won
HumanNumber.currency_number_system(currency_code: 'JPY') #=> :east_asian  # Japanese Yen
HumanNumber.currency_number_system(currency_code: 'CNY') #=> :east_asian  # Chinese Yuan

# Indian system currencies
HumanNumber.currency_number_system(currency_code: 'INR') #=> :indian      # Indian Rupee

# Case-insensitive and handles whitespace
HumanNumber.currency_number_system(currency_code: 'krw')   #=> :east_asian
HumanNumber.currency_number_system(currency_code: ' USD ') #=> :default

Practical Usage: These methods are useful for understanding which formatting system will be applied, or for building custom formatting logic:

# Check system before formatting
system = HumanNumber.number_system(locale: :ko)
if system == :east_asian
  puts "Will use 만/억/조 units"
  result = HumanNumber.human_number(50_000, locale: :ko) #=> "5만"
end

# Currency-aware system detection
currency_system = HumanNumber.currency_number_system(currency_code: 'KRW')
locale_system = HumanNumber.number_system(locale: :ko)
puts "Consistent systems!" if currency_system == locale_system #=> true

Rails Integration

In Rails applications, helper methods are automatically available:

<%= human_number(1_234_567) %>                         <!-- 1.2M -->
<%= human_currency(1_234_567, currency_code: 'USD') %> <!-- $1M -->
<%= currency(1234.56, currency_code: 'USD') %>         <!-- $1,234.56 -->

<!-- With options -->
<%= human_number(1_234_567, max_digits: 3, locale: :ko) %> <!-- 123만 -->
<%= number_to_human_size(1_234_567) %>                     <!-- 1.2M (legacy) -->

Cultural Number Systems

Different cultures have fundamentally different concepts for large numbers:

Western System (K/M/B/T)

Used by: English, German, French, Spanish, Italian, Portuguese, Russian, Dutch, Swedish, Danish, Norwegian

HumanNumber.human_number(1_234_567, locale: :en) #=> "1.2M"
HumanNumber.human_number(1_000_000_000, locale: :en) #=> "1B"

Units: thousand (1,000), million (1,000,000), billion (1,000,000,000), trillion (1,000,000,000,000)

East Asian System (千/万/億/兆)

Used by: Korean (ko), Japanese (ja), Chinese (zh, zh-CN, zh-TW)

HumanNumber.human_number(1_234_567, locale: :ko) #=> "120만"  
HumanNumber.human_number(1_234_567, locale: :ja) #=> "120万"
HumanNumber.human_number(100_000_000, locale: :ko) #=> "1억"

# Complete mode shows cultural spacing differences
HumanNumber.human_number(12_345_678, locale: :ko, max_digits: nil) #=> "1234만 5678"  # Korean: spaces
HumanNumber.human_number(12_345_678, locale: :ja, max_digits: nil) #=> "1234万5678"   # Japanese: no spaces

Units: 만/万 (ten thousand), 억/億 (hundred million), 조/兆 (trillion)
Cultural spacing: Japanese uses no spaces between units, Korean/Chinese use spaces (configurable via locale files)

Indian System (lakh/crore)

Used by: Hindi (hi), Urdu (ur), Bengali (bn), Indian English (en-IN)

HumanNumber.human_number(100_000, locale: :hi) #=> "1 lakh"
HumanNumber.human_number(10_000_000, locale: :hi) #=> "1 crore"

Units: thousand (1,000), lakh (100,000), crore (10,000,000)

The gem automatically selects the appropriate system based on locale, ensuring cultural accuracy.

Architecture & Design Philosophy

Direct Method Design

HumanNumber uses direct class methods rather than instance-based objects for simplicity:

# Direct approach (current)
HumanNumber.human_number(1234567, locale: :ko, max_digits: 2)

# Instance approach (rejected)
formatter = HumanNumber::Formatter.new(locale: :ko, max_digits: 2)
formatter.format(1234567)

Why Direct Methods?

  • Simplicity: Clean, straightforward API
  • Thread Safety: No shared mutable state
  • Rails Compatibility: Matches Rails helper patterns

Separation of Concerns

HumanNumber uses a two-stage formatting process:

  • Number Formatting: Converts numbers to human-readable strings with cultural units
  • Currency Application: Applies currency symbols and format strings
# Internal flow for HumanNumber.human_currency(1234567, currency_code: 'USD', locale: :en)
formatted_number = Formatters::Number.format(1234567, locale: :en, max_digits: 2)     #=> "1.2M"
final_result = Formatters::Currency.format("1.2M", currency_code: 'USD', locale: :en) #=> "$1.2M"

This separation enables:

  • Independent testing of number vs currency logic
  • Reusability of number formatting for non-currency contexts
  • Maintainability when adding new currencies or number systems

Centralized Locale Logic

Currency-locale relationships are managed centrally in LocaleSupport:

CURRENCY_NATIVE_LOCALES = {
  "USD" => [:en], "EUR" => %i[de fr es it nl], 
  "KRW" => [:ko], "JPY" => [:ja]
  # ... 40+ currencies
}

This ensures:

  • Consistent precision rules: USD always shows 2 decimals, JPY shows 0
  • Smart fallbacks: When user locale doesn't match currency's native locale
  • Easy maintenance: New currencies require only one mapping entry

Configuration-Free Design

HumanNumber deliberately avoids runtime configuration in favor of explicit parameters:

# No global configuration
HumanNumber.human_number(1234567, locale: :ko, max_digits: 2)

# Application defaults handled at application level
class ApplicationController
  def format_money(amount, currency)
    HumanNumber.human_currency(amount, currency_code: currency, locale: I18n.locale, max_digits: 2)
  end
end

Benefits:

  • No shared state or thread safety concerns
  • Predictable behavior (each call is independent)
  • Easier testing (no configuration setup/teardown)

Development

After checking out the repo:

$ cd human_number
$ bundle install
$ rake spec

Run tests:

$ rake spec

Run linting:

$ rake rubocop

Run all quality checks:

$ rake  # Runs both spec and rubocop

Contributing

  • Fork it (https://github.com/ether-moon/human_number/fork)
  • Create your feature branch (git checkout -b feature/my-new-feature)
  • Commit your changes (git commit -am 'Add some feature')
  • Push to the branch (git push origin feature/my-new-feature)
  • Create a new Pull Request

Contribution Guidelines

  • Please write tests when adding new features
  • Follow RuboCop style guidelines
  • Write clear and descriptive commit messages
  • Update documentation for API changes
  • Ensure 100% test coverage for new features

Adding New Locales

To add support for a new locale:

  • Add locale to appropriate system mapping in lib/human_number/number_system.rb:
    • Update LOCALE_SYSTEM_MAPPING to include the new locale
    • If needed, add currency mapping to CURRENCY_SYSTEM_MAPPING
  • Create locale file in config/locales/ with unit translations following rails-i18n structure
  • Add currency precision mapping to CURRENCY_NATIVE_LOCALES in lib/human_number/locale_support.rb if applicable
  • Add comprehensive tests covering the new locale in both spec/human_number_spec.rb and spec/human_number/number_system_spec.rb

Example: Adding Portuguese (Brazil) support:

# In lib/human_number/number_system.rb
LOCALE_SYSTEM_MAPPING = {
  east_asian: %i[ko ja zh zh-CN zh-TW].freeze,
  indian: %i[hi ur bn en-IN].freeze,
  # Add pt-BR to default system (no entry needed)
}.freeze

# In config/locales/pt-BR.yml  
pt-BR:
  number:
    human:
      decimal_units:
        abbr_units:
          thousand: "K"
          million: "M" 
          billion: "B"
          trillion: "T"

License

MIT License. See the LICENSE file for details.

Standards Reference

This project is based on the following international standards and documentation:

Changelog

See CHANGELOG.md for details.

Issues

Please report bugs and feature requests at GitHub Issues.

FAQs

Package last updated on 25 Aug 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.