ffi-icu 0.6.1 → 0.8.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/Gemfile +2 -3
- data/README.md +308 -107
- data/Rakefile +6 -10
- data/lib/ffi-icu/break_iterator.rb +24 -5
- data/lib/ffi-icu/collation.rb +20 -15
- data/lib/ffi-icu/currency.rb +123 -0
- data/lib/ffi-icu/duration_formatting.rb +2 -26
- data/lib/ffi-icu/lib/util.rb +1 -1
- data/lib/ffi-icu/lib.rb +82 -24
- data/lib/ffi-icu/list_formatting.rb +52 -0
- data/lib/ffi-icu/locale.rb +1 -0
- data/lib/ffi-icu/message_formatting.rb +217 -0
- data/lib/ffi-icu/normalization.rb +5 -5
- data/lib/ffi-icu/normalizer.rb +11 -12
- data/lib/ffi-icu/number_formatting.rb +44 -3
- data/lib/ffi-icu/time_formatting.rb +12 -12
- data/lib/ffi-icu/transliteration.rb +14 -10
- data/lib/ffi-icu/uchar.rb +23 -0
- data/lib/ffi-icu/version.rb +1 -1
- data/lib/ffi-icu.rb +3 -0
- metadata +4 -1
|
@@ -3,28 +3,28 @@
|
|
|
3
3
|
module ICU
|
|
4
4
|
module Normalization
|
|
5
5
|
def self.normalize(input, mode = :default)
|
|
6
|
-
input_length = input.size
|
|
7
6
|
needed_length = out_length = options = 0
|
|
8
|
-
|
|
7
|
+
input_pointer = UCharPointer.from_utf8(input)
|
|
8
|
+
input_length = input_pointer.length_in_uchars
|
|
9
9
|
out_ptr = UCharPointer.new(out_length)
|
|
10
10
|
|
|
11
11
|
retried = false
|
|
12
12
|
|
|
13
13
|
begin
|
|
14
14
|
Lib.check_error do |error|
|
|
15
|
-
needed_length = Lib.unorm_normalize(
|
|
15
|
+
needed_length = Lib.unorm_normalize(input_pointer, input_length, mode, options, out_ptr, out_length, error)
|
|
16
16
|
end
|
|
17
17
|
rescue BufferOverflowError
|
|
18
18
|
raise(BufferOverflowError, "needed: #{needed_length}") if retried
|
|
19
19
|
|
|
20
|
-
out_ptr = out_ptr.resized_to(needed_length)
|
|
21
20
|
out_length = needed_length + 1
|
|
21
|
+
out_ptr = out_ptr.resized_to(out_length)
|
|
22
22
|
|
|
23
23
|
retried = true
|
|
24
24
|
retry
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
out_ptr.
|
|
27
|
+
out_ptr.utf8_string(needed_length)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
data/lib/ffi-icu/normalizer.rb
CHANGED
|
@@ -11,39 +11,38 @@ module ICU
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def normalize(input)
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
input_pointer = UCharPointer.from_utf8(input)
|
|
15
|
+
input_length = input_pointer.length_in_uchars
|
|
16
16
|
needed_length = capacity = 0
|
|
17
17
|
out_ptr = UCharPointer.new(needed_length)
|
|
18
18
|
|
|
19
19
|
retried = false
|
|
20
20
|
begin
|
|
21
21
|
Lib.check_error do |error|
|
|
22
|
-
needed_length = Lib.unorm2_normalize(@instance,
|
|
22
|
+
needed_length = Lib.unorm2_normalize(@instance, input_pointer, input_length, out_ptr, capacity, error)
|
|
23
23
|
end
|
|
24
24
|
rescue BufferOverflowError
|
|
25
25
|
raise(BufferOverflowError, "needed: #{needed_length}") if retried
|
|
26
26
|
|
|
27
|
-
capacity = needed_length
|
|
28
|
-
out_ptr = out_ptr.resized_to(
|
|
27
|
+
capacity = needed_length + 1
|
|
28
|
+
out_ptr = out_ptr.resized_to(capacity)
|
|
29
29
|
|
|
30
30
|
retried = true
|
|
31
31
|
retry
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
out_ptr.
|
|
34
|
+
out_ptr.utf8_string(needed_length)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
def normalized?(input)
|
|
38
|
+
input_pointer = UCharPointer.from_utf8(input)
|
|
39
|
+
input_length = input_pointer.length_in_uchars
|
|
40
40
|
|
|
41
41
|
Lib.check_error do |error|
|
|
42
|
-
Lib.unorm2_isNormalized(@instance,
|
|
42
|
+
Lib.unorm2_isNormalized(@instance, input_pointer, input_length, error)
|
|
43
43
|
end
|
|
44
|
-
|
|
45
|
-
result
|
|
46
44
|
end
|
|
45
|
+
alias normailzed? normalized?
|
|
47
46
|
|
|
48
47
|
def is_normalized?(input) # rubocop:disable Naming/PredicatePrefix
|
|
49
48
|
Warning.warn('is_normalized? is deprecated and will be removed after v0.7. Please use normalized? instead.')
|
|
@@ -15,6 +15,10 @@ module ICU
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def self.create_with_skeleton(locale, skeleton)
|
|
19
|
+
SkeletonFormatter.new(locale, skeleton)
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
def self.clear_default_options
|
|
19
23
|
@default_options.clear
|
|
20
24
|
end
|
|
@@ -35,6 +39,10 @@ module ICU
|
|
|
35
39
|
create(locale, :currency, options).format(number, currency)
|
|
36
40
|
end
|
|
37
41
|
|
|
42
|
+
def self.format_with_skeleton(locale, number, skeleton)
|
|
43
|
+
create_with_skeleton(locale, skeleton).format(number)
|
|
44
|
+
end
|
|
45
|
+
|
|
38
46
|
def self.spell(locale, number, options = {})
|
|
39
47
|
create(locale, :spellout, options).format(number)
|
|
40
48
|
end
|
|
@@ -48,6 +56,11 @@ module ICU
|
|
|
48
56
|
private
|
|
49
57
|
|
|
50
58
|
def make_formatter(type, locale)
|
|
59
|
+
if type == :pattern_rule_based
|
|
60
|
+
raise(ArgumentError, 'The pattern_rule_based style needs custom rules, which ffi-icu does not support; ' \
|
|
61
|
+
'ICU::NumberFormatting.create_with_skeleton covers most other formats')
|
|
62
|
+
end
|
|
63
|
+
|
|
51
64
|
ptr = Lib.check_error do |error|
|
|
52
65
|
Lib.unum_open(type, FFI::MemoryPointer.new(4), 0, locale, FFI::MemoryPointer.new(4), error)
|
|
53
66
|
end
|
|
@@ -97,7 +110,7 @@ module ICU
|
|
|
97
110
|
end
|
|
98
111
|
end
|
|
99
112
|
end
|
|
100
|
-
out_ptr.
|
|
113
|
+
out_ptr.utf8_string(needed_length)
|
|
101
114
|
rescue BufferOverflowError
|
|
102
115
|
raise(BufferOverflowError, "needed: #{needed_length}") if retried
|
|
103
116
|
|
|
@@ -136,10 +149,10 @@ module ICU
|
|
|
136
149
|
|
|
137
150
|
begin
|
|
138
151
|
Lib.check_error do |error|
|
|
139
|
-
needed_length = Lib.unum_format_currency(@f, number, UCharPointer.
|
|
152
|
+
needed_length = Lib.unum_format_currency(@f, number, UCharPointer.from_utf8(currency, 4), out_ptr,
|
|
140
153
|
needed_length, nil, error)
|
|
141
154
|
end
|
|
142
|
-
out_ptr.
|
|
155
|
+
out_ptr.utf8_string
|
|
143
156
|
rescue BufferOverflowError
|
|
144
157
|
raise(BufferOverflowError, "needed: #{needed_length}") if retried
|
|
145
158
|
|
|
@@ -149,5 +162,33 @@ module ICU
|
|
|
149
162
|
end
|
|
150
163
|
end
|
|
151
164
|
end
|
|
165
|
+
|
|
166
|
+
class SkeletonFormatter
|
|
167
|
+
def initialize(locale, skeleton)
|
|
168
|
+
unless Lib.respond_to?(:unumf_openForSkeletonAndLocale)
|
|
169
|
+
raise('ICU::NumberFormatting::SkeletonFormatter requires ICU >= 62')
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
skeleton_uchar = UCharPointer.from_utf8(skeleton)
|
|
173
|
+
@f = FFI::AutoPointer.new(
|
|
174
|
+
Lib.check_error do |error|
|
|
175
|
+
Lib.unumf_openForSkeletonAndLocale(skeleton_uchar, skeleton_uchar.length_in_uchars, locale, error)
|
|
176
|
+
end,
|
|
177
|
+
Lib.method(:unumf_close)
|
|
178
|
+
)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def format(number)
|
|
182
|
+
decimal = number.to_s
|
|
183
|
+
result = FFI::AutoPointer.new(
|
|
184
|
+
Lib.check_error { |error| Lib.unumf_openResult(error) },
|
|
185
|
+
Lib.method(:unumf_closeResult)
|
|
186
|
+
)
|
|
187
|
+
Lib.check_error { |error| Lib.unumf_formatDecimal(@f, decimal, decimal.bytesize, result, error) }
|
|
188
|
+
Lib::Util.read_uchar_buffer(0) do |buffer, error|
|
|
189
|
+
Lib.unumf_resultToString(result, buffer, buffer.length_in_uchars, error)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
152
193
|
end
|
|
153
194
|
end
|
|
@@ -91,8 +91,8 @@ module ICU
|
|
|
91
91
|
pattern_ptr = FFI::MemoryPointer.new(4)
|
|
92
92
|
|
|
93
93
|
if time_zone_str
|
|
94
|
-
time_zone = UCharPointer.
|
|
95
|
-
tz_len =
|
|
94
|
+
time_zone = UCharPointer.from_utf8(time_zone_str)
|
|
95
|
+
tz_len = time_zone.length_in_uchars
|
|
96
96
|
else
|
|
97
97
|
Lib.check_error do |error|
|
|
98
98
|
i_len = 150
|
|
@@ -142,8 +142,8 @@ module ICU
|
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
def parse(str)
|
|
145
|
-
str_u = UCharPointer.
|
|
146
|
-
str_l =
|
|
145
|
+
str_u = UCharPointer.from_utf8(str)
|
|
146
|
+
str_l = str_u.length_in_uchars
|
|
147
147
|
Lib.check_error do |error|
|
|
148
148
|
ret = Lib.udat_parse(@f, str_u, str_l, nil, error)
|
|
149
149
|
Time.at(ret / 1000.0)
|
|
@@ -170,7 +170,7 @@ module ICU
|
|
|
170
170
|
end
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
-
out_ptr.
|
|
173
|
+
out_ptr.utf8_string
|
|
174
174
|
rescue BufferOverflowError
|
|
175
175
|
raise(BufferOverflowError, "needed: #{needed_length}") if retried
|
|
176
176
|
|
|
@@ -209,7 +209,7 @@ module ICU
|
|
|
209
209
|
needed_length = Lib.udat_toPattern(@f, localized, out_ptr, needed_length, error)
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
-
out_ptr.
|
|
212
|
+
out_ptr.utf8_string
|
|
213
213
|
rescue BufferOverflowError
|
|
214
214
|
raise(BufferOverflowError, "needed: #{needed_length}") if retried
|
|
215
215
|
|
|
@@ -228,8 +228,8 @@ module ICU
|
|
|
228
228
|
end
|
|
229
229
|
|
|
230
230
|
def skeleton_format(skeleton_pattern_str, locale)
|
|
231
|
-
skeleton_pattern_ptr = UCharPointer.
|
|
232
|
-
skeleton_pattern_len =
|
|
231
|
+
skeleton_pattern_ptr = UCharPointer.from_utf8(skeleton_pattern_str)
|
|
232
|
+
skeleton_pattern_len = skeleton_pattern_ptr.length_in_uchars
|
|
233
233
|
|
|
234
234
|
needed_length = 0
|
|
235
235
|
pattern_ptr = UCharPointer.new(needed_length)
|
|
@@ -272,7 +272,7 @@ module ICU
|
|
|
272
272
|
return unless @hour_cycle
|
|
273
273
|
|
|
274
274
|
# Get the current pattern and convert to a skeleton
|
|
275
|
-
skeleton_str = pattern_to_skeleton_uchar(current_pattern_as_uchar).
|
|
275
|
+
skeleton_str = pattern_to_skeleton_uchar(current_pattern_as_uchar).utf8_string
|
|
276
276
|
|
|
277
277
|
# Manipulate the skeleton to make it work with the correct hour cycle.
|
|
278
278
|
skeleton_str.gsub!(/[hHkKjJ]/, HOUR_CYCLE_SYMS[@hour_cycle])
|
|
@@ -286,7 +286,7 @@ module ICU
|
|
|
286
286
|
end
|
|
287
287
|
|
|
288
288
|
# Convert the skeleton back to a pattern
|
|
289
|
-
new_pattern_str = skeleton_to_pattern_uchar(UCharPointer.
|
|
289
|
+
new_pattern_str = skeleton_to_pattern_uchar(UCharPointer.from_utf8(skeleton_str)).utf8_string
|
|
290
290
|
|
|
291
291
|
# We also need to manipulate the _pattern_, a little bit, because (according to Firefox source):
|
|
292
292
|
#
|
|
@@ -345,8 +345,8 @@ module ICU
|
|
|
345
345
|
end
|
|
346
346
|
|
|
347
347
|
def set_date_format_impl(localized, pattern_str)
|
|
348
|
-
pattern = UCharPointer.
|
|
349
|
-
pattern_len =
|
|
348
|
+
pattern = UCharPointer.from_utf8(pattern_str)
|
|
349
|
+
pattern_len = pattern.length_in_uchars
|
|
350
350
|
|
|
351
351
|
Lib.check_error do |_error|
|
|
352
352
|
Lib.udat_applyPattern(@f, localized, pattern, pattern_len)
|
|
@@ -26,15 +26,17 @@ module ICU
|
|
|
26
26
|
rules_length = 0
|
|
27
27
|
|
|
28
28
|
if rules
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
rules = UCharPointer.from_utf8(rules)
|
|
30
|
+
rules_length = rules.length_in_uchars
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
id = UCharPointer.from_utf8(id)
|
|
34
|
+
|
|
33
35
|
parse_error = Lib::UParseError.new
|
|
34
36
|
begin
|
|
35
37
|
Lib.check_error do |status|
|
|
36
|
-
ptr = Lib.utrans_openU(
|
|
37
|
-
|
|
38
|
+
ptr = Lib.utrans_openU(id, id.length_in_uchars, direction, rules, rules_length,
|
|
39
|
+
parse_error, status)
|
|
38
40
|
@tr = FFI::AutoPointer.new(ptr, Lib.method(:utrans_close))
|
|
39
41
|
end
|
|
40
42
|
rescue ICU::Error => e
|
|
@@ -45,9 +47,11 @@ module ICU
|
|
|
45
47
|
def transliterate(from)
|
|
46
48
|
# this is a bit unpleasant
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
input = UCharPointer.from_utf8(from)
|
|
51
|
+
input_length_in_uchars = input.length_in_uchars
|
|
52
|
+
capacity = input_length_in_uchars + 1
|
|
53
|
+
buf = UCharPointer.new(capacity)
|
|
54
|
+
buf.put_bytes(0, input.get_bytes(0, input.size))
|
|
51
55
|
limit = FFI::MemoryPointer.new(:int32)
|
|
52
56
|
text_length = FFI::MemoryPointer.new(:int32)
|
|
53
57
|
|
|
@@ -56,7 +60,7 @@ module ICU
|
|
|
56
60
|
begin
|
|
57
61
|
# resets to original size on retry
|
|
58
62
|
[limit, text_length].each do |ptr|
|
|
59
|
-
ptr.put_int32(0,
|
|
63
|
+
ptr.put_int32(0, input_length_in_uchars)
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
Lib.check_error do |error|
|
|
@@ -73,13 +77,13 @@ module ICU
|
|
|
73
77
|
# create a new buffer with more capacity instead of resizing,
|
|
74
78
|
# since the old buffer now has result data
|
|
75
79
|
buf.free
|
|
76
|
-
buf = UCharPointer.
|
|
80
|
+
buf = UCharPointer.from_utf8(from, capacity)
|
|
77
81
|
|
|
78
82
|
retried = true
|
|
79
83
|
retry
|
|
80
84
|
end
|
|
81
85
|
|
|
82
|
-
buf.
|
|
86
|
+
buf.utf8_string(text_length.get_int32(0))
|
|
83
87
|
end
|
|
84
88
|
end
|
|
85
89
|
end
|
data/lib/ffi-icu/uchar.rb
CHANGED
|
@@ -22,6 +22,22 @@ module ICU
|
|
|
22
22
|
ptr
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def self.from_utf8(str, capacity = nil)
|
|
26
|
+
chars = str.encode(Encoding::UTF_16LE).unpack('v*')
|
|
27
|
+
|
|
28
|
+
if capacity
|
|
29
|
+
raise(ArgumentError, "capacity is too small for string of #{chars.size} UChars") if capacity < chars.size
|
|
30
|
+
|
|
31
|
+
ptr = new(capacity)
|
|
32
|
+
else
|
|
33
|
+
ptr = new(chars.size)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
ptr.write_array_of_uint16(chars)
|
|
37
|
+
|
|
38
|
+
ptr
|
|
39
|
+
end
|
|
40
|
+
|
|
25
41
|
def initialize(size)
|
|
26
42
|
super(UCHAR_TYPE, size)
|
|
27
43
|
end
|
|
@@ -42,6 +58,13 @@ module ICU
|
|
|
42
58
|
wstring.pack('U*')
|
|
43
59
|
end
|
|
44
60
|
|
|
61
|
+
def utf8_string(length = nil)
|
|
62
|
+
length ||= size / TYPE_SIZE
|
|
63
|
+
|
|
64
|
+
wstring = read_array_of_uint16(length).pack('v*')
|
|
65
|
+
wstring.force_encoding(Encoding::UTF_16LE).encode(Encoding::UTF_8)
|
|
66
|
+
end
|
|
67
|
+
|
|
45
68
|
def length_in_uchars
|
|
46
69
|
size / type_size
|
|
47
70
|
end
|
data/lib/ffi-icu/version.rb
CHANGED
data/lib/ffi-icu.rb
CHANGED
|
@@ -25,11 +25,14 @@ require 'ffi-icu/lib/util'
|
|
|
25
25
|
require 'ffi-icu/uchar'
|
|
26
26
|
require 'ffi-icu/chardet'
|
|
27
27
|
require 'ffi-icu/collation'
|
|
28
|
+
require 'ffi-icu/currency'
|
|
28
29
|
require 'ffi-icu/locale'
|
|
29
30
|
require 'ffi-icu/transliteration'
|
|
30
31
|
require 'ffi-icu/normalization'
|
|
31
32
|
require 'ffi-icu/normalizer'
|
|
32
33
|
require 'ffi-icu/break_iterator'
|
|
33
34
|
require 'ffi-icu/number_formatting'
|
|
35
|
+
require 'ffi-icu/message_formatting'
|
|
34
36
|
require 'ffi-icu/time_formatting'
|
|
35
37
|
require 'ffi-icu/duration_formatting'
|
|
38
|
+
require 'ffi-icu/list_formatting'
|
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.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erick Guan
|
|
@@ -76,10 +76,13 @@ files:
|
|
|
76
76
|
- lib/ffi-icu/break_iterator.rb
|
|
77
77
|
- lib/ffi-icu/chardet.rb
|
|
78
78
|
- lib/ffi-icu/collation.rb
|
|
79
|
+
- lib/ffi-icu/currency.rb
|
|
79
80
|
- lib/ffi-icu/duration_formatting.rb
|
|
80
81
|
- lib/ffi-icu/lib.rb
|
|
81
82
|
- lib/ffi-icu/lib/util.rb
|
|
83
|
+
- lib/ffi-icu/list_formatting.rb
|
|
82
84
|
- lib/ffi-icu/locale.rb
|
|
85
|
+
- lib/ffi-icu/message_formatting.rb
|
|
83
86
|
- lib/ffi-icu/normalization.rb
|
|
84
87
|
- lib/ffi-icu/normalizer.rb
|
|
85
88
|
- lib/ffi-icu/number_formatting.rb
|