Commit 93bac8ec authored by Kevin Morris's avatar Kevin Morris Committed by Lukas Fleischer
Browse files

aurweb: Globalize a Translator instance, add more utility



+ Added SUPPORTED_LANGUAGES, a global constant dictionary of
  language => display pairs for languages we support.
+ Add Translator.get_translator, a function used to retrieve a
  translator after initializing it (if needed). Use `fallback=True`
  while creating languages, in case we setup a language that we
  don't have a translation for, it will noop the translation.
  This is particularly useful for "en," since we do not translate
  it, but doing this will allow us to go through our normal translation
  flow in any case.
+ Added typing.
+ Added get_request_language, a function that grabs the language for
  a request session, defaulting to aurweb.config [options] default_lang.
+ Added get_raw_translator_for_request, a function that retrieves
  the concrete translation object for a given language.
+ Added tr, a jinja2 contextfilter that can be used to inline translate
  strings in jinja2 templates.
+ Added `python-jinja` dep to .gitlab-ci.yml. This needs to be
  included in documentation before this set is merged in.
+ Introduce pytest units (test_l10n.py) in `test` along with
  __init__.py, which marks `test` as a test package.
+ Additionally, fix up notify.py to use the global translator. Also
  reduce its source width to <= 80 by newlining some code.
+ Additionally, prepare locale in .gitlab-ci.yml and add
  aurweb.config [options] localedir to config.dev with YOUR_AUR_ROOT
  like others.
Signed-off-by: Kevin Morris's avatarKevin Morris <kevr@0cost.org>
Signed-off-by: Lukas Fleischer's avatarLukas Fleischer <lfleischer@archlinux.org>
parent 3bd4aa4c
...@@ -20,6 +20,7 @@ test: ...@@ -20,6 +20,7 @@ test:
script: script:
- python setup.py install - python setup.py install
- sed -r "s;YOUR_AUR_ROOT;$(pwd);g" conf/config.dev > conf/config - sed -r "s;YOUR_AUR_ROOT;$(pwd);g" conf/config.dev > conf/config
- AUR_CONFIG=conf/config make -C po all install
- AUR_CONFIG=conf/config python -m aurweb.initdb - AUR_CONFIG=conf/config python -m aurweb.initdb
- make -C test - make -C test
- coverage report --include='aurweb/*' - coverage report --include='aurweb/*'
import gettext import gettext
import typing
from collections import OrderedDict
from fastapi import Request
from jinja2 import contextfilter
import aurweb.config import aurweb.config
SUPPORTED_LANGUAGES = OrderedDict({
"ar": "العربية",
"ast": "Asturianu",
"ca": "Català",
"cs": "Český",
"da": "Dansk",
"de": "Deutsch",
"el": "Ελληνικά",
"en": "English",
"es": "Español",
"es_419": "Español (Latinoamérica)",
"fi": "Suomi",
"fr": "Français",
"he": "עברית",
"hr": "Hrvatski",
"hu": "Magyar",
"it": "Italiano",
"ja": "日本語",
"nb": "Norsk",
"nl": "Nederlands",
"pl": "Polski",
"pt_BR": "Português (Brasil)",
"pt_PT": "Português (Portugal)",
"ro": "Română",
"ru": "Русский",
"sk": "Slovenčina",
"sr": "Srpski",
"tr": "Türkçe",
"uk": "Українська",
"zh_CN": "简体中文",
"zh_TW": "正體中文"
})
class Translator: class Translator:
def __init__(self): def __init__(self):
self._localedir = aurweb.config.get('options', 'localedir') self._localedir = aurweb.config.get('options', 'localedir')
self._translator = {} self._translator = {}
def translate(self, s, lang): def get_translator(self, lang: str):
if lang == 'en':
return s
if lang not in self._translator: if lang not in self._translator:
self._translator[lang] = gettext.translation("aurweb", self._translator[lang] = gettext.translation("aurweb",
self._localedir, self._localedir,
languages=[lang]) languages=[lang],
return self._translator[lang].gettext(s) fallback=True)
return self._translator.get(lang)
def translate(self, s: str, lang: str):
return self.get_translator(lang).gettext(s)
# Global translator object.
translator = Translator()
def get_translator_for_request(request): def get_request_language(request: Request):
return request.cookies.get("AURLANG",
aurweb.config.get("options", "default_lang"))
def get_raw_translator_for_request(request: Request):
lang = get_request_language(request)
return translator.get_translator(lang)
def get_translator_for_request(request: Request):
""" """
Determine the preferred language from a FastAPI request object and build a Determine the preferred language from a FastAPI request object and build a
translator function for it. translator function for it.
...@@ -29,12 +84,16 @@ def get_translator_for_request(request): ...@@ -29,12 +84,16 @@ def get_translator_for_request(request):
print(_("Hello")) print(_("Hello"))
``` ```
""" """
lang = request.cookies.get("AURLANG") lang = get_request_language(request)
if lang is None:
lang = aurweb.config.get("options", "default_lang")
translator = Translator()
def translate(message): def translate(message):
return translator.translate(message, lang) return translator.translate(message, lang)
return translate return translate
@contextfilter
def tr(context: typing.Any, value: str):
""" A translation filter; example: {{ "Hello" | tr("de") }}. """
_ = get_translator_for_request(context.get("request"))
return _(value)
...@@ -40,9 +40,6 @@ def pkgbase_from_pkgreq(conn, reqid): ...@@ -40,9 +40,6 @@ def pkgbase_from_pkgreq(conn, reqid):
class Notification: class Notification:
def __init__(self):
self._l10n = aurweb.l10n.Translator()
def get_refs(self): def get_refs(self):
return () return ()
...@@ -97,9 +94,12 @@ class Notification: ...@@ -97,9 +94,12 @@ class Notification:
else: else:
# send email using smtplib; no local MTA required # send email using smtplib; no local MTA required
server_addr = aurweb.config.get('notifications', 'smtp-server') server_addr = aurweb.config.get('notifications', 'smtp-server')
server_port = aurweb.config.getint('notifications', 'smtp-port') server_port = aurweb.config.getint('notifications',
use_ssl = aurweb.config.getboolean('notifications', 'smtp-use-ssl') 'smtp-port')
use_starttls = aurweb.config.getboolean('notifications', 'smtp-use-starttls') use_ssl = aurweb.config.getboolean('notifications',
'smtp-use-ssl')
use_starttls = aurweb.config.getboolean('notifications',
'smtp-use-starttls')
user = aurweb.config.get('notifications', 'smtp-user') user = aurweb.config.get('notifications', 'smtp-user')
passwd = aurweb.config.get('notifications', 'smtp-password') passwd = aurweb.config.get('notifications', 'smtp-password')
...@@ -127,7 +127,8 @@ class ResetKeyNotification(Notification): ...@@ -127,7 +127,8 @@ class ResetKeyNotification(Notification):
cur = conn.execute('SELECT UserName, Email, BackupEmail, ' + cur = conn.execute('SELECT UserName, Email, BackupEmail, ' +
'LangPreference, ResetKey ' + 'LangPreference, ResetKey ' +
'FROM Users WHERE ID = ? AND Suspended = 0', [uid]) 'FROM Users WHERE ID = ? AND Suspended = 0', [uid])
self._username, self._to, self._backup, self._lang, self._resetkey = cur.fetchone() self._username, self._to, self._backup, self._lang, self._resetkey = \
cur.fetchone()
super().__init__() super().__init__()
def get_recipients(self): def get_recipients(self):
...@@ -137,15 +138,15 @@ class ResetKeyNotification(Notification): ...@@ -137,15 +138,15 @@ class ResetKeyNotification(Notification):
return [(self._to, self._lang)] return [(self._to, self._lang)]
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('AUR Password Reset', lang) return aurweb.l10n.translator.translate('AUR Password Reset', lang)
def get_body(self, lang): def get_body(self, lang):
return self._l10n.translate( return aurweb.l10n.translator.translate(
'A password reset request was submitted for the account ' 'A password reset request was submitted for the account '
'{user} associated with your email address. If you wish to ' '{user} associated with your email address. If you wish to '
'reset your password follow the link [1] below, otherwise ' 'reset your password follow the link [1] below, otherwise '
'ignore this message and nothing will happen.', 'ignore this message and nothing will happen.',
lang).format(user=self._username) lang).format(user=self._username)
def get_refs(self): def get_refs(self):
return (aur_location + '/passreset/?resetkey=' + self._resetkey,) return (aur_location + '/passreset/?resetkey=' + self._resetkey,)
...@@ -153,15 +154,16 @@ class ResetKeyNotification(Notification): ...@@ -153,15 +154,16 @@ class ResetKeyNotification(Notification):
class WelcomeNotification(ResetKeyNotification): class WelcomeNotification(ResetKeyNotification):
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('Welcome to the Arch User Repository', return aurweb.l10n.translator.translate(
lang) 'Welcome to the Arch User Repository',
lang)
def get_body(self, lang): def get_body(self, lang):
return self._l10n.translate( return aurweb.l10n.translator.translate(
'Welcome to the Arch User Repository! In order to set an ' 'Welcome to the Arch User Repository! In order to set an '
'initial password for your new account, please click the ' 'initial password for your new account, please click the '
'link [1] below. If the link does not work, try copying and ' 'link [1] below. If the link does not work, try copying and '
'pasting it into your browser.', lang) 'pasting it into your browser.', lang)
class CommentNotification(Notification): class CommentNotification(Notification):
...@@ -186,19 +188,21 @@ class CommentNotification(Notification): ...@@ -186,19 +188,21 @@ class CommentNotification(Notification):
return self._recipients return self._recipients
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('AUR Comment for {pkgbase}', return aurweb.l10n.translator.translate(
lang).format(pkgbase=self._pkgbase) 'AUR Comment for {pkgbase}',
lang).format(pkgbase=self._pkgbase)
def get_body(self, lang): def get_body(self, lang):
body = self._l10n.translate( body = aurweb.l10n.translator.translate(
'{user} [1] added the following comment to {pkgbase} [2]:', '{user} [1] added the following comment to {pkgbase} [2]:',
lang).format(user=self._user, pkgbase=self._pkgbase) lang).format(user=self._user, pkgbase=self._pkgbase)
body += '\n\n' + self._text + '\n\n-- \n' body += '\n\n' + self._text + '\n\n-- \n'
dnlabel = self._l10n.translate('Disable notifications', lang) dnlabel = aurweb.l10n.translator.translate(
body += self._l10n.translate( 'Disable notifications', lang)
'If you no longer wish to receive notifications about this ' body += aurweb.l10n.translator.translate(
'package, please go to the package page [2] and select ' 'If you no longer wish to receive notifications about this '
'"{label}".', lang).format(label=dnlabel) 'package, please go to the package page [2] and select '
'"{label}".', lang).format(label=dnlabel)
return body return body
def get_refs(self): def get_refs(self):
...@@ -231,20 +235,21 @@ class UpdateNotification(Notification): ...@@ -231,20 +235,21 @@ class UpdateNotification(Notification):
return self._recipients return self._recipients
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('AUR Package Update: {pkgbase}', return aurweb.l10n.translator.translate(
lang).format(pkgbase=self._pkgbase) 'AUR Package Update: {pkgbase}',
lang).format(pkgbase=self._pkgbase)
def get_body(self, lang): def get_body(self, lang):
body = self._l10n.translate('{user} [1] pushed a new commit to ' body = aurweb.l10n.translator.translate(
'{pkgbase} [2].', lang).format( '{user} [1] pushed a new commit to {pkgbase} [2].',
user=self._user, lang).format(user=self._user, pkgbase=self._pkgbase)
pkgbase=self._pkgbase)
body += '\n\n-- \n' body += '\n\n-- \n'
dnlabel = self._l10n.translate('Disable notifications', lang) dnlabel = aurweb.l10n.translator.translate(
body += self._l10n.translate( 'Disable notifications', lang)
'If you no longer wish to receive notifications about this ' body += aurweb.l10n.translator.translate(
'package, please go to the package page [2] and select ' 'If you no longer wish to receive notifications about this '
'"{label}".', lang).format(label=dnlabel) 'package, please go to the package page [2] and select '
'"{label}".', lang).format(label=dnlabel)
return body return body
def get_refs(self): def get_refs(self):
...@@ -261,15 +266,16 @@ class FlagNotification(Notification): ...@@ -261,15 +266,16 @@ class FlagNotification(Notification):
def __init__(self, conn, uid, pkgbase_id): def __init__(self, conn, uid, pkgbase_id):
self._user = username_from_id(conn, uid) self._user = username_from_id(conn, uid)
self._pkgbase = pkgbase_from_id(conn, pkgbase_id) self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
cur = conn.execute('SELECT DISTINCT Users.Email, ' + cur = conn.execute(
'Users.LangPreference FROM Users ' + 'SELECT DISTINCT Users.Email, ' +
'LEFT JOIN PackageComaintainers ' + 'Users.LangPreference FROM Users ' +
'ON PackageComaintainers.UsersID = Users.ID ' + 'LEFT JOIN PackageComaintainers ' +
'INNER JOIN PackageBases ' + 'ON PackageComaintainers.UsersID = Users.ID ' +
'ON PackageBases.MaintainerUID = Users.ID OR ' + 'INNER JOIN PackageBases ' +
'PackageBases.ID = PackageComaintainers.PackageBaseID ' + 'ON PackageBases.MaintainerUID = Users.ID OR ' +
'WHERE PackageBases.ID = ? AND ' + 'PackageBases.ID = PackageComaintainers.PackageBaseID ' +
'Users.Suspended = 0', [pkgbase_id]) 'WHERE PackageBases.ID = ? AND ' +
'Users.Suspended = 0', [pkgbase_id])
self._recipients = cur.fetchall() self._recipients = cur.fetchall()
cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' + cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
'ID = ?', [pkgbase_id]) 'ID = ?', [pkgbase_id])
...@@ -280,15 +286,15 @@ class FlagNotification(Notification): ...@@ -280,15 +286,15 @@ class FlagNotification(Notification):
return self._recipients return self._recipients
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('AUR Out-of-date Notification for ' return aurweb.l10n.translator.translate(
'{pkgbase}', 'AUR Out-of-date Notification for {pkgbase}',
lang).format(pkgbase=self._pkgbase) lang).format(pkgbase=self._pkgbase)
def get_body(self, lang): def get_body(self, lang):
body = self._l10n.translate( body = aurweb.l10n.translator.translate(
'Your package {pkgbase} [1] has been flagged out-of-date by ' 'Your package {pkgbase} [1] has been flagged out-of-date by '
'{user} [2]:', lang).format(pkgbase=self._pkgbase, '{user} [2]:', lang).format(pkgbase=self._pkgbase,
user=self._user) user=self._user)
body += '\n\n' + self._text body += '\n\n' + self._text
return body return body
...@@ -320,8 +326,9 @@ class OwnershipEventNotification(Notification): ...@@ -320,8 +326,9 @@ class OwnershipEventNotification(Notification):
return self._recipients return self._recipients
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('AUR Ownership Notification for {pkgbase}', return aurweb.l10n.translator.translate(
lang).format(pkgbase=self._pkgbase) 'AUR Ownership Notification for {pkgbase}',
lang).format(pkgbase=self._pkgbase)
def get_refs(self): def get_refs(self):
return (aur_location + '/pkgbase/' + self._pkgbase + '/', return (aur_location + '/pkgbase/' + self._pkgbase + '/',
...@@ -330,17 +337,17 @@ class OwnershipEventNotification(Notification): ...@@ -330,17 +337,17 @@ class OwnershipEventNotification(Notification):
class AdoptNotification(OwnershipEventNotification): class AdoptNotification(OwnershipEventNotification):
def get_body(self, lang): def get_body(self, lang):
return self._l10n.translate( return aurweb.l10n.translator.translate(
'The package {pkgbase} [1] was adopted by {user} [2].', 'The package {pkgbase} [1] was adopted by {user} [2].',
lang).format(pkgbase=self._pkgbase, user=self._user) lang).format(pkgbase=self._pkgbase, user=self._user)
class DisownNotification(OwnershipEventNotification): class DisownNotification(OwnershipEventNotification):
def get_body(self, lang): def get_body(self, lang):
return self._l10n.translate( return aurweb.l10n.translator.translate(
'The package {pkgbase} [1] was disowned by {user} ' 'The package {pkgbase} [1] was disowned by {user} '
'[2].', lang).format(pkgbase=self._pkgbase, '[2].', lang).format(pkgbase=self._pkgbase,
user=self._user) user=self._user)
class ComaintainershipEventNotification(Notification): class ComaintainershipEventNotification(Notification):
...@@ -355,9 +362,9 @@ class ComaintainershipEventNotification(Notification): ...@@ -355,9 +362,9 @@ class ComaintainershipEventNotification(Notification):
return [(self._to, self._lang)] return [(self._to, self._lang)]
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('AUR Co-Maintainer Notification for ' return aurweb.l10n.translator.translate(
'{pkgbase}', 'AUR Co-Maintainer Notification for {pkgbase}',
lang).format(pkgbase=self._pkgbase) lang).format(pkgbase=self._pkgbase)
def get_refs(self): def get_refs(self):
return (aur_location + '/pkgbase/' + self._pkgbase + '/',) return (aur_location + '/pkgbase/' + self._pkgbase + '/',)
...@@ -365,16 +372,16 @@ class ComaintainershipEventNotification(Notification): ...@@ -365,16 +372,16 @@ class ComaintainershipEventNotification(Notification):
class ComaintainerAddNotification(ComaintainershipEventNotification): class ComaintainerAddNotification(ComaintainershipEventNotification):
def get_body(self, lang): def get_body(self, lang):
return self._l10n.translate( return aurweb.l10n.translator.translate(
'You were added to the co-maintainer list of {pkgbase} [1].', 'You were added to the co-maintainer list of {pkgbase} [1].',
lang).format(pkgbase=self._pkgbase) lang).format(pkgbase=self._pkgbase)
class ComaintainerRemoveNotification(ComaintainershipEventNotification): class ComaintainerRemoveNotification(ComaintainershipEventNotification):
def get_body(self, lang): def get_body(self, lang):
return self._l10n.translate( return aurweb.l10n.translator.translate(
'You were removed from the co-maintainer list of {pkgbase} ' 'You were removed from the co-maintainer list of {pkgbase} '
'[1].', lang).format(pkgbase=self._pkgbase) '[1].', lang).format(pkgbase=self._pkgbase)
class DeleteNotification(Notification): class DeleteNotification(Notification):
...@@ -400,25 +407,27 @@ class DeleteNotification(Notification): ...@@ -400,25 +407,27 @@ class DeleteNotification(Notification):
return self._recipients return self._recipients
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('AUR Package deleted: {pkgbase}', return aurweb.l10n.translator.translate(
lang).format(pkgbase=self._old_pkgbase) 'AUR Package deleted: {pkgbase}',
lang).format(pkgbase=self._old_pkgbase)
def get_body(self, lang): def get_body(self, lang):
if self._new_pkgbase: if self._new_pkgbase:
dnlabel = self._l10n.translate('Disable notifications', lang) dnlabel = aurweb.l10n.translator.translate(
return self._l10n.translate( 'Disable notifications', lang)
'{user} [1] merged {old} [2] into {new} [3].\n\n' return aurweb.l10n.translator.translate(
'-- \n' '{user} [1] merged {old} [2] into {new} [3].\n\n'
'If you no longer wish receive notifications about the ' '-- \n'
'new package, please go to [3] and click "{label}".', 'If you no longer wish receive notifications about the '
lang).format(user=self._user, old=self._old_pkgbase, 'new package, please go to [3] and click "{label}".',
new=self._new_pkgbase, label=dnlabel) lang).format(user=self._user, old=self._old_pkgbase,
new=self._new_pkgbase, label=dnlabel)
else: else:
return self._l10n.translate( return aurweb.l10n.translator.translate(
'{user} [1] deleted {pkgbase} [2].\n\n' '{user} [1] deleted {pkgbase} [2].\n\n'
'You will no longer receive notifications about this ' 'You will no longer receive notifications about this '
'package.', lang).format(user=self._user, 'package.', lang).format(user=self._user,
pkgbase=self._old_pkgbase) pkgbase=self._old_pkgbase)
def get_refs(self): def get_refs(self):
refs = (aur_location + '/account/' + self._user + '/', refs = (aur_location + '/account/' + self._user + '/',
...@@ -432,14 +441,15 @@ class RequestOpenNotification(Notification): ...@@ -432,14 +441,15 @@ class RequestOpenNotification(Notification):
def __init__(self, conn, uid, reqid, reqtype, pkgbase_id, merge_into=None): def __init__(self, conn, uid, reqid, reqtype, pkgbase_id, merge_into=None):
self._user = username_from_id(conn, uid) self._user = username_from_id(conn, uid)
self._pkgbase = pkgbase_from_id(conn, pkgbase_id) self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
cur = conn.execute('SELECT DISTINCT Users.Email FROM PackageRequests ' + cur = conn.execute(
'INNER JOIN PackageBases ' + 'SELECT DISTINCT Users.Email FROM PackageRequests ' +
'ON PackageBases.ID = PackageRequests.PackageBaseID ' + 'INNER JOIN PackageBases ' +
'INNER JOIN Users ' + 'ON PackageBases.ID = PackageRequests.PackageBaseID ' +
'ON Users.ID = PackageRequests.UsersID ' + 'INNER JOIN Users ' +
'OR Users.ID = PackageBases.MaintainerUID ' + 'ON Users.ID = PackageRequests.UsersID ' +
'WHERE PackageRequests.ID = ? AND ' + 'OR Users.ID = PackageBases.MaintainerUID ' +
'Users.Suspended = 0', [reqid]) 'WHERE PackageRequests.ID = ? AND ' +
'Users.Suspended = 0', [reqid])
self._to = aurweb.config.get('options', 'aur_request_ml') self._to = aurweb.config.get('options', 'aur_request_ml')
self._cc = [row[0] for row in cur.fetchall()] self._cc = [row[0] for row in cur.fetchall()]
cur = conn.execute('SELECT Comments FROM PackageRequests WHERE ID = ?', cur = conn.execute('SELECT Comments FROM PackageRequests WHERE ID = ?',
...@@ -489,14 +499,15 @@ class RequestOpenNotification(Notification): ...@@ -489,14 +499,15 @@ class RequestOpenNotification(Notification):
class RequestCloseNotification(Notification): class RequestCloseNotification(Notification):
def __init__(self, conn, uid, reqid, reason): def __init__(self, conn, uid, reqid, reason):
self._user = username_from_id(conn, uid) if int(uid) else None self._user = username_from_id(conn, uid) if int(uid) else None
cur = conn.execute('SELECT DISTINCT Users.Email FROM PackageRequests ' + cur = conn.execute(
'INNER JOIN PackageBases ' + 'SELECT DISTINCT Users.Email FROM PackageRequests ' +
'ON PackageBases.ID = PackageRequests.PackageBaseID ' + 'INNER JOIN PackageBases ' +
'INNER JOIN Users ' + 'ON PackageBases.ID = PackageRequests.PackageBaseID ' +
'ON Users.ID = PackageRequests.UsersID ' + 'INNER JOIN Users ' +
'OR Users.ID = PackageBases.MaintainerUID ' + 'ON Users.ID = PackageRequests.UsersID ' +
'WHERE PackageRequests.ID = ? AND ' + 'OR Users.ID = PackageBases.MaintainerUID ' +
'Users.Suspended = 0', [reqid]) 'WHERE PackageRequests.ID = ? AND ' +
'Users.Suspended = 0', [reqid])
self._to = aurweb.config.get('options', 'aur_request_ml') self._to = aurweb.config.get('options', 'aur_request_ml')
self._cc = [row[0] for row in cur.fetchall()] self._cc = [row[0] for row in cur.fetchall()]
cur = conn.execute('SELECT PackageRequests.ClosureComment, ' + cur = conn.execute('SELECT PackageRequests.ClosureComment, ' +
...@@ -563,14 +574,15 @@ class TUVoteReminderNotification(Notification): ...@@ -563,14 +574,15 @@ class TUVoteReminderNotification(Notification):
return self._recipients return self._recipients
def get_subject(self, lang): def get_subject(self, lang):
return self._l10n.translate('TU Vote Reminder: Proposal {id}', return aurweb.l10n.translator.translate(
lang).format(id=self._vote_id) 'TU Vote Reminder: Proposal {id}',
lang).format(id=self._vote_id)
def get_body(self, lang): def get_body(self, lang):
return self._l10n.translate( return aurweb.l10n.translator.translate(
'Please remember to cast your vote on proposal {id} [1]. ' 'Please remember to cast your vote on proposal {id} [1]. '
'The voting period ends in less than 48 hours.', 'The voting period ends in less than 48 hours.',
lang).format(id=self._vote_id) lang).format(id=self._vote_id)
def get_refs(self): def get_refs(self):
return (aur_location + '/tu/?id=' + str(self._vote_id),) return (aur_location + '/tu/?id=' + str(self._vote_id),)
......
...@@ -19,6 +19,7 @@ name = YOUR_AUR_ROOT/aurweb.sqlite3 ...@@ -19,6 +19,7 @@ name = YOUR_AUR_ROOT/aurweb.sqlite3
aur_location = http://127.0.0.1:8080 aur_location = http://127.0.0.1:8080
disable_http_login = 0 disable_http_login = 0