* finish task #5827.

This commit is contained in:
wangyidong
2019-06-21 16:12:00 +08:00
parent df0f71d7e8
commit e4ca8efe7b
7 changed files with 289 additions and 141 deletions
+3
View File
@@ -45,6 +45,7 @@ $filter->mail = new stdclass();
$filter->user = new stdclass();
$filter->block = new stdclass();
$filter->file = new stdclass();
$filter->translate = new stdclass();
$filter->block->default = new stdclass();
$filter->block->main = new stdclass();
@@ -210,3 +211,5 @@ $filter->git->cat->get['repoUrl'] = 'reg::base64';
$filter->git->diff->get['repoUrl'] = 'reg::base64';
$filter->svn->cat->get['repoUrl'] = 'reg::base64';
$filter->svn->diff->get['repoUrl'] = 'reg::base64';
$filter->translate->module->cookie['translateView'] = 'equal::split';
+15 -5
View File
@@ -143,14 +143,23 @@ class translate extends control
}
if(empty($referLang))
{
$langs = json_decode($this->config->global->langs, true);
$referLang = $langs[$language]['reference'];
$langs = json_decode($this->config->global->langs, true);
if(isset($langs[$language]))
{
$referLang = $langs[$language]['reference'];
}
else
{
$referLang = $language == 'zh-cn' ? 'en' : 'zh-cn';
}
}
$this->view->cmd = $this->translate->checkDirPriv($module);
if($_POST)
{
$this->translate->addTranslation($zentaoVersion, $language, $module);
if(dao::isError()) die(js::error(dao::getError()));
die(js::locate(inlink('showLang', "zentaoVersion=$zentaoVersion&language=$language&module=$module&consultLang=$consultLang"), 'parent'));
$this->translate->addTranslation($language, $module, $referLang);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'reload'));
}
$this->view->referItems = $this->translate->getModuleLangs($module, $referLang);
@@ -159,6 +168,7 @@ class translate extends control
$this->view->currentModule = $module;
$this->view->currentGroup = $group;
$this->view->language = $language;
$this->view->referLang = $referLang;
$this->display();
}
+3
View File
@@ -0,0 +1,3 @@
.table-data tbody>tr>td{border:1px solid #cbd0db !important; padding-right:8px !important;}
.table-data tbody>tr>td.translated{padding:0px !important;}
.translated input, .translated textarea{border:0px;}
+20
View File
@@ -0,0 +1,20 @@
function setTranslateView(view)
{
$.cookie('translateView', view, {expires:config.cookieLife, path:config.webRoot});
location.reload();
}
function adjustKeyWidth()
{
var maxWidth = 0;
$('.table-data tbody tr').each(function()
{
keyWidth = $(this).find('td').eq(1).find('nobr').width();
if(maxWidth < keyWidth) maxWidth = keyWidth;
});
$('.table-data thead tr:first th').eq(1).width(maxWidth);
}
$(function()
{
adjustKeyWidth();
});
+1 -1
View File
@@ -20,7 +20,7 @@ $lang->translate->reference = '参考语言';
$lang->translate->status = '状态';
$lang->translate->refreshPage = '刷新';
$lang->translate->builtIn = '内置语音';
$lang->translate->builtIn = '内置语言';
$lang->translate->finished = '翻译完成';
$lang->translate->progress = '完成 %s';
+140 -135
View File
@@ -103,7 +103,7 @@ class translateModel extends model
if(strpos($line, '$lang->menuOrder') === 0) continue;
if(strpos($line, 'include') === 0 or strpos($line, 'global') === 0) continue;
if(strpos($line, 'unset(') !== false) continue;
if(strpos($line, '/*') === 0 or strpos($line, '//') === 0 or strpos($line, '<?php') === 0) continue;
if(strpos($line, '/*') === 0 or strpos($line, '//') === 0 or strpos($line, '<?php') === 0 or strpos($line, '*') === 0) continue;
if(strpos($line, 'if') !== false and trim($lines[$i + 1]) == '{')
{
$inCondition = true;
@@ -126,7 +126,7 @@ class translateModel extends model
{
$position = strpos($line, '=');
$key = trim(substr($line, 0, $position));
$value = trim(trim(substr($line, $position + 1)), ';');
$value = trim(substr($line, $position + 1));
$items[$key] = $value;
}
elseif(isset($key) and strpos($key, '$lang') === 0)
@@ -135,13 +135,27 @@ class translateModel extends model
}
}
}
foreach($items as $key => $value)
{
/* Remove Notes. */
if(preg_match( '/; *\/\//', $value)) $value = preg_replace('/; *\/\/.*/', '', $value);
$value = trim($value, ';');
/* Trim ['] or ["] when not [.] . */
if(preg_match('/["\'] *\./', $value) == 0 and preg_match('/\. *[\'"]/', $value) == 0)
{
if($value{0} == '"' or $value{0} == "'") $value = trim($value, $value{0});
}
$items[$key] = $value;
}
return $items;
}
public function checkDirPriv()
public function checkDirPriv($moduleName = '')
{
$cmd = '';
$modules = glob($this->app->getModuleRoot() . '*');
$cmd = '';
$moduleRoot = $this->app->getModuleRoot();
$modules = empty($moduleName) ? array($moduleRoot . $moduleName) : glob($moduleRoot . '*');
foreach($modules as $modulePath)
{
if(is_dir($modulePath . '/lang') and !is_writable($modulePath . '/lang')) $cmd .= "chmod 777 {$modulePath}/lang <br />";
@@ -203,8 +217,9 @@ class translateModel extends model
*/
public function getTranslations($language, $module = '', $langKeys = array())
{
return $this->dao->select('id,lang,`key`,`value`,status')->from(TABLE_TRANSLATION)
return $this->dao->select('*')->from(TABLE_TRANSLATION)
->where('lang')->eq($language)
->andWhere('mode')->eq($this->config->global->flow)
->beginIF(!empty($module))->andWhere('module')->eq($module)->fi()
->beginIF(!empty($langKeys))->andWhere('langKey')->in($langKeys)->fi()
->orderBy('id asc')
@@ -220,158 +235,148 @@ class translateModel extends model
* @access public
* @return void
*/
public function addTranslation($zentaoVersion, $language, $module)
public function addTranslation($language, $module, $referLang)
{
$addData = '';
$allWords = ''; //Use to stat word count.
$account = $this->app->user->account;
$postData = $this->untieModuleLang($_POST);
$allEntry = count($postData);
$translateData = $this->getTranslations($zentaoVersion, $language, $module, array_keys($postData));
$translated = count(array_keys($translateData));
foreach($postData as $langKey => $translation)
$referItems = $this->getModuleLangs($module, $referLang);
$dbItems = $this->getTranslations($language, $module);
$translations = array();
$flow = $this->config->global->flow;
foreach($this->post->keys as $i => $key)
{
if(empty($translation))
$dbItem = zget($dbItems, $key, '');
$value = $referItems[$key];
$now = helper::now();
if(!empty($_POST['values'][$i])) $value = $this->post->values[$i];
if($dbItem)
{
unset($postData[$langKey]);
continue;
}
/* Check duplicate translation.*/
if(isset($translateData[$langKey]) and in_array($translation, array_keys($translateData[$langKey])))
{
unset($postData[$langKey]);
continue;
}
if(!isset($translateData[$langKey])) $translated++;
if(get_magic_quotes_gpc()) $translation = stripslashes($translation);
$allWords[$langKey] = $translation;
$translation = $this->dbh->quote($translation);
$addData .= "('" . $account . "', '$zentaoVersion', '$module', '$language', '$langKey', $translation, '" . helper::now() . "'),";
}
if($addData)
{
/* Delete old translation when user edit these.*/
$havingTranslate = $this->dao->select('id, translation')->from(TABLE_TRANSLATE)
->where('account')->eq($account)
->andWhere('version')->eq($zentaoVersion)
->andWhere('module')->eq($module)
->andWhere('language')->eq($language)
->andWhere('langKey')->in(array_keys($allWords))
->fetchPairs('id', 'translation', false);
if($havingTranslate)
{
$this->dao->delete()->from(TABLE_TRANSLATE)->where('id')->in(array_keys($havingTranslate))->exec(false);
$addedScores = $this->countWords(join(' ', $havingTranslate));
}
/* Add translate content*/
$addTranslateData = "INSERT INTO `" . TABLE_TRANSLATE . "` (`account`, `version`, `module`, `language`, `langKey`, `translation`, `time`) VALUES" . rtrim($addData, ',');
$this->dbh->exec($addTranslateData);
/* Add translate log*/
if(get_magic_quotes_gpc()) $postData = stripslashes(json_encode($postData));
$postData = $this->dbh->quote($postData);
$addLogData = "INSERT INTO `" . TABLE_TRANSLATELOG . "` (`account`, `version`, `module`, `language`, `content`, `time`) VALUES('" . $account . "', '$zentaoVersion', '$module', '$language', " . $postData . ", '" . helper::now() . "')";
$this->dbh->exec($addLogData);
/* Count words as scores*/
$scores = $this->countWords(join(' ', $allWords));
if(empty($addedScores))
{
$this->loadModel('score')->log($account, 'translate', 'in', $scores, 'TRANSLATE');
unset($dbItem->id);
$translation = $dbItem;
$translation->version = $this->config->version;
if($dbItem->value != $value)
{
$translation->value = $value;
$translation->status = 'translated';
$translation->translator = $this->app->user->account;
$translation->translationTime = $now;
if($translation->reason) $translation->reason = '';
}
elseif(!$this->checkNeedTranslate($value) and !empty($_POST['values'][$i]) and strpos("waiting|changed", $translation->status) !== false)
{
$translation->status = 'translated';
}
}
else
{
$scores = $scores - $addedScores;
$scoreType = $scores >= 0 ? 'in' : 'out';
$this->loadModel('score')->log($account, 'edittranslate', $scoreType, abs($scores), 'TRANSLATE');
}
/* compute percent of translation and save.*/
$percent = round($translated / $allEntry * 100, 1) . '%';
$languageLog = $this->dao->select('*')->from(TABLE_LANGUAGELOG)->where('version')->eq($zentaoVersion)
->andWhere('language')->eq($language)
->andWhere('module')->eq($module)
->fetch('', false);
if($languageLog)
{
$this->dao->update(TABLE_LANGUAGELOG)->set('translated')->eq($translated)->set('percent')->eq($percent)->where('id')->eq($languageLog->id)->exec(false);
}
else
{
$languageLog->version = $zentaoVersion;
$languageLog->language = $language;
$languageLog->module = $module;
$languageLog->allEntry = $allEntry;
$languageLog->translated = $translated;
$languageLog->percent = $percent;
$this->dao->insert(TABLE_LANGUAGELOG)->data($languageLog, false)->exec();
$translation = new stdclass();
$translation->lang = $language;
$translation->module = $module;
$translation->key = $key;
$translation->value = $value;
$translation->status = 'translated';
$translation->translator = $this->app->user->account;
$translation->translationTime = $now;
$translation->version = $this->config->version;
$translation->mode = $flow;
}
$translations[$key] = $translation;
}
$this->dao->delete()->from(TABLE_TRANSLATION)->where('lang')->eq($language)->andWhere('module')->eq($module)->andWhere('mode')->eq($flow)->exec();
foreach($translations as $translation) $this->dao->replace(TABLE_TRANSLATION)->data($translation)->exec();
if(!dao::isError()) $this->buildLangFile($language, $module, $translations, $referLang);
}
/**
* untieModuleLang
*
* @param string $moduleLangs
* @param string $key
* @access public
* @return void
*/
public function untieModuleLang($moduleLangs = '', $key = '')
public function buildLangFile($language, $module, $translations, $referLang)
{
$untieLang = array();
if(is_object($moduleLangs))
$moduleRoot = $this->app->getModuleRoot();
$newLangFile = $moduleRoot . $module . "/lang/{$language}.php";
$newContent = "<?php\n";
$mainReferLangFile = $moduleRoot . $module . "/lang/{$referLang}.php";
if(file_exists($mainReferLangFile)) $newContent .= $this->getTranslatedLang($mainReferLangFile, $translations) . "\n";
if(is_dir($moduleRoot . $module . "/ext/lang/$referLang"))
{
foreach($moduleLangs as $langKey => $moduleLang)
$extLangFiles = glob($moduleRoot . $module . "/ext/lang/{$referLang}/*.php");
foreach($extLangFiles as $extLangFile) $newContent .= $this->getTranslatedLang($extLangFile, $translations) . "\n";
}
file_put_contents($newLangFile, $newContent);
}
public function getTranslatedLang($referLang, $translations)
{
$lines = file($referLang);
$flow = $this->config->global->flow;
$inFlow = true;
$level = 0;
$content = '';
foreach($lines as $i => $line)
{
$line = trim($line);
if(strpos($line, '/*') === 0 or strpos($line, '//') === 0 or strpos($line, '<?php') === 0 or strpos($line, '*') === 0) continue;
if(empty($line))
{
$nextKey = empty($key) ? $langKey : "$key->$langKey";
if(is_array($moduleLang) or is_object($moduleLang))
$content .= "\n";
continue;
}
if(strpos($line, '$lang->menuOrder') === 0 or strpos($line, 'include') === 0 or strpos($line, 'global') === 0 or strpos($line, 'unset(') !== false)
{
$content .= $line . "\n";
continue;
}
if(strpos($line, 'if') !== false and trim($lines[$i + 1]) == '{')
{
$inCondition = true;
$level ++;
if(strpos($line, 'config->global->flow') !== false and strpos($line, $flow) === false) $inFlow = false;
}
if($line == '}' and $inCondition)
{
$level --;
if($level == 0)
{
$untieLang += $this->untieModuleLang($moduleLang, $nextKey);
$inCondition = false;
$inFlow = true;
}
}
if($line == '{' or $line == '}' or strpos($line, 'if') === 0 or strpos($line, 'else') === 0)
{
$content .= $line . "\n";
continue;
}
if(!$inFlow and $inCondition)
{
$content .= $line . "\n";
}
elseif($inFlow and strpos($line, '$lang') === 0 and strpos($line, '=') !== false)
{
$position = strpos($line, '=');
$key = trim(substr($line, 0, $position));
if(isset($translations[$key]))
{
$translation = $translations[$key];
$value = $translation->value;
if($this->checkNeedTranslate($value) and strpos($value, 'array(') === false)
{
$value = '"' . addslashes($value) . '"';
}
$content .= $key . " = $value;\n";
}
else
{
$untieLang[$nextKey] = $moduleLang;
$content .= $line . "\n";
}
}
}
elseif(is_array($moduleLangs))
{
foreach($moduleLangs as $arrayKey => $moduleLang)
{
/* Init key for delete '"'*/
if(strpos($arrayKey, '\"') !== false) $arrayKey = str_replace(array('\"', '\"'), '', $arrayKey);
$arrayKey = !$arrayKey ? '' : (is_string($arrayKey) and !empty($key)) ? '"' . $arrayKey . '"' : $arrayKey;
$nextKey = empty($key) ? $arrayKey : $key . '[' . $arrayKey . ']';
if(is_array($moduleLang) or is_object($moduleLang))
{
$untieLang += $this->untieModuleLang($moduleLang, $nextKey);
}
else
{
$untieLang[$nextKey] = $moduleLang;
}
}
}
return $untieLang;
return $content;
}
public function countWords($allWords)
public function checkNeedTranslate($value)
{
$scores = 0;
$result = true;
if($value == 'new stdclass()') $result = false;
if(strpos($value, '$') === 0 and strpos($value, '$lang->productCommon') === false and strpos($value, '$lang->projectCommon') === false and preg_match('/["\'] *\./', $value) == 0 and preg_match('/\. *["\']/', $value) == 0) $result = false;
if($value == '$lang->productCommon' or $value == '$lang->projectCommon') $result = false;
$allWords = trim($allWords);
$allWords = preg_replace('/<[a-z\/]+.*>/Ui', '', $allWords);
$allWords = preg_replace('/[\x80-\xff]{1,3}/', '', $allWords, -1, $scores); //Count chinese.
$scores += str_word_count($allWords);
return $scores;
return $result;
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php include '../../common/view/header.html.php';?>
<?php if($cmd):?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='article-content text-danger'><?php printf($lang->translate->notice->failDirPriv, $cmd);?></div>
<hr>
<?php echo html::commonButton($lang->translate->refreshPage, 'onclick=location.href=location.href', 'btn btn-primary');?>
</div>
</div>
<?php else:?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php foreach($lang->dev->groupList as $group => $groupName):?>
<?php $active = $group == $currentGroup ? 'btn-active-text' : '';?>
<?php echo html::a(inlink('module', "language=$language&module=" . current($moduleGroup[$group])), "<span class='text'>{$groupName}</span>", '', "class='btn btn-link $active'");?>
<?php endforeach;?>
</div>
<div class='btn-toolbar pull-right'>
<div class='btn-group'>
<?php $isSplit = $this->cookie->translateView == 'split';?>
<?php echo html::a('javascript:setTranslateView("unified")', "<i class='icon icon-bars'></i>", '', "class='btn btn-icon" . (!$isSplit ? " text-primary" : '') . "'");?>
<?php echo html::a('javascript:setTranslateView("split")', "<i class='icon icon-columns'></i>", '', "class='btn btn-icon" . ($isSplit ? " text-primary" : '') . "'");?>
</div>
</div>
</div>
<div id='mainContent' class='main-row'>
<div class='side-col' id='sidebar'>
<div class='cell'>
<div class='list-group'>
<?php foreach($moduleGroup[$currentGroup] as $module):?>
<?php echo html::a(inlink('module', "language=$language&module=$module"), zget($lang->dev->tableList, $module, $module), '', $module == $currentModule ? "class='active'" : '');?>
<?php endforeach;?>
</div>
</div>
</div>
<div class='main-col main-content'>
<form class='main-form form-ajax' method='post'>
<table class='table table-bordered table-data'>
<thead>
<tr>
<th class='w-50px'></th>
<th><?php echo $lang->translate->key;?></th>
<?php if($isSplit):?>
<th><?php echo $config->langs[$referLang];?></th>
<th><?php echo $config->langs[$language];?></th>
<?php else:?>
<th><?php echo $config->langs[$referLang] . ' / ' . $config->langs[$language];?></th>
<?php endif;;?>
<th class='w-70px'><?php echo $lang->translate->status;?></th>
</tr>
</thead>
<tbody>
<?php $i = 0;?>
<?php foreach($referItems as $key => $item):?>
<?php
$hasNL = strpos($item, "\n") !== false;
$hideClass = $this->translate->checkNeedTranslate($item) ? '' : "class='hidden'";
if(empty($hideClass)) $i++;
$hiddenKey = "value='{$key}'";
if(strpos($key, "'") !== false) $hiddenKey = 'value="' . $key . '"';
$hiddenKey = '<input type="hidden" name="keys[]" id="keys[]" ' . $hiddenKey . '>';
?>
<?php if($isSplit):?>
<tr <?php echo $hideClass;?>>
<td class='text-right'><?php echo $i;?></td>
<td class='text-right'><?php echo "<nobr>$key</nobr>" . $hiddenKey;?></td>
<td><?php echo $hasNL ? nl2br(htmlspecialchars($item)) : htmlspecialchars($item);?></td>
<?php $translation = zget($translations, $key, '');?>
<td class='translated'>
<?php
$function = $hasNL ? 'textarea' : 'input';
echo html::$function('values[]', ($translation and $translation->value != $item) ? htmlspecialchars($translation->value) : '', "class='form-control'");
?>
</td>
<td><?php echo $translation ? zget($lang->translate->statusList, $translation->status) : '';?></td>
</tr>
<?php else:?>
<tr <?php echo $hideClass;?>>
<?php $translation = zget($translations, $key, '');?>
<td class='text-right' rowspan='2'><?php echo $i;?></td>
<td class='text-right' rowspan='2'><?php echo "<nobr>$key</nobr>" . $hiddenKey;?></td>
<td><?php echo $hasNL ? nl2br(htmlspecialchars($item)) : htmlspecialchars($item);?></td>
<td rowspan='2'><?php echo $translation ? zget($lang->translate->statusList, $translation->status) : '';?></td>
</tr>
<tr <?php echo $hideClass;?>>
<td class='translated'>
<?php
$function = $hasNL ? 'textarea' : 'input';
echo html::$function('values[]', ($translation and $translation->value != $item) ? htmlspecialchars($translation->value) : '', "class='form-control'");
?>
</td>
</tr>
<?php endif;?>
<?php endforeach;?>
</tbody>
<tfoot>
<tr>
<td colspan='<?php echo $isSplit ? 5 : 4;?>' class='text-center'><?php echo html::submitButton()?></td>
</tr>
</tfoot>
</table>
</form>
</div>
</div>
<?php endif;?>
<?php include '../../common/view/footer.html.php';?>