Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: localeTools.py

Issue 29336281: Issue 2109 - Allow for translation of app independent repositories (Closed)
Left Patch Set: Created Feb. 11, 2016, 9:04 p.m.
Right Patch Set: Fixed typo spotted during testing Created Feb. 13, 2016, 12:31 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « build.py ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # coding: utf-8 1 # coding: utf-8
2 2
3 # This Source Code Form is subject to the terms of the Mozilla Public 3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 6
7 import re, os, sys, codecs, json, urllib, urllib2 7 import re, os, sys, codecs, json, urllib, urllib2
8 from StringIO import StringIO 8 from StringIO import StringIO
9 from ConfigParser import SafeConfigParser 9 from ConfigParser import SafeConfigParser
10 from zipfile import ZipFile 10 from zipfile import ZipFile
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 yield (key, self[key]) 101 yield (key, self[key])
102 done.add(key) 102 done.add(key)
103 103
104 def escapeEntity(value): 104 def escapeEntity(value):
105 return value.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').r eplace('"', '&quot;') 105 return value.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').r eplace('"', '&quot;')
106 106
107 def unescapeEntity(value): 107 def unescapeEntity(value):
108 return value.replace('&amp;', '&').replace('&lt;', '<').replace('&gt;', '>').r eplace('&quot;', '"') 108 return value.replace('&amp;', '&').replace('&lt;', '<').replace('&gt;', '>').r eplace('&quot;', '"')
109 109
110 def mapLocale(type, locale): 110 def mapLocale(type, locale):
111 if type == 'chrome' or type == 'ISO-15897': 111 mapping = langMappingChrome if type == 'ISO-15897' else langMappingGecko
Sebastian Noack 2016/02/12 16:33:58 Nit: type in {'chrome', 'ISO-15897'} But why do w
Wladimir Palant 2016/02/12 17:13:04 This logic is wrong - you cannot decide on the map
kzar 2016/02/12 17:28:15 Done.
112 return langMappingChrome.get(locale, locale) 112 return mapping.get(locale, locale)
113 else:
114 return langMappingGecko.get(locale, locale)
115 113
116 def parseDTDString(data, path): 114 def parseDTDString(data, path):
117 result = [] 115 result = []
118 currentComment = [None] 116 currentComment = [None]
119 117
120 parser = ParserCreate() 118 parser = ParserCreate()
121 parser.UseForeignDTD(True) 119 parser.UseForeignDTD(True)
122 parser.SetParamEntityParsing(XML_PARAM_ENTITY_PARSING_ALWAYS) 120 parser.SetParamEntityParsing(XML_PARAM_ENTITY_PARSING_ALWAYS)
123 121
124 def ExternalEntityRefHandler(context, base, systemId, publicId): 122 def ExternalEntityRefHandler(context, base, systemId, publicId):
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 265
268 # Delete description from translations 266 # Delete description from translations
269 for key, value in parsed.iteritems(): 267 for key, value in parsed.iteritems():
270 if "description" in value: 268 if "description" in value:
271 del value["description"] 269 del value["description"]
272 270
273 file = codecs.open(path, 'wb', encoding='utf-8') 271 file = codecs.open(path, 'wb', encoding='utf-8')
274 json.dump(parsed, file, ensure_ascii=False, sort_keys=True, indent=2, separato rs=(',', ': ')) 272 json.dump(parsed, file, ensure_ascii=False, sort_keys=True, indent=2, separato rs=(',', ': '))
275 file.close() 273 file.close()
276 274
277 def setupTranslations(projectName, localeConfig, key): 275 def setupTranslations(localeConfig, projectName, key):
278 # Make a new set from the locales list, mapping to Crowdin friendly format 276 # Make a new set from the locales list, mapping to Crowdin friendly format
279 locales = set(map(lambda locale: mapLocale(localeConfig['name_format'], 277 locales = {mapLocale(localeConfig['name_format'], locale)
Sebastian Noack 2016/02/12 16:33:58 Why not using a set expression? {mapLocale(loca
kzar 2016/02/12 17:28:15 Good point, Done.
280 locale), 278 for locale in localeConfig['locales']}
281 localeConfig['locales'].keys()))
282 279
283 # Fill up with locales that we don't have but the browser supports 280 # Fill up with locales that we don't have but the browser supports
284 if 'chrome' in localeConfig['target_platforms']: 281 if 'chrome' in localeConfig['target_platforms']:
285 for locale in chromeLocales: 282 for locale in chromeLocales:
286 locales.add(mapLocale('chrome', locale)) 283 locales.add(mapLocale('ISO-15897', locale))
Wladimir Palant 2016/02/12 17:13:04 mapLocale('ISO-15897', ...) please, that's the for
kzar 2016/02/12 18:25:26 Yep, already changed those in second patch set. Sa
287 284
288 if 'gecko' in localeConfig['target_platforms']: 285 if 'gecko' in localeConfig['target_platforms']:
289 firefoxLocales = urllib2.urlopen('http://www.mozilla.org/en-US/firefox/all.h tml').read() 286 firefoxLocales = urllib2.urlopen('http://www.mozilla.org/en-US/firefox/all.h tml').read()
290 for match in re.finditer(r'&amp;lang=([\w\-]+)"', firefoxLocales): 287 for match in re.finditer(r'&amp;lang=([\w\-]+)"', firefoxLocales):
291 locales.add(mapLocale('gecko', match.group(1))) 288 locales.add(mapLocale('BCP-47', match.group(1)))
Wladimir Palant 2016/02/12 17:13:04 mapLocale('BCP-47', ...) please, that's the format
292 langPacks = urllib2.urlopen('https://addons.mozilla.org/en-US/firefox/langua ge-tools/').read() 289 langPacks = urllib2.urlopen('https://addons.mozilla.org/en-US/firefox/langua ge-tools/').read()
293 for match in re.finditer(r'<tr>.*?</tr>', langPacks, re.S): 290 for match in re.finditer(r'<tr>.*?</tr>', langPacks, re.S):
294 if match.group(0).find('Install Language Pack') >= 0: 291 if match.group(0).find('Install Language Pack') >= 0:
295 match2 = re.search(r'lang="([\w\-]+)"', match.group(0)) 292 match2 = re.search(r'lang="([\w\-]+)"', match.group(0))
296 if match2: 293 if match2:
297 locales.add(mapLocale('gecko', match2.group(1))) 294 locales.add(mapLocale('BCP-47', match2.group(1)))
Wladimir Palant 2016/02/12 17:13:04 mapLocale('BCP-47', ...) please, that's the format
298 295
299 allowed = set() 296 allowed = set()
300 allowedLocales = urllib2.urlopen('http://crowdin.net/page/language-codes').rea d() 297 allowedLocales = urllib2.urlopen('http://crowdin.net/page/language-codes').rea d()
301 for match in re.finditer(r'<tr>\s*<td\b[^<>]*>([\w\-]+)</td>', allowedLocales, re.S): 298 for match in re.finditer(r'<tr>\s*<td\b[^<>]*>([\w\-]+)</td>', allowedLocales, re.S):
302 allowed.add(match.group(1)) 299 allowed.add(match.group(1))
303 if not allowed.issuperset(locales): 300 if not allowed.issuperset(locales):
304 print 'Warning, following locales aren\'t allowed by server: ' + ', '.join(l ocales - allowed) 301 print 'Warning, following locales aren\'t allowed by server: ' + ', '.join(l ocales - allowed)
305 302
306 locales = list(locales & allowed) 303 locales = list(locales & allowed)
307 locales.sort() 304 locales.sort()
(...skipping 14 matching lines...) Expand all
322 body += '--%s--\r\n' % boundary 319 body += '--%s--\r\n' % boundary
323 320
324 body = body.encode('utf-8') 321 body = body.encode('utf-8')
325 request = urllib2.Request(url, StringIO(body)) 322 request = urllib2.Request(url, StringIO(body))
326 request.add_header('Content-Type', 'multipart/form-data; boundary=%s' % bounda ry) 323 request.add_header('Content-Type', 'multipart/form-data; boundary=%s' % bounda ry)
327 request.add_header('Content-Length', len(body)) 324 request.add_header('Content-Length', len(body))
328 result = urllib2.urlopen(request).read() 325 result = urllib2.urlopen(request).read()
329 if result.find('<success') < 0: 326 if result.find('<success') < 0:
330 raise Exception('Server indicated that the operation was not successful\n' + result) 327 raise Exception('Server indicated that the operation was not successful\n' + result)
331 328
332 def updateTranslationMaster(metadata, dir, projectName, localeConfig, key): 329 def updateTranslationMaster(localeConfig, metadata, dir, projectName, key):
333 result = json.load(urllib2.urlopen('http://api.crowdin.net/api/project/%s/info ?key=%s&json=1' % (projectName, key))) 330 result = json.load(urllib2.urlopen('http://api.crowdin.net/api/project/%s/info ?key=%s&json=1' % (projectName, key)))
334 331
335 existing = set(map(lambda f: f['name'], result['files'])) 332 existing = set(map(lambda f: f['name'], result['files']))
336 add = [] 333 add = []
337 update = [] 334 update = []
338 for file in os.listdir(dir): 335 for file in os.listdir(dir):
339 path = os.path.join(dir, file) 336 path = os.path.join(dir, file)
340 if os.path.isfile(path): 337 if os.path.isfile(path):
341 if localeConfig['file_format'] == 'chrome-json': 338 if localeConfig['file_format'] == 'chrome-json' and file.endswith('.json') :
342 if file.endswith('.json'): 339 data = preprocessChromeLocale(path, metadata, True)
343 data = preprocessChromeLocale(path, metadata, True) 340 newName = file
344 newName = file 341 elif localeConfig['file_format'] == 'chrome-json':
345 else: 342 fileHandle = codecs.open(path, 'rb', encoding='utf-8')
346 fileHandle = codecs.open(path, 'rb', encoding='utf-8') 343 data = json.dumps({file: {'message': fileHandle.read()}})
347 data = json.dumps({file: {'message': fileHandle.read()}}) 344 fileHandle.close()
348 fileHandle.close() 345 newName = file + '.json'
349 newName = file + '.json'
350 else: 346 else:
351 data = toJSON(path) 347 data = toJSON(path)
352 newName = file + '.json' 348 newName = file + '.json'
353 349
354 if data: 350 if data:
355 if newName in existing: 351 if newName in existing:
356 update.append((newName, data)) 352 update.append((newName, data))
357 existing.remove(newName) 353 existing.remove(newName)
358 else: 354 else:
359 add.append((newName, data)) 355 add.append((newName, data))
360 356
361 if len(add): 357 if len(add):
362 titles = urllib.urlencode([('titles[%s]' % name, re.sub(r'\.json', '', name) ) for name, data in add]) 358 titles = urllib.urlencode([('titles[%s]' % name, re.sub(r'\.json', '', name) ) for name, data in add])
363 postFiles(add, 'http://api.crowdin.net/api/project/%s/add-file?key=%s&type=c hrome&%s' % (projectName, key, titles)) 359 postFiles(add, 'http://api.crowdin.net/api/project/%s/add-file?key=%s&type=c hrome&%s' % (projectName, key, titles))
364 if len(update): 360 if len(update):
365 postFiles(update, 'http://api.crowdin.net/api/project/%s/update-file?key=%s' % (projectName, key)) 361 postFiles(update, 'http://api.crowdin.net/api/project/%s/update-file?key=%s' % (projectName, key))
366 for file in existing: 362 for file in existing:
367 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/delete-file? key=%s&file=%s' % (projectName, key, file)).read() 363 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/delete-file? key=%s&file=%s' % (projectName, key, file)).read()
368 if result.find('<success') < 0: 364 if result.find('<success') < 0:
369 raise Exception('Server indicated that the operation was not successful\n' + result) 365 raise Exception('Server indicated that the operation was not successful\n' + result)
370 366
371 def uploadTranslations(metadata, dir, locale, projectName, localeConfig, key): 367 def uploadTranslations(localeConfig, metadata, dir, locale, projectName, key):
372 files = [] 368 files = []
373 for file in os.listdir(dir): 369 for file in os.listdir(dir):
374 path = os.path.join(dir, file) 370 path = os.path.join(dir, file)
375 if os.path.isfile(path): 371 if os.path.isfile(path):
376 if localeConfig['file_format'] == 'chrome-json': 372 if localeConfig['file_format'] == 'chrome-json' and file.endswith('.json') :
377 if file.endswith('.json'): 373 data = preprocessChromeLocale(path, metadata, False)
378 data = preprocessChromeLocale(path, metadata, False) 374 newName = file
379 newName = file 375 elif localeConfig['file_format'] == 'chrome-json':
380 else: 376 fileHandle = codecs.open(path, 'rb', encoding='utf-8')
381 fileHandle = codecs.open(path, 'rb', encoding='utf-8') 377 data = json.dumps({file: {'message': fileHandle.read()}})
Sebastian Noack 2016/02/12 16:33:58 Using the "with" statement?
kzar 2016/02/12 17:28:15 I really want to avoid unrelated changes this time
Sebastian Noack 2016/02/12 17:38:17 Well, apparently you change this code anyway. Not
382 data = json.dumps({file: {'message': fileHandle.read()}}) 378 fileHandle.close()
383 fileHandle.close() 379 newName = file + '.json'
384 newName = file + '.json'
385 else: 380 else:
386 data = toJSON(path) 381 data = toJSON(path)
387 newName = file + '.json' 382 newName = file + '.json'
388 383
389 if data: 384 if data:
390 files.append((newName, data)) 385 files.append((newName, data))
391 if len(files): 386 if len(files):
392 postFiles(files, 'http://api.crowdin.net/api/project/%s/upload-translation?k ey=%s&language=%s' % ( 387 postFiles(files, 'http://api.crowdin.net/api/project/%s/upload-translation?k ey=%s&language=%s' % (
393 projectName, key, mapLocale(localeConfig['name_format'], locale)) 388 projectName, key, mapLocale(localeConfig['name_format'], locale))
394 ) 389 )
395 390
396 def getTranslations(localeConfig, projectName, key): 391 def getTranslations(localeConfig, projectName, key):
397 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/export?key=%s' % (projectName, key)).read() 392 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/export?key=%s' % (projectName, key)).read()
398 if result.find('<success') < 0: 393 if result.find('<success') < 0:
399 raise Exception('Server indicated that the operation was not successful\n' + result) 394 raise Exception('Server indicated that the operation was not successful\n' + result)
400 395
401 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/download/all.z ip?key=%s' % (projectName, key)).read() 396 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/download/all.z ip?key=%s' % (projectName, key)).read()
402 zip = ZipFile(StringIO(result)) 397 zip = ZipFile(StringIO(result))
403 dirs = {} 398 dirs = {}
399
400 normalizedDefaultLocale = localeConfig['default_locale']
401 if localeConfig['name_format'] == 'ISO-15897':
402 normalizedDefaultLocale = normalizedDefaultLocale.replace('_', '-')
403 normalizedDefaultLocale = mapLocale(localeConfig['name_format'],
404 normalizedDefaultLocale)
405
404 for info in zip.infolist(): 406 for info in zip.infolist():
405 if not info.filename.endswith('.json'): 407 if not info.filename.endswith('.json'):
406 continue 408 continue
407 409
408 dir, file = os.path.split(info.filename) 410 dir, file = os.path.split(info.filename)
409 if not re.match(r'^[\w\-]+$', dir) or dir == localeConfig['default_locale']: 411 if not re.match(r'^[\w\-]+$', dir) or dir == normalizedDefaultLocale:
410 continue 412 continue
411 if localeConfig['file_format'] == 'chrome-json' and file.count('.') == 1: 413 if localeConfig['file_format'] == 'chrome-json' and file.count('.') == 1:
412 origFile = file 414 origFile = file
413 else: 415 else:
414 origFile = re.sub(r'\.json$', '', file) 416 origFile = re.sub(r'\.json$', '', file)
415 if (localeConfig['file_format'] == 'gecko-dtd' and 417 if (localeConfig['file_format'] == 'gecko-dtd' and
416 not origFile.endswith('.dtd') and 418 not origFile.endswith('.dtd') and
417 not origFile.endswith('.properties')): 419 not origFile.endswith('.properties')):
418 continue 420 continue
419 421
(...skipping 12 matching lines...) Expand all
432 if data == '[]': 434 if data == '[]':
433 continue 435 continue
434 436
435 if not dir in dirs: 437 if not dir in dirs:
436 dirs[dir] = set() 438 dirs[dir] = set()
437 dirs[dir].add(origFile) 439 dirs[dir].add(origFile)
438 440
439 path = os.path.join(localeConfig['base_path'], dir, origFile) 441 path = os.path.join(localeConfig['base_path'], dir, origFile)
440 if not os.path.exists(os.path.dirname(path)): 442 if not os.path.exists(os.path.dirname(path)):
441 os.makedirs(os.path.dirname(path)) 443 os.makedirs(os.path.dirname(path))
442 if localeConfig['file_format'] == 'chrome-json': 444 if localeConfig['file_format'] == 'chrome-json' and file.endswith('.json'):
443 if origFile.endswith('.json'): 445 postprocessChromeLocale(path, data)
444 postprocessChromeLocale(path, data) 446 elif localeConfig['file_format'] == 'chrome-json':
445 else: 447 data = json.loads(data)
446 data = json.loads(data) 448 if origFile in data:
447 if origFile in data: 449 fileHandle = codecs.open(path, 'wb', encoding='utf-8')
448 fileHandle = codecs.open(path, 'wb', encoding='utf-8') 450 fileHandle.write(data[origFile]['message'])
Sebastian Noack 2016/02/12 16:33:58 As discussed before, please use io.open instead co
kzar 2016/02/12 17:28:15 As above, want to avoid unrelated changes and this
Sebastian Noack 2016/02/12 17:38:17 Also here, as you change that code anyway, it's go
449 fileHandle.write(data[origFile]['message']) 451 fileHandle.close()
450 fileHandle.close()
451 else: 452 else:
452 fromJSON(path, data) 453 fromJSON(path, data)
453 454
454 # Remove any extra files 455 # Remove any extra files
455 for dir, files in dirs.iteritems(): 456 for dir, files in dirs.iteritems():
456 baseDir = os.path.join(localeConfig['base_path'], dir) 457 baseDir = os.path.join(localeConfig['base_path'], dir)
457 if not os.path.exists(baseDir): 458 if not os.path.exists(baseDir):
458 continue 459 continue
459 for file in os.listdir(baseDir): 460 for file in os.listdir(baseDir):
460 path = os.path.join(baseDir, file) 461 path = os.path.join(baseDir, file)
461 if os.path.isfile(path) and (file.endswith('.json') or file.endswith('.pro perties') or file.endswith('.dtd')) and not file in files: 462 if os.path.isfile(path) and (file.endswith('.json') or file.endswith('.pro perties') or file.endswith('.dtd')) and not file in files:
462 os.remove(path) 463 os.remove(path)
LEFTRIGHT
« build.py ('k') | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld