Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python3
- # Note: This script has to be in the same directory with sync_po.py (.PO synchronizer).
- """
- Made for combining Django's JavaScript Catalog (translation) implementation with AngularJS templates.
- Code (AngularJS):
- ---------------
- .filter('translate', function() { return function(input, params) { return params != null ? interpolate(gettext(input), params, params.constructor == Object) : gettext(input) } })
- .directive('translate', function($compile) {
- return {
- compile: function(element) {
- var html = element.html().split('<br plural="">');
- return function (scope, element, attrs) {
- function c(h){
- element.html(h);
- $compile(element.contents())(scope);
- }
- if (attrs.translate != '') {
- scope.$watch(function () { return scope.$eval(attrs.translate) }, function (val) {
- var p = html[2];
- html[2] = ngettext(html[0], html[1], attrs.add !== undefined ? val + attrs.add : attrs.subtract !== undefined ? val - attrs.subtract : val);
- if (p != html[2]) c(html[2]);
- });
- } else c(gettext(html[0]));
- }
- }
- }
- })
- ---------------
- Usage in templates and equivalent commands in JS:
- {{ "Save" | translate }}
- -> gettext("Save")
- {{ "May" | translate : null : 'month name' }}
- -> pgettext('month name', "May")
- {{ "Comments (%d)" | translate : [obj.comments.length] }}
- -> interpolate(gettext("Comments (%d)"), [obj.comments.length])
- {{ "User(s) (%(user_count)d) are found %(distance)s away" | translate : {user_count: users.length, distance: obj.distance.value + obj.distance.unit} }}
- -> interpolate(gettext("User(s) (%(user_count)d) are found %(distance)s away"), {user_count: users.length, distance: obj.distance.value + obj.distance.unit}, true)
- <a href="/friends/" translate="friends.length">You have {{ friends.length }} friend.<br plural>You have {{ friends.length }} friends.</a>
- -> ngettext("You have {{ friends.length }} friend.", "You have {{ friends.length }} friends.", friends.length)
- """
- EXT = 'html'
- TAG = 'translate'
- MSGSTR_NUM = 2
- from re import compile
- from html.parser import HTMLParser
- prog = [compile(r'("|\')(.*?)\1'), compile(r'%(\(.*?\))?(s|d)')]
- class TemplateParser(HTMLParser):
- recording = 0
- f = ['', '']
- tmp = [None, '']
- def __init__(self, *args, **kwargs):
- self.tag = kwargs.pop('tag')
- self.msgstr_num = kwargs.pop('msgstr_num')
- self.prog = compile(r'\{{2} *([^\s|}]+(?: +[^\s|}]+)*) *\| *'+self.tag+r'(?: *:(?:[^:]*): *(["\'])([^"\'}]*(?:(?!\2)["\'][^"\'}]*)*)\2)?[^}]*\}{2}')
- super().__init__()
- def genc(self):
- return self.f[0][0]+':'+str(self.getpos()[0])
- def addnew(self, s=''):
- return {'#': {':': [self.genc()]}, 'msgid': [s]}
- def appendpos(self, p, s):
- for i in s:
- if p in i:
- return
- for i, _ in enumerate(s):
- if ' '+self.f[0][0] in s[i]:
- s[i] += ' '+p
- return
- s.append(p)
- def repl(self, s):
- if self.f[0][1]:
- s = s.replace('{% verbatim %}', '').replace('{% endverbatim %}', '')
- return s.replace('"', '\\"').replace('\\', '\\\\')
- def extr_filter(self, t):
- for e in self.prog.findall(t):
- for s in prog[0].findall(e[0]):
- r = self.repl(s[1])
- for i in self.data:
- if not i.get('msgid_plural', None) and ''.join(i['msgid']) == r and ''.join(i.get('msgctxt', [])) == e[2]:
- self.appendpos(self.genc(), i['#'][':'])
- return
- self.data.append(self.addnew(r))
- if prog[1].search(s[1]):
- self.data[-1]['#'][','] = ['javascript-format']
- if e[2] != '':
- self.data[-1]['msgctxt'] = [e[2]]
- self.data[-1]['msgstr'] = ['']
- def handle_starttag(self, tag, attrs):
- s = self.get_starttag_text()
- self.extr_filter(s)
- if self.recording > 0:
- if s == '<br plural>':
- self.f[1] = 'msgid_plural'
- self.tmp[0][self.f[1]] = ['']
- return
- elif tag not in ('br', 'img', 'input', 'hr'):
- self.recording += 1
- self.tmp[0][self.f[1]][0] += self.repl(s)
- return
- for name, _ in attrs:
- if name == self.tag:
- self.tmp[0] = self.addnew()
- self.f[1] = 'msgid'
- self.recording = 1
- break
- def handle_data(self, data):
- self.tmp[1] += data
- if self.recording > 0:
- self.tmp[0][self.f[1]][0] += self.repl(data)
- def handle_endtag(self, tag):
- if self.recording > 0:
- self.recording -= 1
- if self.recording == 0:
- for i in self.data:
- if ''.join(i['msgid']) == ''.join(self.tmp[0]['msgid']) and ''.join(i.get('msgid_plural', [])) == ''.join(self.tmp[0].get('msgid_plural', [])):
- self.appendpos(self.tmp[0]['#'][':'][0], i['#'][':'])
- self.tmp[0] = None
- return
- if 'msgid_plural' in self.tmp[0]:
- self.tmp[0]['msgstr'] = {}
- for i in range(0, self.msgstr_num):
- self.tmp[0]['msgstr'][str(i)] = ['']
- else:
- self.tmp[0]['msgstr'] = ['']
- self.data.append(self.tmp[0].copy())
- self.tmp[0] = None
- else:
- self.tmp[0][self.f[1]][0] += '</'+tag+'>'
- self.extr_filter(self.tmp[1])
- self.tmp[1] = ''
- def handle_entityref(self, name):
- self.tmp[1] += '&'+name+';'
- if self.recording > 0:
- self.tmp[0][self.f[1]][0] += '&'+name+';'
- def main(target_dir, output_file, source_file=None, overwrite=False, tag=TAG, ext=EXT, msgstr_num=MSGSTR_NUM):
- import os
- import sync_po
- parser = TemplateParser(tag=tag, msgstr_num=msgstr_num)
- if not overwrite and os.path.exists(output_file):
- with open(output_file, 'r') as f:
- parser.data = sync_po.extract_strings(f.readlines())
- else:
- parser.data = []
- ext = '.'+ext
- for subdir, _, files in os.walk(target_dir):
- for file in files:
- filepath = subdir + os.sep + file
- if filepath.endswith(ext):
- with open(filepath, 'r') as f:
- parser.f[0] = (filepath if filepath[:2] != './' else filepath[2:], os.sep+'static'+os.sep not in filepath)
- parser.feed(f.read())
- parser.reset()
- if source_file and not os.path.samefile(output_file, source_file.name):
- with source_file as f:
- sync_po.main(sync_po.extract_strings(f.readlines()), parser.data)
- with open(output_file, 'w') as f:
- sync_po.output_strings(parser.data, f)
- if __name__ == '__main__':
- import argparse
- parser = argparse.ArgumentParser(description="Generates .PO file from AngularJS templates (optionally with translations from other .PO file), or updates existing. By MikiSoft")
- parser.add_argument('target_dir', type=str)
- parser.add_argument('output_file', type=str)
- parser.add_argument('-s', dest='source_file', metavar="FILE", type=argparse.FileType('r'), help="source file")
- parser.add_argument('-o', dest='overwrite', action='store_true', help="don't update, overwrite output file if exists")
- parser.add_argument('-tag', type=str, default=TAG, help="default: "+TAG)
- parser.add_argument('-ext', type=str, default=EXT, help="default: "+EXT)
- parser.add_argument('-msgstr_num', metavar="N", type=int, default=MSGSTR_NUM, help="default: "+str(MSGSTR_NUM))
- args = parser.parse_args()
- main(args.target_dir, args.output_file, args.source_file, args.overwrite, args.tag, args.ext, args.msgstr_num)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement