Initial commit: Sirius en Guadeloupe - Application web multilingue

This commit is contained in:
sucupira
2025-07-09 19:56:54 -04:00
parent 0813a32e24
commit b1b1d452c7
20 changed files with 2530 additions and 228 deletions
+49
View File
@@ -0,0 +1,49 @@
name: Deploy to GitHub Pages
on:
push:
branches: [ main ]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./dist
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+27 -27
View File
@@ -1,33 +1,33 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Fichiers de build
dist/
build/
.svelte-kit/
node_modules
dist
dist-ssr
*.local
# Dépendances
node_modules/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Environment variables
# Fichiers d'environnement
.env
.env.local
.env.production
# Build files
build/
.svelte-kit/
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# OS
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
*.swo
# Temporaires
*.tmp
*.temp
.cache/
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 cyber-mawonaj & OKI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+41 -45
View File
@@ -1,31 +1,29 @@
# Sirius en Guadeloupe 🌟
# Sirius en Guadeloupe - Gwada-Sirius
Un site web interactif pour découvrir et observer le lever héliaque de Sirius depuis la Guadeloupe.
Un projet web célébrant le lever héliaque de Sirius en Guadeloupe, fusionnant astronomie, culture africaine et héritage caribéen.
## 🌍 À propos
## 🌟 À propos
Ce projet célèbre le patrimoine astronomique africain et caribéen à travers l'observation du lever héliaque de Sirius. Il combine science moderne et savoirs ancestraux pour offrir un guide complet d'observation.
Cette application web interactive permet de :
- Calculer les dates précises du lever héliaque de Sirius pour différents lieux en Guadeloupe
- Explorer l'héritage culturel de Sirius à travers les civilisations (Égypte ancienne, Dogons, Caraïbes)
- Comprendre la science astronomique derrière ce phénomène
- Découvrir les meilleurs sites d'observation en Guadeloupe
## ✨ Fonctionnalités
## 🚀 Technologies utilisées
- **Calculateur de prédictions** : Trouvez la date et l'heure exactes du lever héliaque pour 7 sites en Guadeloupe
- **Guide scientifique** : Comprenez la mécanique céleste du phénomène
- **Patrimoine culturel** : Découvrez l'importance de Sirius dans les cultures africaines
- **Guide pratique** : Instructions détaillées pour réussir votre observation
- **Visualisation interactive** : Globe animé montrant la progression mondiale du phénomène
- **Svelte** - Framework JavaScript réactif
- **Canvas 2D** - Pour les animations et visualisations
- **GeoJSON** - Pour les cartes interactives
- **i18n** - Support multilingue (Français, English, Kreyòl Ayisyen)
## 🛠️ Technologies
- **Svelte** : Framework JS réactif et performant
- **Vite** : Build tool moderne et rapide
- **Canvas 2D** : Pour les animations et visualisations
- **CSS moderne** : Animations, gradients, et design responsive
## 🚀 Installation
## 📦 Installation
```bash
# Cloner le repository
git clone https://github.com/yourusername/gwada-sirius.git
git clone https://github.com/[votre-username]/gwada-sirius.git
# Entrer dans le dossier
cd gwada-sirius
# Installer les dépendances
@@ -35,41 +33,39 @@ npm install
npm run dev
```
## 📦 Build
## 🌍 Fonctionnalités
```bash
# Créer une version de production
npm run build
# Prévisualiser la version de production
npm run preview
```
- **Calculateur de prédictions** : Dates précises pour 7 sites d'observation
- **Globe interactif** : Visualisation de la trajectoire de Sirius
- **Carte des sites** : Localisation des meilleurs points d'observation
- **Multilingue** : Interface en français, anglais et créole haïtien
- **Responsive** : Optimisé pour mobile et desktop
- **Animations** : Ciel étoilé animé et patterns africains
## 🎨 Design
Le design s'inspire des motifs géométriques africains (Kente, Bogolan, Adinkra) avec une palette de couleurs panafricaine et caribéenne :
- Or (Sirius, soleil)
- Rouge profond
- Vert forêt
- Bleu nuit profond
Le design s'inspire des motifs africains (Kente, Adinkra, Bogolan) avec une palette de couleurs symbolique :
- Or (#FFD700) - Sirius et la lumière divine
- Rouge (#FF6B35) - Force et énergie vitale
- Vert (#4CAF50) - Nature et renouveau
- Bleu nuit (#0A1628) - Ciel nocturne
## 📱 Mobile First
## 👥 Crédits
L'application est conçue en priorité pour les appareils mobiles avec :
- Navigation responsive
- Touch-friendly
- Performance optimisée
Projet réalisé avec ❤️ par :
- [cyber-mawonaj](https://github.com/cyber-mawonaj)
- [OKI](https://o-k-i.net)
## 📜 Licence
Ce projet est sous licence MIT - voir le fichier [LICENSE](LICENSE) pour plus de détails.
## 🙏 Remerciements
- Données astronomiques calculées avec Stellarium
- Inspiré du rapport "Sirius en Guadeloupe : Guide Astronomique et Culturel"
- Patrimoine culturel africain et caribéen
## 📄 Licence
Ce projet est sous licence MIT.
- Les anciens Égyptiens pour leur sagesse astronomique
- Les Dogons du Mali pour avoir préservé ces connaissances
- La communauté guadeloupéenne pour perpétuer ces traditions
---
*"Les étoiles sont les ancêtres qui veillent sur nous"* - Proverbe africain
*"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain*
View File
+10 -4
View File
@@ -1,5 +1,6 @@
<script>
import { onMount } from 'svelte';
import { initLanguage } from './lib/i18n.js';
import Hero from './components/Hero.svelte';
import Navigation from './components/Navigation.svelte';
import About from './components/About.svelte';
@@ -7,15 +8,19 @@
import Science from './components/Science.svelte';
import Culture from './components/Culture.svelte';
import Observatory from './components/Observatory.svelte';
import Globe from './components/Globe.svelte';
import GlobeOpenGlobus from './components/GlobeOpenGlobus.svelte';
import GlobeGeoJSON from './components/GlobeGeoJSON.svelte';
import Footer from './components/Footer.svelte';
import Dogon from './components/Dogon.svelte';
import Calculations from './components/Calculations.svelte';
import ObservationMap from './components/ObservationMap.svelte';
let scrollY = 0;
let activeSection = 'home';
onMount(() => {
// Initialiser la langue
initLanguage();
const handleScroll = () => {
scrollY = window.scrollY;
@@ -45,10 +50,11 @@
<About />
<Predictions />
<Science />
<Calculations />
<Dogon />
<Culture />
<Observatory />
<Globe />
<GlobeOpenGlobus />
<ObservationMap />
<GlobeGeoJSON />
</main>
+14 -2
View File
@@ -1,5 +1,6 @@
<script>
import { onMount } from 'svelte';
import { t } from '../lib/i18n.js';
let visible = false;
@@ -71,8 +72,8 @@
{#each features as feature, i}
<div class="feature-card card" style="animation-delay: {i * 0.2}s">
<div class="feature-icon">{feature.icon}</div>
<h3>{feature.title}</h3>
<p>{feature.description}</p>
<h3>{$t(feature.title)}</h3>
<p>{$t(feature.description)}</p>
</div>
{/each}
</div>
@@ -165,6 +166,17 @@
margin-bottom: 0.5rem;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.features-grid {
grid-template-columns: 1fr;
+230
View File
@@ -0,0 +1,230 @@
<script>
import { onMount } from 'svelte';
let visible = false;
let selectedCalculation = 'heliacal';
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
}
});
});
const section = document.getElementById('calculations');
if (section) observer.observe(section);
return () => observer.disconnect();
});
const calculations = {
heliacal: {
title: 'Lever Héliaque',
description: 'Calcul de la première visibilité de Sirius avant l\'aube'
},
arcus: {
title: 'Arcus Visionis',
description: 'Angle minimal entre le Soleil et Sirius pour la visibilité'
}
};
</script>
<section id="calculations" class="calculations-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Calculs Astronomiques</h2>
<p class="section-subtitle">
La science précise derrière le lever héliaque
</p>
</div>
<div class="calculations-wrapper" class:visible>
<!-- Sélecteur de calcul -->
<div class="calc-selector">
{#each Object.entries(calculations) as [key, calc]}
<button
class="calc-btn"
class:active={selectedCalculation === key}
on:click={() => selectedCalculation = key}
>
<h4>{calc.title}</h4>
<p>{calc.description}</p>
</button>
{/each}
</div>
<!-- Contenu des calculs -->
<div class="calc-content">
{#if selectedCalculation === 'heliacal'}
<div class="calc-panel">
<h3>Calcul du Lever Héliaque de Sirius</h3>
<div class="formula-box">
<h4>Formule Principale</h4>
<div class="formula">
<p><strong>Date du lever héliaque = </strong></p>
<p>Date de conjonction + Arcus Visionis / Vitesse angulaire du Soleil</p>
</div>
</div>
<div class="calculation-steps">
<h4>Étapes du Calcul pour la Guadeloupe</h4>
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h5>Coordonnées de la Guadeloupe</h5>
<code>Latitude: 16.25°N, Longitude: 61.58°O</code>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h5>Position de Sirius</h5>
<code>Ascension droite: 6h 45m 09s</code>
<code>Déclinaison: -16° 42' 58"</code>
</div>
</div>
</div>
</div> {/if}
{#if selectedCalculation === 'arcus'}
<div class="calc-panel">
<h3>Arcus Visionis - L'Arc de Vision</h3>
<div class="explanation">
<p>
L'Arcus Visionis est l'angle minimal entre le Soleil et une étoile
pour que celle-ci devienne visible.
</p>
</div>
</div>
{/if}
</div>
</div>
</div>
</section>
<style>
.calculations-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.calculations-wrapper {
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out;
}
.calculations-wrapper.visible {
opacity: 1;
transform: translateY(0);
}
.calc-selector {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.calc-btn {
background: rgba(255, 255, 255, 0.05);
border: 2px solid transparent;
border-radius: 15px;
padding: 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
text-align: left;
}
.calc-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: var(--color-primary);
transform: translateY(-2px);
}
.calc-btn.active {
background: rgba(255, 215, 0, 0.1);
border-color: var(--color-primary);
}
.calc-btn h4 {
color: var(--color-primary);
margin: 0 0 0.5rem 0;
}
.calc-btn p {
color: var(--color-light);
margin: 0;
font-size: 0.9rem;
opacity: 0.8;
}
.calc-panel {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
}
.formula-box {
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.formula {
font-family: 'Courier New', monospace;
color: var(--color-light);
margin: 1rem 0;
}
.calculation-steps {
margin: 2rem 0;
}
.step {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
align-items: flex-start;
}
.step-number {
background: var(--color-primary);
color: var(--color-dark);
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
flex-shrink: 0;
}
.step-content h5 {
color: var(--color-secondary);
margin: 0 0 0.5rem 0;
}
.step-content code {
display: block;
background: rgba(0, 0, 0, 0.3);
padding: 0.5rem 1rem;
border-radius: 8px;
margin: 0.25rem 0;
font-family: 'Courier New', monospace;
}
@media (max-width: 768px) {
.calc-selector {
grid-template-columns: 1fr;
}
.calc-panel {
padding: 1rem;
}
}
</style>
+11
View File
@@ -224,6 +224,17 @@
color: rgba(255, 255, 255, 0.7);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.timeline-items {
padding-left: 20px;
+587
View File
@@ -0,0 +1,587 @@
<script>
import { onMount } from 'svelte';
let visible = false;
let activeTab = 'cosmogonie';
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
}
});
});
const section = document.getElementById('dogon');
if (section) observer.observe(section);
return () => observer.disconnect();
});
const tabs = [
{ id: 'cosmogonie', label: 'Cosmogonie', icon: '🌌' },
{ id: 'sirius', label: 'Système de Sirius', icon: '⭐' },
{ id: 'astronomie', label: 'Astronomie Dogon', icon: '🔭' },
{ id: 'heritage', label: 'Héritage', icon: '🌍' }
];
</script>
<section id="dogon" class="dogon-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Les Dogons et Sirius</h2>
<p class="section-subtitle">
Une connaissance astronomique millénaire
</p>
</div>
<div class="content-wrapper" class:visible>
<!-- Navigation par onglets -->
<div class="tabs-nav">
{#each tabs as tab}
<button
class="tab-btn"
class:active={activeTab === tab.id}
on:click={() => activeTab = tab.id}
>
<span class="tab-icon">{tab.icon}</span>
<span class="tab-label">{tab.label}</span>
</button>
{/each}
</div>
<!-- Contenu des onglets -->
<div class="tabs-content">
{#if activeTab === 'cosmogonie'}
<div class="tab-panel">
<h3>La Cosmogonie Dogon</h3>
<div class="content-grid">
<div class="content-text">
<p>
Les Dogons du Mali possèdent une cosmogonie complexe centrée sur <strong>Sigu Tolo</strong> (Sirius),
l'étoile la plus brillante du ciel. Leur savoir astronomique, transmis oralement depuis des millénaires,
révèle une compréhension sophistiquée du cosmos.
</p>
<p>
Le <strong>Sigui</strong>, cérémonie célébrée tous les 60 ans, marque le renouvellement du monde
et commémore la révélation de la parole aux hommes. Cette périodicité correspond étrangement
à l'orbite de Sirius B autour de Sirius A (50 ans), ajustée par des considérations rituelles.
</p>
<div class="highlight-box">
<h4>Le Nommo</h4>
<p>
Être primordial amphibie venu de Sirius, le Nommo apporta la civilisation
et les connaissances fondamentales aux ancêtres des Dogons.
</p>
</div>
</div>
<div class="content-visual">
<div class="cosmogony-diagram">
<svg viewBox="0 0 400 400" class="diagram-svg">
<!-- Amma (Dieu créateur) au centre -->
<circle cx="200" cy="200" r="30" fill="var(--color-primary)" opacity="0.8"/>
<text x="200" y="205" text-anchor="middle" fill="white" font-weight="bold">Amma</text>
<!-- Oeuf cosmique -->
<ellipse cx="200" cy="200" rx="150" ry="100" fill="none" stroke="var(--color-secondary)" stroke-width="2" stroke-dasharray="5,5" opacity="0.6"/>
<!-- Les 4 éléments -->
<g class="elements">
<circle cx="200" cy="100" r="25" fill="var(--color-accent)"/>
<text x="200" y="105" text-anchor="middle" fill="white" font-size="12">Eau</text>
<circle cx="300" cy="200" r="25" fill="#FF6B35"/>
<text x="300" y="205" text-anchor="middle" fill="white" font-size="12">Feu</text>
<circle cx="200" cy="300" r="25" fill="#4CAF50"/>
<text x="200" y="305" text-anchor="middle" fill="white" font-size="12">Terre</text>
<circle cx="100" cy="200" r="25" fill="#87CEEB"/>
<text x="100" y="205" text-anchor="middle" fill="white" font-size="12">Air</text>
</g>
<!-- Lignes de connexion -->
<line x1="200" y1="230" x2="200" y2="275" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
<line x1="225" y1="200" x2="275" y2="200" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
<line x1="200" y1="175" x2="200" y2="125" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
<line x1="175" y1="200" x2="125" y2="200" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
</svg>
</div>
</div>
</div>
</div>
{/if}
{#if activeTab === 'sirius'}
<div class="tab-panel">
<h3>Le Système de Sirius selon les Dogons</h3>
<div class="sirius-system">
<div class="star-info">
<h4>Sigu Tolo (Sirius A)</h4>
<p>L'étoile principale, source de lumière et de vie</p>
<ul>
<li>Associée à la féminité et à la fécondité</li>
<li>Guide des saisons et des récoltes</li>
<li>Centre du système stellaire</li>
</ul>
</div>
<div class="star-info">
<h4>Po Tolo (Sirius B)</h4>
<p>L'étoile compagne invisible à l'œil nu</p>
<ul>
<li>Décrite comme "lourde" et "blanche"</li>
<li>Orbite de 50 ans autour de Sirius A</li>
<li>Découverte par les astronomes occidentaux en 1862</li>
</ul>
</div>
<div class="star-info">
<h4>Emme Ya Tolo (Sirius C ?)</h4>
<p>Troisième étoile hypothétique du système</p>
<ul>
<li>Quatre fois plus légère que Po Tolo</li>
<li>Orbite plus large</li>
<li>Non confirmée par l'astronomie moderne</li>
</ul>
</div>
</div>
<div class="knowledge-mystery">
<h4>Le Mystère de la Connaissance</h4>
<p>
Comment les Dogons connaissaient-ils l'existence de Sirius B,
invisible à l'œil nu et découverte en Occident seulement en 1862 ?
</p>
<div class="theories">
<div class="theory-card">
<h5>Transmission Ancienne</h5>
<p>Héritage d'une civilisation antérieure avancée</p>
</div>
<div class="theory-card">
<h5>Contact Extraterrestre</h5>
<p>Les Nommos venus de Sirius</p>
</div>
<div class="theory-card">
<h5>Observation Minutieuse</h5>
<p>Déduction à partir des perturbations de Sirius A</p>
</div>
</div>
</div>
</div>
{/if}
{#if activeTab === 'astronomie'}
<div class="tab-panel">
<h3>L'Astronomie Dogon</h3>
<div class="astronomy-content">
<div class="concept-grid">
<div class="concept-card">
<h4>📐 Géométrie Sacrée</h4>
<p>
Les Dogons utilisent des figures géométriques complexes pour
représenter les mouvements célestes et les cycles cosmiques.
</p>
</div>
<div class="concept-card">
<h4>🌙 Calendrier Lunaire</h4>
<p>
Un système calendaire sophistiqué basé sur les phases lunaires
et la position de Sirius pour l'agriculture.
</p>
</div>
<div class="concept-card">
<h4>🪐 Connaissance Planétaire</h4>
<p>
Description des anneaux de Saturne et des lunes de Jupiter,
invisibles sans télescope.
</p>
</div>
<div class="concept-card">
<h4>🌌 Structure de l'Univers</h4>
<p>
Concept d'univers infini avec des mondes multiples,
similaire aux théories modernes.
</p>
</div>
</div>
<div class="calculation-example">
<h4>Calcul de la Période Sigui</h4>
<pre><code>Période orbitale Po Tolo : 50 ans
Ajustement rituel : × 1.2
Période Sigui : 60 ans
Correspondance avec Sirius B :
- Orbite réelle : 50.1 ans
- Précision : 99.8%</code></pre>
</div>
</div>
</div>
{/if}
{#if activeTab === 'heritage'}
<div class="tab-panel">
<h3>Héritage et Influence</h3>
<div class="heritage-content">
<div class="timeline">
<div class="timeline-item">
<div class="timeline-date">Antiquité</div>
<div class="timeline-content">
<h4>Origines Égyptiennes</h4>
<p>Liens possibles avec les prêtres de l'Égypte ancienne qui vénéraient Sothis (Sirius)</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">XIIIe siècle</div>
<div class="timeline-content">
<h4>Migration vers le Mali</h4>
<p>Installation dans les falaises de Bandiagara, préservation des traditions</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">1931-1956</div>
<div class="timeline-content">
<h4>Marcel Griaule</h4>
<p>Étude ethnographique révélant les connaissances astronomiques dogons</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">Aujourd'hui</div>
<div class="timeline-content">
<h4>Patrimoine Mondial</h4>
<p>Les Dogons inspirent scientifiques et chercheurs du monde entier</p>
</div>
</div>
</div>
<div class="influence-map">
<h4>Influence sur la Caraïbe</h4>
<p>
Les traditions astronomiques africaines, incluant celles des Dogons,
ont traversé l'Atlantique avec la diaspora, enrichissant les pratiques
spirituelles et calendaires des Antilles.
</p>
<ul>
<li>🇭🇹 Haïti : Vaudou et astronomie sacrée</li>
<li>🇬🇵 Guadeloupe : Traditions Gwoka et cycles cosmiques</li>
<li>🇲🇶 Martinique : Bèlè et rythmes stellaires</li>
<li>🇯🇲 Jamaïque : Rastafari et conscience cosmique</li>
</ul>
</div>
</div>
</div>
{/if}
</div>
</div>
</div>
</section>
<style>
.dogon-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-night);
}
.content-wrapper {
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out;
}
.content-wrapper.visible {
opacity: 1;
transform: translateY(0);
}
/* Onglets */
.tabs-nav {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
justify-content: center;
}
.tab-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: rgba(255, 255, 255, 0.1);
border: 2px solid transparent;
border-radius: 50px;
color: var(--color-light);
cursor: pointer;
transition: all 0.3s ease;
}
.tab-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: var(--color-primary);
}
.tab-btn.active {
background: var(--color-primary);
color: var(--color-dark);
border-color: var(--color-primary);
}
.tab-icon {
font-size: 1.2rem;
}
/* Contenu des onglets */
.tab-panel {
animation: fadeIn 0.5s ease-out;
}
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: start;
}
.highlight-box {
background: rgba(255, 215, 0, 0.1);
border-left: 4px solid var(--color-primary);
padding: 1rem;
margin: 1.5rem 0;
border-radius: 0 8px 8px 0;
}
.highlight-box h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
/* Diagramme cosmogonie */
.cosmogony-diagram {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
}
.diagram-svg {
width: 100%;
height: auto;
}
.elements circle {
animation: pulse 3s ease-in-out infinite;
}
.elements circle:nth-child(2) { animation-delay: 0.5s; }
.elements circle:nth-child(4) { animation-delay: 1s; }
.elements circle:nth-child(6) { animation-delay: 1.5s; }
/* Système de Sirius */
.sirius-system {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.star-info {
background: rgba(255, 255, 255, 0.05);
padding: 1.5rem;
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.star-info h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
.star-info ul {
list-style: none;
padding: 0;
margin-top: 1rem;
}
.star-info li {
padding: 0.25rem 0;
padding-left: 1.5rem;
position: relative;
}
.star-info li::before {
content: "✦";
position: absolute;
left: 0;
color: var(--color-primary);
}
/* Mystère de la connaissance */
.knowledge-mystery {
background: rgba(255, 215, 0, 0.05);
padding: 2rem;
border-radius: 15px;
margin-top: 2rem;
}
.theories {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-top: 1.5rem;
}
.theory-card {
background: rgba(255, 255, 255, 0.1);
padding: 1rem;
border-radius: 10px;
text-align: center;
}
.theory-card h5 {
color: var(--color-secondary);
margin-bottom: 0.5rem;
}
/* Astronomie */
.concept-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.concept-card {
background: rgba(255, 255, 255, 0.05);
padding: 1.5rem;
border-radius: 15px;
transition: transform 0.3s ease;
}
.concept-card:hover {
transform: translateY(-5px);
}
.concept-card h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
.calculation-example {
background: rgba(0, 0, 0, 0.3);
padding: 1.5rem;
border-radius: 10px;
}
.calculation-example pre {
margin: 0;
color: var(--color-light);
font-family: 'Courier New', monospace;
}
/* Timeline */
.timeline {
position: relative;
padding-left: 3rem;
}
.timeline::before {
content: '';
position: absolute;
left: 1rem;
top: 0;
bottom: 0;
width: 2px;
background: var(--color-primary);
}
.timeline-item {
position: relative;
margin-bottom: 2rem;
}
.timeline-item::before {
content: '';
position: absolute;
left: -2.5rem;
top: 0.5rem;
width: 1rem;
height: 1rem;
background: var(--color-primary);
border-radius: 50%;
}
.timeline-date {
color: var(--color-primary);
font-weight: bold;
margin-bottom: 0.5rem;
}
.timeline-content h4 {
color: var(--color-secondary);
margin-bottom: 0.25rem;
}
/* Carte d'influence */
.influence-map {
background: rgba(255, 255, 255, 0.05);
padding: 1.5rem;
border-radius: 15px;
margin-top: 2rem;
}
.influence-map ul {
list-style: none;
padding: 0;
margin-top: 1rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 0.5rem;
}
.influence-map li {
padding: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(1.1); opacity: 1; }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.content-grid {
grid-template-columns: 1fr;
}
.tabs-nav {
gap: 0.5rem;
}
.tab-btn {
padding: 0.5rem 1rem;
font-size: 0.9rem;
}
.tab-label {
display: none;
}
.timeline {
padding-left: 2rem;
}
}
</style>
+30
View File
@@ -1,4 +1,5 @@
<script>
import { t } from '../lib/i18n.js';
const currentYear = new Date().getFullYear();
</script>
@@ -57,6 +58,14 @@
<div class="footer-bottom">
<p>&copy; {currentYear} Sirius Gwada. Levez les yeux vers le ciel.</p>
<p class="footer-credits">
{$t('footer_copyright')}
<a href="https://github.com/cyber-mawonaj" target="_blank" rel="noopener">cyber-mawonaj</a>
{$t('footer_and')}
<a href="https://o-k-i.net" target="_blank" rel="noopener">
OKI <img src="https://o-k-i.net/assets/img/logo-oki.png" alt="OKI Logo" class="oki-logo">
</a>
</p>
<p class="footer-quote">
"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain
</p>
@@ -121,6 +130,27 @@
border-top: 1px solid rgba(255, 215, 0, 0.2);
}
.footer-credits {
margin: 0.5rem 0;
color: rgba(255, 255, 255, 0.8);
}
.footer-credits a {
color: var(--color-primary);
text-decoration: none;
transition: opacity 0.3s ease;
}
.footer-credits a:hover {
opacity: 0.8;
}
.oki-logo {
height: 20px;
vertical-align: middle;
margin-left: 5px;
}
.footer-quote {
margin-top: 0.5rem;
font-style: italic;
+1 -1
View File
@@ -264,7 +264,7 @@
// Rotation automatique
if (!isDragging) {
rotation += 0.002;
rotation -= 0.002;
}
requestAnimationFrame(draw);
@@ -29,7 +29,7 @@
async function initGlobe() {
// Charger OpenGlobus depuis CDN
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/@openglobus/og@0.10.0/dist/og.umd.js';
script.src = 'https://unpkg.com/@openglobus/og@0.10.0/dist/og.umd.js';
script.onload = () => {
const { Globe, XYZ, Vector, Entity, Label } = window.og;
+271
View File
@@ -0,0 +1,271 @@
<script>
import { onMount } from 'svelte';
let container;
let visible = false;
let animationId;
let scene, camera, renderer, globe, markers;
onMount(async () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !scene) {
visible = true;
initGlobe();
}
});
});
const section = document.getElementById('threejs-section');
if (section) observer.observe(section);
return () => {
observer.disconnect();
if (animationId) {
cancelAnimationFrame(animationId);
}
if (renderer) {
renderer.dispose();
}
};
});
async function initGlobe() {
// Charger Three.js depuis CDN
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js';
script.onload = () => {
createThreeJSGlobe();
};
document.head.appendChild(script);
}
function createThreeJSGlobe() {
const THREE = window.THREE;
// Scène
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a1628);
// Caméra
camera = new THREE.PerspectiveCamera(
45,
container.clientWidth / container.clientHeight,
0.1,
1000
);
camera.position.z = 2.5;
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
// Lumières
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 3, 5);
scene.add(directionalLight);
// Globe
const geometry = new THREE.SphereGeometry(1, 64, 64);
// Texture de la Terre
const textureLoader = new THREE.TextureLoader();
const earthTexture = textureLoader.load(
'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg',
() => { renderer.render(scene, camera); }
);
const material = new THREE.MeshPhongMaterial({
map: earthTexture,
bumpScale: 0.05,
specular: new THREE.Color('grey'),
shininess: 10
});
globe = new THREE.Mesh(geometry, material);
scene.add(globe);
// Atmosphère
const atmosphereGeometry = new THREE.SphereGeometry(1.1, 64, 64);
const atmosphereMaterial = new THREE.MeshPhongMaterial({
color: 0x4444ff,
transparent: true,
opacity: 0.1,
side: THREE.BackSide
});
const atmosphere = new THREE.Mesh(atmosphereGeometry, atmosphereMaterial);
scene.add(atmosphere);
// Marqueurs
markers = new THREE.Group();
scene.add(markers);
const locations = [
{ name: 'La Réunion', lat: -21.1151, lon: 55.5364, color: 0xFF6B35 },
{ name: 'Kinshasa', lat: -4.4419, lon: 15.2663, color: 0xFFA500 },
{ name: 'Guadeloupe', lat: 16.2650, lon: -61.5510, color: 0xFFD700 },
{ name: 'Martinique', lat: 14.6415, lon: -61.0242, color: 0xFFD700 },
{ name: 'Dakar', lat: 14.7167, lon: -17.4677, color: 0xFFE44D },
{ name: 'Le Caire', lat: 30.0444, lon: 31.2357, color: 0xFFFACD }
];
locations.forEach(loc => {
const markerGeometry = new THREE.SphereGeometry(0.02, 16, 16);
const markerMaterial = new THREE.MeshBasicMaterial({ color: loc.color });
const marker = new THREE.Mesh(markerGeometry, markerMaterial);
// Convertir lat/lon en position 3D
const phi = (90 - loc.lat) * (Math.PI / 180);
const theta = (loc.lon + 180) * (Math.PI / 180);
marker.position.x = -1.05 * Math.sin(phi) * Math.cos(theta);
marker.position.y = 1.05 * Math.cos(phi);
marker.position.z = 1.05 * Math.sin(phi) * Math.sin(theta);
markers.add(marker);
});
// Contrôles de la souris
let mouseX = 0, mouseY = 0;
let isMouseDown = false;
container.addEventListener('mousedown', () => { isMouseDown = true; });
container.addEventListener('mouseup', () => { isMouseDown = false; });
container.addEventListener('mouseleave', () => { isMouseDown = false; });
container.addEventListener('mousemove', (e) => {
if (isMouseDown) {
mouseX = (e.clientX - container.offsetLeft) / container.clientWidth * 2 - 1;
mouseY = -(e.clientY - container.offsetTop) / container.clientHeight * 2 + 1;
}
});
// Animation
function animate() {
animationId = requestAnimationFrame(animate);
// Rotation automatique
if (!isMouseDown) {
globe.rotation.y += 0.002;
markers.rotation.y += 0.002;
} else {
// Rotation manuelle
globe.rotation.y += mouseX * 0.05;
globe.rotation.x = mouseY * 0.5;
markers.rotation.y = globe.rotation.y;
markers.rotation.x = globe.rotation.x;
}
renderer.render(scene, camera);
}
animate();
// Responsive
window.addEventListener('resize', () => {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
});
}
</script>
<section id="threejs-section" class="globe-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Globe avec Three.js</h2>
<p class="section-subtitle">
Rendu 3D WebGL avec textures réalistes
</p>
</div>
<div class="globe-container threejs" class:visible>
<div bind:this={container} class="threejs-container"></div>
<div class="globe-info">
<div class="info-card card">
<h3>Three.js</h3>
<p><strong>Avantages :</strong></p>
<ul>
<li>✅ Rendu 3D réaliste</li>
<li>✅ Textures haute qualité</li>
<li>✅ Effets atmosphériques</li>
<li>✅ Très bien documenté</li>
</ul>
<p><strong>Inconvénients :</strong></p>
<ul>
<li>❌ Plus lourd (~150KB)</li>
<li>❌ Nécessite WebGL</li>
<li>❌ Plus complexe à personnaliser</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<style>
.globe-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.globe-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: center;
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.globe-container.visible {
opacity: 1;
transform: translateY(0);
}
.threejs-container {
width: 100%;
height: 500px;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
cursor: grab;
}
.threejs-container:active {
cursor: grabbing;
}
.info-card h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.info-card ul {
list-style: none;
padding: 0;
margin: 0.5rem 0;
}
.info-card li {
padding: 0.25rem 0;
}
@media (max-width: 768px) {
.globe-container {
grid-template-columns: 1fr;
}
.threejs-container {
height: 400px;
}
}
</style>
+181 -141
View File
@@ -1,123 +1,132 @@
<script>
import { onMount } from 'svelte';
import { t } from '../lib/i18n.js';
let starsCanvas;
let mouseX = 0;
let mouseY = 0;
let mouseX = 0.5;
let mouseY = 0.5;
onMount(() => {
const ctx = starsCanvas.getContext('2d');
const stars = [];
const starCount = 150;
// Créer les étoiles
for (let i = 0; i < 200; i++) {
stars.push({
x: Math.random() * starsCanvas.width,
y: Math.random() * starsCanvas.height,
size: Math.random() * 2,
speed: Math.random() * 0.5 + 0.1,
brightness: Math.random()
});
}
// Animation des étoiles
const animate = () => {
ctx.clearRect(0, 0, starsCanvas.width, starsCanvas.height);
stars.forEach(star => {
// Effet parallaxe avec la souris
const offsetX = (mouseX - 0.5) * star.speed * 20;
const offsetY = (mouseY - 0.5) * star.speed * 20;
// Attendre que le DOM soit prêt
setTimeout(() => {
// Redimensionner le canvas
const resize = () => {
const hero = starsCanvas.parentElement;
starsCanvas.width = hero.offsetWidth;
starsCanvas.height = hero.offsetHeight;
ctx.beginPath();
ctx.arc(
star.x + offsetX,
star.y + offsetY,
star.size,
0,
Math.PI * 2
);
ctx.fillStyle = `rgba(255, 215, 0, ${star.brightness})`;
ctx.fill();
// Animer la brillance
star.brightness += (Math.random() - 0.5) * 0.05;
star.brightness = Math.max(0.1, Math.min(1, star.brightness));
});
// Réinitialiser les étoiles après redimensionnement
if (stars.length === 0) {
for (let i = 0; i < starCount; i++) {
stars.push({
x: Math.random() * starsCanvas.width,
y: Math.random() * starsCanvas.height,
size: Math.random() * 2,
brightness: Math.random(),
speed: 0.1 + Math.random() * 0.5
});
}
}
};
requestAnimationFrame(animate);
};
// Redimensionner le canvas
const resize = () => {
starsCanvas.width = window.innerWidth;
starsCanvas.height = window.innerHeight;
};
resize();
animate();
window.addEventListener('resize', resize);
return () => {
window.removeEventListener('resize', resize);
};
resize();
window.addEventListener('resize', resize);
// Animation des étoiles
const animate = () => {
ctx.clearRect(0, 0, starsCanvas.width, starsCanvas.height);
// Effet de parallaxe avec la souris
const offsetX = (mouseX - 0.5) * 20;
const offsetY = (mouseY - 0.5) * 20;
stars.forEach(star => {
// Mouvement subtil
star.x += Math.sin(Date.now() * 0.0001 * star.speed) * 0.5;
star.y += Math.cos(Date.now() * 0.0001 * star.speed) * 0.3;
ctx.beginPath();
ctx.arc(
star.x + offsetX,
star.y + offsetY,
star.size,
0,
Math.PI * 2
);
ctx.fillStyle = `rgba(255, 215, 0, ${star.brightness})`;
ctx.fill();
// Animer la brillance
star.brightness += (Math.random() - 0.5) * 0.05;
star.brightness = Math.max(0.1, Math.min(1, star.brightness));
});
requestAnimationFrame(animate);
};
animate();
return () => {
window.removeEventListener('resize', resize);
};
}, 100);
});
const handleMouseMove = (e) => {
mouseX = e.clientX / window.innerWidth;
mouseY = e.clientY / window.innerHeight;
const rect = e.currentTarget.getBoundingClientRect();
mouseX = (e.clientX - rect.left) / rect.width;
mouseY = (e.clientY - rect.top) / rect.height;
};
</script>
<section id="home" class="hero" on:mousemove={handleMouseMove}>
<canvas bind:this={starsCanvas} class="stars-canvas"></canvas>
<div class="hero-pattern pattern-adinkra"></div>
<div class="hero-pattern">
<!-- Patterns africains visibles -->
<div class="pattern-overlay pattern-kente"></div>
<div class="pattern-overlay pattern-adinkra"></div>
<div class="pattern-overlay pattern-bogolan"></div>
</div>
<div class="hero-content">
<div class="container">
<div class="hero-text">
<h1 class="hero-title">
<span class="title-line">Sirius</span>
<span class="title-line">en Guadeloupe</span>
<span class="title-line">{$t('hero_title1')}</span>
<span class="title-line">{$t('hero_title2')}</span>
</h1>
<p class="hero-subtitle">
Wep Ronpet - L'Ouverture de l'Année Cosmique
{$t('hero_subtitle')}
</p>
<p class="hero-description">
Le rendez-vous millénaire entre Ciel, Science et Culture
{$t('hero_description')}
</p>
<p class="hero-date">
Lever héliaque : <strong>22 Juillet 2025</strong>
{$t('hero_date')}<strong>{$t('hero_date_value')}</strong>
</p>
<div class="hero-cta">
<a href="#predictions" class="btn btn-primary">
Découvrir les Prédictions
{$t('hero_cta_predictions')}
</a>
<a href="#about" class="btn btn-outline">
En Savoir Plus
{$t('hero_cta_learn')}
</a>
</div>
</div>
<div class="hero-visual">
<div class="sirius-glow"></div>
<div class="ankh-symbol"></div>
</div>
</div>
</div>
<div class="scroll-indicator">
<span>Défiler pour explorer</span>
<div class="scroll-arrow"></div>
</div>
</section>
<style>
.hero {
position: relative;
min-height: 100vh;
background: var(--gradient-dawn);
display: flex;
align-items: center;
overflow: hidden;
@@ -130,6 +139,7 @@
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
.hero-pattern {
@@ -138,36 +148,107 @@
left: 0;
width: 100%;
height: 100%;
opacity: 0.1;
z-index: 2;
pointer-events: none;
}
.pattern-overlay {
position: absolute;
width: 100%;
height: 100%;
opacity: 0.1;
background-size: 200px 200px;
mix-blend-mode: overlay;
}
.pattern-kente {
background-image:
repeating-linear-gradient(
45deg,
var(--color-primary) 0px,
var(--color-primary) 10px,
transparent 10px,
transparent 20px,
var(--color-secondary) 20px,
var(--color-secondary) 30px,
transparent 30px,
transparent 40px
),
repeating-linear-gradient(
-45deg,
var(--color-accent) 0px,
var(--color-accent) 10px,
transparent 10px,
transparent 20px
);
animation: slidePattern 30s linear infinite;
}
.pattern-adinkra {
background-image:
radial-gradient(circle at 20% 20%, var(--color-primary) 2px, transparent 2px),
radial-gradient(circle at 80% 80%, var(--color-secondary) 2px, transparent 2px),
radial-gradient(circle at 50% 50%, var(--color-accent) 3px, transparent 3px);
background-size: 50px 50px;
animation: rotatePattern 40s linear infinite;
}
.pattern-bogolan {
background-image:
repeating-conic-gradient(
from 0deg at 50% 50%,
var(--color-primary) 0deg 10deg,
transparent 10deg 20deg
);
background-size: 100px 100px;
opacity: 0.05;
animation: pulsePattern 20s ease-in-out infinite;
}
@keyframes slidePattern {
from { transform: translateX(0); }
to { transform: translateX(200px); }
}
@keyframes rotatePattern {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulsePattern {
0%, 100% { opacity: 0.05; }
50% { opacity: 0.15; }
}
.hero-content {
position: relative;
z-index: 3;
z-index: 10;
width: 100%;
padding: var(--spacing-xl) 0;
}
.hero-content .container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
.hero-text {
max-width: 600px;
text-align: center;
margin: 0 auto;
}
.hero-title {
font-size: clamp(3rem, 10vw, 6rem);
line-height: 1;
font-size: clamp(3rem, 8vw, 5rem);
font-weight: 700;
line-height: 1.1;
margin-bottom: 1rem;
color: var(--color-light);
}
.title-line {
display: block;
animation: fadeInUp 1s ease-out;
animation-fill-mode: both;
opacity: 0;
animation: fadeInUp 1s ease-out forwards;
}
.title-line:nth-child(2) {
color: var(--color-primary);
animation-delay: 0.2s;
}
@@ -195,10 +276,16 @@
animation-fill-mode: both;
}
.hero-date strong {
color: var(--color-primary);
font-weight: 700;
}
.hero-cta {
display: flex;
gap: 1rem;
flex-wrap: wrap;
justify-content: center;
animation: fadeInUp 1s ease-out 0.8s;
animation-fill-mode: both;
}
@@ -214,44 +301,6 @@
color: var(--color-dark);
}
.hero-visual {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.sirius-glow {
position: absolute;
width: 300px;
height: 300px;
background: radial-gradient(circle, var(--color-primary) 0%, transparent 70%);
opacity: 0.5;
animation: glow 3s ease-in-out infinite;
}
.ankh-symbol {
font-size: 15rem;
color: var(--color-primary);
animation: float 6s ease-in-out infinite;
filter: drop-shadow(0 0 30px rgba(255, 215, 0, 0.5));
}
.scroll-indicator {
position: absolute;
bottom: 2rem;
left: 50%;
transform: translateX(-50%);
text-align: center;
animation: fadeInUp 1s ease-out 1s;
animation-fill-mode: both;
}
.scroll-arrow {
font-size: 2rem;
animation: bounce 2s ease-in-out infinite;
}
@keyframes fadeInUp {
from {
opacity: 0;
@@ -263,28 +312,19 @@
}
}
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(10px);
}
}
/* Mobile */
@media (max-width: 768px) {
.hero-content .container {
grid-template-columns: 1fr;
text-align: center;
}
.hero-visual {
display: none;
.hero-title {
font-size: clamp(2.5rem, 10vw, 4rem);
}
.hero-cta {
justify-content: center;
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
max-width: 300px;
}
}
</style>
</style>
+150
View File
@@ -0,0 +1,150 @@
<script>
import { currentLanguage, availableLanguages, setLanguage } from '../lib/i18n.js';
let isOpen = false;
function toggleDropdown() {
isOpen = !isOpen;
}
function selectLanguage(lang) {
setLanguage(lang);
isOpen = false;
}
// Fermer le dropdown si on clique ailleurs
function handleClickOutside(event) {
if (!event.target.closest('.language-selector')) {
isOpen = false;
}
}
</script>
<svelte:window on:click={handleClickOutside} />
<div class="language-selector">
<button class="language-button" on:click={toggleDropdown}>
<span class="flag">
{availableLanguages.find(l => l.code === $currentLanguage)?.flag}
</span>
<span class="language-name">
{availableLanguages.find(l => l.code === $currentLanguage)?.name}
</span>
<span class="arrow" class:open={isOpen}>▼</span>
</button>
{#if isOpen}
<div class="language-dropdown">
{#each availableLanguages as lang}
<button
class="language-option"
class:active={$currentLanguage === lang.code}
on:click={() => selectLanguage(lang.code)}
>
<span class="flag">{lang.flag}</span>
<span>{lang.name}</span>
</button>
{/each}
</div>
{/if}
</div>
<style>
.language-selector {
position: relative;
}
.language-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: var(--color-light);
cursor: pointer;
transition: all 0.3s ease;
}
.language-button:hover {
background: rgba(255, 255, 255, 0.15);
border-color: var(--color-primary);
}
.flag {
font-size: 1.2rem;
}
.language-name {
font-size: 0.9rem;
}
.arrow {
font-size: 0.7rem;
margin-left: 0.5rem;
transition: transform 0.3s ease;
}
.arrow.open {
transform: rotate(180deg);
}
.language-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: var(--color-dark);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 1000;
animation: slideDown 0.3s ease-out;
}
.language-option {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.75rem 1rem;
background: transparent;
border: none;
color: var(--color-light);
cursor: pointer;
transition: all 0.2s ease;
white-space: nowrap;
}
.language-option:hover {
background: rgba(255, 255, 255, 0.1);
}
.language-option.active {
background: rgba(255, 215, 0, 0.2);
color: var(--color-primary);
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.language-name {
display: none;
}
.language-button {
padding: 0.5rem;
}
}
</style>
+12 -7
View File
@@ -1,4 +1,7 @@
<script>
import { t } from '../lib/i18n.js';
import LanguageSelector from './LanguageSelector.svelte';
export let activeSection = 'home';
let isMenuOpen = false;
@@ -8,12 +11,12 @@
};
const menuItems = [
{ id: 'home', label: 'Accueil', icon: '🌟' },
{ id: 'about', label: 'Découvrir', icon: '✨' },
{ id: 'predictions', label: 'Prédictions', icon: '📅' },
{ id: 'science', label: 'Science', icon: '🔭' },
{ id: 'culture', label: 'Culture', icon: '🌍' },
{ id: 'observatory', label: 'Observer', icon: '👁️' }
{ id: 'home', label: 'nav_home', icon: '🌟' },
{ id: 'about', label: 'nav_about', icon: '✨' },
{ id: 'predictions', label: 'nav_predictions', icon: '📅' },
{ id: 'science', label: 'nav_science', icon: '🔭' },
{ id: 'culture', label: 'nav_culture', icon: '🌍' },
{ id: 'observatory', label: 'nav_observatory', icon: '👁️' }
];
const scrollToSection = (id) => {
@@ -43,11 +46,13 @@
on:click|preventDefault={() => scrollToSection(item.id)}
>
<span class="nav-icon">{item.icon}</span>
<span class="nav-label">{item.label}</span>
<span class="nav-label">{$t(item.label)}</span>
</a>
</li>
{/each}
</ul>
<LanguageSelector />
</div>
</div>
</nav>
+661
View File
@@ -0,0 +1,661 @@
<script>
import { onMount } from 'svelte';
let visible = false;
let selectedSite = null;
let mapCanvas;
let ctx;
let guadeloupeGeoJson = null;
const sites = [
{
id: 'moule',
name: 'Le Moule',
lat: 16.3333,
lng: -61.3472,
description: 'Plage de l\'Autre Bord - Vue dégagée sur l\'Est',
features: ['Plage isolée', 'Horizon Est dégagé', 'Peu de pollution lumineuse'],
bestTime: '5h00 - 5h30',
image: '🏖️'
},
{
id: 'deshaies',
name: 'Deshaies',
lat: 16.3039,
lng: -61.7966,
description: 'Plage de Grande Anse - Cadre idyllique',
features: ['Plage populaire', 'Montagnes en arrière-plan', 'Accès facile'],
bestTime: '5h15 - 5h45',
image: '🌅'
},
{
id: 'sainte-anne',
name: 'Sainte-Anne',
lat: 16.2262,
lng: -61.3809,
description: 'Plage de Bois Jolan - Tranquillité garantie',
features: ['Plage sauvage', 'Très peu fréquentée', 'Nature préservée'],
bestTime: '5h00 - 5h30',
image: '🌴'
},
{
id: 'port-louis',
name: 'Port-Louis',
lat: 16.4180,
lng: -61.5312,
description: 'Plage du Souffleur - Vue panoramique',
features: ['Vue à 180°', 'Site historique', 'Calme au petit matin'],
bestTime: '5h00 - 5h30',
image: '🏛️'
},
{
id: 'marie-galante',
name: 'Marie-Galante',
lat: 15.9500,
lng: -61.2667,
description: 'Plage de la Feuillère - Horizon infini',
features: ['Île préservée', 'Ciel très noir', 'Conditions optimales'],
bestTime: '4h45 - 5h15',
image: '🏝️'
},
{
id: 'soufriere',
name: 'Route de la Soufrière',
lat: 16.0439,
lng: -61.6639,
description: 'Parking de Savane à Mulets - Altitude',
features: ['1000m altitude', 'Air pur', 'Vue dégagée', 'Nuits fraîches'],
bestTime: '5h00 - 5h30',
image: '🌋'
},
{
id: 'petite-terre',
name: 'Petite-Terre',
lat: 16.1700,
lng: -61.1200,
description: 'Réserve naturelle - Conditions parfaites',
features: ['Aucune pollution lumineuse', 'Réserve protégée', 'Accès réglementé'],
bestTime: '4h45 - 5h15',
image: '🦎'
}
];
onMount(async () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !ctx) {
visible = true;
initMap();
}
});
});
const section = document.getElementById('observation-map');
if (section) observer.observe(section);
return () => observer.disconnect();
});
async function initMap() {
ctx = mapCanvas.getContext('2d');
// Charger les données GeoJSON de la Guadeloupe
try {
const response = await fetch('https://raw.githubusercontent.com/gregoiredavid/france-geojson/master/regions/guadeloupe/departements-guadeloupe.geojson');
guadeloupeGeoJson = await response.json();
// Redimensionner le canvas
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Dessiner la carte
drawMap();
} catch (error) {
console.error('Erreur de chargement GeoJSON:', error);
// Fallback : dessiner une carte simple
drawSimpleMap();
}
}
function resizeCanvas() {
const container = mapCanvas.parentElement;
mapCanvas.width = container.offsetWidth;
mapCanvas.height = container.offsetWidth * 0.75; // Ratio 4:3
if (guadeloupeGeoJson) drawMap();
}
function drawMap() {
if (!ctx || !guadeloupeGeoJson) return;
// Effacer le canvas
ctx.clearRect(0, 0, mapCanvas.width, mapCanvas.height);
// Fond océan
ctx.fillStyle = '#1a3a52';
ctx.fillRect(0, 0, mapCanvas.width, mapCanvas.height);
// Calculer les limites et l'échelle
const bounds = calculateBounds(guadeloupeGeoJson);
const scale = calculateScale(bounds, mapCanvas.width, mapCanvas.height);
// Dessiner chaque feature
guadeloupeGeoJson.features.forEach(feature => {
drawFeature(feature, bounds, scale);
});
// Dessiner les sites
drawSites(bounds, scale);
}
function drawSimpleMap() {
if (!ctx) return;
// Effacer le canvas
ctx.clearRect(0, 0, mapCanvas.width, mapCanvas.height);
// Fond océan
ctx.fillStyle = '#1a3a52';
ctx.fillRect(0, 0, mapCanvas.width, mapCanvas.height);
// Dessiner une forme approximative de la Guadeloupe
const centerX = mapCanvas.width / 2;
const centerY = mapCanvas.height / 2;
const scale = mapCanvas.width / 4;
// Grande-Terre
ctx.fillStyle = '#2d5a3d';
ctx.strokeStyle = '#4CAF50';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(centerX + scale * 0.5, centerY - scale * 0.2, scale * 0.4, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Basse-Terre
ctx.beginPath();
ctx.arc(centerX - scale * 0.3, centerY, scale * 0.5, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Marie-Galante
ctx.beginPath();
ctx.arc(centerX + scale * 0.2, centerY + scale * 0.8, scale * 0.25, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Dessiner les sites avec des positions approximatives
const bounds = { minLng: -62, maxLng: -61, minLat: 15.8, maxLat: 16.5 };
const simpleScale = {
x: mapCanvas.width / (bounds.maxLng - bounds.minLng),
y: mapCanvas.height / (bounds.maxLat - bounds.minLat)
};
drawSites(bounds, simpleScale);
}
function calculateBounds(geoJson) {
let minLng = Infinity, maxLng = -Infinity;
let minLat = Infinity, maxLat = -Infinity;
function processBounds(coords) {
if (Array.isArray(coords[0])) {
coords.forEach(processBounds);
} else {
minLng = Math.min(minLng, coords[0]);
maxLng = Math.max(maxLng, coords[0]);
minLat = Math.min(minLat, coords[1]);
maxLat = Math.max(maxLat, coords[1]);
}
}
geoJson.features.forEach(feature => {
if (feature.geometry && feature.geometry.coordinates) {
processBounds(feature.geometry.coordinates);
}
});
// Ajouter une marge
const margin = 0.1;
return {
minLng: minLng - margin,
maxLng: maxLng + margin,
minLat: minLat - margin,
maxLat: maxLat + margin
};
}
function calculateScale(bounds, width, height) {
return {
x: width / (bounds.maxLng - bounds.minLng),
y: height / (bounds.maxLat - bounds.minLat)
};
}
function projectPoint(lng, lat, bounds, scale) {
return {
x: (lng - bounds.minLng) * scale.x,
y: mapCanvas.height - (lat - bounds.minLat) * scale.y
};
}
function drawFeature(feature, bounds, scale) {
ctx.fillStyle = '#2d5a3d';
ctx.strokeStyle = '#4CAF50';
ctx.lineWidth = 2;
function drawCoordinates(coords) {
if (!coords || coords.length === 0) return;
if (Array.isArray(coords[0][0])) {
coords.forEach(drawCoordinates);
return;
}
ctx.beginPath();
coords.forEach((coord, i) => {
const point = projectPoint(coord[0], coord[1], bounds, scale);
if (i === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
});
ctx.closePath();
ctx.fill();
ctx.stroke();
}
if (feature.geometry && feature.geometry.coordinates) {
drawCoordinates(feature.geometry.coordinates);
}
}
function drawSites(bounds, scale) {
sites.forEach(site => {
const point = projectPoint(site.lng, site.lat, bounds, scale);
// Cercle du marqueur
ctx.beginPath();
ctx.arc(point.x, point.y, selectedSite?.id === site.id ? 15 : 10, 0, Math.PI * 2);
ctx.fillStyle = selectedSite?.id === site.id ? '#FFD700' : '#FF6B35';
ctx.fill();
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.stroke();
// Icône du site
ctx.fillStyle = 'white';
ctx.font = selectedSite?.id === site.id ? '18px sans-serif' : '14px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(site.image, point.x, point.y);
// Nom du site si sélectionné
if (selectedSite?.id === site.id) {
ctx.fillStyle = 'white';
ctx.font = 'bold 14px sans-serif';
ctx.fillText(site.name, point.x, point.y + 25);
}
});
}
function handleMapClick(event) {
const rect = mapCanvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// Vérifier si on clique sur un site
const bounds = guadeloupeGeoJson ?
calculateBounds(guadeloupeGeoJson) :
{ minLng: -62, maxLng: -61, minLat: 15.8, maxLat: 16.5 };
const scale = guadeloupeGeoJson ?
calculateScale(bounds, mapCanvas.width, mapCanvas.height) :
{ x: mapCanvas.width / (bounds.maxLng - bounds.minLng),
y: mapCanvas.height / (bounds.maxLat - bounds.minLat) };
let clickedSite = null;
sites.forEach(site => {
const point = projectPoint(site.lng, site.lat, bounds, scale);
const distance = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
if (distance < 20) {
clickedSite = site;
}
});
if (clickedSite) {
selectedSite = selectedSite?.id === clickedSite.id ? null : clickedSite;
drawMap();
}
}
function selectSite(site) {
selectedSite = selectedSite?.id === site.id ? null : site;
if (guadeloupeGeoJson) {
drawMap();
} else {
drawSimpleMap();
}
}
</script>
<section id="observation-map" class="map-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Carte des Sites d'Observation</h2>
<p class="section-subtitle">
Les meilleurs endroits en Guadeloupe pour observer le lever héliaque
</p>
</div>
<div class="map-wrapper" class:visible>
<!-- Carte Canvas de la Guadeloupe -->
<div class="map-container">
<canvas
bind:this={mapCanvas}
class="guadeloupe-map"
on:click={handleMapClick}
></canvas>
</div>
<!-- Liste des sites -->
<div class="sites-list">
<h3>Sites d'Observation</h3>
{#each sites as site}
<div
class="site-card"
class:active={selectedSite?.id === site.id}
on:click={() => selectSite(site)}
>
<div class="site-header">
<span class="site-icon">{site.image}</span>
<h4>{site.name}</h4>
</div>
<p class="site-description">{site.description}</p>
{#if selectedSite?.id === site.id}
<div class="site-details">
<div class="features">
<h5>Caractéristiques :</h5>
<ul>
{#each site.features as feature}
<li>{feature}</li>
{/each}
</ul>
</div>
<div class="observation-time">
<h5>🕐 Meilleur créneau :</h5>
<p>{site.bestTime}</p>
</div>
<div class="coordinates">
<h5>📍 Coordonnées GPS :</h5>
<p>{site.lat}°N, {Math.abs(site.lng)}°O</p>
</div>
</div>
{/if}
</div>
{/each}
</div>
</div>
<!-- Conseils d'observation -->
<div class="observation-tips" class:visible>
<h3>Conseils pour l'Observation</h3>
<div class="tips-grid">
<div class="tip-card">
<span class="tip-icon">🌙</span>
<h4>Phase Lunaire</h4>
<p>Privilégiez les nuits sans Lune ou avec un fin croissant</p>
</div>
<div class="tip-card">
<span class="tip-icon">☁️</span>
<h4>Météo</h4>
<p>Vérifiez l'absence de nuages à l'Est, surtout près de l'horizon</p>
</div>
<div class="tip-card">
<span class="tip-icon"></span>
<h4>Horaire</h4>
<p>Arrivez 30 minutes avant pour habituer vos yeux à l'obscurité</p>
</div>
<div class="tip-card">
<span class="tip-icon">🔦</span>
<h4>Équipement</h4>
<p>Lampe rouge, jumelles optionnelles, vêtements chauds</p>
</div>
</div>
</div>
</div>
</section>
<style>
.map-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-night);
}
.map-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out;
}
.map-wrapper.visible {
opacity: 1;
transform: translateY(0);
}
/* Carte Canvas */
.map-container {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
position: relative;
}
.guadeloupe-map {
width: 100%;
height: auto;
cursor: pointer;
border-radius: 10px;
}
/* Liste des sites */
.sites-list {
max-height: 600px;
overflow-y: auto;
padding-right: 1rem;
}
.sites-list h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.site-card {
background: rgba(255, 255, 255, 0.05);
border: 2px solid transparent;
border-radius: 15px;
padding: 1rem;
margin-bottom: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.site-card:hover {
background: rgba(255, 255, 255, 0.08);
border-color: var(--color-primary);
}
.site-card.active {
background: rgba(255, 215, 0, 0.1);
border-color: var(--color-primary);
}
.site-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.site-icon {
font-size: 1.5rem;
}
.site-header h4 {
color: var(--color-primary);
margin: 0;
}
.site-description {
color: var(--color-light);
margin: 0;
font-size: 0.9rem;
opacity: 0.8;
}
.site-details {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
animation: slideDown 0.3s ease-out;
}
.site-details h5 {
color: var(--color-secondary);
margin: 0.5rem 0;
font-size: 0.9rem;
}
.site-details ul {
list-style: none;
padding: 0;
margin: 0;
}
.site-details li {
padding: 0.25rem 0;
padding-left: 1rem;
position: relative;
font-size: 0.85rem;
}
.site-details li::before {
content: "•";
position: absolute;
left: 0;
color: var(--color-primary);
}
.observation-time, .coordinates {
margin-top: 0.5rem;
}
.observation-time p, .coordinates p {
margin: 0;
color: var(--color-light);
font-size: 0.9rem;
}
/* Conseils d'observation */
.observation-tips {
margin-top: var(--spacing-xl);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.2s;
}
.observation-tips.visible {
opacity: 1;
transform: translateY(0);
}
.observation-tips h3 {
color: var(--color-primary);
text-align: center;
margin-bottom: 2rem;
}
.tips-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
}
.tip-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
padding: 1.5rem;
text-align: center;
transition: transform 0.3s ease;
}
.tip-card:hover {
transform: translateY(-5px);
}
.tip-icon {
font-size: 2.5rem;
display: block;
margin-bottom: 1rem;
}
.tip-card h4 {
color: var(--color-primary);
margin: 0 0 0.5rem 0;
}
.tip-card p {
color: var(--color-light);
margin: 0;
font-size: 0.9rem;
}
/* Scrollbar personnalisée */
.sites-list::-webkit-scrollbar {
width: 8px;
}
.sites-list::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}
.sites-list::-webkit-scrollbar-thumb {
background: var(--color-primary);
border-radius: 4px;
}
@keyframes slideDown {
from {
opacity: 0;
max-height: 0;
}
to {
opacity: 1;
max-height: 300px;
}
}
@media (max-width: 768px) {
.map-wrapper {
grid-template-columns: 1fr;
}
.map-container {
padding: 1rem;
}
.sites-list {
max-height: none;
}
.tips-grid {
grid-template-columns: 1fr 1fr;
}
}
</style>
+232
View File
@@ -0,0 +1,232 @@
import { writable, derived } from 'svelte/store';
// Store pour la langue actuelle
export const currentLanguage = writable('fr');
// Traductions
const translations = {
fr: {
// Navigation
nav_home: 'Accueil',
nav_about: 'À Propos',
nav_predictions: 'Prédictions',
nav_science: 'Science',
nav_culture: 'Culture',
nav_observatory: 'Observatoire',
// Hero
hero_title1: 'Sirius',
hero_title2: 'en Guadeloupe',
hero_subtitle: 'Wep Ronpet - L\'Ouverture de l\'Année Cosmique',
hero_description: 'Le rendez-vous millénaire entre Ciel, Science et Culture',
hero_date: 'Lever héliaque : ',
hero_date_value: '22 Juillet 2025',
hero_cta_predictions: 'Découvrir les Prédictions',
hero_cta_learn: 'En Savoir Plus',
// About
about_title: 'Le Lever Héliaque de Sirius',
about_subtitle: 'Un phénomène astronomique millénaire',
about_intro: 'Le lever héliaque de Sirius est un événement céleste qui a façonné les civilisations depuis des millénaires.',
// Features
feature_astronomy_title: 'Astronomie',
feature_astronomy_desc: 'Sirius, l\'étoile la plus brillante, guide de navigation céleste',
feature_culture_title: 'Culture',
feature_culture_desc: 'De l\'Égypte aux Dogons, un héritage universel',
feature_spirituality_title: 'Spiritualité',
feature_spirituality_desc: 'Renaissance, renouveau et connexion cosmique',
// Predictions
predictions_title: 'Prédictions 2025',
predictions_subtitle: 'Calculez la date exacte pour votre lieu d\'observation',
predictions_select_location: 'Sélectionnez votre lieu',
predictions_results_for: 'Résultats pour',
predictions_heliacal_rising: 'Lever héliaque',
predictions_best_observation: 'Meilleure observation',
predictions_visibility_duration: 'Durée de visibilité',
predictions_azimuth: 'Azimut au lever',
predictions_max_altitude: 'Altitude maximale',
predictions_magnitude: 'Magnitude apparente',
// Science
science_title: 'La Science du Lever Héliaque',
science_subtitle: 'Comprendre le phénomène astronomique',
// Culture
culture_title: 'Héritage Culturel',
culture_subtitle: 'Sirius à travers les civilisations',
culture_timeline_title: 'Chronologie du Savoir',
// Observatory
observatory_title: 'Guide d\'Observation',
observatory_subtitle: 'Tout pour observer Sirius en Guadeloupe',
// Footer
footer_copyright: 'Projet réalisé avec ❤️ par',
footer_and: 'et'
},
en: {
// Navigation
nav_home: 'Home',
nav_about: 'About',
nav_predictions: 'Predictions',
nav_science: 'Science',
nav_culture: 'Culture',
nav_observatory: 'Observatory',
// Hero
hero_title1: 'Sirius',
hero_title2: 'in Guadeloupe',
hero_subtitle: 'Wep Ronpet - The Opening of the Cosmic Year',
hero_description: 'The millennial meeting between Sky, Science and Culture',
hero_date: 'Heliacal rising: ',
hero_date_value: 'July 22, 2025',
hero_cta_predictions: 'Discover Predictions',
hero_cta_learn: 'Learn More',
// About about_title: 'The Heliacal Rising of Sirius',
about_subtitle: 'A millennial astronomical phenomenon',
about_intro: 'The heliacal rising of Sirius is a celestial event that has shaped civilizations for millennia.',
// Features
feature_astronomy_title: 'Astronomy',
feature_astronomy_desc: 'Sirius, the brightest star, celestial navigation guide',
feature_culture_title: 'Culture',
feature_culture_desc: 'From Egypt to the Dogon, a universal heritage',
feature_spirituality_title: 'Spirituality',
feature_spirituality_desc: 'Renaissance, renewal and cosmic connection',
// Predictions
predictions_title: 'Predictions 2025',
predictions_subtitle: 'Calculate the exact date for your observation location',
predictions_select_location: 'Select your location',
predictions_results_for: 'Results for',
predictions_heliacal_rising: 'Heliacal rising',
predictions_best_observation: 'Best observation',
predictions_visibility_duration: 'Visibility duration',
predictions_azimuth: 'Azimuth at rising',
predictions_max_altitude: 'Maximum altitude',
predictions_magnitude: 'Apparent magnitude',
// Science
science_title: 'The Science of Heliacal Rising',
science_subtitle: 'Understanding the astronomical phenomenon',
// Culture
culture_title: 'Cultural Heritage',
culture_subtitle: 'Sirius through civilizations',
culture_timeline_title: 'Timeline of Knowledge',
// Observatory
observatory_title: 'Observation Guide',
observatory_subtitle: 'Everything to observe Sirius in Guadeloupe',
// Footer
footer_copyright: 'Project made with ❤️ by',
footer_and: 'and'
},
ht: {
// Navigation
nav_home: 'Akèy',
nav_about: 'Konsènan',
nav_predictions: 'Prediksyon',
nav_science: 'Syans',
nav_culture: 'Kilti',
nav_observatory: 'Obsèvatwa',
// Hero
hero_title1: 'Sirius',
hero_title2: 'nan Gwadloup',
hero_subtitle: 'Wep Ronpet - Ouvèti Ane Kosmik la',
hero_description: 'Randevou milenè ant Syèl, Syans ak Kilti',
hero_date: 'Leve elyak: ',
hero_date_value: '22 Jiyè 2025',
hero_cta_predictions: 'Dekouvri Prediksyon yo', hero_cta_learn: 'Aprann Plis',
// About
about_title: 'Leve Elyak Sirius la',
about_subtitle: 'Yon fenomèn astwonomik milenè',
about_intro: 'Leve elyak Sirius se yon evènman selès ki fòme sivilizasyon yo depi plizyè milye ane.',
// Features
feature_astronomy_title: 'Astwonomi',
feature_astronomy_desc: 'Sirius, zetwal ki pi klere a, gid navigasyon selès',
feature_culture_title: 'Kilti',
feature_culture_desc: 'Depi Lejip rive Dogon yo, yon eritaj inivèsèl',
feature_spirituality_title: 'Espiritwalite',
feature_spirituality_desc: 'Renesans, renouvèlman ak koneksyon kosmik',
// Predictions
predictions_title: 'Prediksyon 2025',
predictions_subtitle: 'Kalkile dat egzak pou kote w ap obsève a',
predictions_select_location: 'Chwazi kote ou ye a',
predictions_results_for: 'Rezilta pou',
predictions_heliacal_rising: 'Leve elyak',
predictions_best_observation: 'Pi bon obsèvasyon',
predictions_visibility_duration: 'Dire vizibilite',
predictions_azimuth: 'Azimit nan leve',
predictions_max_altitude: 'Altitid maksimòm',
predictions_magnitude: 'Mayitid aparan',
// Science
science_title: 'Syans Leve Elyak la',
science_subtitle: 'Konprann fenomèn astwonomik la',
// Culture
culture_title: 'Eritaj Kiltirèl',
culture_subtitle: 'Sirius atravè sivilizasyon yo',
culture_timeline_title: 'Kwonoloji Konesans lan',
// Observatory
observatory_title: 'Gid Obsèvasyon',
observatory_subtitle: 'Tout bagay pou obsève Sirius nan Gwadloup',
// Footer
footer_copyright: 'Pwojè realize avèk ❤️ pa',
footer_and: 'ak'
}
};
// Store dérivé pour obtenir les traductions de la langue actuelle
export const t = derived(
currentLanguage,
$currentLanguage => key => {
return translations[$currentLanguage]?.[key] || translations['fr'][key] || key;
}
);
// Fonction pour changer de langue
export function setLanguage(lang) {
if (translations[lang]) {
currentLanguage.set(lang);
if (typeof localStorage !== 'undefined') {
localStorage.setItem('preferred-language', lang);
}
}
}
// Initialiser avec la langue sauvegardée ou détecter la langue du navigateur
export function initLanguage() {
if (typeof localStorage !== 'undefined') {
const saved = localStorage.getItem('preferred-language');
if (saved && translations[saved]) {
currentLanguage.set(saved);
return;
}
}
if (typeof navigator !== 'undefined') {
const browserLang = navigator.language.slice(0, 2);
if (translations[browserLang]) {
currentLanguage.set(browserLang);
}
}
}
// Liste des langues disponibles
export const availableLanguages = [
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
{ code: 'en', name: 'English', flag: '🇬🇧' },
{ code: 'ht', name: 'Kreyòl Ayisyen', flag: '🇭🇹' }
];
+1
View File
@@ -3,4 +3,5 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
base: '/gwada-sirius/', // Nom de ton repository
})