i18n: перевод страницы timetrack (ru/uk/en)

Учёт рабочего времени: заголовок, навигация по месяцам, фильтры,
столбцы таблицы, пустое состояние — {% trans %}; метрики (из вью)
переведены через gettext. uk/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 10:06:22 +03:00
parent 655ba8c31a
commit aa5a1bd421
6 changed files with 174 additions and 27 deletions
+6 -5
View File
@@ -12,6 +12,7 @@ from core.models.directories import Directory
from core.models import ConnectorConfig
from core.utils.htmx import render_page
from core.utils.pagination import paginate
from django.utils.translation import gettext_lazy as _
# Create your views here.
@@ -345,11 +346,11 @@ def timetrack_view(request):
fg, muted, warn = 'var(--color-foreground)', 'var(--color-muted-foreground)', 'var(--color-warning)'
metrics = [
{'label': 'Робочих днів', 'value': stats['workdays'], 'hint': 'з відмітками', 'color': fg},
{'label': 'Співробітників', 'value': stats['employees'], 'hint': 'відмічались', 'color': fg},
{'label': 'Змін', 'value': stats['shifts'], 'hint': 'за місяць', 'color': fg},
{'label': 'Незакритих', 'value': stats['incomplete'],'hint': 'без уходу', 'color': warn if stats['incomplete'] else muted},
{'label': 'Всього годин', 'value': stats['hours'], 'hint': 'відпрацьовано','color': fg},
{'label': _('Рабочих дней'), 'value': stats['workdays'], 'hint': _('с отметками'), 'color': fg},
{'label': _('Сотрудников'), 'value': stats['employees'], 'hint': _('отмечались'), 'color': fg},
{'label': _('Смен'), 'value': stats['shifts'], 'hint': _('за месяц'), 'color': fg},
{'label': _('Незакрытых'), 'value': stats['incomplete'],'hint': _('без ухода'), 'color': warn if stats['incomplete'] else muted},
{'label': _('Всего часов'), 'value': stats['hours'], 'hint': _('отработано'), 'color': fg},
]
page = paginate(request, rows)
Binary file not shown.
+76 -4
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-27 21:58+0300\n"
"POT-Creation-Date: 2026-07-28 10:05+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,7 +19,7 @@ msgstr ""
#: apps/ad_connector/models.py:10 apps/employees/models/base.py:186
#: apps/employees/models/permissions.py:47 templates/pages/employees.html:103
#: templates/pages/hr-portal.html:215
#: templates/pages/hr-portal.html:215 templates/pages/timetrack.html:58
msgid "Сотрудник"
msgstr "Employee"
@@ -272,7 +272,7 @@ msgstr "Warehouse"
#: apps/employees/models/base.py:83 apps/locations/models/base.py:31
#: templates/pages/employees.html:56 templates/pages/hr-portal.html:124
#: templates/pages/hr-portal.html:440
#: templates/pages/hr-portal.html:440 templates/pages/timetrack.html:47
msgid "Офис"
msgstr "Office"
@@ -925,7 +925,8 @@ msgid ""
"организации><номер>@домен. Можно задать вручную."
msgstr ""
#: apps/locations/admin.py:376 templates/pages/structure.html:61
#: apps/locations/admin.py:376 core/views.py:350
#: templates/pages/structure.html:61
msgid "Сотрудников"
msgstr ""
@@ -1114,6 +1115,7 @@ msgstr ""
#: apps/locations/models/pharmacies.py:211 templates/index.html:381
#: templates/pages/dislocation.html:16 templates/pages/pharmacies.html:6
#: templates/pages/timetrack.html:49
msgid "Аптеки"
msgstr "Pharmacies"
@@ -1657,6 +1659,42 @@ msgstr ""
msgid "Неверная контрольная сумма ИНН"
msgstr ""
#: core/views.py:349
msgid "Рабочих дней"
msgstr "Working days"
#: core/views.py:349
msgid "с отметками"
msgstr "with check-ins"
#: core/views.py:350
msgid "отмечались"
msgstr "checked in"
#: core/views.py:351
msgid "Смен"
msgstr "Shifts"
#: core/views.py:351
msgid "за месяц"
msgstr "this month"
#: core/views.py:352 templates/pages/timetrack.html:62
msgid "Незакрытых"
msgstr "Open"
#: core/views.py:352
msgid "без ухода"
msgstr "without checkout"
#: core/views.py:353
msgid "Всего часов"
msgstr "Total hours"
#: core/views.py:353
msgid "отработано"
msgstr "worked"
#: templates/index.html:210
msgid "Сеть аптек"
msgstr "Pharmacy network"
@@ -1740,6 +1778,7 @@ msgid "Рекрутинг"
msgstr "Recruiting"
#: templates/index.html:410 templates/pages/hr-portal.html:292
#: templates/pages/timetrack.html:7
msgid "Учёт рабочего времени"
msgstr "Time tracking"
@@ -2161,6 +2200,7 @@ msgstr "employees, of them"
#: templates/pages/employees.html:48 templates/pages/hr-portal.html:122
#: templates/pages/news.html:111 templates/pages/tasks.html:60
#: templates/pages/timetrack.html:45
msgid "Все"
msgstr "All"
@@ -2955,3 +2995,35 @@ msgstr "No tasks with this status"
#: templates/pages/tasks.html:144
msgid "Задач пока нет — создайте первую"
msgstr "No tasks yet — create the first"
#: templates/pages/timetrack.html:9
msgid "сотрудников с отметками"
msgstr "employees with check-ins"
#: templates/pages/timetrack.html:16
msgid "Предыдущий месяц"
msgstr "Previous month"
#: templates/pages/timetrack.html:22
msgid "Следующий месяц"
msgstr "Next month"
#: templates/pages/timetrack.html:59
msgid "Часов"
msgstr "Hours"
#: templates/pages/timetrack.html:60
msgid "Дней"
msgstr "Days"
#: templates/pages/timetrack.html:61
msgid "Сред./день"
msgstr "Avg/day"
#: templates/pages/timetrack.html:84
msgid "Нет отметок рабочего времени за"
msgstr "No time records for"
#: templates/pages/timetrack.html:92
msgid "сотрудников"
msgstr "employees"
Binary file not shown.
+76 -4
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-27 21:58+0300\n"
"POT-Creation-Date: 2026-07-28 10:05+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -22,7 +22,7 @@ msgstr ""
#: apps/ad_connector/models.py:10 apps/employees/models/base.py:186
#: apps/employees/models/permissions.py:47 templates/pages/employees.html:103
#: templates/pages/hr-portal.html:215
#: templates/pages/hr-portal.html:215 templates/pages/timetrack.html:58
msgid "Сотрудник"
msgstr "Співробітник"
@@ -275,7 +275,7 @@ msgstr "Склад"
#: apps/employees/models/base.py:83 apps/locations/models/base.py:31
#: templates/pages/employees.html:56 templates/pages/hr-portal.html:124
#: templates/pages/hr-portal.html:440
#: templates/pages/hr-portal.html:440 templates/pages/timetrack.html:47
msgid "Офис"
msgstr "Офіс"
@@ -928,7 +928,8 @@ msgid ""
"организации><номер>@домен. Можно задать вручную."
msgstr ""
#: apps/locations/admin.py:376 templates/pages/structure.html:61
#: apps/locations/admin.py:376 core/views.py:350
#: templates/pages/structure.html:61
msgid "Сотрудников"
msgstr ""
@@ -1117,6 +1118,7 @@ msgstr ""
#: apps/locations/models/pharmacies.py:211 templates/index.html:381
#: templates/pages/dislocation.html:16 templates/pages/pharmacies.html:6
#: templates/pages/timetrack.html:49
msgid "Аптеки"
msgstr "Аптеки"
@@ -1660,6 +1662,42 @@ msgstr ""
msgid "Неверная контрольная сумма ИНН"
msgstr ""
#: core/views.py:349
msgid "Рабочих дней"
msgstr "Робочих днів"
#: core/views.py:349
msgid "с отметками"
msgstr "з відмітками"
#: core/views.py:350
msgid "отмечались"
msgstr "відмічались"
#: core/views.py:351
msgid "Смен"
msgstr "Змін"
#: core/views.py:351
msgid "за месяц"
msgstr "за місяць"
#: core/views.py:352 templates/pages/timetrack.html:62
msgid "Незакрытых"
msgstr "Незакритих"
#: core/views.py:352
msgid "без ухода"
msgstr "без виходу"
#: core/views.py:353
msgid "Всего часов"
msgstr "Всього годин"
#: core/views.py:353
msgid "отработано"
msgstr "відпрацьовано"
#: templates/index.html:210
msgid "Сеть аптек"
msgstr "Мережа аптек"
@@ -1743,6 +1781,7 @@ msgid "Рекрутинг"
msgstr "Рекрутинг"
#: templates/index.html:410 templates/pages/hr-portal.html:292
#: templates/pages/timetrack.html:7
msgid "Учёт рабочего времени"
msgstr "Облік робочого часу"
@@ -2164,6 +2203,7 @@ msgstr "співробітників, з них"
#: templates/pages/employees.html:48 templates/pages/hr-portal.html:122
#: templates/pages/news.html:111 templates/pages/tasks.html:60
#: templates/pages/timetrack.html:45
msgid "Все"
msgstr "Всі"
@@ -2958,3 +2998,35 @@ msgstr "Задач з таким статусом немає"
#: templates/pages/tasks.html:144
msgid "Задач пока нет — создайте первую"
msgstr "Задач поки немає — створіть першу"
#: templates/pages/timetrack.html:9
msgid "сотрудников с отметками"
msgstr "співробітників з відмітками"
#: templates/pages/timetrack.html:16
msgid "Предыдущий месяц"
msgstr "Попередній місяць"
#: templates/pages/timetrack.html:22
msgid "Следующий месяц"
msgstr "Наступний місяць"
#: templates/pages/timetrack.html:59
msgid "Часов"
msgstr "Годин"
#: templates/pages/timetrack.html:60
msgid "Дней"
msgstr "Днів"
#: templates/pages/timetrack.html:61
msgid "Сред./день"
msgstr "Сер./день"
#: templates/pages/timetrack.html:84
msgid "Нет отметок рабочего времени за"
msgstr "Немає відміток робочого часу за"
#: templates/pages/timetrack.html:92
msgid "сотрудников"
msgstr "співробітників"
+16 -14
View File
@@ -1,24 +1,25 @@
{% load common_tags %}
{% load i18n %}
<div class="page-content" id="timetrack-page">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem;">
<div>
<h1 style="font-size:1.25rem;font-weight:500;color:var(--color-foreground);">Облік робочого часу</h1>
<h1 style="font-size:1.25rem;font-weight:500;color:var(--color-foreground);">{% trans "Учёт рабочего времени" %}</h1>
<p style="font-size:0.875rem;color:var(--color-muted-foreground);margin-top:0.25rem;">
{{ month_label }} · {{ stats.employees }} співробітників з відмітками
{{ month_label }} · {{ stats.employees }} {% trans "сотрудников с отметками" %}
</p>
</div>
<!-- Вибір місяця -->
<div style="display:flex;align-items:center;gap:0.5rem;">
<a href="#" class="btn btn-ghost btn-icon"
hx-get="/timetrack/?month={{ prev_month }}{% if location_type %}&location_type={{ location_type }}{% endif %}"
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader" title="Попередній місяць">
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader" title="{% trans 'Предыдущий месяц' %}">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>
</a>
<span style="font-size:0.875rem;font-weight:500;color:var(--color-foreground);min-width:9rem;text-align:center;">{{ month_label }}</span>
<a href="#" class="btn btn-ghost btn-icon"
hx-get="/timetrack/?month={{ next_month }}{% if location_type %}&location_type={{ location_type }}{% endif %}"
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader" title="Наступний місяць">
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader" title="{% trans 'Следующий месяц' %}">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
</a>
</div>
@@ -41,11 +42,11 @@
<div style="padding:0.875rem 1.5rem;border-bottom:1px solid var(--color-border);">
<div style="display:inline-flex;gap:0.25rem;background:var(--color-muted);border-radius:var(--radius);padding:0.25rem;">
<a href="#" hx-get="/timetrack/?month={{ month }}" hx-target="#main-content" hx-push-url="true" hx-indicator="#loader"
style="display:inline-flex;align-items:center;padding:0.25rem 0.875rem;font-size:0.8125rem;border-radius:calc(var(--radius) - 2px);font-weight:500;text-decoration:none;transition:all 150ms;{% if not location_type %}background:var(--color-card);color:var(--color-card-foreground);box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %}color:var(--color-muted-foreground);{% endif %}">Всі · {{ counts.all }}</a>
style="display:inline-flex;align-items:center;padding:0.25rem 0.875rem;font-size:0.8125rem;border-radius:calc(var(--radius) - 2px);font-weight:500;text-decoration:none;transition:all 150ms;{% if not location_type %}background:var(--color-card);color:var(--color-card-foreground);box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %}color:var(--color-muted-foreground);{% endif %}">{% trans "Все" %} · {{ counts.all }}</a>
<a href="#" hx-get="/timetrack/?month={{ month }}&location_type=office" hx-target="#main-content" hx-push-url="true" hx-indicator="#loader"
style="display:inline-flex;align-items:center;padding:0.25rem 0.875rem;font-size:0.8125rem;border-radius:calc(var(--radius) - 2px);font-weight:500;text-decoration:none;transition:all 150ms;{% if location_type == 'office' %}background:var(--color-card);color:var(--color-card-foreground);box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %}color:var(--color-muted-foreground);{% endif %}">Офіс · {{ counts.office }}</a>
style="display:inline-flex;align-items:center;padding:0.25rem 0.875rem;font-size:0.8125rem;border-radius:calc(var(--radius) - 2px);font-weight:500;text-decoration:none;transition:all 150ms;{% if location_type == 'office' %}background:var(--color-card);color:var(--color-card-foreground);box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %}color:var(--color-muted-foreground);{% endif %}">{% trans "Офис" %} · {{ counts.office }}</a>
<a href="#" hx-get="/timetrack/?month={{ month }}&location_type=pharmacy" hx-target="#main-content" hx-push-url="true" hx-indicator="#loader"
style="display:inline-flex;align-items:center;padding:0.25rem 0.875rem;font-size:0.8125rem;border-radius:calc(var(--radius) - 2px);font-weight:500;text-decoration:none;transition:all 150ms;{% if location_type == 'pharmacy' %}background:var(--color-card);color:var(--color-card-foreground);box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %}color:var(--color-muted-foreground);{% endif %}">Аптеки · {{ counts.pharmacy }}</a>
style="display:inline-flex;align-items:center;padding:0.25rem 0.875rem;font-size:0.8125rem;border-radius:calc(var(--radius) - 2px);font-weight:500;text-decoration:none;transition:all 150ms;{% if location_type == 'pharmacy' %}background:var(--color-card);color:var(--color-card-foreground);box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %}color:var(--color-muted-foreground);{% endif %}">{% trans "Аптеки" %} · {{ counts.pharmacy }}</a>
</div>
</div>
@@ -54,11 +55,11 @@
<table style="width:100%;border-collapse:collapse;font-size:0.875rem;">
<thead>
<tr style="border-bottom:1px solid var(--color-border);">
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">Співробітник</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">Годин</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">Днів</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">Сер./день</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">Незакритих</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Сотрудник" %}</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Часов" %}</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Дней" %}</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Сред./день" %}</th>
<th style="padding:0.75rem 1rem;text-align:right;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Незакрытых" %}</th>
</tr>
</thead>
<tbody>
@@ -80,7 +81,7 @@
{% empty %}
<tr>
<td colspan="5" style="padding:2rem 1.25rem;text-align:center;color:var(--color-muted-foreground);">
За {{ month_label }} немає відміток робочого часу.
{% trans "Нет отметок рабочего времени за" %} {{ month_label }}.
</td>
</tr>
{% endfor %}
@@ -88,6 +89,7 @@
</table>
</div>
{% include "partials/pagination.html" with page=page base_url="/timetrack/" page_param="page" size_param="per_page" item_label="співробітників" %}
{% trans "сотрудников" as tt_item_label %}
{% include "partials/pagination.html" with page=page base_url="/timetrack/" page_param="page" size_param="per_page" item_label=tt_item_label %}
</div>
</div>