/usr/share/pyshared/babel/messages/checkers.py is in python-babel 0.9.6-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | # -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://babel.edgewall.org/wiki/License.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://babel.edgewall.org/log/.
"""Various routines that help with validation of translations.
:since: version 0.9
"""
from itertools import izip
from babel.messages.catalog import TranslationError, PYTHON_FORMAT
from babel.util import set
#: list of format chars that are compatible to each other
_string_format_compatibilities = [
set(['i', 'd', 'u']),
set(['x', 'X']),
set(['f', 'F', 'g', 'G'])
]
def num_plurals(catalog, message):
"""Verify the number of plurals in the translation."""
if not message.pluralizable:
if not isinstance(message.string, basestring):
raise TranslationError("Found plural forms for non-pluralizable "
"message")
return
# skip further tests if no catalog is provided.
elif catalog is None:
return
msgstrs = message.string
if not isinstance(msgstrs, (list, tuple)):
msgstrs = (msgstrs,)
if len(msgstrs) != catalog.num_plurals:
raise TranslationError("Wrong number of plural forms (expected %d)" %
catalog.num_plurals)
def python_format(catalog, message):
"""Verify the format string placeholders in the translation."""
if 'python-format' not in message.flags:
return
msgids = message.id
if not isinstance(msgids, (list, tuple)):
msgids = (msgids,)
msgstrs = message.string
if not isinstance(msgstrs, (list, tuple)):
msgstrs = (msgstrs,)
for msgid, msgstr in izip(msgids, msgstrs):
if msgstr:
_validate_format(msgid, msgstr)
def _validate_format(format, alternative):
"""Test format string `alternative` against `format`. `format` can be the
msgid of a message and `alternative` one of the `msgstr`\s. The two
arguments are not interchangeable as `alternative` may contain less
placeholders if `format` uses named placeholders.
The behavior of this function is undefined if the string does not use
string formattings.
If the string formatting of `alternative` is compatible to `format` the
function returns `None`, otherwise a `TranslationError` is raised.
Examples for compatible format strings:
>>> _validate_format('Hello %s!', 'Hallo %s!')
>>> _validate_format('Hello %i!', 'Hallo %d!')
Example for an incompatible format strings:
>>> _validate_format('Hello %(name)s!', 'Hallo %s!')
Traceback (most recent call last):
...
TranslationError: the format strings are of different kinds
This function is used by the `python_format` checker.
:param format: The original format string
:param alternative: The alternative format string that should be checked
against format
:return: None on success
:raises TranslationError: on formatting errors
"""
def _parse(string):
result = []
for match in PYTHON_FORMAT.finditer(string):
name, format, typechar = match.groups()
if typechar == '%' and name is None:
continue
result.append((name, str(typechar)))
return result
def _compatible(a, b):
if a == b:
return True
for set in _string_format_compatibilities:
if a in set and b in set:
return True
return False
def _check_positional(results):
positional = None
for name, char in results:
if positional is None:
positional = name is None
else:
if (name is None) != positional:
raise TranslationError('format string mixes positional '
'and named placeholders')
return bool(positional)
a, b = map(_parse, (format, alternative))
# now check if both strings are positional or named
a_positional, b_positional = map(_check_positional, (a, b))
if a_positional and not b_positional and not b:
raise TranslationError('placeholders are incompatible')
elif a_positional != b_positional:
raise TranslationError('the format strings are of different kinds')
# if we are operating on positional strings both must have the
# same number of format chars and those must be compatible
if a_positional:
if len(a) != len(b):
raise TranslationError('positional format placeholders are '
'unbalanced')
for idx, ((_, first), (_, second)) in enumerate(izip(a, b)):
if not _compatible(first, second):
raise TranslationError('incompatible format for placeholder '
'%d: %r and %r are not compatible' %
(idx + 1, first, second))
# otherwise the second string must not have names the first one
# doesn't have and the types of those included must be compatible
else:
type_map = dict(a)
for name, typechar in b:
if name not in type_map:
raise TranslationError('unknown named placeholder %r' % name)
elif not _compatible(typechar, type_map[name]):
raise TranslationError('incompatible format for '
'placeholder %r: '
'%r and %r are not compatible' %
(name, typechar, type_map[name]))
def _find_checkers():
try:
from pkg_resources import working_set
except ImportError:
return [num_plurals, python_format]
checkers = []
for entry_point in working_set.iter_entry_points('babel.checkers'):
checkers.append(entry_point.load())
return checkers
checkers = _find_checkers()
|