human_number 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +63 -0
- data/CHANGELOG.md +90 -0
- data/CLAUDE.md +219 -0
- data/Gemfile +23 -0
- data/LICENSE +21 -0
- data/README.md +323 -0
- data/Rakefile +12 -0
- data/config/locales/bn.yml +8 -0
- data/config/locales/de.yml +9 -0
- data/config/locales/en-GB.yml +10 -0
- data/config/locales/en-IN.yml +8 -0
- data/config/locales/en.yml +10 -0
- data/config/locales/es.yml +9 -0
- data/config/locales/fr.yml +9 -0
- data/config/locales/hi.yml +8 -0
- data/config/locales/ja.yml +15 -0
- data/config/locales/ko.yml +15 -0
- data/config/locales/ur.yml +8 -0
- data/config/locales/zh-CN.yml +9 -0
- data/config/locales/zh-TW.yml +9 -0
- data/config/locales/zh.yml +12 -0
- data/human_number.gemspec +42 -0
- data/lib/human_number/formatters/currency.rb +73 -0
- data/lib/human_number/formatters/number.rb +462 -0
- data/lib/human_number/locale_support.rb +125 -0
- data/lib/human_number/rails/helpers.rb +98 -0
- data/lib/human_number/railtie.rb +23 -0
- data/lib/human_number/version.rb +5 -0
- data/lib/human_number.rb +196 -0
- metadata +126 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/railtie"
|
4
|
+
|
5
|
+
module HumanNumber
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
initializer "human_number.configure_rails_initialization" do
|
8
|
+
ActiveSupport.on_load(:action_view) do
|
9
|
+
require "human_number/rails/helpers"
|
10
|
+
include HumanNumber::Rails::Helpers
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load(:action_controller) do
|
14
|
+
require "human_number/rails/helpers"
|
15
|
+
include HumanNumber::Rails::Helpers
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "human_number.load_locale_data" do
|
20
|
+
I18n.load_path += Dir[File.expand_path("../../config/locales/*.yml", __dir__)]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/human_number.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "i18n"
|
4
|
+
require "action_view"
|
5
|
+
require "rails-i18n"
|
6
|
+
|
7
|
+
# Ensure Rails I18n locales are available
|
8
|
+
I18n.load_path += Dir[File.expand_path("**/*.yml", Gem.loaded_specs["rails-i18n"].full_gem_path)]
|
9
|
+
|
10
|
+
require_relative "human_number/version"
|
11
|
+
require_relative "human_number/locale_support"
|
12
|
+
require_relative "human_number/formatters/number"
|
13
|
+
require_relative "human_number/formatters/currency"
|
14
|
+
|
15
|
+
module HumanNumber
|
16
|
+
class Error < StandardError; end
|
17
|
+
|
18
|
+
# Load locale files
|
19
|
+
I18n.load_path += Dir[File.expand_path("../config/locales/*.yml", __dir__)]
|
20
|
+
|
21
|
+
extend ActionView::Helpers::NumberHelper
|
22
|
+
|
23
|
+
class << self
|
24
|
+
# Formats a number with intelligent, locale-aware abbreviations.
|
25
|
+
#
|
26
|
+
# This method provides culturally-appropriate number formatting with automatic
|
27
|
+
# unit system selection based on locale:
|
28
|
+
# - **Western locales**: K/M/B/T (thousand/million/billion/trillion)
|
29
|
+
# - **East Asian locales**: 천/만/억/조 or 千/万/億/兆
|
30
|
+
# - **Indian locales**: thousand/lakh/crore
|
31
|
+
#
|
32
|
+
# @param number [Numeric] The number to format
|
33
|
+
# @param locale [Symbol, String] The locale for formatting (default: current I18n locale)
|
34
|
+
# Determines both number formatting rules and cultural unit systems
|
35
|
+
#
|
36
|
+
# @option options [Boolean] :abbr_units (true) Use abbreviated unit symbols (K/M/B)
|
37
|
+
# vs full words. Only affects Western locales
|
38
|
+
# @option options [Integer, nil] :max_digits (2) Maximum significant digits to display.
|
39
|
+
# When nil, displays complete number using multiple units without decimals
|
40
|
+
# @option options [Integer, nil] :min_unit (nil) Minimum unit threshold for abbreviation.
|
41
|
+
# Numbers below this won't be abbreviated
|
42
|
+
# @option options [Boolean] :trim_zeros (true) Remove trailing decimal zeros.
|
43
|
+
# Only applies in abbreviated mode (when max_digits is set)
|
44
|
+
#
|
45
|
+
# @return [String] The formatted number string
|
46
|
+
#
|
47
|
+
# @example Basic usage
|
48
|
+
# HumanNumber.human_number(1234567) #=> "1.2M"
|
49
|
+
# HumanNumber.human_number(50000, locale: :ko) #=> "5만"
|
50
|
+
#
|
51
|
+
# @example Precision control
|
52
|
+
# HumanNumber.human_number(1234567, max_digits: 2) #=> "1.2M"
|
53
|
+
# HumanNumber.human_number(1234567, max_digits: nil) #=> "1M 234K 567"
|
54
|
+
#
|
55
|
+
# @example Unit preferences
|
56
|
+
# HumanNumber.human_number(1000000, abbr_units: true) #=> "1M"
|
57
|
+
# HumanNumber.human_number(1000000, abbr_units: false) #=> "1 million"
|
58
|
+
#
|
59
|
+
# @example Minimum thresholds
|
60
|
+
# HumanNumber.human_number(5000, min_unit: 10000) #=> "5000"
|
61
|
+
# HumanNumber.human_number(50000, min_unit: 10000) #=> "5만"
|
62
|
+
#
|
63
|
+
# @example Zero trimming
|
64
|
+
# HumanNumber.human_number(1000000, trim_zeros: true) #=> "1M"
|
65
|
+
# HumanNumber.human_number(1000000, trim_zeros: false) #=> "1.0M"
|
66
|
+
#
|
67
|
+
# @note Complete mode (max_digits: nil) shows full precision across multiple units
|
68
|
+
# @see Formatters::Number.format The underlying formatter implementation
|
69
|
+
# @since 1.0.0
|
70
|
+
def human_number(number, locale: I18n.locale, **options)
|
71
|
+
validate_number_input!(number)
|
72
|
+
validate_locale!(locale)
|
73
|
+
|
74
|
+
final_options = Formatters::Number.default_options.merge(options)
|
75
|
+
|
76
|
+
Formatters::Number.format(number, locale:, **final_options)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Formats a currency amount using standard Rails currency formatting.
|
80
|
+
#
|
81
|
+
# This method applies currency-specific precision rules (e.g., 2 decimals for USD,
|
82
|
+
# 0 decimals for JPY/KRW) based on the currency's native locale, ensuring
|
83
|
+
# consistent precision regardless of the display locale.
|
84
|
+
#
|
85
|
+
# @param number [Numeric] The amount to format
|
86
|
+
# @param currency_code [String] ISO 4217 currency code (e.g., 'USD', 'EUR', 'KRW', 'JPY')
|
87
|
+
# @param locale [Symbol, String] Display locale for formatting rules
|
88
|
+
#
|
89
|
+
# @return [String] The formatted currency string
|
90
|
+
#
|
91
|
+
# @example Standard currency formatting
|
92
|
+
# HumanNumber.currency(1234.56, currency_code: 'USD', locale: :en) #=> "$1,234.56"
|
93
|
+
# HumanNumber.currency(50000, currency_code: 'KRW', locale: :ko) #=> "50,000원"
|
94
|
+
# HumanNumber.currency(1234.99, currency_code: 'JPY', locale: :ja) #=> "1,235円"
|
95
|
+
#
|
96
|
+
# @example Cross-locale precision consistency
|
97
|
+
# # USD always shows 2 decimals regardless of display locale
|
98
|
+
# HumanNumber.currency(1234.56, currency_code: 'USD', locale: :ko) #=> "$1,234.56"
|
99
|
+
#
|
100
|
+
# # JPY always shows 0 decimals regardless of display locale
|
101
|
+
# HumanNumber.currency(1234.56, currency_code: 'JPY', locale: :en) #=> "1,235円"
|
102
|
+
#
|
103
|
+
# @note Precision is determined by currency's native locale, not display locale
|
104
|
+
# @see Formatters::Number.format_with_currency_precision Currency precision logic
|
105
|
+
# @see Formatters::Currency.format Currency symbol and format application
|
106
|
+
# @since 1.0.0
|
107
|
+
def currency(number, currency_code:, locale:)
|
108
|
+
validate_number_input!(number)
|
109
|
+
validate_currency_code!(currency_code)
|
110
|
+
validate_locale!(locale)
|
111
|
+
|
112
|
+
formatted_number = Formatters::Number.format_with_currency_precision(number, currency_code:, locale:)
|
113
|
+
Formatters::Currency.format(formatted_number, currency_code:, locale:)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Formats a currency amount with intelligent, locale-aware abbreviations.
|
117
|
+
#
|
118
|
+
# This method combines human-readable number formatting with currency symbols,
|
119
|
+
# providing culturally-appropriate abbreviations while maintaining currency
|
120
|
+
# symbol and format conventions.
|
121
|
+
#
|
122
|
+
# @param number [Numeric] The amount to format
|
123
|
+
# @param currency_code [String] ISO 4217 currency code (e.g., 'USD', 'EUR', 'KRW', 'JPY')
|
124
|
+
# @param locale [Symbol, String] Locale for formatting and unit system selection
|
125
|
+
#
|
126
|
+
# @option options [Boolean] :abbr_units (true) Use abbreviated unit symbols (K/M/B)
|
127
|
+
# vs full words. Only affects Western locales
|
128
|
+
# @option options [Integer, nil] :max_digits (2) Maximum significant digits to display.
|
129
|
+
# When nil, displays complete amount using multiple units without decimals
|
130
|
+
# @option options [Integer, nil] :min_unit (nil) Minimum unit threshold for abbreviation.
|
131
|
+
# Amounts below this won't be abbreviated
|
132
|
+
# @option options [Boolean] :trim_zeros (true) Remove trailing decimal zeros.
|
133
|
+
# Only applies in abbreviated mode (when max_digits is set)
|
134
|
+
#
|
135
|
+
# @return [String] The formatted currency string with abbreviations
|
136
|
+
#
|
137
|
+
# @example Basic usage
|
138
|
+
# HumanNumber.human_currency(1234567, currency_code: 'USD', locale: :en) #=> "$1.2M"
|
139
|
+
# HumanNumber.human_currency(50000, currency_code: 'KRW', locale: :ko) #=> "5만원"
|
140
|
+
#
|
141
|
+
# @example Precision control
|
142
|
+
# HumanNumber.human_currency(1234567, currency_code: 'USD', locale: :en, max_digits: 2) #=> "$1.23M"
|
143
|
+
# HumanNumber.human_currency(1234567, currency_code: 'USD', locale: :en, max_digits: nil) #=> "$1M 234K 567"
|
144
|
+
#
|
145
|
+
# @example Unit preferences
|
146
|
+
# HumanNumber.human_currency(1000000, currency_code: 'USD', locale: :en, abbr_units: true) #=> "$1M"
|
147
|
+
# HumanNumber.human_currency(1000000, currency_code: 'USD', locale: :en, abbr_units: false) #=> "$1 million"
|
148
|
+
#
|
149
|
+
# @example Cultural unit systems
|
150
|
+
# HumanNumber.human_currency(12345678, currency_code: 'JPY', locale: :ja) #=> "1234.6万円"
|
151
|
+
# HumanNumber.human_currency(10000000, currency_code: 'INR', locale: :hi) #=> "1 crore₹"
|
152
|
+
#
|
153
|
+
# @note Combines human number formatting with currency-specific symbol placement
|
154
|
+
# @see #human_number For detailed number formatting options
|
155
|
+
# @see #currency For standard currency formatting without abbreviations
|
156
|
+
# @since 1.0.0
|
157
|
+
def human_currency(number, currency_code:, locale:, **options)
|
158
|
+
validate_number_input!(number)
|
159
|
+
validate_currency_code!(currency_code)
|
160
|
+
validate_locale!(locale)
|
161
|
+
|
162
|
+
final_options = Formatters::Number.default_currency_number_options.merge(options)
|
163
|
+
|
164
|
+
formatted_number = Formatters::Number.format(number, locale:, **final_options)
|
165
|
+
Formatters::Currency.format(formatted_number, currency_code:, locale:)
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
# Input validation methods - minimal validation for stability
|
171
|
+
def validate_number_input!(number)
|
172
|
+
# Only check for the most basic issues that would cause runtime errors
|
173
|
+
return if number.respond_to?(:to_f)
|
174
|
+
|
175
|
+
raise ArgumentError, "Number must be numeric, got #{number.class}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def validate_currency_code!(currency_code)
|
179
|
+
# Basic validation only - let downstream handle specifics
|
180
|
+
return unless currency_code.nil? || (currency_code.respond_to?(:empty?) && currency_code.empty?)
|
181
|
+
|
182
|
+
raise ArgumentError, "Currency code cannot be nil or empty"
|
183
|
+
end
|
184
|
+
|
185
|
+
def validate_locale!(locale)
|
186
|
+
# Very minimal validation - just ensure it's not something that would break
|
187
|
+
return if locale.nil?
|
188
|
+
|
189
|
+
return if locale.respond_to?(:to_sym)
|
190
|
+
|
191
|
+
raise ArgumentError, "Locale must be convertible to symbol, got #{locale.class}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
require_relative "human_number/railtie" if defined?(Rails::Railtie)
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: human_number
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Your Name
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: actionview
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '5.2'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '5.2'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: i18n
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.6'
|
33
|
+
- - "<"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '2'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.6'
|
43
|
+
- - "<"
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rails-i18n
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '5.1'
|
53
|
+
type: :runtime
|
54
|
+
prerelease: false
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '5.1'
|
60
|
+
description: HumanNumber implements accurate number formatting based on international
|
61
|
+
standards including Microsoft Globalization documentation and Unicode CLDR. Provides
|
62
|
+
human-readable number formats with precise locale-specific decimal separators, thousand
|
63
|
+
separators, currency formats, and cultural number conventions.
|
64
|
+
email:
|
65
|
+
- your.email@example.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- ".rspec"
|
71
|
+
- ".rubocop.yml"
|
72
|
+
- CHANGELOG.md
|
73
|
+
- CLAUDE.md
|
74
|
+
- Gemfile
|
75
|
+
- LICENSE
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- config/locales/bn.yml
|
79
|
+
- config/locales/de.yml
|
80
|
+
- config/locales/en-GB.yml
|
81
|
+
- config/locales/en-IN.yml
|
82
|
+
- config/locales/en.yml
|
83
|
+
- config/locales/es.yml
|
84
|
+
- config/locales/fr.yml
|
85
|
+
- config/locales/hi.yml
|
86
|
+
- config/locales/ja.yml
|
87
|
+
- config/locales/ko.yml
|
88
|
+
- config/locales/ur.yml
|
89
|
+
- config/locales/zh-CN.yml
|
90
|
+
- config/locales/zh-TW.yml
|
91
|
+
- config/locales/zh.yml
|
92
|
+
- human_number.gemspec
|
93
|
+
- lib/human_number.rb
|
94
|
+
- lib/human_number/formatters/currency.rb
|
95
|
+
- lib/human_number/formatters/number.rb
|
96
|
+
- lib/human_number/locale_support.rb
|
97
|
+
- lib/human_number/rails/helpers.rb
|
98
|
+
- lib/human_number/railtie.rb
|
99
|
+
- lib/human_number/version.rb
|
100
|
+
homepage: https://github.com/yourusername/human_number
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata:
|
104
|
+
allowed_push_host: https://rubygems.org
|
105
|
+
homepage_uri: https://github.com/yourusername/human_number
|
106
|
+
source_code_uri: https://github.com/yourusername/human_number
|
107
|
+
changelog_uri: https://github.com/yourusername/human_number/blob/main/CHANGELOG.md
|
108
|
+
rubygems_mfa_required: 'true'
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 3.1.0
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubygems_version: 3.6.9
|
124
|
+
specification_version: 4
|
125
|
+
summary: International standard-based number formatting for Ruby applications
|
126
|
+
test_files: []
|