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

Side by Side Diff: localeTools.py

Issue 29556601: Issue 5777 - Update crowdin interface (Closed)
Patch Set: Created Sept. 26, 2017, 9:15 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 urllib 11 import urllib
11 import urllib2 12 import urllib2
12 from StringIO import StringIO 13 from StringIO import StringIO
13 from ConfigParser import SafeConfigParser 14 from ConfigParser import SafeConfigParser
14 from zipfile import ZipFile 15 from zipfile import ZipFile
15 from xml.parsers.expat import ParserCreate, XML_PARAM_ENTITY_PARSING_ALWAYS 16 from xml.parsers.expat import ParserCreate, XML_PARAM_ENTITY_PARSING_ALWAYS
16 17
17 langMappingGecko = { 18 langMappingGecko = {
18 'bn-BD': 'bn', 19 'bn-BD': 'bn',
19 'br': 'br-FR', 20 'br': 'br-FR',
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 'ta', 86 'ta',
86 'te', 87 'te',
87 'th', 88 'th',
88 'tr', 89 'tr',
89 'uk', 90 'uk',
90 'vi', 91 'vi',
91 'zh-CN', 92 'zh-CN',
92 'zh-TW', 93 'zh-TW',
93 ] 94 ]
94 95
96 CROWDIN_AP_URL = 'https://api.crowdin.com/api/project/{}/{}'
97
98
99 def crowdin_url(project_name, action, key, get={}):
100 url = CROWDIN_AP_URL.format(project_name, action)
101
102 get.update({'key': key})
103
104 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
105
106 query = dict(urlparse.parse_qsl(query))
107 query.update(get)
108
109 return urlparse.urlunparse((
110 scheme, netloc, path, params, urllib.urlencode(query), fragment
111 ))
112
95 113
96 class OrderedDict(dict): 114 class OrderedDict(dict):
97 def __init__(self): 115 def __init__(self):
98 self.__order = [] 116 self.__order = []
99 117
100 def __setitem__(self, key, value): 118 def __setitem__(self, key, value):
101 self.__order.append(key) 119 self.__order.append(key)
102 dict.__setitem__(self, key, value) 120 dict.__setitem__(self, key, value)
103 121
104 def iteritems(self): 122 def iteritems(self):
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 locales.add(mapLocale('BCP-47', match.group(1))) 309 locales.add(mapLocale('BCP-47', match.group(1)))
292 langPacks = urllib2.urlopen('https://addons.mozilla.org/en-US/firefox/la nguage-tools/').read() 310 langPacks = urllib2.urlopen('https://addons.mozilla.org/en-US/firefox/la nguage-tools/').read()
293 for match in re.finditer(r'<tr>.*?</tr>', langPacks, re.S): 311 for match in re.finditer(r'<tr>.*?</tr>', langPacks, re.S):
294 if match.group(0).find('Install Language Pack') >= 0: 312 if match.group(0).find('Install Language Pack') >= 0:
295 match2 = re.search(r'lang="([\w\-]+)"', match.group(0)) 313 match2 = re.search(r'lang="([\w\-]+)"', match.group(0))
296 if match2: 314 if match2:
297 locales.add(mapLocale('BCP-47', match2.group(1))) 315 locales.add(mapLocale('BCP-47', match2.group(1)))
298 316
299 allowed = set() 317 allowed = set()
300 allowedLocales = json.load(urllib2.urlopen( 318 allowedLocales = json.load(urllib2.urlopen(
301 'https://crowdin.com/languages/languages_list?callback=' 319 crowdin_url(projectName, 'supported-languages', key, {'json': 1})))
302 )) 320
303 for locale in allowedLocales: 321 for locale in allowedLocales:
304 allowed.add(locale['code']) 322 allowed.add(locale['crowdin_code'])
305 if not allowed.issuperset(locales): 323 if not allowed.issuperset(locales):
306 print "Warning, following locales aren't allowed by server: " + ', '.joi n(locales - allowed) 324 print "Warning, following locales aren't allowed by server: " + ', '.joi n(locales - allowed)
307 325
308 locales = list(locales & allowed) 326 locales = list(locales & allowed)
309 locales.sort() 327 locales.sort()
310 params = urllib.urlencode([('languages[]', locale) for locale in locales]) 328 params = urllib.urlencode([('languages[]', locale) for locale in locales])
311 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/edit-project ?key=%s' % (projectName, key), params).read() 329
330 result = urllib2.urlopen(
331 crowdin_url(projectName, 'edit-project', key), data=params
332 ).read()
333
312 if result.find('<success') < 0: 334 if result.find('<success') < 0:
313 raise Exception('Server indicated that the operation was not successful\ n' + result) 335 raise Exception('Server indicated that the operation was not successful\ n' + result)
314 336
315 337
316 def postFiles(files, url): 338 def postFiles(files, url):
317 boundary = '----------ThIs_Is_tHe_bouNdaRY_$' 339 boundary = '----------ThIs_Is_tHe_bouNdaRY_$'
318 body = '' 340 body = ''
319 for file, data in files: 341 for file, data in files:
320 body += '--%s\r\n' % boundary 342 body += '--%s\r\n' % boundary
321 body += 'Content-Disposition: form-data; name="files[%s]"; filename="%s" \r\n' % (file, file) 343 body += 'Content-Disposition: form-data; name="files[%s]"; filename="%s" \r\n' % (file, file)
322 body += 'Content-Type: application/octet-stream\r\n' 344 body += 'Content-Type: application/octet-stream\r\n'
323 body += 'Content-Transfer-Encoding: binary\r\n' 345 body += 'Content-Transfer-Encoding: binary\r\n'
324 body += '\r\n' + data + '\r\n' 346 body += '\r\n' + data + '\r\n'
325 body += '--%s--\r\n' % boundary 347 body += '--%s--\r\n' % boundary
326 348
327 body = body.encode('utf-8') 349 body = body.encode('utf-8')
328 request = urllib2.Request(url, StringIO(body)) 350 request = urllib2.Request(url, StringIO(body))
329 request.add_header('Content-Type', 'multipart/form-data; boundary=%s' % boun dary) 351 request.add_header('Content-Type', 'multipart/form-data; boundary=%s' % boun dary)
330 request.add_header('Content-Length', len(body)) 352 request.add_header('Content-Length', len(body))
331 result = urllib2.urlopen(request).read() 353 result = urllib2.urlopen(request).read()
332 if result.find('<success') < 0: 354 if result.find('<success') < 0:
333 raise Exception('Server indicated that the operation was not successful\ n' + result) 355 raise Exception('Server indicated that the operation was not successful\ n' + result)
334 356
335 357
336 def updateTranslationMaster(localeConfig, metadata, dir, projectName, key): 358 def updateTranslationMaster(localeConfig, metadata, dir, projectName, key):
337 result = json.load(urllib2.urlopen('http://api.crowdin.net/api/project/%s/in fo?key=%s&json=1' % (projectName, key))) 359 result = json.load(urllib2.urlopen(
360 crowdin_url(projectName, 'info', key, {'json': 1})))
338 361
339 existing = set(map(lambda f: f['name'], result['files'])) 362 existing = set(map(lambda f: f['name'], result['files']))
340 add = [] 363 add = []
341 update = [] 364 update = []
342 for file in os.listdir(dir): 365 for file in os.listdir(dir):
343 path = os.path.join(dir, file) 366 path = os.path.join(dir, file)
344 if os.path.isfile(path): 367 if os.path.isfile(path):
345 if localeConfig['file_format'] == 'chrome-json' and file.endswith('. json'): 368 if localeConfig['file_format'] == 'chrome-json' and file.endswith('. json'):
346 data = preprocessChromeLocale(path, metadata, True) 369 data = preprocessChromeLocale(path, metadata, True)
347 newName = file 370 newName = file
348 elif localeConfig['file_format'] == 'chrome-json': 371 elif localeConfig['file_format'] == 'chrome-json':
349 fileHandle = codecs.open(path, 'rb', encoding='utf-8') 372 fileHandle = codecs.open(path, 'rb', encoding='utf-8')
350 data = json.dumps({file: {'message': fileHandle.read()}}) 373 data = json.dumps({file: {'message': fileHandle.read()}})
351 fileHandle.close() 374 fileHandle.close()
352 newName = file + '.json' 375 newName = file + '.json'
353 else: 376 else:
354 data = toJSON(path) 377 data = toJSON(path)
355 newName = file + '.json' 378 newName = file + '.json'
356 379
357 if data: 380 if data:
358 if newName in existing: 381 if newName in existing:
359 update.append((newName, data)) 382 update.append((newName, data))
360 existing.remove(newName) 383 existing.remove(newName)
361 else: 384 else:
362 add.append((newName, data)) 385 add.append((newName, data))
363 386
364 if len(add): 387 if len(add):
365 titles = urllib.urlencode([('titles[%s]' % name, re.sub(r'\.json', '', n ame)) for name, data in add]) 388 data = {'titles[{}]'.format(name): re.sub(r'\.json', '', name)
366 postFiles(add, 'http://api.crowdin.net/api/project/%s/add-file?key=%s&ty pe=chrome&%s' % (projectName, key, titles)) 389 for name, data in add}
390 data.update({'type': 'chrome'})
391 postFiles(add, crowdin_url(projectName, 'add-file', key, data))
367 if len(update): 392 if len(update):
368 postFiles(update, 'http://api.crowdin.net/api/project/%s/update-file?key =%s' % (projectName, key)) 393 postFiles(update, crowdin_url(projectName, 'update-file', key))
369 for file in existing: 394 for file in existing:
370 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/delete-f ile?key=%s&file=%s' % (projectName, key, file)).read() 395 result = urllib2.urlopen(
396 crowdin_url(projectName, 'delete-file', key, {'file': file})
397 ).read()
371 if result.find('<success') < 0: 398 if result.find('<success') < 0:
372 raise Exception('Server indicated that the operation was not success ful\n' + result) 399 raise Exception('Server indicated that the operation was not success ful\n' + result)
373 400
374 401
375 def uploadTranslations(localeConfig, metadata, dir, locale, projectName, key): 402 def uploadTranslations(localeConfig, metadata, dir, locale, projectName, key):
376 files = [] 403 files = []
377 for file in os.listdir(dir): 404 for file in os.listdir(dir):
378 path = os.path.join(dir, file) 405 path = os.path.join(dir, file)
379 if os.path.isfile(path): 406 if os.path.isfile(path):
380 if localeConfig['file_format'] == 'chrome-json' and file.endswith('. json'): 407 if localeConfig['file_format'] == 'chrome-json' and file.endswith('. json'):
381 data = preprocessChromeLocale(path, metadata, False) 408 data = preprocessChromeLocale(path, metadata, False)
382 newName = file 409 newName = file
383 elif localeConfig['file_format'] == 'chrome-json': 410 elif localeConfig['file_format'] == 'chrome-json':
384 fileHandle = codecs.open(path, 'rb', encoding='utf-8') 411 fileHandle = codecs.open(path, 'rb', encoding='utf-8')
385 data = json.dumps({file: {'message': fileHandle.read()}}) 412 data = json.dumps({file: {'message': fileHandle.read()}})
386 fileHandle.close() 413 fileHandle.close()
387 newName = file + '.json' 414 newName = file + '.json'
388 else: 415 else:
389 data = toJSON(path) 416 data = toJSON(path)
390 newName = file + '.json' 417 newName = file + '.json'
391 418
392 if data: 419 if data:
393 files.append((newName, data)) 420 files.append((newName, data))
394 if len(files): 421 if len(files):
395 postFiles(files, 'http://api.crowdin.net/api/project/%s/upload-translati on?key=%s&language=%s' % ( 422 language = mapLocale(localeConfig['name_format'], locale)
396 projectName, key, mapLocale(localeConfig['name_format'], locale)) 423 postFiles(files,
397 ) 424 crowdin_url(projectName, 'upload-translation', key,
425 {'language': language}))
398 426
399 427
400 def getTranslations(localeConfig, projectName, key): 428 def getTranslations(localeConfig, projectName, key):
401 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/export?key=% s' % (projectName, key)).read() 429 result = urllib2.urlopen(crowdin_url(projectName, 'export', key)).read()
402 if result.find('<success') < 0: 430 if result.find('<success') < 0:
403 raise Exception('Server indicated that the operation was not successful\ n' + result) 431 raise Exception('Server indicated that the operation was not successful\ n' + result)
404 432
405 result = urllib2.urlopen('http://api.crowdin.net/api/project/%s/download/all .zip?key=%s' % (projectName, key)).read() 433 result = urllib2.urlopen(
434 crowdin_url(projectName, 'download/all.zip', key)).read()
406 zip = ZipFile(StringIO(result)) 435 zip = ZipFile(StringIO(result))
407 dirs = {} 436 dirs = {}
408 437
409 normalizedDefaultLocale = localeConfig['default_locale'] 438 normalizedDefaultLocale = localeConfig['default_locale']
410 if localeConfig['name_format'] == 'ISO-15897': 439 if localeConfig['name_format'] == 'ISO-15897':
411 normalizedDefaultLocale = normalizedDefaultLocale.replace('_', '-') 440 normalizedDefaultLocale = normalizedDefaultLocale.replace('_', '-')
412 normalizedDefaultLocale = mapLocale(localeConfig['name_format'], 441 normalizedDefaultLocale = mapLocale(localeConfig['name_format'],
413 normalizedDefaultLocale) 442 normalizedDefaultLocale)
414 443
415 for info in zip.infolist(): 444 for info in zip.infolist():
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 492
464 # Remove any extra files 493 # Remove any extra files
465 for dir, files in dirs.iteritems(): 494 for dir, files in dirs.iteritems():
466 baseDir = os.path.join(localeConfig['base_path'], dir) 495 baseDir = os.path.join(localeConfig['base_path'], dir)
467 if not os.path.exists(baseDir): 496 if not os.path.exists(baseDir):
468 continue 497 continue
469 for file in os.listdir(baseDir): 498 for file in os.listdir(baseDir):
470 path = os.path.join(baseDir, file) 499 path = os.path.join(baseDir, file)
471 if os.path.isfile(path) and (file.endswith('.json') or file.endswith ('.properties') or file.endswith('.dtd')) and not file in files: 500 if os.path.isfile(path) and (file.endswith('.json') or file.endswith ('.properties') or file.endswith('.dtd')) and not file in files:
472 os.remove(path) 501 os.remove(path)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld