momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Rezolvarea practică a unei chestiuni de bidirecţionalitate

Django | HTML | Python | javaScript
2014 sep

Există "dreptaci" și "stângaci", există microprocesoare "little endian" - Intel reprezintă numărul întreg 0x123456 prin secvența de octeți consecutivi 0x00, 0x56, 0x34, 0x12 - și "big endian" - Motorola îl va reprezenta prin 0x12, 0x34, 0x56, 0x00 - și bineînțeles, există limbi - arabă, ebraică - în care se scrie de la dreapta spre stânga. Nimic nou sub soare…

Pentru a reda corect textele care îmbină caractere și segmente de tip LTR și respectiv RTL, a fost necesară elaborarea unui algoritm special - BiDi; browserele, procesoarele de text și în general, orice funcție care generează text bidirecțional - trebuie să țină seama de un asemenea algoritm.

Imaginea alăturată redă o instanță a widget-ului PGN-browser; "titlul" partidei este un text bidirecțional și este redat greșit!

Titlul respectiv a fost generat prin javaScript, combinând conținutul tagurilor PGN White, Black și Result (în această ordine); dar aici, Black este de tip RTL și - neurmând vreun semn care să "stopeze" direcția - rezultatul a fost alipit tot în direcția RTL (apărând astfel ca fiind "0-1", în loc de "1-0").

Căutând textul HTML generat pentru titlul respectiv - folosind eventual, submeniul View Generated Source - găsim:

<div class="GameInfo"><p><b>vlad.bazon - اشرف الشايب</b> <i>1-0</i></p></div>

Încercăm să corectăm lucrurile plecând de la fragmentul HTML tocmai redat. Întâi, îl salvăm ca atare - fără nici un adaos - într-un fișier "bidi.html"; încărcând acest fișier în browser, obținem:

Tagurile apar în ordinea corectă, dar numele pentru Black este redat "incorect"; să completăm "bidi.html" pentru a furniza browserului informațiile necesare interpretării corecte a numelor:

<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
    <title>BiDi</title>
</head>
<div class="GameInfo"><p>vlad.bazon - اشرف الشايب <i>1-0</i></p></div>

Acum numele apare "corect" (datorită specificației "utf-8" pentru charset), dar tagurile nu mai apar în ordinea corectă… Rezultatul trebuie scris totdeauna în direcția LTR și putem forța aceasta (în locul direcționalității contextuale) ambalându-l într-un tag <span dir="ltr">; o altă soluționare constă în a izola numele respectiv într-un context direcțional propriu - ambalând numele într-un element <span dir="auto"> (bineînțeles, nu ="rtl": numai unele nume sunt de tip RTL):

<p>vlad.bazon - اشرف الشايب <span dir="ltr"><i>1-0</i></span></p>
<p>vlad.bazon - <span dir="auto">اشرف الشايب</span> <i>1-0</i></p>

Ca urmare a micului experiment redat mai sus, putem în sfârșit să corectăm fragmentul javaScript care generează titlul partidei (din cadrul funcției _init() a PGN-browser-ului nostru):

if (this.k_Info { // sets the GameInfo content ("titlul" partidei)
    var ihtml = ["<p><span dir='auto'>", this.tags['White'], "</span>"];
    ihtml.push(" - ", "<span dir='auto'>", this.tags['Black'], "</span>");
    ihtml.push(" <em>", this.tags['Result'], "</em>");
    this.k_Info.html(ihtml.join(''));
};

Dar mai avem un loc în care trebuie să corectăm lucrurile, în privința bidirecționalității - de data aceasta implicând Python. Din aplicația slightchess (v. [1-4]) avem aici o listă de cinci partide, dintre care a fost instanțată prin PGN-browser a treia partidă:

Titlul acestei partide este redat corect pe diagrama asociată (dat fiind că am operat în PGN-browser, modificarea javaScript ilustrată mai sus), dar apare incorect în lista partidelor (vezi itemul din mijloc al acesteia: câmpul pentru rezultat a urmat direcționarea RTL a numelui arab respectiv). Această listă a fost produsă prin includerea următorului fragment HTML+Django:

{% load del_redun %}
{% for game in games %}
    <li><span class="game_title" data-gameid="{{game.id}}">
            {% if forloop.first %}
                {{game}}
            {% else %}
                {{game|redundant}}
            {% endif %}</span> 
    </li>
{% endfor %}

La compilarea acestui șablon, variabila {{game}} va fi înlocuită cu textul furnizat de metoda __unicode__ pentru obiectul Game curent din lista contextuală games (specificată în tagul {% for %}) - text care pe lângă cele trei câmpuri din "titlul" de partidă vizat deja mai sus, conține și un câmp care indică între paranteze rotunde, țara partenerului. Pentru cazul când există mai multe partide cu partenerul indicat (cum avem în lista din imaginea redată mai sus) este firesc ca țara să apară numai la primul item al listei partidelor - pentru celelalte am folosit {{game|redundant}}, unde |redundant invocă asupra valorii inițiale o operație de filtrare suplimentară, constând în eliminarea parantezei de țară.

Șablonul redat mai sus poate "vedea" filtrul tocmai menționat datorită prevederii de pe prima linie {% load del_redun %} - unde del_redun.py este fișierul care și definește filtrul respectiv:

# games/templatetags/del_redun.py    in templates: {% load del_redun %}

from django import template
from django.template.defaultfilters import stringfilter
import re
from django.utils.safestring import mark_safe

register = template.Library()

@register.filter(name='redundant') # usage in templates: {{value|redundant}}
@stringfilter # Decorator for filters which should only receive unicode objects.
def delredun(value):
     # return re.sub(r'\(.*\)', '', value)
    title = re.sub(r'\(.*\)', '<span dir="ltr"></span>', value)
    return mark_safe(title)

Paranteza de țară urmează imediat după numele partenerului (posibil RTL) și pentru a restabili direcționalitatea LTR după redarea numelui - am înlocuit paranteza de țară nu cu șirul vid (cum se vede pe linia comentată că am procedat inițial, atrâgând defectul de bidirecționalitate menționat) - ci cu un "span" vid, dar cu atributul dir="ltr" (anunțând că urmează o valoare de tip LTR - anume, rezultatul partidei).

vezi Cărţile mele (de programare)

docerpro | Prev | Next