lib/csl/locale.rb in csl-1.0.0.pre5 vs lib/csl/locale.rb in csl-1.0.0.pre6
- old
+ new
@@ -3,21 +3,21 @@
# CSL::Locales contain locale specific date formatting options, term
# translations, and a number ordinalizer.
#
class Locale < Node
types << CSL::Info
-
+
include Comparable
@default = 'en-US'.freeze
@root = File.expand_path('../../../vendor/locales', __FILE__).freeze
-
+
@extension = '.xml'.freeze
@prefix = 'locales-'.freeze
-
-
+
+
# 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
@@ -26,42 +26,44 @@
}.map(&:to_sym)].freeze
@languages = @regions.invert.merge(Hash[*%w{
AT de BR pt CA en CH de GB en
}.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
+ end
end
-
+
attr_defaults :version => Schema.version, :xmlns => Schema.namespace
attr_struct :xmlns, :version
attr_children :'style-options', :info, :date, :terms
-
- attr_accessor :language, :region
-
+
+ has_language
+
+ attr_accessor :region
+
alias_child :metadata, :info
alias_child :dates, :date
alias_child :options, :style_options
private :attributes
undef_method :[]=
-
+
# call-seq:
# Locale.new -> default
# Locale.new('en') -> American English
# Locale.new('en', :'punctuation-in-quote' => fales) -> with style-options
# Locale.new(:lang => 'en-GB', :version => '1.0') -> British English
@@ -76,119 +78,123 @@
when 0
locale, attributes, options = Locale.default, {}, nil
when 1
if arguments[0].is_a?(Hash)
arguments[0] = arguments[0].symbolize_keys
-
+
locale = arguments[0].delete(:lang) ||
arguments[0].delete(:'xml:lang') || Locale.default
-
+
attributes, options = arguments
else
attributes, locale, options = {}, arguments
end
when 2
attributes, locale, options = {}, *arguments
else
raise ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)"
end
-
+
super(attributes, &nil)
-
+
set(locale) unless locale.nil?
-
+
unless options.nil?
children[:'style-options'] = StyleOptions.new(options)
end
-
+
yield self if block_given?
end
# TODO
# def initialize_copy(other)
# @options = other.options.dup
# end
-
-
+
+
def added_to(node)
raise ValidationError, "not allowed to add locale to #{node.nodename}" unless
node.nodename == 'style'
end
-
-
+
+
def version
attributes[:version]
end
-
+
def version=(version)
raise ArgumentError, "failed to set version to #{version}" unless
version.respond_to?(:to_s)
-
+
version = version.to_s.strip
-
+
raise ArgumentError, "failed to set version to #{version}: not a version string" unless
version =~ /^\d[\d\.]+$/
if version > Schema.version
warn "setting version to #{version}; latest supported version is #{Schema.version}"
end
-
+
attributes[:version] = version
end
# @return [Boolean] whether or not the Locale's version is less than CSL-Ruby's default version
def legacy?
version < Schema.version
end
-
- # call-seq:
- # locale.set('en') -> sets language to :en, region to :US
- # locale.set('de-AT') -> sets language to :de, region to :AT
- # locale.set('-DE') -> sets langauge to :de, region to :DE
+
+ # @example
+ # locale.set('en') #-> sets language to :en, region to :US
+ # locale.set('de-AT') #-> sets language to :de, region to :AT
+ # locale.set('-DE') #-> sets langauge to :de, region to :DE
#
# Sets language and region according to the passed-in locale string. If
# the region part is not defined by the string, this method will set the
# region to the default region for the given language.
#
- # Raises ArgumentError if the argument is no valid locale string. A valid
- # locale string is based on the syntax of IETF language tags; it consists
- # of either a language or region tag (or both), separated by a hyphen.
+ # @raise [ArgumentError] if the argument is no valid locale string.
+ # A valid locale string is based on the syntax of IETF language tags;
+ # 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
-
+
self
end
-
+
# Sets the locale's language and region to nil.
+ # @return [self]
def clear
@language, @region = nil
self
end
-
+
def translate(*arguments)
raise 'not implemented'
end
alias _ translate
alias t translate
-
- # call-seq:
- # locale.each_term { |term| block } -> locale
- # locale.each_term -> enumerator
+
+ # @example
+ # locale.each_term { |term| block } #-> locale
+ # locale.each_term #-> enumerator
#
# Calls block once for each term defined by the locale. If no block is
# given, an enumerator is returned instead.
def each_term
if block_given?
@@ -196,30 +202,30 @@
self
else
enum_for :each_term
end
end
-
- # call-seq:
- # locale.each_date { |date_format| block } -> locale
- # locale.each_date -> enumerator
+
+ # @example
+ # locale.each_date { |date_format| block } #-> locale
+ # locale.each_date #-> enumerator
#
# Calls block once for each date format defined by the locale. If no
# block is given, an enumerator is returned instead.
def each_date
if block_given?
date.each(&Proc.new)
else
enum_for :each_date
end
end
-
+
# @returns [Boolean] whether or not the Locale is the default locale
def default?
to_s == Locale.default
end
-
+
# @return [Boolean] whehter or not the Locale's region is the default
# region for its language
def default_region?
region && region == Locale.regions[language]
end
@@ -228,96 +234,14 @@
# language for its region
def default_language?
language && language == Locale.languages[region]
end
- # Ordinalizes the passed-in number using either the ordinal or
- # long-ordinal forms defined by the locale. If a long-ordinal form is
- # requested but not available, the regular ordinal will be returned
- # instead.
- #
- # @example
- # Locale.load('en').ordinalize(13)
- # #-> "13th"
- #
- # de = Locale.load('de')
- # de.ordinalize(13)
- # #-> "13."
- #
- # de.ordinalize(3, :form => :long, :gender => :feminine)
- # #-> "dritte"
- #
- # @note
- # For CSL 1.0 (and older) locales that do not define an "ordinal-00"
- # term the algorithm specified by CSL 1.0 is used; otherwise uses the
- # CSL 1.0.1 algorithm with improved support for languages other than
- # English.
- #
- # @param number [#to_i] the number to ordinalize
- # @param options [Hash] formatting options
- #
- # @option options [:short,:long] :form (:short) which ordinals form to use
- # @option options [:feminine,:masculine,:neutral] :gender (:neutral)
- # which ordinals gender-form to use
- #
- # @raise [ArgumentError] if number cannot be converted to an integer
- #
- # @return [String] the ordinal for the passed-in number
- def ordinalize(number, options = {})
- raise ArgumentError, "unable to ordinalize #{number}; integer expected" unless
- number.respond_to?(:to_i)
-
- number, query = number.to_i, ordinalize_query_for(options)
-
- key = query[:name]
-
- # try to match long-ordinals first
- if key.start_with?('l')
- query[:name] = key % number.abs
- ordinal = terms[query]
-
- if ordinal.nil?
- key = 'ordinal-%02d'
- else
- return ordinal.to_s(options)
- end
- end
-
- # CSL 1.0
- if legacy? || terms['ordinal-00'].nil?
- return legacy_ordinalize(number)
- end
-
- # CSL 1.0.1
- # 1. try to find exact match
- # 2. if no match is found, try to match modulus of number,
- # dividing mod by 10 at each iteration
- # 3. repeat until a match is found or mod reaches 0
-
- mod = 10 ** Math.log10([number.abs, 1].max).to_i
-
- query[:name] = key % number.abs
- ordinal = terms[query]
-
- while ordinal.nil? && mod > 0
- query[:name] = key % (number.abs % mod)
- ordinal = terms[query]
- mod = mod / 10
- end
-
- if ordinal.nil? && query.key?(:'gender-form')
- query.delete(:'gender-form')
- ordinal = terms[query]
- end
-
- [number, ordinal.to_s(options)].join
- end
-
def validate
Schema.validate self
end
-
+
def valid?
validate.empty?
end
# Locales are sorted first by language, then by region; sort order is
@@ -354,56 +278,28 @@
# @return [String] the Locale's IETF tag
def to_s
[language, region].compact.join('-')
end
-
+
# @return [String] a string representation of the Locale
def inspect
"#<#{self.class.name} #{to_s}>"
end
-
+
private
-
+
def attribute_assignments
if root?
super.push('xml:lang="%s"' % to_s)
else
'xml:lang="%s"' % to_s
end
end
-
+
def preamble
Schema.preamble.dup
- end
-
- # @return [Hash] a valid ordinalize query; the name attribute is a format string
- def ordinalize_query_for(options)
- q = { :name => 'ordinal-%02d' }
-
- unless options.nil?
- if options.key?(:form) && options[:form].to_s =~ /^long(-ordinal)?$/i
- q[:name] = 'long-ordinal-%02d'
- end
-
- gender = (options[:'gender-form'] || options[:gender]).to_s
- unless gender.empty? || gender =~ /^n/i
- q[:'gender-form'] = (gender =~ /^m/i) ? 'masculine' : 'feminine'
- end
- end
-
- q
end
-
- def legacy_ordinalize(number)
- case
- when (11..13).include?(number.abs % 100)
- [number, terms['ordinal-04']].join
- when (1..3).include?(number.abs % 10)
- [number, terms['ordinal-%02d' % (number.abs % 10)]].join
- else
- [number, terms['ordinal-04']].join
- end
- end
+
end
-
+
end
\ No newline at end of file