Class: Rfc5646::Locale
- Inherits:
-
Object
- Object
- Rfc5646::Locale
- Defined in:
- lib/rfc5646/locale.rb
Overview
Represents a locale that can be localized to. Locales are identified by their RFC 5646 code, which can be as simple as just a language (e.g., “en” for English), or arbitrary complex (e.g., “zh-cmn-Hans-CN” for Mandarin Chinese as spoken in China, simplified Han orthography). The entire RFC 5646 spec is supported by this class.
Constant Summary collapse
- RFC5646_EXTLANG =
/(?<extlang>[a-zA-Z]{3})(-[a-zA-Z]{3}){0,2}/
- RFC5646_ISO639 =
/(?<iso639>[a-zA-Z]{2,3})(-#{RFC5646_EXTLANG.source})?/
- RFC5646_RESERVED =
/(?<reserved>[a-zA-Z]{4})/
- RFC5646_SUBTAG =
/(?<subtag>[a-zA-Z]{5,8})/
- RFC5646_REGION =
/(?<region>([a-zA-Z]{2}|\d{3}))/
- RFC5646_VARIANT =
/([a-zA-Z0-9]{5,8}|\d[a-zA-Z0-9]{3})/
- RFC5646_SCRIPT =
/(?<script>[a-zA-Z]{4})/
- RFC5646_EXTENSION =
/([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})/
- RFC5646_LANGUAGE =
/(#{RFC5646_ISO639.source}|#{RFC5646_RESERVED.source}|#{RFC5646_SUBTAG.source})/
- RFC5646_PRIVATE =
/x(?<privates>(-[a-zA-Z0-9]{1,8}){1,})/
- RFC5646_FORMAT =
/\A#{RFC5646_LANGUAGE.source}(-#{RFC5646_SCRIPT.source})?(-#{RFC5646_REGION.source})?(?<variants>(-#{RFC5646_VARIANT.source})*)(?<extensions>(-#{RFC5646_EXTENSION.source})*)(-#{RFC5646_PRIVATE.source})?\z/
Instance Attribute Summary collapse
-
#extended_language ⇒ String
readonly
The dialect (not associated with a specific region or period in time) specifier.
-
#extensions ⇒ Array<String>
readonly
The user-defined extensions applied to this locale.
-
#iso639 ⇒ String
readonly
The ISO 639 code for the base language (e.g., “de” for German).
-
#region ⇒ String
readonly
The ISO 3166 country code for the regional dialect (e.g., “BZ” for Belize).
-
#script ⇒ String
readonly
The RFC 5646 code for the orthography (e.g., “Arab” for Arabic script).
-
#variants ⇒ Array<String>
readonly
The variant or nested subvariant of this locale.
Class Method Summary collapse
-
.from_rfc5646(ident) ⇒ Locale
Generates a new instance from an RFC 5646 code.
-
.from_rfc5646_prefix(prefix, max = nil) ⇒ Array<Locale>
Returns an array of Locales representing all possible completions given a prefix portion of an RFC 5646 code.
- .matches_prefix?(prefix, key, value) ⇒ Boolean
Instance Method Summary collapse
-
#==(other) ⇒ true, false
(also: #eql?, #equal?, #===)
Tests for equality between two locales.
- #as_json(_options = nil) ⇒ Object
-
#child_of?(parent) ⇒ true, false
Returns whether this Languge is a subset of the given locale.
-
#fallbacks ⇒ Array<Locale>
Returns the fallback order for this Locale.
- #hash ⇒ Object
-
#initialize(iso639, script = nil, extlang = nil, region = nil, variants = [], extensions = []) ⇒ Locale
constructor
A new instance of Locale.
- #inspect ⇒ Object
-
#name(locale = nil) ⇒ String
Returns a human-readable localized name of the locale.
-
#pseudo? ⇒ true, false
Whether this locale is a pseudo-locale.
-
#rfc5646 ⇒ String
(also: #to_param)
The full RFC 5646 code for this locale.
- #specificity ⇒ Object
- #specified_parts ⇒ Object
Constructor Details
#initialize(iso639, script = nil, extlang = nil, region = nil, variants = [], extensions = []) ⇒ Locale
Returns a new instance of Locale.
171 172 173 174 175 176 177 178 |
# File 'lib/rfc5646/locale.rb', line 171 def initialize(iso639, script = nil, extlang = nil, region = nil, variants = [], extensions = []) @iso639 = iso639.try!(:downcase) @region = region.try!(:upcase) @variants = variants.map(&:downcase) @extended_language = extlang.try!(:downcase) @extensions = extensions @script = script end |
Instance Attribute Details
#extended_language ⇒ String (readonly)
Returns The dialect (not associated with a specific region or period in time) specifier. For example, “yue” indicates Yue Chinese (Cantonese).
70 71 72 |
# File 'lib/rfc5646/locale.rb', line 70 def extended_language @extended_language end |
#extensions ⇒ Array<String> (readonly)
Returns The user-defined extensions applied to this locale. The meaning of these is not specified in the spec, and left up to private use, and is ignored by this class, but stored for completeness.
74 75 76 |
# File 'lib/rfc5646/locale.rb', line 74 def extensions @extensions end |
#iso639 ⇒ String (readonly)
Returns The ISO 639 code for the base language (e.g., “de” for German).
51 52 53 |
# File 'lib/rfc5646/locale.rb', line 51 def iso639 @iso639 end |
#region ⇒ String (readonly)
Returns The ISO 3166 country code for the regional dialect (e.g., “BZ” for Belize). Some special values are also supported (e.g., “013” for Central America); see the spec for details.
58 59 60 |
# File 'lib/rfc5646/locale.rb', line 58 def region @region end |
#script ⇒ String (readonly)
Returns The RFC 5646 code for the orthography (e.g., “Arab” for Arabic script).
54 55 56 |
# File 'lib/rfc5646/locale.rb', line 54 def script @script end |
#variants ⇒ Array<String> (readonly)
Returns The variant or nested subvariant of this locale. The full path to a subvariant is listed as a top-level Array; an example is ‘[“sl”, “rozaj”, “1994”]`, indicating the 1994 standardization of the Resian orthography of the Rozaj dialect of Slovenian (in case we should ever want to localize one of our projects thusly). Variants can be regional or temporal dialects, or orthographies, or both, and are very specific.
66 67 68 |
# File 'lib/rfc5646/locale.rb', line 66 def variants @variants end |
Class Method Details
.from_rfc5646(ident) ⇒ Locale
Generates a new instance from an RFC 5646 code.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/rfc5646/locale.rb', line 81 def from_rfc5646(ident) ident = String(ident).tr('_', '-') return nil unless (matches = RFC5646_FORMAT.match(ident)) attrs = RFC5646_FORMAT.named_captures.each_with_object({}) do |(name, offsets), hsh| hsh[name] = offsets.map { |offset| matches[offset] }.compact end iso639 = attrs['iso639'].first script = attrs['script'].first return nil unless iso639 region = attrs['region'].first if (variants = attrs['variants'].first) variants = variants.split('-') variants.shift end if (extensions = attrs['extensions'].first) extensions = extensions.split('-') extensions.shift end extlang = attrs['extlang'].first Locale.new iso639, script, extlang, region, variants || [], extensions || [] end |
.from_rfc5646_prefix(prefix, max = nil) ⇒ Array<Locale>
Returns an array of Locales representing all possible completions given a prefix portion of an RFC 5646 code. The resolution of the resultant array is determined by the resolution if the input prefix. Some examples:
-
If just the letter “e” is entered, Locales whose ISO 639 codes begin with the letter “e” will be returned (English, Spanish, etc.). These Locale instances will have no other fields specified.
-
If “en-U” is specified, Locale instances representing “en-US” and “en-UA”, among others, will be returned, as well as “en-Ugar” (for all the sense it makes). “en-US-Ugar” would not be returned, as it is of a higher resolution than the input.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/rfc5646/locale.rb', line 121 def from_rfc5646_prefix(prefix, max = nil) if prefix.include?('-') prefix_path = prefix.split('-') prefix = prefix_path.pop parent_prefix = prefix_path.join('-') parent = from_rfc5646(parent_prefix) return [] unless parent search_paths = if parent.variants.present? # TODO: subvariants [] elsif parent.region # only possible completions are variants [] # TODO: variants elsif parent.script # can be followed with variant or region %w(locale.region) else # can be followed with script, region, or variant %w(locale.region locale.script) end keys = search_paths.map do |path| I18n.t(path).select { |k, v| Locale.matches_prefix? prefix, k, v }.keys end.flatten keys.delete '_END_' keys = keys[0, max] if max return keys.map { |key| from_rfc5646 "#{parent_prefix}-#{key}" } else keys = I18n.t('locale.name').select { |k, v| Locale.matches_prefix? prefix, k, v }.keys keys = keys[0, max] if max return keys.map { |key| from_rfc5646 key } end end |
.matches_prefix?(prefix, key, value) ⇒ Boolean
156 157 158 159 160 161 |
# File 'lib/rfc5646/locale.rb', line 156 def matches_prefix?(prefix, key, value) return false unless value.is_a?(String) return true if key.to_s.downcase.starts_with?(prefix.downcase) return true if value.split(/\w+/).any? { |word| word.downcase.starts_with? prefix.downcase } false end |
Instance Method Details
#==(other) ⇒ true, false Also known as: eql?, equal?, ===
Tests for equality between two locales. Their full RFC 5646 codes must be equal.
262 263 264 265 266 267 268 269 |
# File 'lib/rfc5646/locale.rb', line 262 def ==(other) case other when Locale rfc5646 == other.rfc5646 else false end end |
#as_json(_options = nil) ⇒ Object
329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/rfc5646/locale.rb', line 329 def as_json( = nil) { rfc5646: rfc5646, components: { iso639: iso639, script: script, extended_language: extended_language, region: region, variants: variants, exensions: extensions }, name: name } end |
#child_of?(parent) ⇒ true, false
Returns whether this Languge is a subset of the given locale. “en-US” is a child of “en”.
300 301 302 303 304 |
# File 'lib/rfc5646/locale.rb', line 300 def child_of?(parent) return false if iso639 != parent.iso639 return false if parent.specificity > specificity parent.specified_parts.all? { |part| specified_parts.include?(part) } end |
#fallbacks ⇒ Array<Locale>
Returns the fallback order for this Locale. For example, fr-CA might fall back to fr, which then falls back to en. The fallback order is described in the ‘fallbacks.yml` file.
287 288 289 290 291 292 |
# File 'lib/rfc5646/locale.rb', line 287 def fallbacks fallbacks = Array.wrap(self.class.fallbacks[rfc5646]) .map { |l| self.class.from_rfc5646 l } fallbacks.unshift self fallbacks end |
#hash ⇒ Object
276 277 278 |
# File 'lib/rfc5646/locale.rb', line 276 def hash rfc5646.hash end |
#inspect ⇒ Object
345 346 347 |
# File 'lib/rfc5646/locale.rb', line 345 def inspect "#<#{self.class} #{rfc5646}>" end |
#name(locale = nil) ⇒ String
Returns a human-readable localized name of the locale.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/rfc5646/locale.rb', line 194 def name(locale = nil) I18n.with_locale(locale || I18n.locale) do i18n_language = if extended_language I18n.t "locale.extended.#{iso639}.#{extended_language}" else I18n.t "locale.name.#{iso639}" end i18n_dialect = if variants.present? I18n.t "locale.variant.#{iso639}.#{variants.join '.'}._END_" end i18n_script = script ? I18n.t("locale.script.#{script}") : nil i18n_region = region ? I18n.t("locale.region.#{region}") : nil if i18n_region && i18n_dialect && i18n_script I18n.t('locale.format.scripted_regional_dialectical', script: i18n_script, dialect: i18n_dialect, region: i18n_region, language: i18n_language ) elsif i18n_region && i18n_dialect I18n.t('locale.format.regional_dialectical', dialect: i18n_dialect, region: i18n_region, language: i18n_language ) elsif i18n_region && i18n_script I18n.t('locale.format.scripted_regional', script: i18n_script, region: i18n_region, language: i18n_language ) elsif i18n_dialect && i18n_script I18n.t('locale.format.scripted_dialectical', script: i18n_script, dialect: i18n_dialect, language: i18n_language ) elsif i18n_script I18n.t('locale.format.scripted', script: i18n_script, language: i18n_language ) elsif i18n_dialect I18n.t('locale.format.dialectical', dialect: i18n_dialect, language: i18n_language ) elsif i18n_region I18n.t('locale.format.regional', region: i18n_region, language: i18n_language ) else i18n_language end end end |
#pseudo? ⇒ true, false
Returns Whether this locale is a pseudo-locale.
307 308 309 |
# File 'lib/rfc5646/locale.rb', line 307 def pseudo? variants.include? 'pseudo' end |
#rfc5646 ⇒ String Also known as: to_param
Returns The full RFC 5646 code for this locale.
182 183 184 |
# File 'lib/rfc5646/locale.rb', line 182 def rfc5646 [iso639, script, extended_language, region, *variants].compact.join('-') end |
#specificity ⇒ Object
312 313 314 315 316 317 318 319 |
# File 'lib/rfc5646/locale.rb', line 312 def specificity specificity = 1 specificity += 1 if script specificity += 1 if region specificity += variants.size specificity += 1 if extended_language specificity + extensions.size end |
#specified_parts ⇒ Object
322 323 324 325 326 |
# File 'lib/rfc5646/locale.rb', line 322 def specified_parts # relies on the fact that the namespace for each element of the code is # *globally* unique, not just unique to the code element (variants + extensions + [script, region, extended_language]).compact end |