commit 636a563659f9c5271f52db41cb057db2b3a83248 Author: sHa Date: Tue May 20 12:47:55 2025 +0300 Add multilingual support with Ukrainian flag and phrases - Created Ukrainian flag SVG file. - Developed index.html to include structure for language switching. - Implemented script.js for managing language phrases and automatic switching. - Styled the application with style.css for a cohesive design and responsive layout. diff --git a/README.md b/README.md new file mode 100644 index 0000000..95c816a --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# Omnia Possibilia + +A minimalist single-page site featuring the timeless phrase "Omnia possibilia tempore et opibus" (All things are possible with time and resources) displayed in multiple languages. + +## Features + +- **Multi-language Support**: The phrase is displayed in multiple languages including Latin, English, Ukrainian, German, Spanish, and French. +- **Automatic Language Rotation**: The language automatically changes every 3 minutes, with visual progress indicator. +- **Background Flag Visualization**: Each language is accompanied by a subtle background featuring the corresponding country/region flag. +- **Elegant Typography**: Uses Google's "Great Vibes" font with proper support for both Latin and Cyrillic characters. +- **Responsive Design**: Adapts smoothly to different screen sizes. +- **Interactive Controls**: Users can manually switch to the next language using the provided button. + +## Technical Implementation + +- Pure HTML, CSS, and JavaScript with no external dependencies +- SVG flags stored locally for better performance +- Smooth transitions between languages and backgrounds +- Cross-browser compatibility with special attention to Safari rendering +- Preloaded assets for seamless experience + + +## Quote Translations + +| Language | Translation | +|----------|-------------| +| Latin | Omnia possibilia tempore et opibus | +| English | All things are possible with time and resources | +| Ukrainian| Можливо все за наявності ресурсів та часу | +| German | Alles ist mit Zeit und Ressourcen möglich | +| Spanish | Todo es posible con tiempo y recursos | +| French | Tout est possible avec du temps et des ressources | + +## Development + +Developed by sHa as a simple, elegant display of a powerful philosophical concept across different cultures and languages. diff --git a/flags/english.svg b/flags/english.svg new file mode 100644 index 0000000..fc1c5bb --- /dev/null +++ b/flags/english.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/flags/french.svg b/flags/french.svg new file mode 100644 index 0000000..2aa8500 --- /dev/null +++ b/flags/french.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/flags/german.svg b/flags/german.svg new file mode 100644 index 0000000..7034e8b --- /dev/null +++ b/flags/german.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/flags/latin.svg b/flags/latin.svg new file mode 100644 index 0000000..b067c09 --- /dev/null +++ b/flags/latin.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/flags/spanish.svg b/flags/spanish.svg new file mode 100644 index 0000000..aa2d2d5 --- /dev/null +++ b/flags/spanish.svg @@ -0,0 +1,4498 @@ + +image/svg+xml diff --git a/flags/ukrainian.svg b/flags/ukrainian.svg new file mode 100644 index 0000000..fe35b38 --- /dev/null +++ b/flags/ukrainian.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..6b1523b --- /dev/null +++ b/index.html @@ -0,0 +1,36 @@ + + + + + + Omnia Possibilia + + + + + + + +
+
+
+
+
+
+
+
+
+

+
+
+ + + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..4e7d693 --- /dev/null +++ b/script.js @@ -0,0 +1,160 @@ +document.addEventListener('DOMContentLoaded', function() { + const phrases = { + 'la': { // Latin + text: 'Omnia possibilia tempore et opibus', + flag: 'flags/latin.svg', + name: 'Latin' + }, + 'en': { // English + text: 'All things are possible with time and resources', + flag: 'flags/english.svg', + name: 'English' + }, + 'uk': { // Ukrainian + text: 'Можливо все за наявності ресурсів та часу', + flag: 'flags/ukrainian.svg', + name: 'Ukrainian' + }, + 'de': { // German + text: 'Alles ist mit Zeit und Ressourcen möglich', + flag: 'flags/german.svg', + name: 'German' + }, + 'es': { // Spanish + text: 'Todo es posible con tiempo y recursos', + flag: 'flags/spanish.svg', + name: 'Spanish' + }, + 'fr': { // French + text: 'Tout est possible avec du temps et des ressources', + flag: 'flags/french.svg', + name: 'French' + } + }; + + // Preload all flag images + const preloadedFlags = {}; + Object.keys(phrases).forEach(lang => { + const img = new Image(); + img.src = phrases[lang].flag; + preloadedFlags[lang] = img; + }); + + const phraseElement = document.getElementById('phrase'); + const flagBackground1 = document.getElementById('flag-background-1'); + const flagBackground2 = document.getElementById('flag-background-2'); + const quoteContainer = document.querySelector('.quote'); + const progressLine = document.querySelector('.progress-line'); + const nextLangBtn = document.getElementById('next-lang-btn'); + const currentLangNameElement = document.getElementById('current-lang-name'); + + // Initialize the first background + flagBackground1.style.backgroundImage = `url('${phrases['la'].flag}')`; + + let currentLang = 'la'; // Start with Latin + let languages = Object.keys(phrases); + let switchInterval = 3 * 60 * 1000; // 3 minutes + let progressInterval; + + // Get browser language if available + const browserLang = navigator.language.substring(0, 2); + + // Function to animate progress bar + function startProgressBar() { + // Reset progress bar + if (progressInterval) { + clearInterval(progressInterval); + } + progressLine.style.width = '0%'; + + // Animate progress bar over switchInterval duration + const startTime = Date.now(); + progressInterval = setInterval(() => { + const elapsedTime = Date.now() - startTime; + const progress = Math.min((elapsedTime / switchInterval) * 100, 100); + progressLine.style.width = progress + '%'; + + if (progress >= 100) { + clearInterval(progressInterval); + } + }, 50); // Update every 50ms for smooth animation + } + + // Function to change the phrase and flag + function changeLanguage(lang) { + quoteContainer.classList.add('fade-out'); + + // Get current and next background elements + const currentFlag = flagBackground1.classList.contains('active') ? flagBackground1 : flagBackground2; + const nextFlag = flagBackground1.classList.contains('active') ? flagBackground2 : flagBackground1; + + // Prepare the next flag before showing it + const flagUrl = phrases[lang].flag; + nextFlag.style.backgroundImage = `url('${flagUrl}')`; + + setTimeout(() => { + // Change the text + phraseElement.textContent = phrases[lang].text; + currentLangNameElement.textContent = phrases[lang].name; + + // Swap the flags + currentFlag.classList.remove('active'); + nextFlag.classList.add('active'); + + // Reset animation + quoteContainer.classList.remove('fade-out'); + quoteContainer.classList.remove('visible'); + + // Trigger new animation after a short delay + setTimeout(() => { + quoteContainer.classList.add('visible'); + }, 50); + + currentLang = lang; + + // Restart progress bar + startProgressBar(); + }, 1000); + } + + // Function to get next language + function getNextLanguage() { + // Filter out current language + const availableLangs = languages.filter(lang => lang !== currentLang); + // Choose random language + return availableLangs[Math.floor(Math.random() * availableLangs.length)]; + } + + // Add click event to next button + nextLangBtn.addEventListener('click', function() { + const nextLang = getNextLanguage(); + changeLanguage(nextLang); + + // Reset the automatic timer when manually changing + if (window.nextLangTimeout) { + clearTimeout(window.nextLangTimeout); + } + + // Set up the next automatic change + window.nextLangTimeout = setTimeout(automaticChange, switchInterval); + }); + + // Function for automatic language change + function automaticChange() { + const nextLang = getNextLanguage(); + changeLanguage(nextLang); + window.nextLangTimeout = setTimeout(automaticChange, switchInterval); + } + + // Initial display - Latin + changeLanguage('la'); + + // After 3 minutes, switch to browser language if available, or English + window.nextLangTimeout = setTimeout(() => { + const nextLang = phrases[browserLang] ? browserLang : 'en'; + changeLanguage(nextLang); + + // Start random language rotation after first change + window.nextLangTimeout = setTimeout(automaticChange, switchInterval); + }, switchInterval); +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..3a67903 --- /dev/null +++ b/style.css @@ -0,0 +1,189 @@ +* { + margin: 0; + padding: 0; +} + + + +body { + font-family: 'Great Vibes', cursive; + height: 100vh; + width: 100%; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: #fff; + position: relative; + background-color: #000; +} + +.progress-bar { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 1px; + background-color: rgba(255, 255, 255, 0.1); + z-index: 10; +} + +.progress-line { + height: 100%; + width: 0%; + background-color: rgba(255, 255, 255, 0.7); +} + +.lang-controls { + position: absolute; + bottom: 0; + right: 10px; + display: flex; + align-items: center; + gap: 10px; + z-index: 100; + margin: 10px; +} + +#current-lang-name { + color: rgba(255, 255, 255, 0.3); + font-family: Arial, Helvetica, sans-serif; + font-size: 14px; + letter-spacing: 1px; + text-transform: lowercase; +} + +#next-lang-btn { + background: none; + border: none; + color: rgba(255, 255, 255, 0.2); + font-size: 20px; + cursor: pointer; + transition: all 0.3s ease; + outline: none; + display: flex; + justify-content: center; + align-items: center; + z-index: 100; +} + +#next-lang-btn:hover { + color: rgba(255, 255, 255, 0.9); +} + +#next-lang-btn:active { + transform: scale(0.95); +} + +.flag-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + overflow: hidden; +} + +.flag-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-size: cover !important; + background-position: center; + background-repeat: no-repeat; + opacity: 0; + transition: opacity 1s ease-in-out; + will-change: opacity; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform: translateZ(0); + transform: translateZ(0); +} + +.flag-background.active { + opacity: 0.15; +} + +.container { + flex-grow: 1; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + max-width: 800px; + padding: 20px; + text-align: center; +} + +.quote { + font-size: 3.5rem; + line-height: 1.4; + opacity: 0; + transform: translateY(20px); + word-wrap: break-word; + overflow-wrap: break-word; + font-family: 'Great Vibes', cursive; + font-weight: normal; + font-display: swap; +} + +.quote.visible { + animation: fadeInUp 1.5s forwards; +} + +.quote.fade-out { + animation: fadeOut 1s forwards; +} + +footer { + width: 100%; + padding: 15px; + text-align: center; + font-size: 0.9rem; + background-color: rgba(0, 0, 0, 0.5); + position: absolute; + bottom: 0; + font-family: Arial, Helvetica, sans-serif; + color: #888; +} + +@keyframes typeWriter { + from { + width: 0 + } + + to { + width: 100% + } +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeOut { + to { + opacity: 0; + transform: translateY(-20px); + } +} + +/* Media query for responsive design */ +@media (max-width: 768px) { + .quote { + padding: 0 20px; + } +} + +@media (max-width: 480px) { + .quote { + padding: 0 15px; + } +}