Left: | ||
Right: |
OLD | NEW |
---|---|
1 # This Source Code Form is subject to the terms of the Mozilla Public | 1 # This Source Code Form is subject to the terms of the Mozilla Public |
2 # License, v. 2.0. If a copy of the MPL was not distributed with this | 2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. | 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | 4 |
5 import re | 5 import re |
6 import os | 6 import os |
7 import sys | 7 import sys |
8 import codecs | 8 import codecs |
9 import json | 9 import json |
10 import urlparse | 10 import urlparse |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
154 | 154 |
155 def unescapeEntity(value): | 155 def unescapeEntity(value): |
156 return value.replace('&', '&').replace('<', '<').replace('>', '>') .replace('"', '"') | 156 return value.replace('&', '&').replace('<', '<').replace('>', '>') .replace('"', '"') |
157 | 157 |
158 | 158 |
159 def mapLocale(type, locale): | 159 def mapLocale(type, locale): |
160 mapping = langMappingChrome if type == 'ISO-15897' else langMappingGecko | 160 mapping = langMappingChrome if type == 'ISO-15897' else langMappingGecko |
161 return mapping.get(locale, locale) | 161 return mapping.get(locale, locale) |
162 | 162 |
163 | 163 |
164 def parseDTDString(data, path): | |
165 result = [] | |
166 currentComment = [None] | |
167 | |
168 parser = ParserCreate() | |
169 parser.UseForeignDTD(True) | |
170 parser.SetParamEntityParsing(XML_PARAM_ENTITY_PARSING_ALWAYS) | |
171 | |
172 def ExternalEntityRefHandler(context, base, systemId, publicId): | |
173 subparser = parser.ExternalEntityParserCreate(context, 'utf-8') | |
174 subparser.Parse(data.encode('utf-8'), True) | |
175 return 1 | |
176 | |
177 def CommentHandler(data): | |
178 currentComment[0] = data.strip() | |
179 | |
180 def EntityDeclHandler(entityName, is_parameter_entity, value, base, systemId , publicId, notationName): | |
181 result.append((unescapeEntity(entityName), currentComment[0], unescapeEn tity(value.strip()))) | |
182 currentComment[0] = None | |
183 | |
184 parser.ExternalEntityRefHandler = ExternalEntityRefHandler | |
185 parser.CommentHandler = CommentHandler | |
186 parser.EntityDeclHandler = EntityDeclHandler | |
187 parser.Parse('<!DOCTYPE root SYSTEM "foo"><root/>', True) | |
188 | |
189 for entry in result: | |
190 yield entry | |
191 | |
192 | |
193 def escapeProperty(value): | 164 def escapeProperty(value): |
194 return value.replace('\n', '\\n') | 165 return value.replace('\n', '\\n') |
195 | 166 |
196 | 167 |
197 def unescapeProperty(value): | 168 def unescapeProperty(value): |
198 return value.replace('\\n', '\n') | 169 return value.replace('\\n', '\n') |
199 | 170 |
200 | 171 |
201 def parsePropertiesString(data, path): | 172 def parsePropertiesString(data, path): |
202 currentComment = None | 173 currentComment = None |
203 for line in data.splitlines(): | 174 for line in data.splitlines(): |
204 match = re.search(r'^\s*[#!]\s*(.*)', line) | 175 match = re.search(r'^\s*[#!]\s*(.*)', line) |
205 if match: | 176 if match: |
206 currentComment = match.group(1) | 177 currentComment = match.group(1) |
207 elif '=' in line: | 178 elif '=' in line: |
208 key, value = line.split('=', 1) | 179 key, value = line.split('=', 1) |
209 yield (unescapeProperty(key), currentComment, unescapeProperty(value )) | 180 yield (unescapeProperty(key), currentComment, unescapeProperty(value )) |
210 currentComment = None | 181 currentComment = None |
211 elif re.search(r'\S', line): | 182 elif re.search(r'\S', line): |
212 print >>sys.stderr, 'Unrecognized data in file %s: %s' % (path, line ) | 183 print >>sys.stderr, 'Unrecognized data in file %s: %s' % (path, line ) |
213 | 184 |
214 | 185 |
215 def parseString(data, path): | 186 def parseString(data, path): |
216 result = {'_origData': data} | 187 result = {'_origData': data} |
217 if path.endswith('.dtd'): | 188 if path.endswith('.properties'): |
Sebastian Noack
2017/10/03 02:22:39
Both, the .dtd and the .properties format are spec
tlucas
2017/10/04 11:48:38
Done.
| |
218 it = parseDTDString(data, path) | |
219 elif path.endswith('.properties'): | |
220 it = parsePropertiesString(data, path) | 189 it = parsePropertiesString(data, path) |
221 else: | 190 else: |
222 return None | 191 return None |
223 | 192 |
224 for name, comment, value in it: | 193 for name, comment, value in it: |
225 result[name] = value | 194 result[name] = value |
226 return result | 195 return result |
227 | 196 |
228 | 197 |
229 def readFile(path): | 198 def readFile(path): |
230 fileHandle = codecs.open(path, 'rb', encoding='utf-8') | 199 fileHandle = codecs.open(path, 'rb', encoding='utf-8') |
231 data = fileHandle.read() | 200 data = fileHandle.read() |
232 fileHandle.close() | 201 fileHandle.close() |
233 return parseString(data, path) | 202 return parseString(data, path) |
234 | 203 |
235 | 204 |
236 def generateStringEntry(key, value, path): | |
237 if path.endswith('.dtd'): | |
238 return '<!ENTITY %s "%s">\n' % (escapeEntity(key), escapeEntity(value)) | |
239 else: | |
240 return '%s=%s\n' % (escapeProperty(key), escapeProperty(value)) | |
241 | |
242 | |
243 def toJSON(path): | 205 def toJSON(path): |
244 fileHandle = codecs.open(path, 'rb', encoding='utf-8') | 206 fileHandle = codecs.open(path, 'rb', encoding='utf-8') |
245 data = fileHandle.read() | 207 data = fileHandle.read() |
246 fileHandle.close() | 208 fileHandle.close() |
247 | 209 |
248 if path.endswith('.dtd'): | 210 if path.endswith('.properties'): |
249 it = parseDTDString(data, path) | |
250 elif path.endswith('.properties'): | |
251 it = parsePropertiesString(data, path) | 211 it = parsePropertiesString(data, path) |
252 else: | 212 else: |
253 return None | 213 return None |
254 | 214 |
255 result = OrderedDict() | 215 result = OrderedDict() |
256 for name, comment, value in it: | 216 for name, comment, value in it: |
257 obj = {'message': value} | 217 obj = {'message': value} |
258 if comment == None: | 218 if comment == None: |
259 obj['description'] = name | 219 obj['description'] = name |
260 else: | 220 else: |
261 obj['description'] = '%s: %s' % (name, comment) | 221 obj['description'] = '%s: %s' % (name, comment) |
262 result[name] = obj | 222 result[name] = obj |
263 return json.dumps(result, ensure_ascii=False, indent=2) | 223 return json.dumps(result, ensure_ascii=False, indent=2) |
264 | 224 |
265 | 225 |
266 def fromJSON(path, data): | 226 def fromJSON(path, data): |
267 data = json.loads(data) | 227 data = json.loads(data) |
268 if not data: | 228 if not data: |
269 if os.path.exists(path): | 229 if os.path.exists(path): |
270 os.remove(path) | 230 os.remove(path) |
271 return | 231 return |
272 | 232 |
273 dir = os.path.dirname(path) | 233 dir = os.path.dirname(path) |
274 if not os.path.exists(dir): | 234 if not os.path.exists(dir): |
275 os.makedirs(dir) | 235 os.makedirs(dir) |
276 file = codecs.open(path, 'wb', encoding='utf-8') | 236 file = codecs.open(path, 'wb', encoding='utf-8') |
277 for key, value in data.iteritems(): | 237 for key, value in data.iteritems(): |
278 file.write(generateStringEntry(key, value['message'], path)) | 238 file.write('{}={}\n'.format(escapeProperty(key), |
239 escapeProperty(value['message']))) | |
279 file.close() | 240 file.close() |
280 | 241 |
281 | 242 |
282 def preprocessChromeLocale(path, metadata, isMaster): | 243 def preprocessChromeLocale(path, metadata, isMaster): |
283 fileHandle = codecs.open(path, 'rb', encoding='utf-8') | 244 fileHandle = codecs.open(path, 'rb', encoding='utf-8') |
284 data = json.load(fileHandle) | 245 data = json.load(fileHandle) |
285 fileHandle.close() | 246 fileHandle.close() |
286 | 247 |
287 for key, value in data.iteritems(): | 248 for key, value in data.iteritems(): |
288 if isMaster: | 249 if isMaster: |
(...skipping 28 matching lines...) Expand all Loading... | |
317 def setupTranslations(localeConfig, projectName, key): | 278 def setupTranslations(localeConfig, projectName, key): |
318 # Make a new set from the locales list, mapping to Crowdin friendly format | 279 # Make a new set from the locales list, mapping to Crowdin friendly format |
319 locales = {mapLocale(localeConfig['name_format'], locale) | 280 locales = {mapLocale(localeConfig['name_format'], locale) |
320 for locale in localeConfig['locales']} | 281 for locale in localeConfig['locales']} |
321 | 282 |
322 # Fill up with locales that we don't have but the browser supports | 283 # Fill up with locales that we don't have but the browser supports |
323 if 'chrome' in localeConfig['target_platforms']: | 284 if 'chrome' in localeConfig['target_platforms']: |
324 for locale in chromeLocales: | 285 for locale in chromeLocales: |
325 locales.add(mapLocale('ISO-15897', locale)) | 286 locales.add(mapLocale('ISO-15897', locale)) |
326 | 287 |
327 if 'gecko' in localeConfig['target_platforms']: | 288 if 'gecko-webext' in localeConfig['target_platforms']: |
328 firefoxLocales = urllib2.urlopen('http://www.mozilla.org/en-US/firefox/a ll.html').read() | 289 firefoxLocales = urllib2.urlopen('http://www.mozilla.org/en-US/firefox/a ll.html').read() |
329 for match in re.finditer(r'&lang=([\w\-]+)"', firefoxLocales): | 290 for match in re.finditer(r'&lang=([\w\-]+)"', firefoxLocales): |
330 locales.add(mapLocale('BCP-47', match.group(1))) | 291 locales.add(mapLocale('BCP-47', match.group(1))) |
331 langPacks = urllib2.urlopen('https://addons.mozilla.org/en-US/firefox/la nguage-tools/').read() | 292 langPacks = urllib2.urlopen('https://addons.mozilla.org/en-US/firefox/la nguage-tools/').read() |
332 for match in re.finditer(r'<tr>.*?</tr>', langPacks, re.S): | 293 for match in re.finditer(r'<tr>.*?</tr>', langPacks, re.S): |
333 if match.group(0).find('Install Language Pack') >= 0: | 294 if match.group(0).find('Install Language Pack') >= 0: |
334 match2 = re.search(r'lang="([\w\-]+)"', match.group(0)) | 295 match2 = re.search(r'lang="([\w\-]+)"', match.group(0)) |
335 if match2: | 296 if match2: |
336 locales.add(mapLocale('BCP-47', match2.group(1))) | 297 locales.add(mapLocale('BCP-47', match2.group(1))) |
337 | 298 |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
469 if not info.filename.endswith('.json'): | 430 if not info.filename.endswith('.json'): |
470 continue | 431 continue |
471 | 432 |
472 dir, file = os.path.split(info.filename) | 433 dir, file = os.path.split(info.filename) |
473 if not re.match(r'^[\w\-]+$', dir) or dir == normalizedDefaultLocale: | 434 if not re.match(r'^[\w\-]+$', dir) or dir == normalizedDefaultLocale: |
474 continue | 435 continue |
475 if localeConfig['file_format'] == 'chrome-json' and file.count('.') == 1 : | 436 if localeConfig['file_format'] == 'chrome-json' and file.count('.') == 1 : |
476 origFile = file | 437 origFile = file |
477 else: | 438 else: |
478 origFile = re.sub(r'\.json$', '', file) | 439 origFile = re.sub(r'\.json$', '', file) |
479 if (localeConfig['file_format'] == 'gecko-dtd' and | |
480 not origFile.endswith('.dtd') and | |
481 not origFile.endswith('.properties')): | |
482 continue | |
483 | 440 |
484 if localeConfig['name_format'] == 'ISO-15897': | 441 if localeConfig['name_format'] == 'ISO-15897': |
485 mapping = langMappingChrome | 442 mapping = langMappingChrome |
486 else: | 443 else: |
487 mapping = langMappingGecko | 444 mapping = langMappingGecko |
488 | 445 |
489 for key, value in mapping.iteritems(): | 446 for key, value in mapping.iteritems(): |
490 if value == dir: | 447 if value == dir: |
491 dir = key | 448 dir = key |
492 if localeConfig['name_format'] == 'ISO-15897': | 449 if localeConfig['name_format'] == 'ISO-15897': |
(...skipping 21 matching lines...) Expand all Loading... | |
514 else: | 471 else: |
515 fromJSON(path, data) | 472 fromJSON(path, data) |
516 | 473 |
517 # Remove any extra files | 474 # Remove any extra files |
518 for dir, files in dirs.iteritems(): | 475 for dir, files in dirs.iteritems(): |
519 baseDir = os.path.join(localeConfig['base_path'], dir) | 476 baseDir = os.path.join(localeConfig['base_path'], dir) |
520 if not os.path.exists(baseDir): | 477 if not os.path.exists(baseDir): |
521 continue | 478 continue |
522 for file in os.listdir(baseDir): | 479 for file in os.listdir(baseDir): |
523 path = os.path.join(baseDir, file) | 480 path = os.path.join(baseDir, file) |
524 if os.path.isfile(path) and (file.endswith('.json') or file.endswith ('.properties') or file.endswith('.dtd')) and not file in files: | 481 valid_extension = os.path.splitext(file)[1] in {'.json', |
482 '.properties'} | |
483 if os.path.isfile(path) and valid_extension and not file in files: | |
525 os.remove(path) | 484 os.remove(path) |
OLD | NEW |