json_to_js.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #!/usr/bin/python
  2. # Converts .json files into .js files for use within Blockly apps.
  3. #
  4. # Copyright 2013 Google Inc.
  5. # https://developers.google.com/blockly/
  6. #
  7. # Licensed under the Apache License, Version 2.0 (the "License");
  8. # you may not use this file except in compliance with the License.
  9. # You may obtain a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS,
  15. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18. import argparse
  19. import codecs # for codecs.open(..., 'utf-8')
  20. import glob
  21. import json # for json.load()
  22. import os # for os.path()
  23. import subprocess # for subprocess.check_call()
  24. from common import InputError
  25. from common import read_json_file
  26. # Store parsed command-line arguments in global variable.
  27. args = None
  28. def _create_xlf(target_lang):
  29. """Creates a <target_lang>.xlf file for Soy.
  30. Args:
  31. target_lang: The ISO 639 language code for the target language.
  32. This is used in the name of the file and in the metadata.
  33. Returns:
  34. A pointer to a file to which the metadata has been written.
  35. Raises:
  36. IOError: An error occurred while opening or writing the file.
  37. """
  38. filename = os.path.join(os.curdir, args.output_dir, target_lang + '.xlf')
  39. out_file = codecs.open(filename, 'w', 'utf-8')
  40. out_file.write("""<?xml version="1.0" encoding="UTF-8"?>
  41. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  42. <file original="SoyMsgBundle"
  43. datatype="x-soy-msg-bundle"
  44. xml:space="preserve"
  45. source-language="{0}"
  46. target-language="{1}">
  47. <body>""".format(args.source_lang, target_lang))
  48. return out_file
  49. def _close_xlf(xlf_file):
  50. """Closes a <target_lang>.xlf file created with create_xlf().
  51. This includes writing the terminating XML.
  52. Args:
  53. xlf_file: A pointer to a file created by _create_xlf().
  54. Raises:
  55. IOError: An error occurred while writing to or closing the file.
  56. """
  57. xlf_file.write("""
  58. </body>
  59. </file>
  60. </xliff>
  61. """)
  62. xlf_file.close()
  63. def _process_file(path_to_json, target_lang, key_dict):
  64. """Creates an .xlf file corresponding to the specified .json input file.
  65. The name of the input file must be target_lang followed by '.json'.
  66. The name of the output file will be target_lang followed by '.js'.
  67. Args:
  68. path_to_json: Path to the directory of xx.json files.
  69. target_lang: A IETF language code (RFC 4646), such as 'es' or 'pt-br'.
  70. key_dict: Dictionary mapping Blockly keys (e.g., Maze.turnLeft) to
  71. Closure keys (hash numbers).
  72. Raises:
  73. IOError: An I/O error occurred with an input or output file.
  74. InputError: Input JSON could not be parsed.
  75. KeyError: Key found in input file but not in key file.
  76. """
  77. keyfile = os.path.join(path_to_json, target_lang + '.json')
  78. j = read_json_file(keyfile)
  79. out_file = _create_xlf(target_lang)
  80. for key in j:
  81. if key != '@metadata':
  82. try:
  83. identifier = key_dict[key]
  84. except KeyError, e:
  85. print('Key "%s" is in %s but not in %s' %
  86. (key, keyfile, args.key_file))
  87. raise e
  88. target = j.get(key)
  89. out_file.write(u"""
  90. <trans-unit id="{0}" datatype="html">
  91. <target>{1}</target>
  92. </trans-unit>""".format(identifier, target))
  93. _close_xlf(out_file)
  94. def main():
  95. """Parses arguments and iterates over files."""
  96. # Set up argument parser.
  97. parser = argparse.ArgumentParser(description='Convert JSON files to JS.')
  98. parser.add_argument('--source_lang', default='en',
  99. help='ISO 639-1 source language code')
  100. parser.add_argument('--output_dir', default='generated',
  101. help='relative directory for output files')
  102. parser.add_argument('--key_file', default='json' + os.path.sep + 'keys.json',
  103. help='relative path to input keys file')
  104. parser.add_argument('--template', default='template.soy')
  105. parser.add_argument('--path_to_jar',
  106. default='..' + os.path.sep + 'apps' + os.path.sep
  107. + '_soy',
  108. help='relative path from working directory to '
  109. 'SoyToJsSrcCompiler.jar')
  110. parser.add_argument('files', nargs='+', help='input files')
  111. # Initialize global variables.
  112. global args
  113. args = parser.parse_args()
  114. # Make sure output_dir ends with slash.
  115. if (not args.output_dir.endswith(os.path.sep)):
  116. args.output_dir += os.path.sep
  117. # Read in keys.json, mapping descriptions (e.g., Maze.turnLeft) to
  118. # Closure keys (long hash numbers).
  119. key_file = open(args.key_file)
  120. key_dict = json.load(key_file)
  121. key_file.close()
  122. # Process each input file.
  123. print('Creating .xlf files...')
  124. processed_langs = []
  125. if len(args.files) == 1:
  126. # Windows does not expand globs automatically.
  127. args.files = glob.glob(args.files[0])
  128. for arg_file in args.files:
  129. (path_to_json, filename) = os.path.split(arg_file)
  130. if not filename.endswith('.json'):
  131. raise InputError(filename, 'filenames must end with ".json"')
  132. target_lang = filename[:filename.index('.')]
  133. if not target_lang in ('qqq', 'keys'):
  134. processed_langs.append(target_lang)
  135. _process_file(path_to_json, target_lang, key_dict)
  136. # Output command line for Closure compiler.
  137. if processed_langs:
  138. print('Creating .js files...')
  139. processed_lang_list = ','.join(processed_langs)
  140. subprocess.check_call([
  141. 'java',
  142. '-jar', os.path.join(args.path_to_jar, 'SoyToJsSrcCompiler.jar'),
  143. '--locales', processed_lang_list,
  144. '--messageFilePathFormat', args.output_dir + '{LOCALE}.xlf',
  145. '--outputPathFormat', args.output_dir + '{LOCALE}.js',
  146. '--srcs', args.template])
  147. if len(processed_langs) == 1:
  148. print('Created ' + processed_lang_list + '.js in ' + args.output_dir)
  149. else:
  150. print('Created {' + processed_lang_list + '}.js in ' + args.output_dir)
  151. for lang in processed_langs:
  152. os.remove(args.output_dir + lang + '.xlf')
  153. print('Removed .xlf files.')
  154. if __name__ == '__main__':
  155. main()