From ee396b27f6e38dbc522ba9b23ff5887376ea60ea Mon Sep 17 00:00:00 2001 From: David MENTRE Date: Sat, 19 Dec 2009 20:23:30 +0100 Subject: [PATCH 4/5] Put language specific code into separate objects into i18n module * Follow d2 recommendations and implement a suitable object hierachy that stores the code specific to a given language. * Provide an object for fr_FR language. * Update street_index.py to use those objects. --- ocitysmap/i18n.py | 102 +++++++++++++++++++++++++++----------------- ocitysmap/street_index.py | 28 +++++++----- 2 files changed, 78 insertions(+), 52 deletions(-) diff --git a/ocitysmap/i18n.py b/ocitysmap/i18n.py index 7e95514..46aa138 100644 --- a/ocitysmap/i18n.py +++ b/ocitysmap/i18n.py @@ -23,43 +23,65 @@ import re -APPELLATIONS = [ u"Allée", u"Avenue", u"Boulevard", u"Carrefour", u"Chaussée", - u"Chemin", u"Cité", u"Clos", u"Côte", u"Cour", u"Cours", - u"Degré", - u"Esplanade", u"Impasse", u"Liaison", u"Mail", u"Montée", - u"Passage", u"Place", u"Placette", u"Pont", u"Promenade", - u"Quai", - u"Résidence", u"Rond-Point", u"Rang", u"Route", u"Rue", - u"Ruelle", - u"Square", u"Traboule", u"Traverse", u"Venelle", u"Villa", - u"Voie", u"Rond-point" ] -DETERMINANTS = [ u" des", u" du", u" de la", u" de l'", u" de", u" d'", u"" ] - -SPACE_REDUCE = re.compile(r"\s+") -PREFIX_REGEXP = re.compile(r"^(?P(%s)(%s)?)\s?\b(?P.*)" % - ("|".join(APPELLATIONS), - "|".join(DETERMINANTS)), re.IGNORECASE | re.UNICODE) - -# for IndexPageGenerator._upper_unaccent_string -E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE) -I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE) -A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE) -O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE) -U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE) - -def user_readable_street(name): - name = name.strip() - name = SPACE_REDUCE.sub(" ", name) - name = PREFIX_REGEXP.sub(r"\g (\g)", name) - return name - -def _upper_unaccent_string(s): - s = E_ACCENT.sub("e", s) - s = I_ACCENT.sub("i", s) - s = A_ACCENT.sub("a", s) - s = O_ACCENT.sub("o", s) - s = U_ACCENT.sub("u", s) - return s.upper() - -def first_letter_equal(a, b): - return _upper_unaccent_string(a) == _upper_unaccent_string(b) +class i18n: + """Functions needed to be implemented for a new language. + See i18n_fr_FR_UTF8 below for an example. """ + def language_code(self): + pass + + def user_readable_street(self, name): + pass + + def first_letter_equal(self, a, b): + pass + +class i18n_fr_FR_UTF8(i18n): + APPELLATIONS = [ u"Allée", u"Avenue", u"Boulevard", u"Carrefour", u"Chaussée", + u"Chemin", u"Cité", u"Clos", u"Côte", u"Cour", u"Cours", + u"Degré", + u"Esplanade", u"Impasse", u"Liaison", u"Mail", u"Montée", + u"Passage", u"Place", u"Placette", u"Pont", u"Promenade", + u"Quai", + u"Résidence", u"Rond-Point", u"Rang", u"Route", u"Rue", + u"Ruelle", + u"Square", u"Traboule", u"Traverse", u"Venelle", u"Villa", + u"Voie", u"Rond-point" ] + DETERMINANTS = [ u" des", u" du", u" de la", u" de l'", + u" de", u" d'", u"" ] + + SPACE_REDUCE = re.compile(r"\s+") + PREFIX_REGEXP = re.compile(r"^(?P(%s)(%s)?)\s?\b(?P.*)" % + ("|".join(APPELLATIONS), + "|".join(DETERMINANTS)), re.IGNORECASE + | re.UNICODE) + + # for IndexPageGenerator._upper_unaccent_string + E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE) + I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE) + A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE) + O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE) + U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE) + + def _upper_unaccent_string(self, s): + s = self.E_ACCENT.sub("e", s) + s = self.I_ACCENT.sub("i", s) + s = self.A_ACCENT.sub("a", s) + s = self.O_ACCENT.sub("o", s) + s = self.U_ACCENT.sub("u", s) + return s.upper() + + def language_code(self): + return "fr_FR.UTF8" + + def user_readable_street(self, name): + name = name.strip() + name = self.SPACE_REDUCE.sub(" ", name) + name = self.PREFIX_REGEXP.sub(r"\g (\g)", name) + return name + + def first_letter_equal(self, a, b): + return self._upper_unaccent_string(a) == self._upper_unaccent_string(b) + +# The global map used by module users +language_map = { 'fr_FR.UTF-8': i18n_fr_FR_UTF8() } + diff --git a/ocitysmap/street_index.py b/ocitysmap/street_index.py index 0f859f5..fc47a95 100644 --- a/ocitysmap/street_index.py +++ b/ocitysmap/street_index.py @@ -39,9 +39,9 @@ class BaseOCitySMapError(Exception): class UnsufficientDataError(BaseOCitySMapError): """Not enough data in the OSM database to proceed.""" -def _humanize_street_label(street): +def _user_readable_label(street): """Creates a street label usable in the street list adjacent to the map - (like 'Bréhat (Allée des)' from the street definition tuple.""" + from the street definition tuple.""" def couple_compare(x,y): a = y[0] - x[0] @@ -52,7 +52,6 @@ def _humanize_street_label(street): def distance(a,b): return (b[0]-a[0])**2 + (b[1]-a[1])**2 - name = i18n.user_readable_street(street[0]) squares = street[1] minx = min([x[0] for x in squares]) maxx = max([x[0] for x in squares]) @@ -89,11 +88,12 @@ def _humanize_street_label(street): utils.gen_horizontal_square_label(first[1]), utils.gen_vertical_square_label(last[0]), utils.gen_horizontal_square_label(last[1])) - return (name, label) + return label class IndexPageGenerator: - def __init__(self, streets): + def __init__(self, streets, i18n): self.streets = streets + self.i18n = i18n def _get_font_parameters(self, cr, fontsize): cr.select_font_face("DejaVu", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) @@ -124,7 +124,7 @@ class IndexPageGenerator: prevletter = u'' heading_letter_count = 0 for street in self.streets: - if not i18n.first_letter_equal(street[0][0], prevletter): + if not self.i18n.first_letter_equal(street[0][0], prevletter): heading_letter_count += 1 prevletter = street[0][0] @@ -178,7 +178,7 @@ class IndexPageGenerator: for street in self.streets: # Letter label firstletter = street[0][0] - if not i18n.first_letter_equal(firstletter, prevletter): + if not self.i18n.first_letter_equal(firstletter, prevletter): # Make sure we have no orphelin heading letter label at the # end of a column if y + heading_fheight + fheight > paperheight: @@ -244,9 +244,8 @@ class OCitySMap: assert bool(city_name) ^ bool(boundingbox) (self.city_name, self.boundingbox) = (city_name, boundingbox) - assert language is not None, "Language parameter is mandatory" - self.language = language - l.info('Language ' + self.language) + self.i18n = i18n.language_map[language] + l.info('Language ' + self.i18n.language_code()) if self.city_name: l.info('OCitySMap renderer for %s.' % self.city_name) @@ -471,7 +470,12 @@ class OCitySMap: # built to represent the list of squares, and the list is # alphabetically-sorted. prev_locale = locale.getlocale(locale.LC_COLLATE) - locale.setlocale(locale.LC_COLLATE, self.language) + locale.setlocale(locale.LC_COLLATE, self.i18n.language_code()) + + def _humanize_street_label(street): + return (self.i18n.user_readable_street(street[0]), + _user_readable_label(street)) + try: sl = sorted(map(_humanize_street_label, sl), lambda x, y: locale.strcoll(x[0].lower(), y[0].lower())) @@ -489,7 +493,7 @@ class OCitySMap: output_filename = "%s_index.%s" % (output_prefix, file_type) l.debug("rendering " + output_filename + "...") - generator = IndexPageGenerator(self.streets) + generator = IndexPageGenerator(self.streets, self.i18n) if file_type == 'xml': l.debug('not rendering index as xml (not supported)') -- 1.6.3.3