文章目录
-
- 在当今全球化的互联网环境中,多语言支持和跨语言交流已成为网站建设的重要需求。WordPress作为全球最受欢迎的内容管理系统,其强大的插件架构为开发者提供了无限的可能性。本教程将深入探讨如何通过WordPress插件开发,集成网站实时翻译与跨语言即时通讯工具,将常用互联网小工具功能融入您的WordPress网站。 随着人工智能和自然语言处理技术的飞速发展,实时翻译和即时通讯已成为现代网站的标配功能。通过本教程,您将学习如何利用WordPress的插件架构,创建功能强大、用户友好的多语言交流工具,不仅提升用户体验,还能拓展网站的国际化潜力。
-
- WordPress插件系统基于事件驱动的钩子(Hooks)机制,主要包括动作(Actions)和过滤器(Filters)。理解这一架构是开发高级插件的基础。 创建插件基本结构: <?php /** * Plugin Name: 多语言交流工具包 * Plugin URI: https://yourwebsite.com/ * Description: 集成实时翻译与跨语言即时通讯的WordPress插件 * Version: 1.0.0 * Author: 您的姓名 * License: GPL v2 or later */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('MLC_PLUGIN_PATH', plugin_dir_path(__FILE__)); define('MLC_PLUGIN_URL', plugin_dir_url(__FILE__)); define('MLC_VERSION', '1.0.0'); // 初始化插件 function mlc_init() { // 插件初始化代码 } add_action('plugins_loaded', 'mlc_init');
- 在开始具体功能开发前,遵循以下最佳实践至关重要: 命名空间和前缀:为所有函数、类和变量添加唯一前缀,避免与其他插件冲突 国际化支持:使用WordPress的翻译函数,使插件支持多语言 安全性考虑:对所有用户输入进行验证和清理,使用nonce保护表单 性能优化:合理使用缓存,避免不必要的数据库查询 错误处理:实现完善的错误处理机制,提供有意义的错误信息
-
- 市场上有多种翻译API可供选择,包括Google Translate API、Microsoft Azure Translator、DeepL API等。本教程以Google Translate API为例,但原理适用于其他服务。 API选择考虑因素: 翻译质量和语言支持 价格和免费额度 API调用限制和响应时间 数据隐私和合规性
- 创建翻译处理类,封装API调用和缓存逻辑: class MLC_Translator { private $api_key; private $cache_enabled; private $cache_expiry; public function __construct($api_key) { $this->api_key = $api_key; $this->cache_enabled = true; $this->cache_expiry = 24 * HOUR_IN_SECONDS; // 缓存24小时 // 初始化缓存表 $this->init_cache_table(); } /** * 翻译文本 * @param string $text 待翻译文本 * @param string $target_lang 目标语言代码 * @param string $source_lang 源语言代码(可选,自动检测) * @return string 翻译结果 */ public function translate($text, $target_lang, $source_lang = 'auto') { // 检查缓存 if ($this->cache_enabled) { $cached = $this->get_cached_translation($text, $source_lang, $target_lang); if ($cached !== false) { return $cached; } } // 调用翻译API $translated_text = $this->call_translation_api($text, $target_lang, $source_lang); // 缓存结果 if ($this->cache_enabled && $translated_text) { $this->cache_translation($text, $source_lang, $target_lang, $translated_text); } return $translated_text; } /** * 调用Google翻译API */ private function call_translation_api($text, $target_lang, $source_lang) { $url = 'https://translation.googleapis.com/language/translate/v2'; $args = array( 'method' => 'POST', 'timeout' => 15, 'headers' => array( 'Content-Type' => 'application/json', ), 'body' => json_encode(array( 'q' => $text, 'target' => $target_lang, 'source' => $source_lang, 'format' => 'text', 'key' => $this->api_key )) ); $response = wp_remote_post($url, $args); if (is_wp_error($response)) { // 错误处理 error_log('翻译API调用失败: ' . $response->get_error_message()); return $text; // 返回原文作为降级方案 } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['data']['translations'][0]['translatedText'])) { return $data['data']['translations'][0]['translatedText']; } return $text; } /** * 初始化翻译缓存表 */ private function init_cache_table() { global $wpdb; $table_name = $wpdb->prefix . 'mlc_translation_cache'; // 检查表是否存在 if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id bigint(20) NOT NULL AUTO_INCREMENT, source_text text NOT NULL, source_lang varchar(10) NOT NULL, target_lang varchar(10) NOT NULL, translated_text text NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY source_lang (source_lang), KEY target_lang (target_lang), KEY source_text_hash (source_text(100), source_lang, target_lang) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } } // 其他缓存相关方法... }
- 创建用户友好的前端翻译界面,允许访客实时翻译页面内容: class MLC_Frontend_Translator { public function __construct() { // 添加前端资源 add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts')); // 添加翻译工具栏 add_action('wp_footer', array($this, 'add_translation_toolbar')); // AJAX翻译端点 add_action('wp_ajax_mlc_translate_text', array($this, 'ajax_translate_text')); add_action('wp_ajax_nopriv_mlc_translate_text', array($this, 'ajax_translate_text')); } public function enqueue_scripts() { // 加载CSS样式 wp_enqueue_style( 'mlc-frontend-style', MLC_PLUGIN_URL . 'assets/css/frontend.css', array(), MLC_VERSION ); // 加载JavaScript wp_enqueue_script( 'mlc-frontend-script', MLC_PLUGIN_URL . 'assets/js/frontend.js', array('jquery'), MLC_VERSION, true ); // 本地化脚本,传递参数给JavaScript wp_localize_script('mlc-frontend-script', 'mlc_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('mlc_translate_nonce'), 'default_language' => get_locale(), 'available_languages' => $this->get_available_languages() )); } public function add_translation_toolbar() { if (!is_user_logged_in() && !current_user_can('edit_posts')) { // 可根据需要调整显示条件 return; } $languages = $this->get_available_languages(); ?> <div id="mlc-translation-toolbar" class="mlc-hidden"> <div class="mlc-toolbar-header"> <span class="mlc-toolbar-title"><?php _e('实时翻译', 'mlc'); ?></span> <button class="mlc-close-toolbar">×</button> </div> <div class="mlc-toolbar-body"> <div class="mlc-translation-controls"> <div class="mlc-language-selectors"> <div class="mlc-language-from"> <label for="mlc-source-lang"><?php _e('从', 'mlc'); ?></label> <select id="mlc-source-lang"> <option value="auto"><?php _e('自动检测', 'mlc'); ?></option> <?php foreach ($languages as $code => $name): ?> <option value="<?php echo esc_attr($code); ?>"><?php echo esc_html($name); ?></option> <?php endforeach; ?> </select> </div> <div class="mlc-language-swap"> <button id="mlc-swap-languages">⇄</button> </div> <div class="mlc-language-to"> <label for="mlc-target-lang"><?php _e('到', 'mlc'); ?></label> <select id="mlc-target-lang"> <?php foreach ($languages as $code => $name): ?> <option value="<?php echo esc_attr($code); ?>" <?php selected($code, 'en_US'); ?>> <?php echo esc_html($name); ?> </option> <?php endforeach; ?> </select> </div> </div> <div class="mlc-text-areas"> <textarea id="mlc-source-text" placeholder="<?php esc_attr_e('输入要翻译的文本...', 'mlc'); ?>"></textarea> <textarea id="mlc-translated-text" readonly placeholder="<?php esc_attr_e('翻译结果将显示在这里...', 'mlc'); ?>"></textarea> </div> <div class="mlc-translation-actions"> <button id="mlc-translate-btn" class="mlc-primary-btn"> <?php _e('翻译', 'mlc'); ?> </button> <button id="mlc-clear-text" class="mlc-secondary-btn"> <?php _e('清空', 'mlc'); ?> </button> <button id="mlc-speak-source" class="mlc-icon-btn" title="<?php esc_attr_e('朗读原文', 'mlc'); ?>"> 🔊 </button> <button id="mlc-speak-translation" class="mlc-icon-btn" title="<?php esc_attr_e('朗读翻译', 'mlc'); ?>"> 🔊 </button> <button id="mlc-copy-translation" class="mlc-icon-btn" title="<?php esc_attr_e('复制翻译', 'mlc'); ?>"> 📋 </button> </div> </div> <div class="mlc-page-translation"> <h4><?php _e('翻译当前页面', 'mlc'); ?></h4> <select id="mlc-page-target-lang"> <?php foreach ($languages as $code => $name): ?> <option value="<?php echo esc_attr($code); ?>"><?php echo esc_html($name); ?></option> <?php endforeach; ?> </select> <button id="mlc-translate-page" class="mlc-primary-btn"> <?php _e('翻译页面', 'mlc'); ?> </button> <div class="mlc-page-translation-status"></div> </div> </div> </div> <button id="mlc-toggle-toolbar" class="mlc-floating-btn" title="<?php esc_attr_e('打开翻译工具', 'mlc'); ?>"> 🌐 </button> <?php } public function ajax_translate_text() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'mlc_translate_nonce')) { wp_die('安全验证失败'); } // 获取参数 $text = sanitize_text_field($_POST['text']); $target_lang = sanitize_text_field($_POST['target_lang']); $source_lang = sanitize_text_field($_POST['source_lang']); // 调用翻译器 $translator = new MLC_Translator(get_option('mlc_google_api_key')); $translated = $translator->translate($text, $target_lang, $source_lang); // 返回结果 wp_send_json_success(array( 'translated_text' => $translated, 'source_lang' => $source_lang === 'auto' ? $this->detect_language($text) : $source_lang )); } // 其他辅助方法... }
-
- 跨语言即时通讯系统需要处理以下核心功能: 用户身份管理(注册/登录/匿名) 实时消息传递 消息自动翻译 对话管理 通知系统 系统架构设计: 前端:使用WebSocket或长轮询实现实时通信 后端:WordPress REST API处理消息逻辑 数据库:存储用户、对话和消息数据 翻译服务:集成到消息处理流程中
- class MLC_Chat_Database { public static function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 对话表 $conversations_table = $wpdb->prefix . 'mlc_conversations'; $sql1 = "CREATE TABLE IF NOT EXISTS $conversations_table ( id bigint(20) NOT NULL AUTO_INCREMENT, title varchar(255) DEFAULT '', created_by bigint(20), created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, status varchar(20) DEFAULT 'active', PRIMARY KEY (id), KEY created_by (created_by), KEY status (status) ) $charset_collate;"; // 对话参与者表 $participants_table = $wpdb->prefix . 'mlc_conversation_participants'; $sql2 = "CREATE TABLE IF NOT EXISTS $participants_table ( id bigint(20) NOT NULL AUTO_INCREMENT, conversation_id bigint(20) NOT NULL, user_id bigint(20), user_type varchar(20) DEFAULT 'registered', -- registered, guest, support user_language varchar(10) DEFAULT 'en_US', joined_at datetime DEFAULT CURRENT_TIMESTAMP, last_seen datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY participant_conversation (conversation_id, user_id, user_type), KEY conversation_id (conversation_id), KEY user_id (user_id) ) $charset_collate;"; // 消息表 $messages_table = $wpdb->prefix . 'mlc_messages'; $sql3 = "CREATE TABLE IF NOT EXISTS $messages_table ( id bigint(20) NOT NULL AUTO_INCREMENT, conversation_id bigint(20) NOT NULL, sender_id bigint(20), sender_type varchar(20) DEFAULT 'registered', message_type varchar(20) DEFAULT 'text', -- text, image, file, system original_text text NOT NULL, original_language varchar(10) DEFAULT 'en_US', translated_text text, translated_language varchar(10), translation_status varchar(20) DEFAULT 'pending', -- pending, completed, failed attachments text, -- JSON格式存储附件信息 sent_at datetime DEFAULT CURRENT_TIMESTAMP, delivered_at datetime, read_at datetime, PRIMARY KEY (id), KEY conversation_id (conversation_id), KEY sender_info (sender_id, sender_type), KEY sent_at (sent_at) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql1); dbDelta($sql2); dbDelta($sql3); } }
- class MLC_Chat_Server { private $translator; public function __construct() { $this->translator = new MLC_Translator(get_option('mlc_google_api_key')); // 注册REST API端点 add_action('rest_api_init', array($this, 'register_rest_routes')); // 初始化WebSocket服务器(如果使用WebSocket) $this->init_websocket_server(); } public function register_rest_routes() { // 创建新对话 register_rest_route('mlc/v1', '/conversations', array( 'methods' => 'POST', 'callback' => array($this, 'create_conversation'), 'permission_callback' => array($this, 'check_chat_permission') )); // 发送消息 register_rest_route('mlc/v1', '/conversations/(?P<id>d+)/messages', array( 'methods' => 'POST', 'callback' => array($this, 'send_message'), 'permission_callback' => array($this, 'check_chat_permission') )); // 获取消息历史 register_rest_route('mlc/v1', '/conversations/(?P<id>d+)/messages', array( 'methods' => 'GET', 'callback' => array($this, 'get_messages'), 'permission_callback' => array($this, 'check_chat_permission') )); // 获取用户对话列表 register_rest_route('mlc/v1', '/users/(?P<user_id>d+)/conversations', array( 'methods' => 'GET', 'callback' => array($this, 'get_user_conversations'), 'permission_callback' => array($this, 'check_chat_permission') )); } public function create_conversation($request) { global $wpdb; $participants = $request->get_param('participants'); $title = sanitize_text_field($request->get_param('title')); $current_user_id = get_current_user_id(); // 开始事务 $wpdb->query('START TRANSACTION'); try { // 创建对话 $conversations_table = $wpdb->prefix . 'mlc_conversations'; $wpdb->insert( $conversations_table, array( 'title' => $title, 'created_by' => $current_user_id, 'status' => 'active' ) ); $conversation_id = $wpdb->insert_id; if (!$conversation_id) { throw new Exception('创建对话失败'); } // 添加参与者 $participants_table = $wpdb->prefix . 'mlc_conversation_participants'; // 添加创建者 $wpdb->insert( $participants_table, array( 'conversation_id' => $conversation_id, 'user_id' => $current_user_id, 'user_type' => 'registered', 'user_language' => get_user_locale($current_user_id) ) ); // 添加其他参与者 foreach ($participants as $participant) { $wpdb->insert( $participants_table, array( 'conversation_id' => $conversation_id, 'user_id' => $participant['user_id'], 'user_type' => $participant['user_type'] ?? 'registered', 'user_language' => $participant['language'] ?? 'en_US' ) ); } $wpdb->query('COMMIT'); return rest_ensure_response(array( 'success' => true, 'conversation_id' => $conversation_id, 'message' => '对话创建成功' )); } catch (Exception $e) { $wpdb->query('ROLLBACK'); return new WP_Error('create_failed', $e->getMessage(), array('status' => 500)); } } public function send_message($request) { global $wpdb; $conversation_id = $request->get_param('id'); $message_text = sanitize_textarea_field($request->get_param('message')); $attachments = $request->get_param('attachments'); $current_user_id = get_current_user_id(); // 验证用户是否参与对话 if (!$this->is_user_in_conversation($current_user_id, $conversation_id)) { return new WP_Error('permission_denied', '您不在这个对话中', array('status' => 403)); } // 获取用户语言设置 $user_language = $this->get_user_language($current_user_id, $conversation_id); // 插入原始消息 $messages_table = $wpdb->prefix . 'mlc_messages'; $wpdb->insert( $messages_table, array( 'conversation_id' => $conversation_id, 'sender_id' => $current_user_id, 'sender_type' => 'registered', 'message_type' => 'text', 'original_text' => $message_text, 'original_language' => $user_language, 'translation_status' => 'pending', 'attachments' => $attachments ? json_encode($attachments) : null, 'sent_at' => current_time('mysql') ) ); $message_id = $wpdb->insert_id; if (!$message_id) { return new WP_Error('send_failed', '发送消息失败', array('status' => 500)); } // 异步处理消息翻译 $this->process_message_translation_async($message_id, $conversation_id); // 实时推送消息给其他参与者 $this->broadcast_message($message_id, $conversation_id); return rest_ensure_response(array( 'success' => true, 'message_id' => $message_id, 'sent_at' => current_time('mysql') )); } private function process_message_translation_async($message_id, $conversation_id) { // 使用WordPress的调度系统异步处理翻译 wp_schedule_single_event(time() + 1, 'mlc_process_message_translation', array( 'message_id' => $message_id, 'conversation_id' => $conversation_id )); } public function process_message_translation($message_id, $conversation_id) { global $wpdb; $messages_table = $wpdb->prefix . 'mlc_messages'; $participants_table = $wpdb->prefix . 'mlc_conversation_participants'; // 获取原始消息 $message = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $messages_table WHERE id = %d", $message_id )); if (!$message) { return; } // 获取对话中的所有参与者(除了发送者) $participants = $wpdb->get_results($wpdb->prepare( "SELECT user_id, user_type, user_language FROM $participants_table WHERE conversation_id = %d AND (user_id != %d OR user_type != 'registered')", $conversation_id, $message->sender_id )); // 为每个语言需求生成翻译 $translations = array(); foreach ($participants as $participant) { $target_language = $participant->user_language; // 如果目标语言与原始语言相同,不需要翻译 if ($target_language === $message->original_language) { $translations[$target_language] = $message->original_text; continue; } // 检查是否已有该语言的翻译 if (!isset($translations[$target_language])) { $translated_text = $this->translator->translate( $message->original_text, $target_language, $message->original_language ); $translations[$target_language] = $translated_text; } } // 更新消息记录,存储翻译结果 $wpdb->update( $messages_table, array( 'translation_status' => 'completed', 'translated_text' => json_encode($translations), 'translated_language' => json_encode(array_keys($translations)) ), array('id' => $message_id) ); // 通知客户端翻译完成 $this->notify_translation_complete($message_id, $conversation_id); } private function broadcast_message($message_id, $conversation_id) { // 这里实现WebSocket或Server-Sent Events推送 // 实际实现取决于您选择的实时通信技术 // 示例:使用Action钩子让其他插件处理推送 do_action('mlc_message_sent', $message_id, $conversation_id); } // 其他必要的方法... }
- // assets/js/chat-frontend.js class MLChat { constructor(options) { this.options = Object.assign({ container: '#mlc-chat-container', apiUrl: mlc_ajax.ajax_url, websocketUrl: mlc_ajax.websocket_url, currentUserId: mlc_ajax.current_user_id, currentUserLanguage: mlc_ajax.current_language, nonce: mlc_ajax.nonce }, options); this.conversations = []; this.activeConversation = null; this.websocket = null; this.init(); } init() { this.renderContainer(); this.loadConversations(); this.initWebSocket(); this.bindEvents(); } renderContainer() { const container = document.querySelector(this.options.container); if (!container) return; container.innerHTML = ` <div class="mlc-chat-wrapper"> <div class="mlc-chat-sidebar"> <div class="mlc-chat-header"> <h3>${mlc_ajax.i18n.conversations}</h3> <button class="mlc-new-chat-btn">+ ${mlc_ajax.i18n.new_chat}</button> </div> <div class="mlc-conversations-list"> <!-- 对话列表将通过JavaScript动态加载 --> </div> <div class="mlc-user-profile"> <div class="mlc-user-avatar"> ${this.getUserAvatar()} </div> <div class="mlc-user-info"> <span class="mlc-user-name">${mlc_ajax.current_user_name}</span> <select class="mlc-user-language"> ${this.getLanguageOptions()} </select> </div> </div> </div> <div class="mlc-chat-main"> <div class="mlc-chat-empty-state"> <div class="mlc-empty-icon">💬</div> <h4>${mlc_ajax.i18n.select_conversation}</h4> <p>${mlc_ajax.i18n.select_conversation_desc}</p> <button class="mlc-start-new-chat">${mlc_ajax.i18n.start_chat}</button> </div> <div class="mlc-chat-active" style="display: none;"> <div class="mlc-chat-header"> <div class="mlc-chat-info"> <h4 class="mlc-chat-title"></h4> <span class="mlc-chat-participants"></span> </div> <div class="mlc-chat-actions"> <button class="mlc-translate-chat" title="${mlc_ajax.i18n.translate_chat}">🌐</button> <button class="mlc-chat-settings" title="${mlc_ajax.i18n.settings}">⚙️</button> </div> </div> <div class="mlc-messages-container"> <div class="mlc-messages-loading">${mlc_ajax.i18n.loading}</div> <div class="mlc-messages-list"></div> </div> <div class="mlc-message-input-area"> <div class="mlc-input-tools"> <button class="mlc-attach-file" title="${mlc_ajax.i18n.attach_file}">📎</button> <button class="mlc-emoji-picker" title="${mlc_ajax.i18n.emoji}">😊</button> <select class="mlc-translation-mode"> <option value="auto">${mlc_ajax.i18n.translate_auto}</option> <option value="manual">${mlc_ajax.i18n.translate_manual}</option> <option value="off">${mlc_ajax.i18n.translate_off}</option> </select> </div> <textarea class="mlc-message-input" placeholder="${mlc_ajax.i18n.type_message}"></textarea> <button class="mlc-send-message">${mlc_ajax.i18n.send}</button> </div> </div> </div> <div class="mlc-chat-modal" id="mlc-new-chat-modal" style="display: none;"> <div class="mlc-modal-content"> <h3>${mlc_ajax.i18n.new_conversation}</h3> <div class="mlc-modal-body"> <input type="text" class="mlc-chat-title-input" placeholder="${mlc_ajax.i18n.conversation_title}"> <div class="mlc-participants-selector"> <h4>${mlc_ajax.i18n.select_participants}</h4> <div class="mlc-users-list"></div> </div> </div> <div class="mlc-modal-footer"> <button class="mlc-modal-cancel">${mlc_ajax.i18n.cancel}</button> <button class="mlc-modal-create">${mlc_ajax.i18n.create}</button> </div> </div> </div> </div> `; } async loadConversations() { try { const response = await fetch( `${this.options.apiUrl}/wp-json/mlc/v1/users/${this.options.currentUserId}/conversations`, { headers: { 'X-WP-Nonce': this.options.nonce } } ); if (response.ok) { const data = await response.json(); this.conversations = data; this.renderConversationsList(); } } catch (error) { console.error('加载对话失败:', error); } } async sendMessage(conversationId, messageText, attachments = []) { try { const response = await fetch( `${this.options.apiUrl}/wp-json/mlc/v1/conversations/${conversationId}/messages`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': this.options.nonce }, body: JSON.stringify({ message: messageText, attachments: attachments }) } ); if (response.ok) { const data = await response.json(); return data; } } catch (error) { console.error('发送消息失败:', error); throw error; } } initWebSocket() { if (!this.options.websocketUrl) return; this.websocket = new WebSocket(this.options.websocketUrl); this.websocket.onopen = () => { console.log('WebSocket连接已建立'); // 发送身份验证 this.websocket.send(JSON.stringify({ type: 'auth', userId: this.options.currentUserId, nonce: this.options.nonce })); }; this.websocket.onmessage = (event) => { const data = JSON.parse(event.data); this.handleWebSocketMessage(data); }; this.websocket.onclose = () => { console.log('WebSocket连接已关闭'); // 尝试重新连接 setTimeout(() => this.initWebSocket(), 5000); }; } handleWebSocketMessage(data) { switch (data.type) { case 'new_message': this.handleNewMessage(data.message); break; case 'translation_complete': this.handleTranslationComplete(data.messageId, data.translations); break; case 'user_online': this.updateUserStatus(data.userId, true); break; case 'user_offline': this.updateUserStatus(data.userId, false); break; } } handleNewMessage(message) { // 如果消息属于当前活跃对话,则显示 if (this.activeConversation && this.activeConversation.id === message.conversation_id) { this.appendMessage(message); } // 更新对话列表中的最后消息预览 this.updateConversationPreview(message.conversation_id, message); // 显示通知 if (!document.hasFocus() || this.activeConversation?.id !== message.conversation_id) { this.showNotification(message); } } // 其他前端方法... }
在当今全球化的互联网环境中,多语言支持和跨语言交流已成为网站建设的重要需求。WordPress作为全球最受欢迎的内容管理系统,其强大的插件架构为开发者提供了无限的可能性。本教程将深入探讨如何通过WordPress插件开发,集成网站实时翻译与跨语言即时通讯工具,将常用互联网小工具功能融入您的WordPress网站。
随着人工智能和自然语言处理技术的飞速发展,实时翻译和即时通讯已成为现代网站的标配功能。通过本教程,您将学习如何利用WordPress的插件架构,创建功能强大、用户友好的多语言交流工具,不仅提升用户体验,还能拓展网站的国际化潜力。
WordPress插件系统基于事件驱动的钩子(Hooks)机制,主要包括动作(Actions)和过滤器(Filters)。理解这一架构是开发高级插件的基础。
创建插件基本结构:
<?php
/**
* Plugin Name: 多语言交流工具包
* Plugin URI: https://yourwebsite.com/
* Description: 集成实时翻译与跨语言即时通讯的WordPress插件
* Version: 1.0.0
* Author: 您的姓名
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('MLC_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('MLC_PLUGIN_URL', plugin_dir_url(__FILE__));
define('MLC_VERSION', '1.0.0');
// 初始化插件
function mlc_init() {
// 插件初始化代码
}
add_action('plugins_loaded', 'mlc_init');
在开始具体功能开发前,遵循以下最佳实践至关重要:
- 命名空间和前缀:为所有函数、类和变量添加唯一前缀,避免与其他插件冲突
- 国际化支持:使用WordPress的翻译函数,使插件支持多语言
- 安全性考虑:对所有用户输入进行验证和清理,使用nonce保护表单
- 性能优化:合理使用缓存,避免不必要的数据库查询
- 错误处理:实现完善的错误处理机制,提供有意义的错误信息
市场上有多种翻译API可供选择,包括Google Translate API、Microsoft Azure Translator、DeepL API等。本教程以Google Translate API为例,但原理适用于其他服务。
API选择考虑因素:
- 翻译质量和语言支持
- 价格和免费额度
- API调用限制和响应时间
- 数据隐私和合规性
创建翻译处理类,封装API调用和缓存逻辑:
class MLC_Translator {
private $api_key;
private $cache_enabled;
private $cache_expiry;
public function __construct($api_key) {
$this->api_key = $api_key;
$this->cache_enabled = true;
$this->cache_expiry = 24 * HOUR_IN_SECONDS; // 缓存24小时
// 初始化缓存表
$this->init_cache_table();
}
/**
* 翻译文本
* @param string $text 待翻译文本
* @param string $target_lang 目标语言代码
* @param string $source_lang 源语言代码(可选,自动检测)
* @return string 翻译结果
*/
public function translate($text, $target_lang, $source_lang = 'auto') {
// 检查缓存
if ($this->cache_enabled) {
$cached = $this->get_cached_translation($text, $source_lang, $target_lang);
if ($cached !== false) {
return $cached;
}
}
// 调用翻译API
$translated_text = $this->call_translation_api($text, $target_lang, $source_lang);
// 缓存结果
if ($this->cache_enabled && $translated_text) {
$this->cache_translation($text, $source_lang, $target_lang, $translated_text);
}
return $translated_text;
}
/**
* 调用Google翻译API
*/
private function call_translation_api($text, $target_lang, $source_lang) {
$url = 'https://translation.googleapis.com/language/translate/v2';
$args = array(
'method' => 'POST',
'timeout' => 15,
'headers' => array(
'Content-Type' => 'application/json',
),
'body' => json_encode(array(
'q' => $text,
'target' => $target_lang,
'source' => $source_lang,
'format' => 'text',
'key' => $this->api_key
))
);
$response = wp_remote_post($url, $args);
if (is_wp_error($response)) {
// 错误处理
error_log('翻译API调用失败: ' . $response->get_error_message());
return $text; // 返回原文作为降级方案
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['data']['translations'][0]['translatedText'])) {
return $data['data']['translations'][0]['translatedText'];
}
return $text;
}
/**
* 初始化翻译缓存表
*/
private function init_cache_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'mlc_translation_cache';
// 检查表是否存在
if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
source_text text NOT NULL,
source_lang varchar(10) NOT NULL,
target_lang varchar(10) NOT NULL,
translated_text text NOT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY source_lang (source_lang),
KEY target_lang (target_lang),
KEY source_text_hash (source_text(100), source_lang, target_lang)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
}
// 其他缓存相关方法...
}
创建用户友好的前端翻译界面,允许访客实时翻译页面内容:
class MLC_Frontend_Translator {
public function __construct() {
// 添加前端资源
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
// 添加翻译工具栏
add_action('wp_footer', array($this, 'add_translation_toolbar'));
// AJAX翻译端点
add_action('wp_ajax_mlc_translate_text', array($this, 'ajax_translate_text'));
add_action('wp_ajax_nopriv_mlc_translate_text', array($this, 'ajax_translate_text'));
}
public function enqueue_scripts() {
// 加载CSS样式
wp_enqueue_style(
'mlc-frontend-style',
MLC_PLUGIN_URL . 'assets/css/frontend.css',
array(),
MLC_VERSION
);
// 加载JavaScript
wp_enqueue_script(
'mlc-frontend-script',
MLC_PLUGIN_URL . 'assets/js/frontend.js',
array('jquery'),
MLC_VERSION,
true
);
// 本地化脚本,传递参数给JavaScript
wp_localize_script('mlc-frontend-script', 'mlc_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mlc_translate_nonce'),
'default_language' => get_locale(),
'available_languages' => $this->get_available_languages()
));
}
public function add_translation_toolbar() {
if (!is_user_logged_in() && !current_user_can('edit_posts')) {
// 可根据需要调整显示条件
return;
}
$languages = $this->get_available_languages();
?>
<div id="mlc-translation-toolbar" class="mlc-hidden">
<div class="mlc-toolbar-header">
<span class="mlc-toolbar-title"><?php _e('实时翻译', 'mlc'); ?></span>
<button class="mlc-close-toolbar">×</button>
</div>
<div class="mlc-toolbar-body">
<div class="mlc-translation-controls">
<div class="mlc-language-selectors">
<div class="mlc-language-from">
<label for="mlc-source-lang"><?php _e('从', 'mlc'); ?></label>
<select id="mlc-source-lang">
<option value="auto"><?php _e('自动检测', 'mlc'); ?></option>
<?php foreach ($languages as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"><?php echo esc_html($name); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mlc-language-swap">
<button id="mlc-swap-languages">⇄</button>
</div>
<div class="mlc-language-to">
<label for="mlc-target-lang"><?php _e('到', 'mlc'); ?></label>
<select id="mlc-target-lang">
<?php foreach ($languages as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>" <?php selected($code, 'en_US'); ?>>
<?php echo esc_html($name); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="mlc-text-areas">
<textarea id="mlc-source-text" placeholder="<?php esc_attr_e('输入要翻译的文本...', 'mlc'); ?>"></textarea>
<textarea id="mlc-translated-text" readonly placeholder="<?php esc_attr_e('翻译结果将显示在这里...', 'mlc'); ?>"></textarea>
</div>
<div class="mlc-translation-actions">
<button id="mlc-translate-btn" class="mlc-primary-btn">
<?php _e('翻译', 'mlc'); ?>
</button>
<button id="mlc-clear-text" class="mlc-secondary-btn">
<?php _e('清空', 'mlc'); ?>
</button>
<button id="mlc-speak-source" class="mlc-icon-btn" title="<?php esc_attr_e('朗读原文', 'mlc'); ?>">
🔊
</button>
<button id="mlc-speak-translation" class="mlc-icon-btn" title="<?php esc_attr_e('朗读翻译', 'mlc'); ?>">
🔊
</button>
<button id="mlc-copy-translation" class="mlc-icon-btn" title="<?php esc_attr_e('复制翻译', 'mlc'); ?>">
📋
</button>
</div>
</div>
<div class="mlc-page-translation">
<h4><?php _e('翻译当前页面', 'mlc'); ?></h4>
<select id="mlc-page-target-lang">
<?php foreach ($languages as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"><?php echo esc_html($name); ?></option>
<?php endforeach; ?>
</select>
<button id="mlc-translate-page" class="mlc-primary-btn">
<?php _e('翻译页面', 'mlc'); ?>
</button>
<div class="mlc-page-translation-status"></div>
</div>
</div>
</div>
<button id="mlc-toggle-toolbar" class="mlc-floating-btn" title="<?php esc_attr_e('打开翻译工具', 'mlc'); ?>">
🌐
</button>
<?php
}
public function ajax_translate_text() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'mlc_translate_nonce')) {
wp_die('安全验证失败');
}
// 获取参数
$text = sanitize_text_field($_POST['text']);
$target_lang = sanitize_text_field($_POST['target_lang']);
$source_lang = sanitize_text_field($_POST['source_lang']);
// 调用翻译器
$translator = new MLC_Translator(get_option('mlc_google_api_key'));
$translated = $translator->translate($text, $target_lang, $source_lang);
// 返回结果
wp_send_json_success(array(
'translated_text' => $translated,
'source_lang' => $source_lang === 'auto' ? $this->detect_language($text) : $source_lang
));
}
// 其他辅助方法...
}
跨语言即时通讯系统需要处理以下核心功能:
- 用户身份管理(注册/登录/匿名)
- 实时消息传递
- 消息自动翻译
- 对话管理
- 通知系统
系统架构设计:
- 前端:使用WebSocket或长轮询实现实时通信
- 后端:WordPress REST API处理消息逻辑
- 数据库:存储用户、对话和消息数据
- 翻译服务:集成到消息处理流程中
class MLC_Chat_Database {
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 对话表
$conversations_table = $wpdb->prefix . 'mlc_conversations';
$sql1 = "CREATE TABLE IF NOT EXISTS $conversations_table (
id bigint(20) NOT NULL AUTO_INCREMENT,
title varchar(255) DEFAULT '',
created_by bigint(20),
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
status varchar(20) DEFAULT 'active',
PRIMARY KEY (id),
KEY created_by (created_by),
KEY status (status)
) $charset_collate;";
// 对话参与者表
$participants_table = $wpdb->prefix . 'mlc_conversation_participants';
$sql2 = "CREATE TABLE IF NOT EXISTS $participants_table (
id bigint(20) NOT NULL AUTO_INCREMENT,
conversation_id bigint(20) NOT NULL,
user_id bigint(20),
user_type varchar(20) DEFAULT 'registered', -- registered, guest, support
user_language varchar(10) DEFAULT 'en_US',
joined_at datetime DEFAULT CURRENT_TIMESTAMP,
last_seen datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY participant_conversation (conversation_id, user_id, user_type),
KEY conversation_id (conversation_id),
KEY user_id (user_id)
) $charset_collate;";
// 消息表
$messages_table = $wpdb->prefix . 'mlc_messages';
$sql3 = "CREATE TABLE IF NOT EXISTS $messages_table (
id bigint(20) NOT NULL AUTO_INCREMENT,
conversation_id bigint(20) NOT NULL,
sender_id bigint(20),
sender_type varchar(20) DEFAULT 'registered',
message_type varchar(20) DEFAULT 'text', -- text, image, file, system
original_text text NOT NULL,
original_language varchar(10) DEFAULT 'en_US',
translated_text text,
translated_language varchar(10),
translation_status varchar(20) DEFAULT 'pending', -- pending, completed, failed
attachments text, -- JSON格式存储附件信息
sent_at datetime DEFAULT CURRENT_TIMESTAMP,
delivered_at datetime,
read_at datetime,
PRIMARY KEY (id),
KEY conversation_id (conversation_id),
KEY sender_info (sender_id, sender_type),
KEY sent_at (sent_at)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql1);
dbDelta($sql2);
dbDelta($sql3);
}
}
class MLC_Chat_Database {
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 对话表
$conversations_table = $wpdb->prefix . 'mlc_conversations';
$sql1 = "CREATE TABLE IF NOT EXISTS $conversations_table (
id bigint(20) NOT NULL AUTO_INCREMENT,
title varchar(255) DEFAULT '',
created_by bigint(20),
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
status varchar(20) DEFAULT 'active',
PRIMARY KEY (id),
KEY created_by (created_by),
KEY status (status)
) $charset_collate;";
// 对话参与者表
$participants_table = $wpdb->prefix . 'mlc_conversation_participants';
$sql2 = "CREATE TABLE IF NOT EXISTS $participants_table (
id bigint(20) NOT NULL AUTO_INCREMENT,
conversation_id bigint(20) NOT NULL,
user_id bigint(20),
user_type varchar(20) DEFAULT 'registered', -- registered, guest, support
user_language varchar(10) DEFAULT 'en_US',
joined_at datetime DEFAULT CURRENT_TIMESTAMP,
last_seen datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY participant_conversation (conversation_id, user_id, user_type),
KEY conversation_id (conversation_id),
KEY user_id (user_id)
) $charset_collate;";
// 消息表
$messages_table = $wpdb->prefix . 'mlc_messages';
$sql3 = "CREATE TABLE IF NOT EXISTS $messages_table (
id bigint(20) NOT NULL AUTO_INCREMENT,
conversation_id bigint(20) NOT NULL,
sender_id bigint(20),
sender_type varchar(20) DEFAULT 'registered',
message_type varchar(20) DEFAULT 'text', -- text, image, file, system
original_text text NOT NULL,
original_language varchar(10) DEFAULT 'en_US',
translated_text text,
translated_language varchar(10),
translation_status varchar(20) DEFAULT 'pending', -- pending, completed, failed
attachments text, -- JSON格式存储附件信息
sent_at datetime DEFAULT CURRENT_TIMESTAMP,
delivered_at datetime,
read_at datetime,
PRIMARY KEY (id),
KEY conversation_id (conversation_id),
KEY sender_info (sender_id, sender_type),
KEY sent_at (sent_at)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql1);
dbDelta($sql2);
dbDelta($sql3);
}
}
class MLC_Chat_Server {
private $translator;
public function __construct() {
$this->translator = new MLC_Translator(get_option('mlc_google_api_key'));
// 注册REST API端点
add_action('rest_api_init', array($this, 'register_rest_routes'));
// 初始化WebSocket服务器(如果使用WebSocket)
$this->init_websocket_server();
}
public function register_rest_routes() {
// 创建新对话
register_rest_route('mlc/v1', '/conversations', array(
'methods' => 'POST',
'callback' => array($this, 'create_conversation'),
'permission_callback' => array($this, 'check_chat_permission')
));
// 发送消息
register_rest_route('mlc/v1', '/conversations/(?P<id>d+)/messages', array(
'methods' => 'POST',
'callback' => array($this, 'send_message'),
'permission_callback' => array($this, 'check_chat_permission')
));
// 获取消息历史
register_rest_route('mlc/v1', '/conversations/(?P<id>d+)/messages', array(
'methods' => 'GET',
'callback' => array($this, 'get_messages'),
'permission_callback' => array($this, 'check_chat_permission')
));
// 获取用户对话列表
register_rest_route('mlc/v1', '/users/(?P<user_id>d+)/conversations', array(
'methods' => 'GET',
'callback' => array($this, 'get_user_conversations'),
'permission_callback' => array($this, 'check_chat_permission')
));
}
public function create_conversation($request) {
global $wpdb;
$participants = $request->get_param('participants');
$title = sanitize_text_field($request->get_param('title'));
$current_user_id = get_current_user_id();
// 开始事务
$wpdb->query('START TRANSACTION');
try {
// 创建对话
$conversations_table = $wpdb->prefix . 'mlc_conversations';
$wpdb->insert(
$conversations_table,
array(
'title' => $title,
'created_by' => $current_user_id,
'status' => 'active'
)
);
$conversation_id = $wpdb->insert_id;
if (!$conversation_id) {
throw new Exception('创建对话失败');
}
// 添加参与者
$participants_table = $wpdb->prefix . 'mlc_conversation_participants';
// 添加创建者
$wpdb->insert(
$participants_table,
array(
'conversation_id' => $conversation_id,
'user_id' => $current_user_id,
'user_type' => 'registered',
'user_language' => get_user_locale($current_user_id)
)
);
// 添加其他参与者
foreach ($participants as $participant) {
$wpdb->insert(
$participants_table,
array(
'conversation_id' => $conversation_id,
'user_id' => $participant['user_id'],
'user_type' => $participant['user_type'] ?? 'registered',
'user_language' => $participant['language'] ?? 'en_US'
)
);
}
$wpdb->query('COMMIT');
return rest_ensure_response(array(
'success' => true,
'conversation_id' => $conversation_id,
'message' => '对话创建成功'
));
} catch (Exception $e) {
$wpdb->query('ROLLBACK');
return new WP_Error('create_failed', $e->getMessage(), array('status' => 500));
}
}
public function send_message($request) {
global $wpdb;
$conversation_id = $request->get_param('id');
$message_text = sanitize_textarea_field($request->get_param('message'));
$attachments = $request->get_param('attachments');
$current_user_id = get_current_user_id();
// 验证用户是否参与对话
if (!$this->is_user_in_conversation($current_user_id, $conversation_id)) {
return new WP_Error('permission_denied', '您不在这个对话中', array('status' => 403));
}
// 获取用户语言设置
$user_language = $this->get_user_language($current_user_id, $conversation_id);
// 插入原始消息
$messages_table = $wpdb->prefix . 'mlc_messages';
$wpdb->insert(
$messages_table,
array(
'conversation_id' => $conversation_id,
'sender_id' => $current_user_id,
'sender_type' => 'registered',
'message_type' => 'text',
'original_text' => $message_text,
'original_language' => $user_language,
'translation_status' => 'pending',
'attachments' => $attachments ? json_encode($attachments) : null,
'sent_at' => current_time('mysql')
)
);
$message_id = $wpdb->insert_id;
if (!$message_id) {
return new WP_Error('send_failed', '发送消息失败', array('status' => 500));
}
// 异步处理消息翻译
$this->process_message_translation_async($message_id, $conversation_id);
// 实时推送消息给其他参与者
$this->broadcast_message($message_id, $conversation_id);
return rest_ensure_response(array(
'success' => true,
'message_id' => $message_id,
'sent_at' => current_time('mysql')
));
}
private function process_message_translation_async($message_id, $conversation_id) {
// 使用WordPress的调度系统异步处理翻译
wp_schedule_single_event(time() + 1, 'mlc_process_message_translation', array(
'message_id' => $message_id,
'conversation_id' => $conversation_id
));
}
public function process_message_translation($message_id, $conversation_id) {
global $wpdb;
$messages_table = $wpdb->prefix . 'mlc_messages';
$participants_table = $wpdb->prefix . 'mlc_conversation_participants';
// 获取原始消息
$message = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $messages_table WHERE id = %d",
$message_id
));
if (!$message) {
return;
}
// 获取对话中的所有参与者(除了发送者)
$participants = $wpdb->get_results($wpdb->prepare(
"SELECT user_id, user_type, user_language
FROM $participants_table
WHERE conversation_id = %d
AND (user_id != %d OR user_type != 'registered')",
$conversation_id,
$message->sender_id
));
// 为每个语言需求生成翻译
$translations = array();
foreach ($participants as $participant) {
$target_language = $participant->user_language;
// 如果目标语言与原始语言相同,不需要翻译
if ($target_language === $message->original_language) {
$translations[$target_language] = $message->original_text;
continue;
}
// 检查是否已有该语言的翻译
if (!isset($translations[$target_language])) {
$translated_text = $this->translator->translate(
$message->original_text,
$target_language,
$message->original_language
);
$translations[$target_language] = $translated_text;
}
}
// 更新消息记录,存储翻译结果
$wpdb->update(
$messages_table,
array(
'translation_status' => 'completed',
'translated_text' => json_encode($translations),
'translated_language' => json_encode(array_keys($translations))
),
array('id' => $message_id)
);
// 通知客户端翻译完成
$this->notify_translation_complete($message_id, $conversation_id);
}
private function broadcast_message($message_id, $conversation_id) {
// 这里实现WebSocket或Server-Sent Events推送
// 实际实现取决于您选择的实时通信技术
// 示例:使用Action钩子让其他插件处理推送
do_action('mlc_message_sent', $message_id, $conversation_id);
}
// 其他必要的方法...
}
class MLC_Chat_Server {
private $translator;
public function __construct() {
$this->translator = new MLC_Translator(get_option('mlc_google_api_key'));
// 注册REST API端点
add_action('rest_api_init', array($this, 'register_rest_routes'));
// 初始化WebSocket服务器(如果使用WebSocket)
$this->init_websocket_server();
}
public function register_rest_routes() {
// 创建新对话
register_rest_route('mlc/v1', '/conversations', array(
'methods' => 'POST',
'callback' => array($this, 'create_conversation'),
'permission_callback' => array($this, 'check_chat_permission')
));
// 发送消息
register_rest_route('mlc/v1', '/conversations/(?P<id>d+)/messages', array(
'methods' => 'POST',
'callback' => array($this, 'send_message'),
'permission_callback' => array($this, 'check_chat_permission')
));
// 获取消息历史
register_rest_route('mlc/v1', '/conversations/(?P<id>d+)/messages', array(
'methods' => 'GET',
'callback' => array($this, 'get_messages'),
'permission_callback' => array($this, 'check_chat_permission')
));
// 获取用户对话列表
register_rest_route('mlc/v1', '/users/(?P<user_id>d+)/conversations', array(
'methods' => 'GET',
'callback' => array($this, 'get_user_conversations'),
'permission_callback' => array($this, 'check_chat_permission')
));
}
public function create_conversation($request) {
global $wpdb;
$participants = $request->get_param('participants');
$title = sanitize_text_field($request->get_param('title'));
$current_user_id = get_current_user_id();
// 开始事务
$wpdb->query('START TRANSACTION');
try {
// 创建对话
$conversations_table = $wpdb->prefix . 'mlc_conversations';
$wpdb->insert(
$conversations_table,
array(
'title' => $title,
'created_by' => $current_user_id,
'status' => 'active'
)
);
$conversation_id = $wpdb->insert_id;
if (!$conversation_id) {
throw new Exception('创建对话失败');
}
// 添加参与者
$participants_table = $wpdb->prefix . 'mlc_conversation_participants';
// 添加创建者
$wpdb->insert(
$participants_table,
array(
'conversation_id' => $conversation_id,
'user_id' => $current_user_id,
'user_type' => 'registered',
'user_language' => get_user_locale($current_user_id)
)
);
// 添加其他参与者
foreach ($participants as $participant) {
$wpdb->insert(
$participants_table,
array(
'conversation_id' => $conversation_id,
'user_id' => $participant['user_id'],
'user_type' => $participant['user_type'] ?? 'registered',
'user_language' => $participant['language'] ?? 'en_US'
)
);
}
$wpdb->query('COMMIT');
return rest_ensure_response(array(
'success' => true,
'conversation_id' => $conversation_id,
'message' => '对话创建成功'
));
} catch (Exception $e) {
$wpdb->query('ROLLBACK');
return new WP_Error('create_failed', $e->getMessage(), array('status' => 500));
}
}
public function send_message($request) {
global $wpdb;
$conversation_id = $request->get_param('id');
$message_text = sanitize_textarea_field($request->get_param('message'));
$attachments = $request->get_param('attachments');
$current_user_id = get_current_user_id();
// 验证用户是否参与对话
if (!$this->is_user_in_conversation($current_user_id, $conversation_id)) {
return new WP_Error('permission_denied', '您不在这个对话中', array('status' => 403));
}
// 获取用户语言设置
$user_language = $this->get_user_language($current_user_id, $conversation_id);
// 插入原始消息
$messages_table = $wpdb->prefix . 'mlc_messages';
$wpdb->insert(
$messages_table,
array(
'conversation_id' => $conversation_id,
'sender_id' => $current_user_id,
'sender_type' => 'registered',
'message_type' => 'text',
'original_text' => $message_text,
'original_language' => $user_language,
'translation_status' => 'pending',
'attachments' => $attachments ? json_encode($attachments) : null,
'sent_at' => current_time('mysql')
)
);
$message_id = $wpdb->insert_id;
if (!$message_id) {
return new WP_Error('send_failed', '发送消息失败', array('status' => 500));
}
// 异步处理消息翻译
$this->process_message_translation_async($message_id, $conversation_id);
// 实时推送消息给其他参与者
$this->broadcast_message($message_id, $conversation_id);
return rest_ensure_response(array(
'success' => true,
'message_id' => $message_id,
'sent_at' => current_time('mysql')
));
}
private function process_message_translation_async($message_id, $conversation_id) {
// 使用WordPress的调度系统异步处理翻译
wp_schedule_single_event(time() + 1, 'mlc_process_message_translation', array(
'message_id' => $message_id,
'conversation_id' => $conversation_id
));
}
public function process_message_translation($message_id, $conversation_id) {
global $wpdb;
$messages_table = $wpdb->prefix . 'mlc_messages';
$participants_table = $wpdb->prefix . 'mlc_conversation_participants';
// 获取原始消息
$message = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $messages_table WHERE id = %d",
$message_id
));
if (!$message) {
return;
}
// 获取对话中的所有参与者(除了发送者)
$participants = $wpdb->get_results($wpdb->prepare(
"SELECT user_id, user_type, user_language
FROM $participants_table
WHERE conversation_id = %d
AND (user_id != %d OR user_type != 'registered')",
$conversation_id,
$message->sender_id
));
// 为每个语言需求生成翻译
$translations = array();
foreach ($participants as $participant) {
$target_language = $participant->user_language;
// 如果目标语言与原始语言相同,不需要翻译
if ($target_language === $message->original_language) {
$translations[$target_language] = $message->original_text;
continue;
}
// 检查是否已有该语言的翻译
if (!isset($translations[$target_language])) {
$translated_text = $this->translator->translate(
$message->original_text,
$target_language,
$message->original_language
);
$translations[$target_language] = $translated_text;
}
}
// 更新消息记录,存储翻译结果
$wpdb->update(
$messages_table,
array(
'translation_status' => 'completed',
'translated_text' => json_encode($translations),
'translated_language' => json_encode(array_keys($translations))
),
array('id' => $message_id)
);
// 通知客户端翻译完成
$this->notify_translation_complete($message_id, $conversation_id);
}
private function broadcast_message($message_id, $conversation_id) {
// 这里实现WebSocket或Server-Sent Events推送
// 实际实现取决于您选择的实时通信技术
// 示例:使用Action钩子让其他插件处理推送
do_action('mlc_message_sent', $message_id, $conversation_id);
}
// 其他必要的方法...
}
// assets/js/chat-frontend.js
class MLChat {
constructor(options) {
this.options = Object.assign({
container: '#mlc-chat-container',
apiUrl: mlc_ajax.ajax_url,
websocketUrl: mlc_ajax.websocket_url,
currentUserId: mlc_ajax.current_user_id,
currentUserLanguage: mlc_ajax.current_language,
nonce: mlc_ajax.nonce
}, options);
this.conversations = [];
this.activeConversation = null;
this.websocket = null;
this.init();
}
init() {
this.renderContainer();
this.loadConversations();
this.initWebSocket();
this.bindEvents();
}
renderContainer() {
const container = document.querySelector(this.options.container);
if (!container) return;
container.innerHTML = `
<div class="mlc-chat-wrapper">
<div class="mlc-chat-sidebar">
<div class="mlc-chat-header">
<h3>${mlc_ajax.i18n.conversations}</h3>
<button class="mlc-new-chat-btn">+ ${mlc_ajax.i18n.new_chat}</button>
</div>
<div class="mlc-conversations-list">
<!-- 对话列表将通过JavaScript动态加载 -->
</div>
<div class="mlc-user-profile">
<div class="mlc-user-avatar">
${this.getUserAvatar()}
</div>
<div class="mlc-user-info">
<span class="mlc-user-name">${mlc_ajax.current_user_name}</span>
<select class="mlc-user-language">
${this.getLanguageOptions()}
</select>
</div>
</div>
</div>
<div class="mlc-chat-main">
<div class="mlc-chat-empty-state">
<div class="mlc-empty-icon">💬</div>
<h4>${mlc_ajax.i18n.select_conversation}</h4>
<p>${mlc_ajax.i18n.select_conversation_desc}</p>
<button class="mlc-start-new-chat">${mlc_ajax.i18n.start_chat}</button>
</div>
<div class="mlc-chat-active" style="display: none;">
<div class="mlc-chat-header">
<div class="mlc-chat-info">
<h4 class="mlc-chat-title"></h4>
<span class="mlc-chat-participants"></span>
</div>
<div class="mlc-chat-actions">
<button class="mlc-translate-chat" title="${mlc_ajax.i18n.translate_chat}">🌐</button>
<button class="mlc-chat-settings" title="${mlc_ajax.i18n.settings}">⚙️</button>
</div>
</div>
<div class="mlc-messages-container">
<div class="mlc-messages-loading">${mlc_ajax.i18n.loading}</div>
<div class="mlc-messages-list"></div>
</div>
<div class="mlc-message-input-area">
<div class="mlc-input-tools">
<button class="mlc-attach-file" title="${mlc_ajax.i18n.attach_file}">📎</button>
<button class="mlc-emoji-picker" title="${mlc_ajax.i18n.emoji}">😊</button>
<select class="mlc-translation-mode">
<option value="auto">${mlc_ajax.i18n.translate_auto}</option>
<option value="manual">${mlc_ajax.i18n.translate_manual}</option>
<option value="off">${mlc_ajax.i18n.translate_off}</option>
</select>
</div>
<textarea class="mlc-message-input"
placeholder="${mlc_ajax.i18n.type_message}"></textarea>
<button class="mlc-send-message">${mlc_ajax.i18n.send}</button>
</div>
</div>
</div>
<div class="mlc-chat-modal" id="mlc-new-chat-modal" style="display: none;">
<div class="mlc-modal-content">
<h3>${mlc_ajax.i18n.new_conversation}</h3>
<div class="mlc-modal-body">
<input type="text" class="mlc-chat-title-input"
placeholder="${mlc_ajax.i18n.conversation_title}">
<div class="mlc-participants-selector">
<h4>${mlc_ajax.i18n.select_participants}</h4>
<div class="mlc-users-list"></div>
</div>
</div>
<div class="mlc-modal-footer">
<button class="mlc-modal-cancel">${mlc_ajax.i18n.cancel}</button>
<button class="mlc-modal-create">${mlc_ajax.i18n.create}</button>
</div>
</div>
</div>
</div>
`;
}
async loadConversations() {
try {
const response = await fetch(
`${this.options.apiUrl}/wp-json/mlc/v1/users/${this.options.currentUserId}/conversations`,
{
headers: {
'X-WP-Nonce': this.options.nonce
}
}
);
if (response.ok) {
const data = await response.json();
this.conversations = data;
this.renderConversationsList();
}
} catch (error) {
console.error('加载对话失败:', error);
}
}
async sendMessage(conversationId, messageText, attachments = []) {
try {
const response = await fetch(
`${this.options.apiUrl}/wp-json/mlc/v1/conversations/${conversationId}/messages`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': this.options.nonce
},
body: JSON.stringify({
message: messageText,
attachments: attachments
})
}
);
if (response.ok) {
const data = await response.json();
return data;
}
} catch (error) {
console.error('发送消息失败:', error);
throw error;
}
}
initWebSocket() {
if (!this.options.websocketUrl) return;
this.websocket = new WebSocket(this.options.websocketUrl);
this.websocket.onopen = () => {
console.log('WebSocket连接已建立');
// 发送身份验证
this.websocket.send(JSON.stringify({
type: 'auth',
userId: this.options.currentUserId,
nonce: this.options.nonce
}));
};
this.websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleWebSocketMessage(data);
};
this.websocket.onclose = () => {
console.log('WebSocket连接已关闭');
// 尝试重新连接
setTimeout(() => this.initWebSocket(), 5000);
};
}
handleWebSocketMessage(data) {
switch (data.type) {
case 'new_message':
this.handleNewMessage(data.message);
break;
case 'translation_complete':
this.handleTranslationComplete(data.messageId, data.translations);
break;
case 'user_online':
this.updateUserStatus(data.userId, true);
break;
case 'user_offline':
this.updateUserStatus(data.userId, false);
break;
}
}
handleNewMessage(message) {
// 如果消息属于当前活跃对话,则显示
if (this.activeConversation && this.activeConversation.id === message.conversation_id) {
this.appendMessage(message);
}
// 更新对话列表中的最后消息预览
this.updateConversationPreview(message.conversation_id, message);
// 显示通知
if (!document.hasFocus() || this.activeConversation?.id !== message.conversation_id) {
this.showNotification(message);
}
}
// 其他前端方法...
}
// assets/js/chat-frontend.js
class MLChat {
constructor(options) {
this.options = Object.assign({
container: '#mlc-chat-container',
apiUrl: mlc_ajax.ajax_url,
websocketUrl: mlc_ajax.websocket_url,
currentUserId: mlc_ajax.current_user_id,
currentUserLanguage: mlc_ajax.current_language,
nonce: mlc_ajax.nonce
}, options);
this.conversations = [];
this.activeConversation = null;
this.websocket = null;
this.init();
}
init() {
this.renderContainer();
this.loadConversations();
this.initWebSocket();
this.bindEvents();
}
renderContainer() {
const container = document.querySelector(this.options.container);
if (!container) return;
container.innerHTML = `
<div class="mlc-chat-wrapper">
<div class="mlc-chat-sidebar">
<div class="mlc-chat-header">
<h3>${mlc_ajax.i18n.conversations}</h3>
<button class="mlc-new-chat-btn">+ ${mlc_ajax.i18n.new_chat}</button>
</div>
<div class="mlc-conversations-list">
<!-- 对话列表将通过JavaScript动态加载 -->
</div>
<div class="mlc-user-profile">
<div class="mlc-user-avatar">
${this.getUserAvatar()}
</div>
<div class="mlc-user-info">
<span class="mlc-user-name">${mlc_ajax.current_user_name}</span>
<select class="mlc-user-language">
${this.getLanguageOptions()}
</select>
</div>
</div>
</div>
<div class="mlc-chat-main">
<div class="mlc-chat-empty-state">
<div class="mlc-empty-icon">💬</div>
<h4>${mlc_ajax.i18n.select_conversation}</h4>
<p>${mlc_ajax.i18n.select_conversation_desc}</p>
<button class="mlc-start-new-chat">${mlc_ajax.i18n.start_chat}</button>
</div>
<div class="mlc-chat-active" style="display: none;">
<div class="mlc-chat-header">
<div class="mlc-chat-info">
<h4 class="mlc-chat-title"></h4>
<span class="mlc-chat-participants"></span>
</div>
<div class="mlc-chat-actions">
<button class="mlc-translate-chat" title="${mlc_ajax.i18n.translate_chat}">🌐</button>
<button class="mlc-chat-settings" title="${mlc_ajax.i18n.settings}">⚙️</button>
</div>
</div>
<div class="mlc-messages-container">
<div class="mlc-messages-loading">${mlc_ajax.i18n.loading}</div>
<div class="mlc-messages-list"></div>
</div>
<div class="mlc-message-input-area">
<div class="mlc-input-tools">
<button class="mlc-attach-file" title="${mlc_ajax.i18n.attach_file}">📎</button>
<button class="mlc-emoji-picker" title="${mlc_ajax.i18n.emoji}">😊</button>
<select class="mlc-translation-mode">
<option value="auto">${mlc_ajax.i18n.translate_auto}</option>
<option value="manual">${mlc_ajax.i18n.translate_manual}</option>
<option value="off">${mlc_ajax.i18n.translate_off}</option>
</select>
</div>
<textarea class="mlc-message-input"
placeholder="${mlc_ajax.i18n.type_message}"></textarea>
<button class="mlc-send-message">${mlc_ajax.i18n.send}</button>
</div>
</div>
</div>
<div class="mlc-chat-modal" id="mlc-new-chat-modal" style="display: none;">
<div class="mlc-modal-content">
<h3>${mlc_ajax.i18n.new_conversation}</h3>
<div class="mlc-modal-body">
<input type="text" class="mlc-chat-title-input"
placeholder="${mlc_ajax.i18n.conversation_title}">
<div class="mlc-participants-selector">
<h4>${mlc_ajax.i18n.select_participants}</h4>
<div class="mlc-users-list"></div>
</div>
</div>
<div class="mlc-modal-footer">
<button class="mlc-modal-cancel">${mlc_ajax.i18n.cancel}</button>
<button class="mlc-modal-create">${mlc_ajax.i18n.create}</button>
</div>
</div>
</div>
</div>
`;
}
async loadConversations() {
try {
const response = await fetch(
`${this.options.apiUrl}/wp-json/mlc/v1/users/${this.options.currentUserId}/conversations`,
{
headers: {
'X-WP-Nonce': this.options.nonce
}
}
);
if (response.ok) {
const data = await response.json();
this.conversations = data;
this.renderConversationsList();
}
} catch (error) {
console.error('加载对话失败:', error);
}
}
async sendMessage(conversationId, messageText, attachments = []) {
try {
const response = await fetch(
`${this.options.apiUrl}/wp-json/mlc/v1/conversations/${conversationId}/messages`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': this.options.nonce
},
body: JSON.stringify({
message: messageText,
attachments: attachments
})
}
);
if (response.ok) {
const data = await response.json();
return data;
}
} catch (error) {
console.error('发送消息失败:', error);
throw error;
}
}
initWebSocket() {
if (!this.options.websocketUrl) return;
this.websocket = new WebSocket(this.options.websocketUrl);
this.websocket.onopen = () => {
console.log('WebSocket连接已建立');
// 发送身份验证
this.websocket.send(JSON.stringify({
type: 'auth',
userId: this.options.currentUserId,
nonce: this.options.nonce
}));
};
this.websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleWebSocketMessage(data);
};
this.websocket.onclose = () => {
console.log('WebSocket连接已关闭');
// 尝试重新连接
setTimeout(() => this.initWebSocket(), 5000);
};
}
handleWebSocketMessage(data) {
switch (data.type) {
case 'new_message':
this.handleNewMessage(data.message);
break;
case 'translation_complete':
this.handleTranslationComplete(data.messageId, data.translations);
break;
case 'user_online':
this.updateUserStatus(data.userId, true);
break;
case 'user_offline':
this.updateUserStatus(data.userId, false);
break;
}
}
handleNewMessage(message) {
// 如果消息属于当前活跃对话,则显示
if (this.activeConversation && this.activeConversation.id === message.conversation_id) {
this.appendMessage(message);
}
// 更新对话列表中的最后消息预览
this.updateConversationPreview(message.conversation_id, message);
// 显示通知
if (!document.hasFocus() || this.activeConversation?.id !== message.conversation_id) {
this.showNotification(message);
}
}
// 其他前端方法...
}
class MLC_Tools_Manager {
private $available_tools = array();
public function __construct() {
$this->init_tools();
add_action('widgets_init', array($this, 'register_widgets'));
add_shortcode('mlc_tool', array($this, 'tool_shortcode'));
}
private function init_tools() {
// 注册可用工具
$this->available_tools = array(
'currency_converter' => array(
'name' => __('货币转换器', 'mlc'),
'description' => __('实时货币汇率转换工具', 'mlc'),
'class' => 'MLC_Currency_Converter',
'icon' => '💰'
),
'timezone_converter' => array(
'name' => __('时区转换器', 'mlc'),
'description' => __('全球时区转换和会议时间安排', 'mlc'),
'class' => 'MLC_Timezone_Converter',
'icon' => '🕐'
),
'unit_converter' => array(
'name' => __('单位转换器', 'mlc'),
'description' => __('长度、重量、温度等单位转换', 'mlc'),
class MLC_Tools_Manager {
private $available_tools = array();
public function __construct() {
$this->init_tools();
add_action('widgets_init', array($this, 'register_widgets'));
add_shortcode('mlc_tool', array($this, 'tool_shortcode'));
}
private function init_tools() {
// 注册可用工具
$this->available_tools = array(
'currency_converter' => array(
'name' => __('货币转换器', 'mlc'),
'description' => __('实时货币汇率转换工具', 'mlc'),
'class' => 'MLC_Currency_Converter',
'icon' => '💰'
),
'timezone_converter' => array(
'name' => __('时区转换器', 'mlc'),
'description' => __('全球时区转换和会议时间安排', 'mlc'),
'class' => 'MLC_Timezone_Converter',
'icon' => '🕐'
),
'unit_converter' => array(
'name' => __('单位转换器', 'mlc'),
'description' => __('长度、重量、温度等单位转换', 'mlc'),


