Index: branches/5.2.x/core/units/helpers/page_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/page_helper.php	(revision 15327)
+++ branches/5.2.x/core/units/helpers/page_helper.php	(revision 15328)
@@ -1,315 +1,275 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 defined('FULL_PATH') or die('restricted access!');
 
 class PageHelper extends kHelper {
 
 	/**
 	 * Returns page info
 	 *
 	 * @param int $page_id
 	 * @return Array
 	 */
 	function getPageInfo($page_id)
 	{
 		list ($user_id, $history_permission) = $this->getHistoryPermissionAndUser($page_id);
 
 		$where_clause = Array (
 			'pr.PageId = ' . $page_id,
 			'pr.CreatedById <> ' . $user_id,
 			'pr.IsDraft = 1',
 		);
 
 		$sql = 'SELECT CASE pr.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE u.Username END
 				FROM ' . $this->Application->getUnitOption('page-revision', 'TableName') . ' pr
 				LEFT JOIN ' . TABLE_PREFIX . 'Users u ON u.PortalUserId = pr.CreatedById
 				WHERE (' . implode(') AND (', $where_clause) . ')';
 		$users = $this->Conn->GetCol($sql);
 
 		$page_revisions = Array ();
 
 		if ( $history_permission ) {
 			$tag_params = Array ('per_page' => -1, 'skip_parent_filter' => 1, 'requery' => 1, 'page_id' => $page_id);
 
 			$revisions = $this->Application->recallObject('page-revision.list', 'page-revision_List', $tag_params);
 			/* @var $revisions kDBList */
 
 			$revisions->Query();
 			$revisions->GoFirst();
 
 			$status_options = $revisions->GetFieldOptions('Status');
 			$draft_label = $this->Application->Phrase('la_Draft', false, true);
 			$title_label = $this->Application->Phrase('la_RevisionNumber', false, true);
 			$by_label = $this->Application->Phrase('la_By', false, true);
 
 			while ( !$revisions->EOL() ) {
 				$status = $revisions->GetDBField('Status');
 				$status_label = $this->Application->Phrase($status_options['options'][$status], false, true);
 
 				$page_revisions[ 'r' . $revisions->GetDBField('RevisionNumber') ] = Array (
 					'title' => $revisions->GetDBField('IsDraft') ? $draft_label : sprintf($title_label, $revisions->GetDBField('RevisionNumber')),
 					'status' => $status,
 					'status_label' => mb_strtolower($status_label),
 					'datetime' => $revisions->GetField('CreatedOn'),
 					'author' => $by_label . ': ' . $revisions->GetField('CreatedById'),
 					'draft' => (int)$revisions->GetDBField('IsDraft'),
 				);
 
 				$revisions->GoNext();
 			}
 		}
 
 		$current_revision = $this->Application->recallObject('page-revision.current');
 		/* @var $current_revision kDBItem */
 
 		$revision_status = $current_revision->GetDBField('Status');
 		$status_options = $current_revision->GetFieldOptions('Status');
 		$status_label = $this->Application->Phrase($status_options['options'][$revision_status], false, true);
 
 		$revision_phase = $current_revision->GetDBField('IsDraft') ? 'la_title_EditingDraft' : 'la_title_ViewingRevision';
 		$revision_title = sprintf($this->Application->Phrase($revision_phase, false, true), $current_revision->GetDBField('RevisionNumber'), mb_strtolower($status_label));
 		$current_revision_info = Array ('title' => $revision_title, 'status' => $revision_status, 'saved' => '');
 
 		$autosave_time = $current_revision->GetDBField('AutoSavedOn');
 
 		if ( $autosave_time ) {
 			$phrase = $this->Application->Phrase($current_revision->GetDBField('IsDraft') ? 'la_DraftSavedAt' : 'la_SavedAt', false, true);
 			$current_revision_info['saved'] = sprintf($phrase, $current_revision->GetField('AutoSavedOn_time') . ' (' . $this->getAgoTime($autosave_time) . ')');
 		}
 
-		$currently_editing = $this->getPluralPhrase(
+		$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
+		/* @var $ml_helper kMultiLanguageHelper */
+
+		$currently_editing = $ml_helper->getPluralPhrase(
 			count($users),
 			Array (
 				'phrase1' => 'la_PageCurrentlyEditing1',
 				'phrase2' => 'la_PageCurrentlyEditing2',
 				'phrase5' => 'la_PageCurrentlyEditing5',
 			),
 			false, true
 		);
 
 		$currently_editing = sprintf($currently_editing, implode(', ', $users));
 
 		return Array ('current_revision' => $current_revision_info, 'editors' => $users, 'editors_warning' => $currently_editing, 'revisions' => $page_revisions);
 	}
 
 	/**
 	 * Returns time passed between 2 given dates in "X minutes Y seconds ago" format
 	 *
 	 * @param int $from_date
 	 * @param int $to_date
 	 * @return string
 	 */
 	function getAgoTime($from_date, $to_date = null, $max_levels = 1)
 	{
 		$blocks = Array (
 			Array ('name' => 'year', 'amount' => 60*60*24*365),
 			Array ('name' => 'month' ,'amount' => 60*60*24*31),
 			Array ('name' => 'week', 'amount' => 60*60*24*7),
 			Array ('name' => 'day', 'amount' => 60*60*24),
 			Array ('name' => 'hour', 'amount' => 60*60),
 			Array ('name' => 'minute', 'amount' => 60),
 			Array ('name' => 'second', 'amount' => 1),
 		);
 
 		if ( !isset($to_date) ) {
 			$to_date = adodb_mktime();
 		}
 
 		$diff = abs($to_date - $from_date);
 
 		if ( $diff == 0 ) {
 			return 'now';
 		}
 
 		$current_level = 1;
 		$result = Array ();
 
 		foreach ($blocks as $block) {
 			if ($current_level > $max_levels) {
 				break;
 			}
 
 			if ( $diff / $block['amount'] >= 1 ) {
 				$amount = floor($diff / $block['amount']);
 				$plural = $amount > 1 ? 's' : '';
 
 				$result[] = $amount . ' ' . $block['name'] . $plural;
 				$diff -= $amount * $block['amount'];
 				$current_level++;
 			}
 		}
 
 		return implode(' ', $result) . ' ago';
 	}
 
 	/**
 	 * Returns where clause for loading correct revision for a given page
 	 *
 	 * @param int $page_id
 	 * @param int $live_revision_number
 	 * @param string $table_name
 	 * @return string
 	 */
 	function getRevsionWhereClause($page_id, $live_revision_number, $table_name = '')
 	{
 		$revision = (int)$this->Application->GetVar('revision');
 		list ($user_id, $has_permission) = $this->getHistoryPermissionAndUser($page_id);
 
 		if ( $has_permission && $revision ) {
 			$revision_clause = $table_name . 'RevisionNumber = ' . $revision . ' AND ' . $table_name . 'IsDraft = 0';
 		}
 		else {
 			$editing_mode = $this->Application->GetVar('editing_mode'); // not in a EDITING_MODE constant, while in admin console
 			$revision_clause = $table_name . 'RevisionNumber = ' . $live_revision_number . ' AND ' . $table_name . 'IsDraft = 0';
 
 			if ( $this->Application->GetVar('preview') || $editing_mode == EDITING_MODE_CONTENT ) {
 				$revision_clause = '(' . $table_name . 'CreatedById = ' . $user_id . ' AND ' . $table_name . 'IsDraft = 1) OR (' . $revision_clause . ')';
 			}
 		}
 
 		return $revision_clause;
 	}
 
 	/**
 	 * Returns current admin user id (even, when called from front-end) and it's revision history view permission
 	 *
 	 * @param int $page_id
 	 * @return Array
 	 */
 	function getHistoryPermissionAndUser($page_id)
 	{
 		$user_id = (int)$this->Application->RecallVar($this->Application->isAdmin ? 'user_id' : 'admin_user_id');
 		$history_permission = $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0, $page_id);
 
 		return Array ($user_id, $history_permission);
 	}
 
 	/**
 	 * Creates new content block in every revision that misses it. Plus creates first page revision
 	 *
 	 * @param int $page_id
 	 * @param int $num
 	 */
 	function createNewContentBlock($page_id, $num)
 	{
 		$sql = 'SELECT pc.PageContentId, pr.RevisionId
 				FROM ' . TABLE_PREFIX . 'PageRevisions pr
 				LEFT JOIN ' . TABLE_PREFIX . 'PageContent pc ON pc.RevisionId = pr.RevisionId AND pc.ContentNum = ' . $num . '
 				WHERE pr.PageId = ' . $page_id;
 		$revisions = $this->Conn->GetCol($sql, 'RevisionId');
 
 		if ( !$revisions ) {
 			// no revisions for a page -> create a live revision
 			$revision = $this->Application->recallObject('page-revision.live', null, Array ('skip_autoload' => true));
 			/* @var $revision kDBItem */
 
 			$revision->SetDBField('PageId', $page_id);
 			$revision->SetDBField('RevisionNumber', 1);
 			$revision->SetDBField('Status', STATUS_ACTIVE);
 			$revision->Create();
 
 			$revisions[ $revision->GetID() ] = NULL;
 		}
 
 		$content_block = $this->Application->recallObject('content.new', null, Array ('skip_autoload' => true));
 		/* @var $content_block kDBItem */
 
 		$content_block->SetDBField('PageId', $page_id);
 		$content_block->SetDBField('ContentNum', $num);
 
 		foreach ($revisions as $revision_id => $content_block_id) {
 			if ( is_numeric($content_block_id) ) {
 				continue;
 			}
 
 			$content_block->SetDBField('RevisionId', $revision_id);
 			$content_block->Create();
 		}
 	}
 
 	/**
 	 * Loads content block by it's number
 	 *
 	 * @param kDBItem $content_block
 	 * @param CategoriesItem $page
 	 * @param int $num
 	 *
 	 * @return bool
 	 */
 	function loadContentBlock(&$content_block, &$page, $num)
 	{
 		$page_id = $page->GetID();
 
 		if ( !EDITING_MODE && !$this->Application->GetVar('preview') ) {
 			$revision_clause = 'pr.RevisionNumber = ' . $page->GetDBField('LiveRevisionNumber') . ' AND pr.IsDraft = 0';
 		}
 		else {
 			$revision_clause = $this->getRevsionWhereClause($page_id, $page->GetDBField('LiveRevisionNumber'), 'pr.');
 		}
 
 
 		$sql = 	$content_block->GetSelectSQL() . '
 				WHERE (' . $content_block->TableName . '.PageId = ' . $page_id . ') AND (' . $content_block->TableName . '.ContentNum = ' . $num . ') AND (' . $revision_clause . ')
 				ORDER BY pr.IsDraft DESC, pr.RevisionNumber DESC';
 		$content_data = $this->Conn->GetRow($sql);
 
 		$content_block->LoadFromHash($content_data);
 
 		return $content_block->isLoaded();
 	}
-
-	/**
-	 * Returns phrase based on given number
-	 *
-	 * @param int $number
-	 * @param Array $forms
-	 * @return string
-	 */
-	function getPluralPhrase($number, $forms, $allow_editing = true, $use_admin = false)
-	{
-		// normalize given forms
-		if ( !array_key_exists('phrase5', $forms) ) {
-			$forms['phrase5'] = $forms['phrase2'];
-		}
-
-		$phrase_type = $this->getPluralPhraseType($number);
-
-		return $this->Application->Phrase( $forms['phrase' . $phrase_type], $allow_editing, $use_admin );
-	}
-
-	/**
-	 * Returns phrase type based on given number
-	 *
-	 * @param int $number
-	 * @return int
-	 */
-	function getPluralPhraseType($number)
-	{
-		$last_digit = substr($number, -1);
-		$last_but_one_digit = strlen($number) > 1 ? substr($number, -2, 1) : false;
-		$phrase_type = '5';
-
-		if ($last_but_one_digit != 1) {
-			if ($last_digit == 1) {
-				$phrase_type = '1';
-			}
-			elseif ($last_digit >= 2 && $last_digit <= 4) {
-				$phrase_type = '2';
-			}
-		}
-
-		return $phrase_type;
-	}
 }
Index: branches/5.2.x/core/units/helpers/multilanguage_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/multilanguage_helper.php	(revision 15327)
+++ branches/5.2.x/core/units/helpers/multilanguage_helper.php	(revision 15328)
@@ -1,409 +1,456 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	/**
 	 * Performs action on multilingual fields
 	 *
 	 */
 	class kMultiLanguageHelper extends kHelper {
 
 		/**
 		 * Determines, that language info should be requeried
 		 *
 		 * @var bool
 		 * @access protected
 		 */
 		protected $initMade = false;
 
 		/**
 		 * Maximal language id
 		 *
 		 * @var int
 		 */
 		protected $languageCount = 0;
 		/**
 		 * Languages created in system
 		 *
 		 * @var Array
 		 */
 		protected $languagesIDs = Array ();
 
 		/**
 		 * Structure of table, that is currently processed
 		 *
 		 * @var Array
 		 */
 		var $curStructure = Array();
 
 		/**
 		 * Field, to get structure information from
 		 *
 		 * @var string
 		 */
 		var $curSourceField = false;
 
 		/**
 		 * Indexes used in table of 32
 		 *
 		 * @var int
 		 */
 		var $curIndexCount = 0;
 
 		/**
 		 * Fields from config, that are currently used
 		 *
 		 * @var Array
 		 */
 		var $curFields = Array();
 
 		public function resetState()
 		{
 			$this->initMade = false;
 		}
 
 		/**
 		 * Updates language count in system (always is divisible by 5)
 		 *
 		 */
 		protected function _queryLanguages()
 		{
 			if ( !$this->initMade ) {
 				$this->languagesIDs = $this->getActualLanguages();
 				$this->languageCount = max(max($this->languagesIDs), 5);
 				$this->initMade = true;
 			}
 		}
 
 		/**
 		 * Returns language ids, that can be used
 		 *
 		 * @return Array
 		 */
 		protected function getActualLanguages()
 		{
 			$cache_key = 'actual_language_ids[%LangSerial%]';
 			$ret = $this->Application->getCache($cache_key);
 
 			if ( $ret === false ) {
 				$this->Conn->nextQueryCachable = true;
 				$sql = 'SELECT ' . $this->Application->getUnitOption('lang', 'IDField') . '
 						FROM ' . $this->Application->getUnitOption('lang', 'TableName');
 				$ret = $this->Conn->GetCol($sql);
 
 				$this->Application->setCache($cache_key, $ret);
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Checks if language with specified id is created
 		 *
 		 * @param int $language_id
 		 * @return bool
 		 */
 		protected function LanguageFound($language_id)
 		{
 			return in_array($language_id, $this->languagesIDs) || $language_id <= 5;
 		}
 
 		/**
 		 * Returns list of processable languages
 		 *
 		 * @return Array
 		 */
 		public function getLanguages()
 		{
 			$cache_key = 'processable_language_ids[%LangSerial%]';
 			$ret = $this->Application->getCache($cache_key);
 
 			if ( $ret === false ) {
 				$ret = Array ();
 				$this->_queryLanguages();
 
 				for ($language_id = 1; $language_id <= $this->languageCount; $language_id++) {
 					if ( $this->LanguageFound($language_id) ) {
 						$ret[] = $language_id;
 					}
 				}
 
 				$this->Application->setCache($cache_key, $ret);
 			}
 
 			return $ret;
 		}
 
 		function scanTable($mask)
 		{
 			$i = 0;
 			$fields_found = 0;
 			$fields = array_keys($this->curStructure);
 
 			foreach ($fields as $field_name) {
 				if (preg_match($mask, $field_name)) {
 					$fields_found++;
 				}
 			}
 			return $fields_found;
 		}
 
 		function readTableStructure($table_name, $refresh = false)
 		{
 //			if ($refresh || !getArrayValue($structure_status, $prefix.'.'.$table_name)) {
 				$this->curStructure = $this->Conn->Query('DESCRIBE '.$table_name, 'Field');
 				$this->curIndexCount = count($this->Conn->Query('SHOW INDEXES FROM '.$table_name));
 //			}
 		}
 
 		/**
 		 * Creates missing multilingual fields for all unit configs, registered in system
 		 *
 		 * @param bool $reread_configs
 		 * @return void
 		 * @access public
 		 */
 		public function massCreateFields($reread_configs = true)
 		{
 			if ( $reread_configs ) {
 				$this->Application->UnitConfigReader->ReReadConfigs();
 			}
 
 			foreach ($this->Application->UnitConfigReader->configData as $prefix => $config_data) {
 				$this->createFields($prefix);
 			}
 		}
 
 		/**
 		 * Creates missing multilanguage fields in table by specified prefix
 		 *
 		 * @param string $prefix
 		 * @param bool $refresh Forces config field structure to be re-read from database
 		 * @return void
 		 */
 		function createFields($prefix, $refresh = false)
 		{
 			if ( $refresh && preg_match('/(.*)-cdata$/', $prefix, $regs) ) {
 				// call main item config to clone cdata table
 				$this->Application->UnitConfigReader->loadConfig($regs[1]);
 				$this->Application->UnitConfigReader->runAfterConfigRead($prefix);
 			}
 
 			$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 			$this->curFields = $this->Application->getUnitOption($prefix, 'Fields');
 
 			if ( !($table_name && $this->curFields) || ($table_name && !$this->Conn->TableFound($table_name, kUtil::constOn('IS_INSTALL'))) ) {
 				// invalid config found or prefix not found
 				return ;
 			}
 
 			$this->_queryLanguages();
 
 			$sqls = Array ();
 			$this->readTableStructure($table_name, $refresh);
 
 			foreach ($this->curFields as $field_name => $field_options) {
 				if ( getArrayValue($field_options, 'formatter') == 'kMultiLanguage' ) {
 					if ( isset($field_options['master_field']) ) {
 						unset($this->curFields[$field_name]);
 						continue;
 					}
 
 					$this->setSourceField($field_name);
 					if ( $this->languageCount > 0 ) {
 						// `l77_Name` VARCHAR( 255 ) NULL DEFAULT '0';
 						$field_mask = Array ();
 						$field_mask['name'] = 'l%s_' . $field_name;
 						$field_mask['null'] = getArrayValue($field_options, 'not_null') ? 'NOT NULL' : 'NULL';
 
 						if ( $this->curSourceField ) {
 							$default_value = $this->getFieldParam('Default') != 'NULL'
 									? $this->Conn->qstr($this->getFieldParam('Default'))
 									: $this->getFieldParam('Default');
 							$field_mask['type'] = $this->getFieldParam('Type');
 						}
 						else {
 							$default_value = is_null($field_options['default']) ? 'NULL'
 									: $this->Conn->qstr($field_options['default']);
 							$field_mask['type'] = $field_options['db_type'];
 						}
 
 						$field_mask['default'] = ($field_mask['null'] == 'NOT NULL' && $default_value == 'NULL') ? '' : 'DEFAULT ' . $default_value;
 
 						if ( strtoupper($field_mask['type']) == 'TEXT' ) {
 							// text fields in mysql doesn't have default value
 							$field_mask = $field_mask['name'] . ' ' . $field_mask['type'] . ' ' . $field_mask['null'];
 						}
 						else {
 							$field_mask = $field_mask['name'] . ' ' . $field_mask['type'] . ' ' . $field_mask['null'] . ' ' . $field_mask['default'];
 						}
 
 						$alter_sqls = $this->generateAlterSQL($field_mask, 1, $this->languageCount);
 						if ( $alter_sqls ) {
 							$sqls[] = 'ALTER TABLE ' . $table_name . ' ' . $alter_sqls;
 						}
 					}
 				}
 			}
 
 			foreach ($sqls as $sql_query) {
 				$this->Conn->Query($sql_query);
 			}
 		}
 
 		/**
 		 * Creates missing multilanguage fields in table by specified prefix
 		 *
 		 * @param string $prefix
 		 * @param int $src_language
 		 * @param int $dst_language
 		 * @return void
 		 * @access public
 		 */
 		public function copyMissingData($prefix, $src_language, $dst_language)
 		{
 			$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 			$this->curFields = $this->Application->getUnitOption($prefix, 'Fields');
 
 			if ( !($table_name && $this->curFields) || ($table_name && !$this->Conn->TableFound($table_name, kUtil::constOn('IS_INSTALL'))) ) {
 				// invalid config found or prefix not found
 				return ;
 			}
 
 			foreach ($this->curFields as $field_name => $field_options) {
 				$formatter = isset($field_options['formatter']) ? $field_options['formatter'] : '';
 
 				if ( ($formatter == 'kMultiLanguage') && !isset($field_options['master_field']) ) {
 					$sql = 'UPDATE ' . $table_name . '
 							SET l' . $dst_language . '_' . $field_name . ' = l' . $src_language . '_' . $field_name . '
 							WHERE l' . $dst_language . '_' . $field_name . ' = "" OR l' . $dst_language . '_' . $field_name . ' IS NULL';
 					$this->Conn->Query($sql);
 				}
 			}
 		}
 
 		function deleteField($prefix, $custom_id)
 		{
 			$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 			$sql = 'DESCRIBE '.$table_name.' "l%_cust_'.$custom_id.'"';
 			$fields = $this->Conn->GetCol($sql);
 
 			$sql = 'ALTER TABLE '.$table_name.' ';
 			$sql_template = 'DROP COLUMN %s, ';
 
 			foreach ($fields as $field_name) {
 				$sql .= sprintf($sql_template, $field_name);
 			}
 
 			$this->Conn->Query( substr($sql, 0, -2) );
 		}
 
 		/**
 		 * Returns parameter requested of current source field
 		 *
 		 * @param string $param_name
 		 * @return string
 		 */
 		function getFieldParam($param_name)
 		{
 			return $this->curStructure[$this->curSourceField][$param_name];
 		}
 
 		/**
 		 * Detects field name to create other fields from
 		 *
 		 * @param string $field_name
 		 */
 		function setSourceField($field_name)
 		{
 			$ret = $this->scanTable('/^l[\d]+_'.preg_quote($field_name, '/').'$/');
 			if (!$ret) {
 				// no multilingual fields at all (but we have such field without language prefix)
 				$original_found = $this->scanTable('/^'.preg_quote($field_name, '$/').'/');
 				$this->curSourceField = $original_found ? $field_name : false;
 			}
 			else {
 				$this->curSourceField = 'l1_'.$field_name;
 			}
 		}
 
 		/**
 		 * Returns ALTER statement part for adding required fields to table
 		 *
 		 * @param string $field_mask sql mask for creating field with correct definition (type & size)
 		 * @param int $start_index add new fields starting from this index
 		 * @param int $create_count create this much new multilingual field translations
 		 * @return string
 		 */
 		function generateAlterSQL($field_mask, $start_index, $create_count)
 		{
 			static $single_lang = null;
 			if (!isset($single_lang)) {
 				// if single language mode, then create indexes only on primary columns
 				$table_name = $this->Application->getUnitOption('lang', 'TableName');
 				$sql = 'SELECT COUNT(*)
 						FROM '.$table_name.'
 						WHERE Enabled = 1';
 				// if language count = 0, then assume it's multi language mode
 				$single_lang = $this->Conn->GetOne($sql) == 1;
 			}
 
 			$ret = '';
 			$ml_field = preg_replace('/l(.*?)_(.*?) (.*)/', '\\2', $field_mask);
 
 			$i_count = $start_index + $create_count;
 			while ($start_index < $i_count) {
 
 				if (isset($this->curStructure['l'.$start_index.'_'.$ml_field]) || (!$this->LanguageFound($start_index)) ) {
 					$start_index++;
 					continue;
 				}
 
 				$prev_index = $start_index - 1;
 				do {
 					list($prev_field,$type) = explode(' ', sprintf($field_mask, $prev_index) );
 				} while ($prev_index > 0 && !$this->LanguageFound($prev_index--));
 
 				if (substr($prev_field, 0, 3) == 'l0_') {
 					$prev_field = substr($prev_field, 3, strlen($prev_field));
 					if (!$this->curSourceField) {
 						// get field name before this one
 						$fields = array_keys($this->curFields);
 //						$prev_field = key(end($this->curStructure));
 						$prev_field = $fields[array_search($prev_field, $fields) - 1];
 						if (getArrayValue($this->curFields[$prev_field], 'formatter') == 'kMultiLanguage') {
 							$prev_field = 'l'.$this->languageCount.'_'.$prev_field;
 						}
 					}
 				}
 
 				$field_expression = sprintf($field_mask, $start_index);
 				$ret .= 'ADD COLUMN '.$field_expression.' AFTER `'.$prev_field.'`, ';
 
 				if ($this->curIndexCount < 32 && ($start_index == $this->Application->GetDefaultLanguageId() || !$single_lang)) {
 					// create index for primary language column + for all others (if multiple languages installed)
 					list($field_name, $field_params) = explode(' ', $field_expression, 2);
 
 					$index_type = isset($this->curFields[$ml_field]['index_type']) ? $this->curFields[$prev_field]['index_type'] : 'string';
 
 					$ret .= $index_type == 'string' ? 'ADD INDEX (`'.$field_name.'` (5) ), ' : 'ADD INDEX (`'.$field_name.'`), ';
 					$this->curIndexCount++;
 				}
 
 				$start_index++;
 			}
 			return preg_replace('/, $/', ';', $ret);
 		}
+
+		/**
+		 * Returns phrase based on given number
+		 *
+		 * @param int $number
+		 * @param Array $forms
+		 * @param bool $allow_editing
+		 * @param bool $use_admin
+		 * @return string
+		 * @access public
+		 */
+		public function getPluralPhrase($number, $forms, $allow_editing = true, $use_admin = false)
+		{
+			// normalize given forms
+			if ( !array_key_exists('phrase5', $forms) ) {
+				$forms['phrase5'] = $forms['phrase2'];
+			}
+
+			$phrase_type = $this->getPluralPhraseType($number);
+
+			return $this->Application->Phrase($forms['phrase' . $phrase_type], $allow_editing, $use_admin);
+		}
+
+		/**
+		 * Returns phrase type based on given number
+		 *
+		 * @param int $number
+		 * @return int
+		 * @access protected
+		 */
+		protected function getPluralPhraseType($number)
+		{
+			$last_digit = substr($number, -1);
+			$last_but_one_digit = strlen($number) > 1 ? substr($number, -2, 1) : false;
+			$phrase_type = '5';
+
+			if ( $last_but_one_digit != 1 ) {
+				if ( $last_digit == 1 ) {
+					$phrase_type = '1';
+				}
+				elseif ( $last_digit >= 2 && $last_digit <= 4 ) {
+					$phrase_type = '2';
+				}
+			}
+
+			return $phrase_type;
+		}
 	}