Les Wikis francophones évolués semblent généralement adopter la possibilité de
MotWiki accentués comme
WebSémantique. Cela paraît être de bon sens puisque les accents sont des marques orthographiques qui affectent le sens : comment sinon faire la différence entre
UnHommeTue et
UnHommeTué ?
Les
MotWiki contenant des caractères accentués donnent des URL partiellement codées ; par exemple
WebSémantique donne :
Pourquoi ces différences ?
Hors la lisibilité dégradée, quelles sont les incidences de l'emploi de telles URL ?
Comment sont-elles implémentées dans les différents moteurs de Wiki ?
Je n'ai pas encore de réponse très claire à toutes ces questions. --
CharlesNepote
Pour détecter un
MotWiki,
WikiNi utilise l'expression rationnelle suivante :
/^[A-Z][a-z]+[A-Z,0-9][A-Z,a-z,0-9]*$/
ce qui veut dire tous les mots :
- débutant par une majuscule ASCII ^[A-Z]
- suivie au moins d'une minuscule ASCII [a-z]+
- puis d'une majuscule ASCII ou d'un chiffre arabe [A-Z,0-9]
- et terminés éventuellement par un nombre illimité de majuscule ASCII ou minuscules ASCII ou chiffre [A-Z,a-z,0-9]*$
Au passage je crois que la virgule doit être supprimée de l'expression rationnelle.
Pour gérer les
MotWikiAccentue, l'expression rationnelle deviendrait :
/^[A-Z\xc0-\xdd][a-z\xe0-\xff]+[A-Z0-9\xc0-\xdd][A-Za-z0-9\xc0-\xdd\xe0-\xff]*$/
ce qui veut dire tous les mots :
- débutant par une majuscule ASCII ou une majuscule accentuée de la table ISO-Latin-1 (de À à Ý) ^[A-Z\xc0-\xdd]
- suivie au moins d'une minuscule ASCII ou d'une minuscucle accentuée de la table ISO-Latin-1 (de à à ÿ) [a-z\xe0-\xff]+
- puis d'une majuscule ASCII ou d'un chiffre arabe ou d'une majuscule accentuée de la table ISO-Latin-1 [A-Z0-9\xc0-\xdd]
- et terminés éventuellement par un nombre illimité de majuscule ASCII ou minuscules ASCII ou chiffre ou majuscule accentuée ou minuscule accentuée [A-Za-z0-9\xc0-\xdd\xe0-\xff]*$
Pour réaliser ce changement dans
WikiNi on peut envisager d'utiliser des constantes définissant chaque groupe de lettres :
- UPPER pour [A-Z\xc0-\xdd]
- LOWER pour [a-z\xe0-\xff]
- UPPER_NUM pour [A-Z0-9\xc0-\xdd]
- CHAR pour [A-Za-z0-9\xc0-\xdd\xe0-\xff]
Nous aurions donc par exemple pour /formatters/wakka.php :
- // wiki links!
- else if (preg_match("/^".UPPER.LOWER."+".UPPER_NUM.CHAR."*$/s", $thing))
- {
- return $wiki->Link($thing);
- }
A priori les modifications portent sur les fichiers :
- /formatters/wakka.php
- wakka.php
La modification ajoute quelques octets de code mais on devrait gagner légèrement en performance du fait de l'emploi des constantes.
La modification est cependant d'importance et je ne voudrais pas tout casser... Si cela vous intéresse, je propose donc de publier une version CVS que l'on teste à plusieurs chez nous avant de la passer en production.
--
CharlesNepote (
CharlesNepoteASuivreEnPriorite)
Oui, je suis intérressé par cette fonctionnalité, d'accord pour une mise à jour du CVS .... --
DavidDelon
La modification proposée est très spécifique à l'ISO-8859-1 (Latin-1). On pourrait préférer utiliser
ereg (les expressions régulières standard POSIX, pas celles de Perl) de la manière suivante :
ereg("^[[:upper:]][[:lower:]]+[[:upper:]][[:alnum:]]*$", $thing). --
ProgFou
- Je ne suis pas un grand spécialiste des expressions rationnelles mais Perl -- et donc preg_* -- comprend les classes Posix. Je n'ai pas testé mais cf. la doc Perl : http://www.bribes.org/perl/docfr/perlre.html
- Par ailleurs, j'ai toujours lu que les preg étaient à préférer aux ereg du fait de meilleures performances.
- Sur le fond je suis d'accord qu'il est souhaitable que la solution ne soit pas spécifique à l'ISO-8859-1 mais on risque alors de tomber sur des problèmes de locale pas piqués des vers... Je suggère de faire des tests en indiquant pour chaque test : le code de l'expression, la locale, l'encodage. -- CharlesNepote
- Je viens de faire quelques tests à ce sujet (avec PHP 4.1.2 et 4.3.3) et ce n'est pas très concluant :
- les fonctions ereg_* ne tiennent pas du tout compte des locales (grmph ...) ;
- les fonctions preg_* en tiennent bien compte (ouf !) ;
- l'utilisation des classes fonctionnent très bien à condition que la locale choisie soit installée, ce qui entraine un pré-requis très lourd pour l'installation du Wiki chez un hébergeur [100% d'accord : c'est inenvisageable -- CharlesNepote] ;
- j'ai testé le support UTF-8 pour voir si on pouvait s'en sortir de cette manière : les fonctions ereg_* ne fonctionnent pas et les fonctions preg_* ne fonctionnent que si la locale choisie n'est pas en UTF-8 (c'est très vraisemblablement un bug) !
- As-tu essayé avec l'option "u" ?
- u (PCRE_UTF8) Cette option désactive les fonctionnalités additionnelles de PCRE qui ne sont pas compatibles avec Perl. Les chaînes sont traitées comme des chaînes UTF-8. Cette option est disponible en PHP 4.1.0 et plus récent. (Source : doc de PhP.) Peut-être peut-on envisager de tester la présence de la locale ? -- CharlesNepote
- J'ai bien essayé avec l'option /u (cf code en commentaire, variable preg_u8) et c'est justement ce qui me laisse penser que c'est un bug : l'option /u permet bien de reconnaître une séquence UTF-8 (variable mort_u8), mais uniquement quand le jeu de caractère de la locale est ISO8859-1, ça ne fonctionne pas correctement quand c'est UTF-8. J'en déduis que soit la locale LC_CTYPE pour le jeu UTF-8 est mal définie (peu probable, mais pourquoi pas), soit la librairie PCRE teste mal la validité du caractère UTF-8 rencontré (plus probable). Pour le test de la présence de la locale, c'est très simple : la locale est absente si setlocale renvoit false. Mais je pense qu'il vaudrait mieux voir ça au moment de la localisation globale de WikiNi. -- ProgFou
- Conclusion : pour le moment on est coincé ! On peut difficilement supporter mieux que l'ISO-8859-1 avec une version de PHP <= 4.3.3 sans devoir alourdir le code. La solution proposée de coder directement les caractères accentués est donc tout à fait acceptable dans le contexte actuel. Il sera toujours temps de revoir le codage quand les logiciels utilisés permettront de faire mieux ... J'entrevois éventuellement une solution de secours vers l'UTF-8 qui serait de travailler en deux passes : une première passe très rapide (avec preg) pour repérer les MotWiki "potentiels", puis un test de validation derrière qui ferait du décodage UTF-8 uniquement sur les mots repérés comme acceptables et contenant des caractère non-ASCII. Je garde cette idée sous le coude pour la migration UTF-8, vu que j'en ai personnellement besoin ... ;-)
- Le est en commentaire de cette page? (pour ne pas trop charger celle-ci).
- -- ProgFou
Pour la virgule, il suffit d'essayer un MotWiki avec une virgule (comme Abc,Def) pour se rendre compte qu'elle est effectivement de trop. --
ProgFou
- Je crois que nous sommes tous d'accord pour la supprimer. -- CharlesNepote
Quant à l'optimisation, l'idéal aurait été d'avoir accès à la fonction
regcomp (standard POSIX, compilation d'une expression régulière en vue d'utilisations multiples), mais elle n'est apparement pas présente dans PHP (au moins juqu'en PHP 4.3.0). Je ne suis pas certain du gain obtenu en découpant l'expression en constantes, hormis niveau lisibilité bien sûr. --
ProgFou
- En Perl, l'option /o permet de compiler le motif une bonne fois pour toute, à condition que l'expression ne contienne pas de données variables : ça ne présente donc aucun intérêt pour nous... L'avantage des constantes n'est peut-être en effet pas flagrant en terme de performance mais, grâce à elles, il devient très facile de modifier/adapter les expressions rationnelles à tes besoins. On peut même envisager de fournir des jeux de constantes avec des valeurs différentes selon une locale paramétrée dans wakka.config.php. -- CharlesNepote
- Ok, grace à tes commentaires, ceux de PatrickPaul, le fait que PHP5 optimise en ne faisant plus de copie des variables, et quelques tests, je commence à entrevoir pourquoi les constantes sont plus efficaces : elles sont disponibles globalement sans avoir besoin d'en faire une copie locale à une fonction et l'interprétation du code PHP tient probablement compte du fait qu'elles ne sont pas modifiables. Le gain doit surtout se voir sur les traitements répétitifs. -- ProgFou
Dans tous les cas, je ne vois pas ici la conversion des accents depuis l'URL reçue et au moment de la génération des pages Wiki ; est-ce déjà implémenté (avec des
urldecode/
urlencode bien placés) ? --
ProgFou.
- Non, ce n'est pas implémenté et je ne suis pas très à l'aise avec ces fonctions. Si tu veux faire des essais ils sont les bienvenus. -- CharlesNepote.
- Le principe n'est heureusement pas trop compliqué. Chaque fois que l'on doit afficher du contenu web, il y a essentiellement deux fonctions à utiliser : urlencode pour tout ce qui doit se trouver dans un lien (donc les URL/URI), et htmlspecialchars pour tout le reste (contenu des attributs, contenu d'un textarea, ...). On utilise urldecode pour récupérer le contenu original de PHP_SELF ou encore QUERY_STRING. Il y a une page assez détaillée sur le sujet ici (doc de PHP) : PHP et HTML. -- ProgFou
Quoi qu'on décide, je propose dans un premier temps de construire les expressions rationnelles uniquement à partir de constantes. Cela permettra :
- de facilement adapter le code en fonction de nos décisions
- d'améliorer la lisibilité du code.
Nous aurions donc :
- UPPER pour [A-Z]
- LOWER pour [a-z]
- UPPER_NUM pour [A-Z0-9]
- CHAR pour [A-Za-z0-9]
Dès lors, il devient facile pour un développeur de personnaliser ces chaines. S'il veut ajouter la possibilité du souligné-bas, il lui suffira de modifier comme suit :
- LOWER devient [a-z_]
- CHAR devient [A-Za-z0-9_]
[En ce qui me concerne ça me va. :) --
ProgFou] [C'est mis dans le CVS. --
ProgFou 20040720]
A priori les modifications portent sur les fichiers :
- /formatters/wakka.php
- wakka.php
- Link() où l'on trouve /^([A-Z][A-Z,a-z]+)[:]([A-Z,a-z,0-9]*)$/s pour détecter un lien interwiki
- IsWikiName?() où l'on trouve : /^[A-Z][a-z]+[A-Z,0-9][A-Z,a-z,0-9]*$/ pour détecter si la chaine est un MotWiki
- Action() où l'on trouve : /^([A-Za-z0-9]*)\/?(.*)$/ et /([A-Za-z0-9]*)=\"(.*)\"/U
- le corps du programme :
- // split into page/method
- if (preg_match("#^(.+?)/([A-Za-z0-9_]*)$#", $wiki, $matches)) list(, $page, $method) = $matches;
--
CharlesNepote
En faisant un
grep -ri '.*\[[^-+"$<].*\-.*\]' wikini (version CVS) j'obtiens aussi :
- actions/mychanges.php :
!preg_match("/[A-Z,a-z]/", $firstChar)
- actions/mypages.php :
!preg_match("/[A-Z,a-z]/", $firstChar)
- actions/pageindex.php :
!preg_match("/[A-Z,a-z]/", $firstChar)
- actions/trail.php :
preg_replace("/^([A-Za-z0-9]+\)|-)/",'',$line) et
preg_replace("/^(\[\[.*\]\]|[A-Za-z0-9]+)\s*(.*)$/","$1",$line)
- handlers/page/addcomment.php :
preg_match("/^Comment([0-9]+)$/", $latestComment["tag"], $matches)
Ce qui veut dire que ces constantes seront même utiles globalement.
--
ProgFou
Bon, je viens de l'implémanter dans mon Wiki, et ça a fait ressortir pas mal de problèmes... Tout d'abord il faut faire bien attention à la détection des
MotWiki et certains tests actuels ont du être modifiés (dans le test lui même, je ne parle pas des remplacement de constantes). Ensuite, il a fallu ajouter des urlencode à quelques endroits (je n'ai pas encore
tout vérifié, mais ça a l'air d'aller). Et pour finir, mais pas le moindre des soucis,
MySQL semble faire toutes ses recherches dans le jeu de charactère dans lequel il a été lancé, en reconnaissant les caractères accentués comme étant identiques à leur version sans accent ! Du coup les pages
UnHommeTue et
UnHommeTué ne sont pas différenciables !! Grmph... --
ProgFou
Bon. On peu commencer par les urlencode (tu peux publier ton code ? il y a peu d'endroits je suppose ?). Le problème de
MySQL me semble mineur et n'altère pas véritablement le fonctionnement.
Peux-tu nous en dire plus sur les modifications des tests actuels (code ?). Je veux bien recetter à fond tout ça. --
CharlesNepote
J'ai modifié mon propre
WikiNi (privé, désolé) dès ma première installation de
WikiNi, afin de créer mon utilisateur :
OlivierMengu?é.
C'était avant de découvrir cette page.
Voici ce que j'ai modifié pour que toutes les lettres accentuées de ISO-8859-1 soient supportées :
wakka.php:
- else if (preg_match("/[^[:alnum:]]/", $tag))
+ // OM Majuscules : À-ÖØ-Þ Minuscules : ß-öø-ÿ Tout : À-ÖØ-öø-ÿ
+ else if (preg_match("/[^[:alnum:]À-ÖØ-öø-ÿ]/", $tag))
- function IsWikiName($text) { return preg_match("/^[A-Z][a-z]+[A-Z,0-9][A-Z,a-z,0-9]*$/", $text); }
+ function IsWikiName($text) { return preg_match("/^[A-ZÀ-ÖØ-Þ][a-zß-öø-ÿ]+[A-Z0-9À-ÖØ-Þ][A-Za-z0-9À-ÖØ-öø-ÿ]*$/", $text); }
formatter/wakka.php:
- else if (preg_match("/^[A-Z][a-z]+[A-Z,0-9][A-Z,a-z,0-9]*$/s", $thing))
+ else if (preg_match("/^[A-ZÀ-ÖØ-Þ][a-zß-öø-ÿ]+[A-Z0-9À-ÖØ-Þ][A-Za-z0-9À-ÖØ-öø-ÿ]*$/s", $thing))
- "\b([A-Z][a-z]+[A-Z,0-9][A-Z,a-z,0-9]*)\b|".
+ "(?![^A-Za-z0-9À-ÖØ-öø-ÿ_])([A-ZÀ-ÖØ-Þ][a-zß-öø-ÿ]+[A-Z0-9À-ÖØ-Þ][A-Za-z0-9À-ÖØ-öø-ÿ]*)(?=[^A-Za-z0-9À-ÖØ-öø-ÿ_])|".
Notez sur la dernière ligne le remplacement de \b par des expressions un peu plus complexes, mais qui donnent la même fonctionnalité en ajoutant le support des accents. '\b' est une condition indiquant une frontière de mot : on veut que le mot Wiki ne soit pas à l'intérieur d'un mot. Les conditions (?![...]) (condition "pas un caractère alphanum qui précède") et (?=[...]) (condition "pas un caractère alphanum qui suit"). Avec l'ancienne regexp,
MotWikiAccentue doit matcher (notez ce qui se passe dans é
MotWikiAccentueÉ ou É
MotWikiAccentueÉ), mais pas aMotWikiAccentue, ni AMotWikiAccentue, ni _MotWikiAccentue, ni MotWikiAccentue_. Avec la nouvelle :
OlivierMengu?é matchera, mais pas é
OlivierMengu?é, ni É
OlivierMengu?é, ni _OlivierMengué ou
OlivierMengu?é_.
En lisant cette page je découvre le problème de
MySQL rapporté par
ProgFou. Il va falloir que je teste cela.
--
OlivierMengu?é (qui espère bien pouvoir bientôt créer sa micro-page perso sur
WikiNi.net avec le mot wiki qui va bien).
Pour compléter : j'ai appliqué mon patch sur la version 0.4.1rc.
Je n'ai par rencontré de problème concernant les URLs : elle ne sont pas encodées, mais comme le jeu de caractères iso-8859-1 est spécifié, cela ne pose pas de problème.
Mais pour une vraie internationalisation il faudrait envisager un jeu de caractères plus large, ce que
MySQL ne semble pas supporter.
--
OlivierMengu?é
- Attention : le fait que le jeu de caractères soit spécifié ne change rien au problème. Certains navigateurs, comme Mozilla, fonctionnent bien mais IE 5.0 donnera OlivierMengué. Il est donc absolument nécessaire d'encoder les URLs. -- CharlesN?épote
- En effet, tu as raison car IE encode les URLs en UTF-8 (paramètre par défaut dans les options avancées). Mais en pratique ça fonctionne. Je vais un peu creuser pour faire les choses proprement. -- OlivierMengu?é.
Bonjour Olivier, content de voir que tu travailles aussi sur ce sujet ! Charles et moi avions déjà discuté implémentation, comme tu peux le voir plus haut dans cette page, et j'avais déjà fait une implémentation sur
WikiTeki en suivant ces idées. Je vais publier ici mon .diff (d'ici quelques jours) afin que nous puissions continuer les discussions et intégrer une version commune au tronc actuel. --
ProgFou