0251 / 590 837 15
info@a-coding-project.de
;

Mehrsprachigkeit (CMS)

Einige von euch haben bereits im Wiki unseres CMS eine Übersetzungstabelle gebaut. Diese habe ich noch ein wenig erweitert und ins CMS eingebaut. Früher hab ich ja vorgeschlagen, die Sprachen in jeweils eigenen PHP-Dateien einzubauen und Konstanten dann die einzelnen Sprachelemente einzubauen. Dafür habe ich nun eine elegantere Lösung: SqlLite. SqlLite ist ein sehr einfaches Datenbanksystem, was bereits mit PHP ausgeliefert wird. Hier gibt es für jede Sprache eine eigene kleine Datenbank, über welcher wir die Texte bekommen. Diese Sprachdateien werden wie auch bei den Klassen auch in den Plugins und Skins verfügbar sein.

Die Sprache wird über den neuen Setting language eingestellt:

INSERT INTO `cms_settings` ( `role` , `area` , `areaType` , `property` , `value` , `activated` , `description` , `type` )

VALUES (

‚3‘, ‚global‘, ‚global‘, ‚language‘, ‚de‘, ‚1‘, ‚Sprache‘, ‚languageselector‘

);

Dieser muss selbstverständlich auch in unserem Installer (installer/installer.php):

private function insertRows(){

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu_names` VALUES (1, ‚{admin}‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (1, 1, ‚Dashboard‘, ‚/admin/‘,1;“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (2, 1, ‚Seiten‘, ‚/admin/index.php?page=sites‘,1);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (3, 1, ‚Menüs‘, ‚/admin/index.php?page=menues‘,1);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (4, 1, ‚Dateien‘, ‚/admin/index.php?page=files‘,1);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (5, 1, ‚Plugins‘, ‚/admin/index.php?page=plugins‘,1);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (7, 1, ‚Einstellungen‘, ‚/admin/index.php?page=settings‘,1);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (8, 1, ‚Benutzer‘, ‚/admin/index.php?page=user‘,1);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„menu` VALUES (9, 1, ‚Logout‘, ‚/admin/index.php?page=logout‘,1);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„user` VALUES (1, ‚“.$this->params[4][‚user‘].„‚, ‚“.md5($this->params[4][‚password‘]).„‚,2);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’global‘, ‚global‘, ‚title‘, ‚“.$this->params[4][’name‘].„‚, 1, ‚Titel des CMS‘,’textbox‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’global‘, ‚global‘, ’selectedskin‘, ‚1‘, 1, ‚Aktueller Skin‘,’skinselector‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’global‘, ‚global‘, ’selectedmobileskin‘, ‚1‘, 1, ‚Mobiler Skin‘,’mobileskinselector‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’default‘, ’skins‘, ’skinbgcolor‘, ‚dedede‘, 1, ‚Hintergrundfarbe‘,’colorpicker‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’default‘, ’skins‘, ’skinforecolor‘, ‚525252‘, 1, ‚Schriftfarbe‘,’colorpicker‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’default‘, ’skins‘, ’skinhighlight1′, ‚ff0000‘, 1, ‚Highlight Farbe 1′,’colorpicker‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’default‘, ’skins‘, ’skinhighlight2′, ‚b3fa00‘, 1, ‚Highlight Farbe 2′,’colorpicker‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3,’global‘, ‚global‘, ‚host‘, ‚http://“.$_SERVER[‚HTTP_HOST‘].„‚, 1, ‚URL der Startseite‘,’textbox‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3, ‚global‘, ‚global‘, ‚mainmenu‘, ‚4‘, 0, ‚Hauptmenü‘, ‚menueselector‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (3, ‚global‘, ‚global‘, ‚accessdenied‘, ‚0‘, 1, ‚Zugriff-Verweigert-Seite‘, ‚pageselector‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (2, ‚global‘, ‚global‘, ‚mainmenu‘, ‚4‘, 1, ‚Hauptmenü‘, ‚menueselector‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„settings` VALUES (2, ‚global‘, ‚global‘, ‚language‘, ‚de‘, 1, ‚Sprache‘, ‚languageselector‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„skins` VALUES (1, ‚default‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„skins` VALUES (2, ‚mobile‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„roles` VALUES (1, ‚Gast‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„roles` VALUES (2, ‚Administator‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„roles` VALUES (3, ‚Alle‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„pagetypes` VALUES (1, ‚wysiwyg‘, ‚Textseite‘);“);

mysql_query(„INSERT INTO `“.$this->params[3][‚praefix‘].„pagetypes` VALUES (2, ‚login‘, ‚Login‘);“);

}

Um die Sprache auszuwählen gibt es den /system/classes/languageselctor.php, welcher zur Zeit aber nur die Deutsche Sprache zur auswahl hat:

<?PHP

class languageselector extends Control{

public $style = ;

public function getCode(){

$res = „<select name=.$this->name. style=.$this->style.>“;

$res .= „<option value=de>Deutsch</option>“;

$res .= „</select>“;

return $res;

}

}

?>

Jetzt kommen wir zur neuen Klasse /system/classes/language.php. In der Eigenschaft languagePacks werden dei SqlLite-Verbindungen gespeichert, welche nur geladen werden, wenn sie auch wirklich gebraucht werden. Über die Funktion getString kann man seinen Text übersetzen lassen. Eine Tabelle mit allen Tokens gibt es hier. Es wird automatisch eine neue Datenbank angelegt, wenn sie noch nicht vorhanden ist:

<?PHP

class Language{

public $languagePacks = null;

public $language = null;

public $root = „“;

public function __construct(){

$this->language = getSetting(„global“,„global“,„language“);

}

public function getString($token){

$packageName = $this->getLanguagePackName($token);

if(!$this->languagePacks[$packageName]){

$this->openLanguagePack($packageName);

}

if($this->languagePacks[$packageName]){

$res = $this->languagePacks[$packageName]->query(„SELECT text FROM language WHERE token = ‚“.$token.„‚“);

if($res){

return $res->fetchSingle();

}

}

return $token;

}

private function openLanguagePack($package){

if(!file_exists($this->getLanguagePackUrl($package))){

$create = true;

}

$this->languagePacks[$package] = new SQLiteDatabase($this->getLanguagePackUrl($package),0666);

if($create){

$this->languagePacks[$package]->queryExec(‚CREATE TABLE language (token nvarchar(50), text nvarchar(4000), PRIMARY KEY (token));‘);

}

}

private function getLanguagePackUrl($package){

$namespaces = split(„_“,strtolower($package));

if(sizeOf($namespaces) == 2){

if($namespaces[0] == „plugin“){

return $this->root.„system/plugins/“.$namespaces[1].„/language/“.$this->language.„.db“;

}

else if($namespaces[0] == „skin“){

return $this->root.„system/skins/“.$namespaces[1].„/language/“.$this->language.„.db“;

}

}

return $this->root.„system/language/“.$this->language.„.db“;

}

private function getLanguagePackName($token){

$namespaces = split(„_“,strtolower($token));

if(sizeOf($namespaces) == 3){

if($namespaces[0] == „plugin“){

return „plugin_“.$namespaces[1];

}

else if($namespaces[0] == „skin“){

return „skin_“.$namespaces[1];

}

}

return „global“;

}

public function addTranslation($token,$text){

$packageName = $this->getLanguagePackName($token);

if(!$this->languagePacks[$packageName]){

$this->openLanguagePack($packageName);

}

if($this->languagePacks[$packageName]){

$this->languagePacks[$packageName]->queryExec(„INSERT INTO language (token,text) VALUES (‚“.$token.„‚,'“.$text.„‚)“);

}

}

}

?>

Mit addTranslation kann man dem Sprachpaket neue Übersetzungen hinzufügen. Für die Deutsche Sprache habe ich bereits eine Datenbank erstellt, die im Verzeichnis /system/language liegen muss. Es wäre schön, wenn sich jemand findet, der die Englische übersetzung erstellt. Download de.db

Der Zugriff auf die Language-Klasse läuft wie bei der Datenbank über $GLOBALS. Das Objekt wird in den beiden index.phps erstellt. Das ist die neue /index.php:

<?PHP

function __autoload($class_name){

$namespaces = split(„_“,strtolower($class_name));

if(sizeOf($namespaces) == 3){

if($namespaces[0] == „plugin“){

require_once „system/plugins/“.$namespaces[1].„/classes/“.$namespaces[2].„.php“;

$imported = true;

}

else if($namespaces[0] == „skin“){

require_once „system/skins/“.$namespaces[1].„/classes/“.$namespaces[2].„.php“;

$imported = true;

}

}

if(!$imported){

require_once „system/classes/“.strtolower($class_name).„.php“;

}

}

session_start();

include(„system/settings.php“);

include(„system/filterfilename.php“);

include(„system/sys.php“);

$db = new MySQL(’system/dbsettings.php‘);

$db->Connect();

$language = new Language();

if(!isset($_SESSION[‚user‘])) $_SESSION[‚user‘] = new User();

$currentpage = new Page();

$currentpage->loadProperties($GLOBALS[‚db‘]->EscapeString($_GET[‚include‘]));

if(!$_SESSION[‚user‘]->role->canAccess($currentpage)){

$currentpage = new Page();

$currentpage->loadPropertiesById(getSetting(„global“,„global“,„accessdenied“));

}

if(!$_GET[’skin‘]){

include(SkinController::getCurrentSkinPath().„/index.php“);

}

else{

include(’system/skins/‘.$_GET[’skin‘].„/index.php“);

}

?>

Und das ist der Quellcode für die /admin/index.php:

<?PHP

session_start();

function __autoload($class_name){

$namespaces = split(„_“,strtolower($class_name));

if(sizeOf($namespaces) == 3){

if($namespaces[0] == „plugin“){

require_once „../system/plugins/“.$namespaces[1].„/classes/“.$namespaces[2].„.php“;

$imported = true;

}

else if($namespaces[0] == „skin“){

require_once „../system/skins/“.$namespaces[1].„/classes/“.$namespaces[2].„.php“;

$imported = true;

}

}

if(!$imported){

require_once „../system/classes/“.strtolower($class_name).„.php“;

}

}

include(„../system/settings.php“);

include(„../system/filterfilename.php“);

include(„../system/sys.php“);

$db = new MySQL(‚../system/dbsettings.php‘);

$db->Connect();

$language = new Language();

$language->root = „../“;

?>

<!DOCTYPE html PUBLIC „-//W3C//DTD XHTML 1.0 Strict//EN“

„http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>

<html xmlns=“http://www.w3.org/1999/xhtml“ xml:lang=“de“>

<head>

<title>Admin-Bereich</title>

<link href=“/admin/style.css“ rel=“stylesheet“ type=“text/css“ />

<?PHP

CustomCSS::printStylesheet(„{admin}/“.$_GET[‚page‘]);

?>

</head>

<body>

<?PHP

$user = new User();

if($_POST[‚user‘]){

if($user->login($_POST[‚user‘],$_POST[‚password‘])){

$_SESSION[‚username‘] = $_POST[‚user‘];

}

else{

echo „<p>Login Fehlgeschlagen!</p>“;

}

}

if(isset($_SESSION[‚username‘])){

Menu::display(Menu::getIdByName(„{admin}“),

„<ul id=adminmenue>“,

„</ul>“,

“ <li>“,

„</li>“,

„adminmenue“);

if(!$_GET[‚page‘]){

$_GET[‚page‘] = ‚dashboard‘;

}

echo „<div id=content>“;

include(filterfilename(„includes/“.$_GET[‚page‘]));

echo „</div>“;

}

else{

include(‚includes/login.php‘);

}

?>

</body>

</html>

In unseren Templates sollen wir über {LANG:Textstück} auf die Übersetzungen zurückgreifen. Dafür müssen wir in der /system/classes/template.php die Methode replaceLanguageTokens einbauen und diese in der getCode aufrufen:

private function replaceLanguageTokens(){

preg_match_all(„/{LANG:(.+)}/“, $this->template, $matches, PREG_SET_ORDER);

foreach($matches as $match){

$translation = htmlentities($GLOBALS[‚language‘]->getString($match[1]));

$this->template = str_ireplace($match[0],$translation,$this->template);

}

}

public function getCode(){

foreach($this->loops as $key => $array){

$loop_template = implode(n,$array);

$this->template = str_ireplace(‚<!–LOOP(‚.strtoupper($key).‚)–>‘, $loop_template, $this->template);

}

$this->replaceLanguageTokens();

return $this->template;

}

Jetzt werden noch die bisher erstellten Templates mit den Sprachelementen ausgestattet. Alle anderen Texte werden nach und nach ersetzt. Fangen wir an mit system/templates/site_delete.html:

<p>{LANG:SHOULD_PAGE} <strong>{VAR:TITLE}</strong>

{LANG:IRREVOCABLY_DELETED}</p>

<a href=„{VAR:SITESURL}“>{LANG:CANCEL}</a>

<a href=„{VAR:DELETEURL}“>{LANG:DELETE_PAGE}</a>

Weiter geht’s mit system/templates/site_edit.html:

<h1>{LANG:EDIT_PAGE}</h1>

<form name=„form“ action=„{VAR:PATH}“ method=„post“>

<div style=„float:right;width:20%;“>

<label for=„alias“>{LANG:ALIAS}:</label>

<input name=„alias“ value=„{VAR:ALIAS}“ /><br />

<h2>{LANG:RIGHTS}</h2>

<select name=„rights[]“ size=„4“ multiple=„yes“ style=„width:150px;“>

{LOOP:RIGHTS}

<option {VAR:SELECTED} value=„{VAR:VALUE}“>{VAR:NAME}</option>

{/LOOP:RIGHTS}

</select>

<h2>Seitenmen&uuml;</h2>

<select name=„menu“ onchange=„document.form.submit();“>

{LOOP:MENU}

<option {VAR:SELECTED} value=„{VAR:VALUE}“>{VAR:NAME}</option>

{/LOOP:MENU}

</select>

<div id=„menupreview“>

{VAR:MENUPREVIEW}

</div>

<h2>{LANG:META_DATA}</h2>

<label for=„meta-description“>Description:</label>

<input name=„meta-description“ value=„{VAR:METADESCRIPTION}“ /><br />

<label for=„meta-keywords“>Keywords:</label>

<input name=„meta-keywords“ value=„{VAR:METAKEYWORDS}“ /><br />

<label for=„meta-robots“>Robots:</label>

<input name=„meta-robots“ value=„{VAR:METAROBOTS}“ /><br />

</div>

<div>

<input name=„title“ value=„{VAR:TITLE}“ style=„width:70%;height:25px;margin-bottom:5px;font-size:20px;“ /><br />

{VAR:EDITOR}

</div>

</form>

Für neue Seiten muss system/templates/site_new.php geändert werden:

<form action=„{VAR:URL}“ method=„post“>

<table>

<tr>

<td>{LANG:TYPE}:</td>

<td>

<select name=„type“>

{LOOP:TYPES}

<option value=„{VAR:CLASS}“>{VAR:NAME}</option>

{/LOOP:TYPES}

</select>

</td>

</tr>

<tr>

<td>{LANG:ALIAS}:</td>

<td><input name=„alias“ /></td>

</tr>

<tr>

<td></td>

<td><input type=„submit“ value=„{LANG:CREATE}“ /></td>

</tr>

</table>

</form>

Für das Erfolgreiche anlegen /system/templates/site_new_succeeded.html:

<p>{LANG:PAGE_CREATED}</p>

<form action=„{VAR:URL}“ method=„GET“>

<input type=„hidden“ name=„page“ value=„site-edit“ />

<input type=„hidden“ name=„site“ value=„{VAR:ALIAS}“ />

<input type=„submit“ value=„{LANG:EDIT_PAGE}“ />

</form>

Für die Seitenübersicht /system/templates/sites.html:

<h1>{LANG:PAGES}</h1>

{VAR:MESSAGE}

<p>

{VAR:NEWPAGELINK}

</p>

<form action=„{VAR:FORMURL}“ method=„POST“>

<p>

{LANG:PATH}:

{LOOP:PATH}

<a href=„{VAR:URL}“>{VAR:LABEL}</a>

{/LOOP:PATH}

<input name=„name“ style=„width:100px;“ />

<input name=„newFolder“ type=„submit“ value=„Neu“ />

</p>

</form>

<ul style=„list-style-type:none;“>

{LOOP:DIRS}

<li>

<img src=„{VAR:ICON_DIR}“ />

<a href=„{VAR:URL}“>{VAR:LABEL}</a>

</li>

{/LOOP:DIRS}

</ul>

{VAR:DELETEFOLDERLINK}

<table>

<thead>

<td><strong>{LANG:TITLE}</strong></td>

<td><strong>{LANG:ALIAS}</strong></td>

<td><strong>{LANG:ACTIONS}</strong></td>

</thead>

<tbody>

{LOOP:PAGES}

<tr>

<td>{VAR:TITLE}</td>

<td>{VAR:ALIAS}</td>

<td>

<a title=„{LANG:EDIT}“ href=„{VAR:EDITURL}“>

<img src=„{VAR:ICON_EDIT}“ />

</a>

<a title=„{LANG:DELETE}“ href=„{VAR:DELETEURL}“>

<img src=„{VAR:ICON_DELETE}“ />

</a>

</td>

</tr>

{/LOOP:PAGES}

</tbody>

</table>

{VAR:NOPAGES}

Dann gibt es noch die Benutzerliste in /system/templates/user_list.html:

<table>

<thead>

<tr>

<td><strong>{LANG:ID}</strong></td>

<td><strong>{LANG:NAME}</strong></td>

<td><strong>{LANG:ROLE}</strong></td>

<td><strong>{LANG:ACTIONS}</strong></td>

</tr>

</thead>

<tbody>

{LOOP:USERLIST}

<tr>

<td>{VAR:ID}</td>

<td>{VAR:NAME}</td>

<td>{VAR:ROLE}</td>

<td>{VAR:ACTIONS}</td>

</tr>

{/LOOP:USERLIST}

</tbody>

</table>

Dann kommt system/templates/forms/settings.php an die Reihe:

<form style=„float:left;width:600px;“ action=„{VAR:URL}“ method=„post“>

{LANG:ROLE}: {VAR:Roles}

<fieldset style=„width:500px;“>

<legend>Einstellungen &auml;ndern</legend>

<table style=„width:100%“>

{LOOP:SETTINGS}

<tr>

<td>

<label for=„{VAR:PROPERTY}“>

{VAR:DESCRIPTION}

</label>

</td>

<td>

{VAR:CONTROL}

</td>

</tr>

{/LOOP:SETTINGS}

</table>

<br />

<input type=„submit“ name=„save“ value=„{LANG:SAVE}“ />

</fieldset>

</form>

Und zu guter letzt /system/templates/controls/wysiwyg.html:

<script type=„text/javascript“ src=„/system/WYSIWYG/tiny_mce.js“></script>

<script type=„text/javascript“>

tinyMCE.init({

// General options

language : „de“,

mode : „textareas“,

theme : „advanced“,

plugins : „safari,spellchecker,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,imagemanager,filemanager“,

// Theme options

theme_advanced_buttons1 : „bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect,fontselect,fontsizeselect“,

theme_advanced_buttons2 : „cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,code,|,forecolor,backcolor“,

theme_advanced_buttons3 : „tablecontrols,|,visualaid,|charmap,|,fullscreen,spellchecker,|,visualchars,template,blockquote,|,insertfile,insertimage“,

theme_advanced_toolbar_location : „top“,

theme_advanced_toolbar_align : „left“,

theme_advanced_statusbar_location : „bottom“,

theme_advanced_resizing : true,

// Example content CSS (should be your site CSS)

content_css : „css/example.css“,

// Drop lists for link/image/media/template dialogs

template_external_list_url : „template_list.js“,

external_link_list_url : „link_list.js“,

external_image_list_url : „/content/imagelist.php“,

media_external_list_url : „js/media_list.js“,

// Replace values for the template plugin

template_replace_values : {

username : „Some User“,

staffid : „991234“

}

});

</script>

<textarea name=„content“ style=„width:70%;height:500px;“>{VAR:CONTENT}</textarea>

</div>

<div style=„clear:both;“>

<input name=„vorschau“ type=„submit“ value=„{LANG:PREVIEW}“ onclick=„form.action=’/{VAR:ALIAS}.html‘ ; target=’_blank‘ ; return true“ />

<input name=„save“ type=„submit“ value=„{LANG:CHANGE}“ onclick=„form.action='{VAR:URL}‘ ; target=’_self‘ ; return true“ />

Kommentare

Daniel schrieb am 10.05.2010:

en.db ist in Arbeit.

Daniel schrieb am 10.05.2010:

Ich lade mal eine Datei ins Forum unter CMS Erweiterungen.