Class: Philiprehberger::LocaleKit::Locale

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/philiprehberger/locale_kit/locale.rb

Overview

Represents a parsed BCP 47 language tag.

Locale objects are immutable and comparable. A BCP 47 tag consists of:

  • language: 2-3 character ISO 639 language code (required)

  • script: 4 character ISO 15924 script code (optional)

  • region: 2 alpha ISO 3166-1 or 3 digit UN M.49 code (optional)

  • variant: 5-8 alphanumeric variant subtag (optional)

  • extensions: Unicode extension subtags (optional)

Examples:

locale = Locale.new("en", region: "US")
locale.to_s #=> "en-US"
locale.parent #=> #<Locale language="en">

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(language, script: nil, region: nil, variant: nil, extensions: {}) ⇒ Locale

Creates a new Locale instance.

Parameters:

  • language (String)

    2-3 character language subtag

  • script (String, nil) (defaults to: nil)

    4 character script subtag

  • region (String, nil) (defaults to: nil)

    2 alpha or 3 digit region subtag

  • variant (String, nil) (defaults to: nil)

    5-8 alphanumeric variant subtag

  • extensions (Hash) (defaults to: {})

    extension subtags keyed by singleton letter

Raises:

  • (ArgumentError)

    if language is invalid



44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/philiprehberger/locale_kit/locale.rb', line 44

def initialize(language, script: nil, region: nil, variant: nil, extensions: {})
  validate_language!(language)
  validate_script!(script) if script
  validate_region!(region) if region
  validate_variant!(variant) if variant

  @language = language.downcase.freeze
  @script = script&.then { |s| "#{s[0].upcase}#{s[1..].downcase}" }&.freeze
  @region = region&.upcase&.freeze
  @variant = variant&.downcase&.freeze
  @extensions = extensions.each_with_object({}) { |(k, v), h| h[k.to_s.downcase] = v.to_s.downcase }.freeze
  freeze
end

Instance Attribute Details

#extensionsHash (readonly)

Returns the extension subtags (e.g., { “u” => “ca-buddhist” }).

Returns:

  • (Hash)

    the extension subtags (e.g., { “u” => “ca-buddhist” })



34
35
36
# File 'lib/philiprehberger/locale_kit/locale.rb', line 34

def extensions
  @extensions
end

#languageString (readonly)

Returns the language subtag (e.g., “en”, “zh”).

Returns:

  • (String)

    the language subtag (e.g., “en”, “zh”)



22
23
24
# File 'lib/philiprehberger/locale_kit/locale.rb', line 22

def language
  @language
end

#regionString? (readonly)

Returns the region subtag (e.g., “US”, “419”) or nil.

Returns:

  • (String, nil)

    the region subtag (e.g., “US”, “419”) or nil



28
29
30
# File 'lib/philiprehberger/locale_kit/locale.rb', line 28

def region
  @region
end

#scriptString? (readonly)

Returns the script subtag (e.g., “Hant”, “Latn”) or nil.

Returns:

  • (String, nil)

    the script subtag (e.g., “Hant”, “Latn”) or nil



25
26
27
# File 'lib/philiprehberger/locale_kit/locale.rb', line 25

def script
  @script
end

#variantString? (readonly)

Returns the variant subtag (e.g., “valencia”, “posix”) or nil.

Returns:

  • (String, nil)

    the variant subtag (e.g., “valencia”, “posix”) or nil



31
32
33
# File 'lib/philiprehberger/locale_kit/locale.rb', line 31

def variant
  @variant
end

Instance Method Details

#<=>(other) ⇒ Integer?

Comparison for sorting. Orders by language, then script, then region.

Parameters:

  • other (Locale)

    locale to compare

Returns:

  • (Integer, nil)

    -1, 0, 1, or nil if not comparable



181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/philiprehberger/locale_kit/locale.rb', line 181

def <=>(other)
  return nil unless other.is_a?(Locale)

  result = language <=> other.language
  return result unless result.zero?

  result = (script || '') <=> (other.script || '')
  return result unless result.zero?

  result = (region || '') <=> (other.region || '')
  return result unless result.zero?

  (variant || '') <=> (other.variant || '')
end

#==(other) ⇒ Boolean Also known as: eql?

Equality based on all subtag values.

Parameters:

  • other (Object)

    object to compare

Returns:

  • (Boolean)


160
161
162
163
164
165
166
167
168
# File 'lib/philiprehberger/locale_kit/locale.rb', line 160

def ==(other)
  return false unless other.is_a?(Locale)

  language == other.language &&
    script == other.script &&
    region == other.region &&
    variant == other.variant &&
    extensions == other.extensions
end

#compatible?(other) ⇒ Boolean

Tests whether another locale shares the same primary language subtag.

Comparison is case-insensitive and ignores script, region, variant, and extensions. For example, “en-US” is compatible with “en-GB”, and “en” is compatible with “en-US”.

Parameters:

  • other (Locale, String)

    the locale or BCP 47 tag to compare against

Returns:

  • (Boolean)

    true if both locales share the same primary language subtag, false if other is nil, not a Locale/String, or an unparseable tag



142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/philiprehberger/locale_kit/locale.rb', line 142

def compatible?(other)
  if other.is_a?(String)
    begin
      other = LocaleKit.parse(other)
    rescue ArgumentError
      return false
    end
  end

  return false unless other.is_a?(Locale)

  language.downcase == other.language.downcase
end

#display_name(in_locale: nil) ⇒ String

Returns a human-readable display name for the locale.

Parameters:

  • in_locale (nil) (defaults to: nil)

    reserved for future use

Returns:

  • (String)

    human-readable name (e.g., “English (United States)”)



95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/philiprehberger/locale_kit/locale.rb', line 95

def display_name(in_locale: nil)
  lang_name = Data::LANGUAGES[language]
  return to_s unless lang_name

  parts = [lang_name]
  qualifiers = []
  qualifiers << Data::REGIONS[region] if region && Data::REGIONS[region]
  qualifiers << variant.capitalize if variant

  return "#{parts.first} (#{qualifiers.join(', ')})" unless qualifiers.empty?

  parts.first
end

#hashInteger

Returns hash code based on all subtags.

Returns:

  • (Integer)

    hash code based on all subtags



173
174
175
# File 'lib/philiprehberger/locale_kit/locale.rb', line 173

def hash
  [language, script, region, variant, extensions].hash
end

#inspectString

Returns inspection string.

Returns:

  • (String)

    inspection string



197
198
199
# File 'lib/philiprehberger/locale_kit/locale.rb', line 197

def inspect
  "#<#{self.class} #{self}>"
end

#language_familySymbol

Returns the language family for this locale’s language.

Returns:

  • (Symbol)

    language family (:germanic, :romance, :slavic, :sino_tibetan, :japonic, :koreanic, :semitic, :other)



113
114
115
# File 'lib/philiprehberger/locale_kit/locale.rb', line 113

def language_family
  Data::LANGUAGE_FAMILIES.fetch(language, :other)
end

#match?(other) ⇒ Boolean

Tests whether another locale is a prefix match of this locale.

A locale matches if the other locale’s subtags are a prefix of this locale’s subtags. For example, “en” matches “en-US” and “en-Latn-US”.

Parameters:

  • other (Locale, String)

    the locale to compare against

Returns:

  • (Boolean)

    true if other is a prefix match



124
125
126
127
128
129
130
131
132
# File 'lib/philiprehberger/locale_kit/locale.rb', line 124

def match?(other)
  other = LocaleKit.parse(other) if other.is_a?(String)

  return false unless language == other.language
  return true if other.script.nil? && other.region.nil?
  return false if other.script && script != other.script

  other.region.nil? || region == other.region
end

#parentLocale?

Returns the parent locale by removing the most specific subtag.

en-US-valencia -> en-US -> en -> nil en-u-ca-buddhist -> en -> nil

Returns:

  • (Locale, nil)

    the parent locale, or nil if this is a language-only locale



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/philiprehberger/locale_kit/locale.rb', line 79

def parent
  if !extensions.empty?
    self.class.new(language, script: script, region: region, variant: variant)
  elsif variant
    self.class.new(language, script: script, region: region)
  elsif region
    self.class.new(language, script: script)
  elsif script
    self.class.new(language)
  end
end

#to_sString

Returns the canonical BCP 47 string representation.

Returns:

  • (String)

    canonical tag (e.g., “en-US”, “zh-Hant-TW”)



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/philiprehberger/locale_kit/locale.rb', line 61

def to_s
  parts = [language]
  parts << script if script
  parts << region if region
  parts << variant if variant
  extensions.sort.each do |singleton, value|
    parts << singleton
    parts << value
  end
  parts.join('-')
end