Commit | Line | Data |
---|---|---|
8ee66edd | 1 | '''Tools for l10n.''' |
de1cfa8a FC |
2 | from os import system, makedirs, remove, listdir |
3 | from os.path import exists, isfile, join | |
8ee66edd | 4 | from shutil import move, copy |
aa577aeb FC |
5 | from json import loads |
6 | from polib import pofile, POEntry | |
9f095a28 | 7 | from ya2.build.build import find_file_names, FindFileNamesArgs |
8ee66edd FC |
8 | |
9 | ||
10 | class LanguageBuilder: | |
11 | '''Tools for building files for l10n.''' | |
12 | ||
aa577aeb | 13 | def __init__(self, app_name, po_path, json_path, mo_path): |
4e980d83 FC |
14 | self.__app_name = app_name |
15 | self.__po_path = po_path | |
aa577aeb | 16 | self.__json_path = json_path |
4e980d83 FC |
17 | self.__mo_path = mo_path |
18 | ||
de1cfa8a FC |
19 | def build(self): |
20 | self.__build_pot() | |
21 | po_files = [name for name in listdir(self.__po_path) | |
22 | if isfile(join(self.__po_path, name)) and | |
23 | name.endswith('.po')] | |
24 | language_codes = [f.split('.')[0] for f in po_files] | |
25 | for l in language_codes: self.__process_language(l) | |
8ee66edd | 26 | |
de1cfa8a | 27 | def __build_pot(self): |
8ee66edd | 28 | '''Builds the pot file in the lng_dir_code directory.''' |
9f095a28 | 29 | find_info = FindFileNamesArgs(['py'], ['tests']) |
d3ef798c | 30 | src_files = ' '.join(find_file_names(find_info)) |
8ee66edd FC |
31 | cmd_tmpl = 'xgettext -ci18n -d {appname} -L python ' + \ |
32 | '-o {pot_path}{appname}.pot ' | |
4e980d83 | 33 | system(cmd_tmpl.format(appname=self.__app_name, pot_path=self.__po_path) + src_files) |
aa577aeb FC |
34 | self.__add_from_json_to_pot() |
35 | ||
36 | def __add_from_json_to_pot(self): | |
37 | json_files = [name for name in listdir(self.__json_path) | |
38 | if isfile(join(self.__json_path, name)) and | |
88dde925 FC |
39 | name.endswith('.json') and |
40 | name != 'index.json'] | |
aa577aeb FC |
41 | json_strings = [] |
42 | for json_file in json_files: | |
43 | with open(f'{self.__json_path}{json_file}') as f: | |
44 | json = loads(f.read()) | |
45 | json_strings += [json['name']] | |
88dde925 FC |
46 | for instruction_line in json['instructions'].split('\n'): |
47 | if instruction_line: | |
48 | json_strings += [instruction_line] | |
aa577aeb FC |
49 | def process_json_escape(string): |
50 | return bytes(string, 'utf-8').decode('unicode-escape') | |
51 | json_strings = [process_json_escape(s) for s in json_strings] | |
52 | json_strings = list(set(json_strings)) | |
53 | pot = pofile(f'{self.__po_path}{self.__app_name}.pot') | |
54 | pot_ids = [entry.msgid for entry in pot] | |
55 | for json_string in json_strings: | |
56 | if json_string not in pot_ids: | |
57 | entry = POEntry(msgid=json_string) | |
58 | pot.append(entry) | |
59 | pot.save(f'{self.__po_path}{self.__app_name}.pot') | |
8ee66edd | 60 | |
de1cfa8a FC |
61 | def __process_language(self, language_code): |
62 | self.__merge(language_code) | |
63 | self.__build_mo(language_code) | |
64 | ||
65 | def __build_mo(self, language_code): | |
66 | '''Builds the mo file in the lng_dir_code directory.''' | |
67 | mo_template = '%s%s/LC_MESSAGES/%s.mo' | |
68 | mo_name = mo_template % (self.__mo_path, language_code, self.__app_name) | |
69 | lng_code = mo_name[len(self.__mo_path):].split('/')[0] | |
70 | lng_dir = self.__mo_path + lng_code + '/LC_MESSAGES/' | |
71 | cmd = 'msgfmt -o {lng_dir}{appname}.mo assets/locale/po/{lng_code}.po' | |
72 | system(cmd.format(lng_dir=lng_dir, appname=self.__app_name, lng_code=lng_code)) | |
73 | ||
74 | def __merge(self, lng_code): | |
8ee66edd | 75 | '''Merges the new strings with the previous ones.''' |
4e980d83 | 76 | lng_base_dir = self.__prepare(lng_code) |
de1cfa8a | 77 | self.__do_merge(lng_base_dir, lng_code) |
4e980d83 | 78 | self.__postprocess(lng_code) |
8ee66edd | 79 | |
4e980d83 | 80 | def __prepare(self, lng_code): |
8ee66edd | 81 | '''Prepares a directory for working with languages.''' |
4e980d83 FC |
82 | makedirs(self.__mo_path + lng_code + '/LC_MESSAGES', exist_ok=True) |
83 | lng_dir = self.__mo_path + lng_code + '/LC_MESSAGES/' | |
84 | if not exists('assets/locale/po/' + lng_code + '.po'): | |
8ee66edd | 85 | lines_to_fix = ['CHARSET/UTF-8', 'ENCODING/8bit'] |
4e980d83 | 86 | [self.__fix_line(line, lng_dir) |
e65a09cf | 87 | for line in lines_to_fix] |
4e980d83 | 88 | copy(lng_dir + self.__app_name + '.pot', lng_dir + self.__app_name + '.po') |
8ee66edd FC |
89 | return lng_dir |
90 | ||
4e980d83 | 91 | def __fix_line(self, line, lng_dir): |
8ee66edd FC |
92 | '''Fixes po files (misaligned entries).''' |
93 | cmd_tmpl = "sed 's/{line}/' {lng_dir}{appname}.pot > " + \ | |
94 | "{lng_dir}{appname}tmp.po" | |
4e980d83 FC |
95 | system(cmd_tmpl.format(line=line, lng_dir=lng_dir, appname=self.__app_name)) |
96 | move(lng_dir + self.__app_name + 'tmp.po', lng_dir + self.__app_name + '.pot') | |
8ee66edd | 97 | |
de1cfa8a | 98 | def __do_merge(self, lng_dir, lng_code): |
8ee66edd FC |
99 | '''Manages the msgmerge's invokation.''' |
100 | print('merge', lng_dir) | |
101 | cmd = 'msgmerge -o {lng_dir}{appname}merge.po ' + \ | |
102 | '{tgt_path}{lng_code}.po {tgt_path}{appname}.pot' | |
4e980d83 FC |
103 | cmd = cmd.format(lng_dir=lng_dir, lng_code=lng_code, appname=self.__app_name, |
104 | tgt_path=self.__po_path) | |
8ee66edd | 105 | system(cmd) |
4e980d83 | 106 | copy(lng_dir + self.__app_name + 'merge.po', |
e65a09cf | 107 | 'assets/locale/po/%s.po' % lng_code) |
8ee66edd | 108 | poname_tmpl = '{lng_dir}{appname}merge.po' |
4e980d83 | 109 | remove(poname_tmpl.format(lng_dir=lng_dir, appname=self.__app_name)) |
8ee66edd | 110 | |
4e980d83 | 111 | def __postprocess(self, lng_code): |
8ee66edd | 112 | '''Fixes po files at the end of the building process.''' |
58c1093d FC |
113 | lines = open('assets/locale/po/%s.po' % lng_code, 'r').readlines() |
114 | with open('assets/locale/po/%s.po' % lng_code, 'w') as outf: | |
8ee66edd FC |
115 | for line in lines: |
116 | po_str = '"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\\n"\n' | |
117 | outf.write(po_str if line.startswith(po_str[:20]) else line) |