Magento – fehlende Übersetzungen im Developer Mode

In Magento gibt es das Problem/Feature, das bei eingeschaltetem Developer Mode einige Übersetzungen nicht funktionieren (das ist natürlich nur relevant, sofern man keinen englischsprachigen Shop betreibt).
Den Developer Mode kann man aktivieren, in dem man z.B. beim Apache in der Vhost Datei die Environment Variable wie folgt setzt:

1
SetEnv MAGE_IS_DEVELOPER_MODE "1"

Wenn der Developer Mode eingeschaltet ist, werden z.B. die Exceptions nicht geloggt, sondern direkt „geworfen“, da Magento einen eigenen Error Handler definiert hat.
Siehe Datei: app/code/core/Mage/Core/functions.php

1
2
3
4
5
6
$errorMessage .= ": {$errstr}  in {$errfile} on line {$errline}";
if (Mage::getIsDeveloperMode()) {
   throw new Exception($errorMessage);
} else {
   Mage::log($errorMessage, Zend_Log::ERR);
}

Beim Entwickeln ist es natürlich von Vorteil, wenn man Fehler direkt sieht und nicht immer erst im Logfile nachschauen muss.
Wer wissen möchte welche Auswirkungen der Devolper Mode noch hat, sollte einfach mal im Magento Code nach „Mage::getIsDeveloperMode“ suchen.

Allerdings führt das Einschalten des Developer Modes auch zu einem unschönen Nebeneffekt, und zwar werden selbst definierte Übersetzungstexte nicht mehr angezeigt, sofern sie bereits in einem anderen Modul definiert wurden.

Funktionsweise des Translation Moduls

Magento speichert alle Übersetzungstexte im Model Mage_Core_Model_Translate. Das Einlesen der CSV Files mit den Übersetzungen für die einzelnen Module funktioniert wird folgt:
Beim Laden der Module wird in der Init Funktion der Klasse Mage_Core_Model_Translate die Funktion _loadModuleTranslation aufgerufen.
Siehe: Mage_Core_Model_Translate

1
2
3
4
5
6
7
8
9
10
11
public function init($area, $forceReload = false)
{
    ...
    $this->_data = array();
 
    foreach ($this->getModulesConfig() as $moduleName=>$info) {
        $info = $info->asArray();
        $this->_loadModuleTranslation($moduleName, $info['files'], $forceReload);
    }
    ...
}

In dieser Funktion wird dann das jeweilige CSV File eingelesen

1
2
3
4
5
6
7
8
protected function _loadModuleTranslation($moduleName, $files, $forceReload=false)
{
    foreach ($files as $file) {
        $file = $this->_getModuleFilePath($moduleName, $file);
        $this->_addData($this->_getFileData($file), $moduleName, $forceReload);
    }
    return $this;
}

In der Funktion _addData werden alle Übersetzungstexte aus dem unterschiedlichen Quellen (CSV, Files, Datenbank, XML Files) als Array in der Variable $_data gespeichert.
In $data werden die zu übersetzenden Texte als Array übergeben, Scope enthält standardmäßig den Namespace und den Namen des Moduls (Namespace_Modulename).
Siehe Datei: app/code/core/Mage/Core/Model/Translate.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
protected function _addData($data, $scope, $forceReload=false)
{
	foreach ($data as $key => $value) {
	    if ($key === $value) {
		continue;
	    }
	    $key    = $this->_prepareDataString($key);
	    $value  = $this->_prepareDataString($value);
 
	    if ($scope && isset($this->_dataScope[$key]) && !$forceReload ) {
		/**
		 * Checking previos value
		 */
		$scopeKey = $this->_dataScope[$key] . self::SCOPE_SEPARATOR . $key;
		if (!isset($this->_data[$scopeKey])) {
		    if (isset($this->_data[$key])) {
		        $this->_data[$scopeKey] = $this->_data[$key];
		        /**
		         * Not allow use translation not related to module
                         */
		        if (Mage::getIsDeveloperMode()) {
		            unset($this->_data[$key]);
		        }
		    }
		}
		$scopeKey = $scope . self::SCOPE_SEPARATOR . $key;
		$this->_data[$scopeKey] = $value;
	    }
	    else {
		$this->_data[$key]     = $value;
		$this->_dataScope[$key]= $scope;
	    }
	}
	return $this;
}

Die Übersetzungen in den Templates werden über die Funktion Mage_Core_Block_Abstract::__() geladen:
Mage_Core_Block_Abstract

1
2
3
4
5
6
7
public function __()
{
    $args = func_get_args();
    $expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->getModuleName());
    array_unshift($args, $expr);
    return Mage::app()->getTranslator()->translate($args);
}

Mage_Core_Model_Translate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected function _getTranslatedString($text, $code)
{
    $translated = '';
    if (array_key_exists($code, $this->getData())) {
        $translated = $this->_data[$code];
    }
    elseif (array_key_exists($text, $this->getData())) {
        $translated = $this->_data[$text];
    }
    else {
        $translated = $text;
    }
    return $translated;
}

Das Problem mit der fehlenden Übersetzung

Das oben beschriebene Problem tritt bei den Übersetzungsvariablen auf, die in mehrere Modulen definiert sind.
Das heißt im Developer Mode werden die Übersetzungen von Texten, die in mehreren CSV Files von unterschiedlichen Modulen enthalten sind, unter einem anderen Namensraum (Scope) gespeichert.
Beim Parsen der CSV Datei des ersten gefundenen Moduls wird der Wert im Array $_data gespeichert. Beim Verarbeiten der CSV Datei des zweiten Moduls wird der Wert wieder gelöscht:

if (Mage::getIsDeveloperMode()) {
   unset($this->_data[$key]);
}

Anschließend wird es mit unter einem anderen Scope im Array $_data gespeichert.

$scopeKey = $scope . self::SCOPE_SEPARATOR . $key;
$this->_data[$scopeKey] = $value;

Beispiel:

Um das Ganze mal zu verdeutlichen, hier mal ein Beispiel anhand des Textes „Loading next step…“, der sowohl in einem Core Modul, als auch in einem eigenen Modul übersetzt wurde:

1. Aufruf der Funktion Mage_Core_Model_Translate::_addData beim Laden des Core Moduls Mage_Checkout
Übersetzungfile: „/var/www/VaaTrunk/app/locale/de_DE/Mage_Checkout.csv“
Wert der einzelnen Variablen in der Funktion _addData

$key:      "Loading next step..."		
$scope:    "Mage_Checkout"
$value:    "Nächster Schritt wird geladen ..."

Nach dem Speichern im Translation Array

$scopeKey: "Mage_Checkout::Loading next step..."
$this->_data["Loading next step..."]  = "Nächster Schritt wird geladen ...";
$this->_dataScope["Loading next step..."]= "Mage_Checkout";

2. Aufruf der Funktion Mage_Core_Model_Translate::_addData beim Laden des eigenen Moduls Namespace_Modulname
Übersetzungfile: „/var/www/VaaTrunk/app/locale/de_DE/Namespace_Modulname.csv“

$key:      "Loading next step..."		
$scope:    "Namespace_Modulname"
$scopeKey: "Mage_Checkout::Loading next step..."

Da der Wert bereits unter einen anderen Scope vorhanden ist, wird er wieder gelöscht:

unset($this->_data["Loading next step..."]);

Werte nach dem Löschen

$scopeKey: "Namespace_Modulname::Loading next step..."
$value:    "Lade nächsten Schritt..."

Anschließend wird der Wert mit dem neuen Scope Key im Übersetzungs Array gespeichert

$this->_data["Mage_Checkout::Loading next step..."] = "Lade nächsten Schritt...";

Wenn man jetzt also im Template den String mit der Übersetzungsfunktion $this->__(‚Loading next step…‘) laden will, kann die Übersetzung nicht geladen werden, da der Text ja jetzt mit dem Scope Key „Namespace_Modulname::Loading next step…“ gespeichert ist.
Wird ein Wert im _data Array nicht gefunden, so wird der original String, der an die Übersetzungfunktion übergeben wurde, verwendet.
Mann müsste ihn also wie folgt laden, damit es funktioniert:

$this->__('Namespace_Modulname::Loading next step...')

Ob das jetzt ein Bug oder ein Feature ist, muss jeder selbst entscheiden.

Dieser Beitrag wurde unter Magento abgelegt und mit , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Die Kommentarfunktion ist geschlossen.