Repository: liberapay/liberapay.com

Issue:
Receipts kinda suck
Our receipts are inherited from Gratipay, they currently look like this:

![liberapay-2016-05-02-receipt](https://cloud.githubusercontent.com/assets/1581590/14954605/0453da02-1074-11e6-8ca3-37f13e883211.png)

I think we can do better.
- [x] I see no reason for the receipts to be this small, they could just as well be in a standard size like A4.
- [x] Make it very clear that they are only receipts, not bills/invoices.
- [x] Mention that the payment is for recurrent donations.
- [x] Add Liberapay's logo.



Discussion:
@Changaco 
Few things, Where does the logo need to go? And about making it clear about being receipts and not bills and the mention of recurrent donations, does it go at the bottom?
@subkrish I don't really know, for this kind of issue you just have to try things and see what looks best.
WIP screenshot:

![screenshot of a (fake) receipt](https://user-images.githubusercontent.com/1581590/34649431-7a38642a-f3af-11e7-9c54-40ee9f5a7cbf.png)

Feedback welcome.

Is the word 'reception' used when talking about payments? I think it's more common to say 'We confirm the receipt of ...' that 'We confirm the reception of ...'.
  
https://forum.wordreference.com/threads/receipt-reception.1026869/
Also, a bit of spacing might help...

**Before**:

![34649431-7a38642a-f3af-11e7-9c54-40ee9f5a7cbf](https://user-images.githubusercontent.com/3893573/34649783-7fb62816-f3db-11e7-9745-5973226b73da.png)

**After**:

![liberapayspacing](https://user-images.githubusercontent.com/3893573/34649778-6aef9cc8-f3db-11e7-8569-1ca09486f43b.png)


  

Resolving patch:
diff --git a/liberapay/utils/__init__.py b/liberapay/utils/__init__.py
--- a/liberapay/utils/__init__.py
+++ b/liberapay/utils/__init__.py
@@ -25,6 +25,7 @@
 
 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.website import website
 
 
@@ -467,3 +468,30 @@ def get_ip_net(addr):
         return '.'.join(str(addr).split('.', 3)[:2])
     else:
         return hexlify(addr.packed[:4])
+
+
+def render(context, allow_partial_i18n=True):
+    """Render the next page and return the output.
+
+    This function is meant to be used in the second page of a simplate, e.g.:
+
+    ```
+    from liberapay.utils import render
+    [---]
+    output.body = render(globals(), allow_partial_i18n=False)
+    [---] text/html
+    ...
+    ```
+
+    If `allow_partial_i18n` is `False` and the output is a partially translated
+    page then a second rendering is done so that the final output is entirely in
+    English.
+    """
+    output, resource = context['output'], context['resource']
+    r = resource.renderers[output.media_type](context)
+    if allow_partial_i18n or not context['state'].get('partial_translation'):
+        return r
+    else:
+        # Fall back to English
+        add_helpers_to_context(context, LOCALE_EN)
+        return resource.renderers[output.media_type](context)
diff --git a/liberapay/utils/i18n.py b/liberapay/utils/i18n.py
--- a/liberapay/utils/i18n.py
+++ b/liberapay/utils/i18n.py
@@ -255,6 +255,8 @@ def i_format(loc, s, *a, **kw):
                 c[k] = format_timedelta(o, locale=loc)
             elif isinstance(o, datetime):
                 c[k] = format_datetime(o, locale=loc)
+            elif isinstance(o, date):
+                c[k] = format_date(o, locale=loc)
             if wrapper:
                 c[k] = wrapper % (c[k],)
     return s.format(*a, **kw)
@@ -270,7 +272,9 @@ def get_text(state, loc, s, *a, **kw):
             s2 = s2[0]
     if not s2:
         s2 = s
-        loc = LOCALE_EN
+        if loc != LOCALE_EN:
+            loc = LOCALE_EN
+            state['partial_translation'] = True
     if a or kw:
         try:
             return i_format(loc, escape(_decode(s2)), *a, **kw)
@@ -292,8 +296,10 @@ def n_get_text(state, loc, s, p, n, *a, **kw):
         except Exception as e:
             website.tell_sentry(e, state)
     if not s2:
-        loc = LOCALE_EN
         s2 = s if n == 1 else p
+        if loc != LOCALE_EN:
+            loc = LOCALE_EN
+            state['partial_translation'] = True
     kw['n'] = format_number(n, locale=loc) or n
     if wrapper:
         kw['n'] = wrapper % kw['n']


Test patch: