Back to index

plone3  3.1.7
Public Member Functions | Static Public Attributes | Private Member Functions | Private Attributes | Static Private Attributes
PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService Class Reference
Inheritance diagram for PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService:
Inheritance graph
[legend]
Collaboration diagram for PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService:
Collaboration graph
[legend]

List of all members.

Public Member Functions

def __init__
def calculatePoId
def manage_renameObject
def reloadCatalog
def addCatalog
def getCatalogsForTranslation
def setLanguageFallbacks
def getLanguageName
def getLanguages
def isRTL
def utranslate
def translate
def negotiate_language
def getDomain
def interpolate
def manage_main

Static Public Attributes

string meta_type = 'Placeless Translation Service'
string icon = 'misc_/PlacelessTranslationService/PlacelessTranslationService.png'
tuple all_meta_types = ()
tuple security = ClassSecurityInfo()

Private Member Functions

def _registerMessageCatalog
def _unregister_inner
def _unregisterMessageCatalog
def _load_catalog_file
def _load_i18n_dir
def _updateMoFile
def _load_locales_dir
def _delObject

Private Attributes

 _instance_version
 _domain
 _fallbacks
 _p_changed

Static Private Attributes

tuple _class_version = (1, 4, 13, 0)

Detailed Description

The Placeless Translation Service

Definition at line 138 of file PlacelessTranslationService.py.


Constructor & Destructor Documentation

def PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService.__init__ (   self,
  default_domain = 'global',
  fallbacks = None 
)

Definition at line 157 of file PlacelessTranslationService.py.

00157 
00158     def __init__(self, default_domain='global', fallbacks=None):
00159         self._instance_version = self._class_version
00160         # XXX We haven't specified that ITranslationServices have a default
00161         # domain.  So far, we've required the domain argument to .translate()
00162         self._domain = default_domain
00163         # _catalogs maps (language, domain) to identifiers
00164         catalogRegistry = {}
00165         fbcatalogRegistry = {}
00166         # What languages to fallback to, if there is no catalog for the
00167         # requested language (no fallback on individual messages)
00168         if fallbacks is None:
00169             fallbacks = LANGUAGE_FALLBACKS
00170         self._fallbacks = fallbacks


Member Function Documentation

Definition at line 435 of file PlacelessTranslationService.py.

00435 
00436     def _delObject(self, id, dp=1):
00437         catalog = self._getOb(id)
00438         Folder._delObject(self, id, dp)
00439         self._unregisterMessageCatalog(catalog)

Here is the call graph for this function:

Here is the caller graph for this function:

def PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService._load_catalog_file (   self,
  name,
  popath,
  language = None,
  domain = None 
) [private]
create catalog instances in ZODB

Definition at line 248 of file PlacelessTranslationService.py.

00248 
00249     def _load_catalog_file(self, name, popath, language=None, domain=None):
00250         """
00251         create catalog instances in ZODB
00252         """
00253         id = self.calculatePoId(name, popath, language=language, domain=domain)
00254 
00255         # validate id
00256         try:
00257             self._checkId(id, 1)
00258         except:
00259             id=name # fallback mode for borked paths
00260 
00261         # the po file path
00262         pofile = os.path.join(popath, name)
00263 
00264         ob = self._getOb(id, _marker)
00265         try:
00266             if isinstance(ob, BrokenMessageCatalog):
00267                 # remove broken catalog
00268                 self._delObject(id)
00269                 ob = _marker
00270         except:
00271             pass
00272         try:
00273             if ob is _marker:
00274                 self.addCatalog(GettextMessageCatalog(id, pofile, language, domain))
00275             else:
00276                 self.reloadCatalog(ob)
00277         except IOError:
00278             # io error probably cause of missing or not accessable
00279             try:
00280                 # remove false catalog from PTS instance
00281                 self._delObject(id)
00282             except:
00283                 pass
00284         except KeyboardInterrupt:
00285             raise
00286         except:
00287             exc=sys.exc_info()
00288             log('Message Catalog has errors', logging.WARNING, pofile, exc)
00289             self.addCatalog(BrokenMessageCatalog(id, pofile, exc))

Here is the call graph for this function:

Here is the caller graph for this function:

Loads an i18n directory (Zope3 PTS format)
Format:
    Products/MyProduct/i18n/*.po
The language and domain are stored in the po file

Definition at line 290 of file PlacelessTranslationService.py.

00290 
00291     def _load_i18n_dir(self, basepath):
00292         """
00293         Loads an i18n directory (Zope3 PTS format)
00294         Format:
00295             Products/MyProduct/i18n/*.po
00296         The language and domain are stored in the po file
00297         """
00298         log('looking into ' + basepath, logging.DEBUG)
00299         if not os.path.isdir(basepath):
00300             log('it does not exist', logging.DEBUG)
00301             return
00302 
00303         # print deprecation warning for mo files
00304         depr_names = fnmatch.filter(os.listdir(basepath), '*.mo')
00305         if depr_names:
00306             import warnings
00307             warnings.warn(
00308                 'Compiled po files (*.mo) found in %s. '
00309                 'PlacelessTranslationService now compiles '
00310                 'mo files automatically. All mo files have '
00311                 'been ignored.' % basepath, DeprecationWarning, stacklevel=4)
00312 
00313         # load po files
00314         names = fnmatch.filter(os.listdir(basepath), '*.po')
00315         if not names:
00316             log('nothing found', logging.DEBUG)
00317             return
00318         for name in names:
00319             self._load_catalog_file(name, basepath)
00320 
00321         log('Initialized:', detail = repr(names) + (' from %s\n' % basepath))

Here is the call graph for this function:

Loads an locales directory (Zope3 format)
Format:
    Products/MyProduct/locales/${lang}/LC_MESSAGES/${domain}.po
Where ${lang} and ${domain} are the language and the domain of the po
file (e.g. locales/de/LC_MESSAGES/plone.po)

Definition at line 370 of file PlacelessTranslationService.py.

00370 
00371     def _load_locales_dir(self, basepath):
00372         """
00373         Loads an locales directory (Zope3 format)
00374         Format:
00375             Products/MyProduct/locales/${lang}/LC_MESSAGES/${domain}.po
00376         Where ${lang} and ${domain} are the language and the domain of the po
00377         file (e.g. locales/de/LC_MESSAGES/plone.po)
00378         """
00379         found=[]
00380         log('looking into ' + basepath, logging.DEBUG)
00381         if not os.path.isdir(basepath):
00382             log('it does not exist', logging.DEBUG)
00383             return
00384 
00385         for lang in os.listdir(basepath):
00386             langpath = os.path.join(basepath, lang)
00387             if not os.path.isdir(langpath):
00388                 # it's not a directory
00389                 continue
00390             if not _checkLanguage(lang):
00391                 return
00392             msgpath = os.path.join(langpath, 'LC_MESSAGES')
00393             if not os.path.isdir(msgpath):
00394                 # it doesn't contain a LC_MESSAGES directory
00395                 continue
00396             names = fnmatch.filter(os.listdir(msgpath), '*.po')
00397             for name in names:
00398                 domain = name[:-3]
00399                 found.append('%s:%s' % (lang, domain))
00400                 result = self._updateMoFile(name, msgpath, lang, domain)
00401                 if result:
00402                     # Newly created file, the Z3 domain might not exist
00403                     mofile = os.path.join(msgpath, domain + '.mo')
00404                     if queryUtility(ITranslationDomain, name=domain) is None:
00405                         ts_domain = TranslationDomain(domain)
00406                         sm = getGlobalSiteManager()
00407                         sm.registerUtility(ts_domain, ITranslationDomain, name=domain)
00408 
00409                     util = queryUtility(ITranslationDomain, name=domain)
00410                     if util is not None:
00411                         if PTS_LANGUAGES is not None:
00412                             # If we have restricted the available languages,
00413                             # use the speed and not memory optimized version
00414                             cat = GettextMessageCatalog(lang, domain, mofile)
00415                         else:
00416                             # Otherwise optimize for memory footprint
00417                             cat = LazyGettextMessageCatalog(lang, domain, mofile)
00418                         # Add message catalog
00419                         util.addCatalog(cat)
00420 
00421         if not found:
00422             log('nothing found', logging.DEBUG)
00423             return
00424         log('Initialized:', detail = repr(found) + (' from %s\n' % basepath))

Here is the call graph for this function:

Definition at line 171 of file PlacelessTranslationService.py.

00171 
00172     def _registerMessageCatalog(self, catalog):
00173         # dont register broken message catalogs
00174         if isinstance(catalog, BrokenMessageCatalog):
00175             return
00176 
00177         domain = catalog.getDomain()
00178         language = catalog.getLanguage()
00179         catalogRegistry.setdefault((language, domain), []).append(catalog.getIdentifier())
00180         for lang in catalog.getOtherLanguages():
00181             fbcatalogRegistry.setdefault((lang, domain), []).append(catalog.getIdentifier())
00182         self._p_changed = 1

Here is the caller graph for this function:

Definition at line 183 of file PlacelessTranslationService.py.

00183 
00184     def _unregister_inner(self, catalog, clist):
00185         for key, combo in clist.items():
00186             try:
00187                 combo.remove(catalog.getIdentifier())
00188             except ValueError:
00189                 continue
00190             if not combo: # removed the last catalog for a
00191                           # language/domain combination
00192                 del clist[key]

Here is the caller graph for this function:

Definition at line 193 of file PlacelessTranslationService.py.

00193 
00194     def _unregisterMessageCatalog(self, catalog):
00195         self._unregister_inner(catalog, catalogRegistry)
00196         self._unregister_inner(catalog, fbcatalogRegistry)
00197         self._p_changed = 1

Here is the call graph for this function:

Here is the caller graph for this function:

def PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService._updateMoFile (   self,
  name,
  msgpath,
  lang,
  domain 
) [private]
Creates or updates a mo file in the locales folder. Returns True if a
new file was created.

Definition at line 322 of file PlacelessTranslationService.py.

00322 
00323     def _updateMoFile(self, name, msgpath, lang, domain):
00324         """
00325         Creates or updates a mo file in the locales folder. Returns True if a
00326         new file was created.
00327         """
00328         pofile = os.path.normpath(os.path.join(msgpath, name))
00329         mofile = os.path.normpath(os.path.join(msgpath, domain+'.mo'))
00330         create = False
00331         update = False
00332 
00333         try:
00334             po_mtime = os.stat(pofile)[ST_MTIME]
00335         except (IOError, OSError):
00336             po_mtime = 0
00337 
00338         if os.path.exists(mofile):
00339             # Update mo file?
00340             try:
00341                 mo_mtime = os.stat(mofile)[ST_MTIME]
00342             except (IOError, OSError):
00343                 mo_mtime = 0
00344 
00345             if po_mtime > mo_mtime:
00346                 # Update mo file
00347                 update = True
00348             else:
00349                 # Mo file is current
00350                 return
00351         else:
00352             # Create mo file
00353             create = True
00354 
00355         if create or update:
00356             try:
00357                 mo = Msgfmt(pofile, domain).getAsFile()
00358                 fd = open(mofile, 'wb')
00359                 fd.write(mo.read())
00360                 fd.close()
00361 
00362             except (IOError, OSError, PoSyntaxError):
00363                 log('Error while compiling %s' % pofile, logging.WARNING)
00364                 return
00365 
00366             if create:
00367                 return True
00368 
00369         return None

Here is the caller graph for this function:

Definition at line 451 of file PlacelessTranslationService.py.

00451 
00452     def addCatalog(self, catalog):
00453         try:
00454             self._delObject(catalog.id)
00455         except:
00456             pass
00457         if not isinstance(catalog, BrokenMessageCatalog):
00458             lang = catalog.getLanguage()
00459             if not _checkLanguage(lang):
00460                 return
00461         self._setObject(catalog.id, catalog, set_owner=False)
00462         log('adding %s: %s' % (catalog.id, catalog.title))
00463         self._registerMessageCatalog(catalog)

Here is the call graph for this function:

Here is the caller graph for this function:

def PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService.calculatePoId (   self,
  name,
  popath,
  language = None,
  domain = None 
)
Calulate the po id

Definition at line 199 of file PlacelessTranslationService.py.

00199 
00200     def calculatePoId(self, name, popath, language=None, domain=None):
00201         """Calulate the po id
00202         """
00203         # instance, software and global catalog path for i18n and locales
00204         iPath       = os.path.join(Globals.INSTANCE_HOME, 'Products') + os.sep
00205         sPath       = os.path.join(Globals.SOFTWARE_HOME, 'Products') + os.sep
00206         gci18nNPath = os.path.join(Globals.INSTANCE_HOME, 'i18n')
00207         gcLocPath   = os.path.join(Globals.INSTANCE_HOME, 'locales')
00208 
00209         # a global catalog is
00210         isGlobalCatalog = False
00211 
00212         # remove [isg]Path from the popath
00213         if popath.startswith(iPath):
00214             path = popath[len(iPath):]
00215         elif popath.startswith(sPath):
00216             path = popath[len(sPath):]
00217         elif popath.startswith(gci18nNPath):
00218             path = popath[len(gci18nNPath):]
00219             isGlobalCatalog = True
00220         elif popath.startswith(gcLocPath):
00221             path = popath[len(gcLocPath):]
00222             isGlobalCatalog = True
00223         else:
00224             # po file is located at a strange place calculate the name using
00225             # the position of the i18n/locales directory
00226             p = popath.split(os.sep)
00227             try:
00228                 idx = p.index('i18n')
00229             except ValueError:
00230                 try:
00231                     idx = p.index('locales')
00232                 except ValueError:
00233                     raise OSError('Invalid po path %s for %s. That should not happen' % (popath, name))
00234             path = os.path.join(p[idx-1],p[idx])
00235 
00236         # the po file name is GlobalCatalogs-$name or MyProducts.i18n-$name
00237         # or MyProducts.locales-$name
00238         if not isGlobalCatalog:
00239             p = path.split(os.sep)
00240             pre = '.'.join(p[:2])
00241         else:
00242             pre = 'GlobalCatalogs'
00243 
00244         if language and domain:
00245             return "%s-%s-%s.po" % (pre, language, domain)
00246         else:
00247             return '%s-%s' % (pre, name)

Here is the caller graph for this function:

Definition at line 466 of file PlacelessTranslationService.py.

00466 
00467     def getCatalogsForTranslation(self, request, domain, target_language=None):
00468         if target_language is None:
00469             target_language = self.negotiate_language(request, domain)
00470 
00471         # get the catalogs for translations
00472         catalog_names = catalogRegistry.get((target_language, domain), ()) or \
00473                         fbcatalogRegistry.get((target_language, domain), ())
00474         catalog_names = list(catalog_names)
00475 
00476         # get fallback catalogs
00477         for language in self._fallbacks:
00478             fallback_catalog_names = catalogRegistry.get((language, domain),  ())
00479             if fallback_catalog_names:
00480                 for fallback_catalog_name in fallback_catalog_names:
00481                     if fallback_catalog_name not in catalog_names:
00482                         catalog_names.append(fallback_catalog_name)
00483 
00484         # move global catalogs to the beginning to allow overwriting
00485         # message ids by placing a po file in INSTANCE_HOME/i18n
00486         # use pos to keep the sort order
00487         pos=0
00488         for i in range(len(catalog_names)):
00489             catalog_name = catalog_names[i]
00490             if catalog_name.startswith('GlobalCatalogs-'):
00491                 del catalog_names[i]
00492                 catalog_names.insert(pos, catalog_name)
00493                 pos+=1
00494 
00495         # test for right to left language
00496         if not request.has_key(PTS_IS_RTL):
00497             request.set(PTS_IS_RTL, False)
00498         for name in catalog_names:
00499             if rtlRegistry.get(name):
00500                 request.set(PTS_IS_RTL, True)
00501                 break
00502 
00503         return [translationRegistry[name] for name in catalog_names]

Here is the call graph for this function:

Here is the caller graph for this function:

return a domain instance

Definition at line 606 of file PlacelessTranslationService.py.

00606 
00607     def getDomain(self, domain):
00608         """
00609         return a domain instance
00610         """
00611         return Domain(domain, self)

Definition at line 511 of file PlacelessTranslationService.py.

00511 
00512     def getLanguageName(self, code):
00513         for (ccode, cdomain), cnames in catalogRegistry.items():
00514             if ccode == code:
00515                 for cname in cnames:
00516                     cat = self._getOb(cname)
00517                     if cat.name:
00518                         return cat.name

Here is the call graph for this function:

Get available languages

Definition at line 520 of file PlacelessTranslationService.py.

00520 
00521     def getLanguages(self, domain=None):
00522         """
00523         Get available languages
00524         """
00525         if domain is None:
00526             # no domain, so user wants 'em all
00527             langs = catalogRegistry.keys()
00528             # uniquify
00529             d = {}
00530             for l in langs:
00531                 d[l[0]] = 1
00532             l = d.keys()
00533         else:
00534             l = [k[0] for k in catalogRegistry.keys() if k[1] == domain]
00535         l.sort()
00536         return l

Insert the data passed from mapping into the text

Definition at line 613 of file PlacelessTranslationService.py.

00613 
00614     def interpolate(self, text, mapping):
00615         """
00616         Insert the data passed from mapping into the text
00617         """
00618         # If the mapping does not exist or is empty, make a
00619         # "raw translation" without interpolation.
00620         if not mapping:
00621             return text
00622 
00623         # Find all the spots we want to substitute
00624         to_replace = _interp_regex.findall(text)
00625 
00626         # Now substitute with the variables in mapping
00627         for string in to_replace:
00628             var = _get_var_regex.findall(string)[0]
00629             value = mapping.get(var, None)
00630             if value is None:
00631                 value = string
00632             try:
00633                 if not isinstance(value, basestring):
00634                     value = str(value)
00635                 if isinstance(text, unicode):
00636                     value = u'%s' % value
00637                 text = text.replace(string, value)
00638             except UnicodeDecodeError, msg:
00639                 log('Decoding problem in: %s %s' % (text, msg), logging.WARNING)
00640         return text

Here is the caller graph for this function:

get RTL settings

Definition at line 541 of file PlacelessTranslationService.py.

00541 
00542     def isRTL(self, context, domain):
00543         """get RTL settings
00544         """
00545         request = getattr(context, 'REQUEST', context)
00546         pts_is_rtl = request.get(PTS_IS_RTL, None)
00547         if pts_is_rtl is None:
00548             # call getCatalogsForTranslation to initialize the negotiator
00549             self.getCatalogsForTranslation(request, domain)
00550         return request.get(PTS_IS_RTL, False)

Here is the call graph for this function:

Wrap Folder's manage_main to render international characters

Definition at line 642 of file PlacelessTranslationService.py.

00642 
00643     def manage_main(self, REQUEST, *a, **kw):
00644         """
00645         Wrap Folder's manage_main to render international characters
00646         """
00647         # ugh, API cruft
00648         if REQUEST is self and a:
00649             REQUEST = a[0]
00650             a = a[1:]
00651         # wrap the special dtml method Folder.manage_main into a valid
00652         # acquisition context. Required for Zope 2.8+.
00653         try:
00654             r = Folder.manage_main(self, self, REQUEST, *a, **kw)
00655         except AttributeError:
00656             manage_main = ImplicitAcquisitionWrapper(Folder.manage_main, self)
00657             r = manage_main(self, self, REQUEST, *a, **kw)
00658         if isinstance(r, unicode):
00659             r = r.encode('utf-8')
00660         REQUEST.RESPONSE.setHeader('Content-type', 'text/html; charset=utf-8')
00661         return r
00662 
00663 InitializeClass(PlacelessTranslationService)

Here is the caller graph for this function:

wrap manage_renameObject to deal with registration

Definition at line 426 of file PlacelessTranslationService.py.

00426 
00427     def manage_renameObject(self, id, new_id, REQUEST=None):
00428         """
00429         wrap manage_renameObject to deal with registration
00430         """
00431         catalog = self._getOb(id)
00432         self._unregisterMessageCatalog(catalog)
00433         Folder.manage_renameObject(self, id, new_id, REQUEST=None)
00434         self._registerMessageCatalog(catalog)

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 597 of file PlacelessTranslationService.py.

00597 
00598     def negotiate_language(self, request, domain):
00599         langs = [m[0] for m in catalogRegistry.keys() if m[1] == domain] + \
00600                 [m[0] for m in fbcatalogRegistry.keys() if m[1] == domain]
00601         for fallback in self._fallbacks:
00602             if fallback not in langs:
00603                 langs.append(fallback)
00604         return negotiator.negotiate(langs, request, 'language')

Here is the caller graph for this function:

Definition at line 441 of file PlacelessTranslationService.py.

00441 
00442     def reloadCatalog(self, catalog):
00443         # trigger an exception if we don't know anything about it
00444         id=catalog.id
00445         self._getOb(id)
00446         self._unregisterMessageCatalog(catalog)
00447         catalog.reload()
00448         catalog=self._getOb(id)
00449         self._registerMessageCatalog(catalog)

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 505 of file PlacelessTranslationService.py.

00505 
00506     def setLanguageFallbacks(self, fallbacks=None):
00507         if fallbacks is None:
00508             fallbacks = LANGUAGE_FALLBACKS
00509         self._fallbacks = fallbacks

def PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService.translate (   self,
  domain,
  msgid,
  mapping = None,
  context = None,
  target_language = None,
  default = None 
)
Translate a message using Unicode.

Definition at line 565 of file PlacelessTranslationService.py.

00565 
00566                   target_language=None, default=None):
00567         """
00568         Translate a message using Unicode.
00569         """
00570         if not msgid:
00571             # refuse to translate an empty msgid
00572             return default
00573 
00574         # ZPT passes the object as context.  That's wrong according to spec.
00575         if not IBrowserRequest.providedBy(context):
00576             context = aq_acquire(context, 'REQUEST')
00577         text = msgid
00578 
00579         catalogs = self.getCatalogsForTranslation(context, domain, target_language)
00580         for catalog in catalogs:
00581             try:
00582                 text = getMessage(catalog, msgid, default)
00583             except KeyError:
00584                 # it's not in this catalog, try the next one
00585                 continue
00586             # found!
00587             break
00588         else:
00589             # Did the fallback fail? Sigh, use the default if it is not None.
00590             if default is not None:
00591                 text = default
00592 
00593         # Now we need to do the interpolation
00594         return self.interpolate(text, mapping)

Here is the call graph for this function:

Here is the caller graph for this function:

def PlacelessTranslationService.PlacelessTranslationService.PlacelessTranslationService.utranslate (   self,
  domain,
  msgid,
  mapping = None,
  context = None,
  target_language = None,
  default = None 
)
translate() using Unicode.

Definition at line 556 of file PlacelessTranslationService.py.

00556 
00557                   target_language=None, default=None):
00558         """
00559         translate() using Unicode.
00560         """
00561         return self.translate(domain, msgid, mapping, context,
00562                   target_language, default)

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

Definition at line 152 of file PlacelessTranslationService.py.

Definition at line 161 of file PlacelessTranslationService.py.

Definition at line 169 of file PlacelessTranslationService.py.

Definition at line 158 of file PlacelessTranslationService.py.

Definition at line 181 of file PlacelessTranslationService.py.

Definition at line 153 of file PlacelessTranslationService.py.

Definition at line 145 of file PlacelessTranslationService.py.

Definition at line 144 of file PlacelessTranslationService.py.

Definition at line 155 of file PlacelessTranslationService.py.


The documentation for this class was generated from the following file: