#!/usr/bin/env python"""This script is just a thin wrapper around ``msgcat`` and ``msgfmt``to generate ``.mo`` files"""importsysimportosimportos.pathasospimportshutilfromtempfileimportmktempfromglobimportglobfrommx.DateTimeimportnowfromlogilab.common.fileutilsimportensure_fs_modefromlogilab.common.shellutilsimportfind,rmfromyamsimportBASE_TYPESfromcubicwebimportCW_SOFTWARE_ROOT# from cubicweb.__pkginfo__ import version as cubicwebversioncubicwebversion='2.48.2'DEFAULT_POT_HEAD=r'''# LAX application po filemsgid ""msgstr """Project-Id-Version: cubicweb %s\n""PO-Revision-Date: 2008-03-28 18:14+0100\n""Last-Translator: Logilab Team <contact@logilab.fr>\n""Language-Team: fr <contact@logilab.fr>\n""MIME-Version: 1.0\n""Content-Type: text/plain; charset=UTF-8\n""Content-Transfer-Encoding: 8bit\n""Generated-By: cubicweb-devtools\n""Plural-Forms: nplurals=2; plural=(n > 1);\n"'''%cubicwebversionSTDLIB_ERTYPES=BASE_TYPES|set(('EUser','EProperty','Card','identity','for_user'))defcreate_dir(directory):"""create a directory if it doesn't exist yet"""try:os.makedirs(directory)print'created directory',directoryexceptOSError,ex:importerrnoifex.errno!=errno.EEXIST:raiseprint'directory %s already exists'%directorydefexecute(cmd):"""display the command, execute it and raise an Exception if returned status != 0 """printcmd.replace(os.getcwd()+os.sep,'')status=os.system(cmd)ifstatus!=0:raiseException()defadd_msg(w,msgid):"""write an empty pot msgid definition"""ifisinstance(msgid,unicode):msgid=msgid.encode('utf-8')msgid=msgid.replace('"',r'\"').splitlines()iflen(msgid)>1:w('msgid ""\n')forlineinmsgid:w('"%s"'%line.replace('"',r'\"'))else:w('msgid "%s"\n'%msgid[0])w('msgstr ""\n\n')defgenerate_schema_pot(w,vreg,tmpldir):"""generate a pot file with schema specific i18n messages notice that relation definitions description and static vocabulary should be marked using '_' and extracted using xgettext """cube=tmpldirandosp.split(tmpldir)[-1]config=vreg.configvreg.register_objects(config.vregistry_path())w(DEFAULT_POT_HEAD)_generate_schema_pot(w,vreg,vreg.schema,libschema=None,# no libschema for nowcube=cube)def_generate_schema_pot(w,vreg,schema,libschema=None,cube=None):w('# schema pot file, generated on %s\n'%now().strftime('%Y-%m-%d %H:%M:%S'))w('# \n')w('# singular and plural forms for each entity type\n')w('\n')# XXX hard-coded list of stdlib's entity schemaslibschema=libschemaorSTDLIB_ERTYPESentities=[eforeinschema.entities()ifnoteinlibschema]done=set()foreschemainsorted(entities):etype=eschema.typeadd_msg(w,etype)add_msg(w,'%s_plural'%etype)ifnoteschema.is_final():add_msg(w,'This %s'%etype)add_msg(w,'New %s'%etype)add_msg(w,'add a %s'%etype)add_msg(w,'remove this %s'%etype)ifeschema.descriptionandnoteschema.descriptionindone:done.add(eschema.description)add_msg(w,eschema.description)w('# subject and object forms for each relation type\n')w('# (no object form for final relation types)\n')w('\n')iflibschemaisnotNone:relations=[rforrinschema.relations()ifnotrinlibschema]else:relations=schema.relations()forrschemainsorted(set(relations)):rtype=rschema.typeadd_msg(w,rtype)ifnot(schema.rschema(rtype).is_final()orrschema.symetric):add_msg(w,'%s_object'%rtype)ifrschema.descriptionandrschema.descriptionnotindone:done.add(rschema.description)add_msg(w,rschema.description)w('# add related box generated message\n')w('\n')foreschemainschema.entities():ifeschema.is_final():continueentity=vreg.etype_class(eschema)(None,None)forx,rschemasin(('subject',eschema.subject_relations()),('object',eschema.object_relations())):forrschemainrschemas:ifrschema.is_final():continueforteschemainrschema.targets(eschema,x):ifdefined_in_library(libschema,eschema,rschema,teschema,x):continueifentity.relation_mode(rschema.type,teschema.type,x)=='create':ifx=='subject':label='add %s%s%s%s'%(eschema,rschema,teschema,x)label2="creating %s (%s%%(linkto)s %s%s)"%(teschema,eschema,rschema,teschema)else:label='add %s%s%s%s'%(teschema,rschema,eschema,x)label2="creating %s (%s%s%s%%(linkto)s)"%(teschema,teschema,rschema,eschema)add_msg(w,label)add_msg(w,label2)cube=(cubeor'cubicweb')+'.'done=set()forreg,objdictinvreg.items():forobjectsinobjdict.values():forobjinobjects:objid='%s_%s'%(reg,obj.id)ifobjidindone:continueifobj.__module__.startswith(cube)andobj.property_defs:add_msg(w,'%s_description'%objid)add_msg(w,objid)done.add(objid)defdefined_in_library(libschema,etype,rtype,tetype,x):"""return true if the given relation definition exists in cubicweb's library"""iflibschemaisNone:returnFalseifx=='subject':subjtype,objtype=etype,tetypeelse:subjtype,objtype=tetype,etypetry:returnlibschema.rschema(rtype).has_rdef(subjtype,objtype)except(KeyError,AttributeError):# if libschema is a simple list of entity types (lax specific)# or if the relation could not be foundreturnFalse# XXX check if this is a pure duplication of the original# `cubicweb.common.i18n` functiondefcompile_i18n_catalogs(sourcedirs,destdir,langs):"""generate .mo files for a set of languages into the `destdir` i18n directory """print'compiling %s catalogs...'%destdirerrors=[]forlanginlangs:langdir=osp.join(destdir,lang,'LC_MESSAGES')ifnotosp.exists(langdir):create_dir(langdir)pofiles=[osp.join(path,'%s.po'%lang)forpathinsourcedirs]pofiles=[pofforpofinpofilesifosp.exists(pof)]mergedpo=osp.join(destdir,'%s_merged.po'%lang)try:# merge application messages' catalog with the stdlib's oneexecute('msgcat --use-first --sort-output --strict %s > %s'%(' '.join(pofiles),mergedpo))# make sure the .mo file is writeable and compile with *msgfmt*applmo=osp.join(destdir,lang,'LC_MESSAGES','cubicweb.mo')try:ensure_fs_mode(applmo)exceptOSError:pass# suppose not osp.existsexecute('msgfmt %s -o %s'%(mergedpo,applmo))exceptException,ex:errors.append('while handling language %s: %s'%(lang,ex))try:# clean everythingos.unlink(mergedpo)exceptException:continuereturnerrorsdefupdate_cubes_catalog(vreg,appdirectory,langs):toedit=[]tmpl=osp.basename(osp.normpath(appdirectory))tempdir=mktemp()os.mkdir(tempdir)print'*'*72print'updating %s cube...'%tmplos.chdir(appdirectory)potfiles=[]ifosp.exists(osp.join('i18n','entities.pot')):potfiles=potfiles.append(osp.join('i18n','entities.pot'))print'******** extract schema messages'schemapot=osp.join(tempdir,'schema.pot')potfiles.append(schemapot)# XXXgenerate_schema_pot(open(schemapot,'w').write,vreg,appdirectory)print'******** extract Javascript messages'jsfiles=find('.','.js')ifjsfiles:tmppotfile=osp.join(tempdir,'js.pot')execute('xgettext --no-location --omit-header -k_ -L java --from-code=utf-8 -o %s%s'%(tmppotfile,' '.join(jsfiles)))# no pot file created if there are no string to translateifosp.exists(tmppotfile):potfiles.append(tmppotfile)print'******** create cube specific catalog'tmppotfile=osp.join(tempdir,'generated.pot')execute('xgettext --no-location --omit-header -k_ -o %s%s'%(tmppotfile,' '.join(glob('*.py'))))ifosp.exists(tmppotfile):# doesn't exists of no translation string foundpotfiles.append(tmppotfile)potfile=osp.join(tempdir,'cube.pot')print'******** merging .pot files'execute('msgcat %s > %s'%(' '.join(potfiles),potfile))print'******** merging main pot file with existing translations'os.chdir('i18n')forlanginlangs:print'****',langtmplpo='%s.po'%langifnotosp.exists(tmplpo):shutil.copy(potfile,tmplpo)else:execute('msgmerge -N -s %s%s > %snew'%(tmplpo,potfile,tmplpo))ensure_fs_mode(tmplpo)shutil.move('%snew'%tmplpo,tmplpo)toedit.append(osp.abspath(tmplpo))# cleanuprm(tempdir)# instructions pour la suiteprint'*'*72print'you can now edit the following files:'print'* '+'\n* '.join(toedit)defgetlangs(i18ndir):return[fname[:-3]forfnameinos.listdir(i18ndir)iffname.endswith('.po')]defget_i18n_directory(appdirectory):ifnotosp.isdir(appdirectory):print'%s is not an application directory'%appdirectorysys.exit(2)i18ndir=osp.join(appdirectory,'i18n')ifnotosp.isdir(i18ndir):print'%s is not an application directory ' \'(i18n subdirectory missing)'%appdirectorysys.exit(2)returni18ndir