- vault.hidrago.click: Vaultwarden (password manager) - git.hidrago.click: Gitea (git service) - All static sites (suma, weather, combien-jour, tokyo-trip, etc.) - Nginx configs for all subdomains - Systemd user service for Vaultwarden
248 lines
8.4 KiB
HTML
248 lines
8.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Combien de jours ? — Louis & Maël</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 2rem 1rem;
|
|
}
|
|
.lang-switch {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.lang-btn {
|
|
padding: 0.4rem 1rem;
|
|
border: 2px solid rgba(255,255,255,0.4);
|
|
background: rgba(255,255,255,0.15);
|
|
color: #fff;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
transition: all 0.2s;
|
|
}
|
|
.lang-btn:hover { background: rgba(255,255,255,0.3); }
|
|
.lang-btn.active {
|
|
background: #fff;
|
|
color: #764ba2;
|
|
border-color: #fff;
|
|
}
|
|
h1 {
|
|
color: #fff;
|
|
font-size: 2rem;
|
|
margin-bottom: 0.3rem;
|
|
text-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
}
|
|
.subtitle {
|
|
color: rgba(255,255,255,0.8);
|
|
font-size: 0.9rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1.5rem;
|
|
justify-content: center;
|
|
max-width: 800px;
|
|
width: 100%;
|
|
}
|
|
.card {
|
|
background: rgba(255,255,255,0.95);
|
|
border-radius: 20px;
|
|
padding: 2rem;
|
|
width: 360px;
|
|
box-shadow: 0 8px 32px rgba(0,0,0,0.15);
|
|
transition: transform 0.2s;
|
|
}
|
|
.card:hover { transform: translateY(-4px); }
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.card-header h2 { font-size: 1.5rem; color: #333; }
|
|
.birthdate { font-size: 0.8rem; color: #999; }
|
|
.age-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 1rem;
|
|
}
|
|
.age-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
background: rgba(0,0,0,0.03);
|
|
border-radius: 12px;
|
|
padding: 1rem;
|
|
}
|
|
.age-item.big { grid-column: span 2; }
|
|
.age-item.small { }
|
|
.number {
|
|
font-size: 2.8rem;
|
|
font-weight: 800;
|
|
color: #333;
|
|
line-height: 1;
|
|
}
|
|
.age-item.small .number { font-size: 1.8rem; }
|
|
.unit {
|
|
font-size: 0.85rem;
|
|
color: #888;
|
|
margin-top: 0.3rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
.sub {
|
|
font-size: 0.75rem;
|
|
color: #aaa;
|
|
margin-top: 0.15rem;
|
|
}
|
|
footer {
|
|
margin-top: 2rem;
|
|
color: rgba(255,255,255,0.6);
|
|
font-size: 0.8rem;
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="lang-switch">
|
|
<button class="lang-btn active" data-lang="fr">FR</button>
|
|
<button class="lang-btn" data-lang="en">EN</button>
|
|
<button class="lang-btn" data-lang="ja">日本語</button>
|
|
</div>
|
|
<h1 id="heading">📅 Combien de jours ?</h1>
|
|
<p class="subtitle" id="subtitle"></p>
|
|
<div class="container">
|
|
|
|
<div class="card" style="border-top: 5px solid #667eea;">
|
|
<div class="card-header">
|
|
<h2>👦 Louis</h2>
|
|
<span class="birthdate" data-i18n="born" data-y="2018" data-m="10" data-d="14"></span>
|
|
</div>
|
|
<div class="age-grid">
|
|
<div class="age-item big">
|
|
<span class="number">2,847</span>
|
|
<span class="unit" data-i18n="days">jours</span>
|
|
</div>
|
|
<div class="age-item big">
|
|
<span class="number">406</span>
|
|
<span class="unit" data-i18n="weeks">semaines</span>
|
|
<span class="sub" data-i18n="extra_days" data-n="5"></span>
|
|
</div>
|
|
<div class="age-item small">
|
|
<span class="number">7</span>
|
|
<span class="unit" data-i18n="years">ans</span>
|
|
</div>
|
|
<div class="age-item small">
|
|
<span class="number">9</span>
|
|
<span class="unit" data-i18n="months">mois</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card" style="border-top: 5px solid #764ba2;">
|
|
<div class="card-header">
|
|
<h2>🧒 Maël</h2>
|
|
<span class="birthdate" data-i18n="born" data-y="2020" data-m="6" data-d="24"></span>
|
|
</div>
|
|
<div class="age-grid">
|
|
<div class="age-item big">
|
|
<span class="number">2,228</span>
|
|
<span class="unit" data-i18n="days">jours</span>
|
|
</div>
|
|
<div class="age-item big">
|
|
<span class="number">318</span>
|
|
<span class="unit" data-i18n="weeks">semaines</span>
|
|
<span class="sub" data-i18n="extra_days" data-n="2"></span>
|
|
</div>
|
|
<div class="age-item small">
|
|
<span class="number">6</span>
|
|
<span class="unit" data-i18n="years">ans</span>
|
|
</div>
|
|
<div class="age-item small">
|
|
<span class="number">1</span>
|
|
<span class="unit" data-i18n="months">mois</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<footer>
|
|
<span data-i18n="updated">Mis à jour le</span> 30/07/2026 à 23:00 UTC
|
|
</footer>
|
|
|
|
<script>
|
|
const I18N = {"fr": {"title": "Combien de jours ? — Louis & Maël", "heading": "📅 Combien de jours ?", "born": "né le {date}", "days": "jours", "weeks": "semaines", "years": "ans", "months": "mois", "extra_days": "+ {n} j", "updated": "Mis à jour le"}, "en": {"title": "How Many Days? — Louis & Maël", "heading": "📅 How Many Days?", "born": "born on {date}", "days": "days", "weeks": "weeks", "years": "years", "months": "months", "extra_days": "+ {n} d", "updated": "Updated on"}, "ja": {"title": "何日経った? — Louis & Maël", "heading": "📅 何日経った?", "born": "{date} 生まれ", "days": "日", "weeks": "週間", "years": "歳", "months": "ヶ月", "extra_days": "+ {n} 日", "updated": "更新日時"}};
|
|
const MONTHS = {"fr": ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"], "en": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], "ja": ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]};
|
|
let currentLang = 'fr';
|
|
|
|
function formatDate(y, m, d, lang) {
|
|
if (lang === 'fr') return d + ' ' + MONTHS[lang][m-1] + ' ' + y;
|
|
if (lang === 'en') return MONTHS[lang][m-1] + ' ' + d + ', ' + y;
|
|
if (lang === 'ja') return y + '年' + MONTHS[lang][m-1] + d + '日';
|
|
}
|
|
|
|
function applyLang(lang) {
|
|
currentLang = lang;
|
|
const t = I18N[lang];
|
|
document.title = t.title;
|
|
document.getElementById('heading').textContent = t.heading;
|
|
|
|
// Today's date as subtitle
|
|
const today = new Date();
|
|
document.getElementById('subtitle').textContent = formatDate(today.getFullYear(), today.getMonth()+1, today.getDate(), lang);
|
|
|
|
// Generic i18n labels
|
|
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
const key = el.getAttribute('data-i18n');
|
|
if (!t[key]) return;
|
|
let text = t[key];
|
|
// Substitute date for born, n for extra_days
|
|
if (key === 'born') {
|
|
const y = parseInt(el.getAttribute('data-y'));
|
|
const m = parseInt(el.getAttribute('data-m'));
|
|
const d = parseInt(el.getAttribute('data-d'));
|
|
text = text.replace('{date}', formatDate(y, m, d, lang));
|
|
}
|
|
if (key === 'extra_days') {
|
|
const n = el.getAttribute('data-n');
|
|
text = text.replace('{n}', n);
|
|
}
|
|
el.textContent = text;
|
|
});
|
|
|
|
// Update active button
|
|
document.querySelectorAll('.lang-btn').forEach(btn => {
|
|
btn.classList.toggle('active', btn.getAttribute('data-lang') === lang);
|
|
});
|
|
document.documentElement.lang = lang;
|
|
}
|
|
|
|
document.querySelectorAll('.lang-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
applyLang(btn.getAttribute('data-lang'));
|
|
localStorage.setItem('age-lang', btn.getAttribute('data-lang'));
|
|
});
|
|
});
|
|
|
|
// Auto-detect
|
|
const saved = localStorage.getItem('age-lang');
|
|
if (saved && I18N[saved]) {
|
|
applyLang(saved);
|
|
} else {
|
|
const browserLang = navigator.language.slice(0, 2);
|
|
if (I18N[browserLang]) applyLang(browserLang);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |