Django / DjangoCMS – custom language

We had to create custom languages in Django for two different versions of the Chinese language: yue (Mandarin Chinese) and cmn (Cantonese Chinese)

This is really simple in Django with gettext, as the default languages can just be overriden:

LANGUAGES = (
    ## Customize this
    ('en', "English"),
    ('cmn', "Mandarin Chinese"),
    ('yue', "Cantonese Chinese"),
)

CMS_LANGUAGES = {
    ## Customize this
    1: [
        {
            'code': 'en',
            'name': gettext('en'),
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
        },
        {
            'code': 'cmn',
            'name': "Mandarin Chinese",
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
        },
        {
            'code': 'yue',
            'name': "Cantonese Chinese",
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
        },
    ],
    'default': {
        'redirect_on_fallback': True,
        'public': True,
        'hide_untranslated': False,
    },
}

updating from templates and python code:

./manage.py makemessages -l en
./manage.py makemessages -l yue
./manage.py makemessages -l cmn

"Plural-Forms: nplurals=2; plural=(n != 1);\n" needs to be part of every .po file for DjangoCMS

compiling
./manage.py compilemessages

django language switcher

In Django, it’s not evident how to properly switch languages. Google searches result in many different approaches, but none of them is clean. In django-cms however, there is a clean and supported approach: the language_chooser template tag.

DjangoCMS and ManifestStaticFilesStorage

  • https://docs.djangoproject.com/en/2.1/ref/contrib/staticfiles/#ManifestStaticFilesStorage
  • Allows to remove hashing from the webpack setup which is convenient
  • Allows to also hash images via {% static 'path/to/asset.jpg' %}, respectively assets that are not bundled via webpack
  • Hashing is only active if debug is False
  • The collectstatic will fail if any app that includes static assets in templates is not in INSTALLED_APPS
  • On production, collectstatic will also sometimes fail, in such cases, DEBUG has to be turned on, ./manage.py collectstatic has to be run, then DEBUG can be turned off again. See https://github.com/divio/djangocms-text-ckeditor/issues/474
  • If you use inlined data urls and the svg code itself uses url()s and these contain url_encoded values, then collectstatic command will fail because it will try to analyse these url()s and you have to work around this like https://code.djangoproject.com/ticket/21080#comment:12

Barcelona

  • Aire de Barcelona – Wellness
  • Playa Mar Bella – Chiringuitos
  • W Hotel Rooftop – view
  • Museu Nacional d’Art de Catalunya – Sunset on the stairs
  • Bajofondo Club – Cocktails
  • The Pulitzer Terrace at Hotel Pulitzer – rooftop bar
  • B-Pool Bar at B-Hotel – Tapas, view
  • Grand Hotel Central Skybar – view, drinks

NEO python Shortcuts

Installation

  • create a python virtualenv
  • pip install neo-python

Usage

  • np-prompt in your console

Private Network Setup

  • docker pull cityofzion/neo-privatenet (see https://github.com/CityOfZion/neo-privatenet-docker)
  • np-prompt -p connects to your docker private network
  • then you can import the wallet that is provided in the documentation. It has already all the NEO and GAS.

Clean up Jenkins to save disk space

-> Manage Jenkins -> Script Console

from https://gist.github.com/pkouman/c987ce8cd622820cce9111ea34662c6b

MAX_BUILDS = 10 // max builds to keep

def jobs = Jenkins.instance.items;

for (job in jobs) {
    println "Job: " + job.name
    def recent = job.builds.limit(MAX_BUILDS)
    println "Recent Builds: "  + recent
    println "============================="
    for (build in job.builds) {
        if (!recent.contains(build) && !build.isBuilding()) {
            println "Deleting: " + build
            build.delete()
            println ""
        }
    }
}

replace raven with sentry-sdk

since raven is now deprecated, this is how we should integrate sentry into our django projects: via the new pip install sentry-sdk:

settings.py: No more messing around with LOGGING is required, the following code plugs right into django:

import sentry_sdk
import logging
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import LoggingIntegration

sentry_sdk.init(
   dsn="https://1234@sentry.io/123",
   integrations=[
      DjangoIntegration(),
      LoggingIntegration(
         level=logging.INFO, # Capture info and above as breadcrumbs
         event_level=None # Send no events from log messages
      )
   ],
   environment=env('DJANGO_ENV', 'develop'),
)

django translation tips: makemessages and gettext pitfalls & blocktrans best practises

Below I share some best practises and experiences I had with Django’s gettext implementations.

Fuzzy translations

Fuzzy translations are one of the bigger challenge when using gettext for translation. The following is a best practise in coping with them:

“Fuzzy” messages for not make it into the translation. They are not considered to be translated correctly, but they are not deleted, since they are likely to be “almost correct” and just need a small update.

Part of normal translator operations is to search out any “fuzzy” messages and verify that they are either still correct (just delete the fuzzy comment, but do not delete the python-format bit) or update them and then remove the “fuzzy”.

This isn’t anything special to Django, by the way. It’s the way
gettext-backed i18n support operates.

Malcolm Tredinnick at https://groups.google.com/forum/#!msg/django-users/SjqjUhZwJzU/DaHmdwZeZLkJ

The Django blocktranstag

Best practice: Always use the `trimmed` option. This results in a nicely formatted block of text in the po file which makes it simpler for translators to handle it. If the translation key is not trimmed, arbitrary line breaks make it difficult to translate properly.

{% blocktrans trimmed with business_name=business_name context "password reset email" %}
You're receiving this email because you requested a password reset for your user account at {{ business_name }}.

Another line, which will be trimmed into one single block of text inside the po file so that this can be properly formatted.
{% endblocktrans %}

Translation Process

  1. After changes in the code and templates the developer updates the po files with manage.py makemessages command and review the updates in the re-generated po files and then commits them to the source, best via a Merge Request. Its not a good idea to add new translation entries from templates or code to a po file manually. django makemessages command takes care of this and makes sure everything is updated correctly. (edited)
  2. manage.py compilemessages is then normally run during deployment in the deploy script. It generates the compiled .mo files that are needed by Django to display the translations.

WIP: Django Project Dockerization

docker-compose.yml

version: "2"

services:
  web:
    build: "."
    ports:
      - "8000:80"
    volumes:
      - ".:/app:rw"
    command: python manage.py runserver 0.0.0.0:80

Dockerfile

FROM aldryn/base-project:py3-3.23

# System upgrade
RUN apt-get update && apt-get upgrade -yq

# Setup for ssh onto github
RUN mkdir -p /root/.ssh
ADD credentials/divio-deploy-key.pem /root/.ssh/id_rsa
RUN chmod 700 /root/.ssh/id_rsa
RUN echo "Host gitlab.com\n\tStrictHostKeyChecking no\n" >> /root/.ssh/config

# Python
# we want to keep project-specific sources in the "wee" folder
ENV PYTHONPATH=/app/wee:$PYTHONPATH
COPY requirements.txt /app/
RUN pip install -r requirements.txt

# nvm environment variables
ENV NODE_VERSION=8.11.3
RUN . $NVM_DIR/nvm.sh && nvm install $NODE_VERSION

# add node and npm to path so the commands are available
ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
COPY package.json /app/
RUN npm install

COPY . /app

RUN npm run build

RUN DJANGO_MODE=build python manage.py collectstatic --noinput