ffi-icu 0.5.0 → 0.5.2
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/README.md +113 -57
- data/lib/ffi-icu/lib.rb +4 -0
- data/lib/ffi-icu/locale.rb +24 -0
- data/lib/ffi-icu/time_formatting.rb +1 -1
- data/lib/ffi-icu/version.rb +1 -1
- data/spec/locale_spec.rb +6 -0
- data/spec/time_spec.rb +6 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2075e91bd27944e5725d4379471db7bda9787d7b4a0622e92b71f15d03ed9ef3
|
4
|
+
data.tar.gz: 145eddff12e1d703ed29d01f3661e674439b39ff11c3882fc52633af50e6ca09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d63a1b1f93622d80abbc93889fa0e8eea90fa81025de1b4c5bf672a65d8922809100fa55ccc55a3c42599d2b2dfd3c2d69774601f512308783b66c26e9b6a71
|
7
|
+
data.tar.gz: 99fa27ab152f6f52a62d71a850e201ac61149c2c86b3fbb92fd2ff86f1ec942f19394af43e4068338d6a11121cdaadab9d53509da0d93750efeb1d54b960556d
|
data/README.md
CHANGED
@@ -18,9 +18,11 @@ ICU.
|
|
18
18
|
If you get messages that the library or functions are not found, you can
|
19
19
|
set some environment variables to tell ffi-icu where to find it, e.g.:
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
```sh
|
22
|
+
$ export FFI_ICU_LIB="icui18n.so"
|
23
|
+
$ export FFI_ICU_VERSION_SUFFIX="_3_8"
|
24
|
+
$ ruby -r ffi-icu program.rb
|
25
|
+
```
|
24
26
|
|
25
27
|
Features
|
26
28
|
========
|
@@ -31,17 +33,16 @@ Character Encoding Detection
|
|
31
33
|
Examples:
|
32
34
|
|
33
35
|
```ruby
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
match.confidence # => 80
|
36
|
+
match = ICU::CharDet.detect(str)
|
37
|
+
match.name # => "UTF-8"
|
38
|
+
match.confidence # => 80
|
38
39
|
```
|
39
40
|
|
40
41
|
or
|
41
42
|
|
42
43
|
```ruby
|
43
|
-
|
44
|
-
|
44
|
+
detector = ICU::CharDet::Detector.new
|
45
|
+
detector.detect(str) => #<struct ICU::CharDet::Detector::Match ...>
|
45
46
|
```
|
46
47
|
|
47
48
|
Why not just use rchardet?
|
@@ -54,16 +55,16 @@ Locale Sensitive Collation
|
|
54
55
|
Examples:
|
55
56
|
|
56
57
|
```ruby
|
57
|
-
|
58
|
+
ICU::Collation.collate("nb", %w[å æ ø]) == %w[æ ø å] #=> true
|
58
59
|
```
|
59
60
|
|
60
61
|
or
|
61
62
|
|
62
63
|
```ruby
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
collator = ICU::Collation::Collator.new("nb")
|
65
|
+
collator.compare("a", "b") #=> -1
|
66
|
+
collator.greater?("z", "a") #=> true
|
67
|
+
collator.collate(%w[å æ ø]) #=> ["æ", "ø", "å"]
|
67
68
|
```
|
68
69
|
|
69
70
|
Text Boundary Analysis
|
@@ -72,9 +73,9 @@ Text Boundary Analysis
|
|
72
73
|
Examples:
|
73
74
|
|
74
75
|
```ruby
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
iterator = ICU::BreakIterator.new(:word, "en_US")
|
77
|
+
iterator.text = "This is a sentence."
|
78
|
+
iterator.to_a #=> [0, 4, 5, 7, 8, 9, 10, 18, 19]
|
78
79
|
```
|
79
80
|
|
80
81
|
Number/Currency Formatting
|
@@ -83,50 +84,107 @@ Number/Currency Formatting
|
|
83
84
|
Examples:
|
84
85
|
|
85
86
|
```ruby
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
87
|
+
# class method interface
|
88
|
+
ICU::NumberFormatting.format_number("en", 1_000) #=> "1,000"
|
89
|
+
ICU::NumberFormatting.format_number("de-DE", 1234.56) #=> "1.234,56"
|
90
|
+
ICU::NumberFormatting.format_currency("en", 123.45, 'USD') #=> "$123.45"
|
91
|
+
ICU::NumberFormatting.format_percent("en", 0.53, 'USD') #=> "53%"
|
92
|
+
ICU::NumberFormatting.spell("en_US", 1_000) #=> "one thousand"
|
93
|
+
|
94
|
+
# reusable formatting objects
|
95
|
+
numf = ICU::NumberFormatting.create('fr-CA')
|
96
|
+
numf.format(1000) #=> "1 000"
|
97
|
+
|
98
|
+
curf = ICU::NumberFormatting.create('en-US', :currency)
|
99
|
+
curf.format(1234.56, 'USD') #=> "$1,234.56"
|
99
100
|
```
|
100
101
|
|
101
102
|
Time Formatting/Parsing
|
102
|
-
|
103
|
+
-----------------------
|
103
104
|
|
104
105
|
Examples:
|
105
106
|
|
106
107
|
```ruby
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
# class method interface
|
109
|
+
f = ICU::TimeFormatting.format(Time.mktime(2015, 11, 12, 15, 21, 16), {:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :short, :time => :short})
|
110
|
+
f #=> "12.11.15 15:21"
|
110
111
|
|
111
|
-
|
112
|
-
|
113
|
-
|
112
|
+
# reusable formatting objects
|
113
|
+
formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long, :time => :none)
|
114
|
+
formatter.format(Time.now) #=> "25. února 2015"
|
114
115
|
```
|
115
116
|
|
116
117
|
```ruby
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
# reusable formatting objects
|
119
|
+
formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long, :time => :none)
|
120
|
+
formatter.parse("25. února 2015") #=> Wed Feb 25 00:00:00 +0100 2015
|
120
121
|
```
|
121
122
|
|
122
123
|
For skeleton formatting, visit the [Unicode date field symbol table](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#date-field-symbol-table) page to help find the pattern characters to use.
|
123
124
|
|
124
125
|
```ruby
|
125
|
-
|
126
|
-
|
126
|
+
formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :date => :pattern, :time => :pattern, :skeleton => 'MMMMY')
|
127
|
+
formatter.format(Time.now) #=> "únor 2015"
|
127
128
|
|
128
|
-
|
129
|
-
|
129
|
+
formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :date => :pattern, :time => :pattern, :skeleton => 'Y')
|
130
|
+
formatter.format(Time.now) #=> "2015"
|
131
|
+
```
|
132
|
+
|
133
|
+
Duration Formatting
|
134
|
+
-------------------
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
# What the various styles look like
|
138
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :long)
|
139
|
+
formatter.format({hours: 8, minutes: 40, seconds: 35}) #=> "8 hours, 40 minutes, 35 seconds"
|
140
|
+
|
141
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :short)
|
142
|
+
formatter.format({hours: 8, minutes: 40, seconds: 35}) #=> "8 hrs, 40 mins, 35 secs"
|
143
|
+
|
144
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :narrow)
|
145
|
+
formatter.format({hours: 8, minutes: 40, seconds: 35}) #=> "8h 40min. 35s."
|
146
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :digital)
|
147
|
+
formatter.format({hours: 8, minutes: 40, seconds: 35}) #=> "8:40:35"
|
148
|
+
|
149
|
+
# How digital & non-digital formats deal with units > hours
|
150
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :narrow)
|
151
|
+
formatter.format({days: 2, hours: 8, minutes: 40, seconds: 35}) #=> "2d 8h 40min. 35s."
|
152
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :digital)
|
153
|
+
formatter.format({days: 2, hours: 8, minutes: 40, seconds: 35}) #=> "2d 8:40:35"
|
154
|
+
|
155
|
+
# Missing or zero parts are omitted
|
156
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :long)
|
157
|
+
formatter.format({days: 2, minutes: 40, seconds:0}) #=> "2 days, 40 minutes"
|
158
|
+
|
159
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :digital)
|
160
|
+
formatter.format({hours: 2, minutes: 40}) #=> "2:40"
|
161
|
+
|
162
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :digital)
|
163
|
+
formatter.format({minutes: 40, seconds: 7}) #=> "40:07"
|
164
|
+
|
165
|
+
# Sub-second parts are folded into seconds for digital display
|
166
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :digital)
|
167
|
+
formatter.format({hours: 5, minutes: 7, seconds: 23, milliseconds: 98, microseconds: 997}) #=> "5:07:23.098997"
|
168
|
+
|
169
|
+
# Zero-extension of sub-second parts in digital style
|
170
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :digital)
|
171
|
+
formatter.format({hours: 5, minutes: 7, seconds: 23, milliseconds: 400}) #=> "5:07:23.400"
|
172
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :digital)
|
173
|
+
formatter.format({hours: 5, minutes: 7, seconds: 23, milliseconds: 400, microseconds: 700}) #=> "5:07:23.400700"
|
174
|
+
|
175
|
+
# All fractional parts except the last are truncated
|
176
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'en-AU', style: :long)
|
177
|
+
formatter.format({days: 2, hours: 7.3, minutes: 40.9, seconds:0.43}) #=> "2 days, 7 hours, 40 minutes, 0.43 seconds"
|
178
|
+
|
179
|
+
# With RU locale
|
180
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'ru', style: :long)
|
181
|
+
formatter.format({hours: 1, minutes: 2, seconds: 3}) #=> "1 час 2 минуты 3 секунды"
|
182
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'ru', style: :long)
|
183
|
+
formatter.format({hours: 10, minutes: 20, seconds: 30}) #=> "10 часов 20 минут 30 секунд"
|
184
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'ru', style: :narrow)
|
185
|
+
formatter.format({hours: 1, minutes: 2, seconds: 3}) #=> "1 ч 2 мин 3 с"
|
186
|
+
formatter = ICU::DurationFormatting::DurationFormatter.new(locale: 'ru', style: :narrow)
|
187
|
+
formatter.format({hours: 10, minutes: 20, seconds: 30}) #=> "10 ч 20 мин 30 с"
|
130
188
|
```
|
131
189
|
|
132
190
|
Transliteration
|
@@ -136,23 +194,21 @@ Example:
|
|
136
194
|
|
137
195
|
```ruby
|
138
196
|
ICU::Transliteration.transliterate('Traditional-Simplified', '沈從文') # => "沈从文"
|
139
|
-
|
140
197
|
```
|
141
198
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
Platforms:
|
146
|
-
|
147
|
-
* OS X 10.6 - 10.10
|
148
|
-
* Travis' Linux
|
199
|
+
Locale
|
200
|
+
------
|
149
201
|
|
150
|
-
|
202
|
+
Examples:
|
151
203
|
|
152
|
-
|
153
|
-
|
154
|
-
-
|
155
|
-
|
204
|
+
```ruby
|
205
|
+
locale = ICU::Locale.new('en-US')
|
206
|
+
locale.display_country('en-US') #=> "United States"
|
207
|
+
locale.display_language('es') #=> "inglés"
|
208
|
+
locale.display_name('es') #=> "inglés (Estados Unidos)"
|
209
|
+
locale.display_name_with_context('en-US', [:length_short]) #=> "English (US)"
|
210
|
+
locale.display_name_with_context('en-US', [:length_long]) #=> "English (United States)"
|
211
|
+
```
|
156
212
|
|
157
213
|
TODO:
|
158
214
|
=====
|
data/lib/ffi-icu/lib.rb
CHANGED
@@ -500,5 +500,9 @@ module ICU
|
|
500
500
|
attach_function :ucal_setDefaultTimeZone, "ucal_setDefaultTimeZone#{suffix}", [:pointer, :pointer], :int32_t
|
501
501
|
attach_function :ucal_getDefaultTimeZone, "ucal_getDefaultTimeZone#{suffix}", [:pointer, :int32_t, :pointer], :int32_t
|
502
502
|
|
503
|
+
# ULocaleDisplayNames
|
504
|
+
attach_function :uldn_openForContext, "uldn_openForContext#{suffix}", [:string, :pointer, :int32_t, :pointer], :pointer
|
505
|
+
attach_function :uldn_localeDisplayName, "uldn_localeDisplayName#{suffix}", [:pointer, :string, :pointer, :int32_t, :pointer], :int32_t
|
506
|
+
attach_function :uldn_close, "uldn_close#{suffix}", [:pointer], :void
|
503
507
|
end # Lib
|
504
508
|
end # ICU
|
data/lib/ffi-icu/locale.rb
CHANGED
@@ -42,6 +42,11 @@ module ICU
|
|
42
42
|
|
43
43
|
attr_reader :id
|
44
44
|
|
45
|
+
DISPLAY_CONTEXT = {
|
46
|
+
length_full: 512, # UDISPCTX_LENGTH_FULL = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 0
|
47
|
+
length_short: 513 # UDISPCTX_LENGTH_SHORT = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 1
|
48
|
+
}
|
49
|
+
|
45
50
|
def initialize(id)
|
46
51
|
@id = id.to_s
|
47
52
|
end
|
@@ -96,6 +101,16 @@ module ICU
|
|
96
101
|
end
|
97
102
|
end
|
98
103
|
|
104
|
+
def display_name_with_context(locale, contexts = [])
|
105
|
+
contexts = DISPLAY_CONTEXT.select { |context| contexts.include?(context) }.values
|
106
|
+
|
107
|
+
with_locale_display_name(@id, contexts) do |locale_display_names|
|
108
|
+
Lib::Util.read_uchar_buffer(256) do |buffer, status|
|
109
|
+
Lib.uldn_localeDisplayName(locale_display_names, locale, buffer, buffer.size, status)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
99
114
|
def display_script(locale = nil)
|
100
115
|
locale = locale.to_s unless locale.nil?
|
101
116
|
|
@@ -220,5 +235,14 @@ module ICU
|
|
220
235
|
|
221
236
|
Locale.new(result)
|
222
237
|
end
|
238
|
+
|
239
|
+
def with_locale_display_name(locale, contexts)
|
240
|
+
pointer = FFI::MemoryPointer.new(:int, contexts.length).write_array_of_int(contexts)
|
241
|
+
locale_display_names = ICU::Lib.check_error { |status| ICU::Lib.uldn_openForContext(locale, pointer, contexts.length, status) }
|
242
|
+
|
243
|
+
yield locale_display_names
|
244
|
+
ensure
|
245
|
+
Lib.uldn_close(locale_display_names) if locale_display_names
|
246
|
+
end
|
223
247
|
end
|
224
248
|
end
|
@@ -275,7 +275,7 @@ module ICU
|
|
275
275
|
resolved_hour_cycle = @hour_cycle == :locale ? Locale.new(@locale).keyword('hours') : @hour_cycle
|
276
276
|
|
277
277
|
if HOUR_CYCLE_SYMS.keys.include?(resolved_hour_cycle)
|
278
|
-
new_pattern_str.gsub!(/[hHkK]/, HOUR_CYCLE_SYMS[resolved_hour_cycle])
|
278
|
+
new_pattern_str.gsub!(/[hHkK](?=(?:[^\']|\'[^\']*\')*$)/, HOUR_CYCLE_SYMS[resolved_hour_cycle])
|
279
279
|
end
|
280
280
|
|
281
281
|
# Finally, set the new pattern onto the date time formatter
|
data/lib/ffi-icu/version.rb
CHANGED
data/spec/locale_spec.rb
CHANGED
@@ -123,6 +123,11 @@ module ICU
|
|
123
123
|
expect(Locale.new('zh_CH').display_name('fr')).to eq('chinois (Suisse)')
|
124
124
|
end
|
125
125
|
|
126
|
+
it 'returns the name using display context' do
|
127
|
+
expect(Locale.new('en_US').display_name_with_context('en_HK', [:length_full])).to eq('English (Hong Kong SAR China)')
|
128
|
+
expect(Locale.new('en_US').display_name_with_context('en_HK', [:length_short])).to eq('English (Hong Kong)')
|
129
|
+
end
|
130
|
+
|
126
131
|
it 'returns the script' do
|
127
132
|
expect(Locale.new('ja_Hira_JP').display_script('en')).to eq('Hiragana')
|
128
133
|
expect(Locale.new('ja_Hira_JP').display_script('ru')).to eq('хирагана')
|
@@ -140,6 +145,7 @@ module ICU
|
|
140
145
|
expect(Locale.new('en_VI').display_country('ccp')).to_not be_nil
|
141
146
|
expect(Locale.new('yue_Hant').display_language('ccp')).to_not be_nil
|
142
147
|
expect(Locale.new('en_VI').display_name('ccp')).to_not be_nil
|
148
|
+
expect(Locale.new('ccp').display_name_with_context('en_VI')).to_not be_nil
|
143
149
|
expect(Locale.new('yue_Hant').display_script('ccp')).to_not be_nil
|
144
150
|
expect(Locale.new('en_US_POSIX').display_variant('sl')).to_not be_nil
|
145
151
|
end
|
data/spec/time_spec.rb
CHANGED
@@ -173,6 +173,12 @@ module ICU
|
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
|
+
it 'for lang=fi hour_cycle=h12' do
|
177
|
+
t = Time.new(2021, 04, 01, 13, 05, 0, "+00:00")
|
178
|
+
str = TimeFormatting.format(t, locale: 'fi', zone: 'America/Los_Angeles', date: :long, time: :short, hour_cycle: 'h12')
|
179
|
+
expect(str).to match(/\sklo\s/)
|
180
|
+
end
|
181
|
+
|
176
182
|
it 'works with defaults on a h12 locale' do
|
177
183
|
t = Time.new(2021, 04, 01, 13, 05, 0, "+00:00")
|
178
184
|
str = TimeFormatting.format(t, time: :short, date: :none, locale: 'en_AU', zone: 'UTC', hour_cycle: :locale)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-icu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jari Bakken
|
@@ -130,8 +130,22 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
132
|
requirements: []
|
133
|
-
rubygems_version: 3.
|
133
|
+
rubygems_version: 3.4.10
|
134
134
|
signing_key:
|
135
135
|
specification_version: 4
|
136
136
|
summary: Simple Ruby FFI wrappers for things I need from ICU.
|
137
|
-
test_files:
|
137
|
+
test_files:
|
138
|
+
- spec/break_iterator_spec.rb
|
139
|
+
- spec/chardet_spec.rb
|
140
|
+
- spec/collation_spec.rb
|
141
|
+
- spec/duration_formatting_spec.rb
|
142
|
+
- spec/lib/version_info_spec.rb
|
143
|
+
- spec/lib_spec.rb
|
144
|
+
- spec/locale_spec.rb
|
145
|
+
- spec/normalization_spec.rb
|
146
|
+
- spec/normalizer_spec.rb
|
147
|
+
- spec/number_formatting_spec.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
- spec/time_spec.rb
|
150
|
+
- spec/transliteration_spec.rb
|
151
|
+
- spec/uchar_spec.rb
|