diff --git a/benchmark/detect.rb b/benchmark/detect.rb index fa87ef7..dcd395c 100644 --- a/benchmark/detect.rb +++ b/benchmark/detect.rb @@ -11,4 +11,4 @@ Benchmark.bmbm do |results| results.report("rchardet:") { TESTS.times { CharDet.detect("æåø") } } results.report("ffi-icu:") { TESTS.times { ICU::CharDet.detect("æåø") } } -end \ No newline at end of file +end diff --git a/benchmark/shared.rb b/benchmark/shared.rb index 942554d..b3615a1 100644 --- a/benchmark/shared.rb +++ b/benchmark/shared.rb @@ -13,5 +13,5 @@ Benchmark.bmbm do |results| results.report("rchardet instance:") { TESTS.times { $rchardet.reset; $rchardet.feed("æåø"); $rchardet.result } } - results.report("ffi-icu instance:") { TESTS.times { $icu.detect("æåø") } } -end \ No newline at end of file + results.report("ffi-icu instance:") { TESTS.times { $icu.detect("æåø") } } +end diff --git a/lib/ffi-icu/break_iterator.rb b/lib/ffi-icu/break_iterator.rb index f5030a3..4b0df84 100644 --- a/lib/ffi-icu/break_iterator.rb +++ b/lib/ffi-icu/break_iterator.rb @@ -88,6 +88,5 @@ def current def boundary?(offset) Lib.ubrk_isBoundary(@iterator, Integer(offset)) != 0 end - end # BreakIterator -end # ICU \ No newline at end of file +end # ICU diff --git a/lib/ffi-icu/chardet.rb b/lib/ffi-icu/chardet.rb index 6446307..bf12ee2 100644 --- a/lib/ffi-icu/chardet.rb +++ b/lib/ffi-icu/chardet.rb @@ -1,6 +1,5 @@ module ICU module CharDet - def self.detect(string) Detector.new.detect string end @@ -77,8 +76,6 @@ def set_text(text) Lib.ucsdet_setText(@detector, data, text.bytesize, status) end end - end # Detector end # CharDet end # ICU - diff --git a/lib/ffi-icu/collation.rb b/lib/ffi-icu/collation.rb index d7b9fe8..137f5d2 100644 --- a/lib/ffi-icu/collation.rb +++ b/lib/ffi-icu/collation.rb @@ -1,37 +1,36 @@ module ICU module Collation - ATTRIBUTES = { - french_collation: 0, - alternate_handling: 1, - case_first: 2, - case_level: 3, - normalization_mode: 4, - strength: 5, - hiragana_quaternary_mode: 6, - numeric_collation: 7, + french_collation: 0, + alternate_handling: 1, + case_first: 2, + case_level: 3, + normalization_mode: 4, + strength: 5, + hiragana_quaternary_mode: 6, + numeric_collation: 7, }.freeze ATTRIBUTE_VALUES = { - nil => -1, - primary: 0, - secondary: 1, - default_strength: 2, - tertiary: 2, - quaternary: 3, - identical: 15, - - false => 16, - true => 17, - - shifted: 20, - non_ignorable: 21, - - lower_first: 24, - upper_first: 25, + nil => -1, + primary: 0, + secondary: 1, + default_strength: 2, + tertiary: 2, + quaternary: 3, + identical: 15, + + false => 16, + true => 17, + + shifted: 20, + non_ignorable: 21, + + lower_first: 24, + upper_first: 25, }.freeze - ATTRIBUTE_VALUES_INVERSE = Hash[ATTRIBUTE_VALUES.map {|k,v| [v, k]}].freeze + ATTRIBUTE_VALUES_INVERSE = Hash[ATTRIBUTE_VALUES.map { |k, v| [v, k] }].freeze def self.collate(locale, arr) Collator.new(locale).collate(arr) @@ -80,12 +79,12 @@ def compare(a, b) def greater?(a, b) Lib.ucol_greater(@c, UCharPointer.from_string(a), a.jlength, - UCharPointer.from_string(b), b.jlength) + UCharPointer.from_string(b), b.jlength) end def greater_or_equal?(a, b) Lib.ucol_greaterOrEqual(@c, UCharPointer.from_string(a), a.jlength, - UCharPointer.from_string(b), b.jlength) + UCharPointer.from_string(b), b.jlength) end def equal?(*args) @@ -98,7 +97,7 @@ def equal?(*args) a, b = args Lib.ucol_equal(@c, UCharPointer.from_string(a), a.jlength, - UCharPointer.from_string(b), b.jlength) + UCharPointer.from_string(b), b.jlength) end def collate(sortable) @@ -151,6 +150,5 @@ def #{attribute}=(value) CODE end end # Collator - end # Collate end # ICU diff --git a/lib/ffi-icu/duration_formatting.rb b/lib/ffi-icu/duration_formatting.rb index 6a9e4bc..b9bbd8d 100644 --- a/lib/ffi-icu/duration_formatting.rb +++ b/lib/ffi-icu/duration_formatting.rb @@ -1,282 +1,289 @@ module ICU - module DurationFormatting - VALID_FIELDS = %i[ - years - months - weeks - days - hours - minutes - seconds - milliseconds - microseconds - nanoseconds - ] - HMS_FIELDS = %i[ - hours - minutes - seconds - milliseconds - microseconds - nanoseconds - ] - ROUNDABLE_FIELDS = %i[ - seconds - milliseconds - microseconds - nanoseconds - ] - VALID_STYLES = %i[long short narrow digital] - STYLES_TO_LIST_JOIN_FORMAT = { - long: :wide, - short: :short, - narrow: :narrow, - digital: :narrow, - } - UNIT_FORMAT_STRINGS = { - years: 'measure-unit/duration-year', - months: 'measure-unit/duration-month', - weeks: 'measure-unit/duration-week', - days: 'measure-unit/duration-day', - hours: 'measure-unit/duration-hour', - minutes: 'measure-unit/duration-minute', - seconds: 'measure-unit/duration-second', - milliseconds: 'measure-unit/duration-millisecond', - microseconds: 'measure-unit/duration-microsecond', - nanoseconds: 'measure-unit/duration-nanosecond', - } - STYLES_TO_NUMBER_FORMAT_WIDTH = { - long: 'unit-width-full-name', - short: 'unit-width-short', - narrow: 'unit-width-narrow', - # digital for hours:minutes:seconds has some special casing. - digital: 'unit-width-narrow', - } + module DurationFormatting + VALID_FIELDS = %i[ + years + months + weeks + days + hours + minutes + seconds + milliseconds + microseconds + nanoseconds + ] + HMS_FIELDS = %i[ + hours + minutes + seconds + milliseconds + microseconds + nanoseconds + ] + ROUNDABLE_FIELDS = %i[ + seconds + milliseconds + microseconds + nanoseconds + ] + VALID_STYLES = %i[long short narrow digital] + STYLES_TO_LIST_JOIN_FORMAT = { + long: :wide, + short: :short, + narrow: :narrow, + digital: :narrow, + } + UNIT_FORMAT_STRINGS = { + years: 'measure-unit/duration-year', + months: 'measure-unit/duration-month', + weeks: 'measure-unit/duration-week', + days: 'measure-unit/duration-day', + hours: 'measure-unit/duration-hour', + minutes: 'measure-unit/duration-minute', + seconds: 'measure-unit/duration-second', + milliseconds: 'measure-unit/duration-millisecond', + microseconds: 'measure-unit/duration-microsecond', + nanoseconds: 'measure-unit/duration-nanosecond', + } + STYLES_TO_NUMBER_FORMAT_WIDTH = { + long: 'unit-width-full-name', + short: 'unit-width-short', + narrow: 'unit-width-narrow', + # digital for hours:minutes:seconds has some special casing. + digital: 'unit-width-narrow', + } + + def self.format(fields, locale:, style: :long) + DurationFormatter.new(locale: locale, style: style).format(fields) + end - def self.format(fields, locale:, style: :long) - DurationFormatter.new(locale: locale, style: style).format(fields) + class DurationFormatter + def initialize(locale:, style: :long) + if !Lib.respond_to?(:unumf_openForSkeletonAndLocale) || !Lib.respond_to?(:ulistfmt_openForType) + raise "ICU::DurationFormatting requires ICU >= 67" end - class DurationFormatter - def initialize(locale:, style: :long) - if !Lib.respond_to?(:unumf_openForSkeletonAndLocale) || !Lib.respond_to?(:ulistfmt_openForType) - raise "ICU::DurationFormatting requires ICU >= 67" - end + raise ArgumentError, "Unknown style #{style}" unless VALID_STYLES.include?(style) - raise ArgumentError, "Unknown style #{style}" unless VALID_STYLES.include?(style) + @locale = locale + @style = style + # These are created lazily based on what parts are actually included + @number_formatters = {} - @locale = locale - @style = style - # These are created lazily based on what parts are actually included - @number_formatters = {} + list_join_format = STYLES_TO_LIST_JOIN_FORMAT.fetch(style) + @list_formatter = FFI::AutoPointer.new( + Lib.check_error { |error| + Lib.ulistfmt_openForType(@locale, :units, list_join_format, error) + }, + Lib.method(:ulistfmt_close) + ) + end - list_join_format = STYLES_TO_LIST_JOIN_FORMAT.fetch(style) - @list_formatter = FFI::AutoPointer.new( - Lib.check_error { |error| - Lib.ulistfmt_openForType(@locale, :units, list_join_format, error) - }, - Lib.method(:ulistfmt_close) - ) - end + def format(fields) + fields.each_key do |field| + raise "Unknown field #{field}" unless VALID_FIELDS.include?(field) + end + fields = fields.dup # we might modify this argument. - def format(fields) - fields.each_key do |field| - raise "Unknown field #{field}" unless VALID_FIELDS.include?(field) - end - fields = fields.dup # we might modify this argument. + # Intl.js spec says that rounding options affect only the smallest unit, and only + # if that unit is sub-second. All other fields therefore need to be truncated. + smallest_unit = VALID_FIELDS[fields.keys.map { |k| VALID_FIELDS.index(k) }.max] + fields.each_key do |k| + raise ArgumentError, "Negative durations are not yet supported" if fields[k] < 0 - # Intl.js spec says that rounding options affect only the smallest unit, and only - # if that unit is sub-second. All other fields therefore need to be truncated. - smallest_unit = VALID_FIELDS[fields.keys.map { |k| VALID_FIELDS.index(k) }.max] - fields.each_key do |k| - raise ArgumentError, "Negative durations are not yet supported" if fields[k] < 0 - fields[k] = fields[k].to_i unless k == smallest_unit && ROUNDABLE_FIELDS.include?(smallest_unit) - end + fields[k] = fields[k].to_i unless k == smallest_unit && ROUNDABLE_FIELDS.include?(smallest_unit) + end - formatted_hms = nil - if @style == :digital - # icu::MeasureFormat contains special casing for hours/minutes/seconds formatted - # at numeric width, to render it as h:mm:s, essentially. This involves using - # a pattern called durationUnits defined in the ICU data for the locale. - # If we have data for this combination of hours/mins/seconds in this locale, - # use that and emulate ICU's special casing. - formatted_hms = format_hms(fields) - if formatted_hms - # We've taken care of all these fields now. - HMS_FIELDS.each do |f| - fields.delete f - end - end - end + formatted_hms = nil + if @style == :digital + # icu::MeasureFormat contains special casing for hours/minutes/seconds formatted + # at numeric width, to render it as h:mm:s, essentially. This involves using + # a pattern called durationUnits defined in the ICU data for the locale. + # If we have data for this combination of hours/mins/seconds in this locale, + # use that and emulate ICU's special casing. + formatted_hms = format_hms(fields) + if formatted_hms + # We've taken care of all these fields now. + HMS_FIELDS.each do |f| + fields.delete f + end + end + end - formatted_fields = VALID_FIELDS.map do |f| - next unless fields.key?(f) - next unless fields[f] != 0 + formatted_fields = VALID_FIELDS.map do |f| + next unless fields.key?(f) + next unless fields[f] != 0 - format_number(fields[f], [ - UNIT_FORMAT_STRINGS[f], STYLES_TO_NUMBER_FORMAT_WIDTH[@style], - (".#########" if f == smallest_unit), - ].compact.join(' ')) - end - formatted_fields << formatted_hms - formatted_fields.compact! + format_number(fields[f], [ + UNIT_FORMAT_STRINGS[f], STYLES_TO_NUMBER_FORMAT_WIDTH[@style], + (".#########" if f == smallest_unit), + ].compact.join(' ')) + end + formatted_fields << formatted_hms + formatted_fields.compact! - format_list(formatted_fields) - end + format_list(formatted_fields) + end - private + private - def hms_duration_units_pattern(fields) - return nil unless HMS_FIELDS.any? { |k| fields.key?(k) } - @unit_res_bundle ||= FFI::AutoPointer.new( - Lib.check_error { |error| Lib.ures_open(Lib.resource_bundle_name(:unit), @locale, error) }, - Lib.method(:ures_close) - ) + def hms_duration_units_pattern(fields) + return nil unless HMS_FIELDS.any? { |k| fields.key?(k) } - resource_key = "durationUnits/" - resource_key << "h" if fields.key?(:hours) - resource_key << "m" if fields.key?(:minutes) - resource_key << "s" if [:seconds, :milliseconds, :microseconds, :nanoseconds].any? { |f| fields.key?(f) } + @unit_res_bundle ||= FFI::AutoPointer.new( + Lib.check_error { |error| Lib.ures_open(Lib.resource_bundle_name(:unit), @locale, error) }, + Lib.method(:ures_close) + ) - begin - pattern_resource = FFI::AutoPointer.new( - Lib.check_error { |error| Lib.ures_getBykeyWithFallback(@unit_res_bundle, resource_key, nil, error) }, - Lib.method(:ures_close) - ) - rescue MissingResourceError - # This combination of h,m,s not present for this locale. - return nil - end - # Read the resource as a UChar (whose memory we _do not own_ - it's static data) and - # convert it to a Ruby string. - pattern_uchar_len = FFI::MemoryPointer.new(:int32_t) - pattern_uchar = Lib.check_error { |error| - Lib.ures_getString(pattern_resource, pattern_uchar_len, error) - } - pattern_str = pattern_uchar.read_array_of_uint16(pattern_uchar_len.read_int32).pack("U*") + resource_key = "durationUnits/" + resource_key << "h" if fields.key?(:hours) + resource_key << "m" if fields.key?(:minutes) + resource_key << "s" if [:seconds, :milliseconds, :microseconds, :nanoseconds].any? { |f| + fields.key?(f) + } - # For some reason I can't comprehend, loadNumericDateFormatterPattern in ICU wants to turn - # h's into H's here. I guess we have to do it too because the pattern data could in theory - # now contain either. - pattern_str.gsub('h', 'H') - end + begin + pattern_resource = FFI::AutoPointer.new( + Lib.check_error { |error| + Lib.ures_getBykeyWithFallback(@unit_res_bundle, resource_key, nil, error) + }, + Lib.method(:ures_close) + ) + rescue MissingResourceError + # This combination of h,m,s not present for this locale. + return nil + end + # Read the resource as a UChar (whose memory we _do not own_ - it's static data) and + # convert it to a Ruby string. + pattern_uchar_len = FFI::MemoryPointer.new(:int32_t) + pattern_uchar = Lib.check_error { |error| + Lib.ures_getString(pattern_resource, pattern_uchar_len, error) + } + pattern_str = pattern_uchar.read_array_of_uint16(pattern_uchar_len.read_int32).pack("U*") - def format_hms(fields) - pattern = hms_duration_units_pattern(fields) - return nil if pattern.nil? + # For some reason I can't comprehend, loadNumericDateFormatterPattern in ICU wants to turn + # h's into H's here. I guess we have to do it too because the pattern data could in theory + # now contain either. + pattern_str.gsub('h', 'H') + end - # According to the Intl.js spec, when formatting in digital, everything < seconds - # should be coalesced into decimal seconds - seconds_incl_fractional = fields.fetch(:seconds, 0) - second_precision = 0 - if fields.key?(:milliseconds) - seconds_incl_fractional += fields[:milliseconds] / 1e3 - second_precision = 3 - end - if fields.key?(:microseconds) - seconds_incl_fractional += fields[:microseconds] / 1e6 - second_precision = 6 - end - if fields.key?(:nanoseconds) - seconds_incl_fractional += fields[:nanoseconds] / 1e9 - second_precision = 9 - end + def format_hms(fields) + pattern = hms_duration_units_pattern(fields) + return nil if pattern.nil? - # Follow the rules in ICU measfmt.cpp formatNumeric to fill in the patterns here with - # the appropriate values. - enum = pattern.each_char - protect = false - result = "" - loop do - char = enum.next - next_char = enum.peek rescue nil + # According to the Intl.js spec, when formatting in digital, everything < seconds + # should be coalesced into decimal seconds + seconds_incl_fractional = fields.fetch(:seconds, 0) + second_precision = 0 + if fields.key?(:milliseconds) + seconds_incl_fractional += fields[:milliseconds] / 1e3 + second_precision = 3 + end + if fields.key?(:microseconds) + seconds_incl_fractional += fields[:microseconds] / 1e6 + second_precision = 6 + end + if fields.key?(:nanoseconds) + seconds_incl_fractional += fields[:nanoseconds] / 1e9 + second_precision = 9 + end - if protect - # In literal mode - if char == "'" - protect = false - next - end - result << char - next - end + # Follow the rules in ICU measfmt.cpp formatNumeric to fill in the patterns here with + # the appropriate values. + enum = pattern.each_char + protect = false + result = "" + loop do + char = enum.next + next_char = enum.peek rescue nil - value = case char - when 'H' then fields[:hours] - when 'm' then fields[:minutes] - when 's' then seconds_incl_fractional - end + if protect + # In literal mode + if char == "'" + protect = false + next + end + result << char + next + end - case char - when 'H', 'm', 's' - skeleton = "." - if char == 's' && second_precision > 0 - skeleton << ("0" * second_precision) - else - skeleton << ("#" * 9) - end - if char == next_char - # It's doubled - means format it at zero fill - skeleton << " integer-width/00" - enum.next - end - result << format_number(value, skeleton) - when "'" - if next_char == char - # double-apostrophe, means literal ' - result << "'" - enum.next - else - protect = true - end - else - result << char - end - end + value = case char + when 'H' then fields[:hours] + when 'm' then fields[:minutes] + when 's' then seconds_incl_fractional + end - result + case char + when 'H', 'm', 's' + skeleton = "." + if char == 's' && second_precision > 0 + skeleton << ("0" * second_precision) + else + skeleton << ("#" * 9) end - - def number_formatter(skeleton) - @number_formatters[skeleton] ||= begin - skeleton_uchar = UCharPointer.from_string(skeleton) - FFI::AutoPointer.new( - Lib.check_error { |error| - Lib.unumf_openForSkeletonAndLocale(skeleton_uchar, skeleton_uchar.length_in_uchars, @locale, error) - }, - Lib.method(:unumf_close) - ) - end + if char == next_char + # It's doubled - means format it at zero fill + skeleton << " integer-width/00" + enum.next end - - def format_number(value, skeleton) - formatter = number_formatter(skeleton) - result = FFI::AutoPointer.new( - Lib.check_error { |error| Lib.unumf_openResult(error) }, - Lib.method(:unumf_closeResult) - ) - value_str = value.to_s - Lib.check_error do |error| - Lib.unumf_formatDecimal(formatter, value_str, value_str.size, result, error) - end - Lib::Util.read_uchar_buffer(0) do |buf, error| - Lib.unumf_resultToString(result, buf, buf.length_in_uchars, error) - end + result << format_number(value, skeleton) + when "'" + if next_char == char + # double-apostrophe, means literal ' + result << "'" + enum.next + else + protect = true end + else + result << char + end + end - def format_list(values) - value_uchars = values.map(&UCharPointer.method(:from_string)) - value_uchars_array = FFI::MemoryPointer.new(:pointer, value_uchars.size) - value_uchars_array.put_array_of_pointer(0, value_uchars) - value_lengths_array = FFI::MemoryPointer.new(:int32_t, value_uchars.size) - value_lengths_array.put_array_of_int32(0, value_uchars.map(&:length_in_uchars)) - Lib::Util.read_uchar_buffer(0) do |buf, error| - Lib.ulistfmt_format( - @list_formatter, value_uchars_array, value_lengths_array, - value_uchars.size, buf, buf.length_in_uchars, error - ) - end - end + result + end + + def number_formatter(skeleton) + @number_formatters[skeleton] ||= begin + skeleton_uchar = UCharPointer.from_string(skeleton) + FFI::AutoPointer.new( + Lib.check_error { |error| + Lib.unumf_openForSkeletonAndLocale(skeleton_uchar, skeleton_uchar.length_in_uchars, + @locale, error) + }, + Lib.method(:unumf_close) + ) + end + end + + def format_number(value, skeleton) + formatter = number_formatter(skeleton) + result = FFI::AutoPointer.new( + Lib.check_error { |error| Lib.unumf_openResult(error) }, + Lib.method(:unumf_closeResult) + ) + value_str = value.to_s + Lib.check_error do |error| + Lib.unumf_formatDecimal(formatter, value_str, value_str.size, result, error) + end + Lib::Util.read_uchar_buffer(0) do |buf, error| + Lib.unumf_resultToString(result, buf, buf.length_in_uchars, error) + end + end + + def format_list(values) + value_uchars = values.map(&UCharPointer.method(:from_string)) + value_uchars_array = FFI::MemoryPointer.new(:pointer, value_uchars.size) + value_uchars_array.put_array_of_pointer(0, value_uchars) + value_lengths_array = FFI::MemoryPointer.new(:int32_t, value_uchars.size) + value_lengths_array.put_array_of_int32(0, value_uchars.map(&:length_in_uchars)) + Lib::Util.read_uchar_buffer(0) do |buf, error| + Lib.ulistfmt_format( + @list_formatter, value_uchars_array, value_lengths_array, + value_uchars.size, buf, buf.length_in_uchars, error + ) end + end end + end end diff --git a/lib/ffi-icu/lib.rb b/lib/ffi-icu/lib.rb index a6f4f58..ffe1eb4 100644 --- a/lib/ffi-icu/lib.rb +++ b/lib/ffi-icu/lib.rb @@ -14,7 +14,7 @@ module Lib def self.search_paths @search_paths ||= begin if ENV['FFI_ICU_LIB'] - [ ENV['FFI_ICU_LIB'] ] + [ENV['FFI_ICU_LIB']] elsif FFI::Platform::IS_WINDOWS ENV['PATH'].split(File::PATH_SEPARATOR) else @@ -35,33 +35,38 @@ def self.find_lib(lib) def self.load_icu # First find the library - lib_names = case ICU.platform - when :bsd - [find_lib("libicui18n.#{FFI::Platform::LIBSUFFIX}.??"), - find_lib("libicutu.#{FFI::Platform::LIBSUFFIX}.??")] - when :osx - if ENV.key?('FFI_ICU_LIB') - # Ensure we look in the user-supplied override dir for a user-compiled libicu - [find_lib("libicui18n.??.#{FFI::Platform::LIBSUFFIX}"), - find_lib("libicutu.??.#{FFI::Platform::LIBSUFFIX}")] - elsif Gem::Version.new(`sw_vers -productVersion`) >= Gem::Version.new('11') - # See https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes (62986286) - ["libicucore.#{FFI::Platform::LIBSUFFIX}"] - else - [find_lib("libicucore.#{FFI::Platform::LIBSUFFIX}")] - end - when :linux - [find_lib("libicui18n.#{FFI::Platform::LIBSUFFIX}.??"), - find_lib("libicutu.#{FFI::Platform::LIBSUFFIX}.??")] - when :windows - [find_lib("icuuc??.#{FFI::Platform::LIBSUFFIX}"), - find_lib("icuin??.#{FFI::Platform::LIBSUFFIX}")] - end + lib_names = + case ICU.platform + when :bsd + [find_lib("libicui18n.#{FFI::Platform::LIBSUFFIX}.??"), + find_lib("libicutu.#{FFI::Platform::LIBSUFFIX}.??")] + when :osx + if ENV.key?('FFI_ICU_LIB') + # Ensure we look in the user-supplied override dir for a user-compiled libicu + [find_lib("libicui18n.??.#{FFI::Platform::LIBSUFFIX}"), + find_lib("libicutu.??.#{FFI::Platform::LIBSUFFIX}")] + elsif Gem::Version.new(`sw_vers -productVersion`) >= Gem::Version.new('11') + # See https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes + # (62986286) + ["libicucore.#{FFI::Platform::LIBSUFFIX}"] + else + [find_lib("libicucore.#{FFI::Platform::LIBSUFFIX}")] + end + when :linux + [find_lib("libicui18n.#{FFI::Platform::LIBSUFFIX}.??"), + find_lib("libicutu.#{FFI::Platform::LIBSUFFIX}.??")] + when :windows + [find_lib("icuuc??.#{FFI::Platform::LIBSUFFIX}"), + find_lib("icuin??.#{FFI::Platform::LIBSUFFIX}")] + end lib_names.compact! if lib_names if not lib_names or lib_names.length == 0 - raise LoadError, "Could not find ICU on #{ICU.platform.inspect}. Patches welcome, or you can add the containing directory yourself: #{self}.search_paths << '/path/to/lib'" + raise LoadError, + "Could not find ICU on #{ICU.platform.inspect}. " \ + "Patches welcome, or you can add the containing directory yourself: " \ + "#{self}.search_paths << '/path/to/lib'" end # And now try to load the library @@ -210,7 +215,7 @@ def self.attach_optional_function(*args) attach_function :u_errorName, "u_errorName#{suffix}", [:int], :string attach_function :uenum_count, "uenum_count#{suffix}", [:pointer, :pointer], :int attach_function :uenum_close, "uenum_close#{suffix}", [:pointer], :void - attach_function :uenum_next, "uenum_next#{suffix}", [:pointer, :pointer, :pointer], :string + attach_function :uenum_next, "uenum_next#{suffix}", [:pointer, :pointer, :pointer], :string attach_function :u_charsToUChars, "u_charsToUChars#{suffix}", [:string, :pointer, :int32_t], :void attach_function :u_UCharsToChars, "u_UCharsToChars#{suffix}", [:pointer, :string, :int32_t], :void @@ -225,49 +230,73 @@ def self.attach_optional_function(*args) enum :layout_type, [:ltr, :rtl, :ttb, :btt, :unknown] - attach_function :uloc_canonicalize, "uloc_canonicalize#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_canonicalize, "uloc_canonicalize#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t attach_function :uloc_countAvailable, "uloc_countAvailable#{suffix}", [], :int32_t attach_function :uloc_getAvailable, "uloc_getAvailable#{suffix}", [:int32_t], :string - attach_function :uloc_getBaseName, "uloc_getBaseName#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getCountry, "uloc_getCountry#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getBaseName, "uloc_getBaseName#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t + attach_function :uloc_getCountry, "uloc_getCountry#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t attach_function :uloc_getDefault, "uloc_getDefault#{suffix}", [], :string attach_function :uloc_getISO3Country, "uloc_getISO3Country#{suffix}", [:string], :string attach_function :uloc_getISO3Language, "uloc_getISO3Language#{suffix}", [:string], :string attach_function :uloc_getISOCountries, "uloc_getISOCountries#{suffix}", [], :pointer attach_function :uloc_getISOLanguages, "uloc_getISOLanguages#{suffix}", [], :pointer - attach_function :uloc_getKeywordValue, "uloc_getKeywordValue#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getLanguage, "uloc_getLanguage#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getKeywordValue, "uloc_getKeywordValue#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getLanguage, "uloc_getLanguage#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t attach_function :uloc_getLCID, "uloc_getLCID#{suffix}", [:string], :uint32 - attach_function :uloc_getName, "uloc_getName#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getParent, "uloc_getParent#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getScript, "uloc_getScript#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getVariant, "uloc_getVariant#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getName, "uloc_getName#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t + attach_function :uloc_getParent, "uloc_getParent#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t + attach_function :uloc_getScript, "uloc_getScript#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t + attach_function :uloc_getVariant, "uloc_getVariant#{suffix}", [:string, :pointer, :int32_t, :pointer], + :int32_t attach_function :uloc_openKeywords, "uloc_openKeywords#{suffix}", [:string, :pointer], :pointer attach_function :uloc_setDefault, "uloc_setDefault#{suffix}", [:string, :pointer], :void - attach_function :uloc_setKeywordValue, "uloc_setKeywordValue#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t - - attach_function :uloc_getDisplayCountry, "uloc_getDisplayCountry#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getDisplayKeyword, "uloc_getDisplayKeyword#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getDisplayKeywordValue, "uloc_getDisplayKeywordValue#{suffix}", [:string, :string, :string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getDisplayLanguage, "uloc_getDisplayLanguage#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getDisplayName, "uloc_getDisplayName#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getDisplayScript, "uloc_getDisplayScript#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getDisplayVariant, "uloc_getDisplayVariant#{suffix}", [:string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_setKeywordValue, "uloc_setKeywordValue#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t + + attach_function :uloc_getDisplayCountry, "uloc_getDisplayCountry#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getDisplayKeyword, "uloc_getDisplayKeyword#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getDisplayKeywordValue, "uloc_getDisplayKeywordValue#{suffix}", + [:string, :string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getDisplayLanguage, "uloc_getDisplayLanguage#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getDisplayName, "uloc_getDisplayName#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getDisplayScript, "uloc_getDisplayScript#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getDisplayVariant, "uloc_getDisplayVariant#{suffix}", + [:string, :string, :pointer, :int32_t, :pointer], :int32_t if Gem::Version.new('3.8') <= Gem::Version.new(self.version) - attach_function :uloc_getLocaleForLCID, "uloc_getLocaleForLCID#{suffix}", [:uint32, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getLocaleForLCID, "uloc_getLocaleForLCID#{suffix}", + [:uint32, :pointer, :int32_t, :pointer], :int32_t end if Gem::Version.new('4.0') <= Gem::Version.new(self.version) - attach_function :uloc_addLikelySubtags, "uloc_addLikelySubtags#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_minimizeSubtags, "uloc_minimizeSubtags#{suffix}", [:string, :pointer, :int32_t, :pointer], :int32_t - attach_function :uloc_getCharacterOrientation, "uloc_getCharacterOrientation#{suffix}", [:string, :pointer], :layout_type - attach_function :uloc_getLineOrientation, "uloc_getLineOrientation#{suffix}", [:string, :pointer], :layout_type + attach_function :uloc_addLikelySubtags, "uloc_addLikelySubtags#{suffix}", + [:string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_minimizeSubtags, "uloc_minimizeSubtags#{suffix}", + [:string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uloc_getCharacterOrientation, "uloc_getCharacterOrientation#{suffix}", [:string, :pointer], + :layout_type + attach_function :uloc_getLineOrientation, "uloc_getLineOrientation#{suffix}", [:string, :pointer], + :layout_type end if Gem::Version.new('4.2') <= Gem::Version.new(self.version) - attach_function :uloc_forLanguageTag, "uloc_forLanguageTag#{suffix}", [:string, :pointer, :int32_t, :pointer, :pointer], :int32_t - attach_function :uloc_toLanguageTag, "uloc_toLanguageTag#{suffix}", [:string, :pointer, :int32_t, :int8_t, :pointer], :int32_t + attach_function :uloc_forLanguageTag, "uloc_forLanguageTag#{suffix}", + [:string, :pointer, :int32_t, :pointer, :pointer], :int32_t + attach_function :uloc_toLanguageTag, "uloc_toLanguageTag#{suffix}", + [:string, :pointer, :int32_t, :int8_t, :pointer], :int32_t attach_function :ulocdata_getCLDRVersion, "ulocdata_getCLDRVersion#{suffix}", [:version, :pointer], :void end @@ -279,16 +308,25 @@ def self.attach_optional_function(*args) attach_function :ucsdet_open, "ucsdet_open#{suffix}", [:pointer], :pointer attach_function :ucsdet_close, "ucsdet_close#{suffix}", [:pointer], :void - attach_function :ucsdet_setText, "ucsdet_setText#{suffix}", [:pointer, :pointer, :int32_t, :pointer], :void - attach_function :ucsdet_setDeclaredEncoding, "ucsdet_setDeclaredEncoding#{suffix}", [:pointer, :string, :int32_t, :pointer], :void - attach_function :ucsdet_detect, "ucsdet_detect#{suffix}", [:pointer, :pointer], :pointer - attach_function :ucsdet_detectAll, "ucsdet_detectAll#{suffix}", [:pointer, :pointer, :pointer], :pointer - attach_function :ucsdet_getName, "ucsdet_getName#{suffix}", [:pointer, :pointer], :string - attach_function :ucsdet_getConfidence, "ucsdet_getConfidence#{suffix}", [:pointer, :pointer], :int32_t - attach_function :ucsdet_getLanguage, "ucsdet_getLanguage#{suffix}", [:pointer, :pointer], :string - attach_function :ucsdet_getAllDetectableCharsets, "ucsdet_getAllDetectableCharsets#{suffix}", [:pointer, :pointer], :pointer - attach_function :ucsdet_isInputFilterEnabled, "ucsdet_isInputFilterEnabled#{suffix}", [:pointer], :bool - attach_function :ucsdet_enableInputFilter, "ucsdet_enableInputFilter#{suffix}", [:pointer, :bool], :bool + attach_function :ucsdet_setText, "ucsdet_setText#{suffix}", + [:pointer, :pointer, :int32_t, :pointer], :void + attach_function :ucsdet_setDeclaredEncoding, "ucsdet_setDeclaredEncoding#{suffix}", + [:pointer, :string, :int32_t, :pointer], :void + attach_function :ucsdet_detect, "ucsdet_detect#{suffix}", + [:pointer, :pointer], :pointer + attach_function :ucsdet_detectAll, "ucsdet_detectAll#{suffix}", + [:pointer, :pointer, :pointer], :pointer + attach_function :ucsdet_getName, "ucsdet_getName#{suffix}", + [:pointer, :pointer], :string + attach_function :ucsdet_getConfidence, "ucsdet_getConfidence#{suffix}", + [:pointer, :pointer], :int32_t + attach_function :ucsdet_getLanguage, "ucsdet_getLanguage#{suffix}", + [:pointer, :pointer], :string + attach_function :ucsdet_getAllDetectableCharsets, "ucsdet_getAllDetectableCharsets#{suffix}", + [:pointer, :pointer], :pointer + attach_function :ucsdet_isInputFilterEnabled, "ucsdet_isInputFilterEnabled#{suffix}", [:pointer], :bool + attach_function :ucsdet_enableInputFilter, "ucsdet_enableInputFilter#{suffix}", + [:pointer, :bool], :bool # Collation # @@ -297,21 +335,25 @@ def self.attach_optional_function(*args) attach_function :ucol_open, "ucol_open#{suffix}", [:string, :pointer], :pointer attach_function :ucol_close, "ucol_close#{suffix}", [:pointer], :void - attach_function :ucol_strcoll, "ucol_strcoll#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t], :int + attach_function :ucol_strcoll, "ucol_strcoll#{suffix}", + [:pointer, :pointer, :int32_t, :pointer, :int32_t], :int attach_function :ucol_getKeywords, "ucol_getKeywords#{suffix}", [:pointer], :pointer attach_function :ucol_getKeywordValues, "ucol_getKeywordValues#{suffix}", [:string, :pointer], :pointer attach_function :ucol_getAvailable, "ucol_getAvailable#{suffix}", [:int32_t], :string attach_function :ucol_countAvailable, "ucol_countAvailable#{suffix}", [], :int32_t - attach_function :ucol_getLocale, "ucol_getLocale#{suffix}", [:pointer, :int, :pointer], :string - attach_function :ucol_greater, "ucol_greater#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t], :bool - attach_function :ucol_greaterOrEqual, "ucol_greaterOrEqual#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t], :bool - attach_function :ucol_equal, "ucol_equal#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t], :bool - attach_function :ucol_getRules, "ucol_getRules#{suffix}", [:pointer, :pointer], :pointer - attach_function :ucol_getSortKey, "ucol_getSortKey#{suffix}", [:pointer, :pointer, :int, :pointer, :int], :int + attach_function :ucol_getLocale, "ucol_getLocale#{suffix}", [:pointer, :int, :pointer], :string + attach_function :ucol_greater, "ucol_greater#{suffix}", + [:pointer, :pointer, :int32_t, :pointer, :int32_t], :bool + attach_function :ucol_greaterOrEqual, "ucol_greaterOrEqual#{suffix}", + [:pointer, :pointer, :int32_t, :pointer, :int32_t], :bool + attach_function :ucol_equal, "ucol_equal#{suffix}", + [:pointer, :pointer, :int32_t, :pointer, :int32_t], :bool + attach_function :ucol_getRules, "ucol_getRules#{suffix}", [:pointer, :pointer], :pointer + attach_function :ucol_getSortKey, "ucol_getSortKey#{suffix}", + [:pointer, :pointer, :int, :pointer, :int], :int attach_function :ucol_getAttribute, "ucol_getAttribute#{suffix}", [:pointer, :int, :pointer], :int attach_function :ucol_setAttribute, "ucol_setAttribute#{suffix}", [:pointer, :int, :int, :pointer], :void - # Transliteration # # http://icu-project.org/apiref/icu4c/utrans_8h.html @@ -324,7 +366,7 @@ class UParseError < FFI::Struct :post_context, :pointer def to_s - "#<%s:%x line: %d offset: %d" % [self.class, hash*2, self[:line], self[:offset]] + "#<%s:%x line: %d offset: %d" % [self.class, hash * 2, self[:line], self[:offset]] end end @@ -333,40 +375,45 @@ class UTransPosition < FFI::Struct :context_limit, :int32_t, :start, :int32_t, :end, :int32_t - end enum :trans_direction, [:forward, :reverse] - attach_function :utrans_openIDs, "utrans_openIDs#{suffix}", [:pointer], :pointer - attach_function :utrans_openU, "utrans_openU#{suffix}", [:pointer, :int32_t, :trans_direction, :pointer, :int32_t, :pointer, :pointer], :pointer - attach_function :utrans_open, "utrans_open#{suffix}", [:string, :trans_direction, :pointer, :int32_t, :pointer, :pointer], :pointer - attach_function :utrans_close, "utrans_close#{suffix}", [:pointer], :void - attach_function :utrans_transUChars, "utrans_transUChars#{suffix}", [:pointer, :pointer, :pointer, :int32_t, :int32_t, :pointer, :pointer], :void + attach_function :utrans_openIDs, "utrans_openIDs#{suffix}", [:pointer], :pointer + attach_function :utrans_openU, "utrans_openU#{suffix}", + [:pointer, :int32_t, :trans_direction, :pointer, :int32_t, :pointer, :pointer], :pointer + attach_function :utrans_open, "utrans_open#{suffix}", + [:string, :trans_direction, :pointer, :int32_t, :pointer, :pointer], :pointer + attach_function :utrans_close, "utrans_close#{suffix}", [:pointer], :void + attach_function :utrans_transUChars, "utrans_transUChars#{suffix}", + [:pointer, :pointer, :pointer, :int32_t, :int32_t, :pointer, :pointer], :void # Normalization # # http://icu-project.org/apiref/icu4c/unorm_8h.html # - enum :normalization_mode, [ :none, 1, - :nfd, 2, - :nfkd, 3, - :nfc, 4, - :default, 4, - :nfkc, 5, - :fcd, 6 - ] + enum :normalization_mode, [:none, 1, + :nfd, 2, + :nfkd, 3, + :nfc, 4, + :default, 4, + :nfkc, 5, + :fcd, 6] - attach_function :unorm_normalize, "unorm_normalize#{suffix}", [:pointer, :int32_t, :normalization_mode, :int32_t, :pointer, :int32_t, :pointer], :int32_t + attach_function :unorm_normalize, "unorm_normalize#{suffix}", + [:pointer, :int32_t, :normalization_mode, :int32_t, :pointer, :int32_t, :pointer], :int32_t # http://icu-project.org/apiref/icu4c/unorm2_8h.html if Gem::Version.new('4.4') <= Gem::Version.new(self.version) - enum :normalization2_mode, [ :compose, :decompose, :fcd, :compose_contiguous ] - attach_function :unorm2_getInstance, "unorm2_getInstance#{suffix}", [:pointer, :pointer, :normalization2_mode, :pointer], :pointer - attach_function :unorm2_normalize, "unorm2_normalize#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t - attach_function :unorm2_isNormalized, "unorm2_isNormalized#{suffix}", [:pointer, :pointer, :int32_t, :pointer], :bool + enum :normalization2_mode, [:compose, :decompose, :fcd, :compose_contiguous] + attach_function :unorm2_getInstance, "unorm2_getInstance#{suffix}", + [:pointer, :pointer, :normalization2_mode, :pointer], :pointer + attach_function :unorm2_normalize, "unorm2_normalize#{suffix}", + [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t + attach_function :unorm2_isNormalized, "unorm2_isNormalized#{suffix}", [:pointer, :pointer, :int32_t, :pointer], + :bool end # @@ -375,25 +422,26 @@ class UTransPosition < FFI::Struct # http://icu-project.org/apiref/icu4c/ubrk_8h.html # - enum :iterator_type, [ :character, :word, :line, :sentence, :title] - enum :word_break, [ :none, 0, - :none_limit, 100, - :number, 100, - :number_limit, 200, - :letter, 200, - :letter_limit, 300, - :kana, 300, - :kana_limit, 400, - :ideo, 400, - :ideo_limit, 400 - ] + enum :iterator_type, [:character, :word, :line, :sentence, :title] + enum :word_break, [:none, 0, + :none_limit, 100, + :number, 100, + :number_limit, 200, + :letter, 200, + :letter_limit, 300, + :kana, 300, + :kana_limit, 400, + :ideo, 400, + :ideo_limit, 400] attach_function :ubrk_countAvailable, "ubrk_countAvailable#{suffix}", [], :int32_t attach_function :ubrk_getAvailable, "ubrk_getAvailable#{suffix}", [:int32_t], :string - attach_function :ubrk_open, "ubrk_open#{suffix}", [:iterator_type, :string, :pointer, :int32_t, :pointer], :pointer - attach_function :ubrk_close, "ubrk_close#{suffix}", [:pointer], :void - attach_function :ubrk_setText, "ubrk_setText#{suffix}", [:pointer, :pointer, :int32_t, :pointer], :void + attach_function :ubrk_open, "ubrk_open#{suffix}", + [:iterator_type, :string, :pointer, :int32_t, :pointer], :pointer + attach_function :ubrk_close, "ubrk_close#{suffix}", [:pointer], :void + attach_function :ubrk_setText, "ubrk_setText#{suffix}", + [:pointer, :pointer, :int32_t, :pointer], :void attach_function :ubrk_current, "ubrk_current#{suffix}", [:pointer], :int32_t attach_function :ubrk_next, "ubrk_next#{suffix}", [:pointer], :int32_t attach_function :ubrk_previous, "ubrk_previous#{suffix}", [:pointer], :int32_t @@ -422,26 +470,34 @@ class UTransPosition < FFI::Struct :ignore ] enum :number_format_attribute, [ - :parse_int_only, :grouping_used, :decimal_always_show, :max_integer_digits, - :min_integer_digits, :integer_digits, :max_fraction_digits, :min_fraction_digits, - :fraction_digits, :multiplier, :grouping_size, :rounding_mode, - :rounding_increment, :format_width, :padding_position, :secondary_grouping_size, - :significant_digits_used, :min_significant_digits, :max_significant_digits, :lenient_parse + :parse_int_only, :grouping_used, :decimal_always_show, :max_integer_digits, + :min_integer_digits, :integer_digits, :max_fraction_digits, :min_fraction_digits, + :fraction_digits, :multiplier, :grouping_size, :rounding_mode, + :rounding_increment, :format_width, :padding_position, :secondary_grouping_size, + :significant_digits_used, :min_significant_digits, :max_significant_digits, :lenient_parse ] - attach_function :unum_open, "unum_open#{suffix}", [:number_format_style, :pointer, :int32_t, :string, :pointer, :pointer ], :pointer + attach_function :unum_open, "unum_open#{suffix}", + [:number_format_style, :pointer, :int32_t, :string, :pointer, :pointer], :pointer attach_function :unum_close, "unum_close#{suffix}", [:pointer], :void - attach_function :unum_format_int32, "unum_format#{suffix}", [:pointer, :int32_t, :pointer, :int32_t, :pointer, :pointer], :int32_t - attach_function :unum_format_int64, "unum_formatInt64#{suffix}", [:pointer, :int64_t, :pointer, :int32_t, :pointer, :pointer], :int32_t - attach_function :unum_format_double, "unum_formatDouble#{suffix}", [:pointer, :double, :pointer, :int32_t, :pointer, :pointer], :int32_t - attach_optional_function :unum_format_decimal, "unum_formatDecimal#{suffix}", [:pointer, :string, :int32_t, :pointer, :int32_t, :pointer, :pointer], :int32_t - attach_function :unum_format_currency, "unum_formatDoubleCurrency#{suffix}", [:pointer, :double, :pointer, :pointer, :int32_t, :pointer, :pointer], :int32_t - attach_function :unum_set_attribute, "unum_setAttribute#{suffix}", [:pointer, :number_format_attribute, :int32_t], :void + attach_function :unum_format_int32, "unum_format#{suffix}", + [:pointer, :int32_t, :pointer, :int32_t, :pointer, :pointer], :int32_t + attach_function :unum_format_int64, "unum_formatInt64#{suffix}", + [:pointer, :int64_t, :pointer, :int32_t, :pointer, :pointer], :int32_t + attach_function :unum_format_double, "unum_formatDouble#{suffix}", + [:pointer, :double, :pointer, :int32_t, :pointer, :pointer], :int32_t + attach_optional_function :unum_format_decimal, "unum_formatDecimal#{suffix}", + [:pointer, :string, :int32_t, :pointer, :int32_t, :pointer, :pointer], :int32_t + attach_function :unum_format_currency, "unum_formatDoubleCurrency#{suffix}", + [:pointer, :double, :pointer, :pointer, :int32_t, :pointer, :pointer], :int32_t + attach_function :unum_set_attribute, "unum_setAttribute#{suffix}", [:pointer, :number_format_attribute, :int32_t], + :void # UResourceBundle attach_function :ures_open, "ures_open#{suffix}", [:string, :string, :pointer], :pointer attach_function :ures_close, "ures_close#{suffix}", [:pointer], :void # This function is marked "internal" but it's fully exported by the library ABI, so we can use it anyway. - attach_function :ures_getBykeyWithFallback, "ures_getByKeyWithFallback#{suffix}", [:pointer, :string, :pointer, :pointer], :pointer + attach_function :ures_getBykeyWithFallback, "ures_getByKeyWithFallback#{suffix}", + [:pointer, :string, :pointer, :pointer], :pointer attach_function :ures_getString, "ures_getString#{suffix}", [:pointer, :pointer, :pointer], :pointer def self.resource_bundle_name(type) @@ -450,12 +506,15 @@ def self.resource_bundle_name(type) end # UNumberFormatter - attach_optional_function :unumf_openForSkeletonAndLocale, "unumf_openForSkeletonAndLocale#{suffix}", [:pointer, :int32_t, :string, :pointer], :pointer + attach_optional_function :unumf_openForSkeletonAndLocale, "unumf_openForSkeletonAndLocale#{suffix}", + [:pointer, :int32_t, :string, :pointer], :pointer attach_optional_function :unumf_close, "unumf_close#{suffix}", [:pointer], :void attach_optional_function :unumf_openResult, "unumf_openResult#{suffix}", [:pointer], :pointer attach_optional_function :unumf_closeResult, "unumf_closeResult#{suffix}", [:pointer], :void - attach_optional_function :unumf_formatDecimal, "unumf_formatDecimal#{suffix}", [:pointer, :string, :int32_t, :pointer, :pointer], :void - attach_optional_function :unumf_resultToString, "unumf_resultToString#{suffix}", [:pointer, :pointer, :int32_t, :pointer], :int32_t + attach_optional_function :unumf_formatDecimal, "unumf_formatDecimal#{suffix}", + [:pointer, :string, :int32_t, :pointer, :pointer], :void + attach_optional_function :unumf_resultToString, "unumf_resultToString#{suffix}", + [:pointer, :pointer, :int32_t, :pointer], :int32_t # UListFormatter enum :ulistfmt_type, [ @@ -468,9 +527,11 @@ def self.resource_bundle_name(type) :short, 1, :narrow, 2, ] - attach_optional_function :ulistfmt_openForType, "ulistfmt_openForType#{suffix}", [:string, :ulistfmt_type, :ulistfmt_width, :pointer], :pointer + attach_optional_function :ulistfmt_openForType, "ulistfmt_openForType#{suffix}", + [:string, :ulistfmt_type, :ulistfmt_width, :pointer], :pointer attach_optional_function :ulistfmt_close, "ulistfmt_close#{suffix}", [:pointer], :void - attach_optional_function :ulistfmt_format, "ulistfmt_format#{suffix}", [:pointer, :pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t + attach_optional_function :ulistfmt_format, "ulistfmt_format#{suffix}", + [:pointer, :pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t # date enum :date_format_style, [ @@ -485,24 +546,34 @@ def self.resource_bundle_name(type) :actual_locale, 0, :valid_locale, 1, ] - attach_function :udat_open, "udat_open#{suffix}", [:date_format_style, :date_format_style, :string, :pointer, :int32_t, :pointer, :int32_t, :pointer ], :pointer + attach_function :udat_open, "udat_open#{suffix}", + [:date_format_style, :date_format_style, :string, :pointer, :int32_t, :pointer, :int32_t, :pointer], + :pointer attach_function :udat_close, "unum_close#{suffix}", [:pointer], :void - attach_function :udat_format, "udat_format#{suffix}", [:pointer, :double, :pointer, :int32_t, :pointer, :pointer], :int32_t + attach_function :udat_format, "udat_format#{suffix}", [:pointer, :double, :pointer, :int32_t, :pointer, :pointer], + :int32_t attach_function :udat_parse, "udat_parse#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :pointer], :double - attach_function :udat_toPattern, "udat_toPattern#{suffix}", [:pointer, :bool , :pointer, :int32_t , :pointer], :int32_t - attach_function :udat_applyPattern, "udat_applyPattern#{suffix}", [:pointer, :bool , :pointer, :int32_t ], :void + attach_function :udat_toPattern, "udat_toPattern#{suffix}", + [:pointer, :bool, :pointer, :int32_t, :pointer], :int32_t + attach_function :udat_applyPattern, "udat_applyPattern#{suffix}", [:pointer, :bool, :pointer, :int32_t], + :void # skeleton pattern attach_function :udatpg_open, "udatpg_open#{suffix}", [:string, :pointer], :pointer attach_function :udatpg_close, "udatpg_close#{suffix}", [:pointer], :void - attach_function :udatpg_getBestPattern, "udatpg_getBestPattern#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t - attach_function :udatpg_getSkeleton, "udatpg_getSkeleton#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t + attach_function :udatpg_getBestPattern, "udatpg_getBestPattern#{suffix}", + [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t + attach_function :udatpg_getSkeleton, "udatpg_getSkeleton#{suffix}", + [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t # tz attach_function :ucal_setDefaultTimeZone, "ucal_setDefaultTimeZone#{suffix}", [:pointer, :pointer], :int32_t - attach_function :ucal_getDefaultTimeZone, "ucal_getDefaultTimeZone#{suffix}", [:pointer, :int32_t, :pointer], :int32_t + attach_function :ucal_getDefaultTimeZone, "ucal_getDefaultTimeZone#{suffix}", [:pointer, :int32_t, :pointer], + :int32_t # ULocaleDisplayNames - attach_function :uldn_openForContext, "uldn_openForContext#{suffix}", [:string, :pointer, :int32_t, :pointer], :pointer - attach_function :uldn_localeDisplayName, "uldn_localeDisplayName#{suffix}", [:pointer, :string, :pointer, :int32_t, :pointer], :int32_t + attach_function :uldn_openForContext, "uldn_openForContext#{suffix}", [:string, :pointer, :int32_t, :pointer], + :pointer + attach_function :uldn_localeDisplayName, "uldn_localeDisplayName#{suffix}", + [:pointer, :string, :pointer, :int32_t, :pointer], :int32_t attach_function :uldn_close, "uldn_close#{suffix}", [:pointer], :void end # Lib end # ICU diff --git a/lib/ffi-icu/locale.rb b/lib/ffi-icu/locale.rb index 569a4d5..687690a 100644 --- a/lib/ffi-icu/locale.rb +++ b/lib/ffi-icu/locale.rb @@ -43,8 +43,8 @@ def iso_languages attr_reader :id DISPLAY_CONTEXT = { - length_full: 512, # UDISPCTX_LENGTH_FULL = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 0 - length_short: 513 # UDISPCTX_LENGTH_SHORT = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 1 + length_full: 512, # UDISPCTX_LENGTH_FULL = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 0 + length_short: 513 # UDISPCTX_LENGTH_SHORT = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 1 } def initialize(id) @@ -238,7 +238,9 @@ def with_minimized_subtags def with_locale_display_name(locale, contexts) pointer = FFI::MemoryPointer.new(:int, contexts.length).write_array_of_int(contexts) - locale_display_names = ICU::Lib.check_error { |status| ICU::Lib.uldn_openForContext(locale, pointer, contexts.length, status) } + locale_display_names = ICU::Lib.check_error { |status| + ICU::Lib.uldn_openForContext(locale, pointer, contexts.length, status) + } yield locale_display_names ensure diff --git a/lib/ffi-icu/normalization.rb b/lib/ffi-icu/normalization.rb index beba68d..fadb088 100644 --- a/lib/ffi-icu/normalization.rb +++ b/lib/ffi-icu/normalization.rb @@ -1,6 +1,5 @@ module ICU module Normalization - def self.normalize(input, mode = :default) input_length = input.jlength needed_length = out_length = options = 0 @@ -25,6 +24,5 @@ def self.normalize(input, mode = :default) out_ptr.string end - end # Normalization end # ICU diff --git a/lib/ffi-icu/normalizer.rb b/lib/ffi-icu/normalizer.rb index f90060e..592e6e2 100644 --- a/lib/ffi-icu/normalizer.rb +++ b/lib/ffi-icu/normalizer.rb @@ -42,6 +42,5 @@ def is_normailzed?(input) result end - end # Normalizer end # ICU diff --git a/lib/ffi-icu/number_formatting.rb b/lib/ffi-icu/number_formatting.rb index 5614dd0..f1d593f 100644 --- a/lib/ffi-icu/number_formatting.rb +++ b/lib/ffi-icu/number_formatting.rb @@ -3,7 +3,7 @@ module ICU module NumberFormatting @default_options = {} - + def self.create(locale, type = :decimal, options = {}) case type when :currency @@ -38,7 +38,6 @@ def self.spell(locale, number, options = {}) end class BaseFormatter - def set_attributes(options) options.each { |key, value| Lib.unum_set_attribute(@f, key, value) } self @@ -47,7 +46,9 @@ def set_attributes(options) private def make_formatter(type, locale) - ptr = Lib.check_error { | error| Lib.unum_open(type, FFI::MemoryPointer.new(4), 0, locale, FFI::MemoryPointer.new(4), error) } + ptr = Lib.check_error { |error| + Lib.unum_open(type, FFI::MemoryPointer.new(4), 0, locale, FFI::MemoryPointer.new(4), error) + } FFI::AutoPointer.new(ptr, Lib.method(:unum_close)) end end @@ -75,16 +76,18 @@ def format(number) rescue RangeError # Fall back to stringifying in Ruby and passing that to ICU unless defined? Lib.unum_format_decimal - raise RangeError,"Number #{number} is too big to fit in int64_t and your "\ + raise RangeError, "Number #{number} is too big to fit in int64_t and your " \ "ICU version is too old to have unum_format_decimal" end string_version = number.to_s - needed_length = Lib.unum_format_decimal(@f, string_version, string_version.bytesize, out_ptr, needed_length, nil, error) + needed_length = Lib.unum_format_decimal(@f, string_version, string_version.bytesize, out_ptr, + needed_length, nil, error) end when BigDecimal string_version = number.to_s('F') if Lib.respond_to? :unum_format_decimal - needed_length = Lib.unum_format_decimal(@f, string_version, string_version.bytesize, out_ptr, needed_length, nil, error) + needed_length = Lib.unum_format_decimal(@f, string_version, string_version.bytesize, out_ptr, + needed_length, nil, error) else needed_length = Lib.unum_format_double(@f, number.to_f, out_ptr, needed_length, nil, error) end @@ -93,6 +96,7 @@ def format(number) out_ptr.string needed_length rescue BufferOverflowError raise BufferOverflowError, "needed: #{needed_length}" if retried + out_ptr = out_ptr.resized_to needed_length retried = true retry @@ -106,10 +110,12 @@ def initialize(locale, style = :default) if Lib.version.to_a.first >= 53 style = "currency_#{style}".to_sym else - fail "Your version of ICU (#{Lib.version.to_a.join('.')}) does not support #{style} currency formatting (supported only in version >= 53)" + fail "Your version of ICU (#{Lib.version.to_a.join('.')}) does not " \ + "support #{style} currency formatting (supported only in version >= 53)" end elsif style && style.to_sym != :default - fail "The ffi-icu ruby gem does not support :#{default} currency formatting (only :default, :iso, and :plural)" + fail "The ffi-icu ruby gem does not support :#{default} currency formatting " \ + "(only :default, :iso, and :plural)" else style = :currency end @@ -124,11 +130,13 @@ def format(number, currency) begin Lib.check_error do |error| - needed_length = Lib.unum_format_currency(@f, number, UCharPointer.from_string(currency, 4), out_ptr, needed_length, nil, error) + needed_length = Lib.unum_format_currency(@f, number, UCharPointer.from_string(currency, 4), out_ptr, + needed_length, nil, error) end out_ptr.string rescue BufferOverflowError raise BufferOverflowError, "needed: #{needed_length}" if retried + out_ptr = out_ptr.resized_to needed_length retried = true retry diff --git a/lib/ffi-icu/time_formatting.rb b/lib/ffi-icu/time_formatting.rb index c7a4f1e..7b48d6b 100644 --- a/lib/ffi-icu/time_formatting.rb +++ b/lib/ffi-icu/time_formatting.rb @@ -3,40 +3,50 @@ module ICU module TimeFormatting TZ_MAP = { - :generic_location => 'VVVV',# The generic location format. - # Where that is unavailable, falls back to the long localized GMT format ("OOOO"; - # Note: Fallback is only necessary with a GMT-style Time Zone ID, like Etc/GMT-830.), - # This is especially useful when presenting possible timezone choices for user selection, - # since the naming is more uniform than the "v" format. - # such as "United States Time (New York)", "Italy Time" - :generic_long => 'vvvv', # The long generic non-location format. - # Where that is unavailable, falls back to generic location format ("VVVV")., such as "Eastern Time". - :generic_short => 'v', # The short generic non-location format. - # Where that is unavailable, falls back to the generic location format ("VVVV"), - # then the short localized GMT format as the final fallback., such as "ET". - :specific_long => 'zzzz', # The long specific non-location format. - # Where that is unavailable, falls back to the long localized GMT format ("OOOO"). - :specific_short => 'z', # The short specific non-location format. - # Where that is unavailable, falls back to the short localized GMT format ("O"). - :basic => 'Z', # The ISO8601 basic format with hours, minutes and optional seconds fields. - # The format is equivalent to RFC 822 zone format (when optional seconds field is absent). - # This is equivalent to the "xxxx" specifier. - :localized_long => 'ZZZZ', # The long localized GMT format. This is equivalent to the "OOOO" specifier, such as GMT-8:00 - :extended => 'ZZZZZ', # The ISO8601 extended format with hours, minutes and optional seconds fields. - # The ISO8601 UTC indicator "Z" is used when local time offset is 0. - # This is equivalent to the "XXXXX" specifier, such as -08:00 -07:52:58 - :localized_short => 'O', # The short localized GMT format, such as GMT-8 - :localized_longO => 'OOOO', # The long localized GMT format, such as GMT-08:00 - :tz_id_short => 'V', # The short time zone ID. Where that is unavailable, - # the special short time zone ID unk (Unknown Zone) is used. - # Note: This specifier was originally used for a variant of the short specific non-location format, - # but it was deprecated in the later version of this specification. In CLDR 23, the definition - # of the specifier was changed to designate a short time zone ID, such as uslax - :tz_id_long => 'VV', # The long time zone ID, such as America/Los_Angeles - :city_location => 'VVV', # The exemplar city (location) for the time zone. Where that is unavailable, - # the localized exemplar city name for the special zone Etc/Unknown is used as the fallback - # (for example, "Unknown City"), such as Los Angeles + # The generic location format. + # Where that is unavailable, falls back to the long localized GMT format ("OOOO"; + # Note: Fallback is only necessary with a GMT-style Time Zone ID, like Etc/GMT-830.), + # This is especially useful when presenting possible timezone choices for user selection, + # since the naming is more uniform than the "v" format. + # such as "United States Time (New York)", "Italy Time" + :generic_location => 'VVVV', + # The long generic non-location format. + # Where that is unavailable, falls back to generic location format ("VVVV")., such as "Eastern Time". + :generic_long => 'vvvv', + # The short generic non-location format. + # Where that is unavailable, falls back to the generic location format ("VVVV"), + # then the short localized GMT format as the final fallback., such as "ET". + :generic_short => 'v', + # The long specific non-location format. + # Where that is unavailable, falls back to the long localized GMT format ("OOOO"). + :specific_long => 'zzzz', + # The short specific non-location format. + # Where that is unavailable, falls back to the short localized GMT format ("O"). + :specific_short => 'z', + # The ISO8601 basic format with hours, minutes and optional seconds fields. + # The format is equivalent to RFC 822 zone format (when optional seconds field is absent). + # This is equivalent to the "xxxx" specifier. + # The long localized GMT format. This is equivalent to the "OOOO" specifier, such as GMT-8:00 + :basic => 'Z', + :localized_long => 'ZZZZ', + # The ISO8601 extended format with hours, minutes and optional seconds fields. + # The ISO8601 UTC indicator "Z" is used when local time offset is 0. + # This is equivalent to the "XXXXX" specifier, such as -08:00 -07:52:58 + :extended => 'ZZZZZ', + :localized_short => 'O', # The short localized GMT format, such as GMT-8 + :localized_longO => 'OOOO', # The long localized GMT format, such as GMT-08:00 + # The short time zone ID. Where that is unavailable, + # the special short time zone ID unk (Unknown Zone) is used. + # Note: This specifier was originally used for a variant of the short specific non-location format, + # but it was deprecated in the later version of this specification. In CLDR 23, the definition + # of the specifier was changed to designate a short time zone ID, such as uslax + :tz_id_short => 'V', + :tz_id_long => 'VV', # The long time zone ID, such as America/Los_Angeles + # The exemplar city (location) for the time zone. Where that is unavailable, + # the localized exemplar city name for the special zone Etc/Unknown is used as the fallback + # (for example, "Unknown City"), such as Los Angeles # see: http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns + :city_location => 'VVV', } HOUR_CYCLE_SYMS = { @@ -47,7 +57,7 @@ module TimeFormatting :locale => 'j', } @default_options = {} - + def self.create(options = {}) DateTimeFormatter.new(@default_options.merge(options)) end @@ -65,7 +75,6 @@ def self.format(dt, options = {}) end class BaseFormatter - def set_attributes(options) options.each { |key, value| Lib.unum_set_attribute(@f, key, value) } self @@ -83,11 +92,11 @@ def make_formatter(time_style, date_style, locale, time_zone_str, skeleton) time_zone = UCharPointer.from_string(time_zone_str) tz_len = time_zone_str.size else - Lib.check_error { | error| - i_len = 150 + Lib.check_error do |error| + i_len = 150 time_zone = UCharPointer.new(i_len) - tz_len = Lib.ucal_getDefaultTimeZone(time_zone, i_len, error) - } + tz_len = Lib.ucal_getDefaultTimeZone(time_zone, i_len, error) + end end if skeleton @@ -97,13 +106,16 @@ def make_formatter(time_style, date_style, locale, time_zone_str, skeleton) pattern_len, pattern_ptr = skeleton_format(skeleton, locale) end - ptr = Lib.check_error { | error| Lib.udat_open(time_style, date_style, locale, time_zone, tz_len, pattern_ptr, pattern_len, error) } + ptr = Lib.check_error do |error| + Lib.udat_open(time_style, date_style, locale, time_zone, tz_len, pattern_ptr, pattern_len, error) + end + FFI::AutoPointer.new(ptr, Lib.method(:udat_close)) end end class DateTimeFormatter < BaseFormatter - def initialize(options={}) + def initialize(options = {}) time_style = options[:time] || :short date_style = options[:date] || :short @locale = options[:locale] || 'C' @@ -119,7 +131,7 @@ def initialize(options={}) @f = make_formatter(time_style, date_style, @locale, time_zone, skeleton) if tz_style f0 = date_format(true) - f1 = update_tz_format(f0, tz_style) + f1 = update_tz_format(f0, tz_style) if f1 != f0 set_date_format(true, f1) end @@ -129,12 +141,12 @@ def initialize(options={}) end def parse(str) - str_u = UCharPointer.from_string(str) - str_l = str.size - Lib.check_error do |error| - ret = Lib.udat_parse(@f, str_u, str_l, nil, error) - Time.at(ret / 1000.0) - end + str_u = UCharPointer.from_string(str) + str_l = str.size + Lib.check_error do |error| + ret = Lib.udat_parse(@f, str_u, str_l, nil, error) + Time.at(ret / 1000.0) + end end def format(dt) @@ -147,7 +159,8 @@ def format(dt) Lib.check_error do |error| case dt when Date - needed_length = Lib.udat_format(@f, Time.mktime( dt.year, dt.month, dt.day, 0, 0, 0, 0 ).to_f * 1000.0, out_ptr, needed_length, nil, error) + needed_length = Lib.udat_format(@f, Time.mktime(dt.year, dt.month, dt.day, 0, 0, 0, 0).to_f * 1000.0, + out_ptr, needed_length, nil, error) when Time needed_length = Lib.udat_format(@f, dt.to_f * 1000.0, out_ptr, needed_length, nil, error) end @@ -156,6 +169,7 @@ def format(dt) out_ptr.string rescue BufferOverflowError raise BufferOverflowError, "needed: #{needed_length}" if retried + out_ptr = out_ptr.resized_to needed_length retried = true retry @@ -165,18 +179,20 @@ def format(dt) # time-zone formating def update_tz_format(format, tz_style) return format if format !~ /(.*?)(\s*(?:[zZOVV]+\s*))(.*?)/ + pre, tz, suff = $1, $2, $3 if tz_style == :none tz = ((tz =~ /\s/) && !pre.empty? && !suff.empty?) ? ' ' : '' else repl = TZ_MAP[tz_style] raise 'no such tz_style' unless repl - tz.gsub!(/^(\s*)(.*?)(\s*)$/, '\1'+repl+'\3') + + tz.gsub!(/^(\s*)(.*?)(\s*)$/, '\1' + repl + '\3') end pre + tz + suff end - def date_format(localized=true) + def date_format(localized = true) needed_length = 0 out_ptr = UCharPointer.new(needed_length) @@ -190,6 +206,7 @@ def date_format(localized=true) out_ptr.string rescue BufferOverflowError raise BufferOverflowError, "needed: #{needed_length}" if retried + out_ptr = out_ptr.resized_to needed_length retried = true retry @@ -205,25 +222,27 @@ def set_date_format(localized, pattern_str) end def skeleton_format(skeleton_pattern_str, locale) - skeleton_pattern_ptr = UCharPointer.from_string(skeleton_pattern_str) - skeleton_pattern_len = skeleton_pattern_str.size + skeleton_pattern_ptr = UCharPointer.from_string(skeleton_pattern_str) + skeleton_pattern_len = skeleton_pattern_str.size - needed_length = 0 - pattern_ptr = UCharPointer.new(needed_length) + needed_length = 0 + pattern_ptr = UCharPointer.new(needed_length) - udatpg_ptr = Lib.check_error { |error| Lib.udatpg_open(locale, error) } - generator = FFI::AutoPointer.new(udatpg_ptr, Lib.method(:udatpg_close)) + udatpg_ptr = Lib.check_error { |error| Lib.udatpg_open(locale, error) } + generator = FFI::AutoPointer.new(udatpg_ptr, Lib.method(:udatpg_close)) - retried = false + retried = false begin Lib.check_error do |error| - needed_length = Lib.udatpg_getBestPattern(generator, skeleton_pattern_ptr, skeleton_pattern_len, pattern_ptr, needed_length, error) + needed_length = Lib.udatpg_getBestPattern(generator, skeleton_pattern_ptr, skeleton_pattern_len, + pattern_ptr, needed_length, error) end return needed_length, pattern_ptr rescue BufferOverflowError raise BufferOverflowError, "needed: #{needed_length}" if retried + pattern_ptr = pattern_ptr.resized_to needed_length retried = true retry diff --git a/lib/ffi-icu/transliteration.rb b/lib/ffi-icu/transliteration.rb index 85386da..b9d96b4 100644 --- a/lib/ffi-icu/transliteration.rb +++ b/lib/ffi-icu/transliteration.rb @@ -1,6 +1,5 @@ module ICU module Transliteration - class << self def transliterate(translit_id, str, rules = nil) t = Transliterator.new translit_id, rules @@ -21,7 +20,6 @@ def available_ids end class Transliterator - def initialize(id, rules = nil, direction = :forward) rules_length = 0 @@ -33,7 +31,8 @@ def initialize(id, rules = nil, direction = :forward) parse_error = Lib::UParseError.new begin Lib.check_error do |status| - ptr = Lib.utrans_openU(UCharPointer.from_string(id), id.jlength, direction, rules, rules_length, @parse_error, status) + ptr = Lib.utrans_openU(UCharPointer.from_string(id), id.jlength, direction, rules, rules_length, + @parse_error, status) @tr = FFI::AutoPointer.new(ptr, Lib.method(:utrans_close)) end rescue ICU::Error => ex @@ -80,7 +79,6 @@ def transliterate(from) buf.string text_length.get_int32(0) end - end # Transliterator end # Translit end # ICU diff --git a/lib/ffi-icu/uchar.rb b/lib/ffi-icu/uchar.rb index ad8c5c5..a2157e4 100644 --- a/lib/ffi-icu/uchar.rb +++ b/lib/ffi-icu/uchar.rb @@ -1,6 +1,5 @@ module ICU class UCharPointer < FFI::MemoryPointer - UCHAR_TYPE = :uint16 # not sure how platform-dependent this is.. TYPE_SIZE = FFI.type_size(UCHAR_TYPE) @@ -46,7 +45,5 @@ def string(length = nil) def length_in_uchars size / type_size end - - end # UCharPointer end # ICU diff --git a/spec/break_iterator_spec.rb b/spec/break_iterator_spec.rb index 8faf13f..96784c5 100644 --- a/spec/break_iterator_spec.rb +++ b/spec/break_iterator_spec.rb @@ -2,7 +2,6 @@ module ICU describe BreakIterator do - it "should return available locales" do locales = ICU::BreakIterator.available_locales expect(locales).to be_an(Array) @@ -12,9 +11,11 @@ module ICU it "finds all word boundaries in an English string" do iterator = BreakIterator.new :word, "en_US" - iterator.text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + iterator.text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " \ + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." expect(iterator.to_a).to eq( - [0, 5, 6, 11, 12, 17, 18, 21, 22, 26, 27, 28, 39, 40, 51, 52, 56, 57, 58, 61, 62, 64, 65, 72, 73, 79, 80, 90, 91, 93, 94, 100, 101, 103, 104, 110, 111, 116, 117, 123, 124] + [0, 5, 6, 11, 12, 17, 18, 21, 22, 26, 27, 28, 39, 40, 51, 52, 56, 57, 58, 61, 62, 64, 65, 72, 73, 79, 80, 90, + 91, 93, 94, 100, 101, 103, 104, 110, 111, 116, 117, 123, 124] ) end @@ -72,6 +73,5 @@ module ICU expect(iterator.each).to be_kind_of(Enumerator) end - end # BreakIterator end # ICU diff --git a/spec/chardet_spec.rb b/spec/chardet_spec.rb index 4043de0..4411b49 100644 --- a/spec/chardet_spec.rb +++ b/spec/chardet_spec.rb @@ -1,7 +1,6 @@ # encoding: UTF-8 describe ICU::CharDet::Detector do - let(:detector) { ICU::CharDet::Detector.new } it "should recognize UTF-8" do diff --git a/spec/duration_formatting_spec.rb b/spec/duration_formatting_spec.rb index 08f51c3..ffe8f36 100644 --- a/spec/duration_formatting_spec.rb +++ b/spec/duration_formatting_spec.rb @@ -1,143 +1,143 @@ module ICU - module DurationFormatting - describe 'DurationFormatting::format' do - before(:each) do - skip("Only works on ICU >= 67") if Lib.version.to_a[0] < 67 - end - - it 'produces hours, minutes, and seconds in order' do - result = DurationFormatting.format({hours: 1, minutes: 2, seconds: 3}, locale: 'C', style: :long) - expect(result).to match(/1.*hour.*2.*minute.*3.*second/i) - end - - it 'rounds down fractional seconds < 0.5' do - result = DurationFormatting.format({seconds: 5.4}, locale: 'C', style: :long) - expect(result).to match(/5.*second/i) - end - - it 'rounds up fractional seconds > 0.5' do - result = DurationFormatting.format({seconds: 5.6}, locale: 'C', style: :long) - expect(result).to match(/6.*second/i) - end - - it 'trims off leading zero values' do - result = DurationFormatting.format({hours: 0, minutes: 1, seconds: 30}, locale: 'C', style: :long) - expect(result).to match(/1.*minute.*30.*second/i) - expect(result).to_not match(/hour/i) - end - - it 'trims off leading missing values' do - result = DurationFormatting.format({minutes: 1, seconds: 30}, locale: 'C', style: :long) - expect(result).to match(/1.*minute.*30.*second/i) - expect(result).to_not match(/hour/i) - end - - it 'trims off non-leading zero values' do - result = DurationFormatting.format({hours: 1, minutes: 0, seconds: 10}, locale: 'C', style: :long) - expect(result).to match(/1.*hour.*10.*second/i) - expect(result).to_not match(/minute/i) - end - - it 'trims off non-leading missing values' do - result = DurationFormatting.format({hours: 1, seconds: 10}, locale: 'C', style: :long) - expect(result).to match(/1.*hour.*10.*second/i) - expect(result).to_not match(/minute/i) - end - - it 'uses comma-based number formatting as appropriate for locale' do - result = DurationFormatting.format({seconds: 90123}, locale: 'en-AU', style: :long) - expect(result).to match(/90,123.*second/i) - expect(result).to_not match(/hour/i) - expect(result).to_not match(/minute/i) - end - - it 'localizes unit names' do - result = DurationFormatting.format({hours: 1, minutes: 2, seconds: 3}, locale: 'el', style: :long) - expect(result).to match(/1.*ώρα.*2.*λεπτά.*3.*δευτερόλεπτα/i) - end - - it 'can format long' do - result = DurationFormatting.format({hours: 1, minutes: 2, seconds: 3}, locale: 'en-AU', style: :long) - expect(result).to match(/hour.*minute.*second/i) - end - - it 'can format short' do - result = DurationFormatting.format({hours: 1, minutes: 2, seconds: 3}, locale: 'en-AU', style: :short) - expect(result).to match(/hr.*min.*sec/i) - expect(result).to_not match(/hour/i) - expect(result).to_not match(/minute/i) - expect(result).to_not match(/second/i) - end - - it 'can format narrow' do - result = DurationFormatting.format({hours: 1, minutes: 2, seconds: 3}, locale: 'en-AU', style: :narrow) - expect(result).to match(/h.*min.*s/i) - expect(result).to_not match(/hr/i) - expect(result).to_not match(/sec/i) - end - - it 'can format digital' do - result = DurationFormatting.format({hours: 1, minutes: 2, seconds: 3}, locale: 'en-AU', style: :digital) - expect(result).to eql('1:02:03') - end - - it 'can format the full sequence of time units in order' do - duration = { - years: 1, - months: 2, - weeks: 3, - days: 4, - hours: 5, - minutes: 6, - seconds: 7, - milliseconds: 8, - microseconds: 9, - nanoseconds: 10, - } - result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) - expect(result).to match(/1.yr.*2.*mths.*3.*wks.*4.*days.*5.*hrs.*6.*mins.*7.*secs.*8.*ms.*9.*μs.*10.*ns/) - end - - it 'joins ms, us, ns values to seconds in digital format' do - duration = {minutes: 10, seconds: 5, milliseconds: 325, microseconds: 53, nanoseconds: 236} - result = DurationFormatting.format(duration, locale: 'en-AU', style: :digital) - expect(result).to eql('10:05.325053236') - end - - it 'includes trailing zeros as appropriate for the last unit in digital format' do - duration = {minutes: 10, seconds: 5, milliseconds: 325, microseconds: 400} - result = DurationFormatting.format(duration, locale: 'en-AU', style: :digital) - expect(result).to eql('10:05.325400') - end - - it 'joins h:mm:ss and other units in digital format' do - duration = {days: 8, hours: 23, minutes: 10, seconds: 9} - result = DurationFormatting.format(duration, locale: 'en-AU', style: :digital) - expect(result).to match(/8.*d.*23:10:09/ ) - end - - it 'ignores all decimal parts except the last, if it is seconds' do - duration = {hours: 7.3, minutes: 9.7, seconds: 8.93} - result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) - expect(result).to match(/7[^0-9]*hrs.*9[^0-9]*min.*8\.93[^0-9]*secs/) - end - - it 'ignores all decimal parts except the last, if it is milliseconds' do - duration = {hours: 7.3, minutes: 9.7, seconds: 8.93, milliseconds: 632.2} - result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) - expect(result).to match(/7[^0-9]*hrs.*9[^0-9]*min.*8[^0-9]*secs.*632\.2[^0-9]*ms/) - end - - it 'ignores all decimal parts including the last, if it is > seconds' do - duration = {hours: 7.3, minutes: 9.7} - result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) - expect(result).to match(/7[^0-9]*hrs.*9[^0-9]*min/) - end - - it 'raises on durations with any negative component' do - duration = {hours: 7.3, minutes: -9.7} - expect { DurationFormatting.format(duration, locale: 'en-AU') }.to raise_error(ArgumentError) - end - end + module DurationFormatting + describe 'DurationFormatting::format' do + before(:each) do + skip("Only works on ICU >= 67") if Lib.version.to_a[0] < 67 + end + + it 'produces hours, minutes, and seconds in order' do + result = DurationFormatting.format({ hours: 1, minutes: 2, seconds: 3 }, locale: 'C', style: :long) + expect(result).to match(/1.*hour.*2.*minute.*3.*second/i) + end + + it 'rounds down fractional seconds < 0.5' do + result = DurationFormatting.format({ seconds: 5.4 }, locale: 'C', style: :long) + expect(result).to match(/5.*second/i) + end + + it 'rounds up fractional seconds > 0.5' do + result = DurationFormatting.format({ seconds: 5.6 }, locale: 'C', style: :long) + expect(result).to match(/6.*second/i) + end + + it 'trims off leading zero values' do + result = DurationFormatting.format({ hours: 0, minutes: 1, seconds: 30 }, locale: 'C', style: :long) + expect(result).to match(/1.*minute.*30.*second/i) + expect(result).to_not match(/hour/i) + end + + it 'trims off leading missing values' do + result = DurationFormatting.format({ minutes: 1, seconds: 30 }, locale: 'C', style: :long) + expect(result).to match(/1.*minute.*30.*second/i) + expect(result).to_not match(/hour/i) + end + + it 'trims off non-leading zero values' do + result = DurationFormatting.format({ hours: 1, minutes: 0, seconds: 10 }, locale: 'C', style: :long) + expect(result).to match(/1.*hour.*10.*second/i) + expect(result).to_not match(/minute/i) + end + + it 'trims off non-leading missing values' do + result = DurationFormatting.format({ hours: 1, seconds: 10 }, locale: 'C', style: :long) + expect(result).to match(/1.*hour.*10.*second/i) + expect(result).to_not match(/minute/i) + end + + it 'uses comma-based number formatting as appropriate for locale' do + result = DurationFormatting.format({ seconds: 90123 }, locale: 'en-AU', style: :long) + expect(result).to match(/90,123.*second/i) + expect(result).to_not match(/hour/i) + expect(result).to_not match(/minute/i) + end + + it 'localizes unit names' do + result = DurationFormatting.format({ hours: 1, minutes: 2, seconds: 3 }, locale: 'el', style: :long) + expect(result).to match(/1.*ώρα.*2.*λεπτά.*3.*δευτερόλεπτα/i) + end + + it 'can format long' do + result = DurationFormatting.format({ hours: 1, minutes: 2, seconds: 3 }, locale: 'en-AU', style: :long) + expect(result).to match(/hour.*minute.*second/i) + end + + it 'can format short' do + result = DurationFormatting.format({ hours: 1, minutes: 2, seconds: 3 }, locale: 'en-AU', style: :short) + expect(result).to match(/hr.*min.*sec/i) + expect(result).to_not match(/hour/i) + expect(result).to_not match(/minute/i) + expect(result).to_not match(/second/i) + end + + it 'can format narrow' do + result = DurationFormatting.format({ hours: 1, minutes: 2, seconds: 3 }, locale: 'en-AU', style: :narrow) + expect(result).to match(/h.*min.*s/i) + expect(result).to_not match(/hr/i) + expect(result).to_not match(/sec/i) + end + + it 'can format digital' do + result = DurationFormatting.format({ hours: 1, minutes: 2, seconds: 3 }, locale: 'en-AU', style: :digital) + expect(result).to eql('1:02:03') + end + + it 'can format the full sequence of time units in order' do + duration = { + years: 1, + months: 2, + weeks: 3, + days: 4, + hours: 5, + minutes: 6, + seconds: 7, + milliseconds: 8, + microseconds: 9, + nanoseconds: 10, + } + result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) + expect(result).to match(/1.yr.*2.*mths.*3.*wks.*4.*days.*5.*hrs.*6.*mins.*7.*secs.*8.*ms.*9.*μs.*10.*ns/) + end + + it 'joins ms, us, ns values to seconds in digital format' do + duration = { minutes: 10, seconds: 5, milliseconds: 325, microseconds: 53, nanoseconds: 236 } + result = DurationFormatting.format(duration, locale: 'en-AU', style: :digital) + expect(result).to eql('10:05.325053236') + end + + it 'includes trailing zeros as appropriate for the last unit in digital format' do + duration = { minutes: 10, seconds: 5, milliseconds: 325, microseconds: 400 } + result = DurationFormatting.format(duration, locale: 'en-AU', style: :digital) + expect(result).to eql('10:05.325400') + end + + it 'joins h:mm:ss and other units in digital format' do + duration = { days: 8, hours: 23, minutes: 10, seconds: 9 } + result = DurationFormatting.format(duration, locale: 'en-AU', style: :digital) + expect(result).to match(/8.*d.*23:10:09/) + end + + it 'ignores all decimal parts except the last, if it is seconds' do + duration = { hours: 7.3, minutes: 9.7, seconds: 8.93 } + result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) + expect(result).to match(/7[^0-9]*hrs.*9[^0-9]*min.*8\.93[^0-9]*secs/) + end + + it 'ignores all decimal parts except the last, if it is milliseconds' do + duration = { hours: 7.3, minutes: 9.7, seconds: 8.93, milliseconds: 632.2 } + result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) + expect(result).to match(/7[^0-9]*hrs.*9[^0-9]*min.*8[^0-9]*secs.*632\.2[^0-9]*ms/) + end + + it 'ignores all decimal parts including the last, if it is > seconds' do + duration = { hours: 7.3, minutes: 9.7 } + result = DurationFormatting.format(duration, locale: 'en-AU', style: :short) + expect(result).to match(/7[^0-9]*hrs.*9[^0-9]*min/) + end + + it 'raises on durations with any negative component' do + duration = { hours: 7.3, minutes: -9.7 } + expect { DurationFormatting.format(duration, locale: 'en-AU') }.to raise_error(ArgumentError) + end end + end end diff --git a/spec/lib_spec.rb b/spec/lib_spec.rb index 90b7c85..ddb2577 100644 --- a/spec/lib_spec.rb +++ b/spec/lib_spec.rb @@ -49,7 +49,7 @@ module ICU subject { Lib.cldr_version } it { should be_a Lib::VersionInfo } - it('is populated') { expect(subject.to_a).to_not eq([0,0,0,0]) } + it('is populated') { expect(subject.to_a).to_not eq([0, 0, 0, 0]) } end end @@ -57,7 +57,7 @@ module ICU subject { Lib.version } it { is_expected.to be_a Lib::VersionInfo } - it('is populated') { expect(subject.to_a).to_not eq([0,0,0,0]) } + it('is populated') { expect(subject.to_a).to_not eq([0, 0, 0, 0]) } end end end diff --git a/spec/locale_spec.rb b/spec/locale_spec.rb index dda97c6..f1460f1 100644 --- a/spec/locale_spec.rb +++ b/spec/locale_spec.rb @@ -77,7 +77,8 @@ module ICU if Gem::Version.new('4.4') <= Gem::Version.new(Lib.version) expect(Locale.new('en_US').to_language_tag).to eq('en-US') expect(Locale.new('zh_TW').to_language_tag).to eq('zh-TW') - # Support for this "magic" transform was dropped with https://unicode-org.atlassian.net/browse/ICU-20187, so don't test it + # Support for this "magic" transform was dropped with https://unicode-org.atlassian.net/browse/ICU-20187, + # so don't test it if Gem::Version.new(Lib.version) < Gem::Version.new('64') expect(Locale.new('zh_Hans_CH_PINYIN').to_language_tag).to eq('zh-Hans-CH-u-co-pinyin') else @@ -124,7 +125,8 @@ module ICU end it 'returns the name using display context' do - expect(Locale.new('en_HK').display_name_with_context('en_US', [:length_full])).to eq('English (Hong Kong SAR China)') + expect(Locale.new('en_HK').display_name_with_context('en_US', + [:length_full])).to eq('English (Hong Kong SAR China)') expect(Locale.new('en_HK').display_name_with_context('en_US', [:length_short])).to eq('English (Hong Kong)') end diff --git a/spec/normalization_spec.rb b/spec/normalization_spec.rb index dc69092..e219f6f 100644 --- a/spec/normalization_spec.rb +++ b/spec/normalization_spec.rb @@ -5,7 +5,6 @@ module Normalization # http://bugs.icu-project.org/trac/browser/icu/trunk/source/test/cintltst/cnormtst.c describe "Normalization" do - it "should normalize a string - decomposed" do expect(ICU::Normalization.normalize("Å", :nfd).unpack("U*")).to eq([65, 778]) end @@ -15,8 +14,6 @@ module Normalization end # TODO: add more normalization tests - - end end # Normalization end # ICU diff --git a/spec/number_formatting_spec.rb b/spec/number_formatting_spec.rb index 03bff01..09067b9 100644 --- a/spec/number_formatting_spec.rb +++ b/spec/number_formatting_spec.rb @@ -37,7 +37,10 @@ module NumberFormatting it 'should spell numbers' do expect(NumberFormatting.spell("en_US", 1_000)).to eq('one thousand') - expect(NumberFormatting.spell("de-DE", 123.456)).to eq("ein\u{AD}hundert\u{AD}drei\u{AD}und\u{AD}zwanzig Komma vier fünf sechs") + expect(NumberFormatting.spell("de-DE", + 123.456)).to( + eq("ein\u{AD}hundert\u{AD}drei\u{AD}und\u{AD}zwanzig Komma vier fünf sechs") + ) end it 'should be able to re-use number formatter objects' do @@ -55,10 +58,10 @@ module NumberFormatting if ICU::Lib.version.to_a.first >= 53 curf = NumberFormatting.create('en-US', :currency, style: :iso) expected = if ICU::Lib.version.to_a.first >= 62 - "USD\u00A01,000.12" - else - "USD1,000.12" - end + "USD\u00A01,000.12" + else + "USD1,000.12" + end expect(curf.format(1_000.12, 'USD')).to eq(expected) curf = NumberFormatting.create('en-US', :currency, style: :plural) expect(curf.format(1_000.12, 'USD')).to eq("1,000.12 US dollars") diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0f84f3b..f08e2de 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,7 +6,6 @@ require 'rspec' RSpec.configure do |config| - if ENV['TRAVIS'] config.filter_run_excluding broken: true end diff --git a/spec/time_spec.rb b/spec/time_spec.rb index 63c1478..c46a7ab 100644 --- a/spec/time_spec.rb +++ b/spec/time_spec.rb @@ -13,7 +13,8 @@ module ICU t7 = Time.at(1206758243) # in TZ=Europe/Prague Time.mktime(2008, 03, 29, 03, 37, 23) t8 = Time.at(1206761904) # in TZ=Europe/Prague Time.mktime(2008, 03, 29, 04, 38, 24) - f1 = TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long , :time => :long, :tz_style => :localized_long) + f1 = TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long, :time => :long, + :tz_style => :localized_long) it 'check date_format for lang=cs_CZ' do expect(f1.date_format(true)).to eq("d. MMMM y H:mm:ss ZZZZ") expect(f1.date_format(false)).to eq("d. MMMM y H:mm:ss ZZZZ") @@ -32,7 +33,8 @@ module ICU expect(f1.format(t8)).to eq("29. března 2008 4:38:24 GMT+01:00") end - f2 = TimeFormatting.create(:locale => 'en_US', :zone => 'Europe/Moscow', :date => :short , :time => :long, :tz_style => :generic_location) + f2 = TimeFormatting.create(:locale => 'en_US', :zone => 'Europe/Moscow', :date => :short, :time => :long, + :tz_style => :generic_location) cldr_version = Lib.cldr_version.to_s en_tz = "Moscow Time" en_sep = "," @@ -59,7 +61,7 @@ module ICU expect(f2.format(t8)).to eq("3/29/08#{en_sep} 6:38:24 AM #{en_tz}") end - f3 = TimeFormatting.create(:locale => 'de_DE', :zone => 'Africa/Dakar', :date => :short , :time => :long) + f3 = TimeFormatting.create(:locale => 'de_DE', :zone => 'Africa/Dakar', :date => :short, :time => :long) ge_sep = "" if cldr_version >= "27.0.1" ge_sep = "," @@ -102,35 +104,40 @@ module ICU context "with locale #{locale_name}" do it 'works with hour_cycle: h11' do t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00") - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h11') + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', + hour_cycle: 'h11') expect(str).to match(/0:05/i) expect(str).to match(/(pm|下午)/i) end it 'works with hour_cycle: h12' do t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00") - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h12') + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', + hour_cycle: 'h12') expect(str).to match(/12:05/i) expect(str).to match(/(pm|下午)/i) end it 'works with hour_cycle: h23' do t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00") - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h23') + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', + hour_cycle: 'h23') expect(str).to match(/0:05/i) expect(str).to_not match(/(am|pm)/i) end it 'works with hour_cycle: h24' do t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00") - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h24') + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', + hour_cycle: 'h24') expect(str).to match(/24:05/i) expect(str).to_not match(/(am|pm)/i) end it 'does not include am/pm if time is not requested' do t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00") - str = TimeFormatting.format(t, time: :none, date: :short, locale: locale_name, zone: 'UTC', hour_cycle: 'h12') + str = TimeFormatting.format(t, time: :none, date: :short, locale: locale_name, zone: 'UTC', + hour_cycle: 'h12') expect(str).to_not match(/(am|pm|下午|上午)/i) end @@ -142,14 +149,16 @@ module ICU it 'works with @hours=h11 keyword' do t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00") locale = Locale.new(locale_name).with_keyword('hours', 'h11').to_s - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale) + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', + hour_cycle: :locale) expect(str).to match(/0:05/i) expect(str).to match(/(pm|下午)/i) end it 'works with @hours=h12 keyword' do t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00") locale = Locale.new(locale_name).with_keyword('hours', 'h12').to_s - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale) + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', + hour_cycle: :locale) expect(str).to match(/12:05/i) expect(str).to match(/(pm|下午)/i) end @@ -157,7 +166,8 @@ module ICU it 'works with @hours=h23 keyword' do t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00") locale = Locale.new(locale_name).with_keyword('hours', 'h23').to_s - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale) + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', + hour_cycle: :locale) expect(str).to match(/0:05/i) expect(str).to_not match(/(am|pm)/i) end @@ -165,7 +175,8 @@ module ICU it 'works with @hours=h24 keyword' do t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00") locale = Locale.new(locale_name).with_keyword('hours', 'h24').to_s - str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale) + str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', + hour_cycle: :locale) expect(str).to match(/24:05/i) expect(str).to_not match(/(am|pm)/i) end @@ -175,7 +186,8 @@ module ICU it 'for lang=fi hour_cycle=h12' do t = Time.new(2021, 04, 01, 13, 05, 0, "+00:00") - str = TimeFormatting.format(t, locale: 'fi', zone: 'America/Los_Angeles', date: :long, time: :short, hour_cycle: 'h12') + str = TimeFormatting.format(t, locale: 'fi', zone: 'America/Los_Angeles', date: :long, time: :short, + hour_cycle: 'h12') expect(str).to match(/\sklo\s/) end diff --git a/spec/transliteration_spec.rb b/spec/transliteration_spec.rb index 8d968e3..d52f3c2 100644 --- a/spec/transliteration_spec.rb +++ b/spec/transliteration_spec.rb @@ -9,14 +9,14 @@ def transliterator_for(*args) [ ["Any-Hex", "abcde", "\\u0061\\u0062\\u0063\\u0064\\u0065"], ["Lower", "ABC", "abc"], - ["Han-Latin", "雙屬性集合之空間分群演算法-應用於地理資料", "shuāng shǔ xìng jí hé zhī kōng jiān fēn qún yǎn suàn fǎ-yīng yòng yú de lǐ zī liào"], + ["Han-Latin", "雙屬性集合之空間分群演算法-應用於地理資料", + "shuāng shǔ xìng jí hé zhī kōng jiān fēn qún yǎn suàn fǎ-yīng yòng yú de lǐ zī liào"], ["Devanagari-Latin", "दौलत", "daulata"] ].each do |id, input, output| it "should transliterate #{id}" do tl = transliterator_for(id) expect(tl.transliterate(input)).to eq(output) end - end end # Transliterator @@ -28,9 +28,8 @@ def transliterator_for(*args) expect(ids).to_not be_empty end - it "should transliterate custom rules" do - expect(ICU::Transliteration.translit("NFD; [:Nonspacing Mark:] Remove; NFC", "âêîôû")).to eq("aeiou") - end - + it "should transliterate custom rules" do + expect(ICU::Transliteration.translit("NFD; [:Nonspacing Mark:] Remove; NFC", "âêîôû")).to eq("aeiou") + end end # Transliteration end # ICU