feat(i18n): add English localization with per-user language preference

This commit is contained in:
2026-07-06 22:41:09 +08:00
parent 643f1dc35a
commit 87785494d6
8 changed files with 243 additions and 98 deletions

View File

@@ -1,5 +1,4 @@
{% extends "base.html" %}
{% set lang = lang if lang is defined else 'zh' %}
{% block title %}{{ profile_user.username }} {{ 'showcase.title'|t(lang) }}{% endblock %}
{% block nav %}{% endblock %}
{% block content %}
@@ -114,47 +113,76 @@
</div>
{% endif %}
<!-- Lightbox -->
<div id="lightbox" class="lightbox" onclick="closeLightbox()">
<button class="lightbox-close" onclick="closeLightbox()"></button>
<button class="lightbox-prev" onclick="event.stopPropagation();navLightbox(-1)"></button>
<button class="lightbox-next" onclick="event.stopPropagation();navLightbox(1)"></button>
<div id="lightbox" class="lightbox" onclick="closeLightbox(event)">
<button class="lightbox-close" onclick="closeLightbox(event)">&times;</button>
<button class="lightbox-prev" onclick="navLightbox(-1,event)">&#10094;</button>
<button class="lightbox-next" onclick="navLightbox(1,event)">&#10095;</button>
<div class="lightbox-content" onclick="event.stopPropagation()">
<img id="lightbox-img" src="" alt="">
<div id="lightbox-number" class="lightbox-number"></div>
<div id="lightbox-caption" class="lightbox-caption"></div>
</div>
</div>
<script>
var cards = [];
document.querySelectorAll('.showcase-card').forEach(function(el) {
cards.push({ front: el.dataset.front, number: el.dataset.number });
});
var currentIndex = 0;
function openLightbox(el) {
currentIndex = cards.findIndex(function(c) { return c.number === el.dataset.number; });
showLightbox();
document.getElementById('lightbox').style.display = 'flex';
}
function showLightbox() {
var c = cards[currentIndex];
document.getElementById('lightbox-img').src = c.front;
document.getElementById('lightbox-number').textContent = c.number;
}
function closeLightbox() { document.getElementById('lightbox').style.display = 'none'; }
function navLightbox(dir) { currentIndex = (currentIndex + dir + cards.length) % cards.length; showLightbox(); }
document.addEventListener('keydown', function(e) {
if (document.getElementById('lightbox').style.display !== 'flex') return;
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowLeft') navLightbox(-1);
if (e.key === 'ArrowRight') navLightbox(1);
});
function switchTab(name) {
document.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); });
document.querySelectorAll('.tab-panel').forEach(function(p) { p.style.display = 'none'; });
event.currentTarget.classList.add('active');
document.getElementById('tab-' + name).style.display = '';
}
var lightboxItems = [];
var lightboxIdx = 0;
function buildLightboxItems() {
lightboxItems = [];
var visible = document.querySelectorAll('.tab-panel:not([style*="display:none"]) .showcase-card[data-front]');
visible.forEach(function(card) {
var front = card.getAttribute('data-front');
var number = card.getAttribute('data-number');
if (front) lightboxItems.push({src: front, number: number});
});
}
function openLightbox(el) {
buildLightboxItems();
var front = el.getAttribute('data-front');
if (!front) return;
var number = el.getAttribute('data-number');
for (var i = 0; i < lightboxItems.length; i++) {
if (lightboxItems[i].number === number) { lightboxIdx = i; break; }
}
showLightboxItem();
document.getElementById('lightbox').classList.add('active');
document.body.style.overflow = 'hidden';
}
function showLightboxItem() {
if (!lightboxItems.length) return;
var item = lightboxItems[lightboxIdx];
document.getElementById('lightbox-img').src = item.src;
document.getElementById('lightbox-caption').textContent = item.number + ' (' + (lightboxIdx+1) + '/' + lightboxItems.length + ')';
}
function closeLightbox(e) {
if (e) e.stopPropagation();
document.getElementById('lightbox').classList.remove('active');
document.body.style.overflow = '';
}
function navLightbox(dir, e) {
if (e) e.stopPropagation();
if (!lightboxItems.length) return;
lightboxIdx = (lightboxIdx + dir + lightboxItems.length) % lightboxItems.length;
showLightboxItem();
}
document.addEventListener('keydown', function(e) {
var lb = document.getElementById('lightbox');
if (!lb || !lb.classList.contains('active')) return;
if (e.key === 'Escape') closeLightbox();
else if (e.key === 'ArrowLeft') navLightbox(-1);
else if (e.key === 'ArrowRight') navLightbox(1);
});
</script>
{% endblock %}