summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore22
-rw-r--r--Mailman/Cgi/admin.py10
-rw-r--r--Mailman/Cgi/confirm.py4
-rw-r--r--Mailman/Cgi/create.py6
-rw-r--r--Mailman/Cgi/listinfo.py2
-rw-r--r--Mailman/Cgi/options.py4
-rw-r--r--Mailman/Cgi/rmlist.py2
-rw-r--r--Mailman/Cgi/roster.py2
-rw-r--r--Mailman/Cgi/subscribe.py2
-rw-r--r--Mailman/Defaults.py (renamed from Mailman/Defaults.py.in)29
-rw-r--r--Mailman/Gui/Language.py11
-rw-r--r--Mailman/HTMLFormatter.py6
-rw-r--r--Mailman/MailList.py15
-rw-r--r--Mailman/OldStyleMemberships.py2
-rw-r--r--Mailman/SafeDict.py2
-rw-r--r--Mailman/Utils.py54
-rw-r--r--Mailman/bin/make_instance.py98
-rw-r--r--Mailman/bin/newlist.py2
-rw-r--r--Mailman/bin/rmlist.py2
-rw-r--r--Mailman/bin/testall.py20
-rw-r--r--Mailman/bin/withlist.py4
-rw-r--r--Mailman/configuration.py121
-rw-r--r--Mailman/data/Elixir-0.3.0.tar.gz (renamed from misc/Elixir-0.3.0.tar.gz)bin99715 -> 99715 bytes
-rw-r--r--Mailman/data/PythonPowered.png (renamed from misc/PythonPowered.png)bin945 -> 945 bytes
-rw-r--r--Mailman/data/SQLAlchemy-0.3.3.tar.gz (renamed from misc/SQLAlchemy-0.3.3.tar.gz)bin737019 -> 737019 bytes
-rwxr-xr-xMailman/data/__init__.py0
-rw-r--r--Mailman/data/coverage.py (renamed from misc/coverage.py)0
-rw-r--r--Mailman/data/gnu-head-tiny.jpg (renamed from misc/gnu-head-tiny.jpg)bin3049 -> 3049 bytes
-rw-r--r--Mailman/data/mailman-large.jpg (renamed from misc/mailman-large.jpg)bin6150 -> 6150 bytes
-rw-r--r--Mailman/data/mailman.cfg.in (renamed from misc/mailman.cfg.sample)26
-rw-r--r--Mailman/data/mailman.in (renamed from misc/mailman.in)0
-rw-r--r--Mailman/data/mailman.jpg (renamed from misc/mailman.jpg)bin2022 -> 2022 bytes
-rw-r--r--Mailman/data/mm-icon.png (renamed from misc/mm-icon.png)bin281 -> 281 bytes
-rw-r--r--Mailman/data/munepy-1.1-py2.5.egg (renamed from misc/munepy-1.1-py2.5.egg)bin8192 -> 8192 bytes
-rw-r--r--Mailman/data/paths.py.in (renamed from misc/paths.py.in)0
-rw-r--r--Mailman/data/pysqlite-2.3.2.tar.gz (renamed from misc/pysqlite-2.3.2.tar.gz)bin79532 -> 79532 bytes
-rw-r--r--Mailman/data/setuptools-0.6c3.tar.gz (renamed from misc/setuptools-0.6c3.tar.gz)bin238544 -> 238544 bytes
-rw-r--r--Mailman/data/wsgiref-0.1.2-py2.4.egg (renamed from misc/wsgiref-0.1.2-py2.4.egg)bin39890 -> 39890 bytes
-rw-r--r--Mailman/data/zope.interface-3.3.0.1.tar.gz (renamed from misc/zope.interface-3.3.0.1.tar.gz)bin105075 -> 105075 bytes
-rw-r--r--Mailman/docs/acknowledge.txt1
-rw-r--r--Mailman/docs/digests.txt15
-rw-r--r--Mailman/docs/languages.txt94
-rw-r--r--Mailman/i18n.py9
-rw-r--r--Mailman/interfaces/languages.py65
-rw-r--r--Mailman/interfaces/manager.py57
-rw-r--r--Mailman/languages.py57
-rw-r--r--Mailman/messages/__init__.py0
-rw-r--r--Mailman/messages/ar/LC_MESSAGES/mailman.po (renamed from messages/ar/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/ca/LC_MESSAGES/mailman.po (renamed from messages/ca/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/cs/LC_MESSAGES/mailman.po (renamed from messages/cs/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/da/LC_MESSAGES/mailman.po (renamed from messages/da/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/de/LC_MESSAGES/mailman.po (renamed from messages/de/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/de/README.de (renamed from messages/de/README.de)0
-rw-r--r--Mailman/messages/docstring.files (renamed from messages/docstring.files)0
-rw-r--r--Mailman/messages/es/LC_MESSAGES/mailman.po (renamed from messages/es/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/es/README.es (renamed from messages/es/README.es)0
-rw-r--r--Mailman/messages/et/LC_MESSAGES/mailman.po (renamed from messages/et/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/eu/LC_MESSAGES/mailman.po (renamed from messages/eu/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/eu/README.eu (renamed from messages/eu/README.eu)0
-rw-r--r--Mailman/messages/fi/LC_MESSAGES/mailman.po (renamed from messages/fi/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/fi/README.fi (renamed from messages/fi/README.fi)0
-rw-r--r--Mailman/messages/fr/LC_MESSAGES/mailman.po (renamed from messages/fr/LC_MESSAGES/mailman.po)4
-rw-r--r--Mailman/messages/fr/README.fr (renamed from messages/fr/README.fr)0
-rw-r--r--Mailman/messages/hr/LC_MESSAGES/mailman.po (renamed from messages/hr/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/hu/FAQ.hu (renamed from messages/hu/FAQ.hu)0
-rw-r--r--Mailman/messages/hu/INSTALL.hu (renamed from messages/hu/INSTALL.hu)0
-rw-r--r--Mailman/messages/hu/LC_MESSAGES/mailman.po (renamed from messages/hu/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/hu/README.BSD.hu (renamed from messages/hu/README.BSD.hu)0
-rw-r--r--Mailman/messages/hu/README.CONTRIB.hu (renamed from messages/hu/README.CONTRIB.hu)0
-rw-r--r--Mailman/messages/hu/README.EXIM.hu (renamed from messages/hu/README.EXIM.hu)0
-rw-r--r--Mailman/messages/hu/README.LINUX.hu (renamed from messages/hu/README.LINUX.hu)0
-rw-r--r--Mailman/messages/hu/README.MACOSX.hu (renamed from messages/hu/README.MACOSX.hu)0
-rw-r--r--Mailman/messages/hu/README.NETSCAPE.hu (renamed from messages/hu/README.NETSCAPE.hu)0
-rw-r--r--Mailman/messages/hu/README.POSTFIX.hu (renamed from messages/hu/README.POSTFIX.hu)0
-rw-r--r--Mailman/messages/hu/README.QMAIL.hu (renamed from messages/hu/README.QMAIL.hu)0
-rw-r--r--Mailman/messages/hu/README.SENDMAIL.hu (renamed from messages/hu/README.SENDMAIL.hu)0
-rw-r--r--Mailman/messages/hu/README.USERAGENT.hu (renamed from messages/hu/README.USERAGENT.hu)0
-rw-r--r--Mailman/messages/hu/README.hu (renamed from messages/hu/README.hu)0
-rw-r--r--Mailman/messages/hu/UPGRADING.hu (renamed from messages/hu/UPGRADING.hu)0
-rw-r--r--Mailman/messages/ia/LC_MESSAGES/mailman.po (renamed from messages/ia/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/it/LC_MESSAGES/mailman.po (renamed from messages/it/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/it/README.it (renamed from messages/it/README.it)0
-rw-r--r--Mailman/messages/ja/INSTALL (renamed from messages/ja/INSTALL)0
-rw-r--r--Mailman/messages/ja/LC_MESSAGES/mailman.po (renamed from messages/ja/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/ja/README (renamed from messages/ja/README)0
-rw-r--r--Mailman/messages/ja/README.ja (renamed from messages/ja/README.ja)0
-rw-r--r--Mailman/messages/ja/UPGRADING (renamed from messages/ja/UPGRADING)0
-rw-r--r--Mailman/messages/ja/doc/Defaults.py.in (renamed from messages/ja/doc/Defaults.py.in)0
-rw-r--r--Mailman/messages/ja/doc/mailman-install.tex (renamed from messages/ja/doc/mailman-install.tex)0
-rw-r--r--Mailman/messages/ja/doc/mailman-member.tex (renamed from messages/ja/doc/mailman-member.tex)0
-rw-r--r--Mailman/messages/ko/LC_MESSAGES/mailman.po (renamed from messages/ko/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/ko/README.ko (renamed from messages/ko/README.ko)0
-rw-r--r--Mailman/messages/lt/LC_MESSAGES/mailman.po (renamed from messages/lt/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/mailman.pot (renamed from messages/mailman.pot)0
-rw-r--r--Mailman/messages/marked.files (renamed from messages/marked.files)0
-rw-r--r--Mailman/messages/nl/LC_MESSAGES/mailman.po (renamed from messages/nl/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/no/LC_MESSAGES/mailman.po (renamed from messages/no/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/pl/LC_MESSAGES/mailman.po (renamed from messages/pl/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/pl/README.pl (renamed from messages/pl/README.pl)0
-rw-r--r--Mailman/messages/pt/LC_MESSAGES/mailman.po (renamed from messages/pt/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/pt_BR/LC_MESSAGES/mailman.po (renamed from messages/pt_BR/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/ro/LC_MESSAGES/mailman.po (renamed from messages/ro/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/ru/LC_MESSAGES/mailman.po (renamed from messages/ru/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/ru/README.ru (renamed from messages/ru/README.ru)0
-rw-r--r--Mailman/messages/sl/LC_MESSAGES/mailman.po (renamed from messages/sl/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/sr/LC_MESSAGES/mailman.po (renamed from messages/sr/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/sr/readme.sr (renamed from messages/sr/readme.sr)0
-rw-r--r--Mailman/messages/sv/LC_MESSAGES/mailman.po (renamed from messages/sv/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/sv/README.sv (renamed from messages/sv/README.sv)0
-rw-r--r--Mailman/messages/tr/LC_MESSAGES/mailman.po (renamed from messages/tr/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/uk/LC_MESSAGES/mailman.po (renamed from messages/uk/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/vi/LC_MESSAGES/mailman.po (renamed from messages/vi/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/zh_CN/LC_MESSAGES/mailman.po (renamed from messages/zh_CN/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/messages/zh_TW/LC_MESSAGES/mailman.po (renamed from messages/zh_TW/LC_MESSAGES/mailman.po)0
-rw-r--r--Mailman/templates/__init__.py0
-rw-r--r--Mailman/templates/en/adminaddrchgack.txt (renamed from templates/en/adminaddrchgack.txt)0
-rw-r--r--Mailman/templates/en/admindbdetails.html (renamed from templates/en/admindbdetails.html)0
-rw-r--r--Mailman/templates/en/admindbpreamble.html (renamed from templates/en/admindbpreamble.html)0
-rw-r--r--Mailman/templates/en/admindbsummary.html (renamed from templates/en/admindbsummary.html)0
-rw-r--r--Mailman/templates/en/adminsubscribeack.txt (renamed from templates/en/adminsubscribeack.txt)0
-rw-r--r--Mailman/templates/en/adminunsubscribeack.txt (renamed from templates/en/adminunsubscribeack.txt)0
-rw-r--r--Mailman/templates/en/admlogin.html (renamed from templates/en/admlogin.html)0
-rw-r--r--Mailman/templates/en/approve.txt (renamed from templates/en/approve.txt)0
-rw-r--r--Mailman/templates/en/archidxentry.html (renamed from templates/en/archidxentry.html)0
-rw-r--r--Mailman/templates/en/archidxfoot.html (renamed from templates/en/archidxfoot.html)0
-rw-r--r--Mailman/templates/en/archidxhead.html (renamed from templates/en/archidxhead.html)0
-rw-r--r--Mailman/templates/en/archlistend.html (renamed from templates/en/archlistend.html)0
-rw-r--r--Mailman/templates/en/archliststart.html (renamed from templates/en/archliststart.html)0
-rw-r--r--Mailman/templates/en/archtoc.html (renamed from templates/en/archtoc.html)0
-rw-r--r--Mailman/templates/en/archtocentry.html (renamed from templates/en/archtocentry.html)0
-rw-r--r--Mailman/templates/en/archtocnombox.html (renamed from templates/en/archtocnombox.html)0
-rw-r--r--Mailman/templates/en/article.html (renamed from templates/en/article.html)0
-rw-r--r--Mailman/templates/en/bounce.txt (renamed from templates/en/bounce.txt)0
-rw-r--r--Mailman/templates/en/checkdbs.txt (renamed from templates/en/checkdbs.txt)0
-rw-r--r--Mailman/templates/en/convert.txt (renamed from templates/en/convert.txt)0
-rw-r--r--Mailman/templates/en/cronpass.txt (renamed from templates/en/cronpass.txt)0
-rw-r--r--Mailman/templates/en/disabled.txt (renamed from templates/en/disabled.txt)0
-rw-r--r--Mailman/templates/en/emptyarchive.html (renamed from templates/en/emptyarchive.html)0
-rw-r--r--Mailman/templates/en/headfoot.html (renamed from templates/en/headfoot.html)0
-rw-r--r--Mailman/templates/en/help.txt (renamed from templates/en/help.txt)0
-rw-r--r--Mailman/templates/en/invite.txt (renamed from templates/en/invite.txt)0
-rw-r--r--Mailman/templates/en/listinfo.html (renamed from templates/en/listinfo.html)0
-rw-r--r--Mailman/templates/en/masthead.txt (renamed from templates/en/masthead.txt)0
-rw-r--r--Mailman/templates/en/newlist.txt (renamed from templates/en/newlist.txt)0
-rw-r--r--Mailman/templates/en/nomoretoday.txt (renamed from templates/en/nomoretoday.txt)0
-rw-r--r--Mailman/templates/en/options.html (renamed from templates/en/options.html)0
-rw-r--r--Mailman/templates/en/postack.txt (renamed from templates/en/postack.txt)0
-rw-r--r--Mailman/templates/en/postauth.txt (renamed from templates/en/postauth.txt)0
-rw-r--r--Mailman/templates/en/postheld.txt (renamed from templates/en/postheld.txt)0
-rw-r--r--Mailman/templates/en/private.html (renamed from templates/en/private.html)0
-rw-r--r--Mailman/templates/en/probe.txt (renamed from templates/en/probe.txt)0
-rw-r--r--Mailman/templates/en/refuse.txt (renamed from templates/en/refuse.txt)0
-rw-r--r--Mailman/templates/en/roster.html (renamed from templates/en/roster.html)0
-rw-r--r--Mailman/templates/en/subauth.txt (renamed from templates/en/subauth.txt)0
-rw-r--r--Mailman/templates/en/subscribe.html (renamed from templates/en/subscribe.html)0
-rw-r--r--Mailman/templates/en/subscribeack.txt (renamed from templates/en/subscribeack.txt)0
-rw-r--r--Mailman/templates/en/unsub.txt (renamed from templates/en/unsub.txt)0
-rw-r--r--Mailman/templates/en/unsubauth.txt (renamed from templates/en/unsubauth.txt)0
-rw-r--r--Mailman/templates/en/userpass.txt (renamed from templates/en/userpass.txt)0
-rw-r--r--Mailman/templates/en/verify.txt (renamed from templates/en/verify.txt)0
-rwxr-xr-xMailman/testing/bounces/__init__.py0
-rw-r--r--Mailman/testing/test_bounces.py22
-rw-r--r--Mailman/testing/testing.cfg.in2
-rw-r--r--setup.py17
164 files changed, 530 insertions, 259 deletions
diff --git a/.bzrignore b/.bzrignore
index e007ba8c3..9c3023bfa 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -1,28 +1,6 @@
*.mo
.bzrignore
-Mailman/Defaults.py
-Mailman/mm_cfg.py.dist
-Makefile
build/
-config.log
-config.status
cron/crontab.in
mailman.egg-info
-misc/Elixir-0.3.0/
-misc/SQLAlchemy-0.3.3/
misc/mailman
-misc/paths.py
-misc/setuptools-0.6c3/
-misc/zope.interface-3.3.0.1/
-src/admin
-src/admindb
-src/confirm
-src/create
-src/edithtml
-src/listinfo
-src/mailman
-src/options
-src/private
-src/rmlist
-src/roster
-src/subscribe
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py
index cb001317e..fb0ab1a56 100644
--- a/Mailman/Cgi/admin.py
+++ b/Mailman/Cgi/admin.py
@@ -625,7 +625,8 @@ def get_item_gui_value(mlist, category, kind, varname, params, extra):
values, legend, selected = params
else:
codes = mlist.language_codes
- legend = [Utils.GetLanguageDescr(code) for code in codes]
+ legend = [config.languages.get_language_data(code)[0]
+ for code in codes]
selected = codes.index(mlist.preferred_language)
return SelectOptions(varname, values, legend, selected)
elif kind == config.Topics:
@@ -986,7 +987,8 @@ def membership_options(mlist, subcat, cgidata, doc, form):
# User's preferred language
langpref = mlist.getMemberLanguage(addr)
langs = mlist.language_codes
- langdescs = [_(Utils.GetLanguageDescr(lang)) for lang in langs]
+ langdescs = [_(config.languges.get_language_data(code)[0])
+ for code in langs]
try:
selected = langs.index(langpref)
except ValueError:
@@ -1402,7 +1404,9 @@ def change_options(mlist, category, subcat, cgidata, doc):
newlang = cgidata.getvalue(user+'_language')
oldlang = mlist.getMemberLanguage(user)
- if Utils.IsLanguage(newlang) and newlang <> oldlang:
+ if (newlang not in config.languages.enabled_codes
+ and newlang <> oldlang):
+ # Then
mlist.setMemberLanguage(user, newlang)
moderate = not not cgidata.getvalue(user+'_mod')
diff --git a/Mailman/Cgi/confirm.py b/Mailman/Cgi/confirm.py
index d2bc3b0aa..aee6df46b 100644
--- a/Mailman/Cgi/confirm.py
+++ b/Mailman/Cgi/confirm.py
@@ -286,7 +286,7 @@ def subscription_prompt(mlist, doc, cookie, userdesc):
RadioButtonArray('digests', (_('No'), _('Yes')),
checked=digest, values=(0, 1))])
langs = mlist.language_codes
- values = [_(Utils.GetLanguageDescr(l)) for l in langs]
+ values = [_(config.languages.get_language_data(code)[0]) for code in langs]
try:
selected = langs.index(lang)
except ValueError:
@@ -332,7 +332,7 @@ def subscription_confirm(mlist, doc, cookie, cgidata):
# Some pending values may be overridden in the form. email of
# course is hardcoded. ;)
lang = cgidata.getvalue('language')
- if not Utils.IsLanguage(lang):
+ if lang not in config.languages.enabled_codes:
lang = mlist.preferred_language
i18n.set_language(lang)
doc.set_language(lang)
diff --git a/Mailman/Cgi/create.py b/Mailman/Cgi/create.py
index 0ca51bd65..978f1b9ac 100644
--- a/Mailman/Cgi/create.py
+++ b/Mailman/Cgi/create.py
@@ -361,13 +361,15 @@ def request_creation(doc, cgidata=dummy, errmsg=None):
# invocations.
checked = [0] * len(langs)
checked[langi] = 1
- deflang = _(Utils.GetLanguageDescr(config.DEFAULT_SERVER_LANGUAGE))
+ deflang = _(config.languages.get_language_data(
+ config.DEFAULT_SERVER_LANGUAGE)[0])
ftable.AddRow([Label(_(
"""Initial list of supported languages. <p>Note that if you do not
select at least one initial language, the list will use the server
default language of $deflang""")),
CheckBoxArray('langs',
- [_(Utils.GetLanguageDescr(L)) for L in langs],
+ [_(config.languges.get_language_data(code)[0])
+ for code in langs],
checked=checked,
values=langs)])
ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
diff --git a/Mailman/Cgi/listinfo.py b/Mailman/Cgi/listinfo.py
index 72ef6bfa9..9861e2496 100644
--- a/Mailman/Cgi/listinfo.py
+++ b/Mailman/Cgi/listinfo.py
@@ -57,7 +57,7 @@ def main():
# See if the user want to see this page in other language
cgidata = cgi.FieldStorage()
language = cgidata.getvalue('language')
- if not Utils.IsLanguage(language):
+ if language not in config.languages.enabled_codes:
language = mlist.preferred_language
i18n.set_language(language)
list_listinfo(mlist, language)
diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py
index e7e7264a1..faa813489 100644
--- a/Mailman/Cgi/options.py
+++ b/Mailman/Cgi/options.py
@@ -88,7 +88,7 @@ def main():
# preference to view the page in, so we should honor that here. If that's
# not available, use the list's default language.
language = cgidata.getvalue('language')
- if not Utils.IsLanguage(language):
+ if language not in config.languages.enabled_codes:
language = mlist.preferred_language
i18n.set_language(language)
doc.set_language(language)
@@ -138,7 +138,7 @@ def main():
# user's stored preferred language, overridden by any form settings for
# their new language preference.
userlang = cgidata.getvalue('language')
- if not Utils.IsLanguage(userlang):
+ if userlang not in config.languages.enabled_codes:
userlang = mlist.getMemberLanguage(user)
doc.set_language(userlang)
i18n.set_language(userlang)
diff --git a/Mailman/Cgi/rmlist.py b/Mailman/Cgi/rmlist.py
index 080d3b492..534d06131 100644
--- a/Mailman/Cgi/rmlist.py
+++ b/Mailman/Cgi/rmlist.py
@@ -141,7 +141,7 @@ def process_request(doc, cgidata, mlist):
problems = 0
listname = mlist.internal_name()
for dirtmpl in REMOVABLES:
- dir = os.path.join(config.VAR_PREFIX, dirtmpl % listname)
+ dir = os.path.join(config.VAR_DIR, dirtmpl % listname)
if os.path.islink(dir):
try:
os.unlink(dir)
diff --git a/Mailman/Cgi/roster.py b/Mailman/Cgi/roster.py
index fdeb83d31..4eb3174cd 100644
--- a/Mailman/Cgi/roster.py
+++ b/Mailman/Cgi/roster.py
@@ -64,7 +64,7 @@ def main():
# messages in form should go in selected language (if any...)
lang = cgidata.getvalue('language')
- if not Utils.IsLanguage(lang):
+ if lang not in config.languages.enabled_codes:
lang = mlist.preferred_language
i18n.set_language(lang)
diff --git a/Mailman/Cgi/subscribe.py b/Mailman/Cgi/subscribe.py
index 3d66cac42..512cd3195 100644
--- a/Mailman/Cgi/subscribe.py
+++ b/Mailman/Cgi/subscribe.py
@@ -70,7 +70,7 @@ def main():
# for the results. If not, use the list's preferred language.
cgidata = cgi.FieldStorage()
language = cgidata.getvalue('language')
- if not Utils.IsLanguage(language):
+ if language not in config.languages.enabled_codes:
language = mlist.preferred_language
i18n.set_language(language)
doc.set_language(language)
diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py
index 0bc552d26..3c06b709a 100644
--- a/Mailman/Defaults.py.in
+++ b/Mailman/Defaults.py
@@ -101,6 +101,9 @@ HTML_TO_PLAIN_TEXT_COMMAND = '/usr/bin/lynx -dump %(filename)s'
# available schemes.
PASSWORD_SCHEME = 'ssha'
+# Default run-time directory.
+DEFAULT_VAR_DIRECTORY = '/var/mailman'
+
#####
@@ -200,6 +203,9 @@ WEB_ALINK_COLOR = '' # If true, forces ALINK=
WEB_VLINK_COLOR = '' # If true, forces VLINK=
WEB_HIGHLIGHT_COLOR = '#dddddd' # If true, alternating rows
# in listinfo & admin display
+# CGI file extension.
+CGIEXT = ''
+
#####
@@ -804,8 +810,8 @@ MAX_RESTARTS = 10
#####
# The default language for this server. Whenever we can't figure out the list
-# context or user context, we'll fall back to using this language. See
-# LC_DESCRIPTIONS below for legal values.
+# context or user context, we'll fall back to using this language. This code
+# must be in the list of available language codes.
DEFAULT_SERVER_LANGUAGE = 'en'
# When allowing only members to post to a mailing list, how is the sender of
@@ -1240,14 +1246,6 @@ PENDINGDB_LOCK_DEBUGGING = Off
# any of them in your mailman.cfg file!
#####
-# This is the top-level run-time data directory. All other runtime data by
-# default lives under this directory.
-RUNTIME_DIR = '$runtime_dir'
-
-# Group id that group-owns the Mailman installation
-MAILMAN_USER = '$user_name'
-MAILMAN_GROUP = '$group_name'
-
# Enumeration for Mailman cgi widget types
Toggle = 1
Radio = 2
@@ -1365,8 +1363,7 @@ from Version import *
def _(s):
return s
-LANGUAGES = 'en ' + '@LANGUAGES@'
-LANGUAGE_DICT = {
+_DEFAULT_LANGUAGE_DATA = {
'ar': (_('Arabic'), 'utf-8'),
'ca': (_('Catalan'), 'iso-8859-1'),
'cs': (_('Czech'), 'iso-8859-2'),
@@ -1402,13 +1399,5 @@ LANGUAGE_DICT = {
'zh_TW': (_('Chinese (Taiwan)'), 'utf-8'),
}
-LC_DESCRIPTIONS = {}
-
-def add_language(code, description, charset):
- LC_DESCRIPTIONS[code] = (description, charset)
-
-for lang in LANGUAGES.split():
- if lang in LANGUAGE_DICT.keys():
- add_language(lang, LANGUAGE_DICT[lang][0], LANGUAGE_DICT[lang][1])
del _
diff --git a/Mailman/Gui/Language.py b/Mailman/Gui/Language.py
index 32029577d..bcd0ba93b 100644
--- a/Mailman/Gui/Language.py
+++ b/Mailman/Gui/Language.py
@@ -37,7 +37,7 @@ class Language(GUIBase):
return None
# Set things up for the language choices
langs = mlist.language_codes
- langnames = [_(Utils.GetLanguageDescr(L)) for L in langs]
+ langnames = [_(description) for description in config.enabled_names]
try:
langi = langs.index(mlist.preferred_language)
except ValueError:
@@ -53,12 +53,11 @@ class Language(GUIBase):
except LookupError:
return 0
- all = [key for key in config.LC_DESCRIPTIONS.keys()
- if checkcodec(Utils.GetCharSet(key))]
- all.sort()
+ all = sorted(code for code in config.languages.enabled_codes
+ if checkcodec(Utils.GetCharSet(code)))
checked = [L in langs for L in all]
- allnames = [_(Utils.GetLanguageDescr(L)) for L in all]
-
+ allnames = [_(config.languages.get_language_data(code)[0])
+ for code in all]
return [
_('Natural language (internationalization) options.'),
diff --git a/Mailman/HTMLFormatter.py b/Mailman/HTMLFormatter.py
index ab4c03a32..a5e69dbcd 100644
--- a/Mailman/HTMLFormatter.py
+++ b/Mailman/HTMLFormatter.py
@@ -377,7 +377,8 @@ class HTMLFormatter:
# If only one language is enabled for this mailing list, omit the
# language choice buttons.
if len(self.language_codes) == 1:
- listlangs = _(Utils.GetLanguageDescr(self.preferred_language))
+ listlangs = _(config.languages.get_language_data(
+ self.preferred_language))
else:
listlangs = self.GetLangSelectBox(lang).Format()
d = {
@@ -424,7 +425,8 @@ class HTMLFormatter:
lang = self.preferred_language
# Figure out the available languages
values = self.language_codes
- legend = [Utils.GetLanguageDescr(code) for code in values]
+ legend = [config.languages.get_language_data(code)[0]
+ for code in values]
try:
selected = values.index(lang)
except ValueError:
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 9d784de51..76538e6c2 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -1410,8 +1410,10 @@ bad regexp in bounce_matching_header line: %s
# default server language. The effect would be that the default
# server language would never get added to the list's list of
# languages.
- self.available_languages = [Language(code) for code in language_codes
- if code in config.LC_DESCRIPTIONS]
+ requested_codes = set(language_codes)
+ enabled_codes = set(config.languages.enabled_codes)
+ self.available_languages = [
+ Language(code) for code in requested_codes & enabled_codes]
def add_language(self, language_code):
self.available_languages.append(Language(language_code))
@@ -1419,12 +1421,13 @@ bad regexp in bounce_matching_header line: %s
@property
def language_codes(self):
# Callers of this method expect a list of language codes
- codes = [lang.code for lang in self.available_languages
- if lang.code in config.LC_DESCRIPTIONS]
+ available_codes = set(self.available_languages)
+ enabled_codes = set(config.languages.enabled_codes)
+ codes = available_codes & enabled_codes
# If we don't add this, and the site admin has never added any
# language support to the list, then the general admin page may have a
# blank field where the list owner is supposed to chose the list's
# preferred language.
if config.DEFAULT_SERVER_LANGUAGE not in codes:
- codes.append(config.DEFAULT_SERVER_LANGUAGE)
- return codes
+ codes.add(config.DEFAULT_SERVER_LANGUAGE)
+ return list(codes)
diff --git a/Mailman/OldStyleMemberships.py b/Mailman/OldStyleMemberships.py
index 2a72b8928..594096d9e 100644
--- a/Mailman/OldStyleMemberships.py
+++ b/Mailman/OldStyleMemberships.py
@@ -29,8 +29,8 @@ import time
from Mailman import Errors
from Mailman import MemberAdaptor
from Mailman import Utils
-from Mailman import mm_cfg
from Mailman import passwords
+from Mailman.configuration import config
ISREGULAR = 1
ISDIGEST = 2
diff --git a/Mailman/SafeDict.py b/Mailman/SafeDict.py
index 876ab88e4..52f78c6a2 100644
--- a/Mailman/SafeDict.py
+++ b/Mailman/SafeDict.py
@@ -35,7 +35,7 @@ class SafeDict(dict):
if charset:
self.cset = charset
elif lang:
- self.cset = config.LC_DESCRIPTIONS[lang][1]
+ self.cset = config.languages.get_language_data(lang)[1]
else:
self.cset = 'us-ascii'
diff --git a/Mailman/Utils.py b/Mailman/Utils.py
index 064de570e..4121d3d75 100644
--- a/Mailman/Utils.py
+++ b/Mailman/Utils.py
@@ -39,6 +39,7 @@ import email.Iterators
from email.Errors import HeaderParseError
from string import ascii_letters, digits, whitespace
+import Mailman.templates
from Mailman import Errors
from Mailman import passwords
from Mailman.SafeDict import SafeDict
@@ -51,6 +52,7 @@ EMPTYSTRING = ''
IDENTCHARS = ascii_letters + digits + '_'
NL = '\n'
UEMPTYSTRING = u''
+TEMPLATE_DIR = os.path.dirname(Mailman.templates.__file__)
# Search for $(identifier)s strings, except that the trailing s is optional,
# since that's a common mistake
@@ -450,7 +452,9 @@ def findtext(templatefile, dict=None, raw=False, lang=None, mlist=None):
# language. After that, the server default language is used for
# <language>. If that still doesn't yield a template, then the standard
# distribution's English language template is used as an ultimate
- # fallback. If that's missing you've got big problems. ;)
+ # fallback, and when lang is not 'en', the resulting template is passed
+ # through the translation service. If this template is missing you've got
+ # big problems. ;)
#
# A word on backwards compatibility: Mailman versions prior to 2.1 stored
# templates in templates/*.{html,txt} and lists/<listname>/*.{html,txt}.
@@ -466,19 +470,19 @@ def findtext(templatefile, dict=None, raw=False, lang=None, mlist=None):
# item returned.
#
# Calculate the languages to scan
- languages = []
+ languages = set()
if lang is not None:
- languages.append(lang)
+ languages.add(lang)
if mlist is not None:
- languages.append(mlist.preferred_language)
- languages.append(config.DEFAULT_SERVER_LANGUAGE)
+ languages.add(mlist.preferred_language)
+ languages.add(config.DEFAULT_SERVER_LANGUAGE)
# Calculate the locations to scan
searchdirs = []
if mlist is not None:
searchdirs.append(mlist.full_path)
- searchdirs.append(os.path.join(config.TEMPLATE_DIR, mlist.host_name))
- searchdirs.append(os.path.join(config.TEMPLATE_DIR, 'site'))
- searchdirs.append(config.TEMPLATE_DIR)
+ searchdirs.append(os.path.join(TEMPLATE_DIR, mlist.host_name))
+ searchdirs.append(os.path.join(TEMPLATE_DIR, 'site'))
+ searchdirs.append(TEMPLATE_DIR)
# Start scanning
fp = None
try:
@@ -498,24 +502,31 @@ def findtext(templatefile, dict=None, raw=False, lang=None, mlist=None):
# Try one last time with the distro English template, which, unless
# you've got a really broken installation, must be there.
try:
- filename = os.path.join(config.TEMPLATE_DIR, 'en', templatefile)
+ filename = os.path.join(TEMPLATE_DIR, 'en', templatefile)
fp = open(filename)
except IOError, e:
- if e.errno <> errno.ENOENT: raise
+ if e.errno <> errno.ENOENT:
+ raise
# We never found the template. BAD!
raise IOError(errno.ENOENT, 'No template file found', templatefile)
- template = fp.read()
- fp.close()
- template = unicode(template, GetCharSet(lang), 'replace')
+ else:
+ from Mailman.i18n import get_translation
+ # XXX BROKEN HACK
+ data = fp.read()[:-1]
+ template = get_translation().ugettext(data)
+ fp.close()
+ else:
+ template = fp.read()
+ fp.close()
+ template = unicode(template, GetCharSet(lang), 'replace')
text = template
if dict is not None:
try:
sdict = SafeDict(dict, lang=lang)
text = sdict.interpolate(template)
- except (TypeError, ValueError), e:
+ except (TypeError, ValueError):
# The template is really screwed up
- log.error('broken template: %s\n%s', filename, e)
- pass
+ log.exception('broken template: %s', filename)
if raw:
return text, filename
return wrap(text), filename
@@ -646,15 +657,10 @@ def makedirs(path, mode=02775):
-def GetLanguageDescr(lang):
- return config.LC_DESCRIPTIONS[lang][0]
-
-
+# XXX Replace this with direct calls. For now, existing uses of GetCharSet()
+# are too numerous to change.
def GetCharSet(lang):
- return config.LC_DESCRIPTIONS[lang][1]
-
-def IsLanguage(lang):
- return config.LC_DESCRIPTIONS.has_key(lang)
+ return config.languages.get_language_data(lang)[1]
diff --git a/Mailman/bin/make_instance.py b/Mailman/bin/make_instance.py
index 435b0863a..14eed24ce 100644
--- a/Mailman/bin/make_instance.py
+++ b/Mailman/bin/make_instance.py
@@ -21,23 +21,21 @@ import os
import grp
import pwd
import sys
+import errno
+import shutil
import optparse
import setuptools
from string import Template
-import Mailman
-from Mailman.Version import MAILMAN_VERSION
+import Mailman.data
-# Until an instance is actually created, this module won't be importable
-# because the Defaults.py module won't have been created yet.
-try:
- from Mailman.i18n import _
-except ImportError:
- def _(s): return s
+from Mailman import Defaults
+from Mailman.Version import MAILMAN_VERSION
+from Mailman.i18n import _
__i18n_templates__ = True
SPACE = ' '
-DEFAULT_RUNTIME_DIR = '/var/mailman'
+DATA_DIR = os.path.dirname(Mailman.data.__file__)
@@ -49,11 +47,17 @@ def parseargs():
Create a Mailman instance by generating all the necessary basic configuration
support and intervening directories.
"""))
- parser.add_option('-r', '--runtime-dir',
- type='string', default=DEFAULT_RUNTIME_DIR, help=_("""\
+ parser.add_option('-d', '--var-dir',
+ type='string', default=Defaults.DEFAULT_VAR_DIRECTORY,
+ help=_("""\
The top-level runtime data directory. All supporting runtime data will be
placed in subdirectories of this directory. It will be created if necessary,
although this might require superuser privileges."""))
+ parser.add_option('-f', '--force',
+ default=False, action='store_true', help=_("""\
+Force overwriting of mailman.cfg file with new values. Ordinarily, Mailman
+will never overwrite this file because it would cause you to lose your
+configuration data."""))
parser.add_option('-p', '--permchecks',
default=False, action='store_true', help=_("""\
Perform permission checks on the runtime directory."""))
@@ -65,9 +69,13 @@ current user is used."""))
type='string', default=None, help=_("""\
The group id or name to use for the runtime environment. If not specified, the
current group is used."""))
- parser.add_option('-l', '--language',
- default=[], type='string', action='append', help=_("""\
-Enable the given language. Use 'all' to enable all supported languages."""))
+ parser.add_option('-l', '--languages',
+ default=Defaults.DEFAULT_SERVER_LANGUAGE, type='string',
+ help=_("""\
+Space separated list of language codes to enable. Use -L to print all
+available language codes and the name of the associated native language.
+Default is to enable just English. Use the special code 'all' to enable all
+available languages."""))
opts, args = parser.parse_args()
if args:
unexpected = SPACE.join(args)
@@ -76,13 +84,9 @@ Enable the given language. Use 'all' to enable all supported languages."""))
-def instantiate(user=None, group=None, runtime_dir=None):
- # Create the Defaults.py file using substitutions.
- in_file_path = os.path.join(os.path.dirname(Mailman.__file__),
- 'Defaults.py.in')
- out_file_path = os.path.splitext(in_file_path)[0]
- with open(in_file_path) as fp:
- raw = Template(fp.read())
+def instantiate(var_dir, user, group, languages, force):
+ # XXX This needs to be converted to use package resources.
+ etc_dir = os.path.join(var_dir, 'etc')
# Figure out which user name and group name to use.
if user is None:
uid = os.getuid()
@@ -112,20 +116,56 @@ def instantiate(user=None, group=None, runtime_dir=None):
group_name = grp.getgrgid(gid).gr_name
except KeyError:
parser.print_error(_('Unknown group: $group'))
- # Process the .in file and write it to Defaults.py.
- processed = raw.safe_substitute(runtime_dir=runtime_dir,
- user_name=user_name,
- group_name=group_name)
- with open(out_file_path, 'w') as fp:
- fp.write(processed)
+ # Make the runtime dir if it doesn't yet exist.
+ try:
+ omask = os.umask(0)
+ try:
+ os.makedirs(etc_dir, 02775)
+ finally:
+ os.umask(omask)
+ except OSError, e:
+ # Ignore the exceptions if the directory already exists
+ if e.errno <> errno.EEXIST:
+ raise
+ os.chown(etc_dir, uid, gid)
+ # Create an etc/mailman.cfg file which contains just a few configuration
+ # variables about the run-time environment that can't be calculated.
+ # Don't overwrite mailman.cfg unless the -f flag was given.
+ in_file_path = os.path.join(DATA_DIR, 'mailman.cfg.in')
+ out_file_path = os.path.join(etc_dir, 'mailman.cfg')
+ if os.path.exists(out_file_path) and not force:
+ # The logging subsystem isn't up yet, so just print this to stderr.
+ print >> sys.stderr, 'File exists:', out_file_path
+ print >> sys.stderr, 'Use --force to override.'
+ else:
+ with open(in_file_path) as fp:
+ raw = Template(fp.read())
+ processed = raw.safe_substitute(var_dir=var_dir,
+ user_id=uid,
+ user_name=user_name,
+ group_name=group_name,
+ group_id=gid,
+ languages=SPACE.join(languages),
+ )
+ with open(out_file_path, 'w') as fp:
+ fp.write(processed)
# XXX Do --permchecks
- # XXX Do --language
def main():
parser, opts, args = parseargs()
- instantiate(opts.user, opts.group, opts.runtime_dir)
+ available_languages = set(Defaults.LANGUAGE_DICT)
+ enable_languages = set(opts.languages.split())
+ if 'all' in enable_languages:
+ languages = available_languages
+ else:
+ unknown_language = enable_languages - available_languages
+ if unknown_language:
+ print >> sys.stderr, 'Ignoring unknown language codes:', \
+ SPACE.join(unknown_language)
+ languages = available_languages & enable_languages
+ instantiate(opts.var_dir, opts.user, opts.group, languages, opts.force)
diff --git a/Mailman/bin/newlist.py b/Mailman/bin/newlist.py
index 5ac2dc5a6..1bbe818ff 100644
--- a/Mailman/bin/newlist.py
+++ b/Mailman/bin/newlist.py
@@ -104,7 +104,7 @@ def main():
if opts.language is None:
opts.language = config.DEFAULT_SERVER_LANGUAGE
# Is the language known?
- if opts.language not in config.LC_DESCRIPTIONS:
+ if opts.language not in config.languages.enabled_codes:
parser.print_help()
print >> sys.stderr, _('Unknown language: $opts.language')
sys.exit(1)
diff --git a/Mailman/bin/rmlist.py b/Mailman/bin/rmlist.py
index 26dbd1df7..fcc47b3f0 100644
--- a/Mailman/bin/rmlist.py
+++ b/Mailman/bin/rmlist.py
@@ -83,7 +83,7 @@ def delete_list(listname, mlist=None, archives=True, quiet=False):
])
for dirtmpl, msg in removeables:
- path = os.path.join(config.VAR_PREFIX, dirtmpl)
+ path = os.path.join(config.VAR_DIR, dirtmpl)
remove_it(listname, path, msg, quiet)
diff --git a/Mailman/bin/testall.py b/Mailman/bin/testall.py
index 005808cfe..a5c9d02e6 100644
--- a/Mailman/bin/testall.py
+++ b/Mailman/bin/testall.py
@@ -167,7 +167,7 @@ def main():
# Set up the testing configuration file both for this process, and for all
# sub-processes testing will spawn (e.g. the qrunners).
#
- # Calculate a temporary VAR_PREFIX directory so that run-time artifacts of
+ # Calculate a temporary VAR_DIR directory so that run-time artifacts of
# the tests won't tread on the installation's data. This also makes it
# easier to clean up after the tests are done, and insures isolation of
# test suite runs.
@@ -177,14 +177,24 @@ def main():
os.close(fd)
shutil.copyfile(cfg_in, cfg_out)
- var_prefix = tempfile.mkdtemp()
+ var_dir = tempfile.mkdtemp()
if opts.verbosity > 2:
- print 'VAR_PREFIX :', var_prefix
+ print 'VAR_DIR :', var_dir
print 'config file:', cfg_out
+ user_id = os.getuid()
+ user_name = pwd.getpwuid(user_id).pw_name
+ group_id = os.getgid()
+ group_name = grp.getgrgid(group_id).gr_name
+
try:
with open(cfg_out, 'a') as fp:
- print >> fp, 'VAR_PREFIX = "%s"' % var_prefix
+ print >> fp, 'VAR_DIR = "%s"' % var_dir
+ print >> fp, 'MAILMAN_USER = "%s"' % user_name
+ print >> fp, 'MAILMAN_UID = %d' % user_id
+ print >> fp, 'MAILMAN_GROUP = "%s"' % group_name
+ print >> fp, 'MAILMAN_GID = %d' % group_id
+ print >> fp, "LANGUAGES = 'en'"
initialize_1(cfg_out, propagate_logs=opts.stderr)
mailman_uid = pwd.getpwnam(config.MAILMAN_USER).pw_uid
@@ -216,7 +226,7 @@ def main():
results = runner.run(suite(args))
finally:
os.remove(cfg_out)
- shutil.rmtree(var_prefix)
+ shutil.rmtree(var_dir)
# Turn off coverage and print a report
if opts.coverage:
diff --git a/Mailman/bin/withlist.py b/Mailman/bin/withlist.py
index ac1ab0aac..5e4655100 100644
--- a/Mailman/bin/withlist.py
+++ b/Mailman/bin/withlist.py
@@ -190,10 +190,6 @@ def main():
VERBOSE = not opts.quiet
LOCK = opts.lock
- # Append our bin directory to sys.path so that any withlist scripts living
- # their can be simply imported.
- sys.path.append(config.BIN_DIR)
-
# The default for interact is true unless -r was given
if opts.interactive is None:
if not opts.run:
diff --git a/Mailman/configuration.py b/Mailman/configuration.py
index a8ecdae84..dbb057a5f 100644
--- a/Mailman/configuration.py
+++ b/Mailman/configuration.py
@@ -18,10 +18,12 @@
"""Configuration file loading and management."""
import os
+import sys
import errno
from Mailman import Defaults
from Mailman import Errors
+from Mailman.languages import LanguageManager
_missing = object()
@@ -46,63 +48,80 @@ class Configuration(object):
def load(self, filename=None):
join = os.path.join
- # Load the configuration from the named file, or if not given, search
- # in the runtime data directory for an etc/mailman.cfg file. If that
- # file is missing, use Mailman/mm_cfg.py for backward compatibility.
- #
- # Whatever you find, create a namespace and execfile that file in it.
- # The values in that namespace are exposed as attributes on this
- # Configuration instance.
- original_filename = filename
- if filename is None:
- filename = join(Defaults.RUNTIME_DIR, 'etc', 'mailman.cfg')
# Set up the execfile namespace
ns = Defaults.__dict__.copy()
# Prune a few things, add a few things
del ns['__file__']
del ns['__name__']
del ns['__doc__']
- ns['add_domain'] = self.add_domain
+ ns['add_domain'] = self.add_domain
ns['add_qrunner'] = self.add_qrunner
ns['del_qrunner'] = self.del_qrunner
+ self._languages = LanguageManager()
+ ns['add_language'] = self._languages.add_language
+ ns['enable_language'] = self._languages.enable_language
+ # Add all known languages, but don't enable them.
+ for code, (desc, charset) in Defaults._DEFAULT_LANGUAGE_DATA.items():
+ self._languages.add_language(code, desc, charset, False)
# Set up the default list of qrunners so that the mailman.cfg file may
# add or delete them.
for name in DEFAULT_QRUNNERS:
self.add_qrunner(name)
- # Attempt our first choice
- path = os.path.abspath(os.path.expanduser(filename))
- print 'path:', path
- try:
- execfile(path, ns, ns)
- self.filename = path
- except EnvironmentError, e:
- if e.errno <> errno.ENOENT or original_filename:
- raise
- # The file didn't exist, so try mm_cfg.py
- from Mailman import mm_cfg
- ns.update(mm_cfg.__dict__)
- self.filename = None
- # Based on values possibly set in mailman.cfg, add additional qrunners
+ # Load the configuration from the named file, or if not given, search
+ # in the runtime data directory for an etc/mailman.cfg file. If there
+ # is an instance.cfg file in the same directory, load that first, then
+ # mailman.cfg.
+ #
+ # Whatever you find, create a namespace and execfile that file in it.
+ # The values in that namespace are exposed as attributes on this
+ # Configuration instance.
+ self.filename = None
+ paths = [
+ # Development directories.
+ join(os.getcwd(), 'etc', 'mailman.cfg'),
+ join(os.getcwd(), 'var', 'etc', 'mailman.cfg'),
+ # Standard installation directories.
+ join('/etc', 'mailman.cfg'),
+ join(Defaults.DEFAULT_VAR_DIRECTORY, 'etc', 'mailman.cfg'),
+ ]
+ if filename is not None:
+ paths.insert(0, filename)
+ for cfg_path in paths:
+ path = os.path.abspath(os.path.expanduser(cfg_path))
+ try:
+ execfile(path, ns, ns)
+ except EnvironmentError, e:
+ if e.errno <> errno.ENOENT:
+ # It's okay if the instance.cfg file does not exist. This
+ # can happen for example in the test suite.
+ raise
+ else:
+ self.filename = cfg_path
+ break
+ if self.filename is None:
+ # The logging subsystem isn't running yet, so use stderr.
+ print >> sys.stderr, 'No runtime configuration file file. Use -C'
+ sys.exit(-1)
+ # Based on values possibly set in mailman.cfg, add additional qrunners.
if ns['USE_MAILDIR']:
self.add_qrunner('Maildir')
if ns['USE_LMTP']:
self.add_qrunner('LMTP')
- # Pull out the defaults
- RUNTIME_DIR = ns['RUNTIME_DIR']
+ # Pull out the defaults.
+ VAR_DIR = ns['VAR_DIR']
# Now that we've loaded all the configuration files we're going to
# load, set up some useful directories.
- self.LIST_DATA_DIR = join(RUNTIME_DIR, 'lists')
- self.LOG_DIR = join(RUNTIME_DIR, 'logs')
- self.LOCK_DIR = lockdir = join(RUNTIME_DIR, 'locks')
- self.DATA_DIR = datadir = join(RUNTIME_DIR, 'data')
- self.ETC_DIR = etcdir = join(RUNTIME_DIR, 'etc')
- self.SPAM_DIR = join(RUNTIME_DIR, 'spam')
- self.EXT_DIR = join(RUNTIME_DIR, 'ext')
- self.PUBLIC_ARCHIVE_FILE_DIR = join(RUNTIME_DIR, 'archives', 'public')
- self.PRIVATE_ARCHIVE_FILE_DIR = join(
- RUNTIME_DIR, 'archives', 'private')
+ self.LIST_DATA_DIR = join(VAR_DIR, 'lists')
+ self.LOG_DIR = join(VAR_DIR, 'logs')
+ self.LOCK_DIR = lockdir = join(VAR_DIR, 'locks')
+ self.DATA_DIR = datadir = join(VAR_DIR, 'data')
+ self.ETC_DIR = etcdir = join(VAR_DIR, 'etc')
+ self.SPAM_DIR = join(VAR_DIR, 'spam')
+ self.EXT_DIR = join(VAR_DIR, 'ext')
+ self.PUBLIC_ARCHIVE_FILE_DIR = join(VAR_DIR, 'archives', 'public')
+ self.PRIVATE_ARCHIVE_FILE_DIR = join(VAR_DIR, 'archives', 'private')
# Directories used by the qrunner subsystem
- self.QUEUE_DIR = qdir = join(RUNTIME_DIR, 'qfiles')
+ self.QUEUE_DIR = qdir = join(VAR_DIR, 'qfiles')
self.INQUEUE_DIR = join(qdir, 'in')
self.OUTQUEUE_DIR = join(qdir, 'out')
self.CMDQUEUE_DIR = join(qdir, 'commands')
@@ -121,15 +140,33 @@ class Configuration(object):
self.CONFIG_FILE = join(etcdir, 'mailman.cfg')
self.LOCK_FILE = join(lockdir, 'master-qrunner')
# Now update our dict so attribute syntax just works
- if 'add_domain' in ns:
- del ns['add_domain']
- if 'add_runner' in ns:
- del ns['add_runner']
+ del ns['add_domain']
+ del ns['add_qrunner']
+ del ns['del_qrunner']
self.__dict__.update(ns)
# Add the default domain if there are no virtual domains currently
# defined.
if not self.domains:
self.add_domain(self.DEFAULT_EMAIL_HOST, self.DEFAULT_URL_HOST)
+ # Enable all specified languages, and promote the language manager to
+ # a public attribute.
+ self.languages = self._languages
+ del self._languages
+ available_languages = set(self._DEFAULT_LANGUAGE_DATA)
+ enabled_languages = set(self.LANGUAGES.split())
+ if 'all' in enabled_languages:
+ languages = available_languages
+ else:
+ unknown_language = enabled_languages - available_languages
+ if unknown_language:
+ print >> sys.stderr, 'Ignoring unknown language codes:', \
+ SPACE.join(unknown_language)
+ languages = available_languages & enabled_languages
+ for code in languages:
+ self.languages.enable_language(code)
+ # Always add and enable the default server language.
+ code = self.DEFAULT_SERVER_LANGUAGE
+ self.languages.enable_language(code)
def add_domain(self, email_host, url_host):
"""Add the definition of a virtual domain.
diff --git a/misc/Elixir-0.3.0.tar.gz b/Mailman/data/Elixir-0.3.0.tar.gz
index fd61be97e..fd61be97e 100644
--- a/misc/Elixir-0.3.0.tar.gz
+++ b/Mailman/data/Elixir-0.3.0.tar.gz
Binary files differ
diff --git a/misc/PythonPowered.png b/Mailman/data/PythonPowered.png
index 2e9d99c2d..2e9d99c2d 100644
--- a/misc/PythonPowered.png
+++ b/Mailman/data/PythonPowered.png
Binary files differ
diff --git a/misc/SQLAlchemy-0.3.3.tar.gz b/Mailman/data/SQLAlchemy-0.3.3.tar.gz
index 5732923fd..5732923fd 100644
--- a/misc/SQLAlchemy-0.3.3.tar.gz
+++ b/Mailman/data/SQLAlchemy-0.3.3.tar.gz
Binary files differ
diff --git a/Mailman/data/__init__.py b/Mailman/data/__init__.py
new file mode 100755
index 000000000..e69de29bb
--- /dev/null
+++ b/Mailman/data/__init__.py
diff --git a/misc/coverage.py b/Mailman/data/coverage.py
index 66e55e0c4..66e55e0c4 100644
--- a/misc/coverage.py
+++ b/Mailman/data/coverage.py
diff --git a/misc/gnu-head-tiny.jpg b/Mailman/data/gnu-head-tiny.jpg
index 441be50d6..441be50d6 100644
--- a/misc/gnu-head-tiny.jpg
+++ b/Mailman/data/gnu-head-tiny.jpg
Binary files differ
diff --git a/misc/mailman-large.jpg b/Mailman/data/mailman-large.jpg
index e184f3c6e..e184f3c6e 100644
--- a/misc/mailman-large.jpg
+++ b/Mailman/data/mailman-large.jpg
Binary files differ
diff --git a/misc/mailman.cfg.sample b/Mailman/data/mailman.cfg.in
index 3aab3d61e..3e24acc46 100644
--- a/misc/mailman.cfg.sample
+++ b/Mailman/data/mailman.cfg.in
@@ -19,15 +19,27 @@
"""This module contains your site-specific settings.
-Use this to override the default settings in Mailman/Defaults.py. You only
+Use this to override the default settings in `Mailman/Defaults.py`. You only
need to include those settings that you want to change, and unlike the old
-mm_cfg.py file, you do /not/ need to import Defaults. Its variables will
+`mm_cfg.py` file, you do /not/ need to `import Defaults`. Its variables will
automatically be available in this module's namespace.
-You should consult Defaults.py though for a complete listing of configuration
-variables that you can change.
+You should consult `Defaults.py` though for a complete listing of
+configuration variables that you can change.
-To use this, copy this file to $VAR_PREFIX/etc/mailman.cfg
-
-Mailman's installation procedure will never overwrite mailman.cfg.
+Mailman's installation procedure will never overwrite `mailman.cfg`.
"""
+
+# This is the top-level run-time data directory. All other runtime data by
+# default lives under this directory.
+VAR_DIR = '$var_dir'
+
+# User name and id that owns the Mailman process and files.
+MAILMAN_UID = '$user_id'
+MAILMAN_USER = '$user_name'
+
+# Group name and id that owns the Mailman process and files.
+MAILMAN_GID = '$group_id'
+MAILMAN_GROUP = '$group_name'
+
+LANGUAGES = '$languages'
diff --git a/misc/mailman.in b/Mailman/data/mailman.in
index 4fb0612f3..4fb0612f3 100644
--- a/misc/mailman.in
+++ b/Mailman/data/mailman.in
diff --git a/misc/mailman.jpg b/Mailman/data/mailman.jpg
index 94a4c0112..94a4c0112 100644
--- a/misc/mailman.jpg
+++ b/Mailman/data/mailman.jpg
Binary files differ
diff --git a/misc/mm-icon.png b/Mailman/data/mm-icon.png
index 42c3cf759..42c3cf759 100644
--- a/misc/mm-icon.png
+++ b/Mailman/data/mm-icon.png
Binary files differ
diff --git a/misc/munepy-1.1-py2.5.egg b/Mailman/data/munepy-1.1-py2.5.egg
index 00ed8ccfe..00ed8ccfe 100644
--- a/misc/munepy-1.1-py2.5.egg
+++ b/Mailman/data/munepy-1.1-py2.5.egg
Binary files differ
diff --git a/misc/paths.py.in b/Mailman/data/paths.py.in
index a840e3f14..a840e3f14 100644
--- a/misc/paths.py.in
+++ b/Mailman/data/paths.py.in
diff --git a/misc/pysqlite-2.3.2.tar.gz b/Mailman/data/pysqlite-2.3.2.tar.gz
index 52f5711e9..52f5711e9 100644
--- a/misc/pysqlite-2.3.2.tar.gz
+++ b/Mailman/data/pysqlite-2.3.2.tar.gz
Binary files differ
diff --git a/misc/setuptools-0.6c3.tar.gz b/Mailman/data/setuptools-0.6c3.tar.gz
index 30a4a464a..30a4a464a 100644
--- a/misc/setuptools-0.6c3.tar.gz
+++ b/Mailman/data/setuptools-0.6c3.tar.gz
Binary files differ
diff --git a/misc/wsgiref-0.1.2-py2.4.egg b/Mailman/data/wsgiref-0.1.2-py2.4.egg
index b38cb8321..b38cb8321 100644
--- a/misc/wsgiref-0.1.2-py2.4.egg
+++ b/Mailman/data/wsgiref-0.1.2-py2.4.egg
Binary files differ
diff --git a/misc/zope.interface-3.3.0.1.tar.gz b/Mailman/data/zope.interface-3.3.0.1.tar.gz
index c95bf0698..c95bf0698 100644
--- a/misc/zope.interface-3.3.0.1.tar.gz
+++ b/Mailman/data/zope.interface-3.3.0.1.tar.gz
Binary files differ
diff --git a/Mailman/docs/acknowledge.txt b/Mailman/docs/acknowledge.txt
index 82fdd3fd3..e22bf3d57 100644
--- a/Mailman/docs/acknowledge.txt
+++ b/Mailman/docs/acknowledge.txt
@@ -12,6 +12,7 @@ acknowledgment.
>>> from Mailman.database import flush
>>> mlist = config.list_manager.create('_xtest@example.com')
>>> mlist.real_name = 'XTest'
+ >>> mlist.preferred_language = 'en'
>>> # XXX This will almost certainly change once we've worked out the web
>>> # space layout for mailing lists now.
>>> mlist._data.web_page_url = 'http://lists.example.com/'
diff --git a/Mailman/docs/digests.txt b/Mailman/docs/digests.txt
index 3788651f9..8f2a60ebc 100644
--- a/Mailman/docs/digests.txt
+++ b/Mailman/docs/digests.txt
@@ -418,9 +418,16 @@ Internationalized digests
-------------------------
When messages come in with a content-type character set different than that of
-the list's preferred language, recipients wil get an internationalized digest.
+the list's preferred language, recipients wil get an internationalized
+digest. French is not enabled by default site-wide, so enable that now.
+XXX We also have to set the default server language to French, otherwise the
+English template will be found and the masthead won't be translated.
+
+ >>> config.languages.enable_language('fr')
+ >>> config.DEFAULT_SERVER_LANGUAGE = 'fr'
>>> mlist.preferred_language = 'fr'
+ >>> flush()
>>> msg = message_from_string("""\
... From: aperson@example.org
... To: _xtest@example.com
@@ -537,3 +544,9 @@ Set the digest threshold to zero so that the digests will be sent immediately.
('listname', '_xtest@example.com'),
('received_time', ...),
('recips', set([])), ('version', 3)]
+
+
+Clean up
+--------
+
+ >>> config.DEFAULT_SERVER_LANGUAGE = 'en'
diff --git a/Mailman/docs/languages.txt b/Mailman/docs/languages.txt
new file mode 100644
index 000000000..2c1664da3
--- /dev/null
+++ b/Mailman/docs/languages.txt
@@ -0,0 +1,94 @@
+Languages
+=========
+
+Mailman is multilingual. A language manager handles the known set of
+languages at run time, as well as enabling those languages for use in a
+running Mailman instance.
+
+ >>> from zope.interface.verify import verifyObject
+ >>> from Mailman.interfaces import ILanguageManager
+ >>> from Mailman.languages import LanguageManager
+ >>> mgr = LanguageManager()
+ >>> verifyObject(ILanguageManager, mgr)
+ True
+
+A language manager keeps track of the languages it knows about as well as the
+languages which are enabled. By default, none are known or enabled.
+
+ >>> sorted(mgr.known_codes)
+ []
+ >>> sorted(mgr.enabled_codes)
+ []
+
+The language manager also keeps track of information for each known language,
+but you obviously can't get information for an unknown language.
+
+ >>> mgr.get_language_data('en')
+ Traceback (most recent call last):
+ ...
+ KeyError: 'en'
+
+
+Adding languages
+----------------
+
+Adding a new language requires three pieces of information, the 2-character
+language code, the English description of the language, and the character set
+used by the language.
+
+ >>> mgr.add_language('en', 'English', 'us-ascii')
+ >>> mgr.add_language('it', 'Italian', 'iso-8859-1')
+
+By default, added languages are also enabled.
+
+ >>> sorted(mgr.known_codes)
+ ['en', 'it']
+ >>> sorted(mgr.enabled_codes)
+ ['en', 'it']
+
+And you can get information for all known languages.
+
+ >>> mgr.get_language_data('en')
+ ('English', 'us-ascii')
+ >>> mgr.get_language_data('it')
+ ('Italian', 'iso-8859-1')
+
+You can also add a language without enabling it.
+
+ >>> mgr.add_language('pl', 'Polish', 'iso-8859-2', enable=False)
+ >>> sorted(mgr.known_codes)
+ ['en', 'it', 'pl']
+ >>> sorted(mgr.enabled_codes)
+ ['en', 'it']
+
+You can get language data for disabled languages.
+
+ >>> mgr.get_language_data('pl')
+ ('Polish', 'iso-8859-2')
+
+And of course you can enable a known language.
+
+ >>> mgr.enable_language('pl')
+ >>> sorted(mgr.enabled_codes)
+ ['en', 'it', 'pl']
+
+But you cannot enable languages that the manager does not know about.
+
+ >>> mgr.enable_language('xx')
+ Traceback (most recent call last):
+ ...
+ KeyError: 'xx'
+
+
+Other iterations
+----------------
+
+You can iterate over the descriptions (names) of all enabled languages.
+
+ >>> sorted(mgr.enabled_names)
+ ['English', 'Italian', 'Polish']
+
+You can ask whether a particular language code is enabled.
+
+ >>> 'it' in mgr.enabled_codes
+ True
diff --git a/Mailman/i18n.py b/Mailman/i18n.py
index 66584e895..120e861ad 100644
--- a/Mailman/i18n.py
+++ b/Mailman/i18n.py
@@ -15,17 +15,23 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
+import os
import sys
import time
import string
import gettext
+import Mailman.messages
from Mailman.SafeDict import SafeDict
from Mailman.configuration import config
_translation = None
_missing = object()
+MESSAGES_DIR = os.path.dirname(Mailman.messages.__file__)
+
+
+
class Template(string.Template):
idpattern = r'[_a-z][_a-z0-9.]*'
@@ -47,8 +53,7 @@ def set_language(language=None):
if language is not None:
language = [language]
try:
- _translation = gettext.translation('mailman', config.MESSAGES_DIR,
- language)
+ _translation = gettext.translation('mailman', MESSAGES_DIR, language)
except IOError:
# The selected language was not installed in messages, so fall back to
# untranslated English.
diff --git a/Mailman/interfaces/languages.py b/Mailman/interfaces/languages.py
new file mode 100644
index 000000000..44a284a5b
--- /dev/null
+++ b/Mailman/interfaces/languages.py
@@ -0,0 +1,65 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Interfaces for managing languages."""
+
+from zope.interface import Interface, Attribute
+
+
+
+class ILanguageManager(Interface):
+ """A language manager.
+
+ Current, disabling languages is not supported.
+ """
+
+ def add_language(code, description, charset, enable=True):
+ """Teach the language manager about a language.
+
+ :param code: The short two-character language code for the
+ language. If the language manager already knows about this code,
+ the old language binding is lost.
+ :param description: The English description of the language,
+ e.g. 'English' or 'French'.
+ :param charset: The character set that the language uses,
+ e.g. 'us-ascii', 'iso-8859-1', or 'utf-8'
+ :param enable: Enable the language at the same time.
+ """
+
+ def enable_language(code):
+ """Enable a language that the manager already knows about.
+
+ :raises KeyError: when the manager does not know about the given
+ language code.
+ """
+
+ def get_language_data(code):
+ """Return the description and charset for the given `code`.
+
+ :param code: The code to lookup.
+ :returns: A 2-tuple of the description and charset for the code.
+ :raises KeyError: when the code is unknown.
+ """
+
+ known_codes = Attribute(
+ """An iterator over all known codes.""")
+
+ enabled_codes = Attribute(
+ """An iterator over all enabled codes.""")
+
+ enabled_names = Attribute(
+ """An iterator over all enabled language names.""")
diff --git a/Mailman/interfaces/manager.py b/Mailman/interfaces/manager.py
deleted file mode 100644
index fe22ec74d..000000000
--- a/Mailman/interfaces/manager.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-"""Generic database object manager interface."""
-
-from zope.interface import Interface, Attribute
-
-
-
-class IManaged(Interface):
- """An object managed by an IManager."""
-
- name = Attribute("""The name of the managed object.""")
-
-
-
-class IManager(Interface):
- """Create and manage profiles."""
-
- def create(name):
- """Create and return a new IManaged object.
-
- name is the unique name for this object. Raises
- ExistingManagedObjectError if an IManaged object with the given name
- already exists.
- """
-
- def get(name):
- """Return the named IManaged object.
-
- Raises NoSuchManagedObjectError if the named IManaged object does not
- exist.
- """
-
- def delete(name):
- """Delete the named IManaged object.
-
- Raises NoSuchManagedObjectError if the named IManaged object does not
- exist.
- """
-
- iterator = Attribute(
- """Return an iterator over the all the IManaged objects.""")
diff --git a/Mailman/languages.py b/Mailman/languages.py
new file mode 100644
index 000000000..a6a457a20
--- /dev/null
+++ b/Mailman/languages.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Language manager."""
+
+from zope.interface import implements
+from Mailman.interfaces import ILanguageManager
+
+
+
+class LanguageManager:
+ implements(ILanguageManager)
+
+ def __init__(self):
+ self._language_data = {}
+ self._enabled = set()
+
+ def add_language(self, code, description, charset, enable=True):
+ self._language_data[code] = (description, charset)
+ if enable:
+ self._enabled.add(code)
+
+ def enable_language(self, code):
+ # As per the interface, let KeyError percolate up.
+ self._language_data[code]
+ self._enabled.add(code)
+
+ def get_language_data(self, code):
+ return self._language_data[code]
+
+ @property
+ def known_codes(self):
+ return iter(self._language_data)
+
+ @property
+ def enabled_codes(self):
+ return iter(self._enabled)
+
+ @property
+ def enabled_names(self):
+ for code in self._enabled:
+ description, charset = self._language_data[code]
+ yield description
diff --git a/Mailman/messages/__init__.py b/Mailman/messages/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/Mailman/messages/__init__.py
diff --git a/messages/ar/LC_MESSAGES/mailman.po b/Mailman/messages/ar/LC_MESSAGES/mailman.po
index 076a13434..076a13434 100644
--- a/messages/ar/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/ar/LC_MESSAGES/mailman.po
diff --git a/messages/ca/LC_MESSAGES/mailman.po b/Mailman/messages/ca/LC_MESSAGES/mailman.po
index 3ec74f0fe..3ec74f0fe 100644
--- a/messages/ca/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/ca/LC_MESSAGES/mailman.po
diff --git a/messages/cs/LC_MESSAGES/mailman.po b/Mailman/messages/cs/LC_MESSAGES/mailman.po
index 0b9fe96be..0b9fe96be 100644
--- a/messages/cs/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/cs/LC_MESSAGES/mailman.po
diff --git a/messages/da/LC_MESSAGES/mailman.po b/Mailman/messages/da/LC_MESSAGES/mailman.po
index c4afab6f2..c4afab6f2 100644
--- a/messages/da/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/da/LC_MESSAGES/mailman.po
diff --git a/messages/de/LC_MESSAGES/mailman.po b/Mailman/messages/de/LC_MESSAGES/mailman.po
index 9403a4f92..9403a4f92 100644
--- a/messages/de/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/de/LC_MESSAGES/mailman.po
diff --git a/messages/de/README.de b/Mailman/messages/de/README.de
index 4ca818d31..4ca818d31 100644
--- a/messages/de/README.de
+++ b/Mailman/messages/de/README.de
diff --git a/messages/docstring.files b/Mailman/messages/docstring.files
index c65b0e949..c65b0e949 100644
--- a/messages/docstring.files
+++ b/Mailman/messages/docstring.files
diff --git a/messages/es/LC_MESSAGES/mailman.po b/Mailman/messages/es/LC_MESSAGES/mailman.po
index 7a2224010..7a2224010 100644
--- a/messages/es/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/es/LC_MESSAGES/mailman.po
diff --git a/messages/es/README.es b/Mailman/messages/es/README.es
index 066e7d7dd..066e7d7dd 100644
--- a/messages/es/README.es
+++ b/Mailman/messages/es/README.es
diff --git a/messages/et/LC_MESSAGES/mailman.po b/Mailman/messages/et/LC_MESSAGES/mailman.po
index 2e404618f..2e404618f 100644
--- a/messages/et/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/et/LC_MESSAGES/mailman.po
diff --git a/messages/eu/LC_MESSAGES/mailman.po b/Mailman/messages/eu/LC_MESSAGES/mailman.po
index 979221811..979221811 100644
--- a/messages/eu/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/eu/LC_MESSAGES/mailman.po
diff --git a/messages/eu/README.eu b/Mailman/messages/eu/README.eu
index 8176d5288..8176d5288 100644
--- a/messages/eu/README.eu
+++ b/Mailman/messages/eu/README.eu
diff --git a/messages/fi/LC_MESSAGES/mailman.po b/Mailman/messages/fi/LC_MESSAGES/mailman.po
index eac01f0ca..eac01f0ca 100644
--- a/messages/fi/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/fi/LC_MESSAGES/mailman.po
diff --git a/messages/fi/README.fi b/Mailman/messages/fi/README.fi
index 796275f0a..796275f0a 100644
--- a/messages/fi/README.fi
+++ b/Mailman/messages/fi/README.fi
diff --git a/messages/fr/LC_MESSAGES/mailman.po b/Mailman/messages/fr/LC_MESSAGES/mailman.po
index d6546b6dc..6bccf8d9c 100644
--- a/messages/fr/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/fr/LC_MESSAGES/mailman.po
@@ -7958,8 +7958,8 @@ msgid "Message rejected by filter rule match"
msgstr "Message rejeté par correspondance avec une règle de filtrage"
#: Mailman/Handlers/ToDigest.py:159
-msgid "%(realname)s Digest, Vol %(volume)d, Issue %(issue)d"
-msgstr "Groupe %(realname)s, Vol. %(volume)d, Parution %(issue)d"
+msgid "$realname Digest, Vol $volume, Issue $issue"
+msgstr "Groupe $realname, Vol. $volume, Parution $issue"
#: Mailman/Handlers/ToDigest.py:205
msgid "digest header"
diff --git a/messages/fr/README.fr b/Mailman/messages/fr/README.fr
index 0ae05b224..0ae05b224 100644
--- a/messages/fr/README.fr
+++ b/Mailman/messages/fr/README.fr
diff --git a/messages/hr/LC_MESSAGES/mailman.po b/Mailman/messages/hr/LC_MESSAGES/mailman.po
index 243997947..243997947 100644
--- a/messages/hr/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/hr/LC_MESSAGES/mailman.po
diff --git a/messages/hu/FAQ.hu b/Mailman/messages/hu/FAQ.hu
index 7430f1d22..7430f1d22 100644
--- a/messages/hu/FAQ.hu
+++ b/Mailman/messages/hu/FAQ.hu
diff --git a/messages/hu/INSTALL.hu b/Mailman/messages/hu/INSTALL.hu
index 0fc4aaf76..0fc4aaf76 100644
--- a/messages/hu/INSTALL.hu
+++ b/Mailman/messages/hu/INSTALL.hu
diff --git a/messages/hu/LC_MESSAGES/mailman.po b/Mailman/messages/hu/LC_MESSAGES/mailman.po
index 2d568d554..2d568d554 100644
--- a/messages/hu/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/hu/LC_MESSAGES/mailman.po
diff --git a/messages/hu/README.BSD.hu b/Mailman/messages/hu/README.BSD.hu
index 6ff26c6d7..6ff26c6d7 100644
--- a/messages/hu/README.BSD.hu
+++ b/Mailman/messages/hu/README.BSD.hu
diff --git a/messages/hu/README.CONTRIB.hu b/Mailman/messages/hu/README.CONTRIB.hu
index 0b53cdc12..0b53cdc12 100644
--- a/messages/hu/README.CONTRIB.hu
+++ b/Mailman/messages/hu/README.CONTRIB.hu
diff --git a/messages/hu/README.EXIM.hu b/Mailman/messages/hu/README.EXIM.hu
index 107438b17..107438b17 100644
--- a/messages/hu/README.EXIM.hu
+++ b/Mailman/messages/hu/README.EXIM.hu
diff --git a/messages/hu/README.LINUX.hu b/Mailman/messages/hu/README.LINUX.hu
index 9646a6cee..9646a6cee 100644
--- a/messages/hu/README.LINUX.hu
+++ b/Mailman/messages/hu/README.LINUX.hu
diff --git a/messages/hu/README.MACOSX.hu b/Mailman/messages/hu/README.MACOSX.hu
index 3b9420c7c..3b9420c7c 100644
--- a/messages/hu/README.MACOSX.hu
+++ b/Mailman/messages/hu/README.MACOSX.hu
diff --git a/messages/hu/README.NETSCAPE.hu b/Mailman/messages/hu/README.NETSCAPE.hu
index efcda422e..efcda422e 100644
--- a/messages/hu/README.NETSCAPE.hu
+++ b/Mailman/messages/hu/README.NETSCAPE.hu
diff --git a/messages/hu/README.POSTFIX.hu b/Mailman/messages/hu/README.POSTFIX.hu
index f2a36c3f0..f2a36c3f0 100644
--- a/messages/hu/README.POSTFIX.hu
+++ b/Mailman/messages/hu/README.POSTFIX.hu
diff --git a/messages/hu/README.QMAIL.hu b/Mailman/messages/hu/README.QMAIL.hu
index 72ab96e31..72ab96e31 100644
--- a/messages/hu/README.QMAIL.hu
+++ b/Mailman/messages/hu/README.QMAIL.hu
diff --git a/messages/hu/README.SENDMAIL.hu b/Mailman/messages/hu/README.SENDMAIL.hu
index 028550952..028550952 100644
--- a/messages/hu/README.SENDMAIL.hu
+++ b/Mailman/messages/hu/README.SENDMAIL.hu
diff --git a/messages/hu/README.USERAGENT.hu b/Mailman/messages/hu/README.USERAGENT.hu
index d92dd1233..d92dd1233 100644
--- a/messages/hu/README.USERAGENT.hu
+++ b/Mailman/messages/hu/README.USERAGENT.hu
diff --git a/messages/hu/README.hu b/Mailman/messages/hu/README.hu
index 4638b78ea..4638b78ea 100644
--- a/messages/hu/README.hu
+++ b/Mailman/messages/hu/README.hu
diff --git a/messages/hu/UPGRADING.hu b/Mailman/messages/hu/UPGRADING.hu
index 1a070a27f..1a070a27f 100644
--- a/messages/hu/UPGRADING.hu
+++ b/Mailman/messages/hu/UPGRADING.hu
diff --git a/messages/ia/LC_MESSAGES/mailman.po b/Mailman/messages/ia/LC_MESSAGES/mailman.po
index 2cf4db675..2cf4db675 100644
--- a/messages/ia/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/ia/LC_MESSAGES/mailman.po
diff --git a/messages/it/LC_MESSAGES/mailman.po b/Mailman/messages/it/LC_MESSAGES/mailman.po
index 598968457..598968457 100644
--- a/messages/it/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/it/LC_MESSAGES/mailman.po
diff --git a/messages/it/README.it b/Mailman/messages/it/README.it
index 56fcc422d..56fcc422d 100644
--- a/messages/it/README.it
+++ b/Mailman/messages/it/README.it
diff --git a/messages/ja/INSTALL b/Mailman/messages/ja/INSTALL
index f8c4de6b4..f8c4de6b4 100644
--- a/messages/ja/INSTALL
+++ b/Mailman/messages/ja/INSTALL
diff --git a/messages/ja/LC_MESSAGES/mailman.po b/Mailman/messages/ja/LC_MESSAGES/mailman.po
index 611dba470..611dba470 100644
--- a/messages/ja/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/ja/LC_MESSAGES/mailman.po
diff --git a/messages/ja/README b/Mailman/messages/ja/README
index a163742c1..a163742c1 100644
--- a/messages/ja/README
+++ b/Mailman/messages/ja/README
diff --git a/messages/ja/README.ja b/Mailman/messages/ja/README.ja
index a459d9daa..a459d9daa 100644
--- a/messages/ja/README.ja
+++ b/Mailman/messages/ja/README.ja
diff --git a/messages/ja/UPGRADING b/Mailman/messages/ja/UPGRADING
index 3afc8c5fe..3afc8c5fe 100644
--- a/messages/ja/UPGRADING
+++ b/Mailman/messages/ja/UPGRADING
diff --git a/messages/ja/doc/Defaults.py.in b/Mailman/messages/ja/doc/Defaults.py.in
index cfbcc9ed8..cfbcc9ed8 100644
--- a/messages/ja/doc/Defaults.py.in
+++ b/Mailman/messages/ja/doc/Defaults.py.in
diff --git a/messages/ja/doc/mailman-install.tex b/Mailman/messages/ja/doc/mailman-install.tex
index aa4297400..aa4297400 100644
--- a/messages/ja/doc/mailman-install.tex
+++ b/Mailman/messages/ja/doc/mailman-install.tex
diff --git a/messages/ja/doc/mailman-member.tex b/Mailman/messages/ja/doc/mailman-member.tex
index 8fe6e3543..8fe6e3543 100644
--- a/messages/ja/doc/mailman-member.tex
+++ b/Mailman/messages/ja/doc/mailman-member.tex
diff --git a/messages/ko/LC_MESSAGES/mailman.po b/Mailman/messages/ko/LC_MESSAGES/mailman.po
index 1654f4f2a..1654f4f2a 100644
--- a/messages/ko/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/ko/LC_MESSAGES/mailman.po
diff --git a/messages/ko/README.ko b/Mailman/messages/ko/README.ko
index 268a96d01..268a96d01 100644
--- a/messages/ko/README.ko
+++ b/Mailman/messages/ko/README.ko
diff --git a/messages/lt/LC_MESSAGES/mailman.po b/Mailman/messages/lt/LC_MESSAGES/mailman.po
index 1e87b6eea..1e87b6eea 100644
--- a/messages/lt/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/lt/LC_MESSAGES/mailman.po
diff --git a/messages/mailman.pot b/Mailman/messages/mailman.pot
index ae04e58f2..ae04e58f2 100644
--- a/messages/mailman.pot
+++ b/Mailman/messages/mailman.pot
diff --git a/messages/marked.files b/Mailman/messages/marked.files
index 375fb35a5..375fb35a5 100644
--- a/messages/marked.files
+++ b/Mailman/messages/marked.files
diff --git a/messages/nl/LC_MESSAGES/mailman.po b/Mailman/messages/nl/LC_MESSAGES/mailman.po
index 290808f90..290808f90 100644
--- a/messages/nl/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/nl/LC_MESSAGES/mailman.po
diff --git a/messages/no/LC_MESSAGES/mailman.po b/Mailman/messages/no/LC_MESSAGES/mailman.po
index 5e4c53696..5e4c53696 100644
--- a/messages/no/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/no/LC_MESSAGES/mailman.po
diff --git a/messages/pl/LC_MESSAGES/mailman.po b/Mailman/messages/pl/LC_MESSAGES/mailman.po
index de6273bd2..de6273bd2 100644
--- a/messages/pl/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/pl/LC_MESSAGES/mailman.po
diff --git a/messages/pl/README.pl b/Mailman/messages/pl/README.pl
index 47f40998b..47f40998b 100644
--- a/messages/pl/README.pl
+++ b/Mailman/messages/pl/README.pl
diff --git a/messages/pt/LC_MESSAGES/mailman.po b/Mailman/messages/pt/LC_MESSAGES/mailman.po
index 1976c787f..1976c787f 100644
--- a/messages/pt/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/pt/LC_MESSAGES/mailman.po
diff --git a/messages/pt_BR/LC_MESSAGES/mailman.po b/Mailman/messages/pt_BR/LC_MESSAGES/mailman.po
index 47c983be8..47c983be8 100644
--- a/messages/pt_BR/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/pt_BR/LC_MESSAGES/mailman.po
diff --git a/messages/ro/LC_MESSAGES/mailman.po b/Mailman/messages/ro/LC_MESSAGES/mailman.po
index 05a80aca7..05a80aca7 100644
--- a/messages/ro/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/ro/LC_MESSAGES/mailman.po
diff --git a/messages/ru/LC_MESSAGES/mailman.po b/Mailman/messages/ru/LC_MESSAGES/mailman.po
index 6a02aca15..6a02aca15 100644
--- a/messages/ru/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/ru/LC_MESSAGES/mailman.po
diff --git a/messages/ru/README.ru b/Mailman/messages/ru/README.ru
index 027dbe878..027dbe878 100644
--- a/messages/ru/README.ru
+++ b/Mailman/messages/ru/README.ru
diff --git a/messages/sl/LC_MESSAGES/mailman.po b/Mailman/messages/sl/LC_MESSAGES/mailman.po
index ca1ed16d2..ca1ed16d2 100644
--- a/messages/sl/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/sl/LC_MESSAGES/mailman.po
diff --git a/messages/sr/LC_MESSAGES/mailman.po b/Mailman/messages/sr/LC_MESSAGES/mailman.po
index 058a4939e..058a4939e 100644
--- a/messages/sr/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/sr/LC_MESSAGES/mailman.po
diff --git a/messages/sr/readme.sr b/Mailman/messages/sr/readme.sr
index df7eef0e9..df7eef0e9 100644
--- a/messages/sr/readme.sr
+++ b/Mailman/messages/sr/readme.sr
diff --git a/messages/sv/LC_MESSAGES/mailman.po b/Mailman/messages/sv/LC_MESSAGES/mailman.po
index 724aefce3..724aefce3 100644
--- a/messages/sv/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/sv/LC_MESSAGES/mailman.po
diff --git a/messages/sv/README.sv b/Mailman/messages/sv/README.sv
index f74eaec5b..f74eaec5b 100644
--- a/messages/sv/README.sv
+++ b/Mailman/messages/sv/README.sv
diff --git a/messages/tr/LC_MESSAGES/mailman.po b/Mailman/messages/tr/LC_MESSAGES/mailman.po
index 273880afe..273880afe 100644
--- a/messages/tr/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/tr/LC_MESSAGES/mailman.po
diff --git a/messages/uk/LC_MESSAGES/mailman.po b/Mailman/messages/uk/LC_MESSAGES/mailman.po
index 296c0eecc..296c0eecc 100644
--- a/messages/uk/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/uk/LC_MESSAGES/mailman.po
diff --git a/messages/vi/LC_MESSAGES/mailman.po b/Mailman/messages/vi/LC_MESSAGES/mailman.po
index 880df992c..880df992c 100644
--- a/messages/vi/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/vi/LC_MESSAGES/mailman.po
diff --git a/messages/zh_CN/LC_MESSAGES/mailman.po b/Mailman/messages/zh_CN/LC_MESSAGES/mailman.po
index c5b2382d6..c5b2382d6 100644
--- a/messages/zh_CN/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/zh_CN/LC_MESSAGES/mailman.po
diff --git a/messages/zh_TW/LC_MESSAGES/mailman.po b/Mailman/messages/zh_TW/LC_MESSAGES/mailman.po
index 6c82e3794..6c82e3794 100644
--- a/messages/zh_TW/LC_MESSAGES/mailman.po
+++ b/Mailman/messages/zh_TW/LC_MESSAGES/mailman.po
diff --git a/Mailman/templates/__init__.py b/Mailman/templates/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/Mailman/templates/__init__.py
diff --git a/templates/en/adminaddrchgack.txt b/Mailman/templates/en/adminaddrchgack.txt
index a24dd3d91..a24dd3d91 100644
--- a/templates/en/adminaddrchgack.txt
+++ b/Mailman/templates/en/adminaddrchgack.txt
diff --git a/templates/en/admindbdetails.html b/Mailman/templates/en/admindbdetails.html
index d97fcec11..d97fcec11 100644
--- a/templates/en/admindbdetails.html
+++ b/Mailman/templates/en/admindbdetails.html
diff --git a/templates/en/admindbpreamble.html b/Mailman/templates/en/admindbpreamble.html
index 659b77e72..659b77e72 100644
--- a/templates/en/admindbpreamble.html
+++ b/Mailman/templates/en/admindbpreamble.html
diff --git a/templates/en/admindbsummary.html b/Mailman/templates/en/admindbsummary.html
index 20ffef584..20ffef584 100644
--- a/templates/en/admindbsummary.html
+++ b/Mailman/templates/en/admindbsummary.html
diff --git a/templates/en/adminsubscribeack.txt b/Mailman/templates/en/adminsubscribeack.txt
index 388a3a240..388a3a240 100644
--- a/templates/en/adminsubscribeack.txt
+++ b/Mailman/templates/en/adminsubscribeack.txt
diff --git a/templates/en/adminunsubscribeack.txt b/Mailman/templates/en/adminunsubscribeack.txt
index 2ebcfeb70..2ebcfeb70 100644
--- a/templates/en/adminunsubscribeack.txt
+++ b/Mailman/templates/en/adminunsubscribeack.txt
diff --git a/templates/en/admlogin.html b/Mailman/templates/en/admlogin.html
index 03f763b95..03f763b95 100644
--- a/templates/en/admlogin.html
+++ b/Mailman/templates/en/admlogin.html
diff --git a/templates/en/approve.txt b/Mailman/templates/en/approve.txt
index dfb0dfb1e..dfb0dfb1e 100644
--- a/templates/en/approve.txt
+++ b/Mailman/templates/en/approve.txt
diff --git a/templates/en/archidxentry.html b/Mailman/templates/en/archidxentry.html
index f9bb57aab..f9bb57aab 100644
--- a/templates/en/archidxentry.html
+++ b/Mailman/templates/en/archidxentry.html
diff --git a/templates/en/archidxfoot.html b/Mailman/templates/en/archidxfoot.html
index 0b0a42075..0b0a42075 100644
--- a/templates/en/archidxfoot.html
+++ b/Mailman/templates/en/archidxfoot.html
diff --git a/templates/en/archidxhead.html b/Mailman/templates/en/archidxhead.html
index 4fdf4731d..4fdf4731d 100644
--- a/templates/en/archidxhead.html
+++ b/Mailman/templates/en/archidxhead.html
diff --git a/templates/en/archlistend.html b/Mailman/templates/en/archlistend.html
index 9bc052ddb..9bc052ddb 100644
--- a/templates/en/archlistend.html
+++ b/Mailman/templates/en/archlistend.html
diff --git a/templates/en/archliststart.html b/Mailman/templates/en/archliststart.html
index cdf5d17c4..cdf5d17c4 100644
--- a/templates/en/archliststart.html
+++ b/Mailman/templates/en/archliststart.html
diff --git a/templates/en/archtoc.html b/Mailman/templates/en/archtoc.html
index 6969d5152..6969d5152 100644
--- a/templates/en/archtoc.html
+++ b/Mailman/templates/en/archtoc.html
diff --git a/templates/en/archtocentry.html b/Mailman/templates/en/archtocentry.html
index 00cf9c47d..00cf9c47d 100644
--- a/templates/en/archtocentry.html
+++ b/Mailman/templates/en/archtocentry.html
diff --git a/templates/en/archtocnombox.html b/Mailman/templates/en/archtocnombox.html
index 0b1239ec8..0b1239ec8 100644
--- a/templates/en/archtocnombox.html
+++ b/Mailman/templates/en/archtocnombox.html
diff --git a/templates/en/article.html b/Mailman/templates/en/article.html
index 38dbc5543..38dbc5543 100644
--- a/templates/en/article.html
+++ b/Mailman/templates/en/article.html
diff --git a/templates/en/bounce.txt b/Mailman/templates/en/bounce.txt
index 8e02cc7a5..8e02cc7a5 100644
--- a/templates/en/bounce.txt
+++ b/Mailman/templates/en/bounce.txt
diff --git a/templates/en/checkdbs.txt b/Mailman/templates/en/checkdbs.txt
index d53925a4d..d53925a4d 100644
--- a/templates/en/checkdbs.txt
+++ b/Mailman/templates/en/checkdbs.txt
diff --git a/templates/en/convert.txt b/Mailman/templates/en/convert.txt
index ae17a79e0..ae17a79e0 100644
--- a/templates/en/convert.txt
+++ b/Mailman/templates/en/convert.txt
diff --git a/templates/en/cronpass.txt b/Mailman/templates/en/cronpass.txt
index 52ce5ea6c..52ce5ea6c 100644
--- a/templates/en/cronpass.txt
+++ b/Mailman/templates/en/cronpass.txt
diff --git a/templates/en/disabled.txt b/Mailman/templates/en/disabled.txt
index 54998a83b..54998a83b 100644
--- a/templates/en/disabled.txt
+++ b/Mailman/templates/en/disabled.txt
diff --git a/templates/en/emptyarchive.html b/Mailman/templates/en/emptyarchive.html
index 2f10766ce..2f10766ce 100644
--- a/templates/en/emptyarchive.html
+++ b/Mailman/templates/en/emptyarchive.html
diff --git a/templates/en/headfoot.html b/Mailman/templates/en/headfoot.html
index b2caf10f6..b2caf10f6 100644
--- a/templates/en/headfoot.html
+++ b/Mailman/templates/en/headfoot.html
diff --git a/templates/en/help.txt b/Mailman/templates/en/help.txt
index 654eda315..654eda315 100644
--- a/templates/en/help.txt
+++ b/Mailman/templates/en/help.txt
diff --git a/templates/en/invite.txt b/Mailman/templates/en/invite.txt
index 920c84213..920c84213 100644
--- a/templates/en/invite.txt
+++ b/Mailman/templates/en/invite.txt
diff --git a/templates/en/listinfo.html b/Mailman/templates/en/listinfo.html
index 0f0b5e614..0f0b5e614 100644
--- a/templates/en/listinfo.html
+++ b/Mailman/templates/en/listinfo.html
diff --git a/templates/en/masthead.txt b/Mailman/templates/en/masthead.txt
index 30c526ac9..30c526ac9 100644
--- a/templates/en/masthead.txt
+++ b/Mailman/templates/en/masthead.txt
diff --git a/templates/en/newlist.txt b/Mailman/templates/en/newlist.txt
index 3362887d8..3362887d8 100644
--- a/templates/en/newlist.txt
+++ b/Mailman/templates/en/newlist.txt
diff --git a/templates/en/nomoretoday.txt b/Mailman/templates/en/nomoretoday.txt
index 1019dce34..1019dce34 100644
--- a/templates/en/nomoretoday.txt
+++ b/Mailman/templates/en/nomoretoday.txt
diff --git a/templates/en/options.html b/Mailman/templates/en/options.html
index 8213b1f4b..8213b1f4b 100644
--- a/templates/en/options.html
+++ b/Mailman/templates/en/options.html
diff --git a/templates/en/postack.txt b/Mailman/templates/en/postack.txt
index 7402e4c2a..7402e4c2a 100644
--- a/templates/en/postack.txt
+++ b/Mailman/templates/en/postack.txt
diff --git a/templates/en/postauth.txt b/Mailman/templates/en/postauth.txt
index a10727716..a10727716 100644
--- a/templates/en/postauth.txt
+++ b/Mailman/templates/en/postauth.txt
diff --git a/templates/en/postheld.txt b/Mailman/templates/en/postheld.txt
index 877bb4050..877bb4050 100644
--- a/templates/en/postheld.txt
+++ b/Mailman/templates/en/postheld.txt
diff --git a/templates/en/private.html b/Mailman/templates/en/private.html
index 28ac9bfc8..28ac9bfc8 100644
--- a/templates/en/private.html
+++ b/Mailman/templates/en/private.html
diff --git a/templates/en/probe.txt b/Mailman/templates/en/probe.txt
index e0ae4ff57..e0ae4ff57 100644
--- a/templates/en/probe.txt
+++ b/Mailman/templates/en/probe.txt
diff --git a/templates/en/refuse.txt b/Mailman/templates/en/refuse.txt
index 9b6d9bb9c..9b6d9bb9c 100644
--- a/templates/en/refuse.txt
+++ b/Mailman/templates/en/refuse.txt
diff --git a/templates/en/roster.html b/Mailman/templates/en/roster.html
index aa5392ae4..aa5392ae4 100644
--- a/templates/en/roster.html
+++ b/Mailman/templates/en/roster.html
diff --git a/templates/en/subauth.txt b/Mailman/templates/en/subauth.txt
index 9c20c3dac..9c20c3dac 100644
--- a/templates/en/subauth.txt
+++ b/Mailman/templates/en/subauth.txt
diff --git a/templates/en/subscribe.html b/Mailman/templates/en/subscribe.html
index 20373877b..20373877b 100644
--- a/templates/en/subscribe.html
+++ b/Mailman/templates/en/subscribe.html
diff --git a/templates/en/subscribeack.txt b/Mailman/templates/en/subscribeack.txt
index fad433f28..fad433f28 100644
--- a/templates/en/subscribeack.txt
+++ b/Mailman/templates/en/subscribeack.txt
diff --git a/templates/en/unsub.txt b/Mailman/templates/en/unsub.txt
index b08f65bae..b08f65bae 100644
--- a/templates/en/unsub.txt
+++ b/Mailman/templates/en/unsub.txt
diff --git a/templates/en/unsubauth.txt b/Mailman/templates/en/unsubauth.txt
index 920f6c1b6..920f6c1b6 100644
--- a/templates/en/unsubauth.txt
+++ b/Mailman/templates/en/unsubauth.txt
diff --git a/templates/en/userpass.txt b/Mailman/templates/en/userpass.txt
index 2a53a846e..2a53a846e 100644
--- a/templates/en/userpass.txt
+++ b/Mailman/templates/en/userpass.txt
diff --git a/templates/en/verify.txt b/Mailman/templates/en/verify.txt
index 8e767f072..8e767f072 100644
--- a/templates/en/verify.txt
+++ b/Mailman/templates/en/verify.txt
diff --git a/Mailman/testing/bounces/__init__.py b/Mailman/testing/bounces/__init__.py
new file mode 100755
index 000000000..e69de29bb
--- /dev/null
+++ b/Mailman/testing/bounces/__init__.py
diff --git a/Mailman/testing/test_bounces.py b/Mailman/testing/test_bounces.py
index 54c82ff68..f09c671f4 100644
--- a/Mailman/testing/test_bounces.py
+++ b/Mailman/testing/test_bounces.py
@@ -17,14 +17,18 @@
"""Test the bounce detection modules."""
+from __future__ import with_statement
+
import os
import sys
import email
import unittest
-from paths import prefix
+import Mailman.testing.bounces
from Mailman.Bouncers.BouncerAPI import Stop
+MSGDIR = os.path.dirname(Mailman.testing.bounces.__file__)
+
class BounceTest(unittest.TestCase):
@@ -157,15 +161,13 @@ class BounceTest(unittest.TestCase):
)
def test_bounce(self):
- for modname, file, addrs in self.DATA:
+ for modname, filename, addrs in self.DATA:
module = 'Mailman.Bouncers.' + modname
__import__(module)
- fp = open(os.path.join(prefix, 'Mailman', 'testing', 'bounces',
- file))
- try:
+ # XXX Convert this tousing package resources
+ path = os.path.join(MSGDIR, filename)
+ with open(path) as fp:
msg = email.message_from_file(fp)
- finally:
- fp.close()
foundaddrs = sys.modules[module].process(msg)
# Some modules return None instead of [] for failure
if foundaddrs is None:
@@ -181,12 +183,8 @@ class BounceTest(unittest.TestCase):
def test_SMTP32_failure(self):
from Mailman.Bouncers import SMTP32
# This file has no X-Mailer: header
- fp = open(os.path.join(prefix, 'Mailman', 'testing', 'bounces',
- 'postfix_01.txt'))
- try:
+ with open(os.path.join(MSGDIR, 'postfix_01.txt')) as fp:
msg = email.message_from_file(fp)
- finally:
- fp.close()
self.failIf(msg['x-mailer'] is not None)
self.failIf(SMTP32.process(msg))
diff --git a/Mailman/testing/testing.cfg.in b/Mailman/testing/testing.cfg.in
index 2609ef5cf..074806cb1 100644
--- a/Mailman/testing/testing.cfg.in
+++ b/Mailman/testing/testing.cfg.in
@@ -10,4 +10,4 @@ MTA = None
add_domain('example.com', 'www.example.com')
-# bin/testall will add a SQLALCHEMY_ENGINE_URL below
+# bin/testall will add additional runtime configuration variables here.
diff --git a/setup.py b/setup.py
index cfb83bcdc..975b808a1 100644
--- a/setup.py
+++ b/setup.py
@@ -30,6 +30,22 @@ if sys.hexversion < 0x20500f0:
+# Ensure that all the .mo files are generated from the corresponding .po file.
+# This procedure needs to be made sane, probably when the language packs are
+# properly split out.
+import os
+import Mailman.messages
+start_dir = os.path.dirname(Mailman.messages.__file__)
+for dirpath, dirnames, filenames in os.walk(start_dir):
+ for filename in filenames:
+ po_file = os.path.join(dirpath, filename)
+ basename, ext = os.path.splitext(po_file)
+ mo_file = basename + '.mo'
+ if ext == '.po' and not os.path.exists(mo_file):
+ os.system('bin/msgfmt.py -o %s %s' % (mo_file, po_file))
+
+
+
scripts = ['%(script)s = Mailman.bin.%(script)s:main' % dict(script=script)
for script in (
'make_instance',
@@ -55,6 +71,7 @@ Any other spelling is incorrect.""",
url = 'http://www.list.org',
keywords = 'email',
packages = find_packages(),
+ include_package_data = True,
# Executable scripts
entry_points = {
'console_scripts': scripts,