def create(self, project, slug, name, method, source_language, content, user=None, extra_data={}): """Create a new resource. Any extra arguments will be passed to the Resource initialization method as is. There is no transaction used. The caller is supposed to handle this. Args: project: A Project instance which the resource will belong to. slug: The slug of the resource. name: The name of the resource. method: The i18n method of theresource. source_language: A Language instance of the source language set. content: The content of the resource's source file. user: The user that creates the resource. extra_data: Any extra info for the Resource constructor. Returns: A two-elements tuple. The first element is the number of added strings and the second the number of updated strings. """ # save resource try: r = Resource(project=project, source_language=source_language, slug=slug, name=name) r.i18n_method = method r.full_clean() for key in ifilter(lambda k: k != "content", extra_data.iterkeys()): setattr(r, key, extra_data[key]) except Exception, e: logger.warning( "Error while creating resource %s for project %s: %s" % (slug, project.slug, e), exc_info=True ) raise ResourceBackendError("Invalid arguments given: %s" % e)
def msgfmt_check(po_contents, ispot=False, with_exceptions=True): """Run a `msgfmt -c` on the file contents. Raise a PoParseError in case the stderror has errors/warnings or the command execution returns Error. """ try: if ispot: command = 'msgfmt -o /dev/null --check-format --check-domain -' else: command = 'msgfmt -o /dev/null -c -' status, stdout, stderr = run_command( command, _input=po_contents.encode('UTF-8'), with_extended_output=True, with_exceptions=with_exceptions ) # Not sure why msgfmt sends its output to stderr instead of stdout #if 'warning:' in stderr or 'too many errors, aborting' in stderr: if 'too many errors, aborting' in stderr: logger.warning('msgfmt %s: %s' % (status, stderr, )) raise CommandError(command, status, stderr) except CommandError, e: logger.warning("pofile: The 'msgfmt -c' check failed.") raise PoParseError, ugettext("Your file failed a correctness check " "(msgfmt -c). It returned the following error:\n\n%s\n\n" "Please run this command on " "your system to see the errors for yourself." % e.stderr.lstrip('<stdin>:'))
def _create(self, request, project_slug, data): # Check for unavailable fields try: self._check_fields(data.iterkeys(), self.allowed_fields) except AttributeError, e: msg = "Field '%s' is not allowed." % e logger.warning(msg) raise BadRequestError(msg)
def _parse(self, is_source, lang_rules): """ Parse an INI file and create a stringset with all entries in the file. """ content = self.content self.jformat = JoomlaIniVersion.create(self.content) self._find_linesep(content) comment = "" buf = '' for line in self._iter_by_line(content): # Skip empty lines and comments if not line or line.startswith(self.comment_chars): if is_source: buf += line + self.linesep if line.startswith(self.comment_chars): comment = line[1:] + self.linesep else: comment = "" continue try: source, trans = line.split('=', 1) except ValueError: # Maybe abort instead of skipping? logger.warning('Could not parse line "%s". Skipping...' % line) continue escaped_trans = self.jformat.get_translation(trans) if isinstance(self.jformat, JoomlaIniNew): trans = trans[1:-1] context = "" # We use empty context if is_source: if not trans.strip(): buf += line + self.linesep continue source_len = len(source) new_line = line[:source_len] + re.sub( re.escape(trans), "%(hash)s_tr" % {'hash': hash_tag(source, context)}, line[source_len:] ) buf += new_line + self.linesep elif not SourceEntity.objects.filter(resource=self.resource, string=source).exists()\ or not escaped_trans.strip(): #ignore keys with no translation context="" continue self._add_translation_string(source, self._unescape(escaped_trans), context=context, comment=comment) comment = "" return buf[:buf.rfind(self.linesep)]
def parse_file(self, is_source=False, lang_rules=None): """ Parse a .desktop file. If it is a source file, the file will have every translation in it. Otherwise, it will have just the translation for the specific language. """ stringset = StringSet() suggestions = StringSet() # entries is a dictionary with the entry keys in the file entries = defaultdict(list) fh = codecs.open(self.filename, "r", self.default_encoding) try: buf = fh.read() finally: fh.close() template = u'' for line in buf.split("\n"): if self._should_skip(line) : template += line + "\n" continue key, value = self._get_elements(line) if '[' in key: # this is a translation # find the language of it # Skip the template actual_key = key[:key.find('[')] locale = self._get_locale(key) lang_code = self._get_lang_code(locale) if lang_code == "x-test": template += line + "\n" continue try: lang = Language.objects.by_code_or_alias(lang_code) except Language.DoesNotExist, e: msg = "Unknown language specified: %s" % lang_code logger.warning(msg) raise DesktopParseError(msg) else: lang = False # Use False to mark source string actual_key = key template += line + "\n" if actual_key not in self.localized_keys: # Translate only standard localestring keys continue entries[actual_key].append((value, lang))
def _get_content_from_file(self, filename, encoding): fh = open(filename, "r") try: text = fh.read().decode(encoding) fh.close() except UnicodeDecodeError as e: logger.warning("Unicode decode error in DTDHandler.parse_file(): %s" % unicode(e), exc_info=True) raise self.HandlerParseError(unicode(e)) except IOError, e: logger.warning( "Error opening file %s with encoding %s: %s" %\ (filename, encoding, e.message), exc_info=True ) raise FormatError(e.message)
def _get_content(self, request, data): """Get the content from the request. If it is file-based, return the contents of the file. Args: request: The django request object. Returns: The content of the string/file. """ if 'application/json' in request.content_type: try: return data['content'] except KeyError, e: msg = "No content provided" logger.warning(msg) raise NoContentError(msg)
def create(self, project, slug, name, method, source_language, content, user=None, extra_data={}): """Create a new resource. Any extra arguments will be passed to the Resource initialization method as is. There is no transaction used. The caller is supposed to handle this. Args: project: A Project instance which the resource will belong to. slug: The slug of the resource. name: The name of the resource. method: The i18n method of theresource. source_language: A Language instance of the source language set. content: The content of the resource's source file. user: The user that creates the resource. extra_data: Any extra info for the Resource constructor. Returns: A two-elements tuple. The first element is the number of added strings and the second the number of updated strings. """ # save resource try: r = Resource(project=project, source_language=source_language, slug=slug, name=name) r.i18n_method = method r.full_clean() for key in ifilter(lambda k: k != "content", extra_data.iterkeys()): setattr(r, key, extra_data[key]) except Exception, e: logger.warning( "Error while creating resource %s for project %s: %s" % (slug, project.slug, e), exc_info=True) raise ResourceBackendError("Invalid arguments given: %s" % e)
def _get_stringset(post_data, resources, language, review=False, *args, **kwargs): """Return the source strings for the specified resources and language based on the filters active in the request. Filters are: translated|untranslated, specific user and specific resources, which must be a subset of the resources argument. Also, the user can select to search for a term, sort the columns and show more languages other than the selected one. """ # Find a way to determine the source language of multiple resources #FIXME source_language = get_source_language(resources) try: source_strings = _get_source_strings_for_request( post_data, resources, source_language, language ) except LotteBadRequestError, e: logger.warning("Error in lotte filters: %s" % e.message, exc_info=True) return HttpResponseBadRequest()
def _get_filename(self, request, data): """Get the filename of the uploaded file. Returns: The filename or None, if the request used json. """ if 'application/json' in request.content_type: return None elif 'multipart/form-data' in request.content_type: if not request.FILES: msg = "No file has been uploaded." logger.warning(msg) raise NoContentError(msg) return filename_of_uploaded_file(request.FILES) else: msg = "No content or file found" logger.warning(msg) raise NoContentError(msg)
def _get_content_from_file(self, filename, encoding): fh = open(filename, "r") try: text = fh.read().decode(encoding) fh.close() except UnicodeDecodeError as e: logger.warning( "Unicode decode error in DTDHandler.parse_file(): %s" % unicode(e), exc_info=True) raise self.HandlerParseError(unicode(e)) except IOError, e: logger.warning( "Error opening file %s with encoding %s: %s" %\ (filename, encoding, e.message), exc_info=True ) raise FormatError(e.message)
def _get_content_from_file(self, filename, encoding): f = open(filename, "r") try: content = f.read() if chardet.detect(content)["encoding"].startswith(self.format_encoding): # f = f.decode(self.format_encoding) encoding = self.format_encoding else: # f = f.decode(self.default_encoding) encoding = "utf-8" f.close() f = codecs.open(filename, "r", encoding=encoding) return f.read() except IOError, e: logger.warning( "Error opening file %s with encoding %s: %s" % (filename, self.format_encoding, e.message), exc_info=True, ) raise FormatError(e.message)
def add_from_strings(self, strings): """Add the strings as suggestions. Args: strings: An iterable of strings to add as suggestions """ for j in strings: # Check SE existence try: se = SourceEntity.objects.get(string=j.source_entity, context=j.context or "None", resource=self.resource) except SourceEntity.DoesNotExist: logger.warning("Source entity %s does not exist" % j.source_entity) continue Suggestion.objects.get_or_create(string=j.translation, source_entity=se, language=self.language)
class GettextHandler(SimpleCompilerFactory, Handler): """ Translate Toolkit is using Gettext C library to parse/create PO files in Python TODO: Switch to Gettext C library """ name = "GNU Gettext *.PO/*.POT handler" method_name = 'PO' format = "GNU Gettext Catalog (*.po, *.pot)" copyright_line = re.compile('^# (.*?), ((\d{4}(, ?)?)+)\.?$') HandlerParseError = PoParseError HandlerCompileError = PoCompileError def _check_content(self, content): try: po = polib.pofile(content) except IOError, e: logger.warning("Parse error: %s" % e, exc_info=True) raise PoParseError(unicode(e)) # If file is empty, the method hangs so we should bail out. if not content: logger.warning("Pofile: File '%s' is empty." % self.filename) raise PoParseError("Uploaded file is empty.") # Msgfmt check if settings.FILECHECKS['POFILE_MSGFMT']: msgfmt_check(content, self.is_pot) # Check required header fields required_metadata = ['Content-Type', 'Content-Transfer-Encoding'] for metadata in required_metadata: if not metadata in po.metadata: logger.warning( "pofile: Required metadata '%s' not found." % metadata ) raise PoParseError( "Uploaded file header doesn't have '%s' metadata!" % metadata ) # Save to avoid parsing it again self._po = po
def _get_content_from_file(self, filename, encoding): f = open(filename, 'r') try: content = f.read() if chardet.detect(content)['encoding'].startswith(self.format_encoding): #f = f.decode(self.format_encoding) encoding = self.format_encoding else: #f = f.decode(self.default_encoding) encoding = 'utf-8' f.close() f = codecs.open(filename, 'r', encoding=encoding) return f.read() except IOError, e: logger.warning( "Error opening file %s with encoding %s: %s" %\ (filename, self.format_encoding, e.message), exc_info=True ) raise FormatError(e.message)
def _parse(self, is_source, lang_rules): """ Parses Qt file and exports all entries as GenericTranslations. """ def clj(s, w): return s[:w].replace("\n", " ").ljust(w) if lang_rules: nplural = len(lang_rules) else: nplural = self.language.get_pluralrules_numbers() try: doc = xml.dom.minidom.parseString( self.content.encode(self.format_encoding)) except Exception, e: logger.warning("QT parsing: %s" % e.message, exc_info=True) raise LinguistParseError( _("Your file doesn't seem to contain valid xml: %s!" % e.message))
def _parse(self, is_source=False, lang_rules=None): """ Parse a .desktop file. If it is a source file, the file will have every translation in it. Otherwise, it will have just the translation for the specific language. """ # entries is a dictionary with the entry keys in the file entries = defaultdict(list) template = u'' for line in self._iter_by_line(self.content): if self._should_skip(line): template += line + "\n" continue key, value = self._get_elements(line) if '[' in key: # this is a translation # find the language of it # Skip the template actual_key = key[:key.find('[')] locale = self._get_locale(key) lang_code = self._get_lang_code(locale) if lang_code == "x-test": template += line + "\n" continue try: lang = Language.objects.by_code_or_alias(lang_code) except Language.DoesNotExist, e: msg = _("Unknown language specified: %s" % lang_code) logger.warning(msg) raise DesktopParseError(msg) else: lang = False # Use False to mark source string actual_key = key template += line + "\n" if actual_key not in self.localized_keys: # Translate only standard localestring keys continue entries[actual_key].append((value, lang))
def _parse(self, is_source, lang_rules): """ Parses Qt file and exports all entries as GenericTranslations. """ def clj(s, w): return s[:w].replace("\n", " ").ljust(w) if lang_rules: nplural = len(lang_rules) else: nplural = self.language.get_pluralrules_numbers() try: doc = xml.dom.minidom.parseString( self.content.encode(self.format_encoding) ) except Exception, e: logger.warning("QT parsing: %s" % e.message, exc_info=True) raise LinguistParseError(_( "Your file doesn't seem to contain valid xml: %s!" % e.message ))
def add_from_strings(self, strings): """Add the strings as suggestions. Args: strings: An iterable of strings to add as suggestions """ for j in strings: # Check SE existence try: se = SourceEntity.objects.get( string = j.source_entity, context = j.context or "None", resource = self.resource ) except SourceEntity.DoesNotExist: logger.warning( "Source entity %s does not exist" % j.source_entity ) continue Suggestion.objects.get_or_create( string = j.translation, source_entity = se, language = self.language )
def _get_stringset(post_data, resources, language, review=False, session='', *args, **kwargs): """Return the source strings for the specified resources and language based on the filters active in the request. Filters are: translated|untranslated, specific user and specific resources, which must be a subset of the resources argument. Also, the user can select to search for a term, sort the columns and show more languages other than the selected one. """ # Find a way to determine the source language of multiple resources #FIXME source_language = get_source_language(resources) try: source_strings = _get_source_strings_for_request( post_data, resources, source_language, language, session) except LotteBadRequestError, e: logger.warning("Error in lotte filters: %s" % e.message, exc_info=True) return HttpResponseBadRequest()
else: if request.path.find('resources/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_resource') elif request.path.find('teams/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_team') elif request.path.find('releases/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_release') feeds = feeds.values('action_time', 'message', 'user__username') if limit: feeds = feeds[:limit] return feeds except Project.DoesNotExist, e: logger.warning(unicode(e)) return rc.NOT_FOUND except Resource.DoesNotExist, e: logger.warning(unicode(e)) return rc.NOT_FOUND except Team.DoesNotExist, e: logger.warning(unicode(e)) return rc.NOT_FOUND except Release.DoesNotExist, e: logger.warning(unicode(e)) return rc.NOT_FOUND except User.DoesNotExist, e: logger.warning(unicode(e)) return rc.NOT_FOUND
def read(self, request, project_slug=None, resource_slug=None, release_slug=None, username=None, language_code=None, api_version=2,): try: if request.GET.has_key('limit'): limit = request.GET['limit'] else: limit = None if username: user = User.objects.get(username=username) if request.user == user: feeds = LogEntry.objects.filter(user=user, content_type=ContentType.objects.get(model='project')) else: feeds = LogEntry.objects.filter(user=user, content_type=ContentType.objects.get(model='project'), project__private=False) elif not project_slug: private_slugs = list( Project.objects.filter(private=True).values_list('slug', flat=True) ) feeds = LogEntry.objects.filter( content_type=ContentType.objects.get(model='project') ).exclude( project__slug__in=private_slugs ) for slug in private_slugs: feeds = feeds.exclude(message__contains='/projects/p/%s/'%slug) else: project = Project.objects.get(slug=project_slug) if not self._has_perm(request.user, project): return rc.FORBIDDEN if resource_slug: resource = Resource.objects.get(slug=resource_slug, project=project) feeds = LogEntry.objects.by_object(resource) elif language_code: team = Team.objects.get(language__code=language_code, project=project) feeds = LogEntry.objects.by_object(team) elif release_slug: release = Release.objects.get(slug=release_slug, project=project) feeds = LogEntry.objects.by_object(release) elif project_slug and request.path == reverse('project_actionlogs', args=[project_slug]): feeds = LogEntry.objects.by_object(project) else: if request.path.find('resources/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_resource') elif request.path.find('teams/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_team') elif request.path.find('releases/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_release') feeds = feeds.values('action_time', 'message', 'user__username') if limit: feeds = feeds[:limit] return feeds except Project.DoesNotExist, e: logger.warning(unicode(e)) return rc.NOT_FOUND
def _parse(self, is_source, lang_rules): """ Parse an INI file and create a stringset with all entries in the file. """ content = self.content self._find_linesep(content) comment = "" buf = '' initialized = False for line in self._iter_by_line(content): # Skip empty lines and comments if not line or line.startswith(self.comment_chars): if is_source: buf += line + self.linesep if line.startswith(self.comment_chars): comment = line[1:] + self.linesep else: comment = "" continue try: source, trans = line.split('=', 1) if not initialized: if trans.strip().startswith('"') and\ trans.strip().endswith('"'): self.jformat = JoomlaIniNew() else: self.jformat = JoomlaIniOld() initialized = True except ValueError: # Maybe abort instead of skipping? logger.warning('Could not parse line "%s". Skipping...' % line) continue escaped_trans = self.jformat.get_translation(trans) source = source.strip() trans = trans.strip() if isinstance(self.jformat, JoomlaIniNew): trans = trans[1:-1] context = "" # We use empty context if is_source: if not trans.strip(): buf += line + self.linesep continue source_len = len(source) new_line = line[:source_len] + re.sub( re.escape(trans), "%(hash)s_tr" % {'hash': hash_tag(source, context)}, line[source_len:]) buf += new_line + self.linesep elif not SourceEntity.objects.filter(resource=self.resource, string=source).exists()\ or not escaped_trans.strip(): #ignore keys with no translation context = "" continue self.stringset.add( GenericTranslation(source, self._unescape(escaped_trans), context=context, comment=comment)) comment = "" return buf[:buf.rfind(self.linesep)]
return True @transaction.commit_on_success def _create(self, request, project_slug, data): # Check for unavailable fields try: self._check_fields(data.iterkeys(), self.allowed_fields) except AttributeError, e: msg = "Field '%s' is not allowed." % e logger.warning(msg) raise BadRequestError(msg) # Check for obligatory fields for field in ('name', 'slug', 'i18n_type', ): if field not in data: msg = "Field '%s' must be specified." % field logger.warning(msg) raise BadRequestError(msg) try: project = Project.objects.get(slug=project_slug) except Project.DoesNotExist, e: logger.warning(unicode(e), exc_info=True) raise NotFoundError(unicode(e)) # In multipart/form-encode request variables have lists # as values. So we use __getitem__ isntead of pop, which returns # the last value method = data['i18n_type']; del data['i18n_type'] if not registry.is_supported(method): msg = "i18n_type %s is not supported." % method logger.warning(msg)
def read( self, request, project_slug=None, resource_slug=None, release_slug=None, username=None, language_code=None, api_version=2, ): try: if request.GET.has_key('limit'): limit = request.GET['limit'] else: limit = None if username: user = User.objects.get(username=username) if request.user == user: feeds = LogEntry.objects.filter( user=user, content_type=ContentType.objects.get(model='project')) else: feeds = LogEntry.objects.filter( user=user, content_type=ContentType.objects.get(model='project'), project__private=False) elif not project_slug: private_slugs = list( Project.objects.filter(private=True).values_list( 'slug', flat=True)) feeds = LogEntry.objects.filter( content_type=ContentType.objects.get( model='project')).exclude( project__slug__in=private_slugs) for slug in private_slugs: feeds = feeds.exclude(message__contains='/projects/p/%s/' % slug) else: project = Project.objects.get(slug=project_slug) if not self._has_perm(request.user, project): return rc.FORBIDDEN if resource_slug: resource = Resource.objects.get(slug=resource_slug, project=project) feeds = LogEntry.objects.by_object(resource) elif language_code: team = Team.objects.get(language__code=language_code, project=project) feeds = LogEntry.objects.by_object(team) elif release_slug: release = Release.objects.get(slug=release_slug, project=project) feeds = LogEntry.objects.by_object(release) elif project_slug and request.path == reverse( 'project_actionlogs', args=[project_slug]): feeds = LogEntry.objects.by_object(project) else: if request.path.find('resources/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_resource') elif request.path.find('teams/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_team') elif request.path.find('releases/actionlog/') > 0: feeds = LogEntry.objects.by_object(project).filter( action_type__label__startswith='project_release') feeds = feeds.values('action_time', 'message', 'user__username') if limit: feeds = feeds[:limit] return feeds except Project.DoesNotExist, e: logger.warning(unicode(e)) return rc.NOT_FOUND
logger.warning( "Error while creating resource %s for project %s: %s" % ( slug, project.slug, e ), exc_info=True ) raise ResourceBackendError(_("Invalid arguments given.")) try: r.save() except IntegrityError, e: logger.warning("Error creating resource %s: %s" % (r, e)) raise ResourceBackendError(_( "A resource with the same slug exists in this project." )) except DatabaseError, e: msg = _("Error creating resource: %s") logger.warning(msg % e) raise ResourceBackendError(msg % e) # save source entities try: fb = FormatsBackend(r, source_language, user) except AttributeError, e: raise ResourceBackendError(_( "The content type of the request is not valid." )) try: return fb.import_source( content, filename=extra_data.get('filename') ) except FormatsBackendError, e: raise ResourceBackendError(unicode(e))
extra_data.iterkeys()): setattr(r, key, extra_data[key]) except Exception, e: logger.warning( "Error while creating resource %s for project %s: %s" % (slug, project.slug, e), exc_info=True) raise ResourceBackendError("Invalid arguments given: %s" % e) try: r.save() except IntegrityError, e: logger.warning("Error creating resource %s: %s" % (r, e)) raise ResourceBackendError("Error saving resource: %s" % e) except DatabaseError, e: msg = _("Error creating resource: %s") logger.warning(msg % e) raise ResourceBackendError(msg % e) # save source entities try: fb = FormatsBackend(r, source_language, user) except AttributeError, e: raise ResourceBackendError( _("The content type of the request is not valid.")) try: return fb.import_source(content, filename=extra_data.get('filename')) except FormatsBackendError, e: raise ResourceBackendError(unicode(e)) except Exception, e: logger.error("Unexamined exception raised: %s" % e, exc_info=True) raise ResourceBackendError(unicode(e))
def _check_content(self, content): try: po = polib.pofile(content) except IOError, e: logger.warning("Parse error: %s" % e, exc_info=True) raise PoParseError(unicode(e))
# Check for unavailable fields try: self._check_fields(data.iterkeys(), self.allowed_fields) except AttributeError, e: msg = "Field '%s' is not allowed." % e logger.warning(msg) raise BadRequestError(msg) # Check for obligatory fields for field in ( 'name', 'slug', 'i18n_type', ): if field not in data: msg = "Field '%s' must be specified." % field logger.warning(msg) raise BadRequestError(msg) try: project = Project.objects.get(slug=project_slug) except Project.DoesNotExist, e: logger.warning(unicode(e), exc_info=True) raise NotFoundError(unicode(e)) # In multipart/form-encode request variables have lists # as values. So we use __getitem__ isntead of pop, which returns # the last value method = data['i18n_type'] del data['i18n_type'] if not registry.is_supported(method): msg = "i18n_type %s is not supported." % method
" the translation file you're using has more" " than two plurals which is not supported." ) # English plural rules messages = [(1, entry.msgstr_plural['0'] or entry.msgid), (5, entry.msgstr_plural['1'] or entry.msgid_plural)] plural_keys = [0,1] else: message_keys = entry.msgstr_plural.keys() message_keys.sort() nplural_file = len(message_keys) messages = [] if nplural: if len(nplural) != nplural_file: logger.warning("Passed plural rules has nplurals=%s" ", but '%s' file has nplurals=%s. String '%s'" "skipped." % (len(nplural), self.filename, nplural_file, entry.msgid)) self._set_warning_message('nplural', ugettext("Pluralized entries of the file were " "skipped because the nplural of the upload file " "differs from the nplural (%s) for the given " "language available in the system." % len(nplural))) same_nplural = False else: same_nplural = False if not same_nplural: # Skip half translated plurals continue # plural_keys = message_keys