diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ed80dad4..a8a25d41 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,6 +7,7 @@
- [Contributions](#contributions)
- [Pull request process](#pull-request-process)
+ - [Translation](#translation)
- [Development](#development)
- [Installation](#installation)
- [Gulp Commands](#gulp-commands)
@@ -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
@@ -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)
@@ -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
@@ -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
diff --git a/babybuddy/admin.py b/babybuddy/admin.py
index ee7e4fd0..0eb69815 100644
--- a/babybuddy/admin.py
+++ b/babybuddy/admin.py
@@ -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',)
}),
)
diff --git a/babybuddy/forms.py b/babybuddy/forms.py
index 017a2191..004d7380 100644
--- a/babybuddy/forms.py
+++ b/babybuddy/forms.py
@@ -41,4 +41,4 @@ class UserPasswordForm(PasswordChangeForm):
class UserSettingsForm(forms.ModelForm):
class Meta:
model = Settings
- fields = ['dashboard_refresh_rate']
+ fields = ['dashboard_refresh_rate', 'language']
diff --git a/babybuddy/migrations/0004_settings_language.py b/babybuddy/migrations/0004_settings_language.py
new file mode 100644
index 00000000..d473b65b
--- /dev/null
+++ b/babybuddy/migrations/0004_settings_language.py
@@ -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'),
+ ),
+ ]
diff --git a/babybuddy/models.py b/babybuddy/models.py
index 1c36ed9b..97636c31 100644
--- a/babybuddy/models.py
+++ b/babybuddy/models.py
@@ -1,9 +1,14 @@
# -*- 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
@@ -11,26 +16,32 @@ 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):
"""
@@ -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
diff --git a/babybuddy/settings/base.py b/babybuddy/settings/base.py
index c49e19c8..94b9e126 100644
--- a/babybuddy/settings/base.py
+++ b/babybuddy/settings/base.py
@@ -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, ...)
@@ -53,10 +54,9 @@ INSTALLED_APPS = [
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',
@@ -110,7 +110,7 @@ LOGOUT_REDIRECT_URL = '/login/'
# 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')
@@ -120,6 +120,15 @@ USE_L10N = True
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/
diff --git a/babybuddy/templates/403.html b/babybuddy/templates/403.html
index 0eb0d4d5..530950a0 100644
--- a/babybuddy/templates/403.html
+++ b/babybuddy/templates/403.html
@@ -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 %}
-
Permission Denied
+ {% trans "Permission Denied" %}
{% endblock %}
{% block content %}
- 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 %}
{% endblock %}
\ No newline at end of file
diff --git a/babybuddy/templates/babybuddy/base.html b/babybuddy/templates/babybuddy/base.html
index 04878c35..a17188aa 100644
--- a/babybuddy/templates/babybuddy/base.html
+++ b/babybuddy/templates/babybuddy/base.html
@@ -1,4 +1,4 @@
-{% load static %}
+{% load i18n static %}
@@ -31,7 +31,7 @@
{% block breadcrumb_nav %}
- Home
+ {% trans "Home" %}
{% block breadcrumbs %}{% endblock %}
diff --git a/babybuddy/templates/babybuddy/filter.html b/babybuddy/templates/babybuddy/filter.html
index 0379d6a9..84765a40 100644
--- a/babybuddy/templates/babybuddy/filter.html
+++ b/babybuddy/templates/babybuddy/filter.html
@@ -1,4 +1,4 @@
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
diff --git a/babybuddy/templates/babybuddy/messages.html b/babybuddy/templates/babybuddy/messages.html
index 45627ffe..c751a753 100644
--- a/babybuddy/templates/babybuddy/messages.html
+++ b/babybuddy/templates/babybuddy/messages.html
@@ -1,3 +1,5 @@
+{% load i18n %}
+
{% block messages %}
{% if messages %}
{% for message in messages %}
@@ -13,12 +15,12 @@
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
- Error: {{ error }}
+ {% blocktrans %}Error: {{ error }}{% endblocktrans %}
{% endfor %}
{% elif form.errors %}
- Error: Some fields have errors. See below for details.
+ {% blocktrans %}Error: Some fields have errors. See below for details. {% endblocktrans %}
{% endif %}
{% endif %}
diff --git a/babybuddy/templates/babybuddy/nav-dropdown.html b/babybuddy/templates/babybuddy/nav-dropdown.html
index 43d4013b..1bace63c 100644
--- a/babybuddy/templates/babybuddy/nav-dropdown.html
+++ b/babybuddy/templates/babybuddy/nav-dropdown.html
@@ -1,5 +1,5 @@
{% extends 'babybuddy/base.html' %}
-{% load babybuddy_tags static timers %}
+{% load babybuddy_tags i18n static timers %}
{% block nav %}
@@ -23,32 +23,38 @@
@@ -66,7 +72,8 @@
- Dashboard
+
+ {% trans "Dashboard" %}
@@ -76,40 +83,51 @@
href="#"
data-toggle="dropdown"
aria-haspopup="true"
- aria-expanded="false"> Children
+ aria-expanded="false">
+ {% trans "Children" %}
+
@@ -121,43 +139,61 @@
href="#"
data-toggle="dropdown"
aria-haspopup="true"
- aria-expanded="false"> Activities
+ aria-expanded="false">
+ {% trans "Activities" %}
+
@@ -179,29 +215,29 @@
aria-expanded="false"> {{ request.user }}
diff --git a/babybuddy/templates/babybuddy/paginator.html b/babybuddy/templates/babybuddy/paginator.html
index 0690beff..bf266a96 100644
--- a/babybuddy/templates/babybuddy/paginator.html
+++ b/babybuddy/templates/babybuddy/paginator.html
@@ -1,4 +1,4 @@
-{% load babybuddy_tags %}
+{% load i18n babybuddy_tags %}
{% if is_paginated %}
@@ -8,7 +8,7 @@
- Previous
+ {% trans "Previous" %}
{% endif %}
@@ -25,7 +25,7 @@
- Next
+ {% trans "Next" %}
{% endif %}
diff --git a/babybuddy/templates/babybuddy/user_confirm_delete.html b/babybuddy/templates/babybuddy/user_confirm_delete.html
index ed14ed47..be1a2b71 100644
--- a/babybuddy/templates/babybuddy/user_confirm_delete.html
+++ b/babybuddy/templates/babybuddy/user_confirm_delete.html
@@ -1,19 +1,19 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete User{% endblock %}
+{% block title %}{% trans "Delete User" %}{% endblock %}
{% block breadcrumbs %}
- Users
+ {% trans "Users" %}
{{ object }}
- Delete
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
- Cancel
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/babybuddy/templates/babybuddy/user_form.html b/babybuddy/templates/babybuddy/user_form.html
index 58e87180..fd38ad86 100644
--- a/babybuddy/templates/babybuddy/user_form.html
+++ b/babybuddy/templates/babybuddy/user_form.html
@@ -1,28 +1,29 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if object %}
{{ object }}
{% else %}
- Create User
+ {% trans "Create User" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Users
+ {% trans "Users" %}
{% if object %}
{{ object }}
- Update
+ {% trans "Update" %}
{% else %}
- Create User
+ {% trans "Create User" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Create User
+ {% trans "Create User" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/babybuddy/templates/babybuddy/user_list.html b/babybuddy/templates/babybuddy/user_list.html
index 2e7e0aa8..aed6f57d 100644
--- a/babybuddy/templates/babybuddy/user_list.html
+++ b/babybuddy/templates/babybuddy/user_list.html
@@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %}
-{% load bootstrap widget_tweaks %}
+{% load bootstrap i18n widget_tweaks %}
-{% block title %}Users{% endblock %}
+{% block title %}{% trans "Users" %}{% endblock %}
{% block breadcrumbs %}
- Users
+ {% trans "Users" %}
{% endblock %}
{% block content %}
@@ -14,13 +14,13 @@
- User
- First Name
- Last Name
- Email
- Staff
- Active
- Actions
+ {% trans "User" %}
+ {% trans "First Name" %}
+ {% trans "Last Name" %}
+ {% trans "Email" %}
+ {% trans "Staff" %}
+ {% trans "Active" %}
+ {% trans "Actions" %}
@@ -52,7 +52,7 @@
{% empty %}
- No users found.
+ {% trans "No users found." %}
{% endfor %}
@@ -62,7 +62,7 @@
{% if perms.admin.add_user %}
- Create User
+ {% trans "Create User" %}
{% endif %}
diff --git a/babybuddy/templates/babybuddy/user_password_form.html b/babybuddy/templates/babybuddy/user_password_form.html
index c5684ebb..3b96e379 100644
--- a/babybuddy/templates/babybuddy/user_password_form.html
+++ b/babybuddy/templates/babybuddy/user_password_form.html
@@ -1,14 +1,14 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Change Password{% endblock %}
+{% block title %}{% trans "Change Password" %}{% endblock %}
{% block breadcrumbs %}
- User
- Change Password
+ {% trans "User" %}
+ {% trans "Change Password" %}
{% endblock %}
{% block content %}
- Change Password
+ {% trans "Change Password" %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/babybuddy/templates/babybuddy/user_settings_form.html b/babybuddy/templates/babybuddy/user_settings_form.html
index 26b73c01..b459d511 100644
--- a/babybuddy/templates/babybuddy/user_settings_form.html
+++ b/babybuddy/templates/babybuddy/user_settings_form.html
@@ -1,31 +1,31 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}User Settings{% endblock %}
+{% block title %}{% trans "User Settings" %}{% endblock %}
{% block breadcrumbs %}
- User
- Settings
+ {% trans "User" %}
+ {% trans "Settings" %}
{% endblock %}
{% block content %}
- User Settings
+ {% trans "User Settings" %}
{% csrf_token %}
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
- Error: {{ error }}
+ {% blocktrans %}Error: {{ error }}{% endblocktrans %}
{% endfor %}
{% elif form.errors %}
- Error: Some fields have errors. See below for details.
+ {% blocktrans %}Error: Some fields have errors. See below for details.{% endblocktrans %}
{% endif %}
- User Profile
+ {% trans "User Profile" %}
{% with form_user.first_name as field %}
{% include 'babybuddy/form_field.html' %}
@@ -41,9 +41,14 @@
{% include 'babybuddy/form_field.html' %}
{% endwith %}
+
+ {% with form_settings.language as field %}
+ {% include 'babybuddy/form_field.html' %}
+ {% endwith %}
+
- Dashboard
+ {% trans "Dashboard" %}
{% with form_settings.dashboard_refresh_rate as field %}
{% include 'babybuddy/form_field.html' %}
@@ -51,16 +56,16 @@
- API
+ {% trans "API" %}
- Submit
+ {% trans "Submit" %}
{% endblock %}
diff --git a/babybuddy/templates/babybuddy/welcome.html b/babybuddy/templates/babybuddy/welcome.html
index 15e5bf65..c46c3b60 100644
--- a/babybuddy/templates/babybuddy/welcome.html
+++ b/babybuddy/templates/babybuddy/welcome.html
@@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Welcome!{% endblock %}
+{% block title %}{% trans "Welcome!" %}{% endblock %}
{% block breadcrumbs %}
- Welcome!
+ {% trans "Welcome!" %}
{% endblock %}
{% block content %}
-
Welcome to Baby Buddy!
+
{% trans "Welcome to Baby Buddy!" %}
- Learn about and predict baby's needs without (as much )
- guess work by using Baby Buddy to track —
+ {% blocktrans %}Learn about and predict baby's needs without
+ (as much ) guess work by using Baby Buddy to track —{% endblocktrans %}
@@ -21,7 +21,7 @@
-
Diaper Changes
+ {% trans "Diaper Changes" %}
@@ -29,7 +29,7 @@
-
Feedings
+ {% trans "Feedings" %}
@@ -37,7 +37,7 @@
-
Sleep
+ {% trans "Sleep" %}
@@ -45,23 +45,23 @@
-
Tummy Time
+ {% trans "Tummy Time" %}
- As the amount of entries grows, Baby Buddy will help
+ {% blocktrans %}As the amount of entries grows, Baby Buddy will help
parents and caregivers to identify small patterns in baby's habits
using the dashboard and graphs. Baby Buddy is mobile-friendly and
uses a dark theme to help weary moms and dads with 2AM feedings and
changings. To get started, just click the button below to add your
- first (or second, third, etc.) child!
+ first (or second, third, etc.) child!{% endblocktrans %}
{% if perms.core.add_child %}
- Add a Child
+ {% trans "Add a Child" %}
{% endif %}
diff --git a/babybuddy/templates/registration/login.html b/babybuddy/templates/registration/login.html
index e3158977..2a6a23c0 100644
--- a/babybuddy/templates/registration/login.html
+++ b/babybuddy/templates/registration/login.html
@@ -1,5 +1,5 @@
{% extends "registration/base.html" %}
-{% load static widget_tweaks %}
+{% load i18n static widget_tweaks %}
{% block title %}Login{% endblock %}
{% block content %}
@@ -29,12 +29,12 @@
- Login
+ {% trans "Login" %}
- Forgot your password?
+ {% trans "Forgot your password?" %}
{% endblock %}
\ No newline at end of file
diff --git a/babybuddy/templates/registration/password_reset_complete.html b/babybuddy/templates/registration/password_reset_complete.html
index 0915f701..0c1441f4 100644
--- a/babybuddy/templates/registration/password_reset_complete.html
+++ b/babybuddy/templates/registration/password_reset_complete.html
@@ -1,11 +1,12 @@
{% extends "registration/base.html" %}
+{% load i18n %}
-{% block title %}Password Reset Successfully!{% endblock %}
+{% block title %}{% trans "Password Reset Successfully!" %}{% endblock %}
{% block content %}
-
Your password has been set. You may go ahead and log in now.
-
Log in
+
{% trans "Your password has been set. You may go ahead and log in now." %}
+
{% trans "Log in" %}
{% endblock %}
\ No newline at end of file
diff --git a/babybuddy/templates/registration/password_reset_confirm.html b/babybuddy/templates/registration/password_reset_confirm.html
index 310269ea..d6e752b7 100644
--- a/babybuddy/templates/registration/password_reset_confirm.html
+++ b/babybuddy/templates/registration/password_reset_confirm.html
@@ -1,7 +1,7 @@
{% extends "registration/base.html" %}
-{% load static widget_tweaks %}
+{% load i18n static widget_tweaks %}
-{% block title %}Password Reset{% endblock %}
+{% block title %}{% trans "Password Reset" %}{% endblock %}
{% block content %}
@@ -9,13 +9,13 @@
{% if form.errors %}
-
Oh snap! The two passwords
- did not match. Please try again.
+ {% blocktrans %}
Oh snap! The
+ two passwords did not match. Please try again.
{% endblocktrans %}
{% endif %}
-
Enter your new password in each field below.
+
{% trans "Enter your new password in each field below." %}
@@ -28,7 +28,6 @@
{% render_field form.new_password1 name='new_password1' class+='form-control' id='password1-input-group' %}
-
{{ form.new_password2.label }}
@@ -40,7 +39,7 @@
- Reset Password
+ {% trans "Reset Password" %}
diff --git a/babybuddy/templates/registration/password_reset_done.html b/babybuddy/templates/registration/password_reset_done.html
index c70e2f32..c1f6c3be 100644
--- a/babybuddy/templates/registration/password_reset_done.html
+++ b/babybuddy/templates/registration/password_reset_done.html
@@ -1,15 +1,16 @@
{% extends "registration/base.html" %}
+{% load i18n %}
-{% block title %}Reset Email Sent{% endblock %}
+{% block title %}{% trans "Reset Email Sent" %}{% endblock %}
{% block content %}
-
We've emailed you instructions for setting your
+ {% blocktrans %}
We've emailed you instructions for setting your
password, if an account exists with the email you entered. You
should receive them shortly.
If you don't receive an email, please make sure you've
entered the address you registered with, and check your spam
- folder.
+ folder.{% endblocktrans %}
{% endblock %}
\ No newline at end of file
diff --git a/babybuddy/templates/registration/password_reset_form.html b/babybuddy/templates/registration/password_reset_form.html
index 071187ca..9f08f82f 100644
--- a/babybuddy/templates/registration/password_reset_form.html
+++ b/babybuddy/templates/registration/password_reset_form.html
@@ -1,13 +1,13 @@
{% extends "registration/base.html" %}
-{% load static widget_tweaks %}
+{% load i18n static widget_tweaks %}
-{% block title %}Forgot Password{% endblock %}
+{% block title %}{% trans "Forgot Password" %}{% endblock %}
{% block content %}
-
Enter your account email address in the form below. If
- the address is valid, you will receive instructions for resetting your
- password.
+ {% blocktrans %}
Enter your account email address in the
+ form below. If the address is valid, you will receive instructions for
+ resetting your password.
{% endblocktrans %}
@@ -24,7 +24,7 @@
- Reset Password
+ {% trans "Reset Password" %}
diff --git a/babybuddy/tests/tests_forms.py b/babybuddy/tests/tests_forms.py
index 21f39069..b540dadb 100644
--- a/babybuddy/tests/tests_forms.py
+++ b/babybuddy/tests/tests_forms.py
@@ -25,6 +25,14 @@ class FormsTestCase(TestCase):
cls.user = User.objects.create_user(
is_superuser=True, **cls.credentials)
+ cls.settings_template = {
+ 'first_name': 'User',
+ 'last_name': 'Name',
+ 'email': 'user@user.user',
+ 'dashboard_refresh_rate': '',
+ 'language': 'en'
+ }
+
def test_change_password(self):
self.c.login(**self.credentials)
@@ -85,18 +93,28 @@ class FormsTestCase(TestCase):
def test_user_settings(self):
self.c.login(**self.credentials)
- params = {
- 'first_name': 'User',
- 'last_name': 'Name',
- 'email': 'user@user.user',
- 'dashboard_refresh_rate': ''
- }
+ params = self.settings_template.copy()
+ params['first_name'] = 'New First Name'
- page = self.c.post('/user/settings/', params)
- self.assertEqual(page.status_code, 302)
+ page = self.c.post('/user/settings/', params, follow=True)
+ self.assertEqual(page.status_code, 200)
+ self.assertContains(page, 'New First Name')
+
+ def test_user_settings_invalid(self):
+ self.c.login(**self.credentials)
+
+ params = self.settings_template.copy()
+ params['email'] = 'Not an email address'
- params = {'email': 'Not an email address'}
page = self.c.post('/user/settings/', params)
self.assertEqual(page.status_code, 200)
self.assertFormError(page, 'user_form', 'email',
'Enter a valid email address.')
+
+ def test_user_settings_language(self):
+ self.c.login(**self.credentials)
+
+ params = self.settings_template.copy()
+ params['language'] = 'fr'
+ page = self.c.post('/user/settings/', data=params, follow=True)
+ self.assertContains(page, 'Paramètres Utilisateur')
diff --git a/babybuddy/tests/tests_models.py b/babybuddy/tests/tests_models.py
index 86251e8a..90b17c21 100644
--- a/babybuddy/tests/tests_models.py
+++ b/babybuddy/tests/tests_models.py
@@ -17,10 +17,13 @@ class SettingsTestCase(TestCase):
}
user = User.objects.create_user(is_superuser=True, **credentials)
self.assertIsInstance(user.settings, Settings)
- self.assertEqual(str(user.settings), 'Test\'s Settings')
self.assertEqual(
user.settings.dashboard_refresh_rate_milliseconds, 60000)
user.settings.dashboard_refresh_rate = None
user.save()
self.assertIsNone(user.settings.dashboard_refresh_rate_milliseconds)
+
+ user.settings.language = 'fr'
+ user.save()
+ self.assertEqual(user.settings.language, 'fr')
diff --git a/babybuddy/tests/tests_translations.py b/babybuddy/tests/tests_translations.py
new file mode 100644
index 00000000..358537ea
--- /dev/null
+++ b/babybuddy/tests/tests_translations.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+from django.test import TestCase
+from django.test import Client as HttpClient
+from django.contrib.auth.models import User
+from django.core.management import call_command
+
+from faker import Factory
+
+
+class TranslationTestCase(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TranslationTestCase, cls).setUpClass()
+ fake = Factory.create()
+ call_command('migrate', verbosity=0)
+
+ cls.c = HttpClient()
+
+ fake_user = fake.simple_profile()
+ cls.credentials = {
+ 'username': fake_user['username'],
+ 'password': fake.password()
+ }
+ cls.user = User.objects.create_user(
+ is_superuser=True, **cls.credentials)
+
+ cls.params_template = {
+ 'first_name': 'User',
+ 'last_name': 'Name',
+ 'email': 'user@user.user',
+ 'dashboard_refresh_rate': '',
+ 'language': 'en'
+ }
+
+ cls.c.login(**cls.credentials)
+
+ def test_translation_fr(self):
+ params = self.params_template.copy()
+ params['language'] = 'fr'
+
+ page = self.c.post('/user/settings/', data=params, follow=True)
+ self.assertContains(page, 'Paramètres Utilisateur')
+
+ page = self.c.get('/welcome/')
+ self.assertContains(page, 'Bienvenue à Baby Buddy!')
diff --git a/babybuddy/urls.py b/babybuddy/urls.py
index 542333d7..a18f2a11 100644
--- a/babybuddy/urls.py
+++ b/babybuddy/urls.py
@@ -53,6 +53,7 @@ urlpatterns = [
path('admin/', admin.site.urls),
path('', include('api.urls', namespace='api')),
path('', include((app_patterns, 'babybuddy'), namespace='babybuddy')),
+ path('user/lang', include('django.conf.urls.i18n')),
path('', include('core.urls', namespace='core')),
path('', include('dashboard.urls', namespace='dashboard')),
path('', include('reports.urls', namespace='reports')),
diff --git a/babybuddy/views.py b/babybuddy/views.py
index f540f9e0..691832a8 100644
--- a/babybuddy/views.py
+++ b/babybuddy/views.py
@@ -7,9 +7,12 @@ from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin
from django.shortcuts import redirect, render
from django.urls import reverse, reverse_lazy
+from django.utils.text import format_lazy
+from django.utils.translation import gettext as _, gettext_lazy
from django.views.generic import View
from django.views.generic.base import TemplateView, RedirectView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from django.views.i18n import set_language
from django_filters.views import FilterView
@@ -50,7 +53,7 @@ class UserAdd(StaffOnlyMixin, PermissionRequired403Mixin, SuccessMessageMixin,
permission_required = ('admin.add_user',)
form_class = forms.UserAddForm
success_url = reverse_lazy('babybuddy:user-list')
- success_message = 'User %(username)s added!'
+ success_message = gettext_lazy('User %(username)s added!')
class UserUpdate(StaffOnlyMixin, PermissionRequired403Mixin,
@@ -60,7 +63,7 @@ class UserUpdate(StaffOnlyMixin, PermissionRequired403Mixin,
permission_required = ('admin.change_user',)
form_class = forms.UserUpdateForm
success_url = reverse_lazy('babybuddy:user-list')
- success_message = 'User %(username)s updated.'
+ success_message = gettext_lazy('User %(username)s updated.')
class UserDelete(StaffOnlyMixin, PermissionRequired403Mixin,
@@ -71,7 +74,9 @@ class UserDelete(StaffOnlyMixin, PermissionRequired403Mixin,
success_url = reverse_lazy('babybuddy:user-list')
def delete(self, request, *args, **kwargs):
- success_message = 'User {} deleted.'.format(self.get_object())
+ success_message = format_lazy(gettext_lazy(
+ 'User {user} deleted.'), user=self.get_object()
+ )
messages.success(request, success_message)
return super(UserDelete, self).delete(request, *args, **kwargs)
@@ -93,7 +98,7 @@ class UserPassword(LoginRequiredMixin, View):
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user)
- messages.success(request, 'Password updated.')
+ messages.success(request, _('Password updated.'))
return render(request, self.template_name, {'form': form})
@@ -103,7 +108,7 @@ class UserResetAPIKey(LoginRequiredMixin, View):
"""
def get(self, request):
request.user.settings.api_key(reset=True)
- messages.success(request, 'User API key regenerated.')
+ messages.success(request, _('User API key regenerated.'))
return redirect('babybuddy:user-settings')
@@ -135,7 +140,8 @@ class UserSettings(LoginRequiredMixin, View):
user_settings = form_settings.save(commit=False)
user.settings = user_settings
user.save()
- messages.success(request, 'Settings saved!')
+ set_language(request)
+ messages.success(request, _('Settings saved!'))
return redirect('babybuddy:user-settings')
return render(request, self.template_name, {
'user_form': form_user,
diff --git a/core/forms.py b/core/forms.py
index f6621ee7..9e9b9a07 100644
--- a/core/forms.py
+++ b/core/forms.py
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from django import forms
-from django.utils import timezone
from django.conf import settings
+from django.utils import timezone
+from django.utils.translation import gettext as _
from core import models
@@ -77,7 +78,7 @@ class ChildDeleteForm(forms.ModelForm):
confirm_name = self.cleaned_data['confirm_name']
if confirm_name != str(self.instance):
raise forms.ValidationError(
- 'Name does not match child name.', code='confirm_mismatch')
+ _('Name does not match child name.'), code='confirm_mismatch')
return confirm_name
def save(self, commit=True):
diff --git a/core/migrations/0005_auto_20190416_2048.py b/core/migrations/0005_auto_20190416_2048.py
new file mode 100644
index 00000000..38e996d1
--- /dev/null
+++ b/core/migrations/0005_auto_20190416_2048.py
@@ -0,0 +1,238 @@
+# Generated by Django 2.2 on 2019-04-17 03:48
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0004_child_picture'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='child',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['last_name', 'first_name'], 'verbose_name': 'Child', 'verbose_name_plural': 'Children'},
+ ),
+ migrations.AlterModelOptions(
+ name='diaperchange',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-time'], 'verbose_name': 'Diaper Change', 'verbose_name_plural': 'Diaper Changes'},
+ ),
+ migrations.AlterModelOptions(
+ name='feeding',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-start'], 'verbose_name': 'Feeding', 'verbose_name_plural': 'Feedings'},
+ ),
+ migrations.AlterModelOptions(
+ name='note',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-time'], 'verbose_name': 'Note', 'verbose_name_plural': 'Notes'},
+ ),
+ migrations.AlterModelOptions(
+ name='sleep',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-start'], 'verbose_name': 'Sleep', 'verbose_name_plural': 'Sleep'},
+ ),
+ migrations.AlterModelOptions(
+ name='timer',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-active', '-start', '-end'], 'verbose_name': 'Timer', 'verbose_name_plural': 'Timers'},
+ ),
+ migrations.AlterModelOptions(
+ name='tummytime',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-start'], 'verbose_name': 'Tummy Time', 'verbose_name_plural': 'Tummy Time'},
+ ),
+ migrations.AlterModelOptions(
+ name='weight',
+ options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-date'], 'verbose_name': 'Weight', 'verbose_name_plural': 'Weight'},
+ ),
+ migrations.AlterField(
+ model_name='child',
+ name='birth_date',
+ field=models.DateField(verbose_name='Birth date'),
+ ),
+ migrations.AlterField(
+ model_name='child',
+ name='first_name',
+ field=models.CharField(max_length=255, verbose_name='First name'),
+ ),
+ migrations.AlterField(
+ model_name='child',
+ name='last_name',
+ field=models.CharField(max_length=255, verbose_name='Last name'),
+ ),
+ migrations.AlterField(
+ model_name='child',
+ name='picture',
+ field=models.ImageField(blank=True, null=True, upload_to='child/picture/', verbose_name='Picture'),
+ ),
+ migrations.AlterField(
+ model_name='child',
+ name='slug',
+ field=models.SlugField(editable=False, max_length=100, unique=True, verbose_name='Slug'),
+ ),
+ migrations.AlterField(
+ model_name='diaperchange',
+ name='child',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='diaper_change', to='core.Child', verbose_name='Child'),
+ ),
+ migrations.AlterField(
+ model_name='diaperchange',
+ name='color',
+ field=models.CharField(blank=True, choices=[('black', 'Black'), ('brown', 'Brown'), ('green', 'Green'), ('yellow', 'Yellow')], max_length=255, verbose_name='Color'),
+ ),
+ migrations.AlterField(
+ model_name='diaperchange',
+ name='solid',
+ field=models.BooleanField(verbose_name='Solid'),
+ ),
+ migrations.AlterField(
+ model_name='diaperchange',
+ name='time',
+ field=models.DateTimeField(verbose_name='Time'),
+ ),
+ migrations.AlterField(
+ model_name='diaperchange',
+ name='wet',
+ field=models.BooleanField(verbose_name='Wet'),
+ ),
+ migrations.AlterField(
+ model_name='feeding',
+ name='amount',
+ field=models.FloatField(blank=True, null=True, verbose_name='Amount'),
+ ),
+ migrations.AlterField(
+ model_name='feeding',
+ name='child',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feeding', to='core.Child', verbose_name='Child'),
+ ),
+ migrations.AlterField(
+ model_name='feeding',
+ name='duration',
+ field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
+ ),
+ migrations.AlterField(
+ model_name='feeding',
+ name='end',
+ field=models.DateTimeField(verbose_name='End time'),
+ ),
+ migrations.AlterField(
+ model_name='feeding',
+ name='method',
+ field=models.CharField(choices=[('bottle', 'Bottle'), ('left breast', 'Left breast'), ('right breast', 'Right breast')], max_length=255, verbose_name='Method'),
+ ),
+ migrations.AlterField(
+ model_name='feeding',
+ name='start',
+ field=models.DateTimeField(verbose_name='Start time'),
+ ),
+ migrations.AlterField(
+ model_name='feeding',
+ name='type',
+ field=models.CharField(choices=[('breast milk', 'Breast milk'), ('formula', 'Formula')], max_length=255, verbose_name='Type'),
+ ),
+ migrations.AlterField(
+ model_name='note',
+ name='child',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='note', to='core.Child', verbose_name='Child'),
+ ),
+ migrations.AlterField(
+ model_name='note',
+ name='note',
+ field=models.TextField(verbose_name='Note'),
+ ),
+ migrations.AlterField(
+ model_name='note',
+ name='time',
+ field=models.DateTimeField(auto_now=True, verbose_name='Time'),
+ ),
+ migrations.AlterField(
+ model_name='sleep',
+ name='child',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sleep', to='core.Child', verbose_name='Child'),
+ ),
+ migrations.AlterField(
+ model_name='sleep',
+ name='duration',
+ field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
+ ),
+ migrations.AlterField(
+ model_name='sleep',
+ name='end',
+ field=models.DateTimeField(verbose_name='End time'),
+ ),
+ migrations.AlterField(
+ model_name='sleep',
+ name='start',
+ field=models.DateTimeField(verbose_name='Start time'),
+ ),
+ migrations.AlterField(
+ model_name='timer',
+ name='active',
+ field=models.BooleanField(default=True, editable=False, verbose_name='Active'),
+ ),
+ migrations.AlterField(
+ model_name='timer',
+ name='duration',
+ field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
+ ),
+ migrations.AlterField(
+ model_name='timer',
+ name='end',
+ field=models.DateTimeField(blank=True, editable=False, null=True, verbose_name='End time'),
+ ),
+ migrations.AlterField(
+ model_name='timer',
+ name='name',
+ field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Name'),
+ ),
+ migrations.AlterField(
+ model_name='timer',
+ name='start',
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Start time'),
+ ),
+ migrations.AlterField(
+ model_name='timer',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='timers', to=settings.AUTH_USER_MODEL, verbose_name='User'),
+ ),
+ migrations.AlterField(
+ model_name='tummytime',
+ name='child',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tummy_time', to='core.Child', verbose_name='Child'),
+ ),
+ migrations.AlterField(
+ model_name='tummytime',
+ name='duration',
+ field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
+ ),
+ migrations.AlterField(
+ model_name='tummytime',
+ name='end',
+ field=models.DateTimeField(verbose_name='End time'),
+ ),
+ migrations.AlterField(
+ model_name='tummytime',
+ name='milestone',
+ field=models.CharField(blank=True, max_length=255, verbose_name='Milestone'),
+ ),
+ migrations.AlterField(
+ model_name='tummytime',
+ name='start',
+ field=models.DateTimeField(verbose_name='Start time'),
+ ),
+ migrations.AlterField(
+ model_name='weight',
+ name='child',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='weight', to='core.Child', verbose_name='Child'),
+ ),
+ migrations.AlterField(
+ model_name='weight',
+ name='date',
+ field=models.DateField(verbose_name='Date'),
+ ),
+ migrations.AlterField(
+ model_name='weight',
+ name='weight',
+ field=models.FloatField(verbose_name='Weight'),
+ ),
+ ]
diff --git a/core/models.py b/core/models.py
index 38e5c27a..0acc3123 100644
--- a/core/models.py
+++ b/core/models.py
@@ -6,6 +6,8 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.template.defaultfilters import slugify
from django.utils import timezone
+from django.utils.text import format_lazy
+from django.utils.translation import gettext_lazy as _
def validate_date(date, field_name):
@@ -17,7 +19,7 @@ def validate_date(date, field_name):
"""
if date and date > timezone.localdate():
raise ValidationError(
- {field_name: 'Date can not be in the future.'},
+ {field_name: _('Date can not be in the future.')},
code='date_invalid')
@@ -31,10 +33,10 @@ def validate_duration(model, max_duration=timedelta(hours=24)):
if model.start and model.end:
if model.start > model.end:
raise ValidationError(
- 'Start time must come before end time.',
+ _('Start time must come before end time.'),
code='end_before_start')
if model.end - model.start > max_duration:
- raise ValidationError('Duration too long.', code='max_duration')
+ raise ValidationError(_('Duration too long.'), code='max_duration')
def validate_unique_period(queryset, model):
@@ -50,7 +52,7 @@ def validate_unique_period(queryset, model):
if model.start and model.end:
if queryset.filter(start__lte=model.end, end__gte=model.start):
raise ValidationError(
- 'Another entry intersects the specified time period.',
+ _('Another entry intersects the specified time period.'),
code='period_intersection')
@@ -63,20 +65,30 @@ def validate_time(time, field_name):
"""
if time and time > timezone.localtime():
raise ValidationError(
- {field_name: 'Date/time can not be in the future.'},
+ {field_name: _('Date/time can not be in the future.')},
code='time_invalid')
class Child(models.Model):
model_name = 'child'
- first_name = models.CharField(max_length=255)
- last_name = models.CharField(max_length=255)
- birth_date = models.DateField(blank=False, null=False)
- slug = models.SlugField(max_length=100, unique=True, editable=False)
+ first_name = models.CharField(max_length=255, verbose_name=_('First name'))
+ last_name = models.CharField(max_length=255, verbose_name=_('Last name'))
+ birth_date = models.DateField(
+ blank=False,
+ null=False,
+ verbose_name=_('Birth date')
+ )
+ slug = models.SlugField(
+ editable=False,
+ max_length=100,
+ unique=True,
+ verbose_name=_('Slug')
+ )
picture = models.ImageField(
- upload_to='child/picture/',
blank=True,
- null=True
+ null=True,
+ upload_to='child/picture/',
+ verbose_name=_('Picture')
)
objects = models.Manager()
@@ -84,7 +96,8 @@ class Child(models.Model):
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['last_name', 'first_name']
- verbose_name_plural = 'Children'
+ verbose_name = _('Child')
+ verbose_name_plural = _('Children')
def __str__(self):
return '{} {}'.format(self.first_name, self.last_name)
@@ -103,25 +116,40 @@ class Child(models.Model):
class DiaperChange(models.Model):
model_name = 'diaperchange'
child = models.ForeignKey(
- 'Child', related_name='diaper_change', on_delete=models.CASCADE)
- time = models.DateTimeField(blank=False, null=False)
- wet = models.BooleanField()
- solid = models.BooleanField()
- color = models.CharField(max_length=255, blank=True, choices=[
- ('black', 'Black'),
- ('brown', 'Brown'),
- ('green', 'Green'),
- ('yellow', 'Yellow'),
- ])
+ 'Child',
+ on_delete=models.CASCADE,
+ related_name='diaper_change',
+ verbose_name=_('Child')
+ )
+ time = models.DateTimeField(
+ blank=False,
+ null=False,
+ verbose_name=_('Time')
+ )
+ wet = models.BooleanField(verbose_name=_('Wet'))
+ solid = models.BooleanField(verbose_name=_('Solid'))
+ color = models.CharField(
+ blank=True,
+ choices=[
+ ('black', _('Black')),
+ ('brown', _('Brown')),
+ ('green', _('Green')),
+ ('yellow', _('Yellow')),
+ ],
+ max_length=255,
+ verbose_name=_('Color')
+ )
objects = models.Manager()
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-time']
+ verbose_name = _('Diaper Change')
+ verbose_name_plural = _('Diaper Changes')
def __str__(self):
- return 'Diaper Change'
+ return str(_('Diaper Change'))
def attributes(self):
attributes = []
@@ -139,35 +167,58 @@ class DiaperChange(models.Model):
# One or both of Wet and Solid is required.
if not self.wet and not self.solid:
raise ValidationError(
- 'Wet and/or solid is required.', code='wet_or_solid')
+ _('Wet and/or solid is required.'), code='wet_or_solid')
class Feeding(models.Model):
model_name = 'feeding'
child = models.ForeignKey(
- 'Child', related_name='feeding', on_delete=models.CASCADE)
- start = models.DateTimeField(blank=False, null=False)
- end = models.DateTimeField(blank=False, null=False)
- duration = models.DurationField(null=True, editable=False)
- type = models.CharField(max_length=255, choices=[
- ('breast milk', 'Breast milk'),
- ('formula', 'Formula'),
- ])
- method = models.CharField(max_length=255, choices=[
- ('bottle', 'Bottle'),
- ('left breast', 'Left breast'),
- ('right breast', 'Right breast'),
- ])
- amount = models.FloatField(blank=True, null=True)
+ 'Child',
+ on_delete=models.CASCADE,
+ related_name='feeding',
+ verbose_name=_('Child')
+ )
+ start = models.DateTimeField(
+ blank=False,
+ null=False,
+ verbose_name=_('Start time')
+ )
+ end = models.DateTimeField(
+ blank=False,
+ null=False,
+ verbose_name=_('End time')
+ )
+ duration = models.DurationField(
+ editable=False,
+ null=True,
+ verbose_name=_('Duration')
+ )
+ type = models.CharField(
+ choices=[('breast milk', _('Breast milk')), ('formula', _('Formula'))],
+ max_length=255,
+ verbose_name=_('Type')
+ )
+ method = models.CharField(
+ choices=[
+ ('bottle', _('Bottle')),
+ ('left breast', _('Left breast')),
+ ('right breast', _('Right breast')),
+ ],
+ max_length=255,
+ verbose_name=_('Method')
+ )
+ amount = models.FloatField(blank=True, null=True, verbose_name=_('Amount'))
objects = models.Manager()
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-start']
+ verbose_name = _('Feeding')
+ verbose_name_plural = _('Feedings')
def __str__(self):
- return 'Feeding'
+ return str(_('Feeding'))
def save(self, *args, **kwargs):
if self.start and self.end:
@@ -184,25 +235,31 @@ class Feeding(models.Model):
if self.type == 'formula'and self.method != 'bottle':
raise ValidationError(
{'method':
- 'Only "Bottle" method is allowed with "Formula" type.'},
+ _('Only "Bottle" method is allowed with "Formula" type.')},
code='bottle_formula_mismatch')
class Note(models.Model):
model_name = 'note'
child = models.ForeignKey(
- 'Child', related_name='note', on_delete=models.CASCADE)
- note = models.TextField()
- time = models.DateTimeField(auto_now=True)
+ 'Child',
+ on_delete=models.CASCADE,
+ related_name='note',
+ verbose_name=_('Child')
+ )
+ note = models.TextField(verbose_name=_('Note'))
+ time = models.DateTimeField(auto_now=True, verbose_name=_('Time'))
objects = models.Manager()
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-time']
+ verbose_name = _('Note')
+ verbose_name_plural = _('Notes')
def __str__(self):
- return 'Note'
+ return str(_('Note'))
class NapsManager(models.Manager):
@@ -214,10 +271,26 @@ class NapsManager(models.Manager):
class Sleep(models.Model):
model_name = 'sleep'
child = models.ForeignKey(
- 'Child', related_name='sleep', on_delete=models.CASCADE)
- start = models.DateTimeField(blank=False, null=False)
- end = models.DateTimeField(blank=False, null=False)
- duration = models.DurationField(null=True, editable=False)
+ 'Child',
+ on_delete=models.CASCADE,
+ related_name='sleep',
+ verbose_name=_('Child')
+ )
+ start = models.DateTimeField(
+ blank=False,
+ null=False,
+ verbose_name=_('Start time')
+ )
+ end = models.DateTimeField(
+ blank=False,
+ null=False,
+ verbose_name=_('End time')
+ )
+ duration = models.DurationField(
+ editable=False,
+ null=True,
+ verbose_name=_('Duration')
+ )
objects = models.Manager()
naps = NapsManager()
@@ -225,10 +298,11 @@ class Sleep(models.Model):
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-start']
- verbose_name_plural = 'Sleep'
+ verbose_name = _('Sleep')
+ verbose_name_plural = _('Sleep')
def __str__(self):
- return 'Sleep'
+ return str(_('Sleep'))
@property
def nap(self):
@@ -253,26 +327,50 @@ class Sleep(models.Model):
class Timer(models.Model):
model_name = 'timer'
- name = models.CharField(max_length=255, null=True, blank=True)
+ name = models.CharField(
+ blank=True,
+ max_length=255,
+ null=True,
+ verbose_name=_('Name')
+ )
start = models.DateTimeField(
default=timezone.now,
blank=False,
- verbose_name='Start Time'
+ verbose_name=_('Start time')
+ )
+ end = models.DateTimeField(
+ blank=True,
+ editable=False,
+ null=True,
+ verbose_name=_('End time')
+ )
+ duration = models.DurationField(
+ editable=False,
+ null=True,
+ verbose_name=_('Duration')
+ )
+ active = models.BooleanField(
+ default=True,
+ editable=False,
+ verbose_name=_('Active')
)
- end = models.DateTimeField(blank=True, null=True, editable=False)
- duration = models.DurationField(null=True, editable=False)
- active = models.BooleanField(default=True, editable=False)
user = models.ForeignKey(
- 'auth.User', related_name='timers', on_delete=models.CASCADE)
+ 'auth.User',
+ on_delete=models.CASCADE,
+ related_name='timers',
+ verbose_name=_('User')
+ )
objects = models.Manager()
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-active', '-start', '-end']
+ verbose_name = _('Timer')
+ verbose_name_plural = _('Timers')
def __str__(self):
- return self.name or 'Timer #{}'.format(self.id)
+ return self.name or str(format_lazy(_('Timer #{id}'), id=self.id))
@classmethod
def from_db(cls, db, field_names, values):
@@ -315,20 +413,42 @@ class Timer(models.Model):
class TummyTime(models.Model):
model_name = 'tummytime'
child = models.ForeignKey(
- 'Child', related_name='tummy_time', on_delete=models.CASCADE)
- start = models.DateTimeField(blank=False, null=False)
- end = models.DateTimeField(blank=False, null=False)
- duration = models.DurationField(null=True, editable=False)
- milestone = models.CharField(max_length=255, blank=True)
+ 'Child',
+ on_delete=models.CASCADE,
+ related_name='tummy_time',
+ verbose_name=_('Child')
+ )
+ start = models.DateTimeField(
+ blank=False,
+ null=False,
+ verbose_name=_('Start time')
+ )
+ end = models.DateTimeField(
+ blank=False,
+ null=False,
+ verbose_name=_('End time')
+ )
+ duration = models.DurationField(
+ editable=False,
+ null=True,
+ verbose_name=_('Duration')
+ )
+ milestone = models.CharField(
+ blank=True,
+ max_length=255,
+ verbose_name=_('Milestone')
+ )
objects = models.Manager()
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-start']
+ verbose_name = _('Tummy Time')
+ verbose_name_plural = _('Tummy Time')
def __str__(self):
- return 'Tummy Time'
+ return str(_('Tummy Time'))
def save(self, *args, **kwargs):
if self.start and self.end:
@@ -346,19 +466,32 @@ class TummyTime(models.Model):
class Weight(models.Model):
model_name = 'weight'
child = models.ForeignKey(
- 'Child', related_name='weight', on_delete=models.CASCADE)
- weight = models.FloatField(blank=False, null=False)
- date = models.DateField(blank=False, null=False)
+ 'Child',
+ on_delete=models.CASCADE,
+ related_name='weight',
+ verbose_name=_('Child')
+ )
+ weight = models.FloatField(
+ blank=False,
+ null=False,
+ verbose_name=_('Weight')
+ )
+ date = models.DateField(
+ blank=False,
+ null=False,
+ verbose_name=_('Date')
+ )
objects = models.Manager()
class Meta:
default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-date']
- verbose_name_plural = 'Weight'
+ verbose_name = _('Weight')
+ verbose_name_plural = _('Weight')
def __str__(self):
- return 'Weight'
+ return str(_('Weight'))
def clean(self):
validate_date(self.date, 'date')
diff --git a/core/templates/core/child_confirm_delete.html b/core/templates/core/child_confirm_delete.html
index 07a14a27..a8357fb7 100644
--- a/core/templates/core/child_confirm_delete.html
+++ b/core/templates/core/child_confirm_delete.html
@@ -1,20 +1,22 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete a Child{% endblock %}
+{% block title %}{% trans "Delete a Child" %}{% endblock %}
{% block breadcrumbs %}
- Children
+ {% trans "Children" %}
{{ object }}
- Delete
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
-
- Cancel
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/child_detail.html b/core/templates/core/child_detail.html
index 6b0f8f2e..f5f99c6c 100644
--- a/core/templates/core/child_detail.html
+++ b/core/templates/core/child_detail.html
@@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %}
-{% load static thumbnail %}
+{% load i18n static thumbnail %}
{% block title %}{{ object }}{% endblock %}
{% block breadcrumbs %}
- Children
+ {% trans "Children" %}
{{ object }}
{% endblock %}
@@ -20,8 +20,8 @@
{% endif %}
{{ object }}
- Born {{ object.birth_date }}
- Age {{ object.birth_date|timesince }}
+ {% trans "Born" %} {{ object.birth_date }}
+ {% trans "Age" %} {{ object.birth_date|timesince }}
{% include 'dashboard/child_button_group.html' %}
@@ -31,16 +31,16 @@
@@ -55,7 +55,7 @@
{{ object.event }}
diff --git a/core/templates/core/child_form.html b/core/templates/core/child_form.html
index d53b0630..04bf3853 100644
--- a/core/templates/core/child_form.html
+++ b/core/templates/core/child_form.html
@@ -1,28 +1,29 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if object %}
{{ object }}
{% else %}
- Add a Child
+ {% trans "Add a Child" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Children
+ {% trans "Children" %}
{% if object %}
{{ object }}
- Update
+ {% trans "Update" %}
{% else %}
- Add a Child
+ {% trans "Add a Child" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Add a Child
+ {% trans "Add a Child" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/core/templates/core/child_list.html b/core/templates/core/child_list.html
index 47b3730c..e571f92e 100644
--- a/core/templates/core/child_list.html
+++ b/core/templates/core/child_list.html
@@ -1,24 +1,24 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks static thumbnail %}
+{% load i18n static thumbnail widget_tweaks %}
-{% block title %}Children{% endblock %}
+{% block title %}{% trans "Children" %}{% endblock %}
{% block breadcrumbs %}
- Children
+ {% trans "Children" %}
{% endblock %}
{% block content %}
- Children
+ {% trans "Children" %}
{% include 'babybuddy/filter.html' %}
- First Name
- Last Name
- Birth Date
- Actions
+ {% trans "First Name" %}
+ {% trans "Last Name" %}
+ {% trans "Birth Date" %}
+ {% trans "Actions" %}
@@ -38,7 +38,7 @@
{{ child.last_name }}
{{ child.birth_date }}
-
@@ -67,7 +67,7 @@
{% if perms.core.add_child %}
- Add a Child
+ {% trans "Add a Child" %}
{% endif %}
diff --git a/core/templates/core/diaperchange_confirm_delete.html b/core/templates/core/diaperchange_confirm_delete.html
index 835b4053..8336fe55 100644
--- a/core/templates/core/diaperchange_confirm_delete.html
+++ b/core/templates/core/diaperchange_confirm_delete.html
@@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete a Diaper Change{% endblock %}
+{% block title %}{% trans "Delete a Diaper Change" %}{% endblock %}
{% block breadcrumbs %}
- Diaper Changes
- Delete
+ {% trans "Diaper Changes" %}
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
-
- Cancel
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/diaperchange_form.html b/core/templates/core/diaperchange_form.html
index d515fd48..8f3dd0be 100644
--- a/core/templates/core/diaperchange_form.html
+++ b/core/templates/core/diaperchange_form.html
@@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if request.resolver_match.url_name == 'diaperchange-update' %}
- Update a Diaper Change
+ {% trans "Update a Diaper Change" %}
{% else %}
- Add a Diaper Change
+ {% trans "Add a Diaper Change" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Diaper Changes
+ {% trans "Diaper Changes" %}
{% if object %}
- Update
+ {% trans "Update" %}
{% else %}
- Add
+ {% trans "Add" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Add a Diaper Change
+ {% trans "Add a Diaper Change" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/core/templates/core/diaperchange_list.html b/core/templates/core/diaperchange_list.html
index bc4cad91..1412219b 100644
--- a/core/templates/core/diaperchange_list.html
+++ b/core/templates/core/diaperchange_list.html
@@ -1,26 +1,25 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
-{% load bootstrap %}
+{% load bootstrap i18n widget_tweaks %}
-{% block title %}Diaper Changes{% endblock %}
+{% block title %}{% trans "Diaper Changes" %}{% endblock %}
{% block breadcrumbs %}
- Diaper Changes
+ {% trans "Diaper Changes" %}
{% endblock %}
{% block content %}
- Diaper Changes
+ {% trans "Diaper Changes" %}
{% include 'babybuddy/filter.html' %}
- Child
- Wet
- Solid
- Color
- Time
- Actions
+ {% trans "Child" %}
+ {% trans "Wet" %}
+ {% trans "Solid" %}
+ {% trans "Color" %}
+ {% trans "Time" %}
+ {% trans "Actions" %}
@@ -32,7 +31,7 @@
{{ change.color }}
{{ change.time|date:'n/j/y G:i' }}
-
@@ -61,7 +60,7 @@
{% if perms.core.add_diaperchange %}
- Add a Change
+ {% trans "Add a Change" %}
{% endif %}
diff --git a/core/templates/core/feeding_confirm_delete.html b/core/templates/core/feeding_confirm_delete.html
index 5d294ec1..8d5a0b8f 100644
--- a/core/templates/core/feeding_confirm_delete.html
+++ b/core/templates/core/feeding_confirm_delete.html
@@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete a Feeding{% endblock %}
+{% block title %}{% trans "Delete a Feeding" %}{% endblock %}
{% block breadcrumbs %}
- Feedings
- Delete
+ {% trans "Feedings" %}
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
-
- Cancel
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/feeding_form.html b/core/templates/core/feeding_form.html
index b8c95d39..734f9895 100644
--- a/core/templates/core/feeding_form.html
+++ b/core/templates/core/feeding_form.html
@@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if request.resolver_match.url_name == 'feeding-update' %}
- Update a Feeding
+ {% trans "Update a Feeding" %}
{% else %}
- Add a Feeding
+ {% trans "Add a Feeding" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Feedings
+ {% trans "Feedings" %}
{% if object %}
- Update
+ {% trans "Update" %}
{% else %}
- Add
+ {% trans "Add" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Add a Feeding
+ {% trans "Add a Feeding" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/core/templates/core/feeding_list.html b/core/templates/core/feeding_list.html
index 205df467..af48aad2 100644
--- a/core/templates/core/feeding_list.html
+++ b/core/templates/core/feeding_list.html
@@ -1,27 +1,27 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
-{% load duration %}
+{% load duration i18n widget_tweaks %}
-{% block title %}Feedings{% endblock %}
+{% block title %}{% trans "Feedings" %}{% endblock %}
{% block breadcrumbs %}
- Feedings
+ {% trans "Feedings" %}
{% endblock %}
{% block content %}
- Feedings
+ {% trans "Feedings" %}
{% include 'babybuddy/filter.html' %}
- Child
- Method
- Type
- Amt.
- Duration
- Date
- Actions
+ {% trans "Child" %}
+ {% trans "Method" %}
+ {% trans "Type" %}
+ {% comment %}Abbreviation of "Amount"{% endcomment %}
+ {% trans "Amt." %}
+ {% trans "Duration" %}
+ {% trans "Date" %}
+ {% trans "Actions" %}
@@ -38,7 +38,7 @@
{{ feeding.duration|duration_string }}
{{ feeding.start|date:'n/j/y G:i' }}
-
@@ -67,7 +67,7 @@
{% if perms.core.add_feeding %}
- Add a Feeding
+ {% trans "Add a Feeding" %}
{% endif %}
diff --git a/core/templates/core/note_confirm_delete.html b/core/templates/core/note_confirm_delete.html
index 6fc62977..a70ffa02 100644
--- a/core/templates/core/note_confirm_delete.html
+++ b/core/templates/core/note_confirm_delete.html
@@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete a Note{% endblock %}
+{% block title %}{% trans "Delete a Note" %}{% endblock %}
{% block breadcrumbs %}
- Notes
- Delete
+ {% trans "Notes" %}
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
-
- Cancel
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/note_form.html b/core/templates/core/note_form.html
index e0142f89..1718fa3f 100644
--- a/core/templates/core/note_form.html
+++ b/core/templates/core/note_form.html
@@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if request.resolver_match.url_name == 'note-update' %}
- Update a Note
+ {% trans "Update a Note" %}
{% else %}
- Add a Note
+ {% trans "Add a Note" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Notes
+ {% trans "Notes" %}
{% if object %}
- Update
+ {% trans "Update" %}
{% else %}
- Add
+ {% trans "Add" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Add a Note
+ {% trans "Add a Note" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/note_list.html b/core/templates/core/note_list.html
index 6ccabe7d..fccb94cf 100644
--- a/core/templates/core/note_list.html
+++ b/core/templates/core/note_list.html
@@ -1,23 +1,23 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Notes{% endblock %}
+{% block title %}{% trans "Notes" %}{% endblock %}
{% block breadcrumbs %}
- Notes
+ {% trans "Notes" %}
{% endblock %}
{% block content %}
- Notes
+ {% trans "Notes" %}
{% include 'babybuddy/filter.html' %}
- Child
- Note
- Time
- Actions
+ {% trans "Child" %}
+ {% trans "Note" %}
+ {% trans "Time" %}
+ {% trans "Actions" %}
@@ -27,7 +27,7 @@
{{ note.note }}
{{ note.time }}
-
@@ -56,7 +56,7 @@
{% if perms.core.add_note %}
- Add a Note
+ {% trans "Add a Note" %}
{% endif %}
diff --git a/core/templates/core/sleep_confirm_delete.html b/core/templates/core/sleep_confirm_delete.html
index 1756fdcd..56c265e4 100644
--- a/core/templates/core/sleep_confirm_delete.html
+++ b/core/templates/core/sleep_confirm_delete.html
@@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete a Sleep Entry{% endblock %}
+{% block title %}{% trans "Delete a Sleep Entry" %}{% endblock %}
{% block breadcrumbs %}
- Sleep
- Delete
+ {% trans "Sleep" %}
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
-
- Cancel
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/sleep_form.html b/core/templates/core/sleep_form.html
index 9ec3f9dc..ec09437f 100644
--- a/core/templates/core/sleep_form.html
+++ b/core/templates/core/sleep_form.html
@@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if request.resolver_match.url_name == 'sleep-update' %}
- Update a Sleep Entry
+ {% trans "Update a Sleep Entry" %}
{% else %}
- Add a Sleep Entry
+ {% trans "Add a Sleep Entry" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Sleep
+ {% trans "Sleep" %}
{% if object %}
- Update
+ {% trans "Update" %}
{% else %}
- Add
+ {% trans "Add" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Add a Sleep Entry
+ {% trans "Add a Sleep Entry" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/core/templates/core/sleep_list.html b/core/templates/core/sleep_list.html
index a25fa28d..dd2c4c15 100644
--- a/core/templates/core/sleep_list.html
+++ b/core/templates/core/sleep_list.html
@@ -1,25 +1,25 @@
{% extends 'babybuddy/page.html' %}
-{% load bootstrap duration widget_tweaks %}
+{% load bootstrap duration i18n widget_tweaks %}
-{% block title %}Sleep{% endblock %}
+{% block title %}{% trans "Sleep" %}{% endblock %}
{% block breadcrumbs %}
- Sleep
+ {% trans "Sleep" %}
{% endblock %}
{% block content %}
- Sleep
+ {% trans "Sleep" %}
{% include 'babybuddy/filter.html' %}
- Child
- Duration
- Start
- End
- Nap
- Actions
+ {% trans "Child" %}
+ {% trans "Duration" %}
+ {% trans "Start" %}
+ {% trans "End" %}
+ {% trans "Nap" %}
+ {% trans "Actions" %}
@@ -31,7 +31,7 @@
{{ sleep.end|date:'n/j/y G:i' }}
{{ sleep.nap|bool_icon }}
-
@@ -60,7 +60,7 @@
{% if perms.core.add_sleep %}
- Add a Sleep Entry
+ {% trans "Add a Sleep Entry" %}
{% endif %}
diff --git a/core/templates/core/timer_confirm_delete.html b/core/templates/core/timer_confirm_delete.html
index 54e56223..853effb8 100644
--- a/core/templates/core/timer_confirm_delete.html
+++ b/core/templates/core/timer_confirm_delete.html
@@ -1,19 +1,19 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete {{ object }}{% endblock %}
+{% block title %}{% blocktrans %}Delete {{ object }}{% endblocktrans %}{% endblock %}
{% block breadcrumbs %}
- Timers
+ {% trans "Timers" %}
{{ object }}
- Delete
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
-
- Cancel
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/timer_detail.html b/core/templates/core/timer_detail.html
index 65a0b9f5..88d11bd8 100644
--- a/core/templates/core/timer_detail.html
+++ b/core/templates/core/timer_detail.html
@@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %}
-{% load duration timers %}
+{% load duration i18n timers %}
{% block title %}{{ object }}{% endblock %}
{% block breadcrumbs %}
- Timers
+ {% trans "Timers" %}
{{ object }}
{% endblock %}
@@ -17,34 +17,40 @@
{{ object.duration|seconds }} s
- Started {{ object.start }}
+ {% trans "Started" %} {{ object.start }}
{% if not object.active %}
- / Stopped {{ object.end }}
+ / {% trans "Stopped" %} {{ object.end }}
{% endif %}
- {{ timer }} created by {{ object.user }}
+ {% blocktrans %}{{ timer }} created by {{ object.user }}{% endblocktrans %}
{% if perms.core.add_feeding %}
Feeding
+ role="button">
+ {% trans "Feeding" %}
+
{% endif %}
{% if perms.core.add_sleep %}
Sleep
+ role="button">
+ {% trans "Sleep" %}
+
{% endif %}
{% if perms.core.add_tummytime %}
Tummy Time
+ role="button">
+ {% trans "Tummy Time" %}
+
{% endif %}
-
+
{% if perms.core.delete_timer %}
Timers
+
{% trans "Timers" %}
{% if object %}
{{ object }}
-
Update
+
{% trans "Update" %}
{% else %}
-
Start
+
{% trans "Start" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
-
Update {{ object }}
+ {% blocktrans %}
Update {{ object }} {% endblocktrans %}
{% else %}
-
Start Timer
+
{% trans "Start Timer" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/core/templates/core/timer_list.html b/core/templates/core/timer_list.html
index 6bb1afd2..064ac3f1 100644
--- a/core/templates/core/timer_list.html
+++ b/core/templates/core/timer_list.html
@@ -1,25 +1,25 @@
{% extends 'babybuddy/page.html' %}
-{% load bootstrap duration widget_tweaks %}
+{% load bootstrap duration i18n widget_tweaks %}
-{% block title %}Timers{% endblock %}
+{% block title %}{% trans "Timers" %}{% endblock %}
{% block breadcrumbs %}
-
Timers
+
{% trans "Timers" %}
{% endblock %}
{% block content %}
-
Timers
+
{% trans "Timers" %}
{% include 'babybuddy/filter.html' %}
- Name
- Start
- Duration
- End
- Active
- User
+ {% trans "Name" %}
+ {% trans "Start" %}
+ {% trans "Duration" %}
+ {% trans "End" %}
+ {% trans "Active" %}
+ {% trans "User" %}
@@ -34,7 +34,7 @@
{% empty %}
- No timer entries found.
+ {% trans "No timer entries found." %}
{% endfor %}
diff --git a/core/templates/core/timer_nav.html b/core/templates/core/timer_nav.html
index 92085515..75bed9d6 100644
--- a/core/templates/core/timer_nav.html
+++ b/core/templates/core/timer_nav.html
@@ -1,30 +1,34 @@
+{% load i18n %}
+
+ aria-expanded="false">
+ {% trans "Timers" %}
+
diff --git a/core/templates/core/tummytime_confirm_delete.html b/core/templates/core/tummytime_confirm_delete.html
index 88cc3437..cc613da0 100644
--- a/core/templates/core/tummytime_confirm_delete.html
+++ b/core/templates/core/tummytime_confirm_delete.html
@@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete a Tummy Time Entry{% endblock %}
+{% block title %}{% trans "Delete a Tummy Time Entry" %}{% endblock %}
{% block breadcrumbs %}
- Tummy Time
- Delete
+ {% trans "Tummy Time" %}
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
-
- Cancel
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/tummytime_form.html b/core/templates/core/tummytime_form.html
index 9d05b1a5..63bf927e 100644
--- a/core/templates/core/tummytime_form.html
+++ b/core/templates/core/tummytime_form.html
@@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if request.resolver_match.url_name == 'tummytime-update' %}
- Update a Tummy Time Entry
+ {% trans "Update a Tummy Time Entry" %}
{% else %}
- Add a Tummy Time Entry
+ {% trans "Add a Tummy Time Entry" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Tummy Time
+ {% trans "Tummy Time" %}
{% if object %}
- Update
+ {% trans "Update" %}
{% else %}
- Add
+ {% trans "Add" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Add a Tummy Time Entry
+ {% trans "Add a Tummy Time Entry" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/core/templates/core/tummytime_list.html b/core/templates/core/tummytime_list.html
index dab1bc57..84177d36 100644
--- a/core/templates/core/tummytime_list.html
+++ b/core/templates/core/tummytime_list.html
@@ -1,26 +1,25 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
-{% load duration %}
+{% load duration i18n widget_tweaks %}
-{% block title %}Tummy Time{% endblock %}
+{% block title %}{% trans "Tummy Time" %}{% endblock %}
{% block breadcrumbs %}
- Tummy Time
+ {% trans "Tummy Time" %}
{% endblock %}
{% block content %}
- Tummy Time
+ {% trans "Tummy Time" %}
{% include 'babybuddy/filter.html' %}
- Child
- Duration
- Start
- End
- Milestone
- Actions
+ {% trans "Child" %}
+ {% trans "Duration" %}
+ {% trans "Start" %}
+ {% trans "End" %}
+ {% trans "Milestone" %}
+ {% trans "Actions" %}
@@ -32,7 +31,7 @@
{{ tummytime.end|date:'n/j/y G:i' }}
{{ tummytime.milestone }}
-
@@ -61,7 +60,7 @@
{% if perms.core.add_tummytime %}
- Add a Tummy Time Entry
+ {% trans "Add a Tummy Time Entry" %}
{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/weight_confirm_delete.html b/core/templates/core/weight_confirm_delete.html
index 839a58a1..40cb1759 100644
--- a/core/templates/core/weight_confirm_delete.html
+++ b/core/templates/core/weight_confirm_delete.html
@@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Delete a Weight Entry{% endblock %}
+{% block title %}{% trans "Delete a Weight Entry" %}{% endblock %}
{% block breadcrumbs %}
- Weight
- Delete
+ {% trans "Weight" %}
+ {% trans "Delete" %}
{% endblock %}
{% block content %}
{% csrf_token %}
- Are you sure you want to delete {{ object }} ?
-
- Cancel
+ {% blocktrans %}Are you sure you want to delete {{ object }} ? {% endblocktrans %}
+
+ {% trans "Cancel" %}
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/weight_form.html b/core/templates/core/weight_form.html
index 1575e5e3..40d567d5 100644
--- a/core/templates/core/weight_form.html
+++ b/core/templates/core/weight_form.html
@@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %}
+{% load i18n %}
{% block title %}
{% if object %}
{{ object }}
{% else %}
- Add a Weight Entry
+ {% trans "Add a Weight Entry" %}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
- Weight
+ {% trans "Weight" %}
{% if object %}
- Update
+ {% trans "Update" %}
{% else %}
- Add a Weight Entry
+ {% trans "Add a Weight Entry" %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
- Update {{ object }}
+ {% blocktrans %}Update {{ object }} {% endblocktrans %}
{% else %}
- Add a Weight Entry
+ {% trans "Add a Weight Entry" %}
{% endif %}
{% include 'babybuddy/form.html' %}
{% endblock %}
diff --git a/core/templates/core/weight_list.html b/core/templates/core/weight_list.html
index f7c6689d..4cc90c4e 100644
--- a/core/templates/core/weight_list.html
+++ b/core/templates/core/weight_list.html
@@ -1,23 +1,23 @@
{% extends 'babybuddy/page.html' %}
-{% load widget_tweaks %}
+{% load i18n widget_tweaks %}
-{% block title %}Weight{% endblock %}
+{% block title %}{% trans "Weight" %}{% endblock %}
{% block breadcrumbs %}
- Weight
+ {% trans "Weight" %}
{% endblock %}
{% block content %}
- Weight
+ {% trans "Weight" %}
{% include 'babybuddy/filter.html' %}
- Child
- Weight
- Date
- Actions
+ {% trans "Child" %}
+ {% trans "Weight" %}
+ {% trans "Date" %}
+ {% trans "Actions" %}
@@ -27,7 +27,7 @@
{{ object.weight }}
{{ object.date }}
-
@@ -56,7 +56,7 @@
{% if perms.core.add_weight %}
- Add a Weight Entry
+ {% trans "Add a Weight Entry" %}
{% endif %}
diff --git a/core/timeline.py b/core/timeline.py
index e3e7da7a..267d476d 100644
--- a/core/timeline.py
+++ b/core/timeline.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from django.utils import timezone
+from django.utils.translation import gettext as _
from core.models import DiaperChange, Feeding, Sleep, TummyTime
@@ -20,7 +21,9 @@ def get_objects(child, date):
for instance in instances:
events.append({
'time': timezone.localtime(instance.time),
- 'event': '{} had a diaper change.'.format(child.first_name),
+ 'event': _('%(child)s had a diaper change.') % {
+ 'child': child.first_name
+ },
'model_name': instance.model_name,
})
@@ -29,13 +32,17 @@ def get_objects(child, date):
for instance in instances:
events.append({
'time': timezone.localtime(instance.start),
- 'event': '{} started feeding.'.format(instance.child.first_name),
+ 'event': _('%(child)s started feeding.') % {
+ 'child': instance.child.first_name
+ },
'model_name': instance.model_name,
'type': 'start'
})
events.append({
'time': timezone.localtime(instance.end),
- 'event': '{} finished feeding.'.format(instance.child.first_name),
+ 'event': _('%(child)s finished feeding.') % {
+ 'child': instance.child.first_name
+ },
'model_name': instance.model_name,
'type': 'end'
})
@@ -45,13 +52,17 @@ def get_objects(child, date):
for instance in instances:
events.append({
'time': timezone.localtime(instance.start),
- 'event': '{} fell asleep.'.format(instance.child.first_name),
+ 'event': _('%(child)s fell asleep.') % {
+ 'child': instance.child.first_name
+ },
'model_name': instance.model_name,
'type': 'start'
})
events.append({
'time': timezone.localtime(instance.end),
- 'event': '{} woke up.'.format(instance.child.first_name),
+ 'event': _('%(child)s woke up.') % {
+ 'child': instance.child.first_name
+ },
'model_name': instance.model_name,
'type': 'end'
})
@@ -61,15 +72,17 @@ def get_objects(child, date):
for instance in instances:
events.append({
'time': timezone.localtime(instance.start),
- 'event': '{} started tummy time!'.format(
- instance.child.first_name),
+ 'event': _('%(child)s started tummy time!') % {
+ 'child': instance.child.first_name
+ },
'model_name': instance.model_name,
'type': 'start'
})
events.append({
'time': timezone.localtime(instance.end),
- 'event': '{} finished tummy time.'.format(
- instance.child.first_name),
+ 'event': _('%(child)s finished tummy time.') % {
+ 'child': instance.child.first_name
+ },
'model_name': instance.model_name,
'type': 'end'
})
diff --git a/core/views.py b/core/views.py
index cc4b2dfb..783f1116 100644
--- a/core/views.py
+++ b/core/views.py
@@ -3,6 +3,7 @@ from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse, reverse_lazy
from django.utils import timezone
+from django.utils.translation import gettext as _
from django.views.generic.base import RedirectView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
@@ -16,9 +17,9 @@ class CoreAddView(PermissionRequired403Mixin, SuccessMessageMixin, CreateView):
def get_success_message(self, cleaned_data):
cleaned_data['model'] = self.model._meta.verbose_name.title()
if 'child' in cleaned_data:
- self.success_message = '%(model)s entry for %(child)s added!'
+ self.success_message = _('%(model)s entry for %(child)s added!')
else:
- self.success_message = '%(model)s entry added!'
+ self.success_message = _('%(model)s entry added!')
return self.success_message % cleaned_data
@@ -27,9 +28,9 @@ class CoreUpdateView(PermissionRequired403Mixin, SuccessMessageMixin,
def get_success_message(self, cleaned_data):
cleaned_data['model'] = self.model._meta.verbose_name.title()
if 'child' in cleaned_data:
- self.success_message = '%(model)s entry for %(child)s updated.'
+ self.success_message = _('%(model)s entry for %(child)s updated.')
else:
- self.success_message = '%(model)s entry updated.'
+ self.success_message = _('%(model)s entry updated.')
return self.success_message % cleaned_data
@@ -58,7 +59,7 @@ class ChildAdd(CoreAddView):
permission_required = ('core.add_child',)
form_class = forms.ChildForm
success_url = reverse_lazy('core:child-list')
- success_message = '%(first_name)s %(last_name)s added!'
+ success_message = _('%(first_name)s %(last_name)s added!')
class ChildDetail(PermissionRequired403Mixin, DetailView):
@@ -286,7 +287,7 @@ class TimerRestart(PermissionRequired403Mixin, RedirectView):
class TimerStop(PermissionRequired403Mixin, SuccessMessageMixin, RedirectView):
permission_required = ('core.change_timer',)
- success_message = '%(timer)s stopped.'
+ success_message = _('%(timer)s stopped.')
def get(self, request, *args, **kwargs):
instance = models.Timer.objects.get(id=kwargs['pk'])
diff --git a/dashboard/templates/cards/diaperchange_last.html b/dashboard/templates/cards/diaperchange_last.html
index b85f4111..e5f833e3 100644
--- a/dashboard/templates/cards/diaperchange_last.html
+++ b/dashboard/templates/cards/diaperchange_last.html
@@ -1,12 +1,13 @@
{% extends 'cards/base.html' %}
+{% load i18n %}
-{% block header %}Last Diaper Change{% endblock %}
+{% block header %}{% trans "Last Diaper Change" %}{% endblock %}
{% block title %}
{% if change %}
- {{ change.time|timesince }} ago
+ {% blocktrans with time=change.time|timesince %}{{ time }} ago{% endblocktrans %}
{% else %}
- Never
+ {% trans "Never" %}
{% endif %}
{% endblock %}
diff --git a/dashboard/templates/cards/diaperchange_types.html b/dashboard/templates/cards/diaperchange_types.html
index 7064db22..33d57792 100644
--- a/dashboard/templates/cards/diaperchange_types.html
+++ b/dashboard/templates/cards/diaperchange_types.html
@@ -1,9 +1,10 @@
{% extends 'cards/base.html' %}
+{% load i18n %}
-{% block header %}Diaper Changes{% endblock %}
+{% block header %}{% trans "Diaper Changes" %}{% endblock %}
{% block title %}
- Past Week
+ {% trans "Past Week" %}
{% endblock %}
{% block content %}
@@ -14,23 +15,23 @@
{% if info.wet_pct > 0 %}
{{ info.wet|floatformat:'0' }} wet
+ style="width: {{ info.wet_pct|safe }}%;">{{ info.wet|floatformat:'0' }} {% trans "wet" %}
{% endif %}
{% if info.solid_pct > 0 %}
- {{ info.solid|floatformat:'0' }} solid
+ style="width: {{ info.solid_pct|safe }}%;">
+ {{ info.solid|floatformat:'0' }} {% trans "solid" %}
{% endif %}
{% if key == 0 %}
- today
+ {% trans "today" %}
{% elif key == 1 %}
- yesterday
+ {% trans "yesterday" %}
{% else %}
- {{ key }} days ago
+ {% blocktrans %}{{ key }} days ago{% endblocktrans %}
{% endif %}
{% endif %}
diff --git a/dashboard/templates/cards/feeding_last.html b/dashboard/templates/cards/feeding_last.html
index f32e8178..b1d2ca0b 100644
--- a/dashboard/templates/cards/feeding_last.html
+++ b/dashboard/templates/cards/feeding_last.html
@@ -1,12 +1,13 @@
{% extends 'cards/base.html' %}
+{% load i18n %}
-{% block header %}Last Feeding{% endblock %}
+{% block header %}{% trans "Last Feeding" %}{% endblock %}
{% block title %}
{% if feeding %}
- {{ feeding.end|timesince }} ago
+ {% blocktrans with time=feeding.end|timesince %}{{ time }} ago{% endblocktrans %}
{% else %}
- Never
+ {% trans "Never" %}
{% endif %}
{% endblock %}
diff --git a/dashboard/templates/cards/feeding_last_method.html b/dashboard/templates/cards/feeding_last_method.html
index e5fa0cdb..1f62bf36 100644
--- a/dashboard/templates/cards/feeding_last_method.html
+++ b/dashboard/templates/cards/feeding_last_method.html
@@ -1,11 +1,12 @@
{% extends 'cards/base.html' %}
+{% load i18n %}
-{% block header %}Last Feeding Method{% endblock %}
+{% block header %}{% trans "Last Feeding Method" %}{% endblock %}
{% block title %}
{% if feeding %}
{{ feeding.method }}
{% else %}
- None
+ {% trans "None" %}
{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/dashboard/templates/cards/sleep_day.html b/dashboard/templates/cards/sleep_day.html
index 4e73c438..51fd1d9f 100644
--- a/dashboard/templates/cards/sleep_day.html
+++ b/dashboard/templates/cards/sleep_day.html
@@ -1,18 +1,20 @@
{% extends 'cards/base.html' %}
-{% load duration %}
+{% load duration i18n %}
-{% block header %}Today's Sleep{% endblock %}
+{% block header %}{% trans "Today's Sleep" %}{% endblock %}
{% block title %}
{% if total %}
{{ total|duration_string }}
{% else %}
- None yet today
+ {% trans "None yet today" %}
{% endif %}
{% endblock %}
{% block content %}
- {% if count > 0 %}{{ count }} sleep entries{% endif %}
+ {% if count > 0 %}
+ {% blocktrans %}{{ count }} sleep entries{% endblocktrans %}
+ {% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/dashboard/templates/cards/sleep_last.html b/dashboard/templates/cards/sleep_last.html
index bafca867..cdd6be76 100644
--- a/dashboard/templates/cards/sleep_last.html
+++ b/dashboard/templates/cards/sleep_last.html
@@ -1,13 +1,13 @@
{% extends 'cards/base.html' %}
-{% load duration %}
+{% load duration i18n %}
-{% block header %}Last Slept{% endblock %}
+{% block header %}{% trans "Last Slept" %}{% endblock %}
{% block title %}
{% if sleep %}
- {{ sleep.end|timesince }} ago
+ {% blocktrans with time=sleep.end|timesince %}{{ time }} ago{% endblocktrans %}
{% else %}
- Never
+ {% trans "Never" %}
{% endif %}
{% endblock %}
diff --git a/dashboard/templates/cards/sleep_naps_day.html b/dashboard/templates/cards/sleep_naps_day.html
index 2c5795b0..e8f08e62 100644
--- a/dashboard/templates/cards/sleep_naps_day.html
+++ b/dashboard/templates/cards/sleep_naps_day.html
@@ -1,14 +1,14 @@
{% extends 'cards/base.html' %}
-{% load duration %}
+{% load duration i18n %}
-{% block header %}Today's Naps{% endblock %}
+{% block header %}{% trans "Today's Naps" %}{% endblock %}
{% block title %}
{% if count %}
- {{ count }} nap{{ count|pluralize }}
+ {% blocktrans with plural=count|pluralize %}{{ count }} nap{{ plural }}{% endblocktrans %}
{% else %}
- None yet today
+ {% trans "None yet today" %}
{% endif %}
{% endblock %}
diff --git a/dashboard/templates/cards/statistics.html b/dashboard/templates/cards/statistics.html
index fd73ccfc..3a6c1abd 100644
--- a/dashboard/templates/cards/statistics.html
+++ b/dashboard/templates/cards/statistics.html
@@ -1,9 +1,9 @@
-{% load duration %}
+{% load duration i18n %}
diff --git a/dashboard/templates/cards/timer_list.html b/dashboard/templates/cards/timer_list.html
index f8345bf5..71b32e1a 100644
--- a/dashboard/templates/cards/timer_list.html
+++ b/dashboard/templates/cards/timer_list.html
@@ -1,10 +1,11 @@
{% extends 'cards/base.html' %}
+{% load i18n %}
-{% block header %}Active Timers{% endblock %}
+{% block header %}{% trans "Active Timers" %}{% endblock %}
{% block title %}
{% with instances|length as count %}
- {{ count }} active timer{{ count|pluralize }}
+ {% blocktrans with plural=count|pluralize %}{{ count }} active timer{{ plural }}{% endblocktrans %}
{% endwith %}
{% endblock %}
@@ -13,7 +14,10 @@
{% for instance in instances %}
- {{ instance }} Started by {{ instance.user }} at {{ instance.start|time }}
+ {{ instance }}
+
+ {% blocktrans with start=instance.start|time %}Started by {{ instance.user }} at {{ start }}{% endblocktrans %}
+
{% endfor %}
diff --git a/dashboard/templates/cards/tummytime_day.html b/dashboard/templates/cards/tummytime_day.html
index 9cced7e0..fc677814 100644
--- a/dashboard/templates/cards/tummytime_day.html
+++ b/dashboard/templates/cards/tummytime_day.html
@@ -1,14 +1,14 @@
{% extends 'cards/base.html' %}
-{% load duration %}
+{% load duration i18n %}
-{% block header %}Today's Tummy Time{% endblock %}
+{% block header %}{% trans "Today's Tummy Time" %}{% endblock %}
{% block title %}
{% if stats.count > 0 %}
{{ stats.total|duration_string }}
{% else %}
- None yet today
+ {% trans "None yet today" %}
{% endif %}
{% endblock %}
@@ -16,7 +16,9 @@
{% block listgroup %}
{% for instance in instances %}
- {{ instance.duration|duration_string }} at {{ instance.end|time }}
+
+ {% blocktrans with duration=instance.duration|duration_string end=instance.end|time %}{{ duration }} at {{ end }}{% endblocktrans %}
+
{% endfor %}
{% endblock %}
\ No newline at end of file
diff --git a/dashboard/templates/cards/tummytime_last.html b/dashboard/templates/cards/tummytime_last.html
index e028b461..a23de058 100644
--- a/dashboard/templates/cards/tummytime_last.html
+++ b/dashboard/templates/cards/tummytime_last.html
@@ -1,13 +1,13 @@
{% extends 'cards/base.html' %}
-{% load duration %}
+{% load duration i18n %}
-{% block header %}Last Tummy Time{% endblock %}
+{% block header %}{% trans "Last Tummy Time" %}{% endblock %}
{% block title %}
{% if tummytime %}
- {{ tummytime.time|timesince }} ago
+ {% blocktrans with time=tummytime.time|timesince %}{{ time }} ago{% endblocktrans %}
{% else %}
- Never
+ {% trans "Never" %}
{% endif %}
{% endblock %}
diff --git a/dashboard/templates/dashboard/child.html b/dashboard/templates/dashboard/child.html
index 874af77f..b9af1e81 100644
--- a/dashboard/templates/dashboard/child.html
+++ b/dashboard/templates/dashboard/child.html
@@ -1,12 +1,12 @@
{% extends 'babybuddy/page.html' %}
-{% load cards %}
+{% load cards i18n %}
-{% block title %}Dashboard - {{ object }}{% endblock %}
+{% block title %}{% trans "Dashboard" %} - {{ object }}{% endblock %}
{% block breadcrumbs %}
- Children
+ {% trans "Children" %}
{{ object }}
- Dashboard
+ {% trans "Dashboard" %}
{% endblock %}
{% block content %}
diff --git a/dashboard/templates/dashboard/child_button_group.html b/dashboard/templates/dashboard/child_button_group.html
index 4193f8f2..b3afd976 100644
--- a/dashboard/templates/dashboard/child_button_group.html
+++ b/dashboard/templates/dashboard/child_button_group.html
@@ -1,4 +1,6 @@
-
+{% load i18n %}
+
+
diff --git a/dashboard/templates/dashboard/dashboard.html b/dashboard/templates/dashboard/dashboard.html
index b935d59b..8191102f 100644
--- a/dashboard/templates/dashboard/dashboard.html
+++ b/dashboard/templates/dashboard/dashboard.html
@@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %}
-{% load static thumbnail %}
+{% load i18n static thumbnail %}
-{% block title %}Dashboard{% endblock %}
+{% block title %}{% trans "Dashboard" %}{% endblock %}
{% block breadcrumbs %}
-
Dashboard
+
{% trans "Dashboard" %}
{% endblock %}
{% block content %}
@@ -24,8 +24,8 @@
{{ object }}
- Born {{ object.birth_date }}
- Age {{ object.birth_date|timesince }}
+ {% trans "Born" %} {{ object.birth_date }}
+ {% trans "Age" %} {{ object.birth_date|timesince }}
{% include 'dashboard/child_button_group.html' %}
diff --git a/dashboard/templatetags/cards.py b/dashboard/templatetags/cards.py
index c4e4b13b..3409211e 100644
--- a/dashboard/templatetags/cards.py
+++ b/dashboard/templatetags/cards.py
@@ -3,6 +3,7 @@ from django import template
from django.db.models import Avg, Count, Sum
from django.db.models.functions import TruncDate
from django.utils import timezone
+from django.utils.translation import gettext as _
from core import models
@@ -163,39 +164,39 @@ def card_statistics(child):
stats.append({
'type': 'duration',
'stat': changes['btwn_average'],
- 'title': 'Diaper change frequency'})
+ 'title': _('Diaper change frequency')})
feedings = _feeding_statistics(child)
stats.append({
'type': 'duration',
'stat': feedings['btwn_average'],
- 'title': 'Feeding frequency'})
+ 'title': _('Feeding frequency')})
naps = _nap_statistics(child)
stats.append({
'type': 'duration',
'stat': naps['average'],
- 'title': 'Average nap duration'})
+ 'title': _('Average nap duration')})
stats.append({
'type': 'float',
'stat': naps['avg_per_day'],
- 'title': 'Average naps per day'})
+ 'title': _('Average naps per day')})
sleep = _sleep_statistics(child)
stats.append({
'type': 'duration',
'stat': sleep['average'],
- 'title': 'Average sleep duration'})
+ 'title': _('Average sleep duration')})
stats.append({
'type': 'duration',
'stat': sleep['btwn_average'],
- 'title': 'Average awake duration'})
+ 'title': _('Average awake duration')})
weight = _weight_statistics(child)
stats.append({
'type': 'float',
'stat': weight['change_weekly'],
- 'title': 'Weight change per week'})
+ 'title': _('Weight change per week')})
return {'stats': stats}
diff --git a/gulpfile.js b/gulpfile.js
index 7f419e5f..6276e493 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -171,6 +171,18 @@ gulp.task('makemigrations', function(cb) {
spawn('pipenv', command, { stdio: 'inherit' }).on('exit', cb);
});
+gulp.task('makemessages', function(cb) {
+ var command = ['run', 'python', 'manage.py', 'makemessages'];
+ command = command.concat(process.argv.splice(3));
+ spawn('pipenv', command, { stdio: 'inherit' }).on('exit', cb);
+});
+
+gulp.task('compilemessages', function(cb) {
+ var command = ['run', 'python', 'manage.py', 'compilemessages'];
+ command = command.concat(process.argv.splice(3));
+ spawn('pipenv', command, { stdio: 'inherit' }).on('exit', cb);
+});
+
gulp.task('reset', function(cb) {
spawn(
'pipenv',
diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo
new file mode 100644
index 00000000..eb63aa1f
Binary files /dev/null and b/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
new file mode 100644
index 00000000..4b6c00cd
--- /dev/null
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -0,0 +1,1365 @@
+# French translations file.
+# Copyright (C) 2019 Baby Buddy's Contributors
+# This file is distributed under the same license as the Baby Buddy package.
+# Christopher C. Wells
, 2019.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: babybuddy 1.2.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-04-18 19:56-0700\n"
+"PO-Revision-Date: 2019-04-18 19:56-0700\n"
+"Last-Translator: Christopher C. Wells \n"
+"Language-Team: French \n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: .\babybuddy\admin.py:12 .\babybuddy\admin.py:13
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:219
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:8
+msgid "Settings"
+msgstr "Paramètres"
+
+#: .\babybuddy\admin.py:16 .\babybuddy\templates\babybuddy\nav-dropdown.html:76
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:51
+#: .\dashboard\templates\dashboard\child.html:4
+#: .\dashboard\templates\dashboard\child.html:9
+#: .\dashboard\templates\dashboard\dashboard.html:4
+#: .\dashboard\templates\dashboard\dashboard.html:7
+msgid "Dashboard"
+msgstr "Tableau de bord"
+
+#: .\babybuddy\models.py:19
+msgid "Refresh rate"
+msgstr "Fréquence de rafraîchissement"
+
+#: .\babybuddy\models.py:20
+msgid ""
+"This setting will only be used when a browser does not support refresh on "
+"focus."
+msgstr ""
+"Ce paramètre ne sera utilisé que lorsqu'un navigateur ne prend pas en charge "
+"l'actualisation lors de la mise au point."
+
+#: .\babybuddy\models.py:26
+msgid "disabled"
+msgstr "désactivé"
+
+#: .\babybuddy\models.py:27
+msgid "1 min."
+msgstr ""
+
+#: .\babybuddy\models.py:28
+msgid "2 min."
+msgstr ""
+
+#: .\babybuddy\models.py:29
+msgid "3 min."
+msgstr ""
+
+#: .\babybuddy\models.py:30
+msgid "4 min."
+msgstr ""
+
+#: .\babybuddy\models.py:31
+msgid "5 min."
+msgstr ""
+
+#: .\babybuddy\models.py:32
+msgid "10 min."
+msgstr ""
+
+#: .\babybuddy\models.py:33
+msgid "15 min."
+msgstr ""
+
+#: .\babybuddy\models.py:34
+msgid "30 min."
+msgstr ""
+
+#: .\babybuddy\models.py:40
+msgid "Language"
+msgstr "Langue"
+
+#: .\babybuddy\models.py:44
+#, python-brace-format
+msgid "{user}'s Settings"
+msgstr "Paramètres d'utilisateur {user}"
+
+#: .\babybuddy\settings\base.py:128
+msgid "English"
+msgstr "Anglais"
+
+#: .\babybuddy\settings\base.py:129
+msgid "French"
+msgstr "français"
+
+#: .\babybuddy\templates\403.html:4 .\babybuddy\templates\403.html:7
+msgid "Permission Denied"
+msgstr "Permission Refusée"
+
+#: .\babybuddy\templates\403.html:12
+msgid ""
+"You do not have permission to access this resource.\n"
+" Contact a site administrator for assistance."
+msgstr ""
+"Vous n'êtes pas autorisé à accéder à cette ressource.\n"
+" Contactez un administrateur de site pour obtenir de l'aide."
+
+#: .\babybuddy\templates\babybuddy\base.html:34
+msgid "Home"
+msgstr "Accueil"
+
+#: .\babybuddy\templates\babybuddy\filter.html:18
+msgid "Filter"
+msgstr "Filtre"
+
+#: .\babybuddy\templates\babybuddy\filter.html:19
+msgid "Reset"
+msgstr "Réinitialiser"
+
+#: .\babybuddy\templates\babybuddy\filter.html:32
+msgid "Filters"
+msgstr "Les filtres"
+
+#: .\babybuddy\templates\babybuddy\form.html:11
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:68
+msgid "Submit"
+msgstr "Soumettre"
+
+#: .\babybuddy\templates\babybuddy\messages.html:18
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:19
+#, python-format
+msgid "Error: %(error)s"
+msgstr "Erreur: %(error)s"
+
+#: .\babybuddy\templates\babybuddy\messages.html:23
+msgid ""
+"Error: Some fields have errors. See below for details. "
+msgstr ""
+"Erreur: Certains champs comportent des erreurs. Voir ci-"
+"dessous pour plus de détails. "
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:27 .\core\models.py:148
+#: .\core\models.py:152
+msgid "Diaper Change"
+msgstr "Changement"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:33
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:169 .\core\models.py:217
+#: .\core\models.py:221 .\core\templates\core\timer_detail.html:33
+msgid "Feeding"
+msgstr "Allaitement"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:39
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:115 .\core\models.py:250
+#: .\core\models.py:258 .\core\models.py:262
+#: .\core\templates\core\note_list.html:18
+msgid "Note"
+msgstr "Annotation"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:45
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:176
+#: .\babybuddy\templates\babybuddy\welcome.html:40 .\core\models.py:301
+#: .\core\models.py:302 .\core\models.py:305
+#: .\core\templates\core\sleep_confirm_delete.html:7
+#: .\core\templates\core\sleep_form.html:13
+#: .\core\templates\core\sleep_list.html:4
+#: .\core\templates\core\sleep_list.html:7
+#: .\core\templates\core\sleep_list.html:11
+#: .\core\templates\core\timer_detail.html:41
+msgid "Sleep"
+msgstr "Sommeil"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:51
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:189
+#: .\babybuddy\templates\babybuddy\welcome.html:48 .\core\models.py:447
+#: .\core\models.py:448 .\core\models.py:451
+#: .\core\templates\core\timer_detail.html:49
+#: .\core\templates\core\tummytime_confirm_delete.html:7
+#: .\core\templates\core\tummytime_form.html:13
+#: .\core\templates\core\tummytime_list.html:4
+#: .\core\templates\core\tummytime_list.html:7
+#: .\core\templates\core\tummytime_list.html:11
+msgid "Tummy Time"
+msgstr "Heure de Ventre"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:57
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:123 .\core\models.py:477
+#: .\core\models.py:490 .\core\models.py:491 .\core\models.py:494
+#: .\core\templates\core\weight_confirm_delete.html:7
+#: .\core\templates\core\weight_form.html:13
+#: .\core\templates\core\weight_list.html:4
+#: .\core\templates\core\weight_list.html:7
+#: .\core\templates\core\weight_list.html:11
+#: .\core\templates\core\weight_list.html:18
+#: .\dashboard\templates\dashboard\child_button_group.html:24
+#: .\reports\graphs\weight_weight.py:19 .\reports\graphs\weight_weight.py:30
+#: .\reports\templates\reports\weight_change.html:4
+#: .\reports\templates\reports\weight_change.html:8
+msgid "Weight"
+msgstr "Poids"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:87
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:95 .\core\models.py:100
+#: .\core\templates\core\child_confirm_delete.html:7
+#: .\core\templates\core\child_detail.html:7
+#: .\core\templates\core\child_form.html:13
+#: .\core\templates\core\child_list.html:4
+#: .\core\templates\core\child_list.html:7
+#: .\core\templates\core\child_list.html:11
+#: .\dashboard\templates\dashboard\child.html:7
+#: .\reports\templates\reports\report_base.html:7
+msgid "Children"
+msgstr "Enfants"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:101 .\core\models.py:99
+#: .\core\models.py:122 .\core\models.py:179 .\core\models.py:248
+#: .\core\models.py:277 .\core\models.py:419 .\core\models.py:472
+#: .\core\templates\core\diaperchange_list.html:17
+#: .\core\templates\core\feeding_list.html:17
+#: .\core\templates\core\note_list.html:17
+#: .\core\templates\core\sleep_list.html:17
+#: .\core\templates\core\tummytime_list.html:17
+#: .\core\templates\core\weight_list.html:17
+msgid "Child"
+msgstr "Enfant"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:109 .\core\models.py:259
+#: .\core\templates\core\note_confirm_delete.html:7
+#: .\core\templates\core\note_form.html:13
+#: .\core\templates\core\note_list.html:4
+#: .\core\templates\core\note_list.html:7
+#: .\core\templates\core\note_list.html:11
+msgid "Notes"
+msgstr "Annotations"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:129
+msgid "Weight entry"
+msgstr "Annotation du poids"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:143
+msgid "Activities"
+msgstr "Activités"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:150
+#: .\reports\graphs\diaperchange_lifetimes.py:27
+msgid "Changes"
+msgstr "Changement"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:156
+msgid "Change"
+msgstr "Changement"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:163
+#: .\babybuddy\templates\babybuddy\welcome.html:32 .\core\models.py:218
+#: .\core\templates\core\feeding_confirm_delete.html:7
+#: .\core\templates\core\feeding_form.html:13
+#: .\core\templates\core\feeding_list.html:4
+#: .\core\templates\core\feeding_list.html:7
+#: .\core\templates\core\feeding_list.html:11
+msgid "Feedings"
+msgstr "Allaitement"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:182
+msgid "Sleep entry"
+msgstr "Annotation de sommeil"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:195
+msgid "Tummy Time entry"
+msgstr "Annotation d'heure de ventre"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:218
+#: .\babybuddy\templates\babybuddy\user_list.html:17
+#: .\babybuddy\templates\babybuddy\user_password_form.html:7
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:7
+#: .\core\models.py:361 .\core\templates\core\timer_list.html:22
+msgid "User"
+msgstr "Utilisateur"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:220
+msgid "Password"
+msgstr "Mot de passe"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:221
+msgid "Logout"
+msgstr "Se déconnecter"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:222
+msgid "Site"
+msgstr ""
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:225
+msgid "API Browser"
+msgstr "Navigateur d'API"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:227
+#: .\babybuddy\templates\babybuddy\user_confirm_delete.html:7
+#: .\babybuddy\templates\babybuddy\user_form.html:13
+#: .\babybuddy\templates\babybuddy\user_list.html:4
+#: .\babybuddy\templates\babybuddy\user_list.html:7
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:230
+msgid "Backend Admin"
+msgstr "Administration de Django"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:232
+msgid "Support"
+msgstr ""
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:236
+msgid "Source Code"
+msgstr "Code source"
+
+#: .\babybuddy\templates\babybuddy\nav-dropdown.html:240
+msgid "Chat / Support"
+msgstr "Tchat / Support"
+
+#: .\babybuddy\templates\babybuddy\paginator.html:11
+#: .\core\templates\core\child_detail.html:34
+#: .\core\templates\core\child_detail.html:36
+#: .\dashboard\templates\cards\statistics.html:32
+msgid "Previous"
+msgstr "Précédente"
+
+#: .\babybuddy\templates\babybuddy\paginator.html:28
+#: .\core\templates\core\child_detail.html:41
+#: .\core\templates\core\child_detail.html:43
+#: .\dashboard\templates\cards\statistics.html:36
+msgid "Next"
+msgstr "Suivante"
+
+#: .\babybuddy\templates\babybuddy\user_confirm_delete.html:4
+msgid "Delete User"
+msgstr "Supprimer l'utilisateur"
+
+#: .\babybuddy\templates\babybuddy\user_confirm_delete.html:9
+#: .\core\templates\core\child_confirm_delete.html:9
+#: .\core\templates\core\child_confirm_delete.html:29
+#: .\core\templates\core\diaperchange_confirm_delete.html:8
+#: .\core\templates\core\diaperchange_confirm_delete.html:15
+#: .\core\templates\core\feeding_confirm_delete.html:8
+#: .\core\templates\core\feeding_confirm_delete.html:15
+#: .\core\templates\core\note_confirm_delete.html:8
+#: .\core\templates\core\note_confirm_delete.html:15
+#: .\core\templates\core\sleep_confirm_delete.html:8
+#: .\core\templates\core\sleep_confirm_delete.html:15
+#: .\core\templates\core\timer_confirm_delete.html:9
+#: .\core\templates\core\timer_confirm_delete.html:16
+#: .\core\templates\core\tummytime_confirm_delete.html:8
+#: .\core\templates\core\tummytime_confirm_delete.html:15
+#: .\core\templates\core\weight_confirm_delete.html:8
+#: .\core\templates\core\weight_confirm_delete.html:15
+msgid "Delete"
+msgstr "Supprimer"
+
+#: .\babybuddy\templates\babybuddy\user_confirm_delete.html:15
+#: .\core\templates\core\child_confirm_delete.html:15
+#: .\core\templates\core\diaperchange_confirm_delete.html:14
+#: .\core\templates\core\feeding_confirm_delete.html:14
+#: .\core\templates\core\note_confirm_delete.html:14
+#: .\core\templates\core\sleep_confirm_delete.html:14
+#: .\core\templates\core\timer_confirm_delete.html:15
+#: .\core\templates\core\tummytime_confirm_delete.html:14
+#: .\core\templates\core\weight_confirm_delete.html:14
+#, python-format
+msgid ""
+"Are you sure you want to delete %(object)s"
+"span>? "
+msgstr ""
+"Etes-vous sûr que vous voulez supprimer "
+"%(object)s ? "
+
+#: .\babybuddy\templates\babybuddy\user_confirm_delete.html:17
+#: .\core\templates\core\child_confirm_delete.html:30
+#: .\core\templates\core\diaperchange_confirm_delete.html:16
+#: .\core\templates\core\feeding_confirm_delete.html:16
+#: .\core\templates\core\note_confirm_delete.html:16
+#: .\core\templates\core\sleep_confirm_delete.html:16
+#: .\core\templates\core\timer_confirm_delete.html:17
+#: .\core\templates\core\tummytime_confirm_delete.html:16
+#: .\core\templates\core\weight_confirm_delete.html:16
+msgid "Cancel"
+msgstr "Annuler"
+
+#: .\babybuddy\templates\babybuddy\user_form.html:8
+#: .\babybuddy\templates\babybuddy\user_form.html:18
+#: .\babybuddy\templates\babybuddy\user_form.html:26
+#: .\babybuddy\templates\babybuddy\user_list.html:65
+msgid "Create User"
+msgstr "Créer un utilisateur"
+
+#: .\babybuddy\templates\babybuddy\user_form.html:16
+#: .\core\templates\core\child_form.html:16
+#: .\core\templates\core\diaperchange_form.html:15
+#: .\core\templates\core\feeding_form.html:15
+#: .\core\templates\core\note_form.html:15
+#: .\core\templates\core\sleep_form.html:15
+#: .\core\templates\core\timer_form.html:10
+#: .\core\templates\core\tummytime_form.html:15
+#: .\core\templates\core\weight_form.html:15
+msgid "Update"
+msgstr "Mis à jour"
+
+#: .\babybuddy\templates\babybuddy\user_form.html:24
+#: .\core\templates\core\child_form.html:24
+#: .\core\templates\core\diaperchange_form.html:23
+#: .\core\templates\core\feeding_form.html:23
+#: .\core\templates\core\note_form.html:23
+#: .\core\templates\core\sleep_form.html:23
+#: .\core\templates\core\timer_form.html:18
+#: .\core\templates\core\tummytime_form.html:23
+#: .\core\templates\core\weight_form.html:23
+#, python-format
+msgid "Update %(object)s "
+msgstr "Mettre à jour %(object)s "
+
+#: .\babybuddy\templates\babybuddy\user_list.html:18
+#: .\core\templates\core\child_list.html:18
+msgid "First Name"
+msgstr "Prénom"
+
+#: .\babybuddy\templates\babybuddy\user_list.html:19
+#: .\core\templates\core\child_list.html:19
+msgid "Last Name"
+msgstr "Nom"
+
+#: .\babybuddy\templates\babybuddy\user_list.html:20
+msgid "Email"
+msgstr "Adresse électronique"
+
+#: .\babybuddy\templates\babybuddy\user_list.html:21
+msgid "Staff"
+msgstr "Personnel"
+
+#: .\babybuddy\templates\babybuddy\user_list.html:22 .\core\models.py:355
+#: .\core\templates\core\timer_list.html:21
+msgid "Active"
+msgstr "Actif"
+
+#: .\babybuddy\templates\babybuddy\user_list.html:23
+#: .\core\templates\core\child_list.html:21
+#: .\core\templates\core\child_list.html:41
+#: .\core\templates\core\diaperchange_list.html:22
+#: .\core\templates\core\diaperchange_list.html:34
+#: .\core\templates\core\feeding_list.html:25
+#: .\core\templates\core\feeding_list.html:42
+#: .\core\templates\core\note_list.html:20
+#: .\core\templates\core\note_list.html:30
+#: .\core\templates\core\sleep_list.html:22
+#: .\core\templates\core\sleep_list.html:34
+#: .\core\templates\core\tummytime_list.html:22
+#: .\core\templates\core\tummytime_list.html:34
+#: .\core\templates\core\weight_list.html:20
+#: .\core\templates\core\weight_list.html:30
+msgid "Actions"
+msgstr "Opérations"
+
+#: .\babybuddy\templates\babybuddy\user_list.html:55
+msgid "No users found."
+msgstr "Aucun utilisateur trouvé."
+
+#: .\babybuddy\templates\babybuddy\user_password_form.html:4
+#: .\babybuddy\templates\babybuddy\user_password_form.html:8
+#: .\babybuddy\templates\babybuddy\user_password_form.html:12
+msgid "Change Password"
+msgstr "Changer le mot de passe"
+
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:4
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:12
+msgid "User Settings"
+msgstr "Paramètres Utilisateur"
+
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:24
+msgid "Error: Some fields have errors. See below for details."
+msgstr ""
+"Erreur: Certains champs comportent des erreurs. Voir ci-"
+"dessous pour plus de détails."
+
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:28
+msgid "User Profile"
+msgstr "Profil de l'utilisateur"
+
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:59
+msgid "API"
+msgstr ""
+
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:61
+msgid "Key"
+msgstr "Clé"
+
+#: .\babybuddy\templates\babybuddy\user_settings_form.html:64
+msgid "Regenerate"
+msgstr "Recréer"
+
+#: .\babybuddy\templates\babybuddy\welcome.html:4
+#: .\babybuddy\templates\babybuddy\welcome.html:7
+msgid "Welcome!"
+msgstr "Bienvenue!"
+
+#: .\babybuddy\templates\babybuddy\welcome.html:12
+msgid "Welcome to Baby Buddy!"
+msgstr "Bienvenue à Baby Buddy!"
+
+#: .\babybuddy\templates\babybuddy\welcome.html:14
+msgid ""
+"Learn about and predict baby's needs without\n"
+" (as much ) guess work by using Baby Buddy to track "
+"—"
+msgstr ""
+"En savoir plus sur et en prédire les besoins de bébé sans (trop ) de "
+"devinettes en utilisant Baby Buddy pour suivre —"
+
+#: .\babybuddy\templates\babybuddy\welcome.html:24 .\core\models.py:149
+#: .\core\templates\core\diaperchange_confirm_delete.html:7
+#: .\core\templates\core\diaperchange_form.html:13
+#: .\core\templates\core\diaperchange_list.html:4
+#: .\core\templates\core\diaperchange_list.html:7
+#: .\core\templates\core\diaperchange_list.html:11
+#: .\dashboard\templates\cards\diaperchange_types.html:4
+msgid "Diaper Changes"
+msgstr "Changement"
+
+#: .\babybuddy\templates\babybuddy\welcome.html:54
+msgid ""
+"As the amount of entries grows, Baby Buddy will help\n"
+" parents and caregivers to identify small patterns in baby's "
+"habits\n"
+" using the dashboard and graphs. Baby Buddy is mobile-friendly "
+"and\n"
+" uses a dark theme to help weary moms and dads with 2AM feedings "
+"and\n"
+" changings. To get started, just click the button below to add "
+"your\n"
+" first (or second, third, etc.) child!"
+msgstr ""
+"Au fur et à mesure que le nombre d'entrées augmente, Baby Buddy aidera les "
+"parents et les éducateurs à identifier de petites tendances dans les "
+"habitudes de bébé à l'aide du tableau de bord et des graphiques. Baby Buddy "
+"est compatible avec les appareils mobiles et utilise un thème sombre pour "
+"aider les mamans et les papas épuisés à s'alimenter et à changer de "
+"nourriture. Pour commencer, cliquez simplement sur le bouton ci-dessous pour "
+"ajouter votre premier (ou deuxième, troisième, etc.) enfant!"
+
+#: .\babybuddy\templates\babybuddy\welcome.html:64
+#: .\core\templates\core\child_form.html:8
+#: .\core\templates\core\child_form.html:18
+#: .\core\templates\core\child_form.html:26
+#: .\core\templates\core\child_list.html:70
+msgid "Add a Child"
+msgstr "Ajouter un Enfant"
+
+#: .\babybuddy\templates\registration\login.html:32
+msgid "Login"
+msgstr ""
+
+#: .\babybuddy\templates\registration\login.html:38
+msgid "Forgot your password?"
+msgstr "Mot de passe oublié?"
+
+#: .\babybuddy\templates\registration\password_reset_complete.html:4
+msgid "Password Reset Successfully!"
+msgstr "Mot de passe réinitialisé avec succès!"
+
+#: .\babybuddy\templates\registration\password_reset_complete.html:8
+msgid "Your password has been set. You may go ahead and log in now."
+msgstr ""
+"Votre mot de passe a été défini. Vous pouvez vous connecter maintenant."
+
+#: .\babybuddy\templates\registration\password_reset_complete.html:9
+msgid "Log in"
+msgstr "Se connecter"
+
+#: .\babybuddy\templates\registration\password_reset_confirm.html:4
+msgid "Password Reset"
+msgstr "Réinitialiser le mot de passe"
+
+#: .\babybuddy\templates\registration\password_reset_confirm.html:12
+msgid ""
+"Oh snap! The\n"
+" two passwords did not match. Please try again.
"
+msgstr ""
+"Oh non! Les deux mots de passe ne "
+"correspondent pas. Veuillez réessayer.
"
+
+#: .\babybuddy\templates\registration\password_reset_confirm.html:18
+msgid "Enter your new password in each field below."
+msgstr "Entrez votre nouveau mot de passe dans chaque champ ci-dessous."
+
+#: .\babybuddy\templates\registration\password_reset_confirm.html:42
+#: .\babybuddy\templates\registration\password_reset_form.html:27
+msgid "Reset Password"
+msgstr "Réinitialiser"
+
+#: .\babybuddy\templates\registration\password_reset_done.html:4
+msgid "Reset Email Sent"
+msgstr "Réinitialiser email envoyé"
+
+#: .\babybuddy\templates\registration\password_reset_done.html:8
+msgid ""
+"We've emailed you instructions for setting your\n"
+" password, if an account exists with the email you entered. You\n"
+" should receive them shortly.
\n"
+" If you don't receive an email, please make sure "
+"you've\n"
+" entered the address you registered with, and check your spam\n"
+" folder.
"
+msgstr ""
+"Les instructions pour définir votre mot de passe ont été envoyées, si "
+"uncompte existe avec le courrier électronique que vous avez entré.
\n"
+" Si vous ne recevez pas d'e-mail, assurez-vous "
+"d'avoir entré l'adresse avec laquelle vous vous êtes inscrit et vérifiez "
+"votre dossier de courrier indésirable.
"
+
+#: .\babybuddy\templates\registration\password_reset_form.html:4
+msgid "Forgot Password"
+msgstr "Mot de passe oublié"
+
+#: .\babybuddy\templates\registration\password_reset_form.html:8
+msgid ""
+"Enter your account email address in the\n"
+" form below. If the address is valid, you will receive instructions "
+"for\n"
+" resetting your password.
"
+msgstr ""
+"Entrez l'adresse email de votre compte dans le formulaire "
+"ci-dessous. Si l'adresse est valide, vous recevrez des instructions pour "
+"réinitialiser votre mot de passe.
"
+
+#: .\babybuddy\views.py:56
+#, python-format
+msgid "User %(username)s added!"
+msgstr "L'utilisateur %(username)s a été ajouté!"
+
+#: .\babybuddy\views.py:66
+#, python-format
+msgid "User %(username)s updated."
+msgstr "L'utilisateur %(username)s a été mis à jour!"
+
+#: .\babybuddy\views.py:78
+#, python-brace-format
+msgid "User {user} deleted."
+msgstr "L'utilisateur {user} a été supprimé."
+
+#: .\babybuddy\views.py:101
+msgid "Password updated."
+msgstr "Le mot de passe a été mis à jour."
+
+#: .\babybuddy\views.py:111
+msgid "User API key regenerated."
+msgstr "La clé de l'API utilisateur a été régénérée."
+
+#: .\babybuddy\views.py:144
+msgid "Settings saved!"
+msgstr "Les paramètres ont été enregistrés!"
+
+#: .\core\forms.py:81
+msgid "Name does not match child name."
+msgstr "Le nom ne correspond pas au nom de l'enfant."
+
+#: .\core\models.py:22
+msgid "Date can not be in the future."
+msgstr "La date ne peut pas être dans le futur."
+
+#: .\core\models.py:36
+msgid "Start time must come before end time."
+msgstr "L'heure de début doit arriver avant l'heure de fin."
+
+#: .\core\models.py:39
+msgid "Duration too long."
+msgstr "Durée trop longue."
+
+#: .\core\models.py:55
+msgid "Another entry intersects the specified time period."
+msgstr "Une autre entrée coupe la période spécifiée."
+
+#: .\core\models.py:68
+msgid "Date/time can not be in the future."
+msgstr "La date / heure ne peut pas être dans le futur."
+
+#: .\core\models.py:74
+msgid "First name"
+msgstr "Prénom"
+
+#: .\core\models.py:75
+msgid "Last name"
+msgstr "Nom"
+
+#: .\core\models.py:79
+msgid "Birth date"
+msgstr "Date de Naissance"
+
+#: .\core\models.py:85
+msgid "Slug"
+msgstr "Jeton"
+
+#: .\core\models.py:91
+msgid "Picture"
+msgstr "Image"
+
+#: .\core\models.py:127 .\core\models.py:251
+#: .\core\templates\core\diaperchange_list.html:21
+#: .\core\templates\core\note_list.html:19
+msgid "Time"
+msgstr "Temps"
+
+#: .\core\models.py:129 .\core\templates\core\diaperchange_list.html:18
+#: .\reports\graphs\diaperchange_types.py:33
+msgid "Wet"
+msgstr "Humide"
+
+#: .\core\models.py:130 .\core\templates\core\diaperchange_list.html:19
+#: .\reports\graphs\diaperchange_types.py:27
+msgid "Solid"
+msgstr "Souillés"
+
+#: .\core\models.py:134
+msgid "Black"
+msgstr "Noir"
+
+#: .\core\models.py:135
+msgid "Brown"
+msgstr "Brun"
+
+#: .\core\models.py:136
+msgid "Green"
+msgstr "Verte"
+
+#: .\core\models.py:137
+msgid "Yellow"
+msgstr "Jaune"
+
+#: .\core\models.py:140 .\core\templates\core\diaperchange_list.html:20
+msgid "Color"
+msgstr "Couleur"
+
+#: .\core\models.py:170
+msgid "Wet and/or solid is required."
+msgstr "Humide et / ou solide est requis."
+
+#: .\core\models.py:184 .\core\models.py:282 .\core\models.py:339
+#: .\core\models.py:424
+msgid "Start time"
+msgstr "Heure de début"
+
+#: .\core\models.py:189 .\core\models.py:287 .\core\models.py:345
+#: .\core\models.py:429
+msgid "End time"
+msgstr "Heure de fin"
+
+#: .\core\models.py:194 .\core\models.py:292 .\core\models.py:350
+#: .\core\models.py:434 .\core\templates\core\feeding_list.html:23
+#: .\core\templates\core\sleep_list.html:18
+#: .\core\templates\core\timer_list.html:19
+#: .\core\templates\core\tummytime_list.html:18
+msgid "Duration"
+msgstr "Durée"
+
+#: .\core\models.py:197
+msgid "Breast milk"
+msgstr "Lait maternel"
+
+#: .\core\models.py:197
+msgid "Formula"
+msgstr "Formule bébé"
+
+#: .\core\models.py:199 .\core\templates\core\feeding_list.html:19
+msgid "Type"
+msgstr ""
+
+#: .\core\models.py:203
+msgid "Bottle"
+msgstr "Biberon"
+
+#: .\core\models.py:204
+msgid "Left breast"
+msgstr "Sein gauche"
+
+#: .\core\models.py:205
+msgid "Right breast"
+msgstr "Sein droit"
+
+#: .\core\models.py:208 .\core\templates\core\feeding_list.html:18
+msgid "Method"
+msgstr "Mode"
+
+#: .\core\models.py:210
+msgid "Amount"
+msgstr "Quantité"
+
+#: .\core\models.py:238
+msgid "Only \"Bottle\" method is allowed with \"Formula\" type."
+msgstr "Seule la méthode \"Bottle\" est autorisée avec le type \"Formula\"."
+
+#: .\core\models.py:334 .\core\templates\core\timer_list.html:17
+msgid "Name"
+msgstr "Prénom"
+
+#: .\core\models.py:369 .\core\templates\core\timer_form.html:4
+msgid "Timer"
+msgstr "Chronomètre"
+
+#: .\core\models.py:370 .\core\templates\core\timer_confirm_delete.html:7
+#: .\core\templates\core\timer_detail.html:7
+#: .\core\templates\core\timer_form.html:7
+#: .\core\templates\core\timer_list.html:4
+#: .\core\templates\core\timer_list.html:7
+#: .\core\templates\core\timer_list.html:11
+#: .\core\templates\core\timer_nav.html:10
+msgid "Timers"
+msgstr "Chronomètres"
+
+#: .\core\models.py:373
+#, python-brace-format
+msgid "Timer #{id}"
+msgstr "Chronomètre #{id}"
+
+#: .\core\models.py:439 .\core\templates\core\tummytime_list.html:21
+msgid "Milestone"
+msgstr "Étape importante"
+
+#: .\core\models.py:482 .\core\templates\core\feeding_list.html:24
+#: .\core\templates\core\weight_list.html:19
+#: .\reports\graphs\diaperchange_types.py:46
+#: .\reports\graphs\feeding_duration.py:54
+#: .\reports\graphs\sleep_pattern.py:131 .\reports\graphs\sleep_totals.py:51
+#: .\reports\graphs\weight_weight.py:28
+msgid "Date"
+msgstr ""
+
+#: .\core\templates\core\child_confirm_delete.html:4
+msgid "Delete a Child"
+msgstr "Supprimer un Enfant"
+
+#: .\core\templates\core\child_confirm_delete.html:18
+msgid "To confirm this action. Type the full name of the child below."
+msgstr ""
+"Tapez le nom complet de l'enfant ci-dessous pour confirmer cette action."
+
+#: .\core\templates\core\child_detail.html:23
+#: .\dashboard\templates\dashboard\dashboard.html:27
+msgid "Born"
+msgstr "Né"
+
+#: .\core\templates\core\child_detail.html:24
+#: .\dashboard\templates\dashboard\dashboard.html:28
+msgid "Age"
+msgstr "L'âge"
+
+#: .\core\templates\core\child_detail.html:58
+#, python-format
+msgid "%(since)s ago (%(time)s)"
+msgstr "il y a %(since)s (%(time)s)"
+
+#: .\core\templates\core\child_list.html:20
+msgid "Birth Date"
+msgstr "Date de Naissance"
+
+#: .\core\templates\core\child_list.html:60
+msgid "No children found."
+msgstr "Aucun enfant trouvé."
+
+#: .\core\templates\core\diaperchange_confirm_delete.html:4
+msgid "Delete a Diaper Change"
+msgstr "Supprimer un Changement"
+
+#: .\core\templates\core\diaperchange_form.html:6
+msgid "Update a Diaper Change"
+msgstr "Mettre à Jour un Changement"
+
+#: .\core\templates\core\diaperchange_form.html:8
+#: .\core\templates\core\diaperchange_form.html:25
+msgid "Add a Diaper Change"
+msgstr "Ajuter un Changement"
+
+#: .\core\templates\core\diaperchange_form.html:17
+#: .\core\templates\core\feeding_form.html:17
+#: .\core\templates\core\note_form.html:17
+#: .\core\templates\core\sleep_form.html:17
+#: .\core\templates\core\tummytime_form.html:17
+msgid "Add"
+msgstr "Ajouter"
+
+#: .\core\templates\core\diaperchange_list.html:53
+msgid "No diaper changes found."
+msgstr "Aucun changement trouvé."
+
+#: .\core\templates\core\diaperchange_list.html:63
+msgid "Add a Change"
+msgstr "Ajouter un Changement"
+
+#: .\core\templates\core\feeding_confirm_delete.html:4
+msgid "Delete a Feeding"
+msgstr "Supprimer une Alimentation"
+
+#: .\core\templates\core\feeding_form.html:6
+msgid "Update a Feeding"
+msgstr "Mettre à Jour une Alimentation"
+
+#: .\core\templates\core\feeding_form.html:8
+#: .\core\templates\core\feeding_form.html:25
+#: .\core\templates\core\feeding_list.html:71
+msgid "Add a Feeding"
+msgstr "Ajouter une Alimentation"
+
+#: .\core\templates\core\feeding_list.html:22
+msgid "Amt."
+msgstr "Montant"
+
+#: .\core\templates\core\feeding_list.html:61
+msgid "No feedings found."
+msgstr "Aucun aliment trouvé."
+
+#: .\core\templates\core\note_confirm_delete.html:4
+msgid "Delete a Note"
+msgstr "Supprimer une annotation"
+
+#: .\core\templates\core\note_form.html:6
+msgid "Update a Note"
+msgstr "Mettre à Jour une annotation"
+
+#: .\core\templates\core\note_form.html:8
+#: .\core\templates\core\note_form.html:25
+#: .\core\templates\core\note_list.html:59
+msgid "Add a Note"
+msgstr "Ajouter une annotation"
+
+#: .\core\templates\core\note_list.html:49
+msgid "No notes found."
+msgstr "Aucun annotation trouvé."
+
+#: .\core\templates\core\sleep_confirm_delete.html:4
+msgid "Delete a Sleep Entry"
+msgstr "Supprimer une entrée de sommeil"
+
+#: .\core\templates\core\sleep_form.html:6
+msgid "Update a Sleep Entry"
+msgstr "Mettre à Jour une entrée de sommeil"
+
+#: .\core\templates\core\sleep_form.html:8
+#: .\core\templates\core\sleep_form.html:25
+#: .\core\templates\core\sleep_list.html:63
+msgid "Add a Sleep Entry"
+msgstr "Ajouter une entrée de sommeil"
+
+#: .\core\templates\core\sleep_list.html:19
+#: .\core\templates\core\timer_form.html:12
+#: .\core\templates\core\timer_list.html:18
+#: .\core\templates\core\tummytime_list.html:19
+msgid "Start"
+msgstr "Commencer"
+
+#: .\core\templates\core\sleep_list.html:20
+#: .\core\templates\core\timer_list.html:20
+#: .\core\templates\core\tummytime_list.html:20
+msgid "End"
+msgstr "Fin"
+
+#: .\core\templates\core\sleep_list.html:21
+msgid "Nap"
+msgstr "Sieste"
+
+#: .\core\templates\core\sleep_list.html:53
+msgid "No sleep entries found."
+msgstr "Aucune entrée de sommeil trouvée."
+
+#: .\core\templates\core\timer_confirm_delete.html:4
+#, python-format
+msgid "Delete %(object)s"
+msgstr "Supprimer %(object)s"
+
+#: .\core\templates\core\timer_detail.html:20
+msgid "Started"
+msgstr "Commencé"
+
+#: .\core\templates\core\timer_detail.html:22
+msgid "Stopped"
+msgstr "Arrêté"
+
+#: .\core\templates\core\timer_detail.html:26
+#, python-format
+msgid "%(timer)s created by %(object.user)s"
+msgstr "%(timer)s a été créé par %(object.user)s"
+
+#: .\core\templates\core\timer_detail.html:53
+msgid "Timer actions"
+msgstr "Opérations sur le chronomètre"
+
+#: .\core\templates\core\timer_form.html:20
+#: .\core\templates\core\timer_nav.html:18
+msgid "Start Timer"
+msgstr "Démarrer un chronomètre"
+
+#: .\core\templates\core\timer_list.html:37
+msgid "No timer entries found."
+msgstr "Aucun chronomètre trouvé."
+
+#: .\core\templates\core\timer_nav.html:15
+msgid "Quick Start Timer"
+msgstr "Chronomètre de démarrage rapide"
+
+#: .\core\templates\core\timer_nav.html:23
+msgid "View Timers"
+msgstr "Voir les chronomètres"
+
+#: .\core\templates\core\timer_nav.html:27
+#: .\dashboard\templates\cards\timer_list.html:4
+msgid "Active Timers"
+msgstr "Chronomètres actifs"
+
+#: .\core\templates\core\timer_nav.html:31
+#: .\dashboard\templates\cards\feeding_last_method.html:10
+msgid "None"
+msgstr "Aucun"
+
+#: .\core\templates\core\tummytime_confirm_delete.html:4
+msgid "Delete a Tummy Time Entry"
+msgstr "Supprimer une entrée d'heure de ventre"
+
+#: .\core\templates\core\tummytime_form.html:6
+msgid "Update a Tummy Time Entry"
+msgstr "Mettre à Jour une entrée d'heure de ventre"
+
+#: .\core\templates\core\tummytime_form.html:8
+#: .\core\templates\core\tummytime_form.html:25
+#: .\core\templates\core\tummytime_list.html:63
+msgid "Add a Tummy Time Entry"
+msgstr "Ajouter une entrée d'heure de ventre"
+
+#: .\core\templates\core\tummytime_list.html:53
+msgid "No tummy time entries found."
+msgstr "Aucune entrée d'heure de ventre trouvée."
+
+#: .\core\templates\core\weight_confirm_delete.html:4
+msgid "Delete a Weight Entry"
+msgstr "Supprimer une entrée de poids"
+
+#: .\core\templates\core\weight_form.html:8
+#: .\core\templates\core\weight_form.html:17
+#: .\core\templates\core\weight_form.html:25
+#: .\core\templates\core\weight_list.html:59
+msgid "Add a Weight Entry"
+msgstr "Ajouter une entrée de poids"
+
+#: .\core\templates\core\weight_list.html:49
+msgid "No weight entries found."
+msgstr "Aucune entrée de poids trouvée."
+
+#: .\core\timeline.py:24
+#, python-format
+msgid "%(child)s had a diaper change."
+msgstr "La couche de %(child)s a été changée."
+
+#: .\core\timeline.py:35
+#, python-format
+msgid "%(child)s started feeding."
+msgstr "%(child)s a commencé à se nourrir."
+
+#: .\core\timeline.py:43
+#, python-format
+msgid "%(child)s finished feeding."
+msgstr "%(child)s a fini de se nourrir."
+
+#: .\core\timeline.py:55
+#, python-format
+msgid "%(child)s fell asleep."
+msgstr "%(child)s s'est endormi."
+
+#: .\core\timeline.py:63
+#, python-format
+msgid "%(child)s woke up."
+msgstr "%(child)s s'est réveillé."
+
+#: .\core\timeline.py:75
+#, python-format
+msgid "%(child)s started tummy time!"
+msgstr "%(child)s a commencé l'heure du ventre."
+
+#: .\core\timeline.py:83
+#, python-format
+msgid "%(child)s finished tummy time."
+msgstr "%(child)s a fini l'heure du ventre."
+
+#: .\core\views.py:20
+#, python-format
+msgid "%(model)s entry for %(child)s added!"
+msgstr "Entrée de %(model)s pour %(child)s a été ajouté!"
+
+#: .\core\views.py:22
+#, python-format
+msgid "%(model)s entry added!"
+msgstr "Entrée de %(model)s a été ajouté!"
+
+#: .\core\views.py:31
+#, python-format
+msgid "%(model)s entry for %(child)s updated."
+msgstr "Entrée de %(model)s pour %(child)s a été mise à jour!"
+
+#: .\core\views.py:33
+#, python-format
+msgid "%(model)s entry updated."
+msgstr "Entrée de %(model)s a été mise à jour!"
+
+#: .\core\views.py:62
+#, python-format
+msgid "%(first_name)s %(last_name)s added!"
+msgstr "%(first_name)s %(last_name)s a été ajouté!"
+
+#: .\core\views.py:290
+#, python-format
+msgid "%(timer)s stopped."
+msgstr "%(timer)s a été arrêté"
+
+#: .\dashboard\templates\cards\diaperchange_last.html:4
+msgid "Last Diaper Change"
+msgstr "Dernier Changement"
+
+#: .\dashboard\templates\cards\diaperchange_last.html:8
+#: .\dashboard\templates\cards\feeding_last.html:8
+#: .\dashboard\templates\cards\sleep_last.html:8
+#: .\dashboard\templates\cards\tummytime_last.html:8
+#, python-format
+msgid "%(time)s ago"
+msgstr "il y a %(time)s"
+
+#: .\dashboard\templates\cards\diaperchange_last.html:10
+#: .\dashboard\templates\cards\feeding_last.html:10
+#: .\dashboard\templates\cards\sleep_last.html:10
+#: .\dashboard\templates\cards\tummytime_last.html:10
+msgid "Never"
+msgstr "Jamais"
+
+#: .\dashboard\templates\cards\diaperchange_types.html:7
+msgid "Past Week"
+msgstr "La Semaine Dernière"
+
+#: .\dashboard\templates\cards\diaperchange_types.html:18
+msgid "wet"
+msgstr "humide"
+
+#: .\dashboard\templates\cards\diaperchange_types.html:25
+msgid "solid"
+msgstr "souillé"
+
+#: .\dashboard\templates\cards\diaperchange_types.html:30
+msgid "today"
+msgstr "aujourd'hui"
+
+#: .\dashboard\templates\cards\diaperchange_types.html:32
+msgid "yesterday"
+msgstr "hier"
+
+#: .\dashboard\templates\cards\diaperchange_types.html:34
+#, python-format
+msgid "%(key)s days ago"
+msgstr "il y a %(key)s jours"
+
+#: .\dashboard\templates\cards\feeding_last.html:4
+msgid "Last Feeding"
+msgstr "Dernière Alimentation"
+
+#: .\dashboard\templates\cards\feeding_last_method.html:4
+msgid "Last Feeding Method"
+msgstr "Dernière Méthode d'Alimentation"
+
+#: .\dashboard\templates\cards\sleep_day.html:4
+msgid "Today's Sleep"
+msgstr "Sommeil Aujourd'hui"
+
+#: .\dashboard\templates\cards\sleep_day.html:11
+#: .\dashboard\templates\cards\sleep_naps_day.html:11
+#: .\dashboard\templates\cards\tummytime_day.html:11
+msgid "None yet today"
+msgstr "Pas encore aujourd'hui"
+
+#: .\dashboard\templates\cards\sleep_day.html:18
+#, python-format
+msgid "%(count)s sleep entries"
+msgstr "%(count)s entrées de sommeil."
+
+#: .\dashboard\templates\cards\sleep_last.html:4
+msgid "Last Slept"
+msgstr "Dernier dormi"
+
+#: .\dashboard\templates\cards\sleep_naps_day.html:4
+msgid "Today's Naps"
+msgstr "Siestes Aujourd'hui"
+
+#: .\dashboard\templates\cards\sleep_naps_day.html:8
+#, python-format
+msgid "%(count)s nap%(plural)s"
+msgstr "%(count)s Sieste%(plural)s"
+
+#: .\dashboard\templates\cards\statistics.html:6
+msgid "Statistics"
+msgstr "Statistiques"
+
+#: .\dashboard\templates\cards\statistics.html:23
+msgid "Not enough data"
+msgstr "Pas assez de données"
+
+#: .\dashboard\templates\cards\timer_list.html:8
+#, python-format
+msgid "%(count)s active timer%(plural)s"
+msgstr "%(count)s chronomètre%(plural)s actif"
+
+#: .\dashboard\templates\cards\timer_list.html:19
+#, python-format
+msgid "Started by %(instance.user)s at %(start)s"
+msgstr "Lancé par %(instance.user)s à %(start)s"
+
+#: .\dashboard\templates\cards\tummytime_day.html:4
+msgid "Today's Tummy Time"
+msgstr "L'heure du Ventre Aujourd'hui"
+
+#: .\dashboard\templates\cards\tummytime_day.html:20
+#, python-format
+msgid "%(duration)s at %(end)s"
+msgstr "%(duration)s à %(end)s"
+
+#: .\dashboard\templates\cards\tummytime_last.html:4
+msgid "Last Tummy Time"
+msgstr "Dernière Heure de Ventre"
+
+#: .\dashboard\templates\dashboard\child_button_group.html:3
+msgid "Child actions"
+msgstr "Opérations sur l'enfant"
+
+#: .\dashboard\templates\dashboard\child_button_group.html:19
+#: .\reports\templates\reports\diaperchange_types.html:4
+#: .\reports\templates\reports\diaperchange_types.html:8
+msgid "Diaper Change Types"
+msgstr "Types de Changement"
+
+#: .\dashboard\templates\dashboard\child_button_group.html:20
+#: .\reports\templates\reports\diaperchange_lifetimes.html:4
+#: .\reports\templates\reports\diaperchange_lifetimes.html:8
+msgid "Diaper Lifetimes"
+msgstr "Durabilité des Couches"
+
+#: .\dashboard\templates\dashboard\child_button_group.html:21
+msgid "Feeding Durations (Average)"
+msgstr "Durée d'Alimentation (Moyenne)"
+
+#: .\dashboard\templates\dashboard\child_button_group.html:22
+#: .\reports\templates\reports\sleep_pattern.html:4
+#: .\reports\templates\reports\sleep_pattern.html:8
+msgid "Sleep Pattern"
+msgstr "Rythme de Sommeil"
+
+#: .\dashboard\templates\dashboard\child_button_group.html:23
+#: .\reports\templates\reports\sleep_totals.html:4
+#: .\reports\templates\reports\sleep_totals.html:8
+msgid "Sleep Totals"
+msgstr "Totaux de Sommeil"
+
+#: .\dashboard\templatetags\cards.py:167
+msgid "Diaper change frequency"
+msgstr "Fréquence de changement"
+
+#: .\dashboard\templatetags\cards.py:173
+msgid "Feeding frequency"
+msgstr "Fréquence d'alimentation"
+
+#: .\dashboard\templatetags\cards.py:179
+msgid "Average nap duration"
+msgstr "Durée moyenne des siestes"
+
+#: .\dashboard\templatetags\cards.py:183
+msgid "Average naps per day"
+msgstr "Moyen de siestes par jour"
+
+#: .\dashboard\templatetags\cards.py:189
+msgid "Average sleep duration"
+msgstr "Durée moyenne du sommeil"
+
+#: .\dashboard\templatetags\cards.py:193
+msgid "Average awake duration"
+msgstr "Durée moyenne de veille"
+
+#: .\dashboard\templatetags\cards.py:199
+msgid "Weight change per week"
+msgstr "Changement de poids par semaine"
+
+#: .\reports\graphs\diaperchange_lifetimes.py:35
+msgid "Diaper Lifetimes "
+msgstr "Durabilité des Couches "
+
+#: .\reports\graphs\diaperchange_lifetimes.py:36
+msgid "Time between changes (hours)"
+msgstr "Temps entre les changements (heures)"
+
+#: .\reports\graphs\diaperchange_types.py:38
+msgid "Total"
+msgstr ""
+
+#: .\reports\graphs\diaperchange_types.py:45
+msgid "Diaper Change Types "
+msgstr "Types de Changements "
+
+#: .\reports\graphs\diaperchange_types.py:48
+msgid "Number of changes"
+msgstr "Nombre de changements"
+
+#: .\reports\graphs\feeding_duration.py:36
+msgid "Average duration"
+msgstr "Durée moyenne"
+
+#: .\reports\graphs\feeding_duration.py:44
+msgid "Total feedings"
+msgstr "Alimentation total"
+
+#: .\reports\graphs\feeding_duration.py:53
+msgid "Average Feeding Durations "
+msgstr "Durée d'Alimentation Moyenne "
+
+#: .\reports\graphs\feeding_duration.py:56
+msgid "Average duration (minutes)"
+msgstr "Durée moyenne (minutes)"
+
+#: .\reports\graphs\feeding_duration.py:58
+msgid "Number of feedings"
+msgstr "Nombre de alimentation"
+
+#: .\reports\graphs\sleep_pattern.py:128
+msgid "Sleep Pattern "
+msgstr "Rythme de Sommeil "
+
+#: .\reports\graphs\sleep_pattern.py:141
+msgid "Time of day"
+msgstr "Moment de la journée"
+
+#: .\reports\graphs\sleep_totals.py:40
+msgid "Total sleep"
+msgstr "Sommeil total"
+
+#: .\reports\graphs\sleep_totals.py:50
+msgid "Sleep Totals "
+msgstr "Totaux de sommeil "
+
+#: .\reports\graphs\sleep_totals.py:53
+msgid "Hours of sleep"
+msgstr "D'heures de sommeil total"
+
+#: .\reports\graphs\weight_weight.py:27
+msgid "Weight "
+msgstr "Poids "
+
+#: .\reports\templates\reports\feeding_duration.html:4
+#: .\reports\templates\reports\feeding_duration.html:8
+msgid "Average Feeding Durations"
+msgstr "Durée d'alimentation moyenne"
+
+#: .\reports\templates\reports\report_base.html:9
+msgid "Reports"
+msgstr "Rapports"
+
+#: .\reports\templates\reports\report_base.html:19
+msgid "There is no enough data to generate this report."
+msgstr "Il n'y a pas assez de données pour générer ce rapport."
diff --git a/reports/graphs/diaperchange_lifetimes.py b/reports/graphs/diaperchange_lifetimes.py
index 357185bb..00c34d66 100644
--- a/reports/graphs/diaperchange_lifetimes.py
+++ b/reports/graphs/diaperchange_lifetimes.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+from django.utils.translation import gettext as _
+
import plotly.offline as plotly
import plotly.graph_objs as go
@@ -22,7 +24,7 @@ def diaperchange_lifetimes(changes):
trace = go.Box(
y=[round(d.seconds/3600, 2) for d in durations],
- name='Changes',
+ name=_('Changes'),
jitter=0.3,
pointpos=-1.8,
boxpoints='all'
@@ -30,8 +32,8 @@ def diaperchange_lifetimes(changes):
layout_args = utils.default_graph_layout_options()
layout_args['height'] = 800
- layout_args['title'] = 'Diaper Lifetimes '
- layout_args['yaxis']['title'] = 'Time between changes (hours)'
+ layout_args['title'] = _('Diaper Lifetimes ')
+ layout_args['yaxis']['title'] = _('Time between changes (hours)')
layout_args['yaxis']['zeroline'] = False
layout_args['yaxis']['dtick'] = 1
diff --git a/reports/graphs/diaperchange_types.py b/reports/graphs/diaperchange_types.py
index 0ce7d0b7..f23a5ccd 100644
--- a/reports/graphs/diaperchange_types.py
+++ b/reports/graphs/diaperchange_types.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from django.db.models import Count, Case, When
from django.db.models.functions import TruncDate
+from django.utils.translation import gettext as _
import plotly.offline as plotly
import plotly.graph_objs as go
@@ -23,28 +24,28 @@ def diaperchange_types(changes):
solid_trace = go.Scatter(
mode='markers',
- name='Solid',
+ name=_('Solid'),
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('solid_count', flat=True)),
)
wet_trace = go.Scatter(
mode='markers',
- name='Wet',
+ name=_('Wet'),
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('wet_count', flat=True))
)
total_trace = go.Scatter(
- name='Total',
+ name=_('Total'),
x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('total', flat=True))
)
layout_args = utils.default_graph_layout_options()
layout_args['barmode'] = 'stack'
- layout_args['title'] = 'Diaper Change Types '
- layout_args['xaxis']['title'] = 'Date'
+ layout_args['title'] = _('Diaper Change Types ')
+ layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
- layout_args['yaxis']['title'] = 'Number of changes'
+ layout_args['yaxis']['title'] = _('Number of changes')
fig = go.Figure({
'data': [solid_trace, wet_trace, total_trace],
diff --git a/reports/graphs/feeding_duration.py b/reports/graphs/feeding_duration.py
index a605a514..5128eb42 100644
--- a/reports/graphs/feeding_duration.py
+++ b/reports/graphs/feeding_duration.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from django.db.models import Count, Sum
from django.db.models.functions import TruncDate
+from django.utils.translation import gettext as _
import plotly.offline as plotly
import plotly.graph_objs as go
@@ -32,7 +33,7 @@ def feeding_duration(instances):
averages.append(total['sum']/total['count'])
trace_avg = go.Scatter(
- name='Average duration',
+ name=_('Average duration'),
line=dict(shape='spline'),
x=list(totals.values_list('date', flat=True)),
y=[td.seconds/60 for td in averages],
@@ -40,7 +41,7 @@ def feeding_duration(instances):
text=[_duration_string_ms(td) for td in averages]
)
trace_count = go.Scatter(
- name='Total feedings',
+ name=_('Total feedings'),
mode='markers',
x=list(totals.values_list('date', flat=True)),
y=list(totals.values_list('count', flat=True)),
@@ -49,12 +50,12 @@ def feeding_duration(instances):
)
layout_args = utils.default_graph_layout_options()
- layout_args['title'] = 'Average Feeding Durations '
- layout_args['xaxis']['title'] = 'Date'
+ layout_args['title'] = _('Average Feeding Durations ')
+ layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
- layout_args['yaxis']['title'] = 'Average duration (minutes)'
+ layout_args['yaxis']['title'] = _('Average duration (minutes)')
layout_args['yaxis2'] = dict(layout_args['yaxis'])
- layout_args['yaxis2']['title'] = 'Number of feedings'
+ layout_args['yaxis2']['title'] = _('Number of feedings')
layout_args['yaxis2']['overlaying'] = 'y'
layout_args['yaxis2']['side'] = 'right'
diff --git a/reports/graphs/sleep_pattern.py b/reports/graphs/sleep_pattern.py
index abba7460..3c00c0bd 100644
--- a/reports/graphs/sleep_pattern.py
+++ b/reports/graphs/sleep_pattern.py
@@ -2,6 +2,7 @@
from collections import OrderedDict
from django.utils import timezone
+from django.utils.translation import gettext as _
import pandas as pd
import plotly.offline as plotly
@@ -124,10 +125,10 @@ def sleep_pattern(instances):
layout_args['barmode'] = 'stack'
layout_args['hovermode'] = 'closest'
- layout_args['title'] = 'Sleep Pattern '
+ layout_args['title'] = _('Sleep Pattern ')
layout_args['height'] = 800
- layout_args['xaxis']['title'] = 'Date'
+ layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['tickangle'] = -65
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
@@ -137,7 +138,7 @@ def sleep_pattern(instances):
for i in range(30, 60*24, 30):
ticks[i] = (start + timezone.timedelta(minutes=i)).strftime('%I:%M %p')
- layout_args['yaxis']['title'] = 'Time of day'
+ layout_args['yaxis']['title'] = _('Time of day')
layout_args['yaxis']['rangemode'] = 'tozero'
layout_args['yaxis']['tickmode'] = 'array'
layout_args['yaxis']['tickvals'] = list(ticks.keys())
diff --git a/reports/graphs/sleep_totals.py b/reports/graphs/sleep_totals.py
index fdfabb37..0e5f3a37 100644
--- a/reports/graphs/sleep_totals.py
+++ b/reports/graphs/sleep_totals.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from django.utils import timezone
+from django.utils.translation import gettext as _
import plotly.offline as plotly
import plotly.graph_objs as go
@@ -36,7 +37,7 @@ def sleep_totals(instances):
totals[start.date()] += instance.duration
trace = go.Bar(
- name='Total sleep',
+ name=_('Total sleep'),
x=list(totals.keys()),
y=[td.seconds/3600 for td in totals.values()],
hoverinfo='text',
@@ -46,10 +47,10 @@ def sleep_totals(instances):
layout_args = utils.default_graph_layout_options()
layout_args['barmode'] = 'stack'
- layout_args['title'] = 'Sleep Totals '
- layout_args['xaxis']['title'] = 'Date'
+ layout_args['title'] = _('Sleep Totals ')
+ layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
- layout_args['yaxis']['title'] = 'Hours of sleep'
+ layout_args['yaxis']['title'] = _('Hours of sleep')
fig = go.Figure({
'data': [trace],
diff --git a/reports/graphs/weight_weight.py b/reports/graphs/weight_weight.py
index dad70568..11eeea12 100644
--- a/reports/graphs/weight_weight.py
+++ b/reports/graphs/weight_weight.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+from django.utils.translation import gettext as _
+
import plotly.offline as plotly
import plotly.graph_objs as go
@@ -14,7 +16,7 @@ def weight_weight(objects):
objects = objects.order_by('-date')
trace = go.Scatter(
- name='Weight',
+ name=_('Weight'),
x=list(objects.values_list('date', flat=True)),
y=list(objects.values_list('weight', flat=True)),
fill='tozeroy',
@@ -22,10 +24,10 @@ def weight_weight(objects):
layout_args = utils.default_graph_layout_options()
layout_args['barmode'] = 'stack'
- layout_args['title'] = 'Weight '
- layout_args['xaxis']['title'] = 'Date'
+ layout_args['title'] = _('Weight ')
+ layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
- layout_args['yaxis']['title'] = 'Weight'
+ layout_args['yaxis']['title'] = _('Weight')
fig = go.Figure({
'data': [trace],
diff --git a/reports/templates/reports/diaperchange_lifetimes.html b/reports/templates/reports/diaperchange_lifetimes.html
index fb828e72..d6616254 100644
--- a/reports/templates/reports/diaperchange_lifetimes.html
+++ b/reports/templates/reports/diaperchange_lifetimes.html
@@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %}
+{% load i18n %}
-{% block title %}Diaper Lifetimes - {{ object }}{% endblock %}
+{% block title %}{% trans "Diaper Lifetimes" %} - {{ object }}{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
- Diaper Lifetimes
+ {% trans "Diaper Lifetimes" %}
{% endblock %}
diff --git a/reports/templates/reports/diaperchange_types.html b/reports/templates/reports/diaperchange_types.html
index be74ebb9..08407907 100644
--- a/reports/templates/reports/diaperchange_types.html
+++ b/reports/templates/reports/diaperchange_types.html
@@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %}
+{% load i18n %}
-{% block title %}Diaper Change Types - {{ object }}{% endblock %}
+{% block title %}{% trans "Diaper Change Types" %} - {{ object }}{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
- Diaper Types
+ {% trans "Diaper Change Types" %}
{% endblock %}
diff --git a/reports/templates/reports/feeding_duration.html b/reports/templates/reports/feeding_duration.html
index bd216360..964121ef 100644
--- a/reports/templates/reports/feeding_duration.html
+++ b/reports/templates/reports/feeding_duration.html
@@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %}
+{% load i18n %}
-{% block title %}Average Feeding Durations - {{ object }}{% endblock %}
+{% block title %}{% trans "Average Feeding Durations" %} - {{ object }}{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
- Average Feeding Durations
+ {% trans "Average Feeding Durations" %}
{% endblock %}
diff --git a/reports/templates/reports/report_base.html b/reports/templates/reports/report_base.html
index 92065b73..8f584516 100644
--- a/reports/templates/reports/report_base.html
+++ b/reports/templates/reports/report_base.html
@@ -1,12 +1,12 @@
{% extends 'babybuddy/page.html' %}
-{% load static %}
+{% load i18n static %}
{% block title %}{% endblock %}
{% block breadcrumbs %}
- Children
+ {% trans "Children" %}
{{ object }}
- Reports
+ {% trans "Reports" %}
{% endblock %}
{% block content %}
@@ -16,7 +16,7 @@
{% else %}
- There is no enough data to generate this report.
+ {% trans "There is no enough data to generate this report." %}
{% endif %}
diff --git a/reports/templates/reports/sleep_pattern.html b/reports/templates/reports/sleep_pattern.html
index 1b457a89..a650a3ce 100644
--- a/reports/templates/reports/sleep_pattern.html
+++ b/reports/templates/reports/sleep_pattern.html
@@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %}
+{% load i18n %}
-{% block title %}Sleep Patterns - {{ object }}{% endblock %}
+{% block title %}{% trans "Sleep Pattern" %} - {{ object }}{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
- Sleep Pattern
+ {% trans "Sleep Pattern" %}
{% endblock %}
diff --git a/reports/templates/reports/sleep_totals.html b/reports/templates/reports/sleep_totals.html
index e5475436..fe8f84cb 100644
--- a/reports/templates/reports/sleep_totals.html
+++ b/reports/templates/reports/sleep_totals.html
@@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %}
+{% load i18n %}
-{% block title %}Sleep Totals - {{ object }}{% endblock %}
+{% block title %}{% trans "Sleep Totals" %} - {{ object }}{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
- Sleep Totals
+ {% trans "Sleep Totals" %}
{% endblock %}
diff --git a/reports/templates/reports/weight_change.html b/reports/templates/reports/weight_change.html
index 72c75a3e..3e5a1f22 100644
--- a/reports/templates/reports/weight_change.html
+++ b/reports/templates/reports/weight_change.html
@@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %}
+{% load i18n %}
-{% block title %}Weight - {{ object }}{% endblock %}
+{% block title %}{% trans "Weight" %} - {{ object }}{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
- Weight
+ {% trans "Weight" %}
{% endblock %}