Package translate :: Package storage :: Module jsonl10n
[hide private]
[frames] | no frames]

Source Code for Module translate.storage.jsonl10n

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2007,2009,2010 Zuza Software Foundation 
  5  # 
  6  # This file is part of the Translate Toolkit. 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, see <http://www.gnu.org/licenses/>. 
 20   
 21  """Class that manages JSON data files for translation 
 22   
 23  JSON is an acronym for JavaScript Object Notation, it is an open standard 
 24  designed for human-readable data interchange. 
 25   
 26  JSON basic types 
 27  ================ 
 28    - Number (integer or real) 
 29    - String (double-quoted Unicode with backslash escaping) 
 30    - Boolean (true or false) 
 31    - Array (an ordered sequence of values, comma-separated and enclosed 
 32      in square brackets) 
 33    - Object (a collection of key:value pairs, comma-separated and 
 34      enclosed in curly braces) 
 35    - null 
 36   
 37  Example 
 38  ======= 
 39   
 40    { 
 41         "firstName": "John", 
 42         "lastName": "Smith", 
 43         "age": 25, 
 44         "address": { 
 45             "streetAddress": "21 2nd Street", 
 46             "city": "New York", 
 47             "state": "NY", 
 48             "postalCode": "10021" 
 49         }, 
 50         "phoneNumber": [ 
 51             { 
 52               "type": "home", 
 53               "number": "212 555-1234" 
 54             }, 
 55             { 
 56               "type": "fax", 
 57               "number": "646 555-4567" 
 58             } 
 59         ] 
 60     } 
 61   
 62   
 63  TODO 
 64  ==== 
 65    - Handle \u and other escapes in Unicode 
 66    - Manage data type storage and conversion. True -> "True" -> True 
 67    - Sort the extracted data to the order of the JSON file 
 68   
 69  """ 
 70   
 71  import os 
 72  from StringIO import StringIO 
 73  try: 
 74      import json as json #available since Python 2.6 
 75  except ImportError: 
 76      import simplejson as json #API compatible with the json module 
 77   
 78  from translate.storage import base 
 79   
 80   
81 -class JsonUnit(base.TranslationUnit):
82 """A JSON entry""" 83
84 - def __init__(self, source=None, ref=None, item=None, encoding="UTF-8"):
85 self._id = None 86 self._item = str(os.urandom(30)) 87 if item is not None: 88 self._item = item 89 self._ref = {} 90 if ref is not None: 91 self._ref = ref 92 if ref is None and item is None: 93 self._ref[self._item] = "" 94 if source: 95 self.source = source 96 super(JsonUnit, self).__init__(source)
97
98 - def getsource(self):
99 return self.gettarget()
100
101 - def setsource(self, source):
102 self.settarget(source)
103 source = property(getsource, setsource) 104
105 - def gettarget(self):
106 107 def change_type(value): 108 if isinstance(value, bool): 109 return str(value) 110 return value 111 112 return newvalue
113 if isinstance(self._ref, list): 114 return change_type(self._ref[self._item]) 115 elif isinstance(self._ref, dict): 116 return change_type(self._ref[self._item])
117
118 - def settarget(self, target):
119 120 def change_type(oldvalue, newvalue): 121 if isinstance(oldvalue, bool): 122 newvalue = bool(newvalue) 123 return newvalue
124 125 if isinstance(self._ref, list): 126 self._ref[int(self._item)] = change_type(self._ref[int(self._item)], 127 target) 128 elif isinstance(self._ref, dict): 129 self._ref[self._item] = change_type(self._ref[self._item], target) 130 else: 131 raise ValueError("We don't know how to handle:\n" 132 "Type: %s\n" 133 "Value: %s" % (type(self._ref), target)) 134 target = property(gettarget, settarget) 135
136 - def setid(self, value):
137 self._id = value
138
139 - def getid(self):
140 return self._id
141
142 - def getlocations(self):
143 return [self.getid()]
144 145
146 -class JsonFile(base.TranslationStore):
147 """A JSON file""" 148 UnitClass = JsonUnit 149
150 - def __init__(self, inputfile=None, unitclass=UnitClass, filter=None):
151 """construct a JSON file, optionally reading in from inputfile.""" 152 base.TranslationStore.__init__(self, unitclass=unitclass) 153 self._filter = filter 154 self.filename = '' 155 self._file = u'' 156 if inputfile is not None: 157 self.parse(inputfile)
158
159 - def __str__(self):
160 return json.dumps(self._file, sort_keys=True, 161 indent=4, ensure_ascii=False).encode('utf-8')
162
163 - def _extract_translatables(self, data, stop=None, prev="", name_node=None, 164 name_last_node=None, last_node=None):
165 """Recursive function to extract items from the data files 166 167 data - is the current branch to walk down 168 stop - is a list of leaves to extract or None to extract everything 169 prev - is the heirchy of the tree at this iteration 170 last - is the name of the last node 171 last_node - the last list or dict 172 """ 173 usable = {} 174 if isinstance(data, dict): 175 for k, v in data.iteritems(): 176 usable.update(self._extract_translatables(v, stop, 177 "%s.%s" % (prev, k), 178 k, None, data)) 179 elif isinstance(data, list): 180 for i, item in enumerate(data): 181 usable.update(self._extract_translatables(item, stop, 182 "%s[%s]" % (prev, i), 183 i, name_node, data)) 184 elif isinstance(data, str) or isinstance(data, unicode): 185 if (stop is None \ 186 or (isinstance(last_node, dict) and name_node in stop) \ 187 or (isinstance(last_node, list) and name_last_node in stop)): 188 usable[prev] = (data, last_node, name_node) 189 elif isinstance(data, bool): 190 if (stop is None \ 191 or (isinstance(last_node, dict) and name_node in stop) \ 192 or (isinstance(last_node, list) and name_last_node in stop)): 193 usable[prev] = (str(data), last_node, name_node) 194 elif data is None: 195 pass 196 #usable[prev] = None 197 else: 198 raise ValueError("We don't handle these values:\n" 199 "Type: %s\n" 200 "Data: %s\n" 201 "Previous: %s" % (type(data), data, prev)) 202 return usable
203
204 - def parse(self, input):
205 """parse the given file or file source string""" 206 if hasattr(input, 'name'): 207 self.filename = input.name 208 elif not getattr(self, 'filename', ''): 209 self.filename = '' 210 if hasattr(input, "read"): 211 src = input.read() 212 input.close() 213 input = src 214 if isinstance(input, str): 215 input = StringIO(input) 216 try: 217 self._file = json.load(input) 218 except ValueError, e: 219 raise base.ParseError(e.message) 220 221 for k, v in self._extract_translatables(self._file, 222 stop=self._filter).iteritems(): 223 data, ref, item = v 224 unit = self.UnitClass(data, ref, item) 225 unit.setid(k) 226 self.addunit(unit)
227