Skip to content

Commit e2a9e28

Browse files
authored
Merge pull request #2 from tutosrive/web
Add i18n languages
2 parents ba58047 + 2c753a8 commit e2a9e28

23 files changed

Lines changed: 2709 additions & 246 deletions

website/src/App.jsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { HashRouter as Router, Routes, Route } from 'react-router-dom'
2+
import { LanguageProvider } from './i18n/LanguageContext'
23
import Navbar from './components/Navbar'
34
import Footer from './components/Footer'
45
import Home from './pages/Home'
@@ -11,22 +12,24 @@ import './App.css'
1112

1213
function App() {
1314
return (
14-
<Router>
15-
<div className="app">
16-
<Navbar />
17-
<main className="main-content">
18-
<Routes>
19-
<Route path="/" element={<Home />} />
20-
<Route path="/about" element={<About />} />
21-
<Route path="/docs" element={<Docs />} />
22-
<Route path="/install" element={<Install />} />
23-
<Route path="/download" element={<Download />} />
24-
<Route path="/license" element={<License />} />
25-
</Routes>
26-
</main>
27-
<Footer />
28-
</div>
29-
</Router>
15+
<LanguageProvider>
16+
<Router>
17+
<div className="app">
18+
<Navbar />
19+
<main className="main-content">
20+
<Routes>
21+
<Route path="/" element={<Home />} />
22+
<Route path="/about" element={<About />} />
23+
<Route path="/docs" element={<Docs />} />
24+
<Route path="/install" element={<Install />} />
25+
<Route path="/download" element={<Download />} />
26+
<Route path="/license" element={<License />} />
27+
</Routes>
28+
</main>
29+
<Footer />
30+
</div>
31+
</Router>
32+
</LanguageProvider>
3033
)
3134
}
3235

website/src/components/Flags.jsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// SVG Flag components for language selector
2+
// Using simple, clean flag representations
3+
4+
export const FlagES419 = ({ className }) => (
5+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
6+
<rect width="32" height="24" fill="#AA151B"/>
7+
<rect y="6" width="32" height="12" fill="#F1BF00"/>
8+
</svg>
9+
)
10+
11+
export const FlagGB = ({ className }) => (
12+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
13+
<rect width="32" height="24" fill="#012169"/>
14+
<path d="M0 0L32 24M32 0L0 24" stroke="#fff" strokeWidth="4"/>
15+
<path d="M0 0L32 24M32 0L0 24" stroke="#C8102E" strokeWidth="2.5"/>
16+
<path d="M16 0V24M0 12H32" stroke="#fff" strokeWidth="6"/>
17+
<path d="M16 0V24M0 12H32" stroke="#C8102E" strokeWidth="4"/>
18+
</svg>
19+
)
20+
21+
export const FlagPT = ({ className }) => (
22+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
23+
<rect width="32" height="24" fill="#FF0000"/>
24+
<rect width="12" height="24" fill="#006600"/>
25+
<circle cx="12" cy="12" r="4" fill="#FFCC00"/>
26+
</svg>
27+
)
28+
29+
export const FlagFR = ({ className }) => (
30+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
31+
<rect width="10.67" height="24" fill="#002654"/>
32+
<rect x="10.67" width="10.67" height="24" fill="#FFFFFF"/>
33+
<rect x="21.34" width="10.67" height="24" fill="#ED2939"/>
34+
</svg>
35+
)
36+
37+
export const FlagAR = ({ className }) => (
38+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
39+
<rect width="32" height="24" fill="#006C35"/>
40+
<rect x="8" y="8" width="16" height="8" fill="#FFFFFF"/>
41+
</svg>
42+
)
43+
44+
export const FlagDE = ({ className }) => (
45+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
46+
<rect width="32" height="8" fill="#000000"/>
47+
<rect y="8" width="32" height="8" fill="#DD0000"/>
48+
<rect y="16" width="32" height="8" fill="#FFCC00"/>
49+
</svg>
50+
)
51+
52+
export const FlagRU = ({ className }) => (
53+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
54+
<rect width="32" height="8" fill="#FFFFFF"/>
55+
<rect y="8" width="32" height="8" fill="#0039A6"/>
56+
<rect y="16" width="32" height="8" fill="#D52B1E"/>
57+
</svg>
58+
)
59+
60+
export const FlagCN = ({ className }) => (
61+
<svg viewBox="0 0 32 24" className={className} aria-hidden="true">
62+
<rect width="32" height="24" fill="#DE2910"/>
63+
<g fill="#FFDE00">
64+
<polygon points="5,3 6.2,6.7 3,4.8 7,4.8 3.8,6.7"/>
65+
<polygon points="9,1 9.4,2.5 8,1.7 10,1.7 8.6,2.5"/>
66+
<polygon points="11,3 11.4,4.5 10,3.7 12,3.7 10.6,4.5"/>
67+
<polygon points="11,6 11.4,7.5 10,6.7 12,6.7 10.6,7.5"/>
68+
<polygon points="9,8 9.4,9.5 8,8.7 10,8.7 8.6,9.5"/>
69+
</g>
70+
</svg>
71+
)
72+
73+
const flags = {
74+
es419: FlagES419,
75+
gb: FlagGB,
76+
pt: FlagPT,
77+
fr: FlagFR,
78+
ar: FlagAR,
79+
de: FlagDE,
80+
ru: FlagRU,
81+
cn: FlagCN,
82+
}
83+
84+
export const Flag = ({ code, className }) => {
85+
const FlagComponent = flags[code]
86+
if (!FlagComponent) return null
87+
return <FlagComponent className={className} />
88+
}
89+
90+
export default Flag

website/src/components/Footer.jsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { Link } from 'react-router-dom'
2+
import { useLanguage } from '../i18n/useLanguage'
23
import './Footer.css'
34

45
const Footer = () => {
6+
const { t } = useLanguage()
7+
58
return (
69
<footer className="footer glass">
710
<div className="footer-container">
@@ -20,21 +23,21 @@ const Footer = () => {
2023

2124
<div className="footer-links">
2225
<div className="footer-section">
23-
<h4>Navegación</h4>
24-
<Link to="/">Inicio</Link>
25-
<Link to="/about">Acerca</Link>
26-
<Link to="/docs">Documentación</Link>
26+
<h4>{t.footer.navigation}</h4>
27+
<Link to="/">{t.footer.home}</Link>
28+
<Link to="/about">{t.footer.about}</Link>
29+
<Link to="/docs">{t.footer.documentation}</Link>
2730
</div>
2831

2932
<div className="footer-section">
30-
<h4>Recursos</h4>
31-
<Link to="/install">Instalación</Link>
32-
<Link to="/download">Descargar</Link>
33-
<Link to="/license">Licencia</Link>
33+
<h4>{t.footer.resources}</h4>
34+
<Link to="/install">{t.footer.install}</Link>
35+
<Link to="/download">{t.footer.download}</Link>
36+
<Link to="/license">{t.footer.license}</Link>
3437
</div>
3538

3639
<div className="footer-section">
37-
<h4>Enlaces</h4>
40+
<h4>{t.footer.links}</h4>
3841
<a href="https://github.com/tutosrive/E-SRM" target="_blank" rel="noopener noreferrer">GitHub</a>
3942
<a href="https://pypi.org/project/effect-srm/" target="_blank" rel="noopener noreferrer">PyPI</a>
4043
<a href="https://sourceforge.net/projects/e-srm/" target="_blank" rel="noopener noreferrer">SourceForge</a>
@@ -43,7 +46,7 @@ const Footer = () => {
4346
</div>
4447

4548
<div className="footer-bottom">
46-
<p>© 2025 Dev2Forge - ESRM - Proyecto de código abierto bajo licencia MIT</p>
49+
<p>{t.footer.copyright}</p>
4750
<div className="footer-social">
4851
<a href="https://github.com/dev2forge" target="_blank" rel="noopener noreferrer" aria-label="GitHub">
4952
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
.language-selector {
2+
position: relative;
3+
z-index: 1000;
4+
}
5+
6+
.language-selector-trigger {
7+
display: flex;
8+
align-items: center;
9+
gap: 8px;
10+
padding: 8px 12px;
11+
background: rgba(255, 255, 255, 0.05);
12+
border: 1px solid var(--glass-border);
13+
border-radius: 8px;
14+
cursor: pointer;
15+
transition: all 0.3s ease;
16+
color: var(--text-secondary);
17+
font-size: 0.875rem;
18+
font-weight: 500;
19+
}
20+
21+
.language-selector-trigger:hover {
22+
background: rgba(255, 255, 255, 0.1);
23+
color: var(--text-primary);
24+
border-color: rgba(255, 255, 255, 0.2);
25+
}
26+
27+
.language-flag {
28+
width: 20px;
29+
height: 15px;
30+
border-radius: 2px;
31+
flex-shrink: 0;
32+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
33+
}
34+
35+
.language-code {
36+
min-width: 24px;
37+
text-align: center;
38+
}
39+
40+
.language-chevron {
41+
transition: transform 0.2s ease;
42+
opacity: 0.7;
43+
}
44+
45+
.language-chevron.open {
46+
transform: rotate(180deg);
47+
}
48+
49+
.language-dropdown {
50+
position: absolute;
51+
top: calc(100% + 8px);
52+
right: 0;
53+
min-width: 200px;
54+
background: rgba(20, 20, 25, 0.98);
55+
backdrop-filter: blur(20px);
56+
border: 1px solid var(--glass-border);
57+
border-radius: 12px;
58+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
59+
padding: 8px;
60+
list-style: none;
61+
margin: 0;
62+
animation: dropdownFadeIn 0.2s ease;
63+
}
64+
65+
@keyframes dropdownFadeIn {
66+
from {
67+
opacity: 0;
68+
transform: translateY(-8px);
69+
}
70+
to {
71+
opacity: 1;
72+
transform: translateY(0);
73+
}
74+
}
75+
76+
.language-option {
77+
display: flex;
78+
align-items: center;
79+
gap: 12px;
80+
width: 100%;
81+
padding: 10px 12px;
82+
background: transparent;
83+
border: none;
84+
border-radius: 8px;
85+
cursor: pointer;
86+
transition: all 0.2s ease;
87+
color: var(--text-secondary);
88+
text-align: left;
89+
font-size: 0.875rem;
90+
}
91+
92+
.language-option:hover {
93+
background: rgba(255, 255, 255, 0.08);
94+
color: var(--text-primary);
95+
}
96+
97+
.language-option.active {
98+
background: rgba(6, 182, 212, 0.15);
99+
color: var(--primary-cyan);
100+
}
101+
102+
.language-option.active:hover {
103+
background: rgba(6, 182, 212, 0.2);
104+
}
105+
106+
.language-option .language-flag {
107+
width: 24px;
108+
height: 18px;
109+
}
110+
111+
.language-name {
112+
flex: 1;
113+
}
114+
115+
.language-check {
116+
opacity: 0.8;
117+
color: var(--primary-cyan);
118+
}
119+
120+
/* RTL Support */
121+
[dir="rtl"] .language-dropdown {
122+
right: auto;
123+
left: 0;
124+
}
125+
126+
[dir="rtl"] .language-option {
127+
text-align: right;
128+
}
129+
130+
/* Mobile adjustments */
131+
@media (max-width: 768px) {
132+
.language-selector {
133+
width: 100%;
134+
margin-top: 8px;
135+
}
136+
137+
.language-selector-trigger {
138+
width: 100%;
139+
justify-content: center;
140+
}
141+
142+
.language-dropdown {
143+
right: 50%;
144+
transform: translateX(50%);
145+
width: calc(100% - 40px);
146+
max-width: 280px;
147+
}
148+
149+
@keyframes dropdownFadeIn {
150+
from {
151+
opacity: 0;
152+
transform: translateX(50%) translateY(-8px);
153+
}
154+
to {
155+
opacity: 1;
156+
transform: translateX(50%) translateY(0);
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)