6926: merge to trunk p2: gramps/ code changes

svn: r23324
This commit is contained in:
Vassilii Khachaturov 2013-10-16 14:04:21 +00:00
parent dc4404e555
commit be84468d3f
18 changed files with 1534 additions and 950 deletions

View File

@ -24,6 +24,8 @@
Class handling language-specific selection for date parser and displayer.
"""
from __future__ import print_function, unicode_literals
#-------------------------------------------------------------------------
#
# set up logging
@ -36,6 +38,7 @@ _ = glocale.translation.sgettext
# import prerequisites for localized handlers
from ._datehandler import (LANG, LANG_SHORT, LANG_TO_PARSER, LANG_TO_DISPLAY,
register_datehandler)
from . import _datestrings
# Import all the localized handlers
from . import _date_ar
@ -91,5 +94,16 @@ except:
# Import utility functions
from ._dateutils import *
from ._grampslocale import (codeset, month_to_int, long_months, short_months,
long_days, short_days, tformat)
from ._grampslocale import (codeset, long_days, short_days, tformat)
if __name__ == "__main__":
from ._datedisplay import DateDisplay
m = 0
for l,d in LANG_TO_DISPLAY.items():
if len(l) != 2:
continue
m = max(m, len(d.formats))
print("{}: {} {} own dg: {}".format(
l, len(d.formats), d.formats,
d._display_gregorian != DateDisplay._display_gregorian))
print("MAX: ", m)

View File

@ -21,7 +21,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
# $Id: _date_fr.py 22672 2013-07-13 18:01:08Z paul-franklin $
#-------------------------------------------------------------------------
#
@ -60,92 +60,15 @@ class DateParserFR(DateParser):
month_to_int = DateParser.month_to_int
# Custom short months not the same as long months
month_to_int["janv"] = 1
month_to_int["févr"] = 2
month_to_int["juil"] = 7
month_to_int["sept"] = 9
month_to_int["oct"] = 10
month_to_int["déc"] = 12
# Add common value
month_to_int["bluviose"] = 5
month_to_int["vendose"] = 6
month_to_int["7bre"] = 9
month_to_int["8bre"] = 10
month_to_int["9bre"] = 11
month_to_int["10bre"] = 12
month_to_int["xbre"] = 12
# Add common latin
month_to_int["januaris"] = 1
month_to_int["januarii"] = 1
month_to_int["januarius"] = 1
month_to_int["februaris"] = 2
month_to_int["februarii"] = 2
month_to_int["februarius"] = 2
month_to_int["martii"] = 3
month_to_int["martius"] = 3
month_to_int["aprilis"] = 4
month_to_int["maius"] = 5
month_to_int["maii"] = 5
month_to_int["junius"] = 6
month_to_int["junii"] = 6
month_to_int["julius"] = 7
month_to_int["julii"] = 7
month_to_int["augustus"] = 8
month_to_int["augusti"] = 8
month_to_int["septembris"] = 9
month_to_int["7bris"] = 9
month_to_int["september"] = 9
month_to_int["october"] = 10
month_to_int["octobris"] = 10
month_to_int["8bris"] = 10
month_to_int["novembris"] = 11
month_to_int["9bris"] = 11
month_to_int["november"] = 11
month_to_int["decembris"] = 12
month_to_int["10bris"] = 12
month_to_int["xbris"] = 12
month_to_int["december"] = 12
#local and historical variants
# Add common on east france
month_to_int["janer"] = 1
month_to_int["jenner"] = 1
month_to_int["hartmonat"] = 1
month_to_int["hartung"] = 1
month_to_int["eismond"] = 1
month_to_int["hornung"] = 2
month_to_int["wintermonat"] = 2
month_to_int["taumond"] = 2
month_to_int["narrenmond"] = 2
month_to_int["lenzing"] = 3
month_to_int["ostermond"] = 4
month_to_int["wonnemond"] = 5
month_to_int["wiesenmonat"] = 5
month_to_int["brachet"] = 6
month_to_int["heuet"] = 7
month_to_int["ernting"] = 8
month_to_int["scheiding"] = 9
month_to_int["gilbhard"] = 10
month_to_int["nebelmonat"] = 11
month_to_int["nebelung"] = 11
month_to_int["julmond"] = 12
modifier_to_int = {
'avant': Date.MOD_BEFORE,
'av.' : Date.MOD_BEFORE,
#u'av' : Date.MOD_BEFORE, # Broke Hebrew "Av" month name
#u'<' : Date.MOD_BEFORE, # Worrying about XML/HTML parsing
'après': Date.MOD_AFTER,
'ap.' : Date.MOD_AFTER,
'ap' : Date.MOD_AFTER,
#u'>' : Date.MOD_AFTER, # Worrying about XML/HTML parsing
'avant' : Date.MOD_BEFORE,
'av.' : Date.MOD_BEFORE,
#u'av' : Date.MOD_BEFORE, # Broke Hebrew "Av" month name
#u'<' : Date.MOD_BEFORE, # Worrying about XML/HTML parsing
'après' : Date.MOD_AFTER,
'ap.' : Date.MOD_AFTER,
'ap' : Date.MOD_AFTER,
#u'>' : Date.MOD_AFTER, # Worrying about XML/HTML parsing
'environ' : Date.MOD_ABOUT,
'env.' : Date.MOD_ABOUT,
'env' : Date.MOD_ABOUT,
@ -157,23 +80,6 @@ class DateParserFR(DateParser):
'~' : Date.MOD_ABOUT,
}
calendar_to_int = {
'grégorien': Date.CAL_GREGORIAN,
'g' : Date.CAL_GREGORIAN,
'julien': Date.CAL_JULIAN,
'j' : Date.CAL_JULIAN,
'hébreu': Date.CAL_HEBREW,
'h' : Date.CAL_HEBREW,
'islamique': Date.CAL_ISLAMIC,
'i' : Date.CAL_ISLAMIC,
'révolutionnaire': Date.CAL_FRENCH,
'r' : Date.CAL_FRENCH,
'perse': Date.CAL_PERSIAN,
'p' : Date.CAL_PERSIAN,
'suédois': Date.CAL_SWEDISH,
's' : Date.CAL_SWEDISH,
}
quality_to_int = {
'estimée': Date.QUAL_ESTIMATED,
'est.' : Date.QUAL_ESTIMATED,
@ -202,6 +108,83 @@ class DateParserFR(DateParser):
"""
DateParser.init_strings(self)
DateParser.calendar_to_int.update({
'grégorien' : Date.CAL_GREGORIAN,
'g' : Date.CAL_GREGORIAN,
'julien' : Date.CAL_JULIAN,
'j' : Date.CAL_JULIAN,
'hébreu' : Date.CAL_HEBREW,
'h' : Date.CAL_HEBREW,
'islamique' : Date.CAL_ISLAMIC,
'i' : Date.CAL_ISLAMIC,
'révolutionnaire' : Date.CAL_FRENCH,
'r' : Date.CAL_FRENCH,
'perse' : Date.CAL_PERSIAN,
'p' : Date.CAL_PERSIAN,
'suédois' : Date.CAL_SWEDISH,
's' : Date.CAL_SWEDISH,
})
DateParser.month_to_int.update({
"januaris" : 1,
"januarii" : 1,
"januarius" : 1,
"janer" : 1,
"jenner" : 1,
"hartmonat" : 1,
"hartung" : 1,
"eismond" : 1,
"februaris" : 2,
"februarii" : 2,
"februarius" : 2,
"hornung" : 2,
"wintermonat" : 2,
"taumond" : 2,
"narrenmond" : 2,
"martii" : 3,
"martius" : 3,
"lenzing" : 3,
"aprilis" : 4,
"ostermond" : 4,
"maius" : 5,
"maii" : 5,
"bluviose" : 5,
"wonnemond" : 5,
"wiesenmonat" : 5,
"junius" : 6,
"junii" : 6,
"vendose" : 6,
"brachet" : 6,
"julius" : 7,
"julii" : 7,
"heuet" : 7,
"augustus" : 8,
"augusti" : 8,
"ernting" : 8,
"septembris" : 9,
"7bre" : 9,
"7bris" : 9,
"september" : 9,
"october" : 10,
"octobris" : 10,
"8bre" : 10,
"8bris" : 10,
"gilbhard" : 10,
"november" : 11,
"novembris" : 11,
"9bre" : 11,
"9bris" : 11,
"nebelmonat" : 11,
"nebelung" : 11,
"december" : 12,
"decembris" : 12,
"10bre" : 12,
"10bris" : 12,
"xbre" : 12,
"xbris" : 12,
"julmond" : 12,
})
# This self._numeric is different from the base
# avoid bug gregorian / french calendar conversion (+/-10 days)
@ -211,35 +194,35 @@ class DateParserFR(DateParser):
self._range = re.compile("(entre|ent\.|ent)\s+(?P<start>.+)\s+(et)\s+(?P<stop>.+)",
re.IGNORECASE)
# This self._text are different from the base
# by adding ".?" after the first date and removing "\s*$" at the end
# This self._text are different from the base
# by adding ".?" after the first date and removing "\s*$" at the end
#gregorian and julian
#gregorian and julian
self._text2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' %
self._mon_str, re.IGNORECASE)
#hebrew
#hebrew
self._jtext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' %
self._jmon_str, re.IGNORECASE)
#french
#french
self._ftext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' %
self._fmon_str, re.IGNORECASE)
#persian
#persian
self._ptext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' %
self._pmon_str, re.IGNORECASE)
#islamic
#islamic
self._itext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' %
self._imon_str, re.IGNORECASE)
#swedish
#swedish
self._stext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' %
self._smon_str, re.IGNORECASE)
@ -253,19 +236,6 @@ class DateDisplayFR(DateDisplay):
"""
French language date display class.
"""
long_months = ( "", "janvier", "février", "mars", "avril", "mai",
"juin", "juillet", "août", "septembre", "octobre",
"novembre", "décembre" )
short_months = ( "", "janv", "févr", "mars", "avril", "mai", "juin",
"juil", "août", "sept", "oct", "nov", "déc" )
calendar = ("", "Julien", "Hébreu", "Révolutionnaire",
"Perse", "Islamique", "Suédois")
_mod_str = ("", "avant ", "après ", "vers ", "", "", "")
_qual_str = ("", "estimée ", "calculée ", "")
_bce_str = "%s avant le calendrier"
@ -287,7 +257,7 @@ class DateDisplayFR(DateDisplay):
)
# this definition must agree with its "_display_gregorian" method
def _display_gregorian(self, date_val):
def _display_gregorian(self, date_val, **kwargs):
"""
display gregorian calendar date in different format
"""
@ -409,40 +379,7 @@ class DateDisplayFR(DateDisplay):
else:
return value
def display(self, date):
"""
Return a text string representing the date.
"""
mod = date.get_modifier()
cal = date.get_calendar()
qual = date.get_quality()
start = date.get_start_date()
newyear = date.get_new_year()
qual_str = (self._qual_str)[qual]
if mod == Date.MOD_TEXTONLY:
return date.get_text()
elif start == Date.EMPTY:
return ""
elif mod == Date.MOD_SPAN:
date1 = self.display_cal[cal](start)
date2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%s%s %s %s %s%s" % (qual_str, 'de', date1, 'à',
date2, scal)
elif mod == Date.MOD_RANGE:
date1 = self.display_cal[cal](start)
date2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%s%s %s %s %s%s" % (qual_str, 'entre', date1, 'et',
date2, scal)
else:
text = self.display_cal[date.get_calendar()](start)
scal = self.format_extras(cal, newyear)
return "%s%s%s%s" % (qual_str, (self._mod_str)[mod], text,
scal)
display = DateDisplay.display_formatted
#-------------------------------------------------------------------------
#

View File

@ -18,7 +18,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
# $Id: _date_hr.py 22672 2013-07-13 18:01:08Z paul-franklin $
#
# Croatian version 2008 by Josip
@ -50,98 +50,6 @@ from ._datehandler import register_datehandler
#
#-------------------------------------------------------------------------
class DateParserHR(DateParser):
"""
Converts a text string into a Date object
"""
month_to_int = DateParser.month_to_int
month_to_int["siječanj"] = 1
month_to_int["siječnja"] = 1
month_to_int["sij"] = 1
month_to_int["januar"] = 1
month_to_int["januara"] = 1
month_to_int["i"] = 1
month_to_int["veljača"] = 2
month_to_int["veljače"] = 2
month_to_int["velj"] = 2
month_to_int["februar"] = 2
month_to_int["februara"] = 2
month_to_int["ii"] = 2
month_to_int["ožujak"] = 3
month_to_int["ožujka"] = 3
month_to_int["ožu"] = 3
month_to_int["mart"] = 3
month_to_int["marta"] = 3
month_to_int["iii"] = 3
month_to_int["travanj"] = 4
month_to_int["travnja"] = 4
month_to_int["tra"] = 4
month_to_int["april"] = 4
month_to_int["aprila"] = 4
month_to_int["iv"] = 4
month_to_int["svibanj"] = 5
month_to_int["svibnja"] = 5
month_to_int["svi"] = 5
month_to_int["maj"] = 5
month_to_int["maja"] = 5
month_to_int["v"] = 5
month_to_int["lipanj"] = 6
month_to_int["lipnja"] = 6
month_to_int["lip"] = 6
month_to_int["jun"] = 6
month_to_int["juna"] = 6
month_to_int["vi"] = 6
month_to_int["srpanj"] = 7
month_to_int["srpnja"] = 7
month_to_int["srp"] = 7
month_to_int["juli"] = 7
month_to_int["jula"] = 7
month_to_int["vii"] = 7
month_to_int["kolovoz"] = 8
month_to_int["kolovoza"] = 8
month_to_int["kol"] = 8
month_to_int["august"] = 8
month_to_int["augusta"] = 8
month_to_int["viii"] = 8
month_to_int["rujan"] = 9
month_to_int["rujna"] = 9
month_to_int["ruj"] = 9
month_to_int["septembar"] = 9
month_to_int["septembra"] = 9
month_to_int["ix"] = 9
month_to_int["7ber"] = 9
month_to_int["listopad"] = 10
month_to_int["listopada"] = 10
month_to_int["lis"] = 10
month_to_int["oktobar"] = 10
month_to_int["oktobra"] = 10
month_to_int["x"] = 10
month_to_int["8ber"] = 10
month_to_int["studeni"] = 11
month_to_int["studenog"] = 11
month_to_int["stu"] = 11
month_to_int["novembar"] = 11
month_to_int["novembra"] = 11
month_to_int["xi"] = 11
month_to_int["9ber"] = 11
month_to_int["prosinac"] = 12
month_to_int["prosinca"] = 12
month_to_int["pro"] = 12
month_to_int["decembar"] = 12
month_to_int["decembra"] = 12
month_to_int["xii"] = 12
modifier_to_int = {
'prije' : Date.MOD_BEFORE,
'pr. ' : Date.MOD_BEFORE,
@ -152,23 +60,6 @@ class DateParserHR(DateParser):
}
calendar_to_int = {
'gregorijanski' : Date.CAL_GREGORIAN,
'greg.' : Date.CAL_GREGORIAN,
'julijanski' : Date.CAL_JULIAN,
'jul.' : Date.CAL_JULIAN,
'hebrejski' : Date.CAL_HEBREW,
'hebr.' : Date.CAL_HEBREW,
'islamski' : Date.CAL_ISLAMIC,
'isl.' : Date.CAL_ISLAMIC,
'francuski republikanski': Date.CAL_FRENCH,
'franc.' : Date.CAL_FRENCH,
'perzijski' : Date.CAL_PERSIAN,
'perz. ' : Date.CAL_PERSIAN,
'švedski' : Date.CAL_SWEDISH,
's' : Date.CAL_SWEDISH,
}
quality_to_int = {
'približno' : Date.QUAL_ESTIMATED,
'prb.' : Date.QUAL_ESTIMATED,
@ -184,20 +75,20 @@ class DateParserHR(DateParser):
compiles regular expression strings for matching dates
"""
DateParser.init_strings(self)
# match 'Day. MONTH year.' format with or without dots
self._text2 = re.compile('(\d+)?\.?\s*?%s\.?\s*((\d+)(/\d+)?)?\s*\.?$'
% self._mon_str, re.IGNORECASE)
# match Day.Month.Year.
self._numeric = re.compile(
"((\d+)[/\. ])?\s*((\d+)[/\.])?\s*(\d+)\.?$"
)
self._span = re.compile("(od)\s+(?P<start>.+)\s+(do)\s+(?P<stop>.+)",
re.IGNORECASE)
self._range = re.compile(
"(između)\s+(?P<start>.+)\s+(i)\s+(?P<stop>.+)",
re.IGNORECASE)
self._jtext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'\
% self._jmon_str, re.IGNORECASE)
#~ DateParser.calendar_to_int.update({
#~ 'персидский' : Date.CAL_PERSIAN,
#~ 'п' : Date.CAL_PERSIAN,
#~ })
_span_1 = ['od']
_span_2 = ['do']
_range_1 = ['između']
_range_2 = ['i']
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
('|'.join(_span_1), '|'.join(_span_2)),
re.IGNORECASE)
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
('|'.join(_range_1), '|'.join(_range_2)),
re.IGNORECASE)
#-------------------------------------------------------------------------
#
@ -208,150 +99,11 @@ class DateDisplayHR(DateDisplay):
"""
Croatian language date display class.
"""
long_months = ( "",
"siječnja",
"veljače",
"ožujka",
"travnja",
"svibnja",
"lipnja",
"srpnja",
"kolovoza",
"rujna",
"listopada",
"studenog",
"prosinca"
)
#currently unused
short_months = ( "", "sij", "velj", "ožu", "tra", "svi", "lip",
"srp", "kol", "ruj", "lis", "stu", "pro"
)
calendar = (
"", "julijanski", "hebrejski",
"francuski republikanski", "perzijski", "islamski",
"swedish"
)
_mod_str = ("", "prije ", "poslije ", "okolo ", "", "", "")
_qual_str = ("", "približno ", "izračunato ")
# TODO fix BUG 7064: non-Gregorian calendars wrongly use BCE notation for negative dates
# not refactoring _bce_str into base class because it'll be gone under #7064
_bce_str = "%s p.n.e."
formats = (
"GGGG-MM-DD (ISO-8601)",
"Numerički",
"D.M.GGGG.",
"D. MMMM GGGG.",
"D. Rb GGGG."
)
# this definition must agree with its "_display_gregorian" method
roman_months = (
"",
"I",
"II",
"III",
"IV",
"V",
"VI",
"VII",
"VIII",
"IX",
"X",
"XI",
"XII"
)
def _display_gregorian(self, date_val):
"""
display gregorian calendar date in different format
"""
# this must agree with its locale-specific "formats" definition
year = self._slash_year(date_val[2], date_val[3])
if self.format == 0:
return self.display_iso(date_val)
elif self.format == 1:
# numerical
if date_val[3]:
return self.display_iso(date_val)
else:
if date_val[0] == 0 and date_val[1] == 0:
value = str(date_val[2])
else:
value = self._tformat.replace('%m', str(date_val[1]))
value = value.replace('%d', str(date_val[0]))
value = value.replace('%Y', str(abs(date_val[2])))
value = value.replace('-', '/')
elif self.format == 2:
# day.month_number.year.
if date_val[0] == 0:
if date_val[1] == 0:
value = year
else:
value = "%s.%s." % (date_val[1], year)
else:
value = "%s.%d.%s." % (date_val[0], date_val[1], year)
elif self.format == 3:
# day. month_name year.
if date_val[0] == 0:
if date_val[1] == 0:
value = "%s." % year
else:
value = "%s %s." % (self.long_months[date_val[1]], year)
else:
value = "%d. %s %s." % (date_val[0],
self.long_months[date_val[1]], year)
else:
# day. Roman_number_month year.
if date_val[0] == 0:
if date_val[1] == 0:
value = "%s." % year
else:
value = "%s %s." % (self.roman_months[date_val[1]], year)
else:
value = "%d. %s %s." % (date_val[0],
self.roman_months[date_val[1]], year)
if date_val[2] < 0:
return self._bce_str % value
else:
return value
def display(self, date):
"""
Return a text string representing the date.
"""
mod = date.get_modifier()
cal = date.get_calendar()
qual = date.get_quality()
start = date.get_start_date()
newyear = date.get_new_year()
qual_str = self._qual_str[qual]
if mod == Date.MOD_TEXTONLY:
return date.get_text()
elif start == Date.EMPTY:
return ""
elif mod == Date.MOD_SPAN:
d_1 = self.display_cal[cal](start)
d_2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%s%s %s %s %s%s" % (qual_str, 'od', d_1, 'do', d_2,
scal)
elif mod == Date.MOD_RANGE:
d_1 = self.display_cal[cal](start)
d_2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%s%s %s %s %s%s" % (qual_str, 'između', d_1, 'i', d_2,
scal)
else:
text = self.display_cal[date.get_calendar()](start)
scal = self.format_extras(cal, newyear)
return "%s%s%s%s" % (qual_str, self._mod_str[mod], text,
scal)
display = DateDisplay.display_formatted
#-------------------------------------------------------------------------
#

View File

@ -48,7 +48,6 @@ from ._datehandler import register_datehandler
#
#-------------------------------------------------------------------------
class DateParserRU(DateParser):
modifier_to_int = {
'перед' : Date.MOD_BEFORE,
'по' : Date.MOD_BEFORE,
@ -70,22 +69,6 @@ class DateParserRU(DateParser):
'прибл' : Date.MOD_ABOUT,
}
calendar_to_int = {
'григорианский' : Date.CAL_GREGORIAN,
'г' : Date.CAL_GREGORIAN,
'юлианский' : Date.CAL_JULIAN,
'ю' : Date.CAL_JULIAN,
'еврейский' : Date.CAL_HEBREW,
'е' : Date.CAL_HEBREW,
'исламский' : Date.CAL_ISLAMIC,
'и' : Date.CAL_ISLAMIC,
'республиканский': Date.CAL_FRENCH,
'р' : Date.CAL_FRENCH,
'персидский' : Date.CAL_PERSIAN,
'п' : Date.CAL_PERSIAN,
'swedish' : Date.CAL_SWEDISH,
's' : Date.CAL_SWEDISH,
}
quality_to_int = {
'оценено' : Date.QUAL_ESTIMATED,
@ -100,127 +83,19 @@ class DateParserRU(DateParser):
'выч' : Date.QUAL_CALCULATED,
}
hebrew_to_int = {
"тишрей":1,
"тишрея":1,
"хешван":2,
"хешвана":2,
"кислев":3,
"кислева":3,
"тевет":4,
"тевета":4,
"шеват":5,
"шевата":5,
"адар":6,
"адара":6,
"адара бет":7,
"нисан":8,
"нисана":8,
"ниссан":8,
"ниссана":8,
"ияр":9,
"ияра":9,
"сиван":10,
"сивана":10,
"тамуз":11,
"тамуза":11,
"таммуз":11,
"таммуза":11,
"ав":12,
"ава":12,
"элул":13,
"элула":13,
"элуль":13,
"элуля":13,
}
islamic_to_int = {
"мухаррам":1,
"мухаррама":1,
"сафар":2,
"сафара":2,
"раби-аль-авваль":3,
"раби-аль-авваля":3,
"раби-ассани":4,
"джумада-аль-уля":5,
"джумада-аль-ахира":6,
"раджаб":7,
"раджаба":7,
"шаабан":8,
"шаабана":8,
"рамадан":9,
"рамадана":9,
"шавваль":10,
"шавваля":10,
"зуль-каада":11,
"зуль-хиджжа":12,
}
persian_to_int = {
"фарвардин":1,
"фарвардина":1,
"урдбихишт":2,
"урдбихишта":2,
"хурдад":3,
"хурдада":3,
"тир":4,
"тира":4,
"мурдад":5,
"мурдада":5,
"шахривар":6,
"шахривара":6,
"михр":7,
"михра":7,
"абан":8,
"абана":8,
"азар":9,
"азара":9,
"дай":10,
"дая":10,
"бахман":11,
"бахмана":11,
"исфаидармуз":12,
"исфаидармуза":12,
}
french_to_int = {
"вандемьер":1,
"вандемьера":1,
"брюмер":2,
"брюмера":2,
"фример":3,
"фримера":3,
"нивоз":4,
"нивоза":4,
"плювиоз":5,
"плювиоза":5,
"вантоз":6,
"вантоза":6,
"жерминаль":7,
"жерминаля":7,
"флореаль":8,
"флореаля":8,
"прериаль":9,
"прериаля":9,
"мессидор":10,
"мессидора":10,
"термидор":11,
"термидора":11,
"фрюктидор":12,
"фрюктидора":12,
"доп.":13,
"дополн.":13,
"дополнит.":13,
}
bce = [
'до нашей эры', 'до н. э.', 'до н.э.',
'до н э', 'до нэ'] + DateParser.bce
def init_strings(self):
DateParser.init_strings(self)
DateParser.calendar_to_int.update({
'персидский' : Date.CAL_PERSIAN,
'п' : Date.CAL_PERSIAN,
})
_span_1 = ['с', 'от']
_span_2 = ['по', 'до']
#_span_2 = ['по', 'до'] # <-- clashes with bce parsing :-(
_span_2 = ['по']
_range_1 = ['между', 'меж\.', 'меж']
_range_2 = ['и']
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
@ -239,136 +114,12 @@ class DateDisplayRU(DateDisplay):
"""
Russian language date display class.
"""
long_months = ( "", "января", "февраля", "марта", "апреля", "мая",
"июня", "июля", "августа", "сентября", "октября",
"ноября", "декабря" )
short_months = ( "", "янв", "фев", "мар", "апр", "мая", "июн",
"июл", "авг", "сен", "окт", "ноя", "дек" )
calendar = (
"",
"юлианский",
"еврейский",
"республиканский",
"персидский",
"исламский",
"шведский"
)
_mod_str = (
"",
"перед ",
"после ",
"около ",
"", "", "")
_qual_str = ("", "оцен ", "вычисл ")
# TODO fix BUG 7064: non-Gregorian calendars wrongly use BCE notation for negative dates
# not refactoring _bce_str into base class because it'll be gone under #7064
_bce_str = "%s до н.э."
formats = (
"ГГГГ-ММ-ДД (ISO)", "Численный", "Месяц День, Год",
"МЕС ДД, ГГГГ", "День Месяц, Год", "ДД МЕС, ГГГГ"
)
# this must agree with DateDisplayEn's "formats" definition
# (since no locale-specific _display_gregorian exists, here)
hebrew = ( "",
"тишрея",
"хешвана",
"кислева",
"тевета",
"шевата",
"адара",
"адара бет",
"нисана",
"ияра",
"сивана",
"таммуза",
"ава",
"элула",
)
islamic = ( "",
"мухаррама",
"сафара",
"раби-аль-авваля",
"раби-ассани",
"джумада-аль-уля",
"джумада-аль-ахира",
"раджаба",
"шаабана",
"рамадана",
"шавваля",
"зуль-каада",
"зуль-хиджжа",
)
persian = ( "",
"фарвардина",
"урдбихишта",
"хурдада",
"тира",
"мурдада",
"шахривара",
"михра",
"абана",
"азара",
"дая",
"бахмана",
"исфаидармуза",
)
french = ( "",
"вандемьера",
"брюмера",
"фримера",
"нивоза",
"плювиоза",
"вантоза",
"жерминаля",
"флореаля",
"прериаля",
"мессидора",
"термидора",
"фрюктидора",
"дополнит."
)
def display(self, date):
"""
Return a text string representing the date.
"""
mod = date.get_modifier()
cal = date.get_calendar()
qual = date.get_quality()
start = date.get_start_date()
newyear = date.get_new_year()
qual_str = self._qual_str[qual]
if mod == Date.MOD_TEXTONLY:
return date.get_text()
elif start == Date.EMPTY:
return ""
elif mod == Date.MOD_SPAN:
d1 = self.display_cal[cal](start)
d2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%sс %s %s %s%s" % (qual_str, d1, 'по', d2,
scal)
elif mod == Date.MOD_RANGE:
d1 = self.display_cal[cal](start)
d2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%s%s %s %s %s%s" % (qual_str, 'между', d1, 'и',
d2, scal)
else:
text = self.display_cal[date.get_calendar()](start)
scal = self.format_extras(cal, newyear)
return "%s%s%s%s" % (qual_str, self._mod_str[mod],
text, scal)
display = DateDisplay.display_formatted
#-------------------------------------------------------------------------
#

View File

@ -41,6 +41,8 @@ log = logging.getLogger(".DateDisplay")
#-------------------------------------------------------------------------
from ..lib.date import Date
from . import _grampslocale
from ..utils.grampslocale import GrampsLocale
from ._datestrings import DateStrings
#-------------------------------------------------------------------------
#
@ -51,78 +53,63 @@ class DateDisplay(object):
"""
Base date display class.
"""
long_months = ( "", "January", "February", "March", "April", "May",
"June", "July", "August", "September", "October",
"November", "December" )
short_months = ( "", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" )
_locale = GrampsLocale(lang='en_US', languages='en')
_tformat = _grampslocale.tformat
hebrew = (
"", "Tishri", "Heshvan", "Kislev", "Tevet", "Shevat",
"AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz",
"Av", "Elul"
)
_ = _grampslocale.glocale.translation.sgettext
formats = (
# format 0 - must always be ISO
_("YYYY-MM-DD (ISO)"),
french = (
'',
"Vendémiaire",
'Brumaire',
'Frimaire',
"Nivôse",
"Pluviôse",
"Ventôse",
'Germinal',
"Floréal",
'Prairial',
'Messidor',
'Thermidor',
'Fructidor',
'Extra',
)
# format # 1 - must always be locale-preferred numerical format
# such as YY.MM.DD, MM-DD-YY, or whatever your locale prefers.
# This should be the format that is used under the locale by
# strftime() for '%x'.
# You may translate this string as "Numerical", "System preferred", or similar.
_("date format|Numerical"),
persian = (
"", "Farvardin", "Ordibehesht", "Khordad", "Tir",
"Mordad", "Shahrivar", "Mehr", "Aban", "Azar",
"Dey", "Bahman", "Esfand"
)
# Full month name, day, year
_("Month Day, Year"),
islamic = (
"", "Muharram", "Safar", "Rabi`al-Awwal", "Rabi`ath-Thani",
"Jumada l-Ula", "Jumada t-Tania", "Rajab", "Sha`ban",
"Ramadan", "Shawwal", "Dhu l-Qa`da", "Dhu l-Hijja"
)
# Abbreviated month name, day, year
_("MON DAY, YEAR"),
swedish = (
"", "Januari", "Februari", "Mars",
"April", "Maj", "Juni",
"Juli", "Augusti", "September",
"Oktober", "November", "December"
)
# Day, full month name, year
_("Day Month Year"),
formats = ("YYYY-MM-DD (ISO)", )
# this will be overridden if a locale-specific date displayer exists
calendar = (
"", "Julian", "Hebrew", "French Republican",
"Persian", "Islamic", "Swedish"
# Day, abbreviated month name, year
_("DAY MON YEAR")
)
# this will be overridden if a locale-specific date displayer exists
"""
:Note:
Will be overridden if a locale-specific date displayer exists.
If your localized ``_display_gregorian`` / ``_display_calendar`` are overridden,
you should override the whole formats list according to your own formats,
and you need not localize the format names here.
this ``formats`` must agree with
:meth:`~_display_calendar`/:meth:`~_display_gregorian`
"""
del _
newyear = ("", "Mar1", "Mar25", "Sep1")
_mod_str = ("", "before ", "after ", "about ", "", "", "")
# this will be overridden if a locale-specific date displayer exists
_qual_str = ("", "estimated ", "calculated ")
# this will be overridden if a locale-specific date displayer exists
_bce_str = "%s B.C.E."
# this will be overridden if a locale-specific date displayer exists
def __init__(self, format=None):
self._ds = DateStrings(self._locale)
calendar = list(self._ds.calendar)
calendar[Date.CAL_GREGORIAN] = "" # that string only used in parsing,
# gregorian cal name shouldn't be output!
self.calendar = tuple(calendar)
self.short_months = self._ds.short_months
self.swedish = self.long_months = self._ds.long_months
self.hebrew = self._ds.hebrew
self.french = self._ds.french
self.persian = self._ds.persian
self.islamic = self._ds.islamic
self.display_cal = [
self._display_gregorian,
self._display_julian,
@ -131,12 +118,119 @@ class DateDisplay(object):
self._display_persian,
self._display_islamic,
self._display_swedish]
self._mod_str = self._ds.modifiers
self._qual_str = self._ds.qualifiers
if format is None:
self.format = 0
else:
self.format = format
self._ = _ = self._locale.translation.sgettext
self.FORMATS_long_month_year = {
# Inflection control due to modifier.
# Protocol: DateDisplayXX passes a key to the dictionary in the
# parameter ``inflect`` to ``_display_calendar``.
# The modifier passed is not necessarily the one printed, it's just
# a representative that induces the same inflection control.
# For example, in Russian "before May", "after May", and "about May"
# all require genitive form for May, whereas no modifier (just "May 1234")
# require nominative, so DateDisplayRU.display will pass "before"
# in all 3 cases, collapsing the 3 modifiers into 1.
#
# Another example in Russian is that "between April 1234 and June 1235"
# requires the same inflection for both April and June, so just "between"
# is used by DateDisplayRU.display, collapsing two more modifiers into 1.
#
# If inflect is not specified, then it means that the modifier doesn't have
# grammatical control over the format, and so the format can be
# localized in a context-free way.
# The translator is responsible for:
# 1) proper collapse of modifier classes
# 2) translating the formats that are selected in runtime
# 3) ignoring the other formats in .po (it does no harm to translate them,
# it's just a lot of extra work)
#
# To prevent POT pollution, not all possibilities are populated here yet.
# To be amended as the actual localized handlers use it.
#
# Not moving to DateStrings, as this is part of display code only,
# coupled tightly with the formats used in this file.
""
: _("{long_month} {year}"),
"from"
# first date in a span
# You only need to translate this string if you translate one of the
# inflect=_("...") with "from"
: _("from|{long_month} {year}"),
"to"
# second date in a span
# You only need to translate this string if you translate one of the
# inflect=_("...") with "to"
: _("to|{long_month} {year}"),
"between"
# first date in a range
# You only need to translate this string if you translate one of the
# inflect=_("...") with "between"
: _("between|{long_month} {year}"),
"and"
# second date in a range
# You only need to translate this string if you translate one of the
# inflect=_("...") with "and"
: _("and|{long_month} {year}"),
"before"
# You only need to translate this string if you translate one of the
# inflect=_("...") with "before"
: _("before|{long_month} {year}"),
"after"
# You only need to translate this string if you translate one of the
# inflect=_("...") with "after"
: _("after|{long_month} {year}"),
"about"
# You only need to translate this string if you translate one of the
# inflect=_("...") with "about"
: _("about|{long_month} {year}"),
# TODO if no modifier, but with qual, might need to inflect in some lang.
}
self.FORMATS_short_month_year = {
""
: _("{short_month} {year}"),
"from"
# first date in a span
: _("from|{short_month} {year}"),
"to"
# second date in a span
: _("to|{short_month} {year}"),
"between"
# first date in a range
: _("between|{short_month} {year}"),
"and"
# second date in a range
: _("and|{short_month} {year}"),
"before"
: _("before|{short_month} {year}"),
"after"
: _("after|{short_month} {year}"),
"about"
: _("about|{short_month} {year}"),
}
def set_format(self, format):
self.format = format
@ -164,7 +258,9 @@ class DateDisplay(object):
def display(self, date):
"""
Return a text string representing the date.
(will be overridden if a locale-specific date displayer exists)
(Will be overridden if a locale-specific date displayer exists.)
Disregard any format settings and use display_iso for each date.
"""
mod = date.get_modifier()
cal = date.get_calendar()
@ -218,10 +314,115 @@ class DateDisplay(object):
else:
return value
def _display_gregorian(self, date_val):
def display_formatted(self, date):
"""
Return a text string representing the date, according to the format.
"""
mod = date.get_modifier()
cal = date.get_calendar()
qual = date.get_quality()
start = date.get_start_date()
newyear = date.get_new_year()
qual_str = self._qual_str[qual]
_ = self._
if mod == Date.MOD_TEXTONLY:
return date.get_text()
elif start == Date.EMPTY:
return ""
elif mod == Date.MOD_SPAN:
d1 = self.display_cal[cal](start,
# If there is no special inflection for "from <Month>" in your
# language, don't translate this string.
# Otherwise, translate it to the ENGLISH!!! ENGLISH!!!
# key appearing above in the FORMATS_... dict
# that maps to the special inflected format string that you need to localize.
inflect=_("from-date|"))
d2 = self.display_cal[cal](date.get_stop_date(),
# If there is no special inflection for "to <Month>" in your
# language, don't translate this string.
# Otherwise, translate it to the ENGLISH!!! ENGLISH!!!
# key appearing above in the FORMATS_... dict
# that maps to the special inflected format string that you need to localize.
inflect=_("to-date|"))
scal = self.format_extras(cal, newyear)
return _("{date_quality}from {date_start} to {date_stop}"
"{nonstd_calendar_and_ny}").format(
date_quality=qual_str,
date_start=d1,
date_stop=d2,
nonstd_calendar_and_ny=scal)
elif mod == Date.MOD_RANGE:
d1 = self.display_cal[cal](start,
# If there is no special inflection for "between <Month>" in your
# language, don't translate this string.
# Otherwise, translate it to the ENGLISH!!! ENGLISH!!!
# key appearing above in the FORMATS_... dict
# that maps to the special inflected format string that you need to localize.
inflect=_("between-date|"))
d2 = self.display_cal[cal](date.get_stop_date(),
# If there is no special inflection for "and <Month>" in your
# language, don't translate this string.
# Otherwise, translate it to the ENGLISH!!! ENGLISH!!!
# key appearing above in the FORMATS_... dict
# that maps to the special inflected format string that you need to localize.
inflect=_("and-date|"))
scal = self.format_extras(cal, newyear)
return _("{date_quality}between {date_start} and {date_stop}"
"{nonstd_calendar_and_ny}").format(
date_quality=qual_str,
date_start=d1,
date_stop=d2,
nonstd_calendar_and_ny=scal)
else:
text = self.display_cal[date.get_calendar()](start,
# If there is no special inflection for "before/after/around <Month>" in your
# language, don't translate this string.
# Otherwise, translate it to the ENGLISH!!! ENGLISH!!!
# key appearing above in the FORMATS_... dict
# that maps to the special inflected format string that you need to localize.
# TODO are there languages for which the inflections for the different
# modifiers are different?!
inflect=_("before-date|") if mod != Date.MOD_NONE else "")
scal = self.format_extras(cal, newyear)
return _("{date_quality}{noncompound_modifier}{date}"
"{nonstd_calendar_and_ny}").format(
date_quality=qual_str,
noncompound_modifier=self._mod_str[mod],
date=text,
nonstd_calendar_and_ny=scal)
def _display_gregorian(self, date_val, **kwargs):
return self._display_calendar(date_val, self.long_months,
self.short_months, **kwargs)
# Julian and Swedish date display is the same as Gregorian
_display_julian = _display_swedish = _display_gregorian
def _display_calendar(self, date_val, long_months, short_months = None,
inflect=""):
if short_months is None:
# Let the short formats work the same as long formats
short_months = long_months
_ = self._locale.translation.sgettext
# this one must agree with DateDisplayEn's "formats" definition
# (it may be overridden if a locale-specific date displayer exists)
year = self._slash_year(date_val[2], date_val[3])
# For partial dates, several formats reduce to just month + year.
def format_long_month_year():
return self.FORMATS_long_month_year[inflect].format(
long_month = long_months[date_val[1]],
year = year)
def format_short_month_year():
return self.FORMATS_short_month_year[inflect].format(
short_month = short_months[date_val[1]],
year = year)
if self.format == 0:
return self.display_iso(date_val)
elif self.format == 1:
@ -242,30 +443,45 @@ class DateDisplay(object):
if date_val[1] == 0:
value = year
else:
value = "%s %s" % (self.long_months[date_val[1]], year)
value = format_long_month_year()
else:
value = "%s %d, %s" % (self.long_months[date_val[1]],
date_val[0], year)
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection for your language.
value = _("{long_month} {day:d}, {year}").format(
long_month = long_months[date_val[1]],
day = date_val[0],
year = year)
elif self.format == 3:
# month_abbreviation day, year
if date_val[0] == 0:
if date_val[1] == 0:
value = year
else:
value = "%s %s" % (self.short_months[date_val[1]], year)
value = format_short_month_year()
else:
value = "%s %d, %s" % (self.short_months[date_val[1]],
date_val[0], year)
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection for your language.
value = _("{short_month} {day:d}, {year}").format(
short_month = short_months[date_val[1]],
day = date_val[0],
year = year)
elif self.format == 4:
# day month_name year
if date_val[0] == 0:
if date_val[1] == 0:
value = year
else:
value = "%s %s" % (self.long_months[date_val[1]], year)
value = format_long_month_year()
else:
value = "%d %s %s" % (date_val[0],
self.long_months[date_val[1]], year)
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection for your language.
value = _("{day:d} {long_month} {year}").format(
day = date_val[0],
long_month = long_months[date_val[1]],
year = year)
# elif self.format == 5:
else:
# day month_abbreviation year
@ -273,78 +489,39 @@ class DateDisplay(object):
if date_val[1] == 0:
value = year
else:
value = "%s %s" % (self.short_months[date_val[1]], year)
value = format_short_month_year()
else:
value = "%d %s %s" % (date_val[0],
self.short_months[date_val[1]], year)
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection for your language.
value = _("{day:d} {short_month} {year}").format(
short_month = short_months[date_val[1]],
day = date_val[0],
year = year)
if date_val[2] < 0:
# TODO fix BUG 7064: non-Gregorian calendars wrongly use BCE notation for negative dates
return self._bce_str % value
else:
return value
def _display_julian(self, date_val):
# Julian date display is the same as Gregorian
return self._display_gregorian(date_val)
def _display_calendar(self, date_val, month_list):
# used to display non-Gregorian calendars (Hebrew, Islamic, etc.)
year = abs(date_val[2])
if self.format == 0 or self.format == 1:
return self.display_iso(date_val)
else:
if date_val[0] == 0:
if date_val[1] == 0:
value = year
else:
value = "%s %d" % (month_list[date_val[1]], year)
else:
value = "%s %d, %s" % (month_list[date_val[1]], date_val[0],
year)
if date_val[2] < 0:
return self._bce_str % value
else:
return value
def _display_french(self, date_val, **kwargs):
return self._display_calendar(date_val, self.french, **kwargs)
def _display_french(self, date_val):
year = abs(date_val[2])
if self.format == 0 or self.format == 1:
return self.display_iso(date_val)
else:
if date_val[0] == 0:
if date_val[1] == 0:
value = year
else:
value = "%s %d" % (self.french[date_val[1]], year)
else:
value = "%d %s %s" % (date_val[0], self.french[date_val[1]],
year)
if date_val[2] < 0:
return self._bce_str % value
else:
return value
def _display_hebrew(self, date_val, **kwargs):
return self._display_calendar(date_val, self.hebrew, **kwargs)
def _display_hebrew(self, date_val):
return self._display_calendar(date_val, self.hebrew)
def _display_persian(self, date_val, **kwargs):
return self._display_calendar(date_val, self.persian, **kwargs)
def _display_persian(self, date_val):
return self._display_calendar(date_val, self.persian)
def _display_islamic(self, date_val):
return self._display_calendar(date_val, self.islamic)
def _display_swedish(self, date_val):
return self._display_calendar(date_val, self.swedish)
def _display_islamic(self, date_val, **kwargs):
return self._display_calendar(date_val, self.islamic, **kwargs)
class DateDisplayEn(DateDisplay):
"""
English language date display class.
"""
formats = (
"YYYY-MM-DD (ISO)", "Numerical", "Month Day, Year",
"MON DAY, YEAR", "Day Month Year", "DAY MON YEAR"
)
# this (English) "formats" must agree with "_display_gregorian" (above)
def __init__(self, format=None):
"""
@ -355,33 +532,5 @@ class DateDisplayEn(DateDisplay):
DateDisplay.__init__(self, format)
def display(self, date):
"""
Return a text string representing the date.
"""
mod = date.get_modifier()
cal = date.get_calendar()
qual = date.get_quality()
start = date.get_start_date()
newyear = date.get_new_year()
display = DateDisplay.display_formatted
qual_str = self._qual_str[qual]
if mod == Date.MOD_TEXTONLY:
return date.get_text()
elif start == Date.EMPTY:
return ""
elif mod == Date.MOD_SPAN:
d1 = self.display_cal[cal](start)
d2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%sfrom %s to %s%s" % (qual_str, d1, d2, scal)
elif mod == Date.MOD_RANGE:
d1 = self.display_cal[cal](start)
d2 = self.display_cal[cal](date.get_stop_date())
scal = self.format_extras(cal, newyear)
return "%sbetween %s and %s%s" % (qual_str, d1, d2, scal)
else:
text = self.display_cal[date.get_calendar()](start)
scal = self.format_extras(cal, newyear)
return "%s%s%s%s" % (qual_str, self._mod_str[mod], text, scal)

View File

@ -49,6 +49,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay, DateDisplayEn
from ..constfunc import win, cuni
from ..const import GRAMPS_LOCALE as glocale
from gramps.gen.utils.grampslocale import GrampsLocale
#-------------------------------------------------------------------------
#
@ -99,6 +100,9 @@ def register_datehandler(locales,parse_class,display_class):
Registers the passed date parser class and date displayer
classes with the specified language locales.
Set the parser_class and display_class ._locale attribute
to the corresponding GrampsLocale object.
@param locales: tuple of strings containing language codes.
The character encoding is not included, so the language
should be in the form of fr_FR, not fr_FR.utf8
@ -111,3 +115,5 @@ def register_datehandler(locales,parse_class,display_class):
for lang_str in locales:
LANG_TO_PARSER[lang_str] = parse_class
LANG_TO_DISPLAY[lang_str] = display_class
parse_class._locale = display_class._locale = GrampsLocale(lang=locales[0])

View File

@ -51,6 +51,8 @@ log = logging.getLogger(".DateParser")
#-------------------------------------------------------------------------
from ..lib.date import Date, DateError
from . import _grampslocale
from ..utils.grampslocale import GrampsLocale
from ._datestrings import DateStrings
#-------------------------------------------------------------------------
#
@ -120,6 +122,70 @@ def french_valid(date_tuple):
valid = False
return valid
def _build_prefix_table(month_to_int, month_variants):
"""
Populate a DateParser.month_to_int-like dict
with all the prefixes found in month_variants.
"""
month_variants = list(month_variants) # drain the generator, if any
month_to_int_new = {}
# Populate with full names first, w/o prefixes
log.debug("Mapping full names...")
for i in range(0,len(month_variants)):
for month in month_variants[i]:
m = month.lower()
log.debug("Mapping {} -> {}".format(m, i))
month_to_int_new[m] = i
month_to_int.update(month_to_int_new)
log.debug("Mapping new prefixes...")
months_sorted = list(month_to_int_new.keys())
months_sorted.sort(key=len, reverse=True)
for m in months_sorted:
for prefixlen in reversed(range(1,len(m))):
mp = m[:prefixlen]
if mp.strip() != mp:
continue
if mp in month_to_int:
break
else:
i = month_to_int[m]
log.debug("Mapping {} -> {}".format(mp, i))
month_to_int[mp] = i
def _generate_variants(months):
"""
Generate all month variants for passing to _build_prefix_table
@param months an iterable ordered collection,
1st item is empty, the rest 1..N, for a
calendar with N months overall, contain, each,
an iterable of alternative specifications.
Each such specification can be:
1) a Lexeme, supporting .variants()
to return the list of variants underneath
2) a literal string
3) a |-separated string of alternatives
Empty strings are discarded.
@return generator of lists per month with all variants listed once only
the 1st item will be empty
"""
for month_lexemes_and_alternatives in months:
v = []
for m in month_lexemes_and_alternatives:
try:
# Lexeme? ask it to compute the variants it knows
mv = list(m.variants())
except AttributeError:
# plain string, not a lexeme with inflections...
# Maybe a '|'-separated list of alternatives, maybe empty,
# maybe a single string. Suppress empty strings!
mv = (s for s in m.split('|') if s)
v.extend(mv)
yield(list(set(v)))
#-------------------------------------------------------------------------
#
# DateParser class
@ -131,6 +197,8 @@ class DateParser(object):
converted, the text string is assigned.
"""
_locale = GrampsLocale(lang='en', languages='en')
_fmt_parse = re.compile(".*%(\S).*%(\S).*%(\S).*")
# RFC-2822 only uses capitalized English abbreviated names, no locales.
@ -141,7 +209,12 @@ class DateParser(object):
'Sep' : 9, 'Oct' : 10, 'Nov' : 11, 'Dec' : 12,
}
month_to_int = _grampslocale.month_to_int
# seeded with __init_prefix_tables
swedish_to_int = month_to_int = {}
"""
Map Gregorian month names and their prefixes, wherever unambiguous,
to the relevant integer index (1..12).
"""
# modifiers before the date
# (overridden if a locale-specific date parser exists)
@ -172,13 +245,7 @@ class DateParser(object):
}
french_to_int = {
'vendémiaire' : 1, 'brumaire' : 2,
'frimaire' : 3, 'nivôse': 4,
'pluviôse' : 5, 'ventôse' : 6,
'germinal' : 7, 'floréal' : 8,
'prairial' : 9, 'messidor' : 10,
'thermidor' : 11, 'fructidor' : 12,
'extra' : 13,
# the long ones are seeded with __init_prefix_tables
#GEDCOM months
'vend' : 1, 'brum' : 2,
'frim' : 3, 'nivo' : 4,
@ -190,6 +257,8 @@ class DateParser(object):
}
islamic_to_int = {
# some are already seeded with __init_prefix_tables,
# but it is a pain to separate them out from the variants...
"muharram" : 1, "muharram ul haram" : 1,
"safar" : 2, "rabi`al-awwal" : 3,
"rabi'l" : 3, "rabi`ul-akhir" : 4,
@ -207,44 +276,14 @@ class DateParser(object):
"dhu hijja" : 12, "thw al-hijjah" : 12,
}
persian_to_int = {
"farvardin" : 1, "ordibehesht" : 2,
"khordad" : 3, "tir" : 4,
"mordad" : 5, "shahrivar" : 6,
"mehr" : 7, "aban" : 8,
"azar" : 9, "dey" : 10,
"bahman" : 11, "esfand" : 12,
}
swedish_to_int = {
"januari" : 1, "februari" : 2,
"mars" : 3, "april" : 4,
"maj" : 5, "juni" : 6,
"juli" : 7, "augusti" : 8,
"september" : 9, "oktober" : 10,
"november" : 11, "december" : 12,
}
# seeded with __init_prefix_tables
persian_to_int = { }
bce = ["B.C.E.", "B.C.E", "BCE", "B.C.", "B.C", "BC" ]
# (overridden if a locale-specific date parser exists)
# seeded with __init_prefix_tables
calendar_to_int = {
'gregorian' : Date.CAL_GREGORIAN,
'g' : Date.CAL_GREGORIAN,
'julian' : Date.CAL_JULIAN,
'j' : Date.CAL_JULIAN,
'hebrew' : Date.CAL_HEBREW,
'h' : Date.CAL_HEBREW,
'islamic' : Date.CAL_ISLAMIC,
'i' : Date.CAL_ISLAMIC,
'french' : Date.CAL_FRENCH,
'french republican': Date.CAL_FRENCH,
'f' : Date.CAL_FRENCH,
'persian' : Date.CAL_PERSIAN,
'p' : Date.CAL_PERSIAN,
'swedish' : Date.CAL_SWEDISH,
's' : Date.CAL_SWEDISH,
}
# (probably overridden if a locale-specific date parser exists)
@ -265,6 +304,31 @@ class DateParser(object):
}
# (overridden if a locale-specific date parser exists)
_langs = set()
def __init_prefix_tables(self):
lang = self._locale.lang
if lang in DateParser._langs:
log.debug("Prefix tables for {} already built".format(lang))
return
else:
DateParser._langs.add(lang)
ds = DateStrings(self._locale)
log.debug("Begin building parser prefix tables for {}".format(lang))
_build_prefix_table(DateParser.month_to_int,
_generate_variants(
zip(ds.long_months, ds.short_months,
ds.swedish_SV, ds.alt_long_months)))
_build_prefix_table(DateParser.hebrew_to_int,
_generate_variants(zip(ds.hebrew)))
_build_prefix_table(DateParser.french_to_int,
_generate_variants(zip(ds.french)))
_build_prefix_table(DateParser.islamic_to_int,
_generate_variants(zip(ds.islamic)))
_build_prefix_table(DateParser.persian_to_int,
_generate_variants(zip(ds.persian)))
_build_prefix_table(DateParser.calendar_to_int,
_generate_variants(zip(ds.calendar)))
def __init__(self):
self.init_strings()
self.parser = {
@ -294,8 +358,8 @@ class DateParser(object):
sorted so that longest keys match first. Any '.' characters
are quoted.
"""
keys.sort(key=lambda x: len(x), reverse=True)
return '(' + '|'.join([key.replace('.', '\.') for key in keys]) + ')'
keys.sort(key=len, reverse=True)
return '(' + '|'.join([re.escape(key) for key in keys]) + ')'
def init_strings(self):
"""
@ -308,6 +372,8 @@ class DateParser(object):
can be coded after DateParser.init_strings(self) call, that way they
override stuff from this method. See DateParserRU() as an example.
"""
self.__init_prefix_tables()
self._rfc_mon_str = '(' + '|'.join(list(self._rfc_mons_to_int.keys())) + ')'
self._rfc_day_str = '(' + '|'.join(self._rfc_days) + ')'

View File

@ -0,0 +1,346 @@
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Vassilii Khachaturov
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
"""
Date strings to translate per each language for display and parsing.
"""
from __future__ import print_function, unicode_literals
#-------------------------------------------------------------------------
#
# set up logging
#
#-------------------------------------------------------------------------
import logging
log = logging.getLogger(".DateStrings")
#-------------------------------------------------------------------------
#
# DateStrings
#
#-------------------------------------------------------------------------
class DateStrings(object):
"""
String tables for :class:`~DateDisplay` and :class:`~DateParser`.
"""
# This table needs not be localized, it's only for parsing
# Swedish calendar dates using Swedish month names.
# Display of these months uses the regular long_months.
# TODO should we pack these into alt_long_months instead?
swedish_SV = (
"", "Januari", "Februari", "Mars",
"April", "Maj", "Juni",
"Juli", "Augusti", "September",
"Oktober", "November", "December"
)
def __init__(self, locale):
_ = locale.translation.lexgettext
self.long_months = ( "",
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection to be used in your localized
# DateDisplayer code!
_("localized lexeme inflections||January"),
_("localized lexeme inflections||February"),
_("localized lexeme inflections||March"),
_("localized lexeme inflections||April"),
_("localized lexeme inflections||May"),
_("localized lexeme inflections||June"),
_("localized lexeme inflections||July"),
_("localized lexeme inflections||August"),
_("localized lexeme inflections||September"),
_("localized lexeme inflections||October"),
_("localized lexeme inflections||November"),
_("localized lexeme inflections||December") )
self.short_months = ( "",
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection to be used in your localized
# DateDisplayer code!
_("localized lexeme inflections - short month form||Jan"),
_("localized lexeme inflections - short month form||Feb"),
_("localized lexeme inflections - short month form||Mar"),
_("localized lexeme inflections - short month form||Apr"),
_("localized lexeme inflections - short month form||May"),
_("localized lexeme inflections - short month form||Jun"),
_("localized lexeme inflections - short month form||Jul"),
_("localized lexeme inflections - short month form||Aug"),
_("localized lexeme inflections - short month form||Sep"),
_("localized lexeme inflections - short month form||Oct"),
_("localized lexeme inflections - short month form||Nov"),
_("localized lexeme inflections - short month form||Dec") )
_ = locale.translation.sgettext
self.alt_long_months = ( "",
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to add proper alternatives to be recognized in your localized
# DateParser code!
_("alternative month names for January||"),
_("alternative month names for February||"),
_("alternative month names for March||"),
_("alternative month names for April||"),
_("alternative month names for May||"),
_("alternative month names for June||"),
_("alternative month names for July||"),
_("alternative month names for August||"),
_("alternative month names for September||"),
_("alternative month names for October||"),
_("alternative month names for November||"),
_("alternative month names for December||") )
self.calendar = (
# Must appear in the order indexed by Date.CAL_... numeric constants
_("calendar|Gregorian"),
_("calendar|Julian"),
_("calendar|Hebrew"),
_("calendar|French Republican"),
_("calendar|Persian"),
_("calendar|Islamic"),
_("calendar|Swedish") )
_ = locale.translation.lexgettext
self.hebrew = (
"",
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection to be used in your localized
# DateDisplayer code!
_("Hebrew month lexeme|Tishri"),
_("Hebrew month lexeme|Heshvan"),
_("Hebrew month lexeme|Kislev"),
_("Hebrew month lexeme|Tevet"),
_("Hebrew month lexeme|Shevat"),
_("Hebrew month lexeme|AdarI"),
_("Hebrew month lexeme|AdarII"),
_("Hebrew month lexeme|Nisan"),
_("Hebrew month lexeme|Iyyar"),
_("Hebrew month lexeme|Sivan"),
_("Hebrew month lexeme|Tammuz"),
_("Hebrew month lexeme|Av"),
_("Hebrew month lexeme|Elul")
)
self.french = (
"",
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection to be used in your localized
# DateDisplayer code!
_("French month lexeme|Vendémiaire"),
_("French month lexeme|Brumaire"),
_("French month lexeme|Frimaire"),
_("French month lexeme|Nivôse"),
_("French month lexeme|Pluviôse"),
_("French month lexeme|Ventôse"),
_("French month lexeme|Germinal"),
_("French month lexeme|Floréal"),
_("French month lexeme|Prairial"),
_("French month lexeme|Messidor"),
_("French month lexeme|Thermidor"),
_("French month lexeme|Fructidor"),
_("French month lexeme|Extra"),
)
self.islamic = (
"",
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection to be used in your localized
# DateDisplayer code!
_("Islamic month lexeme|Muharram"),
_("Islamic month lexeme|Safar"),
_("Islamic month lexeme|Rabi`al-Awwal"),
_("Islamic month lexeme|Rabi`ath-Thani"),
_("Islamic month lexeme|Jumada l-Ula"),
_("Islamic month lexeme|Jumada t-Tania"),
_("Islamic month lexeme|Rajab"),
_("Islamic month lexeme|Sha`ban"),
_("Islamic month lexeme|Ramadan"),
_("Islamic month lexeme|Shawwal"),
_("Islamic month lexeme|Dhu l-Qa`da"),
_("Islamic month lexeme|Dhu l-Hijja"),
)
self.persian = (
"",
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection to be used in your localized
# DateDisplayer code!
_("Persian month lexeme|Farvardin"),
_("Persian month lexeme|Ordibehesht"),
_("Persian month lexeme|Khordad"),
_("Persian month lexeme|Tir"),
_("Persian month lexeme|Mordad"),
_("Persian month lexeme|Shahrivar"),
_("Persian month lexeme|Mehr"),
_("Persian month lexeme|Aban"),
_("Persian month lexeme|Azar"),
_("Persian month lexeme|Dey"),
_("Persian month lexeme|Bahman"),
_("Persian month lexeme|Esfand"),
)
self.modifiers = ("",
_("date modifier|before "),
_("date modifier|after "),
_("date modifier|about "),
"", "", "")
self.qualifiers = ("",
_("date quality|estimated "),
_("date quality|calculated "),
)
# TODO extend with further strings
# along with the date displayer and parser refactoring
__doc__ += """
__main__
--------
Run this code with the appropriate ``LANG`` and ``LC_DATE`` set for your target language,
in order to generate the .po snippets initialized with the strings from your locale
(from the deprecated data provided in _grampslocale).
E.g., for French:
::
LANG=fr_FR.utf8 LC_ALL=fr_FR.utf8 GRAMPS_RESOURCES=$PWD python -m gramps.gen.datehandler._datestrings
Then merge the output into your language's .po file, and further modify the strings as needed.
Then remove the strings from your language's ``DateParserXX`` and ``DateHandlerXX`` classes.
"""
if __name__ == '__main__':
import sys
from ..utils.grampslocale import GrampsLocale
from gramps.gen.const import GRAMPS_LOCALE as glocale
from ._grampslocale import (_deprecated_long_months as old_long,
_deprecated_short_months as old_short)
from ._datedisplay import DateDisplay
import gettext
lang = glocale.lang
lang_short = lang[:2]
available_langs = glocale.get_available_translations()
if glocale.check_available_translations(lang) is None:
print ("Translation for current language {lang} not available.\n"
"Available translations: {list}.\n"
"Does po/{lang_short}*.po exist in gramps source tree?!\n"
"Please set your LANG / LC_ALL environment to something else...\n".format(
lang=lang, list=available_langs, lang_short=lang_short),
file=sys.stderr)
sys.exit(1)
print ("# Generating snippets for {}*.po\n"
"# Available languages: {}".format(
lang_short, available_langs))
glocale = GrampsLocale(languages=(lang))
dd = glocale.date_displayer
ds = dd._ds
glocale_EN = GrampsLocale(languages=('en'))
ds_EN = DateStrings(glocale_EN)
filename = __file__
try:
localized_months = dd.__class__.long_months
except AttributeError:
localized_months = old_long
def print_po_snippet(en_loc_old_lists, context):
for m,localized,old in zip(*en_loc_old_lists):
if m == "":
continue
if m == localized:
localized = old
print ('#: {file}:{line}\n'
'msgid "{context}{en_month}"\n'
'msgstr "{localized_month}"\n'.format(
context = context,
file = filename,
line = print_po_snippet.line,
en_month = m,
localized_month = localized))
print_po_snippet.line += 1
print_po_snippet.line = 10000
try:
localized_months = dd.__class__.long_months
except AttributeError:
localized_months = old_long
print_po_snippet((ds_EN.long_months, localized_months, old_long),
"localized lexeme inflections||")
try:
localized_months = dd.__class__.short_months
except AttributeError:
localized_months = old_short
print_po_snippet((ds_EN.short_months, localized_months, old_short),
"localized lexeme inflections - short month form||")
try:
loc = dd.__class__.hebrew
print_po_snippet((ds_EN.hebrew, loc, loc),
"Hebrew month lexeme|")
except AttributeError:
pass
try:
loc = dd.__class__.french
print_po_snippet((ds_EN.french, loc, loc),
"French month lexeme|")
except AttributeError:
pass
try:
loc = dd.__class__.islamic
print_po_snippet((ds_EN.islamic, loc, loc),
"Islamic month lexeme|")
except AttributeError:
pass
try:
loc = dd.__class__.persian
print_po_snippet((ds_EN.persian, loc, loc),
"Persian month lexeme|")
except AttributeError:
pass
try:
loc = dd.__class__._mod_str
print_po_snippet((ds_EN.modifiers, loc, loc),
"date modifier|")
except AttributeError:
pass
try:
loc = dd.__class__._qual_str
print_po_snippet((ds_EN.qualifiers, loc, loc),
"date quality|")
except AttributeError:
pass

View File

@ -46,34 +46,8 @@ codeset = glocale.encoding
try:
month_to_int = {
to_uni(locale.nl_langinfo(locale.MON_1), codeset).lower() : 1,
to_uni(locale.nl_langinfo(locale.ABMON_1), codeset).lower() : 1,
to_uni(locale.nl_langinfo(locale.MON_2), codeset).lower() : 2,
to_uni(locale.nl_langinfo(locale.ABMON_2), codeset).lower() : 2,
to_uni(locale.nl_langinfo(locale.MON_3), codeset).lower() : 3,
to_uni(locale.nl_langinfo(locale.ABMON_3), codeset).lower() : 3,
to_uni(locale.nl_langinfo(locale.MON_4), codeset).lower() : 4,
to_uni(locale.nl_langinfo(locale.ABMON_4), codeset).lower() : 4,
to_uni(locale.nl_langinfo(locale.MON_5), codeset).lower() : 5,
to_uni(locale.nl_langinfo(locale.ABMON_5), codeset).lower() : 5,
to_uni(locale.nl_langinfo(locale.MON_6), codeset).lower() : 6,
to_uni(locale.nl_langinfo(locale.ABMON_6), codeset).lower() : 6,
to_uni(locale.nl_langinfo(locale.MON_7), codeset).lower() : 7,
to_uni(locale.nl_langinfo(locale.ABMON_7), codeset).lower() : 7,
to_uni(locale.nl_langinfo(locale.MON_8), codeset).lower() : 8,
to_uni(locale.nl_langinfo(locale.ABMON_8), codeset).lower() : 8,
to_uni(locale.nl_langinfo(locale.MON_9), codeset).lower() : 9,
to_uni(locale.nl_langinfo(locale.ABMON_9), codeset).lower() : 9,
to_uni(locale.nl_langinfo(locale.MON_10), codeset).lower() : 10,
to_uni(locale.nl_langinfo(locale.ABMON_10), codeset).lower(): 10,
to_uni(locale.nl_langinfo(locale.MON_11), codeset).lower() : 11,
to_uni(locale.nl_langinfo(locale.ABMON_11), codeset).lower(): 11,
to_uni(locale.nl_langinfo(locale.MON_12), codeset).lower() : 12,
to_uni(locale.nl_langinfo(locale.ABMON_12), codeset).lower(): 12,
}
long_months = (
# here only for the upgrade tool, see _datestrings.py __main__
_deprecated_long_months = (
"",
to_uni(locale.nl_langinfo(locale.MON_1), codeset),
to_uni(locale.nl_langinfo(locale.MON_2), codeset),
@ -89,7 +63,7 @@ try:
to_uni(locale.nl_langinfo(locale.MON_12), codeset),
)
short_months = (
_deprecated_short_months = (
"",
to_uni(locale.nl_langinfo(locale.ABMON_1), codeset),
to_uni(locale.nl_langinfo(locale.ABMON_2), codeset),
@ -141,34 +115,7 @@ try:
except:
import time
month_to_int = {
to_uni(time.strftime('%B',(1,1,1,1,1,1,1,1,1)), codeset).lower() : 1,
to_uni(time.strftime('%b',(1,1,1,1,1,1,1,1,1)), codeset).lower() : 1,
to_uni(time.strftime('%B',(1,2,1,1,1,1,1,1,1)), codeset).lower() : 2,
to_uni(time.strftime('%b',(1,2,1,1,1,1,1,1,1)), codeset).lower() : 2,
to_uni(time.strftime('%B',(1,3,1,1,1,1,1,1,1)), codeset).lower() : 3,
to_uni(time.strftime('%b',(1,3,1,1,1,1,1,1,1)), codeset).lower() : 3,
to_uni(time.strftime('%B',(1,4,1,1,1,1,1,1,1)), codeset).lower() : 4,
to_uni(time.strftime('%b',(1,4,1,1,1,1,1,1,1)), codeset).lower() : 4,
to_uni(time.strftime('%B',(1,5,1,1,1,1,1,1,1)), codeset).lower() : 5,
to_uni(time.strftime('%b',(1,5,1,1,1,1,1,1,1)), codeset).lower() : 5,
to_uni(time.strftime('%B',(1,6,1,1,1,1,1,1,1)), codeset).lower() : 6,
to_uni(time.strftime('%b',(1,6,1,1,1,1,1,1,1)), codeset).lower() : 6,
to_uni(time.strftime('%B',(1,7,1,1,1,1,1,1,1)), codeset).lower() : 7,
to_uni(time.strftime('%b',(1,7,1,1,1,1,1,1,1)), codeset).lower() : 7,
to_uni(time.strftime('%B',(1,8,1,1,1,1,1,1,1)), codeset).lower() : 8,
to_uni(time.strftime('%b',(1,8,1,1,1,1,1,1,1)), codeset).lower() : 8,
to_uni(time.strftime('%B',(1,9,1,1,1,1,1,1,1)), codeset).lower() : 9,
to_uni(time.strftime('%b',(1,9,1,1,1,1,1,1,1)), codeset).lower() : 9,
to_uni(time.strftime('%B',(1,10,1,1,1,1,1,1,1)), codeset).lower() : 10,
to_uni(time.strftime('%b',(1,10,1,1,1,1,1,1,1)), codeset).lower() : 10,
to_uni(time.strftime('%B',(1,11,1,1,1,1,1,1,1)), codeset).lower() : 11,
to_uni(time.strftime('%b',(1,11,1,1,1,1,1,1,1)), codeset).lower() : 11,
to_uni(time.strftime('%B',(1,12,1,1,1,1,1,1,1)), codeset).lower() : 12,
to_uni(time.strftime('%b',(1,12,1,1,1,1,1,1,1)), codeset).lower() : 12,
}
long_months = (
_deprecated_long_months = (
"",
to_uni(time.strftime('%B',(1,1,1,1,1,1,1,1,1)), codeset),
to_uni(time.strftime('%B',(1,2,1,1,1,1,1,1,1)), codeset),
@ -184,7 +131,7 @@ except:
to_uni(time.strftime('%B',(1,12,1,1,1,1,1,1,1)), codeset),
)
short_months = (
_deprecated_short_months = (
"",
to_uni(time.strftime('%b',(1,1,1,1,1,1,1,1,1)), codeset),
to_uni(time.strftime('%b',(1,2,1,1,1,1,1,1,1)), codeset),

View File

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Vassilii Khachaturov
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
"""
Deeper testing of some DateParser internals.
"""
from __future__ import print_function, unicode_literals
import unittest
from ...utils.grampslocale import GrampsLocale
from ...lib.date import Date
class DateDisplayTest(unittest.TestCase):
def setUp(self):
from .._datedisplay import DateDisplay
self.display = DateDisplay()
self.display_RU = GrampsLocale(lang='ru').date_displayer
def assert_map_key_val(self, m, k, v):
try:
self.assertEqual(m[k], v)
except KeyError:
self.assertTrue(False, list(m.items()))
class DateDisplayCalendarTest(DateDisplayTest):
def test_calendar_gregorian_is_empty(self):
self.assert_map_key_val(self.display.calendar, Date.CAL_GREGORIAN, "")
def test_calendar_julian_RU(self):
self.assert_map_key_val(self.display_RU.calendar, Date.CAL_JULIAN, 'юлианский')
# This class tests common functionality in DateDisplay as applied to RU,
# and so it is coupled to translated strings and inflection names
# extracted by lexgettext from ru.po
class DateDisplayInflectionsTestRU(DateDisplayTest):
def setUp(self):
DateDisplayTest.setUp(self)
self.dd = self.display = self.display_RU
self.months = self.dd._ds.long_months
# TODO hardwired magic numbers! Bad API smell.
self.dd.set_format(4) # day month_name year
self.may = self.months[5]
def assertInflectionInDate(self, inflection, date, month=None):
if month is None:
month = date.get_month()
month_lexeme = self.months[month]
self.assertIn(month_lexeme.f[inflection],
self.dd.display(date))
def test_month_only_date_nominative(self):
for qual in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED):
d1945may = Date(1945, 5, 0)
d1945may.set_quality(qual)
self.assertInflectionInDate('И', d1945may)
def test_day_month_date_genitive(self):
d1945may9 = Date(1945, 5, 9)
self.assertInflectionInDate('Р', d1945may9)
def test_before_month_only_date_genitive(self):
d1945may = Date(1945, 5, 0)
d1945may.set_modifier(Date.MOD_BEFORE)
# TODO hardwired magic numbers! Bad API smell.
for inflecting_format in (3,4):
self.dd.set_format(inflecting_format)
# this depends on the fact that in Russian the short and long forms for May
# will be the same!
self.assertIn("до мая", self.dd.display(d1945may))
def test_between_month_only_dates_ablative(self):
b1945may_1946may = Date()
b1945may_1946may.set(
modifier=Date.MOD_RANGE,
value=(0, 5, 1945, False, 0, 5, 1946, False))
# TODO hardwired magic numbers! Bad API smell.
for inflecting_format in (3,4):
self.dd.set_format(inflecting_format)
# this depends on the fact that in Russian the short and long forms for May
# will be the same!
self.assertIn("между маем", self.dd.display(b1945may_1946may))
self.assertIn("и маем", self.dd.display(b1945may_1946may))
def test_month_only_date_span_from_genitive_to_accusative(self):
f1945may_t1946may = Date()
f1945may_t1946may.set(
modifier=Date.MOD_SPAN,
value=(0, 5, 1945, False, 0, 5, 1946, False))
# TODO hardwired magic numbers! Bad API smell.
for inflecting_format in (3,4):
self.dd.set_format(inflecting_format)
# this depends on the fact that in Russian the short and long forms for May
# will be the same!
self.assertIn("с мая", self.dd.display(f1945may_t1946may))
self.assertIn("по май", self.dd.display(f1945may_t1946may))
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Vassilii Khachaturov
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
"""
Deeper testing of some DateParser internals.
"""
from __future__ import print_function, unicode_literals
import unittest
from ...utils.grampslocale import GrampsLocale
from ...lib.date import Date
class DateParserTest(unittest.TestCase):
def setUp(self):
from .._dateparser import DateParser
self.parser = DateParser()
self.parser_RU = GrampsLocale(lang='ru').date_parser
def assert_map_key_val(self, m, k, v):
try:
self.assertEqual(m[k], v)
except KeyError:
self.assertTrue(False, list(m.items()))
def test_month_to_int_jan_is_1(self):
self.assert_map_key_val(self.parser.month_to_int, 'jan', 1)
def test_prefix_table_for_RU_built(self):
self.assertIn('ru_RU', self.parser._langs)
def test_month_to_int_septem_RU_is_9(self):
self.assert_map_key_val(self.parser.month_to_int, 'сентяб', 9)
def test_hebrew_to_int_av_is_12(self):
self.assert_map_key_val(self.parser.hebrew_to_int, 'av', 12)
self.assert_map_key_val(self.parser.hebrew_to_int, 'ав', 12) # RU
def test_french_to_int_thermidor_is_11(self):
self.assert_map_key_val(self.parser.french_to_int, 'thermidor', 11)
self.assert_map_key_val(self.parser.french_to_int, 'термидор', 11) # RU
def test_islamic_to_int_ramadan_is_9(self):
self.assert_map_key_val(self.parser.islamic_to_int, 'ramadan', 9)
self.assert_map_key_val(self.parser.islamic_to_int, 'рамадан', 9) # RU
def test_persian_to_int_tir_is_4(self):
self.assert_map_key_val(self.parser.persian_to_int, 'tir', 4)
self.assert_map_key_val(self.parser.persian_to_int, 'тир', 4) # RU
def test_calendar_to_int_gregorian(self):
self.assert_map_key_val(self.parser.calendar_to_int, 'gregorian', Date.CAL_GREGORIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'g', Date.CAL_GREGORIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'григорианский', Date.CAL_GREGORIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'г', Date.CAL_GREGORIAN)
def test_calendar_to_int_julian(self):
self.assert_map_key_val(self.parser.calendar_to_int, 'julian', Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'j', Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'юлианский', Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'ю', Date.CAL_JULIAN)
class Test_generate_variants(unittest.TestCase):
def setUp(self):
from .. import _datestrings
from .._dateparser import _generate_variants
self.ds = ds = _datestrings.DateStrings(GrampsLocale(languages=('ru')))
self.month_variants = list(_generate_variants(
zip(ds.long_months, ds.short_months,
ds.swedish_SV, ds.alt_long_months)))
def testVariantsSameLengthAsLongMonths(self):
self.assertEqual(len(self.ds.long_months),
len(self.month_variants))
def testRussianHasDifferentVariantsForEachMonth(self):
for i in range(1, 13):
mvi = self.month_variants[i]
self.assertTrue(len(mvi) > 1, msg=mvi)
def testNoEmptyStringInVariants(self):
for i in range(1, 13):
mvi = self.month_variants[i]
self.assertNotIn("", mvi)
def testLongMonthsAppearInVariants(self):
for i in range(1, 13):
lmi = self.ds.long_months[i]
mvi = self.month_variants[i]
self.assertIn("{}".format(lmi), mvi)
def testShortMonthsAppearInVariants(self):
for i in range(1, 13):
smi = self.ds.short_months[i]
mvi = self.month_variants[i]
self.assertIn("{}".format(smi), mvi)
def testLongMonthVariantsUnique(self):
for i in range(1, 13):
mvi = self.month_variants[i]
self.assertEqual(len(mvi), len(set(mvi)), msg=mvi)
def testRuMayVariantsContainSvMaj(self):
v = self.month_variants[5]
self.assertIn("Maj", v)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Vassilii Khachaturov
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
from __future__ import print_function, unicode_literals
import unittest
from .. import _datestrings
from ...lib.date import Date
class DateStringsTest(unittest.TestCase):
def setUp(self):
from ...utils.grampslocale import GrampsLocale
self.ds = _datestrings.DateStrings(GrampsLocale()) # whatever the default...
self.ds_EN = _datestrings.DateStrings(GrampsLocale(languages='en'))
self.ds_RU = _datestrings.DateStrings(GrampsLocale(languages='ru'))
def testTwelfthMonthIsDecember(self):
self.assertEqual(self.ds_EN.long_months[12], 'December')
self.assertEqual(self.ds_EN.short_months[12], 'Dec')
# May is 3-letter in Russian, and so abbreviated form
# will be different for inflections!
def testRussianHasDifferentInflectionsForShortMay(self):
v5 = list(self.ds_RU.short_months[5].variants())
self.assertTrue(len(v5) > 1, msg=v5)
def testEnAdarI_in_AdarII(self):
adar1 = self.ds_EN.hebrew[6]
adar2 = self.ds_EN.hebrew[7]
self.assertIn(str(adar1), str(adar2))
def testEnLastFrenchIsExtra(self):
self.assertEqual(str(self.ds_EN.french[-1]), "Extra")
def testEnPersianKhordadMordad(self):
khordad = self.ds_EN.persian[3].lower()
mordad = self.ds_EN.persian[5].lower()
self.assertEqual(khordad, "khordad")
self.assertEqual(mordad, "mordad")
def testEnIslamicRamadan9(self):
self.assertEqual(str(self.ds_EN.islamic[9]), "Ramadan")
def testFirstStringEmpty(self):
self.assertEqual(self.ds.long_months[0], "")
self.assertEqual(self.ds.short_months[0], "")
self.assertEqual(self.ds.alt_long_months[0], "")
def testCalendarIndex(self):
self.assertEqual(self.ds_EN.calendar[Date.CAL_GREGORIAN], "Gregorian")
self.assertEqual(self.ds_EN.calendar[Date.CAL_JULIAN], "Julian")
self.assertEqual(self.ds_EN.calendar[Date.CAL_HEBREW], "Hebrew")
self.assertEqual(self.ds_EN.calendar[Date.CAL_FRENCH], "French Republican")
self.assertEqual(self.ds_EN.calendar[Date.CAL_PERSIAN], "Persian")
self.assertEqual(self.ds_EN.calendar[Date.CAL_ISLAMIC], "Islamic")
self.assertEqual(self.ds_EN.calendar[Date.CAL_SWEDISH], "Swedish")
if __name__ == "__main__":
unittest.main()

View File

@ -530,7 +530,7 @@ class Date(object):
QUAL_NONE = 0 # BITWISE
QUAL_ESTIMATED = 1
QUAL_CALCULATED = 2
QUAL_INTERPRETED = 4
#QUAL_INTERPRETED = 4 unused in source!!
CAL_GREGORIAN = 0 # CODE
CAL_JULIAN = 1

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
@ -26,12 +27,13 @@
# python modules
#
#------------------------------------------------------------------------
from __future__ import print_function
from __future__ import print_function, unicode_literals
import gettext
import sys
import os
import codecs
import locale
import collections
import logging
LOG = logging.getLogger("." + __name__)
@ -155,6 +157,7 @@ class GrampsLocale(object):
codes corresponding to subidrectries in the localedir,
e.g.: "fr" or "zh_CN".
"""
DEFAULT_TRANSLATION_STR = "default"
__first_instance = None
encoding = None
@ -936,6 +939,128 @@ class GrampsLocale(object):
# Translations Classes
#
#-------------------------------------------------------------------------
if sys.version_info < (3,0):
_LexemeBaseStr = unicode
_isstring = lambda s: isinstance(s, basestring)
else:
_LexemeBaseStr = str
_isstring = lambda s: isinstance(s, str)
class Lexeme(_LexemeBaseStr):
r"""
Created with :meth:`~GrampsTranslations.lexgettext`
.. rubric:: Example
Python code:
::
_ = lexgettext
dec = _("localized lexeme inflections||December")
xmas = _("lexeme||Christmas")
text = _("{holiday} is celebrated in {month}".format(
holiday=xmas, month=dec))
greeting = _("Merry {holiday}!").format(holiday=xmas)
XMAS = xmas.upper()
print ("\n".join([XMAS, text, greeting]))
Translation database (Russian example):
::
msgid "lexeme||December"
msgstr "NOMINATIVE=декабрь|GENITIVE=декабря|ABLATIVE=декабрём|LOCATIVE=декабре"
msgid "lexeme||Christmas"
msgstr "NOMINATIVE=рождество|GENITIVE=рождества|ABLATIVE=рождеством"
msgid "{holiday} is celebrated in {month}"
msgstr "{holiday} празднуют в {month.f[LOCATIVE]}"
msgid "Merry {holiday}!"
msgstr "Счастливого {holiday.f[GENITIVE]}!"
Prints out:
In English locale:
::
CHRISTMAS
Christmas is celebrated in December
Merry Christmas!
In Russian locale:
::
РОЖДЕСТВО
рождество празднуют в декабре
Счастливого рождества!
.. rubric:: Description
Stores an arbitrary number of forms, e.g., inflections.
These forms are accessible under dictionary keys for each form.
The names of the forms are language-specific. They are assigned
by the human translator of the corresponding language (in XX.po)
as in the example above,
see :meth:`~GrampsTranslations.lexgettext` docs
for more info.
The translated format string can then refer to a specific form
of the lexeme using ``.``:attr:`~Lexeme.f` and square brackets:
``{holiday.f[GENITIVE]}``
expects holiday to be a Lexeme which has a form ``'GENITIVE'`` in it.
An instance of Lexeme can also be used as a regular unicode string.
In this case, the work will be delegated to the string for the very
first form provided in the translated string. In the example above,
``{holiday}`` in the translated string will expand to the Russian
nominative form for Christmas, and ``xmas.upper()`` will produce
the same nominative form in capital letters.
.. rubric:: Motivation
Lexeme is the term used in linguistics for the set of forms taken
by a particular word, e.g. cases for a noun or tenses for a verb.
Gramps often needs to compose sentences from several blocks of
text and single words, often by using python string formatting.
For instance, formatting a date range is done similarly to this:
::
_("Between {startdate_month} {startdate_year}"
"and {enddate_month} {enddate_year}").format(
startdate_month = m1,
startdate_year = y1,
enddate_month = m2,
enddate_year = y2)
To make such text translatable, the arguments injected into
format string need to bear all the linguistical information
on how to plug them into a sentence, i.e., the forms, depending
on the linguistic context of where the argument appears.
The format string needs to select the relevant linguistic form.
This is why ``m1`` and ``m2`` are instances of :class:`~Lexeme`.
On the other hand, for languages where there is no linguistic
variation in such sentences, the code needs not to be aware of
the underlying :class:`~Lexeme` complexity;
and so they can be processed just like simple strings
both when passed around in the code and when formatted.
"""
def __new__(cls, iterable, *args, **kwargs):
if _isstring(iterable):
newobj = _LexemeBaseStr.__new__(cls, iterable, *args, **kwargs)
else:
od = collections.OrderedDict(iterable)
l = list(od.values()) or [""]
newobj = _LexemeBaseStr.__new__(cls, l[0], *args, **kwargs)
newobj._forms = od
return newobj
def variants(self):
"""All lexeme forms, in the same order as given upon construction.
The first one returned is the default form, which is used when the
Lexeme instance is used in lieu of a string object.
Same as ``f.values()``"""
return self._forms.values()
@property
def f(self):
"""Dictionary of the lexeme forms"""
return self._forms
class GrampsTranslations(gettext.GNUTranslations):
"""
Overrides and extends gettext.GNUTranslations. See the Python gettext
@ -950,6 +1075,7 @@ class GrampsTranslations(gettext.GNUTranslations):
def gettext(self, msgid):
"""
Obtain translation of gettext, return a unicode object
:param msgid: The string to translated.
:type msgid: unicode
:returns: Translation or the original.
@ -1009,6 +1135,32 @@ class GrampsTranslations(gettext.GNUTranslations):
msgval = msgid[sep_idx+1:]
return msgval
def lexgettext(self, msgid):
"""
Extract all inflections of the same lexeme,
stripping the '|'-separated context using :meth:`~sgettext`
The *resulting* message provided by the translator
is supposed to be '|'-separated as well.
The possible formats are either (1) a single string
for a language with no inflections, or (2) a list of
<inflection name>=<inflected form>, separated with '|'.
For example:
(1) "Uninflectable"
(2) "n=Inflected-nominative|g=Inflected-genitive|d=Inflected-dative"
See :class:`~Lexeme` documentation for detailed explanation and example.
:param msgid: The string to translated.
:type msgid: unicode
:returns: Translation or the original with context stripped.
:rtype: unicode (for option (1)) / Lexeme (option (2))
"""
variants = self.sgettext(msgid).split('|')
return Lexeme([v.split('=') for v in variants]
) if len(variants) > 1 else variants[0]
class GrampsNullTranslations(gettext.NullTranslations):
"""
Extends gettext.NullTranslations to provide the sgettext method.
@ -1023,6 +1175,8 @@ class GrampsNullTranslations(gettext.NullTranslations):
msgval = msgid[sep_idx+1:]
return msgval
lexgettext = sgettext
def language(self):
"""
The null translation returns the raw msgids, which are in English

View File

@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Vassilii Khachaturov
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
from __future__ import print_function, unicode_literals
import unittest
import sys
try:
if sys.version_info < (3,3):
from mock import Mock
else:
from unittest.mock import Mock
MOCKING = True
except:
MOCKING = False
print ("Mocking disabled, some testing skipped", sys.exc_info()[0:2])
class LexGettextTest(unittest.TestCase):
SRC_WORD = "Inflect-me"
MSGID = "how-to-use-lexgettext||" + SRC_WORD
def setUp(self):
from ..grampslocale import GrampsTranslations
from ..grampslocale import GrampsLocale as Loc
self.trans = GrampsTranslations()
def setup_sgettext_mock(self, msgval_expected):
if MOCKING:
mock = Mock(return_value=msgval_expected)
else:
mock = lambda msgid: msgval_expected
self.trans.sgettext = mock
def tearDown(self):
if MOCKING:
try:
self.trans.sgettext.assert_called_once_with(
self.MSGID)
except AttributeError as e:
print ("Apparently the test has never set up the mock: ", e)
def testSrcWordOnlyIfNoTranslation(self):
self.setup_sgettext_mock(self.SRC_WORD)
result = self.trans.lexgettext(self.MSGID)
self.assertEqual(result, self.SRC_WORD)
def test3InflectionsExtractableByNameThroughForm(self):
translated = "n=TargetNom|g=TargetGen|d=TargetDat"
self.setup_sgettext_mock(translated)
lex = self.trans.lexgettext(self.MSGID)
formatted = "{lex.f[n]},{lex.f[g]},{lex.f[d]}".format(lex=lex)
self.assertEqual(formatted, "TargetNom,TargetGen,TargetDat")
def testFirstLexemeFormExtractableAsDefaultString(self):
translated = "def=Default|v1=Option1|a=AnotherOption"
self.setup_sgettext_mock(translated)
lex = self.trans.lexgettext(self.MSGID)
formatted = "{}".format(lex)
self.assertEqual(formatted, "Default")
class LexemeTest(unittest.TestCase):
def setUp(self):
from ..grampslocale import Lexeme
self.lex = Lexeme((('a', 'aaa'), ('b', 'bbb'), ('c', 'ccc')))
self.zlex = Lexeme({'z' : 'zzz'})
self.elex = Lexeme({})
def testIsHashable(self):
hash(self.lex) # throws if not hashable
# test delegation to an arbitrary string method pulled in from unicode
def testDefaultStringStartsWithAA(self):
self.assertTrue(self.lex.startswith('aa'),
msg="default string: {} dict: {}".format(
self.lex, self.lex.__dict__))
def testCanConcatenateStringAndLexeme(self):
moo = "moo"
self.assertEqual(moo + self.lex, "mooaaa")
def testCanConcatenateStringAndLexemeInPlace(self):
moo = "moo"
moo += self.lex
self.assertEqual(moo, "mooaaa")
def testCanConcatenateLexemeAndStringInPlace(self):
moo = "moo"
self.lex += moo
self.assertEqual(self.lex, "aaamoo")
def testCanConcatenateTwoLexemes(self):
aaazzz = self.lex + self.zlex
self.assertEqual(aaazzz, "aaazzz")
def testCanJoinTwoLexemes(self):
aaa_zzz = "_".join([self.lex,self.zlex])
self.assertEqual(aaa_zzz, "aaa_zzz")
def testEmptyIterableLikeEmptyString(self):
self.assertEqual(self.elex, "")
if __name__ == "__main__":
unittest.main()

View File

@ -194,7 +194,13 @@ class Calendar(Report):
self.doc.draw_box("CAL-Title", "", 0, 0, width, header, mark)
self.doc.draw_line("CAL-Border", 0, header, width, header)
year = self.year
title = "%s %d" % (_dd.long_months[month].capitalize(), year)
# TRANSLATORS: see
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
# to learn how to select proper inflection for your language.
title = _("{long_month} {year}").format(
long_month = _dd.long_months[month],
year = year
).capitalize()
mark = IndexMark(title, INDEX_TYPE_TOC, 2)
font_height = pt2cm(ptitle.get_font().get_size())
self.doc.center_text("CAL-Title", title,

View File

@ -41,7 +41,7 @@ import copy
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
_ = glocale.translation.sgettext
from gramps.gen.plug.menu import (PersonOption, FilterOption,
EnumeratedListOption)
from gramps.gen.plug.report import Report
@ -71,8 +71,8 @@ cal = config.get('preferences.calendar-format-report')
#------------------------------------------------------------------------
def _get_sort_functions(sort):
return [
(_("Birth Date"),sort.by_birthdate_key),
(_("Name"),sort.by_last_name_key),
(_("sorted by|Birth Date"),sort.by_birthdate_key),
(_("sorted by|Name"),sort.by_last_name_key),
]
#------------------------------------------------------------------------

View File

@ -1703,7 +1703,10 @@ def get_day_list(event_date, holiday_list, bday_anniv_list):
if event == 'Birthday':
txt_str = (text + ', <em>'
+ (_('%s old') % str(age_str) if nyears else _('birth'))
# TRANSLATORS: expands to smth like "12 years old",
# where "12 years" is already localized to your language
+ (_('%s old') % str(age_str)
if nyears else _('birth'))
+ '</em>')
# an anniversary