Back to index

plone3  3.1.7
i18nl10n.py
Go to the documentation of this file.
00001 """
00002 Collection of i18n and l10n utility methods.
00003 """
00004 import re
00005 import logging
00006 
00007 from zope.i18n import translate
00008 from zope.publisher.interfaces.browser import IBrowserRequest
00009 
00010 from Acquisition import aq_acquire
00011 from DateTime import DateTime
00012 from DateTime.interfaces import IDateTime
00013 
00014 from Products.CMFCore.utils import getToolByName
00015 from Products.CMFPlone.log import log
00016 from Products.CMFPlone.utils import safe_unicode
00017 
00018 # get the registered translation service
00019 from Products.PageTemplates.GlobalTranslationService import \
00020      getGlobalTranslationService
00021 
00022 from Products.PlacelessTranslationService.utility import PTSTranslationDomain
00023 
00024 # Create a PTS surrogate domain
00025 plonedomain = PTSTranslationDomain('plone')
00026 atctdomain = PTSTranslationDomain('atcontenttypes')
00027 pltdomain = PTSTranslationDomain('plonelanguagetool')
00028 prtdomain = PTSTranslationDomain('passwordresettool')
00029 cmfpwdomain = PTSTranslationDomain('cmfplacefulworkflow')
00030 cmfedomain = PTSTranslationDomain('cmfeditions')
00031 
00032 # these are taken from PTS, used for format interpolation
00033 NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
00034 _interp_regex = re.compile(r'(?<!\$)(\$(?:%(n)s|{%(n)s}))' %({'n': NAME_RE}))
00035 
00036 datetime_formatvariables = ('H', 'I', 'm', 'd', 'M', 'p', 'S', 'Y', 'y', 'Z')
00037 name_formatvariables = ('a', 'A', 'b', 'B')
00038 
00039 # unicode aware translate method (i18n)
00040 def utranslate(*args, **kw):
00041     # safety precaution for cases where we get passed in an encoded string
00042     return safe_unicode(getGlobalTranslationService().translate(*args, **kw))
00043 
00044 # unicode aware localized time method (l10n)
00045 def ulocalized_time(time, long_format=None, context=None,
00046                     domain='plonelocales', request=None):
00047     # get msgid
00048     msgid = long_format and 'date_format_long' or 'date_format_short'
00049 
00050     # NOTE: this requires the presence of two msgids inside the translation catalog
00051     #       date_format_long and date_format_short
00052     #       These msgids are translated using interpolation.
00053     #       The variables used here are the same as used in the strftime formating.
00054     #       Supported are %A, %a, %B, %b, %H, %I, %m, %d, %M, %p, %S, %Y, %y, %Z, each used as
00055     #       variable in the msgstr without the %.
00056     #       For example: "${A} ${d}. ${B} ${Y}, ${H}:${M} ${Z}"
00057     #       Each language dependend part is translated itself as well.
00058 
00059     # From http://docs.python.org/lib/module-time.html
00060     #
00061     # %a    Locale's abbreviated weekday name.   
00062     # %A      Locale's full weekday name.        
00063     # %b      Locale's abbreviated month name.   
00064     # %B      Locale's full month name.   
00065     # %d      Day of the month as a decimal number [01,31].    
00066     # %H      Hour (24-hour clock) as a decimal number [00,23].       
00067     # %I      Hour (12-hour clock) as a decimal number [01,12].       
00068     # %m      Month as a decimal number [01,12].        
00069     # %M      Minute as a decimal number [00,59].       
00070     # %p      Locale's equivalent of either AM or PM.   
00071     # %S      Second as a decimal number [00,61].       
00072     # %y      Year without century as a decimal number [00,99].       
00073     # %Y      Year with century as a decimal number.    
00074     # %Z      Time zone name (no characters if no time zone exists).  
00075 
00076     mapping = {}
00077     # convert to DateTime instances. Either a date string or 
00078     # a DateTime instance needs to be passed.
00079     if not IDateTime.providedBy(time):
00080         try:
00081             time = DateTime(time)
00082         except:
00083             log('Failed to convert %s to a DateTime object' % time,
00084                 severity=logging.DEBUG)
00085             return None
00086 
00087     if context is None:
00088         # when without context, we cannot do very much.
00089         return time.ISO()
00090 
00091     if request is None:
00092         request = aq_acquire(context, 'REQUEST')
00093 
00094     # get the formatstring
00095     formatstring = translate(msgid, domain, mapping, request)
00096 
00097     if formatstring is None or formatstring.startswith('date_'):
00098         # msg catalog was not able to translate this msgids
00099         # use default setting
00100 
00101         properties=getToolByName(context, 'portal_properties').site_properties
00102         if long_format:
00103             format=properties.localLongTimeFormat
00104         else:
00105             format=properties.localTimeFormat
00106 
00107         return time.strftime(format)
00108     
00109     # get the format elements used in the formatstring
00110     formatelements = _interp_regex.findall(formatstring)
00111     # reformat the ${foo} to foo
00112     formatelements = [el[2:-1] for el in formatelements]
00113 
00114     # add used elements to mapping
00115     elements = [e for e in formatelements if e in datetime_formatvariables]
00116 
00117     # add weekday name, abbr. weekday name, month name, abbr month name
00118     week_included = True
00119     month_included = True
00120 
00121     name_elements = [e for e in formatelements if e in name_formatvariables]
00122     if not ('a' in name_elements or 'A' in name_elements):
00123         week_included = False
00124     if not ('b' in name_elements or 'B' in name_elements):
00125         month_included = False
00126 
00127     for key in elements:
00128         mapping[key]=time.strftime('%'+key)
00129 
00130     if week_included:
00131         weekday = int(time.strftime('%w')) # weekday, sunday = 0
00132         if 'a' in name_elements:
00133             mapping['a']=weekdayname_msgid_abbr(weekday)
00134         if 'A' in name_elements:
00135             mapping['A']=weekdayname_msgid(weekday)
00136     if month_included:
00137         monthday = int(time.strftime('%m')) # month, january = 1
00138         if 'b' in name_elements:
00139             mapping['b']=monthname_msgid_abbr(monthday)
00140         if 'B' in name_elements:
00141             mapping['B']=monthname_msgid(monthday)
00142 
00143     # translate translateable elements
00144     for key in name_elements:
00145         mapping[key] = translate(mapping[key], domain, context=request, default=mapping[key])
00146 
00147     # translate the time string
00148     return translate(msgid, domain, mapping, request)
00149 
00150 def _numbertoenglishname(number, format=None, attr='_days'):
00151     # returns the english name of day or month number
00152     # starting with Sunday == 0
00153     # and January = 1
00154     # format is either None, 'a' or 'p')
00155     #   None  means full name (January, February, ...)
00156     #   'a' means abbreviated (Jan, Feb, ..)
00157     #   'p' means abbreviated with . (dot) at end (Jan., Feb., ...)
00158     
00159     number = int(number)
00160     if format is not None:
00161         attr = '%s_%s' % (attr, format)
00162     
00163     # get list from DateTime attribute
00164     thelist = getattr(DateTime, attr)
00165 
00166     return thelist[number]
00167     
00168 def monthname_english(number, format=None):
00169     # returns the english name of month with number
00170     return _numbertoenglishname(number, format=format, attr='_months')
00171 
00172 def weekdayname_english(number, format=None):
00173     # returns the english name of week with number
00174     return _numbertoenglishname(number, format=format, attr='_days')
00175 
00176 def monthname_msgid(number):
00177     # returns the msgid for monthname
00178     # use to translate to full monthname (January, February, ...)
00179     # eg. month_jan, month_feb, ...
00180     return "month_%s" % monthname_english(number, format='a').lower()
00181     
00182 def monthname_msgid_abbr(number):
00183     # returns the msgid for the abbreviated monthname
00184     # use to translate to abbreviated format (Jan, Feb, ...)
00185     # eg. month_jan_abbr, month_feb_abbr, ...
00186     return "month_%s_abbr" % monthname_english(number, format='a').lower()
00187     
00188 def weekdayname_msgid(number):
00189     # returns the msgid for the weekdayname
00190     # use to translate to full weekdayname (Monday, Tuesday, ...)
00191     # eg. weekday_mon, weekday_tue, ...
00192     return "weekday_%s" % weekdayname_english(number, format='a').lower()
00193     
00194 def weekdayname_msgid_abbr(number):
00195     # returns the msgid for abbreviated weekdayname
00196     # use to translate to abbreviated format (Mon, Tue, ...)
00197     # eg. weekday_mon_abbr, weekday_tue_abbr, ...
00198     return "weekday_%s_abbr" % weekdayname_english(number, format='a').lower()
00199     
00200 def weekdayname_msgid_short(number):
00201     # return the msgid for short weekdayname
00202     # use to translate to 2 char format (Mo, Tu, ...)
00203     # eg. weekday_mon_short, weekday_tue_short, ...
00204     return "weekday_%s_short" % weekdayname_english(number, format='a').lower()