full rework (in progress) see changelog
parent
bdb4b6d865
commit
174e7a4090
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,9 +1,13 @@
|
|||
2021.09.02.1 - dev
|
||||
- [ ] help translation
|
||||
- [ ] source translation
|
||||
- [ ] fix constante for official modules
|
||||
- [ ] fix third-party API (all are broken)
|
||||
- [ ] add dashboard icon
|
||||
- [ ] fix third-party API (temp remoed)
|
||||
- [ ] add support for plural (.po ok)
|
||||
- add dashboard icon
|
||||
- fix constante for official modules
|
||||
- fix superadmin permissions
|
||||
- remove modules list tab and add button to existing lists
|
||||
- remove multi-modules import/export
|
||||
|
||||
2021.09.02
|
||||
- clean up code and fix typo
|
||||
|
|
62
_admin.php
62
_admin.php
|
@ -15,21 +15,16 @@ if (!defined('DC_CONTEXT_ADMIN')) {
|
|||
return;
|
||||
}
|
||||
|
||||
$core->blog->settings->addNamespace('translater');
|
||||
$core->addBehavior('adminModulesListGetActions', ['translaterAdminBehaviors', 'adminModulesGetActions']);
|
||||
$core->addBehavior('adminModulesListDoActions', ['translaterAdminBehaviors', 'adminModulesDoActions']);
|
||||
$core->addBehavior('adminDashboardFavorites', ['translaterAdminBehaviors', 'adminDashboardFavorites']);
|
||||
$core->addBehavior('addTranslaterProposalTool', ['translaterAdminBehaviors', 'addGoogleProposalTool']);
|
||||
$core->addBehavior('addTranslaterProposalTool', ['translaterAdminBehaviors', 'addYahooProposalTool']);
|
||||
$core->addBehavior('addTranslaterProposalTool', ['translaterAdminBehaviors', 'addMicrosoftProposalTool']);
|
||||
$core->rest->addFunction('getProposal', ['translaterRest', 'getProposal']);
|
||||
|
||||
$_menu['Plugins']->addItem(
|
||||
__('Translater'),
|
||||
$core->adminurl->get('admin.plugin.translater'),
|
||||
$core->adminurl->get('translater'),
|
||||
dcPage::getPF('translater/icon.png'),
|
||||
preg_match(
|
||||
'/' . preg_quote($core->adminurl->get('admin.plugin.translater')) . '(&.*)?$/',
|
||||
'/' . preg_quote($core->adminurl->get('translater')) . '(&.*)?$/',
|
||||
$_SERVER['REQUEST_URI']
|
||||
),
|
||||
$core->auth->isSuperAdmin()
|
||||
|
@ -37,6 +32,23 @@ $_menu['Plugins']->addItem(
|
|||
|
||||
class translaterAdminBehaviors
|
||||
{
|
||||
/** @var dcTranlsater dcTranslater instance */
|
||||
private static $translater = null;
|
||||
|
||||
/**
|
||||
* Create instance of dcTranslater once
|
||||
*
|
||||
* @param dCore $core dcCore instance
|
||||
* @return dctranslater dcTranslater instance
|
||||
*/
|
||||
private static function translater($core)
|
||||
{
|
||||
if (!(self::$translater instanceof dcTranslater)) {
|
||||
self::$translater = new dcTranslater($core);
|
||||
}
|
||||
return self::$translater;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add button to go to module translation
|
||||
*
|
||||
|
@ -48,7 +60,7 @@ class translaterAdminBehaviors
|
|||
public static function adminModulesGetActions(adminModulesList $list, string $id, array $prop): ?string
|
||||
{
|
||||
if ($list->getList() != $prop['type'] . '-activate'
|
||||
|| !$list->core->blog->settings->translater->get('translater_' . $prop['type'] . '_menu')
|
||||
|| !self::translater($list->core)->getSetting($prop['type'] . '_menu')
|
||||
|| !$list->core->auth->isSuperAdmin()
|
||||
) {
|
||||
return null;
|
||||
|
@ -74,7 +86,7 @@ class translaterAdminBehaviors
|
|||
}
|
||||
|
||||
$list->core->adminurl->redirect(
|
||||
'admin.plugin.translater',
|
||||
'translater',
|
||||
['part' => 'module', 'type' => $type, 'module' => key($_POST['translater'])],
|
||||
'#module-lang'
|
||||
);
|
||||
|
@ -90,40 +102,10 @@ class translaterAdminBehaviors
|
|||
{
|
||||
$favs->register('translater', [
|
||||
'title' => __('Translater'),
|
||||
'url' => $core->adminurl->get('admin.plugin.translater'),
|
||||
'url' => $core->adminurl->get('translater'),
|
||||
'small-icon' => urldecode(dcPage::getPF('translater/icon.png')),
|
||||
'large-icon' => urldecode(dcPage::getPF('translater/icon-big.png')),
|
||||
'permissions' => $core->auth->isSuperAdmin()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Google Translater tools in translate
|
||||
*
|
||||
* @param translaterProposals $proposal translaterProposals instance
|
||||
*/
|
||||
public static function addGoogleProposalTool(translaterProposals $proposal)
|
||||
{
|
||||
$proposal->addTool('googleProposalTool');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Yahoo Babelfish tools in translater
|
||||
*
|
||||
* @param translaterProposals $proposal translaterProposals instance
|
||||
*/
|
||||
public static function addYahooProposalTool(translaterProposals $proposal)
|
||||
{
|
||||
$proposal->addTool('yahooProposalTool');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Microsoft Bing tools in translater
|
||||
*
|
||||
* @param translaterProposals $proposal translaterProposals instance
|
||||
*/
|
||||
public static function addMicrosoftProposalTool(translaterProposals $proposal)
|
||||
{
|
||||
$proposal->addTool('microsoftProposalTool');
|
||||
}
|
||||
}
|
203
_config.php
203
_config.php
|
@ -15,148 +15,89 @@ if (!defined('DC_CONTEXT_MODULE')) {
|
|||
return null;
|
||||
}
|
||||
|
||||
$redir = empty($_REQUEST['redir']) ?
|
||||
$list->getURL() . '#plugins' : $_REQUEST['redir'];
|
||||
|
||||
# -- Get settings --
|
||||
$core->blog->settings->addNamespace('translater');
|
||||
$s = $core->blog->settings->translater;
|
||||
|
||||
$translater = new dcTranslater($core);
|
||||
|
||||
$combo_backup_limit = [
|
||||
5 => 5,
|
||||
10 => 10,
|
||||
15 => 15,
|
||||
20 => 20,
|
||||
40 => 40,
|
||||
60 => 60
|
||||
];
|
||||
|
||||
$combo_backup_folder = [
|
||||
'module' => __('locales folders of each module'),
|
||||
'plugin' => __('plugins folder root'),
|
||||
'public' => __('public folder root'),
|
||||
'cache' => __('cache folder of Dotclear'),
|
||||
'translater' =>__('locales folder of translater')
|
||||
];
|
||||
|
||||
$combo_start_page = [
|
||||
'modules_plugin' => __('Plugins'),
|
||||
'modules_theme' => __('Themes'),
|
||||
'pack' => __('Import/Export')
|
||||
];
|
||||
|
||||
# -- Set settings --
|
||||
if (!empty($_POST['save'])) {
|
||||
try {
|
||||
if (empty($_POST['translater_write_po'])
|
||||
&& empty($_POST['translater_write_langphp'])) {
|
||||
throw new Exception('You must choose one file format at least');
|
||||
}
|
||||
foreach($translater->getDefaultSettings() as $k => $v) {
|
||||
$translater->setSetting($k, (isset($_POST['translater_' . $k]) ? $_POST['translater_' . $k] : ''));
|
||||
}
|
||||
foreach($translater->proposal->getTools() AS $k => $v) {
|
||||
$v->save();
|
||||
}
|
||||
dcPage::addSuccessNotice(
|
||||
__('Configuration has been successfully updated.')
|
||||
try {
|
||||
if (empty($_POST['write_po']) && empty($_POST['write_langphp'])) {
|
||||
throw new Exception(
|
||||
__('You must at least choose one file format to write')
|
||||
);
|
||||
http::redirect(
|
||||
$list->getURL('module=translater&conf=1&redir=' .
|
||||
$list->getRedir())
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$core->error->add(sprintf($errors[$action], $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
# -- Display form --
|
||||
echo '
|
||||
<div class="fieldset">
|
||||
<h4>' . __('Translation') . '</h4>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_write_po', '1' ,$translater->write_po) . '
|
||||
' . __('Write .po files') . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_write_langphp', '1', $translater->write_langphp) . '
|
||||
' . __('Write .lang.php files') . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_scan_tpl', '1', $translater->scan_tpl) . '
|
||||
' . __('Translate also strings of template files') . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_parse_nodc', '1', $translater->parse_nodc) . '
|
||||
' . __('Translate only unknow strings') . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_hide_default', '1', $translater->hide_default) . '
|
||||
' . __('Hide default modules of Dotclear') . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_parse_comment', '1', $translater->parse_comment) . '
|
||||
' . __('Write comments in files') . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_parse_user', '1', $translater->parse_user) . '
|
||||
' . __('Write informations about author in files') . '</label><br />
|
||||
' . form::field('translater_parse_userinfo', 65, 255, $translater->parse_userinfo) . '</p>
|
||||
</div>
|
||||
|
||||
<div class="fieldset">
|
||||
<h4>' . __('Tools') . '</h4>
|
||||
<p><label class="classic">' . __('Default language of l10n source:') . '<br />' .
|
||||
form::combo('translater_proposal_lang',
|
||||
array_flip($translater->getIsoCodes()), $translater->proposal_lang) . '</label></p>
|
||||
|
||||
<h4>' . __('Select and configure the tool to use to translate strings:') . '</h4>';
|
||||
|
||||
foreach($translater->proposal->getTools() AS $k => $v) {
|
||||
$form = $v->form();
|
||||
|
||||
echo '
|
||||
<dd>
|
||||
<dt><label class="classic">' .
|
||||
form::radio('translater_proposal_tool', $k, $k == $translater->proposal_tool) . '
|
||||
' . $v->getDesc() . '</label></dt><dd>' .
|
||||
(empty($form) ?
|
||||
'<p>' . sprintf(__('Nothing to configure for %s tool . '), $v->getName()) . '</p>' :
|
||||
$form
|
||||
) . '</dd></dl>';
|
||||
foreach($translater->getDefaultSettings() as $k => $v) {
|
||||
$translater->setSetting($k, (isset($_POST[$k]) ? $_POST[$k] : ''));
|
||||
}
|
||||
dcPage::addSuccessNotice(
|
||||
__('Configuration successfully updated.')
|
||||
);
|
||||
$core->adminurl->redirect(
|
||||
'admin.plugins',
|
||||
['module' => 'translater', 'conf' => 1, 'redir' => $list->getRedir()]
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$core->error->add($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
echo '
|
||||
<div class="fieldset"><h4>' . __('Translation') . '</h4>
|
||||
<p><label for="write_po">' .
|
||||
form::checkbox('write_po', '1' ,$translater->write_po) .
|
||||
__('Write .po files') . '</label></p>
|
||||
<p><label for="write_langphp">' .
|
||||
form::checkbox('write_langphp', '1', $translater->write_langphp) .
|
||||
__('Write .lang.php files') . '</label></p>
|
||||
<p><label for="scan_tpl">' .
|
||||
form::checkbox('scan_tpl', '1', $translater->scan_tpl) .
|
||||
__('Translate also strings of template files') . '</label></p>
|
||||
<p><label for="parse_nodc">' .
|
||||
form::checkbox('parse_nodc', '1', $translater->parse_nodc) .
|
||||
__('Translate only unknow strings') . '</label></p>
|
||||
<p><label for="hide_default">' .
|
||||
form::checkbox('hide_default', '1', $translater->hide_default) .
|
||||
__('Hide default modules of Dotclear') . '</label></p>
|
||||
<p><label for="parse_comment">' .
|
||||
form::checkbox('parse_comment', '1', $translater->parse_comment) .
|
||||
__('Write comments in files') . '</label></p>
|
||||
<p><label for="parse_user">' .
|
||||
form::checkbox('parse_user', '1', $translater->parse_user) .
|
||||
__('Write informations about author in files') . '</label></p>
|
||||
<p><label for="parse_userinfo">' . __('User info:') . '</label>' .
|
||||
form::field('parse_userinfo', 65, 255, $translater->parse_userinfo) . '</p>
|
||||
<p class="form-note">' . sprintf(
|
||||
__('Following informations can be used: %s '), implode(', ', $translater::$allowed_user_informations)) . '
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="fieldset">
|
||||
<h4>' . __('Import/Export') . '</h4>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_import_overwrite', '1', $translater->import_overwrite) . '
|
||||
' . __('Overwrite existing languages') . '</label></p>
|
||||
<p><label class="classic">' . __('Name of exported package') . '<br />
|
||||
' . form::field('translater_export_filename', 65, 255, $translater->export_filename) . '</label></p>
|
||||
<div class="fieldset"><h4>' . __('Import/Export') . '</h4>
|
||||
<p><label for="import_overwrite">' .
|
||||
form::checkbox('import_overwrite', '1', $translater->import_overwrite) .
|
||||
__('Overwrite existing languages') . '</label></p>
|
||||
<p><label for="export_filename">' . __('Name of exported package:') . '</label>' .
|
||||
form::field('export_filename', 65, 255, $translater->export_filename) . '</p>
|
||||
</div>
|
||||
|
||||
<div class="fieldset">
|
||||
<h4>' . __('Backups') . '</h4>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_backup_auto', '1', $translater->backup_auto) . '
|
||||
' . __('Make backups when changes are made') . '</label></p>
|
||||
<p><label class="classic">' . sprintf(__('Limit backups to %s files per module'),
|
||||
form::combo('translater_backup_limit',
|
||||
array_flip($combo_backup_limit), $translater->backup_limit)) . '</label></p>
|
||||
<p><label class="classic">' . sprintf(__('Store backups in %s'),
|
||||
form::combo('translater_backup_folder',
|
||||
array_flip($combo_backup_folder), $translater->backup_folder)) . '</label></p>
|
||||
<div class="fieldset"><h4>' . __('Backups') . '</h4>
|
||||
<p><label for="backup_auto">' .
|
||||
form::checkbox('backup_auto', '1', $translater->backup_auto) .
|
||||
__('Make backups when changes are made') . '</label></p>
|
||||
<p><label for="backup_limit" class="classic">' . sprintf(__('Limit backups to %s files per module'),
|
||||
form::number('backup_limit', ['min' => 0, 'max' => 50, 'default' => $translater->backup_limit])) . '</label></p>
|
||||
<p><label for="backup_folder">' . __('Store backups in:') . '</label>' .
|
||||
form::combo('backup_folder', $translater::$allowed_backup_folders, $translater->backup_folder) . '</p>
|
||||
</div>
|
||||
|
||||
<div class="fieldset">
|
||||
<h4>' . __('Behaviors') . '</h4>
|
||||
<p><label class="classic">' . __('Default start menu:') . '<br />' .
|
||||
form::combo('translater_start_page',
|
||||
array_flip($combo_start_page), $translater->start_page) . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_plugin_menu', '1', $translater->plugin_menu) . '
|
||||
' . __('Enable menu on extensions page') . '</label></p>
|
||||
<p><label class="classic">' .
|
||||
form::checkbox('translater_theme_menu', '1', $translater->theme_menu) . '
|
||||
' . __('Enable menu on themes page') . '</label></p>
|
||||
</div>
|
||||
';
|
||||
<div class="fieldset"><h4>' . __('Behaviors') . '</h4>
|
||||
<p><label for="start_page">' . __('Default start menu:') . '</label>' .
|
||||
form::combo('start_page',[
|
||||
__('Plugins') => 'plugin',
|
||||
__('Themes') => 'theme',
|
||||
__('Home') => '-'
|
||||
], $translater->start_page) . '</p>
|
||||
<p><label for="plugin_menu">' .
|
||||
form::checkbox('plugin_menu', '1', $translater->plugin_menu) .
|
||||
__('Enable menu on extensions page') . '</label></p>
|
||||
<p><label for="theme_menu">' .
|
||||
form::checkbox('theme_menu', '1', $translater->theme_menu) .
|
||||
__('Enable menu on themes page') . '</label></p>
|
||||
</div>';
|
31
_install.php
31
_install.php
|
@ -13,38 +13,21 @@
|
|||
|
||||
if (!defined('DC_CONTEXT_ADMIN')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$s = [
|
||||
['translater_plugin_menu', 0, 'boolean', 'Put a link in plugins page'],
|
||||
['translater_theme_menu', 0,'boolean', 'Put a link in themes page'],
|
||||
['translater_backup_auto', 1,'boolean', 'Make a backup of languages old files when there are modified'],
|
||||
['translater_backup_limit', 20,'string', 'Maximum backups per module'],
|
||||
['translater_backup_folder', 'module',' string', 'In which folder to store backups'],
|
||||
['translater_start_page', 'setting,', 'string', 'Page to start on'],
|
||||
['translater_write_po', 1, 'boolean', 'Write .po languages files'],
|
||||
['translater_write_langphp', 1, 'boolean', 'Write .lang.php languages files'],
|
||||
['translater_scan_tpl', 0, 'boolean', 'Translate strings of templates files'],
|
||||
['translater_parse_nodc', 1, 'boolean', 'Translate only untranslated strings of Dotclear'],
|
||||
['translater_hide_default', 1, 'boolean', 'Hide default modules of Dotclear'],
|
||||
['translater_parse_comment', 1, 'boolean', 'Write comments and strings informations in lang files'],
|
||||
['translater_parse_user', 1,'boolean', 'Write inforamtions about author in lang files'],
|
||||
['translater_parse_userinfo', 'displayname, email', 'string','Type of informations about user to write'],
|
||||
['translater_import_overwrite', 0, 'boolean', 'Overwrite existing languages when import packages'],
|
||||
['translater_export_filename', 'type-module-l10n-timestamp', 'string','Name of files of exported package'],
|
||||
['translater_proposal_tool', 'google', 'string', 'Id of default tool for proposed translation'],
|
||||
['translater_proposal_lang', 'en', 'string', 'Default source language for proposed translation']
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
if (version_compare($core->getVersion($id), $this->moduleInfo($id, 'version'), '>=')) {
|
||||
return null;
|
||||
}
|
||||
$core->blog->settings->addNamespace('translater');
|
||||
|
||||
$t = new dcTranslater($core);
|
||||
$s = $t->getDefaultSettings();
|
||||
foreach($s as $v) {
|
||||
$core->blog->settings->translater->put($v[0], $v[1], $v[2], $v[3], false, true);
|
||||
$t->setSetting($v[0], $v[1], false);
|
||||
}
|
||||
|
||||
$core->setVersion($id, $this->moduleInfo($id, 'version'));
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$core->error->add($e->getMessage());
|
||||
|
|
11
_prepend.php
11
_prepend.php
|
@ -17,10 +17,9 @@ if (!defined('DC_RC_PATH')) {
|
|||
|
||||
$d = dirname(__FILE__) . '/inc/';
|
||||
|
||||
$__autoload['dcTranslater'] = $d . 'class.dc.translater.php';
|
||||
$__autoload['translaterRest'] = $d . 'class.translater.rest.php';
|
||||
$__autoload['translaterProposals'] = $d . 'class.translater.proposals.php';
|
||||
$__autoload['dcTranslater'] = $d . 'class.dc.translater.php';
|
||||
$__autoload['dcTranslaterModule'] = $d . 'class.dc.translater.module.php';
|
||||
$__autoload['dcTranslaterLang'] = $d . 'class.dc.translater.lang.php';
|
||||
$__autoload['translaterRest'] = $d . 'class.translater.rest.php';
|
||||
|
||||
$__autoload['translaterProposalTool'] = $d . 'lib.translater.proposal.php';
|
||||
$__autoload['googleProposalTool'] = $d . 'lib.translater.google.php';
|
||||
$__autoload['microsoftProposalTool'] = $d . 'lib.translater.microsoft.php';
|
||||
$core->adminurl->register('translater', 'plugin.php', ['p' => 'translater']);
|
|
@ -0,0 +1 @@
|
|||
.addfield, .togglelist { border: none; }
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 209 B |
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
/**
|
||||
* @brief translater, a plugin for Dotclear 2
|
||||
*
|
||||
* @package Dotclear
|
||||
* @subpackage Plugin
|
||||
*
|
||||
* @author Jean-Christian Denis & contributors
|
||||
*
|
||||
* @copyright Jean-Christian Denis
|
||||
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
|
||||
*/
|
||||
|
||||
class dcTranslaterLang
|
||||
{
|
||||
/** @var dCore dcCore instance */
|
||||
public $core = null;
|
||||
/** @var dcTranslater dcTranslater instance */
|
||||
public $translater = null;
|
||||
/** @var dcTranslaterModule dcTranslaterModule instance */
|
||||
public $module = null;
|
||||
|
||||
/** @var array Lang properies */
|
||||
private $prop = [];
|
||||
|
||||
public function __construct(dcTranslaterModule $module, string $lang)
|
||||
{
|
||||
$this->core = $module->core;
|
||||
$this->translater = $module->translater;
|
||||
$this->module = $module;
|
||||
|
||||
$this->prop['code'] = $lang;
|
||||
$this->prop['name'] = l10n::getLanguageName($lang);
|
||||
$this->prop['plural'] = explode(':', l10n::getLanguagePluralExpression($lang));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lang property
|
||||
*
|
||||
* @param string $key The lang property key
|
||||
* @return mixed The lang property value or null
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
return array_key_exists($key, $this->prop) ? $this->prop[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic get
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lang messages
|
||||
*
|
||||
* @return array The messages ids and translations
|
||||
*/
|
||||
public function getMessages(): array
|
||||
{
|
||||
$res = [];
|
||||
$m_msgids = $this->getMsgIds();
|
||||
$m_msgstrs = $this->getMsgStrs();
|
||||
|
||||
foreach($this->translater->getModules() as $module) {
|
||||
if ($module->id != $this->module->id) {
|
||||
$m_o_msgstrs[$module->id] = $this->translater->getlang($module, $this->code)->getMsgStrs();
|
||||
}
|
||||
}
|
||||
$dc_module = new dcTranslaterModule($this->translater, ['id' => 'dotclear', 'root' => DC_ROOT]);
|
||||
$dc_lang = new dctranslaterLang($dc_module, $this->code);
|
||||
$m_o_msgstrs['dotclear'] = $dc_lang->getMsgStrs();
|
||||
|
||||
# From id list
|
||||
foreach($m_msgids as $rs) {
|
||||
$res[$rs['msgid']]['files'][] = [trim($rs['file'],'/'), $rs['line']];
|
||||
$res[$rs['msgid']]['group'] = 'main';
|
||||
$res[$rs['msgid']]['plural'] = $rs['msgid_plural'];
|
||||
$res[$rs['msgid']]['msgstr'] = [''];
|
||||
$res[$rs['msgid']]['in_dc'] = false;
|
||||
$res[$rs['msgid']]['o_msgstrs'] = [];
|
||||
}
|
||||
|
||||
# From str list
|
||||
foreach($m_msgstrs as $rs) {
|
||||
|
||||
if (!isset($res[$rs['msgid']])) {
|
||||
$res[$rs['msgid']]['files'][] = [];
|
||||
$res[$rs['msgid']]['in_dc'] = false;
|
||||
$res[$rs['msgid']]['o_msgstrs'] = [];
|
||||
}
|
||||
$res[$rs['msgid']]['group'] = $rs['group'];
|
||||
$res[$rs['msgid']]['plural'] = $rs['msgid_plural'];
|
||||
$res[$rs['msgid']]['msgstr'] = is_array($rs['msgstr']) ? $rs['msgstr'] : [$rs['msgstr']];
|
||||
$res[$rs['msgid']]['in_dc'] = false;
|
||||
}
|
||||
|
||||
# From others str list
|
||||
foreach($m_o_msgstrs as $o_module => $o_msgstrs) {
|
||||
foreach($o_msgstrs as $rs) {
|
||||
if (!isset($res[$rs['msgid']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$res[$rs['msgid']]['o_msgstrs'][] = [
|
||||
'msgstr' => is_array($rs['msgstr']) ? $rs['msgstr'] : [$rs['msgstr']],
|
||||
'module' => $o_module,
|
||||
'file' => $rs['file']
|
||||
];
|
||||
if ($o_module == 'dotclear') {
|
||||
$res[$rs['msgid']]['in_dc'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages ids
|
||||
*
|
||||
* @return array The messages ids
|
||||
*/
|
||||
public function getMsgIds(): array
|
||||
{
|
||||
$res = [];
|
||||
$files = dcTranslater::scandir($this->module->root);
|
||||
|
||||
$scan_ext = ['php'];
|
||||
if ($this->translater->scan_tpl) {
|
||||
// $scan_ext[] = 'html';
|
||||
}
|
||||
|
||||
foreach($files AS $file) {
|
||||
if (is_dir($this->module->root . '/' . $file)
|
||||
|| !in_array(files::getExtension($file), $scan_ext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($this->module->root . '/' . $file);
|
||||
# php files
|
||||
$msgs = dcTranslater::extractPhpMsgs($contents);
|
||||
foreach($msgs as $msg) {
|
||||
$res[] = [
|
||||
'msgid' => dcTranslater::encodeMsg($msg[0][0]),
|
||||
'msgid_plural' => empty($msg[0][1]) ? '' : dcTranslater::encodeMsg($msg[0][1]),
|
||||
'file' => $file,
|
||||
'line' => $msg[1]
|
||||
];
|
||||
}
|
||||
|
||||
unset($contents);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages translations
|
||||
*
|
||||
* @return array The messages translations
|
||||
*/
|
||||
public function getMsgStrs(): array
|
||||
{
|
||||
$res = $scanned = [];
|
||||
|
||||
$langs = $this->module->getLangs(true);
|
||||
if (!isset($langs[$this->code])) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
foreach($langs[$this->code] as $file) {
|
||||
if (in_array($file, $scanned)) {
|
||||
continue;
|
||||
}
|
||||
$scanned[] = $file;
|
||||
$path = path::clean($this->module->locales . '/' . $file);
|
||||
|
||||
if (dcTranslater::isPoFile($file)) {
|
||||
$po = l10n::parsePoFile($path);
|
||||
if (!is_array($po)) {
|
||||
continue;
|
||||
}
|
||||
$entries = $po[1];
|
||||
foreach($entries as $entry) {
|
||||
$res[] = array(
|
||||
'msgid' => $entry['msgid'],
|
||||
'msgid_plural' => $entry['msgid_plural'] ?? '',
|
||||
'msgstr' => is_array($entry['msgstr']) ? $entry['msgstr'] : [$entry['msgstr']],
|
||||
'lang' => $this->code,
|
||||
'type' => 'po',
|
||||
'path' => $path,
|
||||
'file' => basename($file),
|
||||
'group'=> str_replace('.po', '', basename($file))
|
||||
);
|
||||
}
|
||||
/*
|
||||
# .lang.php files
|
||||
} elseif (self::isLangphpFile($file)) {
|
||||
$php = self::getLangphpFile($path);
|
||||
foreach($php AS $id => $str) {
|
||||
# Don't overwrite .po
|
||||
if (isset($is_po[$requested_lang][$id])) {
|
||||
continue;
|
||||
}
|
||||
$res[] = array(
|
||||
'msgid' => self::encodeMsg($id),
|
||||
'msgstr' => self::encodeMsg($str),
|
||||
'lang' => $requested_lang,
|
||||
'type' => 'php',
|
||||
'path' => $path,
|
||||
'file' => basename($file),
|
||||
'group'=> str_replace('.lang.php', '', basename($file))
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,749 @@
|
|||
<?php
|
||||
/**
|
||||
* @brief translater, a plugin for Dotclear 2
|
||||
*
|
||||
* @package Dotclear
|
||||
* @subpackage Plugin
|
||||
*
|
||||
* @author Jean-Christian Denis & contributors
|
||||
*
|
||||
* @copyright Jean-Christian Denis
|
||||
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
|
||||
*/
|
||||
|
||||
if (!defined('DC_CONTEXT_ADMIN')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translater tools.
|
||||
*/
|
||||
class dcTranslaterModule
|
||||
{
|
||||
/** @var dCore dcCore instance */
|
||||
public $core = null;
|
||||
/** @var dcTranslater dcTranslater instance */
|
||||
public $translater = null;
|
||||
|
||||
/** @var array Module properies */
|
||||
private $prop = [];
|
||||
|
||||
/** @var string Backup file regexp */
|
||||
private $backup_file_regexp = '/^l10n-%s-(.*?)-[0-9]*?\.bck\.zip$/';
|
||||
|
||||
/** @var string Locales file regexp */
|
||||
private $locales_file_regexp = '/^(.*?)\/locales\/(.*?)\/(.*?)(.po|.lang.php)$/';
|
||||
|
||||
public function __construct(dcTranslater $translater, array $module)
|
||||
{
|
||||
$this->core = $translater->core;
|
||||
$this->translater = $translater;
|
||||
$this->prop = $module;
|
||||
|
||||
$this->prop['root'] = path::real($this->prop['root']);
|
||||
$i = path::info($this->prop['root']);
|
||||
$this->prop['basename'] = $i['basename'];
|
||||
$this->prop['locales'] = $this->prop['root'] . '/locales';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a module property
|
||||
*
|
||||
* @param string $key The module property key
|
||||
* @return mixed The module property value or null
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
return array_key_exists($key, $this->prop) ? $this->prop[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic get
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/// @name backup methods
|
||||
//@{
|
||||
/**
|
||||
* Find backup folder of a module
|
||||
*
|
||||
* @param boolean $throw Silently failed
|
||||
* @return mixed The backup folder directory or false
|
||||
*/
|
||||
public function getBackupRoot(bool $throw = false)
|
||||
{
|
||||
$dir = false;
|
||||
switch($this->translater->backup_folder) {
|
||||
case 'module':
|
||||
if ($this->prop['root_writable']) {
|
||||
$dir = $this->prop['locales'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'plugin':
|
||||
$tmp = path::real(array_pop(explode(PATH_SEPARATOR, DC_PLUGINS_ROOT)));
|
||||
if ($tmp && is_writable($tmp)) {
|
||||
$dir = $tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'public':
|
||||
$tmp = path::real($this->core->blog->public_path);
|
||||
if ($tmp && is_writable($tmp)) {
|
||||
$dir = $tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cache':
|
||||
$tmp = path::real(DC_TPL_CACHE);
|
||||
if ($tmp && is_writable($tmp)) {
|
||||
@mkDir($tmp . '/l10n');
|
||||
$dir = $tmp . '/l10n';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'translater':
|
||||
$tmp = path::real($this->core->plugins->moduleRoot('translater'));
|
||||
if ($tmp && is_writable($tmp)) {
|
||||
@mkDir($tmp . '/locales');
|
||||
$dir = $tmp . '/locales';
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!$dir && $throw) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot find backups folder for module %s'), $id
|
||||
));
|
||||
}
|
||||
|
||||
return $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of available backups
|
||||
*
|
||||
* @param boolean $return_filename Return only filenames
|
||||
* @return array The module backups info
|
||||
*/
|
||||
public function getBackups(bool $return_filename = false): array
|
||||
{
|
||||
$backup = $this->getBackupRoot();
|
||||
if (!$backup) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$res = [];
|
||||
$files = dcTranslater::scandir($backup);
|
||||
foreach($files AS $file) {
|
||||
$is_backup = preg_match(sprintf($this->backup_file_regexp, preg_quote($this->prop['id'])), $file, $m);
|
||||
|
||||
if (is_dir($backup . '/' . $file)
|
||||
|| !$is_backup
|
||||
|| !l10n::isCode($m[1])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($return_filename) {
|
||||
$res[] = $file;
|
||||
} else {
|
||||
$res[$m[1]][$file]['code'] = $m[1];
|
||||
$res[$m[1]][$file]['name'] = l10n::getLanguageName($m[1]);
|
||||
$res[$m[1]][$file]['path'] = path::info($backup . '/' . $file);
|
||||
$res[$m[1]][$file]['time'] = filemtime($backup . '/' . $file);
|
||||
$res[$m[1]][$file]['size'] = filesize($backup . '/' . $file);
|
||||
$res[$m[1]][$file]['module'] = $this->prop['id'];
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a backup
|
||||
*
|
||||
* @param string $lang The backup lang
|
||||
* @return boolean True on success
|
||||
*/
|
||||
public function createBackup(string $lang): bool
|
||||
{
|
||||
$backup = $this->getBackupRoot(true);
|
||||
|
||||
if (!is_dir($this->prop['locales'] . '/' . $lang)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot find language folder %s for module %s') ,$lang, $this->prop['id']
|
||||
));
|
||||
}
|
||||
|
||||
$res = [];
|
||||
$files = dcTranslater::scandir($this->prop['locales'] . '/' . $lang);
|
||||
foreach($files as $file) {
|
||||
if (!is_dir($this->prop['locales'] . '/' . $lang . '/' . $file)
|
||||
&& (dcTranslater::isLangphpFile($file) || dcTranslater::isPoFile($file))
|
||||
) {
|
||||
$res[$this->prop['locales'] . '/' . $lang . '/' .$file] =
|
||||
$this->prop['id'] . '/locales/' . $lang . '/' . $file;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($res)) {
|
||||
dcTranslater::isBackupLimit($backup, $this->translater->backup_limit, true);
|
||||
|
||||
@set_time_limit(300);
|
||||
$fp = fopen($backup . '/l10n-' . $this->prop['id'] . '-' . $lang . '-' . time() . '.bck.zip', 'wb');
|
||||
$zip = new fileZip($fp);
|
||||
foreach($res AS $from => $to) {
|
||||
$zip->addFile($from, $to);
|
||||
}
|
||||
$zip->write();
|
||||
$zip->close();
|
||||
unset($zip);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retore a backup
|
||||
*
|
||||
* @param string $file The backup filename
|
||||
* @return boolean True on success
|
||||
*/
|
||||
public function restoreBackup(string $file): bool
|
||||
{
|
||||
$backup = self::getBackupRoot(true);
|
||||
|
||||
if (!file_exists($backup . '/' . $file)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot find backup file %s'), $file
|
||||
));
|
||||
}
|
||||
|
||||
$zip = new fileUnzip($backup . '/' . $file);
|
||||
$zip_files = $zip->getFilesList();
|
||||
|
||||
foreach($zip_files AS $zip_file) {
|
||||
$f = $this->parseZipFilename($zip_file, true);
|
||||
$zip->unzip($zip_file, $this->prop['locales'] . '/' . $f['lang'] . '/' . $f['group'] . $f['ext']);
|
||||
$done = true;
|
||||
}
|
||||
$zip->close();
|
||||
unset($zip);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a module backup
|
||||
*
|
||||
* @param string $file The backup filename
|
||||
* @return boolean True on success
|
||||
*/
|
||||
public function deleteBackup(string $file): bool
|
||||
{
|
||||
$backup = $this->getBackupRoot(true);
|
||||
|
||||
$is_backup = preg_match(sprintf($this->backup_file_regexp, preg_quote($this->prop['id'])), $file, $m);
|
||||
|
||||
if (!file_exists($backup . '/' . $file)
|
||||
|| !$is_backup
|
||||
|| !l10n::isCode($m[1])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!files::isDeletable($backup . '/' . $file)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot delete backup file %s'), $file
|
||||
));
|
||||
}
|
||||
|
||||
unlink($backup . '/' . $file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a language pack
|
||||
*
|
||||
* @param array $zip_file The uploaded file info
|
||||
* @return boolean True on success
|
||||
*/
|
||||
public function importPack(array $zip_file): bool
|
||||
{
|
||||
files::uploadStatus($zip_file);
|
||||
|
||||
$imported = false;
|
||||
$not_overwrited = [];
|
||||
$res = [];
|
||||
|
||||
# Load Unzip object
|
||||
$zip = new fileUnzip($zip_file['tmp_name']);
|
||||
$files = $zip->getFilesList();
|
||||
|
||||
foreach($files AS $file) {
|
||||
$f = $this->parseZipFilename($file, true);
|
||||
|
||||
if (!$this->translater->import_overwrite
|
||||
&& file_exists($this->prop['locales'] . '/' . $f['lang'] . '/' . $f['group'] . $f['ext'])
|
||||
) {
|
||||
$not_overwrited[] = implode('-', [$f['lang'], $f['group'], $f['ext']]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$res[] = [
|
||||
'from' => $file,
|
||||
'root' => $this->prop['locales'] . '/' . $f['lang'],
|
||||
'to' => $this->prop['locales'] . '/' . $f['lang'] . '/' . $f['group'] . $f['ext']
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($res AS $rs) {
|
||||
if (!is_dir($rs['root'])) {
|
||||
files::makeDir($rs['root'], true);
|
||||
}
|
||||
|
||||
$zip->unzip($rs['from'], $rs['to']);
|
||||
$imported = true;
|
||||
}
|
||||
$zip->close();
|
||||
unlink($zip_file['tmp_name']);
|
||||
|
||||
if (!empty($not_overwrited)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Some languages has not been overwrited %s'), implode(', ', $not_overwrited)
|
||||
));
|
||||
} elseif (!$done) {
|
||||
throw new Exception(sprintf(
|
||||
__('Nothing to import for these modules in pack %s'), $zip_file['name']
|
||||
));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export (to output) language pack
|
||||
*
|
||||
* @param array $modules The modules to work on
|
||||
* @param array $langs Langs to export
|
||||
*/
|
||||
public function exportPack(array $langs)
|
||||
{
|
||||
if (empty($langs)) {
|
||||
throw new Exception(
|
||||
__('Wrong export query')
|
||||
);
|
||||
}
|
||||
|
||||
$filename = files::tidyFileName($this->translater->export_filename);
|
||||
if (empty($filename)) {
|
||||
throw new Exception(
|
||||
__('Export mask is not set in plugin configuration')
|
||||
);
|
||||
}
|
||||
|
||||
$res = [];
|
||||
foreach($langs AS $lang) {
|
||||
if (!is_dir($this->prop['locales'] . '/' . $lang)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$files = dcTranslater::scandir($this->prop['locales'] . '/' . $lang);
|
||||
foreach($files as $file) {
|
||||
|
||||
if (is_dir($this->prop['locales'] . '/' . $lang . '/' . $file)
|
||||
|| !dcTranslater::isLangphpFile($file)
|
||||
&& !dcTranslater::isPoFile($file)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$res[$this->prop['locales'] . '/' . $lang . '/' . $file] =
|
||||
$this->prop['id'] . '/locales/' . $lang . '/' . $file;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($res)) {
|
||||
throw new Exception(
|
||||
__('Nothing to export')
|
||||
);
|
||||
}
|
||||
|
||||
@set_time_limit(300);
|
||||
$fp = fopen('php://output', 'wb');
|
||||
$zip = new fileZip($fp);
|
||||
foreach($res as $from => $to) {
|
||||
$zip->addFile($from, $to);
|
||||
}
|
||||
|
||||
$filename = files::tidyFileName(dt::str(str_replace(
|
||||
['timestamp', 'module', 'type', 'version'],
|
||||
[time(), $this->prop['id'], $this->prop['type'], $this->prop['version']],
|
||||
$this->translater->export_filename
|
||||
)));
|
||||
|
||||
header('Content-Disposition: attachment;filename=' . $filename . '.zip');
|
||||
header('Content-Type: application/x-zip');
|
||||
$zip->write();
|
||||
unset($zip);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse zip filename to module, lang info
|
||||
*
|
||||
* @param string $file The zip filename
|
||||
* @param boolean $throw Silently failed
|
||||
* @return mixed Array of file info or false
|
||||
*/
|
||||
public function parseZipFilename(string $file = '', bool $throw = false)
|
||||
{
|
||||
$is_file = preg_match('/^(.*?)\/locales\/(.*?)\/(.*?)(.po|.lang.php)$/', $file, $f);
|
||||
|
||||
if ($is_file) {
|
||||
$module = $f[1] == $this->prop['id'] ?$f[1] : false;
|
||||
$lang = l10n::isCode($f[2]) ? $f[2] : false;
|
||||
$group = in_array($f[3], dctranslater::$allowed_l10n_groups) ? $f[3] : false;
|
||||
$ext = dctranslater::isLangphpFile($f[4]) || dctranslater::isPoFile($f[4]) ? $f[4] : false;
|
||||
}
|
||||
|
||||
if (!$is_file || !$module || !$lang || !$group || !$ext) {
|
||||
if ($throw) {
|
||||
throw new Exception(sprintf(
|
||||
__('Zip file %s is not in translater format'), $file
|
||||
));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return [
|
||||
'module' => $module,
|
||||
'lang' => $lang,
|
||||
'group' => $group,
|
||||
'ext' => $ext
|
||||
];
|
||||
}
|
||||
//@}
|
||||
|
||||
/// @name lang methods
|
||||
//@{
|
||||
/**
|
||||
* List available langs of a module
|
||||
*
|
||||
* @param boolean $return_path Return path or name
|
||||
* @return array The lang list
|
||||
*/
|
||||
public function getLangs(bool $return_path = false)
|
||||
{
|
||||
$res = [];
|
||||
|
||||
$prefix = preg_match('/(locales(.*))$/', $this->prop['locales']) ? 'locales' : '';
|
||||
|
||||
$files = dcTranslater::scandir($this->prop['locales']);
|
||||
foreach($files as $file) {
|
||||
if (!preg_match('/.*?locales\/([^\/]*?)\/([^\/]*?)(.lang.php|.po)$/', $prefix . $file, $m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!l10n::isCode($m[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($return_path) {
|
||||
$res[$m[1]][] = $file; // Path
|
||||
} else {
|
||||
$res[$m[1]] = l10n::getLanguageName($m[1]); // Lang name
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of used langs of a module
|
||||
*
|
||||
* @return array The list of iso names and codes
|
||||
*/
|
||||
public function getUsedLangs()
|
||||
{
|
||||
return array_flip($this->getLangs());
|
||||
}
|
||||
|
||||
/**
|
||||
* List of unsused langs of a module
|
||||
*
|
||||
* @return array The list of iso names and codes
|
||||
*/
|
||||
public function getUnusedLangs()
|
||||
{
|
||||
return array_diff(l10n::getISOcodes(true, false), $this->getUsedLangs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a lang to a module
|
||||
*
|
||||
* @param string $lang The lang id
|
||||
* @param string $from_lang The lang to copy from
|
||||
* @return boolean True on success
|
||||
*/
|
||||
public function addLang(string $lang, string $from_lang = '')
|
||||
{
|
||||
if (!l10n::isCode($lang)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Wrong language %s'), $lang
|
||||
));
|
||||
}
|
||||
|
||||
$langs = $this->getLangs();
|
||||
if (isset($langs[$lang])) {
|
||||
throw new Exception(sprintf(
|
||||
__('Language %s already exists'), $lang
|
||||
));
|
||||
}
|
||||
|
||||
files::makeDir($this->prop['locales'] . '/' . $lang, true);
|
||||
|
||||
if (!empty($from_lang) && !isset($langs[$from_lang])) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot copy file from language %s'), $from_lang
|
||||
));
|
||||
}
|
||||
|
||||
if (!empty($from_lang) && isset($langs[$from_lang])) {
|
||||
$files = dcTranslater::scandir($this->prop['locales'] . '/' . $from_lang);
|
||||
foreach($files as $file) {
|
||||
if (is_dir($this->prop['locales'] . '/' . $from_lang . '/' . $file)
|
||||
|| !dcTranslater::isLangphpFile($file)
|
||||
&& !dcTranslater::isPoFile($file)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
files::putContent($this->prop['locales'] . '/' . $lang . '/' . $file,
|
||||
file_get_contents($this->prop['locales'] . '/' . $from_lang . '/' . $file)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
//$this->setLangphpFile($lang, 'main', []);
|
||||
$this->setPoContent($lang, 'main', []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing lang
|
||||
*
|
||||
* @param string $lang The lang
|
||||
* @param array $msgs The messages
|
||||
*/
|
||||
public function updLang(string $lang, array $msgs)
|
||||
{
|
||||
if (!l10n::isCode($lang)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Wrong language %s'), $lang
|
||||
));
|
||||
}
|
||||
|
||||
$langs = $this->getLangs();
|
||||
if (!isset($langs[$lang])) {
|
||||
throw new Exception(sprintf(
|
||||
__('Failed to find language %s'), $lang
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->translater->backup_auto) {
|
||||
$this->createBackup($lang);
|
||||
}
|
||||
|
||||
$rs = [];
|
||||
foreach($msgs as $msg) {
|
||||
if (empty($msg['msgstr'][0])) {
|
||||
continue;
|
||||
}
|
||||
$rs[$msg['group']][] = $msg;
|
||||
}
|
||||
|
||||
foreach(dcTranslater::$allowed_l10n_groups as $group) {
|
||||
if (isset($rs[$group])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$po_file = $this->prop['locales'] . '/' . $lang . '/' . $group . '.po';
|
||||
$langphp_file = $this->prop['locales'] . '/' . $lang . '/' . $group . '.lang.php';
|
||||
|
||||
if (file_exists($po_file)) {
|
||||
unlink($po_file);
|
||||
}
|
||||
if (file_exists($langphp_file)) {
|
||||
unlink($langphp_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($rs)) {
|
||||
throw new Exception(sprintf(
|
||||
__('No string to write, language %s deleted for module %s'),
|
||||
$lang, $id
|
||||
));
|
||||
}
|
||||
|
||||
foreach($rs as $group => $msgs) {
|
||||
$this->setPoContent($lang, $group, $msgs);
|
||||
//$this->setLangphpFile($lang, $group, $msgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a lang
|
||||
*
|
||||
* @param string $lang The lang code
|
||||
* @param boolean $del_empty_dir Also remove empty locales dir
|
||||
* @return boolean True on success
|
||||
*/
|
||||
public function delLang(string $lang, bool $del_empty_dir = true): bool
|
||||
{
|
||||
# Path is right formed
|
||||
if (!l10n::isCode($lang)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Wrong language %s'), $lang
|
||||
));
|
||||
}
|
||||
|
||||
$files = $this->getLangs(true);
|
||||
if (!isset($files[$lang])) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot find language folder %s'), $lang
|
||||
));
|
||||
}
|
||||
|
||||
foreach($files[$lang] as $file) {
|
||||
unlink($this->prop['locales'] . '/' . $file);
|
||||
}
|
||||
|
||||
$dir = dcTranslater::scandir($this->prop['locales'] . '/' . $lang);
|
||||
if (empty($dir)) {
|
||||
rmdir($this->prop['locales'] . '/' . $lang);
|
||||
}
|
||||
|
||||
$loc = dcTranslater::scandir($this->prop['locales']);
|
||||
if (empty($loc)) {
|
||||
rmdir($this->prop['locales']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a lang file
|
||||
*
|
||||
* @param string $file The full file path
|
||||
* @param string $content The file content
|
||||
* @param boolean $throw Silently failed
|
||||
* @return boolean True on success
|
||||
*/
|
||||
private function writeLang(string $file, string $content, bool $throw = false)
|
||||
{
|
||||
$path = path::info($file);
|
||||
if (is_dir($path['dirname']) && !is_writable($path['dirname'])
|
||||
|| file_exists($file) && !is_writable($file)) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot grant write acces on lang file %s'), $file
|
||||
));
|
||||
}
|
||||
|
||||
# -- BEHAVIOR -- dcTranslaterBeforeWriteLangFile
|
||||
$this->core->callBehavior('dcTranslaterBeforeWriteLangFile', $file, $content, $throw);
|
||||
|
||||
$f = @files::putContent($file,$content);
|
||||
if (!$f && $throw) {
|
||||
throw new Exception(sprintf(
|
||||
__('Cannot write lang file %s'), $file
|
||||
));
|
||||
}
|
||||
|
||||
# -- BEHAVIOR -- dcTranslaterAfterWriteLangFile
|
||||
$this->core->callBehavior('dcTranslaterAfterWriteLangFile', $f, $file, $content, $throw);
|
||||
|
||||
return $f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct and parse a .po file
|
||||
*
|
||||
* @param string $lang The lang code
|
||||
* @param string $group The lang group
|
||||
* @param array $msgs The strings
|
||||
*/
|
||||
private function setPoContent(string $lang, string $group, array $msgs)
|
||||
{
|
||||
if (!$this->translater->write_po) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lang = new dcTranslaterLang($this, $lang);
|
||||
|
||||
$l = '';
|
||||
if ($this->translater->parse_comment) {
|
||||
$l .=
|
||||
'# Language: ' . $lang->name . "\n" .
|
||||
'# Module: ' . $this->prop['id'] . " - " . $this->prop['version'] . "\n" .
|
||||
'# Date: ' . dt::str('%Y-%m-%d %H:%M:%S') . "\n";
|
||||
|
||||
if ($this->translater->parse_user && !empty($this->translater->parse_userinfo)) {
|
||||
$search = dctranslater::$allowed_user_informations;
|
||||
foreach($search AS $n) {
|
||||
$replace[] = $this->core->auth->getInfo('user_' . $n);
|
||||
}
|
||||
$info = trim(str_replace($search, $replace, $this->translater->parse_userinfo));
|
||||
if (!empty($info)) {
|
||||
$l .= '# Author: ' . html::escapeHTML($info) . "\n";
|
||||
}
|
||||
}
|
||||
$l .=
|
||||
'# Translated with translater ' . $this->core->plugins->moduleInfo('translater', 'version') . "\n";
|
||||
}
|
||||
$l .=
|
||||
"\n".
|
||||
"msgid \"\"\n" .
|
||||
"msgstr \"\"\n" .
|
||||
'"Content-Type: text/plain; charset=UTF-8\n"' . "\n" .
|
||||
'"Project-Id-Version: ' . $this->prop['id'] . ' ' . $this->prop['version'] . '\n"' . "\n" .
|
||||
'"POT-Creation-Date: \n"' . "\n" .
|
||||
'"PO-Revision-Date: ' . date('c') . '\n"' . "\n" .
|
||||
'"Last-Translator: ' . $this->core->auth->getInfo('user_cn') . '\n"' . "\n" .
|
||||
'"Language-Team: \n"' . "\n" .
|
||||
'"MIME-Version: 1.0\n"' . "\n" .
|
||||
'"Content-Transfer-Encoding: 8bit\n"' . "\n" .
|
||||
'"Plural-Forms: nplurals=2; plural=(n > 1);\n"' . "\n\n";
|
||||
|
||||
$comments = [];
|
||||
if ($this->translater->parse_comment) {
|
||||
$msgids = $lang->getMsgids();
|
||||
foreach($msgids as $msg) {
|
||||
$comments[$msg['msgid']] = (isset($comments[$msg['msgid']]) ?
|
||||
$comments[$msg['msgid']] : '') .
|
||||
'#: '.trim($msg['file'],'/') . ':' . $msg['line'] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$content = '';
|
||||
foreach($msgs as $msg) {
|
||||
if (empty($msg['msgstr'][0])) {
|
||||
continue;
|
||||
}
|
||||
if ($this->translater->parse_comment && isset($comments[$msg['msgid']])) {
|
||||
$content .= $comments[$msg['msgid']];
|
||||
}
|
||||
$content .= 'msgid "' . dcTranslater::poString($msg['msgid'], true) . '"' . "\n";
|
||||
if (empty($msg['msgid_plural'])) {
|
||||
$content .= 'msgstr "' . dcTranslater::poString($msg['msgstr'][0], true) . '"' . "\n";
|
||||
} else {
|
||||
$content .= 'msgid_plural "' . dcTranslater::poString($msg['msgid_plural'], true) . '"' . "\n";
|
||||
foreach($msg['msgstr'] as $i => $plural) {
|
||||
$content .= 'msgstr[' . $i . '] "' . dcTranslater::poString(($msg['msgstr'][$i] ?: ''), true) . '"' . "\n";
|
||||
}
|
||||
}
|
||||
$content .= "\n";
|
||||
}
|
||||
|
||||
self::writeLang($this->locales . '/' . $lang->code . '/' . $group . '.po', $content, true);
|
||||
}
|
||||
//@}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,91 +0,0 @@
|
|||
/* -- BEGIN LICENSE BLOCK ----------------------------------
|
||||
*
|
||||
* This file is part of translater, a plugin for Dotclear 2.
|
||||
*
|
||||
* Copyright (c) 2009-2013 Jean-Christian Denis and contributors
|
||||
* contact@jcdenis.fr
|
||||
*
|
||||
* Licensed under the GPL version 2.0 license.
|
||||
* A copy of this license is available in LICENSE file or at
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
*
|
||||
* -- END LICENSE BLOCK ------------------------------------*/
|
||||
|
||||
;if(window.jQuery) (function($){
|
||||
$.fn.translater = function(options){
|
||||
var opts = $.extend({}, $.fn.translater.defaults, options);
|
||||
return this.each(function(){
|
||||
|
||||
var img = '<img src="index.php?pf=translater/inc/img/field.png" |