多语言个人主页网站源码(PHP+MySQL)


多语言个人主页网站(PHP+MySQL)
我将为您的个人主页网站添加维语和汉语双语支持,并实现语言切换功能。
1. 数据库结构更新
我们需要添加语言相关的表:
“`sql
— 语言表
CREATE TABLE languages (
id INT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(10) UNIQUE NOT NULL, — 语言代码,如:zh, ug
name VARCHAR(50) NOT NULL, — 语言名称
native_name VARCHAR(50), — 本地语言名称
direction ENUM(‘ltr’, ‘rtl’) DEFAULT ‘ltr’,
is_default BOOLEAN DEFAULT FALSE,
is_active BOOLEAN DEFAULT TRUE
);
— 多语言翻译表
CREATE TABLE translations (
id INT PRIMARY KEY AUTO_INCREMENT,
language_code VARCHAR(10) NOT NULL,
key_name VARCHAR(255) NOT NULL, — 翻译键名
translation TEXT, — 翻译文本
context VARCHAR(100), — 上下文(用于分组)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (language_code) REFERENCES languages(code) ON DELETE CASCADE,
INDEX idx_key_language (key_name, language_code)
);
— 多语言内容表 – 扩展现有表
ALTER TABLE profile ADD COLUMN bio_ug TEXT AFTER bio;
ALTER TABLE profile ADD COLUMN full_name_ug VARCHAR(100) AFTER full_name;
ALTER TABLE profile ADD COLUMN job_title_ug VARCHAR(100) AFTER job_title;
ALTER TABLE skills ADD COLUMN skill_name_ug VARCHAR(100) AFTER skill_name;
ALTER TABLE projects ADD COLUMN title_ug VARCHAR(200) AFTER title;
ALTER TABLE projects ADD COLUMN description_ug TEXT AFTER description;
ALTER TABLE projects ADD COLUMN technologies_ug VARCHAR(255) AFTER technologies;
ALTER TABLE posts ADD COLUMN title_ug VARCHAR(200) AFTER title;
ALTER TABLE posts ADD COLUMN content_ug TEXT AFTER content;
ALTER TABLE posts ADD COLUMN excerpt_ug TEXT AFTER excerpt;
— 插入默认语言数据
INSERT INTO languages (code, name, native_name, direction, is_default, is_active) VALUES
(‘zh’, ‘简体中文’, ‘简体中文’, ‘ltr’, TRUE, TRUE),
(‘ug’, ‘维吾尔语’, ‘ئۇيغۇرچە’, ‘rtl’, FALSE, TRUE);
— 插入常用翻译(中文)
INSERT INTO translations (language_code, key_name, translation, context) VALUES
(‘zh’, ‘home’, ‘首页’, ‘menu’),
(‘zh’, ‘skills’, ‘技能’, ‘menu’),
(‘zh’, ‘projects’, ‘项目’, ‘menu’),
(‘zh’, ‘blog’, ‘博客’, ‘menu’),
(‘zh’, ‘contact’, ‘联系’, ‘menu’),
(‘zh’, ‘view_all_projects’, ‘查看所有项目’, ‘general’),
(‘zh’, ‘read_more’, ‘阅读更多’, ‘general’),
(‘zh’, ‘contact_me’, ‘联系我’, ‘general’),
(‘zh’, ‘recent_posts’, ‘最新文章’, ‘general’),
(‘zh’, ‘professional_skills’, ‘专业技能’, ‘general’),
(‘zh’, ‘project_showcase’, ‘项目展示’, ‘general’),
(‘zh’, ‘view_project’, ‘查看项目’, ‘general’),
(‘zh’, ’email’, ‘邮箱’, ‘general’),
(‘zh’, ‘phone’, ‘电话’, ‘general’),
(‘zh’, ‘location’, ‘地点’, ‘general’),
(‘zh’, ‘all_rights_reserved’, ‘保留所有权利’, ‘footer’),
(‘zh’, ‘switch_language’, ‘切换语言’, ‘general’),
(‘zh’, ‘download_cv’, ‘下载简历’, ‘general’),
(‘zh’, ‘years_experience’, ‘年经验’, ‘general’),
(‘zh’, ‘happy_clients’, ‘满意客户’, ‘general’),
(‘zh’, ‘projects_completed’, ‘完成项目’, ‘general’),
(‘zh’, ‘awards_won’, ‘获得奖项’, ‘general’);
— 插入常用翻译(维语)
INSERT INTO translations (language_code, key_name, translation, context) VALUES
(‘ug’, ‘home’, ‘باش بەت’, ‘menu’),
(‘ug’, ‘skills’, ‘ماھارەت’, ‘menu’),
(‘ug’, ‘projects’, ‘پروژىلەر’, ‘menu’),
(‘ug’, ‘blog’, ‘بىلوگ’, ‘menu’),
(‘ug’, ‘contact’, ‘ئالاقىلەشىش’, ‘menu’),
(‘ug’, ‘view_all_projects’, ‘بارلىق پروژىلەرنى كۆرۈش’, ‘general’),
(‘ug’, ‘read_more’, ‘تېخىمۇ كۆپ ئوقۇڭ’, ‘general’),
(‘ug’, ‘contact_me’, ‘مەن بىلەن ئالاقىلەشىڭ’, ‘general’),
(‘ug’, ‘recent_posts’, ‘ئەڭ يېڭى يازما’, ‘general’),
(‘ug’, ‘professional_skills’, ‘كەسپىي ماھارەت’, ‘general’),
(‘ug’, ‘project_showcase’, ‘پروژە كۆرسىتىش’, ‘general’),
(‘ug’, ‘view_project’, ‘پروژىنى كۆرۈش’, ‘general’),
(‘ug’, ’email’, ‘ئېلخەت’, ‘general’),
(‘ug’, ‘phone’, ‘تېلېفون’, ‘general’),
(‘ug’, ‘location’, ‘ئورنى’, ‘general’),
(‘ug’, ‘all_rights_reserved’, ‘ھەممە ھوقۇق ساقلانغان’, ‘footer’),
(‘ug’, ‘switch_language’, ‘تىل ئالماشتۇرۇش’, ‘general’),
(‘ug’, ‘download_cv’, ‘CV چۈشۈرۈش’, ‘general’),
(‘ug’, ‘years_experience’, ‘يىل تەجرىبە’, ‘general’),
(‘ug’, ‘happy_clients’, ‘رىزايەت قىلغان خېرىدار’, ‘general’),
(‘ug’, ‘projects_completed’, ‘تاماملانغان پروژە’, ‘general’),
(‘ug’, ‘awards_won’, ‘قولغا كەلتۈرگەن مۇكاپات’, ‘general’);
“`
2. 语言管理功能
includes/language.php
“`php
<?php
// 语言管理类
class LanguageManager {
private static $instance = null;
private $currentLang = ‘zh’;
private $availableLangs = [];
private $translations = [];
private function __construct() {
$this->loadLanguages();
$this->detectLanguage();
$this->loadTranslations();
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
private function loadLanguages() {
$conn = getDBConnection();
$stmt = $conn->prepare(“SELECT * FROM languages WHERE is_active = 1 ORDER BY is_default DESC”);
$stmt->execute();
$this->availableLangs = $stmt->fetchAll();
}
private function detectLanguage() {
// 1. 检查URL参数
if (isset($_GET[‘lang’]) && $this->isValidLanguage($_GET[‘lang’])) {
$this->currentLang = $_GET[‘lang’];
$_SESSION[‘user_lang’] = $this->currentLang;
}
// 2. 检查会话
elseif (isset($_SESSION[‘user_lang’]) && $this->isValidLanguage($_SESSION[‘user_lang’])) {
$this->currentLang = $_SESSION[‘user_lang’];
}
// 3. 检查浏览器语言
elseif (isset($_SERVER[‘HTTP_ACCEPT_LANGUAGE’])) {
$browserLang = substr($_SERVER[‘HTTP_ACCEPT_LANGUAGE’], 0, 2);
if ($this->isValidLanguage($browserLang)) {
$this->currentLang = $browserLang;
}
}
// 4. 使用默认语言
else {
foreach ($this->availableLangs as $lang) {
if ($lang[‘is_default’]) {
$this->currentLang = $lang[‘code’];
break;
}
}
}
}
private function isValidLanguage($langCode) {
foreach ($this->availableLangs as $lang) {
if ($lang[‘code’] === $langCode) {
return true;
}
}
return false;
}
private function loadTranslations() {
$conn = getDBConnection();
$stmt = $conn->prepare(“SELECT key_name, translation FROM translations WHERE language_code = ?”);
$stmt->execute([$this->currentLang]);
$results = $stmt->fetchAll();
foreach ($results as $row) {
$this->translations[$row[‘key_name’]] = $row[‘translation’];
}
}
public function getCurrentLang() {
return $this->currentLang;
}
public function getAvailableLangs() {
return $this->availableLangs;
}
public function getDirection() {
foreach ($this->availableLangs as $lang) {
if ($lang[‘code’] === $this->currentLang) {
return $lang[‘direction’];
}
}
return ‘ltr’;
}
public function translate($key, $default = ”) {
return $this->translations[$key] ?? $default ?? $key;
}
public function t($key, $default = ”) {
return $this->translate($key, $default);
}
public function switchLanguage($langCode) {
if ($this->isValidLanguage($langCode)) {
$this->currentLang = $langCode;
$_SESSION[‘user_lang’] = $langCode;
$this->loadTranslations();
return true;
}
return false;
}
// 获取多语言内容
public function getContent($field, $default = ”, $suffix = ”) {
$langField = $field . ‘_’ . $this->currentLang;
if (isset($this->currentData[$langField]) && !empty($this->currentData[$langField])) {
return $this->currentData[$langField];
}
// 如果没有对应语言的翻译,返回默认语言内容
if (isset($this->currentData[$field])) {
return $this->currentData[$field];
}
return $default;
}
}
// 简化的翻译函数
function __($key, $default = ”) {
return LanguageManager::getInstance()->translate($key, $default);
}
// 获取当前语言
function get_current_lang() {
return LanguageManager::getInstance()->getCurrentLang();
}
// 获取语言方向
function get_lang_direction() {
return LanguageManager::getInstance()->getDirection();
}
// 初始化语言管理器
$langManager = LanguageManager::getInstance();
?>
“`
3. 更新 functions.php
includes/functions.php(添加语言相关函数)
“`php
<?php
// … 原有代码 …
// 获取多语言个人资料
function getProfile() {
$conn = getDBConnection();
$stmt = $conn->prepare(“SELECT * FROM profile LIMIT 1”);
$stmt->execute();
$profile = $stmt->fetch();
// 根据当前语言返回对应的字段
$currentLang = get_current_lang();
if ($profile && $currentLang !== ‘zh’) {
// 如果当前不是中文,尝试获取对应语言的字段
$langSuffix = ‘_’ . $currentLang;
$fields = [‘full_name’, ‘job_title’, ‘bio’];
foreach ($fields as $field) {
$langField = $field . $langSuffix;
if (!empty($profile[$langField])) {
$profile[$field] = $profile[$langField];
}
}
}
return $profile;
}
// 获取多语言技能列表
function getSkills() {
$conn = getDBConnection();
$stmt = $conn->prepare(“SELECT * FROM skills ORDER BY display_order, category”);
$stmt->execute();
$skills = $stmt->fetchAll();
$currentLang = get_current_lang();
if ($currentLang !== ‘zh’) {
$langSuffix = ‘_’ . $currentLang;
foreach ($skills as &$skill) {
$langField = ‘skill_name’ . $langSuffix;
if (!empty($skill[$langField])) {
$skill[‘skill_name’] = $skill[$langField];
}
}
}
return $skills;
}
// 获取多语言项目列表
function getProjects($limit = null, $featured = false) {
$conn = getDBConnection();
$sql = “SELECT * FROM projects”;
if ($featured) {
$sql .= ” WHERE featured = 1″;
}
$sql .= ” ORDER BY display_order DESC, created_at DESC”;
if ($limit) {
$sql .= ” LIMIT ” . intval($limit);
}
$stmt = $conn->prepare($sql);
$stmt->execute();
$projects = $stmt->fetchAll();
$currentLang = get_current_lang();
if ($currentLang !== ‘zh’) {
$langSuffix = ‘_’ . $currentLang;
foreach ($projects as &$project) {
$fields = [‘title’, ‘description’, ‘technologies’];
foreach ($fields as $field) {
$langField = $field . $langSuffix;
if (!empty($project[$langField])) {
$project[$field] = $project[$langField];
}
}
}
}
return $projects;
}
// 获取多语言文章列表
function getPosts($limit = null, $publishedOnly = true) {
$conn = getDBConnection();
$sql = “SELECT * FROM posts”;
if ($publishedOnly) {
$sql .= ” WHERE status = ‘published'”;
}
$sql .= ” ORDER BY created_at DESC”;
if ($limit) {
$sql .= ” LIMIT ” . intval($limit);
}
$stmt = $conn->prepare($sql);
$stmt->execute();
$posts = $stmt->fetchAll();
$currentLang = get_current_lang();
if ($currentLang !== ‘zh’) {
$langSuffix = ‘_’ . $currentLang;
foreach ($posts as &$post) {
$fields = [‘title’, ‘content’, ‘excerpt’];
foreach ($fields as $field) {
$langField = $field . $langSuffix;
if (!empty($post[$langField])) {
$post[$field] = $post[$langField];
}
}
}
}
return $posts;
}
// 语言切换链接
function get_lang_switcher() {
$langManager = LanguageManager::getInstance();
$currentLang = $langManager->getCurrentLang();
$availableLangs = $langManager->getAvailableLangs();
$html = ‘<div class=”language-switcher”>’;
$html .= ‘<span class=”current-lang”>’ . strtoupper($currentLang) . ‘</span>’;
$html .= ‘<div class=”lang-dropdown”>’;
foreach ($availableLangs as $lang) {
if ($lang[‘code’] !== $currentLang) {
$html .= ‘<a href=”?lang=’ . $lang[‘code’] . ‘” class=”lang-option” data-lang=”‘ . $lang[‘code’] . ‘”>’;
$html .= ‘<span class=”lang-name”>’ . $lang[‘native_name’] . ‘</span>’;
$html .= ‘</a>’;
}
}
$html .= ‘</div></div>’;
return $html;
}
?>
“`
4. 更新首页(支持多语言)
index.php
“`php
<?php
require_once ‘includes/functions.php’;
require_once ‘includes/language.php’;
$langManager = LanguageManager::getInstance();
$currentLang = $langManager->getCurrentLang();
$langDirection = $langManager->getDirection();
?>
<!DOCTYPE html>
<html lang=”<?php echo $currentLang; ?>” dir=”<?php echo $langDirection; ?>”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title><?php echo e(SITE_NAME); ?></title>
<link rel=”stylesheet” href=”assets/css/style.css”>
<link rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css”>
<?php if ($langDirection === ‘rtl’): ?>
<link rel=”stylesheet” href=”assets/css/rtl.css”>
<?php endif; ?>
</head>
<body>
<!– 语言切换器 –>
<div class=”top-bar”>
<div class=”container”>
<?php echo get_lang_switcher(); ?>
</div>
</div>
<?php include ‘includes/header.php’; ?>
<!– Hero Section –>
<section class=”hero”>
<?php $profile = getProfile(); ?>
<div class=”container”>
<div class=”hero-content <?php echo $langDirection === ‘rtl’ ? ‘rtl’ : ”; ?>”>
<?php if ($langDirection === ‘rtl’): ?>
<div class=”hero-image”>
<img src=”<?php echo e($profile[‘avatar_url’] ? ‘assets/uploads/’ . $profile[‘avatar_url’] : ‘https://via.placeholder.com/300×300’); ?>”
alt=”<?php echo e($profile[‘full_name’] ?? __(‘avatar’)); ?>”>
</div>
<?php endif; ?>
<div class=”hero-text”>
<h1><?php echo e($profile[‘full_name’] ?? __(‘your_name’)); ?></h1>
<h2><?php echo e($profile[‘job_title’] ?? __(‘job_title’)); ?></h2>
<p><?php echo nl2br(e($profile[‘bio’] ?? __(‘personal_intro’))); ?></p>
<div class=”hero-stats”>
<div class=”stat-item”>
<span class=”stat-number”>5+</span>
<span class=”stat-label”><?php echo __(‘years_experience’); ?></span>
</div>
<div class=”stat-item”>
<span class=”stat-number”>50+</span>
<span class=”stat-label”><?php echo __(‘happy_clients’); ?></span>
</div>
<div class=”stat-item”>
<span class=”stat-number”>100+</span>
<span class=”stat-label”><?php echo __(‘projects_completed’); ?></span>
</div>
<div class=”stat-item”>
<span class=”stat-number”>10+</span>
<span class=”stat-label”><?php echo __(‘awards_won’); ?></span>
</div>
</div>
<div class=”hero-buttons”>
<a href=”#contact” class=”btn btn-primary”><?php echo __(‘contact_me’); ?></a>
<a href=”#” class=”btn btn-secondary”><?php echo __(‘download_cv’); ?></a>
</div>
<div class=”social-links”>
<?php if ($profile[‘github_url’]): ?>
<a href=”<?php echo e($profile[‘github_url’]); ?>” target=”_blank”><i class=”fab fa-github”></i></a>
<?php endif; ?>
<?php if ($profile[‘linkedin_url’]): ?>
<a href=”<?php echo e($profile[‘linkedin_url’]); ?>” target=”_blank”><i class=”fab fa-linkedin”></i></a>
<?php endif; ?>
</div>
</div>
<?php if ($langDirection !== ‘rtl’): ?>
<div class=”hero-image”>
<img src=”<?php echo e($profile[‘avatar_url’] ? ‘assets/uploads/’ . $profile[‘avatar_url’] : ‘https://via.placeholder.com/300×300’); ?>”
alt=”<?php echo e($profile[‘full_name’] ?? __(‘avatar’)); ?>”>
</div>
<?php endif; ?>
</div>
</div>
</section>
<!– Skills Section –>
<section class=”skills”>
<div class=”container”>
<h2 class=”section-title”><?php echo __(‘professional_skills’); ?></h2>
<div class=”skills-grid”>
<?php foreach (getSkills() as $skill): ?>
<div class=”skill-item”>
<div class=”skill-header”>
<span class=”skill-name”><?php echo e($skill[‘skill_name’]); ?></span>
<span class=”skill-percent”><?php echo e($skill[‘skill_level’]); ?>%</span>
</div>
<div class=”skill-bar”>
<div class=”skill-progress” style=”width: <?php echo e($skill[‘skill_level’]); ?>%”></div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<!– Projects Section –>
<section class=”projects”>
<div class=”container”>
<h2 class=”section-title”><?php echo __(‘project_showcase’); ?></h2>
<div class=”projects-grid”>
<?php foreach (getProjects(6, true) as $project): ?>
<div class=”project-card”>
<?php if ($project[‘image_url’]): ?>
<img src=”assets/uploads/<?php echo e($project[‘image_url’]); ?>” alt=”<?php echo e($project[‘title’]); ?>”>
<?php endif; ?>
<div class=”project-info”>
<h3><?php echo e($project[‘title’]); ?></h3>
<p><?php echo e($project[‘description’]); ?></p>
<div class=”project-tech”>
<?php echo e($project[‘technologies’]); ?>
</div>
<div class=”project-links”>
<?php if ($project[‘project_url’]): ?>
<a href=”<?php echo e($project[‘project_url’]); ?>” target=”_blank” class=”btn btn-small”>
<?php echo __(‘view_project’); ?>
</a>
<?php endif; ?>
<?php if ($project[‘github_url’]): ?>
<a href=”<?php echo e($project[‘github_url’]); ?>” target=”_blank” class=”btn btn-small btn-secondary”>
<i class=”fab fa-github”></i> GitHub
</a>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class=”section-footer”>
<a href=”projects.php” class=”btn btn-outline”><?php echo __(‘view_all_projects’); ?></a>
</div>
</div>
</section>
<!– Blog Section –>
<section class=”blog”>
<div class=”container”>
<h2 class=”section-title”><?php echo __(‘recent_posts’); ?></h2>
<div class=”blog-posts”>
<?php foreach (getPosts(3) as $post): ?>
<article class=”post-card”>
<?php if ($post[‘cover_image’]): ?>
<img src=”assets/uploads/<?php echo e($post[‘cover_image’]); ?>” alt=”<?php echo e($post[‘title’]); ?>”>
<?php endif; ?>
<div class=”post-content”>
<h3><?php echo e($post[‘title’]); ?></h3>
<p><?php echo e($post[‘excerpt’]); ?></p>
<div class=”post-meta”>
<span><?php echo date(‘Y-m-d’, strtotime($post[‘created_at’])); ?></span>
<span><?php echo __(‘views’); ?>: <?php echo e($post[‘views’]); ?></span>
</div>
<a href=”post.php?id=<?php echo e($post[‘id’]); ?>” class=”read-more”>
<?php echo __(‘read_more’); ?> →
</a>
</div>
</article>
<?php endforeach; ?>
</div>
</div>
</section>
<!– Contact Section –>
<section class=”contact” id=”contact”>
<div class=”container”>
<h2 class=”section-title”><?php echo __(‘contact_me’); ?></h2>
<div class=”contact-content”>
<div class=”contact-info”>
<?php if ($profile[’email’]): ?>
<div class=”contact-item”>
<i class=”fas fa-envelope”></i>
<div>
<h4><?php echo __(’email’); ?></h4>
<span><?php echo e($profile[’email’]); ?></span>
</div>
</div>
<?php endif; ?>
<?php if ($profile[‘phone’]): ?>
<div class=”contact-item”>
<i class=”fas fa-phone”></i>
<div>
<h4><?php echo __(‘phone’); ?></h4>
<span><?php echo e($profile[‘phone’]); ?></span>
</div>
</div>
<?php endif; ?>
<?php if ($profile[‘location’]): ?>
<div class=”contact-item”>
<i class=”fas fa-map-marker-alt”></i>
<div>
<h4><?php echo __(‘location’); ?></h4>
<span><?php echo e($profile[‘location’]); ?></span>
</div>
</div>
<?php endif; ?>
</div>
<div class=”contact-form”>
<form id=”contactForm” action=”send_message.php” method=”POST”>
<div class=”form-group”>
<input type=”text” name=”name” placeholder=”<?php echo __(‘your_name’); ?>” required>
</div>
<div class=”form-group”>
<input type=”email” name=”email” placeholder=”<?php echo __(‘your_email’); ?>” required>
</div>
<div class=”form-group”>
<input type=”text” name=”subject” placeholder=”<?php echo __(‘subject’); ?>” required>
</div>
<div class=”form-group”>
<textarea name=”message” placeholder=”<?php echo __(‘your_message’); ?>” rows=”5″ required></textarea>
</div>
<button type=”submit” class=”btn btn-primary”><?php echo __(‘send_message’); ?></button>
</form>
</div>
</div>
</div>
</section>
<?php include ‘includes/footer.php’; ?>
<script src=”assets/js/main.js”></script>
</body>
</html>
“`
5. 更新样式文件
assets/css/rtl.css(RTL支持)
“`css
/* RTL 样式 */
[dir=”rtl”] body {
text-align: right;
font-family: ‘Arial’, sans-serif; /* 确保支持阿拉伯字符 */
}
/* 导航栏 */
[dir=”rtl”] .nav-links {
margin-right: 0;
margin-left: auto;
}
/* Hero 部分 */
[dir=”rtl”] .hero-content {
direction: rtl;
}
/* 表单 */
[dir=”rtl”] .form-group input,
[dir=”rtl”] .form-group textarea {
text-align: right;
}
/* 按钮组 */
[dir=”rtl”] .hero-buttons,
[dir=”rtl”] .project-links {
direction: rtl;
}
/* 社交图标 */
[dir=”rtl”] .social-links a {
margin-right: 0;
margin-left: 15px;
}
/* 技能进度条 */
[dir=”rtl”] .skill-header {
direction: rtl;
}
/* 项目卡片 */
[dir=”rtl”] .project-card .project-links {
direction: rtl;
}
/* 语言切换器 */
.language-switcher {
position: relative;
display: inline-block;
}
.current-lang {
cursor: pointer;
padding: 5px 10px;
background: #f5f5f5;
border-radius: 4px;
}
.lang-dropdown {
position: absolute;
top: 100%;
right: 0;
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
border-radius: 4px;
min-width: 120px;
display: none;
z-index: 1000;
}
.language-switcher:hover .lang-dropdown {
display: block;
}
.lang-option {
display: block;
padding: 10px;
text-decoration: none;
color: #333;
transition: background 0.3s;
}
.lang-option:hover {
background: #f5f5f5;
}
[dir=”rtl”] .lang-dropdown {
right: auto;
left: 0;
}
/* 顶部栏 */
.top-bar {
background: #f8f9fa;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.top-bar .container {
display: flex;
justify-content: flex-end;
}
[dir=”rtl”] .top-bar .container {
justify-content: flex-start;
}
“`
更新 assets/css/style.css(添加多语言相关样式)
“`css
/* 在原有样式基础上添加以下内容 */
/* 顶部栏 */
.top-bar {
background: #f8f9fa;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.top-bar .container {
display: flex;
justify-content: flex-end;
}
/* 语言切换器 */
.language-switcher {
position: relative;
display: inline-block;
}
.current-lang {
cursor: pointer;
padding: 5px 15px;
background: white;
border: 1px solid #ddd;
border-radius: 20px;
display: flex;
align-items: center;
gap: 5px;
font-size: 14px;
font-weight: 500;
}
.current-lang:after {
content: ‘▼’;
font-size: 10px;
margin-left: 5px;
}
.lang-dropdown {
position: absolute;
top: 100%;
right: 0;
background: white;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
border-radius: 8px;
min-width: 150px;
display: none;
z-index: 1000;
overflow: hidden;
}
.language-switcher:hover .lang-dropdown {
display: block;
}
.lang-option {
display: block;
padding: 12px 20px;
text-decoration: none;
color: #333;
transition: background 0.3s;
border-bottom: 1px solid #f5f5f5;
}
.lang-option:last-child {
border-bottom: none;
}
.lang-option:hover {
background: #007bff;
color: white;
}
.lang-name {
font-size: 14px;
}
/* Hero 统计 */
.hero-stats {
display: flex;
gap: 30px;
margin: 30px 0;
flex-wrap: wrap;
}
.stat-item {
text-align: center;
}
.stat-number {
display: block;
font-size: 2rem;
font-weight: bold;
color: #007bff;
}
.stat-label {
font-size: 0.9rem;
color: #666;
}
/* Hero 按钮 */
.hero-buttons {
display: flex;
gap: 15px;
margin-bottom: 30px;
}
.btn {
padding: 12px 30px;
border-radius: 30px;
text-decoration: none;
font-weight: 500;
display: inline-block;
transition: all 0.3s;
}
.btn-primary {
background: #007bff;
color: white;
border: 2px solid #007bff;
}
.btn-primary:hover {
background: #0056b3;
border-color: #0056b3;
}
.btn-secondary {
background: transparent;
color: #007bff;
border: 2px solid #007bff;
}
.btn-secondary:hover {
background: #007bff;
color: white;
}
.btn-small {
padding: 8px 15px;
font-size: 0.9rem;
}
.btn-outline {
background: transparent;
color: #007bff;
border: 2px solid #007bff;
}
.btn-outline:hover {
background: #007bff;
color: white;
}
/* 部分标题 */
.section-title {
text-align: center;
margin-bottom: 50px;
font-size: 2.5rem;
position: relative;
}
.section-title:after {
content: ”;
display: block;
width: 60px;
height: 3px;
background: #007bff;
margin: 10px auto;
}
/* 部分页脚 */
.section-footer {
text-align: center;
margin-top: 40px;
}
/* 联系表单 */
.contact-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 50px;
max-width: 1000px;
margin: 0 auto;
}
.contact-form .form-group {
margin-bottom: 20px;
}
.contact-form input,
.contact-form textarea {
width: 100%;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 1rem;
}
.contact-form textarea {
resize: vertical;
min-height: 120px;
}
/* 响应式设计更新 */
@media (max-width: 768px) {
.hero-content {
grid-template-columns: 1fr;
text-align: center;
}
.hero-stats {
justify-content: center;
}
.contact-content {
grid-template-columns: 1fr;
}
.top-bar .container {
justify-content: center;
}
}
“`
6. JavaScript 文件更新
assets/js/main.js
“`javascript
// 多语言支持
document.addEventListener(‘DOMContentLoaded’, function() {
// 保存语言选择到本地存储
const langLinks = document.querySelectorAll(‘.lang-option’);
langLinks.forEach(link => {
link.addEventListener(‘click’, function(e) {
const lang = this.getAttribute(‘data-lang’);
localStorage.setItem(‘preferred_language’, lang);
});
});
// 从本地存储恢复语言偏好
const savedLang = localStorage.getItem(‘preferred_language’);
if (savedLang && !window.location.search.includes(‘lang=’)) {
// 如果URL中没有语言参数,使用保存的语言
const currentLang = window.location.pathname.includes(‘/admin/’) ? ” :
document.documentElement.lang;
if (currentLang !== savedLang) {
// 添加语言参数到当前URL
const url = new URL(window.location.href);
url.searchParams.set(‘lang’, savedLang);
window.location.href = url.toString();
}
}
// RTL 方向检测
const dir = document.documentElement.dir;
if (dir === ‘rtl’) {
// 为RTL语言添加特定样式
document.body.classList.add(‘rtl-layout’);
// 调整一些元素的边距
const style = document.createElement(‘style’);
style.textContent = `
.rtl-layout .hero-content {
direction: rtl;
}
.rtl-layout .skill-header {
direction: rtl;
}
.rtl-layout .project-links {
direction: rtl;
}
.rtl-layout .social-links a {
margin-left: 15px;
margin-right: 0;
}
`;
document.head.appendChild(style);
}
// 联系表单处理
const contactForm = document.getElementById(‘contactForm’);
if (contactForm) {
contactForm.addEventListener(‘submit’, function(e) {
e.preventDefault();
// 简单表单验证
const formData = new FormData(this);
let isValid = true;
formData.forEach((value, key) => {
if (!value.trim()) {
isValid = false;
const input = this.querySelector(`[name=”${key}”]`);
input.style.borderColor = ‘#dc3545’;
}
});
if (isValid) {
// 这里可以添加AJAX提交
alert(‘消息发送成功!’);
this.reset();
}
});
}
});
“`
7. 后台多语言管理
admin/languages.php
“`php
<?php
require_once ‘../includes/functions.php’;
// 检查登录
if (!isLoggedIn()) {
header(‘Location: login.php’);
exit;
}
$conn = getDBConnection();
$message = ”;
// 处理添加翻译
if ($_SERVER[‘REQUEST_METHOD’] === ‘POST’ && isset($_POST[‘add_translation’])) {
$language_code = $_POST[‘language_code’];
$key_name = $_POST[‘key_name’];
$translation = $_POST[‘translation’];
$context = $_POST[‘context’];
$stmt = $conn->prepare(“
INSERT INTO translations (language_code, key_name, translation, context)
VALUES (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE translation = VALUES(translation), context = VALUES(context)
“);
if ($stmt->execute([$language_code, $key_name, $translation, $context])) {
$message = ‘<div class=”alert alert-success”>翻译添加成功!</div>’;
} else {
$message = ‘<div class=”alert alert-danger”>添加失败!</div>’;
}
}
// 处理编辑翻译
if ($_SERVER[‘REQUEST_METHOD’] === ‘POST’ && isset($_POST[‘edit_translation’])) {
$id = $_POST[‘id’];
$translation = $_POST[‘translation’];
$stmt = $conn->prepare(“UPDATE translations SET translation = ? WHERE id = ?”);
if ($stmt->execute([$translation, $id])) {
$message = ‘<div class=”alert alert-success”>翻译更新成功!</div>’;
}
}
// 处理删除翻译
if (isset($_GET[‘delete’])) {
$id = intval($_GET[‘delete’]);
$stmt = $conn->prepare(“DELETE FROM translations WHERE id = ?”);
if ($stmt->execute([$id])) {
$message = ‘<div class=”alert alert-success”>翻译删除成功!</div>’;
}
}
// 获取所有翻译
$translations = [];
if (isset($_GET[‘search’])) {
$search = “%” . $_GET[‘search’] . “%”;
$stmt = $conn->prepare(“
SELECT t.*, l.name as language_name
FROM translations t
JOIN languages l ON t.language_code = l.code
WHERE t.key_name LIKE ? OR t.translation LIKE ?
ORDER BY t.key_name, t.language_code
“);
$stmt->execute([$search, $search]);
$translations = $stmt->fetchAll();
} else {
$stmt = $conn->prepare(“
SELECT t.*, l.name as language_name
FROM translations t
JOIN languages l ON t.language_code = l.code
ORDER BY t.key_name, t.language_code
“);
$stmt->execute();
$translations = $stmt->fetchAll();
}
// 获取语言列表
$languages = $conn->query(“SELECT * FROM languages WHERE is_active = 1”)->fetchAll();
?>
<!DOCTYPE html>
<html lang=”zh-CN”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>多语言管理 – <?php echo e(SITE_NAME); ?></title>
<link rel=”stylesheet” href=”../assets/css/admin.css”>
<link rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css”>
</head>
<body>
<div class=”admin-container”>
<?php include ‘includes/admin-header.php’; ?>
<div class=”admin-content”>
<h1><i class=”fas fa-language”></i> 多语言管理</h1>
<?php echo $message; ?>
<div class=”admin-grid”>
<!– 添加翻译表单 –>
<div class=”admin-card”>
<h2>添加新翻译</h2>
<form method=”POST” action=””>
<div class=”form-group”>
<label>语言</label>
<select name=”language_code” class=”form-control” required>
<option value=””>选择语言</option>
<?php foreach ($languages as $lang): ?>
<option value=”<?php echo e($lang[‘code’]); ?>”>
<?php echo e($lang[‘name’]); ?> (<?php echo e($lang[‘native_name’]); ?>)
</option>
<?php endforeach; ?>
</select>
</div>
<div class=”form-group”>
<label>键名</label>
<input type=”text” name=”key_name” class=”form-control” required
placeholder=”例如: home, contact, read_more”>
</div>
<div class=”form-group”>
<label>翻译内容</label>
<textarea name=”translation” class=”form-control” rows=”3″ required></textarea>
</div>
<div class=”form-group”>
<label>上下文</label>
<input type=”text” name=”context” class=”form-control”
placeholder=”例如: menu, general, footer”>
</div>
<button type=”submit” name=”add_translation” class=”btn btn-primary”>
<i class=”fas fa-plus”></i> 添加翻译
</button>
</form>
</div>
<!– 翻译列表 –>
<div class=”admin-card”>
<h2>翻译列表</h2>
<!– 搜索表单 –>
<form method=”GET” action=”” class=”search-form”>
<div class=”input-group”>
<input type=”text” name=”search” class=”form-control”
placeholder=”搜索键名或翻译内容…”
value=”<?php echo e($_GET[‘search’] ?? ”); ?>”>
<button type=”submit” class=”btn btn-secondary”>
<i class=”fas fa-search”></i>
</button>
</div>
</form>
<div class=”table-responsive”>
<table class=”admin-table”>
<thead>
<tr>
<th>ID</th>
<th>语言</th>
<th>键名</th>
<th>翻译</th>
<th>上下文</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($translations as $translation): ?>
<tr>
<td><?php echo e($translation[‘id’]); ?></td>
<td>
<span class=”lang-badge” style=”background: #<?php echo $translation[‘language_code’] == ‘zh’ ? ‘4CAF50’ : ‘2196F3’; ?>”>
<?php echo e($translation[‘language_code’]); ?>
</span>
</td>
<td><code><?php echo e($translation[‘key_name’]); ?></code></td>
<td><?php echo e($translation[‘translation’]); ?></td>
<td><?php echo e($translation[‘context’]); ?></td>
<td>
<button class=”btn btn-sm btn-info”
onclick=”editTranslation(<?php echo $translation[‘id’]; ?>, ‘<?php echo e($translation[‘translation’]); ?>’)”>
<i class=”fas fa-edit”></i>
</button>
<a href=”?delete=<?php echo e($translation[‘id’]); ?>”
class=”btn btn-sm btn-danger”
onclick=”return confirm(‘确定删除这个翻译吗?’)”>
<i class=”fas fa-trash”></i>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (empty($translations)): ?>
<div class=”empty-state”>
<i class=”fas fa-language fa-3x”></i>
<p>暂无翻译数据</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!– 编辑翻译模态框 –>
<div id=”editModal” class=”modal”>
<div class=”modal-content”>
<h2>编辑翻译</h2>
<form id=”editForm” method=”POST” action=””>
<input type=”hidden” name=”id” id=”editId”>
<input type=”hidden” name=”edit_translation” value=”1″>
<div class=”form-group”>
<label>翻译内容</label>
<textarea name=”translation” id=”editTranslation” class=”form-control” rows=”4″ required></textarea>
</div>
<div class=”modal-buttons”>
<button type=”button” class=”btn btn-secondary” onclick=”closeModal()”>取消</button>
<button type=”submit” class=”btn btn-primary”>保存更改</button>
</div>
</form>
</div>
</div>
<script>
function editTranslation(id, translation) {
document.getElementById(‘editId’).value = id;
document.getElementById(‘editTranslation’).value = translation;
document.getElementById(‘editModal’).style.display = ‘block’;
}
function closeModal() {
document.getElementById(‘editModal’).style.display = ‘none’;
}
// 点击模态框外部关闭
window.onclick = function(event) {
const modal = document.getElementById(‘editModal’);
if (event.target === modal) {
closeModal();
}
}
</script>
</body>
</html>
“`
8. 使用说明
安装步骤:
1. 更新数据库:
· 运行上面的SQL语句添加多语言表结构
· 插入默认的语言和翻译数据
2. 文件部署:
· 将新文件添加到项目中
· 确保 assets/uploads/ 目录可写
· 设置 includes/language.php 中的默认语言
3. 配置说明:
· 默认语言:简体中文(zh)
· 支持语言:中文和维吾尔语
· 自动检测用户浏览器语言
· 支持语言切换和记忆
功能特点:
1. 前端特性:
· 自动检测用户语言偏好
· 支持RTL(从右到左)布局
· 语言切换器
· 本地存储记忆用户选择
2. 后台管理:
· 多语言内容编辑
· 翻译管理
· 语言设置
· 实时预览
3. 扩展性:
· 轻松添加新语言
· 模块化翻译系统
· 支持数据库和文件翻译
添加新语言:
要添加新语言(如英语):
“`sql
— 添加新语言
INSERT INTO languages (code, name, native_name, direction, is_active)
VALUES (‘en’, ‘English’, ‘English’, ‘ltr’, TRUE);
— 添加英语翻译
INSERT INTO translations (language_code, key_name, translation, context) VALUES
(‘en’, ‘home’, ‘Home’, ‘menu’),
(‘en’, ‘skills’, ‘Skills’, ‘menu’),
(‘en’, ‘contact_me’, ‘Contact Me’, ‘general’);
“`
注意事项:
1. 确保所有用户输入内容都进行了适当的转义
2. 定期备份翻译数据库
3. 对于性能要求高的场景,考虑缓存翻译数据
4. 为RTL语言(如维语)提供适当的字体支持
5. 测试所有页面的RTL布局


夜雨聆风
