blob: dbb4d0c08c57b6f685d0953f7c699131aa311f4a [file] [log] [blame]
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +08001#!/usr/bin/env python
2# Copyright 2021 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""Script for strings updating."""
6
7import argparse
8import glob
9import logging
10import os
11import re
12from xml.etree import ElementTree
13
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080014DEFAULT_SRC_STRING_PATH = ('blaze-genfiles/googleclient/chrome/'
15 'transconsole_resources/strings/cros')
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080016DEFAULT_DEST_STRINGS_PATH = os.path.join(os.path.dirname(__file__), 'strings',
17 'locale')
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080018
19
20def get_locales_from_dir(src_dir):
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080021 """Gets a set of locales of xtb files in src_dir."""
22 locales = set()
23 for file in glob.glob(os.path.join(src_dir, 'firmware_strings_*.xtb')):
24 basename = os.path.basename(file)
25 m = re.match(r'^firmware_strings_([A-Za-z0-9-]+).xtb$', basename)
26 locales.add(m.group(1))
27 return locales
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080028
29
30def load_xtb_to_dict(xtb_dir, locale):
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080031 """Loads xtb file to dict.
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080032
33 Args:
34 xtb_dir: The directory of xtb files.
35 locale: The locale of the xtb file to be loaded.
36
37 Returns:
38 A dict of message_id => message.
39 """
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080040 xtb_file = os.path.join(xtb_dir, f'firmware_strings_{locale}.xtb')
41 xtb_root = ElementTree.parse(xtb_file).getroot()
42 res = {}
43 for item in xtb_root:
44 res[item.attrib['id']] = item.text
45 return res
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080046
47
48def save_dict_to_xtb(data, out_dir, locale):
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080049 """Saves the dict to xtb file.
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080050
51 Args:
52 data: The dict of message_id => message.
53 out_dir: The directory of xtb files.
54 locale: The locale of the xtb file to be saved.
55 """
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080056 out_xtb = ElementTree.Element('translationbundle', {'lang': locale})
57 out_xtb.text = '\n'
58 for message_id, text in sorted(data.items()):
59 e = ElementTree.SubElement(out_xtb, 'translation', {'id': message_id})
60 e.text = text
61 e.tail = '\n'
62 out_file = os.path.join(out_dir, f'firmware_strings_{locale}.xtb')
63 logging.info('Saving %r', out_file)
64 with open(out_file, 'rb+') as f:
65 # From chromium/src/tools/grit/grit/xtb_reader.py.
66 # Skip the header and write the data of the <translationbundle> tag.
67 front_of_file = f.read(1024)
68 f.seek(front_of_file.find(b'<translationbundle'))
69 out = ElementTree.tostring(out_xtb, encoding='utf-8')
70 f.write(out)
71 f.truncate()
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080072
73
74def merge_xtb_data(locale, in_dir, out_dir, message_ids):
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080075 """Merges the xtb data.
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080076
77 Args:
78 locale: The locale of the xtb file to be merged.
79 in_dir: The source.
80 out_dir: The destination.
81 message_ids: List of the message ids. Only these ids will be modified.
82
83 Returns:
84 (new_ids, update_ids, del_ids): The set of the new/updated/removed message
85 ids.
86 """
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080087 logging.info('Merging %r', locale)
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080088
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080089 new_ids = set()
90 update_ids = set()
91 del_ids = set()
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +080092
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +080093 in_data = load_xtb_to_dict(in_dir, locale)
94 out_data = load_xtb_to_dict(out_dir, locale)
95 for message_id in message_ids:
96 if message_id in in_data and message_id not in out_data:
97 new_ids.add(message_id)
98 out_data[message_id] = in_data[message_id]
99 elif message_id in in_data and message_id in out_data:
100 if in_data[message_id] != out_data[message_id]:
101 update_ids.add(message_id)
102 out_data[message_id] = in_data[message_id]
103 else:
104 logging.warning("Locale %r: Id %r didn't change.", locale,
105 message_id)
106 elif message_id not in in_data and message_id in out_data:
107 del_ids.add(message_id)
108 out_data.pop(message_id)
109 else:
110 logging.warning('Locale %r: Id %r not in input/output file.',
111 locale, message_id)
112 logging.info('New: %s', new_ids)
113 logging.info('Updated: %s', update_ids)
114 logging.info('Removed: %s', del_ids)
115 save_dict_to_xtb(out_data, out_dir, locale)
116 return new_ids, update_ids, del_ids
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800117
118
119def get_arguments():
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800120 parser = argparse.ArgumentParser()
121 parser.add_argument('--verbosity', '-v', action='count', default=0)
122 parser.add_argument(
123 '--from',
124 dest='in_dir',
125 default=DEFAULT_SRC_STRING_PATH,
126 help='The source directory of the generated xtb files.')
127 parser.add_argument('--to',
128 dest='out_dir',
129 default=DEFAULT_DEST_STRINGS_PATH,
130 help='The destination directory of the xtb files.')
131 subparser = parser.add_subparsers(dest='cmd')
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800132
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800133 merge_parser = subparser.add_parser(
134 'merge', help='Merge the xtb files with specific message ids.')
135 merge_parser.add_argument(
136 'message_ids',
137 metavar='ID',
138 nargs='+',
139 help='The ids of the strings which should be updated.')
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800140
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800141 diff_parser = subparser.add_parser(
142 'diff', help='Show the different of message ids of a xtb file.')
143 diff_parser.add_argument('--id-only',
144 action='store_true',
145 help="Don't show the message content.")
146 diff_parser.add_argument('locale', help='The locale file to diff.')
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800147
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800148 return parser.parse_args(), parser
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800149
150
151def print_diff_item(key, data, id_only):
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800152 if id_only:
153 print(key)
154 else:
155 print(f'{key!r}: {data}')
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800156
157
158def diff(args):
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800159 in_data = load_xtb_to_dict(args.in_dir, args.locale)
160 out_data = load_xtb_to_dict(args.out_dir, args.locale)
161 print(
162 '---------------------------------------------------------------------'
163 )
164 print('New:')
165 for key in in_data:
166 if key not in out_data:
167 print_diff_item(key, in_data[key], args.id_only)
168 print(
169 '---------------------------------------------------------------------'
170 )
171 print('Updated:')
172 for key in in_data:
173 if key in out_data and in_data[key] != out_data[key]:
174 print_diff_item(key,
175 f'\n{out_data[key]!r}\n=>\n{in_data[key]!r}\n',
176 args.id_only)
177 print(
178 '---------------------------------------------------------------------'
179 )
180 print('Deleted:')
181 for key in out_data:
182 if key not in in_data:
183 print_diff_item(key, out_data[key], args.id_only)
184 print(
185 '---------------------------------------------------------------------'
186 )
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800187
188
189def merge(args):
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800190 in_locales = get_locales_from_dir(args.in_dir)
191 out_locales = get_locales_from_dir(args.out_dir)
192 if in_locales != out_locales:
193 raise RuntimeError(
194 "The input xtb files and the output xtb files don't match.")
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800195
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800196 prev_id_sets = None
197 for locale in sorted(in_locales):
198 id_sets = merge_xtb_data(locale, args.in_dir, args.out_dir,
199 args.message_ids)
200 if prev_id_sets and id_sets != prev_id_sets:
201 logging.warning(
202 'Locale %r: Updated ids are different with the previous '
203 'locale:\n%r\n=>\n%r', locale, prev_id_sets, id_sets)
204 prev_id_sets = id_sets
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800205
206
207def main():
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800208 args, parser = get_arguments()
209 logging.basicConfig(level=logging.WARNING - 10 * args.verbosity)
210 if args.cmd == 'diff':
211 diff(args)
212 elif args.cmd == 'merge':
213 merge(args)
214 else:
215 parser.print_help()
Chung-Sheng Wuc0e32c72021-03-30 17:01:28 +0800216
217
218if __name__ == '__main__':
Yu-Ping Wud6a7abb2021-04-14 15:36:31 +0800219 main()