number_helper.rb 24.9 KB
Newer Older
1
require 'active_support/core_ext/big_decimal/conversions'
J
Jeremy Kemper 已提交
2 3
require 'active_support/core_ext/float/rounding'

4
module ActionView
5
  module Helpers #:nodoc:
6

7
    # Provides methods for converting numbers into formatted strings.
8
    # Methods are provided for phone numbers, currency, percentage,
9 10 11 12
    # precision, positional notation, file size and pretty printing.
    #
    # Most methods expect a +number+ argument, and will return it
    # unchanged if can't be converted into a valid number.
13
    module NumberHelper
14 15 16 17 18 19 20 21 22 23

      # Raised when argument +number+ param given to the helpers is invalid and
      # the option :raise is set to  +true+.
      class InvalidNumberError < StandardError
        attr_accessor :number
        def initialize(number)
          @number = number
        end
      end

24
      # Formats a +number+ into a US phone number (e.g., (555) 123-9876). You can customize the format
25
      # in the +options+ hash.
26 27
      #
      # ==== Options
28
      # * <tt>:area_code</tt>  - Adds parentheses around the area code.
29
      # * <tt>:delimiter</tt>  - Specifies the delimiter to use (defaults to "-").
30
      # * <tt>:extension</tt>  - Specifies an extension to add to the end of the
31
      #   generated number.
32 33
      # * <tt>:country_code</tt>  - Sets the country code for the phone number.
      #
34
      # ==== Examples
35
      #  number_to_phone(5551234)                                           # => 555-1234
36 37 38 39 40 41
      #  number_to_phone(1235551234)                                        # => 123-555-1234
      #  number_to_phone(1235551234, :area_code => true)                    # => (123) 555-1234
      #  number_to_phone(1235551234, :delimiter => " ")                     # => 123 555 1234
      #  number_to_phone(1235551234, :area_code => true, :extension => 555) # => (123) 555-1234 x 555
      #  number_to_phone(1235551234, :country_code => 1)                    # => +1-123-555-1234
      #
42
      #  number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
43
      #  => +1.123.555.1234 x 1343
44
      def number_to_phone(number, options = {})
45 46
        return nil if number.nil?

47 48 49 50 51 52 53 54 55 56 57
        begin
          Float(number)
          is_number_html_safe = true
        rescue ArgumentError, TypeError
          if options[:raise]
            raise InvalidNumberError, number
          else
            is_number_html_safe = number.to_s.html_safe?
          end
        end

58
        number       = number.to_s.strip
59 60 61 62 63
        options      = options.symbolize_keys
        area_code    = options[:area_code] || nil
        delimiter    = options[:delimiter] || "-"
        extension    = options[:extension].to_s.strip || nil
        country_code = options[:country_code] || nil
64

65 66 67 68 69 70 71
        str = ""
        str << "+#{country_code}#{delimiter}" unless country_code.blank?
        str << if area_code
          number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
        else
          number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
          number.starts_with?('-') ? number.slice!(1..-1) : number
72
        end
73
        str << " x #{extension}" unless extension.blank?
74
        is_number_html_safe ? str.html_safe : str
75 76
      end

77
      # Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
78 79
      # in the +options+ hash.
      #
80 81
      # ==== Options
      # * <tt>:precision</tt>  -  Sets the level of precision (defaults to 2).
82
      # * <tt>:unit</tt>       - Sets the denomination of the currency (defaults to "$").
83 84
      # * <tt>:separator</tt>  - Sets the separator between the units (defaults to ".").
      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to ",").
85
      # * <tt>:format</tt>     - Sets the format of the output string (defaults to "%u%n"). The field types are:
86 87 88
      #
      #     %u  The currency unit
      #     %n  The number
89 90 91 92 93 94
      #
      # ==== Examples
      #  number_to_currency(1234567890.50)                    # => $1,234,567,890.50
      #  number_to_currency(1234567890.506)                   # => $1,234,567,890.51
      #  number_to_currency(1234567890.506, :precision => 3)  # => $1,234,567,890.506
      #
95
      #  number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "")
96
      #  # => &pound;1234567890,50
97 98
      #  number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "", :format => "%n %u")
      #  # => 1234567890,50 &pound;
99
      def number_to_currency(number, options = {})
100 101
        return nil if number.nil?

102 103
        options.symbolize_keys!

104 105
        defaults  = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
        currency  = I18n.translate(:'number.currency.format', :locale => options[:locale], :default => {})
106
        defaults  = defaults.merge(currency)
107

108
        options = options.reverse_merge(defaults)
109

110 111 112
        unit      = options.delete(:unit)
        format    = options.delete(:format)

113 114
        begin
          value = number_with_precision(number, options.merge(:raise => true))
115
          format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
116 117 118 119 120 121 122
        rescue InvalidNumberError => e
          if options[:raise]
            raise
          else
            formatted_number = format.gsub(/%n/, e.number).gsub(/%u/, unit)
            e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
          end
123
        end
124

125 126
      end

127
      # Formats a +number+ as a percentage string (e.g., 65%). You can customize the
128 129
      # format in the +options+ hash.
      #
130
      # ==== Options
131 132 133
      # * <tt>:precision</tt>  - Sets the precision of the number (defaults to 3).
      # * <tt>:significant</tt>  - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
      # * <tt>:separator</tt>  - Sets the separator between the fractional and integer digits (defaults to ".").
134
      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to "").
135
      # * <tt>:strip_unsignificant_zeros</tt>  - If +true+ removes unsignificant zeros after the decimal separator (defaults to +false+)
136 137
      #
      # ==== Examples
138 139 140 141
      #  number_to_percentage(100)                                        # => 100.000%
      #  number_to_percentage(100, :precision => 0)                       # => 100%
      #  number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
      #  number_to_percentage(302.24398923423, :precision => 5)           # => 302.24399%
142
      def number_to_percentage(number, options = {})
143 144
        return nil if number.nil?

145 146
        options.symbolize_keys!

147 148
        defaults   = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
        percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :default => {})
149
        defaults  = defaults.merge(percentage)
150

151
        options = options.reverse_merge(defaults)
152

153 154 155 156 157 158 159 160 161
        begin
          "#{number_with_precision(number, options.merge(:raise => true))}%".html_safe
        rescue InvalidNumberError => e
          if options[:raise]
            raise
          else
            e.number.to_s.html_safe? ? "#{e.number}%".html_safe : "#{e.number}%"
          end
        end
162 163
      end

164 165
      # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
      # customize the format in the +options+ hash.
166
      #
167
      # ==== Options
168
      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to ",").
169
      # * <tt>:separator</tt>  - Sets the separator between the fractional and integer digits (defaults to ".").
170 171
      #
      # ==== Examples
172 173 174
      #  number_with_delimiter(12345678)                        # => 12,345,678
      #  number_with_delimiter(12345678.05)                     # => 12,345,678.05
      #  number_with_delimiter(12345678, :delimiter => ".")     # => 12.345.678
P
Pratik Naik 已提交
175
      #  number_with_delimiter(12345678, :separator => ",")     # => 12,345,678
176
      #  number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
177
      #  # => 98 765 432,98
178 179 180 181
      #
      # You can still use <tt>number_with_delimiter</tt> with the old API that accepts the
      # +delimiter+ as its optional second and the +separator+ as its
      # optional third parameter:
182
      #  number_with_delimiter(12345678, " ")                     # => 12 345 678
183
      #  number_with_delimiter(12345678.05, ".", ",")             # => 12.345.678,05
184 185
      def number_with_delimiter(number, *args)
        options = args.extract_options!
186 187
        options.symbolize_keys!

188 189 190 191 192 193 194 195 196 197
        begin
          Float(number)
        rescue ArgumentError, TypeError
          if options[:raise]
            raise InvalidNumberError, number
          else
            return number
          end
        end

198
        defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
199

200
        unless args.empty?
201 202
          ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' +
            'instead of separate delimiter and precision arguments.', caller)
203 204
          options[:delimiter] ||= args[0] if args[0]
          options[:separator] ||= args[1] if args[1]
205
        end
206

207
        options = options.reverse_merge(defaults)
208

209
        parts = number.to_s.split('.')
210 211
        parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
        parts.join(options[:separator]).html_safe
212

213
      end
214

215 216
      # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision
      # of 2 if +:significant+ is +false+, and 5 if +:significant+ is +true+).
217 218 219
      # You can customize the format in the +options+ hash.
      #
      # ==== Options
220 221 222
      # * <tt>:precision</tt>  - Sets the precision of the number (defaults to 3).
      # * <tt>:significant</tt>  - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
      # * <tt>:separator</tt>  - Sets the separator between the fractional and integer digits (defaults to ".").
223
      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to "").
224
      # * <tt>:strip_unsignificant_zeros</tt>  - If +true+ removes unsignificant zeros after the decimal separator (defaults to +false+)
225
      #
226
      # ==== Examples
227 228 229 230 231 232 233 234 235 236
      #  number_with_precision(111.2345)                                            # => 111.235
      #  number_with_precision(111.2345, :precision => 2)                           # => 111.23
      #  number_with_precision(13, :precision => 5)                                 # => 13.00000
      #  number_with_precision(389.32314, :precision => 0)                          # => 389
      #  number_with_precision(111.2345, :significant => true)                      # => 111
      #  number_with_precision(111.2345, :precision => 1, :significant => true)     # => 100
      #  number_with_precision(13, :precision => 5, :significant => true)           # => 13.000
      #  number_with_precision(13, :precision => 5, :significant => true, strip_unsignificant_zeros => true)
      #  # => 13
      #  number_with_precision(389.32314, :precision => 4, :significant => true)    # => 389.3
237 238
      #  number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
      #  # => 1.111,23
239 240 241
      #
      # You can still use <tt>number_with_precision</tt> with the old API that accepts the
      # +precision+ as its optional second parameter:
242
      #   number_with_precision(111.2345, 2)   # => 111.23
243
      def number_with_precision(number, *args)
244 245 246 247

        options = args.extract_options!
        options.symbolize_keys!

248 249 250
        number = begin
          Float(number)
        rescue ArgumentError, TypeError
251 252 253 254 255
          if options[:raise]
            raise InvalidNumberError, number
          else
            return number
          end
256 257 258 259
        end

        defaults           = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
        precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale], :default => {})
260
        defaults           = defaults.merge(precision_defaults)
261

262
        #Backwards compatibility
263
        unless args.empty?
264 265
          ActiveSupport::Deprecation.warn('number_with_precision takes an option hash ' +
            'instead of a separate precision argument.', caller)
266
          options[:precision] ||= args[0] if args[0]
267
        end
268

269 270 271 272
        options = options.reverse_merge(defaults)  # Allow the user to unset default values: Eg.: :significant => false
        precision = options.delete :precision
        significant = options.delete :significant
        strip_unsignificant_zeros = options.delete :strip_unsignificant_zeros
273

274 275 276 277 278 279 280
        if significant and precision > 0
          digits = (Math.log10(number) + 1).floor
          rounded_number = BigDecimal.new((number / 10 ** (digits - precision)).to_s).round.to_f * 10 ** (digits - precision)
          precision = precision - digits
          precision = precision > 0 ? precision : 0  #don't let it be negative
        else
          rounded_number = BigDecimal.new((number * (10 ** precision)).to_s).round.to_f / 10 ** precision
281
        end
282 283 284
        formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
        if strip_unsignificant_zeros
          escaped_separator = Regexp.escape(options[:separator])
285
          formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '').html_safe
286
        else
287
          formatted_number
288
        end
289

290
      end
291

292 293
      STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze

294
      # Formats the bytes in +number+ into a more understandable representation
295
      # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
296
      # reporting file sizes to users. You can customize the
297 298
      # format in the +options+ hash.
      #
299 300
      # See <tt>number_to_human</tt> if you want to pretty-print a generic number.
      #
301
      # ==== Options
302 303 304
      # * <tt>:precision</tt>  - Sets the precision of the number (defaults to 3).
      # * <tt>:significant</tt>  - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
      # * <tt>:separator</tt>  - Sets the separator between the fractional and integer digits (defaults to ".").
305
      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to "").
306
      # * <tt>:strip_unsignificant_zeros</tt>  - If +true+ removes unsignificant zeros after the decimal separator (defaults to +true+)
307
      # ==== Examples
308
      #  number_to_human_size(123)                                          # => 123 Bytes
309
      #  number_to_human_size(1234)                                         # => 1.21 KB
310
      #  number_to_human_size(12345)                                        # => 12.1 KB
311 312 313 314 315 316
      #  number_to_human_size(1234567)                                      # => 1.18 MB
      #  number_to_human_size(1234567890)                                   # => 1.15 GB
      #  number_to_human_size(1234567890123)                                # => 1.12 TB
      #  number_to_human_size(1234567, :precision => 2)                     # => 1.2 MB
      #  number_to_human_size(483989, :precision => 2)                      # => 470 KB
      #  number_to_human_size(1234567, :precision => 2, :separator => ',')  # => 1,2 MB
317
      #
318 319 320 321
      # Unsignificant zeros after the fractional separator are stripped out by default (set
      # <tt>:strip_unsignificant_zeros</tt> to +false+ to change that):
      #  number_to_human_size(1234567890123, :precision => 5)        # => "1.1229 TB"
      #  number_to_human_size(524288000, :precision=>5)              # => "500 MB"
322
      #
323 324
      # You can still use <tt>number_to_human_size</tt> with the old API that accepts the
      # +precision+ as its optional second parameter:
325 326
      #  number_to_human_size(1234567, 1)    # => 1 MB
      #  number_to_human_size(483989, 2)     # => 470 KB
327
      def number_to_human_size(number, *args)
328 329 330
        options = args.extract_options!
        options.symbolize_keys!

331 332 333
        number = begin
          Float(number)
        rescue ArgumentError, TypeError
334 335 336 337 338
          if options[:raise]
            raise InvalidNumberError, number
          else
            return number
          end
339
        end
340

341 342
        defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
        human    = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
343 344
        defaults = defaults.merge(human)

345
        unless args.empty?
346 347
          ActiveSupport::Deprecation.warn('number_to_human_size takes an option hash ' +
            'instead of a separate precision argument.', caller)
348
          options[:precision] ||= args[0] if args[0]
349
        end
350

351 352 353
        options = options.reverse_merge(defaults)
        #for backwards compatibility with those that didn't add strip_unsignificant_zeros to their locale files
        options[:strip_unsignificant_zeros] = true if not options.key?(:strip_unsignificant_zeros)
354

355
        storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
356

357 358
        if number.to_i < 1024
          unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
359
          storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit).html_safe
360 361 362 363 364 365 366 367 368
        else
          max_exp  = STORAGE_UNITS.size - 1
          exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
          exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
          number  /= 1024 ** exponent

          unit_key = STORAGE_UNITS[exponent]
          unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)

369
          formatted_number = number_with_precision(number, options)
370
          storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).html_safe
371
        end
372
      end
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450

      DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
        -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze

      # Pretty prints (formats and approximates) a number in a way it is more readable by humans
      # (eg.: 1200000000 becomes "1.2 Billion"). This is useful for numbers that
      # can get very large (and too hard to read).
      #
      # See <tt>number_to_human_size</tt> if you want to print a file size.
      #
      # You can also define you own unit-quantifier names if you want to use other decimal units
      # (eg.: 1500 becomes "1.5 kilometers", 0.150 becomes "150 mililiters", etc). You may define
      # a wide range of unit quantifiers, even fractional ones (centi, deci, mili, etc).
      #
      # ==== Options
      # * <tt>:precision</tt>  - Sets the precision of the number (defaults to 3).
      # * <tt>:significant</tt>  - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
      # * <tt>:separator</tt>  - Sets the separator between the fractional and integer digits (defaults to ".").
      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to "").
      # * <tt>:strip_unsignificant_zeros</tt>  - If +true+ removes unsignificant zeros after the decimal separator (defaults to +true+)
      # * <tt>:units</tt> - A Hash of unit quantifier names. Or a string containing an i18n scope where to find this hash. It might have the following keys:
      #   * *integers*: <tt>:unit</tt>, <tt>:ten</tt>, <tt>:hundred</tt>, <tt>:thousand</tt>,  <tt>:million</tt>,  <tt>:billion</tt>, <tt>:trillion</tt>, <tt>:quadrillion</tt>
      #   * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>, <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>, <tt>:pico</tt>, <tt>:femto</tt>
      # * <tt>:format</tt> - Sets the format of the output string (defaults to "%n %u"). The field types are:
      #
      #     %u  The quantifier (ex.: 'thousand')
      #     %n  The number
      #
      # ==== Examples
      #  number_to_human(123)                                          # => "123"
      #  number_to_human(1234)                                         # => "1.23 Thousand"
      #  number_to_human(12345)                                        # => "12.3 Thousand"
      #  number_to_human(1234567)                                      # => "1.23 Million"
      #  number_to_human(1234567890)                                   # => "1.23 Billion"
      #  number_to_human(1234567890123)                                # => "1.23 Trillion"
      #  number_to_human(1234567890123456)                             # => "1.23 Quadrillion"
      #  number_to_human(1234567890123456789)                          # => "1230 Quadrillion"
      #  number_to_human(489939, :precision => 2)                      # => "490 Thousand"
      #  number_to_human(489939, :precision => 4)                      # => "489.9 Thousand"
      #  number_to_human(1234567, :precision => 4,
      #                           :significant => false)               # => "1.2346 Million"
      #  number_to_human(1234567, :precision => 1,
      #                           :separator => ',',
      #                           :significant => false)               # => "1,2 Million"
      #
      # Unsignificant zeros after the decimal separator are stripped out by default (set
      # <tt>:strip_unsignificant_zeros</tt> to +false+ to change that):
      #  number_to_human(12345012345, :significant_digits => 6)       # => "12.345 Billion"
      #  number_to_human(500000000, :precision=>5)                    # => "500 Million"
      #
      # ==== Custom Unit Quantifiers
      #
      # You can also use your own custom unit quantifiers:
      #  number_to_human(500000, :units => {:unit => "ml", :thousand => "lt"})  # => "500 lt"
      #
      # If in your I18n locale you have:
      #   distance:
      #     centi:
      #       one: "centimeter"
      #       other: "centimeters"
      #     unit:
      #       one: "meter"
      #       other: "meters"
      #     thousand:
      #       one: "kilometer"
      #       other: "kilometers"
      #     billion: "gazilion-distance"
      #
      # Then you could do:
      #
      #  number_to_human(543934, :units => :distance)                              # => "544 kilometers"
      #  number_to_human(54393498, :units => :distance)                            # => "54400 kilometers"
      #  number_to_human(54393498000, :units => :distance)                         # => "54.4 gazilion-distance"
      #  number_to_human(343, :units => :distance, :precision => 1)                # => "300 meters"
      #  number_to_human(1, :units => :distance)                                   # => "1 meter"
      #  number_to_human(0.34, :units => :distance)                                # => "34 centimeters"
      #
      def number_to_human(number, options = {})
451 452
        options.symbolize_keys!

453 454 455
        number = begin
          Float(number)
        rescue ArgumentError, TypeError
456 457 458 459 460
          if options[:raise]
            raise InvalidNumberError, number
          else
            return number
          end
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
        end

        defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
        human    = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
        defaults = defaults.merge(human)

        options = options.reverse_merge(defaults)
        #for backwards compatibility with those that didn't add strip_unsignificant_zeros to their locale files
        options[:strip_unsignificant_zeros] = true if not options.key?(:strip_unsignificant_zeros)

        units = options.delete :units
        unit_exponents = case units
        when Hash
          units
        when String, Symbol
          I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
        when nil
          I18n.translate(:"number.human.decimal_units.units", :locale => options[:locale], :raise => true)
        else
          raise ArgumentError, ":units must be a Hash or String translation scope."
        end.keys.map{|e_name| DECIMAL_UNITS.invert[e_name] }.sort_by{|e| -e}

        number_exponent = Math.log10(number).floor
        display_exponent = unit_exponents.find{|e| number_exponent >= e }
        number  /= 10 ** display_exponent

        unit = case units
        when Hash
          units[DECIMAL_UNITS[display_exponent]]
        when String, Symbol
          I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
        else
          I18n.translate(:"number.human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
        end

        decimal_format = options[:format] || I18n.translate(:'number.human.decimal_units.format', :locale => options[:locale], :default => "%n %u")
        formatted_number = number_with_precision(number, options)
498
        decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip.html_safe
499 500
      end

501 502
    end
  end
503
end