├── .gitignore ├── README.md ├── babel.cfg ├── hello_world.py ├── locale ├── en_US │ └── LC_MESSAGES │ │ └── helloworld.po └── nb_NO │ └── LC_MESSAGES │ └── helloworld.po ├── requirements.txt └── templates └── my_template.jinja2 /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | .venv 89 | venv/ 90 | ENV/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # JetBrains IDE 99 | .idea 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ```bash 4 | [sudo] pip install -r requirements.txt 5 | ``` 6 | 7 | ## Compile `po` files 8 | 9 | The `po` files (human readable) need to be compiled into machine readable `mo` files before gettext can use them 10 | 11 | ```bash 12 | pybabel compile --domain=helloworld --directory=locale --use-fuzzy 13 | ``` 14 | 15 | # Run 16 | 17 | ```bash 18 | python hello_world.py 19 | ``` 20 | 21 | # Workflow 22 | 23 | When working on the application, you might want to add new messages. For example, add the following line to `templates/my_template.jinja2`: 24 | 25 | ```jinja2 26 | {{ gettext('Yet another message') }} 27 | ``` 28 | 29 | Babel can automatically detect that this message was added. First we need to use the `extract` command to extract all localizable messages from the source files: 30 | 31 | ```bash 32 | pybabel extract --mapping babel.cfg --output-file=locale/helloworld.pot . 33 | ``` 34 | 35 | Then update the *.po files so they contain the new localizable message: 36 | 37 | ```bash 38 | pybabel update --domain=helloworld --input-file=locale/helloworld.pot --output-dir=locale 39 | ``` 40 | 41 | You can now see in the git diff for the *.po files where the new message was added, and you can hand-edit these files to localize the new message. 42 | 43 | For example, open `locale/nb_NO/LC_MESSAGES/helloworld.po` and edit 44 | ```pot 45 | #: templates/my_template.jinja2:3 46 | msgid "Yet another message" 47 | msgstr "" 48 | ``` 49 | to 50 | ```pot 51 | #: templates/my_template.jinja2:3 52 | msgid "Yet another message" 53 | msgstr "Enda en melding" 54 | ``` 55 | 56 | [Poedit](https://poedit.net/) is a good editor for doing this. For example, it'll highlight missing translations and suggest translations, and you'll just have to press a keyboard shortcut to apply the suggestion. 57 | 58 | ## Command line aliases 59 | 60 | Since the `pybabel` commands can be quite long, and you might want to use them frequently, it is recommended to add short aliases to these commands to save time 61 | -------------------------------------------------------------------------------- /babel.cfg: -------------------------------------------------------------------------------- 1 | # Extraction from Python files 2 | [python: **.py] 3 | 4 | # Extraction from Jinja2 template files 5 | [jinja2: templates/**.jinja2] 6 | encoding = utf-8 7 | -------------------------------------------------------------------------------- /hello_world.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from jinja2 import Environment, FileSystemLoader 3 | import locale 4 | import gettext 5 | 6 | domain = 'helloworld' 7 | 8 | current_locale = 'en_US' # replace with 'nb_NO' to show norwegian text 9 | # current_locale, encoding = locale.getdefaultlocale() 10 | print('Current locale: {}'.format(current_locale)) 11 | 12 | locale_path = 'locale/' 13 | gnu_translations = gettext.translation( 14 | domain='helloworld', 15 | localedir=locale_path, 16 | languages=[current_locale] 17 | ) 18 | gnu_translations.install() # Magically make the _ function globally available 19 | 20 | print(_('helloworld')) 21 | 22 | env = Environment( 23 | extensions=['jinja2.ext.i18n'], 24 | loader=FileSystemLoader('templates') 25 | ) 26 | env.install_gettext_translations(gnu_translations, newstyle=True) 27 | 28 | template = env.get_template('my_template.jinja2') 29 | result = template.render() 30 | print(result) 31 | -------------------------------------------------------------------------------- /locale/en_US/LC_MESSAGES/helloworld.po: -------------------------------------------------------------------------------- 1 | # English (United States) translations for PROJECT. 2 | # Copyright (C) 2017 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2017. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2017-02-28 12:52+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language: en_US\n" 15 | "Language-Team: en_US \n" 16 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 2.3.4\n" 21 | 22 | #: hello_world.py:20 23 | msgid "helloworld" 24 | msgstr "Hello world!" 25 | 26 | #: templates/my_template.jinja2:1 27 | msgid "Jinja2 is awesome" 28 | msgstr "Jinja2 is awesome" 29 | 30 | #: templates/my_template.jinja2:2 31 | msgid "Gettext works" 32 | msgstr "Gettext works" 33 | -------------------------------------------------------------------------------- /locale/nb_NO/LC_MESSAGES/helloworld.po: -------------------------------------------------------------------------------- 1 | # Norwegian Bokmål (Norway) translations for PROJECT. 2 | # Copyright (C) 2017 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2017. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2017-02-28 12:52+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language: nb_NO\n" 15 | "Language-Team: nb_NO \n" 16 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 2.3.4\n" 21 | 22 | #: hello_world.py:20 23 | msgid "helloworld" 24 | msgstr "Hallo verden!" 25 | 26 | #: templates/my_template.jinja2:1 27 | msgid "Jinja2 is awesome" 28 | msgstr "Jinja2 er kjempebra" 29 | 30 | #: templates/my_template.jinja2:2 31 | msgid "Gettext works" 32 | msgstr "Gettext fungerer" 33 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.11.3 2 | Babel==2.3.4 3 | -------------------------------------------------------------------------------- /templates/my_template.jinja2: -------------------------------------------------------------------------------- 1 | {% trans %}Jinja2 is awesome{% endtrans %} 2 | {{ gettext('Gettext works') }} 3 | --------------------------------------------------------------------------------