| 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 |