Add internationalization support with i18next and language selector component
This commit is contained in:
100
package-lock.json
generated
100
package-lock.json
generated
@@ -8,8 +8,11 @@
|
|||||||
"name": "www.cialloo.com",
|
"name": "www.cialloo.com",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"i18next": "^25.5.3",
|
||||||
|
"i18next-browser-languagedetector": "^8.2.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1"
|
"react-dom": "^19.1.1",
|
||||||
|
"react-i18next": "^16.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.36.0",
|
"@eslint/js": "^9.36.0",
|
||||||
@@ -259,6 +262,15 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.28.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
||||||
|
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.27.2",
|
"version": "7.27.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||||
@@ -1983,6 +1995,55 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-parse-stringify": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"void-elements": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next": {
|
||||||
|
"version": "25.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.5.3.tgz",
|
||||||
|
"integrity": "sha512-joFqorDeQ6YpIXni944upwnuHBf5IoPMuqAchGVeQLdWC2JOjxgM9V8UGLhNIIH/Q8QleRxIi0BSRQehSrDLcg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://locize.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://locize.com/i18next.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next-browser-languagedetector": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||||
@@ -2716,6 +2777,32 @@
|
|||||||
"react": "^19.1.1"
|
"react": "^19.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-i18next": {
|
||||||
|
"version": "16.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.0.0.tgz",
|
||||||
|
"integrity": "sha512-JQ+dFfLnFSKJQt7W01lJHWRC0SX7eDPobI+MSTJ3/gP39xH2g33AuTE7iddAfXYHamJdAeMGM0VFboPaD3G68Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.6",
|
||||||
|
"html-parse-stringify": "^3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"i18next": ">= 25.5.2",
|
||||||
|
"react": ">= 16.8.0",
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.17.0",
|
"version": "0.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||||
@@ -2986,7 +3073,7 @@
|
|||||||
"version": "5.8.3",
|
"version": "5.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -3169,6 +3256,15 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/void-elements": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -10,8 +10,11 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"i18next": "^25.5.3",
|
||||||
|
"i18next-browser-languagedetector": "^8.2.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1"
|
"react-dom": "^19.1.1",
|
||||||
|
"react-i18next": "^16.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.36.0",
|
"@eslint/js": "^9.36.0",
|
||||||
|
|||||||
107
src/App.tsx
107
src/App.tsx
@@ -1,6 +1,9 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import LanguageSelector from './components/LanguageSelector'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
// Mock data - in real app, this would come from API
|
// Mock data - in real app, this would come from API
|
||||||
const stats = {
|
const stats = {
|
||||||
@@ -24,14 +27,15 @@ function App() {
|
|||||||
<nav className="navbar">
|
<nav className="navbar">
|
||||||
<div className="nav-container">
|
<div className="nav-container">
|
||||||
<div className="nav-logo">
|
<div className="nav-logo">
|
||||||
<h2>🎯 CS Community</h2>
|
<h2>🎯 {t('nav.logo')}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-links">
|
<div className="nav-links">
|
||||||
<a href="#servers">Servers</a>
|
<a href="#servers">{t('nav.servers')}</a>
|
||||||
<a href="#blog">Blog</a>
|
<a href="#blog">{t('nav.blog')}</a>
|
||||||
<a href="#git">Git</a>
|
<a href="#git">{t('nav.git')}</a>
|
||||||
<a href="#forum">Forum</a>
|
<a href="#forum">{t('nav.forum')}</a>
|
||||||
<button className="join-btn">Join Now</button>
|
<LanguageSelector />
|
||||||
|
<button className="join-btn">{t('nav.joinNow')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -40,16 +44,15 @@ function App() {
|
|||||||
<section className="hero">
|
<section className="hero">
|
||||||
<div className="hero-content">
|
<div className="hero-content">
|
||||||
<h1 className="hero-title">
|
<h1 className="hero-title">
|
||||||
Welcome to the Ultimate<br />
|
{t('hero.title')}<br />
|
||||||
<span className="highlight">Counter-Strike</span> Community
|
<span className="highlight">{t('hero.titleHighlight')}</span> {t('hero.titleEnd')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="hero-subtitle">
|
<p className="hero-subtitle">
|
||||||
Join thousands of players in competitive matches, casual games, and community events.
|
{t('hero.subtitle')}
|
||||||
Experience the best gaming community with dedicated servers and passionate players.
|
|
||||||
</p>
|
</p>
|
||||||
<div className="hero-buttons">
|
<div className="hero-buttons">
|
||||||
<button className="btn-primary">🎮 Start Playing</button>
|
<button className="btn-primary">{t('hero.startPlaying')}</button>
|
||||||
<button className="btn-secondary">📊 View Stats</button>
|
<button className="btn-secondary">{t('hero.viewStats')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-visual">
|
<div className="hero-visual">
|
||||||
@@ -71,22 +74,22 @@ function App() {
|
|||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon">👥</div>
|
<div className="stat-icon">👥</div>
|
||||||
<div className="stat-number">{stats.onlinePlayers.toLocaleString()}</div>
|
<div className="stat-number">{stats.onlinePlayers.toLocaleString()}</div>
|
||||||
<div className="stat-label">Online Players</div>
|
<div className="stat-label">{t('stats.onlinePlayers')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon">🖥️</div>
|
<div className="stat-icon">🖥️</div>
|
||||||
<div className="stat-number">{stats.totalServers}</div>
|
<div className="stat-number">{stats.totalServers}</div>
|
||||||
<div className="stat-label">Active Servers</div>
|
<div className="stat-label">{t('stats.activeServers')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon">⏱️</div>
|
<div className="stat-icon">⏱️</div>
|
||||||
<div className="stat-number">{stats.totalPlayTime}</div>
|
<div className="stat-number">{stats.totalPlayTime}</div>
|
||||||
<div className="stat-label">Hours Played</div>
|
<div className="stat-label">{t('stats.hoursPlayed')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon">🎯</div>
|
<div className="stat-icon">🎯</div>
|
||||||
<div className="stat-number">{stats.activeGames}</div>
|
<div className="stat-number">{stats.activeGames}</div>
|
||||||
<div className="stat-label">Live Matches</div>
|
<div className="stat-label">{t('stats.liveMatches')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -94,31 +97,31 @@ function App() {
|
|||||||
{/* Features Section */}
|
{/* Features Section */}
|
||||||
<section className="features-section">
|
<section className="features-section">
|
||||||
<div className="features-container">
|
<div className="features-container">
|
||||||
<h2 className="section-title">Community Features</h2>
|
<h2 className="section-title">{t('features.title')}</h2>
|
||||||
<div className="features-grid">
|
<div className="features-grid">
|
||||||
<div className="feature-card" id="servers">
|
<div className="feature-card" id="servers">
|
||||||
<div className="feature-icon">🖥️</div>
|
<div className="feature-icon">🖥️</div>
|
||||||
<h3>Server Browser</h3>
|
<h3>{t('features.serverBrowser.title')}</h3>
|
||||||
<p>Find and join the best Counter-Strike servers. Browse by game mode, region, and player count.</p>
|
<p>{t('features.serverBrowser.description')}</p>
|
||||||
<button className="feature-btn">Browse Servers</button>
|
<button className="feature-btn">{t('features.browseServers')}</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="feature-card" id="blog">
|
<div className="feature-card" id="blog">
|
||||||
<div className="feature-icon">📝</div>
|
<div className="feature-icon">📝</div>
|
||||||
<h3>Community Blog</h3>
|
<h3>{t('features.blog.title')}</h3>
|
||||||
<p>Stay updated with the latest news, tournament results, and community announcements.</p>
|
<p>{t('features.blog.description')}</p>
|
||||||
<button className="feature-btn">Read Blog</button>
|
<button className="feature-btn">{t('features.readBlog')}</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="feature-card" id="git">
|
<div className="feature-card" id="git">
|
||||||
<div className="feature-icon">📦</div>
|
<div className="feature-icon">📦</div>
|
||||||
<h3>Open Source</h3>
|
<h3>{t('features.git.title')}</h3>
|
||||||
<p>Contribute to our open-source projects. Custom maps, configs, and community tools.</p>
|
<p>{t('features.git.description')}</p>
|
||||||
<button className="feature-btn">View GitHub</button>
|
<button className="feature-btn">{t('features.viewGitHub')}</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="feature-card" id="forum">
|
<div className="feature-card" id="forum">
|
||||||
<div className="feature-icon">💬</div>
|
<div className="feature-icon">💬</div>
|
||||||
<h3>Discussion Forum</h3>
|
<h3>{t('features.forum.title')}</h3>
|
||||||
<p>Join discussions about strategies, share your experiences, and connect with fellow gamers.</p>
|
<p>{t('features.forum.description')}</p>
|
||||||
<button className="feature-btn">Join Forum</button>
|
<button className="feature-btn">{t('features.joinForum')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,7 +131,7 @@ function App() {
|
|||||||
<section className="activity-section">
|
<section className="activity-section">
|
||||||
<div className="activity-container">
|
<div className="activity-container">
|
||||||
<div className="activity-main">
|
<div className="activity-main">
|
||||||
<h2 className="section-title">Recent Activity</h2>
|
<h2 className="section-title">{t('activity.title')}</h2>
|
||||||
<div className="chat-feed">
|
<div className="chat-feed">
|
||||||
{recentChats.map((chat, index) => (
|
{recentChats.map((chat, index) => (
|
||||||
<div key={index} className="chat-message">
|
<div key={index} className="chat-message">
|
||||||
@@ -141,7 +144,7 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="activity-sidebar">
|
<div className="activity-sidebar">
|
||||||
<div className="sidebar-card">
|
<div className="sidebar-card">
|
||||||
<h3>🏆 Top Players</h3>
|
<h3>{t('activity.topPlayers')}</h3>
|
||||||
<div className="leaderboard">
|
<div className="leaderboard">
|
||||||
<div className="leader-item">1. ProGamer99 - 2,450 pts</div>
|
<div className="leader-item">1. ProGamer99 - 2,450 pts</div>
|
||||||
<div className="leader-item">2. SniperElite - 2,180 pts</div>
|
<div className="leader-item">2. SniperElite - 2,180 pts</div>
|
||||||
@@ -149,11 +152,11 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sidebar-card">
|
<div className="sidebar-card">
|
||||||
<h3>🎯 Quick Stats</h3>
|
<h3>{t('activity.quickStats')}</h3>
|
||||||
<div className="quick-stats">
|
<div className="quick-stats">
|
||||||
<div>Avg Match Time: 12m 34s</div>
|
<div>{t('activity.avgMatchTime')}</div>
|
||||||
<div>Most Popular Map: Dust2</div>
|
<div>{t('activity.mostPopularMap')}</div>
|
||||||
<div>Peak Hours: 8-11 PM</div>
|
<div>{t('activity.peakHours')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,32 +167,32 @@ function App() {
|
|||||||
<footer className="footer">
|
<footer className="footer">
|
||||||
<div className="footer-container">
|
<div className="footer-container">
|
||||||
<div className="footer-section">
|
<div className="footer-section">
|
||||||
<h4>🎯 CS Community</h4>
|
<h4>{t('footer.community')}</h4>
|
||||||
<p>The ultimate destination for Counter-Strike enthusiasts worldwide.</p>
|
<p>{t('footer.communityDesc')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer-section">
|
<div className="footer-section">
|
||||||
<h4>Quick Links</h4>
|
<h4>{t('footer.quickLinks')}</h4>
|
||||||
<a href="#servers">Servers</a>
|
<a href="#servers">{t('nav.servers')}</a>
|
||||||
<a href="#blog">Blog</a>
|
<a href="#blog">{t('nav.blog')}</a>
|
||||||
<a href="#forum">Forum</a>
|
<a href="#forum">{t('nav.forum')}</a>
|
||||||
<a href="#git">GitHub</a>
|
<a href="#git">{t('nav.git')}</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer-section">
|
<div className="footer-section">
|
||||||
<h4>Community</h4>
|
<h4>{t('footer.communityLinks')}</h4>
|
||||||
<a href="#">Discord</a>
|
<a href="#">{t('footer.discord')}</a>
|
||||||
<a href="#">Steam Group</a>
|
<a href="#">{t('footer.steamGroup')}</a>
|
||||||
<a href="#">Tournaments</a>
|
<a href="#">{t('footer.tournaments')}</a>
|
||||||
<a href="#">Support</a>
|
<a href="#">{t('footer.support')}</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer-section">
|
<div className="footer-section">
|
||||||
<h4>Legal</h4>
|
<h4>{t('footer.legal')}</h4>
|
||||||
<a href="#">Privacy Policy</a>
|
<a href="#">{t('footer.privacyPolicy')}</a>
|
||||||
<a href="#">Terms of Service</a>
|
<a href="#">{t('footer.termsOfService')}</a>
|
||||||
<a href="#">Contact</a>
|
<a href="#">{t('footer.contact')}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer-bottom">
|
<div className="footer-bottom">
|
||||||
<p>© 2025 CS Community. All rights reserved. | Made with ❤️ for gamers</p>
|
<p>{t('footer.copyright')}</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
35
src/components/LanguageSelector.css
Normal file
35
src/components/LanguageSelector.css
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
.language-selector {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-dropdown {
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
border: 1px solid var(--primary-red);
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 100px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-dropdown:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-red-hover);
|
||||||
|
box-shadow: 0 0 0 2px rgba(255, 70, 85, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-dropdown option {
|
||||||
|
background: var(--dark-bg);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.language-dropdown {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/components/LanguageSelector.tsx
Normal file
33
src/components/LanguageSelector.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import './LanguageSelector.css'
|
||||||
|
|
||||||
|
function LanguageSelector() {
|
||||||
|
const { i18n, t } = useTranslation()
|
||||||
|
|
||||||
|
const changeLanguage = (lng: string) => {
|
||||||
|
i18n.changeLanguage(lng)
|
||||||
|
}
|
||||||
|
|
||||||
|
const languages = [
|
||||||
|
{ code: 'en', name: t('languages.english') },
|
||||||
|
{ code: 'zh', name: t('languages.chinese') }
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="language-selector">
|
||||||
|
<select
|
||||||
|
value={i18n.language}
|
||||||
|
onChange={(e) => changeLanguage(e.target.value)}
|
||||||
|
className="language-dropdown"
|
||||||
|
>
|
||||||
|
{languages.map((lang) => (
|
||||||
|
<option key={lang.code} value={lang.code}>
|
||||||
|
{lang.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LanguageSelector
|
||||||
36
src/i18n.ts
Normal file
36
src/i18n.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import i18n from 'i18next'
|
||||||
|
import { initReactI18next } from 'react-i18next'
|
||||||
|
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||||
|
|
||||||
|
import en from './locales/en.json'
|
||||||
|
import zh from './locales/zh.json'
|
||||||
|
|
||||||
|
const resources = {
|
||||||
|
en: {
|
||||||
|
translation: en
|
||||||
|
},
|
||||||
|
zh: {
|
||||||
|
translation: zh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(LanguageDetector) // Detect user language
|
||||||
|
.use(initReactI18next) // Pass i18n down to react-i18next
|
||||||
|
.init({
|
||||||
|
resources,
|
||||||
|
fallbackLng: 'en', // Fallback to English if detection fails
|
||||||
|
debug: false,
|
||||||
|
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false // React already does escaping
|
||||||
|
},
|
||||||
|
|
||||||
|
detection: {
|
||||||
|
order: ['localStorage', 'navigator', 'htmlTag'],
|
||||||
|
lookupLocalStorage: 'language',
|
||||||
|
caches: ['localStorage']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default i18n
|
||||||
74
src/locales/en.json
Normal file
74
src/locales/en.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"logo": "CS Community",
|
||||||
|
"servers": "Servers",
|
||||||
|
"blog": "Blog",
|
||||||
|
"git": "Git",
|
||||||
|
"forum": "Forum",
|
||||||
|
"joinNow": "Join Now"
|
||||||
|
},
|
||||||
|
"hero": {
|
||||||
|
"title": "Welcome to the Ultimate",
|
||||||
|
"titleHighlight": "Counter-Strike",
|
||||||
|
"titleEnd": "Community",
|
||||||
|
"subtitle": "Join thousands of players in competitive matches, casual games, and community events. Experience the best gaming community with dedicated servers and passionate players.",
|
||||||
|
"startPlaying": "🎮 Start Playing",
|
||||||
|
"viewStats": "📊 View Stats"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"onlinePlayers": "Online Players",
|
||||||
|
"activeServers": "Active Servers",
|
||||||
|
"hoursPlayed": "Hours Played",
|
||||||
|
"liveMatches": "Live Matches"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"title": "Community Features",
|
||||||
|
"serverBrowser": {
|
||||||
|
"title": "Server Browser",
|
||||||
|
"description": "Find and join the best Counter-Strike servers. Browse by game mode, region, and player count."
|
||||||
|
},
|
||||||
|
"blog": {
|
||||||
|
"title": "Community Blog",
|
||||||
|
"description": "Stay updated with the latest news, tournament results, and community announcements."
|
||||||
|
},
|
||||||
|
"git": {
|
||||||
|
"title": "Open Source",
|
||||||
|
"description": "Contribute to our open-source projects. Custom maps, configs, and community tools."
|
||||||
|
},
|
||||||
|
"forum": {
|
||||||
|
"title": "Discussion Forum",
|
||||||
|
"description": "Join discussions about strategies, share your experiences, and connect with fellow gamers."
|
||||||
|
},
|
||||||
|
"browseServers": "Browse Servers",
|
||||||
|
"readBlog": "Read Blog",
|
||||||
|
"viewGitHub": "View GitHub",
|
||||||
|
"joinForum": "Join Forum"
|
||||||
|
},
|
||||||
|
"activity": {
|
||||||
|
"title": "Recent Activity",
|
||||||
|
"topPlayers": "🏆 Top Players",
|
||||||
|
"quickStats": "🎯 Quick Stats",
|
||||||
|
"avgMatchTime": "Avg Match Time: 12m 34s",
|
||||||
|
"mostPopularMap": "Most Popular Map: Dust2",
|
||||||
|
"peakHours": "Peak Hours: 8-11 PM"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"community": "🎯 CS Community",
|
||||||
|
"communityDesc": "The ultimate destination for Counter-Strike enthusiasts worldwide.",
|
||||||
|
"quickLinks": "Quick Links",
|
||||||
|
"communityLinks": "Community",
|
||||||
|
"legal": "Legal",
|
||||||
|
"discord": "Discord",
|
||||||
|
"steamGroup": "Steam Group",
|
||||||
|
"tournaments": "Tournaments",
|
||||||
|
"support": "Support",
|
||||||
|
"privacyPolicy": "Privacy Policy",
|
||||||
|
"termsOfService": "Terms of Service",
|
||||||
|
"contact": "Contact",
|
||||||
|
"copyright": "© 2025 CS Community. All rights reserved. | Made with ❤️ for gamers"
|
||||||
|
},
|
||||||
|
"languages": {
|
||||||
|
"english": "English",
|
||||||
|
"chinese": "中文"
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/locales/zh.json
Normal file
74
src/locales/zh.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"logo": "CS 社区",
|
||||||
|
"servers": "服务器",
|
||||||
|
"blog": "博客",
|
||||||
|
"git": "开源",
|
||||||
|
"forum": "论坛",
|
||||||
|
"joinNow": "立即加入"
|
||||||
|
},
|
||||||
|
"hero": {
|
||||||
|
"title": "欢迎来到终极",
|
||||||
|
"titleHighlight": "反恐精英",
|
||||||
|
"titleEnd": "社区",
|
||||||
|
"subtitle": "加入数千名玩家参与竞技比赛、休闲游戏和社区活动。在专用服务器和热情玩家中体验最佳游戏社区。",
|
||||||
|
"startPlaying": "🎮 开始游戏",
|
||||||
|
"viewStats": "📊 查看统计"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"onlinePlayers": "在线玩家",
|
||||||
|
"activeServers": "活跃服务器",
|
||||||
|
"hoursPlayed": "游戏时长",
|
||||||
|
"liveMatches": "实时比赛"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"title": "社区功能",
|
||||||
|
"serverBrowser": {
|
||||||
|
"title": "服务器浏览器",
|
||||||
|
"description": "查找并加入最佳反恐精英服务器。按游戏模式、地区和玩家数量浏览。"
|
||||||
|
},
|
||||||
|
"blog": {
|
||||||
|
"title": "社区博客",
|
||||||
|
"description": "及时了解最新新闻、锦标赛结果和社区公告。"
|
||||||
|
},
|
||||||
|
"git": {
|
||||||
|
"title": "开源项目",
|
||||||
|
"description": "为我们的开源项目做出贡献。自定义地图、配置和社区工具。"
|
||||||
|
},
|
||||||
|
"forum": {
|
||||||
|
"title": "讨论论坛",
|
||||||
|
"description": "加入关于策略的讨论,分享您的经验,与其他玩家交流。"
|
||||||
|
},
|
||||||
|
"browseServers": "浏览服务器",
|
||||||
|
"readBlog": "阅读博客",
|
||||||
|
"viewGitHub": "查看 GitHub",
|
||||||
|
"joinForum": "加入论坛"
|
||||||
|
},
|
||||||
|
"activity": {
|
||||||
|
"title": "最近活动",
|
||||||
|
"topPlayers": "🏆 顶级玩家",
|
||||||
|
"quickStats": "🎯 快速统计",
|
||||||
|
"avgMatchTime": "平均比赛时长:12分34秒",
|
||||||
|
"mostPopularMap": "最受欢迎地图:Dust2",
|
||||||
|
"peakHours": "高峰时段:晚上8-11点"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"community": "🎯 CS 社区",
|
||||||
|
"communityDesc": "反恐精英爱好者的终极目的地。",
|
||||||
|
"quickLinks": "快速链接",
|
||||||
|
"communityLinks": "社区",
|
||||||
|
"legal": "法律",
|
||||||
|
"discord": "Discord",
|
||||||
|
"steamGroup": "Steam 群组",
|
||||||
|
"tournaments": "锦标赛",
|
||||||
|
"support": "支持",
|
||||||
|
"privacyPolicy": "隐私政策",
|
||||||
|
"termsOfService": "服务条款",
|
||||||
|
"contact": "联系我们",
|
||||||
|
"copyright": "© 2025 CS 社区。保留所有权利。| 用 ❤️ 为玩家打造"
|
||||||
|
},
|
||||||
|
"languages": {
|
||||||
|
"english": "English",
|
||||||
|
"chinese": "中文"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
import './i18n'
|
||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
|||||||
Reference in New Issue
Block a user