Mehrsprachigkeit - Multilinguale Sprachverknüpfungen
Mit Joomla ist es möglich, eine mehrsprachige Website einzurichten, ohne Erweiterungen von Dritten zu installieren. In diesem Tutorial zeige ich dir, wie du deine Komponente so programmierst, dass sie Joomla-Sprachverknüpfungen unterstützt.
Mehrsprachigkeit und Sprachverknüpfungen: Mehrsprachige Inhalte, Menüpunkte und Sprachumschalter werden mit einer Standard Joomla Installation ohne zusätzliche Erweiterungen eingerichtet. Bis zur Version 3.7 war es in Joomla erforderlich, zwischen Ansichten zu wechseln, um Inhalte zu übersetzen. Seit 3.7 gibt es eine Verbesserung der Usability, die sogenannten Sprachverknüpfungen. Mit dieser Erweiterung lassen sich mehrsprachige Inhalte benutzerfreundlich erstellen und verknüpfen. Dabei bleibt man in einer Ansicht. Die Sprachverknüpfungen zeigen nebenbei, welche mehrsprachigen Inhalte fehlen.
Das Kapitel ist eines der umfangreichsten in dieser Serie. Dafür deckt es alle Bereiche der Mehrsprachigkeit und der Sprachverknüpfungen in Joomla ab.
Für Ungeduldige: Sieh dir den geänderten Programmcode in der Diff-Ansicht[^codeberg.org/astrid/j4examplecode/compare/t14b…t15a] an und übernimm diese Änderungen in deine Entwicklungsversion.
Schritt für Schritt
Neue Dateien
Damit die Sprache zum Element gespeichert wird, fügen wir eine Spalte zur Datenbanktabelle hinzu. Bei einer Aktualisierung der Komponente ist das Skript 15.0.0.sql
dasjenige, welches für Version 15.0.0. ausgeführt wird.
administrator/components/com_foos/sql/updates/mysql/15.0.0.sql
administrator/components/com_foos/sql/updates/mysql/15.0.0.sql
<!-- https://codeberg.org/astrid/j4examplecode/raw/branch/t15a/src/administrator/components/com_foos/sql/updates/mysql/15.0.0.sql -->
ALTER TABLE `#__foos_details` ADD COLUMN `language` char(7) NOT NULL DEFAULT '*' AFTER `alias`;
ALTER TABLE `#__foos_details` ADD KEY `idx_language` (`language`);
administrator/components/ com_foos/src/Helper/AssociationsHelper.php
Die Hilfsdatei AssociationsHelper.php
ist die Schnittstelle zur Komponente Sprachverknüpfungen com_associations
. AssociationsHelper.php
gibt es im Frontend und im Backend - letztere sehen wir uns als erstes an, die Frontend-Version kommt später in diesem Kapitel an die Reihe.
In dieser Helper-Datei stellen wir die Angaben bereit, die für unsere Komponente spezifisch sind, so dass die Joomla eigenen Routinen sich in unserer Komponente zurecht finden.
administrator/components/ com_foos/src/Helper/AssociationsHelper.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t15a/src/administrator/components/com_foos/src/Helper/AssociationsHelper.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Administrator\Helper;
\defined('_JEXEC') or die;
use Joomla\CMS\Association\AssociationExtensionHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Table\Table;
use FooNamespace\Component\Foos\Site\Helper\AssociationHelper;
/**
* Foo associations helper.
*
* @since __BUMP_VERSION__
*/
class AssociationsHelper extends AssociationExtensionHelper
{
/**
* The extension name
*
* @var array $extension
*
* @since __BUMP_VERSION__
*/
protected $extension = 'com_foos';
/**
* Array of item types
*
* @var array $itemTypes
*
* @since __BUMP_VERSION__
*/
protected $itemTypes = ['foo', 'category'];
/**
* Has the extension association support
*
* @var boolean $associationsSupport
*
* @since __BUMP_VERSION__
*/
protected $associationsSupport = true;
/**
* Method to get the associations for a given item.
*
* @param integer $id Id of the item
* @param string $view Name of the view
*
* @return array Array of associations for the item
*
* @since __BUMP_VERSION__
*/
public function getAssociationsForItem($id = 0, $view = null)
{
return AssociationHelper::getAssociations($id, $view);
}
/**
* Get the associated items for an item
*
* @param string $typeName The item type
* @param int $id The id of item for which we need the associated items
*
* @return array
*
* @since __BUMP_VERSION__
*/
public function getAssociations($typeName, $id)
{
$type = $this->getType($typeName);
$context = $this->extension . '.item';
$catidField = 'catid';
if ($typeName === 'category') {
$context = 'com_categories.item';
$catidField = '';
}
// Get the associations.
$associations = Associations::getAssociations(
$this->extension,
$type['tables']['a'],
$context,
$id,
'id',
'alias',
$catidField
);
return $associations;
}
/**
* Get item information
*
* @param string $typeName The item type
* @param int $id The id of item for which we need the associated items
*
* @return Table|null
*
* @since __BUMP_VERSION__
*/
public function getItem($typeName, $id)
{
if (empty($id)) {
return null;
}
$table = null;
switch ($typeName) {
case 'foo':
$table = Table::getInstance('FooTable', 'FooNamespace\\Component\\Foos\\Administrator\\Table\\');
break;
case 'category':
$table = Table::getInstance('Category');
break;
}
if (empty($table)) {
return null;
}
$table->load($id);
return $table;
}
/**
* Get information about the type
*
* @param string $typeName The item type
*
* @return array Array of item types
*
* @since __BUMP_VERSION__
*/
public function getType($typeName = '')
{
$fields = $this->getFieldsTemplate();
$tables = [];
$joins = [];
$support = $this->getSupportTemplate();
$title = '';
if (in_array($typeName, $this->itemTypes)) {
switch ($typeName) {
case 'foo':
$fields['title'] = 'a.name';
$fields['state'] = 'a.published';
$support['state'] = true;
$support['acl'] = true;
$support['category'] = true;
$support['save2copy'] = true;
$tables = [
'a' => '#__foos_details'
];
$title = 'foo';
break;
case 'category':
$fields['created_user_id'] = 'a.created_user_id';
$fields['ordering'] = 'a.lft';
$fields['level'] = 'a.level';
$fields['catid'] = '';
$fields['state'] = 'a.published';
$support['state'] = true;
$support['acl'] = true;
$support['checkout'] = false;
$support['level'] = false;
$tables = [
'a' => '#__categories'
];
$title = 'category';
break;
}
}
return [
'fields' => $fields,
'support' => $support,
'tables' => $tables,
'joins' => $joins,
'title' => $title
];
}
/**
* Get default values for fields array
*
* @return array
*
* @since __BUMP_VERSION__
*/
protected function getFieldsTemplate()
{
return [
'id' => 'a.id',
'title' => 'a.title',
'alias' => 'a.alias',
'ordering' => 'a.id',
'menutype' => '',
'level' => '',
'catid' => 'a.catid',
'language' => 'a.language',
'access' => 'a.access',
'state' => 'a.state',
'created_user_id' => '',
'checked_out' => '',
'checked_out_time' => ''
];
}
}
components/com_foos/src/Helper/AssociationHelper.php
Die Hilfsdatei AssociationsHelper.php
ist die Schnittstelle zur Komponente Sprachverknüpfungen com_associations
. In ihr konfigurieren wir die Angaben, die für unsere Komponente spezifisch sind. Ist dies erledigt, übernehmen die Joomla eigenen Routinen und wir erfinden das Rad nicht neu.
Achtung: Ich hatte es schon geschrieben: Die Klasse
AssociationsHelper.php
gibt es im Frontend und im Backend:src/components/com_foos/src/Helper/AssociationHelper.php
undsrc/
administrator
/components/com_foos/src/Helper/AssociationHelper.php
. Die Datei für das Backend hatten wir vorher schon angesehen. Hier behandeln wir nun die für das Frontend.
components/com_foos/src/Helper/AssociationHelper.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t15a/src/components/com_foos/src/Helper/AssociationHelper.php
<?php
/**
* @package Joomla.Site
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Site\Helper;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Associations;
use Joomla\Component\Categories\Administrator\Helper\CategoryAssociationHelper;
use FooNamespace\Component\Foos\Site\Helper\RouteHelper;
/**
* Foos Component Association Helper
*
* @since __BUMP_VERSION__
*/
abstract class AssociationHelper extends CategoryAssociationHelper
{
/**
* Method to get the associations for a given item
*
* @param integer $id Id of the item
* @param string $view Name of the view
*
* @return array Array of associations for the item
*
* @since __BUMP_VERSION__
*/
public static function getAssociations($id = 0, $view = null)
{
$jinput = Factory::getApplication()->input;
$view = $view ?? $jinput->get('view');
$id = empty($id) ? $jinput->getInt('id') : $id;
if ($view === 'foos') {
if ($id) {
$associations = Associations::getAssociations('com_foos', '#__foos_details', 'com_foos.item', $id);
$return = [];
foreach ($associations as $tag => $item) {
$return[$tag] = RouteHelper::getFoosRoute($item->id, (int) $item->catid, $item->language);
}
return $return;
}
}
if ($view === 'category' || $view === 'categories') {
return self::getCategoryAssociations($id, 'com_foos');
}
return [];
}
}
components/com_foos/src/Helper/RouteHelper.php
Wir erzeugen die Klasse RouteHelper
, damit die Links korrekt zusammengesetzt werden, die wir in diesem Kapitel erstellen. Innerhalb des Links gibt es eine weitere Information als Parameter: die Sprache.
components/com_foos/src/Helper/RouteHelper.php
// https://codeberg.org/astrid/j4examplecode/raw/branch//t15a/src/components/com_foos/src/Helper/RouteHelper.php
<?php
/**
* @package Joomla.Site
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Site\Helper;
\defined('_JEXEC') or die;
use Joomla\CMS\Categories\CategoryNode;
use Joomla\CMS\Language\Multilanguage;
/**
* Foos Component Route Helper
*
* @static
* @package Joomla.Site
* @subpackage com_foos
* @since __DEPLOY_VERSION__
*/
abstract class RouteHelper
{
/**
* Get the URL route for a foos from a foo ID, foos category ID and language
*
* @param integer $id The id of the foos
* @param integer $catid The id of the foos's category
* @param mixed $language The id of the language being used.
*
* @return string The link to the foos
*
* @since __DEPLOY_VERSION__
*/
public static function getFoosRoute($id, $catid, $language = 0)
{
// Create the link
$link = 'index.php?option=com_foos&view=foos&id=' . $id;
if ($catid > 1) {
$link .= '&catid=' . $catid;
}
if ($language && $language !== '*' && Multilanguage::isEnabled()) {
$link .= '&lang=' . $language;
}
return $link;
}
/**
* Get the URL route for a foo from a foo ID, foos category ID and language
*
* @param integer $id The id of the foos
* @param integer $catid The id of the foos's category
* @param mixed $language The id of the language being used.
*
* @return string The link to the foos
*
* @since __DEPLOY_VERSION__
*/
public static function getFooRoute($id, $catid, $language = 0)
{
// Create the link
$link = 'index.php?option=com_foos&view=foo&id=' . $id;
if ($catid > 1) {
$link .= '&catid=' . $catid;
}
if ($language && $language !== '*' && Multilanguage::isEnabled()) {
$link .= '&lang=' . $language;
}
return $link;
}
/**
* Get the URL route for a foos category from a foos category ID and language
*
* @param mixed $catid The id of the foos's category either an integer id or an instance of CategoryNode
* @param mixed $language The id of the language being used.
*
* @return string The link to the foos
*
* @since __DEPLOY_VERSION__
*/
public static function getCategoryRoute($catid, $language = 0)
{
if ($catid instanceof CategoryNode) {
$id = $catid->id;
} else {
$id = (int) $catid;
}
if ($id < 1) {
$link = '';
} else {
// Create the link
$link = 'index.php?option=com_foos&view=category&id=' . $id;
if ($language && $language !== '*' && Multilanguage::isEnabled()) {
$link .= '&lang=' . $language;
}
}
return $link;
}
}
Geänderte Dateien
administrator/components/com_foos/forms/foo.xml
Wir erstellen ein Feld, über das ein Autor die Sprachverknüpfung auswählt. Dies ist das Feld name="language"
. Damit Joomla dieses Feld findet, fügen wir den Pfad in der Form addfieldprefix= "FooNamespace\Component\Foos\Administrator\Field"
als Parameter im <fieldset>
ein.
administrator/components/com_foos/forms/foo.xml
<?xml version="1.0" encoding="utf-8"?>
<form>
- <fieldset addruleprefix="FooNamespace\Component\Foos\Administrator\Rule">
+ <fieldset
+ addruleprefix="FooNamespace\Component\Foos\Administrator\Rule"
+ addfieldprefix="FooNamespace\Component\Foos\Administrator\Field"
+ >
<field
name="id"
type="number"
hint="JFIELD_ALIAS_PLACEHOLDER"
/>
+ <field
+ name="language"
+ type="contentlanguage"
+ label="JFIELD_LANGUAGE_LABEL"
+ >
+ <option value="*">JALL</option>
+ </field>
+
<field
name="published"
type="list"
administrator/components/com_foos/services/provider.php
Im Provider registrieren wir unseren AssociationsHelper
als Service der AssociationExtensionInterface
[^libraries/src/association/associationextensioninterface.php] implementiert. So stellen wir sicher, dass alle notwendigen Funktionen in unsere Komponente vererbt werden und so vorhanden sind.
administrator/components/com_foos/services/provider.php
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use FooNamespace\Component\Foos\Administrator\Extension\FoosComponent;
+use FooNamespace\Component\Foos\Administrator\Helper\AssociationsHelper;
+use Joomla\CMS\Association\AssociationExtensionInterface;
/**
* The foos service provider.
*/
public function register(Container $container)
{
+ $container->set(AssociationExtensionInterface::class, new AssociationsHelper);
+
$container->registerServiceProvider(new CategoryFactory('\\FooNamespace\\Component\\Foos'));
$container->registerServiceProvider(new MVCFactory('\\FooNamespace\\Component\\Foos'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\FooNamespace\\Component\\Foos'));
function (Container $container) {
$component->setRegistry($container->get(Registry::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
$component->setCategoryFactory($container->get(CategoryFactoryInterface::class));
+ $component->setAssociationExtension($container->get(AssociationExtensionInterface::class));
return $component;
}
administrator/components/com_foos/sql/install.mysql.utf8.sql
administrator/components/com_foos/sql/install.mysql.utf8.sql
Damit die Sprache zum Element gespeichert wird, fügen wir eine Spalte in der Datenbanktabelle hinzu. Bei Neuinstallationen ist das Skript install.mysql.utf8.sql
dasjenige, welches aufgerufen wird.
ALTER TABLE `#__foos_details` ADD COLUMN `publish_down` datetime AFTER `alias`;
ALTER TABLE `#__foos_details` ADD KEY `idx_state` (`published`);
+
+ALTER TABLE `#__foos_details` ADD COLUMN `language` char(7) NOT NULL DEFAULT '*' AFTER `alias`;
+
+ALTER TABLE `#__foos_details` ADD KEY `idx_language` (`language`);
administrator/components/com_foos/src/Extension/FoosComponent.php
In FoosComponent ergänzen wir AssociationServiceInterface
und AssociationServiceTrait
, so das alles Notwendige in unserer Erweiterung implementiert ist.
Traits sind ein Mechanismus zur Wiederverwendung von Code, der in Programmiersprachen mit einfacher Vererbung wie PHP verwendet wird.
administrator/components/com_foos/src/Extension/FoosComponent.php
defined('JPATH_PLATFORM') or die;
+use Joomla\CMS\Association\AssociationServiceInterface;
+use Joomla\CMS\Association\AssociationServiceTrait;
use Joomla\CMS\Categories\CategoryServiceInterface;
use Joomla\CMS\Categories\CategoryServiceTrait;
use Joomla\CMS\Extension\BootableExtensionInterface;
*
* @since __BUMP_VERSION__
*/
-class FoosComponent extends MVCComponent implements BootableExtensionInterface, CategoryServiceInterface
+class FoosComponent extends MVCComponent implements BootableExtensionInterface, CategoryServiceInterface, AssociationServiceInterface
{
use CategoryServiceTrait;
+ use AssociationServiceTrait;
use HTMLRegistryAwareTrait;
/**
administrator/components/com_foos/src/Field/Modal/FooField.php
Das Modal haben wir bisher genutzt, um beim Anlegen eines Menüpunkts ein Foo-Element mithilfe eines Popups auszuwählen. Jetzt verwenden wir es wieder, um eine Sprachverknüpfung zu selektieren. Damit nur die passenden Sprachen angezeigt werden, erweitern wir die URL um die Sprachinformation.
administrator/components/com_foos/src/Field/Modal/FooField.php
// Setup variables for display.
$linkFoos = 'index.php?option=com_foos&view=foos&layout=modal&tmpl=component&'
. Session::getFormToken() . '=1';
- $linkFoo = 'index.php?option=com_foos&view=foo&layout=modal&tmpl=component&'
- . Session::getFormToken() . '=1';
$modalTitle = Text::_('COM_FOOS_CHANGE_FOO');
+ if (isset($this->element['language'])) {
+ $linkFoos .= '&forcedLanguage=' . $this->element['language'];
+ $modalTitle .= ' — ' . $this->element['label'];
+ }
+
$urlSelect = $linkFoos . '&function=jSelectFoo_' . $this->id;
if ($value) {
Verwirren dich die Zeichen
—
[^unicode-table.com/de/2014/] oder&
[^unicode-table.com/de/0026/]? Die sind ganz harmlos.—
ist nichts weiter als ein Gedankenstrich[de.wikipedia.org/wiki/Halbgeviertstrich#Gedankenstrich]-
.&
steht für das kaufmännische Und-Zeichen&
. In HTML steht letzteres für den Beginn einer Entity-Referenz. Somit ist es ein besonderes Zeichen. Wenn du ein solches Zeichen in einem Text nutzt der aus sicherheitsgründen überprüft wird, sollten du die kodierte Entität&
verwenden - mehr Technisches auf w3c.org[^w3.org/tr/xhtml1/guidelines.html#c_12]. Beim Gedankenstrich-
nutzen wir Unicode[^de.wikipedia.org/wiki/unicode]. Ziel ist in diesem Fall, die Verwendung unterschiedlicher und inkompatibler Kodierungen in verschiedenen Ländern oder Kulturkreisen zu vereinheitlichen.
administrator/components/com_foos/src/Model/FooModel.php
Das Model administrator/components/com_foos/src/Model/FooModel.php
, mit dem Daten eines Elementes berechnet werden, passen wir bezüglich der Sprache an. Dabei spielen getItem
und preprocessForm
die wesentliche Rolle.
administrator/components/com_foos/src/Model/FooModel.php
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
+use Joomla\CMS\Language\Associations;
use Joomla\CMS\MVC\Model\AdminModel;
+use Joomla\CMS\Language\LanguageHelper;
/**
* Item Model for a Foo.
class FooModel extends AdminModel
*/
public $typeAlias = 'com_foos.foo';
+ protected $associationsContext = 'com_foos.item';
+
/**
* Method to get the row form.
*
protected function loadFormData()
return $data;
}
+ public function getItem($pk = null)
+ {
+ $item = parent::getItem($pk);
+
+ // Load associated foo items
+ $assoc = Associations::isEnabled();
+
+ if ($assoc) {
+ $item->associations = [];
+
+ if ($item->id != null) {
+ $associations = Associations::getAssociations('com_foos', '#__foos_details', 'com_foos.item', $item->id, 'id', null);
+
+ foreach ($associations as $tag => $association) {
+ $item->associations[$tag] = $association->id;
+ }
+ }
+ }
+
+ return $item;
+ }
+
+ protected function preprocessForm(\JForm $form, $data, $group = 'content')
+ {
+ if (Associations::isEnabled()) {
+ $languages = LanguageHelper::getContentLanguages(false, true, null, 'ordering', 'asc');
+
+ if (count($languages) > 1) {
+ $addform = new \SimpleXMLElement('<form />');
+ $fields = $addform->addChild('fields');
+ $fields->addAttribute('name', 'associations');
+ $fieldset = $fields->addChild('fieldset');
+ $fieldset->addAttribute('name', 'item_associations');
+
+ foreach ($languages as $language) {
+ $field = $fieldset->addChild('field');
+ $field->addAttribute('name', $language->lang_code);
+ $field->addAttribute('type', 'modal_foo');
+ $field->addAttribute('language', $language->lang_code);
+ $field->addAttribute('label', $language->title);
+ $field->addAttribute('translate_label', 'false');
+ $field->addAttribute('select', 'true');
+ $field->addAttribute('new', 'true');
+ $field->addAttribute('edit', 'true');
+ $field->addAttribute('clear', 'true');
+ }
+
+ $form->load($addform, false);
+ }
+ }
+
+ parent::preprocessForm($form, $data, $group);
+ }
+
/**
* Prepare and sanitise the table prior to saving.
*
administrator/components/com_foos/src/Model/FoosModel.php
Achtung:
FooModel.php
ist das Model welches die Daten für ein Element berechnet.FoosModel.php
- beachte dass
- ist das Model der Listenansicht - es behandelt Daten für eine Gruppe von Elementen.
Im Model der Liste ist es neben dem Hinzufügen der Sprachinformationen wichtig, den Status über populateState
zu aktualisieren. Andernfalls ist nicht jederzeit die passende Sprache aktiv. Der Status beinhaltet die Information, welche Sprache aktiv ist.
administrator/components/com_foos/src/Model/FoosModel.php
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\Model\ListModel;
+use Joomla\CMS\Language\Associations;
+use Joomla\CMS\Factory;
/**
* Methods supporting a list of foo records.
protected function getListQuery()
// Select the required fields from the table.
$query->select(
- $db->quoteName(['a.id', 'a.name', 'a.alias', 'a.access', 'a.catid', 'a.published', 'a.publish_up', 'a.publish_down'])
+ $db->quoteName(
+ [
+ 'a.id', 'a.name', 'a.alias', 'a.access',
+ 'a.catid', 'a.published', 'a.publish_up', 'a.publish_down',
+ 'a.language'
+ ]
+ )
);
$query->from($db->quoteName('#__foos_details', 'a'));
protected function getListQuery()
$db->quoteName('#__categories', 'c') . ' ON ' . $db->quoteName('c.id') . ' = ' . $db->quoteName('a.catid')
);
+ // Join over the language
+ $query->select($db->quoteName('l.title', 'language_title'))
+ ->select($db->quoteName('l.image', 'language_image'))
+ ->join(
+ 'LEFT',
+ $db->quoteName('#__languages', 'l') . ' ON ' . $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.language')
+ );
+
+ // Join over the associations.
+ if (Associations::isEnabled()) {
+ $subQuery = $db->getQuery(true)
+ ->select('COUNT(' . $db->quoteName('asso1.id') . ') > 1')
+ ->from($db->quoteName('#__associations', 'asso1'))
+ ->join('INNER', $db->quoteName('#__associations', 'asso2'), $db->quoteName('asso1.key') . ' = ' . $db->quoteName('asso2.key'))
+ ->where(
+ [
+ $db->quoteName('asso1.id') . ' = ' . $db->quoteName('a.id'),
+ $db->quoteName('asso1.context') . ' = ' . $db->quote('com_foos.item'),
+ ]
+ );
+
+ $query->select('(' . $subQuery . ') AS ' . $db->quoteName('association'));
+ }
+
+ // Filter on the language.
+ if ($language = $this->getState('filter.language')) {
+ $query->where($db->quoteName('a.language') . ' = ' . $db->quote($language));
+ }
+
return $query;
}
+
+ protected function populateState($ordering = 'a.name', $direction = 'asc')
+ {
+ $app = Factory::getApplication();
+ $forcedLanguage = $app->input->get('forcedLanguage', '', 'cmd');
+
+ // Adjust the context to support modal layouts.
+ if ($layout = $app->input->get('layout')) {
+ $this->context .= '.' . $layout;
+ }
+
+ // Adjust the context to support forced languages.
+ if ($forcedLanguage) {
+ $this->context .= '.' . $forcedLanguage;
+ }
+
+ // List state information.
+ parent::populateState($ordering, $direction);
+
+ // Force a language.
+ if (!empty($forcedLanguage)) {
+ $this->setState('filter.language', $forcedLanguage);
+ }
+ }
}
administrator/components/com_foos/src/Service/HTML/AdministratorService.php
Wir implementieren den Service association
in AdministratorService.php
. Über die ID gibt die Funktion das HTML-Markup zum Bearbeiten der Sprachverknüpfungen zurück.
administrator/components/com_foos/src/Service/HTML/AdministratorService.php
defined('JPATH_BASE') or die;
+use Joomla\CMS\Factory;
+use Joomla\CMS\Language\Associations;
+use Joomla\CMS\Language\Text;
+use Joomla\CMS\Layout\LayoutHelper;
+use Joomla\CMS\Router\Route;
class AdministratorService
{
+ public function association($fooid)
+ {
+ // Defaults
+ $html = '';
+
+ // Get the associations
+ if ($associations = Associations::getAssociations('com_foos', '#__foos_details', 'com_foos.item', $fooid, 'id', null)) {
+ foreach ($associations as $tag => $associated) {
+ $associations[$tag] = (int) $associated->id;
+ }
+
+ // Get the associated foo items
+ $db = Factory::getDbo();
+ $query = $db->getQuery(true)
+ ->select('c.id, c.name as title')
+ ->select('l.sef as lang_sef, lang_code')
+ ->from('#__foos_details as c')
+ ->select('cat.title as category_title')
+ ->join('LEFT', '#__categories as cat ON cat.id=c.catid')
+ ->where('c.id IN (' . implode(',', array_values($associations)) . ')')
+ ->where('c.id != ' . $fooid)
+ ->join('LEFT', '#__languages as l ON c.language=l.lang_code')
+ ->select('l.image')
+ ->select('l.title as language_title');
+ $db->setQuery($query);
+
+ try {
+ $items = $db->loadObjectList('id');
+ } catch (\RuntimeException $e) {
+ throw new \Exception($e->getMessage(), 500, $e);
+ }
+
+ if ($items) {
+ foreach ($items as &$item) {
+ $text = strtoupper($item->lang_sef);
+ $url = Route::_('index.php?option=com_foos&task=foo.edit&id=' . (int) $item->id);
+ $tooltip = '<strong>' . htmlspecialchars($item->language_title, ENT_QUOTES, 'UTF-8') . '</strong><br>'
+ . htmlspecialchars($item->title, ENT_QUOTES, 'UTF-8') . '<br>' . Text::sprintf('JCATEGORY_SPRINTF', $item->category_title);
+ $classes = 'badge bg-secondary';
+
+ $item->link = '<a href="' . $url . '" title="' . $item->language_title . '" class="' . $classes . '">' . $text . '</a>'
+ . '<div role="tooltip" id="tip' . (int) $item->id . '">' . $tooltip . '</div>';
+ }
+ }
+
+ $html = LayoutHelper::render('joomla.content.associations', $items);
+ }
+
+ return $html;
+ }
}
administrator/components/com_foos/src/View/Foo/HtmlView.php
Wenn nur eine Sprache möglich ist beziehungsweise das Ändern nicht gewünscht ist, setzen wir den Wert des Sprachauswahlfeldes und schützte es vor Schreibzugriff. Außerdem sind nur Kategorien dieser Sprache auswählbar.
administrator/components/com_foos/src/View/Foo/HtmlView.php
$this->form = $this->get('Form');
$this->item = $this->get('Item');
+ // If we are forcing a language in modal (used for associations).
+ if ($this->getLayout() === 'modal' && $forcedLanguage = Factory::getApplication()->input->get('forcedLanguage', '', 'cmd')) {
+ // Set the language field to the forcedLanguage and disable changing it.
+ $this->form->setValue('language', null, $forcedLanguage);
+ $this->form->setFieldAttribute('language', 'readonly', 'true');
+
+ // Only allow to select categories with All language or with the forced language.
+ $this->form->setFieldAttribute('catid', 'language', '*,' . $forcedLanguage);
+ }
+
$this->addToolbar();
return parent::display($tpl);
administrator/components/com_foos/src/View/Foos/HtmlView.php
Die View der Liste soll die Sidebar und die Toolbar enthalten, wenn es sich nicht um eine Modalansicht oder ein Popup handelt. Falls die Ansicht modal ist, verwirren Toolbar und Sidebar. In dem Fall filtern wir die Items automatisch nach der gerade aktiven Sprache.
administrator/components/com_foos/src/View/Foos/HtmlView.php
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
+use Joomla\CMS\Factory;
/**
* View class for a list of foos.
public function display($tpl = null): void
$this->setLayout('emptystate');
}
- $this->addToolbar();
+ // We don't need toolbar in the modal window.
+ if ($this->getLayout() !== 'modal') {
+ $this->addToolbar();
+ $this->sidebar = \JHtmlSidebar::render();
+ } else {
+ // In article associations modal we need to remove language filter if forcing a language.
+ // We also need to change the category filter to show show categories with All or the forced language.
+ if ($forcedLanguage = Factory::getApplication()->input->get('forcedLanguage', '', 'CMD')) {
+ // If the language is forced we can't allow to select the language, so transform the language selector filter into a hidden field.
+ $languageXml = new \SimpleXMLElement('<field name="language" type="hidden" default="' . $forcedLanguage . '" />');
+ }
+ }
parent::display($tpl);
}
administrator/components/com_foos/tmpl/foo/edit.php
In das Formular zur Bearbeitung eines Elements fügen wir ein Formularfeld zur Angabe der Sprache ein. Dazu verwenden wir das Layout administrator/components/com_foos/tmpl/foo/edit_associations.php
, das wir zuvor in diesem Teil erstellten.
Warum das Layout
edit_associations.php
in der Dateiedit.php
mit dem Namenassociations
aufgerufen wird, denkst du dir vielleicht bereits. In dem Teil über die Layouts gehe ich darauf näher ein.
administrator/components/com_foos/tmpl/foo/edit.php
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
+use Joomla\CMS\Language\Associations;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
$app = Factory::getApplication();
$input = $app->input;
+$assoc = Associations::isEnabled();
+
+$this->ignore_fieldsets = ['item_associations'];
$this->useCoreUI = true;
+$isModal = $input->get('layout') === 'modal';
$wa = $this->document->getWebAssetManager();
<?php echo $this->getForm()->renderField('publish_up'); ?>
<?php echo $this->getForm()->renderField('publish_down'); ?>
<?php echo $this->getForm()->renderField('catid'); ?>
+ <?php echo $this->getForm()->renderField('language'); ?>
</div>
</div>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
+ <?php if (!$isModal && $assoc) : ?>
+ <?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'associations', Text::_('JGLOBAL_FIELDSET_ASSOCIATIONS')); ?>
+ <fieldset id="fieldset-associations" class="options-form">
+ <legend><?php echo Text::_('JGLOBAL_FIELDSET_ASSOCIATIONS'); ?></legend>
+ <div>
+ <?php echo LayoutHelper::render('joomla.edit.associations', $this); ?>
+ </div>
+ </fieldset>
+ <?php echo HTMLHelper::_('uitab.endTab'); ?>
+ <?php elseif ($isModal && $assoc) : ?>
+ <div class="hidden"><?php echo LayoutHelper::render('joomla.edit.associations', $this); ?></div>
+ <?php endif; ?>
<?php echo LayoutHelper::render('joomla.edit.params', $this); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
administrator/components/com_foos/tmpl/foos/default.php
In der Übersicht zur Komponenten im Administrationsbereich ergänzen wir Spalten, um die Sprachinformationen anzuzeigen. Diese Spalten zeigen wir lediglich an, wenn es erforderlich ist. Dies ist der Fall, wenn Sprachverknüpfungen und Mehrsprachigkeit aktiviert sind. Um dies herauszufinden nutzen wir die Joomla eigene Funktionen Associations::isEnabled()
und Multilanguage::isEnabled()
.
administrator/components/com_foos/tmpl/foos/default.php
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
+use Joomla\CMS\Language\Multilanguage;
+use Joomla\CMS\Language\Associations;
+use Joomla\CMS\Layout\LayoutHelper;
+
+$assoc = Associations::isEnabled();
+
?>
<form action="<?php echo Route::_('index.php?option=com_foos'); ?>" method="post" name="adminForm" id="adminForm">
<div class="row">
<th scope="col" style="width:10%" class="d-none d-md-table-cell">
<?php echo TEXT::_('JGRID_HEADING_ACCESS') ?>
</th>
+ <?php if ($assoc) : ?>
+ <th scope="col" style="width:10%">
+ <?php echo Text::_('COM_FOOS_HEADING_ASSOCIATION'); ?>
+ </th>
+ <?php endif; ?>
+ <?php if (Multilanguage::isEnabled()) : ?>
+ <th scope="col" style="width:10%" class="d-none d-md-table-cell">
+ <?php echo Text::_('JGRID_HEADING_LANGUAGE'); ?>
+ </th>
+ <?php endif; ?>
<th scope="col">
<?php echo Text::_('COM_FOOS_TABLE_TABLEHEAD_ID'); ?>
</th>
<td class="small d-none d-md-table-cell">
<?php echo $item->access_level; ?>
</td>
+
+ <?php if ($assoc) : ?>
+ <td class="d-none d-md-table-cell">
+ <?php if ($item->association) : ?>
+ <?php
+ echo HTMLHelper::_('foosadministrator.association', $item->id);
+ ?>
+ <?php endif; ?>
+ </td>
+ <?php endif; ?>
+ <?php if (Multilanguage::isEnabled()) : ?>
+ <td class="small d-none d-md-table-cell">
+ <?php echo LayoutHelper::render('joomla.content.language', $item); ?>
+ </td>
+ <?php endif; ?>
+
<td class="d-none d-md-table-cell">
<?php echo $item->id; ?>
</td>
Teste deine Joomla-Komponente
- Installiere deine Komponente in Joomla Version 4, um sie zu testen:
Kopiere die Dateien im administrator
Ordner in den administrator
Ordner deiner Joomla 4 Installation.
Kopiere die Dateien im components
Ordner in den components
Ordner deiner Joomla 4 Installation.
-
Die Datenbank ist geändert worden, so dass es erforderlich ist, sie zu aktualisieren. Öffne den Bereich
System | Information | Database
, wie im TeilVeröffentlichen und Verstecken
beschrieben. Wähle deine Komponente aus und klicke aufUpdate Structure
. -
Installiere über
System | Install | Languages
mindestens eine weitere Sprache. Ich habe die deutsche und die persische Sprache gewählt.
Persisch[^de.wikipedia.org/wiki/persische_sprache] ist neben Arabic, Hebrew, Pashto, Urdu und Sindhi eine der am weitesten verbreiteten RTL-Schreibsysteme[^wikipedia.org/wiki/right-to-left_script] der Neuzeit und kann deshalb zum Testen der RTL-Integration in Joomla verwendet werden. In einer Rechts-nach-links, von oben nach unten Schrift[^de.wikipedia.org/wiki/schrift] (häufig abgekürzt als Rechts-nach-links oder RTL) schreibt man auf einer Seite von rechts nach links, wobei neue Zeilen von oben nach unten geschrieben werden. Dies steht im Gegensatz zur Links-nach-Rechts-Schreibweise, bei der die Schrift von links beginnt und nach rechts fortgesetzt wird.
- Stelle über
System | Manage | Plugins
sicher, dass das PluginSystem - Language Filter
veröffentlicht ist.
- Öffne die Ansicht eines Items deiner Komponente im Administrationsbereich und überzeuge dich davon, dass der Status
Language
änderbar ist. Ändere diesen vonAll
in eine beliebige Sprache.
- Spiele mit den Sprachverknüpfungen und überzeuge dich davon, dass alles korrekt verknüpft wird. Achte auf auf die Einstellungen bei den zugeordneten Kategorien.
- Erweitere die Tests auf die Komponente
Multilingual Associations
. Diese unterstützt deine Erweiterung ebenfalls.