思路:javascript
1,將模板中須要翻譯的內容使用特定標籤包裹。在加載模板的時候對制定標籤進行翻譯操做php
2, 創建翻譯數據庫,對要翻譯的內容首先使用第三方api翻譯併入庫,對不合理的地方,進行人工更新。css
code:html
1,session中存儲語言信息html5
/** * 語言信息初始化 * author liuxiaodong * date 2018/7/12 09:57 * @throws Exception */ protected function _initLauInfo() { //添加類映射 Think::addMap( [ 'translate\Translate' => dirname( THINK_PATH ) . DIRECTORY_SEPARATOR . 'Lib' . DIRECTORY_SEPARATOR . 'Extend' . DIRECTORY_SEPARATOR . 'Translate' . DIRECTORY_SEPARATOR . 'Translate' . EXT ] ); if( !($this->translate instanceof Translate ) ) $this->translate = new Translate(); $this->_initLauguage(); $this->_initLauArr(); //初始化語言數組信息 } /** * 設置語言信息 * @param bool $force 是否強制刷新語言 */ protected function _initLauguage( $language = Translate::ZH, $force = false ) { if( $force ) $this->setSessInfo( $this->lanSessName, null ); $tmpLau = $this->getSessInfo( $this->lanSessName ); if( !$tmpLau ){ $tmpLau = $language; $this->setSessInfo( $this->lanSessName, $tmpLau ); } $this->language = $tmpLau; }
/** * 設置語言數組信息 */ private function _initLauArr() { $this->lanShowArr = [ Translate::ZH => '中文', Translate::EN => 'Englist', Translate::KOR => '한국어', Translate::CHT => '繁體字', Translate::JP => '日本語' ]; }
2,改寫模板的一些基礎方法java
/** * 重寫display。加入模板語言過濾 */ protected function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') { $this->assign('lanShowArr', $this->language, false ); $this->assign( 'lau', $this->lanShowArr, false ); $this->assign( 'title', ( $this->title ? $this->title . ' · ' : '' ) . '馬卡龍·管理後臺' ); //title設置 $this->assign( 'user', $this->getSessInfo( $this->userSessName ), false ); $this->assign( 'menu', $this->getSessInfo( $this->menuSessName ), true ); $this->assign( 'curl', strtolower( MODULE_NAME . '/' . CONTROLLER_NAME . '/' ) ); $this->assign( 'fullurl', strtolower( MODULE_NAME . '/' . CONTROLLER_NAME . '/' . ACTION_NAME ) ); define( 'THEME_PATH', '.' . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . MODULE_NAME . DIRECTORY_SEPARATOR ); // 視圖開始標籤 Hook::listen('view_begin',$templateFile); // 解析並獲取模板內容 $content = $this->view->fetch($templateFile,$content,$prefix); // 輸出模板內容 $this->_render($this->_translate( $content ),$charset,$contentType); // 視圖結束標籤 Hook::listen('view_end'); } /** * 解析須要翻譯的中文內容 */ private function _translate( $content ) { $start_sign = C('START_CN');//定義標籤信息 $end_sign = C('END_CN'); $pattern = "/{$start_sign}(.*?){$end_sign}/is"; $content = preg_replace_callback( $pattern, function( $matchs ) { if( $matchs && $matchs[1] ) { return $this->translate->run( $matchs[1], $this->language ); } }, $content ); return $content; } /** * 輸出內容文本能夠包括Html * @access private * @param string $content 輸出內容 * @param string $charset 模板輸出字符集 * @param string $contentType 輸出類型 * @return mixed */ private function _render($content,$charset='',$contentType=''){ if(empty($charset)) $charset = C('DEFAULT_CHARSET'); if(empty($contentType)) $contentType = C('TMPL_CONTENT_TYPE'); // 網頁字符編碼 header('Content-Type:'.$contentType.'; charset='.$charset); header('Cache-control: '.C('HTTP_CACHE_CONTROL')); // 頁面緩存控制 header('X-Powered-By:ThinkCMF'); // 輸出模板文件 echo $content; } /** * 重寫assign方法, * @param mixed $name * @param string $value * @param bool $translate 是否翻譯,默認否 * @return \Think\Action|void */ protected function assign( $name, $value ='', $translate = false ) { if( $value && $translate ) { $this->_translateAssign( $value ); } parent::assign($name,$value); } /** * assign 翻譯 * @param mixed $data * @throws * @return void */ private function _translateAssign( &$data ) { if( is_array( $data ) ) { array_walk_recursive( $data, function( &$val ) { if( is_string( $val ) && ( strlen( $val ) != mb_strlen( $val, 'utf8' ) ) ) { $val = $this->translate->run( $val, $this->language ); } } ); }else { if( strlen( $data ) != mb_strlen( $data ) ) $data = $this->translate->run( $data, $this->language ); } } /** * 重寫ajaxReturn */ protected function ajaxReturn( $data, $type='', $translate = true) { if( $data && $translate ) $this->_translateAssign( $data ); parent::ajaxReturn( $data, $type ); }
<?php /** * Created by PhpStorm. * User: lxd * Date: 2018/7/3 * Time: 16:15 */ /** * 多語言表 create table mkl_translate ( id int unsigned auto_increment, zh varchar(100) not null default '' comment '中文', en varchar(1000) not null default '' comment '英文', jp varchar(1000) not null default '' comment '日文', kor varchar(1000) not null default '' comment '韓文', cht varchar(1000) not null default '' comment '繁體中文', zh_md5 varchar(32) not null default '' comment '中文md5', primary key (`id`), unique key (`zh_md5`) ) engine innodb charset utf8 comment '多語言對應表'; */ namespace translate; use Think\Exception; use Think\Log; use Think\Model; use Think\Think; class Translate { protected $db = null; protected $redis = null; protected $sdk = null; //來自百度api const ZH = 'zh'; //中文 const EN = 'en'; //英文 const JP = 'jp'; //日文 const KOR = 'kor'; //韓語 const CHT = 'cht'; //繁體中文 //對應數據庫字段信息 public static function getAllType( $isTrans = false ) { //是否partner應用請求而且要求翻譯 if( $isTrans ) { return [ self::ZH => '<{中文}>', self::EN => '<{英文}>', self::JP => '<{日文}>', self::KOR => '<{韓語}>', self::CHT => '<{繁體中文}>', ]; } return [ self::ZH => '中文', self::EN => '英文', self::JP => '日文', self::KOR => '韓語', self::CHT => '繁體中文', ]; } public function __construct() { $this->db = new Model( 'translate' ); if( !( $this->db instanceof Model) ) throw new Exception( '初始化數據庫失敗~' ); } /** * 轉換語言 * 1,查緩存,redis,hash * 2,查數據庫,有,入緩存 * 3,查接口,入庫,入緩存 * @param string $str 要轉換的語言 * @param string $type 要轉換成的語言類型 * @return mixed/exception */ public function run( $str, $type = self::EN, $from = self::ZH ) { if( $type == self::ZH || empty( $str ) || !in_array( $type, array_keys( self::getAllType() ) ) ) return $str; if( $from != self::ZH ) throw new Exception( '很抱歉,當前僅支持中文轉換爲其餘文字' ); return preg_replace_callback( '/([\x{4e00}-\x{9fa5}]+)/u', function( $match ) use ( $type, $from ) { return $this->selectDb( $match[1], $type, $from ); }, $str ); } /** * 查庫 * @param string $force 是否強制讀庫 */ protected function selectDb( $str, $to, $from, $force = false ) { if( $from != self::ZH ) throw new Exception( '很抱歉,當前僅支持中文轉換爲其餘文字' ); $str_md5 = md5( $str ); $res = ''; //redis 暫放 $redisRes = false; if( !$redisRes || $force ) { $info = $this->db->field( $to )->where( ['zh_md5' => $str_md5] )->find(); if( $info && isset( $info[$to] ) && $info[$to] ) { //更新redis return $info[$to]; } import( 'Lib.Extend.Translate.sdk.Baidu', dirname( THINK_PATH ) ); $this->sdk = new Baidu(); $str = addslashes( $str ); $res = $this->sdk->run( $str, $to, $from ); $res = addslashes( $res ); if( $res ) { $sql = "INSERT INTO {$this->db->getTableName()} (`{$from}`,`{$to}`,`zh_md5`) VALUES ( '{$str}', '{$res}', '{$str_md5}' ) ON DUPLICATE KEY UPDATE `{$to}` = '{$res}'"; $dbres = $this->db->execute( $sql ); if( !$dbres ) Log::record( '翻譯結果入庫失敗,sql= ' . var_export( $sql, true ) ); //redis 更新 } } return $res; } }
{__NOLAYOUT__} <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta charset="utf-8" /> <title><{登錄頁面}></title> <meta name="description" content="User login page" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <!-- bootstrap & fontawesome --> <link rel="stylesheet" href="__CSS__/bootstrap.min.css" /> <link rel="stylesheet" href="__FONT_AWESOME__/4.5.0/css/font-awesome.min.css" /> <link rel="stylesheet" href="__CSS__/anti-bot-style.css" /> <!-- text fonts --> <!--<link rel="stylesheet" href="__CSS__/fonts.googleapis.com.css" />--> <!-- ace styles --> <link rel="stylesheet" href="__CSS__/ace.min.css" /> <!--[if lte IE 9]> <link rel="stylesheet" href="__CSS__/ace-part2.min.css" /> <![endif]--> <link rel="stylesheet" href="__CSS__/ace-rtl.min.css" /> <!--[if lte IE 9]> <link rel="stylesheet" href="__CSS__/ace-ie.min.css" /> <![endif]--> <!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries --> <style> .help-block {color:red;font-size: 12px;} </style> <!--[if lte IE 8]> <script src="__JS__/html5shiv.min.js"></script> <script src="__JS__/respond.min.js"></script> <![endif]--> </head> <body class="login-layout light-login"> <div class="main-container"> <div class="main-content"> <div class="row"> <div class="col-sm-10 col-sm-offset-1"> <div class="login-container"> <div class="center" style="margin-bottom: 30px;"> <h1> <i class="ace-icon fa fa-leaf green"></i> <span class="red"><{??}></span> <span class="grey" id="id-text2" style="font-size: 16px;"><{管理後臺}></span> <select class="setLau" style="height:19px;font-size:12px;text-align: center;"> <foreach name="lau" item="vo"> <option value="{$key}" <if condition="$lanShowArr eq $key">selected</if>>{$vo}</option> </foreach> </select> </h1> </div> <div class="space-6"></div> <div class="position-relative"> <div id="login-box" class="login-box visible widget-box no-border"> <div class="widget-body"> <div class="widget-main"> <h4 class="header blue lighter bigger"> <i class="ace-icon fa fa-coffee green"></i> <{請輸入如下信息}>: </h4> <div class="space-6"></div> <form id="validation-form" method="post" action="{:U('login')}"> <fieldset> <label class="block clearfix"> <span class="block input-icon input-icon-right"> <input type="text" name="username" id="username" class="form-control" placeholder="<{用戶名}>" /> <i class="ace-icon fa fa-user"></i> </span> </label> <label class="block clearfix"> <span class="block input-icon input-icon-right"> <input type="password" name="password" id="password" class="form-control" placeholder="<{密碼}>" /> <i class="ace-icon fa fa-lock"></i> </span> </label> <label class=" clearfix verify" style="display: none;"> <div id="anti_bot_frame" class="anti-bot-frame-large"></div> <input type="hidden" name="verify" id="anti_bot_frame_hidden" value="" /> </label> <div class="space"></div> <div class="clearfix"> <input type="submit" class="width-35 pull-right btn btn-sm btn-primary" value="<{登錄}>"/> </div> <div class="space-4"></div> </fieldset> </form> <div class="space-6"></div> </div><!-- /.widget-main --> </div><!-- /.widget-body --> </div><!-- /.login-box --> </div><!-- /.position-relative --> </div> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.main-content --> </div><!-- /.main-container --> <!-- basic scripts --> <!--[if !IE]> --> <script src="__JS__/jquery-2.1.4.min.js"></script> <!-- <![endif]--> <!--[if IE]> <script src="__JS__/jquery-1.11.3.min.js"></script> <![endif]--> <script src="__JS__/jquery.validate.min.js"></script> <script type="text/javascript"> anti_bot_verification_path = '{:U('Partner/public/getImage')}'; anti_bot_verification_init = '{:U('Partner/public/initVerify')}'; anti_bot_verification_verify = '{:U('Partner/public/verify')}'; if('ontouchstart' in document.documentElement) document.write("<script src='__JS__/jquery.mobile.custom.min.js'>"+"<"+"/script>"); $('.setLau').on('change',function() { var _this = $(this); $.get( '{:U('Public/setLau')}', {'lau':_this.val()}, function( res ) { if(res.msg == 'ok') window.location.reload(); }, 'json'); }); $('#validation-form').validate({ ignore: [], errorElement: 'div', errorClass: 'help-block', rules:{ username: "required", password: { required: true, minlength: 6 }, verify : "required" }, messages:{ username: "<{用戶名不能爲空}>", password: { required: "<{密碼不能爲空}>", minlength: "<{密碼最少6個字符}>" }, verify : "<{請拖動圖標完成驗證}>" } }); $('input[name="password"]').on( 'keyup', function() { var _val = $(this).val(); if( _val.length >= 6 ) { $('.verify').show(); } }) </script> <script src="__JS__/verify.js"></script> </body> </html>
3, 創建後臺翻譯列表jquery
4,問題ajax
1,翻譯每次都是單次請求,沒有作批量請求redis
2,要讀庫或者redis,不如直接在文本中寫的速度快sql
感受優勢就是開發快,並且後期對語言 的維護性好些,直接後臺改就行
效果:
後臺: