Project Conventions (git, l10n, Python, etc.)

This document contains coding conventions, and things to watch out for, etc.

Coding conventions

Editor configuration

We use .editorconfig to codify and enforce editor settings using editorconfig ( Install the editorconfig plugin for your editor to make use of the recommended settings.

Git pre-commit hook

We have a Git pre-commit hook that makes it easier to make sure you’re checking in linted code. To set it up, run this command in the VM:

$ pre-commit install

That’ll set up the pre-commit hook. After that, every time you commit something in Git, it’ll run the hook first and if everything is fine continue with the commit. If things are not fine, it’ll notify you and stop. In case, you had already installed the previous pre-commit hook overwrite it by using the -f flag with the above command.

Python conventions

Follow PEP-8 for code and PEP-257 for docstrings.

If you don’t have an editor that checks PEP-8 issues and runs pyflakes for you, it’s worth setting it up.

The pre-commit hook uses the flake8 tool to lint the code to be committed.


Use jshint for JavaScript code. This is automatically done in the pre-commit hook.

Use jsdoc for JavaScript function documentation.

Git conventions

Git commit messages

Git commit messages should have the following form:

[bug xxxxxxx] Short summary

Longer explanation with paragraphs and lists and all that where
each line is under 72 characters.

* bullet 1
* bullet 2

Etc. etc.

Summary line should be capitalized, short and shouldn’t exceed 50 characters. Why? Because this is a convention many git tools take advantage of.

If the commit relates to a bug, the bug should show up in the summary line in brackets.

Lines shouldn’t exceed 72 characters.

See these guidelines for more explanation.

Localization conventions


You can localize strings both in Python code as well as Jinja templates.

In Python:

from django.utils.translation import ugettext as _, ugettext_lazy as _lazy

yodawg = _lazy('The Internet')

def myview(request):
    return render('template.html', {msg=_('Hello World'), msg2=yodawg})

_lazy is used when we are not in scope of a request. This lets us evaluate a string, such as yodawg, at the last possible second when we finally can draw upon the request’s context. E.g. in a template:

{{ msg2 }}

Will be evaluated to whatever The Internet is in the requester’s locale.

In Jinja we can use the following syntax for localized strings:

<h1>{{ _('Hello') }}</h1>

{% trans link='' %}
  <p>Go to this <a href="{{ link }}">site</a>.</p>
{% endtrans %}

Good Practices

Let’s say you have some template:


  Is it <a href="">me</a> you're
  looking for?

Let’s say you are told to translate this. You could do the following:

{% trans %}

    Is it <a href="">me</a> you're looking for?
{% endtrans %}

This has a few problems, however:

  1. It forces every localizer to mimic your HTML, potentially breaking it.
  2. If you decide to change the HTML, you need to either update your .po files or buy all your localizers a nice gift because of all the pain you’re inflicting upon them.
  3. If the URL changes, your localizer has to update everything.

Here’s an alternative:


  {% trans about_url='' %}
    Is it <a href="{{ about_url }}">me</a> you're looking for?
  {% endtrans %}

or if you have multiple paragraphs:


{% trans about_url='' %}
    Is it <a href="{{ about_url }}">me</a> you're looking for?
    I can see it in your eyes.
{% endtrans %}

Here are the advantages:

  1. Localizers have to do minimal HTML.
  2. The links and even structure of the document can change, but the localizations can stay put.

Be mindful of work that localizers will have to do.

Testing strings

Fjord comes with bin/ script which makes it pretty easy to test that strings in the user interface are getting gettext’d. It creates a faux “Pirate” translation of the strings in the xx locale.

You need to install polib for the script to work:

$ pip install polib

After that, cd into the project directory and do:

$ bin/

After that runs, you can see what happened by doing:

$ ./ runserver

and going to