RSS
Arrays in language files

Arrays in language files

Eén van de weinige tekortkomingen in het CodeIgniter framework zijn de language files. Deze bestanden bevatten arrays die vertalingen aanbieden voor een bepaalde tekst. Standaard biedt CodeIgniter slechts de mogelijkheid aan om 1-dimensionale arrays te gebruiken, en dit leid al snel tot beperkingen. In deze blogpost zal ik toelichten hoe je de language klasse van CodeIgniter kan uitbreiden zodat deze ook meerdimensionale arrays ondersteunt. Op die manier moet je slechts één bestand per taal aanmaken.

De situatie nu

Wanneer je CodeIgniter download van de website en upload naar je eigen webspace, ben je gebonden aan de 1-dimensionale arrays in language files. Concreet houdt dit in dat je je vertalingen niet kan bundelen. Een kleine language file ziet er bijvoorbeeld zo uit:

1
2
3
4
<?php
$lang['usernameMinLength'] = 'Je gebruikersnaam is niet lang genoeg.';
$lang['usernameMaxLength'] = 'Je gebruikersnaam is te lang.';
$lang['usernameInvalid'] = 'Je gebruikersnaam bevat ongeldige tekens.';

Voor simpele applicaties is dit goed genoeg, maar wanneer je veel teksten hebt die moeten vertaald worden is dit onbegonnen werk. Denk maar eens aan het feit dat je teksten in categorieën wil indelen. Zo wil ik bijvoorbeeld alle foutmeldingen, alle waarschuwingen en alle informatieteksten bij elkaar groeperen. De handleiding van CodeIgniter raad in dat geval het volgende aan:

It’s a good practice to use a common prefix for all messages in a given file to avoid collisions with similarly named items in other files. For example, if you are creating error messages you might prefix them with error_

Dit is weer een leuke oplossing, maar ook niet goed genoeg voor hele grote websites. Een andere oplossing zou zijn om meerdere language files te maken per taal. Zo kan je bijvoorbeeld voor nederlands 3 bestanden aanmaken: nl_foutmeldingen_lang.php, nl_waarschuwingen_lang.php en nl_informatie_lang.php. Ook weer een tof alternatief, maar een oplossing die bijna niet onderhouden is.

Het mag dus wel duidelijk zijn dat er nood is aan een beter oplossing, en die gaan we nu maken.

Hoe het zou moeten

De ideale oplossing zou één bestand per taal zijn, waarin een meerdimensionale array staat die de teksten in categorieën onderverdeeld. Een klein voorbeeldje, in nl_lang.php staat het volgende:

1
2
3
4
5
6
7
8
9
<?php
$lang['error']['usernameMinLength'] = 'Je gebruikersnaam is niet lang genoeg.';
$lang['error']['usernameMaxLength'] = 'Je gebruikersnaam is te lang.';
$lang['error']['usernameInvalid'] = 'Je gebruikersnaam bevat ongeldige tekens.';
 
$lang['warning']['passwordMinLength'] = 'Je wachtwoord is niet lang genoeg.';
$lang['warning']['passwordMinLength'] = 'Je gebruikersnaam is te lang.';
 
$lang['info']['welcome'] = 'Welkom op mijn blog';

Het spreekt voor zich dat in en_lang.php exact hetzelfde staat, maar dan in het engels. Voor de_lang.php, fr_lang.php, etc. is dit uiteraard ook zo. Zoals je kan zien in bovenstaande code, zijn de teksten gegroepeerd. Een array voor alle foutmeldingen, een voor alle waarschuwingen en een voor alle informatieteksten.

Er rest nu nog één probleem. CodeIgniter kan standaard niet om met meerdimensionale arrays in de language files. We moeten dus de language class uitbreiden met een functie die de array wel kan uitlezen.

De oplossing

Wie al een beetje ervaring heeft met CodeIgniter, weet meteen dat voor problemen als deze de ideale oplossing al aanwezig is. Je kan in CI namelijk heel makkelijk bestaande klassen (of libraries in dit geval) overschrijven. Eerst maken we een nieuw bestand aan in /system/application/libraries en we noemen dit MY_Language.php. De inhoud van dit bestand is als volgt:

1
2
3
4
5
6
7
8
9
<?php
class MY_Language extends CI_Language
{
    function line2($array, $line)
    {
        $line = ($line === '' OR $array === '' OR ! isset($this->language[$array][$line])) ? FALSE : $this->language[$array][$line];
        return $line;
    }
}

Aangezien de naam van het bestand MY_ + een_bestaande_klasse is, zal deze automatisch worden geladen door CodeIgniter en moeten we ons hier verder geen zorgen meer om maken. We hebben nu een functie toegevoegd aan de Language klasse van CI, namelijk line2. We geven 2 parameters mee aan deze functie, de array waarin we willen zoeken en de regel die we willen ophalen. Ik heb voor dit voorbeeld deze functie line2 genoemd. In een realistische omgeving zou ik deze functie een kortere naam geven, bijvoorbeeld l2. Zo besparen we onszelf elke keer we deze functie aanroepen weer een beetje werk.

De functie gebruiken kan nu als volgt:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class Home extends Controller
{
    function index()
    {
        // Eerst laden we het bestand in ('mapnaam', 'bestandsnaam zonder _lang')
        $this->lang->load('dutch', 'dutch');
 
        // Daarna printen we de regel 'usernameMinLength' uit de array 'error'
        echo $this->lang->line2('error', 'usernameMinLength');
    }
}

Wanneer we dan een bezoekje brengen aan home/index krijgen we het volgende resultaat:

1
Je gebruikersnaam is niet lang genoeg.

Exact wat we wilden bereiken. Missie geslaagd.

Noot

De code hierboven is bedoeld als voorbeeld en kan nog heel wat beter geschreven worden. Nu moeten we bijvoorbeeld nog steeds als onze language files in een aparte map plaatsen. dutch_lang.php in language/dutch/, english_lang.php in language/english/. Volgens de zelfde manier als hierboven kan de load functie van de Language klasse overschreven worden zodat we de language files rechtstreeks in language/ mogen plaatsen.

Ook kan de code hierboven worden uitgebreid om ondersteuning te bieden voor meerdimensionale array zodat je nog meer vrijheid krijgt. Beide ideeën laat ik als oefening voor de lezer :-)

Delen

Facebook Facebook     Twitter Twitter     TinyUrl: http://tinyurl.com/cf7f68

9 reacties op deze post

  1. Dirk Bonhomme zei op 6 mei :

    Dit is al stukken handiger dan een gewone array!

    De beste en makkelijkst te onderhouden manier is natuurlijk om je vertalingen in een database te zetten. Het is zeer gemakkelijk om de translations library uit een db te laten lezen in plaats van een taalbestand. Misschien iets voor een volgende blogpost?

  2. FinalFrag zei op 7 mei :

    Dat is inderdaad ook een oplossing. Ik geef toe dat de database-oplossing makkelijker te beheren is, maar het is niet in alle situaties de ideale oplossing.

    Stel, je maakt een website in 20 talen, waaronder Turks of Zuid-Afrikaans. Ik kan geen Turks, dus dan zou ik het turkse bestand kunnen doorsturen naar iemand die dat wel kan. Achteraf kan ik dan gewoon het bestand uploaden en klaar is Kees.

    Met een database kan dit natuurlijk ook: exporteren naar een Excel bestand, laten aanpassen en achteraf weer importeren in de database, maar dit vind ik zelf een omweg.

    Beide oplossingen zijn goed, en misschien doe ik later eens een post over de database-oplossing. Allesinds bedankt voor de tip :-P

  3. Bart Geraerts zei op 7 mei :

    Goed verwoord wat ik van plan was. Soms kan een database handig zijn, soms ook helemaal niet (zie comment 2).

  4. Thomas Timmers zei op 7 mei :

    Handige post !!
    Maar eerst zal ik mij eens verder moeten verdiepen in CodeIgniter zelf !!
    Daar toevallig tips voor? (sites/boeken)

    Grtz,

    Thomas

  5. FinalFrag zei op 7 mei :

    In de loop van volgende week komt er een post aan van mij die het installeren van CodeIgniter behandeld. Zo is het bijvoorbeeld aan te raden de bestanden buiten de web root te plaatsen, zodat mensen deze niet rechtstreeks kunnen opvragen (wat natuurlijk een stuk veiliger is). Ook het opzetten van meerdere sites (bijvoorbeeld een back- en een front-end) op 1 installatie zal in een blogpost behandeld worden.

    Verder raad ik zeker de video tutorials van CodeIgniter zelf aan. Deze behandelen de basis van het MVC principe. Wanneer je dat onder de knie hebt kom je al een heel eind verder met de handleiding en het forum.

    Met gewoon zelf iets in elkaar te boksen leer je natuurlijk het meest, maar indien je echt de boekenwurm wil uithangen raad ik Professional CodeIgniter van Wrox en CodeIgniter for rapid PHP development van Packt Publishing aan.

  6. TheEvilDuckie zei op 7 mei :

    Als je al met een ander MVC Web Framework hebt gewerkt (zoals Ruby on Rails of Groovy on Grails) is CodeIgniter ook niet zo moeilijk, gewoon een beetje aanpassen aan de gebruikte conventies…

  7. Thomas Timmers zei op 7 mei :

    @FinalFrag: Mooi voor de toekomstige blogspots :) Ik heb in een ver verleden de screencast van CI al eens bekeken … dus nog maar eens doen :) Thnx voor de boeken … zal er eens achter gaan zoeken :)

    @TheEvilDuckie: dat is inderdaad zo … maar toch wil ik mij deze keer eens goed in een framework verdiepen en niet zo half en half zoals ik ze meestal gebruik.

  8. Michaël zei op 8 mei :

    Mooie en duidelijk uitleg over die uitbreiding met de taalbestanden. Je hebt duidelijk een leuk onderwerp aangehaald aan het aantal reacties te zien :p Ben benieuwd naar meer CI howto’s

  9. FinalFrag zei op 8 mei :

    Ja, inderdaad, merci iedereen voor de comments. Omdat het onderwerp zo in de smaak valt ben ik bezig aan een follow-up…

Reageer zelf!