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: Belatedly addressed Sebastian's comments Created Sept. 28, 2017, 9:29 p.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 urlparse
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 'ta', 86 'ta',
87 'te', 87 'te',
88 'th', 88 'th',
89 'tr', 89 'tr',
90 'uk', 90 'uk',
91 'vi', 91 'vi',
92 'zh-CN', 92 'zh-CN',
93 'zh-TW', 93 'zh-TW',
94 ] 94 ]
95 95
96 CROWDIN_AP_URL = 'https://api.crowdin.com/api/project/{}/{}' 96 CROWDIN_AP_URL = 'https://api.crowdin.com/api/project'
97 97
98 98
99 def crowdin_url(project_name, action, key, get={}): 99 def crowdin_url(project_name, action, key, get={}):
100 """Create a valid url for a crowdin endpoint.""" 100 """Create a valid url for a crowdin endpoint."""
101 url = CROWDIN_AP_URL.format(project_name, action) 101 return '{}/{}/{}?{}'.format(CROWDIN_AP_URL,
tlucas 2017/09/28 21:33:58 as discussed: simpler building of the desired url
Sebastian Noack 2017/09/28 22:18:37 Is this even worth a separate function now? I woul
tlucas 2017/09/29 09:09:37 I agree, no extra function necessary - Done.
102 get['key'] = key 102 urllib.quote(project_name),
103 get['json'] = 1 103 urllib.quote(action),
104 104 urllib.urlencode(dict(get, key=key, json=1)))
105 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
106
107 query = urlparse.parse_qs(query)
108 query.update(get)
109
110 return urlparse.urlunparse((
111 scheme, netloc, path, params, urllib.urlencode(query), fragment
112 ))
113 105
114 106
115 def crowdin_request(project_name, action, key, get={}, post_data=None, 107 def crowdin_request(project_name, action, key, get={}, post_data=None,
116 headers={}, raw=False): 108 headers={}, raw=False):
117 """Perform a call to crowdin and raise an Exception on failure.""" 109 """Perform a call to crowdin and raise an Exception on failure."""
118 request = urllib2.Request( 110 request = urllib2.Request(
119 crowdin_url(project_name, action, key, get), 111 crowdin_url(project_name, action, key, get),
120 post_data, 112 post_data,
121 headers, 113 headers,
122 ) 114 )
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 locales.sort() 340 locales.sort()
349 params = urllib.urlencode([('languages[]', locale) for locale in locales]) 341 params = urllib.urlencode([('languages[]', locale) for locale in locales])
350 342
351 crowdin_request(projectName, 'edit-project', key, post_data=params) 343 crowdin_request(projectName, 'edit-project', key, post_data=params)
352 344
353 345
354 def crowdin_prepare_upload(files): 346 def crowdin_prepare_upload(files):
355 """Create a post body and matching headers, which Crowdin can handle.""" 347 """Create a post body and matching headers, which Crowdin can handle."""
356 boundary = '----------ThIs_Is_tHe_bouNdaRY_$' 348 boundary = '----------ThIs_Is_tHe_bouNdaRY_$'
357 body = '' 349 body = ''
358 for file, data in files: 350 for name, data in files:
359 body += '--%s\r\n' % boundary 351 body += ('--{boundary}\r\n'
360 body += 'Content-Disposition: form-data; name="files[%s]"; filename="%s" \r\n' % (file, file) 352 'Content-Disposition: form-data; name="files[{name}]"; '
361 body += 'Content-Type: application/octet-stream\r\n' 353 'filename="{name}"\r\n'
Sebastian Noack 2017/09/28 22:18:37 Nit: You can avoid wrapping before the end of line
tlucas 2017/09/29 09:09:37 Done.
362 body += 'Content-Transfer-Encoding: binary\r\n' 354 'Content-Type: application/octet-stream; charset=utf-8\r\n'
tlucas 2017/09/28 21:33:58 as discussed: charset in each file's individual Co
Sebastian Noack 2017/09/28 22:18:37 This should be "application/json", I guess?
Sebastian Noack 2017/09/28 22:51:55 Or even better, you could dynamically detect the t
tlucas 2017/09/29 09:09:37 Good point - Done.
363 body += '\r\n' + data + '\r\n' 355 'Content-Transfer-Encoding: binary\r\n'
364 body += '--%s--\r\n' % boundary 356 '\r\n{data}\r\n'
357 '--{boundary}--\r\n').format(boundary=boundary, name=name,
358 data=data)
365 359
366 body = body.encode('utf-8') 360 body = body.encode('utf-8')
367 return ( 361 return (
368 StringIO(body), 362 StringIO(body),
369 { 363 {
370 'Content-Type': ('multipart/form-data; ; charset=utf-8; ' 364 'Content-Type': ('multipart/form-data; boundary=' + boundary),
371 'boundary=' + boundary),
372 'Content-Length': len(body) 365 'Content-Length': len(body)
373 } 366 },
374 ) 367 )
375 368
376 369
377 def updateTranslationMaster(localeConfig, metadata, dir, projectName, key): 370 def updateTranslationMaster(localeConfig, metadata, dir, projectName, key):
378 result = crowdin_request(projectName, 'info', key) 371 result = crowdin_request(projectName, 'info', key)
379 372
380 existing = set(map(lambda f: f['name'], result['files'])) 373 existing = set(map(lambda f: f['name'], result['files']))
381 add = [] 374 add = []
382 update = [] 375 update = []
383 for file in os.listdir(dir): 376 for file in os.listdir(dir):
(...skipping 12 matching lines...) Expand all
396 newName = file + '.json' 389 newName = file + '.json'
397 390
398 if data: 391 if data:
399 if newName in existing: 392 if newName in existing:
400 update.append((newName, data)) 393 update.append((newName, data))
401 existing.remove(newName) 394 existing.remove(newName)
402 else: 395 else:
403 add.append((newName, data)) 396 add.append((newName, data))
404 397
405 if len(add): 398 if len(add):
406 data = {'titles[{}]'.format(name): re.sub(r'\.json', '', name) 399 query = {'titles[{}]'.format(name): os.path.splitext(name)[0]
tlucas 2017/09/28 21:33:58 os.path.splitext as discussed
407 for name, data in add} 400 for name, _ in add}
408 data['type'] = 'chrome' 401 query['type'] = 'chrome'
409 data, headers = crowdin_prepare_upload(add) 402 data, headers = crowdin_prepare_upload(add)
410 crowdin_request(projectName, 'add-file', key, post_data=data, 403 crowdin_request(projectName, 'add-file', key, query, post_data=data,
tlucas 2017/09/28 21:33:58 Hasn't benn noticed before: the query was overwrit
411 headers=headers) 404 headers=headers)
412 if len(update): 405 if len(update):
413 data, headers = crowdin_prepare_upload(update) 406 data, headers = crowdin_prepare_upload(update)
414 crowdin_request(projectName, 'update-file', key, post_data=data, 407 crowdin_request(projectName, 'update-file', key, post_data=data,
415 headers=headers) 408 headers=headers)
416 for file in existing: 409 for file in existing:
417 crowdin_request(projectName, 'delete-file', key, {'file': file}) 410 crowdin_request(projectName, 'delete-file', key, {'file': file})
418 411
419 412
420 def uploadTranslations(localeConfig, metadata, dir, locale, projectName, key): 413 def uploadTranslations(localeConfig, metadata, dir, locale, projectName, key):
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
513 506
514 # Remove any extra files 507 # Remove any extra files
515 for dir, files in dirs.iteritems(): 508 for dir, files in dirs.iteritems():
516 baseDir = os.path.join(localeConfig['base_path'], dir) 509 baseDir = os.path.join(localeConfig['base_path'], dir)
517 if not os.path.exists(baseDir): 510 if not os.path.exists(baseDir):
518 continue 511 continue
519 for file in os.listdir(baseDir): 512 for file in os.listdir(baseDir):
520 path = os.path.join(baseDir, file) 513 path = os.path.join(baseDir, file)
521 if os.path.isfile(path) and (file.endswith('.json') or file.endswith ('.properties') or file.endswith('.dtd')) and not file in files: 514 if os.path.isfile(path) and (file.endswith('.json') or file.endswith ('.properties') or file.endswith('.dtd')) and not file in files:
522 os.remove(path) 515 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