乐于分享
好东西不私藏

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

多语言个人主页网站源码(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布局

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 多语言个人主页网站源码(PHP+MySQL)

评论 抢沙发

5 + 4 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮