mirror of
https://github.com/django/django.git
synced 2025-10-24 22:26:08 +00:00
Merged i18n branch into the trunk! Fixes #65, and perhaps some others. NB: this means that the i18n branch is now obsolete and will be made read-only.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@1068 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -33,6 +33,12 @@ class DjangoContext(Context):
|
||||
self['user'] = request.user
|
||||
self['messages'] = request.user.get_and_delete_messages()
|
||||
self['perms'] = PermWrapper(request.user)
|
||||
from django.conf import settings
|
||||
self['LANGUAGES'] = settings.LANGUAGES
|
||||
if hasattr(request, 'LANGUAGE_CODE'):
|
||||
self['LANGUAGE_CODE'] = request.LANGUAGE_CODE
|
||||
else:
|
||||
self['LANGUAGE_CODE'] = settings.LANGUAGE_CODE
|
||||
if DEBUG and request.META.get('REMOTE_ADDR') in INTERNAL_IPS:
|
||||
self['debug'] = True
|
||||
from django.core import db
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from django.conf import settings
|
||||
from django.core import formfields, validators
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.functional import curry
|
||||
from django.utils.functional import curry, lazy
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import gettext_lazy
|
||||
import datetime, os
|
||||
|
||||
# Random entropy string used by "default" param.
|
||||
@@ -26,6 +27,16 @@ prep_for_like_query = lambda x: str(x).replace("%", "\%").replace("_", "\_")
|
||||
# returns the <ul> class for a given radio_admin value
|
||||
get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
|
||||
|
||||
def string_concat(*strings):
|
||||
""""
|
||||
lazy variant of string concatenation, needed for translations that are
|
||||
constructed from multiple parts. Handles lazy strings and non-strings by
|
||||
first turning all arguments to strings, before joining them.
|
||||
"""
|
||||
return ''.join([str(el) for el in strings])
|
||||
|
||||
string_concat = lazy(string_concat, str)
|
||||
|
||||
def manipulator_valid_rel_key(f, self, field_data, all_data):
|
||||
"Validates that the value is a valid foreign key"
|
||||
mod = f.rel.to.get_model_module()
|
||||
@@ -80,9 +91,11 @@ class Field(object):
|
||||
self.db_column = db_column
|
||||
if rel and isinstance(rel, ManyToMany):
|
||||
if rel.raw_id_admin:
|
||||
self.help_text += ' Separate multiple IDs with commas.'
|
||||
self.help_text = string_concat(self.help_text,
|
||||
gettext_lazy(' Separate multiple IDs with commas.'))
|
||||
else:
|
||||
self.help_text += ' Hold down "Control", or "Command" on a Mac, to select more than one.'
|
||||
self.help_text = string_concat(self.help_text,
|
||||
gettext_lazy(' Hold down "Control", or "Command" on a Mac, to select more than one.'))
|
||||
|
||||
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
||||
if db_index is None:
|
||||
|
||||
@@ -234,6 +234,97 @@ class Parser:
|
||||
def delete_first_token(self):
|
||||
del self.tokens[0]
|
||||
|
||||
class TokenParser:
|
||||
"""
|
||||
You need to subclass this class and implement the
|
||||
top method to parse your template line. When instantiating
|
||||
the parser, you pass in the line from the django template
|
||||
parser.
|
||||
|
||||
If your tag needs to know what tag name it was called with,
|
||||
you find it in the tagname instance variable of the parser.
|
||||
"""
|
||||
|
||||
def __init__(self, subject):
|
||||
self.subject = subject
|
||||
self.pointer = 0
|
||||
self.backout = []
|
||||
self.tagname = self.tag()
|
||||
|
||||
def top(self):
|
||||
"""
|
||||
You need to overload this method to do the actual parsing
|
||||
and return the result.
|
||||
"""
|
||||
raise NotImplemented
|
||||
|
||||
def more(self):
|
||||
"""
|
||||
This returns True if there is more stuff in the tag.
|
||||
"""
|
||||
return self.pointer < len(self.subject)
|
||||
|
||||
def back(self):
|
||||
"""
|
||||
This method undos the last microparser. This can be
|
||||
used for lookahead and backtracking.
|
||||
"""
|
||||
if not len(self.backout):
|
||||
raise TemplateSyntaxError, "back called without some previous parsing"
|
||||
self.pointer = self.backout.pop()
|
||||
|
||||
def tag(self):
|
||||
"""
|
||||
This microparser just returns the next tag from the line.
|
||||
"""
|
||||
subject = self.subject
|
||||
i = self.pointer
|
||||
if i >= len(subject):
|
||||
raise TemplateSyntaxError, "expected another tag, found end of string: %s" % subject
|
||||
p = i
|
||||
while i < len(subject) and subject[i] not in (' ', '\t'):
|
||||
i += 1
|
||||
s = subject[p:i]
|
||||
while i < len(subject) and subject[i] in (' ', '\t'):
|
||||
i += 1
|
||||
self.backout.append(self.pointer)
|
||||
self.pointer = i
|
||||
return s
|
||||
|
||||
def value(self):
|
||||
"""
|
||||
This microparser parses for a value - some string constant or
|
||||
variable name.
|
||||
"""
|
||||
subject = self.subject
|
||||
i = self.pointer
|
||||
if i >= len(subject):
|
||||
raise TemplateSyntaxError, "searching for value expected another value, found end of string: %s" % subject
|
||||
if subject[i] in ('"', "'"):
|
||||
p = i
|
||||
i += 1
|
||||
while i < len(subject) and subject[i] != subject[p]:
|
||||
i += 1
|
||||
if i >= len(subject):
|
||||
raise TemplateSyntaxError, "searching for value, unexpected end of string in column %d: %s" % subject
|
||||
i += 1
|
||||
res = subject[p:i]
|
||||
while i < len(subject) and subject[i] in (' ', '\t'):
|
||||
i += 1
|
||||
self.backout.append(self.pointer)
|
||||
self.pointer = i
|
||||
return res
|
||||
else:
|
||||
p = i
|
||||
while i < len(subject) and subject[i] not in (' ', '\t'):
|
||||
i += 1
|
||||
s = subject[p:i]
|
||||
while i < len(subject) and subject[i] in (' ', '\t'):
|
||||
i += 1
|
||||
self.backout.append(self.pointer)
|
||||
self.pointer = i
|
||||
return s
|
||||
|
||||
class FilterParser:
|
||||
"""Parse a variable token and its optional filters (all as a single string),
|
||||
and return a list of tuples of the filter name and arguments.
|
||||
@@ -255,8 +346,12 @@ class FilterParser:
|
||||
self.filters = []
|
||||
self.current_filter_name = None
|
||||
self.current_filter_arg = None
|
||||
# First read the variable part
|
||||
self.var = self.read_alphanumeric_token()
|
||||
# First read the variable part - decide on wether we need
|
||||
# to parse a string or a variable by peeking into the stream
|
||||
if self.peek_char() in ('_', '"', "'"):
|
||||
self.var = self.read_constant_string_token()
|
||||
else:
|
||||
self.var = self.read_alphanumeric_token()
|
||||
if not self.var:
|
||||
raise TemplateSyntaxError, "Could not read variable name: '%s'" % self.s
|
||||
if self.var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or self.var[0] == '_':
|
||||
@@ -269,6 +364,12 @@ class FilterParser:
|
||||
# We have a filter separator; start reading the filters
|
||||
self.read_filters()
|
||||
|
||||
def peek_char(self):
|
||||
try:
|
||||
return self.s[self.i+1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def next_char(self):
|
||||
self.i = self.i + 1
|
||||
try:
|
||||
@@ -276,6 +377,37 @@ class FilterParser:
|
||||
except IndexError:
|
||||
self.current = None
|
||||
|
||||
def read_constant_string_token(self):
|
||||
"""Read a constant string that must be delimited by either "
|
||||
or ' characters. The string is returned with it's delimiters."""
|
||||
val = ''
|
||||
qchar = None
|
||||
i18n = False
|
||||
self.next_char()
|
||||
if self.current == '_':
|
||||
i18n = True
|
||||
self.next_char()
|
||||
if self.current != '(':
|
||||
raise TemplateSyntaxError, "Bad character (expecting '(') '%s'" % self.current
|
||||
self.next_char()
|
||||
if not self.current in ('"', "'"):
|
||||
raise TemplateSyntaxError, "Bad character (expecting '\"' or ''') '%s'" % self.current
|
||||
qchar = self.current
|
||||
val += qchar
|
||||
while 1:
|
||||
self.next_char()
|
||||
if self.current == qchar:
|
||||
break
|
||||
val += self.current
|
||||
val += self.current
|
||||
self.next_char()
|
||||
if i18n:
|
||||
if self.current != ')':
|
||||
raise TemplateSyntaxError, "Bad character (expecting ')') '%s'" % self.current
|
||||
self.next_char()
|
||||
val = qchar+_(val.strip(qchar))+qchar
|
||||
return val
|
||||
|
||||
def read_alphanumeric_token(self):
|
||||
"""Read a variable name or filter name, which are continuous strings of
|
||||
alphanumeric characters + the underscore"""
|
||||
@@ -318,8 +450,15 @@ class FilterParser:
|
||||
return (self.current_filter_name, self.current_filter_arg)
|
||||
|
||||
def read_arg(self):
|
||||
# First read a "
|
||||
# First read a " or a _("
|
||||
self.next_char()
|
||||
translated = False
|
||||
if self.current == '_':
|
||||
self.next_char()
|
||||
if self.current != '(':
|
||||
raise TemplateSyntaxError, "Bad character (expecting '(') '%s'" % self.current
|
||||
translated = True
|
||||
self.next_char()
|
||||
if self.current != '"':
|
||||
raise TemplateSyntaxError, "Bad character (expecting '\"') '%s'" % self.current
|
||||
self.escaped = False
|
||||
@@ -346,6 +485,11 @@ class FilterParser:
|
||||
arg += self.current
|
||||
# self.current must now be '"'
|
||||
self.next_char()
|
||||
if translated:
|
||||
if self.current != ')':
|
||||
raise TemplateSyntaxError, "Bad character (expecting ')') '%s'" % self.current
|
||||
self.next_char()
|
||||
arg = _(arg)
|
||||
return arg
|
||||
|
||||
def get_filters_from_token(token):
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, get_filters_from_token, registered_filters
|
||||
from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, register_tag
|
||||
from django.utils import translation
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
class CommentNode(Node):
|
||||
def render(self, context):
|
||||
|
||||
@@ -25,6 +25,7 @@ slug_re = re.compile(r'^[-\w]+$')
|
||||
url_re = re.compile(r'^http://\S+$')
|
||||
|
||||
from django.conf.settings import JING_PATH
|
||||
from django.utils.translation import gettext_lazy, ngettext
|
||||
|
||||
class ValidationError(Exception):
|
||||
def __init__(self, message):
|
||||
@@ -54,11 +55,11 @@ class CriticalValidationError(Exception):
|
||||
|
||||
def isAlphaNumeric(field_data, all_data):
|
||||
if not alnum_re.search(field_data):
|
||||
raise ValidationError, "This value must contain only letters, numbers and underscores."
|
||||
raise ValidationError, _("This value must contain only letters, numbers and underscores.")
|
||||
|
||||
def isAlphaNumericURL(field_data, all_data):
|
||||
if not alnumurl_re.search(field_data):
|
||||
raise ValidationError, "This value must contain only letters, numbers, underscores or slashes."
|
||||
raise ValidationError, _("This value must contain only letters, numbers, underscores and slashes.")
|
||||
|
||||
def isSlug(field_data, all_data):
|
||||
if not slug_re.search(field_data):
|
||||
@@ -66,18 +67,18 @@ def isSlug(field_data, all_data):
|
||||
|
||||
def isLowerCase(field_data, all_data):
|
||||
if field_data.lower() != field_data:
|
||||
raise ValidationError, "Uppercase letters are not allowed here."
|
||||
raise ValidationError, _("Uppercase letters are not allowed here.")
|
||||
|
||||
def isUpperCase(field_data, all_data):
|
||||
if field_data.upper() != field_data:
|
||||
raise ValidationError, "Lowercase letters are not allowed here."
|
||||
raise ValidationError, _("Lowercase letters are not allowed here.")
|
||||
|
||||
def isCommaSeparatedIntegerList(field_data, all_data):
|
||||
for supposed_int in field_data.split(','):
|
||||
try:
|
||||
int(supposed_int)
|
||||
except ValueError:
|
||||
raise ValidationError, "Enter only digits separated by commas."
|
||||
raise ValidationError, _("Enter only digits separated by commas.")
|
||||
|
||||
def isCommaSeparatedEmailList(field_data, all_data):
|
||||
"""
|
||||
@@ -89,51 +90,51 @@ def isCommaSeparatedEmailList(field_data, all_data):
|
||||
try:
|
||||
isValidEmail(supposed_email.strip(), '')
|
||||
except ValidationError:
|
||||
raise ValidationError, "Enter valid e-mail addresses separated by commas."
|
||||
raise ValidationError, _("Enter valid e-mail addresses separated by commas.")
|
||||
|
||||
def isValidIPAddress4(field_data, all_data):
|
||||
if ip4_re.search(field_data):
|
||||
valid_parts = [el for el in field_data.split('.') if 0 <= int(el) <= 255]
|
||||
if len(valid_parts) == 4:
|
||||
return
|
||||
raise validators.ValidationError, "Please enter a valid IP address."
|
||||
raise validators.ValidationError, _("Please enter a valid IP address.")
|
||||
|
||||
def isNotEmpty(field_data, all_data):
|
||||
if field_data.strip() == '':
|
||||
raise ValidationError, "Empty values are not allowed here."
|
||||
raise ValidationError, _("Empty values are not allowed here.")
|
||||
|
||||
def isOnlyDigits(field_data, all_data):
|
||||
if not field_data.isdigit():
|
||||
raise ValidationError, "Non-numeric characters aren't allowed here."
|
||||
raise ValidationError, _("Non-numeric characters aren't allowed here.")
|
||||
|
||||
def isNotOnlyDigits(field_data, all_data):
|
||||
if field_data.isdigit():
|
||||
raise ValidationError, "This value can't be comprised solely of digits."
|
||||
raise ValidationError, _("This value can't be comprised solely of digits.")
|
||||
|
||||
def isInteger(field_data, all_data):
|
||||
# This differs from isOnlyDigits because this accepts the negative sign
|
||||
if not integer_re.search(field_data):
|
||||
raise ValidationError, "Enter a whole number."
|
||||
raise ValidationError, _("Enter a whole number.")
|
||||
|
||||
def isOnlyLetters(field_data, all_data):
|
||||
if not field_data.isalpha():
|
||||
raise ValidationError, "Only alphabetical characters are allowed here."
|
||||
raise ValidationError, _("Only alphabetical characters are allowed here.")
|
||||
|
||||
def isValidANSIDate(field_data, all_data):
|
||||
if not ansi_date_re.search(field_data):
|
||||
raise ValidationError, 'Enter a valid date in YYYY-MM-DD format.'
|
||||
raise ValidationError, _('Enter a valid date in YYYY-MM-DD format.')
|
||||
|
||||
def isValidANSITime(field_data, all_data):
|
||||
if not ansi_time_re.search(field_data):
|
||||
raise ValidationError, 'Enter a valid time in HH:MM format.'
|
||||
raise ValidationError, _('Enter a valid time in HH:MM format.')
|
||||
|
||||
def isValidANSIDatetime(field_data, all_data):
|
||||
if not ansi_datetime_re.search(field_data):
|
||||
raise ValidationError, 'Enter a valid date/time in YYYY-MM-DD HH:MM format.'
|
||||
raise ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.')
|
||||
|
||||
def isValidEmail(field_data, all_data):
|
||||
if not email_re.search(field_data):
|
||||
raise ValidationError, 'Enter a valid e-mail address.'
|
||||
raise ValidationError, _('Enter a valid e-mail address.')
|
||||
|
||||
def isValidImage(field_data, all_data):
|
||||
"""
|
||||
@@ -145,18 +146,18 @@ def isValidImage(field_data, all_data):
|
||||
try:
|
||||
Image.open(StringIO(field_data['content']))
|
||||
except IOError: # Python Imaging Library doesn't recognize it as an image
|
||||
raise ValidationError, "Upload a valid image. The file you uploaded was either not an image or a corrupted image."
|
||||
raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.")
|
||||
|
||||
def isValidImageURL(field_data, all_data):
|
||||
uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png'))
|
||||
try:
|
||||
uc(field_data, all_data)
|
||||
except URLMimeTypeCheck.InvalidContentType:
|
||||
raise ValidationError, "The URL %s does not point to a valid image." % field_data
|
||||
raise ValidationError, _("The URL %s does not point to a valid image.") % field_data
|
||||
|
||||
def isValidPhone(field_data, all_data):
|
||||
if not phone_re.search(field_data):
|
||||
raise ValidationError, 'Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.' % field_data
|
||||
raise ValidationError, _('Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.') % field_data
|
||||
|
||||
def isValidQuicktimeVideoURL(field_data, all_data):
|
||||
"Checks that the given URL is a video that can be played by QuickTime (qt, mpeg)"
|
||||
@@ -164,11 +165,11 @@ def isValidQuicktimeVideoURL(field_data, all_data):
|
||||
try:
|
||||
uc(field_data, all_data)
|
||||
except URLMimeTypeCheck.InvalidContentType:
|
||||
raise ValidationError, "The URL %s does not point to a valid QuickTime video." % field_data
|
||||
raise ValidationError, _("The URL %s does not point to a valid QuickTime video.") % field_data
|
||||
|
||||
def isValidURL(field_data, all_data):
|
||||
if not url_re.search(field_data):
|
||||
raise ValidationError, "A valid URL is required."
|
||||
raise ValidationError, _("A valid URL is required.")
|
||||
|
||||
def isValidHTML(field_data, all_data):
|
||||
import urllib, urllib2
|
||||
@@ -182,14 +183,14 @@ def isValidHTML(field_data, all_data):
|
||||
return
|
||||
from xml.dom.minidom import parseString
|
||||
error_messages = [e.firstChild.wholeText for e in parseString(u.read()).getElementsByTagName('messages')[0].getElementsByTagName('msg')]
|
||||
raise ValidationError, "Valid HTML is required. Specific errors are:\n%s" % "\n".join(error_messages)
|
||||
raise ValidationError, _("Valid HTML is required. Specific errors are:\n%s") % "\n".join(error_messages)
|
||||
|
||||
def isWellFormedXml(field_data, all_data):
|
||||
from xml.dom.minidom import parseString
|
||||
try:
|
||||
parseString(field_data)
|
||||
except Exception, e: # Naked except because we're not sure what will be thrown
|
||||
raise ValidationError, "Badly formed XML: %s" % str(e)
|
||||
raise ValidationError, _("Badly formed XML: %s") % str(e)
|
||||
|
||||
def isWellFormedXmlFragment(field_data, all_data):
|
||||
isWellFormedXml('<root>%s</root>' % field_data, all_data)
|
||||
@@ -199,15 +200,15 @@ def isExistingURL(field_data, all_data):
|
||||
try:
|
||||
u = urllib2.urlopen(field_data)
|
||||
except ValueError:
|
||||
raise ValidationError, "Invalid URL: %s" % field_data
|
||||
raise ValidationError, _("Invalid URL: %s") % field_data
|
||||
except: # urllib2.HTTPError, urllib2.URLError, httplib.InvalidURL, etc.
|
||||
raise ValidationError, "The URL %s is a broken link." % field_data
|
||||
raise ValidationError, _("The URL %s is a broken link.") % field_data
|
||||
|
||||
def isValidUSState(field_data, all_data):
|
||||
"Checks that the given string is a valid two-letter U.S. state abbreviation"
|
||||
states = ['AA', 'AE', 'AK', 'AL', 'AP', 'AR', 'AS', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'FM', 'GA', 'GU', 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', 'MH', 'MI', 'MN', 'MO', 'MP', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'PR', 'PW', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VA', 'VI', 'VT', 'WA', 'WI', 'WV', 'WY']
|
||||
if field_data.upper() not in states:
|
||||
raise ValidationError, "Enter a valid U.S. state abbreviation."
|
||||
raise ValidationError, _("Enter a valid U.S. state abbreviation.")
|
||||
|
||||
def hasNoProfanities(field_data, all_data):
|
||||
"""
|
||||
@@ -222,15 +223,14 @@ def hasNoProfanities(field_data, all_data):
|
||||
if words_seen:
|
||||
from django.utils.text import get_text_list
|
||||
plural = len(words_seen) > 1
|
||||
raise ValidationError, "Watch your mouth! The word%s %s %s not allowed here." % \
|
||||
(plural and 's' or '',
|
||||
get_text_list(['"%s%s%s"' % (i[0], '-'*(len(i)-2), i[-1]) for i in words_seen], 'and'),
|
||||
plural and 'are' or 'is')
|
||||
raise ValidationError, ngettext("Watch your mouth! The word %s is not allowed here.",
|
||||
"Watch your mouth! The words %s are not allowed here.", plural) % \
|
||||
get_text_list(['"%s%s%s"' % (i[0], '-'*(len(i)-2), i[-1]) for i in words_seen], 'and')
|
||||
|
||||
class AlwaysMatchesOtherField:
|
||||
def __init__(self, other_field_name, error_message=None):
|
||||
self.other = other_field_name
|
||||
self.error_message = error_message or "This field must match the '%s' field." % self.other
|
||||
self.error_message = error_message or gettext_lazy("This field must match the '%s' field.") % self.other
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
@@ -249,7 +249,7 @@ class ValidateIfOtherFieldEquals:
|
||||
v(field_data, all_data)
|
||||
|
||||
class RequiredIfOtherFieldNotGiven:
|
||||
def __init__(self, other_field_name, error_message="Please enter something for at least one field."):
|
||||
def __init__(self, other_field_name, error_message=gettext_lazy("Please enter something for at least one field.")):
|
||||
self.other, self.error_message = other_field_name, error_message
|
||||
self.always_test = True
|
||||
|
||||
@@ -258,7 +258,7 @@ class RequiredIfOtherFieldNotGiven:
|
||||
raise ValidationError, self.error_message
|
||||
|
||||
class RequiredIfOtherFieldsGiven:
|
||||
def __init__(self, other_field_names, error_message="Please enter both fields or leave them both empty."):
|
||||
def __init__(self, other_field_names, error_message=gettext_lazy("Please enter both fields or leave them both empty.")):
|
||||
self.other, self.error_message = other_field_names, error_message
|
||||
self.always_test = True
|
||||
|
||||
@@ -269,14 +269,15 @@ class RequiredIfOtherFieldsGiven:
|
||||
|
||||
class RequiredIfOtherFieldGiven(RequiredIfOtherFieldsGiven):
|
||||
"Like RequiredIfOtherFieldsGiven, but takes a single field name instead of a list."
|
||||
def __init__(self, other_field_name, error_message="Please enter both fields or leave them both empty."):
|
||||
def __init__(self, other_field_name, error_message=gettext_lazy("Please enter both fields or leave them both empty.")):
|
||||
RequiredIfOtherFieldsGiven.__init__(self, [other_field_name], error_message)
|
||||
|
||||
class RequiredIfOtherFieldEquals:
|
||||
def __init__(self, other_field, other_value, error_message=None):
|
||||
self.other_field = other_field
|
||||
self.other_value = other_value
|
||||
self.error_message = error_message or "This field must be given if %s is %s" % (other_field, other_value)
|
||||
self.error_message = error_message or gettext_lazy("This field must be given if %(field)s is %(value)s") % {
|
||||
'field': other_field, 'value': other_value}
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
@@ -287,7 +288,8 @@ class RequiredIfOtherFieldDoesNotEqual:
|
||||
def __init__(self, other_field, other_value, error_message=None):
|
||||
self.other_field = other_field
|
||||
self.other_value = other_value
|
||||
self.error_message = error_message or "This field must be given if %s is not %s" % (other_field, other_value)
|
||||
self.error_message = error_message or gettext_lazy("This field must be given if %(field)s is not %(value)s") % {
|
||||
'field': other_field, 'value': other_value}
|
||||
self.always_test = True
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
@@ -305,7 +307,7 @@ class IsLessThanOtherField:
|
||||
class UniqueAmongstFieldsWithPrefix:
|
||||
def __init__(self, field_name, prefix, error_message):
|
||||
self.field_name, self.prefix = field_name, prefix
|
||||
self.error_message = error_message or "Duplicate values are not allowed."
|
||||
self.error_message = error_message or gettext_lazy("Duplicate values are not allowed.")
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
for field_name, value in all_data.items():
|
||||
@@ -328,7 +330,7 @@ class IsAPowerOf:
|
||||
from math import log
|
||||
val = log(int(field_data)) / log(self.power_of)
|
||||
if val != int(val):
|
||||
raise ValidationError, "This value must be a power of %s." % self.power_of
|
||||
raise ValidationError, _("This value must be a power of %s.") % self.power_of
|
||||
|
||||
class IsValidFloat:
|
||||
def __init__(self, max_digits, decimal_places):
|
||||
@@ -339,13 +341,13 @@ class IsValidFloat:
|
||||
try:
|
||||
float(data)
|
||||
except ValueError:
|
||||
raise ValidationError, "Please enter a valid decimal number."
|
||||
raise ValidationError, _("Please enter a valid decimal number.")
|
||||
if len(data) > (self.max_digits + 1):
|
||||
raise ValidationError, "Please enter a valid decimal number with at most %s total digit%s." % \
|
||||
(self.max_digits, self.max_digits > 1 and 's' or '')
|
||||
raise ValidationError, ngettext( "Please enter a valid decimal number with at most %s total digit.",
|
||||
"Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
|
||||
if '.' in data and len(data.split('.')[1]) > self.decimal_places:
|
||||
raise ValidationError, "Please enter a valid decimal number with at most %s decimal place%s." % \
|
||||
(self.decimal_places, self.decimal_places > 1 and 's' or '')
|
||||
raise ValidationError, ngettext("Please enter a valid decimal number with at most %s decimal place.",
|
||||
"Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places
|
||||
|
||||
class HasAllowableSize:
|
||||
"""
|
||||
@@ -354,8 +356,8 @@ class HasAllowableSize:
|
||||
"""
|
||||
def __init__(self, min_size=None, max_size=None, min_error_message=None, max_error_message=None):
|
||||
self.min_size, self.max_size = min_size, max_size
|
||||
self.min_error_message = min_error_message or "Make sure your uploaded file is at least %s bytes big." % min_size
|
||||
self.max_error_message = max_error_message or "Make sure your uploaded file is at most %s bytes big." % min_size
|
||||
self.min_error_message = min_error_message or gettext_lazy("Make sure your uploaded file is at least %s bytes big.") % min_size
|
||||
self.max_error_message = max_error_message or gettext_lazy("Make sure your uploaded file is at most %s bytes big.") % min_size
|
||||
|
||||
def __call__(self, field_data, all_data):
|
||||
if self.min_size is not None and len(field_data['content']) < self.min_size:
|
||||
@@ -368,7 +370,7 @@ class MatchesRegularExpression:
|
||||
Checks that the field matches the given regular-expression. The regex
|
||||
should be in string format, not already compiled.
|
||||
"""
|
||||
def __init__(self, regexp, error_message="The format for this field is wrong."):
|
||||
def __init__(self, regexp, error_message=gettext_lazy("The format for this field is wrong.")):
|
||||
self.regexp = re.compile(regexp)
|
||||
self.error_message = error_message
|
||||
|
||||
@@ -383,7 +385,7 @@ class AnyValidator:
|
||||
as a validation error. The message is rather unspecific, so it's best to
|
||||
specify one on instantiation.
|
||||
"""
|
||||
def __init__(self, validator_list=[], error_message="This field is invalid."):
|
||||
def __init__(self, validator_list=[], error_message=gettext_lazy("This field is invalid.")):
|
||||
self.validator_list = validator_list
|
||||
self.error_message = error_message
|
||||
for v in validator_list:
|
||||
@@ -418,10 +420,11 @@ class URLMimeTypeCheck:
|
||||
try:
|
||||
info = urllib2.urlopen(field_data).info()
|
||||
except (urllib2.HTTPError, urllib2.URLError):
|
||||
raise URLMimeTypeCheck.CouldNotRetrieve, "Could not retrieve anything from %s." % field_data
|
||||
raise URLMimeTypeCheck.CouldNotRetrieve, _("Could not retrieve anything from %s.") % field_data
|
||||
content_type = info['content-type']
|
||||
if content_type not in self.mime_type_list:
|
||||
raise URLMimeTypeCheck.InvalidContentType, "The URL %s returned the invalid Content-Type header '%s'." % (field_data, content_type)
|
||||
raise URLMimeTypeCheck.InvalidContentType, _("The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'.") % {
|
||||
'url': field_data, 'contenttype': content_type}
|
||||
|
||||
class RelaxNGCompact:
|
||||
"Validate against a Relax NG compact schema"
|
||||
@@ -453,31 +456,31 @@ class RelaxNGCompact:
|
||||
# Scrape the Jing error messages to reword them more nicely.
|
||||
m = re.search(r'Expected "(.*?)" to terminate element starting on line (\d+)', message)
|
||||
if m:
|
||||
display_errors.append('Please close the unclosed %s tag from line %s. (Line starts with "%s".)' % \
|
||||
(m.group(1).replace('/', ''), m.group(2), lines[int(m.group(2)) - 1][:30]))
|
||||
display_errors.append(_('Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "%(start)s".)') % \
|
||||
{'tag':m.group(1).replace('/', ''), 'line':m.group(2), 'start':lines[int(m.group(2)) - 1][:30]})
|
||||
continue
|
||||
if message.strip() == 'text not allowed here':
|
||||
display_errors.append('Some text starting on line %s is not allowed in that context. (Line starts with "%s".)' % \
|
||||
(line, lines[int(line) - 1][:30]))
|
||||
display_errors.append(_('Some text starting on line %(line)s is not allowed in that context. (Line starts with "%(start)s".)') % \
|
||||
{'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
m = re.search(r'\s*attribute "(.*?)" not allowed at this point; ignored', message)
|
||||
if m:
|
||||
display_errors.append('"%s" on line %s is an invalid attribute. (Line starts with "%s".)' % \
|
||||
(m.group(1), line, lines[int(line) - 1][:30]))
|
||||
display_errors.append(_('"%(attr)s" on line %(line)s is an invalid attribute. (Line starts with "%(start)s".)') % \
|
||||
{'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
m = re.search(r'\s*unknown element "(.*?)"', message)
|
||||
if m:
|
||||
display_errors.append('"<%s>" on line %s is an invalid tag. (Line starts with "%s".)' % \
|
||||
(m.group(1), line, lines[int(line) - 1][:30]))
|
||||
display_errors.append(_('"<%(tag)s>" on line %(line)s is an invalid tag. (Line starts with "%(start)s".)') % \
|
||||
{'tag':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
if message.strip() == 'required attributes missing':
|
||||
display_errors.append('A tag on line %s is missing one or more required attributes. (Line starts with "%s".)' % \
|
||||
(line, lines[int(line) - 1][:30]))
|
||||
display_errors.append(_('A tag on line %(line)s is missing one or more required attributes. (Line starts with "%(start)s".)') % \
|
||||
{'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
m = re.search(r'\s*bad value for attribute "(.*?)"', message)
|
||||
if m:
|
||||
display_errors.append('The "%s" attribute on line %s has an invalid value. (Line starts with "%s".)' % \
|
||||
(m.group(1), line, lines[int(line) - 1][:30]))
|
||||
display_errors.append(_('The "%(attr)s" attribute on line %(line)s has an invalid value. (Line starts with "%(start)s".)') % \
|
||||
{'attr':m.group(1), 'line':line, 'start':lines[int(line) - 1][:30]})
|
||||
continue
|
||||
# Failing all those checks, use the default error message.
|
||||
display_error = 'Line %s: %s [%s]' % (line, message, level.strip())
|
||||
|
||||
Reference in New Issue
Block a user