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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77b5088964964f78d79893dc7260d5e01dde9e701c62877eb19556ecc3ee460b
4
- data.tar.gz: b008d71ecdd7360bbe8bf24544f888a71fede1934a5fdaf1bb46b121b21be95e
3
+ metadata.gz: 2075e91bd27944e5725d4379471db7bda9787d7b4a0622e92b71f15d03ed9ef3
4
+ data.tar.gz: 145eddff12e1d703ed29d01f3661e674439b39ff11c3882fc52633af50e6ca09
5
5
  SHA512:
6
- metadata.gz: 9074f912c536402fadf8e489989bc475f4330fac69cd32b720c5916fa0f5a4b447a1ef4994692f0116c36def1cc84632d2e6f4529071412d4a44b01d2f10e5a1
7
- data.tar.gz: '028fab98aaf0c3ddef7f6a4af81f9c228c84bd1d2a17be241ab4834fb0c4922ca51dd1c7e49860589a54178a0c02430b2a60cb3e1f3cd460e605336a58200982'
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
- $ export FFI_ICU_LIB="icui18n.so"
22
- $ export FFI_ICU_VERSION_SUFFIX="_3_8"
23
- $ ruby -r ffi-icu program.rb
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
- match = ICU::CharDet.detect(str)
36
- match.name # => "UTF-8"
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
- detector = ICU::CharDet::Detector.new
44
- detector.detect(str) => #<struct ICU::CharDet::Detector::Match ...>
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
- ICU::Collation.collate("nb", %w[å æ ø]) == %w[æ ø å] #=> true
58
+ ICU::Collation.collate("nb", %w[å æ ø]) == %w[æ ø å] #=> true
58
59
  ```
59
60
 
60
61
  or
61
62
 
62
63
  ```ruby
63
- collator = ICU::Collation::Collator.new("nb")
64
- collator.compare("a", "b") #=> -1
65
- collator.greater?("z", "a") #=> true
66
- collator.collate(%w[å æ ø]) #=> ["æ", "ø", "å"]
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
- iterator = ICU::BreakIterator.new(:word, "en_US")
76
- iterator.text = "This is a sentence."
77
- iterator.to_a #=> [0, 4, 5, 7, 8, 9, 10, 18, 19]
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
- # class method interface
87
- ICU::NumberFormatting.format_number("en", 1_000) #=> "1,000"
88
- ICU::NumberFormatting.format_number("de-DE", 1234.56) #=> "1.234,56"
89
- ICU::NumberFormatting.format_currency("en", 123.45, 'USD') #=> "$123.45"
90
- ICU::NumberFormatting.format_percent("en", 0.53, 'USD') #=> "53%"
91
- ICU::NumberFormatting.spell("en_US", 1_000) #=> "one thousand"
92
-
93
- # reusable formatting objects
94
- numf = ICU::NumberFormatting.create('fr-CA')
95
- numf.format(1000) #=> "1 000"
96
-
97
- curf = ICU::NumberFormatting.create('en-US', :currency)
98
- curf.format(1234.56, 'USD') #=> "$1,234.56"
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
- # class method interface
108
- f = ICU::TimeFormatting.format(Time.mktime(2015, 11, 12, 15, 21, 16), {:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :short, :time => :short})
109
- f #=> "12.11.15 15:21"
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
- # reusable formatting objects
112
- formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long, :time => :none)
113
- formatter.format(Time.now) #=> "25. února 2015"
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
- # reusable formatting objects
118
- formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long, :time => :none)
119
- formatter.parse("25. února 2015") #=> Wed Feb 25 00:00:00 +0100 2015
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
- formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :date => :pattern, :time => :pattern, :skeleton => 'MMMMY')
126
- formatter.format(Time.now) #=> "únor 2015"
126
+ formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :date => :pattern, :time => :pattern, :skeleton => 'MMMMY')
127
+ formatter.format(Time.now) #=> "únor 2015"
127
128
 
128
- formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :date => :pattern, :time => :pattern, :skeleton => 'Y')
129
- formatter.format(Time.now) #=> "2015"
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
- Tested on:
143
- ==========
144
-
145
- Platforms:
146
-
147
- * OS X 10.6 - 10.10
148
- * Travis' Linux
199
+ Locale
200
+ ------
149
201
 
150
- Rubies:
202
+ Examples:
151
203
 
152
- - 2.5
153
- - 2.6
154
- - 2.7
155
- - ruby-head
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module ICU
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.2"
3
3
  end
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.0
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.3.7
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