<?php
/**
  * Ahead WebSoft Technologies Pvt Ltd.
  * @copyright  Copyright 2006-2026 Ahead WebSoft Technologies Pvt Ltd.
  * @license    https://www.socialengine.com/policies
*/
class Core_AdminLanguageController extends Core_Controller_Action_Admin
{
    protected $_languagePath;

    public function init()
    {
        $this->_languagePath = APPLICATION_PATH . '/application/languages';
    }

    public function indexAction()
    {

        // Prepare default langauge
        $this->view->defaultLanguage = $defaultLanguage = Engine_Api::_()->getApi('settings', 'core')->getSetting('core.locale.locale', 'en');
        if ($defaultLanguage == 'auto') {
            $this->view->defaultLanguage = $defaultLanguage = 'en';
        }

        $languageTable = Engine_Api::_()->getDbTable('languages', 'core');
        // Init default locale
        $localeObject = Zend_Registry::get('Locale');
        $languages = Zend_Locale::getTranslationList('language', $localeObject);
        $territories = Zend_Locale::getTranslationList('territory', $localeObject);

        //Create in table
        $languagesDir = [];
        $items = scandir($this->_languagePath);
        foreach ($items as $item) {
            if ($item == '..' || $item == '.' || $item == 'index.html')
                continue;
            if (is_dir($this->_languagePath . '/' . $item))
                $languagesDir[] = $item;
        }

        foreach ($languagesDir as $language) {
            $isLanguageExist = Engine_Api::_()->getDbTable('languages', 'core')->isLanguageExist($language);
            if (empty($isLanguageExist)) {
                try {
                    if (isset($languages[$language]) && $languages[$language]) {
                        $languageName = $languages[$language];
                    } else {
                        $languageArray = explode('_', $language);
                        if (isset($languageArray[1])) {
                            $languageName = $territories[$languageArray[1]];
                        }
                    }
                    $row = $languageTable->createRow();
                    $values['code'] = $language;
                    $values['name'] = $languageName;
                    $values['fallback'] = $language;
                    $row->setFromArray($values);
                    $row->save();
                    $row->order = $row->language_id;
                    $row->save();
                } catch (Exception $e) {
                    //silence
                }
            }
        }

        $this->view->languageNameList = $localeMultiOptions = Engine_Api::_()->getApi('languages', 'core')->getLanguages(array('admin' => 1));
    }

    public function createAction()
    {
        $translate = Zend_Registry::get('Zend_Translate');
        $form = $this->view->form = new Core_Form_Admin_Language_Create();
        $this->view->languageList = $languageList = $translate->getList();

        if ($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost())) {
            $localeCode = $this->_getParam('language');

            $defaultLanguage = Engine_Api::_()->getApi('settings', 'core')->getSetting('core.locale.locale', 'en');
            if (!engine_in_array($defaultLanguage, $languageList)) {
                if ($defaultLanguage == 'auto' && isset($languageList['en'])) {
                    $defaultLanguage = 'en';
                } else {
                    $defaultLanguage = null;
                }
            }

            if (!engine_in_array($localeCode, $translate->getList())) {
                $filename = APPLICATION_PATH . "/application/languages/$localeCode/custom.csv";
                mkdir(dirname($filename));
                chmod(dirname($filename), 0777);
                touch($filename);
                chmod($filename, 0777);
                $csv = new Engine_Translate_Writer_Csv($filename);
                // each language pack must have at least one line written to it to be recognized
                $csv->setTranslation($localeCode, $localeCode);
                $csv->write();

                $adapter = Engine_Api::_()->getApi('settings', 'core')->getSetting('core.translate.adapter', 'array');

                if ($adapter == 'array') {
                    //create array file
                    $folder = APPLICATION_PATH . "/application/languages/$localeCode";
                    $this->csv_folder_to_array($folder, $localeCode);
                }


                //Create in table
                $languageName = Zend_Locale_Data::getList($localeCode, 'language');
                $languageTable = Engine_Api::_()->getDbTable('languages', 'core');
                $language = $languageTable->createRow();
                $values['code'] = $localeCode;
                $values['name'] = $languageName[$localeCode];
                $values['fallback'] = $localeCode;
                $language->setFromArray($values);
                $language->save();
                $language->order = $language->language_id;
                $language->save();
            }
            $this->_helper->redirector->gotoRoute(array('action' => 'index'));
        }
    }

    public function uploadAction()
    {
        $form = $this->view->form = new Core_Form_Admin_Language_Upload();
        if ($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost())) {
            $localeCode = $this->_getParam('locale', null);
            if (!empty($localeCode)) {
                $filename = APPLICATION_PATH . "/application/languages/$localeCode/custom.csv";
                @mkdir(dirname($filename));
                @chmod(dirname($filename), 0777);

                if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {
                    @chmod($filename, 0777);
                    $adapter = Engine_Api::_()->getApi('settings', 'core')->getSetting('core.translate.adapter', 'array');

                    if ($adapter == 'array') {
                        $folder = APPLICATION_PATH . "/application/languages/$localeCode";
                        $this->csv_folder_to_array($folder, $localeCode);
                    }

                    $this->_forward('success', 'utility', 'core', array(
                        'parentRefresh' => 2000,
                        'messages' => array(Zend_Registry::get('Zend_Translate')->_('Language file has been uploaded.')),
                        'redirect' => Zend_Controller_Front::getInstance()->getRouter()->assemble(array('action' => 'index')),
                    )
                    );
                } else {
                    $form->addError('Unable to import language file to this language.  Please CHMOD 777 the "/application/languages" directory an all directories and files inside it, then try again.');
                }
            } else {
                $form->addError('Unknown language');
            }
        }
    }

    public function defaultAction()
    {
        if ($this->getRequest()->isPost()) {
            $locale = $this->_getParam('locale', 'en');

            $languageNameList = Engine_Api::_()->getApi('languages', 'core')->getLanguages(array('admin' => 1));

            if (engine_in_array($locale, array_keys($languageNameList))) {
                Engine_Api::_()->getApi('settings', 'core')->core_locale_locale = $locale;
                if ($locale == 'en') {
                    $dbGetInsert = Engine_Db_Table::getDefaultAdapter();
                    $dbGetInsert->update('engine4_core_languages', array('enabled' => 1), array('code =?' => $locale));
                }
            }
        }
    }

    public function enabledAction()
    {
        if ($this->getRequest()->isPost()) {
            $locale = $this->_getParam('locale', 'en');
            $disableLocale = $this->_getParam('disableLocale', 1);
            $dbGetInsert = Engine_Db_Table::getDefaultAdapter();
            if (!empty($disableLocale)) {
                $dbGetInsert->update('engine4_core_languages', array('enabled' => 0), array('code =?' => $locale));
            } else {
                $dbGetInsert->update('engine4_core_languages', array('enabled' => 1), array('code =?' => $locale));
            }
        }
    }

    public function editIconAction()
    {

        $this->_helper->layout->setLayout('admin-simple');
        $locale = $this->_getParam('locale', null);
        $this->view->form = $form = new Core_Form_Admin_Language_EditIcon();
        $isLanguageExist = Engine_Api::_()->getDbTable('languages', 'core')->isLanguageExist($locale);
        $language = Engine_Api::_()->getItem('core_language', $isLanguageExist);
        $form->icon->setValue($language->icon);
        if ($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost())) {
            $language->icon = $_POST['icon'];
            $language->save();

            $this->_forward('success', 'utility', 'core', array(
                'smoothboxClose' => 10,
                'parentRefresh' => 10,
                'messages' => array('Icon has been upoaded successfully.')
            )
            );
        }
    }

    public function deleteAction()
    {
        $form = $this->view->form = new Core_Form_Admin_Language_Delete();

        $languageList = Zend_Locale_Data::getList('en', 'language');
        $territoryList = Zend_Locale_Data::getList('en', 'territory');
        $localeCode = $this->_getParam('locale', null);
        if (empty($localeCode)) {
            return;
        }
        if (false !== strpos($localeCode, '_')) {
            list($locale, $territory) = explode('_', $localeCode);
        } else {
            $locale = $localeCode;
            $territory = null;
        }


        $languagePack = $languageList[$locale];
        if ($territory && !empty($territoryList[$territory])) {
            $languagePack .= " ({$territoryList[$territory]})";
        }
        $languagePack .= "  [$localeCode]";

        $form->setDescription(sprintf($form->getDescription(), $languagePack));


        if ($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost())) {
            $lang_dir = APPLICATION_PATH . '/application/languages/' . $localeCode;
            try {
                @Engine_Package_Utilities::fsRmdirRecursive($lang_dir, true);

                Engine_Api::_()->getDbTable('languages', 'core')->delete(array('code =?' => $localeCode, "fallback =?" => $localeCode));

                $this->_forward('success', 'utility', 'core', array(
                    'smoothboxClose' => 2000,
                    'parentRefresh' => 2000,
                    'format' => 'smoothbox',
                    'messages' => array(Zend_Registry::get('Zend_Translate')->_('Language has been deleted.')),
                )
                );
            } catch (Exception $e) {
                $form->addError('Unable to delete language files.  Please log in through FTP and delete the directory "/application/languages/' . $localeCode . '/ and all of the files inside.');
            }
        }
    }

    public function editAction()
    {
        $this->view->locale = $locale = $this->_getParam('locale');
        $this->view->page = $page = $this->_getParam('page');

        $translate = new Zend_Translate(
            'Engine_Translate_Adapter_Csv',
            APPLICATION_PATH . '/application/languages/' . $locale,
            null,
            array(),
        );

        try {
            if (!$locale || !Zend_Locale::findLocale($locale)) {
                throw new Exception('missing locale ' . $locale);
            }
        } catch (Exception $e) {
            return $this->_helper->redirector->gotoRoute(array('action' => 'index', 'controller' => 'language'), 'admin_default', true);
        }

        // Process filter form
        $this->view->filterForm = $filterForm = new Core_Form_Admin_Language_Filter();
        $filterForm->isValid($this->_getAllParams());
        $filterValues = $filterForm->getValues();
        extract($filterValues); // search, show

        // Make query
        $filterValues = array_filter($filterValues);
        $this->view->values = $filterValues;
        $this->view->query = (empty($filterValues) ? '' : '?' . http_build_query($filterValues));

        // Assign basic locale info
        $this->view->localeObject = $localeObject = new Zend_Locale($locale);
        $this->view->locale = $locale = $localeObject->toString();

        // Get locale translation info
        $localeLanguage = $localeObject->getLanguage();
        $localeRegion = $localeObject->getRegion();
        $this->view->localeLanguageTranslation = $localeLanguageTranslation
            = Zend_Locale::getTranslation($localeLanguage, 'language', Zend_Registry::get('Locale'));
        $this->view->localeRegionTranslation = $localeRegionTranslation
            = Zend_Locale::getTranslation($localeRegion, 'territory', Zend_Registry::get('Locale'));

        $translate1 = Zend_Registry::get('Zend_Translate');

        if ($localeLanguageTranslation && $localeRegionTranslation) {
            $this->view->localeTranslation = $localeLanguageTranslation . ' '
                . sprintf($translate->translate('(%s)'), $localeRegionTranslation) . ' '
                . sprintf($translate->translate('[%s]'), $locale);
        } elseif ($localeLanguageTranslation) {
            $this->view->localeTranslation = $localeLanguageTranslation . ' '
                . sprintf($translate->translate('[%s]'), $locale);
        } else {
            $this->view->localeTranslation = sprintf($translate->translate('[%s]'), $locale);
        }

        // Query plural system for max and sample space
        $sample = array();
        $max = 0;
        for ($i = 0; $i <= 1000; $i++) {
            $form = Zend_Translate_Plural::getPlural($i, $locale);
            $max = max($max, $form);
            if (@engine_count($sample[$form]) < 3) {
                $sample[$form][] = $i;
            }
        }
        $this->view->pluralFormCount = ($max + 1);
        $this->view->pluralFormSample = $sample;

        // Get initial and default values
        $baseMessages = $translate1->getMessages('en');
        if ($translate->isAvailable($locale)) {
            $currentMessages = $translate->getMessages($locale);
        } else {
            $currentMessages = array(); // @todo this should redirect or smth
        }

        // Get phrases that are not in the english pack?
        if (!empty($currentMessages) && $locale != 'en') {
            $missingBasePhrases = array_diff_key($currentMessages, $baseMessages);
            $missingBasePhrases = array_combine(array_keys($missingBasePhrases), array_keys($missingBasePhrases));
            $baseMessages = array_merge($baseMessages, $missingBasePhrases);
        }

        // Build the fancy array
        $resultantMessages = array();
        $missing = 0;
        $index = 0;
        foreach ($baseMessages as $key => $value) {
            // Build
            $composite = array(
                'uid' => ++$index,
                'key' => $key,
                'original' => $value,
                'plural' => (bool) is_array($value),
            );

            // filters, plurals, and missing, oh my.
            if (isset($currentMessages[$key])) {
                if ('missing' == $show) {
                    continue;
                }
                if (is_array($value) && !is_array($currentMessages[$key])) {
                    $composite['current'] = array($currentMessages[$key]);
                } elseif (!is_array($value) && is_array($currentMessages[$key])) {
                    $composite['current'] = current($currentMessages[$key]);
                } else {
                    $composite['current'] = $currentMessages[$key];
                }
            } else {
                if ('translated' == $show) {
                    continue;
                }
                if (is_array($value)) {
                    $composite['current'] = array();
                } else {
                    $composite['current'] = '';
                }
                $missing++;
            }

            // Do search
            if ($search && !$this->_searchArrayRecursive($search, $composite)) {
                continue;
            }
            // Add
            $resultantMessages[] = $composite;
        }

        // Build the paginator
        $this->view->paginator = $paginator = Zend_Paginator::factory($resultantMessages);
        $paginator->setItemCountPerPage(50);
        $paginator->setCurrentPageNumber($page);


        // Process form POST
        if ($this->getRequest()->isPost()) {
            $keys = $this->_getParam('keys');
            $values = $this->_getParam('values');

            // Try to combine the values and keys arrays
            $combined = array();
            foreach ($values as $index => $value) {
                if (is_string($value)) {
                    if (empty($value)) {
                        continue;
                    }
                    $key = $keys[$index];
                    $combined[$key] = $value;
                } elseif (is_array($value)) {
                    if (empty($value) || array_filter($value) === array()) {
                        continue;
                    }
                    $key = $keys[$index][0];
                    if (engine_count($value) == 1)
                        $combined[$key] = array_merge($value, array($value[0]));
                    else
                        $combined[$key] = $value;
                }
            }

            // Try to write to a file
            $targetFile = APPLICATION_PATH . '/application/languages/' . $locale . '/custom.csv';
            if (!file_exists($targetFile)) {
                touch($targetFile);
                chmod($targetFile, 0777);
            }

            $writer = new Engine_Translate_Writer_Csv($targetFile);
            $writer->setTranslations($combined);
            $writer->write();

            // flush cached language vars
            @Zend_Registry::get('Zend_Cache')->clean();

            // redirect to this same page to get the new values
            return $this->_redirect($_SERVER['REQUEST_URI'], array('prependBase' => false));
        }
    }

    public function addPhraseAction()
    {

        $form = $this->view->form = new Core_Form_Admin_Language_AddPhrase();

        if ($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost())) {
            $phrase = $this->_getParam('phrase');

            $locale = $this->_getParam('locale');

            if ($phrase && $locale) {
                $targetFile = APPLICATION_PATH . '/application/languages/' . $locale . '/custom.csv';
                if (!file_exists($targetFile)) {
                    touch($targetFile);
                    chmod($targetFile, 0777);
                }
                if (file_exists($targetFile)) {
                    $writer = new Engine_Translate_Writer_Csv($targetFile);
                    $writer->setTranslations(
                        array(
                            $phrase => $phrase,
                        )
                    );
                    $writer->write();
                    @Zend_Registry::get('Zend_Cache')->clean();
                }
            }
            $this->_forward('success', 'utility', 'core', array(
                'parentRedirect' => Zend_Controller_Front::getInstance()->getRouter()->assemble(array('module' => 'core', 'controller' => 'language', 'action' => 'edit', 'locale' => $locale, 'search' => $phrase), 'admin_default', true),
            )
            );
        }
    }

    public function translateAction()
    {
        // Prepare form
        $this->view->form = $form = new Core_Form_Admin_Language_Translate();

        // Check method/valid
        if (!$this->getRequest()->isPost()) {
            return;
        }
        if (!$form->isValid($this->getRequest()->getPost())) {
            return;
        }

        $this->view->form = null;
        $this->view->values = $form->getValues();
    }

    public function translateRemoteAction()
    {
        set_time_limit(0);

        // Get params
        $this->view->source = $source = $this->_getParam('source');
        $this->view->target = $target = $this->_getParam('target');
        $this->view->batchCount = $batchCount = $this->_getParam('batchCount', 50);
        $this->view->retranslate = $retranslate = $this->_getParam('retranslate');
        $this->view->file = $file = $this->_getParam('file');
        $this->view->offset = $offset = $this->_getParam('offset', 0);

        // Check params

        // Check source
        if (empty($source) || !is_string($source)) {
            throw new Engine_Exception('Invalid source data type');
        }
        if (!Zend_Locale::isLocale($source)) {
            $this->view->status = false;
            $this->view->error = 'The source language does not appear to be a valid locale.';
            return;
        }
        if (!Engine_Service_GTranslate::isAvailableLanguage($source)) {
            $this->view->status = false;
            $this->view->error = 'The source language does not appear to be a available for translation.';
            return;
        }

        // Check source dir
        $sourceDir = $this->_languagePath . '/' . $source;
        if (!is_dir($sourceDir)) {
            $this->view->status = false;
            $this->view->error = 'The source language does not appear to exist.';
            return;
        }

        // Check target
        if (empty($target) || !is_string($target)) {
            $this->view->status = false;
            $this->view->error = 'Invalid target data type.';
            return;
        }

        // See if we need to expand target
        if (engine_in_array($target, array('all', 'all-translated', 'all-untranslated'))) {
            $form = new Core_Form_Admin_Language_Translate();
            $targetMultiOptions = $form->target->options;
            if ($target == 'all-translated') {
                $targets = $targetMultiOptions['Translated'];
            } elseif ($target == 'all-untranslated') {
                $targets = $targetMultiOptions['Untranslated'];
            } else {
                $targets = array_merge($targetMultiOptions['Untranslated'], $targetMultiOptions['Translated']);
            }

            // Meh
            $this->view->status = true;
            $this->view->state = 'listTargets';
            $this->view->targets = $targets;
            return;
        }

        // Check target
        if (!Zend_Locale::isLocale($source)) {
            $this->view->status = false;
            $this->view->error = 'The target language does not appear to be a valid locale.';
            return;
        }
        if (!Engine_Service_GTranslate::isAvailableLanguage($source)) {
            $this->view->status = false;
            $this->view->error = 'The target language does not appear to be a available for translation.';
            return;
        }

        // Check target dir
        $targetDir = $this->_languagePath . '/' . $target;
        if (!is_dir($targetDir)) {
            // try to create
            if (!@mkdir($targetDir, 0777, true)) {
                $this->view->status = false;
                $this->view->error = 'The target language pack does not exist and it was not possible to create it.';
                return;
            }
        }


        // Check files
        $sourceFiles = array();
        $it = new DirectoryIterator($sourceDir);
        foreach ($it as $file) {
            // Ignore dirs (duh)
            if (!$file->isFile()) {
                continue;
            }
            if (strtolower(substr($file->getFilename(), -4)) !== '.csv') {
                continue;
            }
            // Ignore files that have already been translated (at least for now)
            //if( file_exists($targetDir . '/' . $file->getFilename()) ) continue;
            $files[] = basename($file->getPathName());
        }

        if (empty($files)) {
            $this->view->status = false;
            $this->view->error = 'No source files.';
            return;
        }

        // See if we need to expand file
        if (empty($file)) {
            $this->view->status = true;
            $this->view->state = 'listFiles';
            $this->view->files = $files;
            return;
        } elseif (!engine_in_array($file, $files)) {
            $this->view->status = false;
            $this->view->error = 'Not a valid file.';
            return;
        }

        $sourceFile = $sourceDir . '/' . $file;
        $targetFile = $targetDir . '/' . $file;

        // Init translate API
        $languageApi = new Engine_Service_GTranslate();
        $languageApi->setRequestType('curl');

        // Init reader and writer
        $reader = new Engine_Translate_Writer_Csv($sourceFile);
        $writer = new Engine_Translate_Writer_Csv($targetFile);

        // Get messages and diff
        $sourceMessages = $reader->getTranslations();
        $targetMessages = $writer->getTranslations();
        $deltaMessages = array_diff_key($sourceMessages, $targetMessages);

        // Take the batchCount of messages
        // In retranslate mode, we need to go by offset
        $currentMessages = array_slice($deltaMessages, ($retranslate ? $offset : 0), $batchCount);
        if ($retranslate) {
            $this->view->newOffset = $offset = ($offset + engine_count($currentMessages));
        }

        // If it's empty, we're done with the file
        if ((!$retranslate && empty($deltaMessages)) || empty($currentMessages)) {
            $this->view->status = true;
            $this->view->state = 'fileComplete';
            return;
        }

        // Process plurals into normal variables temporarily
        $currentKeys = array();
        $currentValues = array();

        foreach ($currentMessages as $key => $value) {
            if (is_array($value)) {
                foreach ($value as $val) {
                    $currentKeys[] = $key;
                    $currentValues[] = $this->_escape($val);
                }
            } else {
                $currentKeys[] = $key;
                $currentValues[] = $this->_escape($value);
            }
        }

        // Send to google
        $response = (array) $languageApi->query($source, $target, $currentValues);
        if (!$response || engine_count($response) !== engine_count($currentValues)) {
            $this->view->status = false;
            $this->view->error = 'Translation failed';
            return;
        }

        // Process response back into plurals
        $resultantMessages = array();
        foreach ($currentKeys as $index => $key) {
            //$previousValue = $currentValues[$index];
            $value = $this->_unescape($response[$index]);

            if (isset($resultant[$key])) {
                if (!is_array($resultant[$key])) {
                    $resultant[$key] = array($resultant[$key]);
                }
                $resultant[$key][] = $value;
            } else {
                $resultant[$key] = $value;
            }
        }


        // Write to file
        $writer->setTranslations($resultantMessages);
        $writer->write();


        // Send back statistics
        $this->view->status = true;
        $this->view->sourceCount = engine_count($sourceMessages);
        $this->view->targetCount = engine_count($targetMessages);
        $this->view->untranslatedCount = engine_count($deltaMessages);
        $this->view->translatedCount = engine_count($resultantMessages);
    }

    public function translatePhraseAction()
    {
        // Make sure source is valid
        $this->view->source = $source = $this->_getParam('source');
        if (!Zend_Locale::isLocale($source)) {
            $this->view->status = false;
            $this->view->error = 'Source is not a valid Zend locale';
            return;
        }
        if (!Engine_Service_GTranslate::isAvailableLanguage($source)) {
            $this->view->status = false;
            $this->view->error = 'Source is not a valid Google locale';
            return;
        }

        // Make sure target is valid
        $this->view->target = $target = $this->_getParam('target');
        if (!Zend_Locale::isLocale($target)) {
            $this->view->status = false;
            $this->view->error = 'Target is not a valid Zend locale';
            return;
        }
        if (!Engine_Service_GTranslate::isAvailableLanguage($target)) {
            $this->view->status = false;
            $this->view->error = 'Target is not a valid Google locale';
            return;
        }

        // Make sure we have a phrase
        $text = $this->_getParam('text');
        if (!$text) {
            $this->view->status = false;
            $this->view->error = 'No text was given';
            return;
        }

        // Check for escape param
        $escape = (bool) $this->_getParam('escape', true);

        // Send query
        $languageApi = new Engine_Service_GTranslate();
        $languageApi->setRequestType('curl');

        if ($escape) {
            $this->view->sourcePhraseUnescaped = $text;
            $text = $this->_escape($text);
        }

        $response = $languageApi->query($source, $target, $text);

        if ($escape) {
            $this->view->targetPhraseEscaped = $response;
            $response = $this->_unescape($response);
        }


        // Assign response
        $this->view->sourcePhrase = $text;
        $this->view->targetPhrase = $response;
    }

		public function exportAction()
		{
			$locale = $this->_getParam('locale', 'en');  // e.g., 'ar'
			$translationDir = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'application' . DIRECTORY_SEPARATOR . 'languages' . DIRECTORY_SEPARATOR . $locale;
			$output = [];

			if (!is_dir($translationDir)) {
					throw new Exception("Translation directory not found: " . $translationDir);
			}

			$csvFiles = glob($translationDir . '/*.csv');
			if (empty($csvFiles)) {
					throw new Exception("No CSV files found in translation directory.");
			}

			foreach ($csvFiles as $csvFile) {
					$handle = fopen($csvFile, 'r');
					if (!$handle) continue;

					while (($data = fgetcsv($handle, 0, ';')) !== false) {
							if (count($data) >= 2) {
									$key = trim($data[0]);
									$value = trim($data[1]);

									if (!isset($output[$key])) {
											$output[$key] = $value;
									}
							}
					}
					fclose($handle);
			}

			ksort($output); // Optional: sort keys alphabetically

			if (empty($output)) {
					throw new Exception("No translations found after merging.");
			}

			$tmpFile = APPLICATION_PATH . "/temporary/merged_{$locale}.csv";
			$fp = fopen($tmpFile, 'w');
			if (!$fp) {
					throw new Exception("Cannot write to file: $tmpFile");
			}

			// Write using semicolon as delimiter
			foreach ($output as $key => $value) {
				// Skip empty keys to prevent leading semicolon
    		if ($key === '') continue;
				fputcsv($fp, [$key, $value], ';');
			}

			fclose($fp);

			// Force download
			header("Content-Disposition: attachment; filename=\"{$locale}.csv\"");
			header("Content-Type: text/csv; charset=utf-8");
			header("Content-Length: " . filesize($tmpFile));
			readfile($tmpFile);
			unlink($tmpFile);
			exit;
		}

    protected function _searchArrayRecursive($searchStr, $searchValue)
    {
        $found = false;
        if (is_array($searchValue)) {
            foreach ($searchValue as $key => $value) {
                if (is_string($value) && stripos($value, $searchStr) !== false) {
                    $found = true;
                    break;
                } elseif (is_array($value)) {
                    if ($this->_searchArrayRecursive($searchStr, $value)) {
                        $found = true;
                        break;
                    }
                }
            }
        }

        return $found;
    }

    protected function _escape($string)
    {
        // <![CDATA[
        // ]]>

        // Cdata
        // Format: {}
        //$cdataChar = "\x10";
        //$cdataData = null;
        //$string = $this->_replaceAll('/' . preg_quote('<![CDATA[', '/') . '.+?' . preg_quote(']]>', '/') . '/iu', $string, $cdataChar, $cdataData);

        // Just strip cdata for now
        $string = str_replace(array('<![CDATA[', ']]>'), array('', ''), $string);

        // Sprintf
        // Format: php.net
        $sprintfChar = "\x14";
        $sprintfData = null;
        $string = $this->_replaceAll('/\%([0-9]\$)?[ds]/iu', $string, $sprintfChar, $sprintfData);

        // Activity
        // Format: {}
        $activityChar = "\x11";
        $activityData = null;
        $string = $this->_replaceAll('/\{[^{}\x11\x12\x13\x14]+?\}/iu', $string, $activityChar, $activityData);

        // Substitution
        // Format: %%
        $substitutionChar = "\x12";
        $substitutionData = null;
        $string = $this->_replaceAll('/\%[^%\x11\x12\x13\x14]+?\%/iu', $string, $substitutionChar, $substitutionData);

        // Mail
        // Format: []
        $mailChar = "\x13";
        $mailData = null;
        $string = $this->_replaceAll('/\[[^\[\]\x11\x12\x13\x14]+?\]/iu', $string, $mailChar, $mailData);


        // Line breaks
        $lineChar = "\x15";
        $lineData = null;
        $string = $this->_replaceAll('/[\r\n]+/iu', $string, $lineChar, $lineData);


        // Restore everything
        $string = $this->_restoreAll($activityChar, $activityData, $string);
        $string = $this->_restoreAll($substitutionChar, $substitutionData, $string);
        $string = $this->_restoreAll($mailChar, $mailData, $string);
        $string = $this->_restoreAll($sprintfChar, $sprintfData, $string);
        $string = $this->_restoreAll($lineChar, $lineData, $string);

        return $string;
    }

    protected function _unescape($string)
    {
        // Decode
        $string = htmlspecialchars_decode($string, ENT_QUOTES);

        // Remove
        $string = preg_replace('/' . preg_quote('<![CDATA[', '/') . '(.*?)' . preg_quote(']]>', '/') . '/ius', '$1', $string);

        /*
        // Mail
        $string = preg_replace('/\<\?\{{3}([^\[\]]+?)\}{3}\?\>/iu', '[$1]', $string);

        // Substitution
        $string = preg_replace('/\<\?\{{2}([^%]+?)\}{2}\?\>/iu', '%$1%', $string);

        // Activity
        $string = preg_replace('/\<\?\{([^%]+?)\}\?\>/iu', '{$1}', $string);

        // Sprintf
        $string = preg_replace('/\<\?(\%([0-9]\$)?[ds])\?\>/iu', '$1', $string);
        */

        return $string;
    }

    protected function _replaceAll($pattern, $subject, $replace, &$replaceData)
    {
        preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE);
        if (empty($matches)) {
            return array();
        }

        $replaceData = array();
        $delta = 0;
        foreach ($matches[0] as $match) {
            $subString = $match[0];
            $start = $match[1] - $delta;
            $length = strlen($subString);
            $replaceData[] = $subString;
            $subject = substr($subject, 0, $start)
                . $replace
                . substr($subject, $start + $length);
            $delta += $length - 1;
        }

        return $subject;
    }

    protected function _restoreAll($char, $data, $string)
    {
        // <![CDATA[
        // ]]>
        if (is_array($data) && engine_count($data) > 0) {
            $string = str_replace($char, '<![CDATA[' . $char . ']]>', $string);
            foreach ($data as $datum) {
                $pos = strpos($string, $char);
                if (false !== $pos) {
                    $string = substr($string, 0, $pos)
                        . $datum
                        . substr($string, $pos + strlen($char))
                    ;
                }
            }
        }
        return $string;
    }
}
