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

Side by Side Diff: packagerChrome.py

Issue 29561557: Issue 5763 - Target languages supported by Firefox (Closed)
Patch Set: Fixed import_locales() Created Oct. 5, 2017, 6:25 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
« localeTools.py ('K') | « localeTools.py ('k') | 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 errno 5 import errno
6 import io 6 import io
7 import json 7 import json
8 import os 8 import os
9 import re 9 import re
10 from StringIO import StringIO 10 from StringIO import StringIO
11 import struct 11 import struct
12 import sys 12 import sys
13 import collections 13 import collections
14 import glob
14 15
15 from packager import (readMetadata, getDefaultFileName, getBuildVersion, 16 from packager import (readMetadata, getDefaultFileName, getBuildVersion,
16 getTemplate, Files) 17 getTemplate, Files)
17 18
18 defaultLocale = 'en_US' 19 defaultLocale = 'en_US'
19 20
20 21
21 def getIgnoredFiles(params): 22 def getIgnoredFiles(params):
22 return {'store.description'} 23 return {'store.description'}
23 24
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 value = match.group(1) 207 value = match.group(1)
207 else: 208 else:
208 index = value.find('&') 209 index = value.find('&')
209 if index >= 0: 210 if index >= 0:
210 value = value[0:index] + value[index + 1:] 211 value = value[0:index] + value[index + 1:]
211 212
212 data[key] = {'message': value} 213 data[key] = {'message': value}
213 214
214 215
215 def import_locales(params, files): 216 def import_locales(params, files):
216 import localeTools 217 for item in params['metadata'].items('import_locales'):
217 218 filename, keys = item
218 # FIXME: localeTools doesn't use real Chrome locales, it uses dash as 219 for sourceFile in glob.glob(os.path.join(os.path.dirname(item.source),
219 # separator instead. 220 *filename.split('/'))):
220 convert_locale_code = lambda code: code.replace('-', '_') 221 locale = sourceFile.split(os.path.sep)[-2].replace('-', '_')
Sebastian Noack 2017/10/05 19:01:43 Replacing dashes with underscore here is only nece
221 222 targetFile = os.path.join('_locales', locale, 'messages.json')
222 # We need to map Chrome locales to Gecko locales. Start by mapping Chrome 223 data = json.loads(files.get(targetFile, '{}').decode('utf-8'))
223 # locales to themselves, merely with the dash as separator.
224 locale_mapping = {convert_locale_code(l): l for l in localeTools.chromeLocal es}
225
226 # Convert values to Crowdin locales first (use Chrome => Crowdin mapping).
227 for chrome_locale, crowdin_locale in localeTools.langMappingChrome.iteritems ():
228 locale_mapping[convert_locale_code(chrome_locale)] = crowdin_locale
229
230 # Now convert values to Gecko locales (use Gecko => Crowdin mapping).
231 reverse_mapping = {v: k for k, v in locale_mapping.iteritems()}
232 for gecko_locale, crowdin_locale in localeTools.langMappingGecko.iteritems() :
233 if crowdin_locale in reverse_mapping:
234 locale_mapping[reverse_mapping[crowdin_locale]] = gecko_locale
235
236 for target, source in locale_mapping.iteritems():
237 targetFile = '_locales/%s/messages.json' % target
238 if not targetFile in files:
239 continue
240
241 for item in params['metadata'].items('import_locales'):
242 fileName, keys = item
243 parts = map(lambda n: source if n == '*' else n, fileName.split('/') )
244 sourceFile = os.path.join(os.path.dirname(item.source), *parts)
245 incompleteMarker = os.path.join(os.path.dirname(sourceFile), '.incom plete')
246 if not os.path.exists(sourceFile) or os.path.exists(incompleteMarker ):
Sebastian Noack 2017/10/05 19:01:43 I didn't care to keep supporting the .incomplete m
247 continue
248
249 data = json.loads(files[targetFile].decode('utf-8'))
Sebastian Noack 2017/10/05 19:01:43 I only tested the translation commands after remov
250 224
251 try: 225 try:
252 # The WebExtensions (.json) and Gecko format provide 226 # The WebExtensions (.json) and Gecko format provide
253 # translations differently and/or provide additional 227 # translations differently and/or provide additional
254 # information like e.g. "placeholders". We want to adhere to 228 # information like e.g. "placeholders". We want to adhere to
255 # that and preserve the addtional info. 229 # that and preserve the addtional info.
256 if sourceFile.endswith('.json'): 230 if sourceFile.endswith('.json'):
257 with io.open(sourceFile, 'r', encoding='utf-8') as handle: 231 with io.open(sourceFile, 'r', encoding='utf-8') as handle:
258 sourceData = json.load(handle) 232 sourceData = json.load(handle)
259 import_string = import_string_webext 233 import_string = import_string_webext
260 else: 234 else:
235 import localeTools
261 sourceData = localeTools.readFile(sourceFile) 236 sourceData = localeTools.readFile(sourceFile)
262 import_string = import_string_gecko 237 import_string = import_string_gecko
263 238
264 # Resolve wildcard imports 239 # Resolve wildcard imports
265 if keys == '*' or keys == '=*': 240 if keys == '*' or keys == '=*':
266 importList = sourceData.keys() 241 importList = sourceData.keys()
267 importList = filter(lambda k: not k.startswith('_'), importL ist) 242 importList = filter(lambda k: not k.startswith('_'), importL ist)
268 if keys == '=*': 243 if keys == '=*':
269 importList = map(lambda k: '=' + k, importList) 244 importList = map(lambda k: '=' + k, importList)
270 keys = ' '.join(importList) 245 keys = ' '.join(importList)
(...skipping 18 matching lines...) Expand all
289 264
290 files[targetFile] = toJson(data) 265 files[targetFile] = toJson(data)
291 266
292 267
293 def truncate(text, length_limit): 268 def truncate(text, length_limit):
294 if len(text) <= length_limit: 269 if len(text) <= length_limit:
295 return text 270 return text
296 return text[:length_limit - 1].rstrip() + u'\u2026' 271 return text[:length_limit - 1].rstrip() + u'\u2026'
297 272
298 273
299 def fixTranslationsForCWS(files): 274 def fix_translations_for_chrome(files):
300 # Chrome Web Store requires messages used in manifest.json to be present in
301 # all languages. It also enforces length limits for extension names and
302 # descriptions.
303 defaults = {} 275 defaults = {}
304 data = json.loads(files['_locales/%s/messages.json' % defaultLocale]) 276 data = json.loads(files['_locales/%s/messages.json' % defaultLocale])
305 for match in re.finditer(r'__MSG_(\S+)__', files['manifest.json']): 277 for match in re.finditer(r'__MSG_(\S+)__', files['manifest.json']):
306 name = match.group(1) 278 name = match.group(1)
307 defaults[name] = data[name] 279 defaults[name] = data[name]
308 280
309 limits = {} 281 limits = {}
310 manifest = json.loads(files['manifest.json']) 282 manifest = json.loads(files['manifest.json'])
311 for key, limit in (('name', 45), ('description', 132), ('short_name', 12)): 283 for key, limit in (('name', 45), ('description', 132), ('short_name', 12)):
312 match = re.search(r'__MSG_(\S+)__', manifest.get(key, '')) 284 match = re.search(r'__MSG_(\S+)__', manifest.get(key, ''))
313 if match: 285 if match:
314 limits[match.group(1)] = limit 286 limits[match.group(1)] = limit
315 287
316 for filename in files: 288 for path in list(files):
317 if not filename.startswith('_locales/') or not filename.endswith('/messa ges.json'): 289 match = re.search(r'^_locales/(?:es_(AR|CL|(MX))|[^/]+)/(.*)', path)
290 if not match:
318 continue 291 continue
319 292
320 data = json.loads(files[filename]) 293 # The Chrome Web Store requires messages used in manifest.json to
321 for name, info in defaults.iteritems(): 294 # be present in all languages, and enforces length limits on
322 data.setdefault(name, info) 295 # extension name and description.
323 for name, limit in limits.iteritems(): 296 is_latam, is_mexican, filename = match.groups()
324 if name in data: 297 if filename == 'messages.json':
325 data[name]['message'] = truncate(data[name]['message'], limit) 298 data = json.loads(files[path])
326 files[filename] = toJson(data) 299 for name, info in defaults.iteritems():
300 data.setdefault(name, info)
301 for name, limit in limits.iteritems():
302 info = data.get(name)
303 if info:
304 info['message'] = truncate(info['message'], limit)
305 files[path] = toJson(data)
306
307 # Chrome combines Latin American dialects of Spanish into es-419.
308 if is_latam:
309 data = files.pop(path)
310 if is_mexican:
311 files['_locales/es_419/' + filename] = data
327 312
328 313
329 def signBinary(zipdata, keyFile): 314 def signBinary(zipdata, keyFile):
330 from Crypto.Hash import SHA 315 from Crypto.Hash import SHA
331 from Crypto.PublicKey import RSA 316 from Crypto.PublicKey import RSA
332 from Crypto.Signature import PKCS1_v1_5 317 from Crypto.Signature import PKCS1_v1_5
333 318
334 try: 319 try:
335 with open(keyFile, 'rb') as file: 320 with open(keyFile, 'rb') as file:
336 key = RSA.importKey(file.read()) 321 key = RSA.importKey(file.read())
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
396 files.preprocess( 381 files.preprocess(
397 [f for f, _ in metadata.items('preprocess')], 382 [f for f, _ in metadata.items('preprocess')],
398 {'needsExt': True} 383 {'needsExt': True}
399 ) 384 )
400 385
401 if metadata.has_section('import_locales'): 386 if metadata.has_section('import_locales'):
402 import_locales(params, files) 387 import_locales(params, files)
403 388
404 files['manifest.json'] = createManifest(params, files) 389 files['manifest.json'] = createManifest(params, files)
405 if type == 'chrome': 390 if type == 'chrome':
406 fixTranslationsForCWS(files) 391 fix_translations_for_chrome(files)
407 392
408 if devenv: 393 if devenv:
409 import buildtools 394 import buildtools
410 import random 395 import random
411 files.read(os.path.join(buildtools.__path__[0], 'chromeDevenvPoller__.js '), relpath='devenvPoller__.js') 396 files.read(os.path.join(buildtools.__path__[0], 'chromeDevenvPoller__.js '), relpath='devenvPoller__.js')
412 files['devenvVersion__'] = str(random.random()) 397 files['devenvVersion__'] = str(random.random())
413 398
414 if metadata.has_option('general', 'testScripts'): 399 if metadata.has_option('general', 'testScripts'):
415 files['qunit/index.html'] = createScriptPage( 400 files['qunit/index.html'] = createScriptPage(
416 params, 'testIndex.html.tmpl', ('general', 'testScripts') 401 params, 'testIndex.html.tmpl', ('general', 'testScripts')
417 ) 402 )
418 403
419 zipdata = files.zipToString() 404 zipdata = files.zipToString()
420 signature = None 405 signature = None
421 pubkey = None 406 pubkey = None
422 if keyFile != None: 407 if keyFile != None:
423 signature = signBinary(zipdata, keyFile) 408 signature = signBinary(zipdata, keyFile)
424 pubkey = getPublicKey(keyFile) 409 pubkey = getPublicKey(keyFile)
425 writePackage(outFile, pubkey, signature, zipdata) 410 writePackage(outFile, pubkey, signature, zipdata)
OLDNEW
« localeTools.py ('K') | « localeTools.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld