1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-12 17:54:44 +02:00

Add @LostKobrakai PR #52 - Implement a way for modules to supply translations, plus some related updates

Co-authored-by: LostKobrakai <benni@kobrakai.de>
This commit is contained in:
Ryan Cramer
2021-07-02 12:38:22 -04:00
parent 15d2982dcd
commit ccc3d1f5bb
6 changed files with 317 additions and 58 deletions

View File

@@ -4974,9 +4974,9 @@ class Modules extends WireArray {
if(!in_array($id, $this->moduleIDs)) unset($this->modulesLastVersions[$id]); if(!in_array($id, $this->moduleIDs)) unset($this->modulesLastVersions[$id]);
} }
if(count($this->modulesLastVersions)) { if(count($this->modulesLastVersions)) {
$this->wire('cache')->save(self::moduleLastVersionsCacheName, $this->modulesLastVersions, WireCache::expireReserved); $this->wire()->cache->save(self::moduleLastVersionsCacheName, $this->modulesLastVersions, WireCache::expireReserved);
} else { } else {
$this->wire('cache')->delete(self::moduleLastVersionsCacheName); $this->wire()->cache->delete(self::moduleLastVersionsCacheName);
} }
} }
@@ -5355,6 +5355,48 @@ class Modules extends WireArray {
return $cnt; return $cnt;
} }
/**
* Get module language translation files
*
* @param Module|string $module
* @return array Array of translation files including full path, indexed by basename without extension
* @since 3.0.181
*
*/
public function getModuleLanguageFiles($module) {
$module = $this->getModuleClass($module);
if(empty($module)) return array();
$path = $this->wire()->config->paths($module);
if(empty($path)) return array();
$pathHidden = $path . '.languages/';
$pathVisible = $path . 'languages/';
if(is_dir($pathVisible)) {
$path = $pathVisible;
} else if(is_dir($pathHidden)) {
$path = $pathHidden;
} else {
return array();
}
$items = array();
$options = array(
'extensions' => array('csv'),
'recursive' => false,
'excludeHidden' => true,
);
foreach($this->wire()->files->find($path, $options) as $file) {
$basename = basename($file, '.csv');
$items[$basename] = $file;
}
return $items;
}
/** /**
* Enables use of $modules('ModuleName') * Enables use of $modules('ModuleName')
* *

View File

@@ -171,6 +171,22 @@ class LanguageTranslator extends Wire {
$reflection = new \ReflectionClass($o); $reflection = new \ReflectionClass($o);
$filename = $reflection->getFileName(); $filename = $reflection->getFileName();
if($o instanceof Module) {
$ds = \DIRECTORY_SEPARATOR;
if(strpos($filename, "{$ds}wire{$ds}modules{$ds}") === false) {
// not a core module
$config = $this->wire()->config;
$filename = $this->wire()->files->unixFileName($filename);
if(strpos($filename, $config->urls($o)) === false && strpos($filename, "/$class/") !== false) {
// module likely in a symbolic link directory, so determine our own path for textdomain
// rather than using the one provided by ReflectionClass
list(, $filename) = explode("/$class/", $filename, 2);
$filename = $config->paths($class) . $filename;
}
}
}
$textdomain = $this->filenameToTextdomain($filename); $textdomain = $this->filenameToTextdomain($filename);
$this->classNamesToTextdomains[$class] = $textdomain; $this->classNamesToTextdomains[$class] = $textdomain;
$parentTextdomains = array(); $parentTextdomains = array();
@@ -286,7 +302,7 @@ class LanguageTranslator extends Wire {
* *
*/ */
public function getTranslation($textdomain, $text, $context = '') { public function getTranslation($textdomain, $text, $context = '') {
if($this->wire('hooks')->isHooked('LanguageTranslator::getTranslation()')) { if($this->wire()->hooks->isHooked('LanguageTranslator::getTranslation()')) {
// if method has hooks, we let them run // if method has hooks, we let them run
return $this->__call('getTranslation', array($textdomain, $text, $context)); return $this->__call('getTranslation', array($textdomain, $text, $context));
} else { } else {
@@ -474,7 +490,7 @@ class LanguageTranslator extends Wire {
*/ */
public function textdomainFileExists($textdomain) { public function textdomainFileExists($textdomain) {
$file = $this->getTextdomainTranslationFile($textdomain); $file = $this->getTextdomainTranslationFile($textdomain);
return is_file($file); return file_exists($file);
} }
/** /**

View File

@@ -768,6 +768,27 @@ class Languages extends PagesType {
return parent::get($key); return parent::get($key);
} }
/**
* Import a language translations file
*
* @param Language|string $language
* @param string $file Full path to .csv translations file
* The .csv file must be one generated by ProcessWires language translation tools.
* @param bool $quiet Specify true to suppress error/success notifications being generated (default=false)
* @return bool|int Returns integer with number of translations imported or boolean false on error
* @throws WireException
* @since 3.0.181
*
*/
public function importTranslationsFile($language, $file, $quiet = false) {
if(!wireInstanceOf($language, 'Language')) $language = $this->get($language);
if(!$language || !$language->id) throw new WireException("Unknown language");
$process = $this->wire()->modules->getModule('ProcessLanguage', array('noInit' => true)); /** @var ProcessLanguage $process */
if(!$this->wire()->files->exists($file)) throw new WireException("Language file does not exist: $file");
if(pathinfo($file, PATHINFO_EXTENSION) !== 'csv') throw new WireException("Language file does not have .csv extension");
return $process->processCSV($file, $language, array('quiet' => $quiet));
}
/** /**
* Hook to WireDatabasePDO::unknownColumnError * Hook to WireDatabasePDO::unknownColumnError
* *

View File

@@ -8,14 +8,16 @@
text-transform: none; text-transform: none;
} }
.Inputfields .InputfieldFile .InputfieldFileLanguageInfo {
.AdminThemeReno .Inputfields .InputfieldFile .InputfieldFileLanguageInfo,
.AdminThemeDefault .Inputfields .InputfieldFile .InputfieldFileLanguageInfo {
position: relative; position: relative;
margin-top: 0; margin-top: 0;
padding-top: 0; padding-top: 0;
} }
.InputfieldFileList .InputfieldFileLanguageInfo { .Inputfields .InputfieldFile a.action:hover {
margin-top: -1em; text-decoration: none;
} }
.InputfieldFileList .InputfieldFileLanguageInfo a i.hover-only { .InputfieldFileList .InputfieldFileLanguageInfo a i.hover-only {

View File

@@ -253,9 +253,9 @@ class ProcessLanguage extends ProcessPageType {
$out = $out =
"<div class='InputfieldFileData InputfieldFileLanguageInfo'>" . "<div class='InputfieldFileData InputfieldFileLanguageInfo'>" .
"<span class='InputfieldFileLanguageFilename description'>/$file &#8212;</span> <span class='notes'>$message</span> " . "<span class='InputfieldFileLanguageFilename description'>/$file &#8212;</span> <span class='notes'>$message</span> " .
"<a class='action' href='{$translationUrl}edit/?language_id={$page->id}&amp;textdomain=$textdomain'>&nbsp; " . "<a class='action' href='{$translationUrl}edit/?language_id={$page->id}&amp;textdomain=$textdomain'>&nbsp; " .
"<i class='fa fa-edit'></i> $editLabel <i class='fa fa-angle-double-right hover-only'></i></a>" . "<i class='fa fa-edit'></i> $editLabel <i class='fa fa-angle-double-right hover-only'></i></a>" .
"</div>"; "</div>";
$page->translator->unloadTextdomain($textdomain); $page->translator->unloadTextdomain($textdomain);
@@ -347,27 +347,62 @@ class ProcessLanguage extends ProcessPageType {
*/ */
public function ___executeDownload() { public function ___executeDownload() {
$id = (int) $this->input->get('language_id'); $config = $this->wire()->config;
$input = $this->wire()->input;
$id = (int) $input->get('language_id');
if(!$id) throw new WireException("No language specified"); if(!$id) throw new WireException("No language specified");
$language = $this->wire('languages')->get($id); $language = $this->wire()->languages->get($id);
if(!$language->id) throw new WireException("Unknown language"); if(!$language->id) throw new WireException("Unknown language");
$fieldName = $this->input->get('field') == 'language_files_site' ? 'language_files_site' : 'language_files'; $fieldName = $input->get('field') == 'language_files_site' ? 'language_files_site' : 'language_files';
$csv = (int) $this->wire('input')->get('csv'); $textdomain = $this->wire()->sanitizer->textdomain($input->get('textdomain'));
$textdomains = array();
$csv = (int) $input->get('csv');
$path = $language->$fieldName->path(); $path = $language->$fieldName->path();
$files = array(); $files = array();
foreach($language->$fieldName as $file) { if($textdomain) {
$files[] = $file->filename; $file = $language->translator->textdomainToFilename($textdomain);
if($file) {
$files[] = $file;
$textdomains[$file] = $textdomain;
} else {
$textdomain = '';
}
}
if(!count($files) && $fieldName) {
foreach($language->$fieldName as $file) {
$files[] = $file->filename;
}
}
if(!count($files)) {
throw new WireException('No translation files specified to download');
} }
if($csv) { if($csv) {
// CSV // CSV
$filename = $language->name . "-" . (strpos($fieldName, 'site') ? 'site' : 'wire') . ".csv"; if($textdomain) {
header("Content-type: application/force-download"); // i.e. es-modulename.csv
header("Content-Transfer-Encoding: Binary"); $parts = explode('--', $textdomain);
header("Content-disposition: attachment; filename=$filename"); $basename = array_pop($parts);
$parts = explode('-', $basename);
$basename = array_shift($parts);
$filename = "$language->name-$basename.csv";
} else {
// i.e. es-site.csv or es-wire.csv
$filename = $language->name . "-" . (strpos($fieldName, 'site') ? 'site' : 'wire') . ".csv";
}
if($input->get('view')) {
header("Content-type: text/plain");
} else {
header("Content-type: application/force-download");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=$filename");
}
$fp = fopen('php://output', 'w'); $fp = fopen('php://output', 'w');
$defaultCol = $language->name == 'en' ? 'default' : 'en'; $defaultCol = $language->name == 'en' ? 'default' : 'en';
@@ -376,12 +411,19 @@ class ProcessLanguage extends ProcessPageType {
foreach($files as $f) { foreach($files as $f) {
$textdomain = basename($f, '.json'); if(isset($textdomains[$f])) {
$textdomain = $textdomains[$f];
} else {
$textdomain = basename($f, '.json');
}
$data = $language->translator->getTextdomain($textdomain); $data = $language->translator->getTextdomain($textdomain);
if(empty($data)) continue;
$file = $data['file']; $file = $data['file'];
$pathname = $this->wire('config')->paths->root . $file; $pathname = $config->paths->root . $file;
$translated =& $data['translations']; $translated =& $data['translations'];
$parser = $this->wire(new LanguageParser($language->translator, $pathname)); $parser = $this->wire(new LanguageParser($language->translator, $pathname)); /** @var LanguageParser $parser */
$untranslated = $parser->getUntranslated(); $untranslated = $parser->getUntranslated();
$comments = $parser->getComments(); $comments = $parser->getComments();
@@ -420,16 +462,26 @@ class ProcessLanguage extends ProcessPageType {
* *
* @param string $csvFile * @param string $csvFile
* @param Language $language * @param Language $language
* @return bool * @param array $options Additional options (3.0.181+)
* - `file` (string): Use this path/file (relative to install root)
* - `quiet` (bool): Suppress generating notifications? (default=false)
* @return bool|int Returns false on error or integer on success, where value is number of translations imported
* @throws WireException * @throws WireException
* *
*/ */
public function processCSV($csvFile, Language $language) { public function processCSV($csvFile, Language $language, array $options = array()) {
$defaults = array(
'file' => '',
'quiet' => false,
);
$options = array_merge($defaults, $options);
$fp = fopen($csvFile, "r"); $fp = fopen($csvFile, "r");
if($fp === false) { if($fp === false) {
$this->error($this->csvImportLabel . "Unable to open: $csvFile"); if(!$options['quiet']) $this->error($this->csvImportLabel . "Unable to open: $csvFile");
return false; return false;
} }
@@ -450,8 +502,16 @@ class ProcessLanguage extends ProcessPageType {
$numTotal = 0; $numTotal = 0;
$numGross = 0; $numGross = 0;
$translations = null; $translations = null;
$optionsFileBasename = '';
$halt = false; $halt = false;
$this->wire($translator);
if(!empty($options['file'])) {
$options['file'] = ltrim($this->wire()->files->unixFileName($options['file']), '/');
$optionsFileBasename = basename($options['file']);
}
while(($csvData = fgetcsv($fp, 8192, ",")) !== FALSE) { while(($csvData = fgetcsv($fp, 8192, ",")) !== FALSE) {
if(++$n === 1) { if(++$n === 1) {
@@ -463,13 +523,16 @@ class ProcessLanguage extends ProcessPageType {
// make sure everything we need is present // make sure everything we need is present
foreach($keys as $k => $key) { foreach($keys as $k => $key) {
if($k > 1 && !in_array($key, $header)) { if($k > 1 && !in_array($key, $header)) {
$this->error($this->csvImportLabel . "CSV data missing required column '$key'"); if($key === 'file' && !empty($options['file'])) {
$halt = true; // default file provided so not required in CSV data
} else {
if(!$options['quiet']) $this->error($this->csvImportLabel . "CSV data missing required column '$key'");
$halt = true;
}
} }
} }
if($halt) break; if($halt) break;
continue; continue;
} }
$row = array(); $row = array();
@@ -479,7 +542,22 @@ class ProcessLanguage extends ProcessPageType {
$row[$name] = $csvData[$key]; $row[$name] = $csvData[$key];
} }
if(empty($row['file']) || empty($row['original'])) continue; if($options['file']) {
if(empty($row['file'])) {
$row['file'] = $options['file'];
} else {
$rowFileBasename = basename($row['file']);
if($rowFileBasename === $optionsFileBasename) {
// i.e. site/modules/Hello/Hello.module
$row['file'] = $options['file'];
} else {
// i.e. site/modules/Hello/World.module
$row['file'] = dirname($options['file']) . '/' . $rowFileBasename;
}
}
}
if(empty($row['original']) || empty($row['file'])) continue;
$file = $row['file']; $file = $row['file'];
$hash = $row['hash']; $hash = $row['hash'];
@@ -489,17 +567,17 @@ class ProcessLanguage extends ProcessPageType {
if(!$translator->textdomainFileExists($textdomain)) { if(!$translator->textdomainFileExists($textdomain)) {
$textdomain = $translator->addFileToTranslate($file, false, false); $textdomain = $translator->addFileToTranslate($file, false, false);
//$translator->loadTextdomain($textdomain);
} }
if(is_null($translations)) $translations = $translator->getTranslations($textdomain); if(is_null($translations)) {
$translations = $translator->getTranslations($textdomain);
}
if(!$textdomain) { if(!$textdomain) {
$this->warning($this->csvImportLabel . sprintf( if(!$options['quiet']) $this->warning($this->csvImportLabel . sprintf(
$this->_('Unrecognized textdomain for file: %s'), $this->_('Unrecognized textdomain for file: %s'),
$this->wire('sanitizer')->entities($file) $this->wire()->sanitizer->entities($file)
) ));
);
continue; continue;
} }
@@ -529,9 +607,11 @@ class ProcessLanguage extends ProcessPageType {
$language->save(); $language->save();
fclose($fp); fclose($fp);
$this->message($this->csvImportLabel . sprintf($this->_('%d total translations, %d total changes'), $numGross, $numTotal)); if(!$options['quiet']) {
$this->message($this->csvImportLabel . sprintf($this->_('%d total translations, %d total changes'), $numGross, $numTotal), Notice::noGroup);
}
return $halt ? false : true; return $halt ? false : $numGross;
} }
/** /**
@@ -549,7 +629,7 @@ class ProcessLanguage extends ProcessPageType {
if($numChanges) { if($numChanges) {
try { try {
$translator->saveTextdomain($textdomain); $translator->saveTextdomain($textdomain);
$this->message($this->csvImportLabel . sprintf($this->_('Saved %d change(s) for file: %s'), $numChanges, $file)); $this->message($this->csvImportLabel . sprintf($this->_('Saved %d change(s) for file: %s'), $numChanges, $file), Notice::noGroup);
} catch(\Exception $e) { } catch(\Exception $e) {
$this->error($e->getMessage()); $this->error($e->getMessage());
} }

View File

@@ -139,6 +139,7 @@ class ProcessModule extends Process {
$this->labels['download_install'] = $this->_('Download and Install'); $this->labels['download_install'] = $this->_('Download and Install');
} }
$this->labels['get_module_info'] = $this->_('Get Module Info'); $this->labels['get_module_info'] = $this->_('Get Module Info');
$this->labels['modules'] = $this->_('Modules');
$this->labels['module_information'] = $this->_x("Module Information", 'edit'); $this->labels['module_information'] = $this->_x("Module Information", 'edit');
$this->labels['download_now'] = $this->_('Download Now'); $this->labels['download_now'] = $this->_('Download Now');
$this->labels['download_dir'] = $this->_('Add Module From Directory'); $this->labels['download_dir'] = $this->_('Add Module From Directory');
@@ -197,7 +198,17 @@ class ProcessModule extends Process {
* *
*/ */
public function ___executeNavJSON(array $options = array()) { public function ___executeNavJSON(array $options = array()) {
$page = $this->wire('page');
$page = $this->wire()->page;
$input = $this->wire()->input;
$modules = $this->wire()->modules;
$site = (int) $input->get('site');
$core = (int) $input->get('core');
$configurable = (int) $input->get('configurable');
$install = (int) $input->get('install');
$moduleNames = array();
$data = array( $data = array(
'url' => $page->url, 'url' => $page->url,
'label' => (string) $page->get('title|name'), 'label' => (string) $page->get('title|name'),
@@ -205,36 +216,32 @@ class ProcessModule extends Process {
'list' => array(), 'list' => array(),
); );
$site = $this->wire('input')->get('site');
$core = $this->wire('input')->get('core');
$configurable = $this->wire('input')->get('configurable');
$install = $this->wire('input')->get('install');
if($site || $install) $data['add'] = array( if($site || $install) $data['add'] = array(
'url' => "?new#tab_new_modules", 'url' => "?new#tab_new_modules",
'label' => __('Add New', '/wire/templates-admin/default.php'), 'label' => __('Add New', '/wire/templates-admin/default.php'),
'icon' => 'plus-circle', 'icon' => 'plus-circle',
); );
$modules = $this->wire('modules');
$moduleNames = array();
if($install) { if($install) {
$moduleNames = array_keys($modules->getInstallable()); $moduleNames = array_keys($modules->getInstallable());
} else { } else {
foreach($modules as $module) $moduleNames[] = $module->className(); foreach($modules as $module) {
$moduleNames[] = $module->className();
}
} }
sort($moduleNames); sort($moduleNames);
foreach($moduleNames as $moduleName) { foreach($moduleNames as $moduleName) {
$info = $this->wire('modules')->getModuleInfoVerbose($moduleName); $info = $modules->getModuleInfoVerbose($moduleName);
if($site && $info['core']) continue; if($site && $info['core']) continue;
if($core && !$info['core']) continue; if($core && !$info['core']) continue;
if($configurable) { if($configurable) {
if(!$info['configurable'] || !$info['installed']) continue; if(!$info['configurable'] || !$info['installed']) continue;
$flags = $this->wire('modules')->getFlags($moduleName); $flags = $modules->getFlags($moduleName);
if($flags & Modules::flagsNoUserConfig) continue; if($flags & Modules::flagsNoUserConfig) continue;
} }
@@ -242,7 +249,7 @@ class ProcessModule extends Process {
// exclude already installed modules // exclude already installed modules
if($info['installed']) continue; if($info['installed']) continue;
// check that it can be installed NOW (i.e. all dependencies met) // check that it can be installed NOW (i.e. all dependencies met)
if(!$this->wire('modules')->isInstallable($moduleName, true)) continue; if(!$modules->isInstallable($moduleName, true)) continue;
} }
$label = $info['name']; $label = $info['name'];
@@ -264,7 +271,8 @@ class ProcessModule extends Process {
ksort($data['list']); ksort($data['list']);
$data['list'] = array_values($data['list']); $data['list'] = array_values($data['list']);
if($this->wire('config')->ajax) header("Content-Type: application/json"); if($this->wire()->config->ajax) header("Content-Type: application/json");
return json_encode($data); return json_encode($data);
} }
@@ -864,6 +872,12 @@ class ProcessModule extends Process {
} }
} else $requires = array(); } else $requires = array();
$nsClassName = $modules->getModuleClass($name, true);
if(!wireInstanceOf($nsClassName, 'Module')) {
$summary .= "<span class='notes requires'>" . $this->_('Module class must implement the “ProcessWire\Module” interface.') . "</span>";
$requires[] = 'Module interface';
}
if(count($info['installs'])) { if(count($info['installs'])) {
$summary .= "<span class='detail installs'>" . $this->labels['installs'] . " - " . implode(', ', $info['installs']) . "</span>"; $summary .= "<span class='detail installs'>" . $this->labels['installs'] . " - " . implode(', ', $info['installs']) . "</span>";
} }
@@ -1698,6 +1712,13 @@ class ProcessModule extends Process {
$table->row(array($this->_x('Hooks To', 'edit'), $hooksStr)); $table->row(array($this->_x('Hooks To', 'edit'), $hooksStr));
} }
$languageFiles = $languages ? $modules->getModuleLanguageFiles($moduleName) : array();
if(count($languageFiles)) {
$languages = wireIconMarkup('language') . ' ' . $sanitizer->entities(implode(', ', array_keys($languageFiles)));
$languages .= " - <a href='{$config->urls->admin}module/translation/?name=$moduleName'>" . $this->_('install translations') . "</a>";
$table->row(array($this->_x('Languages', 'edit'), $languages));
}
if(!empty($moduleInfo['href'])) { if(!empty($moduleInfo['href'])) {
$table->row(array($this->_x('More Information', 'edit'), "<a target='_blank' class='label' href='$moduleInfo[href]'>$moduleInfo[href]</a>")); $table->row(array($this->_x('More Information', 'edit'), "<a target='_blank' class='label' href='$moduleInfo[href]'>$moduleInfo[href]</a>"));
} }
@@ -1750,10 +1771,9 @@ class ProcessModule extends Process {
public function ___executeInstallConfirm() { public function ___executeInstallConfirm() {
$name = $this->wire('input')->get('name'); $name = $this->wire('input')->get->name('name');
if(!$name) throw new WireException("No module name specified"); if(!$name) throw new WireException("No module name specified");
$name = $this->wire('sanitizer')->fieldName($name); if(!$this->wire()->modules->isInstallable($name, true)) throw new WireException("Module is not currently installable");
if(!$this->wire('modules')->isInstallable($name, true)) throw new WireException("Module is not currently installable");
$this->headline($this->labels['install']); $this->headline($this->labels['install']);
@@ -1778,6 +1798,84 @@ class ProcessModule extends Process {
return $form->render(); return $form->render();
} }
/**
* Languages translations import
*
* @return string
* @since 3.0.181
*
*/
public function ___executeTranslation() {
$languages = $this->wire()->languages;
$modules = $this->wire()->modules;
$session = $this->wire()->session;
$input = $this->wire()->input;
$config = $this->wire()->config;
$moduleName = $input->get->name('name');
if(empty($moduleName)) throw new WireException('No module name specified');
if(!$modules->isInstalled($moduleName)) throw new WireException("Unknown module: $moduleName");
$moduleEditUrl = $modules->getModuleEditUrl($moduleName);
$languageFiles = $modules->getModuleLanguageFiles($moduleName);
if(!$languages || !count($languageFiles)){
$session->message($this->_('No module language files available'));
$session->location($moduleEditUrl);
}
$this->headline($this->_('Module language translations'));
$this->breadcrumb($config->urls->admin . 'modules/', $this->labels['modules']);
$this->breadcrumb($moduleEditUrl, $moduleName);
/** @var InputfieldForm $form */
$form = $modules->get('InputfieldForm');
$form->attr('id', 'ModuleImportTranslationForm');
$form->attr('action', $config->urls->admin . "module/translation/?name=$moduleName");
$form->attr('method', 'post');
$form->description = sprintf($this->_('Import translations for module %s'), $moduleName);
foreach($languages as $language) {
/** @var InputfieldSelect $lang */
$langLabel = $language->get('title');
$langLabel .= $langLabel ? " ($language->name)" : $language->name;
/** @var InputfieldSelect $f */
$f = $modules->get('InputfieldSelect');
$f->attr('name', "language_$language->name");
$f->label = sprintf($this->_('Import into %s'), $langLabel);
$f->addOption('');
foreach($languageFiles as $basename => $filename) {
$f->addOption($basename);
}
$form->append($f);
}
if($input->post('submit_import_translations')) {
foreach($languages as $language) {
$basename = $input->post->pageName("language_$language->name");
if(empty($basename)) continue;
if(empty($languageFiles[$basename])) continue;
$file = $languageFiles[$basename];
if(!is_file($file)) {
$session->error($this->_('Cannot find CSV file') . " - " . basename($file));
continue;
}
$languages->importTranslationsFile($language, $file);
}
$session->location($moduleEditUrl);
}
/** @var InputfieldSubmit $f */
$f = $modules->get('InputfieldSubmit');
$f->attr('name', 'submit_import_translations');
$f->showInHeader(true);
$form->add($f);
return $form->render();
}
/** /**
* URL to redirect to after non-authenticated user is logged-in, or false if module does not support * URL to redirect to after non-authenticated user is logged-in, or false if module does not support
* *