
简介针对 Discuz! 3.2 论坛系统的完美英文语言包面向需要搭建多语言社区或吸引海外用户的站长、开发者和运营人员可将注册登录、个人中心、版块管理、帖子操作、搜索、站内消息、积分系统、勋章系统以及插件和主题模板等前后台界面全面切换为英文帮助非中文用户顺畅地浏览、发帖与管理论坛解决非中文环境下的可读性与易用性问题。压缩包共194个文件以136个PHP语言文件为主体配合19个HTM模板、35个GIF与1个PNG界面图片、2个PSD源文件以及1个JS脚本整体仅772KB安装替换轻量便捷。语言包覆盖 Discuz! 3.2 全部核心模块翻译经精心校对力求准确自然管理员可一键启用英文模式开发者也能借助英文对照深入理解源码结构方便二次开发、插件定制与问题排查。目前已有1399人学习下载适合准备拓展国际用户群体、优化海外访问体验的论坛站点使用。1. 为什么“discuz!3.2 完美英文语言包”让老站长又爱又恨假如你手里正维护着一个 Discuz! 3.2 论坛想把它翻成英文站、面向海外用户开放搜到“discuz!3.2 完美英文语言包”时先别急着下载——官方从来没有发布过英文语言包。网上流传的“完美”版本大多是基于中文语言文件手工翻译或机翻后重新打包的产物质量参差覆盖范围也各不相同。它能解决两件事一是把前台界面、系统提示、邮件通知这些“程序输出”变成英文二是让新用户注册、发帖、收邮件时不再面对一串看不懂的中文。适合外贸站站长、接手海外论坛的运维以及靠 Discuz 做二次开发交付的外包工程师。但“完美”到底怎么验证靠的是把语言包拆开看再上线前认真排查一轮。2. 先摸清 Discuz! 3.2 语言包结构改哪些文件才算数Discuz! 3.2 里的语言包不是一个文件而是一组 PHP 数组文件。很多人拿到英文包后直接覆盖进source/language就完事结果前台变了英文后台还是中文发出去的邮件还是中文于是断言“这个包不完美”。其实问题多半出在没搞清楚语言包的覆盖边界。2.1 语言文件的位置清单与各自职责在 Discuz! 3.2 安装目录里语言文件全部集中在source/language/下按模块又分成若干子目录和独立文件。少说也有一二十个但真正影响日常用户体验的是下面这几类文件路径覆盖范围source/language/forum/lang_template.php版块列表、主题页、发帖表单、按钮文字等前台模板字串source/language/forum/lang_message.php发帖权限、验证码、操作成功/失败等前台提示消息source/language/lang_email.php注册验证、找回密码、新通知等系统邮件模板source/language/admin/lang_admin.php后台左侧菜单、设置项名称、管理操作按钮source/language/lang_install.php安装向导界面文字这里要特别提醒template/default/目录下的模板文件里也有大量直接写死的中文它们不属于语言包文件。只覆盖source/language目录永远做不到“完美英文”。2.2 一条提示从语言包到屏幕的取词链路Discuz! 的语言包文件本质上就是一个返回 PHP 数组的文件。以lang_template.php为例常见结构是声明一个$lang数组再把 key 对应的文本抛给模板引擎?php // source/language/forum/lang_template.php示意结构 $lang array( forum Forum, thread Thread, post Post, reply Reply, login Log in, register Sign up, );这里的 key 是程序里写死的调用标识value 才是最终显示在页面上的文字。Discuz! 初始化时会把对应的语言文件加载进来挂到全局语言数组里模板里通过{lang xxx}或语言函数按 key 取词。所以翻译时只改 valuekey 一个都不能动。还有一类 key 会带占位符比如thread_count Threads: %s。里面的%s、%d是后面要拼入数字或用户名的翻译时如果手滑删了占位符页面上就会显示空缺或异常。另外修改语言文件后必须更新缓存否则前台拿到的还是旧的数组。Discuz 的缓存机制是个黑匣子很多新手在这里翻车以为自己文件没改对。2.3 为什么第三方“完美英文语言包”换机后就不完美第三方英文包一般只覆盖前台模板和一部分提示消息后台语言包、邮件模板经常被忽略。机翻的术语不一致也很常见比如 “thread” 在导航里是 “Thread”在提示消息里却变成 “Topic”用户会觉得很山寨。再一个问题Discuz! 3.1 到 3.2 的语言 key 并非完全一致。有些 key 被废弃有些是新加的。网上流传的英文包如果没有标明适用版本覆盖后轻则个别按钮空白重则 PHP 直接报错白屏。判断两套语言包的 key 差异需要专门的比对工具下一章我会给一个可以直接跑的 PHP 脚本。还有一块语言包永远覆盖不到数据库里存的中文配置比如版块名称、用户组名、主题分类。它们存在表里不是程序输出SQL 改不了的部分只能人工维护。插件也是语言包的重灾区插件目录下可能有自己的语言文件优先级独立于主语言包。这些边界问题后面几章会逐个展开。3. 手工落地英文语言包从备份到重建缓存的完整操作搞清楚了边界就可以动手了。这一章给一套我验证过多次的操作流程覆盖备份、对比、覆盖、清缓存、扫模板五个环节。3.1 先备份给操作留后悔药任何直接覆盖source/language的操作都是改程序文件开工前必须把语言目录和数据库都备份一遍否则改到一半想回退连原版中文都没了。# 备份语言文件目录cp -a 保留原文件权限与属主 cp -a source/language source/language.bak.$(date %Y%m%d) # 备份数据库--default-character-setutf8 防止导出时中文乱码 mysqldump -u root -p --default-character-setutf8 discuz discuz.bak.$(date %Y%m%d).sql逻辑说明cp -a是归档模式能保留原目录的权限和属主避免覆盖后 PHP 进程因为权限变化读不了文件mysqldump加字符集参数是防止导出文件里中文变成问号恢复时连原始中文都找不回来。参数说明discuz要换成你的实际数据库名数据库账号密码按config/config_global.php里的配置填如果表前缀不是默认的pre_后面所有 SQL 里的pre_也要一并替换。另外config/config_global.php和config/config_ucenter.php也建议各复制一份语言改造过程中有时会改动字符集或缓存相关配置留个底更稳。3.2 用 PHP 差异脚本核对两套语言包的 key第三方英文包和自己正在用的语言文件key 很可能对不上。直接覆盖前先跑一个简单的差异检查?php // diff_lang_keys.php // 用法: php diff_lang_keys.php 英文包文件 当前语言文件 function load_lang($file) { $lang array(); include $file; // 语言文件内是 $lang array(...) return $lang; } $A load_lang($argv[1]); $B load_lang($argv[2]); $onlyA array_diff(array_keys($A), array_keys($B)); $onlyB array_diff(array_keys($B), array_keys($A)); printf(英文包独有 key: %d 个\n, count($onlyA)); printf(当前包独有 key: %d 个\n, count($onlyB)); echo implode(,, array_slice($onlyA, 0, 10)), PHP_EOL;逻辑说明语言文件是纯 PHP 数组include进函数作用域后就能拿到$lang变量。放在函数里是为了避免两个文件都声明$lang时互相覆盖。array_keys取出所有 key再用array_diff比较两边的差异。参数说明两个文件路径都要写绝对路径或相对于当前目录的路径。输出会显示差异数量和前 10 个独有 key。如果差异超过几十个最好不要直接覆盖否则会出现大量未定义语言变量页面满屏报错。这个脚本同样适用于source/language/admin/下的后台语言包。3.3 覆盖语言文件后强制重建缓存语言包源文件改完后缓存不重建等于白改。Discuz! 会把语言数组、模板解析结果等写进data/cache/下的 PHP 文件前台读取的是缓存而不是源文件。# 只清理 data/cache 下生成的 PHP 缓存文件Discuz 会在访问时自动重建 find data/cache -name *.php -delete逻辑说明Discuz 的缓存文件都是 PHP 格式程序运行时发现缓存缺失会自动重新生成所以直接删文件比进后台点“更新缓存”更彻底很多后端操作场景下也更快。参数说明命令要在 Discuz! 根目录执行如果根目录不是当前目录就把data/cache换成绝对路径。不要删除data/cache目录下的.htaccess和index.htm这两个文件是安全保护用的。删完第一次访问会慢一些因为要重建全部缓存属正常现象。提示生产环境如果访问量大建议选在低峰期操作或者直接用后台“工具 - 更新缓存”按钮。CLI 删缓存是救急手段别养成习惯。3.4 模板里的中文硬编码语言包之外的第二战场语言包文件处理完模板里还有大量硬编码中文比如导航栏的“社区”“论坛”、页脚的“联系我们”。这些不清理英文版体验直接打折扣。扫描模板目录里的中文字符串我习惯用 perl 一行式# 递归扫描模板目录里的中文字符串并输出文件名、行号和内容 perl -Mutf8 -e while(){ if(/[\x{4e00}-\x{9fa5}]/){ print $ARGV:$.:$_ } } $(find template/default -name *.htm)逻辑说明-Mutf8让 Perl 按 UTF-8 处理字符[\x{4e00}-\x{9fa5}]是中文基本区 Unicode 范围。用find先把模板文件列表抓出来再逐行匹配输出。参数说明template/default要换成你自己的模板目录比如第三方风格是template/yourstyle。除了.htm模板目录里的.js和.css文件也可能有中文提示需要一并扫。清理策略是模板里的固定导航文字优先改成语言变量比如把a hrefforum.php社区/a改成a hrefforum.php{lang forum}/a。前提是语言包里已经有forum这个 key否则页面会直接显示{lang forum}原始字符串。页面内嵌的帖子内容、用户签名这些动态数据不归语言包管靠用户自己用英文发布。4. 后台管理界面英文化比前台多一倍的隐蔽工作量前台界面变英文后后台还是一堆中文对运营和技术维护来说很别扭客户看到截图也会觉得没完工。后台英文化的工作量比前台大因为它有不少文字是直接写在程序模块里的不走语言包。4.1 为什么覆盖 lang_admin.php 不等于后台全英文后台界面由菜单和操作页面两部分组成。左侧菜单、设置项名称这些确实由source/language/admin/下的语言文件控制但每个管理模块文件里还有大量表单提示、校验错误、操作日志文字。比如source/admincp/admin_forums.php里新增版块时的表单说明很多版本就是 PHP 字符串里直接写死的中文。常见做法是先翻译source/language/admin/下的语言文件再扫描source/admincp/下的 PHP 源码把硬编码中文逐个替换成语言包引用。替换方式看代码结构能加语言变量就加加不了就直接把字符串改成英文。4.2 用扫描脚本揪出后台程序里的中文残留手翻几十个后台文件不现实我一般先跑一个 PHP 扫描脚本把中文全部列出来再按文件优先级处理?php // scan_chinese.php // 用法: php scan_chinese.php 目录路径 [扩展名过滤] // 示例: php scan_chinese.php source/admincp php,htm,html $dir isset($argv[1]) ? $argv[1] : .; $exts isset($argv[2]) ? explode(,, $argv[2]) : array(php, htm, html); $it new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS) ); foreach ($it as $file) { if (!in_array($file-getExtension(), $exts)) { continue; } $lines file($file-getPathname()); foreach ($lines as $n $line) { if (preg_match(/[\x{4e00}-\x{9fa5}]/u, $line)) { printf(%s:%d: %s\n, $file-getPathname(), $n 1, trim($line)); } } }逻辑说明递归遍历目录按扩展名过滤文件逐行用正则匹配中文输出文件绝对路径、行号和原文。脚本只做定位不做自动替换避免把注释里的中文或 JS 里的逻辑误改掉。参数说明$dir传绝对路径最稳妥比如扫描后台就传source/admincp$exts缺省是php,htm,html如果还想扫模板目录下的 JS就把js加进第二个参数。脚本输出的中文分为三类真正要翻译的界面字串、PHP 注释、JS 弹窗提示。优先级是先处理输出函数和 HTML 里的文字再处理 JS 提示最后忽略注释。4.3 邮件模板是“英文感”最容易露馅的地方很多站长以为前台英文就完事了结果用户注册完收到一封中文验证邮件一下子露馅。系统邮件的正文模板在source/language/lang_email.php里。第三方英文包漏掉这个文件的情况非常常见需要重点检查。翻译时最容易踩的坑是占位符。邮件模板里{username}、{bbname}、{siteurl}这些变量名不能翻译发信时系统会把它们替换成实际用户名、论坛名、站点地址。正确的译法是// source/language/lang_email.php示意 register_email Welcome to {bbname}, {username}! Please click the link below to activate your account: {siteurl},逻辑说明变量名用大括号包住Discuz 发信时做模板变量替换。如果把{username}改成 “user name” 这种词占位符就失效了用户收到信里会看到一串原样的{username}。另外后台“邮件设置”里的模板如果已经被修改过语言包文件改了也不一定生效因为邮件模板可能已存进数据库。遇到这种情况到后台找到对应邮件模板点重置或手动改成英文并保存。4.4 数据库里的中文语言包管不到的最后一个角落语言包改变的是程序输出但版块名称、用户组名称、主题分类这些内容存的是数据不是语言需要直接改数据库或后台手动改。英文站上线前至少要把这几张表里的配置清一遍数据表内容示例pre_forum_forum版块名称name、版块描述descriptionpre_common_usergroup用户组名称titlepre_forum_threadclass主题分类名称name改之前先查再改别拿 UPDATE 一把梭-- 先查出版块 ID 再更新避免误伤 SELECT fid, name, description FROM pre_forum_forum; -- 确认 fid2 是目标板块后再更新 UPDATE pre_forum_forum SET name General Discussion, description Talk about anything here WHERE fid 2;逻辑说明先 SELECT 看清数据再 UPDATE。fid 条件是示例实际以自己的站为准。参数说明pre_前缀要和config_global.php里的表前缀设置一致。重点提醒不要尝试用 SQL 批量替换帖子内容表里的中文那不是语言包的职责误操作会导致帖子正文被改坏。5. 避坑排查笔记英文语言包上线前后的五个常见翻车点这一章是踩坑记录每条都对应一个真实出现过的症状、原因和处理方式排序按发生频率来。5.1 界面出现白色菱形问号或方框文件编码和 BOM 问题现象英文单词中间夹着或者页面顶部多出几个乱码字符有些页面直接白屏。原因语言包文件被 Windows 记事本或某些编辑器存成了带 BOM 的 UTF-8PHP 输出时把 BOM 字节当成内容输出导致headers already sent轻则乱码重则白屏。解决批量检查source/language下是否有文件带 BOMgrep -rl $\xEF\xBB\xBF source/language找到后用编辑器把文件另存为“UTF-8 无 BOM”或者用脚本批量去 BOM。判断标准处理后文件开头不能有EF BB BF三个字节。5.2 改完语言包、后台也更新了缓存前台还是大部分中文现象语言文件确实改了后台缓存也更新了但导航、页头、按钮依然是中文。原因站点用的不是默认模板而是第三方模板或手工改过的模板固定文字直接写在模板 HTML 里根本没有调用语言包。解决把template/default换成你实际用的风格目录重新跑一遍中文扫描。发现硬编码文字后优先改成{lang xxx}语言变量如果这个模板是买来的商业风格改完要保留一份记录下次升级风格时重新打补丁。5.3 某个页面突然白屏日志里只有一条 PHP Parse error现象覆盖英文包后forum.php或后台某个子菜单直接 500PHP 错误日志只显示syntax error, unexpected ...。原因语言包 PHP 文件语法错误。常见于第三方包编辑时删了一个逗号、括号没闭合或者文件被截断。解决对所有改动过的语言文件逐个做 PHP 语法检查php -l source/language/forum/lang_template.php php -l source/language/forum/lang_message.php-l只检查语法不执行代码报错会直接指出文件和行号。如果语法正常但页面还是白屏打开 PHP 的display_errors看具体错误多半是某个语言 key 被误删程序调用时拿不到值。经验是每次只改一个模块的语言文件刷新一页验证通过后再改下一个。不要几十个文件一次性覆盖出问题后定位成本翻倍。5.4 邮件发出来英文是英文但用户名变成乱码现象邮件模板英文化之后正文没问题但{username}、{bbname}替换出来的用户名和论坛名是乱码。原因邮件发送时字符集设置不对或数据库连接字符集和页面字符集不一致。这是环境问题不是语言包翻译问题。解决先查数据库字符集SHOW VARIABLES LIKE character_set_database;确认数据库是utf8再检查config/config_global.php里的数据库字符集配置和模板字符集是否一致。最后确认邮件头里的Content-Type: text/html; charsetutf-8没有被改成 GBK。排查顺序先环境后语言包别一上来就重翻文件。5.5 插件后台还是中文语言包管不到的“法外之地”现象前台和主后台都英文了但某个插件后台页面或插件的用户通知还是中文。原因插件目录source/plugin/xxx/下存在独立语言包或硬编码中文加载优先级高于或独立于主语言包。解决先看插件目录里有没有language子目录或语言 PHP 文件有就先翻译没有就扫描插件目录里的模板文件把硬编码中文替换掉。常见做法是插件的语言包支持自定义翻译后更新缓存即生效。这类工作没有统一公式因为每个插件写法不同但规律一致优先找插件自带语言文件找不到再动模板。用了大量第三方插件的站点“完美英文”的成本主要在插件上。6. 验证“完美”英文语言包是否达标一页脚本揪出漏网中文语言包改完最后一步不是重启而是验证。我习惯把这个扫描脚本当成语言包的单测每次改完都跑一遍# scan_zh.py # 用法: python scan_zh.py /var/www/discuz import os import re import sys zh re.compile(r[\u4e00-\u9fa5]) root sys.argv[1] if len(sys.argv) 1 else . exts (.php, .htm, .html, .js, .sql) for dirpath, dirs, files in os.walk(root): # 跳过缓存和备份目录避免误报 if data/cache in dirpath or .bak in dirpath: continue for fn in files: if not fn.endswith(exts): continue path os.path.join(dirpath, fn) with open(path, encodingutf-8, errorsignore) as f: for lineno, line in enumerate(f, 1): if zh.search(line): print(f{path}:{lineno}:{line.strip()[:120]})逻辑说明递归遍历站点目录扫描 PHP、HTML、JS、SQL 四类文件命中中文字符就输出文件路径、行号和内容片段。跳过data/cache和.bak目录是为减少误报缓存文件由系统生成不需要翻译。errorsignore防止遇到非 UTF-8 文件时整个脚本中断。参数说明root改成论坛绝对路径如果只想扫模板把路径指向template目录会更快。跑完把输出重定向到文件再逐条过滤python scan_zh.py /var/www/discuz zh_report.txt拿到报告后我的习惯是做三件事第一把 PHP 注释和数据库内容标记为“可忽略”第二把模板硬编码按前面说的方法改成语言变量或直接换英文第三做一次“新用户全流程”走查从注册页开始收验证邮件、激活、发帖、回复、进后台改设置全程截图。只有截图里一个中文都不出现这个语言包才配得上“完美”两个字。我自己有一次就是改完语言包直接上线结果用户收到一封中文找回密码邮件整站“英文感”瞬间破功。后来养成了扫描脚本加全流程走查的双保险习惯再没在语言包上返过工。希望帮到你。本文还有配套的精品资源点击获取