Merge pull request #63 from cdubz/i18n

Make Baby Buddy translatable
This commit is contained in:
Christopher Charbonneau Wells 2019-04-18 20:36:37 -07:00 committed by GitHub
commit 22536f88cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 2610 additions and 594 deletions

View File

@ -7,6 +7,7 @@
- [Contributions](#contributions) - [Contributions](#contributions)
- [Pull request process](#pull-request-process) - [Pull request process](#pull-request-process)
- [Translation](#translation)
- [Development](#development) - [Development](#development)
- [Installation](#installation) - [Installation](#installation)
- [Gulp Commands](#gulp-commands) - [Gulp Commands](#gulp-commands)
@ -34,6 +35,33 @@ Gitter.
New pull requests will be reviewed by project maintainers as soon as possible 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. 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 ## Development
### Requirements ### Requirements
@ -92,10 +120,12 @@ in the [`babybuddy/management/commands`](babybuddy/management/commands) folder.
- [`gulp build`](#build) - [`gulp build`](#build)
- [`gulp clean`](#clean) - [`gulp clean`](#clean)
- [`gulp collectstatic`](#collectstatic) - [`gulp collectstatic`](#collectstatic)
- [`gulp compilemessages`](#compilemessages)
- [`gulp coverage`](#coverage) - [`gulp coverage`](#coverage)
- [`gulp extras`](#extras) - [`gulp extras`](#extras)
- [`gulp fake`](#fake) - [`gulp fake`](#fake)
- [`gulp lint`](#lint) - [`gulp lint`](#lint)
- [`gulp makemessages`](#makemessages)
- [`gulp makemigrations`](#makemigrations) - [`gulp makemigrations`](#makemigrations)
- [`gulp migrate`](#migrate) - [`gulp migrate`](#migrate)
- [`gulp reset`](#reset) - [`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 this command for production deployments. Gulp also passes along
non-overlapping arguments for this command, e.g. `--no-input`. 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` #### `coverage`
Create a test coverage report. See [`.coveragerc`](.coveragerc) for default 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. 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` #### `makemigrations`
Executes Django's `makemigrations` management task. Gulp also passes along Executes Django's `makemigrations` management task. Gulp also passes along

View File

@ -2,16 +2,18 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from babybuddy import models from babybuddy import models
class SettingsInline(admin.StackedInline): class SettingsInline(admin.StackedInline):
model = models.Settings model = models.Settings
verbose_name_plural = 'Settings' verbose_name = _('Settings')
verbose_name_plural = _('Settings')
can_delete = False can_delete = False
fieldsets = ( fieldsets = (
('Dashboard', { (_('Dashboard'), {
'fields': ('dashboard_refresh_rate',) 'fields': ('dashboard_refresh_rate',)
}), }),
) )

View File

@ -41,4 +41,4 @@ class UserPasswordForm(PasswordChangeForm):
class UserSettingsForm(forms.ModelForm): class UserSettingsForm(forms.ModelForm):
class Meta: class Meta:
model = Settings model = Settings
fields = ['dashboard_refresh_rate'] fields = ['dashboard_refresh_rate', 'language']

View File

@ -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'),
),
]

View File

@ -1,9 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.signals import user_logged_in
from django.db import models from django.db import models
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.timezone import timedelta 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 from rest_framework.authtoken.models import Token
@ -11,26 +16,32 @@ from rest_framework.authtoken.models import Token
class Settings(models.Model): class Settings(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) user = models.OneToOneField(User, on_delete=models.CASCADE)
dashboard_refresh_rate = models.DurationField( dashboard_refresh_rate = models.DurationField(
verbose_name='Refresh rate', verbose_name=_('Refresh rate'),
help_text='This setting will only be used when a browser does not ' help_text=_('This setting will only be used when a browser does not '
'support refresh on focus.', 'support refresh on focus.'),
blank=True, blank=True,
null=True, null=True,
default=timedelta(minutes=1), default=timedelta(minutes=1),
choices=[ choices=[
(None, 'disabled'), (None, _('disabled')),
(timedelta(minutes=1), '1 min.'), (timedelta(minutes=1), _('1 min.')),
(timedelta(minutes=2), '2 min.'), (timedelta(minutes=2), _('2 min.')),
(timedelta(minutes=3), '3 min.'), (timedelta(minutes=3), _('3 min.')),
(timedelta(minutes=4), '4 min.'), (timedelta(minutes=4), _('4 min.')),
(timedelta(minutes=5), '5 min.'), (timedelta(minutes=5), _('5 min.')),
(timedelta(minutes=10), '10 min.'), (timedelta(minutes=10), _('10 min.')),
(timedelta(minutes=15), '15 min.'), (timedelta(minutes=15), _('15 min.')),
(timedelta(minutes=30), '30 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): 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): def api_key(self, reset=False):
""" """
@ -63,3 +74,9 @@ def create_user_settings(sender, instance, created, **kwargs):
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def save_user_settings(sender, instance, **kwargs): def save_user_settings(sender, instance, **kwargs):
instance.settings.save() 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

View File

@ -1,5 +1,6 @@
import os import os
from django.utils.translation import gettext_lazy as _
from dotenv import load_dotenv, find_dotenv from dotenv import load_dotenv, find_dotenv
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
@ -53,10 +54,9 @@ INSTALLED_APPS = [
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -110,7 +110,7 @@ LOGOUT_REDIRECT_URL = '/login/'
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/ # 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') TIME_ZONE = os.environ.get('TIME_ZONE', 'Etc/UTC')
@ -120,6 +120,15 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
LOCALE_PATHS = [
os.path.join(BASE_DIR, "locale"),
]
LANGUAGES = [
('en', _('English')),
('fr', _('French')),
]
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/ # https://docs.djangoproject.com/en/1.11/howto/static-files/

View File

@ -1,15 +1,15 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Permission Denied</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Permission Denied" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
You do not have permission to access this resource. Contact a site {% blocktrans %}You do not have permission to access this resource.
administrator for assistance. Contact a site administrator for assistance.{% endblocktrans %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,4 +1,4 @@
{% load static %} {% load i18n static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
@ -31,7 +31,7 @@
{% block breadcrumb_nav %} {% block breadcrumb_nav %}
<nav aria-label="breadcrumb" role="navigation"> <nav aria-label="breadcrumb" role="navigation">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li> <li class="breadcrumb-item"><a href="/">{% trans "Home" %}</a></li>
{% block breadcrumbs %}{% endblock %} {% block breadcrumbs %}{% endblock %}
</ol> </ol>
</nav> </nav>

View File

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

View File

@ -1,4 +1,4 @@
{% load widget_tweaks %} {% load i18n widget_tweaks %}
<div class="container-fluid"> <div class="container-fluid">
<form role="form" method="post" enctype="multipart/form-data"> <form role="form" method="post" enctype="multipart/form-data">
@ -8,6 +8,6 @@
{% include 'babybuddy/form_field.html' %} {% include 'babybuddy/form_field.html' %}
</div> </div>
{% endfor %} {% endfor %}
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
</form> </form>
</div> </div>

View File

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

View File

@ -1,5 +1,5 @@
{% extends 'babybuddy/base.html' %} {% extends 'babybuddy/base.html' %}
{% load babybuddy_tags static timers %} {% load babybuddy_tags i18n static timers %}
{% block nav %} {% block nav %}
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top"> <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
@ -23,32 +23,38 @@
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-quick-add-link"> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-quick-add-link">
{% if perms.core.add_diaperchange %} {% if perms.core.add_diaperchange %}
<a class="dropdown-item p-2" href="{% url 'core:diaperchange-add' %}"> <a class="dropdown-item p-2" href="{% url 'core:diaperchange-add' %}">
<i class="icon icon-diaperchange" aria-hidden="true"></i> Diaper Change <i class="icon icon-diaperchange" aria-hidden="true"></i>
{% trans "Diaper Change" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_feeding %} {% if perms.core.add_feeding %}
<a class="dropdown-item p-2" href="{% url 'core:feeding-add' %}"> <a class="dropdown-item p-2" href="{% url 'core:feeding-add' %}">
<i class="icon icon-feeding" aria-hidden="true"></i> Feeding <i class="icon icon-feeding" aria-hidden="true"></i>
{% trans "Feeding" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_note %} {% if perms.core.add_note %}
<a class="dropdown-item p-2" href="{% url 'core:note-add' %}"> <a class="dropdown-item p-2" href="{% url 'core:note-add' %}">
<i class="icon icon-note" aria-hidden="true"></i> Note <i class="icon icon-note" aria-hidden="true"></i>
{% trans "Note" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_sleep %} {% if perms.core.add_sleep %}
<a class="dropdown-item p-2" href="{% url 'core:sleep-add' %}"> <a class="dropdown-item p-2" href="{% url 'core:sleep-add' %}">
<i class="icon icon-sleep" aria-hidden="true"></i> Sleep <i class="icon icon-sleep" aria-hidden="true"></i>
{% trans "Sleep" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_tummytime %} {% if perms.core.add_tummytime %}
<a class="dropdown-item p-2" href="{% url 'core:tummytime-add' %}"> <a class="dropdown-item p-2" href="{% url 'core:tummytime-add' %}">
<i class="icon icon-tummytime" aria-hidden="true"></i> Tummy Time <i class="icon icon-tummytime" aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_weight %} {% if perms.core.add_weight %}
<a class="dropdown-item p-2" href="{% url 'core:weight-add' %}"> <a class="dropdown-item p-2" href="{% url 'core:weight-add' %}">
<i class="icon icon-weight" aria-hidden="true"></i> Weight <i class="icon icon-weight" aria-hidden="true"></i>
{% trans "Weight" %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
@ -66,7 +72,8 @@
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li class="nav-item{% if request.path == '/' %} active{% endif %}"> <li class="nav-item{% if request.path == '/' %} active{% endif %}">
<a class="nav-link" href="{% url 'dashboard:dashboard' %}"> <a class="nav-link" href="{% url 'dashboard:dashboard' %}">
<i class="icon icon-dashboard" aria-hidden="true"></i> Dashboard <i class="icon icon-dashboard" aria-hidden="true"></i>
{% trans "Dashboard" %}
</a> </a>
</li> </li>
@ -76,40 +83,51 @@
href="#" href="#"
data-toggle="dropdown" data-toggle="dropdown"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false"><i class="icon icon-child" aria-hidden="true"></i> Children</a> aria-expanded="false"><i class="icon icon-child" aria-hidden="true"></i>
{% trans "Children" %}
</a>
<div class="dropdown-menu" aria-labelledby="nav-children-menu-link"> <div class="dropdown-menu" aria-labelledby="nav-children-menu-link">
{% if perms.core.view_child %} {% if perms.core.view_child %}
<a class="dropdown-item{% if request.path == '/children/' %} active{% endif %}" <a class="dropdown-item{% if request.path == '/children/' %} active{% endif %}"
href="{% url 'core:child-list' %}"> href="{% url 'core:child-list' %}">
<i class="icon icon-child" aria-hidden="true"></i> Children <i class="icon icon-child" aria-hidden="true"></i>
{% trans "Children" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_child %} {% if perms.core.add_child %}
<a class="dropdown-item pl-5{% if request.path == '/children/add/' %} active{% endif %}" <a class="dropdown-item pl-5{% if request.path == '/children/add/' %} active{% endif %}"
href="{% url 'core:child-add' %}"><i class="icon icon-add" aria-hidden="true"></i> Child</a> href="{% url 'core:child-add' %}"><i class="icon icon-add" aria-hidden="true"></i>
{% trans "Child" %}
</a>
{% endif %} {% endif %}
{% if perms.core.view_note %} {% if perms.core.view_note %}
<a class="dropdown-item{% if request.path == '/notes/' %} active{% endif %}" <a class="dropdown-item{% if request.path == '/notes/' %} active{% endif %}"
href="{% url 'core:note-list' %}"> href="{% url 'core:note-list' %}">
<i class="icon icon-note" aria-hidden="true"></i> Notes <i class="icon icon-note" aria-hidden="true"></i>
{% trans "Notes" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_note %} {% if perms.core.add_note %}
<a class="dropdown-item pl-5{% if request.path == '/notes/add/' %} active{% endif %}" <a class="dropdown-item pl-5{% if request.path == '/notes/add/' %} active{% endif %}"
href="{% url 'core:note-add' %}"><i class="icon icon-add" aria-hidden="true"></i> Note</a> href="{% url 'core:note-add' %}"><i class="icon icon-add" aria-hidden="true"></i>
{% trans "Note" %}
</a>
{% endif %} {% endif %}
{% if perms.core.view_weight %} {% if perms.core.view_weight %}
<a class="dropdown-item{% if request.path == '/weight/' %} active{% endif %}" <a class="dropdown-item{% if request.path == '/weight/' %} active{% endif %}"
href="{% url 'core:weight-list' %}"> href="{% url 'core:weight-list' %}">
<i class="icon icon-weight" aria-hidden="true"></i> Weight <i class="icon icon-weight" aria-hidden="true"></i>
{% trans "Weight" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.add_weight %} {% if perms.core.add_weight %}
<a class="dropdown-item pl-5{% if request.path == '/weight/add/' %} active{% endif %}" <a class="dropdown-item pl-5{% if request.path == '/weight/add/' %} active{% endif %}"
href="{% url 'core:weight-add' %}"><i class="icon icon-add" aria-hidden="true"></i> Weight entry</a> href="{% url 'core:weight-add' %}"><i class="icon icon-add" aria-hidden="true"></i>
{% trans "Weight entry" %}
</a>
{% endif %} {% endif %}
</div> </div>
@ -121,43 +139,61 @@
href="#" href="#"
data-toggle="dropdown" data-toggle="dropdown"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false"><i class="icon icon-activities" aria-hidden="true"></i> Activities</a> aria-expanded="false"><i class="icon icon-activities" aria-hidden="true"></i>
{% trans "Activities" %}
</a>
<div class="dropdown-menu" aria-labelledby="nav-activity-menu-link"> <div class="dropdown-menu" aria-labelledby="nav-activity-menu-link">
{% if perms.core.view_diaperchange %} {% if perms.core.view_diaperchange %}
<a class="dropdown-item{% if request.path == '/changes/' %} active{% endif %}" <a class="dropdown-item{% if request.path == '/changes/' %} active{% endif %}"
href="{% url 'core:diaperchange-list' %}"><i class="icon icon-diaperchange" aria-hidden="true"></i> Changes</a> href="{% url 'core:diaperchange-list' %}"><i class="icon icon-diaperchange" aria-hidden="true"></i>
{% trans "Changes" %}
</a>
{% endif %} {% endif %}
{% if perms.core.add_diaperchange %} {% if perms.core.add_diaperchange %}
<a class="dropdown-item pl-5{% if request.path == '/changes/add/' %} active{% endif %}" <a class="dropdown-item pl-5{% if request.path == '/changes/add/' %} active{% endif %}"
href="{% url 'core:diaperchange-add' %}"><i class="icon icon-add" aria-hidden="true"></i> Change</a> href="{% url 'core:diaperchange-add' %}"><i class="icon icon-add" aria-hidden="true"></i>
{% trans "Change" %}
</a>
{% endif %} {% endif %}
{% if perms.core.view_feeding %} {% if perms.core.view_feeding %}
<a class="dropdown-item{% if request.path == '/feedings/' %} active{% endif %}" <a class="dropdown-item{% if request.path == '/feedings/' %} active{% endif %}"
href="{% url 'core:feeding-list' %}"><i class="icon icon-feeding" aria-hidden="true"></i> Feedings</a> href="{% url 'core:feeding-list' %}"><i class="icon icon-feeding" aria-hidden="true"></i>
{% trans "Feedings" %}
</a>
{% endif %} {% endif %}
{% if perms.core.add_diaperchange %} {% if perms.core.add_diaperchange %}
<a class="dropdown-item pl-5{% if request.path == '/feedings/add/' %} active{% endif %}" <a class="dropdown-item pl-5{% if request.path == '/feedings/add/' %} active{% endif %}"
href="{% url 'core:feeding-add' %}"><i class="icon icon-add" aria-hidden="true"></i> Feeding</a> href="{% url 'core:feeding-add' %}"><i class="icon icon-add" aria-hidden="true"></i>
{% trans "Feeding" %}
</a>
{% endif %} {% endif %}
{% if perms.core.view_sleep %} {% if perms.core.view_sleep %}
<a class="dropdown-item{% if request.path == '/sleep/' %} active{% endif %}" <a class="dropdown-item{% if request.path == '/sleep/' %} active{% endif %}"
href="{% url 'core:sleep-list' %}"><i class="icon icon-sleep" aria-hidden="true"></i> Sleep</a> href="{% url 'core:sleep-list' %}"><i class="icon icon-sleep" aria-hidden="true"></i>
{% trans "Sleep" %}
</a>
{% endif %} {% endif %}
{% if perms.core.add_sleep %} {% if perms.core.add_sleep %}
<a class="dropdown-item pl-5{% if request.path == '/sleep/add/' %} active{% endif %}" <a class="dropdown-item pl-5{% if request.path == '/sleep/add/' %} active{% endif %}"
href="{% url 'core:sleep-add' %}"><i class="icon icon-add" aria-hidden="true"></i> Sleep entry</a> href="{% url 'core:sleep-add' %}"><i class="icon icon-add" aria-hidden="true"></i>
{% trans "Sleep entry" %}
</a>
{% endif %} {% endif %}
{% if perms.core.view_tummytime %} {% if perms.core.view_tummytime %}
<a class="dropdown-item{% if request.path == '/tummy-time/' %} active{% endif %}" <a class="dropdown-item{% if request.path == '/tummy-time/' %} active{% endif %}"
href="{% url 'core:tummytime-list' %}"><i class="icon icon-tummytime" aria-hidden="true"></i> Tummy Time</a> href="{% url 'core:tummytime-list' %}"><i class="icon icon-tummytime" aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a>
{% endif %} {% endif %}
{% if perms.core.add_tummytime %} {% if perms.core.add_tummytime %}
<a class="dropdown-item pl-5{% if request.path == '/tummy-time/add/' %} active{% endif %}" <a class="dropdown-item pl-5{% if request.path == '/tummy-time/add/' %} active{% endif %}"
href="{% url 'core:tummytime-add' %}"><i class="icon icon-add" aria-hidden="true"></i> Tummy Time entry</a> href="{% url 'core:tummytime-add' %}"><i class="icon icon-add" aria-hidden="true"></i>
{% trans "Tummy Time entry" %}
</a>
{% endif %} {% endif %}
</div> </div>
@ -179,29 +215,29 @@
aria-expanded="false"><i class="icon icon-user" aria-hidden="true"></i> {{ request.user }} aria-expanded="false"><i class="icon icon-user" aria-hidden="true"></i> {{ request.user }}
</a> </a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-user-menu-link"> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-user-menu-link">
<h6 class="dropdown-header">User</h6> <h6 class="dropdown-header">{% trans "User" %}</h6>
<a href="{% url 'babybuddy:user-settings' %}" class="dropdown-item">Settings</a> <a href="{% url 'babybuddy:user-settings' %}" class="dropdown-item">{% trans "Settings" %}</a>
<a href="{% url 'babybuddy:user-password' %}" class="dropdown-item">Password</a> <a href="{% url 'babybuddy:user-password' %}" class="dropdown-item">{% trans "Password" %}</a>
<a href="{% url 'babybuddy:logout' %}" class="dropdown-item">Logout</a> <a href="{% url 'babybuddy:logout' %}" class="dropdown-item">{% trans "Logout" %}</a>
<h6 class="dropdown-header">Site</h6> <h6 class="dropdown-header">{% trans "Site" %}</h6>
<a href="{% url 'api:api-root' %}" <a href="{% url 'api:api-root' %}"
class="dropdown-item" class="dropdown-item"
target="_blank">API Browser</a> target="_blank">{% trans "API Browser" %}</a>
{% if request.user.is_staff %} {% if request.user.is_staff %}
<a href="{% url 'babybuddy:user-list' %}" class="dropdown-item">Users</a> <a href="{% url 'babybuddy:user-list' %}" class="dropdown-item">{% trans "Users" %}</a>
<a href="{% url 'admin:index' %}" <a href="{% url 'admin:index' %}"
class="dropdown-item" class="dropdown-item"
target="_blank">Backend Admin</a> target="_blank">{% trans "Backend Admin" %}</a>
{% endif %} {% endif %}
<h6 class="dropdown-header">Support</h6> <h6 class="dropdown-header">{% trans "Support" %}</h6>
<a href="https://github.com/cdubz/babybuddy" <a href="https://github.com/cdubz/babybuddy"
class="dropdown-item" class="dropdown-item"
target="_blank"> target="_blank">
<i class="icon icon-source" aria-hidden="true"></i> Source Code</a> <i class="icon icon-source" aria-hidden="true"></i> {% trans "Source Code" %}</a>
<a href="https://gitter.im/babybuddy/Lobby" <a href="https://gitter.im/babybuddy/Lobby"
class="dropdown-item" class="dropdown-item"
target="_blank"> target="_blank">
<i class="icon icon-chat" aria-hidden="true"></i> Chat / Support</a> <i class="icon icon-chat" aria-hidden="true"></i> {% trans "Chat / Support" %}</a>
<h6 class="dropdown-header">v{% version_string %}</h6> <h6 class="dropdown-header">v{% version_string %}</h6>
</div> </div>
</li> </li>

View File

@ -1,4 +1,4 @@
{% load babybuddy_tags %} {% load i18n babybuddy_tags %}
{% if is_paginated %} {% if is_paginated %}
<nav aria-label="Page navigation"> <nav aria-label="Page navigation">
@ -8,7 +8,7 @@
<li class="page-item"> <li class="page-item">
<a class="page-link" href="{% relative_url 'page' page_obj.previous_page_number %}" aria-label="Previous"> <a class="page-link" href="{% relative_url 'page' page_obj.previous_page_number %}" aria-label="Previous">
<i class="icon icon-chevron-left" aria-hidden="true"></i> <i class="icon icon-chevron-left" aria-hidden="true"></i>
<span class="sr-only">Previous</span> <span class="sr-only">{% trans "Previous" %}</span>
</a> </a>
</li> </li>
{% endif %} {% endif %}
@ -25,7 +25,7 @@
<li class="page-item"> <li class="page-item">
<a class="page-link" href="{% relative_url 'page' page_obj.next_page_number %}" aria-label="Next"> <a class="page-link" href="{% relative_url 'page' page_obj.next_page_number %}" aria-label="Next">
<i class="icon icon-chevron-right" aria-hidden="true"></i> <i class="icon icon-chevron-right" aria-hidden="true"></i>
<span class="sr-only">Next</span> <span class="sr-only">{% trans "Next" %}</span>
</a> </a>
</li> </li>
{% endif %} {% endif %}

View File

@ -1,19 +1,19 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load i18n widget_tweaks %}
{% block title %}Delete User{% endblock %} {% block title %}{% trans "Delete User" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'babybuddy:user-list' %}">Users</a></li> <li class="breadcrumb-item"><a href="{% url 'babybuddy:user-list' %}">{% trans "Users" %}</a></li>
<li class="breadcrumb-item">{{ object }}</li> <li class="breadcrumb-item">{{ object }}</li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="Delete" class="btn btn-danger" />
<a href="{% url 'babybuddy:user-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'babybuddy:user-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,28 +1,29 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if object %} {% if object %}
{{ object }} {{ object }}
{% else %} {% else %}
Create User {% trans "Create User" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'babybuddy:user-list' %}">Users</a></li> <li class="breadcrumb-item"><a href="{% url 'babybuddy:user-list' %}">{% trans "Users" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item font-weight-bold">{{ object }}</li> <li class="breadcrumb-item font-weight-bold">{{ object }}</li>
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Create User</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Create User" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Create User</h1> <h1>{% trans "Create User" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load bootstrap widget_tweaks %} {% load bootstrap i18n widget_tweaks %}
{% block title %}Users{% endblock %} {% block title %}{% trans "Users" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Users</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Users" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -14,13 +14,13 @@
<table class="table table-striped table-hover user-list"> <table class="table table-striped table-hover user-list">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>User</th> <th>{% trans "User" %}</th>
<th>First Name</th> <th>{% trans "First Name" %}</th>
<th>Last Name</th> <th>{% trans "Last Name" %}</th>
<th>Email</th> <th>{% trans "Email" %}</th>
<th>Staff</th> <th>{% trans "Staff" %}</th>
<th>Active</th> <th>{% trans "Active" %}</th>
<th class="text-center">Actions</th> <th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -52,7 +52,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="4">No users found.</th> <th colspan="4">{% trans "No users found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -62,7 +62,7 @@
{% if perms.admin.add_user %} {% if perms.admin.add_user %}
<a href="{% url 'babybuddy:user-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'babybuddy:user-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-add" aria-hidden="true"></i> Create User <i class="icon icon-add" aria-hidden="true"></i> {% trans "Create User" %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,14 +1,14 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load i18n widget_tweaks %}
{% block title %}Change Password{% endblock %} {% block title %}{% trans "Change Password" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item">User</li> <li class="breadcrumb-item">{% trans "User" %}</li>
<li class="breadcrumb-item active">Change Password</li> <li class="breadcrumb-item active">{% trans "Change Password" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Change Password</h1> <h1>{% trans "Change Password" %}</h1>
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,31 +1,31 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load i18n widget_tweaks %}
{% block title %}User Settings{% endblock %} {% block title %}{% trans "User Settings" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item">User</li> <li class="breadcrumb-item">{% trans "User" %}</li>
<li class="breadcrumb-item active">Settings</li> <li class="breadcrumb-item active">{% trans "Settings" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>User Settings</h1> <h1>{% trans "User Settings" %}</h1>
<div class="container-fluid"> <div class="container-fluid">
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
{% if form.non_field_errors %} {% if form.non_field_errors %}
{% for error in form.non_field_errors %} {% for error in form.non_field_errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
<strong>Error:</strong> {{ error }} {% blocktrans %}<strong>Error:</strong> {{ error }}{% endblocktrans %}
</div> </div>
{% endfor %} {% endfor %}
{% elif form.errors %} {% elif form.errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
<strong>Error:</strong> Some fields have errors. See below for details. {% blocktrans %}<strong>Error:</strong> Some fields have errors. See below for details.{% endblocktrans %}
</div> </div>
{% endif %} {% endif %}
<fieldset> <fieldset>
<legend>User Profile</legend> <legend>{% trans "User Profile" %}</legend>
<div class="form-group row"> <div class="form-group row">
{% with form_user.first_name as field %} {% with form_user.first_name as field %}
{% include 'babybuddy/form_field.html' %} {% include 'babybuddy/form_field.html' %}
@ -41,9 +41,14 @@
{% include 'babybuddy/form_field.html' %} {% include 'babybuddy/form_field.html' %}
{% endwith %} {% endwith %}
</div> </div>
<div class="form-group row">
{% with form_settings.language as field %}
{% include 'babybuddy/form_field.html' %}
{% endwith %}
</div>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Dashboard</legend> <legend>{% trans "Dashboard" %}</legend>
<div class="form-group row"> <div class="form-group row">
{% with form_settings.dashboard_refresh_rate as field %} {% with form_settings.dashboard_refresh_rate as field %}
{% include 'babybuddy/form_field.html' %} {% include 'babybuddy/form_field.html' %}
@ -51,16 +56,16 @@
</div> </div>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>API</legend> <legend>{% trans "API" %}</legend>
<div class="form-group row"> <div class="form-group row">
<label for="id_email" class="col-sm-2 col-form-label">Key</label> <label for="id_email" class="col-sm-2 col-form-label">{% trans "Key" %}</label>
<div class="col-sm-10"> <div class="col-sm-10">
<samp>{{ user.settings.api_key }}</samp> <samp>{{ user.settings.api_key }}</samp>
<a class="btn btn-xs btn-danger" href="{% url 'babybuddy:user-reset-api-key' %}">Regenerate</a> <a class="btn btn-xs btn-danger" href="{% url 'babybuddy:user-reset-api-key' %}">{% trans "Regenerate" %}</a>
</div> </div>
</div> </div>
</fieldset> </fieldset>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
</form> </form>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load i18n widget_tweaks %}
{% block title %}Welcome!{% endblock %} {% block title %}{% trans "Welcome!" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Welcome!</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Welcome!" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="jumbotron"> <div class="jumbotron">
<h1 class="display-3">Welcome to Baby Buddy!</h1> <h1 class="display-3">{% trans "Welcome to Baby Buddy!" %}</h1>
<p class="lead"> <p class="lead">
Learn about and predict baby's needs without (<em>as much</em>) {% blocktrans %}Learn about and predict baby's needs without
guess work by using Baby Buddy to track &mdash; (<em>as much</em>) guess work by using Baby Buddy to track &mdash;{% endblocktrans %}
</p> </p>
<hr class="my-4"> <hr class="my-4">
<div class="card-deck"> <div class="card-deck">
@ -21,7 +21,7 @@
<i class="icon icon-2x icon-diaperchange" aria-hidden="true"></i> <i class="icon icon-2x icon-diaperchange" aria-hidden="true"></i>
</div> </div>
<div class="card-body"> <div class="card-body">
<h3 class="card-title text-center">Diaper Changes</h3> <h3 class="card-title text-center">{% trans "Diaper Changes" %}</h3>
</div> </div>
</div> </div>
<div class="card card-feeding"> <div class="card card-feeding">
@ -29,7 +29,7 @@
<i class="icon icon-2x icon-feeding" aria-hidden="true"></i> <i class="icon icon-2x icon-feeding" aria-hidden="true"></i>
</div> </div>
<div class="card-body"> <div class="card-body">
<h3 class="card-title text-center">Feedings</h3> <h3 class="card-title text-center">{% trans "Feedings" %}</h3>
</div> </div>
</div> </div>
<div class="card card-sleep"> <div class="card card-sleep">
@ -37,7 +37,7 @@
<i class="icon icon-2x icon-sleep" aria-hidden="true"></i> <i class="icon icon-2x icon-sleep" aria-hidden="true"></i>
</div> </div>
<div class="card-body"> <div class="card-body">
<h3 class="card-title text-center">Sleep</h3> <h3 class="card-title text-center">{% trans "Sleep" %}</h3>
</div> </div>
</div> </div>
<div class="card card-tummytime"> <div class="card card-tummytime">
@ -45,23 +45,23 @@
<i class="icon icon-2x icon-tummytime" aria-hidden="true"></i> <i class="icon icon-2x icon-tummytime" aria-hidden="true"></i>
</div> </div>
<div class="card-body"> <div class="card-body">
<h3 class="card-title text-center">Tummy Time</h3> <h3 class="card-title text-center">{% trans "Tummy Time" %}</h3>
</div> </div>
</div> </div>
</div> </div>
<hr class="my-4"> <hr class="my-4">
<p class="lead"> <p class="lead">
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 parents and caregivers to identify small patterns in baby's habits
using the dashboard and graphs. Baby Buddy is mobile-friendly and 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 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 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 %}
</p> </p>
<p class="text-center"> <p class="text-center">
{% if perms.core.add_child %} {% if perms.core.add_child %}
<a href="{% url 'core:child-add' %}" class="btn btn-lg btn-success"> <a href="{% url 'core:child-add' %}" class="btn btn-lg btn-success">
<i class="icon icon-child" aria-hidden="true"></i> Add a Child <i class="icon icon-child" aria-hidden="true"></i> {% trans "Add a Child" %}
</a> </a>
{% endif %} {% endif %}
</p> </p>

View File

@ -1,5 +1,5 @@
{% extends "registration/base.html" %} {% extends "registration/base.html" %}
{% load static widget_tweaks %} {% load i18n static widget_tweaks %}
{% block title %}Login{% endblock %} {% block title %}Login{% endblock %}
{% block content %} {% block content %}
@ -29,12 +29,12 @@
</div> </div>
<button class="btn btn-primary w-100 fade-in" type="submit" name="login"> <button class="btn btn-primary w-100 fade-in" type="submit" name="login">
Login {% trans "Login" %}
</button> </button>
</form> </form>
<div class="bg-faded text-center px-4 py-3 rounded-bottom"> <div class="bg-faded text-center px-4 py-3 rounded-bottom">
<a href="{% url 'babybuddy:password_reset' %}" name="reset"> <a href="{% url 'babybuddy:password_reset' %}" name="reset">
Forgot your password?</a> {% trans "Forgot your password?" %}</a>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,11 +1,12 @@
{% extends "registration/base.html" %} {% extends "registration/base.html" %}
{% load i18n %}
{% block title %}Password Reset Successfully!{% endblock %} {% block title %}{% trans "Password Reset Successfully!" %}{% endblock %}
{% block content %} {% block content %}
<div class="text-center mb-0"> <div class="text-center mb-0">
<p>Your password has been set. You may go ahead and log in now.</p> <p>{% trans "Your password has been set. You may go ahead and log in now." %}</p>
<p class="mb-0"><a href="{{ login_url }}">Log in</a></p> <p class="mb-0"><a href="{{ login_url }}">{% trans "Log in" %}</a></p>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "registration/base.html" %} {% 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 %} {% block content %}
<form method="post"> <form method="post">
@ -9,13 +9,13 @@
{% if form.errors %} {% if form.errors %}
<div class="alert alert-danger"> <div class="alert alert-danger">
<p class="mb-0"><strong>Oh snap!</strong> The two passwords {% blocktrans %}<p class="mb-0"><strong>Oh snap!</strong> The
did not match. Please try again.</p> two passwords did not match. Please try again.</p>{% endblocktrans %}
</div> </div>
{% endif %} {% endif %}
<div class="text-center mb-4"> <div class="text-center mb-4">
<p class="mb-0">Enter your new password in each field below.</p> <p class="mb-0">{% trans "Enter your new password in each field below." %}</p>
</div> </div>
<label class="sr-only" for="password1-input-group"> <label class="sr-only" for="password1-input-group">
@ -28,7 +28,6 @@
{% render_field form.new_password1 name='new_password1' class+='form-control' id='password1-input-group' %} {% render_field form.new_password1 name='new_password1' class+='form-control' id='password1-input-group' %}
</div> </div>
<label class="sr-only" for="password2-input-group"> <label class="sr-only" for="password2-input-group">
{{ form.new_password2.label }} {{ form.new_password2.label }}
</label> </label>
@ -40,7 +39,7 @@
</div> </div>
<button class="btn btn-primary w-100 fade-in" type="submit" name="reset"> <button class="btn btn-primary w-100 fade-in" type="submit" name="reset">
Reset Password {% trans "Reset Password" %}
</button> </button>
</form> </form>

View File

@ -1,15 +1,16 @@
{% extends "registration/base.html" %} {% extends "registration/base.html" %}
{% load i18n %}
{% block title %}Reset Email Sent{% endblock %} {% block title %}{% trans "Reset Email Sent" %}{% endblock %}
{% block content %} {% block content %}
<div class="text-center mb-0"> <div class="text-center mb-0">
<p>We've emailed you instructions for setting your {% blocktrans %}<p>We've emailed you instructions for setting your
password, if an account exists with the email you entered. You password, if an account exists with the email you entered. You
should receive them shortly.</p> should receive them shortly.</p>
<p class="mb-0">If you don't receive an email, please make sure you've <p class="mb-0">If you don't receive an email, please make sure you've
entered the address you registered with, and check your spam entered the address you registered with, and check your spam
folder.</p> folder.</p>{% endblocktrans %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,13 +1,13 @@
{% extends "registration/base.html" %} {% 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 %} {% block content %}
<div class="text-center mb-4"> <div class="text-center mb-4">
<p class="mb-0">Enter your account email address in the form below. If {% blocktrans %}<p class="mb-0">Enter your account email address in the
the address is valid, you will receive instructions for resetting your form below. If the address is valid, you will receive instructions for
password.</p> resetting your password.</p>{% endblocktrans %}
</div> </div>
<form method="post"> <form method="post">
@ -24,7 +24,7 @@
</div> </div>
<button class="btn btn-primary w-100 fade-in" type="submit" name="reset"> <button class="btn btn-primary w-100 fade-in" type="submit" name="reset">
Reset Password {% trans "Reset Password" %}
</button> </button>
</form> </form>

View File

@ -25,6 +25,14 @@ class FormsTestCase(TestCase):
cls.user = User.objects.create_user( cls.user = User.objects.create_user(
is_superuser=True, **cls.credentials) 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): def test_change_password(self):
self.c.login(**self.credentials) self.c.login(**self.credentials)
@ -85,18 +93,28 @@ class FormsTestCase(TestCase):
def test_user_settings(self): def test_user_settings(self):
self.c.login(**self.credentials) self.c.login(**self.credentials)
params = { params = self.settings_template.copy()
'first_name': 'User', params['first_name'] = 'New First Name'
'last_name': 'Name',
'email': 'user@user.user',
'dashboard_refresh_rate': ''
}
page = self.c.post('/user/settings/', params) page = self.c.post('/user/settings/', params, follow=True)
self.assertEqual(page.status_code, 302) 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) page = self.c.post('/user/settings/', params)
self.assertEqual(page.status_code, 200) self.assertEqual(page.status_code, 200)
self.assertFormError(page, 'user_form', 'email', self.assertFormError(page, 'user_form', 'email',
'Enter a valid email address.') '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')

View File

@ -17,10 +17,13 @@ class SettingsTestCase(TestCase):
} }
user = User.objects.create_user(is_superuser=True, **credentials) user = User.objects.create_user(is_superuser=True, **credentials)
self.assertIsInstance(user.settings, Settings) self.assertIsInstance(user.settings, Settings)
self.assertEqual(str(user.settings), 'Test\'s Settings')
self.assertEqual( self.assertEqual(
user.settings.dashboard_refresh_rate_milliseconds, 60000) user.settings.dashboard_refresh_rate_milliseconds, 60000)
user.settings.dashboard_refresh_rate = None user.settings.dashboard_refresh_rate = None
user.save() user.save()
self.assertIsNone(user.settings.dashboard_refresh_rate_milliseconds) self.assertIsNone(user.settings.dashboard_refresh_rate_milliseconds)
user.settings.language = 'fr'
user.save()
self.assertEqual(user.settings.language, 'fr')

View File

@ -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!')

View File

@ -53,6 +53,7 @@ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('', include('api.urls', namespace='api')), path('', include('api.urls', namespace='api')),
path('', include((app_patterns, 'babybuddy'), namespace='babybuddy')), path('', include((app_patterns, 'babybuddy'), namespace='babybuddy')),
path('user/lang', include('django.conf.urls.i18n')),
path('', include('core.urls', namespace='core')), path('', include('core.urls', namespace='core')),
path('', include('dashboard.urls', namespace='dashboard')), path('', include('dashboard.urls', namespace='dashboard')),
path('', include('reports.urls', namespace='reports')), path('', include('reports.urls', namespace='reports')),

View File

@ -7,9 +7,12 @@ from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.urls import reverse, reverse_lazy 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 import View
from django.views.generic.base import TemplateView, RedirectView from django.views.generic.base import TemplateView, RedirectView
from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.i18n import set_language
from django_filters.views import FilterView from django_filters.views import FilterView
@ -50,7 +53,7 @@ class UserAdd(StaffOnlyMixin, PermissionRequired403Mixin, SuccessMessageMixin,
permission_required = ('admin.add_user',) permission_required = ('admin.add_user',)
form_class = forms.UserAddForm form_class = forms.UserAddForm
success_url = reverse_lazy('babybuddy:user-list') 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, class UserUpdate(StaffOnlyMixin, PermissionRequired403Mixin,
@ -60,7 +63,7 @@ class UserUpdate(StaffOnlyMixin, PermissionRequired403Mixin,
permission_required = ('admin.change_user',) permission_required = ('admin.change_user',)
form_class = forms.UserUpdateForm form_class = forms.UserUpdateForm
success_url = reverse_lazy('babybuddy:user-list') 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, class UserDelete(StaffOnlyMixin, PermissionRequired403Mixin,
@ -71,7 +74,9 @@ class UserDelete(StaffOnlyMixin, PermissionRequired403Mixin,
success_url = reverse_lazy('babybuddy:user-list') success_url = reverse_lazy('babybuddy:user-list')
def delete(self, request, *args, **kwargs): 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) messages.success(request, success_message)
return super(UserDelete, self).delete(request, *args, **kwargs) return super(UserDelete, self).delete(request, *args, **kwargs)
@ -93,7 +98,7 @@ class UserPassword(LoginRequiredMixin, View):
if form.is_valid(): if form.is_valid():
user = form.save() user = form.save()
update_session_auth_hash(request, user) update_session_auth_hash(request, user)
messages.success(request, 'Password updated.') messages.success(request, _('Password updated.'))
return render(request, self.template_name, {'form': form}) return render(request, self.template_name, {'form': form})
@ -103,7 +108,7 @@ class UserResetAPIKey(LoginRequiredMixin, View):
""" """
def get(self, request): def get(self, request):
request.user.settings.api_key(reset=True) 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') return redirect('babybuddy:user-settings')
@ -135,7 +140,8 @@ class UserSettings(LoginRequiredMixin, View):
user_settings = form_settings.save(commit=False) user_settings = form_settings.save(commit=False)
user.settings = user_settings user.settings = user_settings
user.save() user.save()
messages.success(request, 'Settings saved!') set_language(request)
messages.success(request, _('Settings saved!'))
return redirect('babybuddy:user-settings') return redirect('babybuddy:user-settings')
return render(request, self.template_name, { return render(request, self.template_name, {
'user_form': form_user, 'user_form': form_user,

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django import forms from django import forms
from django.utils import timezone
from django.conf import settings from django.conf import settings
from django.utils import timezone
from django.utils.translation import gettext as _
from core import models from core import models
@ -77,7 +78,7 @@ class ChildDeleteForm(forms.ModelForm):
confirm_name = self.cleaned_data['confirm_name'] confirm_name = self.cleaned_data['confirm_name']
if confirm_name != str(self.instance): if confirm_name != str(self.instance):
raise forms.ValidationError( raise forms.ValidationError(
'Name does not match child name.', code='confirm_mismatch') _('Name does not match child name.'), code='confirm_mismatch')
return confirm_name return confirm_name
def save(self, commit=True): def save(self, commit=True):

View File

@ -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'),
),
]

View File

@ -6,6 +6,8 @@ from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.utils import timezone 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): def validate_date(date, field_name):
@ -17,7 +19,7 @@ def validate_date(date, field_name):
""" """
if date and date > timezone.localdate(): if date and date > timezone.localdate():
raise ValidationError( raise ValidationError(
{field_name: 'Date can not be in the future.'}, {field_name: _('Date can not be in the future.')},
code='date_invalid') 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 and model.end:
if model.start > model.end: if model.start > model.end:
raise ValidationError( raise ValidationError(
'Start time must come before end time.', _('Start time must come before end time.'),
code='end_before_start') code='end_before_start')
if model.end - model.start > max_duration: 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): def validate_unique_period(queryset, model):
@ -50,7 +52,7 @@ def validate_unique_period(queryset, model):
if model.start and model.end: if model.start and model.end:
if queryset.filter(start__lte=model.end, end__gte=model.start): if queryset.filter(start__lte=model.end, end__gte=model.start):
raise ValidationError( raise ValidationError(
'Another entry intersects the specified time period.', _('Another entry intersects the specified time period.'),
code='period_intersection') code='period_intersection')
@ -63,20 +65,30 @@ def validate_time(time, field_name):
""" """
if time and time > timezone.localtime(): if time and time > timezone.localtime():
raise ValidationError( 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') code='time_invalid')
class Child(models.Model): class Child(models.Model):
model_name = 'child' model_name = 'child'
first_name = models.CharField(max_length=255) first_name = models.CharField(max_length=255, verbose_name=_('First name'))
last_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255, verbose_name=_('Last name'))
birth_date = models.DateField(blank=False, null=False) birth_date = models.DateField(
slug = models.SlugField(max_length=100, unique=True, editable=False) blank=False,
null=False,
verbose_name=_('Birth date')
)
slug = models.SlugField(
editable=False,
max_length=100,
unique=True,
verbose_name=_('Slug')
)
picture = models.ImageField( picture = models.ImageField(
upload_to='child/picture/',
blank=True, blank=True,
null=True null=True,
upload_to='child/picture/',
verbose_name=_('Picture')
) )
objects = models.Manager() objects = models.Manager()
@ -84,7 +96,8 @@ class Child(models.Model):
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['last_name', 'first_name'] ordering = ['last_name', 'first_name']
verbose_name_plural = 'Children' verbose_name = _('Child')
verbose_name_plural = _('Children')
def __str__(self): def __str__(self):
return '{} {}'.format(self.first_name, self.last_name) return '{} {}'.format(self.first_name, self.last_name)
@ -103,25 +116,40 @@ class Child(models.Model):
class DiaperChange(models.Model): class DiaperChange(models.Model):
model_name = 'diaperchange' model_name = 'diaperchange'
child = models.ForeignKey( child = models.ForeignKey(
'Child', related_name='diaper_change', on_delete=models.CASCADE) 'Child',
time = models.DateTimeField(blank=False, null=False) on_delete=models.CASCADE,
wet = models.BooleanField() related_name='diaper_change',
solid = models.BooleanField() verbose_name=_('Child')
color = models.CharField(max_length=255, blank=True, choices=[ )
('black', 'Black'), time = models.DateTimeField(
('brown', 'Brown'), blank=False,
('green', 'Green'), null=False,
('yellow', 'Yellow'), 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() objects = models.Manager()
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-time'] ordering = ['-time']
verbose_name = _('Diaper Change')
verbose_name_plural = _('Diaper Changes')
def __str__(self): def __str__(self):
return 'Diaper Change' return str(_('Diaper Change'))
def attributes(self): def attributes(self):
attributes = [] attributes = []
@ -139,35 +167,58 @@ class DiaperChange(models.Model):
# One or both of Wet and Solid is required. # One or both of Wet and Solid is required.
if not self.wet and not self.solid: if not self.wet and not self.solid:
raise ValidationError( 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): class Feeding(models.Model):
model_name = 'feeding' model_name = 'feeding'
child = models.ForeignKey( child = models.ForeignKey(
'Child', related_name='feeding', on_delete=models.CASCADE) 'Child',
start = models.DateTimeField(blank=False, null=False) on_delete=models.CASCADE,
end = models.DateTimeField(blank=False, null=False) related_name='feeding',
duration = models.DurationField(null=True, editable=False) verbose_name=_('Child')
type = models.CharField(max_length=255, choices=[ )
('breast milk', 'Breast milk'), start = models.DateTimeField(
('formula', 'Formula'), blank=False,
]) null=False,
method = models.CharField(max_length=255, choices=[ verbose_name=_('Start time')
('bottle', 'Bottle'), )
('left breast', 'Left breast'), end = models.DateTimeField(
('right breast', 'Right breast'), blank=False,
]) null=False,
amount = models.FloatField(blank=True, null=True) 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() objects = models.Manager()
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-start'] ordering = ['-start']
verbose_name = _('Feeding')
verbose_name_plural = _('Feedings')
def __str__(self): def __str__(self):
return 'Feeding' return str(_('Feeding'))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.start and self.end: if self.start and self.end:
@ -184,25 +235,31 @@ class Feeding(models.Model):
if self.type == 'formula'and self.method != 'bottle': if self.type == 'formula'and self.method != 'bottle':
raise ValidationError( raise ValidationError(
{'method': {'method':
'Only "Bottle" method is allowed with "Formula" type.'}, _('Only "Bottle" method is allowed with "Formula" type.')},
code='bottle_formula_mismatch') code='bottle_formula_mismatch')
class Note(models.Model): class Note(models.Model):
model_name = 'note' model_name = 'note'
child = models.ForeignKey( child = models.ForeignKey(
'Child', related_name='note', on_delete=models.CASCADE) 'Child',
note = models.TextField() on_delete=models.CASCADE,
time = models.DateTimeField(auto_now=True) related_name='note',
verbose_name=_('Child')
)
note = models.TextField(verbose_name=_('Note'))
time = models.DateTimeField(auto_now=True, verbose_name=_('Time'))
objects = models.Manager() objects = models.Manager()
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-time'] ordering = ['-time']
verbose_name = _('Note')
verbose_name_plural = _('Notes')
def __str__(self): def __str__(self):
return 'Note' return str(_('Note'))
class NapsManager(models.Manager): class NapsManager(models.Manager):
@ -214,10 +271,26 @@ class NapsManager(models.Manager):
class Sleep(models.Model): class Sleep(models.Model):
model_name = 'sleep' model_name = 'sleep'
child = models.ForeignKey( child = models.ForeignKey(
'Child', related_name='sleep', on_delete=models.CASCADE) 'Child',
start = models.DateTimeField(blank=False, null=False) on_delete=models.CASCADE,
end = models.DateTimeField(blank=False, null=False) related_name='sleep',
duration = models.DurationField(null=True, editable=False) 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() objects = models.Manager()
naps = NapsManager() naps = NapsManager()
@ -225,10 +298,11 @@ class Sleep(models.Model):
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-start'] ordering = ['-start']
verbose_name_plural = 'Sleep' verbose_name = _('Sleep')
verbose_name_plural = _('Sleep')
def __str__(self): def __str__(self):
return 'Sleep' return str(_('Sleep'))
@property @property
def nap(self): def nap(self):
@ -253,26 +327,50 @@ class Sleep(models.Model):
class Timer(models.Model): class Timer(models.Model):
model_name = 'timer' 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( start = models.DateTimeField(
default=timezone.now, default=timezone.now,
blank=False, 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( 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() objects = models.Manager()
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-active', '-start', '-end'] ordering = ['-active', '-start', '-end']
verbose_name = _('Timer')
verbose_name_plural = _('Timers')
def __str__(self): def __str__(self):
return self.name or 'Timer #{}'.format(self.id) return self.name or str(format_lazy(_('Timer #{id}'), id=self.id))
@classmethod @classmethod
def from_db(cls, db, field_names, values): def from_db(cls, db, field_names, values):
@ -315,20 +413,42 @@ class Timer(models.Model):
class TummyTime(models.Model): class TummyTime(models.Model):
model_name = 'tummytime' model_name = 'tummytime'
child = models.ForeignKey( child = models.ForeignKey(
'Child', related_name='tummy_time', on_delete=models.CASCADE) 'Child',
start = models.DateTimeField(blank=False, null=False) on_delete=models.CASCADE,
end = models.DateTimeField(blank=False, null=False) related_name='tummy_time',
duration = models.DurationField(null=True, editable=False) verbose_name=_('Child')
milestone = models.CharField(max_length=255, blank=True) )
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() objects = models.Manager()
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-start'] ordering = ['-start']
verbose_name = _('Tummy Time')
verbose_name_plural = _('Tummy Time')
def __str__(self): def __str__(self):
return 'Tummy Time' return str(_('Tummy Time'))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.start and self.end: if self.start and self.end:
@ -346,19 +466,32 @@ class TummyTime(models.Model):
class Weight(models.Model): class Weight(models.Model):
model_name = 'weight' model_name = 'weight'
child = models.ForeignKey( child = models.ForeignKey(
'Child', related_name='weight', on_delete=models.CASCADE) 'Child',
weight = models.FloatField(blank=False, null=False) on_delete=models.CASCADE,
date = models.DateField(blank=False, null=False) 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() objects = models.Manager()
class Meta: class Meta:
default_permissions = ('view', 'add', 'change', 'delete') default_permissions = ('view', 'add', 'change', 'delete')
ordering = ['-date'] ordering = ['-date']
verbose_name_plural = 'Weight' verbose_name = _('Weight')
verbose_name_plural = _('Weight')
def __str__(self): def __str__(self):
return 'Weight' return str(_('Weight'))
def clean(self): def clean(self):
validate_date(self.date, 'date') validate_date(self.date, 'date')

View File

@ -1,20 +1,22 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">Children</a></li> <li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li> <li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<div class="form-group"> <div class="form-group">
<label for="{{ form.confirm_name.id_for_label }}">To confirm this action. Type the full name of the child below.</label> <label for="{{ form.confirm_name.id_for_label }}">
{% trans "To confirm this action. Type the full name of the child below." %}
</label>
{% if form.confirm_name.errors %} {% if form.confirm_name.errors %}
{{ form.confirm_name|add_class:"form-control is-invalid" }} {{ form.confirm_name|add_class:"form-control is-invalid" }}
{% else %} {% else %}
@ -24,7 +26,7 @@
<div class="invalid-feedback">{{ form.confirm_name.errors.0 }}</div> <div class="invalid-feedback">{{ form.confirm_name.errors.0 }}</div>
{% endif %} {% endif %}
</div> </div>
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="{% url 'core:child-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'core:child-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load static thumbnail %} {% load i18n static thumbnail %}
{% block title %}{{ object }}{% endblock %} {% block title %}{{ object }}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">Children</a></li> <li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item font-weight-bold">{{ object }}</li> <li class="breadcrumb-item font-weight-bold">{{ object }}</li>
{% endblock %} {% endblock %}
@ -20,8 +20,8 @@
{% endif %} {% endif %}
<div class="child-name display-4">{{ object }}</div> <div class="child-name display-4">{{ object }}</div>
<p class="lead"> <p class="lead">
Born <span class="text-secondary">{{ object.birth_date }}</span><br/> {% trans "Born" %} <span class="text-secondary">{{ object.birth_date }}</span><br/>
Age <span class="text-secondary">{{ object.birth_date|timesince }}</span> {% trans "Age" %} <span class="text-secondary">{{ object.birth_date|timesince }}</span>
</p> </p>
{% include 'dashboard/child_button_group.html' %} {% include 'dashboard/child_button_group.html' %}
</div> </div>
@ -31,16 +31,16 @@
<div class="col-lg-8 offset-lg-4 col-md-6 offset-md-6"> <div class="col-lg-8 offset-lg-4 col-md-6 offset-md-6">
<h3 class="text-center"> <h3 class="text-center">
{% if date_previous %} {% if date_previous %}
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="Previous"> <a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "Previous" %}">
<i class="icon icon-chevron-left" aria-hidden="true"></i> <i class="icon icon-chevron-left" aria-hidden="true"></i>
<span class="sr-only">Previous</span> <span class="sr-only">{% trans "Previous" %}</span>
</a> </a>
{% endif %} {% endif %}
{{ date|date }} {{ date|date }}
{% if date_next %} {% if date_next %}
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="Next"> <a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "Next" %}">
<i class="icon icon-chevron-right" aria-hidden="true"></i> <i class="icon icon-chevron-right" aria-hidden="true"></i>
<span class="sr-only">Next</span> <span class="sr-only">{% trans "Next" %}</span>
</a> </a>
{% endif %} {% endif %}
</h3> </h3>
@ -55,7 +55,7 @@
{{ object.event }} {{ object.event }}
</div> </div>
<div class="card-footer text-muted"> <div class="card-footer text-muted">
{{ object.time|timesince }} ago ({{ object.time|time }}) {% blocktrans with since=object.time|timesince time=object.time|time %}{{ since }} ago ({{ time }}){% endblocktrans %}
</div> </div>
</div> </div>
</li> </li>

View File

@ -1,28 +1,29 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if object %} {% if object %}
{{ object }} {{ object }}
{% else %} {% else %}
Add a Child {% trans "Add a Child" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">Children</a></li> <li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li> <li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Add a Child</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Add a Child" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Add a Child</h1> <h1>{% trans "Add a Child" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,24 +1,24 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Children</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Children" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Children</h1> <h1>{% trans "Children" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover child-list"> <table class="table table-striped table-hover child-list">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th class="picture-column"><i class="icon icon-camera" aria-hidden="true"></i></th> <th class="picture-column"><i class="icon icon-camera" aria-hidden="true"></i></th>
<th>First Name</th> <th>{% trans "First Name" %}</th>
<th>Last Name</th> <th>{% trans "Last Name" %}</th>
<th>Birth Date</th> <th>{% trans "Birth Date" %}</th>
<th class="text-center">Actions</th> <th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -38,7 +38,7 @@
<td>{{ child.last_name }}</td> <td>{{ child.last_name }}</td>
<td>{{ child.birth_date }}</td> <td>{{ child.birth_date }}</td>
<td class="text-center"> <td class="text-center">
<div class="btn-group btn-group-sm" role="group" aria-label="Actions"> <div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
{% if perms.core.change_child %} {% if perms.core.change_child %}
<a href="{% url 'core:child-update' child.slug %}" class="btn btn-warning"> <a href="{% url 'core:child-update' child.slug %}" class="btn btn-warning">
@ -57,7 +57,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="4">No children found.</th> <th colspan="4">{% trans "No children found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -67,7 +67,7 @@
{% if perms.core.add_child %} {% if perms.core.add_child %}
<a href="{% url 'core:child-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'core:child-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-child" aria-hidden="true"></i> Add a Child <i class="icon icon-child" aria-hidden="true"></i> {% trans "Add a Child" %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:diaperchange-list' %}">Diaper Changes</a></li> <li class="breadcrumb-item"><a href="{% url 'core:diaperchange-list' %}">{% trans "Diaper Changes" %}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="{% url 'core:diaperchange-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'core:diaperchange-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if request.resolver_match.url_name == 'diaperchange-update' %} {% if request.resolver_match.url_name == 'diaperchange-update' %}
Update a Diaper Change {% trans "Update a Diaper Change" %}
{% else %} {% else %}
Add a Diaper Change {% trans "Add a Diaper Change" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:diaperchange-list' %}">Diaper Changes</a></li> <li class="breadcrumb-item"><a href="{% url 'core:diaperchange-list' %}">{% trans "Diaper Changes" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Add</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Add" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Add a Diaper Change</h1> <h1>{% trans "Add a Diaper Change" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,26 +1,25 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load bootstrap i18n widget_tweaks %}
{% load bootstrap %}
{% block title %}Diaper Changes{% endblock %} {% block title %}{% trans "Diaper Changes" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Diaper Changes</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Diaper Changes" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Diaper Changes</h1> <h1>{% trans "Diaper Changes" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Child</th> <th>{% trans "Child" %}</th>
<th class="text-center">Wet</th> <th class="text-center">{% trans "Wet" %}</th>
<th class="text-center">Solid</th> <th class="text-center">{% trans "Solid" %}</th>
<th>Color</th> <th>{% trans "Color" %}</th>
<th>Time</th> <th>{% trans "Time" %}</th>
<th class="text-center">Actions</th> <th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -32,7 +31,7 @@
<td>{{ change.color }}</td> <td>{{ change.color }}</td>
<td>{{ change.time|date:'n/j/y G:i' }}</td> <td>{{ change.time|date:'n/j/y G:i' }}</td>
<td class="text-center"> <td class="text-center">
<div class="btn-group btn-group-sm" role="group" aria-label="Actions"> <div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
{% if perms.core.change_diaperchange %} {% if perms.core.change_diaperchange %}
<a href="{% url 'core:diaperchange-update' change.id %}" class="btn btn-primary"> <a href="{% url 'core:diaperchange-update' change.id %}" class="btn btn-primary">
@ -51,7 +50,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="6">No diaper changes found.</th> <th colspan="6">{% trans "No diaper changes found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -61,7 +60,7 @@
{% if perms.core.add_diaperchange %} {% if perms.core.add_diaperchange %}
<a href="{% url 'core:diaperchange-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'core:diaperchange-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-diaperchange" aria-hidden="true"></i> Add a Change <i class="icon icon-diaperchange" aria-hidden="true"></i> {% trans "Add a Change" %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:feeding-list' %}">Feedings</a></li> <li class="breadcrumb-item"><a href="{% url 'core:feeding-list' %}">{% trans "Feedings" %}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="{% url 'core:feeding-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'core:feeding-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if request.resolver_match.url_name == 'feeding-update' %} {% if request.resolver_match.url_name == 'feeding-update' %}
Update a Feeding {% trans "Update a Feeding" %}
{% else %} {% else %}
Add a Feeding {% trans "Add a Feeding" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:feeding-list' %}">Feedings</a></li> <li class="breadcrumb-item"><a href="{% url 'core:feeding-list' %}">{% trans "Feedings" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Add</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Add" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Add a Feeding</h1> <h1>{% trans "Add a Feeding" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,27 +1,27 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load duration i18n widget_tweaks %}
{% load duration %}
{% block title %}Feedings{% endblock %} {% block title %}{% trans "Feedings" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Feedings</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Feedings" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Feedings</h1> <h1>{% trans "Feedings" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Child</th> <th>{% trans "Child" %}</th>
<th>Method</th> <th>{% trans "Method" %}</th>
<th>Type</th> <th>{% trans "Type" %}</th>
<th>Amt.</th> {% comment %}Abbreviation of "Amount"{% endcomment %}
<th>Duration</th> <th>{% trans "Amt." %}</th>
<th>Date</th> <th>{% trans "Duration" %}</th>
<th class="text-center">Actions</th> <th>{% trans "Date" %}</th>
<th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -38,7 +38,7 @@
<td>{{ feeding.duration|duration_string }}</td> <td>{{ feeding.duration|duration_string }}</td>
<td>{{ feeding.start|date:'n/j/y G:i' }}</td> <td>{{ feeding.start|date:'n/j/y G:i' }}</td>
<td class="text-center"> <td class="text-center">
<div class="btn-group btn-group-sm" role="group" aria-label="Actions"> <div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
{% if perms.core.change_feeding %} {% if perms.core.change_feeding %}
<a href="{% url 'core:feeding-update' feeding.id %}" class="btn btn-primary"> <a href="{% url 'core:feeding-update' feeding.id %}" class="btn btn-primary">
@ -57,7 +57,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="7">No feedings found.</th> <th colspan="7">{% trans "No feedings found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -67,7 +67,7 @@
{% if perms.core.add_feeding %} {% if perms.core.add_feeding %}
<a href="{% url 'core:feeding-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'core:feeding-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-feeding" aria-hidden="true"></i> Add a Feeding <i class="icon icon-feeding" aria-hidden="true"></i> {% trans "Add a Feeding" %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:note-list' %}">Notes</a></li> <li class="breadcrumb-item"><a href="{% url 'core:note-list' %}">{% trans "Notes" %}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="{% url 'core:note-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'core:note-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if request.resolver_match.url_name == 'note-update' %} {% if request.resolver_match.url_name == 'note-update' %}
Update a Note {% trans "Update a Note" %}
{% else %} {% else %}
Add a Note {% trans "Add a Note" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:note-list' %}">Notes</a></li> <li class="breadcrumb-item"><a href="{% url 'core:note-list' %}">{% trans "Notes" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Add</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Add" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Add a Note</h1> <h1>{% trans "Add a Note" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,23 +1,23 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load i18n widget_tweaks %}
{% block title %}Notes{% endblock %} {% block title %}{% trans "Notes" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Notes</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Notes" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Notes</h1> <h1>{% trans "Notes" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Child</th> <th>{% trans "Child" %}</th>
<th>Note</th> <th>{% trans "Note" %}</th>
<th>Time</th> <th>{% trans "Time" %}</th>
<th class="text-center">Actions</th> <th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -27,7 +27,7 @@
<td>{{ note.note }}</td> <td>{{ note.note }}</td>
<td>{{ note.time }}</td> <td>{{ note.time }}</td>
<td class="text-center"> <td class="text-center">
<div class="btn-group btn-group-sm" role="group" aria-label="Actions"> <div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
{% if perms.core.change_note %} {% if perms.core.change_note %}
<a href="{% url 'core:note-update' note.id %}" class="btn btn-primary"> <a href="{% url 'core:note-update' note.id %}" class="btn btn-primary">
@ -46,7 +46,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="4">No notes found.</th> <th colspan="4">{% trans "No notes found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -56,7 +56,7 @@
{% if perms.core.add_note %} {% if perms.core.add_note %}
<a href="{% url 'core:note-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'core:note-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-note" aria-hidden="true"></i> Add a Note <i class="icon icon-note" aria-hidden="true"></i> {% trans "Add a Note" %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:sleep-list' %}">Sleep</a></li> <li class="breadcrumb-item"><a href="{% url 'core:sleep-list' %}">{% trans "Sleep" %}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="{% url 'core:sleep-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'core:sleep-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if request.resolver_match.url_name == 'sleep-update' %} {% if request.resolver_match.url_name == 'sleep-update' %}
Update a Sleep Entry {% trans "Update a Sleep Entry" %}
{% else %} {% else %}
Add a Sleep Entry {% trans "Add a Sleep Entry" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:sleep-list' %}">Sleep</a></li> <li class="breadcrumb-item"><a href="{% url 'core:sleep-list' %}">{% trans "Sleep" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Add</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Add" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Add a Sleep Entry</h1> <h1>{% trans "Add a Sleep Entry" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,25 +1,25 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Sleep</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Sleep" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Sleep</h1> <h1>{% trans "Sleep" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Child</th> <th>{% trans "Child" %}</th>
<th>Duration</th> <th>{% trans "Duration" %}</th>
<th>Start</th> <th>{% trans "Start" %}</th>
<th>End</th> <th>{% trans "End" %}</th>
<th class="text-center">Nap</th> <th class="text-center">{% trans "Nap" %}</th>
<th class="text-center">Actions</th> <th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -31,7 +31,7 @@
<td>{{ sleep.end|date:'n/j/y G:i' }}</td> <td>{{ sleep.end|date:'n/j/y G:i' }}</td>
<td class="text-center">{{ sleep.nap|bool_icon }}</td> <td class="text-center">{{ sleep.nap|bool_icon }}</td>
<td class="text-center"> <td class="text-center">
<div class="btn-group btn-group-sm" role="group" aria-label="Actions"> <div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
{% if perms.core.change_sleep %} {% if perms.core.change_sleep %}
<a href="{% url 'core:sleep-update' sleep.id %}" class="btn btn-primary"> <a href="{% url 'core:sleep-update' sleep.id %}" class="btn btn-primary">
@ -50,7 +50,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="5">No sleep entries found.</th> <th colspan="5">{% trans "No sleep entries found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -60,7 +60,7 @@
{% if perms.core.add_sleep %} {% if perms.core.add_sleep %}
<a href="{% url 'core:sleep-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'core:sleep-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-sleep" aria-hidden="true"></i> Add a Sleep Entry <i class="icon icon-sleep" aria-hidden="true"></i> {% trans "Add a Sleep Entry" %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,19 +1,19 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">Timers</a></li> <li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li> <li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="/" class="btn btn-default">Cancel</a> <a href="/" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load duration timers %} {% load duration i18n timers %}
{% block title %}{{ object }}{% endblock %} {% block title %}{{ object }}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">Timers</a></li> <li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
<li class="breadcrumb-item font-weight-bold">{{ object }}</li> <li class="breadcrumb-item font-weight-bold">{{ object }}</li>
{% endblock %} {% endblock %}
@ -17,34 +17,40 @@
<span class="timer-seconds">{{ object.duration|seconds }}</span>s <span class="timer-seconds">{{ object.duration|seconds }}</span>s
</div> </div>
<p class="lead text-secondary"> <p class="lead text-secondary">
Started {{ object.start }} {% trans "Started" %} {{ object.start }}
{% if not object.active %} {% if not object.active %}
/ Stopped {{ object.end }} / {% trans "Stopped" %} {{ object.end }}
{% endif %} {% endif %}
</p> </p>
<p class="text-muted"> <p class="text-muted">
{{ timer }} created by {{ object.user }} {% blocktrans %}{{ timer }} created by {{ object.user }}{% endblocktrans %}
</p> </p>
{% if perms.core.add_feeding %} {% if perms.core.add_feeding %}
<a class="btn btn-success btn-lg btn-block mb-3" <a class="btn btn-success btn-lg btn-block mb-3"
href="{% url 'core:feeding-add' %}?timer={{ timer.id }}" href="{% url 'core:feeding-add' %}?timer={{ timer.id }}"
role="button"><i class="icon icon-feeding" aria-hidden="true"></i> Feeding</a> role="button"><i class="icon icon-feeding" aria-hidden="true"></i>
{% trans "Feeding" %}
</a>
{% endif %} {% endif %}
{% if perms.core.add_sleep %} {% if perms.core.add_sleep %}
<a class="btn btn-success btn-lg btn-block mb-3" <a class="btn btn-success btn-lg btn-block mb-3"
href="{% url 'core:sleep-add' %}?timer={{ timer.id }}" href="{% url 'core:sleep-add' %}?timer={{ timer.id }}"
role="button"><i class="icon icon-sleep" aria-hidden="true"></i> Sleep</a> role="button"><i class="icon icon-sleep" aria-hidden="true"></i>
{% trans "Sleep" %}
</a>
{% endif %} {% endif %}
{% if perms.core.add_tummytime %} {% if perms.core.add_tummytime %}
<a class="btn btn-success btn-lg btn-block mb-3" <a class="btn btn-success btn-lg btn-block mb-3"
href="{% url 'core:tummytime-add' %}?timer={{ timer.id }}" href="{% url 'core:tummytime-add' %}?timer={{ timer.id }}"
role="button"><i class="icon icon-tummytime" aria-hidden="true"></i> Tummy Time</a> role="button"><i class="icon icon-tummytime" aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a>
{% endif %} {% endif %}
<div class="btn-group btn-group-lg center-block" role="group" aria-label="Timer actions"> <div class="btn-group btn-group-lg center-block" role="group" aria-label="{% trans "Timer actions" %}">
{% if perms.core.delete_timer %} {% if perms.core.delete_timer %}
<a class="btn btn-danger" <a class="btn btn-danger"

View File

@ -1,23 +1,23 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load duration %} {% load duration i18n %}
{% block title %}Timer{% endblock %} {% block title %}{% trans "Timer" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">Timers</a></li> <li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li> <li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Start</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Start" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Start Timer</h1> <h1>{% trans "Start Timer" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,25 +1,25 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Timers</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Timers" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Timers</h1> <h1>{% trans "Timers" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Name</th> <th>{% trans "Name" %}</th>
<th>Start</th> <th>{% trans "Start" %}</th>
<th>Duration</th> <th>{% trans "Duration" %}</th>
<th>End</th> <th>{% trans "End" %}</th>
<th>Active</th> <th>{% trans "Active" %}</th>
<th>User</th> <th>{% trans "User" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -34,7 +34,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="7">No timer entries found.</th> <th colspan="7">{% trans "No timer entries found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -1,30 +1,34 @@
{% load i18n %}
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a id="nav-timer-menu-link" <a id="nav-timer-menu-link"
class="nav-link dropdown-toggle" class="nav-link dropdown-toggle"
href="#" href="#"
data-toggle="dropdown" data-toggle="dropdown"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false"><i class="icon icon-timer" aria-hidden="true"></i> Timers</a> aria-expanded="false"><i class="icon icon-timer" aria-hidden="true"></i>
{% trans "Timers" %}
</a>
<div class="dropdown-menu" aria-labelledby="nav-timer-menu-link"> <div class="dropdown-menu" aria-labelledby="nav-timer-menu-link">
{% if perms.core.add_timer %} {% if perms.core.add_timer %}
<a class="dropdown-item" href="{% url 'core:timer-add-quick' %}"> <a class="dropdown-item" href="{% url 'core:timer-add-quick' %}">
<i class="icon icon-timer" aria-hidden="true"></i> Quick Start Timer <i class="icon icon-timer" aria-hidden="true"></i> {% trans "Quick Start Timer" %}
</a> </a>
<a class="dropdown-item" href="{% url 'core:timer-add' %}"> <a class="dropdown-item" href="{% url 'core:timer-add' %}">
<i class="icon icon-add" aria-hidden="true"></i> Start Timer <i class="icon icon-add" aria-hidden="true"></i> {% trans "Start Timer" %}
</a> </a>
{% endif %} {% endif %}
{% if perms.core.view_timer %} {% if perms.core.view_timer %}
<a class="dropdown-item" href="{% url 'core:timer-list' %}"> <a class="dropdown-item" href="{% url 'core:timer-list' %}">
<i class="icon icon-list" aria-hidden="true"></i> View Timers <i class="icon icon-list" aria-hidden="true"></i> {% trans "View Timers" %}
</a> </a>
{% endif %} {% endif %}
{% if timers %} {% if timers %}
<h6 class="dropdown-header">Active Timers</h6> <h6 class="dropdown-header">{% trans "Active Timers" %}</h6>
{% for timer in timers %} {% for timer in timers %}
<a class="dropdown-item" href="{% url 'core:timer-detail' timer.id %}">{{ timer }} ({{ timer.user }})</a> <a class="dropdown-item" href="{% url 'core:timer-detail' timer.id %}">{{ timer }} ({{ timer.user }})</a>
{% empty %} {% empty %}
<a class="dropdown-item disabled" href="#">None</a> <a class="dropdown-item disabled" href="#">{% trans "None" %}</a>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</div> </div>

View File

@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:tummytime-list' %}">Tummy Time</a></li> <li class="breadcrumb-item"><a href="{% url 'core:tummytime-list' %}">{% trans "Tummy Time" %}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="{% url 'core:tummytime-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'core:tummytime-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if request.resolver_match.url_name == 'tummytime-update' %} {% if request.resolver_match.url_name == 'tummytime-update' %}
Update a Tummy Time Entry {% trans "Update a Tummy Time Entry" %}
{% else %} {% else %}
Add a Tummy Time Entry {% trans "Add a Tummy Time Entry" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:tummytime-list' %}">Tummy Time</a></li> <li class="breadcrumb-item"><a href="{% url 'core:tummytime-list' %}">{% trans "Tummy Time" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Add</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Add" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Add a Tummy Time Entry</h1> <h1>{% trans "Add a Tummy Time Entry" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,26 +1,25 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load duration i18n widget_tweaks %}
{% load duration %}
{% block title %}Tummy Time{% endblock %} {% block title %}{% trans "Tummy Time" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Tummy Time</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Tummy Time" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Tummy Time</h1> <h1>{% trans "Tummy Time" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Child</th> <th>{% trans "Child" %}</th>
<th>Duration</th> <th>{% trans "Duration" %}</th>
<th>Start</th> <th>{% trans "Start" %}</th>
<th>End</th> <th>{% trans "End" %}</th>
<th>Milestone</th> <th>{% trans "Milestone" %}</th>
<th class="text-center">Actions</th> <th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -32,7 +31,7 @@
<td>{{ tummytime.end|date:'n/j/y G:i' }}</td> <td>{{ tummytime.end|date:'n/j/y G:i' }}</td>
<td>{{ tummytime.milestone }}</td> <td>{{ tummytime.milestone }}</td>
<td class="text-center"> <td class="text-center">
<div class="btn-group btn-group-sm" role="group" aria-label="Actions"> <div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
{% if perms.core.change_tummytime %} {% if perms.core.change_tummytime %}
<a href="{% url 'core:tummytime-update' tummytime.id %}" class="btn btn-primary"> <a href="{% url 'core:tummytime-update' tummytime.id %}" class="btn btn-primary">
@ -51,7 +50,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="6">No tummy time entries found.</th> <th colspan="6">{% trans "No tummy time entries found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -61,7 +60,7 @@
{% if perms.core.add_tummytime %} {% if perms.core.add_tummytime %}
<a href="{% url 'core:tummytime-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'core:tummytime-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-tummytime" aria-hidden="true"></i> Add a Tummy Time Entry</a> <i class="icon icon-tummytime" aria-hidden="true"></i> {% trans "Add a Tummy Time Entry" %}</a>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,18 +1,18 @@
{% extends 'babybuddy/page.html' %} {% 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 %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:weight-list' %}">Weight</a></li> <li class="breadcrumb-item"><a href="{% url 'core:weight-list' %}">{% trans "Weight" %}</a></li>
<li class="breadcrumb-item active" aria-current="page">Delete</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<form role="form" method="post"> <form role="form" method="post">
{% csrf_token %} {% csrf_token %}
<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1> {% blocktrans %}<h1>Are you sure you want to delete <span class="text-info">{{ object }}</span>?</h1>{% endblocktrans %}
<input type="submit" value="Delete" class="btn btn-danger" /> <input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
<a href="{% url 'core:weight-list' %}" class="btn btn-default">Cancel</a> <a href="{% url 'core:weight-list' %}" class="btn btn-default">{% trans "Cancel" %}</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,28 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load i18n %}
{% block title %} {% block title %}
{% if object %} {% if object %}
{{ object }} {{ object }}
{% else %} {% else %}
Add a Weight Entry {% trans "Add a Weight Entry" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:weight-list' %}">Weight</a></li> <li class="breadcrumb-item"><a href="{% url 'core:weight-list' %}">{% trans "Weight" %}</a></li>
{% if object %} {% if object %}
<li class="breadcrumb-item active" aria-current="page">Update</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %} {% else %}
<li class="breadcrumb-item active" aria-current="page">Add a Weight Entry</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Add a Weight Entry" %}</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h1>Update <span class="text-info">{{ object }}</span></h1> {% blocktrans %}<h1>Update <span class="text-info">{{ object }}</span></h1>{% endblocktrans %}
{% else %} {% else %}
<h1>Add a Weight Entry</h1> <h1>{% trans "Add a Weight Entry" %}</h1>
{% endif %} {% endif %}
{% include 'babybuddy/form.html' %} {% include 'babybuddy/form.html' %}
{% endblock %} {% endblock %}

View File

@ -1,23 +1,23 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load widget_tweaks %} {% load i18n widget_tweaks %}
{% block title %}Weight{% endblock %} {% block title %}{% trans "Weight" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item active" aria-current="page">Weight</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Weight" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Weight</h1> <h1>{% trans "Weight" %}</h1>
{% include 'babybuddy/filter.html' %} {% include 'babybuddy/filter.html' %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Child</th> <th>{% trans "Child" %}</th>
<th>Weight</th> <th>{% trans "Weight" %}</th>
<th>Date</th> <th>{% trans "Date" %}</th>
<th class="text-center">Actions</th> <th class="text-center">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -27,7 +27,7 @@
<td>{{ object.weight }}</td> <td>{{ object.weight }}</td>
<td>{{ object.date }}</td> <td>{{ object.date }}</td>
<td class="text-center"> <td class="text-center">
<div class="btn-group btn-group-sm" role="group" aria-label="Actions"> <div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
{% if perms.core.change_weight %} {% if perms.core.change_weight %}
<a href="{% url 'core:weight-update' object.id %}" class="btn btn-primary"> <a href="{% url 'core:weight-update' object.id %}" class="btn btn-primary">
@ -46,7 +46,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<th colspan="4">No weight entries found.</th> <th colspan="4">{% trans "No weight entries found." %}</th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -56,7 +56,7 @@
{% if perms.core.add_weight %} {% if perms.core.add_weight %}
<a href="{% url 'core:weight-add' %}" class="btn btn-sm btn-success"> <a href="{% url 'core:weight-add' %}" class="btn btn-sm btn-success">
<i class="icon icon-weight" aria-hidden="true"></i> Add a Weight Entry <i class="icon icon-weight" aria-hidden="true"></i> {% trans "Add a Weight Entry" %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _
from core.models import DiaperChange, Feeding, Sleep, TummyTime from core.models import DiaperChange, Feeding, Sleep, TummyTime
@ -20,7 +21,9 @@ def get_objects(child, date):
for instance in instances: for instance in instances:
events.append({ events.append({
'time': timezone.localtime(instance.time), '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, 'model_name': instance.model_name,
}) })
@ -29,13 +32,17 @@ def get_objects(child, date):
for instance in instances: for instance in instances:
events.append({ events.append({
'time': timezone.localtime(instance.start), '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, 'model_name': instance.model_name,
'type': 'start' 'type': 'start'
}) })
events.append({ events.append({
'time': timezone.localtime(instance.end), '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, 'model_name': instance.model_name,
'type': 'end' 'type': 'end'
}) })
@ -45,13 +52,17 @@ def get_objects(child, date):
for instance in instances: for instance in instances:
events.append({ events.append({
'time': timezone.localtime(instance.start), '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, 'model_name': instance.model_name,
'type': 'start' 'type': 'start'
}) })
events.append({ events.append({
'time': timezone.localtime(instance.end), '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, 'model_name': instance.model_name,
'type': 'end' 'type': 'end'
}) })
@ -61,15 +72,17 @@ def get_objects(child, date):
for instance in instances: for instance in instances:
events.append({ events.append({
'time': timezone.localtime(instance.start), 'time': timezone.localtime(instance.start),
'event': '{} started tummy time!'.format( 'event': _('%(child)s started tummy time!') % {
instance.child.first_name), 'child': instance.child.first_name
},
'model_name': instance.model_name, 'model_name': instance.model_name,
'type': 'start' 'type': 'start'
}) })
events.append({ events.append({
'time': timezone.localtime(instance.end), 'time': timezone.localtime(instance.end),
'event': '{} finished tummy time.'.format( 'event': _('%(child)s finished tummy time.') % {
instance.child.first_name), 'child': instance.child.first_name
},
'model_name': instance.model_name, 'model_name': instance.model_name,
'type': 'end' 'type': 'end'
}) })

View File

@ -3,6 +3,7 @@ from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView 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): def get_success_message(self, cleaned_data):
cleaned_data['model'] = self.model._meta.verbose_name.title() cleaned_data['model'] = self.model._meta.verbose_name.title()
if 'child' in cleaned_data: 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: else:
self.success_message = '%(model)s entry added!' self.success_message = _('%(model)s entry added!')
return self.success_message % cleaned_data return self.success_message % cleaned_data
@ -27,9 +28,9 @@ class CoreUpdateView(PermissionRequired403Mixin, SuccessMessageMixin,
def get_success_message(self, cleaned_data): def get_success_message(self, cleaned_data):
cleaned_data['model'] = self.model._meta.verbose_name.title() cleaned_data['model'] = self.model._meta.verbose_name.title()
if 'child' in cleaned_data: 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: else:
self.success_message = '%(model)s entry updated.' self.success_message = _('%(model)s entry updated.')
return self.success_message % cleaned_data return self.success_message % cleaned_data
@ -58,7 +59,7 @@ class ChildAdd(CoreAddView):
permission_required = ('core.add_child',) permission_required = ('core.add_child',)
form_class = forms.ChildForm form_class = forms.ChildForm
success_url = reverse_lazy('core:child-list') 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): class ChildDetail(PermissionRequired403Mixin, DetailView):
@ -286,7 +287,7 @@ class TimerRestart(PermissionRequired403Mixin, RedirectView):
class TimerStop(PermissionRequired403Mixin, SuccessMessageMixin, RedirectView): class TimerStop(PermissionRequired403Mixin, SuccessMessageMixin, RedirectView):
permission_required = ('core.change_timer',) permission_required = ('core.change_timer',)
success_message = '%(timer)s stopped.' success_message = _('%(timer)s stopped.')
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
instance = models.Timer.objects.get(id=kwargs['pk']) instance = models.Timer.objects.get(id=kwargs['pk'])

View File

@ -1,12 +1,13 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load i18n %}
{% block header %}Last Diaper Change{% endblock %} {% block header %}{% trans "Last Diaper Change" %}{% endblock %}
{% block title %} {% block title %}
{% if change %} {% if change %}
{{ change.time|timesince }} ago {% blocktrans with time=change.time|timesince %}{{ time }} ago{% endblocktrans %}
{% else %} {% else %}
Never {% trans "Never" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,9 +1,10 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load i18n %}
{% block header %}Diaper Changes{% endblock %} {% block header %}{% trans "Diaper Changes" %}{% endblock %}
{% block title %} {% block title %}
Past Week {% trans "Past Week" %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -14,23 +15,23 @@
{% if info.wet_pct > 0 %} {% if info.wet_pct > 0 %}
<div class="progress-bar bg-primary lead" <div class="progress-bar bg-primary lead"
role="progressbar" role="progressbar"
style="width: {{ info.wet_pct }}%;">{{ info.wet|floatformat:'0' }}&nbsp;wet</div> style="width: {{ info.wet_pct|safe }}%;">{{ info.wet|floatformat:'0' }}&nbsp;{% trans "wet" %}</div>
{% endif %} {% endif %}
{% if info.solid_pct > 0 %} {% if info.solid_pct > 0 %}
<div class="progress-bar bg-secondary lead" <div class="progress-bar bg-secondary lead"
role="progressbar" role="progressbar"
style="width: {{ info.solid_pct }}%;"> style="width: {{ info.solid_pct|safe }}%;">
{{ info.solid|floatformat:'0' }}&nbsp;solid</div> {{ info.solid|floatformat:'0' }}&nbsp;{% trans "solid" %}</div>
{% endif %} {% endif %}
</div> </div>
<div class="text-center text-light small"> <div class="text-center text-light small">
{% if key == 0 %} {% if key == 0 %}
today {% trans "today" %}
{% elif key == 1 %} {% elif key == 1 %}
yesterday {% trans "yesterday" %}
{% else %} {% else %}
{{ key }} days ago {% blocktrans %}{{ key }} days ago{% endblocktrans %}
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}

View File

@ -1,12 +1,13 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load i18n %}
{% block header %}Last Feeding{% endblock %} {% block header %}{% trans "Last Feeding" %}{% endblock %}
{% block title %} {% block title %}
{% if feeding %} {% if feeding %}
{{ feeding.end|timesince }} ago {% blocktrans with time=feeding.end|timesince %}{{ time }} ago{% endblocktrans %}
{% else %} {% else %}
Never {% trans "Never" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,11 +1,12 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load i18n %}
{% block header %}Last Feeding Method{% endblock %} {% block header %}{% trans "Last Feeding Method" %}{% endblock %}
{% block title %} {% block title %}
{% if feeding %} {% if feeding %}
<div class="display-4 text-center">{{ feeding.method }}</div> <div class="display-4 text-center">{{ feeding.method }}</div>
{% else %} {% else %}
None {% trans "None" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,18 +1,20 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load duration %} {% load duration i18n %}
{% block header %}Today's Sleep{% endblock %} {% block header %}{% trans "Today's Sleep" %}{% endblock %}
{% block title %} {% block title %}
{% if total %} {% if total %}
{{ total|duration_string }} {{ total|duration_string }}
{% else %} {% else %}
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
None yet today {% trans "None yet today" %}
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if count > 0 %}{{ count }} sleep entries{% endif %} {% if count > 0 %}
{% blocktrans %}{{ count }} sleep entries{% endblocktrans %}
{% endif %}
{% endblock %} {% endblock %}

View File

@ -1,13 +1,13 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load duration %} {% load duration i18n %}
{% block header %}Last Slept{% endblock %} {% block header %}{% trans "Last Slept" %}{% endblock %}
{% block title %} {% block title %}
{% if sleep %} {% if sleep %}
{{ sleep.end|timesince }} ago {% blocktrans with time=sleep.end|timesince %}{{ time }} ago{% endblocktrans %}
{% else %} {% else %}
Never {% trans "Never" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,14 +1,14 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load duration %} {% load duration i18n %}
{% block header %}Today's Naps{% endblock %} {% block header %}{% trans "Today's Naps" %}{% endblock %}
{% block title %} {% block title %}
{% if count %} {% if count %}
{{ count }} nap{{ count|pluralize }} {% blocktrans with plural=count|pluralize %}{{ count }} nap{{ plural }}{% endblocktrans %}
{% else %} {% else %}
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
None yet today {% trans "None yet today" %}
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,9 +1,9 @@
{% load duration %} {% load duration i18n %}
<div class="card card-dashboard card-statistics"> <div class="card card-dashboard card-statistics">
<div class="card-header"> <div class="card-header">
<i class="icon icon-graph pull-left" aria-hidden="true"></i> <i class="icon icon-graph pull-left" aria-hidden="true"></i>
Statistics {% trans "Statistics" %}
</div> </div>
<div class="card-body text-center"> <div class="card-body text-center">
<div id="statistics-carousel" class="carousel slide" data-interval="false"> <div id="statistics-carousel" class="carousel slide" data-interval="false">
@ -20,7 +20,7 @@
{{ stat.stat }} {{ stat.stat }}
{% endif %} {% endif %}
{% else %} {% else %}
<em>Not enough data</em> <em>{% trans "Not enough data" %}</em>
{% endif %} {% endif %}
</span> </span>
<div class="card-text">{{ stat.title }}</div> <div class="card-text">{{ stat.title }}</div>
@ -29,11 +29,11 @@
</div> </div>
<a class="carousel-control-prev" href="#statistics-carousel" role="button" data-slide="prev"> <a class="carousel-control-prev" href="#statistics-carousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span> <span class="sr-only">{% trans "Previous" %}</span>
</a> </a>
<a class="carousel-control-next" href="#statistics-carousel" role="button" data-slide="next"> <a class="carousel-control-next" href="#statistics-carousel" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span> <span class="sr-only">{% trans "Next" %}</span>
</a> </a>
</div> </div>
</div> </div>

View File

@ -1,10 +1,11 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load i18n %}
{% block header %}Active Timers{% endblock %} {% block header %}{% trans "Active Timers" %}{% endblock %}
{% block title %} {% block title %}
{% with instances|length as count %} {% with instances|length as count %}
{{ count }} active timer{{ count|pluralize }} {% blocktrans with plural=count|pluralize %}{{ count }} active timer{{ plural }}{% endblocktrans %}
{% endwith %} {% endwith %}
{% endblock %} {% endblock %}
@ -13,7 +14,10 @@
{% for instance in instances %} {% for instance in instances %}
<a href="{% url 'core:timer-detail' instance.id %}" <a href="{% url 'core:timer-detail' instance.id %}"
class="list-group-item list-group-item-action"> class="list-group-item list-group-item-action">
<strong>{{ instance }}</strong> <p class="text-muted small m-0">Started by {{ instance.user }} at {{ instance.start|time }}</p> <strong>{{ instance }}</strong>
<p class="text-muted small m-0">
{% blocktrans with start=instance.start|time %}Started by {{ instance.user }} at {{ start }}{% endblocktrans %}
</p>
</a> </a>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -1,14 +1,14 @@
{% extends 'cards/base.html' %} {% 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 %} {% block title %}
{% if stats.count > 0 %} {% if stats.count > 0 %}
{{ stats.total|duration_string }} {{ stats.total|duration_string }}
{% else %} {% else %}
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
None yet today {% trans "None yet today" %}
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
@ -16,7 +16,9 @@
{% block listgroup %} {% block listgroup %}
<ul class="list-group list-group-flush text-muted small"> <ul class="list-group list-group-flush text-muted small">
{% for instance in instances %} {% for instance in instances %}
<li class="list-group-item">{{ instance.duration|duration_string }} at {{ instance.end|time }}</li> <li class="list-group-item">
{% blocktrans with duration=instance.duration|duration_string end=instance.end|time %}{{ duration }} at {{ end }}{% endblocktrans %}
</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}

View File

@ -1,13 +1,13 @@
{% extends 'cards/base.html' %} {% extends 'cards/base.html' %}
{% load duration %} {% load duration i18n %}
{% block header %}Last Tummy Time{% endblock %} {% block header %}{% trans "Last Tummy Time" %}{% endblock %}
{% block title %} {% block title %}
{% if tummytime %} {% if tummytime %}
{{ tummytime.time|timesince }} ago {% blocktrans with time=tummytime.time|timesince %}{{ time }} ago{% endblocktrans %}
{% else %} {% else %}
Never {% trans "Never" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,12 +1,12 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load cards %} {% load cards i18n %}
{% block title %}Dashboard - {{ object }}{% endblock %} {% block title %}{% trans "Dashboard" %} - {{ object }}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">Children</a></li> <li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li> <li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">Dashboard</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Dashboard" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -1,4 +1,6 @@
<div class="child-actions btn-group btn-group-lg center-block" role="group" aria-label="Child actions"> {% load i18n %}
<div class="child-actions btn-group btn-group-lg center-block" role="group" aria-label="{% trans "Child actions" %}">
{% if perms.core.view_child %} {% if perms.core.view_child %}
<a href="{% url 'dashboard:dashboard-child' object.slug %}" class="btn btn-success"> <a href="{% url 'dashboard:dashboard-child' object.slug %}" class="btn btn-success">
@ -14,12 +16,12 @@
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false"><i class="icon icon-graph" aria-hidden="true"></i></button> aria-expanded="false"><i class="icon icon-graph" aria-hidden="true"></i></button>
<div class="dropdown-menu" aria-labelledby="reports-dropdown"> <div class="dropdown-menu" aria-labelledby="reports-dropdown">
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-types-child' object.slug %}">Diaper Change Types</a> <a class="dropdown-item" href="{% url 'reports:report-diaperchange-types-child' object.slug %}">{% trans "Diaper Change Types" %}</a>
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-lifetimes-child' object.slug %}">Diaper Lifetimes</a> <a class="dropdown-item" href="{% url 'reports:report-diaperchange-lifetimes-child' object.slug %}">{% trans "Diaper Lifetimes" %}</a>
<a class="dropdown-item" href="{% url 'reports:report-feeding-duration-child' object.slug %}">Feeding Durations (Average)</a> <a class="dropdown-item" href="{% url 'reports:report-feeding-duration-child' object.slug %}">{% trans "Feeding Durations (Average)" %}</a>
<a class="dropdown-item" href="{% url 'reports:report-sleep-pattern-child' object.slug %}">Sleep Pattern</a> <a class="dropdown-item" href="{% url 'reports:report-sleep-pattern-child' object.slug %}">{% trans "Sleep Pattern" %}</a>
<a class="dropdown-item" href="{% url 'reports:report-sleep-totals-child' object.slug %}">Sleep Totals</a> <a class="dropdown-item" href="{% url 'reports:report-sleep-totals-child' object.slug %}">{% trans "Sleep Totals" %}</a>
<a class="dropdown-item" href="{% url 'reports:report-weight-weight-child' object.slug %}">Weight</a> <a class="dropdown-item" href="{% url 'reports:report-weight-weight-child' object.slug %}">{% trans "Weight" %}</a>
</div> </div>
</div> </div>

View File

@ -1,10 +1,10 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load static thumbnail %} {% load i18n static thumbnail %}
{% block title %}Dashboard{% endblock %} {% block title %}{% trans "Dashboard" %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item font-weight-bold">Dashboard</li> <li class="breadcrumb-item font-weight-bold">{% trans "Dashboard" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -24,8 +24,8 @@
<h4 class="card-title">{{ object }}</h4> <h4 class="card-title">{{ object }}</h4>
<div class="card-text"> <div class="card-text">
<p class="lead"> <p class="lead">
Born <span class="text-secondary">{{ object.birth_date }}</span><br/> {% trans "Born" %} <span class="text-secondary">{{ object.birth_date }}</span><br/>
Age <span class="text-secondary">{{ object.birth_date|timesince }}</span> {% trans "Age" %} <span class="text-secondary">{{ object.birth_date|timesince }}</span>
</p> </p>
{% include 'dashboard/child_button_group.html' %} {% include 'dashboard/child_button_group.html' %}
</div> </div>

View File

@ -3,6 +3,7 @@ from django import template
from django.db.models import Avg, Count, Sum from django.db.models import Avg, Count, Sum
from django.db.models.functions import TruncDate from django.db.models.functions import TruncDate
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _
from core import models from core import models
@ -163,39 +164,39 @@ def card_statistics(child):
stats.append({ stats.append({
'type': 'duration', 'type': 'duration',
'stat': changes['btwn_average'], 'stat': changes['btwn_average'],
'title': 'Diaper change frequency'}) 'title': _('Diaper change frequency')})
feedings = _feeding_statistics(child) feedings = _feeding_statistics(child)
stats.append({ stats.append({
'type': 'duration', 'type': 'duration',
'stat': feedings['btwn_average'], 'stat': feedings['btwn_average'],
'title': 'Feeding frequency'}) 'title': _('Feeding frequency')})
naps = _nap_statistics(child) naps = _nap_statistics(child)
stats.append({ stats.append({
'type': 'duration', 'type': 'duration',
'stat': naps['average'], 'stat': naps['average'],
'title': 'Average nap duration'}) 'title': _('Average nap duration')})
stats.append({ stats.append({
'type': 'float', 'type': 'float',
'stat': naps['avg_per_day'], 'stat': naps['avg_per_day'],
'title': 'Average naps per day'}) 'title': _('Average naps per day')})
sleep = _sleep_statistics(child) sleep = _sleep_statistics(child)
stats.append({ stats.append({
'type': 'duration', 'type': 'duration',
'stat': sleep['average'], 'stat': sleep['average'],
'title': 'Average sleep duration'}) 'title': _('Average sleep duration')})
stats.append({ stats.append({
'type': 'duration', 'type': 'duration',
'stat': sleep['btwn_average'], 'stat': sleep['btwn_average'],
'title': 'Average awake duration'}) 'title': _('Average awake duration')})
weight = _weight_statistics(child) weight = _weight_statistics(child)
stats.append({ stats.append({
'type': 'float', 'type': 'float',
'stat': weight['change_weekly'], 'stat': weight['change_weekly'],
'title': 'Weight change per week'}) 'title': _('Weight change per week')})
return {'stats': stats} return {'stats': stats}

View File

@ -171,6 +171,18 @@ gulp.task('makemigrations', function(cb) {
spawn('pipenv', command, { stdio: 'inherit' }).on('exit', 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) { gulp.task('reset', function(cb) {
spawn( spawn(
'pipenv', 'pipenv',

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils.translation import gettext as _
import plotly.offline as plotly import plotly.offline as plotly
import plotly.graph_objs as go import plotly.graph_objs as go
@ -22,7 +24,7 @@ def diaperchange_lifetimes(changes):
trace = go.Box( trace = go.Box(
y=[round(d.seconds/3600, 2) for d in durations], y=[round(d.seconds/3600, 2) for d in durations],
name='Changes', name=_('Changes'),
jitter=0.3, jitter=0.3,
pointpos=-1.8, pointpos=-1.8,
boxpoints='all' boxpoints='all'
@ -30,8 +32,8 @@ def diaperchange_lifetimes(changes):
layout_args = utils.default_graph_layout_options() layout_args = utils.default_graph_layout_options()
layout_args['height'] = 800 layout_args['height'] = 800
layout_args['title'] = '<b>Diaper Lifetimes</b>' layout_args['title'] = _('<b>Diaper Lifetimes</b>')
layout_args['yaxis']['title'] = 'Time between changes (hours)' layout_args['yaxis']['title'] = _('Time between changes (hours)')
layout_args['yaxis']['zeroline'] = False layout_args['yaxis']['zeroline'] = False
layout_args['yaxis']['dtick'] = 1 layout_args['yaxis']['dtick'] = 1

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.db.models import Count, Case, When from django.db.models import Count, Case, When
from django.db.models.functions import TruncDate from django.db.models.functions import TruncDate
from django.utils.translation import gettext as _
import plotly.offline as plotly import plotly.offline as plotly
import plotly.graph_objs as go import plotly.graph_objs as go
@ -23,28 +24,28 @@ def diaperchange_types(changes):
solid_trace = go.Scatter( solid_trace = go.Scatter(
mode='markers', mode='markers',
name='Solid', name=_('Solid'),
x=list(changes.values_list('date', flat=True)), x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('solid_count', flat=True)), y=list(changes.values_list('solid_count', flat=True)),
) )
wet_trace = go.Scatter( wet_trace = go.Scatter(
mode='markers', mode='markers',
name='Wet', name=_('Wet'),
x=list(changes.values_list('date', flat=True)), x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('wet_count', flat=True)) y=list(changes.values_list('wet_count', flat=True))
) )
total_trace = go.Scatter( total_trace = go.Scatter(
name='Total', name=_('Total'),
x=list(changes.values_list('date', flat=True)), x=list(changes.values_list('date', flat=True)),
y=list(changes.values_list('total', flat=True)) y=list(changes.values_list('total', flat=True))
) )
layout_args = utils.default_graph_layout_options() layout_args = utils.default_graph_layout_options()
layout_args['barmode'] = 'stack' layout_args['barmode'] = 'stack'
layout_args['title'] = '<b>Diaper Change Types</b>' layout_args['title'] = _('<b>Diaper Change Types</b>')
layout_args['xaxis']['title'] = 'Date' layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_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({ fig = go.Figure({
'data': [solid_trace, wet_trace, total_trace], 'data': [solid_trace, wet_trace, total_trace],

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.db.models import Count, Sum from django.db.models import Count, Sum
from django.db.models.functions import TruncDate from django.db.models.functions import TruncDate
from django.utils.translation import gettext as _
import plotly.offline as plotly import plotly.offline as plotly
import plotly.graph_objs as go import plotly.graph_objs as go
@ -32,7 +33,7 @@ def feeding_duration(instances):
averages.append(total['sum']/total['count']) averages.append(total['sum']/total['count'])
trace_avg = go.Scatter( trace_avg = go.Scatter(
name='Average duration', name=_('Average duration'),
line=dict(shape='spline'), line=dict(shape='spline'),
x=list(totals.values_list('date', flat=True)), x=list(totals.values_list('date', flat=True)),
y=[td.seconds/60 for td in averages], 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] text=[_duration_string_ms(td) for td in averages]
) )
trace_count = go.Scatter( trace_count = go.Scatter(
name='Total feedings', name=_('Total feedings'),
mode='markers', mode='markers',
x=list(totals.values_list('date', flat=True)), x=list(totals.values_list('date', flat=True)),
y=list(totals.values_list('count', 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 = utils.default_graph_layout_options()
layout_args['title'] = '<b>Average Feeding Durations</b>' layout_args['title'] = _('<b>Average Feeding Durations</b>')
layout_args['xaxis']['title'] = 'Date' layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_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'] = 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']['overlaying'] = 'y'
layout_args['yaxis2']['side'] = 'right' layout_args['yaxis2']['side'] = 'right'

View File

@ -2,6 +2,7 @@
from collections import OrderedDict from collections import OrderedDict
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _
import pandas as pd import pandas as pd
import plotly.offline as plotly import plotly.offline as plotly
@ -124,10 +125,10 @@ def sleep_pattern(instances):
layout_args['barmode'] = 'stack' layout_args['barmode'] = 'stack'
layout_args['hovermode'] = 'closest' layout_args['hovermode'] = 'closest'
layout_args['title'] = '<b>Sleep Pattern</b>' layout_args['title'] = _('<b>Sleep Pattern</b>')
layout_args['height'] = 800 layout_args['height'] = 800
layout_args['xaxis']['title'] = 'Date' layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['tickangle'] = -65 layout_args['xaxis']['tickangle'] = -65
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date() layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
@ -137,7 +138,7 @@ def sleep_pattern(instances):
for i in range(30, 60*24, 30): for i in range(30, 60*24, 30):
ticks[i] = (start + timezone.timedelta(minutes=i)).strftime('%I:%M %p') 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']['rangemode'] = 'tozero'
layout_args['yaxis']['tickmode'] = 'array' layout_args['yaxis']['tickmode'] = 'array'
layout_args['yaxis']['tickvals'] = list(ticks.keys()) layout_args['yaxis']['tickvals'] = list(ticks.keys())

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _
import plotly.offline as plotly import plotly.offline as plotly
import plotly.graph_objs as go import plotly.graph_objs as go
@ -36,7 +37,7 @@ def sleep_totals(instances):
totals[start.date()] += instance.duration totals[start.date()] += instance.duration
trace = go.Bar( trace = go.Bar(
name='Total sleep', name=_('Total sleep'),
x=list(totals.keys()), x=list(totals.keys()),
y=[td.seconds/3600 for td in totals.values()], y=[td.seconds/3600 for td in totals.values()],
hoverinfo='text', hoverinfo='text',
@ -46,10 +47,10 @@ def sleep_totals(instances):
layout_args = utils.default_graph_layout_options() layout_args = utils.default_graph_layout_options()
layout_args['barmode'] = 'stack' layout_args['barmode'] = 'stack'
layout_args['title'] = '<b>Sleep Totals</b>' layout_args['title'] = _('<b>Sleep Totals</b>')
layout_args['xaxis']['title'] = 'Date' layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_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({ fig = go.Figure({
'data': [trace], 'data': [trace],

View File

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils.translation import gettext as _
import plotly.offline as plotly import plotly.offline as plotly
import plotly.graph_objs as go import plotly.graph_objs as go
@ -14,7 +16,7 @@ def weight_weight(objects):
objects = objects.order_by('-date') objects = objects.order_by('-date')
trace = go.Scatter( trace = go.Scatter(
name='Weight', name=_('Weight'),
x=list(objects.values_list('date', flat=True)), x=list(objects.values_list('date', flat=True)),
y=list(objects.values_list('weight', flat=True)), y=list(objects.values_list('weight', flat=True)),
fill='tozeroy', fill='tozeroy',
@ -22,10 +24,10 @@ def weight_weight(objects):
layout_args = utils.default_graph_layout_options() layout_args = utils.default_graph_layout_options()
layout_args['barmode'] = 'stack' layout_args['barmode'] = 'stack'
layout_args['title'] = '<b>Weight</b>' layout_args['title'] = _('<b>Weight</b>')
layout_args['xaxis']['title'] = 'Date' layout_args['xaxis']['title'] = _('Date')
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date() layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
layout_args['yaxis']['title'] = 'Weight' layout_args['yaxis']['title'] = _('Weight')
fig = go.Figure({ fig = go.Figure({
'data': [trace], 'data': [trace],

View File

@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %} {% extends 'reports/report_base.html' %}
{% load i18n %}
{% block title %}Diaper Lifetimes - {{ object }}{% endblock %} {% block title %}{% trans "Diaper Lifetimes" %} - {{ object }}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
{{ block.super }} {{ block.super }}
<li class="breadcrumb-item active" aria-current="page">Diaper Lifetimes</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Diaper Lifetimes" %}</li>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %} {% 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 breadcrumbs %}
{{ block.super }} {{ block.super }}
<li class="breadcrumb-item active" aria-current="page">Diaper Types</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Diaper Change Types" %}</li>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %} {% 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 breadcrumbs %}
{{ block.super }} {{ block.super }}
<li class="breadcrumb-item active" aria-current="page">Average Feeding Durations</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Average Feeding Durations" %}</li>
{% endblock %} {% endblock %}

View File

@ -1,12 +1,12 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load static %} {% load i18n static %}
{% block title %}{% endblock %} {% block title %}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">Children</a></li> <li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li> <li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item">Reports</li> <li class="breadcrumb-item">{% trans "Reports" %}</li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -16,7 +16,7 @@
{% else %} {% else %}
<div class="jumbotron text-center"> <div class="jumbotron text-center">
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
There is no enough data to generate this report. {% trans "There is no enough data to generate this report." %}
<i class="icon icon-sad" aria-hidden="true"></i> <i class="icon icon-sad" aria-hidden="true"></i>
</div> </div>
{% endif %} {% endif %}

View File

@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %} {% extends 'reports/report_base.html' %}
{% load i18n %}
{% block title %}Sleep Patterns - {{ object }}{% endblock %} {% block title %}{% trans "Sleep Pattern" %} - {{ object }}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
{{ block.super }} {{ block.super }}
<li class="breadcrumb-item active" aria-current="page">Sleep Pattern</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Sleep Pattern" %}</li>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %} {% extends 'reports/report_base.html' %}
{% load i18n %}
{% block title %}Sleep Totals - {{ object }}{% endblock %} {% block title %}{% trans "Sleep Totals" %} - {{ object }}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
{{ block.super }} {{ block.super }}
<li class="breadcrumb-item active" aria-current="page">Sleep Totals</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Sleep Totals" %}</li>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,9 @@
{% extends 'reports/report_base.html' %} {% extends 'reports/report_base.html' %}
{% load i18n %}
{% block title %}Weight - {{ object }}{% endblock %} {% block title %}{% trans "Weight" %} - {{ object }}{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
{{ block.super }} {{ block.super }}
<li class="breadcrumb-item active" aria-current="page">Weight</li> <li class="breadcrumb-item active" aria-current="page">{% trans "Weight" %}</li>
{% endblock %} {% endblock %}