Unverified Commit 681963de authored by Philipp Winter's avatar Philipp Winter
Browse files

Merge branch 'release-0.9.0'

parents c91498a3 330d21f1
Current core developers:
Matthew Finkel <sysrqb@torproject.org> 0x017DD169EA793BE2
Philipp Winter <phw@torproject.org>
Past core developers:
Matthew Finkel <sysrqb@torproject.org> 0x017DD169EA793BE2
Isis Lovecruft <isis@torproject.org> 0xA3ADB67A2CDB8B35
Nick Mathewson <nickm@torproject.org> 0x21194EBB165733EA
Aaron Gibson <aagbsn@torproject.org> 0x2C4B239DD876C9F6
......
Changes in version 0.9.0 - 2019-10-16
* FIXES https://bugs.torproject.org/26543
Implement a language switcher that allows users to override the locale
that BridgeDB automatically selects by inspecting the client's request
headers.
Changes in version 0.8.3 - 2019-10-03
* FIXES https://bugs.torproject.org/31903
......
......@@ -10,8 +10,6 @@
"""_langs.py - Storage for information on installed language support."""
RTL_LANGS = ('ar', 'he', 'fa', 'gu_IN', 'ku')
def get_langs():
"""Return a list of two-letter country codes of translations which were
......
......@@ -28,6 +28,7 @@ import random
import re
import time
import os
import operator
from functools import partial
......@@ -37,6 +38,8 @@ import mako.exceptions
from mako.template import Template
from mako.lookup import TemplateLookup
import babel.core
from twisted.internet import defer
from twisted.internet import reactor
from twisted.internet import task
......@@ -84,8 +87,8 @@ lookup = TemplateLookup(directories=[TEMPLATE_DIR],
collection_size=500)
logging.debug("Set template root to %s" % TEMPLATE_DIR)
#: Localisations which BridgeDB supports which should be rendered right-to-left.
rtl_langs = ('ar', 'he', 'fa', 'gu_IN', 'ku')
#: A list of supported language tuples. Use getSortedLangList() to read this variable.
supported_langs = []
# We use our metrics singleton to keep track of BridgeDB metrics such as
# "number of failed HTTPS bridge requests."
......@@ -156,6 +159,45 @@ def redirectMaliciousRequest(request):
return request
def getSortedLangList(rebuild=False):
"""
Build and return a list of tuples that contains all of BridgeDB's supported
languages, e.g.: [("az", "Azərbaycan"), ("ca", "Català"), ..., ].
:param rebuild bool: Force a rebuild of ``supported_langs`` if the argument
is set to ``True``. The default is ``False``.
:rtype: list
:returns: A list of tuples of the form (language-locale, language). The
list is sorted alphabetically by language. We use this list to
provide a language switcher in BridgeDB's web interface.
"""
# If we already compiled our languages, return them right away.
global supported_langs
if supported_langs and not rebuild:
return supported_langs
logging.debug("Building supported languages for language switcher.")
langDict = {}
for l in translations.getSupportedLangs():
# We don't support 'en_GB', and 'en' and 'en_US' are the same. 'zh_HK'
# is very similar to 'zh_TW' and we also lack translators for it, so we
# drop the locale: <https://bugs.torproject.org/26543#comment:17>
if l in ("en_GB", "en_US", "zh_HK"):
continue
try:
langDict[l] = "%s" % (babel.core.Locale.parse(l).display_name.capitalize())
except Exception as err:
logging.warning("Failed to create language switcher option for %s: %s" % (l, err))
# Sort languages alphabetically.
supported_langs = sorted(langDict.items(), key=operator.itemgetter(1))
return supported_langs
class MaliciousRequest(Exception):
"""Raised when we received a possibly malicious request."""
......@@ -345,7 +387,11 @@ class TranslatedTemplateResource(CustomErrorHandlingResource, CSPResource):
langs = translations.getLocaleFromHTTPRequest(request)
rtl = translations.usingRTLLang(langs)
template = lookup.get_template(self.template)
rendered = template.render(strings, rtl=rtl, lang=langs[0])
rendered = template.render(strings,
getSortedLangList(),
rtl=rtl,
lang=langs[0],
langOverride=translations.isLangOverridden(request))
except Exception as err: # pragma: no cover
rendered = replaceErrorPage(request, err)
request.setHeader("Content-Type", "text/html; charset=utf-8")
......@@ -469,8 +515,10 @@ class CaptchaProtectedResource(CustomErrorHandlingResource, CSPResource):
imgstr = 'data:image/jpeg;base64,%s' % base64.b64encode(image)
template = lookup.get_template('captcha.html')
rendered = template.render(strings,
getSortedLangList(),
rtl=rtl,
lang=langs[0],
langOverride=translations.isLangOverridden(request),
imgstr=imgstr,
challenge_field=challenge)
except Exception as err:
......@@ -994,8 +1042,10 @@ class BridgesResource(CustomErrorHandlingResource, CSPResource):
rtl = translations.usingRTLLang(langs)
template = lookup.get_template('bridges.html')
rendered = template.render(strings,
getSortedLangList(),
rtl=rtl,
lang=langs[0],
langOverride=translations.isLangOverridden(request),
answer=bridgeLines,
qrcode=qrcode)
except Exception as err:
......
......@@ -439,3 +439,9 @@ div.bridge-lines.-webkit-scrollbar-thumb.horizontal{
}
@media (min-width: 1px) and (max-width: 480px), handheld {
}
.dropdown:hover .dropdown-menu {
display: block;
height: 350px;
overflow: auto;
}
## -*- coding: utf-8 -*-
<%namespace name="base" file="base.html" inheritable="True"/>
<%page args="strings, rtl=False, lang='en', **kwargs"/>
<%page args="strings, langs, rtl=False, lang='en', langOverride=False, **kwargs"/>
<!DOCTYPE html>
<html lang="${lang}">
......@@ -37,6 +37,19 @@
<a class="navbar-brand" href="../">BridgeDB</a>
</div>
<ul class="nav navbar-nav pull-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" role="button">
${_("Language")}<span class="caret"></span>
</a>
<ul class="dropdown-menu">
% for tuple in langs:
<li>
<a href="?lang=${tuple[0]}">${tuple[1]} (${tuple[0]})</a>
</li>
% endfor
</ul>
</li>
<li>
<a href="https://www.torproject.org">The Tor Project</a>
</li>
......@@ -44,7 +57,7 @@
</div>
</div>
${next.body(strings, rtl=rtl, lang=lang, **kwargs)}
${next.body(strings, langs, rtl=rtl, lang=lang, langOverride=langOverride, **kwargs)}
<div class="faq">
<div class="row-fluid marketing">
......
## -*- coding: utf-8 -*-
<%inherit file="base.html"/>
<%page args="strings, rtl=False, lang='en', answer=0, qrcode=0, **kwargs"/>
<%page args="strings, langs, rtl=False, lang='en', langOverride=False, answer=0, qrcode=0, **kwargs"/>
</div>
......@@ -129,9 +129,15 @@ ${_("""Uh oh, spaghettios!""")}
</p>
<p>
${_("""There currently aren't any bridges available...""")}
${_(""" Perhaps you should try %s going back %s and choosing a""" \
""" different bridge type!""") % \
("""<a class="alert-link" href="options">""", """</a>""")}
% if langOverride:
${_(""" Perhaps you should try %s going back %s and choosing a""" \
""" different bridge type!""") % \
("""<a class="alert-link" href="options?lang="""+lang+""">""", """</a>""")}
% else:
${_(""" Perhaps you should try %s going back %s and choosing a""" \
""" different bridge type!""") % \
("""<a class="alert-link" href="options">""", """</a>""")}
% endif
</p>
</div>
</div>
......
## -*- coding: utf-8 -*-
<%inherit file="base.html"/>
<%page args="strings, rtl=False, lang='en', imgstr=0, captcha_challenge=0, **kwargs"/>
<%page args="strings, langs, rtl=False, lang='en', langOverride=False, imgstr=0, captcha_challenge=0, **kwargs"/>
<div class="container-narrow" id="captcha-submission-container">
<div class="container-fluid container-fluid-inner-5">
......
## -*- coding: utf-8 -*-
<%inherit file="base.html"/>
<%page args="strings, rtl=False, lang='en', **kwargs"/>
<%page args="strings, langs, rtl=False, lang='en', langOverride=False, **kwargs"/>
<br />
......
## -*- coding: utf-8 -*-
<%inherit file="base.html"/>
<%page args="strings, rtl=False, lang='en', **kwargs"/>
<%page args="strings, langs, rtl=False, lang='en', langOverride=False, **kwargs"/>
<div class="main-steps">
<div class="step row" id="step-1">
......@@ -24,7 +24,11 @@
<span class="step-title">
${_("Step %s2%s") % ("""<u>""", """</u>""")}</span>
<span class="step-text">
% if langOverride:
${_("Get %s bridges %s") % ("""<a href="/options?lang="""+lang+"""" accesskey="2">""", "</a>")}</span>
% else:
${_("Get %s bridges %s") % ("""<a href="/options" accesskey="2">""", "</a>")}</span>
% endif
</span>
</div>
</div>
......@@ -35,9 +39,15 @@
<span class="step-title">
${_("Step %s3%s") % ("""<u>""", """</u>""")}</span>
<span class="step-text">
% if langOverride:
${_("""Now %s add the bridges to Tor Browser %s""") % \
("""<a href="/howto?lang="""+lang+"""" accesskey="3">""",
"""</a>""")}</span>
% else:
${_("""Now %s add the bridges to Tor Browser %s""") % \
("""<a href="/howto" accesskey="3">""",
"""</a>""")}</span>
% endif
</span>
</div>
</div>
## -*- coding: utf-8 -*-
<%inherit file="base.html"/>
<%page args="strings, rtl=False, lang='en', **kwargs"/>
<%page args="strings, langs, rtl=False, lang='en', langOverride=False, **kwargs"/>
<div class="container-fluid container-fluid-outer-96">
<!--<div class="container-fluid step-semi-transparent">-->
......@@ -26,7 +26,11 @@
<div class="container-fluid container-fluid-outer">
<div class="container-fluid-inner-5">
<p class="bs-component">
% if langOverride:
<a href="./bridges?lang=${lang}">
% else:
<a href="./bridges">
% endif
<button class="btn btn-success btn-lg btn-block"
id="just-give-me-bridges-btn"
type="button"
......@@ -54,6 +58,9 @@
<!-- BEGIN bridge options selection form -->
<form class="form-horizontal" id="advancedOptions" action="bridges" method="GET">
<fieldset>
% if langOverride:
<input type="hidden" id="lang" name="lang" value="${lang}">
% endif
<div class="container-fluid" id="instructions">
<legend id="advanced-options-legend">
<br />
......
......@@ -3,7 +3,6 @@
# This file is distributed under the same license as the bridgedb project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2019.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: bridgedb 0.8.0+15.gd9d886f.dirty\n"
......
......@@ -27,6 +27,7 @@ from twisted.trial import unittest
from twisted.web.resource import Resource
from twisted.web.test import requesthelper
from bridgedb import translations
from bridgedb.distributors.https import server
from bridgedb.schedule import ScheduledInterval
......@@ -43,6 +44,18 @@ logging.disable(50)
#server.logging.getLogger().setLevel(10)
class GetSortedLangListTests(unittest.TestCase):
"""Tests for :func:`bridgedb.distributors.https.server.getSortedLangList`."""
def test_getSortedLangList(self):
"""getSortedLangList should return a list of tuples containing sorted
locales and languages."""
origFunc = translations.getSupportedLangs
translations.getSupportedLangs = lambda: ["en", "de"]
l = server.getSortedLangList(rebuild=True)
self.assertEqual(l, [("de", u"Deutsch"), ("en", u"English")])
translations.getSupportedLangs = origFunc
class ReplaceErrorPageTests(unittest.TestCase):
"""Tests for :func:`bridgedb.distributors.https.server.replaceErrorPage`."""
......
......@@ -76,3 +76,8 @@ class TranslationsMiscTests(unittest.TestCase):
emailAddr = 'bridges+ar@torproject.org'
replyLocale = translations.getLocaleFromPlusAddr(emailAddr)
self.assertEqual('ar', replyLocale)
def test_usingRTLLang(self):
self.assertFalse(translations.usingRTLLang(['foo_BAR']))
self.assertFalse(translations.usingRTLLang(['en']))
self.assertTrue(translations.usingRTLLang(['fa']))
......@@ -12,6 +12,8 @@ import logging
import os
import re
import babel.core
from bridgedb import _langs
from bridgedb import safelog
from bridgedb.parse import headers
......@@ -20,6 +22,28 @@ from bridgedb.parse import headers
TRANSLATIONS_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'i18n')
def isLangOverridden(request):
"""
Return True if the `lang' HTTP GET argument is set in the given request.
:type request: :api:`twisted.web.server.Request`
:param request: An incoming request from a client.
:rtype: bool
:returns: ``True`` if the given request has a `lang` argument and ``False``
otherwise.
"""
return request.args.get("lang", [None])[0] is not None
def getSupportedLangs():
"""Return all supported languages.
:rtype: set
:returns: A set of language locales, e.g.: set(['el', 'eo', ..., ]).
"""
return _langs.get_langs()
def getFirstSupportedLang(langs):
"""Return the first language in **langs** that we support.
......@@ -117,6 +141,10 @@ def usingRTLLang(langs):
otherwise.
"""
lang = getFirstSupportedLang(langs)
if lang in _langs.RTL_LANGS:
return True
return False
rtl = False
try:
rtl = babel.core.Locale.parse(lang).text_direction == "rtl"
except ValueError as err:
logging.warning("Couldn't parse locale %s: %s" % (lang, err))
return rtl
......@@ -369,8 +369,8 @@ setuptools.setup(
description='Backend systems for distribution of Tor bridge relays',
author='Nick Mathewson',
author_email='nickm at torproject dot org',
maintainer='Matthew Finkel',
maintainer_email='sysrqb@torproject.org 0xCB8FC772D1AA1D30',
maintainer='Philipp Winter',
maintainer_email='phw@torproject.org',
url='https://www.torproject.org',
download_url='https://gitweb.torproject.org/bridgedb.git',
package_dir={'bridgedb': 'bridgedb'},
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment