create a liberapay.i18n submodule

This commit is contained in:
Changaco 2018-11-21 14:39:57 +01:00
parent 5ee0485f61
commit 3f63e9b68e
42 changed files with 157 additions and 150 deletions

View File

@ -1,7 +1,7 @@
[extractors]
jinja2_custom = liberapay.utils.i18n:extract_jinja2_custom
python_custom = liberapay.utils.i18n:extract_python_custom
spt = liberapay.utils.i18n:extract_spt
jinja2_custom = liberapay.i18n.extract:extract_jinja2_custom
python_custom = liberapay.i18n.extract:extract_python_custom
spt = liberapay.i18n.extract:extract_spt
[python_custom: **.py]
[jinja2_custom: **.html]

View File

@ -89,7 +89,7 @@ pytest-profiling: env
_i18n_extract: env
@PYTHONPATH=. $(env_bin)/pybabel extract -F .babel_extract --no-wrap -o i18n/core.pot --sort-by-file _i18n_warning.html emails liberapay templates www *.spt
@PYTHONPATH=. $(env_bin)/python liberapay/utils/i18n.py po-reflag i18n/core.pot
@PYTHONPATH=. $(env_bin)/python cli/po-tools.py reflag i18n/core.pot
@for f in i18n/*/*.po; do \
$(env_bin)/pybabel update -i i18n/core.pot -l $$(basename -s '.po' "$$f") -o "$$f" --ignore-obsolete --no-fuzzy-matching --no-wrap; \
done

31
cli/po-tools.py Normal file
View File

@ -0,0 +1,31 @@
from __future__ import print_function, unicode_literals
import sys
from babel.messages.pofile import read_po, write_po
if sys.argv[1] == 'reflag':
# This adds the `python-brace-format` flag to messages that contain braces
# https://github.com/python-babel/babel/issues/333
pot_path = sys.argv[2]
print('rewriting PO template file', pot_path)
# read PO file
with open(pot_path, 'rb') as pot:
catalog = read_po(pot)
# tweak message flags
for m in catalog:
msg = m.id
contains_brace = any(
'{' in s for s in (msg if isinstance(msg, tuple) else (msg,))
)
if contains_brace:
m.flags.add('python-brace-format')
m.flags.discard('python-format')
# write back
with open(pot_path, 'wb') as pot:
write_po(pot, catalog, width=0)
else:
print("unknown command")
raise SystemExit(1)

View File

@ -3,7 +3,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from pando.http import status_strings
from liberapay.exceptions import LazyResponse
from liberapay.utils.i18n import HTTP_ERRORS
from liberapay.i18n.base import HTTP_ERRORS
[----------------------------------------]

View File

@ -20,9 +20,9 @@ import requests
from liberapay import constants
from liberapay.billing.transactions import Money, transfer
from liberapay.exceptions import NegativeBalance
from liberapay.i18n.currencies import MoneyBasket
from liberapay.models.participant import Participant
from liberapay.utils import NS, group_by
from liberapay.utils.currencies import MoneyBasket
from liberapay.website import website

View File

@ -24,7 +24,7 @@ class LazyResponse(Response):
def render_in_english(self):
f = self.lazy_body
fake_state = {}
from liberapay.utils.i18n import LOCALE_EN, add_helpers_to_context
from liberapay.i18n.base import LOCALE_EN, add_helpers_to_context
add_helpers_to_context(fake_state, LOCALE_EN)
return f(*resolve_dependencies(f, fake_state).as_args)

View File

View File

@ -4,29 +4,23 @@ from __future__ import print_function, unicode_literals
from collections import namedtuple, OrderedDict
from datetime import date, datetime, timedelta
from decimal import Decimal, InvalidOperation
from hashlib import md5
from io import BytesIO
import re
from unicodedata import combining, normalize
from six import text_type
from aspen.simplates.pagination import parse_specline, split_and_escape
import babel.core
from babel.dates import format_date, format_datetime, format_timedelta
from babel.messages.extract import extract_python
from babel.messages.pofile import Catalog
from babel.numbers import (
format_currency, format_decimal, format_number, format_percent,
)
import jinja2.ext
from markupsafe import Markup
from pando.utils import utcnow
from liberapay.constants import CURRENCIES, D_MAX
from liberapay.exceptions import AmbiguousNumber, InvalidNumber
from liberapay.utils.currencies import Money, MoneyBasket
from liberapay.website import website
from ..constants import CURRENCIES, D_MAX
from ..exceptions import AmbiguousNumber, InvalidNumber
from ..website import website
from .currencies import Money, MoneyBasket
def LegacyMoney(o):
@ -267,30 +261,6 @@ HTTP_ERRORS = {
del _
ternary_re = re.compile(r'^(.+?) *\? *(.+?) *: *(.+?)$')
and_re = re.compile(r' *&& *')
or_re = re.compile(r' *\|\| *')
def strip_parentheses(s):
s = s.strip()
if s[:1] == '(' and s[-1:] == ')':
s = s[1:-1].strip()
return s
def ternary_sub(m):
g1, g2, g3 = m.groups()
return '%s if %s else %s' % (g2, g1, ternary_re.sub(ternary_sub, strip_parentheses(g3)))
def get_function_from_rule(rule):
rule = ternary_re.sub(ternary_sub, strip_parentheses(rule))
rule = and_re.sub(' and ', rule)
rule = or_re.sub(' or ', rule)
return eval('lambda n: ' + rule, {'__builtins__': {}})
def _decode(o):
return o.decode('ascii') if isinstance(o, bytes) else o
@ -501,70 +471,3 @@ def add_currency_to_state(request, user):
return {'currency': user.main_currency}
else:
return {'currency': CURRENCIES_MAP.get(request.country) or 'EUR'}
def extract_custom(extractor, *args, **kw):
for match in extractor(*args, **kw):
msg = match[2]
if isinstance(msg, tuple) and msg[0] == '':
unused = "<unused singular (hash=%s)>" % md5(msg[1]).hexdigest()
msg = (unused, msg[1], msg[2])
match = (match[0], match[1], msg, match[3])
yield match
def extract_jinja2_custom(*args, **kw):
return extract_custom(jinja2.ext.babel_extract, *args, **kw)
def extract_python_custom(*args, **kw):
return extract_custom(extract_python, *args, **kw)
def extract_spt(fileobj, *args, **kw):
pages = list(split_and_escape(fileobj.read().decode('utf8')))
npages = len(pages)
for i, page in enumerate(pages, 1):
f = BytesIO(b'\n' * page.offset + page.content.encode('utf8'))
content_type, renderer = parse_specline(page.header)
extractor = None
python_page = i < 3 and i < npages and not page.header
json_page = renderer == 'json_dump'
if python_page or json_page:
extractor = extract_python_custom
else:
extractor = extract_jinja2_custom
if extractor:
for match in extractor(f, *args, **kw):
yield match
if __name__ == '__main__':
import sys
from babel.messages.pofile import read_po, write_po
if sys.argv[1] == 'po-reflag':
# This adds the `python-brace-format` flag to messages that contain braces
# https://github.com/python-babel/babel/issues/333
pot_path = sys.argv[2]
print('rewriting PO template file', pot_path)
# read PO file
with open(pot_path, 'rb') as pot:
catalog = read_po(pot)
# tweak message flags
for m in catalog:
msg = m.id
contains_brace = any(
'{' in s for s in (msg if isinstance(msg, tuple) else (msg,))
)
if contains_brace:
m.flags.add('python-brace-format')
m.flags.discard('python-format')
# write back
with open(pot_path, 'wb') as pot:
write_po(pot, catalog, width=0)
else:
print("unknown command")
raise SystemExit(1)

View File

@ -10,8 +10,8 @@ from mangopay.utils import Money
import requests
import xmltodict
from liberapay.constants import CURRENCIES, D_CENT, D_ZERO
from liberapay.website import website
from ..constants import CURRENCIES, D_CENT, D_ZERO
from ..website import website
def _convert(self, c, rounding=ROUND_HALF_UP):

44
liberapay/i18n/extract.py Normal file
View File

@ -0,0 +1,44 @@
from __future__ import division, print_function, unicode_literals
from hashlib import md5
from io import BytesIO
from aspen.simplates.pagination import parse_specline, split_and_escape
from babel.messages.extract import extract_python
import jinja2.ext
def extract_custom(extractor, *args, **kw):
for match in extractor(*args, **kw):
msg = match[2]
if isinstance(msg, tuple) and msg[0] == '':
unused = "<unused singular (hash=%s)>" % md5(msg[1]).hexdigest()
msg = (unused, msg[1], msg[2])
match = (match[0], match[1], msg, match[3])
yield match
def extract_jinja2_custom(*args, **kw):
return extract_custom(jinja2.ext.babel_extract, *args, **kw)
def extract_python_custom(*args, **kw):
return extract_custom(extract_python, *args, **kw)
def extract_spt(fileobj, *args, **kw):
pages = list(split_and_escape(fileobj.read().decode('utf8')))
npages = len(pages)
for i, page in enumerate(pages, 1):
f = BytesIO(b'\n' * page.offset + page.content.encode('utf8'))
content_type, renderer = parse_specline(page.header)
extractor = None
python_page = i < 3 and i < npages and not page.header
json_page = renderer == 'json_dump'
if python_page or json_page:
extractor = extract_python_custom
else:
extractor = extract_jinja2_custom
if extractor:
for match in extractor(f, *args, **kw):
yield match

View File

@ -0,0 +1,27 @@
from __future__ import print_function, unicode_literals
import re
ternary_re = re.compile(r'^(.+?) *\? *(.+?) *: *(.+?)$')
and_re = re.compile(r' *&& *')
or_re = re.compile(r' *\|\| *')
def strip_parentheses(s):
s = s.strip()
if s[:1] == '(' and s[-1:] == ')':
s = s[1:-1].strip()
return s
def ternary_sub(m):
g1, g2, g3 = m.groups()
return '%s if %s else %s' % (g2, g1, ternary_re.sub(ternary_sub, strip_parentheses(g3)))
def get_function_from_rule(rule):
rule = ternary_re.sub(ternary_sub, strip_parentheses(rule))
rule = and_re.sub(' and ', rule)
rule = or_re.sub(' or ', rule)
return eval('lambda n: ' + rule, {'__builtins__': {}})

View File

@ -21,15 +21,16 @@ from liberapay import utils, wireup
from liberapay.billing.payday import Payday, create_payday_issue
from liberapay.cron import Cron, Daily, Weekly
from liberapay.exceptions import PayinMethodIsUnavailable, PayinsAreDisabled
from liberapay.i18n.base import add_currency_to_state, set_up_i18n
from liberapay.i18n.currencies import Money, MoneyBasket, fetch_currency_exchange_rates
from liberapay.models.account_elsewhere import refetch_elsewhere_data
from liberapay.models.community import Community
from liberapay.models.participant import Participant, clean_up_closed_accounts
from liberapay.models.repository import refetch_repos
from liberapay.security import authentication, csrf, set_default_security_headers
from liberapay.utils import (
b64decode_s, b64encode_s, erase_cookie, http_caching, i18n, set_cookie, urlquote,
b64decode_s, b64encode_s, erase_cookie, http_caching, set_cookie, urlquote,
)
from liberapay.utils.currencies import Money, MoneyBasket, fetch_currency_exchange_rates
from liberapay.utils.emails import handle_email_bounces
from liberapay.utils.state_chain import (
attach_environ_to_request, create_response_object, reject_requests_bypassing_proxy,
@ -141,12 +142,12 @@ algorithm.functions = [
canonize,
set_default_security_headers,
i18n.set_up_i18n,
set_up_i18n,
insert_constants,
authentication.start_user_as_anon,
csrf.reject_forgeries,
authentication.authenticate_user_if_possible,
i18n.add_currency_to_state,
add_currency_to_state,
_dispatch_path_to_filesystem,
algorithm['handle_dispatch_exception'],

View File

@ -10,8 +10,8 @@ from statistics import median
import mangopay
from liberapay.constants import TAKE_THROTTLING_THRESHOLD
from liberapay.i18n.currencies import Money, MoneyBasket
from liberapay.utils import NS, group_by
from liberapay.utils.currencies import Money, MoneyBasket
class MemberLimitReached(Exception): pass

View File

@ -65,15 +65,16 @@ from liberapay.exceptions import (
ValueContainsForbiddenCharacters,
VerificationEmailAlreadySent,
)
from liberapay.i18n import base as i18n
from liberapay.i18n.currencies import Money, MoneyBasket
from liberapay.models._mixin_team import MixinTeam
from liberapay.models.account_elsewhere import AccountElsewhere
from liberapay.models.community import Community
from liberapay.security.crypto import constant_time_compare
from liberapay.utils import (
NS, deserialize, erase_cookie, serialize, set_cookie, urlquote,
emails, i18n, markdown,
emails, markdown,
)
from liberapay.utils.currencies import Money, MoneyBasket
from liberapay.utils.emails import check_email_blacklist, normalize_email_address
from liberapay.website import website

View File

@ -4,8 +4,8 @@ from ..exceptions import (
AccountSuspended, MissingPaymentAccount, RecipientAccountSuspended,
NoSelfTipping,
)
from ..i18n.currencies import Money
from ..models.participant import Participant
from ..utils.currencies import Money
def prepare_payin(db, payer, amount, route):

View File

@ -5,7 +5,7 @@ import logging
import requests
from ..exceptions import PaymentError
from ..utils.currencies import Money
from ..i18n.currencies import Money
from ..website import website
from .common import update_payin, update_payin_transfer

View File

@ -9,8 +9,8 @@ from six import text_type
import stripe
import stripe.error
from ..i18n.currencies import Money
from ..models.exchange_route import ExchangeRoute
from ..utils.currencies import Money
from ..website import website
from .common import update_payin, update_payin_transfer

View File

@ -19,6 +19,7 @@ from liberapay.billing.transactions import (
from liberapay.constants import SESSION
from liberapay.elsewhere._base import UserInfo
from liberapay.exceptions import MissingPaymentAccount
from liberapay.i18n.currencies import Money
from liberapay.main import website
from liberapay.models.account_elsewhere import AccountElsewhere
from liberapay.models.exchange_route import ExchangeRoute
@ -30,7 +31,6 @@ from liberapay.payin.common import (
)
from liberapay.security.csrf import CSRF_TOKEN
from liberapay.testing.vcr import use_cassette
from liberapay.utils.currencies import Money
TOP = realpath(join(dirname(dirname(__file__)), '..'))

View File

@ -8,10 +8,10 @@ from mangopay.resources import (
import mock
import requests
from liberapay.i18n.currencies import Money
from liberapay.models.exchange_route import ExchangeRoute
from liberapay.testing import Harness
from liberapay.testing.vcr import use_cassette
from liberapay.utils.currencies import Money
class MangopayHarness(Harness):

View File

@ -27,7 +27,7 @@ from liberapay.elsewhere._paginators import _modify_query
from liberapay.elsewhere._utils import urlquote
from liberapay.exceptions import AccountSuspended, AuthRequired, LoginRequired, InvalidNumber
from liberapay.models.community import Community
from liberapay.utils.i18n import LOCALE_EN, add_helpers_to_context
from liberapay.i18n.base import LOCALE_EN, add_helpers_to_context
from liberapay.website import website

View File

@ -11,9 +11,9 @@ from liberapay.billing.transactions import (
record_exchange_result, lock_bundles, _record_transfer_result
)
from liberapay.constants import D_CENT, DONATION_LIMITS, PERIOD_CONVERSION_RATES
from liberapay.i18n.currencies import Money, MoneyBasket
from liberapay.models.exchange_route import ExchangeRoute
from liberapay.models import community
from liberapay.utils.currencies import Money, MoneyBasket
DONATION_PERIODS = tuple(PERIOD_CONVERSION_RATES.keys())

View File

@ -6,8 +6,8 @@ from itertools import chain
from pando import Response
from pando.utils import utc, utcnow
from ..i18n.currencies import MoneyBasket
from ..website import website
from .currencies import MoneyBasket
from . import group_by

View File

@ -32,6 +32,11 @@ from liberapay import elsewhere
import liberapay.billing.payday
import liberapay.billing.watcher
from liberapay.exceptions import NeedDatabase
from liberapay.i18n.base import (
ALIASES, ALIASES_R, COUNTRIES, LANGUAGES_2, LOCALES, Locale, make_sorted_dict
)
from liberapay.i18n.currencies import Money, MoneyBasket, get_currency_exchange_rates
from liberapay.i18n.plural_rules import get_function_from_rule
from liberapay.models.account_elsewhere import _AccountElsewhere, AccountElsewhere
from liberapay.models.community import _Community, Community
from liberapay.models.exchange_route import ExchangeRoute
@ -39,13 +44,8 @@ from liberapay.models.participant import Participant
from liberapay.models.repository import Repository
from liberapay.models import DB
from liberapay.utils import find_files, markdown, mkdir_p, resolve, urlquote
from liberapay.utils.currencies import Money, MoneyBasket, get_currency_exchange_rates
from liberapay.utils.emails import compile_email_spt
from liberapay.utils.http_caching import asset_etag
from liberapay.utils.i18n import (
ALIASES, ALIASES_R, COUNTRIES, LANGUAGES_2, LOCALES, Locale,
get_function_from_rule, make_sorted_dict
)
from liberapay.utils.query_cache import QueryCache
from liberapay.version import get_version
from liberapay.website import CustomUndefined

View File

@ -7,10 +7,10 @@ from mock import patch
from liberapay.billing.transactions import swap_currencies, Transfer
from liberapay.constants import CURRENCIES
from liberapay.exceptions import NegativeBalance, TransferError
from liberapay.i18n.currencies import Money, MoneyBasket
from liberapay.payin.stripe import int_to_Money, Money_to_int
from liberapay.testing import EUR, JPY, USD, Harness, Foobar
from liberapay.testing.mangopay import FakeTransfersHarness, MangopayHarness, fake_transfer
from liberapay.utils.currencies import Money, MoneyBasket
class TestCurrencies(Harness):

View File

@ -3,8 +3,8 @@
from __future__ import absolute_import, division, print_function, unicode_literals
from liberapay.exceptions import AmbiguousNumber, InvalidNumber
from liberapay.i18n.base import LOCALE_EN, Money
from liberapay.testing import Harness
from liberapay.utils.i18n import LOCALE_EN, Money
class Tests(Harness):

View File

@ -19,9 +19,9 @@ from liberapay.exceptions import (
UsernameIsEmpty,
UsernameTooLong,
)
from liberapay.i18n.currencies import Money
from liberapay.models.participant import NeedConfirmation, Participant
from liberapay.testing import EUR, USD, Harness
from liberapay.utils.currencies import Money
class TestNeedConfirmation(Harness):

View File

@ -6,12 +6,12 @@ import mock
from liberapay.billing.payday import create_payday_issue, main, NoPayday, Payday
from liberapay.billing.transactions import create_debt
from liberapay.i18n.currencies import MoneyBasket
from liberapay.models.exchange_route import ExchangeRoute
from liberapay.models.participant import Participant
from liberapay.testing import EUR, USD, Foobar
from liberapay.testing.mangopay import FakeTransfersHarness, MangopayHarness
from liberapay.testing.emails import EmailHarness
from liberapay.utils.currencies import MoneyBasket
class TestPayday(EmailHarness, FakeTransfersHarness, MangopayHarness):

View File

@ -10,10 +10,10 @@ from six.moves.http_cookies import SimpleCookie
from babel.messages.catalog import Message
from liberapay.constants import SESSION
from liberapay.i18n.base import LOCALES
from liberapay.models.participant import Participant
from liberapay.testing import postgres_readonly
from liberapay.testing.emails import EmailHarness
from liberapay.utils.i18n import LOCALES
password = 'password'

View File

@ -3,9 +3,9 @@ from __future__ import unicode_literals
from psycopg2 import InternalError
from liberapay.billing.payday import Payday
from liberapay.testing import EUR, USD, Harness
from liberapay.i18n.currencies import MoneyBasket
from liberapay.models.participant import Participant
from liberapay.utils.currencies import MoneyBasket
from liberapay.testing import EUR, USD, Harness
TEAM = 'A Team'

View File

@ -10,8 +10,8 @@ this to mean, "no chart."
"""
from liberapay.i18n.currencies import Money
from liberapay.utils import get_participant
from liberapay.utils.currencies import Money
[---]

View File

@ -1,7 +1,7 @@
# coding: utf8
from liberapay.i18n.base import LANGUAGES_2, get_lang_options
from liberapay.utils import excerpt_intro, form_post_success, get_participant, markdown
from liberapay.utils.i18n import LANGUAGES_2, get_lang_options
[---]
participant = get_participant(state, restrict=True, allow_member=True)

View File

@ -9,8 +9,8 @@ from liberapay.billing.fees import upcharge_bank_wire
from liberapay.billing.transactions import cancel_bank_wire_payin, payin_bank_wire
from liberapay.constants import EVENTS, PAYIN_BANK_WIRE_MIN
from liberapay.exceptions import InvalidNumber
from liberapay.i18n.currencies import Money
from liberapay.utils import b64decode_s, get_participant
from liberapay.utils.i18n import Money
NOTIF_BIT_FAIL = EVENTS['payin_failed'].bit
NOTIF_BIT_SUCC = EVENTS['payin_succeeded'].bit

View File

@ -11,9 +11,9 @@ from liberapay.billing.transactions import (
)
from liberapay.constants import FEE_PAYIN_CARD, PAYIN_CARD_MIN
from liberapay.exceptions import InvalidNumber, Redirect
from liberapay.i18n.currencies import Money
from liberapay.models.exchange_route import ExchangeRoute
from liberapay.utils import b64decode_s, check_address, get_participant, is_card_expired
from liberapay.utils.i18n import Money
[---]

View File

@ -12,11 +12,11 @@ from liberapay.billing.transactions import (
)
from liberapay.constants import FEE_PAYIN_DIRECT_DEBIT, PAYIN_DIRECT_DEBIT_MIN
from liberapay.exceptions import InvalidNumber
from liberapay.i18n.currencies import Money
from liberapay.models.exchange_route import ExchangeRoute
from liberapay.utils import (
b64decode_s, get_owner_address, get_owner_name, get_participant, obfuscate
)
from liberapay.utils.i18n import Money
# https://docs.mangopay.com/endpoints/v2.01/mandates#e231_create-a-mandate
MANDATE_LANGS = {'en', 'fr', 'nl', 'de', 'es', 'it', 'pl'}

View File

@ -1,7 +1,7 @@
from markupsafe import Markup
from liberapay.i18n.base import Wrap
from liberapay.utils import get_participant
from liberapay.utils.i18n import Wrap
span_open, span_close, br = Markup('<br><span style="font-size: 125%%">'), Markup('</span>'), Markup('<br>')

View File

@ -2,7 +2,7 @@ import os
from markupsafe import Markup
from liberapay.utils.i18n import getdoc
from liberapay.i18n.base import getdoc
MANGOPAY_TERMS = Markup().join(
Markup('<li><a href="%s">%s</a></li>') % (website.asset('mangopay/'+f), f)

View File

@ -1,7 +1,7 @@
# coding: utf8
from __future__ import division, print_function, unicode_literals
from liberapay.utils.currencies import Money, MoneyBasket
from liberapay.i18n.currencies import Money, MoneyBasket
db = website.db
db_qc5 = website.db_qc5

View File

@ -1,5 +1,5 @@
from liberapay.i18n.base import LANGUAGES_2, get_lang_options
from liberapay.utils import get_community
from liberapay.utils.i18n import LANGUAGES_2, get_lang_options
[---]

View File

@ -1,6 +1,6 @@
from liberapay.exceptions import AuthRequired
from liberapay.i18n.base import LANGUAGES_2, get_lang_options
from liberapay.models.community import name_maxlength, normalize
from liberapay.utils.i18n import LANGUAGES_2, get_lang_options
[---]

View File

@ -2,8 +2,8 @@
from math import ceil
from liberapay.i18n.base import LANGUAGES_2
from liberapay.models.participant import Participant
from liberapay.utils.i18n import LANGUAGES_2
query_cache = website.db_qc5

View File

@ -4,9 +4,9 @@ import requests
from liberapay.elsewhere._base import UserInfo
from liberapay.exceptions import LoginRequired, UsernameAlreadyTaken
from liberapay.i18n.base import SEARCH_CONFS
from liberapay.models.account_elsewhere import AccountElsewhere
from liberapay.models.participant import NeedConfirmation, Participant
from liberapay.utils.i18n import SEARCH_CONFS
pledge_platforms = {'github', 'twitter'}

View File

@ -1,5 +1,5 @@
from liberapay.i18n.base import LANGUAGES_2, SEARCH_CONFS, strip_accents
from liberapay.utils import markdown
from liberapay.utils.i18n import LANGUAGES_2, SEARCH_CONFS, strip_accents
[---]