Files
fc/server/tools/lang/lang.php
T
2026-05-23 22:10:14 +08:00

329 lines
10 KiB
PHP

<?php
/**----------------------------------------------------+
* 多语言版本翻译处理
* @author whjing2012@gmail.com
+-----------------------------------------------------*/
error_reporting(E_ALL ^E_NOTICE);
set_time_limit(1800);
ini_set("memory_limit", "2048M");
define('MOD', str_replace('\\', '/', realpath(dirname(__FILE__))));
define('ROOT', MOD.'/../..');
define('CLIENT_DIR', ROOT.'/client');
define('SERVER_DIR', ROOT.'/server');
define('TOOLS_DIR', ROOT.'/tools');
define('LANG_DATA', MOD.'/data');
define('LANG_XML_TPL', MOD.'/lang.xml'); // xml模板
define('LANG_DATA_CLI', 'client.php'); // 客户端相关抽取结果
define('LANG_DATA_SRV', 'server.php'); // 服务端相关抽取结果
define('LANG_DATA_ALL', 'all.php'); // 所有抽取翻译结果集合
define('LANG_DATA_XML', 'lang.xml'); // 抽取结果需翻译输出
define('LANG_DATA_CONF_CLI', 'lang.lua'); // 客户端语言
define('LANG_DATA_CONF_SRV', 'lang.erl'); // 服务端语言
define('LANG_TYPES', "tw,jp,en"); // 多语言版本类型
require_once(MOD."/lang_cli.php");
require_once(MOD."/lang_srv.php");
/* 是否为需要过滤的文件 */
function is_filter($file) {
// 过滤指定文件/目录
$array = array(
"lang.lua",
"log.hrl",
"gm_tools.erl", "lang.erl", "keyword_ad_data.erl", "keywords_data.erl",
"item_flag_data.erl"
);
$basefile = basename($file);
return in_array($basefile, $array);
}
// 获取客户端代码目录
function cli_dir($lang){
if(is_dir(CLIENT_DIR.'_'.$lang)){
return CLIENT_DIR.'_'.$lang;
}
return CLIENT_DIR;
}
// 获取服务端代码目录
function srv_dir($lang){
if(is_dir(SERVER_DIR.'_'.$lang)){
return SERVER_DIR.'_'.$lang;
}
return SERVER_DIR;
}
// 文件保存
function save_file($file, $content){
file_put_contents($file, $content);
}
// 读取文件
function read_file($file){
return file_get_contents($file);
}
// 保存成xml文件
function save_xml($file, $arr, $flag = false){
$str = "";
foreach($arr as $k => $v){
$str .= "<Row>";
$str .= "<Cell ss:Index='1'><Data ss:Type='String'>".str2xml_replace($k)."</Data></Cell>";
if($flag){
$str .= "<Cell ss:Index='2'><Data ss:Type='String'>".str2xml_replace($v)."</Data></Cell>";
}
$str .= "</Row>\n";
}
$tpl = read_file(LANG_XML_TPL);
$str = str_replace("{{rows}}", $str, $tpl);
save_file($file, $str);
}
// xml特殊字符替换处理
function str2xml_replace($str){
$str = str_replace("<", "&lt;", $str);
$str = str_replace(">", "&gt;", $str);
$str = str_replace("\n", "&#10;", $str);
$str = str_replace("\"", "&quot;", $str);
return trim($str);
}
// 写数组文件
function save_array($file, $arr){
$str = "<?php\n\$words=array();";
foreach($arr as $k => $v){
$k = trim(str_replace("'", "\'", $k));
$v = trim(str_replace("'", "\'", $v));
$str .= "\n\$words['$k'] = '$v';";
}
$str .= "\nreturn \$words;";
save_file($file, $str);
}
// 获取数组文件中数组
function get_file_array($file){
if(!is_file($file)) return array();
return require($file);
}
// 比对数组获取未翻译集合
function diff_array($all, $need){
$arr = array();
foreach($need as $k => $v){
if(!isset($all[$k])){
$arr[$k] = $v;
}
}
return $arr;
}
// 目录历遍处理
function fordir($path, $callfun, $funparams = array(), $is_next = true){
$handle = @opendir($path);
while($f = readdir($handle)){
if($f == '.' || $f == '..' || $f == '.svn' || $f == '.git') continue;
$file = $path.'/'.$f;
if(is_file($file) || $is_next){
call_user_func_array($callfun, array_merge(array($file), $funparams));
}
if(is_dir($file))
fordir($file, $callfun, $funparams, $is_next);
}
closedir($handle);
}
// 匹配多语言
function match_lang($lang){
$cli = array();
$srv = array();
// cli_config_match(cli_dir($lang)."/src/config", $cli, false);
cli_match(cli_dir($lang)."/src", $cli, true);
srv_match(srv_dir($lang)."/src/mod", $srv, true);
echo("需翻译客户端内容数量:".count($cli)."\n");
echo("需翻译服务端内容数量:".count($srv)."\n");
$all = get_file_array(LANG_DATA."/$lang/".LANG_DATA_ALL);
# $oldcli = get_file_array(LANG_DATA."/$lang/".LANG_DATA_CLI);
# $oldsrv = get_file_array(LANG_DATA."/$lang/".LANG_DATA_SRV);
# $cli = array_merge($cli, $oldcli);
# $srv = array_merge($srv, $oldsrv);
$need = array_merge($cli, $srv);
$diff = diff_array($all, $need);
save_array(LANG_DATA."/$lang/".LANG_DATA_CLI, $cli);
save_array(LANG_DATA."/$lang/".LANG_DATA_SRV, $srv);
save_xml(LANG_DATA."/$lang/".LANG_DATA_XML, $diff);
echo("需翻译内容数量:".count($diff)."\n");
echo("生成待翻译文件,请手动翻译:". LANG_DATA."/$lang/".LANG_DATA_XML. "\n");
}
// 合并翻译结果
function merge_lang($lang){
$rows = parse_xml(LANG_DATA."/$lang/".LANG_DATA_XML, "语言转换或翻译");
$all = get_file_array(LANG_DATA."/$lang/".LANG_DATA_ALL);
$cli = get_file_array(LANG_DATA."/$lang/".LANG_DATA_CLI);
$srv = get_file_array(LANG_DATA."/$lang/".LANG_DATA_SRV);
$need = array_merge($cli, $srv);
$num = 0;
foreach($rows as $row){
// echo("===============".$row['cn']."\n");
if(!isset($need[$row['cn']])) continue;
if(!isset($row['ret'])) continue;
if($row['ret'] == '') continue;
if(isset($all[$row['cn']]))
if($all[$row['cn']] == $row['ret']) continue;
if(!check_lang_ret($row['cn'], $row['ret'])) continue;
$all[$row['cn']] = $row['ret']; // 未做校验
++$num;
}
echo("正确翻译合并数量:$num\n");
if($num){
save_array(LANG_DATA."/$lang/".LANG_DATA_ALL, $all);
$diff = diff_array($all, $need);
save_xml(LANG_DATA."/$lang/".LANG_DATA_XML, $diff);
}
}
// 判断2个字符串包含某字符的数量是否一致
function check_lang_ret($str1, $str2, $arr = array('~ts','~s','~w','~f','{','}','[',']',',','%s','%d')){
foreach($arr as $v){
if(substr_count($str1, $v) != substr_count($str2, $v)) {
echo("翻译不正确: $str1 <> $str2\n");
return false;
}
}
return true;
}
// 判断语言类型
function check_lang_type($lang){
$types = explode(",", LANG_TYPES);
if(!in_array($lang, $types)){
exit("多语言类型[$lang]不正确:" . LANG_TYPES . "\n");
}
if(!is_dir(LANG_DATA."/$lang")){
mkdir(LANG_DATA."/$lang",0777,true);
}
}
// xml解析
function parse_xml($xmlfile, $sheetName, $headIndex = 1){
$res = simplexml_load_file($xmlfile);
$table = null;
$keys = array();
$rows = array();
$index = 0;
for($i=0; $i<count($res->Worksheet); $i++){
if($res->Worksheet[$i]->attributes('ss', true)->Name == $sheetName){
$table = $res->Worksheet[$i]->Table;
break;
}
}
foreach($table->Row[$headIndex] as $cell){
$keys[trim($cell->Data)] = $index;
++$index;
};
$index = 0;
foreach($table->Row as $row){
if(($index++)<=$headIndex) continue; //排除首行
if(!$row->Cell[0]->Data) continue;
$tmp = array();
foreach($keys as $key => $col){
$val = (string)$row->Cell[$col]->Data;
if(!$row->Cell[$col]){
$val = "";
}elseif($val == null){
$cell_ss = $row->Cell[$col]->children('ss', true);
if($cell_ss->Data){
$val = (string)$cell_ss->Data->children()->Font;
}
}
$tmp[$key] = trim($val);
}
$rows[] = $tmp;
}
return $rows;
}
/* 控制台颜色 */
function colorize($text, $status = "INFO"){
$out = "";
$status = strtoupper($status);
switch($status) {
case "INFO":
$out = "[92m"; //Green background
break;
case "ERROR":
$out = "[91m"; //Red background
break;
case "WARNING":
$out = "[93m"; //Yellow background
break;
default:
throw new Exception("Invalid status: " . $status);
}
return chr(27) . "$out" . "$text" . chr(27) . "[0m";
}
/* 警告、错误、Notice等抛出*/
function error_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
if (0 === error_reporting()) {
return false;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
// 帮助接口
function help(){
echo("php lang.php cin {FILE} // 对客户端指定文件注入多语言函数\n");
echo("php lang.php sin {FILE} // 对服务端指定文件注入多语言函数\n");
echo("php lang.php match {LANG} // 匹配客户端/服务端多语言信息并生成需翻译文件\n");
echo("php lang.php merge {LANG} // 合并翻译结果\n");
echo("php lang.php gen {LANG} // 导出生成客户端/服务端翻译结果文件\n");
exit();
}
// 入口函数
function main($argc, $argv){
if($argc < 3){
help();
}
$cmd = $argv[1];
set_error_handler('error_handler');
try {
if($cmd == 'cin'){
check_lang_type($argv[2]);
cli_infun(cli_dir($argv[2])."/src/".$argv[3]);
}else if($cmd == 'sin'){
check_lang_type($argv[2]);
$sinDir = "";
if($argv[3] == 'inc')
$sinDir = srv_dir($argv[2])."/inc";
else
$sinDir = srv_dir($argv[2])."/src/mod/".$argv[3];
srv_infun($sinDir);
}else if($cmd == 'match'){
check_lang_type($argv[2]);
match_lang($argv[2]);
}else if($cmd == 'merge'){
check_lang_type($argv[2]);
merge_lang($argv[2]);
}else if($cmd == 'gen'){
check_lang_type($argv[2]);
cli_lang($argv[2]);
srv_lang($argv[2]);
}else{
help();
}
restore_error_handler();
}
catch (ErrorException $e) {
echo colorize("Code: ".$e->getCode()."\n", "ERROR");
echo colorize("File: ".$e->getFile()."\n", "ERROR");
echo colorize("Line: ".$e->getLine()."\n", "ERROR");
echo colorize("Message: ".$e->getMessage()."\n", "ERROR");
restore_error_handler();
exit;
}
}
main($argc, $argv);