lib/csl/locale.rb in csl-1.0.0.pre10 vs lib/csl/locale.rb in csl-1.0.0.pre11

- old
+ new

@@ -5,73 +5,95 @@ # class Locale < Node types << CSL::Info include Comparable - + @default = 'en-US'.freeze - @root = File.expand_path('../../../vendor/locales', __FILE__).freeze + @root = '/usr/local/share/csl/locales'.freeze @extension = '.xml'.freeze @prefix = 'locales-'.freeze + @tag_pattern = /^[a-z]{2}(-[A-Z]{2})?|-[A-Z]{2}$/ # Default languages/regions. # Auto-detection is based on these lists. @regions = Hash[*%w{ af ZA ar AR bg BG ca AD cs CZ da DK de DE el GR en US es ES et EE fa IR fr FR he IL hu HU is IS it IT ja JP km KH ko KR mn MN nb NO nl NL nn NO pl PL pt PT ro RO ru RU sk SK sl SI sr RS sv SE th TH tr TR uk UA vi VN - zh CN zh TW + zh CN }.map(&:to_sym)].freeze @languages = @regions.invert.merge(Hash[*%w{ - AT de BR pt CA en CH de GB en + AT de BR pt CA en CH de GB en TW zh }.map(&:to_sym)]).freeze class << self include Loader attr_accessor :default attr_reader :languages, :regions - def parse(data) - node = CSL.parse!(data, self) - - raise ParseError, "root node is not a locale: #{node.inspect}" unless - node.is_a?(self) - - node - end - def load(input = Locale.default) + input = normalize input if input.to_s =~ tag_pattern super end + + # Normalizes an IETF tag; adds a language's default region or a + # region's default language. + # + # @example + # Locale.normalize("en") #-> "en-US" + # Locale.normalize("-BR") #-> "pt-BR" + # + # @raise [ArgumentError] if the passed-in string is no IETF tag + # + # @param tag [String] an IETF tag to be normalized + # @return [String] the normalized IETF tag + def normalize(tag) + tag = tag.to_s.strip + + raise ArgumentError, "not a valid IETF tag: #{tag.inspect}" unless + tag =~ tag_pattern + + language, region = tag.split(/-/) + + return [language, regions[language.to_sym]].compact.join('-') if region.nil? + return [languages[region.to_sym], region].join('-') if language.empty? + + tag + end + + private + + attr_reader :tag_pattern end attr_defaults :version => Schema.version, :xmlns => Schema.namespace attr_struct :xmlns, :version attr_children :'style-options', :info, :date, :terms has_language - + attr_accessor :region alias_child :metadata, :info alias_child :dates, :date alias_child :options, :style_options - private :attributes + protected :attributes undef_method :[]= # @example # Locale.new #-> default # Locale.new('en') #-> American English - # Locale.new('en', :'punctuation-in-quote' => fales) #-> with style-options + # Locale.new('en', :'punctuation-in-quote' => false) #-> with style-options # Locale.new(:lang => 'en-GB', :version => '1.0') #-> British English # # Returns a new locale. In the first form, the language/regions is set # to the default language and region. In the second form the # language/region is set by the passed-in IETF tag. The third form @@ -88,11 +110,11 @@ locale = arguments[0].delete(:lang) || arguments[0].delete(:'xml:lang') || Locale.default attributes, options = arguments else - attributes, locale, options = {}, arguments + attributes, locale, options = {}, *arguments end when 2 attributes, locale, options = {}, *arguments else raise ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)" @@ -107,14 +129,14 @@ end yield self if block_given? end - # TODO - # def initialize_copy(other) - # @options = other.options.dup - # end + def initialize_copy(other) + super + @language, @region = other.language, other.region + end def added_to(node) raise ValidationError, "not allowed to add locale to #{node.nodename}" unless node.nodename == 'style' @@ -160,25 +182,11 @@ # it consists of either a language or region tag (or both), separated # by a hyphen. # # @return [self] def set(locale) - language, region = locale.to_s.scan(/([a-z]{2})?(?:-([A-Z]{2}))?/)[0].map do |tag| - tag.respond_to?(:to_sym) ? tag.to_sym : nil - end - - case - when language && region - @language, @region = language, region - when language - @language, @region = language, Locale.regions[language] - when region - @language, @region = Locale.languages[region], region - else - raise ArgumentError, "not a valid locale string: #{locale.inspect}" - end - + @language, @region = Locale.normalize(locale).split(/-/).map(&:to_sym) self end # Sets the locale's language and region to nil. # @return [self] @@ -244,10 +252,26 @@ def valid? validate.empty? end + # @return [Locale] + def merge(*others) + deep_copy.merge!(*others) + end + + # @return [self] + def merge!(*others) + others.each do |other| + merge_options other + merge_dates other + end + + self + end + + # Locales are sorted first by language, then by region; sort order is # alphabetical with the following exceptions: the default locale is # prioritised; in case of a language match the default region of that # language will be prioritised (e.g., de-DE will come before de-AT even # though the alphabetical order would be different). @@ -300,8 +324,40 @@ def preamble Schema.preamble.dup end + # @param other [Locale] an other locale whose options should be merged + # @return [self] + def merge_options(other) + return self unless other.has_options? + + if has_options? + options.attributes.merge! other.options.attributes + else + add_child other.options.dup + end + + self + end + + # @param other [Locale] an other locale whose date nodes should be merged + # @return [self] + def merge_dates(other) + return self unless other.has_dates? + + if has_dates? + other.each_date do |date| + delete_children each_date.select { |d| d[:form] == date[:form] } + add_child date.deep_copy + end + else + other.each_date do |date| + add_child date.deep_copy + end + end + + self + end end end \ No newline at end of file