<commit_before><?php

/*
 * This file is part of Fork CMS.
 *
 * For the full copyright and license information, please view the license
 * file that was distributed with this source code.
 */

/**
 * In this file we store all generic functions that we will be using in the locale module
 *
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 */
class BackendLocaleModel
{
	/**
	 * Build the language files
	 *
	 * @param string $language The language to build the locale-file for.
	 * @param string $application The application to build the locale-file for.
	 */
	public static function buildCache($language, $application)
	{
		// get db
		$db = Spoon::get('database');

		// get types
		$types = $db->getEnumValues('locale', 'type');

		// get locale for backend
		$locale = (array) $db->getRecords(
			'SELECT type, module, name, value
			 FROM locale
			 WHERE language = ? AND application = ?
			 ORDER BY type ASC, name ASC, module ASC',
			array((string) $language, (string) $application)
		);

		// start generating PHP
		$value = '<?php' . "\n\n";
		$value .= '/**' . "\n";
		$value .= ' *' . "\n";
		$value .= ' * This file is generated by Fork CMS, it contains' . "\n";
		$value .= ' * more information about the locale. Do NOT edit.' . "\n";
		$value .= ' * ' . "\n";
		$value .= ' * @author Fork CMS' . "\n";
		$value .= ' * @generated	' . date('Y-m-d H:i:s') . "\n";
		$value .= ' */' . "\n";
		$value .= "\n";

		foreach($types as $type)
		{
			// default module
			$modules = array('core');

			// continue output
			$value .= "\n";
			$value .= '// init var' . "\n";
			$value .= '$' . $type . ' = array();' . "\n";
			$value .= '$' . $type . '[\'core\'] = array();' . "\n";

			// loop locale
			foreach($locale as $i => $item)
			{
				// types match
				if($item['type'] == $type)
				{
					// new module
					if(!in_array($item['module'], $modules))
					{
						$value .= '$' . $type . '[\'' . $item['module'] . '\'] = array();' . "\n";
						$modules[] = $item['module'];
					}

					// parse
					if($application == 'backend') $value .= '$' . $type . '[\'' . $item['module'] . '\'][\'' . $item['name'] . '\'] = \'' . str_replace('\"', '"', addslashes($item['value'])) . '\';' . "\n";
					else $value .= '$' . $type . '[\'' . $item['name'] . '\'] = \'' . str_replace('\"', '"', addslashes($item['value'])) . '\';' . "\n";

					// unset
					unset($locale[$i]);
				}
			}
		}

		$value .= "\n";
		$value .= '?>';

		// store
		SpoonFile::setContent(constant(mb_strtoupper($application) . '_CACHE_PATH') . '/locale/' . $language . '.php', $value);
	}

	/**
	 * Build a query for the URL based on the filter
	 *
	 * @param array $filter The filter.
	 * @return array
	 */
	public static function buildURLQueryByFilter($filter)
	{
		$query = '';

		// loop filter items
		foreach($filter as $key => $value)
		{
			// is it an array?
			if(is_array($value))
			{
				// loop the array
				foreach($value as $v)
				{
					// add to the query
					$query .= '&' . $key . '[]=' . $v;
				}
			}

			// not an array
			else
			{
				// add to the query
				$query .= '&' . $key . '=' . $value;
			}
		}

		return $query;
	}

	/**
	 * Create an XML-string that can be used for export.
	 *
	 * @param array $items The items.
	 * @return string
	 */
	public static function createXMLForExport(array $items)
	{
		$xml = new DOMDocument('1.0', 'utf-8');

		// set some properties
		$xml->preserveWhiteSpace = false;
		$xml->formatOutput = true;

		// locale root element
		$root = $xml->createElement('locale');
		$xml->appendChild($root);

		// loop applications
		foreach($items as $application => $modules)
		{
			// create application element
			$applicationElement = $xml->createElement($application);
			$root->appendChild($applicationElement);

			// loop modules
			foreach($modules as $module => $types)
			{
				// create application element
				$moduleElement = $xml->createElement($module);
				$applicationElement->appendChild($moduleElement);

				// loop types
				foreach($types as $type => $items)
				{
					// loop items
					foreach($items as $name => $translations)
					{
						// create application element
						$itemElement = $xml->createElement('item');
						$moduleElement->appendChild($itemElement);

						// attributes
						$itemElement->setAttribute('type', BackendLocaleModel::getTypeName($type));
						$itemElement->setAttribute('name', $name);

						// loop translations
						foreach($translations as $translation)
						{
							// create translation
							$translationElement = $xml->createElement('translation');
							$itemElement->appendChild($translationElement);

							// attributes
							$translationElement->setAttribute('language', $translation['language']);

							// set content
							$translationElement->appendChild(new DOMCdataSection($translation['value']));
						}
					}
				}
			}
		}

		return $xml->saveXML();
	}

	/**
	 * Delete (multiple) items from locale
	 *
	 * @param array $ids The id(s) to delete.
	 */
	public static function delete(array $ids)
	{
		// loop and cast to integers
		foreach($ids as &$id) $id = (int) $id;

		// create an array with an equal amount of questionmarks as ids provided
		$idPlaceHolders = array_fill(0, count($ids), '?');

		// delete records
		BackendModel::getDB(true)->delete('locale', 'id IN (' . implode(', ', $idPlaceHolders) . ')', $ids);

		// rebuild cache
		self::buildCache(BL::getWorkingLanguage(), 'backend');
		self::buildCache(BL::getWorkingLanguage(), 'frontend');
	}

	/**
	 * Does an id exist.
	 *
	 * @param int $id The id to check for existence.
	 * @return bool
	 */
	public static function exists($id)
	{
		return (bool) BackendModel::getDB()->getVar(
			'SELECT COUNT(id)
			 FROM locale
			 WHERE id = ?',
			array((int) $id)
		);
	}

	/**
	 * Does a locale exists by its name.
	 *
	 * @param string $name The name of the locale.
	 * @param string $type The type of the locale.
	 * @param string $module The module wherin will be searched.
	 * @param string $language The language to use.
	 * @param string $application The application wherin will be searched.
	 * @param int[optional] $id The id to exclude in the check.
	 * @return bool
	 */
	public static function existsByName($name, $type, $module, $language, $application, $id = null)
	{
		$name = (string) $name;
		$type = (string) $type;
		$module = (string) $module;
		$language = (string) $language;
		$application = (string) $application;
		$id = ($id !== null) ? (int) $id : null;

		// get db
		$db = BackendModel::getDB();

		// return
		if($id !== null) return (bool) $db->getVar(
			'SELECT COUNT(id)
			 FROM locale
			 WHERE name = ? AND type = ? AND module = ? AND language = ? AND application = ? AND id != ?',
			array($name, $type, $module, $language, $application, $id)
		);

		return (bool) BackendModel::getDB()->getVar(
			'SELECT COUNT(id)
			 FROM locale
			 WHERE name = ? AND type = ? AND module = ? AND language = ? AND application = ?',
			array($name, $type, $module, $language, $application)
		);
	}

	/**
	 * Get a single item from locale.
	 *
	 * @param int $id The id of the item to get.
	 * @return array
	 */
	public static function get($id)
	{
		// fetch record from db
		$record = (array) BackendModel::getDB()->getRecord('SELECT * FROM locale WHERE id = ?', array((int) $id));

		// actions are urlencoded
		if($record['type'] == 'act') $record['value'] = urldecode($record['value']);

		return $record;
	}

	/**
	 * Get a locale by its name
	 *
	 * @param string $name The name of the locale.
	 * @param string $type The type of the locale.
	 * @param string $module The module wherin will be searched.
	 * @param string $language The language to use.
	 * @param string $application The application wherin will be searched.
	 * @return bool
	 */
	public static function getByName($name, $type, $module, $language, $application)
	{
		$name = (string) $name;
		$type = (string) $type;
		$module = (string) $module;
		$language = (string) $language;
		$application = (string) $application;

		return BackendModel::getDB()->getVar(
			'SELECT l.id
			 FROM locale AS l
			 WHERE name = ? AND type = ? AND module = ? AND language = ? AND application = ?',
			array($name, $type, $module, $language, $application)
		);
	}

	/**
	 * Grab labels found in the backend navigation.
	 *
	 * @return array
	 */
	private static function getLabelsFromBackendNavigation()
	{
		return (array) BackendModel::getDB()->getColumn('SELECT label FROM backend_navigation');
	}

	/**
	 * Get the languages for a multicheckbox.
	 *
	 * @param bool[optional] $includeInterfaceLanguages Should we also get the interfacelanguages?
	 * @return array
	 */
	public static function getLanguagesForMultiCheckbox($includeInterfaceLanguages = false)
	{
		// get working languages
		$aLanguages = BL::getWorkingLanguages();

		// add the interface languages if needed
		if($includeInterfaceLanguages) $aLanguages = array_merge($aLanguages, BL::getInterfaceLanguages());

		// create a new array to redefine the langauges for the multicheckbox
		$languages = array();

		// loop the languages
		foreach($aLanguages as $key => $lang)
		{
			// add to array
			$languages[$key]['value'] = $key;
			$languages[$key]['label'] = $lang;
		}

		return $languages;
	}

	/**
	 * Get the locale that is used in the backend but doesn't exists.
	 *
	 * @param string $language The language to check.
	 * @return array
	 */
	public static function getNonExistingBackendLocale($language)
	{
		$tree = self::getTree(BACKEND_PATH);
		$modules = BackendModel::getModules();

		// search fo the error module
		$key = array_search('error', $modules);

		// remove error module
		if($key !== false) unset($modules[$key]);

		$used = array();

		// get labels from navigation
		$lbl = self::getLabelsFromBackendNavigation();
		foreach((array) $lbl as $label) $used['lbl'][$label] = array('files' => array('<small>used in navigation</small>'), 'module_specific' => array());

		// get labels from table
		$lbl = (array) BackendModel::getDB()->getColumn('SELECT label FROM modules_extras');
		foreach((array) $lbl as $label) $used['lbl'][$label] = array('files' => array('<small>used in database</small>'), 'module_specific' => array());

		// loop files
		foreach($tree as $file)
		{
			// grab content
			$content = SpoonFile::getContent($file);

			// process based on extension
			switch(SpoonFile::getExtension($file))
			{
				// javascript file
				case 'js':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)(.*)(\|.*)?\}/iU', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// loop modules
							foreach($modules as $module)
							{
								// determine if this is a module specific locale
								if(substr($match, 0, mb_strlen($module)) == SpoonFilter::toCamelCase($module) && mb_strlen($match) > mb_strlen($module))
								{
									// cleanup
									$match = str_replace(SpoonFilter::toCamelCase($module), '', $match);

									// init if needed
									if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

									// add module
									$used[$type][$match]['module_specific'][] = $module;
								}
							}

							// init if needed
							if(!isset($used[$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// PHP file
				case 'php':
					$matches = array();
					$matchesURL = array();

					// get matches
					preg_match_all('/(BackendLanguage|BL)::(get(Label|Error|Message)|act|err|lbl|msg)\(\'(.*)\'(.*)?\)/iU', $content, $matches);

					// match errors
					preg_match_all('/&(amp;)?(error|report)=([A-Z0-9-_]+)/i', $content, $matchesURL);

					// any errormessages
					if(!empty($matchesURL[0]))
					{
						// loop matches
						foreach($matchesURL[3] as $key => $match)
						{
							$type = 'lbl';
							if($matchesURL[2][$key] == 'error') $type = 'Error';
							if($matchesURL[2][$key] == 'report') $type = 'Message';

							$matches[0][] = '';
							$matches[1][] = 'BL';
							$matches[2][] = '';
							$matches[3][] = $type;
							$matches[4][] = SpoonFilter::toCamelCase(SpoonFilter::toCamelCase($match, '-'), '_');
							$matches[5][] = '';
						}
					}

					// any matches?
					if(!empty($matches[4]))
					{
						// loop matches
						foreach($matches[4] as $key => $match)
						{
							// set type
							$type = 'lbl';
							if($matches[3][$key] == 'Error' || $matches[2][$key] == 'err') $type = 'err';
							if($matches[3][$key] == 'Message' || $matches[2][$key] == 'msg') $type = 'msg';

							// specific module?
							if(isset($matches[5][$key]) && $matches[5][$key] != '')
							{
								// try to grab the module
								$specificModule = $matches[5][$key];
								$specificModule = trim(str_replace(array(',', '\''), '', $specificModule));

								// not core?
								if($specificModule != 'core')
								{
									// dynamic module
									if($specificModule == '$this->URL->getModule(' || $specificModule == '$this->getModule(')
									{
										// init var
										$count = 0;

										// replace
										$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

										// validate
										if($count == 1)
										{
											// split into chunks
											$chunks = (array) explode('/', trim($modulePath, '/'));

											// set specific module
											if(isset($chunks[0])) $specificModule = $chunks[0];

											// skip
											else continue;
										}
									}

									// init if needed
									if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

									// add module
									$used[$type][$match]['module_specific'][] = $specificModule;
								}
							}

							else
							{
								// loop modules
								foreach($modules as $module)
								{
									// determine if this is a module specific locale
									if(substr($match, 0, mb_strlen($module)) == SpoonFilter::toCamelCase($module) && mb_strlen($match) > mb_strlen($module) && ctype_upper(substr($match, mb_strlen($module) + 1, 1)))
									{
										// cleanup
										$match = str_replace(SpoonFilter::toCamelCase($module), '', $match);

										// init if needed
										if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

										// add module
										$used[$type][$match]['module_specific'][] = $module;
									}
								}
							}

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// template file
				case 'tpl':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)([A-Z][a-zA-Z_]*)(\|.*)?\}/U', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// loop modules
							foreach($modules as $module)
							{
								// determine if this is a module specific locale
								if(substr($match, 0, mb_strlen($module)) == SpoonFilter::toCamelCase($module) && mb_strlen($match) > mb_strlen($module))
								{
									// cleanup
									$match = str_replace(SpoonFilter::toCamelCase($module), '', $match);

									// init if needed
									if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

									// add module
									$used[$type][$match]['module_specific'][] = $module;
								}
							}

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;
			}
		}

		// init var
		$nonExisting = array();

		// check if the locale is present in the current language
		foreach($used as $type => $items)
		{
			// loop items
			foreach($items as $key => $data)
			{
				// process based on type
				switch($type)
				{
					// error
					case 'err':
						// module specific?
						if(!empty($data['module_specific']))
						{
							// loop modules
							foreach($data['module_specific'] as $module)
							{
								// if the error isn't found add it to the list
								if(substr_count(BL::err($key, $module), '{$' . $type) > 0) $nonExisting[] = array('language' => $language, 'application' => 'backend', 'module' => $module, 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}

						// not specific
						else
						{
							// if the error isn't found add it to the list
							if(substr_count(BL::err($key), '{$' . $type) > 0)
							{
								// init var
								$exists = false;

								// loop files
								foreach($data['files'] as $file)
								{
									// init var
									$count = 0;

									// replace
									$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

									// validate
									if($count == 1)
									{
										// split into chunks
										$chunks = (array) explode('/', trim($modulePath, '/'));

										// first part is the module
										if(isset($chunks[0]) && BL::err($key, $chunks[0]) != '{$' . $type . SpoonFilter::toCamelCase($chunks[0]) . $key . '}') $exists = true;
									}
								}

								// doesn't exists
								if(!$exists) $nonExisting[] = array('language' => $language, 'application' => 'backend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}
						break;

					// label
					case 'lbl':
						// module specific?
						if(!empty($data['module_specific']))
						{
							// loop modules
							foreach($data['module_specific'] as $module)
							{
								// if the label isn't found add it to the list
								if(substr_count(BL::lbl($key, $module), '{$' . $type) > 0) $nonExisting[] = array('language' => $language, 'application' => 'backend', 'module' => $module, 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}

						// not specific
						else
						{
							// if the label isn't found, check in the specific module
							if(substr_count(BL::lbl($key), '{$' . $type) > 0)
							{
								// init var
								$exists = false;

								// loop files
								foreach($data['files'] as $file)
								{
									// init var
									$count = 0;

									// replace
									$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

									// validate
									if($count == 1)
									{
										// split into chunks
										$chunks = (array) explode('/', trim($modulePath, '/'));

										// first part is the module
										if(isset($chunks[0]) && BL::lbl($key, $chunks[0]) != '{$' . $type . SpoonFilter::toCamelCase($chunks[0]) . $key . '}') $exists = true;
									}
								}

								// doesn't exists
								if(!$exists) $nonExisting[] = array('language' => $language, 'application' => 'backend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}
						break;

					// message
					case 'msg':
						// module specific?
						if(!empty($data['module_specific']))
						{
							// loop modules
							foreach($data['module_specific'] as $module)
							{
								// if the message isn't found add it to the list
								if(substr_count(BL::msg($key, $module), '{$' . $type) > 0) $nonExisting[] = array('language' => $language, 'application' => 'backend', 'module' => $module, 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}

						// not specific
						else
						{
							// if the message isn't found add it to the list
							if(substr_count(BL::msg($key), '{$' . $type) > 0)
							{
								// init var
								$exists = false;

								// loop files
								foreach($data['files'] as $file)
								{
									// init var
									$count = 0;

									// replace
									$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

									// validate
									if($count == 1)
									{
										// split into chunks
										$chunks = (array) explode('/', trim($modulePath, '/'));

										// first part is the module
										if(isset($chunks[0]) && BL::msg($key, $chunks[0]) != '{$' . $type . SpoonFilter::toCamelCase($chunks[0]) . $key . '}') $exists = true;
									}
								}

								// doesn't exists
								if(!$exists) $nonExisting[] = array('language' => $language, 'application' => 'backend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}
						break;
				}
			}
		}

		// return
		return $nonExisting;
	}

	/**
	 * Get the locale that is used in the frontend but doesn't exists.
	 *
	 * @param string $language The language to check.
	 * @return array
	 */
	public static function getNonExistingFrontendLocale($language)
	{
		// get files to process
		$tree = self::getTree(FRONTEND_PATH);
		$used = array();

		// loop files
		foreach($tree as $file)
		{
			// grab content
			$content = SpoonFile::getContent($file);

			// process the file based on extension
			switch(SpoonFile::getExtension($file))
			{
				// javascript file
				case 'js':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)(.*)(\|.*)?\}/iU', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// init if needed
							if(!isset($used[$match])) $used[$type][$match] = array('files' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// PHP file
				case 'php':
					$matches = array();

					// get matches
					preg_match_all('/(FrontendLanguage|FL)::(get(Action|Label|Error|Message)|act|lbl|err|msg)\(\'(.*)\'\)/iU', $content, $matches);

					// any matches?
					if(!empty($matches[4]))
					{
						// loop matches
						foreach($matches[4] as $key => $match)
						{
							$type = 'lbl';
							if($matches[3][$key] == 'Action') $type = 'act';
							if($matches[2][$key] == 'act') $type = 'act';
							if($matches[3][$key] == 'Error') $type = 'err';
							if($matches[2][$key] == 'err') $type = 'err';
							if($matches[3][$key] == 'Message') $type = 'msg';
							if($matches[2][$key] == 'msg') $type = 'msg';

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// template file
				case 'tpl':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)([a-z-_]*)(\|.*)?\}/iU', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;
			}
		}

		// init var
		$nonExisting = array();

		// set language
		FrontendLanguage::setLocale($language);

		// check if the locale is present in the current language
		foreach($used as $type => $items)
		{
			// loop items
			foreach($items as $key => $data)
			{
				// process based on type
				switch($type)
				{
					case 'act':
						// if the action isn't available add it to the list
						if(FL::act($key) == '{$' . $type . $key . '}') $nonExisting[] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;

					case 'err':
						// if the error isn't available add it to the list
						if(FL::err($key) == '{$' . $type . $key . '}') $nonExisting[] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;

					case 'lbl':
						// if the label isn't available add it to the list
						if(FL::lbl($key) == '{$' . $type . $key . '}') $nonExisting[] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;

					case 'msg':
						// if the message isn't available add it to the list
						if(FL::msg($key) == '{$' . $type . $key . '}') $nonExisting[] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;
				}
			}
		}

		return $nonExisting;
	}

	/**
	 * Get the translations
	 *
	 * @param string $application The application.
	 * @param string $module The module.
	 * @param array $types The types of the translations to get.
	 * @param array $languages The languages of the translations to get.
	 * @param string $name The name.
	 * @param string $value The value.
	 * @return array
	 */
	public static function getTranslations($application, $module, $types, $languages, $name, $value)
	{
		$languages = (array) $languages;

		// create an array for the languages, surrounded by quotes (example: 'en')
		$aLanguages = array();
		foreach($languages as $key => $val) $aLanguages[$key] = '\'' . $val . '\'';
<commit_msg>Sort the missing locale items.<commit_after><?php

/*
 * This file is part of Fork CMS.
 *
 * For the full copyright and license information, please view the license
 * file that was distributed with this source code.
 */

/**
 * In this file we store all generic functions that we will be using in the locale module
 *
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 */
class BackendLocaleModel
{
	/**
	 * Build the language files
	 *
	 * @param string $language The language to build the locale-file for.
	 * @param string $application The application to build the locale-file for.
	 */
	public static function buildCache($language, $application)
	{
		// get db
		$db = Spoon::get('database');

		// get types
		$types = $db->getEnumValues('locale', 'type');

		// get locale for backend
		$locale = (array) $db->getRecords(
			'SELECT type, module, name, value
			 FROM locale
			 WHERE language = ? AND application = ?
			 ORDER BY type ASC, name ASC, module ASC',
			array((string) $language, (string) $application)
		);

		// start generating PHP
		$value = '<?php' . "\n\n";
		$value .= '/**' . "\n";
		$value .= ' *' . "\n";
		$value .= ' * This file is generated by Fork CMS, it contains' . "\n";
		$value .= ' * more information about the locale. Do NOT edit.' . "\n";
		$value .= ' * ' . "\n";
		$value .= ' * @author Fork CMS' . "\n";
		$value .= ' * @generated	' . date('Y-m-d H:i:s') . "\n";
		$value .= ' */' . "\n";
		$value .= "\n";

		foreach($types as $type)
		{
			// default module
			$modules = array('core');

			// continue output
			$value .= "\n";
			$value .= '// init var' . "\n";
			$value .= '$' . $type . ' = array();' . "\n";
			$value .= '$' . $type . '[\'core\'] = array();' . "\n";

			// loop locale
			foreach($locale as $i => $item)
			{
				// types match
				if($item['type'] == $type)
				{
					// new module
					if(!in_array($item['module'], $modules))
					{
						$value .= '$' . $type . '[\'' . $item['module'] . '\'] = array();' . "\n";
						$modules[] = $item['module'];
					}

					// parse
					if($application == 'backend') $value .= '$' . $type . '[\'' . $item['module'] . '\'][\'' . $item['name'] . '\'] = \'' . str_replace('\"', '"', addslashes($item['value'])) . '\';' . "\n";
					else $value .= '$' . $type . '[\'' . $item['name'] . '\'] = \'' . str_replace('\"', '"', addslashes($item['value'])) . '\';' . "\n";

					// unset
					unset($locale[$i]);
				}
			}
		}

		$value .= "\n";
		$value .= '?>';

		// store
		SpoonFile::setContent(constant(mb_strtoupper($application) . '_CACHE_PATH') . '/locale/' . $language . '.php', $value);
	}

	/**
	 * Build a query for the URL based on the filter
	 *
	 * @param array $filter The filter.
	 * @return array
	 */
	public static function buildURLQueryByFilter($filter)
	{
		$query = '';

		// loop filter items
		foreach($filter as $key => $value)
		{
			// is it an array?
			if(is_array($value))
			{
				// loop the array
				foreach($value as $v)
				{
					// add to the query
					$query .= '&' . $key . '[]=' . $v;
				}
			}

			// not an array
			else
			{
				// add to the query
				$query .= '&' . $key . '=' . $value;
			}
		}

		return $query;
	}

	/**
	 * Create an XML-string that can be used for export.
	 *
	 * @param array $items The items.
	 * @return string
	 */
	public static function createXMLForExport(array $items)
	{
		$xml = new DOMDocument('1.0', 'utf-8');

		// set some properties
		$xml->preserveWhiteSpace = false;
		$xml->formatOutput = true;

		// locale root element
		$root = $xml->createElement('locale');
		$xml->appendChild($root);

		// loop applications
		foreach($items as $application => $modules)
		{
			// create application element
			$applicationElement = $xml->createElement($application);
			$root->appendChild($applicationElement);

			// loop modules
			foreach($modules as $module => $types)
			{
				// create application element
				$moduleElement = $xml->createElement($module);
				$applicationElement->appendChild($moduleElement);

				// loop types
				foreach($types as $type => $items)
				{
					// loop items
					foreach($items as $name => $translations)
					{
						// create application element
						$itemElement = $xml->createElement('item');
						$moduleElement->appendChild($itemElement);

						// attributes
						$itemElement->setAttribute('type', BackendLocaleModel::getTypeName($type));
						$itemElement->setAttribute('name', $name);

						// loop translations
						foreach($translations as $translation)
						{
							// create translation
							$translationElement = $xml->createElement('translation');
							$itemElement->appendChild($translationElement);

							// attributes
							$translationElement->setAttribute('language', $translation['language']);

							// set content
							$translationElement->appendChild(new DOMCdataSection($translation['value']));
						}
					}
				}
			}
		}

		return $xml->saveXML();
	}

	/**
	 * Delete (multiple) items from locale
	 *
	 * @param array $ids The id(s) to delete.
	 */
	public static function delete(array $ids)
	{
		// loop and cast to integers
		foreach($ids as &$id) $id = (int) $id;

		// create an array with an equal amount of questionmarks as ids provided
		$idPlaceHolders = array_fill(0, count($ids), '?');

		// delete records
		BackendModel::getDB(true)->delete('locale', 'id IN (' . implode(', ', $idPlaceHolders) . ')', $ids);

		// rebuild cache
		self::buildCache(BL::getWorkingLanguage(), 'backend');
		self::buildCache(BL::getWorkingLanguage(), 'frontend');
	}

	/**
	 * Does an id exist.
	 *
	 * @param int $id The id to check for existence.
	 * @return bool
	 */
	public static function exists($id)
	{
		return (bool) BackendModel::getDB()->getVar(
			'SELECT COUNT(id)
			 FROM locale
			 WHERE id = ?',
			array((int) $id)
		);
	}

	/**
	 * Does a locale exists by its name.
	 *
	 * @param string $name The name of the locale.
	 * @param string $type The type of the locale.
	 * @param string $module The module wherin will be searched.
	 * @param string $language The language to use.
	 * @param string $application The application wherin will be searched.
	 * @param int[optional] $id The id to exclude in the check.
	 * @return bool
	 */
	public static function existsByName($name, $type, $module, $language, $application, $id = null)
	{
		$name = (string) $name;
		$type = (string) $type;
		$module = (string) $module;
		$language = (string) $language;
		$application = (string) $application;
		$id = ($id !== null) ? (int) $id : null;

		// get db
		$db = BackendModel::getDB();

		// return
		if($id !== null) return (bool) $db->getVar(
			'SELECT COUNT(id)
			 FROM locale
			 WHERE name = ? AND type = ? AND module = ? AND language = ? AND application = ? AND id != ?',
			array($name, $type, $module, $language, $application, $id)
		);

		return (bool) BackendModel::getDB()->getVar(
			'SELECT COUNT(id)
			 FROM locale
			 WHERE name = ? AND type = ? AND module = ? AND language = ? AND application = ?',
			array($name, $type, $module, $language, $application)
		);
	}

	/**
	 * Get a single item from locale.
	 *
	 * @param int $id The id of the item to get.
	 * @return array
	 */
	public static function get($id)
	{
		// fetch record from db
		$record = (array) BackendModel::getDB()->getRecord('SELECT * FROM locale WHERE id = ?', array((int) $id));

		// actions are urlencoded
		if($record['type'] == 'act') $record['value'] = urldecode($record['value']);

		return $record;
	}

	/**
	 * Get a locale by its name
	 *
	 * @param string $name The name of the locale.
	 * @param string $type The type of the locale.
	 * @param string $module The module wherin will be searched.
	 * @param string $language The language to use.
	 * @param string $application The application wherin will be searched.
	 * @return bool
	 */
	public static function getByName($name, $type, $module, $language, $application)
	{
		$name = (string) $name;
		$type = (string) $type;
		$module = (string) $module;
		$language = (string) $language;
		$application = (string) $application;

		return BackendModel::getDB()->getVar(
			'SELECT l.id
			 FROM locale AS l
			 WHERE name = ? AND type = ? AND module = ? AND language = ? AND application = ?',
			array($name, $type, $module, $language, $application)
		);
	}

	/**
	 * Grab labels found in the backend navigation.
	 *
	 * @return array
	 */
	private static function getLabelsFromBackendNavigation()
	{
		return (array) BackendModel::getDB()->getColumn('SELECT label FROM backend_navigation');
	}

	/**
	 * Get the languages for a multicheckbox.
	 *
	 * @param bool[optional] $includeInterfaceLanguages Should we also get the interfacelanguages?
	 * @return array
	 */
	public static function getLanguagesForMultiCheckbox($includeInterfaceLanguages = false)
	{
		// get working languages
		$aLanguages = BL::getWorkingLanguages();

		// add the interface languages if needed
		if($includeInterfaceLanguages) $aLanguages = array_merge($aLanguages, BL::getInterfaceLanguages());

		// create a new array to redefine the langauges for the multicheckbox
		$languages = array();

		// loop the languages
		foreach($aLanguages as $key => $lang)
		{
			// add to array
			$languages[$key]['value'] = $key;
			$languages[$key]['label'] = $lang;
		}

		return $languages;
	}

	/**
	 * Get the locale that is used in the backend but doesn't exists.
	 *
	 * @param string $language The language to check.
	 * @return array
	 */
	public static function getNonExistingBackendLocale($language)
	{
		$tree = self::getTree(BACKEND_PATH);
		$modules = BackendModel::getModules();

		// search fo the error module
		$key = array_search('error', $modules);

		// remove error module
		if($key !== false) unset($modules[$key]);

		$used = array();

		// get labels from navigation
		$lbl = self::getLabelsFromBackendNavigation();
		foreach((array) $lbl as $label) $used['lbl'][$label] = array('files' => array('<small>used in navigation</small>'), 'module_specific' => array());

		// get labels from table
		$lbl = (array) BackendModel::getDB()->getColumn('SELECT label FROM modules_extras');
		foreach((array) $lbl as $label) $used['lbl'][$label] = array('files' => array('<small>used in database</small>'), 'module_specific' => array());

		// loop files
		foreach($tree as $file)
		{
			// grab content
			$content = SpoonFile::getContent($file);

			// process based on extension
			switch(SpoonFile::getExtension($file))
			{
				// javascript file
				case 'js':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)(.*)(\|.*)?\}/iU', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// loop modules
							foreach($modules as $module)
							{
								// determine if this is a module specific locale
								if(substr($match, 0, mb_strlen($module)) == SpoonFilter::toCamelCase($module) && mb_strlen($match) > mb_strlen($module))
								{
									// cleanup
									$match = str_replace(SpoonFilter::toCamelCase($module), '', $match);

									// init if needed
									if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

									// add module
									$used[$type][$match]['module_specific'][] = $module;
								}
							}

							// init if needed
							if(!isset($used[$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// PHP file
				case 'php':
					$matches = array();
					$matchesURL = array();

					// get matches
					preg_match_all('/(BackendLanguage|BL)::(get(Label|Error|Message)|act|err|lbl|msg)\(\'(.*)\'(.*)?\)/iU', $content, $matches);

					// match errors
					preg_match_all('/&(amp;)?(error|report)=([A-Z0-9-_]+)/i', $content, $matchesURL);

					// any errormessages
					if(!empty($matchesURL[0]))
					{
						// loop matches
						foreach($matchesURL[3] as $key => $match)
						{
							$type = 'lbl';
							if($matchesURL[2][$key] == 'error') $type = 'Error';
							if($matchesURL[2][$key] == 'report') $type = 'Message';

							$matches[0][] = '';
							$matches[1][] = 'BL';
							$matches[2][] = '';
							$matches[3][] = $type;
							$matches[4][] = SpoonFilter::toCamelCase(SpoonFilter::toCamelCase($match, '-'), '_');
							$matches[5][] = '';
						}
					}

					// any matches?
					if(!empty($matches[4]))
					{
						// loop matches
						foreach($matches[4] as $key => $match)
						{
							// set type
							$type = 'lbl';
							if($matches[3][$key] == 'Error' || $matches[2][$key] == 'err') $type = 'err';
							if($matches[3][$key] == 'Message' || $matches[2][$key] == 'msg') $type = 'msg';

							// specific module?
							if(isset($matches[5][$key]) && $matches[5][$key] != '')
							{
								// try to grab the module
								$specificModule = $matches[5][$key];
								$specificModule = trim(str_replace(array(',', '\''), '', $specificModule));

								// not core?
								if($specificModule != 'core')
								{
									// dynamic module
									if($specificModule == '$this->URL->getModule(' || $specificModule == '$this->getModule(')
									{
										// init var
										$count = 0;

										// replace
										$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

										// validate
										if($count == 1)
										{
											// split into chunks
											$chunks = (array) explode('/', trim($modulePath, '/'));

											// set specific module
											if(isset($chunks[0])) $specificModule = $chunks[0];

											// skip
											else continue;
										}
									}

									// init if needed
									if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

									// add module
									$used[$type][$match]['module_specific'][] = $specificModule;
								}
							}

							else
							{
								// loop modules
								foreach($modules as $module)
								{
									// determine if this is a module specific locale
									if(substr($match, 0, mb_strlen($module)) == SpoonFilter::toCamelCase($module) && mb_strlen($match) > mb_strlen($module) && ctype_upper(substr($match, mb_strlen($module) + 1, 1)))
									{
										// cleanup
										$match = str_replace(SpoonFilter::toCamelCase($module), '', $match);

										// init if needed
										if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

										// add module
										$used[$type][$match]['module_specific'][] = $module;
									}
								}
							}

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// template file
				case 'tpl':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)([A-Z][a-zA-Z_]*)(\|.*)?\}/U', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// loop modules
							foreach($modules as $module)
							{
								// determine if this is a module specific locale
								if(substr($match, 0, mb_strlen($module)) == SpoonFilter::toCamelCase($module) && mb_strlen($match) > mb_strlen($module))
								{
									// cleanup
									$match = str_replace(SpoonFilter::toCamelCase($module), '', $match);

									// init if needed
									if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

									// add module
									$used[$type][$match]['module_specific'][] = $module;
								}
							}

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array(), 'module_specific' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;
			}
		}

		// init var
		$nonExisting = array();

		// check if the locale is present in the current language
		foreach($used as $type => $items)
		{
			// loop items
			foreach($items as $key => $data)
			{
				// process based on type
				switch($type)
				{
					// error
					case 'err':
						// module specific?
						if(!empty($data['module_specific']))
						{
							// loop modules
							foreach($data['module_specific'] as $module)
							{
								// if the error isn't found add it to the list
								if(substr_count(BL::err($key, $module), '{$' . $type) > 0) $nonExisting['backend' . $key . $type . $module] = array('language' => $language, 'application' => 'backend', 'module' => $module, 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}

						// not specific
						else
						{
							// if the error isn't found add it to the list
							if(substr_count(BL::err($key), '{$' . $type) > 0)
							{
								// init var
								$exists = false;

								// loop files
								foreach($data['files'] as $file)
								{
									// init var
									$count = 0;

									// replace
									$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

									// validate
									if($count == 1)
									{
										// split into chunks
										$chunks = (array) explode('/', trim($modulePath, '/'));

										// first part is the module
										if(isset($chunks[0]) && BL::err($key, $chunks[0]) != '{$' . $type . SpoonFilter::toCamelCase($chunks[0]) . $key . '}') $exists = true;
									}
								}

								// doesn't exists
								if(!$exists) $nonExisting['backend' . $key . $type . 'core'] = array('language' => $language, 'application' => 'backend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}
						break;

					// label
					case 'lbl':
						// module specific?
						if(!empty($data['module_specific']))
						{
							// loop modules
							foreach($data['module_specific'] as $module)
							{
								// if the label isn't found add it to the list
								if(substr_count(BL::lbl($key, $module), '{$' . $type) > 0) $nonExisting['backend' . $key . $type . $module] = array('language' => $language, 'application' => 'backend', 'module' => $module, 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}

						// not specific
						else
						{
							// if the label isn't found, check in the specific module
							if(substr_count(BL::lbl($key), '{$' . $type) > 0)
							{
								// init var
								$exists = false;

								// loop files
								foreach($data['files'] as $file)
								{
									// init var
									$count = 0;

									// replace
									$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

									// validate
									if($count == 1)
									{
										// split into chunks
										$chunks = (array) explode('/', trim($modulePath, '/'));

										// first part is the module
										if(isset($chunks[0]) && BL::lbl($key, $chunks[0]) != '{$' . $type . SpoonFilter::toCamelCase($chunks[0]) . $key . '}') $exists = true;
									}
								}

								// doesn't exists
								if(!$exists) $nonExisting['backend' . $key . $type . 'core'] = array('language' => $language, 'application' => 'backend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}
						break;

					// message
					case 'msg':
						// module specific?
						if(!empty($data['module_specific']))
						{
							// loop modules
							foreach($data['module_specific'] as $module)
							{
								// if the message isn't found add it to the list
								if(substr_count(BL::msg($key, $module), '{$' . $type) > 0) $nonExisting['backend' . $key . $type . $module] = array('language' => $language, 'application' => 'backend', 'module' => $module, 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}

						// not specific
						else
						{
							// if the message isn't found add it to the list
							if(substr_count(BL::msg($key), '{$' . $type) > 0)
							{
								// init var
								$exists = false;

								// loop files
								foreach($data['files'] as $file)
								{
									// init var
									$count = 0;

									// replace
									$modulePath = str_replace(BACKEND_MODULES_PATH, '', $file, $count);

									// validate
									if($count == 1)
									{
										// split into chunks
										$chunks = (array) explode('/', trim($modulePath, '/'));

										// first part is the module
										if(isset($chunks[0]) && BL::msg($key, $chunks[0]) != '{$' . $type . SpoonFilter::toCamelCase($chunks[0]) . $key . '}') $exists = true;
									}
								}

								// doesn't exists
								if(!$exists) $nonExisting['backend' . $key . $type . 'core'] = array('language' => $language, 'application' => 'backend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
							}
						}
						break;
				}
			}
		}

		ksort($nonExisting);

		// return
		return $nonExisting;
	}

	/**
	 * Get the locale that is used in the frontend but doesn't exists.
	 *
	 * @param string $language The language to check.
	 * @return array
	 */
	public static function getNonExistingFrontendLocale($language)
	{
		// get files to process
		$tree = self::getTree(FRONTEND_PATH);
		$used = array();

		// loop files
		foreach($tree as $file)
		{
			// grab content
			$content = SpoonFile::getContent($file);

			// process the file based on extension
			switch(SpoonFile::getExtension($file))
			{
				// javascript file
				case 'js':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)(.*)(\|.*)?\}/iU', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// init if needed
							if(!isset($used[$match])) $used[$type][$match] = array('files' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// PHP file
				case 'php':
					$matches = array();

					// get matches
					preg_match_all('/(FrontendLanguage|FL)::(get(Action|Label|Error|Message)|act|lbl|err|msg)\(\'(.*)\'\)/iU', $content, $matches);

					// any matches?
					if(!empty($matches[4]))
					{
						// loop matches
						foreach($matches[4] as $key => $match)
						{
							$type = 'lbl';
							if($matches[3][$key] == 'Action') $type = 'act';
							if($matches[2][$key] == 'act') $type = 'act';
							if($matches[3][$key] == 'Error') $type = 'err';
							if($matches[2][$key] == 'err') $type = 'err';
							if($matches[3][$key] == 'Message') $type = 'msg';
							if($matches[2][$key] == 'msg') $type = 'msg';

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;

				// template file
				case 'tpl':
					$matches = array();

					// get matches
					preg_match_all('/\{\$(act|err|lbl|msg)([a-z-_]*)(\|.*)?\}/iU', $content, $matches);

					// any matches?
					if(isset($matches[2]))
					{
						// loop matches
						foreach($matches[2] as $key => $match)
						{
							// set type
							$type = $matches[1][$key];

							// init if needed
							if(!isset($used[$type][$match])) $used[$type][$match] = array('files' => array());

							// add file
							if(!in_array($file, $used[$type][$match]['files'])) $used[$type][$match]['files'][] = $file;
						}
					}
					break;
			}
		}

		// init var
		$nonExisting = array();

		// set language
		FrontendLanguage::setLocale($language);

		// check if the locale is present in the current language
		foreach($used as $type => $items)
		{
			// loop items
			foreach($items as $key => $data)
			{
				// process based on type
				switch($type)
				{
					case 'act':
						// if the action isn't available add it to the list
						if(FL::act($key) == '{$' . $type . $key . '}') $nonExisting['frontend' . $key . $type] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;

					case 'err':
						// if the error isn't available add it to the list
						if(FL::err($key) == '{$' . $type . $key . '}') $nonExisting['frontend' . $key . $type] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;

					case 'lbl':
						// if the label isn't available add it to the list
						if(FL::lbl($key) == '{$' . $type . $key . '}') $nonExisting['frontend' . $key . $type] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;

					case 'msg':
						// if the message isn't available add it to the list
						if(FL::msg($key) == '{$' . $type . $key . '}') $nonExisting['frontend' . $key . $type] = array('language' => $language, 'application' => 'frontend', 'module' => 'core', 'type' => $type, 'name' => $key, 'used_in' => serialize($data['files']));
						break;
				}
			}
		}

		ksort($nonExisting);

		return $nonExisting;
	}

	/**
	 * Get the translations
	 *
	 * @param string $application The application.
	 * @param string $module The module.
	 * @param array $types The types of the translations to get.
	 * @param array $languages The languages of the translations to get.
	 * @param string $name The name.
	 * @param string $value The value.
	 * @return array
	 */
	public static function getTranslations($application, $module, $types, $languages, $name, $value)
	{
		$languages = (array) $languages;

		// create an array for the languages, surrounded by quotes (example: 'en')
		$aLanguages = array();
		foreach($languages as $key => $val) $aLanguages[$key] = '\'' . $val . '\'';

{'fraction_non_alphanumeric': 0.19268127400045176, 'fraction_numerical': 0.0028235825615541, 'mean_word_length': 2.532717116637851, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 321, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 20}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3091353', 'n_tokens_mistral': 22579, 'n_tokens_neox': 16800, 'n_words': 4214}