icu4x 0.8.1 → 0.9.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.
@@ -390,6 +390,27 @@
390
390
  # #
391
391
  # def select(number); end
392
392
  #
393
+ # # Selects the plural category for a range of numbers.
394
+ # #
395
+ # # This is equivalent to JavaScript's `Intl.PluralRules.selectRange()`.
396
+ # # The result depends on both the start and end values according to
397
+ # # locale-specific range plural rules.
398
+ # #
399
+ # # @param start_value [Integer, Float] the start of the range
400
+ # # @param end_value [Integer, Float] the end of the range
401
+ # # @return [Symbol] one of `:zero`, `:one`, `:two`, `:few`, `:many`, or `:other`
402
+ # #
403
+ # # @example
404
+ # # rules.select_range(1, 5) #=> :other (in English, "1-5 items")
405
+ # # rules.select_range(0, 1) #=> :other (in English)
406
+ # #
407
+ # # @example Russian plural ranges
408
+ # # ru_rules = ICU4X::PluralRules.new(ICU4X::Locale.parse("ru"), provider: provider)
409
+ # # ru_rules.select_range(1, 2) #=> :few
410
+ # # ru_rules.select_range(1, 5) #=> :many
411
+ # #
412
+ # def select_range(start_value, end_value); end
413
+ #
393
414
  # # Returns all plural categories available for this locale.
394
415
  # #
395
416
  # # @return [Array<Symbol>] array of category symbols
@@ -425,6 +446,11 @@
425
446
  # # formatter = ICU4X::NumberFormat.new(locale, style: :percent)
426
447
  # # formatter.format(0.42) #=> "42%"
427
448
  # #
449
+ # # @example Han decimal numerals via locale extension
450
+ # # locale = ICU4X::Locale.parse("ja-JP-u-nu-hanidec")
451
+ # # formatter = ICU4X::NumberFormat.new(locale, provider: provider)
452
+ # # formatter.format(1234) #=> "一,二三四"
453
+ # #
428
454
  # class NumberFormat
429
455
  # # Creates a new NumberFormat instance.
430
456
  # #
@@ -458,6 +484,32 @@
458
484
  # #
459
485
  # def format(number); end
460
486
  #
487
+ # # Formats a number and returns an array of parts.
488
+ # #
489
+ # # Each part contains a type and value, allowing for custom styling
490
+ # # or processing of individual components.
491
+ # #
492
+ # # @param number [Integer, Float, BigDecimal] the number to format
493
+ # # @return [Array<FormattedPart>] array of formatted parts
494
+ # #
495
+ # # @note For `style: :percent` and `style: :currency`, the current ICU4X
496
+ # # experimental formatters do not provide part annotations. These styles
497
+ # # return a single `:literal` part containing the entire formatted string.
498
+ # #
499
+ # # @example
500
+ # # parts = formatter.format_to_parts(-1234.56)
501
+ # # # => [
502
+ # # # #<ICU4X::FormattedPart type=:minus_sign value="-">,
503
+ # # # #<ICU4X::FormattedPart type=:integer value="1,234">,
504
+ # # # #<ICU4X::FormattedPart type=:decimal value=".">,
505
+ # # # #<ICU4X::FormattedPart type=:fraction value="56">
506
+ # # # ]
507
+ # #
508
+ # # @example Reconstruct the formatted string
509
+ # # parts.map(&:value).join #=> "-1,234.56"
510
+ # #
511
+ # def format_to_parts(number); end
512
+ #
461
513
  # # Returns the resolved options for this instance.
462
514
  # #
463
515
  # # @return [Hash] options hash with keys:
@@ -476,36 +528,73 @@
476
528
  # # Formats dates and times according to locale-specific conventions.
477
529
  # #
478
530
  # # DateTimeFormat supports various date and time styles and calendar systems.
531
+ # # You can use either style options (date_style, time_style) or component options
532
+ # # (year, month, day, weekday, hour, minute, second), but not both.
479
533
  # #
480
- # # @example Format a date
534
+ # # @example Format a date with style
481
535
  # # formatter = ICU4X::DateTimeFormat.new(locale, date_style: :long)
482
536
  # # formatter.format(Time.now) #=> "January 1, 2026"
483
537
  # #
484
- # # @example Format date and time
538
+ # # @example Format date and time with styles
485
539
  # # formatter = ICU4X::DateTimeFormat.new(locale, date_style: :short, time_style: :short)
486
540
  # # formatter.format(Time.now) #=> "1/1/26, 12:00 PM"
487
541
  # #
542
+ # # @example Format with component options
543
+ # # formatter = ICU4X::DateTimeFormat.new(locale, year: :numeric, month: :numeric, day: :numeric)
544
+ # # formatter.format(Time.now) #=> "Dec 28, 2025"
545
+ # #
488
546
  # # @example Use Japanese calendar
489
547
  # # formatter = ICU4X::DateTimeFormat.new(locale, date_style: :long, calendar: :japanese)
490
548
  # # formatter.format(Time.now) #=> "令和8年1月1日"
491
549
  # #
550
+ # # @example Han decimal numerals via locale extension
551
+ # # locale = ICU4X::Locale.parse("ja-JP-u-nu-hanidec")
552
+ # # formatter = ICU4X::DateTimeFormat.new(locale, provider: provider, date_style: :long)
553
+ # # formatter.format(Time.utc(2025, 12, 28)) #=> "二〇二五年一二月二八日"
554
+ # #
492
555
  # class DateTimeFormat
493
556
  # # Creates a new DateTimeFormat instance.
494
557
  # #
558
+ # # You must specify either style options (date_style/time_style) or component options
559
+ # # (year, month, day, weekday, hour, minute, second). These are mutually exclusive.
560
+ # #
495
561
  # # @param locale [Locale] the locale for formatting
496
562
  # # @param provider [DataProvider, nil] data provider (uses default if nil)
497
563
  # # @param date_style [Symbol, nil] date format style: `:full`, `:long`, `:medium`, or `:short`
498
564
  # # @param time_style [Symbol, nil] time format style: `:full`, `:long`, `:medium`, or `:short`
565
+ # # @param year [Symbol, nil] year component: `:numeric` or `:two_digit`
566
+ # # @param month [Symbol, nil] month component: `:numeric`, `:two_digit`, `:long`, `:short`, or `:narrow`
567
+ # # @param day [Symbol, nil] day component: `:numeric` or `:two_digit`
568
+ # # @param weekday [Symbol, nil] weekday component: `:long`, `:short`, or `:narrow`
569
+ # # @param hour [Symbol, nil] hour component: `:numeric` or `:two_digit`
570
+ # # @param minute [Symbol, nil] minute component: `:numeric` or `:two_digit`
571
+ # # @param second [Symbol, nil] second component: `:numeric` or `:two_digit`
499
572
  # # @param time_zone [String, nil] IANA time zone identifier (e.g., "America/New_York")
500
573
  # # @param calendar [Symbol] calendar system to use
574
+ # # @param hour_cycle [Symbol, nil] hour cycle: `:h11` (0-11), `:h12` (1-12), or `:h23` (0-23)
575
+ # # @param hour12 [Boolean, nil] `true` for 12-hour format, `false` for 24-hour format
501
576
  # # @return [DateTimeFormat] a new instance
577
+ # # @raise [ArgumentError] if both style and component options are specified
502
578
  # # @raise [DataError] if data for the locale is unavailable
503
579
  # #
504
- # # @example
580
+ # # @example With style options
505
581
  # # formatter = ICU4X::DateTimeFormat.new(locale, date_style: :long, time_style: :short)
506
582
  # #
583
+ # # @example With component options
584
+ # # formatter = ICU4X::DateTimeFormat.new(locale, year: :numeric, month: :long, day: :numeric)
585
+ # #
586
+ # # @example With 24-hour format using hour_cycle
587
+ # # formatter = ICU4X::DateTimeFormat.new(locale, time_style: :short, hour_cycle: :h23)
588
+ # # formatter.format(Time.utc(2025, 1, 1, 0, 30)) #=> "00:30:00"
589
+ # #
590
+ # # @example With 12-hour format using hour12
591
+ # # formatter = ICU4X::DateTimeFormat.new(locale, time_style: :short, hour12: true)
592
+ # # formatter.format(Time.utc(2025, 1, 1, 14, 30)) #=> "2:30:00 PM"
593
+ # #
507
594
  # def initialize(locale, provider: nil, date_style: nil, time_style: nil,
508
- # time_zone: nil, calendar: :gregory); end
595
+ # year: nil, month: nil, day: nil, weekday: nil,
596
+ # hour: nil, minute: nil, second: nil,
597
+ # time_zone: nil, calendar: :gregory, hour_cycle: nil, hour12: nil); end
509
598
  #
510
599
  # # Formats a time value according to the configured options.
511
600
  # #
@@ -514,14 +603,51 @@
514
603
  # #
515
604
  # def format(time); end
516
605
  #
606
+ # # Formats a time value and returns an array of parts.
607
+ # #
608
+ # # Each part contains a type and value, allowing for custom styling
609
+ # # or processing of individual components.
610
+ # #
611
+ # # @param time [Time, #to_time] the time to format (or any object responding to #to_time)
612
+ # # @return [Array<FormattedPart>] array of formatted parts
613
+ # #
614
+ # # @example
615
+ # # parts = formatter.format_to_parts(Time.utc(2025, 1, 31))
616
+ # # # => [
617
+ # # # #<ICU4X::FormattedPart type=:month value="January">,
618
+ # # # #<ICU4X::FormattedPart type=:literal value=" ">,
619
+ # # # #<ICU4X::FormattedPart type=:day value="31">,
620
+ # # # #<ICU4X::FormattedPart type=:literal value=", ">,
621
+ # # # #<ICU4X::FormattedPart type=:year value="2025">
622
+ # # # ]
623
+ # #
624
+ # # @example Reconstruct the formatted string
625
+ # # parts.map(&:value).join #=> "January 31, 2025"
626
+ # #
627
+ # # @example Japanese calendar with era
628
+ # # formatter = ICU4X::DateTimeFormat.new(locale, date_style: :long, calendar: :japanese)
629
+ # # parts = formatter.format_to_parts(Time.utc(2025, 1, 31))
630
+ # # era_part = parts.find { |p| p.type == :era }
631
+ # # era_part.value #=> "令和"
632
+ # #
633
+ # def format_to_parts(time); end
634
+ #
517
635
  # # Returns the resolved options for this instance.
518
636
  # #
519
637
  # # @return [Hash] options hash with keys:
520
638
  # # - `:locale` [String] the resolved locale identifier
521
639
  # # - `:calendar` [Symbol] the calendar system
522
- # # - `:date_style` [Symbol] the date style (if set)
523
- # # - `:time_style` [Symbol] the time style (if set)
640
+ # # - `:date_style` [Symbol] the date style (if style options used)
641
+ # # - `:time_style` [Symbol] the time style (if style options used)
642
+ # # - `:year` [Symbol] the year component (if component options used)
643
+ # # - `:month` [Symbol] the month component (if component options used)
644
+ # # - `:day` [Symbol] the day component (if component options used)
645
+ # # - `:weekday` [Symbol] the weekday component (if component options used)
646
+ # # - `:hour` [Symbol] the hour component (if component options used)
647
+ # # - `:minute` [Symbol] the minute component (if component options used)
648
+ # # - `:second` [Symbol] the second component (if component options used)
524
649
  # # - `:time_zone` [String] the time zone (if set)
650
+ # # - `:hour_cycle` [Symbol] the hour cycle (if set)
525
651
  # #
526
652
  # def resolved_options; end
527
653
  # end
@@ -538,6 +664,11 @@
538
664
  # # formatter.format(-1, :day) #=> "yesterday"
539
665
  # # formatter.format(0, :day) #=> "today"
540
666
  # #
667
+ # # @example Han decimal numerals via locale extension
668
+ # # locale = ICU4X::Locale.parse("ja-u-nu-hanidec")
669
+ # # formatter = ICU4X::RelativeTimeFormat.new(locale, provider: provider)
670
+ # # formatter.format(-3, :day) #=> "三 日前"
671
+ # #
541
672
  # class RelativeTimeFormat
542
673
  # # Creates a new RelativeTimeFormat instance.
543
674
  # #
@@ -563,6 +694,26 @@
563
694
  # #
564
695
  # def format(value, unit); end
565
696
  #
697
+ # # Formats a relative time value and returns an array of parts.
698
+ # #
699
+ # # @param value [Integer] the relative time value (negative for past, positive for future)
700
+ # # @param unit [Symbol] time unit: `:second`, `:minute`, `:hour`, `:day`,
701
+ # # `:week`, `:month`, `:quarter`, or `:year`
702
+ # # @return [Array<FormattedPart>] array of formatted parts
703
+ # #
704
+ # # @note The current ICU4X experimental RelativeTimeFormatter does not
705
+ # # provide separate part annotations for the numeric value. The entire
706
+ # # formatted string is returned as a single `:literal` part.
707
+ # #
708
+ # # @example
709
+ # # parts = formatter.format_to_parts(-3, :day)
710
+ # # # => [#<ICU4X::FormattedPart type=:literal value="3 days ago">]
711
+ # #
712
+ # # @example Reconstruct the formatted string
713
+ # # parts.map(&:value).join #=> "3 days ago"
714
+ # #
715
+ # def format_to_parts(value, unit); end
716
+ #
566
717
  # # Returns the resolved options for this instance.
567
718
  # #
568
719
  # # @return [Hash] options hash with keys:
@@ -606,6 +757,29 @@
606
757
  # #
607
758
  # def format(list); end
608
759
  #
760
+ # # Formats a list of strings and returns an array of parts.
761
+ # #
762
+ # # Each part contains a type and value, allowing for custom styling
763
+ # # or processing of individual components.
764
+ # #
765
+ # # @param list [Array<String>] the list items to format
766
+ # # @return [Array<FormattedPart>] array of formatted parts
767
+ # #
768
+ # # @example
769
+ # # parts = formatter.format_to_parts(["Apple", "Banana", "Cherry"])
770
+ # # # => [
771
+ # # # #<ICU4X::FormattedPart type=:element value="Apple">,
772
+ # # # #<ICU4X::FormattedPart type=:literal value=", ">,
773
+ # # # #<ICU4X::FormattedPart type=:element value="Banana">,
774
+ # # # #<ICU4X::FormattedPart type=:literal value=", and ">,
775
+ # # # #<ICU4X::FormattedPart type=:element value="Cherry">
776
+ # # # ]
777
+ # #
778
+ # # @example Reconstruct the formatted string
779
+ # # parts.map(&:value).join #=> "Apple, Banana, and Cherry"
780
+ # #
781
+ # def format_to_parts(list); end
782
+ #
609
783
  # # Returns the resolved options for this instance.
610
784
  # #
611
785
  # # @return [Hash] options hash with keys:
data/lib/icu4x.rb CHANGED
@@ -54,6 +54,28 @@ module ICU4X
54
54
  class DataGeneratorError < Error; end
55
55
  end
56
56
 
57
+ # Define FormattedPart data class for format_to_parts methods
58
+ module ICU4X
59
+ FormattedPart = Data.define(:type, :value)
60
+ end
61
+
62
+ # Enhance the FormattedPart data class
63
+ module ICU4X
64
+ # Represents a part of a formatted string.
65
+ #
66
+ # Used by format_to_parts methods in DateTimeFormat, NumberFormat,
67
+ # ListFormat, and RelativeTimeFormat.
68
+ #
69
+ # @!attribute [r] type
70
+ # @return [Symbol] The part type (e.g., :integer, :literal, :year)
71
+ # @!attribute [r] value
72
+ # @return [String] The formatted value
73
+ class FormattedPart
74
+ # @return [String] Human-readable representation
75
+ def inspect = "#<ICU4X::FormattedPart type=#{type.inspect} value=#{value.inspect}>"
76
+ end
77
+ end
78
+
57
79
  # Define Segment data class for Segmenter
58
80
  module ICU4X
59
81
  class Segmenter
data/sig/icu4x.rbs CHANGED
@@ -18,6 +18,14 @@ module ICU4X
18
18
  class DataGeneratorError < Error
19
19
  end
20
20
 
21
+ class FormattedPart
22
+ attr_reader type: Symbol
23
+ attr_reader value: String
24
+
25
+ def self.[]: (Symbol type, String value) -> FormattedPart
26
+ def self.new: (type: Symbol, value: String) -> FormattedPart
27
+ end
28
+
21
29
  class DataProvider
22
30
  def self.from_blob: (Pathname path, ?priority: :language | :region) -> DataProvider
23
31
  end
@@ -28,7 +36,8 @@ module ICU4X
28
36
  end
29
37
 
30
38
  class Locale
31
- def self.parse: (String locale_str) -> Locale
39
+ def self.parse_bcp47: (String locale_str) -> Locale
40
+ alias self.parse self.parse_bcp47
32
41
  def self.parse_posix: (String posix_str) -> Locale
33
42
 
34
43
  def language: () -> String?
@@ -49,6 +58,7 @@ module ICU4X
49
58
  def self.new: (Locale locale, ?provider: DataProvider, ?type: plural_rule_type) -> PluralRules
50
59
 
51
60
  def select: (Integer | Float number) -> plural_category
61
+ def select_range: (Integer | Float start_value, Integer | Float end_value) -> plural_category
52
62
  def categories: () -> Array[plural_category]
53
63
  def resolved_options: () -> { locale: String, type: plural_rule_type }
54
64
  end
@@ -59,6 +69,14 @@ module ICU4X
59
69
  type date_style = :full | :long | :medium | :short
60
70
  type time_style = :full | :long | :medium | :short
61
71
  type datetime_calendar = :gregory | :japanese | :buddhist | :chinese | :hebrew | :islamic | :persian | :indian | :ethiopian | :coptic | :roc | :dangi
72
+ type hour_cycle = :h11 | :h12 | :h23
73
+ type year_style = :numeric | :two_digit
74
+ type month_style = :numeric | :two_digit | :long | :short | :narrow
75
+ type day_style = :numeric | :two_digit
76
+ type weekday_style = :long | :short | :narrow
77
+ type hour_style = :numeric | :two_digit
78
+ type minute_style = :numeric | :two_digit
79
+ type second_style = :numeric | :two_digit
62
80
 
63
81
  class NumberFormat
64
82
  def self.new: (
@@ -74,6 +92,7 @@ module ICU4X
74
92
  ) -> NumberFormat
75
93
 
76
94
  def format: (Integer | Float | BigDecimal number) -> String
95
+ def format_to_parts: (Integer | Float | BigDecimal number) -> Array[FormattedPart]
77
96
  def resolved_options: () -> {
78
97
  locale: String,
79
98
  style: number_format_style,
@@ -92,17 +111,35 @@ module ICU4X
92
111
  ?provider: DataProvider,
93
112
  ?date_style: date_style,
94
113
  ?time_style: time_style,
114
+ ?year: year_style,
115
+ ?month: month_style,
116
+ ?day: day_style,
117
+ ?weekday: weekday_style,
118
+ ?hour: hour_style,
119
+ ?minute: minute_style,
120
+ ?second: second_style,
95
121
  ?time_zone: String,
96
- ?calendar: datetime_calendar
122
+ ?calendar: datetime_calendar,
123
+ ?hour_cycle: hour_cycle,
124
+ ?hour12: bool
97
125
  ) -> DateTimeFormat
98
126
 
99
127
  def format: (Time time) -> String
128
+ def format_to_parts: (Time time) -> Array[FormattedPart]
100
129
  def resolved_options: () -> {
101
130
  locale: String,
102
131
  calendar: datetime_calendar,
103
132
  ?date_style: date_style,
104
133
  ?time_style: time_style,
105
- ?time_zone: String
134
+ ?year: year_style,
135
+ ?month: month_style,
136
+ ?day: day_style,
137
+ ?weekday: weekday_style,
138
+ ?hour: hour_style,
139
+ ?minute: minute_style,
140
+ ?second: second_style,
141
+ ?time_zone: String,
142
+ ?hour_cycle: hour_cycle
106
143
  }
107
144
  end
108
145
 
@@ -119,6 +156,7 @@ module ICU4X
119
156
  ) -> RelativeTimeFormat
120
157
 
121
158
  def format: (Integer value, relative_time_unit unit) -> String
159
+ def format_to_parts: (Integer value, relative_time_unit unit) -> Array[FormattedPart]
122
160
  def resolved_options: () -> {
123
161
  locale: String,
124
162
  style: relative_time_format_style,
@@ -141,6 +179,7 @@ module ICU4X
141
179
  ) -> ListFormat
142
180
 
143
181
  def format: (Array[String] list) -> String
182
+ def format_to_parts: (Array[String] list) -> Array[FormattedPart]
144
183
  def resolved_options: () -> {
145
184
  locale: String,
146
185
  type: list_format_type,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: icu4x
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OZAWA Sakuro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-12 00:00:00.000000000 Z
11
+ date: 2026-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-configurable
@@ -72,6 +72,7 @@ files:
72
72
  - ext/icu4x/src/list_format.rs
73
73
  - ext/icu4x/src/locale.rs
74
74
  - ext/icu4x/src/number_format.rs
75
+ - ext/icu4x/src/parts_collector.rs
75
76
  - ext/icu4x/src/plural_rules.rs
76
77
  - ext/icu4x/src/relative_time_format.rs
77
78
  - ext/icu4x/src/segmenter.rs