mirror of
https://github.com/shadoll/p9o.git
synced 2025-12-20 00:25:50 +00:00
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.
This commit is contained in:
36
README.md
Normal file
36
README.md
Normal file
@@ -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.
|
||||
11
flags/english.svg
Normal file
11
flags/english.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 60 30">
|
||||
<clipPath id="t">
|
||||
<path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/>
|
||||
</clipPath>
|
||||
<path d="M0,0 v30 h60 v-30 z" fill="#00247d"/>
|
||||
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
|
||||
<path d="M0,0 L60,30 M60,0 L0,30" clip-path="url(#t)" stroke="#cf142b" stroke-width="4"/>
|
||||
<path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
|
||||
<path d="M30,0 v30 M0,15 h60" stroke="#cf142b" stroke-width="6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 577 B |
6
flags/french.svg
Normal file
6
flags/french.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="533" viewBox="0 0 900 600">
|
||||
<rect width="300" height="600" fill="#002654"/>
|
||||
<rect width="300" height="600" x="300" fill="#FFFFFF"/>
|
||||
<rect width="300" height="600" x="600" fill="#ED2939"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 300 B |
6
flags/german.svg
Normal file
6
flags/german.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="480" viewBox="0 0 5 3">
|
||||
<rect width="5" height="3" fill="#000"/>
|
||||
<rect width="5" height="2" y="1" fill="#D00"/>
|
||||
<rect width="5" height="1" y="2" fill="#FFCE00"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 274 B |
5
flags/latin.svg
Normal file
5
flags/latin.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="800" viewBox="0 0 6 4">
|
||||
<rect width="6" height="4" fill="#8e001c"/>
|
||||
<rect width="3" height="4" x="3" fill="#ffb300"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
4498
flags/spanish.svg
Normal file
4498
flags/spanish.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 606 KiB |
5
flags/ukrainian.svg
Normal file
5
flags/ukrainian.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="533" viewBox="0 0 1200 800">
|
||||
<rect width="1200" height="400" fill="#0057B7"/>
|
||||
<rect width="1200" height="400" y="400" fill="#FFD700"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 245 B |
36
index.html
Normal file
36
index.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Omnia Possibilia</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Great+Vibes&subset=cyrillic,latin&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-line"></div>
|
||||
</div>
|
||||
<div class="flag-container">
|
||||
<div id="flag-background-1" class="flag-background active"></div>
|
||||
<div id="flag-background-2" class="flag-background"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="quote">
|
||||
<p id="phrase"></p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<p>Developed by sHa</p>
|
||||
<div class="lang-controls">
|
||||
<span id="current-lang-name">Latin</span>
|
||||
<button id="next-lang-btn" aria-label="Next language">⏭︎</button>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
160
script.js
Normal file
160
script.js
Normal file
@@ -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);
|
||||
});
|
||||
189
style.css
Normal file
189
style.css
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user