-
' . __('Backups') . '
-
-
-
+
' . __('Backups') . '
+
+
+
' .
+form::combo('backup_folder', $translater::$allowed_backup_folders, $translater->backup_folder) . '
-
-
' . __('Behaviors') . '
-
-
-
-
-';
\ No newline at end of file
+
' . __('Behaviors') . '
+
' .
+form::combo('start_page',[
+ __('Plugins') => 'plugin',
+ __('Themes') => 'theme',
+ __('Home') => '-'
+], $translater->start_page) . '
+
+
+
';
\ No newline at end of file
diff --git a/_install.php b/_install.php
index 8e9ee33..fa41ce2 100644
--- a/_install.php
+++ b/_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());
diff --git a/_prepend.php b/_prepend.php
index 4a59052..760a593 100644
--- a/_prepend.php
+++ b/_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';
\ No newline at end of file
+$core->adminurl->register('translater', 'plugin.php', ['p' => 'translater']);
\ No newline at end of file
diff --git a/css/translater.css b/css/translater.css
new file mode 100644
index 0000000..4fc9664
--- /dev/null
+++ b/css/translater.css
@@ -0,0 +1 @@
+.addfield, .togglelist { border: none; }
\ No newline at end of file
diff --git a/inc/img/field.png b/img/field.png
similarity index 100%
rename from inc/img/field.png
rename to img/field.png
diff --git a/inc/img/toggle.png b/img/toggle.png
similarity index 100%
rename from inc/img/toggle.png
rename to img/toggle.png
diff --git a/inc/class.dc.translater.lang.php b/inc/class.dc.translater.lang.php
new file mode 100644
index 0000000..5ef5790
--- /dev/null
+++ b/inc/class.dc.translater.lang.php
@@ -0,0 +1,221 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/inc/class.dc.translater.module.php b/inc/class.dc.translater.module.php
new file mode 100644
index 0000000..5d6e946
--- /dev/null
+++ b/inc/class.dc.translater.module.php
@@ -0,0 +1,749 @@
+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);
+ }
+ //@}
+}
\ No newline at end of file
diff --git a/inc/class.dc.translater.php b/inc/class.dc.translater.php
index 52d88bc..c11b0b7 100644
--- a/inc/class.dc.translater.php
+++ b/inc/class.dc.translater.php
@@ -20,42 +20,11 @@ if (!defined('DC_CONTEXT_ADMIN')) {
*/
class dcTranslater
{
+ /** @var dCore dcCore instance */
public $core;
- public $proposal;
-
- /** @var array $iso List of l10n code/name allowed from clearbricks l10n class */
- protected static $iso = [];
-
- /** @var array $allowed_backup_folders List of allowed backup folder */
- public static $allowed_backup_folders = [
- 'public',
- 'module',
- 'plugin',
- 'cache',
- 'translater'
- ];
-
- /** @var array $allowed_l10n_groups List of place of tranlsations */
- public static $allowed_l10n_groups = [
- 'main',
- 'public',
- 'theme',
- 'admin',
- 'date',
- 'error'
- ];
-
- /** @var array $allowed_user_informations List of user info can be parsed */
- public static $allowed_user_informations = [
- 'firstname',
- 'displayname',
- 'name',
- 'email',
- 'url'
- ];
/** @var array $default_settings Plugins default settings */
- private $default_settings = [
+ private static $default_settings = [
'plugin_menu' => [
'id' => 'translater_plugin_menu',
'value' => 0,
@@ -100,7 +69,7 @@ class dcTranslater
],
'write_langphp' => [
'id' => 'translater_write_langphp',
- 'value' => 1,
+ 'value' => 0,
'type' => 'boolean',
'label' => 'Write .lang.php languages files'
],
@@ -166,30 +135,50 @@ class dcTranslater
]
];
- /** @var array $default_dotclear_modules List of distributed plugins and themes */
- public static $default_dotclear_modules = ['plugin' => [], 'theme' => []];
+ /** @var array $allowed_backup_folders List of allowed backup folder */
+ public static $allowed_backup_folders = [];
+
+ /** @var array $allowed_l10n_groups List of place of tranlsations */
+ public static $allowed_l10n_groups = [
+ 'main', 'public', 'theme', 'admin', 'date', 'error'
+ ];
+
+ /** @var array $allowed_user_informations List of user info can be parsed */
+ public static $allowed_user_informations = [
+ 'firstname', 'displayname', 'name', 'email', 'url'
+ ];
+
+ /** @var array $default_distrib_modules List of distributed plugins and themes */
+ public static $default_distrib_modules = ['plugin' => [], 'theme' => []];
/** @var array $modules List of modules we could work on */
private $modules = [];
- /** @var array $module Module to work on */
- private $module = [];
-
/**
- * Main translater object
+ * translater instance
*
* @param dcCore $core dcCore instance
*/
- public function __construct(dcCore $core)
+ public function __construct($core)
{
- self::$default_dotclear_modules = [
- 'plugin' => explode(',', DC_DISTRIB_PLUGINS),
- 'theme' => explode(',', DC_DISTRIB_THEMES)
- ];
$this->core = $core;
$core->blog->settings->addNamespace('translater');
$this->loadModules();
- $this->proposal = new translaterProposals($core);
+
+ // fill with translated string
+ self::$allowed_backup_folders = [
+ __('locales folders of each module') => 'module',
+ __('plugins folder root') => 'plugin',
+ __('public folder root') => 'public',
+ __('cache folder of Dotclear') => 'cache',
+ __('locales folder of translater') => 'translater'
+ ];
+
+ // fill distrib modules list
+ self::$default_distrib_modules = [
+ 'plugin' => explode(',', DC_DISTRIB_PLUGINS),
+ 'theme' => explode(',', DC_DISTRIB_THEMES)
+ ];
}
/// @name settings methods
@@ -201,7 +190,7 @@ class dcTranslater
*/
public function getDefaultSettings(): array
{
- return $this->default_settings;
+ return self::$default_settings;
}
/**
@@ -211,13 +200,9 @@ class dcTranslater
* @return mixed The setting value if exists or null
*/
public function getSetting(string $id)
- {
- if (!isset($this->default_settings[$id])) {
- return null;
- }
- return $this->core->blog->settings->translater->get(
- $this->default_settings[$id]['id']
- );
+ {
+ return array_key_exists($id, self::$default_settings) ?
+ $this->core->blog->settings->translater->get(self::$default_settings[$id]['id']) : '';
}
/**
@@ -231,99 +216,72 @@ class dcTranslater
/**
* Set a setting according to default settings list
*
- * @param string $k The setting short id
- * @param mixed $v The setting value
+ * @param string $id The setting short id
+ * @param mixed $value The setting value
+ * @param mixed $overwrite Overwrite settings if exists
+ * @return boolean Success
*/
- public function setSetting(string $k, $v)
+ public function setSetting(string $id, $value, $overwrite = true)
{
- if (!isset($this->default_settings[$k])) {
+ if (!array_key_exists($id, self::$default_settings)) {
return false;
}
-
- $this->core->blog->settings->translater->drop($id);
+ $this->core->blog->settings->translater->drop(self::$default_settings[$id]['id']);
$this->core->blog->settings->translater->put(
- $this->default_settings[$k]['id'],
- $v,
- $this->default_settings[$k]['type'],
- $this->default_settings[$k]['label'],
- true,
+ self::$default_settings[$id]['id'],
+ $value,
+ self::$default_settings[$id]['type'],
+ self::$default_settings[$id]['label'],
+ $overwrite,
true
);
return true;
}
/**
- * Magis setSetting
+ * Magic setSetting
*/
- public function __set($k, $v)
+ public function __set($id, $value)
{
- return $this->setSetting($k, $v);
+ return $this->setSetting($id, $value);
}
//@}
/// @name modules methods
//@{
- /**
- * Retrieve a particular info for a given module
- *
- * @param string $id The module id
- * @param string $info The module info
- * @return mixed The module info value or null
- */
- public function moduleInfo(string $id, string $info)
- {
- if (isset($this->modules['plugin'][$id])) {
- $type = 'plugin';
- } elseif (isset($this->modules['theme'][$id])) {
- $type = 'theme';
- } else {
- return null;
- }
-
- if ($info == 'type') {
- return $type;
- }
-
- return isset($this->modules[$type][$id][$info]) ? $this->modules[$type][$id][$info] : null;
- }
-
/**
* Load array of modules infos by type of modules
*/
private function loadModules()
{
- $themes = new dcThemes($this->core);
- $themes->loadModules($this->core->blog->themes_path, null);
$this->modules['theme'] = $this->modules['plugin'] = [];
- $m = $themes->getModules();
- foreach($m AS $k => $v) {
- if (!$v['root_writable']) {
- continue;
- }
- $this->modules['theme'][$k] = $v;
- $this->modules['theme'][$k]['id'] = $k;
- $this->modules['theme'][$k]['type'] = 'theme';
- }
+ $themes = new dcThemes($this->core);
+ $themes->loadModules($this->core->blog->themes_path, null);
- $m = $this->core->plugins->getModules();
- foreach($m AS $k => $v) {
- if (!$v['root_writable']) {
- continue;
+ $list = [
+ 'theme' => $themes->getModules(),
+ 'plugin' => $this->core->plugins->getModules()
+ ];
+ foreach($list as $type => $modules) {
+ foreach($modules as $id => $info) {
+ if (!$info['root_writable']) {
+// continue;
+ }
+ $info['id'] = $id;
+ $info['type'] = $type;
+ $this->modules[$type][$id] = new dcTranslaterModule($this, $info);
}
- $this->modules['plugin'][$k] = $v;
- $this->modules['plugin'][$k]['id'] = $k;
- $this->modules['plugin'][$k]['type'] = 'plugin';
}
}
/**
* Return array of modules infos by type of modules
*
- * @param string $type The module type
- * @return array The list of modules infos
+ * @param string $type The modules type
+ * @return array The list of modules infos
*/
- public function listModules(string $type = '')
+ public function getModules(string $type = ''): array
{
return in_array($type, ['plugin', 'theme']) ?
$this->modules[$type] :
@@ -331,1276 +289,62 @@ class dcTranslater
}
/**
- * Return array object of a particular module for a given type of module
+ * Return module class of a particular module for a given type of module
*
- * @param string $id The module id
- * @param string $type The module type
- * @return ArrayObject The module info
+ * @param string $type The module type
+ * @param string $id The module id
+ * @return dcTranslaterModule The dcTranslaterModule instance
*/
- public function getModule(string $id = '', string $type = '')
+ public function getModule(string $type, string $id)
{
- $o = new ArrayObject();
-
- # Load nothing?
- if (empty($id)) {
- return false;
- }
-
- # Unknow type?
- $modules = !in_array($type, ['plugin', 'theme']) ?
- array_merge($this->modules['theme'], $this->modules['plugin']) :
- $this->modules[$type];
-
- # Unknow module?
- if (!isset($modules[$id])) {
+ if (!isset($this->modules[$type][$id])) {
throw new Exception(
sprintf(__('Cannot find module %s'), $id)
);
return false;
}
+ return $this->modules[$type][$id];
+ }
- # Module info
- foreach($modules[$id] as $a => $b) {
- $o->{$a} = $b;
+ /**
+ * Return module class of a particular module for a given type of module
+ *
+ * @param string $module dcTranslaterModule instance
+ * @param string $lang The lang iso code
+ * @return dcTranslaterLang dcTranslaterLang instance or false
+ */
+ public function getLang(dcTranslaterModule $module, string $lang)
+ {
+ if (!l10n::isCode($lang)) {
+ throw new Exception(
+ sprintf(__('Cannot find lang %s'), $lang)
+ );
+ return false;
}
- $o->root = path::real($o->root);
- # Locales path
- $o->locales = $o->root . '/locales';
- # Module exists
- $o->exists = true;
- # Module Basename
- $i = path::info($o->root);
- $o->basename = $i['basename'];
-
- return $o;
+ return new dcTranslaterLang($module, $lang);
}
//@}
- /**
- * Find backup folder of a module
+ /// @name helper methods
+ //@{
+ /**
+ * Scan recursively a folder and return files and folders names
*
- * @param string $id The module id
- * @param boolean $throw Silently failed
- * @return mixed The backup folder directory or false
+ * @param string $path The path to scan
+ * @param string $dir Internal recursion
+ * @param array $res Internal recursion
+ * @return array List of path
*/
- public function getBackupFolder(string $id, bool $throw = false)
+ public static function scandir(string $path, string $dir = '', array $res = [])
{
- $dir = false;
- switch($this->backup_folder) {
- case 'module':
- # plugin
- if (isset($this->modules['plugin'][$id])
- && $this->modules['plugin'][$id]['root_writable']
- ) {
- $dir = path::real($this->modules['plugin'][$id]['root']) . '/locales';
- #theme
- } elseif (isset($this->modules['theme'][$id])
- && $this->modules['theme'][$id]['root_writable']
- ) {
- $dir = path::real($this->modules['theme'][$id]['root']) . '/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->modules['plugin']['translater']['root']);
- 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;
- }
-
- /**
- * Find language path of a module
- *
- * @param string $id The module id
- * @param boolean $throw Silently failed
- * @return mixed The module language path or false
- */
- public function getLangsFolder(string $id = '', bool $throw = false)
- {
- $dir = $id == 'dotclear' ?
- DC_ROOT :
- self::getModuleFolder($id, false);
-
- if (!$dir && $throw) {
- throw new Exception(
- sprintf(__('Cannot find languages folder for module %s'), $id)
- );
- }
-
- return !$dir ? false : $dir . '/locales';
- }
-
- /**
- * Find root path of a module
- *
- * @param string $module The module id
- * @param boolean $throw Silently failed
- * @return mixed The module root path or false
- */
- public function getModuleFolder($id = '', $throw = false)
- {
- $dir = false;
- if ((isset($this->modules['plugin'][$id]['root'])
- && ($tmp = path::real($this->modules['plugin'][$id]['root'])))
- || (isset($this->modules['theme'][$id]['root'])
- && ($tmp = path::real($this->modules['theme'][$id]['root']))
- )) {
- $dir = $tmp;
- }
- if (!$dir && $throw) {
- throw new Exception(
- sprintf(__('Cannot find root folder for module %s'), $id)
- );
- }
-
- return $dir;
- }
-
- /**
- * Check limit number of backup for a module
- *
- * @param string $id The module id
- * @param boolean $throw Silently failed
- * @return boolean True if limit is riched
- */
- public function isBackupLimit(string $id, bool $throw = false): bool
- {
- # Find folder of backups
- $backup = self::getBackupFolder($id, true);
-
- # Count backup files
- $count = 0;
- foreach(self::scandir($backup) AS $file) {
- if (!is_dir($backup . '/' . $file)
- && preg_match('/^(l10n-'. $id . '(.*?).bck.zip)$/', $backup)
- ) {
- $count++;
- }
- }
-
- # Limite exceed
- if ($count >= $this->backup_limit) {
- if ($throw) {
- throw new Exception(
- sprintf(__('Limit of %s backups for module %s exceed'), $this->backup_limit, $id)
- );
- }
- return true;
- }
- return false;
- }
-
- /**
- * Get a list of available backups
- *
- * @param string $id The module id
- * @param bool|boolean $return_filename Return only filenames
- * @return array The module backups info
- */
- public function listBackups(string $id, bool $return_filename = false): array
- {
- # Not a module installed
- self::getLangsFolder($id, true);
-
- # No backup folder for this module
- $backup = self::getBackupFolder($id, false);
- if (!$backup) {
+ $path = path::real($path, false);
+ if (!is_dir($path) || !is_readable($path)) {
return [];
}
- # Scan files for backups
- $res = $sort = [];
- $files = self::scandir($backup);
- foreach($files AS $file) {
- # Not a bakcup file
- $is_backup = preg_match(
- '/^(l10n-(' . $id . ')-(.*?)-([0-9]*?).bck.zip)$/', $file, $m
- );
-
- if (is_dir($backup . '/' . $file)
- || !$is_backup
- || !self::isIsoCode($m[3])
- ) {
- continue;
- }
-
- # Backup infos
- if ($return_filename) {
- $res[] = $file;
- } else {
- $res[$m[3]][$file] = path::info($backup . '/' . $file);
- $res[$m[3]][$file]['time']= filemtime($backup . '/' . $file);
- $res[$m[3]][$file]['size'] = filesize($backup . '/' . $file);
- $res[$m[3]][$file]['module'] = $id;
- }
- }
- return $res;
- }
-
- /**
- * Create a backup
- *
- * @param string $id The module id
- * @param string $lang The backup lang
- * @return boolean True on success
- */
- public function createBackup(string $id, string $lang): bool
- {
- # Not a module installed
- $locales = self::getLangsFolder($id, true);
-
- # No backup folder for this module
- $backup = self::getBackupFolder($id, true);
-
- # Not an existing lang
- if (!is_dir($locales . '/' . $lang)) {
- throw new Exception(sprintf(
- __('Cannot find language folder %s for module %s') ,$lang, $id
- ));
- }
-
- # Scan files for a lang
- $res = [];
- $files = self::scandir($locales . '/' . $lang);
- foreach($files as $file) {
- # Only lang file
- if (!is_dir($locales . '/' . $lang . '/' . $file)
- && (self::isLangphpFile($file)
- || self::isPoFile($file))) {
- $res[$locales . '/' . $lang . '/' . $file] =
- $id . '/locales/' . $lang . '/' . $file;
- }
- }
-
- # Do Zip
- if (!empty($res)) {
- self::isBackupLimit($id, true);
-
- @set_time_limit(300);
- $fp = fopen($backup . '/l10n-' . $id . '-' . $lang . '-' . time() . '.bck.zip', 'wb');
- $zip = new fileZip($fp);
- foreach($res AS $src => $dest) {
- $zip->addFile($src, $dest);
- }
- $zip->write();
- $zip->close();
- unset($zip);
-
- return true;
- }
- }
-
- /**
- * Delete a module backup
- *
- * @param string $id The module id
- * @param string $file The backup filename
- * @return boolean True on success
- */
- public function deleteBackup(string $id, string $file): bool
- {
- # Not a module installed
- self::getLangsFolder($id, true);
-
- # No backup folder for this module
- $backup = self::getBackupFolder($id, true);
-
- # Not a bakcup file
- $is_backup = preg_match('/^(l10n-(' . $id . ')-(.*?)-([0-9]*?).bck.zip)$/', $file, $m);
-
- if (is_dir($backup . '/' . $file)
- || !$is_backup
- || !self::isIsoCode($m[3])
- ) {
- return false;
- }
-
- if (!files::isDeletable($backup . '/' . $file)) {
- throw new Exception(sprintf(
- __('Cannot delete backup file %s'), $file
- ));
- }
-
- unlink($backup . '/' . $file);
-
- return true;
- }
-
- /**
- * Retore a backup
- *
- * @param string $module The module id
- * @param string $file The backup filename
- * @return boolean True on success
- */
- public function restoreBackup(string $id, string $file): bool
- {
- # Not a module installed
- $locales = self::getModuleFolder($id, true);
-
- # No backup folder for this module
- $backup = self::getBackupFolder($id, 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 = self::explodeZipFilename($zip_file, true);
- if ($id != $f['module']) {
- continue;
- }
-
- $zip->unzip($zip_file, $locales . '/locales/' . $f['lang'] . '/' . $f['group'] . $f['ext']);
- $done = true;
- }
- $zip->close();
- unset($zip);
-
- 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 $modules, array $langs)
- {
- # Not a query well formed
- if (!is_array($modules) || 1 > count($modules)
- || !is_array($langs) || 1 > count($langs)
- ) {
- throw new Exception(
- __('Wrong export query')
- );
- }
-
- # Filter default filename
- $filename = files::tidyFileName($this->export_filename);
-
- # Not a filename good formed
- if (empty($filename)) {
- throw new Exception(
- sprintf(__('Cannot use export mask %s'), $this->export_filename)
- );
- }
-
- # Modules folders
- $res = $count = [];
- foreach($modules AS $module) {
- # Not a module installed
- $locales = self::getLangsFolder($module, false);
- if (!$locales) {
- continue;
- }
-
- # Langs folders
- foreach($langs AS $lang) {
- # Not a lang folder
- if (!is_dir($locales . '/' . $lang)) {
- continue;
- }
-
- # Scan files for a lang
- $files = self::scandir($locales . '/' . $lang);
- foreach($files as $file) {
- # Not a lang file
- if (is_dir($locales . '/' . $lang . '/' . $file)
- || !self::isLangphpFile($file)
- && !self::isPoFile($file)
- ) {
- continue;
- }
-
- # Add file to zip in format "module/locales/lang/filename"
- $res[$locales . '/' . $lang . '/' . $file] =
- $module . '/locales/' . $lang . '/' . $file;
-
- $count[$module] = 1;
- }
- }
- }
-
- # Nothing to do
- if (empty($res)) {
- throw new Exception('Nothing to export');
- }
-
- # Prepare files to zip
- @set_time_limit(300);
- $fp = fopen('php://output', 'wb');
- $zip = new fileZip($fp);
- foreach($res as $from => $to) {
- $zip->addFile($from,$to);
- }
-
- # Set filename
- $file_infos = 1 < count($count) ?
- [time(), 'modules', 'multi', self::$dcTranslaterVersion] :
- [
- time(),
- $modules[0],
- self::moduleInfo($modules[0], 'type'),
- self::moduleInfo($modules[0], 'version')
- ];
- $filename =
- files::tidyFileName(
- dt::str(
- str_replace(
- ['timestamp', 'module', 'type', 'version'],
- $file_infos,
- $this->export_filename
- )
- )
- );
-
- # Send Zip
- header('Content-Disposition: attachment;filename=' . $filename . '.zip');
- header('Content-Type: application/x-zip');
- $zip->write();
- unset($zip);
- exit;
- }
-
- /**
- * Import a language pack
- *
- * @param array $modules The modules to work on
- * @param array $zip_file The uploaded file info
- * @return boolean True on success
- */
- public function importPack($modules, $zip_file)
- {
- # Not a file uploaded
- files::uploadStatus($zip_file);
-
- # No modules to update
- if (!is_array($modules) || 1 > count($modules)) {
- throw new Exception(__('Wrong import query'));
- }
-
- $done = false;
- $res = [];
-
- # Load Unzip object
- $zip = new fileUnzip($zip_file['tmp_name']);
- $files = $zip->getFilesList();
-
- # Scan zip
- foreach($files AS $file) {
- $f = self::explodeZipFilename($file, false);
-
- # Not a requested module
- if (!is_array($f)
- || !in_array($f['module'],$modules)
- ) {
- continue;
- }
-
- # Get locales folder (even if "locales" is not set)
- if (!$dir = self::getModuleFolder($f['module'], false)) {
- continue;
- }
- $locales = $dir . '/locales';
-
- # Not allow overwrite
- if (!$this->import_overwrite
- && file_exists($locales . '/' . $f['lang'] . '/' . $f['group'] . $f['ext'])
- ) {
- continue;
- }
-
- $res[] = [
- 'from' => $file,
- 'root' => $locales . '/' . $f['lang'],
- 'to' => $locales . '/' . $f['lang'] . '/' . $f['group'] . $f['ext']
- ];
- }
- # Unzip files
- foreach ($res AS $rs) {
- if (!is_dir($rs['root'])) {
- files::makeDir($rs['root'], true);
- }
-
- $zip->unzip($rs['from'], $rs['to']);
- $done = true;
- }
- $zip->close();
- unlink($zip_file['tmp_name']);
-
- # No file unzip
- if (!$done) {
- throw new Exception(
- sprintf(__('Nothing to import for these modules in pack %s'), $zip_file['name'])
- );
- }
- return true;
- }
-
- /**
- * 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 explodeZipFilename(string $file = '', bool $throw = false)
- {
- # module/locales/lang/group.ext
- $is_file = preg_match(
- '/^(.*?)\/locales\/(.*?)\/(.*?)(.po|.lang.php)$/', $file, $f
- );
-
- # Explode file to infos
- if ($is_file) {
- $module = null !== self::moduleInfo($f[1], 'name') ?
- $f[1] : false;
- $lang = self::isIsoCode($f[2]) ?
- $f[2] : false;
- $group = in_array($f[3], self::$allowed_l10n_groups) ?
- $f[3] : false;
- $ext = self::isLangphpFile($f[4]) || self::isPoFile($f[4]) ?
- $f[4] : false;
- }
- # Not good formed
- 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
- ];
- }
-
- /**
- * List available langs of a module
- *
- * @param string $id The module id
- * @param boolean $return_path Return path or name
- * @return array The lang list
- */
- public function listLangs(string $id, bool $return_path = false)
- {
- $res = [];
-
- # Not a module installed
- $locales = self::getLangsFolder($id, true);
-
- # Add prefix "locales" as scandir remove it
- $prefix = preg_match('/(locales(.*))$/', $locales) ? 'locales' : '';
-
- # Retrieve langs folders
- $files = self::scandir($locales);
- foreach($files as $file) {
- if (!preg_match(
- '/(.*?(locales\/)([^\/]*?)\/([^\/]*?)(.lang.php|.po))$/',
- $prefix . $file, $m
- )) {
- continue;
- }
-
- if (!self::isIsoCode($m[3])) {
- continue;
- }
-
- if ($return_path) {
- $res[$m[3]][] = $file; # Path
- } else {
- $res[$m[3]] = self::$iso[$m[3]]; # Lang name
- }
- }
- return $res;
- }
-
- /**
- * Add a lang to a module
- *
- * @param string $module The module id
- * @param string $lang The lang id
- * @param string $from_lang The lang to copy from
- */
- public function addLang(string $id, string $lang, string $from_lang = '')
- {
- # Not a module installed
- $locales = self::getLangsFolder($id, true);
-
- # Path is right formed
- self::isIsoCode($lang, true);
-
- # Retrieve langs folders
- $langs = self::listLangs($id);
-
- # Lang folder is not present
- if (isset($langs[$lang])) {
- throw new Exception(
- sprintf(__('Language %s already exists for module %s'), $lang, $id)
- );
- }
-
- # Create new lang directory
- files::makeDir($locales . '/' . $lang, true);
-
- # Verify folder of other lang
- if (!empty($from_lang) && !isset($langs[$from_lang])) {
- throw new Exception(
- sprintf(__('Cannot copy file from language %s for module %s'), $from_lang, $id)
- );
- }
-
- # Copy files from other lang
- if (!empty($from_lang)
- && isset($langs[$from_lang])
- ) {
- $files = self::scandir($locales . '/' . $from_lang);
- foreach($files as $file) {
- if (is_dir($locales . '/' . $from_lang . '/' . $file)
- || !self::isLangphpFile($file)
- && !self::isPoFile($file)
- ) {
- continue;
- }
-
- files::putContent($locales . '/' . $lang . '/' . $file,
- file_get_contents($locales . '/' . $from_lang . '/' . $file)
- );
- }
- } else {
- # Create basic empty lang file as translater need these files to be present
- self::setLangphpFile($id, $lang, 'main', []);
- self::setPoFile($id, $lang, 'main', []);
- }
- }
-
- /**
- * Update an existing lang
- *
- * @param string $module The module id
- * @param string $lang The lang
- * @param array $msgs The messages
- */
- public function updLang(string $id, string $lang, array $msgs)
- {
- # Not a module installed
- $locales = self::getLangsFolder($id, true);
-
- # Path is right formed
- self::isIsoCode($lang, true);
-
- # Retrieve langs folders
- $langs = self::listLangs($id);
-
- # Lang folder is not present
- if (!isset($langs[$lang])) {
- throw new Exception(sprintf(
- __('Cannot find language folder %s for module %s'), $lang, $id
- ));
- }
-
- # Sort msgids by groups
- $rs = [];
- foreach($msgs as $msg) {
- $msg['group'] = isset($msg['group']) ? $msg['group'] : '';
- $msg['msgid'] = isset($msg['msgid']) ? $msg['msgid'] : '';
- $msg['msgstr'] = isset($msg['msgstr']) ? trim($msg['msgstr']) : '';
-/*
- if (get_magic_quotes_gpc()) {
- $msg['msgid'] = stripcslashes($msg['msgid']);
- $msg['msgstr'] = stripcslashes($msg['msgstr']);
- }
-*/ if ($msg['msgstr'] == '') {
- continue;
- }
-
- $rs[$msg['group']][$msg['msgid']] = $msg['msgstr'];
- }
-
- # Backup files if auto-backup is on
- if ($this->backup_auto) {
- self::createBackup($id, $lang);
- }
-
- # Delete empty files (files with no group)
- foreach(self::$allowed_l10n_groups AS $group) {
- if (isset($rs[$group])) {
- continue;
- }
-
- $po_file = $locales . '/' . $lang . '/' . $group . '.po';
- $langphp_file = $locales . '/' . $lang . '/' . $group . '.lang.php';
-
- if (file_exists($po_file)) {
- unlink($po_file);
- }
- if (file_exists($langphp_file)) {
- unlink($langphp_file);
- }
- }
-
- # No msgstr to write
- if (empty($rs)) {
- throw new Exception(sprintf(
- __('No string to write, language %s deleted for module %s'),
- $lang, $id
- ));
- }
-
- # Write .po and .lang.php files
- foreach($rs AS $group => $msgs) {
- self::setLangphpFile($id, $lang, $group, $msgs);
- self::setPoFile($id, $lang, $group, $msgs);
- }
- }
-
- public function delLang($module, $lang, $del_empty_dir = true)
- {
- # Not a module installed
- $locales = self::getLangsFolder($module, true);
-
- # Path is right formed
- self::isIsoCode($lang, true);
-
- # Retrieve langs folders
- $files = self::listLangs($module, true);
-
- # Lang folder is not present
- if (!isset($files[$lang])) {
- throw new Exception(sprintf(
- __('Cannot find language folder %s for module %s'), $lang, $module
- ));
- }
-
- # Delete .po and .lang.php files
- foreach($files[$lang] as $file) {
- unlink($locales . '/' . $file);
- }
-
- # Delete lang folder if empty
- $dir = self::scandir($locales . '/' . $lang);
- if (empty($dir)) {
- rmdir($locales . '/' . $lang);
- }
-
- # Delete locales folder if empty
- $loc = self::scandir($locales);
- if (empty($loc)) {
- rmdir($locales);
- }
- }
-
- public static function encodeMsg($str)
- {
- return text::toUTF8(stripslashes(trim($str)));
- }
-
- /* Scan a module folder to find all l10n strings in .php files */
- public function getMsgIds($module)
- {
- $res = array();
-
- # Not a module installed
- $dir = self::getModuleFolder($module, true);
-
- $files = self::scandir($dir);
-
- $scan_ext = array('php');
- if ($this->scan_tpl) {
- $scan_ext[] = 'html';
- }
-
- foreach($files AS $file) {
- if (is_dir($dir . '/' . $file)
- || !in_array(files::getExtension($file),$scan_ext)) {
- continue;
- }
-
- $contents = file($dir . '/' . $file);
- foreach($contents AS $line => $content) {
- # php files
- //if (preg_match_all("|__\((['\"]{1})(.*)([\"']{1})\)|U", $content, $matches))
- if (preg_match_all("|__\((['\"]{1})(.*?)([\"']{1})\)|", $content, $matches)) {
- foreach($matches[2] as $id) {
- $res[] = array(
- 'msgid' => self::encodeMsg($id),
- 'file' => $file,
- 'line' => $line + 1
- );
- }
- }
- # tpl files
- if ($this->scan_tpl
- && preg_match_all('/\{\{tpl:lang\s([^}]+)\}\}/', $content, $matches)) {
- foreach($matches[1] as $id) {
- $res[] = array(
- 'msgid' => self::encodeMsg($id),
- 'file' => $file,
- 'line' => $line + 1
- );
- }
- }
- }
- unset($contents);
- }
- return $res;
- }
-
- /* Scan a lang folder to find l10n translations in files */
- public function getMsgStrs($module, $requested_lang = '')
- {
- $res = array();
-
- # Not a module installed
- $locales = self::getLangsFolder($module, true);
-
- $langs = self::listLangs($module, true);
-
- # Not an existing lang
- if (!isset($langs[$requested_lang])) {
- return $res;
- }
-
- # Lang files
- $exists = array();
- foreach($langs[$requested_lang] as $file) {
- if (in_array($file,$exists)) {
- continue;
- }
- $exists[] = $file;
- $path = path::clean($locales . '/' . $file);
-
- # .po files
- if (self::isPoFile($file)) {
- $po = self::getPoFile($path);
- if (!is_array($po)) {
- continue;
- }
-
- foreach($po as $id => $str) {
- $is_po[$requested_lang][$id] = 1;
-
- $res[] = array(
- 'msgid' => self::encodeMsg($id),
- 'msgstr' => self::encodeMsg($str),
- 'lang' => $requested_lang,
- '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;
- }
-
- public function getMsgs($module, $requested_lang = '')
- {
- # Get messages ids of a module
- $m_msgids = self::getMsgIds($module);
-
- # Get messages translations for a module
- $m_msgstrs = self::getMsgStrs($module, $requested_lang);
-
- # Get messages translations for others modules
- foreach(self::listModules() AS $o_module => $o_infos) {
- if ($o_module == $module) continue;
- $m_o_msgstrs[$o_module] = self::getMsgStrs($o_module, $requested_lang);
- }
- $m_o_msgstrs['dotclear'] = self::getMsgStrs('dotclear', $requested_lang);
-
- # Only one lang or all
- $langs = '' == $requested_lang ?
- self::listLangs($module) :
- array($requested_lang => self::isIsoCode($requested_lang));
-
- # Let's go reorder the mixture
- $res = array();
- foreach($langs AS $lang => $iso) {
- $res[$lang] = array();
-
- # From id list
- foreach($m_msgids AS $rs) {
- $res[$lang][$rs['msgid']]['files'][] = array(trim($rs['file'],'/'), $rs['line']);
- $res[$lang][$rs['msgid']]['group'] = 'main';
- $res[$lang][$rs['msgid']]['msgstr'] = '';
- $res[$lang][$rs['msgid']]['in_dc'] = false;
- $res[$lang][$rs['msgid']]['o_msgstrs'] = array();
- }
-
- # From str list
- foreach($m_msgstrs AS $rs) {
- if ($rs['lang'] != $lang) {
- continue;
- }
-
- if (!isset($res[$lang][$rs['msgid']])) {
- $res[$lang][$rs['msgid']]['files'][] = array();
- $res[$lang][$rs['msgid']]['in_dc'] = false;
- $res[$lang][$rs['msgid']]['o_msgstrs'] = array();
- }
- $res[$lang][$rs['msgid']]['group'] = $rs['group'];
- $res[$lang][$rs['msgid']]['msgstr'] = $rs['msgstr'];
- $res[$lang][$rs['msgid']]['in_dc'] = false;
- }
-
- # From others str list
- foreach($m_o_msgstrs AS $o_module => $o_msgstrs) {
- foreach($o_msgstrs AS $rs) {
- if ($rs['lang'] != $lang) {
- continue;
- }
-
- if (!isset($res[$lang][$rs['msgid']])) {
- continue;
- }
-
- $res[$lang][$rs['msgid']]['o_msgstrs'][] = array(
- 'msgstr' => $rs['msgstr'],
- 'module' => $o_module,
- 'file' => $rs['file']
- );
- if ($o_module == 'dotclear') {
- $res[$lang][$rs['msgid']]['in_dc'] = true;
- }
- }
- }
- }
- return '' == $requested_lang ? $res : $res[$requested_lang];
- }
-
- /* Write a lang file */
- private function writeLangFile($dir, $content, $throw)
- {
- $path = path::info($dir);
- if (is_dir($path['dirname']) && !is_writable($path['dirname'])
- || file_exists($dir) && !is_writable($dir)) {
- throw new Exception(sprintf(
- __('Cannot grant write acces on lang file %s'), $dir
- ));
- }
-
- # -- BEHAVIOR -- dcTranslaterBeforeWriteLangFile
- $this->core->callBehavior('dcTranslaterBeforeWriteLangFile', $dir, $content, $throw);
-
- $f = @files::putContent($dir,$content);
- if (!$f && $throw) {
- throw new Exception(sprintf(
- __('Cannot write lang file %s'), $dir
- ));
- }
-
- # -- BEHAVIOR -- dcTranslaterAfterWriteLangFile
- $this->core->callBehavior('dcTranslaterAfterWriteLangFile', $f, $dir, $content, $throw);
-
- return $f;
- }
-
- /* Try if a file is a .lang.php file */
- public static function isLangphpFile($file)
- {
- return files::getExtension($file) == 'php' && stristr($file, '.lang.php');
- }
-
- /* Get and parse a .lang.php file */
- public static function getLangphpFile($file)
- {
- if (!file_exists($file)) {
- return array();
- }
-
- $res = array();
- $content = implode('',file($file));
- $count = preg_match_all('/(\$GLOBALS\[\'__l10n\'\]\[\'(.*?)\'\]\s*\x3D\s*\'(.*?)\';)/', $content, $m);
-
- if (!$count) {
- return array();
- }
-
- for ($i = 0; $i < $count; $i++) {
- $id = $m[2][$i];
- $str = self::langphpString($m[3][$i]);
-
- if ($str) {
- $res[self::langphpString($id)] = $str;
- }
- }
- if (!empty($res[''])) {
- $res = array_diff_key($res,array(''=>1));
- }
- return $res;
- }
-
- /* Construct and write a .lang.php file */
- private function setLangphpFile($module, $lang, $group, $fields)
- {
- if (!$this->write_langphp) {
- return;
- }
-
- # Not a module installed
- $locales = self::getLangsFolder($module, true);
-
- # Path is right formed
- $lang_name = self::isIsoCode($lang, true);
-
- $l = "parse_comment) {
- $l .=
- '// Language: ' . $lang_name . " \n" .
- '// Module: ' . $module . " - " . self::moduleInfo($module, 'version') . "\n" .
- '// Date: ' . dt::str('%Y-%m-%d %H:%M:%S') . " \n";
-
- if ($this->parse_user && !empty($this->parse_userinfo)) {
- $search = self::$allowed_user_informations;
- foreach($search AS $n) {
- $replace[] = $this->core->auth->getInfo('user_' . $n);
- }
- $info = trim(str_replace($search,$replace,$this->parse_userinfo));
- if (!empty($info)) {
- $l .= '// Author: ' . html::escapeHTML($info) . "\n";
- }
- }
- $l .=
- '// Translated with dcTranslater - ' . $this->core->plugins->moduleInfo('translater', 'version') . " \n\n";
- }
- if ($this->parse_comment) {
- $infos = self::getMsgids($module);
- foreach($infos AS $info) {
- if (isset($fields[$info['msgid']])) {
- $comments[$info['msgid']] = (isset($comments[$info['msgid']]) ?
- $comments[$info['msgid']] : '') .
- '#'.trim($info['file'],'/') . ':' . $info['line'] . "\n";
- }
- }
- }
-
- foreach($fields as $id => $str) {
- if ($this->parse_comment && isset($comments[$id])) {
- $l .= $comments[$id];
- }
-
- $l .=
- '$GLOBALS[\'__l10n\'][\'' . addcslashes($id, "'") . '\'] = ' .
- '\'' . self::langphpString($str, true) . "';\n";
-
- if ($this->parse_comment) {
- $l .= "\n";
- }
- }
- $l .= "";
-
- self::writeLangFile($locales . '/' . $lang . '/' . $group . '.lang.php', $l, true);
- }
-
- /* Parse a .lang.php string */
- private static function langphpString($string, $reverse = false)
- {
- if ($reverse) {
- $smap = array('\'', "\n", "\t", "\r");
- $rmap = array('\\\'', '\\n"' . "\n" . '"', '\\t', '\\r');
- return trim((string) str_replace($smap, $rmap, $string));
- } else {
- $smap = array('/\\\\n/', '/\\\\r/', '/\\\\t/', "/\\\'/");
- $rmap = array("\n", "\r", "\t", "'");
- return trim((string) preg_replace($smap, $rmap, $string));
- }
- }
-
- /* Try if a file is a .po file */
- public static function isPoFile($file)
- {
- return files::getExtension($file) == 'po';
- }
-
- /* Get and parse a .po file */
- public static function getPoFile($file)
- {
- if (!file_exists($file)) {
- return false;
- }
-
- $res = array();
- $content = implode('',file($file));
-
- $count = preg_match_all('/msgid\s(.*(?:\n".*")*)\nmsgstr\s(.*(?:\n".*")*)/', $content, $m);
-
- if (!$count) return false;
-
- for ($i = 0; $i < $count; $i++) {
- $id = preg_replace('/"(.*)"/s','\\1', $m[1][$i]);
- $str= preg_replace('/"(.*)"/s','\\1', $m[2][$i]);
-
- $str = self::poString($str);
-
- if ($str) {
- $res[self::poString($id)] = $str;
- }
- }
-
- if (!empty($res[''])) {
- $res = array_diff_key($res, array(''=>1));
- }
- return $res;
- }
- /* Construct and parse a .po file */
- private function setPoFile($module, $lang, $group, $fields)
- {
- if (!$this->write_po) {
- return;
- }
-
- # Not a module installed
- $locales = self::getLangsFolder($module, true);
-
- # Path is right formed
- self::isIsoCode($lang, true);
-
- $l = '';
- if ($this->parse_comment) {
- $l .=
- '# Language: ' . self::$iso[$lang] . "\n" .
- '# Module: ' . $module . " - " . self::moduleInfo($module, 'version') . "\n" .
- '# Date: ' . dt::str('%Y-%m-%d %H:%M:%S') . "\n";
-
- if ($this->parse_user && !empty($this->parse_userinfo)) {
- $search = self::$allowed_user_informations;
- foreach($search AS $n) {
- $replace[] = $this->core->auth->getInfo('user_' . $n);
- }
- $info = trim(str_replace($search, $replace, $this->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: ' . $module . ' ' . self::moduleInfo($module, '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";
-
- if ($this->parse_comment) {
- $infos = self::getMsgids($module);
- foreach($infos AS $info) {
- if (isset($fields[$info['msgid']])) {
- $comments[$info['msgid']] = (isset($comments[$info['msgid']]) ?
- $comments[$info['msgid']] : '') .
- '#: '.trim($info['file'],'/') . ':' . $info['line'] . "\n";
- }
- }
- }
-
- foreach($fields as $id => $str) {
- if ($this->parse_comment && isset($comments[$id])) {
- $l .= $comments[$id];
- }
- $l .=
- 'msgid "' . self::poString($id, true) . '"' . "\n" .
- 'msgstr "' . self::poString($str, true) . '"' . "\n\n";
- }
-
- self::writeLangFile($locales . '/' . $lang . '/' . $group . '.po', $l, true);
- }
-
- /* Parse .po string */
- private static function poString($string, $reverse = false)
- {
- if ($reverse) {
- $smap = array('"', "\n", "\t", "\r");
- $rmap = array('\\"', '\\n"' . "\n" . '"', '\\t', '\\r');
- return trim((string) str_replace($smap, $rmap, $string));
- } else {
- $smap = array('/"\s+"/', '/\\\\n/', '/\\\\r/', '/\\\\t/', '/\\\"/');
- $rmap = array('', "\n", "\r", "\t", '"');
- return trim((string) preg_replace($smap, $rmap, $string));
- }
- }
-
- /* Scan recursively a folder and return files and folders names */
- public static function scandir($path, $dir = '', $res = array())
- {
- $path = path::real($path);
- if (!is_dir($path) || !is_readable($path)) {
- return array();
- }
-
$files = files::scandir($path);
-
foreach($files AS $file) {
- if ($file == '.' || $file == '..') {
+ if (in_array($file, ['.', '..'])) {
continue;
}
@@ -1614,25 +358,130 @@ class dcTranslater
return $res;
}
- /* Return array of langs like in clearbreaks l10n */
- public static function getIsoCodes($flip = false, $name_with_code = false)
+ /* Try if a file is a .lang.php file */
+ public static function isLangphpFile($file)
{
- if (empty(self::$iso)) {
- self::$iso = l10n::getISOcodes($flip, $name_with_code);
- }
- return self::$iso;
+ return files::getExtension($file) == 'php' && stristr($file, '.lang.php');
}
- /* Find if lang code exists or lang name */
- public static function isIsoCode($iso, $throw = false)
+ /* Try if a file is a .po file */
+ public static function isPoFile($file)
{
- $codes = self::getIsoCodes();
- $code = isset($codes[$iso]) ? $codes[$iso] : false;
- if (!$code && $throw) {
- throw new Exception(sprintf(
- __('Cannot find language for code %s'), $iso
- ));
- }
- return $code;
+ return files::getExtension($file) == 'po';
}
+
+ /**
+ * Check limit number of backup for a module
+ *
+ * @param string $root The backups root
+ * @param string $limit The backups limit
+ * @param boolean $throw Silently failed
+ * @return boolean True if limit is riched
+ */
+ public static function isBackupLimit(string $root, int $limit = 10, bool $throw = false): bool
+ {
+ $count = 0;
+ foreach(self::scandir($root) AS $file) {
+ if (!is_dir($root . '/' . $file)
+ && preg_match('/^(l10n-'. $id . '(.*?).bck.zip)$/', $root)
+ ) {
+ $count++;
+ }
+ }
+
+ # Limite exceed
+ if ($count >= $limit) {
+ if ($throw) {
+ throw new Exception(
+ sprintf(__('Limit of %s backups for module %s exceed'), $this->backup_limit, $id)
+ );
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Extract messages from a php contents
+ *
+ * support plurals
+ *
+ * @param string $content The contents
+ * @param string $func The function name
+ * @return array The messages
+ */
+ public static function extractPhpMsgs(string $content, string $func = '__'): array
+ {
+ $duplicate = $final_strings = $lines = [];
+ // split content by line to combine match/line on the end
+ $content = str_replace("\r\n", "\n", $content);
+ $o = 0;
+ $parts = explode("\n", $content);
+ foreach($parts as $li => $part) {
+ $m = explode($func . '(', $part);
+ for($i = 1; $i < count($m); $i++) {
+ $lines[$o] = $li+1;
+ $o++;
+ }
+ }
+ // split content by translation function
+ $parts = explode($func . '(', $content);
+ // remove fisrt element from array
+ array_shift($parts);
+ // put back first parenthesis
+ $parts = array_map(function($v){ return '(' . $v;}, $parts);
+ // walk through parts
+ $p = 0;
+ foreach($parts as $part) {
+ // find pairs of parenthesis
+ preg_match_all("/\((?:[^\)\(]+|(?R))*+\)/s", $part, $subparts);
+ // find quoted strings (single or double)
+ preg_match_all("/\'(?:[^\']+)\'|\"(?:[^\"]+)\"/s", $subparts[0][0], $strings);
+ // strings exist
+ if (!empty($strings[0])) {
+ // remove quotes
+ $strings[0] = array_map(function($v){ return substr($v, 1, -1);}, $strings[0]);
+ // filter duplicate strings (only check first string for plurals form)
+ if (!in_array($strings[0][0], $duplicate)) {
+ // fill final array
+ $final_strings[] = [$strings[0], $lines[$p]];
+ $duplicate[] = $strings[0][0];
+ }
+ }
+ $p++;
+ }
+ return $final_strings;
+ }
+
+ /**
+ * Encode a string
+ *
+ * @param string $str The string to encode
+ * @return string The encoded string
+ */
+ public static function encodeMsg(string $str): string
+ {
+ return text::toUTF8(stripslashes(trim($str)));
+ }
+
+ /**
+ * Clean a po string
+ *
+ * @param string $string The string to clean
+ * @param boolean $reverse Un/escape string
+ * @return string The cleaned string
+ */
+ public static function poString(string $string, bool $reverse = false): string
+ {
+ if ($reverse) {
+ $smap = array('"', "\n", "\t", "\r");
+ $rmap = array('\\"', '\\n"' . "\n" . '"', '\\t', '\\r');
+ return trim((string) str_replace($smap, $rmap, $string));
+ } else {
+ $smap = array('/"\s+"/', '/\\\\n/', '/\\\\r/', '/\\\\t/', '/\\\"/');
+ $rmap = array('', "\n", "\r", "\t", '"');
+ return trim((string) preg_replace($smap, $rmap, $string));
+ }
+ }
+ //@}
}
\ No newline at end of file
diff --git a/index.php b/index.php
index f112363..5390f18 100644
--- a/index.php
+++ b/index.php
@@ -17,1061 +17,655 @@ if (!defined('DC_CONTEXT_ADMIN')) {
dcPage::checkSuper();
-#
-# Init some variables
-#
-
$translater = new dcTranslater($core);
-$succes = [
- 'save_setting' => __('Configuration successfully updated'),
- 'update_lang' => __('Translation successfully updated'),
- 'add_lang' => __('Translation successfully created'),
- 'delete_lang' => __('Translation successfully deleted'),
- 'create_backup' => __('Backups successfully create'),
- 'restore_backup' => __('Backups successfully restored'),
- 'delete_backup' => __('Backups successfully deleted'),
- 'import_pack' => __('Package successfully imported'),
- 'export_pack' => __('Package successfully exported')
-];
+$type = $_REQUEST['type'] ?? $translater->start_page ?: '';
+$module = $_REQUEST['module'] ?? '';
+$lang = $_REQUEST['lang'] ?? '';
+$action = $_POST['action'] ?? '';
-$errors = [
- 'save_setting' => __('Failed to update settings: %s'),
- 'update_lang' => __('Failed to update translation: %s'),
- 'add_lang' => __('Failed to create translation: %s'),
- 'delete_lang' => __('Failed to delete translation: %s'),
- 'create_backup' => __('Failed to create backups: %s'),
- 'restore_backup' => __('Failed to restore backups: %s'),
- 'delete_backup' => __('Failed to delete backups: %s'),
- 'import_pack' => __('Failed to import package: %s'),
- 'export_pack' => __('Failed to export package: %s')
-];
-
-$p_url = 'plugin.php?p=translater';
-$start_page = @explode('_', $translater->start_page);
-if (count($start_page) < 2) {
- $start_page[1] = '';
+if (!in_array($type, ['plugin', 'theme'])) {
+ $type = '';
}
-#
-# Parse request
-#
-
-$msg = isset($_REQUEST['msg']) ? $_REQUEST['msg'] : '';
-$part = isset($_REQUEST['part']) ? $_REQUEST['part'] : $start_page[0];
-$tab = isset($_REQUEST['tab']) ? $_REQUEST['tab'] : '';
-$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
-$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : $start_page[1];
-$module = isset($_REQUEST['module']) ? $_REQUEST['module'] : '';
-$from = isset($_POST['from']) && $_POST['from'] != '-' ? $_POST['from'] : '';
-$lang = isset($_REQUEST['lang']) && $_REQUEST['lang'] != '-' ? $_REQUEST['lang'] : '';
-
-if ($type == '-' || $module == '-') {
- $type = $module = '';
-}
-
-#
-# Manage action
-#
-
-switch ($action) {
- case '':
- break;
-
- /**
- * Create lang for a module
- */
- case 'module_add_lang':
- try {
- if (empty($lang)) {
- throw new Exception(__('No lang to create'));
- }
- $translater->addLang($module, $lang, $from);
-
- http::redirect($p_url . '&part=lang&module=' . $module . '&type=' . $type . '&lang=' . $lang . '&msg=add_lang');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
- }
- break;
-
- /**
- * Delete lang for a module
- */
- case 'module_delete_lang':
- try {
- if (empty($lang)) {
- throw new Exception(__('No lang to delete'));
- }
- $translater->delLang($module, $lang);
-
- http::redirect($p_url . '&part=module&module=' . $module . '&type=' . $type . '&tab=module-lang&msg=delete_lang');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
+if (!empty($type) && !empty($module)) {
+ try {
+ $module = $translater->getModule($type, $module);
+ } catch(Exception $e) {
+ $core->error->add($e->getMessage());
+ $module = '';
}
- break;
+}
- /**
- * Create backup for a module
- */
- case 'module_create_backup':
- try {
- if (empty($_POST['modules']) || empty($_POST['langs'])) {
- throw new Exception(__('No lang to backup'));
- }
+if (!empty($module) && !empty($lang)) {
+ try {
+ $lang = $translater->getLang($module, $lang);
+ } catch(Exception $e) {
+ $core->error->add($e->getMessage());
+ $lang = '';
+ }
+}
- foreach($_POST['modules'] as $b_module) {
- $b_list = $translater->listLangs($b_module);
+$breadcrumb = [__('Translater') => $core->adminurl->get('translater')];
+if (empty($type)) {
+ $breadcrumb = [__('Translater') => ''];
+} elseif (empty($module)) {
+ $breadcrumb[$type == 'plugin' ? __('Plugins') : __('Themes')] = '';
+} elseif (empty($lang)) {
+ $breadcrumb[$type == 'plugin' ? __('Plugins') : __('Themes')] = $core->adminurl->get('translater', ['type' => $type]);
+ $breadcrumb[html::escapeHTML($module->name)] = '';
+} elseif (!empty($lang)) {
+ $breadcrumb[$type == 'plugin' ? __('Plugins') : __('Themes')] = $core->adminurl->get('translater', ['type' => $type]);
+ $breadcrumb[html::escapeHTML($module->name)] = $core->adminurl->get('translater', ['type' => $type, 'module' => $module->id]);
+ $breadcrumb[html::escapeHTML(sprintf(__('%s language edition'), $lang->name))] = '';
+}
- foreach($_POST['langs'] as $b_lang) {
- if (isset($b_list[$b_lang])) {
- $translater->createBackup($b_module, $b_lang);
- }
- }
- }
-
- http::redirect($p_url . '&part=module&module=' . $module . '&type=' . $type . '&tab=module-backup&msg=creat_backup');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
+try {
+ if ($action == 'module_create_backup') {
+ if (empty($module) || empty($_POST['codes'])) {
+ throw new Exception(__('No lang to backup'));
}
- break;
-
- /**
- * Restore backup for a module
- */
- case 'module_restore_backup':
- try {
- if (empty($_POST['modules']) || empty($_POST['files'])) {
- throw New Exception(__('No blackup to restore'));
+ $module_codes = $module->getUsedlangs();
+ foreach($module_codes as $code_id) {
+ if (in_array($code_id, $_POST['codes'])) {
+ $module->createBackup($code_id);
}
-
- sort($_POST['files']);
- $done = false;
- foreach($_POST['modules'] as $b_module) {
- $b_list = $translater->listBackups($b_module, true);
-
- foreach($_POST['files'] as $b_file) {
- if (in_array($b_file, $b_list)) {
- $translater->restoreBackup($b_module, $b_file);
- $done = true;
- }
- }
- }
- if (!$done) {
- throw new Exception(__('No bakcup to to restore'));
- }
-
- http::redirect($p_url . '&part=module&module=' . $module . '&type=' . $type . '&tab=module-backup&msg=restore_backup');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
}
- break;
+ dcPage::addSuccessNotice(__('Backup successfully created'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id], '#module-backup');
+ }
- /**
- * Delete backup for a module
- */
- case 'module_delete_backup':
- try {
- if (empty($_POST['modules']) || empty($_POST['files'])) {
- throw New Exception(__('No backup to delete'));
- }
-
- $done = false;
- foreach($_POST['modules'] as $b_module) {
- $b_list = $translater->listBackups($b_module, true);
-
- foreach($_POST['files'] as $b_file) {
- if (in_array($b_file, $b_list)) {
- $translater->deleteBackup($b_module, $b_file);
- $done = true;
- }
- }
- }
- if (!$done) {
- throw new Exception(__('No backup to delete'));
- }
-
- http::redirect($p_url . '&part=module&module=' . $module . '&type=' . $type . '&tab=module-backup&msg=delete_backup');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action],$e->getMessage()));
+ if ($action == 'module_restore_backup') {
+ if (empty($module) || empty($_POST['files'])) {
+ throw New Exception(__('No backup to restore'));
}
- break;
-
- /**
- * Import language package for a module
- */
- case 'module_import_pack':
- try {
- if (empty($_FILES['packfile']['name'])) {
- throw new Exception(__('Nothing to import'));
+ $module_backups = $module->getBackups(true);
+ foreach($module_backups as $backup_file) {
+ if (in_array($backup_file, $_POST['files'])) {
+ $module->restoreBackup($backup_file);
}
- $translater->importPack($_POST['modules'], $_FILES['packfile']);
-
- http::redirect($p_url . '&part=module&module=' . $module . '&type=' . $type . '&tab=module-pack&msg=import_pack');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
}
- break;
+ dcPage::addSuccessNotice(__('Backup successfully restored'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id], '#module-backup');
+ }
- /**
- * Export language package for a module
- */
- case 'module_export_pack':
- try {
- if (empty($_POST['modules']) || empty($_POST['entries'])) {
- throw new Exception(__('Nothing to export'));
- }
- $translater->exportPack($_POST['modules'], $_POST['entries']);
-
- http::redirect($p_url . '&part=module&module=' . $module . '&type=' . $type . '&tab=module-pack&msg=export_pack');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
+ if ($action == 'module_delete_backup') {
+ if (empty($module) || empty($_POST['files'])) {
+ throw New Exception(__('No backup to delete'));
}
- break;
-
- /**
- * Update language
- */
- case 'update_lang':
- try {
- if (empty($_POST['entries']) || empty($lang) || empty($module)) {
- throw new Exception(__('No language to update'));
+ $module_backups = $module->getBackups(true);
+ foreach($module_backups as $backup_file) {
+ if (in_array($backup_file, $_POST['files'])) {
+ $module->deleteBackup($backup_file);
}
+ }
+ dcPage::addSuccessNotice(__('Backup successfully deleted'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id], '#module-backup');
+ }
+
+ if ($action == 'module_export_pack') {
+ if (empty($module) || empty($_POST['codes'])) {
+ throw new Exception(__('Nothing to export'));
+ }
+ $module->exportPack($_POST['codes']);
+
+ dcPage::addSuccessNotice(__('Package successfully exported'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id], '#module-pack');
+ }
+
+ if ($action == 'module_import_pack') {
+ if (empty($_FILES['packfile']['name'])) {
+ throw new Exception(__('Nothing to import'));
+ }
+ $module->importPack($_FILES['packfile']);
+
+ dcPage::addSuccessNotice(__('Package successfully imported'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id], '#module-pack');
+ }
+
+ if ($action == 'module_add_code') {
+ if (empty($module) || empty($_POST['code'])) {
+ throw new Exception(__('No lang to create'));
+ }
+ $module->addLang($_POST['code'], $_POST['from'] ?? '');
+
+ dcPage::addSuccessNotice(__('Lang successfully added'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id, 'lang' => $_POST['code']]);
+ }
+
+ if ($action == 'module_delete_code') {
+ if (empty($module) || empty($_POST['code'])) {
+ throw new Exception(__('No lang to delete'));
+ }
+ $module->delLang($_POST['code']);
+
+ dcPage::addSuccessNotice(__('Lang successfully deleted'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id, 'lang' => $_POST['code']]);
+ }
+
+ if ($action == 'module_update_code') {
+ if (empty($module) || empty($_POST['code']) || empty($_POST['entries'])) {
+ throw new Exception(__('No lang to update'));
+ }
+ if (!empty($_POST['update_group'])) {
foreach($_POST['entries'] as $i => $entry) {
if (isset($entry['check']) && isset($_POST['multigroup'])) {
$_POST['entries'][$i]['group'] = $_POST['multigroup'];
- unset($_POST['entries'][$i]['check']);
}
}
- $translater->updLang($module, $lang, $_POST['entries']);
-
- http::redirect($p_url . '&part=lang&module=' . $module . '&type=' . $type . '&lang=' . $lang . '&msg=' . $action);
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
}
- break;
+ $module->updLang($_POST['code'], $_POST['entries']);
- /**
- * Import language packages
- */
- case 'import_pack':
- try {
- if (empty($_FILES['packfile']['name'])) {
- throw new Exception(__('Nothing to import'));
- }
- $translater->importPack($_POST['modules'], $_FILES['packfile']);
+ dcPage::addSuccessNotice(__('Lang successfully updated'));
+ $core->adminurl->redirect('translater', ['type' => $type, 'module' => $module->id, 'lang' => $_POST['code']]);
- http::redirect($p_url . '&part=pack&msg=' . $action . '&tab=pack-import');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
- }
- break;
+ }
- /**
- * Export language packages
- */
- case 'export_pack':
- try {
- if (empty($_POST['modules']) || empty($_POST['entries'])) {
- throw new Exception(__('Nothing to export'));
- }
- $translater->exportPack($_POST['modules'], $_POST['entries']);
-
- http::redirect($p_url . '&part=pack&msg=' . $action . '&tab=pack-export');
- } catch (Exception $e) {
- $core->error->add(sprintf($errors[$action], $e->getMessage()));
- }
- break;
-
- /**
- * Modules / Unknow / None
- */
- default:
- break;
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
}
-#
-# Fill in title and prepare display
-#
-
-switch ($part) {
- /**
- * Modules
- */
- case 'modules':
- $title = '
' . ($type == 'theme' ? __('Themes') : __('Plugins')) . '';
- break;
-
- /**
- * Module
- */
- case 'module':
- # Get info about requested module
- try {
- $M = $translater->getModule($module, $type);
- } catch(Exception $e) {
- $core->error->add(sprintf(__('Failed to launch translater: %s'), $e->getMessage()));
- $action = $module = $type = '';
- $M = false;
- }
- if (!empty($module) && !empty($type) && !$M) {
- $action = $module = $type = '';
- $M = false;
- }
-
- $M->langs = $translater->listLangs($module);
- $M->backups = $translater->listBackups($module);
- $M->unused_langs = array_flip(array_diff($translater->getIsoCodes(), $M->langs));
- $M->used_langs = array_flip(array_diff($M->langs, array_flip($translater->getIsoCodes())));
- $allowed_groups = array_combine(
- dcTranslater::$allowed_l10n_groups,
- dcTranslater::$allowed_l10n_groups
- );
-
- $title =
- '
' . ($type == 'theme' ? __('Themes') : __('Plugins')) . '' .
- ' ›
' . $module . '';
- break;
-
- /**
- * Lang
- */
- case 'lang':
-
- # Get infos on module wanted
- try {
- $M = $translater->getModule($module, $type);
-
- # Retrieve some infos
- $M->langs = $translater->listLangs($module);
- $M->backups = $translater->listBackups($module);
- $M->unused_langs = array_flip(array_diff($translater->getIsoCodes(), $M->langs));
- $M->used_langs = array_flip(array_diff($M->langs, array_flip($translater->getIsoCodes())));
- $allowed_groups = array_combine(
- dcTranslater::$allowed_l10n_groups,
- dcTranslater::$allowed_l10n_groups
- );
- } catch(Exception $e) {
- $core->error->add(sprintf(__('Failed to launch translater: %s'), $e->getMessage()));
- $action = $module = $type = '';
- $M = false;
- }
- if (!empty($module) && !empty($type) && !$M) {
- $action = $module = $type = '';
- $M = false;
- }
-
- $title =
- '
' . ($type == 'theme' ? __('Themes') : __('Plugins')) . '' .
- ' › ' .
- '
' . $module . '';
- if (!empty($M->langs) && isset($M->langs[$lang])) {
- $title .= ' ›
' . $M->langs[$lang] . '';
- }
- break;
-
- /**
- * Import/Export (pack)
- */
- case 'pack':
- $title = '
' . __('Import/Export') . '';
- break;
-}
-
-#
-# Display page
-#
-
-echo '
-
-
' . __('Translater') . '' .
-dcPage::jsPageTabs($tab) .
-dcPage::jsLoad('js/_posts_list.js') .
-dcPage::jsLoad('index.php?pf=translater/js/jquery.translater.js');
-
-if ('' != $translater->proposal_tool) {
- echo
- '\n" .
- "\n";
-}
-
-# --BEHAVIOR-- translaterAdminHeaders
-$core->callBehavior('translaterAdminHeaders');
-
echo
-'
-
' . __('Translater') . ' › ' . $title;
-if ($part == 'pack' || $type != 'plugin') {
- echo ' / ' . __('Plugins') . '';
-}
-if ($part == 'pack' || $type != 'theme') {
- echo ' / ' . __('Themes') . '';
-}
-if ($part != 'pack') {
- echo ' / ' . __('Import/Export') . '';
-}
-echo
-'
';
+'
' . __('translater') . '' .
+dcPage::jsPageTabs() .
+dcPage::cssLoad(dcPage::getPF('translater/css/translater.css')) .
+dcpage::jsJson('translater', [
+ 'title_add' => __('Use this text'),
+ 'image_field' => dcPage::getPF('translater/img/field.png'),
+ 'image_toggle' => dcPage::getPF('translater/img/toggle.png')
+]) .
+dcPage::jsLoad(dcPage::getPF('translater/js/translater.js')) .
-if (isset($succes[$msg])) {
- dcPage::success($succes[$msg]);
-}
+# --BEHAVIOR-- translaterAdminHeaders
+$core->callBehavior('translaterAdminHeaders') .
-switch ($part) {
- /**
- * Modules
- */
- case 'modules':
- echo '
-
';
+
+
+ ' . $type .' list
+
+ ' . __('Name') . ' |
+ ' . __('Languages') . ' |
+ ' . __('Id') . ' |
+ ' . __('Version') . ' |
+
' .
+ $res .
+ '
';
- # Export
- if (!empty($M->used_langs)) {
+ } else {
+ echo '
' . __('There is no editable modules') . ' |
';
+ }
+ echo '';
+
+} elseif (!empty($module) && empty($lang)) {
+ $codes = $module->getUsedLangs();
+ $backups = $module->getBackups();
+ $unused_codes = $module->getUnusedLangs();
+
+ // module summary
+ echo '
+
+
' . __('Module') . '
+
+ ' .__('Module summary') . '
+
+ ' . __('Name') . ' | ' . $module->name . ' |
+
+ ' . __('Version') . ' | ' . $module->version . ' |
+
+ ' . __('Author') . ' | ' . $module->author . ' |
+
+ ' . __('Type') . ' | ' . $module->type . ' |
+
+ ' . __('Root') . ' | ' . $module->root . ' |
+
+ ' . __('Locales') . ' | ' . $module->locales . ' |
+
+ ' . __('Backups') . ' | ' . $module->getBackupRoot() . ' |
+
+
+
';
+ if (count($codes)) {
+ echo
+ '
' . __('Translations') . '
' .
+ '
' .
+ '' . __('Existing module languages') .'' .
+ '' .
+ '' . __('Languages') . ' | ' .
+ '' . __('Code') . ' | ' .
+ '' . __('Backups') . ' | ' .
+ '' . __('Last backup') . ' | ' .
+ '
';
+
+ foreach($codes AS $code_name => $code_id) {
echo
- '' . __('Export') . '
' .
- '
' .
- '
' .
- '
' .
- '' .
- form::hidden(array('modules[]'), $module) .
+ '
' .
+ form::combo(['from'], array_merge(['-' => ''], $codes)) . ' (' . __('Optionnal') . ')
';
+ }
+ echo '
+
' .
+ $core->formNonce() .
+ $core->adminurl->getHiddenFormFields(
+ 'translater',
+ ['type' => $module->type, 'module' => $module->id, 'action' => 'module_add_code']
+ ) . '
+
';
+ }
+
+ // delete langs
+ if (!empty($codes)) {
+ echo '
' . __('Delete language') . '
+
+ ' .
+ form::combo(['code'], array_merge(['-' => ''], $codes)) . '
+ ' .
+ $core->formNonce() .
+ $core->adminurl->getHiddenFormFields(
+ 'translater',
+ ['type' => $module->type, 'module' => $module->id, 'action' => 'module_delete_code']
+ ) . '
+
';
+ }
+ echo '
';
+
+ // backups
+ if (!empty($codes) || !empty($backups)) {
+
+ echo '
';
+
+ // create backups
+ if (!empty($codes)) {
+ echo '
' . __('Create backups') . '
+
+
+ ' . __('Existing module languages') . '
+ ' . __('Language') . ' | ' . __('Code') . ' |
';
+ $i=0;
+ foreach($codes AS $code_name => $code_id) {
+ $i++;
+ echo '
+
+ ' . form::checkbox(['codes[]', 'backup_code_' . $code_id], $code_id, '', '', '', false) . ' |
+ |
+ ' . $code_id . ' |
+
';
+ }
+ echo '
+
+
+
+
+
+
' .
$core->formNonce() .
- form::hidden(array('type'), $type) .
- form::hidden(array('module'), $module) .
- form::hidden(array('action'), 'module_export_pack') .
- form::hidden(array('part'), 'module') .
- form::hidden(array('tab'), 'module-pack') .
- form::hidden(array('p'), 'translater') .
- '' .
- '' .
- '
';
+ $core->adminurl->getHiddenFormFields(
+ 'translater',
+ ['type' => $module->type, 'module' => $module->id, 'action' => 'module_create_backup']
+ ) . '
+
';
+ }
+
+ // delete / retore backups
+ if (!empty($backups)) {
+ echo '
' . __('List of backups') . '
' .
+ '
' .
+ '' .
+ '' . __('Existing backuped languages') . '' .
+ '' .
+ '' . __('Name') . ' | ' .
+ '' . __('Code') . ' | ' .
+ '' . __('Date') . ' | ' .
+ '' . __('File') . ' | ' .
+ '' . __('Size') . ' | ' .
+ '
';
+ $i=0;
+ foreach($backups as $backup_codes) {
+ foreach($backup_codes as $backup_file => $backup_code) {
+ $i++;
+ $form_id = 'form_file_' . $backup_code['code'] . $backup_code['time'];
+ echo
+ '' .
+ '' . form::checkbox(['files[]', $form_id], $backup_file, '', '', '', false) . ' | ' .
+ ' | ' .
+ '' . $backup_code['code'] . ' | ' .
+ '' . dt::str(
+ $core->blog->settings->system->date_format . ' ' . $core->blog->settings->system->time_format,
+ $backup_code['time'],
+ $core->blog->settings->system->blog_timezone
+ ) . ' | ' .
+ '' . $backup_code['path']['basename'] . ' | ' .
+ '' . files::size($backup_code['size']) . ' | ' .
+ '
';
+ }
+ }
+ echo '
+
+
+
+
+
' . __('Selected backups action:') . ' ' .
+ form::combo('action', [
+ __('Restore backups') => 'module_restore_backup',
+ __('Delete backups') => 'module_delete_backup'
+ ]) . '
+
' .
+ $core->formNonce() .
+ $core->adminurl->getHiddenFormFields(
+ 'translater',
+ ['type' => $module->type, 'module' => $module->id]
+ ) . '
+
';
}
echo '
';
- break;
+ }
- /**
- * Lang
- */
- case 'lang':
- # Existing langs
- if (empty($M->langs) || !isset($M->langs[$lang])) {
- break;
+ // Import/Export pack
+ echo '
';
+
+ // Import
+ echo '
' . __('Import') . '
+
+
+
+ ' .
+ $core->formNonce() .
+ $core->adminurl->getHiddenFormFields(
+ 'translater',
+ ['type' => $module->type, 'module' => $module->id, 'action' => 'module_import_pack']
+ ) . '
+
';
+
+ // Export
+ if (!empty($codes)) {
+ echo '
' . __('Export') . '
' .
+ '
' .
+ '' .
+ '' . __('Existing module languages') . '' .
+ '' . __('Language') . ' | ' . __('Code') . ' |
';
+ $i=0;
+ foreach($codes AS $code_name => $code_id) {
+ $i++;
+ echo
+ '' .
+ '' .
+ form::checkbox(['codes[]', 'export_code_' . $code_id], $code_id, '', '', '', false) .
+ ' | ' .
+ '' . $code_name . ' | ' .
+ '' . $code_id . ' | ' .
+ '
';
+ }
+ echo
+ '
' .
+ '' .
+ '
' .
+
+ '
' .
+ '
' .
+ $core->formNonce() .
+ $core->adminurl->getHiddenFormFields(
+ 'translater',
+ ['type' => $module->type, 'module' => $module->id, 'action' => 'module_export_pack']
+ ) .
+ '
';
+ }
+ echo '
';
+
+} elseif (!empty($lang)) {
+ $lines = $lang->getMessages();
+
+ echo
+ '
';
- # Export
- echo '
-
-
- ' . __('Choose modules to export') . '
-
- ' . __('Modules') . ' | ' . __('Languages') . ' |
';
- $i=0;
- $langs_list = array();
-
- foreach($translater->listModules() AS $k => $v) {
- if ($translater->hide_default && (
- in_array($k,dcTranslater::$default_dotclear_modules['plugin']) ||
- in_array($k,dcTranslater::$default_dotclear_modules['theme']))) {
- continue;
- }
-
- $info_lang = $translater->listLangs($k);
- if (!is_array($info_lang) || 1 > count($info_lang)) {
- continue;
- }
-
- $i++;
- $langs_list = array_merge($langs_list, $info_lang);
-
- echo '
-
- ' . form::checkbox(array('modules[]'), $k, '', '', '', false) . ' |
- ' . $v['name'] . ' |
- ' . implode(', ', $info_lang) . ' |
-
';
- }
-
- echo '
-
- ' . __('Choose languages to export') . '
-
- ' . __('Languages') . ' | ' . __('Code') . ' |
';
- $i=0;
- foreach($langs_list AS $k => $v) {
- $i++;
- echo '
-
- ' . form::checkbox(array('entries[]'), $k, '', '', '', false) . ' |
- ' . $v . ' |
- ' . $k . ' |
-
';
- }
- echo '
-
-
-
- ' .
- $core->formNonce() .
- form::hidden(array('type'), $type) .
- form::hidden(array('module'), $module) .
- form::hidden(array('action'), 'export_pack') .
- form::hidden(array('part'), 'pack') .
- form::hidden(array('tab'), 'pack-export') .
- form::hidden(array('p'), 'translater') . '
-
-
-
';
- break;
+} else {
+ $line = '
%s';
+ echo sprintf(
+ '
',
+ sprintf(
+ $line,
+ $core->adminurl->get('translater', ['type' => 'plugin']),
+ $type == 'plugin' ? ' class="active"' : '',
+ __('Translate plugins')) .
+ sprintf(
+ $line,
+ $core->adminurl->get('translater', ['type' => 'theme']),
+ $type == 'theme' ? ' class="active"' : '',
+ __('Translate themes'))
+ );
}
dcPage::helpBlock('translater');
-echo '
-';
\ No newline at end of file
+echo '';
\ No newline at end of file
diff --git a/js/jquery.translater.js b/js/jquery.translater.js
deleted file mode 100644
index 896c0b0..0000000
--- a/js/jquery.translater.js
+++ /dev/null
@@ -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 = '
';
- var tog = '
';
- var line = this;
- var msgid = $(line).children('.translatermsgid');
- var msgfile = $(line).children('.translatermsgfile');
- var msgstr = $(line).children('.translatermsgstr');
- var target = $(line).children('.translatertarget');
-
- $('.strlist').hide();
-
- var img_go = $('
'+img+'').css('cursor','pointer');
- $(msgid).prepend(' ').prepend(img_go);
- $(img_go).click(function(){
- var txt = $(msgid).text();
- $(img_go).css('cursor','wait');
- $.get(opts.url,{f:opts.func,langFrom:opts.from,langTo:opts.to,langTool:opts.tool,langStr:txt},function(data){
- data=$(data);
- if(data.find('rsp').attr('status')=='ok' && $(data).find('proposal').attr('str_to')){
- var resp = $(data).find('proposal').attr('str_to');
- if (confirm(opts.title+'\n'+resp)){
- addText(target,resp);
- $(img_go).css('cursor','pointer');
- }
- else{
- $(img_go).css('cursor','pointer');
- }
- }else{
- alert(opts.failed);
- $(img_go).css('cursor','pointer');
- }
- });
- });
-
- $(msgstr).children('.subtranslatermsgstr').each(function(){
- var img_str = $('
'+tog+'').css('cursor','pointer');
- $(this).children('strong').each(function(){
- var txt = $(this).text();
- var img_add = $('
'+img+'').css('cursor','pointer');
- $(this).prepend(' ').prepend(img_add);
- $(img_add).click(function(){addText(target,txt);});
-
- $(this).append(' ').append(img_str);
- var strlist=$(this).siblings('.strlist');
- $(strlist).click(function(){$(strlist).toggle();});
- $(img_str).click(function(){$(strlist).toggle();});
- });
- });
-
- var img_file = $('
'+tog+'').css('cursor','pointer');
- $(msgfile).children('strong').each(function(){
- $(this).append(' ').append(img_file);
- var strlist=$(this).siblings('.strlist');
- $(strlist).click(function(){$(strlist).toggle();});
- $(img_file).click(function(){$(strlist).toggle();});
- });
- });
- };
- function addText(target,text){
- $(target).children(':text').val(text);
- }
-
- $.fn.translater.defaults = {
- url: '',
- func: '',
- from: 'en',
- to: 'fr',
- tool: 'google',
- failed: 'Failed to translate this',
- title: 'Copy translation to field:',
- title_go: 'Find translation',
- title_add: 'Add this translation'
- };
-})(jQuery);
\ No newline at end of file
diff --git a/js/translater.js b/js/translater.js
new file mode 100644
index 0000000..f2aa1de
--- /dev/null
+++ b/js/translater.js
@@ -0,0 +1,64 @@
+/*global $, dotclear */
+'use strict';
+$(function () {
+ $('#module-backup-create-form .checkboxes-helpers').each(function () {
+ dotclear.checkboxesHelpers(this, undefined, '#module-backup-create-form td input[type=checkbox]', '#module-backup-create-form #do-action');
+ });
+ $('#module-backup-create-form td input[type=checkbox]').enableShiftClick();
+ dotclear.condSubmit('#module-backup-create-form td input[type=checkbox]', '#module-backup-create-form #do-action');
+
+ $('#module-backup-edit-form .checkboxes-helpers').each(function () {
+ dotclear.checkboxesHelpers(this, undefined, '#module-backup-edit-form td input[type=checkbox]', '#module-backup-edit-form #do-action');
+ });
+ $('#module-backup-edit-form td input[type=checkbox]').enableShiftClick();
+ dotclear.condSubmit('#module-backup-edit-form td input[type=checkbox]', '#module-backup-edit-form #do-action');
+
+ $('#module-pack-export-form .checkboxes-helpers').each(function () {
+ dotclear.checkboxesHelpers(this, undefined, '#module-pack-export-form td input[type=checkbox]', '#module-pack-export-form #do-action');
+ });
+ $('#module-pack-export-form td input[type=checkbox]').enableShiftClick();
+ dotclear.condSubmit('#module-pack-export-form td input[type=checkbox]', '#module-pack-export-form #do-action');
+
+ $('#lang-edit-form .checkboxes-helpers').each(function () {
+ dotclear.checkboxesHelpers(this, undefined, '#lang-edit-form td input[type=checkbox]', undefined);
+ });
+ $('#lang-edit-form td input[type=checkbox]').enableShiftClick();
+
+ const dctranslater = dotclear.getData('translater');
+ $('.translaterline').each(function(){
+
+ var line = this;
+ var msgfile = $(line).children('.translatermsgfile');
+ var msgstr = $(line).children('.translatermsgstr');
+ var target = $(line).children('.translatertarget');
+ var img = '
';
+ var tog = '
';
+
+ $('.strlist').hide();
+
+ $(msgstr).children('.subtranslater').each(function(){
+ var img_str = $('
'+tog+'').css('cursor','pointer');
+ $(this).children('strong').each(function(){
+ var txt = $(this).text();
+ var img_add = $('
'+img+'').css('cursor','pointer');
+ $(this).prepend(' ').prepend(img_add);
+ $(img_add).click(function(){$(target).children(':text').val(txt)});
+
+ $(this).append(' ').append(img_str);
+ var strlist=$(this).siblings('.strlist');
+ $(strlist).click(function(){$(strlist).toggle();});
+ $(img_str).click(function(){$(strlist).toggle();});
+ });
+ });
+
+ $(msgfile).children('.subtranslater').each(function(){
+ var img_file = $('
'+tog+'').css('cursor','pointer');
+ $(this).children('strong').each(function(){
+ $(this).append(' ').append(img_file);
+ var strlist=$(this).siblings('.strlist');
+ $(strlist).click(function(){$(strlist).toggle();});
+ $(img_file).click(function(){$(strlist).toggle();});
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/locales/fr/main.lang.php b/locales/fr/main.lang.php
deleted file mode 100644
index a1836c2..0000000
--- a/locales/fr/main.lang.php
+++ /dev/null
@@ -1,419 +0,0 @@
- 1);\n"
-
-#: _admin.php:25
-#: _admin.php:112
-#: index.php:536
-#: index.php:569
-#: index.php:1383
-msgid "Translater"
-msgstr "Traducteur"
-
-#: _admin.php:43
-msgid "Translate plugins"
-msgstr "Traduire les plugins"
-
-#: _admin.php:61
-msgid "Translate this plugin"
-msgstr "Traduire ce plugin"
-
-#: _admin.php:84
-msgid "Translate this theme"
-msgstr "Traduire ce thème"
-
-#: _uninstall.php:20
-msgid "delete all settings"
-msgstr "effacer tous les paramètres"
-
-#: _uninstall.php:27
-msgid "delete plugin files"
-msgstr "effacer les fichiers du plugin"
-
-#: _uninstall.php:34
-msgid "delete the version number"
-msgstr "effacer le n° de version"
-
-#: _uninstall.php:42
-msgid "delete all %s settings"
-msgstr "effacer tous les paramètres de %s"
-
-#: _uninstall.php:49
-msgid "delete %s plugin files"
-msgstr "effacer les fichiers du plugin %s"
-
-#: _uninstall.php:56
-msgid "delete %s version number"
-msgstr "effacer la version de %s"
-
-#: inc/class.dc.translater.php:359
-msgid "Cannot find module %s"
-msgstr "Impossible de trouver le module %s"
-
-#: inc/class.dc.translater.php:437
-msgid "Cannot find backups folder for module %s"
-msgstr "Impossible de trouver le dossier de sauvegarde du module %s"
-
-#: inc/class.dc.translater.php:453
-msgid "Cannot find languages folder for module %s"
-msgstr "Impossible de trouver le dossier de langue du module %s"
-
-#: inc/class.dc.translater.php:473
-msgid "Cannot find root folder for module %s"
-msgstr "Impossible de trouver le dossier racine du module %s"
-
-#: inc/class.dc.translater.php:502
-msgid "Limit of %s backups for module %s exceed"
-msgstr "La limite de %s sauvegardes pour le module % est dépassée"
-
-#: inc/class.dc.translater.php:564
-#: inc/class.dc.translater.php:954
-#: inc/class.dc.translater.php:1031
-msgid "Cannot find language folder %s for module %s"
-msgstr "Impossible de trouver le dossier de langue %s pour le module %s"
-
-#: inc/class.dc.translater.php:619
-msgid "Cannot delete backup file %s"
-msgstr "Impossible d'effacer le fichier de sauvegarde %s"
-
-#: inc/class.dc.translater.php:637
-msgid "Cannot find backup file %s"
-msgstr "Impossible de trouver le fichier de sauvegarde %s"
-
-#: inc/class.dc.translater.php:663
-msgid "Wrong export query"
-msgstr "Mauvaise requète d'exportation"
-
-#: inc/class.dc.translater.php:674
-msgid "Cannot use export mask %s"
-msgstr "Impossible d'utiliser le masque d'exportation %s"
-
-#: inc/class.dc.translater.php:762
-msgid "Wrong import query"
-msgstr "Mauvaise requète d'importation"
-
-#: inc/class.dc.translater.php:811
-msgid "Nothing to import for these modules in pack %s"
-msgstr "Rien à importer pour ces modules dans le paquetage %s"
-
-#: inc/class.dc.translater.php:840
-msgid "Zip file %s is not in translater format"
-msgstr "Le fichier zip n'est pas au format de translater"
-
-#: inc/class.dc.translater.php:900
-msgid "Language %s already exists for module %s"
-msgstr "La langue %s existe dèjà pour le module %s"
-
-#: inc/class.dc.translater.php:911
-msgid "Cannot copy file from language %s for module %s"
-msgstr "Impossible de copier le fichier depuis le langage %s pour le module %s"
-
-#: inc/class.dc.translater.php:1003
-msgid "No string to write, language %s deleted for module %s"
-msgstr "Aucune chaine à écrire, la langue %s a été effacée pour le module %s"
-
-#: inc/class.dc.translater.php:1267
-msgid "Cannot grant write acces on lang file %s"
-msgstr "Impossible d'avoir les droits en écriture sur le fichier de langue %s"
-
-#: inc/class.dc.translater.php:1278
-msgid "Cannot write lang file %s"
-msgstr "Impossible d'écrire le fichier de langue %s"
-
-#: inc/class.dc.translater.php:1583
-msgid "Cannot find language for code %s"
-msgstr "Impossible de trouver de langue pour le code %s"
-
-#: inc/class.translater.rest.php:41
-msgid "Missing params"
-msgstr "Paramètres manquants"
-
-#: inc/class.translater.rest.php:50
-msgid "Failed to get translation tool"
-msgstr "Impossible de trouver l'outil de traduction"
-
-#: inc/class.translater.rest.php:54
-msgid "Translation tool is not configured"
-msgstr "L'outil de traduction n'est pas configuré"
-
-#: inc/lib.translater.google.php:43
-msgid "You must have on Google API console:"
-msgstr "Vous devez avoir sur la console des API Google :"
-
-#: inc/lib.translater.google.php:46
-msgid "Activate the \"translate API\" service"
-msgstr "Activer le service \"translate API\""
-
-#: inc/lib.translater.google.php:79
-msgid "Failed to query service."
-msgstr "Impossible d'interroger le service."
-
-#: inc/lib.translater.microsoft.php:48
-msgid "You must have:"
-msgstr "Vous devez avoir :"
-
-#: index.php:25
-#: index.php:525
-#: index.php:1381
-msgid "Settings"
-msgstr "Paramètres"
-
-#: index.php:27
-#: index.php:426
-#: index.php:462
-#: index.php:500
-#: index.php:571
-msgid "Themes"
-msgstr "Thèmes"
-
-#: index.php:41
-msgid "locales folders of each module"
-msgstr "le répertoire locales de chaque module"
-
-#: index.php:42
-msgid "plugins folder root"
-msgstr "le répertoire racine des plugins"
-
-#: index.php:43
-msgid "public folder root"
-msgstr "le répertoire public du blog"
-
-#: index.php:44
-msgid "cache folder of Dotclear"
-msgstr "le répertoire cache de Dotclear"
-
-#: index.php:45
-msgid "locales folder of translater"
-msgstr "le répertoire locales du plugin translater"
-
-#: index.php:50
-msgid "Translation successfully updated"
-msgstr "Traduction mise à jour avec succès"
-
-#: index.php:51
-msgid "Translation successfully created"
-msgstr "Traduction créée avec succès"
-
-#: index.php:52
-msgid "Translation successfully deleted"
-msgstr "Traduction effacée avec succès"
-
-#: index.php:53
-msgid "Backups successfully create"
-msgstr "Sauvegardes créées avec succès"
-
-#: index.php:54
-msgid "Backups successfully restored"
-msgstr "Sauvegardes effectuées avec succès"
-
-#: index.php:55
-msgid "Backups successfully deleted"
-msgstr "Sauvegardes effacées avec succès"
-
-#: index.php:56
-msgid "Package successfully imported"
-msgstr "Paquetage importé avec succès"
-
-#: index.php:57
-msgid "Package successfully exported"
-msgstr "Paquetage exporté avec succès"
-
-#: index.php:61
-msgid "Failed to update settings: %s"
-msgstr "Impossible de mettre à jour les paramètres : %s"
-
-#: index.php:62
-msgid "Failed to update translation: %s"
-msgstr "Impossible de mettre à jour la traduction : %s"
-
-#: index.php:63
-msgid "Failed to create translation: %s"
-msgstr "Impossible de créer la traduction : %s"
-
-#: index.php:64
-msgid "Failed to delete translation: %s"
-msgstr "Impossible d'effacer la traduction : %s"
-
-#: index.php:65
-msgid "Failed to create backups: %s"
-msgstr "Impossible de créer les sauvegardes : %s"
-
-#: index.php:66
-msgid "Failed to restore backups: %s"
-msgstr "Impossible de restaurer les sauvegardes : %s"
-
-#: index.php:67
-msgid "Failed to delete backups: %s"
-msgstr "Impossible d'effacer les sauvegardes : %s"
-
-#: index.php:68
-msgid "Failed to import package: %s"
-msgstr "Impossible d'importer le paquetage : %s"
-
-#: index.php:69
-msgid "Failed to export package: %s"
-msgstr "Impossible d'exporter le paquetage : %s"
-
-#: index.php:111
-msgid "No lang to create"
-msgstr "Pas de langue à créer"
-
-#: index.php:133
-msgid "No lang to delete"
-msgstr "Pas de langue à effacer"
-
-#: index.php:155
-msgid "No lang to backup"
-msgstr "Pas de langue à sauvegarder"
-
-#: index.php:188
-msgid "No blackup to restore"
-msgstr "Pas de langue à restaurer"
-
-#: index.php:207
-msgid "No bakcup to to restore"
-msgstr "Pas de sauvegarde à restaurer"
-
-#: index.php:228
-#: index.php:246
-msgid "No backup to delete"
-msgstr "Pas de sauvegarde à effacer"
-
-#: index.php:289
-#: index.php:363
-msgid "Nothing to export"
-msgstr "Rien à exporter"
-
-#: index.php:311
-msgid "No language to update"
-msgstr "Pas de langue à mettre à jour"
-
-#: index.php:442
-#: index.php:489
-msgid "Failed to launch translater: %s"
-msgstr "Impossible de démarrer translater : %s"
-
-#: index.php:555
-msgid "Use this %s translation:"
-msgstr "Utiliser la traduction de %s :"
-
-#: index.php:556
-msgid "Translate this text with %s"
-msgstr "Traduire ce texte avec %s"
-
-#: index.php:557
-msgid "Use this text"
-msgstr "Utiliser ce texte"
-
-#: index.php:605
-msgid "Translate theme \"%s\" (by %s)"
-msgstr "Traduire le thème \"%s\" (de %s)"
-
-#: index.php:606
-msgid "Translate plugin \"%s\" (by %s)"
-msgstr "Traduire le plugin \"%s\" (de %s)"
-
-#: index.php:626
-#: index.php:740
-msgid "Edit translation"
-msgstr "Modifier la traduction"
-
-#: index.php:638
-msgid "Id"
-msgstr "Id"
-
-#: index.php:648
-msgid "There is no editable modules"
-msgstr "Il n'y a pas de modules modifiables."
-
-#: index.php:664
-msgid "Summary"
-msgstr "Résumé"
-
-#: index.php:665
-msgid "Module"
-msgstr "Module"
-
-#: index.php:667
-msgid "About"
-msgstr "À propos"
-
-#: index.php:677
-msgid "Root"
-msgstr "Racine"
-
-#: index.php:679
-#: index.php:693
-#: index.php:810
-#: index.php:1341
-msgid "Backups"
-msgstr "Sauvegardes"
-
-#: index.php:694
-msgid "Last backup"
-msgstr "Dernière sauvegarde"
-
-#: index.php:721
-msgid "no backup"
-msgstr "Aucune sauvegarde"
-
-#: index.php:736
-msgid "Edit language"
-msgstr "Modifier une langue"
-
-#: index.php:756
-msgid "Add language"
-msgstr "Ajouter une langue"
-
-#: index.php:763
-msgid "Copy from language:"
-msgstr "Copier depuis la langue :"
-
-#: index.php:765
-msgid "Optionnal"
-msgstr "Optionnel"
-
-#: index.php:772
-msgid "Add translation"
-msgstr "Ajouter la traduction"
-
-#: index.php:789
-msgid "Delete language"
-msgstr "Effacer une langue"
-
-#: index.php:793
-msgid "Delete translation"
-msgstr "Effacer la traduction"
-
-#: index.php:815
-msgid "Create backups"
-msgstr "Créer une sauvegarde"
-
-#: index.php:817
-msgid "Choose languages to backup"
-msgstr "Choisir la langue à sauvegarder"
-
-#: index.php:855
-msgid "List of backups"
-msgstr "Liste des sauvegardes"
-
-#: index.php:859
-#: index.php:1065
-msgid "File"
-msgstr "Fichier"
-
-#: index.php:886
-msgid "Selected backups action:"
-msgstr "Action sur les sauvegardes sélectionnées :"
-
-#: index.php:888
-msgid "Restore backups"
-msgstr "Restaurer les sauvegardes"
-
-#: index.php:889
-msgid "Delete backups"
-msgstr "Effacer les sauvegardes"
-
-#: index.php:915
-#: index.php:1182
-msgid "Choose language package to import"
-msgstr "Choisir le paquetage de langue à importer"
-
-#: index.php:937
-#: index.php:1239
-msgid "Choose languages to export"
-msgstr "Choisir les langues à exporter"
-
-#: index.php:1058
-msgid "Group"
-msgstr "Groupe"
-
-#: index.php:1060
-msgid "String"
-msgstr "Chaîne"
-
-#: index.php:1062
-#: index.php:1283
-msgid "Translation"
-msgstr "Traduction"
-
-#: index.php:1063
-msgid "Existing"
-msgstr "Existant"
-
-#: index.php:1126
-msgid "%s occurrences"
-msgstr "%s occurrences"
-
-#: index.php:1152
-msgid "Total of %s strings."
-msgstr "Total de %s chaînes."
-
-#: index.php:1154
-msgid "Change the group of the selected entries to:"
-msgstr "Changer le groupe des entrées sélectionnées vers :"
-
-#: index.php:1211
-msgid "Choose modules to export"
-msgstr "Choisir les modules à exporter"
-
-#: index.php:1213
-msgid "Modules"
-msgstr "Modules"
-
-#: index.php:1286
-msgid "Write .po files"
-msgstr "Écrire les fichiers .po"
-
-#: index.php:1289
-msgid "Write .lang.php files"
-msgstr "Écrire les fichiers .lang.php"
-
-#: index.php:1292
-msgid "Translate also strings of template files"
-msgstr "Traduire également les chaînes des fichiers de template"
-
-#: index.php:1295
-msgid "Translate only unknow strings"
-msgstr "Traduire uniquement les chaînes inconnues"
-
-#: index.php:1298
-msgid "Hide default modules of Dotclear"
-msgstr "Cacher les modules d'origine de Dotclear"
-
-#: index.php:1301
-msgid "Write comments in files"
-msgstr "Écrire les commentaires dans les fichiers"
-
-#: index.php:1304
-msgid "Write informations about author in files"
-msgstr "Écrire les informations à propos de l'auteur dans les fichiers"
-
-#: index.php:1308
-msgid "Tools"
-msgstr "Outils"
-
-#: index.php:1309
-msgid "Default language of l10n source:"
-msgstr "Langue par défaut des sources l10n :"
-
-#: index.php:1313
-msgid "Select and configure the tool to use to translate strings:"
-msgstr "Sélectionner et configurer un outil de traduction :"
-
-#: index.php:1325
-msgid "Nothing to configure for %s tool."
-msgstr "Rien à configurer pour l'outil %s."
-
-#: index.php:1336
-msgid "Overwrite existing languages"
-msgstr "Écraser les langages existants"
-
-#: index.php:1337
-msgid "Name of exported package"
-msgstr "Nom du paquetage exporté"
-
-#: index.php:1344
-msgid "Make backups when changes are made"
-msgstr "Faire des sauvegardes lors des modifications"
-
-#: index.php:1345
-msgid "Limit backups to %s files per module"
-msgstr "Limiter les sauvegardes à %s fichiers par module"
-
-#: index.php:1348
-msgid "Store backups in %s"
-msgstr "Stocker les sauvegardes dans %s"
-
-#: index.php:1353
-msgid "Behaviors"
-msgstr "Comportements"
-
-#: index.php:1354
-msgid "Default start menu:"
-msgstr "Menu de départ par défaut :"
-
-#: index.php:1359
-msgid "Enable menu on extensions page"
-msgstr "Activer le menu sur la page des plugins"
-
-#: index.php:1362
-msgid "Enable menu on themes page"
-msgstr "Activer le menu sur la page des thèmes"
-
-msgid "translater"
-msgstr "Traducteur"
-
-msgid "Translate your Dotclear plugins and themes"
-msgstr "Traduire vos plugins et thèmes pour Dotclear"
-