Zur Navigation

Sprachweiche [3]

mit weiteren seltsamen RegEx-Problemen

21 Ranma (Gast)

In $Babelfish habe ich eine kleine Korrektur vorgenommen, auf gd-ie (eine Variante, um irisches Gälisch zu bezeichnen) muß natürlich vor gd (schottisches Gälisch) geprüft werden.


$Babelfish=array(array('中文 (繁體/正字)','pu','zh-tw','zh-hk','zh-hant','zh-cht'),array('中文 (简体)','cn','zh-cn','zh-sg','zh-hans','zh- chs'),array('Ελληνικά','el'),array('Magyar','hu'),array('Íslenska','is'),array('Bahasia Indonesia','id','in'),array('Kalaallisut','kl'),array('ᐃᓄᒃᑎᑐᑦ','iu'),array('Català','ca'),array('العربية','ar'),array('עִבְרִית','he'),array('Gaeilge','ga','gd- ie'),array('Gàidhlig','gd'),array('Italiano','it'),array(' 日本語','ja','jp'),array('韓國語/朝鮮語 ','ko'),array('Latviešu','lv'),array('Lietuvių','lt'),array('Македонски','mk'),array('Malti','mt'),array('Norsk','no','nn','nb'),array(' ພາສາລາວ','lo'),array('کوردی','ku'),array('ภาษา ไทย','th'),array('ქართული','ka'),array('Sámegiella','sz'),array('Slovenščina','sl'),array('Slovenčina','sk'),array('Српски','sr'),array('Русский','ru'),array('Suomi','fi'),array('Français','fr'),array('Bosanski','bs'),array('Беларуская','be'),array('Română','ro'),array('Português','pt'),array('Polski','pl'),array('Svenska','sv'),array('Türkçe','tr'),array('Українська','uk'),array('Nederlands','nl'),array('Eesti','et'),array('Български','bg'),array('Euskara','eu'),array('Shqip','sq'),array('Hrvatski','hr'),array('Čeština','cs','cz'),array('Dansk','da'),array('اردو‎','ur'),array('Tiếng Việt','vi'),array('יידיש‎','yi','ji'),array('Español','es'),array('lingua latina','la'),array('فارسی‎','fa'),array('English','en'),array('Deutsch','de'));
// Außer einer kleinen Korrektur der gleiche zweidimensionale Array
// wie in Beitrag #1.

if(!isset($_SESSION['language'])):
  $short=array(); $long=array();
  // meiner Erfahrung nach funktionieren Arrays einfach nicht, wenn man sie nicht vorformatiert.

  if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])):
      foreach($Babelfish as $Sprache):
        $part=$Sprache;
        // diese Variable (immernoch ein Array) wird mit unterschiedlichen Indizes gebraucht.
        $dummy0=array_shift($part);
        // schon sind die Indizes unterschiedlich, der Eigenname der Sprache ist nicht mehr
        // in $part, wo nur auf die Kürzel geprüft wird.
        $test=FALSE;
        foreach($part as $code):
            if(preg_match('@(\W|^)'.$code.'@ismuU',$_SERVER['HTTP_ACCEPT_LANGUAGE'])):
              $test=TRUE;
              $where=strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'],$code);
              $long[$where]=$Sprache[0];
              $short[$where]=$Sprache[1];
            endif;
        endforeach;
        // Das war das Suchen nach Treffern mittels einem Regulärem Ausdruck.
      endforeach;

      if(preg_match('@(\W|^)zh@ismuU',$_SERVER['HTTP_ACCEPT_LANGUAGE'])and!in_array('cn',$short)and!in_array('pu',$short)):
        $long[strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'],'zh')]='中文 (繁體/正字)';$short[strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'],'zh')]='pu';$long[strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'],'zh')+1]='中文 (简体)';$short[strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'],'zh')+1]='cn';
      endif;
      // Das Kürzel zh ist nicht eindeutig, sondern kann sowohl auf traditionelles als
      // auch auf vereinfachtes Chinesisch verweisen. Jetzt sind zwei Einträge daraus
      // geworden. Der Index für den zweiten ließ sich problemlos um eines hochzählen,
      // weil zh zwei Zeichen lang ist.
  else:
      $long[0]='English';
      $short[0]='en';
  endif;

  // Falls die Umgebungsvariable garnicht erst gesetzt ist, gehen wir mal davon aus,
  // daß sich das um einen Ami handeln muß, der nicht weiß, daß es außerhalb
  // Nordamerikas noch andere Gegenden mit anderen Kulturen und Sprachen gibt.
  ksort($short); ksort($long);
  // Komischerweise funktioniert die Sortierung nur mit ksort(), obwohl es sich eigentlich
  // weiterhin um numerische Arrays handeln sollte.

  $prefered=array(array(),array()); $alltogether=array(array(),array());
  // am besten gleich zweidimensional vorformatiert, um keine bösen Überraschungen zu erleben.
  $prefered=array_map(NULL,$long,$short);
  // aus zwei eindimensionalen Arrays wird ein zweidimensionales gemacht.
  // Bis hierhin war es relativ einfach.
  $prefered_assoc=array(); $Babelfish_assoc=array(); $without_assoc=array(); $alltogether_assoc=array();
  for($i=1;$i<5;$i++):
      ${'dummy'.$i}=array();
  endfor;
  // vorformatieren von Arrays ist echt wichtig.

  foreach($Babelfish as $part):
      $dummy1[]=$part[0];
      $dummy2[]=$part[1];
  endforeach;
  // Zerlegung des zweidimensionalen Arrays in zwei eindimensionale Arrays.
  foreach($prefered as $part):
      $dummy3[]=$part[0];
      $dummy4[]=$part[1];
  endforeach;
  // Das Gleiche. Auf den ersten Blick könnte es so aussehen als wären $dummy3 und $dummy4
  // gleich $long und $short; die Werte sind zwar gleich, aber die Indizes nicht.
  // Die Indizes folgten bei $long und $short nicht direkt aufeinander, das ist aber
  // notwendig, damit $without hintendran angehängt wird und nicht untergemischt.

  $Babelfish_assoc=array_combine($dummy1,$dummy2);
  $prefered_assoc=array_combine($dummy3,$dummy4);
  // Umwandlungen in assoziative Arrays.
  $without_assoc=array_diff_assoc($Babelfish_assoc,$prefered_assoc);
  // Das geht. Zweidimensionale Arrays hätten sich nicht subtrahieren lassen.
  $alltogether_assoc=array_merge($prefered_assoc,$without_assoc);
  // Zwei der assoziativen Arrays aneinanderhängen. Schon fast das gewünschte Ergebnis.
  $part=array_keys($alltogether_assoc);
  // Die Schlüssel des vorläufigen Ergebnisses (das sind übrigens die Eigennamen der Sprachen)
  // in ein eigenes Array abtrennen. Möglicherweise hätte es völlig ausgereicht, nur die
  // Hilfsvariablen miteinander zu vereinen?
  $alltogether_assoc=array_values($alltogether_assoc);
  // Die Werte des vorläufigen Ergebnisses wieder numerisch indizieren.
  $alltogether=array_map(NULL,$part,$alltogether_assoc);
  // Die beiden eindimensionalen Arrays mit den beiden Teilen des vorläufigen Ergebnisses
  // zu einem zweidimensionalem Array mit dem endgültigem Ergebnis zusammenfügen.
  // Dadurch kann weiterhin in anderen Teilen des Skriptes wie bisher auf eine
  // zweidimensionale Variable zugegriffen werden, so daß dort keine Änderungen
  // erforderlich sind. Der Teil innerhalb des IF-Blocks (der ganz außenherum)
  // funktioniert bis hierher.

  $_SESSION['language']=array(array(),array());
  // Das Vorformatieren von Arrays hat schon manches Problem gelöst, aber hier bringt
  // es anscheinend nichts.
  $_SESSION['language']=$alltogether;
  // Damit nicht alles immer wieder durchlaufen werden muß, wird das Endergebnis in eine
  // SESSION-Variable gepackt und mit dem IF-Block ganz außenherum abgefragt, ob die
  // schon gesetzt ist. Das führt dazu, daß keine Auswahloptionen mehr angezeigt werden.
  // Und zwar nachdem im Rest des Skriptes überall $_SESSION['language'] für $alltogether
  // gesetzt wurde und zwar ausschließlich per copy&paste, damit sich nichtmal ein
  // Tippfehler einschleichen konnte.
endif;

Es war ziemlich zeitraubend, alles so umzuformatieren, daß es den Ansprüchen in deinem Forum einigermaßen entspricht. Aber falls ich dafür erfahre, was da schließlich noch schiefgelaufen ist, dann hat sich das gelohnt.
Ranma

03.10.2015 02:19

22 Ranma (Gast)

Sorry, an manchen Stellen habe ich noch einzufügende Zeilenumbrüche übersehen. Die Stellen waren bei mir im Texteditor automatisch umgebrochen worden.
Ranma

03.10.2015 02:23

23 Jörg

Ich frage mich, ob der Aufbau der "without" Arrays nicht einfacher schon in der zweiten Schleife erfolgen kann, also so in der Art:

        foreach($part as $code):
            if(preg_match('@(\W|^)'.$code.'@ismuU',$_SERVER['HTTP_ACCEPT_LANGUAGE'])):
              $test=TRUE;
              $where=strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'],$code);
              $long[$where]=$Sprache[0];
              $short[$where]=$Sprache[1];
            else:
              $long_without[]=$Sprache[0];
              $short_without[]=$Sprache[1];
            endif;
        endforeach;

Dann wäre das nachträgliche und umständliche Bearbeiten der Arrays nicht mehr nowendig

03.10.2015 18:51

24 Ranma (Gast)

Deswegen bin ich kein Programmierer. Ich habe die Möglichkeit wahrscheinlich einfach nur übersehen. Ob sie funktioniert, finde ich auch nur durch ausprobieren heraus. Das hat aber keine Priorität, weil es im Moment funktioniert.

Die anderen Probleme mit der Sprachweiche sind gelöst.

Dann wäre hier aber ein OR statt eines AND angebracht. Bei einem AND werden nur Ergebnisse geliefert, in denen der Begriff in beiden Sprachen identisch ist. "handy" würde z.B. keine Ergebnisse liefern, weil "handy" ("handlich") != "Handy" ("mobile phone")

Mit OR funktioniert es nicht anders als mit AND, nämlich garnicht. Produziert sogar die gleiche Fehlermeldung.

Man hat dann zumindest einen Kontextwechsel weniger

Dafür funktioniert es mit dem Kontextwechsel.

Was mich daran stört ist, daß einige der Spalten auf NOT NULL gesetzt sind.

Hattest du beim Anlegen der Tabelle was anderes definiert?

Nein, ich hatte NOT NULL definiert. Es sind aber völlig leere Einträge vorhanden. Für MySQL ist ein Eintrag aus nur leeren Strings ein nichtleerer Eintrag. Ich kann mich vage erinnern, davor irgendwo und irgendwann mal gewarnt worden zu sein. Jetzt prüft mein Skript auf mögliche leere Einträge und ignoriert sie. NOT NULL in MySQL erscheint mir daher als nicht besonders sinnvoll. Schadet aber auch nicht.

Die leeren Einträge habe ich über phpMyAdmin gelöscht. Bei der Gelegenheit konnte ich gleich beobachten, daß es jetzt Lücken in der Zählung des Indexes gibt. Ich weiß noch nicht, ob das irgendwie stört. Ein Verbessern der in die Datenbanktabelle eingetragenen Daten will ich nämlich später noch ermöglichen.

Weil es so viele Zeilen Code waren, dachte ich, daß ich das Ganze am besten in eine IF-Verzweigung und das Ergebnis in eine SESSION-Variable packe. Leider führt das dazu, daß die ganze Arbeit umsonst war! Also die gesamte Auswertung von $_SERVER['HTTP_ACCEPT_LANGUAGE'] befindet sich schließlich in $_SESSION['LANGUAGE'] aber diese Variable zu verwenden führt zu einer völlig leeren Auswahlliste. Immerhin ohne Fehlermeldung.

Hier hat mir dein Forum auf unerwartete Weise geholfen. Ich benenne dafür die Variablen um. Die Umbenennung sorgte dafür, daß es auch über die SESSION-Variable funktioniert. Eine kurze Recherche erbrachte, daß es für das Problem anscheinend nur einen Präzedenzfall gab. Damals wurden für dessen Auftreten drei Bedingungen identifiziert:
1. Ältere PHP-Version,
2. register_globals=on,
3. eine andere Variable hat den gleichen Namen wie der Index des $_SESSION-Feldes.
Aber diese drei Bedingungen waren bei mir nicht gegeben und sind darum nicht erforderlich. Stattdessen braucht es nur zwei:
1. Man versucht einen Array in einem $_SESSION-Feld unterzubringen. Träte der Fehler auch bei anderen Variablen auf, dann wäre er besser bekannt.
2. eine andere Variable hat (Achtung:) bei Ignorierung der Groß-klein-Schreibung (!) einen gleichen Namen wie der Index des $_SESSION-Feldes.
Eigentlich sollte PHP Groß-klein-Schreibung nicht ignorieren. Darum ist das ein seltsamer und seltener Fehler von $_SESSION und wird durch Umbenennung umgangen.

Ich werde wohl langsam besser. Wesentliche Teile meines Wörterbuches funktionieren schon. Jetzt kann ich mich an Experimente mit dem Ziel der Lokalisierung wagen. Außerdem habe ich bei der Suche nach gleichbenannten Variablen festgestellt, daß ich meine Sicherheitsabfrage (ein CAPTCHA in Form eines Drop-down-Menüs) noch implementieren muß.
Ranma

04.10.2015 05:52

25 Ranma (Gast)

Weil es kein großer Aufwand war, habe ich die kürzere Version mal ausprobiert. Das Ergebnis war eigenartig. Mit dem Firefox überprüft war alles gleich. Mit dem Konquerer überprüft (wo ich nicht herausgefunden habe, wo man die bevorzugten Sprachen einstellen könnte, weswegen der nur auf Deutsch und Englisch eingestellt ist) befinden sich im Auswahlmenü erst die Einträge aus $prefered (hier zwei Stück) und danach zwei leere Einträge, dann ist die Auswahlliste auch schon zu Ende. Eigenartig finde ich das, weil die Verarbeitung nur auf dem Server stattfinden sollte. Das Skript müßte also jedesmal die gleiche Liste ausliefern. Das kann ich mir nur so erklären, daß die kürzere Version (tatsächlich ist sie garnicht so viel kürzer, weil schließlich doch noch alle Teile zum zweidimensionalen Array zusammengesetzt werden müssen) irgendwie durch unbemerkt seltsame Eingaben beeinflußt werden. Also eigentlich habe ich keine richtige Erklärung dafür.

Darum bin ich wieder auf die vorherige Version mit einer umständlichen Bearbeitung assoziativer Arrays zurückgegangen. Schon funktioniert alles wieder tadellos.
Ranma

05.10.2015 04:59

26 Jörg

Dann wäre hier aber ein OR statt eines AND angebracht. Bei einem AND werden nur Ergebnisse geliefert, in denen der Begriff in beiden Sprachen identisch ist. "handy" würde z.B. keine Ergebnisse liefern, weil "handy" ("handlich") != "Handy" ("mobile phone")

Mit OR funktioniert es nicht anders als mit AND, nämlich garnicht. Produziert sogar die gleiche Fehlermeldung.

Die Query in Beitrag 11 ist auch kein gültiges SQL. Korrekt wäre in etwa so:

SELECT Ziel, Zielsprache 
FROM Woerterbuch 
WHERE Quelle = 'Suchterm'
OR Ziel = 'Suchterm'

Die leeren Einträge habe ich über phpMyAdmin gelöscht. Bei der Gelegenheit konnte ich gleich beobachten, daß es jetzt Lücken in der Zählung des Indexes gibt. Ich weiß noch nicht, ob das irgendwie stört.

An und für sich nicht. Mit "OPTIMIZE TABLE" könnte man die Tabelle nach Löschaktionen noch optimieren, so dass dann auch weniger Speicherplatz belegt wird.

Mit dem Konquerer überprüft (wo ich nicht herausgefunden habe, wo man die bevorzugten Sprachen einstellen könnte, weswegen der nur auf Deutsch und Englisch eingestellt ist) befinden sich im Auswahlmenü erst die Einträge aus $prefered (hier zwei Stück) und danach zwei leere Einträge, dann ist die Auswahlliste auch schon zu Ende.

Wie lautet denn HTTP_ACCEPT_LANGUAGE im Falle von Konqueror?

<?php echo $_SERVER['HTTP_ACCEPT_LANGUAGE']; ?>

05.10.2015 09:21

27 Ranma (Gast)

Darin steht:

de,en-US;q=0.9,en;q=0.8

Daraus macht mein Skript die Variablen $long und $short, die mit print_r() ausgegeben folgenden Inhalt haben:

Array ( [0] => Deutsch [3] => English )
Array ( [0] => de [3] => en )

Konquerer ist nicht in der Lage, Unicode automatisch zu erkennen. Aber das dürfte hier eigentilch nichts ausmachen. Die Inhalte von $long und $short würde ich auch so erwarten. Also das Verhalten bleibt rätselhaft. Darum bleibe ich lieber bei der umständlichen Lösung.
Ranma

07.10.2015 02:39

28 Jörg

Bei mir funktioniert das so:

<?php

// Test von Konquerors HTTP-Header:
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'de,en-US;q=0.9,en;q=0.8';

$Babelfish=array(array('中文 (繁體/正字)','pu','zh-tw','zh-hk','zh-hant','zh-cht'),array('中文 (简体)','cn','zh-cn','zh-sg','zh-hans','zh- chs'),array('Ελληνικά','el'),array('Magyar','hu'),array('Íslenska','is'),array('Bahasia Indonesia','id','in'),array('Kalaallisut','kl'),array('ᐃᓄᒃᑎᑐᑦ','iu'),array('Català','ca'),array('ﺎﻠﻋﺮﺒﻳﺓ','ar'),array('עִבְרִית','he'),array('Gaeilge','ga','gd- ie'),array('Gàidhlig','gd'),array('Italiano','it'),array(' 日本語','ja','jp'),array('韓國語/朝鮮語 ','ko'),array('Latviešu','lv'),array('Lietuvių','lt'),array('Македонски','mk'),array('Malti','mt'),array('Norsk','no','nn','nb'),array(' ພາສາລາວ','lo'),array('کﻭﺭﺩی','ku'),array('ภาษา ไทย','th'),array('ქართული','ka'),array('Sámegiella','sz'),array('Slovenščina','sl'),array('Slovenčina','sk'),array('Српски','sr'),array('Русский','ru'),array('Suomi','fi'),array('Français','fr'),array('Bosanski','bs'),array('Беларуская','be'),array('Română','ro'),array('Português','pt'),array('Polski','pl'),array('Svenska','sv'),array('Türkçe','tr'),array('Українська','uk'),array('Nederlands','nl'),array('Eesti','et'),array('Български','bg'),array('Euskara','eu'),array('Shqip','sq'),array('Hrvatski','hr'),array('Čeština','cs','cz'),array('Dansk','da'),array('ﺍﺭﺩﻭ<200e>','ur'),array('Tiếng Việt','vi'),array('יידיש<200e>','yi','ji'),array('Español','es'),array('lingua latina','la'),array('ﻑﺍﺮﺳی<200e>','fa'),array('English','en'),array('Deutsch','de'));
// Außer einer kleinen Korrektur der gleiche zweidimensionale Array
// wie in Beitrag #1.

// Bedingung auskommentiert, damit Werte nicht aus der Session gelesen werden:
//if(!isset($_SESSION['language'])):
  $short=array(); $long=array();
  // meiner Erfahrung nach funktionieren Arrays einfach nicht, wenn man sie nicht vorformatiert.

  if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])):
      foreach($Babelfish as $Sprache):
        $part=$Sprache;
        // diese Variable (immernoch ein Array) wird mit unterschiedlichen Indizes gebraucht.
        $dummy0=array_shift($part);
        // schon sind die Indizes unterschiedlich, der Eigenname der Sprache ist nicht mehr
        // in $part, wo nur auf die Kürzel geprüft wird.
        $test=FALSE;
        foreach($part as $code):
            if(preg_match('@(\W|^)'.$code.'@ismuU',$_SERVER['HTTP_ACCEPT_LANGUAGE'])):
              $test=TRUE;
              $where=strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'],$code);
              $long[$where]=$Sprache[0];
              $short[$where]=$Sprache[1];
            else:
              $long_without[]=$Sprache[0];
              $short_without[]=$Sprache[1];
            endif;
        endforeach;
        // Das war das Suchen nach Treffern mittels einem Regulärem Ausdruck.
      endforeach;

echo '<pre>';                                                                   
print_r ($long);                                                                
print_r ($short);                                                               
print_r ($long_without);                                                        
print_r ($short_without);                                                       
echo '</pre>';

exit;

wurden die Werte vielleicht noch aus $_SESSION['language'] ausgelesen?

07.10.2015 14:40 | geändert: 07.10.2015 14:44

29 Ranma (Gast)

Bevor ich den ELSE-Teil einfügte, funktionierte die ganze Sache. Das aus der SESSION-Variablen zu lesen kann kaum dazu führen, daß es dann doch nicht funktioniert. Deswegen habe ich den ELSE-Teil wieder herausgenommen und bin zur umständlichen Bearbeitung assoziativer Arrays zurückgekehrt, was funktioniert. So viel mehr Zeilen Code sind es aber auch nicht. Am Ende soll schließlich auf jeden Fall ein zweidimensionaler Array herauskommen, weil andere Teile im Skript (zur Zeit an drei Stellen) den erwarten.

Die Query in Beitrag 11 ist auch kein gültiges SQL. Korrekt wäre in etwa so:

SELECT Ziel, Zielsprache
FROM Woerterbuch
WHERE Quelle = 'Suchterm'
OR Ziel = 'Suchterm'

Wäre die Query in Beitrag 11 korrekt gewesen, dann hätte sie sicherlich nicht zu einer Fehlermeldung geführt. So weit war mir das schon einigermaßen klar. Aber dieser Verbesserungsvorschlag führt dazu, daß aus der Spalte Ziel dann 'Suchterm' ausgelesen wird, wenn explizit 'Suchterm' in die Frage eingegeben wurde. Steht in Woerterbuch also

INDEX        QUELLSPRACHE        QUELLE        ZIELSPRACHE        ZIEL
1                 de           Wörterbuch       en              dictionary
2                 es           dictionario      de              Wörterbuch
und gesucht wird nach dem Wort 'Wörterbuch', dann soll die Ausgabe lauten:

en        dictionary
es        dictionario

Deine Verbesserung führt aber zu:

en        dictionary
de        Wörterbuch

Das ist nicht so ganz der Sinn der Sache. Da muß ich wohl bei zwei Kontexten bleiben.
Ranma

08.10.2015 06:39

30 Jörg

Du kannst die Felder auch in Abhängigkeit davon selektieren, welches Feld dem Suchterm entspricht

SELECT IF(Quelle = 'Suchterm', Zielsprache, Quellsprache) AS Sprache,
       IF(Quelle = 'Suchterm', Ziel, Quelle) AS Wort
FROM Woerterbuch
WHERE Quelle = 'Suchterm'
OR Ziel = 'Suchterm'
ORDER BY Sprache

08.10.2015 16:02