ordinalize_full 3.0.0 → 3.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 +4 -4
- data/.rubocop.yml +3 -18
- data/README.md +5 -3
- data/Rakefile +1 -8
- data/lib/ordinalize_full/locales/en.yml +71 -101
- data/lib/ordinalize_full.rb +102 -31
- data/ordinalize_full.gemspec +2 -3
- data/spec/ordinalize_full_spec.rb +11 -3
- metadata +3 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 141ca2978cba4542c3753808f1a4e7097c44b4c5d2baa6648a8a8f097c50e4d0
|
|
4
|
+
data.tar.gz: 1345bbea85ce31a8c67341003bad1ebe32695fbbb488d1d7528f36df1bd57b51
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d071d17ee79d77eb2254ab950867f27ed7a267ee3f26e108572ce85d9746e3433d2812cb0651e6a5b682617ae8da4dfa2e99da1f843d04a8f97005e9eed68b82
|
|
7
|
+
data.tar.gz: 7eafd8f37d55f00e8dba46ea12fc12ca8fd7ee99f18f6c54ec47d6cf0edd66a7b1949d1aae18bb92a124638b9765e57dee09a25a30c1e48e77cb829b6b143c08
|
data/.rubocop.yml
CHANGED
|
@@ -6,32 +6,17 @@ AllCops:
|
|
|
6
6
|
TargetRubyVersion: 3.0
|
|
7
7
|
NewCops: enable
|
|
8
8
|
|
|
9
|
+
Gemspec/DevelopmentDependencies:
|
|
10
|
+
Enabled: false
|
|
11
|
+
|
|
9
12
|
Metrics:
|
|
10
13
|
Enabled: false
|
|
11
14
|
|
|
12
15
|
Layout/AccessModifierIndentation:
|
|
13
16
|
EnforcedStyle: outdent
|
|
14
17
|
|
|
15
|
-
Layout/CaseIndentation:
|
|
16
|
-
Enabled: false
|
|
17
|
-
|
|
18
|
-
Layout/EndAlignment:
|
|
19
|
-
Enabled: false
|
|
20
|
-
|
|
21
|
-
Layout/EmptyLinesAroundArguments:
|
|
22
|
-
Enabled: false
|
|
23
|
-
|
|
24
|
-
Layout/LineLength:
|
|
25
|
-
Enabled: false
|
|
26
|
-
|
|
27
18
|
Style/Alias:
|
|
28
19
|
EnforcedStyle: prefer_alias_method
|
|
29
20
|
|
|
30
|
-
Style/Documentation:
|
|
31
|
-
Enabled: false
|
|
32
|
-
|
|
33
21
|
Style/StringLiterals:
|
|
34
22
|
EnforcedStyle: double_quotes
|
|
35
|
-
|
|
36
|
-
Style/SpecialGlobalVars:
|
|
37
|
-
Enabled: false
|
data/README.md
CHANGED
|
@@ -8,9 +8,8 @@ Like Rails' [ordinalize](http://api.rubyonrails.org/classes/ActiveSupport/Inflec
|
|
|
8
8
|
Features:
|
|
9
9
|
|
|
10
10
|
- i18n support
|
|
11
|
-
- doesn't monkey-
|
|
11
|
+
- doesn't require monkey-patching
|
|
12
12
|
- easy to integrate with Rails but doesn't require Rails
|
|
13
|
-
- less than 50 lines of code
|
|
14
13
|
|
|
15
14
|
## Usage
|
|
16
15
|
|
|
@@ -49,6 +48,9 @@ MyIntegerLikeClass.new.ordinalize_in_full #=> "forty second"
|
|
|
49
48
|
|
|
50
49
|
## Limitations
|
|
51
50
|
|
|
52
|
-
- only works up to 100
|
|
51
|
+
- only works up to 100 for non-English languages
|
|
53
52
|
- locales only available in English, French, Italian, Spanish, and Dutch (pull requests welcome!)
|
|
54
53
|
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
`SPDX-License-Identifier: MIT`
|
data/Rakefile
CHANGED
|
@@ -10,11 +10,4 @@ end
|
|
|
10
10
|
require "rubocop/rake_task"
|
|
11
11
|
RuboCop::RakeTask.new
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
Cane::RakeTask.new do |t|
|
|
15
|
-
t.no_doc = true
|
|
16
|
-
t.style_measure = 120
|
|
17
|
-
t.abc_max = 20
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
task default: %i[spec rubocop cane]
|
|
13
|
+
task default: %i[spec rubocop]
|
|
@@ -1,102 +1,72 @@
|
|
|
1
1
|
en:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
n_71: seventy first
|
|
74
|
-
n_72: seventy second
|
|
75
|
-
n_73: seventy third
|
|
76
|
-
n_74: seventy fourth
|
|
77
|
-
n_75: seventy fifth
|
|
78
|
-
n_76: seventy sixth
|
|
79
|
-
n_77: seventy seventh
|
|
80
|
-
n_78: seventy eighth
|
|
81
|
-
n_79: seventy ninth
|
|
82
|
-
n_80: eightieth
|
|
83
|
-
n_81: eighty first
|
|
84
|
-
n_82: eighty second
|
|
85
|
-
n_83: eighty third
|
|
86
|
-
n_84: eighty fourth
|
|
87
|
-
n_85: eighty fifth
|
|
88
|
-
n_86: eighty sixth
|
|
89
|
-
n_87: eighty seventh
|
|
90
|
-
n_88: eighty eighth
|
|
91
|
-
n_89: eighty ninth
|
|
92
|
-
n_90: ninetieth
|
|
93
|
-
n_91: ninety first
|
|
94
|
-
n_92: ninety second
|
|
95
|
-
n_93: ninety third
|
|
96
|
-
n_94: ninety fourth
|
|
97
|
-
n_95: ninety fifth
|
|
98
|
-
n_96: ninety sixth
|
|
99
|
-
n_97: ninety seventh
|
|
100
|
-
n_98: ninety eighth
|
|
101
|
-
n_99: ninety ninth
|
|
102
|
-
n_100: one hundredth
|
|
2
|
+
long_form:
|
|
3
|
+
negative: negative
|
|
4
|
+
simple:
|
|
5
|
+
ones:
|
|
6
|
+
n_0: zero
|
|
7
|
+
n_1: one
|
|
8
|
+
n_2: two
|
|
9
|
+
n_3: three
|
|
10
|
+
n_4: four
|
|
11
|
+
n_5: five
|
|
12
|
+
n_6: six
|
|
13
|
+
n_7: seven
|
|
14
|
+
n_8: eight
|
|
15
|
+
n_9: nine
|
|
16
|
+
n_10: ten
|
|
17
|
+
n_11: eleven
|
|
18
|
+
n_12: twelve
|
|
19
|
+
n_13: thirteen
|
|
20
|
+
n_14: fourteen
|
|
21
|
+
n_15: fifteen
|
|
22
|
+
n_16: sixteen
|
|
23
|
+
n_17: seventeen
|
|
24
|
+
n_18: eighteen
|
|
25
|
+
n_19: nineteen
|
|
26
|
+
tens:
|
|
27
|
+
n_2: twenty
|
|
28
|
+
n_3: thirty
|
|
29
|
+
n_4: forty
|
|
30
|
+
n_5: fifty
|
|
31
|
+
n_6: sixty
|
|
32
|
+
n_7: seventy
|
|
33
|
+
n_8: eighty
|
|
34
|
+
n_9: ninety
|
|
35
|
+
hundred: hundred
|
|
36
|
+
larger:
|
|
37
|
+
n_0: ""
|
|
38
|
+
n_1: thousand
|
|
39
|
+
n_2: million
|
|
40
|
+
n_3: billion
|
|
41
|
+
n_4: trillion
|
|
42
|
+
ordinal:
|
|
43
|
+
ones:
|
|
44
|
+
n_0: zeroth
|
|
45
|
+
n_1: first
|
|
46
|
+
n_2: second
|
|
47
|
+
n_3: third
|
|
48
|
+
n_4: fourth
|
|
49
|
+
n_5: fifth
|
|
50
|
+
n_6: sixth
|
|
51
|
+
n_7: seventh
|
|
52
|
+
n_8: eighth
|
|
53
|
+
n_9: ninth
|
|
54
|
+
n_10: tenth
|
|
55
|
+
n_11: eleventh
|
|
56
|
+
n_12: twelfth
|
|
57
|
+
n_13: thirteenth
|
|
58
|
+
n_14: fourteenth
|
|
59
|
+
n_15: fifteenth
|
|
60
|
+
n_16: sixteenth
|
|
61
|
+
n_17: seventeenth
|
|
62
|
+
n_18: eigthteenth
|
|
63
|
+
n_19: nineteenth
|
|
64
|
+
tens:
|
|
65
|
+
n_2: twentieth
|
|
66
|
+
n_3: thirtieth
|
|
67
|
+
n_4: fortieth
|
|
68
|
+
n_5: fiftieth
|
|
69
|
+
n_6: sixtieth
|
|
70
|
+
n_7: seventieth
|
|
71
|
+
n_8: eightieth
|
|
72
|
+
n_9: ninetieth
|
data/lib/ordinalize_full.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "i18n"
|
|
4
4
|
|
|
5
|
+
# Main module
|
|
5
6
|
module OrdinalizeFull
|
|
6
7
|
I18n.load_path += Dir[File.join(__dir__, "ordinalize_full/locales/*.yml")]
|
|
7
8
|
|
|
@@ -35,7 +36,11 @@ module OrdinalizeFull
|
|
|
35
36
|
|
|
36
37
|
value
|
|
37
38
|
else
|
|
38
|
-
|
|
39
|
+
begin
|
|
40
|
+
integer_to_long_form_ordinal
|
|
41
|
+
rescue ArgumentError
|
|
42
|
+
I18n.t("ordinalize_full.n_#{self}", throw: true)
|
|
43
|
+
end
|
|
39
44
|
end
|
|
40
45
|
rescue ArgumentError
|
|
41
46
|
raise NotImplementedError, "Unknown locale #{I18n.locale}"
|
|
@@ -47,40 +52,106 @@ private
|
|
|
47
52
|
|
|
48
53
|
def ordinalize_in_short(gender: :masculine, plurality: :singular)
|
|
49
54
|
abs_number = to_i.abs
|
|
50
|
-
suffix =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
suffix = \
|
|
56
|
+
case I18n.locale
|
|
57
|
+
when :en
|
|
58
|
+
if (11..13).cover?(abs_number % 100)
|
|
59
|
+
"th"
|
|
60
|
+
else
|
|
61
|
+
case abs_number % 10
|
|
62
|
+
when 1 then "st"
|
|
63
|
+
when 2 then "nd"
|
|
64
|
+
when 3 then "rd"
|
|
65
|
+
else "th"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
when :fr
|
|
69
|
+
self == 1 ? "er" : "ème"
|
|
70
|
+
when :it
|
|
71
|
+
"°"
|
|
72
|
+
when :nl
|
|
73
|
+
[8, 1, 0].include?(self % 100) || self % 100 > 19 ? "ste" : "de"
|
|
74
|
+
when :es
|
|
75
|
+
value = ordinalize_in_full(gender: gender, plurality: plurality)
|
|
76
|
+
|
|
77
|
+
if value.end_with?("er")
|
|
78
|
+
".ᵉʳ"
|
|
79
|
+
elsif value.end_with?("a")
|
|
80
|
+
".ᵃ"
|
|
81
|
+
elsif value.end_with?("o")
|
|
82
|
+
".ᵒ"
|
|
83
|
+
elsif value.end_with?("os")
|
|
84
|
+
".ᵒˢ"
|
|
85
|
+
elsif value.end_with?("as")
|
|
86
|
+
".ᵃˢ"
|
|
60
87
|
end
|
|
61
88
|
end
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
|
|
90
|
+
[self, suffix].join
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Build the long form of a given number. In this context, it is used for every digit except the last two.
|
|
94
|
+
# @param number [Integer] - A number to write in long form
|
|
95
|
+
# @return [String] - The long form of a number
|
|
96
|
+
def number_to_word(number)
|
|
97
|
+
if number.zero?
|
|
98
|
+
I18n.t("long_form.simple.ones.n_0", throw: true)
|
|
99
|
+
elsif number < 20
|
|
100
|
+
I18n.t("long_form.simple.ones.n_#{number}", throw: true)
|
|
101
|
+
elsif number < 100
|
|
102
|
+
[
|
|
103
|
+
I18n.t("long_form.simple.tens.n_#{number / 10}", throw: true),
|
|
104
|
+
(number % 10).zero? ? "" : "-#{I18n.t("long_form.simple.ones.n_#{number % 10}", throw: true)}"
|
|
105
|
+
].join
|
|
106
|
+
elsif number < 1000
|
|
107
|
+
[
|
|
108
|
+
I18n.t("long_form.simple.ones.n_#{number / 100}", throw: true),
|
|
109
|
+
" ",
|
|
110
|
+
I18n.t("long_form.simple.hundred", throw: true),
|
|
111
|
+
(number % 100).zero? ? "" : " and #{number_to_word(number % 100)}"
|
|
112
|
+
].join
|
|
113
|
+
else
|
|
114
|
+
i = 0
|
|
115
|
+
meow = number
|
|
116
|
+
while meow >= 1000
|
|
117
|
+
meow /= 1000
|
|
118
|
+
i += 1
|
|
81
119
|
end
|
|
120
|
+
the_one_tenth = number % ((10**(3 * i)))
|
|
121
|
+
[
|
|
122
|
+
number_to_word(meow),
|
|
123
|
+
" ",
|
|
124
|
+
i.positive? ? I18n.t("long_form.simple.larger.n_#{i}", throw: true) : "",
|
|
125
|
+
the_one_tenth.zero? ? "" : " #{number_to_word(the_one_tenth)}"
|
|
126
|
+
].join
|
|
82
127
|
end
|
|
128
|
+
end
|
|
83
129
|
|
|
84
|
-
|
|
130
|
+
# Builds the ordinalized version of a number. Specific to english. Really only the two least significant digits are
|
|
131
|
+
# ordinalized, and the rest are just in long format
|
|
132
|
+
# @param number [Integer] - A number to ordinalize in long form
|
|
133
|
+
# @return [String] - The long form of a number as an ordinal
|
|
134
|
+
def number_to_ordinal_word(number)
|
|
135
|
+
if number.zero?
|
|
136
|
+
I18n.t("long_form.ordinal.ones.n_0", throw: true)
|
|
137
|
+
elsif number < 20
|
|
138
|
+
I18n.t("long_form.ordinal.ones.n_#{number}", throw: true)
|
|
139
|
+
elsif number < 100 && (number % 10).zero?
|
|
140
|
+
I18n.t("long_form.ordinal.tens.n_#{number / 10}", throw: true)
|
|
141
|
+
elsif number < 100
|
|
142
|
+
"#{number_to_word(number - (number % 10))}-#{I18n.t("long_form.ordinal.ones.n_#{number % 10}", throw: true)}"
|
|
143
|
+
elsif number >= 100 && (number % 100).zero?
|
|
144
|
+
"#{number_to_word(number - (number % 100))}th"
|
|
145
|
+
else
|
|
146
|
+
"#{number_to_word(number - (number % 100))} and #{number_to_ordinal_word((number % 100))}"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Builds the long form ordinal of an integer
|
|
151
|
+
# @return [String] - The long form ordinal of an integer (including negative)
|
|
152
|
+
def integer_to_long_form_ordinal
|
|
153
|
+
return "#{I18n.t('long_form.negative', throw: true)} #{number_to_ordinal_word(-self)}" if negative?
|
|
154
|
+
|
|
155
|
+
number_to_ordinal_word(self)
|
|
85
156
|
end
|
|
86
157
|
end
|
data/ordinalize_full.gemspec
CHANGED
|
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = "ordinalize_full"
|
|
8
|
-
spec.version = "3.
|
|
8
|
+
spec.version = "3.1.0"
|
|
9
9
|
spec.authors = ["Cédric Félizard"]
|
|
10
10
|
spec.email = ["cedric@felizard.fr"]
|
|
11
11
|
spec.summary = "Turns a number into an ordinal string such as first, second, third or 1st, 2nd, 3rd."
|
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.homepage = "https://github.com/infertux/ordinalize_full"
|
|
14
14
|
spec.license = "MIT"
|
|
15
15
|
|
|
16
|
-
spec.files = `git ls-files`.split($/)
|
|
16
|
+
spec.files = `git ls-files`.split($/) # rubocop:disable Style/SpecialGlobalVars
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
18
|
spec.require_paths = ["lib"]
|
|
19
19
|
|
|
@@ -22,7 +22,6 @@ Gem::Specification.new do |spec|
|
|
|
22
22
|
spec.add_dependency "i18n", "~> 1.8"
|
|
23
23
|
|
|
24
24
|
spec.add_development_dependency "bundler", ">= 1.5"
|
|
25
|
-
spec.add_development_dependency "cane"
|
|
26
25
|
spec.add_development_dependency "rake"
|
|
27
26
|
spec.add_development_dependency "rspec", "~> 3"
|
|
28
27
|
spec.add_development_dependency "rubocop"
|
|
@@ -8,10 +8,18 @@ describe OrdinalizeFull do
|
|
|
8
8
|
before { I18n.locale = :en }
|
|
9
9
|
|
|
10
10
|
specify { expect(1.ordinalize_in_full).to eq("first") }
|
|
11
|
-
specify { expect(42.ordinalize_in_full).to eq("forty
|
|
11
|
+
specify { expect(42.ordinalize_in_full).to eq("forty-second") }
|
|
12
|
+
specify { expect(2023.ordinalize_in_full).to eq("two thousand and twenty-third") }
|
|
13
|
+
specify { expect(2_147_483_647.ordinalize_in_full).to eq("two billion one hundred and forty-seven million four hundred and eighty-three thousand six hundred and forty-seventh") } # rubocop:disable Layout/LineLength
|
|
14
|
+
|
|
15
|
+
it "converts any number" do
|
|
16
|
+
10.times do
|
|
17
|
+
expect { rand(1_000_000).ordinalize_in_full }.not_to raise_error
|
|
18
|
+
end
|
|
19
|
+
end
|
|
12
20
|
|
|
13
|
-
it "
|
|
14
|
-
expect
|
|
21
|
+
it "handles negatives too" do
|
|
22
|
+
expect(-273.ordinalize_in_full).to eq("negative two hundred and seventy-third")
|
|
15
23
|
end
|
|
16
24
|
end
|
|
17
25
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ordinalize_full
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cédric Félizard
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-09-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: i18n
|
|
@@ -38,20 +38,6 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '1.5'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: cane
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
41
|
- !ruby/object:Gem::Dependency
|
|
56
42
|
name: rake
|
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -167,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
167
153
|
- !ruby/object:Gem::Version
|
|
168
154
|
version: '0'
|
|
169
155
|
requirements: []
|
|
170
|
-
rubygems_version: 3.
|
|
156
|
+
rubygems_version: 3.4.10
|
|
171
157
|
signing_key:
|
|
172
158
|
specification_version: 4
|
|
173
159
|
summary: Turns a number into an ordinal string such as first, second, third or 1st,
|