I recommend to keep things simple whenever possible. For most of our website projects we use simple dictionaries that we typically store in YAML files, just like this:
https://github.com/DPKit/gdpr-portal/blob/master/src/transla...
We then have a custom Jinja filter that looks up the translation for a given word, such as
{{'summary'|translate}}
In code, we have a simple helper function like this:
def t(key, lang):
return translations[lang].get(key, '[{} not found]'.format(key))
In practice we use a slightly more advanced version of this function (
https://github.com/adewes/beam/blob/master/beam/site.py#L91) which allows us to also include formatting parameters to allow things like {{'contact-us'|translate(name='Max',email='max@mustermann.de')}}.
You can also keep date and number formats in the i18n dictionary to internationalize dates. Most internationalization libraries do not provide much more than that and try to be overly clever by e.g. offering automated pluralization etc. but in my experience they often subtly break things.
Works quite well for us both on the backend as well as frontend, here's an example of a website (also open source at https://github.com/DPKit/gdpr-portal) that uses this method:
DE: https://dsgvo.dpkit.com
EN: https://gdpr.dpkit.com
(we decided to keep the different language versions on separate subdomains for better usability).
Don't be afraid to write your own solution for simple problems like that, no need to use 3rd party libraries for everything :)