Skip to content

Commit

Permalink
Merge pull request #63 from cdubz/i18n
Browse files Browse the repository at this point in the history
Make Baby Buddy translatable
  • Loading branch information
cdubz committed Apr 19, 2019
2 parents e94e11f + ff5a21f commit 22536f8
Show file tree
Hide file tree
Showing 92 changed files with 2,610 additions and 594 deletions.
43 changes: 43 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

- [Contributions](#contributions)
- [Pull request process](#pull-request-process)
- [Translation](#translation)
- [Development](#development)
- [Installation](#installation)
- [Gulp Commands](#gulp-commands)
Expand Down Expand Up @@ -34,6 +35,33 @@ Gitter.
New pull requests will be reviewed by project maintainers as soon as possible
and we will do our best to provide feedback and support potential contributors.

### Translation

Baby Buddy has support for translation/localization to any language. A general
translation process will look something like this:

1. Set up a development environment (see [Development](#development) below).
1. Run `gulp makemessages -l xx` where `xx` is a specific locale code (e.g. "fr"
for French or "es" for Spanish). This create a new translation file at
`locale/xx/LC_MESSAGES/django.po`, or update one if it exits.
1. Open the created/updated `django.po` file and update the header template with
license and contact info.
1. Start translating! Each translatable string will have a `msgid` value with
the string in English and a corresponding (empty) `msgstr` value where a
translated string can be filled in.
1. Once all strings have been translated, run `gulp compilemessages -l xx` to
compile an optimized translation file (`locale/xx/LC_MESSAGES/django.mo`).
1. To expose the new translation as a user setting, add the locale code to the
`LANGUAGES` array in the base settings file (`babybuddy/settings/base.py`).
1. Run the development server, log in, and update the user language to test the
newly translated strings.

Once the translation is complete, commit the new files and changes to a fork and
[create a pull request](#pull-request-process) for review.

For more information on the Django translation process, see Django's
documentation section: [Translation](https://docs.djangoproject.com/en/dev/topics/i18n/translation/).

## Development

### Requirements
Expand Down Expand Up @@ -92,10 +120,12 @@ in the [`babybuddy/management/commands`](babybuddy/management/commands) folder.
- [`gulp build`](#build)
- [`gulp clean`](#clean)
- [`gulp collectstatic`](#collectstatic)
- [`gulp compilemessages`](#compilemessages)
- [`gulp coverage`](#coverage)
- [`gulp extras`](#extras)
- [`gulp fake`](#fake)
- [`gulp lint`](#lint)
- [`gulp makemessages`](#makemessages)
- [`gulp makemigrations`](#makemigrations)
- [`gulp migrate`](#migrate)
- [`gulp reset`](#reset)
Expand Down Expand Up @@ -126,6 +156,11 @@ the `babybuddy/static` folder, so generally `gulp build` should be run before
this command for production deployments. Gulp also passes along
non-overlapping arguments for this command, e.g. `--no-input`.

#### `compilemessages`

Executes Django's `compilemessages` management task. See [django-admin compilemessages](https://docs.djangoproject.com/en/dev/ref/django-admin/#compilemessages)
for more details about other options and functionality of this command.

#### `coverage`

Create a test coverage report. See [`.coveragerc`](.coveragerc) for default
Expand All @@ -147,6 +182,14 @@ children and seven days of data for each.

Executes Python and SASS linting for all relevant source files.

#### `makemessages`

Executes Django's `makemessages` management task. See [django-admin makemessages](https://docs.djangoproject.com/en/dev/ref/django-admin/#makemessages)
for more details about other options and functionality of this command. When
working on a single translation, the `-l` flag is useful to make message for
only that language, e.g. `gulp makemessages -l fr` to make only a French
language translation file.

#### `makemigrations`

Executes Django's `makemigrations` management task. Gulp also passes along
Expand Down
6 changes: 4 additions & 2 deletions babybuddy/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _

from babybuddy import models


class SettingsInline(admin.StackedInline):
model = models.Settings
verbose_name_plural = 'Settings'
verbose_name = _('Settings')
verbose_name_plural = _('Settings')
can_delete = False
fieldsets = (
('Dashboard', {
(_('Dashboard'), {
'fields': ('dashboard_refresh_rate',)
}),
)
Expand Down
2 changes: 1 addition & 1 deletion babybuddy/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ class Meta:
class UserSettingsForm(forms.ModelForm):
class Meta:
model = Settings
fields = ['dashboard_refresh_rate']
fields = ['dashboard_refresh_rate', 'language']
18 changes: 18 additions & 0 deletions babybuddy/migrations/0004_settings_language.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2 on 2019-04-17 04:02

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('babybuddy', '0003_add_refresh_help_text'),
]

operations = [
migrations.AddField(
model_name='settings',
name='language',
field=models.CharField(choices=[], default='en', max_length=255, verbose_name='Language'),
),
]
43 changes: 30 additions & 13 deletions babybuddy/models.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth.signals import user_logged_in
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.timezone import timedelta
from django.utils.text import format_lazy
from django.utils import translation
from django.utils.translation import gettext_lazy as _

from rest_framework.authtoken.models import Token


class Settings(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dashboard_refresh_rate = models.DurationField(
verbose_name='Refresh rate',
help_text='This setting will only be used when a browser does not '
'support refresh on focus.',
verbose_name=_('Refresh rate'),
help_text=_('This setting will only be used when a browser does not '
'support refresh on focus.'),
blank=True,
null=True,
default=timedelta(minutes=1),
choices=[
(None, 'disabled'),
(timedelta(minutes=1), '1 min.'),
(timedelta(minutes=2), '2 min.'),
(timedelta(minutes=3), '3 min.'),
(timedelta(minutes=4), '4 min.'),
(timedelta(minutes=5), '5 min.'),
(timedelta(minutes=10), '10 min.'),
(timedelta(minutes=15), '15 min.'),
(timedelta(minutes=30), '30 min.'),
(None, _('disabled')),
(timedelta(minutes=1), _('1 min.')),
(timedelta(minutes=2), _('2 min.')),
(timedelta(minutes=3), _('3 min.')),
(timedelta(minutes=4), _('4 min.')),
(timedelta(minutes=5), _('5 min.')),
(timedelta(minutes=10), _('10 min.')),
(timedelta(minutes=15), _('15 min.')),
(timedelta(minutes=30), _('30 min.')),
])
language = models.CharField(
choices=settings.LANGUAGES,
default=settings.LANGUAGE_CODE,
max_length=255,
verbose_name=_('Language')
)

def __str__(self):
return '{}\'s Settings'.format(self.user)
return str(format_lazy(_('{user}\'s Settings'), user=self.user))

def api_key(self, reset=False):
"""
Expand Down Expand Up @@ -63,3 +74,9 @@ def create_user_settings(sender, instance, created, **kwargs):
@receiver(post_save, sender=User)
def save_user_settings(sender, instance, **kwargs):
instance.settings.save()


@receiver(user_logged_in)
def user_logged_in_callback(sender, request, user, **kwargs):
translation.activate(user.settings.language)
request.session[translation.LANGUAGE_SESSION_KEY] = user.settings.language
15 changes: 12 additions & 3 deletions babybuddy/settings/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

from django.utils.translation import gettext_lazy as _
from dotenv import load_dotenv, find_dotenv

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
Expand Down Expand Up @@ -53,10 +54,9 @@

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',

'whitenoise.middleware.WhiteNoiseMiddleware',

'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
Expand Down Expand Up @@ -110,7 +110,7 @@
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'en'

TIME_ZONE = os.environ.get('TIME_ZONE', 'Etc/UTC')

Expand All @@ -120,6 +120,15 @@

USE_TZ = True

LOCALE_PATHS = [
os.path.join(BASE_DIR, "locale"),
]

LANGUAGES = [
('en', _('English')),
('fr', _('French')),
]


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
Expand Down
10 changes: 5 additions & 5 deletions babybuddy/templates/403.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{% extends 'babybuddy/page.html' %}
{% load widget_tweaks %}
{% load i18n widget_tweaks %}

{% block title %}403 Permission Denied{% endblock %}
{% block title %}403 {% trans "Permission Denied" %}{% endblock %}

{% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Permission Denied</li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Permission Denied" %}</li>
{% endblock %}

{% block content %}
<div class="alert alert-danger" role="alert">
You do not have permission to access this resource. Contact a site
administrator for assistance.
{% blocktrans %}You do not have permission to access this resource.
Contact a site administrator for assistance.{% endblocktrans %}
</div>
{% endblock %}
4 changes: 2 additions & 2 deletions babybuddy/templates/babybuddy/base.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% load static %}
{% load i18n static %}

<!DOCTYPE html>
<html lang="en">
Expand Down Expand Up @@ -31,7 +31,7 @@
{% block breadcrumb_nav %}
<nav aria-label="breadcrumb" role="navigation">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="/">{% trans "Home" %}</a></li>
{% block breadcrumbs %}{% endblock %}
</ol>
</nav>
Expand Down
8 changes: 4 additions & 4 deletions babybuddy/templates/babybuddy/filter.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% load widget_tweaks %}
{% load i18n widget_tweaks %}

<form id="filter_form" role="form" action="" method="get" class="collapse{% if request.GET.filtered %} show{% endif %}">
<div class="form-group form-row">
Expand All @@ -15,8 +15,8 @@
</div>
{% endfor %}
<div class="col-xs-12 col-sm-auto mt-3 mt-sm-0">
<button type="submit" class="btn btn-sm btn-primary mr-2">Filter</button>
<a href="{{ request.path }}" class="btn btn-sm btn-error">Reset</a>
<button type="submit" class="btn btn-sm btn-primary mr-2">{% trans "Filter" %}</button>
<a href="{{ request.path }}" class="btn btn-sm btn-error">{% trans "Reset" %}</a>
</div>
</div>
<input type="hidden" name="filtered" value="1"/>
Expand All @@ -29,6 +29,6 @@
role="button"
aria-expanded="false"
aria-controls="filter_form">
Filters
{% trans "Filters" %}
</a>
</p>
4 changes: 2 additions & 2 deletions babybuddy/templates/babybuddy/form.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% load widget_tweaks %}
{% load i18n widget_tweaks %}

<div class="container-fluid">
<form role="form" method="post" enctype="multipart/form-data">
Expand All @@ -8,6 +8,6 @@
{% include 'babybuddy/form_field.html' %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
<button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
</form>
</div>
6 changes: 4 additions & 2 deletions babybuddy/templates/babybuddy/messages.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{% load i18n %}

{% block messages %}
{% if messages %}
{% for message in messages %}
Expand All @@ -13,12 +15,12 @@
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger" role="alert">
<strong>Error:</strong> {{ error }}
{% blocktrans %}<strong>Error:</strong> {{ error }}{% endblocktrans %}
</div>
{% endfor %}
{% elif form.errors %}
<div class="alert alert-danger" role="alert">
<strong>Error:</strong> Some fields have errors. See below for details.
{% blocktrans %}<strong>Error:</strong> Some fields have errors. See below for details. {% endblocktrans %}
</div>
{% endif %}
{% endif %}
Expand Down

0 comments on commit 22536f8

Please sign in to comment.