#!/usr/bin/python import sys, gettext, re from optparse import OptionParser from path import path from ZopePageTemplates import PageTemplate, PTRuntimeError from ZopePageTemplates.GlobalTranslationService import setGlobalTranslationService #msgfmt -o locale/it/LC_MESSAGES/wp-frontend.mo locale/it/LC_MESSAGES/wp-frontend class TranslationService: def __init__(self, languages, domain='wp-frontend', localedir='locale', codeset='UTF-8'): self.languages = languages self.domain = domain self.localedir = localedir self.codeset = codeset self._active = False translators = {} for lang in languages: if not lang in translators: try: translators[lang] = gettext.translation(domain, localedir, [lang,], codeset=codeset) except IOError, e: raise Usage, "Error initializing TranslationService for lang '%s': %s" % (lang, e) self._translators = translators def setLang(self, lang): if not lang in self._translators: raise SystemError, "%s knows nothing about %s" % (self, lang) self._translators[lang].install() self._active = True def translate(self, domain, msgid, mapping=None, context=None, target_language=None): if not self._active: raise SystemError, "%s is inactive, cannot translate %s" % (self, msgid) return _(msgid) class PageTemplate(PageTemplate): is_html = True def __call__(self, context={}, *args): if not context.has_key('args'): context['args'] = args return self.pt_render(extra_context=context) class Usage(Exception): """exception used by main() below""" def __init__(self, msg): self.msg = msg def main(): """main method used to run this script from the command line""" usage = "usage: themes-converter.py [options] [LANG.CHARSET] [LANG.CHARSET] [...]" parser = OptionParser(usage) parser.add_option("-m", "--map", type="string", dest="lang_map", help="language map name, listing one LANG.CHARSET combination on each line") parser.add_option("-g", "--gettext-locale-dir", type="string", dest="localedir", default="locale", help="dir containing the gettext locale files, defaults to locale/ in current folder") parser.add_option("-s", "--source-dir", type="string", dest="sourcedir", default="master-themes", help="dir containing the master themes files, defaults to master-themes/ in current folder") parser.add_option("-t", "--themes-output", type="string", dest="destdir", default="themes", help="dir where processed templates are written, defaults to themes/ in current folder") parser.add_option("-l", "--lang-output", type="string", dest="messages_dir", default="lang", help="dir where processed language files are written, defaults to lang/ in current folder") parser.add_option("-f", "--force", action="store_true", dest="force", help="overwrite existing files") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="output status messages on stderr") parser.add_option("-o", "--old-dir", action="store_true", dest="old_dir", help="Old directory names for localized themes: e.g. en_US") (opts, args) = parser.parse_args() try: ######################################################################## # check and normalize options and args ######################################################################## # determine input dir sourcedir = path(opts.sourcedir) if not sourcedir.exists(): raise Usage, "master themes folder '%s' missing" % sourcedir plugin_dir = sourcedir / '_plugins' if not plugin_dir.exists(): raise Usage, "master themes folder '%s' is missing plugins" % sourcedir common_dir = sourcedir / '_common' if not common_dir.exists(): raise Usage, "master themes folder '%s' is missing common files" % sourcedir # determine output dir destdir = path(opts.destdir) if destdir.exists() and not opts.force: raise Usage, "destination theme folder '%s' exists, use --force to overwrite" % destdir # determine output dir for language files messages_dir = path(opts.messages_dir) if messages_dir.exists() and not opts.force: raise Usage, "destination lang folder '%s' exists, use --force to overwrite" % destdir # determine output langs and charsets if opts.lang_map: try: lang_codes = file(opts.lang_map).read().split('\n') except IOError, e: raise Usage, "missing map file '%s'" % opts.lang_map if opts.verbose: print >> sys.stderr, "using lang.charset combinations from map file '%s'" % opts.lang_map elif len(args) > 0: if opts.verbose: print >> sys.stderr, "using lang.charset combinations from command line" lang_codes = args else: raise Usage, "no lang.charset combinations and no langmap specified" lang_codes = [l.strip() for l in lang_codes if l.strip()] if len(lang_codes) == 0: raise Usage, "no valid lang.charset combinations found" if opts.verbose: print >> sys.stderr, "lang.charset combinations: '%s'" % "', '".join(lang_codes) # sort out languages from charset, and remove country localization info output_langs = {} output_locs = {} for lang_code in lang_codes: try: langloc, charset = lang_code.split('.') except ValueError: raise Usage, "cannot split '%s' into language and charset" % lang_code lang = langloc.split('_')[0] if not lang in output_langs: output_langs[lang] = {} elif charset in output_langs[lang]: raise Usage, "duplicate charset '%s' for language '%s'" % (charset, lang) output_langs[lang][charset] = None output_locs[lang] = langloc # initialize and set up translation service translation_service = TranslationService(output_langs.keys()) setGlobalTranslationService(translation_service) # look for master themes master_themes = {} for d in sourcedir.dirs(): if d.name.startswith('_') or d.name.startswith('.'): continue d_t = d / 'theme' if not d_t.exists(): raise Usage, "no 'theme' folder in '%s'" % d d_d = destdir / d.name d_p = d_t / 'plugins' d_plugins = {} if d_p.exists(): for p in d_p.files(): d_plugins[p.name] = p master_themes[d] = [d_d, d_t, d_plugins] if len(master_themes) == 0: raise Usage, "no master themes to process" if opts.verbose: print >> sys.stderr, "%s master themes to process: '%s'" % (len(master_themes), "', '".join([d.name for d in master_themes])) # look for message file messages = [] messages_file = sourcedir / '_messages/lang.xml' if messages_file.exists(): r = re.compile(r'([^<]+)') messages = r.findall(messages_file.text()) messages.sort() ######################################################################## # do the actual conversion work ######################################################################## # create the destination folders if not destdir.exists(): if opts.verbose: print >> sys.stderr, "creating destination theme folder '%s'" % destdir destdir.mkdir() if not messages_dir.exists(): if opts.verbose: print >> sys.stderr, "creating destination lang folder '%s'" % destdir messages_dir.mkdir() # gather common files common_files = [] for cf in common_dir.files(): common_files.append(cf) # process static theme files (images, styles, etc.) for theme_dir in master_themes.keys(): theme_outdir, theme_dir_t, theme_plugins = master_themes[theme_dir] if not theme_outdir.exists(): if opts.verbose: print >> sys.stderr, "creating theme folder '%s'" % theme_outdir theme_outdir.mkdir() for f in theme_dir.files(): if opts.verbose: print >> sys.stderr, "copying theme file '%s' in '%s'" % (f.name, theme_outdir) f.copy(theme_outdir / f.name) for d in theme_dir.dirs(): if d.name == 'theme' or d.name == '.svn': continue #d.copytree(theme_outdir / d.name) dd = theme_outdir / d.name if not dd.exists(): if opts.verbose: print >> sys.stderr, "creating theme subfolder '%s'" % dd dd.mkdir() for f in d.files(): if opts.verbose: print >> sys.stderr, "copying theme file '%s' in subfolder '%s'" % (f.name, dd) f.copy(dd / f.name) # set up PageTemplate instance pt = PageTemplate() # process each language # (we loop twice on themes so as to store plugins in a single language each time) for lang in output_langs.keys(): # set up language in translation service translation_service.setLang(lang) # translate messages if len(messages) > 0: messages_translated = [] for m in messages: messages_translated.append("\t'%s' => '%s'" % (m[0], _(m[1]).replace("'", '''))) messages_translated = "" % ',\n'.join(messages_translated) messages_translated = messages_translated.decode('UTF-8') for charset in output_langs[lang].keys(): messages_outfile = '%s_%s.php' % (lang, charset) messages_outfile = messages_dir / messages_outfile messages_outfile.write_text(messages_translated, charset); if opts.verbose: print >> sys.stderr, "translating messages file '%s'" % messages_outfile # translate plugins plugins = {} for plugin in plugin_dir.files(): if plugin.name.endswith('.svn-work') or plugin.name.endswith('.svn-base'): continue plugins[plugin.name] = translate_file(pt, plugin) # translate themes for theme_dir in master_themes.keys(): theme_outdir, theme_dir_t, theme_plugins = master_themes[theme_dir] # create output directories for each lang-charset charset_dirs = {} for charset in output_langs[lang].keys(): if opts.old_dir: charset_dir_name = '%s.%s' % (output_locs[lang], charset) else: charset_dir_name = '%s.%s' % (lang, charset) charset_dirs[charset] = theme_outdir / charset_dir_name if not charset_dirs[charset].exists(): if opts.verbose: print >> sys.stderr, "creating localized theme folder '%s'" % charset_dirs[charset] charset_dirs[charset].mkdir() # translate files for f in theme_dir_t.files(): out = translate_file(pt, f) for charset, charset_dir in charset_dirs.items(): if f.name == 'theme.xml': out_tokens = out.split('') if len(out_tokens) != 2: raise Usage, "cannot split '%s' into header and footer" % f for i, token in enumerate(('header.xml', 'footer.xml')): out_f = charset_dir / token out_f.write_text(out_tokens[i], charset) if opts.verbose: print >> sys.stderr, "translating file '%s'" % out_f else: out_f = charset_dir / f.name out_f.write_text(out, charset) if opts.verbose: print >> sys.stderr, "translating file '%s'" % out_f # write common files for charset, charset_dir in charset_dirs.items(): for cf in common_files: if opts.verbose: print >> sys.stderr, "copying common file '%s' in '%s'" % (cf, charset_dir) cf.copy(charset_dir / cf.name) # write plugins for charset, charset_dir in charset_dirs.items(): charset_dirs[charset] = charset_dir / 'plugins' if not charset_dirs[charset].exists(): if opts.verbose: print >> sys.stderr, "creating localized theme plugins folder '%s'" % charset_dirs[charset] charset_dirs[charset].mkdir() for p_name, p_out in plugins.items(): if p_name in theme_plugins: p_out = translate_file(pt, theme_plugins[p_name]) for charset, charset_dir in charset_dirs.items(): out_f = charset_dir / p_name out_f.write_text(p_out, charset) if opts.verbose: print >> sys.stderr, "translating plugin '%s'" % out_f except Usage, e: print >>sys.stderr, e.msg print >>sys.stderr, "for help use --help" return 2 def translate_file(pt, f, verbose=False): pt.id = str(f) pt.write(f.open().read()) try: return pt().decode('utf-8') except KeyError, e: raise Usage, "key %s not found in template '%s'" % (e, f) except PTRuntimeError, e: raise Usage, "%s\n%s" % (str(e), ': '.join(pt._v_errors)) if __name__ == '__main__': sys.exit(main())