Commit b23c857b by Greg Price

Merge pull request #17 from edx/gprice/pref-lang

Use user's preferred language for notifications
parents 15325858 c9ff3ca7
......@@ -3,7 +3,7 @@ host = https://www.transifex.com
[edx-platform.notifier-django]
file_filter = locale/<lang>/LC_MESSAGES/django.po
source_file = locale/en_US/LC_MESSAGES/django.po
source_lang = en_US
source_file = locale/en/LC_MESSAGES/django.po
source_lang = en
type = PO
......@@ -33,7 +33,7 @@ __ http://support.transifex.com/customer/portal/articles/1000855-configuring-the
Django relies on GNU's gettext utilities, which must be installed on your system (packages are available via ``brew`` on OS X and ``apt-get`` on Ubuntu Linux) and on the PATH of the shell from which you run the commands below.
To extract and upload translatable strings: ``python manage.py makemessages -l en_US; tx push -s``
To extract and upload translatable strings: ``python manage.py makemessages -l en; tx push -s``
To download and compile a translation: ``tx pull -l <locale>; python manage.py compilemessages``, where ``<locale>`` is the `locale name`__ for the desired language.
......
*
!.gitignore
\ No newline at end of file
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# francoisedocq, 2014
msgid ""
msgstr ""
"Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-02-10 14:58-0600\n"
"PO-Revision-Date: 2014-02-14 15:33+0000\n"
"Last-Translator: francoisedocq\n"
"Language-Team: French (http://www.transifex.com/projects/p/edx-platform/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. Translators: This string separates two items in a pair (e.g.
#. "Foo and Bar"); note that this includes any necessary whitespace to
#. accommodate languages that do not use whitespace in such a pair construct.
#: notifier/digest.py:88
msgid " and "
msgstr "et"
#. Translators: This string separates items in a list (e.g.
#. "Foo, Bar, Baz, and Quux"); note that this includes any necessary
#. whitespace to accommodate languages that do not use whitespace in
#. such a list construct.
#: notifier/digest.py:93
msgid ", "
msgstr ", "
#. Translators: This string separates the final two items in a list (e.g.
#. "Foo, Bar and Baz"); note that this includes any necessary whitespace to
#. accommodate languages that do not use whitespace in such a list construct.
#: notifier/digest.py:97
msgid ", and "
msgstr ", et"
#: notifier/templates/digest-email.html:72
#: notifier/templates/digest-email.txt:8
#, python-format
msgid "Hi %(name)s,"
msgstr "Bonjour %(name)s,"
#: notifier/templates/digest-email.html:74
#: notifier/templates/digest-email.txt:10
#, python-format
msgid ""
"You have %(thread_count)s discussion thread with updates in "
"%(course_names)s. The most recent highlights are shown below. As a reminder,"
" you can turn off all discussion digests from any course's Discussion Home "
"page."
msgid_plural ""
"You have %(thread_count)s discussion threads with updates in "
"%(course_names)s. The most recent highlights are shown below. As a reminder,"
" you can turn off all discussion digests from any course's Discussion Home "
"page."
msgstr[0] "Vous avez %(thread_count)s fils de discussion avec des nouveautés dans %(course_names)s. Les nouveautés les plus récentes sont mises en évidence ci-dessous. Pour rappel, vous pouvez arrêter les résumés des nouveautés dans la page d'accueil \"Discussion\" de n'importe quel cours."
msgstr[1] "Vous avez %(thread_count)s fils de discussion avec des nouveautés dans %(course_names)s. Les nouveautés les plus récentes sont mises en évidence ci-dessous. Pour rappel, vous pouvez arrêter les résumés des nouveautés dans la page d'accueil \"Discussion\" de n'importe quel cours."
#: notifier/templates/digest-email.html:103
#, python-format
msgid ""
"<span %(author_span_attrs)s>%(author)s: </span><span "
"%(datetime_span_attrs)s>on %(datetime)s UTC</span>"
msgstr "<span %(author_span_attrs)s>%(author)s: </span><span %(datetime_span_attrs)s>à %(datetime)s UTC</span>"
#: notifier/templates/digest-email.html:119
#, python-format
msgid ""
"If you would like to stop receiving these updates, you can turn off all "
"Course Discussion digests from any course's Discussion Home page. You can "
"also <a %(a_attrs)s>quickly turn off these notifications from this "
"email.</a>"
msgstr "Si vous souhaitez ne plus recevoir ces informations de nouveautés, vous pouvez arrêter les résumés de nouveautés dans la page d'accueil \"Discussion\" de n'importe quel cours. Vous pouvez aussi <a %(a_attrs)s> arrêter ces notifications à partir de cet email. "
#: notifier/templates/digest-email.txt:20
#, python-format
msgid "%(author)s: on %(datetime)s UTC"
msgstr "%(author)s: à %(datetime)s UTC"
#: notifier/templates/digest-email.txt:28
#, python-format
msgid ""
"If you would like to stop receiving these updates, you can turn off all "
"Course Discussion digests from any course's Discussion Home page. You can "
"also quickly turn off these notifications by going to %(unsubscribe_url)s."
msgstr "Si vous souhaitez ne plus recevoir ces informations de nouveautés, vous pouvez arrêter les résumés de nouveautés dans la page d'accueil \"Discussion\" de n'importe quel cours. Vous pouvez aussi arrêter ces notifications en allant sur %(unsubscribe_url)s."
......@@ -2,6 +2,7 @@
General formatting and rendering helpers for digest notifications.
"""
from contextlib import contextmanager
import datetime
import logging
import struct
......@@ -10,10 +11,10 @@ from django.conf import settings
from django.template.loader import get_template
from django.template import Context
from django.utils.html import strip_tags
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext as _, activate, deactivate
from statsd import statsd
from notifier.user import UsernameCipher
from notifier.user import UsernameCipher, LANGUAGE_PREFERENCE_KEY
# maximum number of threads to display per course
MAX_COURSE_THREADS = 30
......@@ -157,6 +158,19 @@ def _get_unsubscribe_url(username):
return '{}/notification_prefs/unsubscribe/{}/'.format(settings.LMS_URL_BASE, token)
@contextmanager
def _activate_user_lang(user):
"""
On enter, activate the user's preferred language, if supported. On exit,
deactivate the language.
"""
user_lang = user["preferences"].get(LANGUAGE_PREFERENCE_KEY)
if user_lang and user_lang in dict(settings.LANGUAGES):
activate(user_lang)
yield
deactivate()
class Digest(object):
def __init__(self, courses):
self.courses = sorted(courses, key=lambda c: c.title.lower())
......@@ -216,8 +230,9 @@ def render_digest(user, digest, title, description):
'unsubscribe_url': _get_unsubscribe_url(user['username']),
'postal_address': settings.EMAIL_SENDER_POSTAL_ADDRESS,
})
text = get_template('digest-email.txt').render(context)
html = get_template('digest-email.html').render(context)
with _activate_user_lang(user):
text = get_template('digest-email.txt').render(context)
html = get_template('digest-email.html').render(context)
return (text, html)
......@@ -192,7 +192,11 @@ BROKER_HEARTBEAT_CHECKRATE = 2
# Each worker should only fetch one message at a time
CELERYD_PREFETCH_MULTIPLIER = 1
LANGUAGE_CODE = os.getenv('NOTIFIER_LANGUAGE', 'en-us')
LANGUAGE_CODE = os.getenv('NOTIFIER_LANGUAGE', 'en')
LANGUAGES = (
("en", "English"),
("fr", "French"),
)
USE_L10N = True
LOCALE_PATHS = (os.path.join(os.path.dirname(os.path.dirname(__file__)), 'locale'),)
......
......@@ -5,6 +5,7 @@ from django.test import TestCase
from mock import patch
from notifier.digest import Digest, DigestCourse, DigestItem, DigestThread, render_digest
from notifier.user import LANGUAGE_PREFERENCE_KEY
TEST_COURSE_ID = "test_org/test_num/test_course"
TEST_COMMENTABLE = "test_commentable"
......@@ -59,24 +60,31 @@ class DigestThreadTestCase(TestCase):
@patch("notifier.digest.THREAD_TITLE_MAXLEN", 17)
class RenderDigestTestCase(TestCase):
def _test_unicode_data(self, input_text, expected_text, expected_html=None):
user = {
"id": "0",
"username": "test_user",
}
digest = Digest([
def set_digest(self, thread_title):
self.digest = Digest([
DigestCourse(
TEST_COURSE_ID,
[DigestThread(
"0",
TEST_COURSE_ID,
TEST_COMMENTABLE,
input_text,
thread_title,
[DigestItem("test content", None, None)]
)]
)
])
(rendered_text, rendered_html) = render_digest(user, digest, "Test Title", "Test Description")
def setUp(self):
self.user = {
"id": "0",
"username": "test_user",
"preferences": {}
}
self.set_digest("test title")
def _test_unicode_data(self, input_text, expected_text, expected_html=None):
self.set_digest(input_text)
(rendered_text, rendered_html) = render_digest(self.user, self.digest, "Test Title", "Test Description")
self.assertIn(expected_text, rendered_text)
self.assertIn(expected_html if expected_html else expected_text, rendered_html)
......@@ -101,3 +109,26 @@ class RenderDigestTestCase(TestCase):
def test_string_interp(self):
self._test_unicode_data(u"This post contains %s string interpolation #{syntax}", u"This post...")
@patch("notifier.digest.deactivate")
@patch("notifier.digest.activate")
def test_user_lang_pref_supported(self, mock_activate, mock_deactivate):
user_lang = "fr"
self.user["preferences"][LANGUAGE_PREFERENCE_KEY] = user_lang
render_digest(self.user, self.digest, "dummy", "dummy")
mock_activate.assert_called_with(user_lang)
mock_deactivate.assert_called()
@patch("notifier.digest.activate")
def test_user_lang_pref_unsupported(self, mock_activate):
user_lang = "x-unsupported-lang"
self.user["preferences"][LANGUAGE_PREFERENCE_KEY] = user_lang
render_digest(self.user, self.digest, "dummy", "dummy")
mock_activate.assert_not_called()
@patch("notifier.digest.activate")
def test_user_lang_pref_absent(self, mock_activate):
if LANGUAGE_PREFERENCE_KEY in self.user["preferences"]:
del self.user["preferences"][LANGUAGE_PREFERENCE_KEY]
render_digest(self.user, self.digest, "dummy", "dummy")
mock_activate.assert_not_called()
......@@ -17,6 +17,7 @@ logger = logging.getLogger(__name__)
DIGEST_NOTIFICATION_PREFERENCE_KEY = 'notification_pref'
LANGUAGE_PREFERENCE_KEY = 'pref-lang'
class UserServiceException(Exception):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment