Hello community, here is the log from the commit of package i18nspector for openSUSE:Factory checked in at 2014-08-03 07:50:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/i18nspector (Old) and /work/SRC/openSUSE:Factory/.i18nspector.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "i18nspector" Changes: -------- --- /work/SRC/openSUSE:Factory/i18nspector/i18nspector.changes 2014-07-12 17:15:01.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.i18nspector.new/i18nspector.changes 2014-08-03 07:50:43.000000000 +0200 @@ -1,0 +2,14 @@ +Mon Jul 21 11:58:41 UTC 2014 - lazy.k...@opensuse.org + +- Update to 0.14. + * Summary of tag changes: + + Added: + - translation-in-template + * Check for PO template files containing translated messages. + * Check for duplicate messages, for problems with message flags, + and for empty files even when encoding is broken or unknown. + * Check for inconsistent leading/trailing newlines between msgid + and msgid_plural even when encoding in unknown or broken. + * Improve the test suite. + +------------------------------------------------------------------- Old: ---- i18nspector-0.13.5.tar.gz i18nspector-0.13.5.tar.gz.asc New: ---- i18nspector-0.14.tar.gz i18nspector-0.14.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ i18nspector.spec ++++++ --- /var/tmp/diff_new_pack.PCFrMZ/_old 2014-08-03 07:50:44.000000000 +0200 +++ /var/tmp/diff_new_pack.PCFrMZ/_new 2014-08-03 07:50:44.000000000 +0200 @@ -17,7 +17,7 @@ Name: i18nspector -Version: 0.13.5 +Version: 0.14 Release: 0 Summary: Tool for Checking gettext POT/PO/MO Files License: MIT ++++++ i18nspector-0.13.5.tar.gz -> i18nspector-0.14.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/data/tags new/i18nspector-0.14/data/tags --- old/i18nspector-0.13.5/data/tags 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/data/tags 2014-07-16 15:28:57.000000000 +0200 @@ -655,6 +655,12 @@ references = https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html +[translation-in-template] +severity = minor +certainty = certain +description = + The PO template file contains a translated message. + [unable-to-determine-language] severity = normal certainty = wild-guess diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/doc/changelog new/i18nspector-0.14/doc/changelog --- old/i18nspector-0.13.5/doc/changelog 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/doc/changelog 2014-07-16 15:28:57.000000000 +0200 @@ -1,3 +1,18 @@ +i18nspector (0.14) unstable; urgency=low + + * Summary of tag changes: + + Added: + - translation-in-template + + * Check for PO template files containing translated messages. + * Check for duplicate messages, for problems with message flags, and for + empty files even when encoding is broken or unknown. + * Check for inconsistent leading/trailing newlines between msgid and + msgid_plural even when encoding in unknown or broken. + * Improve the test suite. + + -- Jakub Wilk <jw...@jwilk.net> Wed, 16 Jul 2014 15:28:40 +0200 + i18nspector (0.13.5) unstable; urgency=low * Use HTTPS URLs when they are available, in documentation and code. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/doc/i18nspector.1 new/i18nspector-0.14/doc/i18nspector.1 --- old/i18nspector-0.13.5/doc/i18nspector.1 2014-07-07 15:08:08.000000000 +0200 +++ new/i18nspector-0.14/doc/i18nspector.1 2014-07-16 15:28:58.000000000 +0200 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH I18NSPECTOR 1 "2014-07-07" "i18nspector 0.13.5" "" +.TH I18NSPECTOR 1 "2014-07-16" "i18nspector 0.14" "" .SH NAME i18nspector \- checking tool for gettext POT, PO and MO files . @@ -1297,6 +1297,16 @@ important, possible .UNINDENT .UNINDENT +.SS translation\-in\-template +.sp +The PO template file contains a translated message. +.sp +Severity, certainty: +.INDENT 0.0 +.INDENT 3.5 +minor, certain +.UNINDENT +.UNINDENT .SS unable\-to\-determine\-language .sp i18nspector was unable to determine language of this file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/doc/i18nspector.txt new/i18nspector-0.14/doc/i18nspector.txt --- old/i18nspector-0.13.5/doc/i18nspector.txt 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/doc/i18nspector.txt 2014-07-16 15:28:57.000000000 +0200 @@ -7,7 +7,7 @@ ---------------------------------------------- :manual section: 1 -:version: i18nspector 0.13.5 +:version: i18nspector 0.14 :date: |date| Synopsis diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/doc/tags.txt new/i18nspector-0.14/doc/tags.txt --- old/i18nspector-0.13.5/doc/tags.txt 2014-07-07 15:08:08.000000000 +0200 +++ new/i18nspector-0.14/doc/tags.txt 2014-07-16 15:28:57.000000000 +0200 @@ -922,6 +922,14 @@ important, possible +translation-in-template +~~~~~~~~~~~~~~~~~~~~~~~ +The PO template file contains a translated message. + +Severity, certainty: + + minor, certain + unable-to-determine-language ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ i18nspector was unable to determine language of this file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/doc/todo.txt new/i18nspector-0.14/doc/todo.txt --- old/i18nspector-0.13.5/doc/todo.txt 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/doc/todo.txt 2014-07-16 15:28:57.000000000 +0200 @@ -82,8 +82,6 @@ Use `blessings <https://pypi.python.org/pypi/blessings/>`_ for colored output. -Check for POT files containing translations. - Run ``msgfmt --check-format`` on PO files. Reimplement the EUC-TW codec with PyICU. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/lib/checker.py new/i18nspector-0.14/lib/checker.py --- old/i18nspector-0.13.5/lib/checker.py 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/lib/checker.py 2014-07-16 15:28:57.000000000 +0200 @@ -171,23 +171,26 @@ broken_encoding.encoding.upper(), ) broken_encoding = True - # check_headers() modifies the file metadata, - # so it has to be the first check: - self.check_headers(file, is_template=is_template) - language = self.check_language(file, is_template=is_template) - self.check_plurals(file, is_template=is_template, language=language) - encoding = self.check_mime(file, is_template=is_template, language=language) + class ctx: + pass + ctx.file = file + ctx.is_template = is_template + self.check_headers(ctx) + self.check_language(ctx) + self.check_plurals(ctx) + self.check_mime(ctx) if broken_encoding: - encoding = None - self.check_dates(file, is_template=is_template) - self.check_project(file) - self.check_translator(file, is_template=is_template) - self.check_messages(file, encoding=encoding) + ctx.encoding = None + self.check_dates(ctx) + self.check_project(ctx) + self.check_translator(ctx) + self.check_messages(ctx) @checks_header_fields('Language', 'X-Poedit-Language', 'X-Poedit-Country') - def check_language(self, file, *, is_template): + def check_language(self, ctx): + ctx.language = None duplicate_meta_language = False - meta_languages = file.metadata['Language'] + meta_languages = ctx.metadata['Language'] if len(meta_languages) > 1: self.tag('duplicate-header-field-language') meta_languages = sorted(set(meta_languages)) @@ -198,7 +201,7 @@ else: meta_language = None orig_meta_language = meta_language - if is_template: + if ctx.is_template: if meta_language is None: self.tag('no-language-header-field') return @@ -285,11 +288,11 @@ '!=', meta_language, tags.safestr('(Language header field)') ) - poedit_languages = file.metadata['X-Poedit-Language'] + poedit_languages = ctx.metadata['X-Poedit-Language'] if len(poedit_languages) > 1: self.tag('duplicate-header-field-x-poedit', 'X-Poedit-Language') poedit_languages = sorted(set(poedit_languages)) - poedit_countries = file.metadata['X-Poedit-Country'] + poedit_countries = ctx.metadata['X-Poedit-Country'] if len(poedit_countries) > 1: self.tag('duplicate-header-field-x-poedit', 'X-Poedit-Country') poedit_countries = sorted(set(poedit_countries)) @@ -317,11 +320,11 @@ return if not orig_meta_language and not duplicate_meta_language: self.tag('no-language-header-field', tags.safestr('Language:'), language) - return language + ctx.language = language @checks_header_fields('Plural-Forms') - def check_plurals(self, file, *, is_template, language): - plural_forms = file.metadata['Plural-Forms'] + def check_plurals(self, ctx): + plural_forms = ctx.metadata['Plural-Forms'] if len(plural_forms) > 1: self.tag('duplicate-header-field-plural-forms') plural_forms = sorted(set(plural_forms)) @@ -333,11 +336,11 @@ assert len(plural_forms) == 0 plural_forms = None correct_plural_forms = None - if language is not None: - correct_plural_forms = language.get_plural_forms() + if ctx.language is not None: + correct_plural_forms = ctx.language.get_plural_forms() has_plurals = False # messages with plural forms (translated or not)? expected_nplurals = {} # number of plurals in _translated_ messages - for message in file: + for message in ctx.file: if message.obsolete: continue if message.msgid_plural: @@ -352,7 +355,7 @@ for n, message in sorted(expected_nplurals.items()): args += [n, message_repr(message, template='({})'), '!='] self.tag('inconsistent-number-of-plural-forms', *args[:-1]) - if is_template: + if ctx.is_template: plural_forms_hint = 'nplurals=INTEGER; plural=EXPRESSION;' elif correct_plural_forms: plural_forms_hint = tags.safe_format( @@ -368,7 +371,7 @@ else: self.tag('no-plural-forms-header-field', plural_forms_hint) return - if is_template: + if ctx.is_template: return try: (n, expr) = gettext.parse_plural_forms(plural_forms) @@ -448,9 +451,10 @@ self.tag('codomain-error-in-unused-plural-forms', message) @checks_header_fields('MIME-Version', 'Content-Transfer-Encoding', 'Content-Type') - def check_mime(self, file, *, is_template, language): + def check_mime(self, ctx): + ctx.encoding = None # MIME-Version: - mime_versions = file.metadata['MIME-Version'] + mime_versions = ctx.metadata['MIME-Version'] if len(mime_versions) > 1: self.tag('duplicate-header-field-mime-version') mime_versions = sorted(set(mime_versions)) @@ -460,7 +464,7 @@ if len(mime_versions) == 0: self.tag('no-mime-version-header-field', tags.safestr('MIME-Version: 1.0')) # Content-Transfer-Encoding: - ctes = file.metadata['Content-Transfer-Encoding'] + ctes = ctx.metadata['Content-Transfer-Encoding'] if len(ctes) > 1: self.tag('duplicate-header-field-content-transfer-encoding') ctes = sorted(set(ctes)) @@ -470,7 +474,7 @@ if len(ctes) == 0: self.tag('no-content-transfer-encoding-header-field', tags.safestr('Content-Transfer-Encoding: 8bit')) # Content-Type: - cts = file.metadata['Content-Type'] + cts = ctx.metadata['Content-Type'] if len(cts) > 1: self.tag('duplicate-header-field-content-type') cts = sorted(set(cts)) @@ -488,7 +492,7 @@ is_ascii_compatible = encinfo.is_ascii_compatible_encoding(encoding, missing_ok=False) except encinfo.EncodingLookupError: if encoding == 'CHARSET': - if not is_template: + if not ctx.is_template: self.tag('boilerplate-in-content-type', ct) else: self.tag('unknown-encoding', encoding) @@ -505,8 +509,8 @@ encoding = new_encoding else: self.tag('non-portable-encoding', encoding) - if language is not None: - unrepresentable_characters = language.get_unrepresentable_characters(encoding) + if ctx.language is not None: + unrepresentable_characters = ctx.language.get_unrepresentable_characters(encoding) if unrepresentable_characters: if len(unrepresentable_characters) > 5: unrepresentable_characters[4:] = ['...'] @@ -520,18 +524,17 @@ else: self.tag('invalid-content-type', ct, '=>', content_type_hint) if len(encodings) == 1: - [encoding] = encodings - return encoding + [ctx.encoding] = encodings @checks_header_fields('POT-Creation-Date', 'PO-Revision-Date') - def check_dates(self, file, *, is_template): + def check_dates(self, ctx): try: - content_type = file.metadata['Content-Type'][0] + content_type = ctx.metadata['Content-Type'][0] except IndexError: content_type = '' is_publican = content_type.startswith('application/x-publican;') for field in 'POT-Creation-Date', 'PO-Revision-Date': - dates = file.metadata[field] + dates = ctx.metadata[field] if len(dates) > 1: self.tag('duplicate-header-field-date', field) dates = sorted(set(dates)) @@ -539,7 +542,7 @@ self.tag('no-date-header-field', field) continue for date in dates: - if is_template and field.startswith('PO-') and (date == gettext.boilerplate_date): + if ctx.is_template and field.startswith('PO-') and (date == gettext.boilerplate_date): continue if 'T' in date and is_publican: # Publican uses DateTime->now(), which uses the UTC timezone by default: @@ -566,9 +569,9 @@ self.tag('ancient-date', tags.safestr(field + ':'), date) @checks_header_fields('Project-Id-Version', 'Report-Msgid-Bugs-To') - def check_project(self, file): + def check_project(self, ctx): # Project-Id-Version: - project_id_versions = file.metadata['Project-Id-Version'] + project_id_versions = ctx.metadata['Project-Id-Version'] if len(project_id_versions) > 1: self.tag('duplicate-header-field-project-id-version') project_id_versions = sorted(set(project_id_versions)) @@ -578,12 +581,12 @@ if project_id_version in {'PACKAGE VERSION', 'PROJECT VERSION'}: self.tag('boilerplate-in-project-id-version', project_id_version) else: - if not re.compile(r'[^_\d\W]', re.UNICODE).search(project_id_version): + if not re.compile(r'[^_\d\W]').search(project_id_version): self.tag('no-package-name-in-project-id-version', project_id_version) if not re.search(r'[0-9]', project_id_version): self.tag('no-version-in-project-id-version', project_id_version) # Report-Msgid-Bugs-To: - report_msgid_bugs_tos = file.metadata['Report-Msgid-Bugs-To'] + report_msgid_bugs_tos = ctx.metadata['Report-Msgid-Bugs-To'] if len(report_msgid_bugs_tos) > 1: self.tag('duplicate-header-field-report-msgid-bugs-to') report_msgid_bugs_tos = sorted(set(report_msgid_bugs_tos)) @@ -603,9 +606,9 @@ self.tag('boilerplate-in-report-msgid-bugs-to', report_msgid_bugs_to) @checks_header_fields('Last-Translator', 'Language-Team') - def check_translator(self, file, *, is_template): + def check_translator(self, ctx): # Last-Translator: - translators = file.metadata['Last-Translator'] + translators = ctx.metadata['Last-Translator'] if len(translators) > 1: self.tag('duplicate-header-field-last-translator') translators = sorted(set(translators)) @@ -620,10 +623,10 @@ elif domains.is_email_in_special_domain(translator_email): self.tag('invalid-last-translator', translator) elif translator_email == 'EMAIL@ADDRESS': - if not is_template: + if not ctx.is_template: self.tag('boilerplate-in-last-translator', translator) # Language-Team: - teams = file.metadata['Language-Team'] + teams = ctx.metadata['Language-Team'] if len(teams) > 1: self.tag('duplicate-header-field-language-team') teams = sorted(set(teams)) @@ -638,18 +641,18 @@ elif domains.is_email_in_special_domain(team_email): self.tag('invalid-language-team', team) elif team_email in {'l...@li.org', 'EMAIL@ADDRESS'}: - if not is_template: + if not ctx.is_template: self.tag('boilerplate-in-language-team', team) else: if team_email in translator_emails: self.tag('language-team-equal-to-last-translator', team, translator) - def check_headers(self, file, *, is_template): + def check_headers(self, ctx): metadata = collections.defaultdict(list) strays = [] - file.header_entry = None + ctx.file.header_entry = None seen_header_entry = False - for entry in file: + for entry in ctx.file: if not is_header_entry(entry) or entry.obsolete: continue if seen_header_entry: @@ -675,7 +678,7 @@ flags = collections.Counter(resplit_flags(entry.flags)) for flag, n in sorted(flags.items()): if flag == 'fuzzy': - if not is_template: + if not ctx.is_template: self.tag('fuzzy-header-entry') elif difflib.get_close_matches(flag.lower(), ['fuzzy'], cutoff=0.8): self.tag('unexpected-flag-for-header-entry', flag, '=>', 'fuzzy') @@ -683,7 +686,7 @@ self.tag('unexpected-flag-for-header-entry', flag) if n > 1: self.tag('duplicate-flag-for-header-entry', flag) - if entry is not file[0]: + if entry is not ctx.file[0]: self.tag('distant-header-entry') unusual_chars = set(find_unusual_characters(msgstr)) if unusual_chars: @@ -719,119 +722,34 @@ self.tag('unknown-header-field', key, '=>', hint) if len(values) > 1 and key not in header_fields_with_dedicated_checks: self.tag('duplicate-header-field', key) - file.metadata = metadata - del file.metadata_is_fuzzy + ctx.metadata = metadata + del ctx.file.metadata + del ctx.file.metadata_is_fuzzy - def check_messages(self, file, *, encoding): - if encoding is None: - return + def check_messages(self, ctx): found_unusual_characters = set() msgid_counter = collections.Counter() - messages = [msg for msg in file if not is_header_entry(msg)] + messages = [msg for msg in ctx.file if not is_header_entry(msg)] for message in messages: if message.obsolete: continue if is_header_entry(message): continue - flags = collections.Counter(resplit_flags(message.flags)) - fuzzy = False - wrap = None - format_flags = collections.defaultdict(dict) - for flag, n in sorted(flags.items()): - known_flag = True - if flag == 'fuzzy': - fuzzy = True - elif flag in {'wrap', 'no-wrap'}: - new_wrap = flag == 'wrap' - if wrap == (not new_wrap): - self.tag('conflicting-message-flags', - message_repr(message, template='{}:'), - 'wrap', 'no-wrap' - ) - else: - wrap = new_wrap - elif flag.startswith('range:'): - if not message.msgid_plural: - self.tag('range-flag-without-plural-string') - match = re.match('([0-9]+)[.][.]([0-9]+)', flag[6:].strip(' \t\r\f\v')) - if match is not None: - i, j = map(int, match.groups()) - if i >= j: - match = None - if match is None: - self.tag('invalid-range-flag', - message_repr(message, template='{}:'), - flag - ) - elif flag.endswith('-format'): - known_flag = False - for prefix in 'no-', 'possible-', 'impossible-', '': - tp = prefix.rstrip('-') - if not flag.startswith(prefix): - continue - string_format = flag[len(prefix):-7] - if string_format in gettext.string_formats: - known_flag = True - format_flags[tp][string_format] = flag - break - else: - known_flag = False - if not known_flag: - self.tag('unknown-message-flag', - message_repr(message, template='{}:'), - flag - ) - if n > 1 and flag: - self.tag('duplicate-message-flag', - message_repr(message, template='{}:'), - flag - ) - positive_format_flags = format_flags[''] - if len(positive_format_flags) > 1: - self.tag('conflicting-message-flags', - message_repr(message, template='{}:'), - *sorted(positive_format_flags.values()) - ) - elif len(positive_format_flags) == 1: - [[positive_format, positive_format_flag]] = positive_format_flags.items() - negative_format_flags = sorted( - format_flag - for fmt, format_flag in - format_flags['no'].items() - if fmt != positive_format - ) - if negative_format_flags: - args = ( - negative_format_flags + - [tags.safe_format('(implied by {flag})'.format(flag=positive_format_flag))] - ) - self.tag('redundant-message-flag', - message_repr(message, template='{}:'), - *args - ) - for positive_key, negative_key in [('', 'no'), ('', 'impossible'), ('possible', 'impossible')]: - positive_format_flags = format_flags[positive_key] - negative_format_flags = format_flags[negative_key] - conflicting_formats = frozenset(positive_format_flags) & frozenset(negative_format_flags) - for fmt in sorted(conflicting_formats): - self.tag('conflicting-message-flags', - message_repr(message, template='{}:'), - positive_format_flags[fmt], - negative_format_flags[fmt], - ) - positive_format_flags = format_flags[''] - possible_format_flags = format_flags['possible'] - redundant_formats = frozenset(positive_format_flags) & frozenset(possible_format_flags) - for fmt in sorted(redundant_formats): - self.tag('redundant-message-flag', - message_repr(message, template='{}:'), - possible_format_flags[fmt], - tags.safe_format('(implied by {flag})'.format(flag=positive_format_flags[fmt])) + fuzzy = self._check_message_flags(message) + msgid_counter[message.msgid, message.msgctxt] += 1 + if msgid_counter[message.msgid, message.msgctxt] == 2: + self.tag('duplicate-message-definition', message_repr(message)) + if ctx.is_template: + has_translations = ( + message.msgstr or + any(message.msgstr_plural.values()) ) + if has_translations: + self.tag('translation-in-template', message_repr(message)) leading_lf = message.msgid.startswith('\n') trailing_lf = message.msgid.endswith('\n') strings = [message.msgid_plural] - if not fuzzy: + if (not fuzzy) and (ctx.encoding is not None): strings += [message.msgstr] strings += message.msgstr_plural.values() strings = [s for s in strings if s != ''] @@ -843,6 +761,8 @@ if s.endswith('\n') != trailing_lf: self.tag('inconsistent-trailing-newlines', message_repr(message)) break + if ctx.encoding is None: + continue msgid_uc = ( set(find_unusual_characters(message.msgid)) | set(find_unusual_characters(message.msgid_plural)) @@ -864,16 +784,111 @@ if conflict_marker is not None: conflict_marker = conflict_marker.group(0) self.tag('conflict-marker-in-translation', message_repr(message), conflict_marker) - msgid_counter[message.msgid, message.msgctxt] += 1 - if msgid_counter[message.msgid, message.msgctxt] == 2: - self.tag('duplicate-message-definition', message_repr(message)) if len(msgid_counter) == 0: possible_hidden_strings = False - if isinstance(file, polib.MOFile): - possible_hidden_strings = file.possible_hidden_strings + if isinstance(ctx.file, polib.MOFile): + possible_hidden_strings = ctx.file.possible_hidden_strings if not possible_hidden_strings: self.tag('empty-file') + def _check_message_flags(self, message): + flags = collections.Counter(resplit_flags(message.flags)) + fuzzy = False + wrap = None + format_flags = collections.defaultdict(dict) + for flag, n in sorted(flags.items()): + known_flag = True + if flag == 'fuzzy': + fuzzy = True + elif flag in {'wrap', 'no-wrap'}: + new_wrap = flag == 'wrap' + if wrap == (not new_wrap): + self.tag('conflicting-message-flags', + message_repr(message, template='{}:'), + 'wrap', 'no-wrap' + ) + else: + wrap = new_wrap + elif flag.startswith('range:'): + if not message.msgid_plural: + self.tag('range-flag-without-plural-string') + match = re.match('([0-9]+)[.][.]([0-9]+)', flag[6:].strip(' \t\r\f\v')) + if match is not None: + i, j = map(int, match.groups()) + if i >= j: + match = None + if match is None: + self.tag('invalid-range-flag', + message_repr(message, template='{}:'), + flag + ) + elif flag.endswith('-format'): + known_flag = False + for prefix in 'no-', 'possible-', 'impossible-', '': + tp = prefix.rstrip('-') + if not flag.startswith(prefix): + continue + string_format = flag[len(prefix):-7] + if string_format in gettext.string_formats: + known_flag = True + format_flags[tp][string_format] = flag + break + else: + known_flag = False + if not known_flag: + self.tag('unknown-message-flag', + message_repr(message, template='{}:'), + flag + ) + if n > 1 and flag: + self.tag('duplicate-message-flag', + message_repr(message, template='{}:'), + flag + ) + positive_format_flags = format_flags[''] + if len(positive_format_flags) > 1: + self.tag('conflicting-message-flags', + message_repr(message, template='{}:'), + *sorted(positive_format_flags.values()) + ) + elif len(positive_format_flags) == 1: + [[positive_format, positive_format_flag]] = positive_format_flags.items() + negative_format_flags = sorted( + format_flag + for fmt, format_flag in + format_flags['no'].items() + if fmt != positive_format + ) + if negative_format_flags: + args = ( + negative_format_flags + + [tags.safe_format('(implied by {flag})'.format(flag=positive_format_flag))] + ) + self.tag('redundant-message-flag', + message_repr(message, template='{}:'), + *args + ) + for positive_key, negative_key in [('', 'no'), ('', 'impossible'), ('possible', 'impossible')]: + positive_format_flags = format_flags[positive_key] + negative_format_flags = format_flags[negative_key] + conflicting_formats = frozenset(positive_format_flags) & frozenset(negative_format_flags) + for fmt in sorted(conflicting_formats): + self.tag('conflicting-message-flags', + message_repr(message, template='{}:'), + positive_format_flags[fmt], + negative_format_flags[fmt], + ) + positive_format_flags = format_flags[''] + possible_format_flags = format_flags['possible'] + redundant_formats = frozenset(positive_format_flags) & frozenset(possible_format_flags) + for fmt in sorted(redundant_formats): + self.tag('redundant-message-flag', + message_repr(message, template='{}:'), + possible_format_flags[fmt], + tags.safe_format('(implied by {flag})'.format(flag=positive_format_flags[fmt])) + ) + return fuzzy + __all__ = ['Checker'] def is_header_entry(entry): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/lib/cli.py new/i18nspector-0.14/lib/cli.py --- old/i18nspector-0.13.5/lib/cli.py 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/lib/cli.py 2014-07-16 15:28:57.000000000 +0200 @@ -37,7 +37,7 @@ from . import tags from . import terminal -__version__ = '0.13.5' +__version__ = '0.14' def initialize_terminal(): if sys.stdout.isatty(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/coverage.txt new/i18nspector-0.14/tests/blackbox_tests/coverage.txt --- old/i18nspector-0.13.5/tests/blackbox_tests/coverage.txt 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/coverage.txt 2014-07-16 15:28:57.000000000 +0200 @@ -79,6 +79,7 @@ [X] syntax-error-in-plural-forms [X] syntax-error-in-po-file [X] syntax-error-in-unused-plural-forms +[X] translation-in-template [X] unable-to-determine-language [X] unexpected-flag-for-header-entry [X] unknown-encoding diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/duplicate-flag-for-header-entry.pot new/i18nspector-0.14/tests/blackbox_tests/duplicate-flag-for-header-entry.pot --- old/i18nspector-0.13.5/tests/blackbox_tests/duplicate-flag-for-header-entry.pot 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/duplicate-flag-for-header-entry.pot 2014-07-16 15:28:57.000000000 +0200 @@ -6,13 +6,13 @@ "Project-Id-Version: Gizmo Enhancer 1.0\n" "Report-Msgid-Bugs-To: gizmoenhan...@jwilk.net\n" "POT-Creation-Date: 2012-11-01 14:42+0100\n" -"PO-Revision-Date: 2012-11-01 14:42+0100\n" -"Last-Translator: Jakub Wilk <jw...@jwilk.net>\n" -"Language-Team: Latin <l...@li.org>\n" -"Language: la\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <l...@li.org>\n" +"Language: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "A quick brown fox jumps over the lazy dog." -msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." +msgstr "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/empty-file.pot new/i18nspector-0.14/tests/blackbox_tests/empty-file.pot --- old/i18nspector-0.13.5/tests/blackbox_tests/empty-file.pot 1970-01-01 01:00:00.000000000 +0100 +++ new/i18nspector-0.14/tests/blackbox_tests/empty-file.pot 2014-07-16 15:28:57.000000000 +0200 @@ -0,0 +1,15 @@ +# W: empty-file + +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Gizmo Enhancer 1.0\n" +"Report-Msgid-Bugs-To: gizmoenhan...@jwilk.net\n" +"POT-Creation-Date: 2012-11-01 14:42+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <l...@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-leading-newlines-fuzzy.po new/i18nspector-0.14/tests/blackbox_tests/inconsistent-leading-newlines-fuzzy.po --- old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-leading-newlines-fuzzy.po 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/inconsistent-leading-newlines-fuzzy.po 2014-07-16 15:28:57.000000000 +0200 @@ -12,5 +12,5 @@ "Content-Transfer-Encoding: 8bit\n" #, fuzzy -msgid "A quick brown fox jumps over the lazy dog." -msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis.\n" +msgid "\nA quick brown fox jumps over the lazy dog." +msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-leading-newlines.po new/i18nspector-0.14/tests/blackbox_tests/inconsistent-leading-newlines.po --- old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-leading-newlines.po 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/inconsistent-leading-newlines.po 2014-07-16 15:28:57.000000000 +0200 @@ -1,4 +1,4 @@ -# E: inconsistent-trailing-newlines msgid 'A quick brown fox jumps over the lazy dog.' +# E: inconsistent-leading-newlines msgid '\nA quick brown fox jumps over the lazy dog.' msgid "" msgstr "" @@ -13,5 +13,5 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -msgid "A quick brown fox jumps over the lazy dog." -msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis.\n" +msgid "\nA quick brown fox jumps over the lazy dog." +msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-leading-newlines.pot new/i18nspector-0.14/tests/blackbox_tests/inconsistent-leading-newlines.pot --- old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-leading-newlines.pot 1970-01-01 01:00:00.000000000 +0100 +++ new/i18nspector-0.14/tests/blackbox_tests/inconsistent-leading-newlines.pot 2014-07-16 15:28:57.000000000 +0200 @@ -0,0 +1,20 @@ +# E: inconsistent-leading-newlines msgid '\n%d quick brown fox jumps over the lazy dog.' + +msgid "" +msgstr "" +"Project-Id-Version: Gizmo Enhancer 1.0\n" +"Report-Msgid-Bugs-To: gizmoenhan...@jwilk.net\n" +"POT-Creation-Date: 2012-11-01 14:42+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <l...@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +msgid "\n%d quick brown fox jumps over the lazy dog." +msgid_plural "%d quick brown foxes jump over the lazy dog." +msgstr[0] "" +msgstr[1] "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-trailing-newlines-fuzzy.po new/i18nspector-0.14/tests/blackbox_tests/inconsistent-trailing-newlines-fuzzy.po --- old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-trailing-newlines-fuzzy.po 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/inconsistent-trailing-newlines-fuzzy.po 2014-07-16 15:28:57.000000000 +0200 @@ -12,5 +12,5 @@ "Content-Transfer-Encoding: 8bit\n" #, fuzzy -msgid "\nA quick brown fox jumps over the lazy dog." -msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." +msgid "A quick brown fox jumps over the lazy dog." +msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis.\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-trailing-newlines.po new/i18nspector-0.14/tests/blackbox_tests/inconsistent-trailing-newlines.po --- old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-trailing-newlines.po 2014-07-07 15:08:07.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/inconsistent-trailing-newlines.po 2014-07-16 15:28:57.000000000 +0200 @@ -1,4 +1,4 @@ -# E: inconsistent-leading-newlines msgid '\nA quick brown fox jumps over the lazy dog.' +# E: inconsistent-trailing-newlines msgid 'A quick brown fox jumps over the lazy dog.' msgid "" msgstr "" @@ -13,5 +13,5 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -msgid "\nA quick brown fox jumps over the lazy dog." -msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." +msgid "A quick brown fox jumps over the lazy dog." +msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis.\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-trailing-newlines.pot new/i18nspector-0.14/tests/blackbox_tests/inconsistent-trailing-newlines.pot --- old/i18nspector-0.13.5/tests/blackbox_tests/inconsistent-trailing-newlines.pot 1970-01-01 01:00:00.000000000 +0100 +++ new/i18nspector-0.14/tests/blackbox_tests/inconsistent-trailing-newlines.pot 2014-07-16 15:28:57.000000000 +0200 @@ -0,0 +1,20 @@ +# E: inconsistent-trailing-newlines msgid '%d quick brown fox jumps over the lazy dog.' + +msgid "" +msgstr "" +"Project-Id-Version: Gizmo Enhancer 1.0\n" +"Report-Msgid-Bugs-To: gizmoenhan...@jwilk.net\n" +"POT-Creation-Date: 2012-11-01 14:42+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <l...@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +msgid "%d quick brown fox jumps over the lazy dog." +msgid_plural "%d quick brown foxes jump over the lazy dog.\n" +msgstr[0] "" +msgstr[1] "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/no-plural-forms.pot new/i18nspector-0.14/tests/blackbox_tests/no-plural-forms.pot --- old/i18nspector-0.13.5/tests/blackbox_tests/no-plural-forms.pot 2014-07-07 15:08:08.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/no-plural-forms.pot 2014-07-16 15:28:57.000000000 +0200 @@ -10,7 +10,7 @@ "Language-Team: LANGUAGE <l...@li.org>\n" "Language: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "%d quick brown fox jumps over the lazy dog." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/plural-forms-okay.pot new/i18nspector-0.14/tests/blackbox_tests/plural-forms-okay.pot --- old/i18nspector-0.13.5/tests/blackbox_tests/plural-forms-okay.pot 2014-07-07 15:08:08.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/plural-forms-okay.pot 2014-07-16 15:28:57.000000000 +0200 @@ -8,7 +8,7 @@ "Language-Team: LANGUAGE <l...@li.org>\n" "Language: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/translation-in-template.pot new/i18nspector-0.14/tests/blackbox_tests/translation-in-template.pot --- old/i18nspector-0.13.5/tests/blackbox_tests/translation-in-template.pot 1970-01-01 01:00:00.000000000 +0100 +++ new/i18nspector-0.14/tests/blackbox_tests/translation-in-template.pot 2014-07-16 15:28:57.000000000 +0200 @@ -0,0 +1,17 @@ +# W: translation-in-template msgid 'A quick brown fox jumps over the lazy dog.' + +msgid "" +msgstr "" +"Project-Id-Version: Gizmo Enhancer 1.0\n" +"Report-Msgid-Bugs-To: gizmoenhan...@jwilk.net\n" +"POT-Creation-Date: 2012-11-01 14:42+0100\n" +"PO-Revision-Date: 2012-11-01 14:42+0100\n" +"Last-Translator: Jakub Wilk <jw...@jwilk.net>\n" +"Language-Team: Latin <l...@li.org>\n" +"Language: la\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "A quick brown fox jumps over the lazy dog." +msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/i18nspector-0.13.5/tests/blackbox_tests/zero-bytes-file.po.tags new/i18nspector-0.14/tests/blackbox_tests/zero-bytes-file.po.tags --- old/i18nspector-0.13.5/tests/blackbox_tests/zero-bytes-file.po.tags 2014-07-07 15:08:08.000000000 +0200 +++ new/i18nspector-0.14/tests/blackbox_tests/zero-bytes-file.po.tags 2014-07-16 15:28:57.000000000 +0200 @@ -9,3 +9,4 @@ W: no-report-msgid-bugs-to-header-field W: no-last-translator-header-field P: no-language-team-header-field +W: empty-file -- To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org For additional commands, e-mail: opensuse-commit+h...@opensuse.org