Skip to content

Commit bfb1065

Browse files
committed
Add support for script subtag to Locale
Set Latn default script for sr
1 parent e94e6de commit bfb1065

2 files changed

Lines changed: 63 additions & 26 deletions

File tree

lib/csl/locale.rb

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Locale < Node
1515
@extension = '.xml'.freeze
1616
@prefix = 'locales-'.freeze
1717

18-
@tag_pattern = /^[a-z]{2}(-[A-Z]{2})?|-[A-Z]{2}$/
18+
@tag_pattern = /^[a-z]{2}(-[a-z]{4})?(-[a-z]{2})?|-[a-z]{2}$/i
1919

2020
# Default languages/regions.
2121
# Auto-detection is based on these lists.
@@ -29,25 +29,30 @@ class Locale < Node
2929
AT de BR pt CA en CH de GB en TW zh
3030
}.map(&:to_sym)]).freeze
3131

32+
@scripts = Hash[*%w{
33+
sr Latn
34+
}.map(&:to_sym)].freeze
35+
3236

3337
class << self
3438
include Loader
3539

3640
attr_accessor :default
37-
attr_reader :languages, :regions
41+
attr_reader :languages, :regions, :scripts
3842

3943
def load(input = nil)
4044
input ||= Locale.default
4145
input = normalize input if input.to_s =~ @tag_pattern
4246
super(input)
4347
end
4448

45-
# Normalizes an IETF tag; adds a language's default region or a
46-
# region's default language.
49+
# Normalizes an IETF tag; adds default language, region, script.
4750
#
4851
# @example
4952
# Locale.normalize("en") #-> "en-US"
5053
# Locale.normalize("-BR") #-> "pt-BR"
54+
# Locale.normalize("de-at") #-> "de-AT"
55+
# Locale.normalize("sr") #-> "sr-Latn-RS"
5156
#
5257
# @raise [ArgumentError] if the passed-in string is no IETF tag
5358
#
@@ -56,15 +61,32 @@ def load(input = nil)
5661
def normalize(tag)
5762
tag = tag.to_s.strip
5863

59-
raise ArgumentError, "not a valid IETF tag: #{tag.inspect}" unless
60-
tag =~ @tag_pattern
64+
unless tag =~ @tag_pattern
65+
raise ArgumentError, "unsupported IETF tag: #{tag.inspect}"
66+
end
67+
68+
language, *rs = tag.split(/-/)
69+
region, script = rs.reverse
6170

62-
language, region = tag.split(/-/)
71+
if language.nil? || language.empty?
72+
language = languages[region.to_sym]
73+
else
74+
language.downcase!
75+
end
6376

64-
return [language, regions[language.to_sym]].compact.join('-') if region.nil?
65-
return [languages[region.to_sym], region].join('-') if language.empty?
77+
if region.nil?
78+
region = regions[language.to_sym]
79+
else
80+
region.upcase!
81+
end
82+
83+
if script.nil?
84+
script = scripts[language.to_sym]
85+
else
86+
script.capitalize!
87+
end
6688

67-
tag
89+
[language, script, region].compact.join('-')
6890
end
6991
end
7092

@@ -78,7 +100,7 @@ def normalize(tag)
78100

79101
has_language
80102

81-
attr_accessor :region
103+
attr_accessor :region, :script
82104

83105
alias_child :metadata, :info
84106
alias_child :dates, :date
@@ -181,14 +203,15 @@ def legacy?
181203
#
182204
# @return [self]
183205
def set(locale)
184-
@language, @region = Locale.normalize(locale).split(/-/).map(&:to_sym)
206+
@language, *rs = Locale.normalize(locale).split(/-/).map(&:to_sym)
207+
@region, @script = rs.reverse
185208
self
186209
end
187210

188-
# Sets the locale's language and region to nil.
211+
# Sets the locale's language, script and region to nil.
189212
# @return [self]
190213
def clear
191-
@language, @region = nil
214+
@language, @script, @region = nil
192215
self
193216
end
194217

@@ -414,11 +437,12 @@ def reverse_merge(other)
414437
end
415438

416439

417-
# Locales are sorted first by language, then by region; sort order is
418-
# alphabetical with the following exceptions: the default locale is
419-
# prioritised; in case of a language match the default region of that
420-
# language will be prioritised (e.g., de-DE will come before de-AT even
421-
# though the alphabetical order would be different).
440+
# Locales are sorted first by language, then by region and script;
441+
# sort order is alphabetical with the following exceptions:
442+
# the default locale is prioritised; in case of a language match
443+
# the default region of that language will be prioritised (e.g.,
444+
# de-DE will come before de-AT even though the alphabetical order
445+
# would be different).
422446
#
423447
# @param other [Locale] the locale used for comparison
424448
# @return [1,0,-1,nil] the result of the comparison
@@ -427,7 +451,7 @@ def <=>(other)
427451
when !other.is_a?(Locale)
428452
nil
429453
when [language, region] == [other.language, other.region]
430-
0
454+
script <=> other.script
431455
when default?
432456
-1
433457
when other.default?
@@ -448,7 +472,7 @@ def <=>(other)
448472

449473
# @return [String] the Locale's IETF tag
450474
def to_s
451-
[language, region].compact.join('-')
475+
[language, script, region].compact.join('-')
452476
end
453477

454478
# @return [String] a string representation of the Locale

spec/csl/locale_spec.rb

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,13 @@ module CSL
3737
'en' => 'en-US',
3838
'-GB' => 'en-GB',
3939
'-BR' => 'pt-BR',
40-
'de-AT' => 'de-AT'
40+
'de-AT' => 'de-AT',
41+
'de-aT' => 'de-AT',
42+
'sr-RS' => 'sr-Latn-RS',
43+
'sr' => 'sr-Latn-RS',
44+
'-RS' => 'sr-Latn-RS',
45+
'sr-latn-rs' => 'sr-Latn-RS',
46+
'sr-Cyrl-RS' => 'sr-Cyrl-RS'
4147
}.each_pair do |tag, expected|
4248
it "converts #{tag.inspect} to #{expected.inspect}" do
4349
expect(Locale.normalize(tag)).to eq(expected)
@@ -56,6 +62,10 @@ module CSL
5662
expect(Locale.new.region).to be_nil
5763
end
5864

65+
it 'has no script' do
66+
expect(Locale.new.script).to be_nil
67+
end
68+
5969
it 'contains no dates by default' do
6070
expect(Locale.new.dates).to be_nil
6171
end
@@ -94,22 +104,25 @@ module CSL
94104
end
95105

96106
describe '#set' do
97-
98107
it 'when passed "en-GB" sets language to :en and region to :GB' do
99108
locale.set('en-GB')
100-
expect([locale.language, locale.region]).to eq([:en, :GB])
109+
expect([locale.language, locale.region, locale.script]).to eq([:en, :GB, nil])
101110
end
102111

103112
it 'when passed "de" sets language to :de and region to :DE' do
104113
locale.set('de')
105-
expect([locale.language, locale.region]).to eq([:de, :DE])
114+
expect([locale.language, locale.region, locale.script]).to eq([:de, :DE, nil])
106115
end
107116

108117
it 'when passed "-AT" sets language to :de and region to :AT' do
109118
locale.set('-AT')
110-
expect([locale.language, locale.region]).to eq([:de, :AT])
119+
expect([locale.language, locale.region, locale.script]).to eq([:de, :AT, nil])
111120
end
112121

122+
it 'when passed "sr" sets language, region, and script' do
123+
locale.set('sr')
124+
expect([locale.language, locale.region, locale.script]).to eq([:sr, :RS, :Latn])
125+
end
113126
end
114127

115128
describe '#merge!' do

0 commit comments

Comments
 (0)