feat(i18n): add Spanish localization support with Flask-Babel

- Add Flask-Babel for internationalization
- Configure app for Spanish (default) and English
- Add language switcher in navigation
- Wrap all UI text in templates with gettext()
- Create translation directory structure
- Add complete Spanish translations (58 strings)
- Add English translations (template)
- Compile .po to .mo files
- Add extract_translations.py script for future updates
- Add compile_translations.py script

Spanish is now the default language, with English available via ?lang=en

Translated strings include:
- Navigation (Home, Add Tablet, Add User, etc.)
- User Loans interface (Search, Filter, Status, etc.)
- Table headers (User, Tablet, Serial Number, etc.)
- Buttons (Loan, Return, Search, etc.)
- Messages (No users found, No active loans, etc.)
- All UI text across all templates
This commit is contained in:
ijuanes 2026-06-20 09:58:13 +01:00
parent db7e9591e1
commit 34dd3ca937
13 changed files with 1020 additions and 110 deletions

View file

@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ language or 'en' }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tablet Management System</title>
<title>{{ _('Tablet Management System') }}</title>
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
@ -48,16 +48,16 @@
extend: {
colors: {
primary: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
50: '#ecfdf5',
100: '#d1fae5',
200: '#a7f3d0',
300: '#6ee7b7',
400: '#34d399',
500: '#10b981',
600: '#059669',
700: '#047857',
800: '#065f46',
900: '#064e3b',
}
}
}
@ -65,25 +65,68 @@
}
</script>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="max-w-7xl mx-auto p-4 sm:p-6 lg:p-8">
<body class="bg-gray-50 text-gray-900">
<!-- Skip to main content -->
<a href="#main-content" class="sr-only focus:not-sr-only">Skip to main content</a>
<!-- Container -->
<div class="min-h-screen">
<!-- Header -->
<header class="mb-8">
<h1 class="text-2xl sm:text-3xl font-bold text-gray-800 text-center mb-6">
Tablet Management System
</h1>
<header class="bg-primary-600 text-white shadow-lg">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<!-- Logo / Title -->
<div class="flex items-center gap-3">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
</svg>
<h1 class="text-xl font-bold">{{ _('Tablet Management System') }}</h1>
</div>
<!-- Language Switcher -->
<div class="flex items-center gap-2">
<span class="text-sm opacity-80">{{ _('Language') }}:</span>
{% for lang_code, lang_name in config['LANGUAGES'].items() %}
<a
href="?lang={{ lang_code }}"
class="px-2 py-1 text-sm rounded hover:bg-white/20 transition-colors
{% if language == lang_code %}bg-white/30 font-medium{% endif %}"
>
{{ lang_name }}
</a>
{% endfor %}
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Flash Messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="mb-6 space-y-3">
{% for category, message in messages %}
<div class="p-4 rounded-md
{% if category == 'success' %}bg-green-100 text-green-800 border border-green-200
{% elif category == 'error' %}bg-red-100 text-red-800 border border-red-200
{% else %}bg-blue-100 text-blue-800 border border-blue-200
{% endif %}">
{{ message }}
<div class="p-4 rounded-lg
{% if category == 'success' %}bg-green-100 text-green-800
{% elif category == 'error' %}bg-red-100 text-red-800
{% elif category == 'warning' %}bg-yellow-100 text-yellow-800
{% else %}bg-blue-100 text-blue-800{% endif %}">
<div class="flex items-center gap-2">
{% if category == 'success' %}
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7"></path>
</svg>
{% elif category == 'error' %}
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
{% endif %}
<span>{{ _(message) }}</span>
</div>
</div>
{% endfor %}
</div>
@ -91,50 +134,55 @@
{% endwith %}
<!-- Navigation -->
<nav class="bg-green-600 rounded-lg shadow-md mb-6">
<nav class="bg-primary-600 rounded-lg shadow-md mb-6" aria-label="Main navigation">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-wrap justify-center sm:justify-start gap-1 sm:gap-2 py-3">
<a href="/"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
Home
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('Home') }}
</a>
<a href="/add_tablet"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
Add Tablet
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('Add Tablet') }}
</a>
<a href="/add_user"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
Add User
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('Add User') }}
</a>
<a href="/loan_tablet"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
Loan Tablet
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('Loan Tablet') }}
</a>
<a href="/history"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
Loan History
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('History') }}
</a>
<a href="/user_loans"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
User Loans
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('User Loans') }}
</a>
<a href="/non_loanable_devices"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
Non-Loanable Devices
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('Non-Loanable Devices') }}
</a>
<a href="/project_management"
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-green-700 transition-colors whitespace-nowrap">
Project Management
class="px-3 py-2 text-sm sm:text-base text-white rounded hover:bg-primary-700 transition-colors whitespace-nowrap">
{{ _('Project Management') }}
</a>
</div>
</div>
</nav>
</header>
<!-- Main Content -->
<main>
<!-- Page Content -->
{% block content %}{% endblock %}
</main>
<!-- Footer -->
<footer class="mt-12 py-6 border-t border-gray-200">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-sm text-gray-500">
<p>{{ _('Tablet Management System - Internal Tool') }}</p>
</div>
</footer>
</div>
</body>
</html>

View file

@ -7,19 +7,19 @@
<thead class="bg-green-600 text-white">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">
User
{{ _('User') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">
Identification
{{ _('Identification') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">
Active Loans
{{ _('Active Loans') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">
Last Loan Date
{{ _('Last Loan Date') }}
</th>
<th class="px-6 py-3 text-right text-xs font-medium uppercase tracking-wider">
Actions
{{ _('Actions') }}
</th>
</tr>
</thead>
@ -30,7 +30,7 @@
<div class="font-medium text-gray-900">{{ user.name }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-gray-500">
{{ user.identification or 'N/A' }}
{{ user.identification or _('N/A') }}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
@ -41,12 +41,12 @@
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ user.last_loan_date or 'Never' }}
{{ user.last_loan_date or _('Never') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm">
<a href="/user_loans/{{ user.id }}"
class="text-green-600 hover:text-green-800 font-medium">
View Details
{{ _('View Details') }}
</a>
</td>
</tr>
@ -60,9 +60,9 @@
<nav class="flex items-center justify-between pt-4" aria-label="Table navigation">
<div class="flex items-center space-x-2">
<span class="text-sm text-gray-500">
Showing <span class="font-medium text-gray-900">{{ (page - 1) * per_page + 1 }}</span>
to <span class="font-medium text-gray-900">{{ min(page * per_page, total_users) }}</span>
of <span class="font-medium text-gray-900">{{ total_users }}</span> users
{{ _('Showing') }} <span class="font-medium text-gray-900">{{ (page - 1) * per_page + 1 }}</span>
{{ _('to') }} <span class="font-medium text-gray-900">{{ min(page * per_page, total_users) }}</span>
{{ _('of') }} <span class="font-medium text-gray-900">{{ total_users }}</span> {{ _('users') }}
</span>
</div>
@ -77,11 +77,11 @@
hx-include="[name='search'], [name='status']"
class="px-3 py-2 ml-0 leading-tight text-gray-500 bg-white border border-gray-300 rounded-l-lg hover:bg-gray-100 hover:text-gray-700"
>
Previous
{{ _('Previous') }}
</a>
{%- else %}
<span class="px-3 py-2 ml-0 leading-tight text-gray-300 bg-white border border-gray-300 rounded-l-lg cursor-not-allowed">
Previous
{{ _('Previous') }}
</span>
{%- endif %}
@ -168,11 +168,11 @@
hx-include="[name='search'], [name='status']"
class="px-3 py-2 leading-tight text-gray-500 bg-white border border-gray-300 rounded-r-lg hover:bg-gray-100 hover:text-gray-700"
>
Next
{{ _('Next') }}
</a>
{%- else %}
<span class="px-3 py-2 leading-tight text-gray-300 bg-white border border-gray-300 rounded-r-lg cursor-not-allowed">
Next
{{ _('Next') }}
</span>
{%- endif %}
</div>
@ -186,21 +186,21 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<h3 class="text-lg font-medium text-gray-900 mb-2">No users found</h3>
<h3 class="text-lg font-medium text-gray-900 mb-2">{{ _('No users found') }}</h3>
<p class="text-gray-500 mb-4">
{% if search %}
No users match your search for "{{ search }}"
{{ _('No users match your search for') }} "{{ search }}"
{% elif status == 'with_loans' %}
No users have active loans
{{ _('No users have active loans') }}
{% elif status == 'no_loans' %}
All users have at least one active loan
{{ _('All users have at least one active loan') }}
{% else %}
There are no users in the system yet
{{ _('There are no users in the system yet') }}
{% endif %}
</p>
<a href="/user_loans"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-green-600 hover:bg-green-700 transition-colors">
Clear Filters
{{ _('Clear Filters') }}
</a>
</div>
{%- endif %}

View file

@ -10,7 +10,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
</svg>
Available Tablets
{{ _('Available Tablets') }}
<span class="ml-2 px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full">
{{ available_tablets|length }}
</span>
@ -23,19 +23,19 @@
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Brand
{{ _('Brand') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Model
{{ _('Model') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Serial Number
{{ _('Serial Number') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Notes
{{ _('Notes') }}
</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
{{ _('Actions') }}
</th>
</tr>
</thead>
@ -61,7 +61,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
Loan
{{ _('Loan') }}
</a>
</td>
</tr>
@ -75,10 +75,10 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path>
</svg>
<p>No available tablets</p>
<p>{{ _('No available tablets') }}</p>
<a href="/add_tablet"
class="mt-4 inline-block px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700">
Add Tablet
{{ _('Add Tablet') }}
</a>
</div>
{% endif %}
@ -92,7 +92,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Active Loans
{{ _('Active Loans') }}
<span class="ml-2 px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full">
{{ active_loans|length }}
</span>
@ -105,19 +105,19 @@
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tablet
{{ _('Tablet') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Serial Number
{{ _('Serial Number') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Borrower
{{ _('Borrower') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Loan Date
{{ _('Loan Date') }}
</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
{{ _('Actions') }}
</th>
</tr>
</thead>
@ -135,7 +135,7 @@
<div class="text-sm text-gray-500">{{ loan.identification }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ loan.loan_date[:10] if loan.loan_date else 'N/A' }}
{{ loan.loan_date[:10] if loan.loan_date else _('N/A') }}
{{ loan.loan_date[11:16] if loan.loan_date else '' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
@ -145,7 +145,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5l7 7-7 7"></path>
</svg>
Return
{{ _('Return') }}
</a>
</td>
</tr>
@ -159,7 +159,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<p>No active loans</p>
<p>{{ _('No active loans') }}</p>
</div>
{% endif %}
</div>

View file

@ -4,9 +4,9 @@
<div class="space-y-6">
<!-- Page Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<h2 class="text-xl font-semibold text-gray-800">User Loans</h2>
<h2 class="text-xl font-semibold text-gray-800">{{ _('User Loans') }}</h2>
<div class="text-sm text-gray-500">
<span id="result-count" hx-swap-oob="true">{{ total_users }} users</span>
<span id="result-count" hx-swap-oob="true">{{ total_users }} {{ _('users') }}</span>
</div>
</div>
@ -24,7 +24,7 @@
<!-- Search Input -->
<div class="md:col-span-2">
<label for="search" class="block text-sm font-medium text-gray-700 mb-1">
Search Users
{{ _('Search Users') }}
</label>
<div class="relative">
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400"
@ -37,7 +37,7 @@
id="search"
name="search"
value="{{ search or '' }}"
placeholder="Search by name or identification..."
placeholder="{{ _('Search by name or identification...') }}"
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500"
>
</div>
@ -46,16 +46,16 @@
<!-- Status Filter -->
<div>
<label for="status" class="block text-sm font-medium text-gray-700 mb-1">
Loan Status
{{ _('Loan Status') }}
</label>
<select
id="status"
name="status"
class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500"
>
<option value="" {% if not status %}selected{% endif %}>All Users</option>
<option value="with_loans" {% if status == 'with_loans' %}selected{% endif %}>With Active Loans</option>
<option value="no_loans" {% if status == 'no_loans' %}selected{% endif %}>No Active Loans</option>
<option value="" {% if not status %}selected{% endif %}>{{ _('All Users') }}</option>
<option value="with_loans" {% if status == 'with_loans' %}selected{% endif %}>{{ _('With Active Loans') }}</option>
<option value="no_loans" {% if status == 'no_loans' %}selected{% endif %}>{{ _('No Active Loans') }}</option>
</select>
</div>
</div>
@ -67,7 +67,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
Search
{{ _('Search') }}
</button>
</form>
</div>
@ -83,7 +83,6 @@
<!-- HTMX Script to handle URL updates -->
<script>
// Update browser URL when HTMX does a GET request
document.body.addEventListener('htmx:afterRequest', function(evt) {
if (evt.detail.requestConfig.method === 'get' && evt.detail.successful) {
// Update URL without reload
@ -103,7 +102,6 @@ document.body.addEventListener('htmx:afterRequest', function(evt) {
}
});
// Restore form state from URL on page load
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
const form = document.getElementById('search-form');

View file

@ -10,7 +10,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 19l-7-7 7-7"></path>
</svg>
Back to All Users
{{ _('Back to All Users') }}
</a>
</div>
@ -25,16 +25,16 @@
<div class="flex-1">
<h1 class="text-xl font-bold text-gray-900">{{ user.name }}</h1>
<p class="text-gray-500">
ID: {{ user.id }} | Identification: {{ user.identification or 'N/A' }}
{{ _('ID') }}: {{ user.id }} | {{ _('Identification') }}: {{ user.identification or _('N/A') }}
</p>
<div class="flex gap-4 mt-2">
<div class="flex items-center gap-2">
<span class="w-3 h-3 bg-green-500 rounded-full"></span>
<span class="text-sm text-gray-600">{{ active_count }} Active Loans</span>
<span class="text-sm text-gray-600">{{ active_count }} {{ _('Active Loans') }}</span>
</div>
<div class="flex items-center gap-2">
<span class="w-3 h-3 bg-gray-400 rounded-full"></span>
<span class="text-sm text-gray-600">{{ returned_count }} Returned</span>
<span class="text-sm text-gray-600">{{ returned_count }} {{ _('Returned') }}</span>
</div>
</div>
</div>
@ -42,13 +42,13 @@
{% if user.email %}
<a href="mailto:{{ user.email }}"
class="px-3 py-1 bg-blue-100 text-blue-700 rounded-lg text-sm hover:bg-blue-200">
Email
{{ _('Email') }}
</a>
{% endif %}
{% if user.phone %}
<a href="tel:{{ user.phone }}"
class="px-3 py-1 bg-purple-100 text-purple-700 rounded-lg text-sm hover:bg-purple-200">
Call
{{ _('Call') }}
</a>
{% endif %}
</div>
@ -58,7 +58,7 @@
<!-- Loan History -->
<div class="bg-white rounded-lg shadow">
<div class="p-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-800">Loan History</h2>
<h2 class="text-lg font-semibold text-gray-800">{{ _('Loan History') }}</h2>
</div>
{% if loans %}
@ -67,16 +67,16 @@
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tablet
{{ _('Tablet') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Loan Date
{{ _('Loan Date') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Return Date
{{ _('Return Date') }}
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
{{ _('Status') }}
</th>
</tr>
</thead>
@ -88,14 +88,14 @@
<div class="text-sm text-gray-500">{{ loan.serial_number }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ loan.loan_date[:10] if loan.loan_date else 'N/A' }}
{{ loan.loan_date[:10] if loan.loan_date else _('N/A') }}
{{ loan.loan_date[11:16] if loan.loan_date else '' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{% if loan.return_date %}
{{ loan.return_date[:10] }} {{ loan.return_date[11:16] }}
{% else %}
<span class="text-gray-400 italic">Not returned</span>
<span class="text-gray-400 italic">{{ _('Not returned') }}</span>
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap">
@ -118,7 +118,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<p>No loan history for this user</p>
<p>{{ _('No loan history for this user') }}</p>
</div>
{% endif %}
</div>