
Security News
Risky Biz Podcast: Making Reachability Analysis Work in Real-World Codebases
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
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.
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"
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
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 systemsmax_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"
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/croreHumanNumber.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
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) -->
Different cultures have fundamentally different concepts for large numbers:
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)
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)
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.
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?
HumanNumber uses a two-stage formatting process:
# 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:
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:
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:
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
git checkout -b feature/my-new-feature
)git commit -am 'Add some feature'
)git push origin feature/my-new-feature
)To add support for a new locale:
lib/human_number/number_system.rb
:
LOCALE_SYSTEM_MAPPING
to include the new localeCURRENCY_SYSTEM_MAPPING
config/locales/
with unit translations following rails-i18n structureCURRENCY_NATIVE_LOCALES
in lib/human_number/locale_support.rb
if applicablespec/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"
MIT License. See the LICENSE file for details.
This project is based on the following international standards and documentation:
See CHANGELOG.md for details.
Please report bugs and feature requests at GitHub Issues.
FAQs
Unknown package
We found that human_number demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
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.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.
Security News
CISA’s 2025 draft SBOM guidance adds new fields like hashes, licenses, and tool metadata to make software inventories more actionable.