Implemented i18n support
authorAlexander Ebert <ebert@woltlab.com>
Mon, 26 Sep 2011 18:07:33 +0000 (20:07 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 26 Sep 2011 18:07:33 +0000 (20:07 +0200)
wcfsetup/install/files/acp/templates/userGroupAdd.tpl
wcfsetup/install/files/acp/templates/userGroupList.tpl
wcfsetup/install/files/js/WCF.js
wcfsetup/install/files/lib/acp/form/UserGroupAddForm.class.php
wcfsetup/install/files/lib/acp/form/UserGroupEditForm.class.php
wcfsetup/install/files/lib/system/language/I18nHandler.class.php [new file with mode: 0644]

index e113fcfe75798fdf149571ac2ed4f82f3e55dd82..6be83ef450e7b33f75a7c620f18562fb59f753e1 100644 (file)
@@ -4,6 +4,10 @@
        //<![CDATA[
        $(function() {
                WCF.TabMenu.init();
+
+               var $availableLanguages = { {implode from=$availableLanguages key=languageID item=languageName}{@$languageID}: '{$languageName}'{/implode} };
+               var $groupNameValues = { {implode from=$groupName_i18n key=languageID item=value}'{@$languageID}': '{$value}'{/implode} };
+               new WCF.MultipleLanguageInput('groupName', false, $groupNameValues, $availableLanguages);
        });
        //]]>
 </script>
index 4b568098cec6b97a5fcbac4aeff0d4b5121d4a12..8533495ec77befa354bea279f6a003fc3ddabab8 100644 (file)
@@ -52,7 +52,7 @@
                                                        {if $additionalButtons[$group->groupID]|isset}{@$additionalButtons[$group->groupID]}{/if}
                                                </td>
                                                <td class="columnID columnGroupID"><p>{@$group->groupID}</p></td>
-                                               <td class="columnTitle columnGroupName">{if $group->isEditable()}<p><a title="{lang}wcf.acp.group.edit{/lang}" href="index.php?form=UserGroupEdit&amp;groupID={@$group->groupID}{@SID_ARG_2ND}">{$group->groupName}</a>{else}{$group->groupName}</p>{/if}</td>
+                                               <td class="columnTitle columnGroupName">{if $group->isEditable()}<p><a title="{lang}wcf.acp.group.edit{/lang}" href="index.php?form=UserGroupEdit&amp;groupID={@$group->groupID}{@SID_ARG_2ND}">{lang}{$group->groupName}{/lang}</a>{else}{lang}{$group->groupName}{/lang}</p>{/if}</td>
                                                <td class="columnDigits columnMembers"><p><a title="{lang}wcf.acp.group.showMembers{/lang}" href="index.php?form=UserSearch&amp;groupID={@$group->groupID}{@SID_ARG_2ND}">{#$group->members}</p></a></td>
                                                
                                                {if $additionalColumns[$group->groupID]|isset}{@$additionalColumns[$group->groupID]}{/if}
index cdeab028c4c6f32c2546472568515b8a4b5517cb..852c8743195f85d2e1a2a9a4dba9b0a8e21fd882 100644 (file)
@@ -34,6 +34,24 @@ $.extend(true, {
         */
        wcfIsset: function(id) {
                return !!$('#' + $.wcfEscapeID(id)).length;
+       },
+
+       /**
+        * Returns the length of an object.
+        * 
+        * @param       object          targetObject
+        * @return      integer
+        */
+       getLength: function(targetObject) {
+               var $length = 0;
+
+               for (var $key in targetObject) {
+                       if (targetObject.hasOwnProperty($key)) {
+                               $length++;
+                       }
+               }
+
+               return $length;
        }
 });
 
@@ -1532,6 +1550,277 @@ WCF.Language = {
        }
 };
 
+/**
+ * Handles multiple language input fields.
+ * 
+ * @param      string          elementID
+ * @param      boolean         forceSelection
+ * @param      object          values
+ * @param      object          availableLanguages
+ */
+WCF.MultipleLanguageInput = function(elementID, forceSelection, values, availableLanguages) { this.init(elementID, forceSelection, values, availableLanguages); };
+WCF.MultipleLanguageInput.prototype = {
+       /**
+        * list of available languages
+        * @var object
+        */
+       _availableLanguages: {},
+
+       /**
+        * initialization state
+        * @var boolean
+        */
+       _didInit: false,
+
+       /**
+        * target input element
+        * @var jQuery
+        */
+       _element: null,
+
+       /**
+        * enables multiple language ability
+        * @var boolean
+        */
+       _isEnabled: false,
+
+       /**
+        * enforce multiple language ability
+        * @var boolean
+        */
+       _forceSelection: false,
+
+       /**
+        * currently active language id
+        * @var integer
+        */
+       _languageID: 0,
+
+       /**
+        * language selection list
+        * @var jQuery
+        */
+       _list: null,
+
+       /**
+        * list of language values on init
+        * @var object
+        */
+       _values: null,
+
+       /**
+        * Initializes multiple language ability for given element id.
+        * 
+        * @param       integer         elementID
+        * @param       boolean         forceSelection
+        * @param       boolean         isEnabled
+        * @param       object          values
+        * @param       object          availableLanguages
+        */
+       init: function(elementID, forceSelection, values, availableLanguages) {
+               this._element = $('#' + $.wcfEscapeID(elementID));
+               this._forceSelection = forceSelection;
+               this._values = values;
+               this._availableLanguages = availableLanguages;
+               
+               // default to current user language
+               this._languageID = LANGUAGE_ID;
+               if (this._element.length == 0) {
+                       console.debug("[WCF.MultipleLanguageInput] element id '" + elementID + "' is unknown");
+                       return;
+               }
+               
+               // build selection handler
+               var $enableOnInit = ($.getLength(this._values) > 0) ? true : false;
+               this._prepareElement($enableOnInit);
+               
+               // listen for submit event
+               this._element.parents('form').submit($.proxy(this._submit, this));
+
+               this._didInit = true;
+       },
+
+       /**
+        * Builds language handler.
+        * 
+        * @param       boolean         enableOnInit
+        */
+       _prepareElement: function(enableOnInit) {
+               this._element.wrap('<div class="preInput" />');
+               var $wrapper = this._element.parent();
+               var $button = $('<p class="dropdownCaption"><span>enable i18n</span></p>').prependTo($wrapper);
+               var $span = $button.children('span');
+
+               $span.click($.proxy(this._enable, this));
+               WCF.CloseOverlayHandler.addCallback(this._element.wcfIdentify(), $.proxy(this._closeSelection, this));
+               
+               if (enableOnInit) {
+                       $span.trigger('click');
+
+                       // pre-select current language
+                       this._list.children('li').each($.proxy(function(index, listItem) {
+                               var $listItem = $(listItem);
+                               if ($listItem.data('languageID') == this._languageID) {
+                                       $listItem.trigger('click');
+                               }
+                       }, this));
+               }
+       },
+
+       /**
+        * Enables the language selection or shows the selection if already enabled.
+        * 
+        * @param       object          event
+        */
+       _enable: function(event) {
+               if (!this._isEnabled) {
+                       var $button = $(event.target);
+                       $button.addClass('active');
+
+                       // insert list
+                       if (this._list === null) {
+                               this._list = $('<ul class="dropdown"></ul>').insertAfter($button.parent());
+                               this._list.click(function(event) {
+                                       // discard click event
+                                       event.stopPropagation();
+                               });
+                               
+                               // insert available languages
+                               for (var $languageID in this._availableLanguages) {
+                                       $('<li>' + this._availableLanguages[$languageID] + '</li>').data('languageID', $languageID).click($.proxy(this._changeLanguage, this)).appendTo(this._list);
+                               }
+
+                               // disable language input
+                               $('<li class="divider">disable i18n</li>').click($.proxy(this._disable, this)).appendTo(this._list);
+                       }
+
+                       this._isEnabled = true;
+               }
+
+               // toggle list
+               if (this._list.is(':visible')) {
+                       this._closeSelection();
+               }
+               else {
+                       this._showSelection();
+               }
+
+               // discard event
+               event.stopPropagation();
+       },
+
+       /**
+        * Shows the language selection.
+        */
+       _showSelection: function() {
+               if (this._isEnabled) {
+                       // display status for each language
+                       this._list.children('li').each($.proxy(function(index, listItem) {
+                               var $listItem = $(listItem);
+                               var $languageID = $listItem.data('languageID');
+
+                               if ($languageID) {
+                                       if (this._values[$languageID] && this._values[$languageID] != '') {
+                                               $listItem.removeClass('missingValue');
+                                       }
+                                       else {
+                                               $listItem.addClass('missingValue');
+                                       }
+                               }
+                       }, this));
+
+                       // show list
+                       this._list.show();
+               }
+       },
+
+       /**
+        * Closes the language selection.
+        */
+       _closeSelection: function() {
+               this._list.hide();
+       },
+
+       /**
+        * Changes the currently active language.
+        * 
+        * @param       object          event
+        */
+       _changeLanguage: function(event) {
+               var $button = $(event.target);
+
+               // save current value
+               if (this._didInit) {
+                       this._values[this._languageID] = this._element.val();
+               }
+
+               // set new language
+               this._languageID = $button.data('languageID');
+               if (this._values[this._languageID]) {
+                       this._element.val(this._values[this._languageID]);
+               }
+               else {
+                       this._element.val('');
+               }
+
+               // update marking
+               this._list.children('li').removeClass('active');
+               $button.addClass('active');
+
+               // update label
+               this._list.prev('.dropdownCaption').children('span').text(this._availableLanguages[this._languageID]);
+
+               // close selection and set focus on input element
+               this._closeSelection();
+               this._element.focus();
+       },
+
+       /**
+        * Disables language selection for current element.
+        */
+       _disable: function() {
+               // remove active marking
+               this._list.prev('.dropdownCaption').children('span').removeClass('active').text('enable i18n');
+               this._closeSelection();
+
+               // update element value
+               if (this._values[LANGUAGE_ID]) {
+                       this._element.val(this._values[LANGUAGE_ID]);
+               }
+               else {
+                       // no value for current language found, proceed with empty input
+                       this._element.val();
+               }
+
+               this._isEnabled = false;
+       },
+
+       /**
+        * Prepares language variables on before submit.
+        */
+       _submit: function() {
+               // insert hidden form elements on before submit
+               if (!this._isEnabled) {
+                       return 0xDEADBEAF;
+               }
+
+               // fetch active value
+               if (this._languageID) {
+                       this._values[this._languageID] = this._element.val();
+               }
+
+               var $form = $(this._element.parents('form')[0]);
+               var $elementID = this._element.wcfIdentify();
+
+               for (var $languageID in this._values) {
+                       $('<input type="hidden" name="' + $elementID + '_i18n[' + $languageID + ']" value="' + this._values[$languageID] + '" />').appendTo($form);
+               }
+
+               // remove name attribute to prevent conflict with i18n values
+               this._element.removeAttr('name');
+       }
+};
+
 /**
  * Icon collection used across all JavaScript classes.
  * 
index 650565b9bd2a19d9925a0ecb03806cb9be2a333d..8d4eeaba7cb1377a6a80127fd9335b5101bc4d9d 100755 (executable)
@@ -3,8 +3,10 @@ namespace wcf\acp\form;
 use wcf\system\menu\acp\ACPMenu;
 use wcf\data\user\group\UserGroup;
 use wcf\data\user\group\UserGroupAction;
+use wcf\data\user\group\UserGroupEditor;
 use wcf\system\exception\UserInputException;
 use wcf\system\exception\SystemException;
+use wcf\system\language\I18nHandler;
 use wcf\system\WCF;
 use wcf\system\WCFACP;
 use wcf\util\ClassUtil;
@@ -72,13 +74,21 @@ class UserGroupAddForm extends AbstractOptionListForm {
         */
        public $additionalFields = array();
        
+       public function readParameters() {
+               parent::readParameters();
+               
+               I18nHandler::getInstance()->register('groupName');
+       }
+       
        /**
         * @see wcf\form\IForm::readFormParameters()
         */
        public function readFormParameters() {
                parent::readFormParameters();
                
-               if (isset($_POST['groupName'])) $this->groupName = StringUtil::trim($_POST['groupName']);
+               I18nHandler::getInstance()->readValues();
+               
+               if (I18nHandler::getInstance()->isPlainValue('groupName')) $this->groupName = I18nHandler::getInstance()->getValue('groupName');
                if (isset($_POST['activeTabMenuItem'])) $this->activeTabMenuItem = $_POST['activeTabMenuItem'];
                if (isset($_POST['activeSubTabMenuItem'])) $this->activeSubTabMenuItem = $_POST['activeSubTabMenuItem'];
        }
@@ -92,7 +102,7 @@ class UserGroupAddForm extends AbstractOptionListForm {
                
                // validate group name
                try {
-                       if (empty($this->groupName)) {
+                       if (!I18nHandler::getInstance()->validateValue('groupName')) {
                                throw new UserInputException('groupName');
                        }
                }
@@ -126,6 +136,19 @@ class UserGroupAddForm extends AbstractOptionListForm {
                );
                $groupAction = new UserGroupAction(array(), 'create', $data);
                $groupAction->executeAction();
+               
+               if (!I18nHandler::getInstance()->isPlainValue('groupName')) {
+                       $returnValues = $groupAction->getReturnValues();
+                       $groupID = $returnValues['returnValues']->groupID;
+                       I18nHandler::getInstance()->save('groupName', 'wcf.acp.group.group'.$groupID, 'wcf.acp.group', 1);
+                       
+                       // update group name
+                       $groupEditor = new UserGroupEditor($returnValues['returnValues']);
+                       $groupEditor->update(array(
+                               'groupName' => 'wcf.acp.group.group'.$groupID
+                       ));
+               }
+               
                $this->saved();
                
                // show success message
@@ -156,6 +179,8 @@ class UserGroupAddForm extends AbstractOptionListForm {
        public function assignVariables() {
                parent::assignVariables();
                
+               I18nHandler::getInstance()->assignVariables(1);
+               
                WCF::getTPL()->assign(array(
                        'groupName' => $this->groupName,
                        'optionTree' => $this->optionTree,
index dcf95a7cabb355961c1cd03183dda01cd725c56a..d60fe5257851781e2221811d17e1462b8bc4356d 100755 (executable)
@@ -7,6 +7,7 @@ use wcf\data\user\group\UserGroupEditor;
 use wcf\form\AbstractForm;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\PermissionDeniedException;
+use wcf\system\language\I18nHandler;
 use wcf\system\WCF;
 
 /**
@@ -66,6 +67,7 @@ class UserGroupEditForm extends UserGroupAddForm {
         */
        public function readData() {
                if (!count($_POST)) {
+                       I18nHandler::getInstance()->setOptions('groupName',  $this->group->groupName, 'wcf.acp.group.group\d+');
                        $this->groupName = $this->group->groupName;
                        
                        // get default values
@@ -96,6 +98,9 @@ class UserGroupEditForm extends UserGroupAddForm {
        public function assignVariables() {
                parent::assignVariables();
                
+               $useRequestData = (count($_POST)) ? true : false;
+               I18nHandler::getInstance()->assignVariables(1, $useRequestData);
+               
                WCF::getTPL()->assign(array(
                        'groupID' => $this->group->groupID,
                        'action' => 'edit'
@@ -129,6 +134,16 @@ class UserGroupEditForm extends UserGroupAddForm {
                                }
                        }
                }
+               
+               $this->groupName = 'wcf.acp.group.group'.$this->group->groupID;
+               if (I18nHandler::getInstance()->isPlainValue('groupName')) {
+                       I18nHandler::getInstance()->remove($this->groupName, 1);
+                       $this->groupName = I18nHandler::getInstance()->getValue('groupName');
+               }
+               else {
+                       I18nHandler::getInstance()->save('groupName', $this->groupName, 'wcf.acp.group', 1);
+               }
+               
                $data = array(
                        'data' => array_merge(array('groupName' => $this->groupName), $this->additionalFields),
                        'options' => $saveOptions
diff --git a/wcfsetup/install/files/lib/system/language/I18nHandler.class.php b/wcfsetup/install/files/lib/system/language/I18nHandler.class.php
new file mode 100644 (file)
index 0000000..7b2ce6b
--- /dev/null
@@ -0,0 +1,311 @@
+<?php
+namespace wcf\system\language;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\exception\SystemException;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Provides internationalization support for input fields.
+ *
+ * @author     Alexander Ebert
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.language
+ * @category   Community Framework
+ */
+class I18nHandler extends SingletonFactory {
+       /**
+        * list of element ids
+        * @var array<string>
+        */
+       protected $elementIDs = array();
+       
+       /**
+        * list of plain values for elements
+        * @var array<string>
+        */
+       protected $plainValues = array();
+       
+       /**
+        * i18n values for elements
+        * @var array<array>
+        */
+       protected $i18nValues = array();
+       
+       /**
+        * element options
+        * @var array<array>
+        */
+       protected $elementOptions = array();
+       
+       /**
+        * Registers a new element id, returns false if element id is already set.
+        * 
+        * @param       string          elementID
+        * @return      boolean
+        */
+       public function register($elementID) {
+               if (in_array($elementID, $this->elementIDs)) {
+                       return false;
+               }
+               
+               $this->elementIDs[] = $elementID;
+               return true;
+       }
+       
+       /**
+        * Reads plain and i18n values from request data.
+        */
+       public function readValues() {
+               foreach ($this->elementIDs as $elementID) {
+                       if (isset($_POST[$elementID])) {
+                               $this->plainValues[$elementID] = $_POST[$elementID];
+                               continue;
+                       }
+                       
+                       $i18nElementID = $elementID . '_i18n';
+                       if (isset($_POST[$i18nElementID]) && is_array($_POST[$i18nElementID])) {
+                               $this->i18nValues[$elementID] = array();
+                               
+                               foreach ($_POST[$i18nElementID] as $languageID => $value) {
+                                       $this->i18nValues[$elementID][$languageID] = $value;
+                               }
+                               
+                               continue;
+                       }
+                       
+                       throw new SystemException("Missing expected value for element id '".$elementID."'");
+               }
+       }
+       
+       /**
+        * Returns true, if given element has disabled i18n functionality.
+        * 
+        * @param       string          elementID
+        * @return      boolean
+        */
+       public function isPlainValue($elementID) {
+               if (isset($this->plainValues[$elementID])) {
+                       return true;
+               }
+               
+               return false;
+       }
+       
+       /**
+        * Returns true, if given element has enabled i18n functionality.
+        */
+       public function hasI18nValues($elementID) {
+               if (isset($this->i18nValues[$elementID])) {
+                       return true;
+               }
+               
+               return false;
+       }
+       
+       /**
+        * Returns plain value for given element.
+        * 
+        * @param       string          elementID
+        * @return      string
+        * @see         wcf\system\language\I18nHandler::isPlainValue()
+        */
+       public function getValue($elementID) {
+               return $this->plainValues[$elementID];
+       }
+       
+       /**
+        * Returns false, if element value is not empty.
+        * 
+        * @param       string          $elementID
+        * @return      boolean
+        */
+       public function validateValue($elementID) {
+               if ($this->isPlainValue($elementID)) {
+                       if ($this->getValue($elementID) == '') {
+                               return false;
+                       }
+               }
+               else {
+                       foreach ($this->i18nValues[$elementID] as $value) {
+                               if (empty($value)) {
+                                       return false;
+                               }
+                       }
+               }
+               
+               return true;
+       }
+       
+       /**
+        * Saves language variable for i18n. Given package id must match the associated
+        * packages, using PACKAGE_ID is highly discouraged as this breaks the ability
+        * to delete unused language items on package uninstallation using foreign keys.
+        * 
+        * @param       string          $elementID
+        * @param       string          $languageVariable
+        * @param       string          $languageCategory
+        * @param       integer         $packageID
+        */
+       public function save($elementID, $languageVariable, $languageCategory, $packageID) {
+               // get language category id
+               $sql = "SELECT  languageCategoryID
+                       FROM    wcf".WCF_N."_language_category
+                       WHERE   languageCategory = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute(array($languageCategory));
+               $row = $statement->fetchArray();
+               $languageCategoryID = $row['languageCategoryID'];
+               
+               $languageIDs = array_keys($this->i18nValues[$elementID]);
+               
+               $conditions = new PreparedStatementConditionBuilder();
+               $conditions->add("languageID IN (?)", array($languageIDs));
+               $conditions->add("languageItem = ?", array($languageVariable));
+               $conditions->add("packageID = ?", array($packageID));
+               
+               $sql = "SELECT  languageItemID, languageID
+                       FROM    wcf".WCF_N."_language_item
+                       ".$conditions;
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute($conditions->getParameters());
+               $languageItemIDs = array();
+               while ($row = $statement->fetchArray()) {
+                       $languageItemIDs[$row['languageID']] = $row['languageItemID'];
+               }
+               
+               $insertLanguageIDs = $updateLanguageIDs = array();
+               foreach ($languageIDs as $languageID) {
+                       if (isset($languageItemIDs[$languageID])) {
+                               $updateLanguageIDs[] = $languageID;
+                       }
+                       else {
+                               $insertLanguageIDs[] = $languageID;
+                       }
+               }
+               
+               // insert language items
+               if (count($insertLanguageIDs)) {
+                       $sql = "INSERT INTO     wcf".WCF_N."_language_item
+                                               (languageID, languageItem, languageItemValue, languageCategoryID, packageID)
+                               VALUES          (?, ?, ?, ?, ?)";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       
+                       foreach ($insertLanguageIDs as $languageID) {
+                               $statement->execute(array(
+                                       $languageID,
+                                       $languageVariable,
+                                       $this->i18nValues[$elementID][$languageID],
+                                       $languageCategoryID,
+                                       $packageID
+                               ));
+                       }
+               }
+               
+               // update language items
+               if (count($updateLanguageIDs)) {
+                       $sql = "UPDATE  wcf".WCF_N."_language_item
+                               SET     languageItemValue = ?
+                               WHERE   languageItemID = ?";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       
+                       foreach ($updateLanguageIDs as $languageID) {
+                               $statement->execute(array(
+                                       $this->i18nValues[$elementID][$languageID],
+                                       $languageItemIDs[$languageID]
+                               ));
+                       }
+               }
+       }
+       
+       /**
+        * Removes previously created i18n language variables.
+        * 
+        * @param       string          $languageVariable
+        * @param       integer         $packageID
+        */
+       public function remove($languageVariable, $packageID) {
+               $sql = "DELETE FROM     wcf".WCF_N."_language_item
+                       WHERE           languageItem = ?
+                                       AND packageID = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute(array(
+                       $languageVariable,
+                       $packageID
+               ));
+       }
+       
+       /**
+        * Sets additional options for elements, required if updating values.
+        * 
+        * @param       integer         $elementID
+        * @param       string          $value
+        * @param       string          $pattern
+        */
+       public function setOptions($elementID, $value, $pattern) {
+               $this->elementOptions[$elementID] = array(
+                       'pattern' => $pattern,
+                       'value' => $value
+               );
+       }
+       
+       /**
+        * Assigns element values to template. Using request data once reading
+        * initial database data is explicitly disallowed. Restrictions on package
+        * id apply as explained in save().
+        * 
+        * @param       integer         $packageID
+        * @param       boolean         $useRequestData
+        */
+       public function assignVariables($packageID, $useRequestData = true) {
+               foreach ($this->elementIDs as $elementID) {
+                       $elementValue = '';
+                       $elementI18nValues = array();
+                       
+                       // use POST values instead of querying database
+                       if ($useRequestData) {
+                               if ($this->isPlainValue($elementID)) {
+                                       $elementValue = $this->getValue($elementID);
+                               }
+                               else {
+                                       if ($this->hasI18nValues($elementID)) {
+                                               $elementI18nValues = $this->i18nValues[$elementID];
+                                       }
+                                       else {
+                                               $elementI18nValues = array();
+                                       }
+                               }
+                       }
+                       else {
+                               if (preg_match('~^'.$this->elementOptions[$elementID]['pattern'].'$~', $this->elementOptions[$elementID]['value'])) {
+                                       // use i18n values from language items
+                                       $sql = "SELECT  languageID, languageItemValue
+                                               FROM    wcf".WCF_N."_language_item
+                                               WHERE   languageItem = ?
+                                                       AND packageID = ?";
+                                       $statement = WCF::getDB()->prepareStatement($sql);
+                                       $statement->execute(array(
+                                               $this->elementOptions[$elementID]['value'],
+                                               $packageID
+                                               ));
+                                       while ($row = $statement->fetchArray()) {
+                                               $elementI18nValues[$row['languageID']] = $row['languageItemValue'];
+                                       }
+                                       
+                               }
+                               else {
+                                       // use data provided by setOptions()
+                                       $elementValue = $this->elementOptions[$elementID]['value'];
+                               }
+                       }
+                       
+                       WCF::getTPL()->assign(array(
+                               'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
+                               $elementID => $elementValue,
+                               $elementID.'_i18n' => $elementI18nValues
+                       ));
+               }
+       }
+}