1. Andriy Kornatskyy
  2. wheezy.captcha

Commits

Andriy Kornatskyy  committed 8d04eb4

Introduced validation localization.

  • Participants
  • Parent commits 6d09dfc
  • Branches default

Comments (0)

Files changed (7)

File .hgignore

View file
 *.pyc
 *.pyo
 doc/_build
+*.mo

File Makefile

View file
-.SILENT: clean env doctest-cover test doc release upload
-.PHONY: clean env doctest-cover test doc release upload
+.SILENT: clean env doctest-cover test doc release upload po
+.PHONY: clean env doctest-cover test doc release upload po
 
 VERSION=2.7
 PYPI=http://pypi.python.org/simple
 NOSE=env/bin/nosetests-$(VERSION)
 SPHINX=/usr/bin/python /usr/bin/sphinx-build
 
-all: clean doctest-cover test release
+all: clean po doctest-cover test release
 
 debian:
 	apt-get -y update
 	# http://mindref.blogspot.com/2011/09/compile-python-from-source.html
 	apt-get -y install libbz2-dev build-essential python \
 		python-dev python-setuptools python-virtualenv \
-		python-sphinx libfreetype6-dev libjpeg8-dev
+		python-sphinx gettext libfreetype6-dev libjpeg8-dev
 
 env:
 	# The following packages available for python < 3.0
 test-demos:
 	$(PYTEST) -q -x
 
+po:
+	xgettext --join-existing --sort-by-file --omit-header \
+		--add-comments \
+		-o i18n/captcha.po src/wheezy/captcha/*.py
+	cp i18n/captcha.po i18n/en/LC_MESSAGES
+	for l in `ls --hide *.po i18n`; do \
+		echo -n "$$l => "; \
+		msgfmt -v i18n/$$l/LC_MESSAGES/captcha.po \
+			-o i18n/$$l/LC_MESSAGES/captcha.mo; \
+	done
+
 run:
 	$(PYTHON) demos/app.py
 

File demos/app.py

View file
 
 
 captcha = CaptchaContext(captcha_image, cache_factory)
+captcha_handler = captcha.render(quality=65)
 
 
 @accept_method(('GET', 'POST'))
     message, error = '', ''
     if request.method == 'POST':
         errors = {}
-        if not captcha.validate(request, errors):
+        if not captcha.validate(request, errors, gettext=lambda s: s):
             error = errors['turing_number'][-1]
         else:
             message = 'Well done!'
     if path == '/':
         response = welcome(request)
     elif path.startswith('/captcha.jpg'):
-        response = captcha.render(request)
+        response = captcha_handler(request)
     else:
         response = not_found()
     return response

File i18n/captcha.po

View file
+# Messages for ``wheezy.captcha``.
+# Copyright (C) 2012 Andriy Kornatskyy
+# Andriy Kornatskyy <andriy.kornatskyy@live.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: wheezy.captcha\n"
+"Report-Msgid-Bugs-To: Andriy Kornatskyy <andriy.kornatskyy@live.com>\n"
+"POT-Creation-Date: 2012-07-02 10:15+0300\n"
+"PO-Revision-Date: 2012-07-02 10:15+0300\n"
+"Last-Translator: Andriy Kornatskyy <andriy.kornatskyy@live.com>\n"
+"Language: US English\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/wheezy/captcha/http.py:86 src/wheezy/captcha/http.py:92
+#: src/wheezy/captcha/http.py:95
+msgid "The challenge code is not available."
+msgstr "The challenge code is not available."
+
+#: src/wheezy/captcha/http.py:90 src/wheezy/captcha/http.py:96
+#: src/wheezy/captcha/http.py:99
+msgid "The turing number is not available."
+msgstr "The turing number is not available."
+
+#: src/wheezy/captcha/http.py:96 src/wheezy/captcha/http.py:102
+#: src/wheezy/captcha/http.py:105
+msgid "The challenge code is invalid."
+msgstr "The challenge code is invalid."
+
+#: src/wheezy/captcha/http.py:101 src/wheezy/captcha/http.py:107
+#: src/wheezy/captcha/http.py:110
+msgid "The turing number is invalid."
+msgstr "The turing number is invalid."
+
+#: src/wheezy/captcha/http.py:111 src/wheezy/captcha/http.py:117
+#: src/wheezy/captcha/http.py:120
+#, python-format
+msgid "The code you typed has expired after %d seconds."
+msgstr "The code you typed has expired after %d seconds."
+
+#: src/wheezy/captcha/http.py:120 src/wheezy/captcha/http.py:126
+#: src/wheezy/captcha/http.py:129
+#, python-format
+msgid "The code was typed too quickly. Wait at least %d seconds."
+msgstr "The code was typed too quickly. Wait at least %d seconds."
+
+#: src/wheezy/captcha/http.py:125 src/wheezy/captcha/http.py:131
+#: src/wheezy/captcha/http.py:134
+msgid "The code you typed has no match."
+msgstr "The code you typed has no match."

File i18n/en/LC_MESSAGES/captcha.po

View file
+# Messages for ``wheezy.captcha``.
+# Copyright (C) 2012 Andriy Kornatskyy
+# Andriy Kornatskyy <andriy.kornatskyy@live.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: wheezy.captcha\n"
+"Report-Msgid-Bugs-To: Andriy Kornatskyy <andriy.kornatskyy@live.com>\n"
+"POT-Creation-Date: 2012-07-02 10:15+0300\n"
+"PO-Revision-Date: 2012-07-02 10:15+0300\n"
+"Last-Translator: Andriy Kornatskyy <andriy.kornatskyy@live.com>\n"
+"Language: US English\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/wheezy/captcha/http.py:86 src/wheezy/captcha/http.py:92
+#: src/wheezy/captcha/http.py:95
+msgid "The challenge code is not available."
+msgstr "The challenge code is not available."
+
+#: src/wheezy/captcha/http.py:90 src/wheezy/captcha/http.py:96
+#: src/wheezy/captcha/http.py:99
+msgid "The turing number is not available."
+msgstr "The turing number is not available."
+
+#: src/wheezy/captcha/http.py:96 src/wheezy/captcha/http.py:102
+#: src/wheezy/captcha/http.py:105
+msgid "The challenge code is invalid."
+msgstr "The challenge code is invalid."
+
+#: src/wheezy/captcha/http.py:101 src/wheezy/captcha/http.py:107
+#: src/wheezy/captcha/http.py:110
+msgid "The turing number is invalid."
+msgstr "The turing number is invalid."
+
+#: src/wheezy/captcha/http.py:111 src/wheezy/captcha/http.py:117
+#: src/wheezy/captcha/http.py:120
+#, python-format
+msgid "The code you typed has expired after %d seconds."
+msgstr "The code you typed has expired after %d seconds."
+
+#: src/wheezy/captcha/http.py:120 src/wheezy/captcha/http.py:126
+#: src/wheezy/captcha/http.py:129
+#, python-format
+msgid "The code was typed too quickly. Wait at least %d seconds."
+msgstr "The code was typed too quickly. Wait at least %d seconds."
+
+#: src/wheezy/captcha/http.py:125 src/wheezy/captcha/http.py:131
+#: src/wheezy/captcha/http.py:134
+msgid "The code you typed has no match."
+msgstr "The code you typed has no match."

File i18n/ru/LC_MESSAGES/captcha.po

View file
+# Messages for ``wheezy.captcha``.
+# Copyright (C) 2012 Andriy Kornatskyy
+# Andriy Kornatskyy <andriy.kornatskyy@live.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: wheezy.captcha\n"
+"Report-Msgid-Bugs-To: Andriy Kornatskyy <andriy.kornatskyy@live.com>\n"
+"POT-Creation-Date: 2012-07-02 10:15+0300\n"
+"PO-Revision-Date: 2012-07-02 10:15+0300\n"
+"Last-Translator: Andriy Kornatskyy <andriy.kornatskyy@live.com>\n"
+"Language: Russian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/wheezy/captcha/http.py:95
+msgid "The challenge code is not available."
+msgstr "Проверочный код недоступен."
+
+#: src/wheezy/captcha/http.py:99
+msgid "The turing number is not available."
+msgstr "Число Тьюринга недоступно."
+
+#: src/wheezy/captcha/http.py:105
+msgid "The challenge code is invalid."
+msgstr "Неверный проверочный код."
+
+#: src/wheezy/captcha/http.py:110
+msgid "The turing number is invalid."
+msgstr "Неверное число Тьюринга."
+
+#: src/wheezy/captcha/http.py:120
+#, python-format
+msgid "The code you typed has expired after %d seconds."
+msgstr "Вышел строк действия кода после {0:N0} секунд ожидания."
+
+#: src/wheezy/captcha/http.py:129
+#, python-format
+msgid "The code was typed too quickly. Wait at least %d seconds."
+msgstr "Код введен слишком быстро. Подождите не менее {0:N0} секунд."
+
+#: src/wheezy/captcha/http.py:134
+msgid "The code you typed has no match."
+msgstr "Нет соответствий с введенным кодом."

File src/wheezy/captcha/http.py

View file
 class FileAdapter(object):
 
     def __init__(self, response):
-        self.response = response
-
-    def write(self, b):
-        self.response.write_bytes(b)
+        self.write = response.write_bytes
 
 
 class CaptchaContext(object):
                  timeout=5 * 60, profile=None,
                  chars='ABCDEFGHJKLMNPQRSTUVWXYZ23456789',
                  max_chars=4, wait_timeout=2,
-                 challenge_key='c', turing_key='turing_number',
-                 gettext=None):
+                 challenge_key='c', turing_key='turing_number'):
         self.image = image
         self.cache_factory = cache_factory
         self.prefix = prefix
                 vary_query=[challenge_key],
                 duration=timedelta(seconds=wait_timeout),
                 no_store=True)
-        if gettext:
-            self._ = gettext
-        else:
-            self._ = lambda s: s
 
-    @property
-    def render(self):
+    def render(self, content_type='image/jpg', format='JPEG', **options):
         @accept_method('GET')
         @response_cache(self.profile)
         def handler(request):
                     return bad_request()
             finally:
                 context.__exit__(None, None, None)
-            response = HTTPResponse('image/jpg')
+            response = HTTPResponse(content_type)
             self.image(turing_number).save(
-                FileAdapter(response), 'JPEG', quality=65)
+                FileAdapter(response), format, **options)
             return response
         return handler
 
         else:
             return request.query[self.challenge_key][0]
 
-    def validate(self, request, errors):
+    def validate(self, request, errors, gettext):
         if self.challenge_key not in request.form:
-            self.append_error(errors, self._(
+            self.append_error(errors, gettext(
                 'The challenge code is not available.'))
             return False
         if self.turing_key not in request.form:
-            self.append_error(errors, self._(
+            self.append_error(errors, gettext(
                 'The turing number is not available.'))
             return False
         form = last_item_adapter(request.form)
         challenge_code = form[self.challenge_key]
         if len(challenge_code) != 22:
-            self.append_error(errors, self._(
+            self.append_error(errors, gettext(
                 'The challenge code is invalid.'))
             return False
         entered_turing_number = form[self.turing_key]
         if len(entered_turing_number) != self.max_chars:
-            self.append_error(errors, self._(
+            self.append_error(errors, gettext(
                 'The turing number is invalid.'))
             return False
 
             cache = context.__enter__()
             data = cache.get(key, self.namespace)
             if not data:
-                self.append_error(errors, self._(
+                self.append_error(errors, gettext(
                     'The code you typed has expired after %d seconds.')
                     % self.timeout)
                 return False
             context.__exit__(None, None, None)
         issued, turing_number = data
         if issued + self.wait_timeout > int(time()):
-            self.append_error(errors, self._(
+            self.append_error(errors, gettext(
                 'The code was typed too quickly. Wait at least %d seconds.')
                 % self.wait_timeout)
             return False
         if turing_number != entered_turing_number.upper():
             self.append_error(
-                errors, self._('The code you typed has no match.'))
+                errors, gettext('The code you typed has no match.'))
             return False
         return True