Skip to content

Commit

Permalink
Merge branch 'localization' of https://github.com/luan/chronic into l…
Browse files Browse the repository at this point in the history
…uan-localization

Conflicts:
	lib/chronic/numerizer.rb
	lib/chronic/parser.rb
  • Loading branch information
leejarvis committed May 22, 2013
2 parents f90ab0c + bade124 commit 024943d
Show file tree
Hide file tree
Showing 12 changed files with 345 additions and 187 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,19 @@ class to get full time zone support.
=> Thu, 15 Jun 2006 05:45:00 UTC +00:00
```

## Localization

Chronic supports basic localization. You can either create your own based on
the default english locale or use the
[chronic-l10n](http://github.com/luan/chronic-l10n) gem. You can change the
locale by simply changing the `Chronic.locale` value, such as (given you have
:pt-BR available):

>> Chronic.locale = :'pt-BR'
>> Chronic.parse("15 de Junho de 2006 as 5:45 da manha")
=> Thu, 15 Jun 2006 05:45:00 UTC +00:00


## Limitations

Chronic uses Ruby's built in Time class for all time storage and computation.
Expand Down
62 changes: 60 additions & 2 deletions lib/chronic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ module Chronic
VERSION = "0.9.1"

class << self

# Returns true when debug mode is enabled.
attr_accessor :debug

Expand All @@ -69,11 +68,33 @@ class << self
#
# Returns The Time class Chronic uses internally.
attr_accessor :time_class

# Returns the available locales that Chronic can use
attr_accessor :locale_hashes

# The current locale Chronic is using to parse strings
#
# Examples:
#
# require 'chronic'
#
# Chronic.locale = :'pt-BR'
# Chronic.parse('15 de Junho de 2006 as 5:54 da manha ')
# # => Thu, 15 Jun 2006 05:45:00 UTC +00:00
#
# Returns the locale name Chronic uses internally
attr_accessor :locale
end

self.debug = false
self.time_class = Time
self.locale = :en

require 'chronic/locales/en'

self.locale_hashes = {
:en => Chronic::Locales::EN
}

# Parses a string containing a natural language date or time.
#
Expand All @@ -84,9 +105,47 @@ class << self
# text - The String text to parse.
# opts - An optional Hash of configuration options passed to Parser::new.
def self.parse(text, options = {})
# ensure current locale is available
raise ArgumentError, "#{locale} is not an available locale" unless has_locale(locale)

Parser.new(options).parse(text)
end

# Adds a locale to the locale hash
#
# name - Symbol locale name
# locale - Hash locale values
def self.add_locale(name, locale)
raise ArgumentError, "Locale shoud be a hash" unless locale.is_a?(Hash)
locale_hashes[name] = locale
end

# Checks if a locale is available
#
# name - Symbol locale name
#
# Returns true if the locale is available, false if not
def self.has_locale(name)
locale_hashes.include? name
end


# Returns the translations for the current locale
def self.translate(keys, loc=nil)
loc ||= locale
node = locale_hashes[loc]

keys.each do |key|
if node.include? key
node = node[key]
else
return translate(keys, :en)
end
end

node
end

# Construct a new time object determining possible month overflows
# and leap years.
#
Expand Down Expand Up @@ -140,5 +199,4 @@ def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)

Chronic.time_class.local(year, month, day, hour, minute, second)
end

end
9 changes: 2 additions & 7 deletions lib/chronic/grabber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@ def self.scan(tokens, options)
#
# Returns a new Grabber object.
def self.scan_for_all(token)
scan_for token, self,
{
/last/ => :last,
/this/ => :this,
/next/ => :next
}
scan_for token, self, Chronic.translate([:grabber])
end

def to_s
'grabber-' << @type.to_s
end
end
end
end
183 changes: 183 additions & 0 deletions lib/chronic/locales/en.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
module Chronic
module Locales
EN = {
:pointer => {
/\bpast\b/ => :past,
/\b(?:future|in)\b/ => :future,
},
:ordinal_regex => /^(\d*)(st|nd|rd|th)$/,
:numerizer => {
:and => 'and',
:preprocess => [
[/ +|([^\d])-([^\d])/, '\1 \2'], # will mutilate hyphenated-words but shouldn't matter for date extraction
[/a half/, 'haAlf'] # take the 'a' out so it doesn't turn into a 1, save the half for the end
],
:fractional => [
[/(\d+)(?: | and |-)*haAlf/i, proc { ($1.to_f + 0.5).to_s }]
],
:direct_nums => [
['eleven', '11'],
['twelve', '12'],
['thirteen', '13'],
['fourteen', '14'],
['fifteen', '15'],
['sixteen', '16'],
['seventeen', '17'],
['eighteen', '18'],
['nineteen', '19'],
['ninteen', '19'], # Common mis-spelling
['zero', '0'],
['one', '1'],
['two', '2'],
['three', '3'],
['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
['five', '5'],
['six(\W|$)', '6\1'],
['seven(\W|$)', '7\1'],
['eight(\W|$)', '8\1'],
['nine(\W|$)', '9\1'],
['ten', '10'],
['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
],
:ordinals => [
['first', '1'],
['third', '3'],
['fourth', '4'],
['fifth', '5'],
['sixth', '6'],
['seventh', '7'],
['eighth', '8'],
['ninth', '9'],
['tenth', '10']
],
:ten_prefixes => [
['twenty', 20],
['thirty', 30],
['forty', 40],
['fourty', 40], # Common mis-spelling
['fifty', 50],
['sixty', 60],
['seventy', 70],
['eighty', 80],
['ninety', 90]
],
:big_prefixes => [
['hundred', 100],
['thousand', 1000],
['million', 1_000_000],
['billion', 1_000_000_000],
['trillion', 1_000_000_000_000],
],
},

:repeater => {
:season_names => {
/^springs?$/ => :spring,
/^summers?$/ => :summer,
/^(autumn)|(fall)s?$/ => :autumn,
/^winters?$/ => :winter
},
:month_names => {
/^jan[:\.]?(uary)?$/ => :january,
/^feb[:\.]?(ruary)?$/ => :february,
/^mar[:\.]?(ch)?$/ => :march,
/^apr[:\.]?(il)?$/ => :april,
/^may$/ => :may,
/^jun[:\.]?e?$/ => :june,
/^jul[:\.]?y?$/ => :july,
/^aug[:\.]?(ust)?$/ => :august,
/^sep[:\.]?(t[:\.]?|tember)?$/ => :september,
/^oct[:\.]?(ober)?$/ => :october,
/^nov[:\.]?(ember)?$/ => :november,
/^dec[:\.]?(ember)?$/ => :december
},
:day_names => {
/^m[ou]n(day)?$/ => :monday,
/^t(ue|eu|oo|u|)s?(day)?$/ => :tuesday,
/^we(d|dnes|nds|nns)(day)?$/ => :wednesday,
/^th(u|ur|urs|ers)(day)?$/ => :thursday,
/^fr[iy](day)?$/ => :friday,
/^sat(t?[ue]rday)?$/ => :saturday,
/^su[nm](day)?$/ => :sunday
},
:day_portions => {
/^ams?$/ => :am,
/^pms?$/ => :pm,
/^mornings?$/ => :morning,
/^afternoons?$/ => :afternoon,
/^evenings?$/ => :evening,
/^(night|nite)s?$/ => :night
},
:units => {
/^years?$/ => :year,
/^seasons?$/ => :season,
/^months?$/ => :month,
/^fortnights?$/ => :fortnight,
/^weeks?$/ => :week,
/^weekends?$/ => :weekend,
/^(week|business)days?$/ => :weekday,
/^days?$/ => :day,
/^hrs?$/ => :hour,
/^hours?$/ => :hour,
/^mins?$/ => :minute,
/^minutes?$/ => :minute,
/^secs?$/ => :second,
/^seconds?$/ => :second
}
},

:pre_normalize => {
:preprocess => proc {|str| str},
:pre_numerize => [
[/\b([ap])\.m\.?/, '\1m'],
[/\./, ':'],
[/['"]/, ''],
[/,/, ' '],
[/^second /, '2nd '],
[/\bsecond (of|day|month|hour|minute|second)\b/, '2nd \1']
],
:pos_numerize => [
[/\-(\d{2}:?\d{2})\b/, 'tzminus\1'],
[/([\/\-\,\@])/, ' \1 '],
[/(?:^|\s)0(\d+:\d+\s*pm?\b)/, ' \1'],
[/\btoday\b/, 'this day'],
[/\btomm?orr?ow\b/, 'next day'],
[/\byesterday\b/, 'last day'],
[/\bnoon\b/, '12:00pm'],
[/\bmidnight\b/, '24:00'],
[/\bnow\b/, 'this second'],
['quarter', '15'],
['half', '30'],
[/(\d{1,2}) (to|till|prior to|before)\b/, '\1 minutes past'],
[/(\d{1,2}) (after|past)\b/, '\1 minutes future'],
[/\b(?:ago|before(?: now)?)\b/, 'past'],
[/\bthis (?:last|past)\b/, 'last'],
[/\b(?:in|during) the (morning)\b/, '\1'],
[/\b(?:in the|during the|at) (afternoon|evening|night)\b/, '\1'],
[/\btonight\b/, 'this night'],
[/\b\d+:?\d*[ap]\b/,'\0m'],
[/\b(\d{2})(\d{2})(am|pm)\b/, '\1:\2\3'],
[/(\d)([ap]m|oclock)\b/, '\1 \2'],
[/\b(hence|after|from)\b/, 'future'],
[/^\s?an? /i, '1 '],
[/\b(\d{4}):(\d{2}):(\d{2})\b/, '\1 / \2 / \3'], # DTOriginal
[/\b0(\d+):(\d{2}):(\d{2}) ([ap]m)\b/, '\1:\2:\3 \4']
]
},

:grabber => {
/last/ => :last,
/this/ => :this,
/next/ => :next
},

:token => {
:comma => /^,$/,
:at => /^(at|@)$/,
:in => /^in$/,
:on => /^on$/,
:and => /^and$/
}
}
end
end
Loading

0 comments on commit 024943d

Please sign in to comment.