Sprog i Smarty
I forlængelse at mit forrige indlæg vil jeg komme med nogle idéer til hvordan man kan tilføje sprogsupport i Smarty.
Det jeg gerne vil ende med er en løsning hvor jeg i mine templates kan skrive noget lignende:
1 | <h1>{translate}Welcome to my site{/translate}</h1> |
Men også
1 | <title>{$title|translate}</title> |
Jeg vil også kunne angive sproget direkte, fx:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <html> <head> <title>{$title|translate:"fr"}</title> </head> <body> <h1>{translate lang="fr"}Welcome to my site{/translate}</h1> </body> </html> |
Jeg vil aldrig kalde Smarty direkte, men altid via min egen klasse som extend’er Smarty. Det gør det bl.a. nemmere at sætte diverse indstillinger op. I min egen klasse vil jeg, for at understøtte sprog, tilføje en block og en modifier. Det gør jeg på følgende måde:
1 2 3 4 5 6 7 8 | class mbnSmarty extends Smarty { public function __construct() { $this->register_modifier('translate', 'smarty_m_translate'); $this->register_block('translate', 'smarty_b_translate'); } } |
Bagefter skal jeg skrive de to funktioner hhv. smarty_b_translate og smarty_m_translate.
1 2 3 4 5 6 7 8 9 10 11 12 13 | function smarty_b_translate($args, $str, &$smarty, &$repeat) { if (!$repeat) { $text = new Text(); $text->setText($content); return $text->translate($args['lang'] ? $args['lang'] : LANG); } } function smarty_m_translate($str, $lang = LANG) { $text = new Text(); $text->setText($str); return $text->translate($lang); } |
Jeg har i mine eksempler gået ud fra, at der er defineret en LANG-konstant på følgende måde:
1 | define("LANG", "da"); |
Jeg bruger også en Text-klasse, som I selv må lave :)
Sprogstyring via mod_rewrite
En lille idé til hvordan man kan styre sprog via Apaches mod_rewrite.
Normalt definerer jeg en konstant ved navn URL der indeholder den absolutte adresse til forsiden af mit website. Fx:
1 | define("URL", "http://www.example.com"); |
Hver gang jeg skal linke i HTML/Smarty eller sende videre via PHP bruger jeg konstanten.
Nu vil jeg tilføje sprog som et ekstra “lag” på mine adresser, så jeg fremover kalder example.com/en/about.php for den engelske udgave, og example.com/fr/about.php for den fanske udgave af about.php; men jeg vil ikke have to versioner af about.php liggende.
Derfor laver jeg en lille rewrite-regl der omskriver førnævnte adresser til example.com/about.php?lang=en og example.com/about.php?lang=fr.
Reglen kan ses herunder. Bemærk at jeg kun checker på to a-z og ikke på de tilladte sprog. Det burde man måske gøre.
1 2 | RewriteEngine On RewriteRule ^([a-z]{2})/(.*)$ $2?lang=$1 [QSA] |
Jeg tilføjer QSA som betyder query string append, hvilket gør at eventuelle andre parametre til siden stadig kommer med.
Næste trin bliver at lave noget PHP der behandler ovenstående. Jeg vil typisk sætte min URL-konstant på i en header-fil der bliver inkluderet på alle sider. Før jeg definerer min konstant, laver jeg et check på $_GET['lang'] for at se om den er på listen over tilladte sprog. Er den det liver den tilføjet til konstanten, og på den måde vil alle mine linke indeholde sprogangivelsen.
1 2 3 4 5 6 7 8 9 | $valid_languages = array("en", "de", "fr", "da"); if (in_array($_GET['lang'], $valid_languages)) { define("URL", "http://www.example.com/".$_GET['lang']); } else { define("URL", "http://www.example.com"); } |
Det kan måske være meget smart at have to konstanter til URL’en. Én med sprog, og en uden, da det ikke er alle steder man vil angive sprog – fx billeder, stylesheets og javascript m.m.
E_NOTICE i PHP
Jeg ender altid med at kommer i slåskamp med andre php-udviklere omkring notices i koden, fordi min kode laver typisk mange. Hvis man lige har lært at slå notices til i php, vil man måske opfatte en notice som en fejl, men det er det ikke. Hvilket man også kan aflede af navnet – fejl plejer at hedde noget med error.
Jeg gør det typisk altid i if-sætninger for at gøre koden mere overskuelig og letlæselig. Nedenstående to eksempler viser først hvordan jeg plejer at lave mine if-sætninger, og sidste viser hvordan man skal skrive samme kode for at undgå en E_NOTICE.
1 2 | if ($id = (int) $_GET['id']) { } |
1 2 3 | if (isset($_GET['id']) && is_numeric($_GET['id'])) { $id = (int) $_GET['id']; } |
Man opnår det samme med de to stykker kode, men jeg vil mene at første er noget nemmere at læse.
Få styr på ens domæne
De fleste har både example.com og www.example.com, og de to domæner viser samme site, men det giver tit nogle problemer.
For mit vedkommende har jeg typisk en konstant der hedder URL der indeholder “http://www.example.com”, og så bruger jeg den hver gang jeg linker til undersider. Går man ind på example.com og logger direkte ind fra forsiden, vil man blive sendt til www.example.com der sætter en cookie og sender en tilbage til siden man kom fra. Problemet er bare at www.example.com ikke altid sætter cookies for *.example.com. Hvilket resulterer i at man ikke bliver logget ind.
Der kan være andre problemer. Måske har man *.example.com og nogen ved et uheld kommer til at linke til ww.example.com, og Google indekserer den side. Så har man lige pludselig tre kopier af sit site liggende i Google.
Løsningen er følgende i ens .htaccess:
1 2 3 | RewriteEngine On RewriteCond %{HTTP_HOST} !^www\.example\.com$ RewriteRule ^(.*) http://www.example.com/$1 [L,R=301] |
Jeg kontrollerer domænenavnet om det er www.example.com. Hvis ikke, så sender jeg videre til www.example.com. Jeg ta’r selvfølgelig hele URL’en med i videresendingen.
“If-sætninger” i Apaches mod_rewrite
Når jeg udvikler gør jeg det typisk på min egen maskine, og så via SVN synkroniserer jeg med live-serveren. Det gi’r tit nogle problemer med rewrite-regler, da de typisk vil være anderledes fra localhost til den endelige server.
Min løsning på det er Skip-flaget der fortæller Apache hvor mange regler den skal springe over.
Nedenstående eksempel springer én regel over hvis man sidder på localhost.
1 2 3 4 5 6 7 8 9 10 11 12 | RewriteEngine on RewriteCond %{HTTP_HOST} ^localhost$ RewriteRule .* - [S=1] # Bliver ikke kørt på localhost RewriteCond %{HTTP_HOST} !^www\.example\.com$ RewriteRule ^(.*) http://www.example.com/$1 [L,R=301] # Bliver kørt på alle sites RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^[^/]+-([0-9]+)$ item/view/$1 [QSA] |
Se mere i manualen for mod_rewrite.
Konvertere links til posts (Fra GET til POST)
Det er aldrig smart at lave GET-requests der skal ændre noget på en server. Skal der ske ændringer skal man bruge POST, dvs. en formular for method er sat til post. Det er bare ikke altid hensigtsmæssigt fordi man måske har noget design, hvor man skal slette noget ved at trykke på et link. En smart måde at omgås problemet på er ved at bruge jQuery til at konvertere ens links.
Start med at sætte en class=”convert_to_post” på dine links. Fx:
1 | <a href="bookmark.php?page=123" class="convert_to_post">Bookmark</a> |
Normalt ville overstående bare være et almindeligt link, men pga. class=”convert_to_post” og nedenstående stykke javascript vil den blive konverteret til et ajax-kald der poster i stedet.
1 2 3 4 5 6 | $(".convert_to_post").click(function() { $.post($(this).attr('href'), {}, function() { window.location.reload(); }); return false; }); |
Man skal dog være opmærksom på at hvis man bruger PHP så ligger ens data stadig i $_GET og ikke i $_POST, man skal derfor for at sikre sig at der er brugt POST checke at $_SERVER['REQUEST_METHOD'] er sat til POST.
Et lille jQuery tip
jQuery er rigtig smart, og en lille ting jeg opdage her til aften var, hvor nemt det egentlig er at arbejde med attributter.
Mit problem var at jeg skulle fange kliks på nogle labels, og efterfølgende skjule eller vise dens pågældende inputfelt. Løsningen blev:
1 2 3 | $("label").click(function () { $("input#" + $(this).attr('for')).toggle(); }); |
HTML’en til ovenstående er:
1 | <label for="str">Tekst</label> <input id="str"/> |
Jeg fanger kliks på label-elementer og skjuler eller viser det inputfelt der har samme ‘id’ som label-feltet har ‘for’. Eksemplet er lidt simplificeret ;)

Jeg hedder Morten, og jeg har udviklet webapplikationer siden slutningen af 90'erne.