Delphi Meets Babylon: meertalige flexibiliteit voor dummies

 

De situatie

Eén van mijn accounts is een offshore reparatiebedrijf, een kleine multinational. Multinational in de zin dat het vestigingen in diverse landen wereldwijd heeft, klein in de zin dat er geen enkele vestiging is, ook het hoofdkantoor in Frankrijk niet, waar meer dan vijftig man werken.

 

Vooruitstrevend zijn ze echter wel: al een paar jaar geleden is men ertoe overgegaan alle werkzaamheden elektronisch aan de technici aan te bieden. Een bepaalde opdracht wordt bij binnenkomst geanalyseerd en dan opgedeeld in een reeks van kleine werkopdrachten, die elk aan een daarvoor gespecialiseerde technicus wordt aangeboden middels een naast de werkbank geplaatst werkstation. “Als het kantoor niet papierloos kan, dan de werkplaats maar”, zo zal men wel gedacht hebben. Voordeel: waar ter wereld men zich ook bevindt, elk probleem wordt overal op exact dezelfde wijze opgelost. Voorwaar een instelling waar wij ICT-ers nog een puntje aan kunnen zuigen.

 

Als het kantoor niet papierloos kan, dan de werkplaats maar

 

Nu staat men voor de klus alle vestigingen via een WAN met elkaar in verbinding te brengen. Men is al een heel eind gevorderd met het op één lijn brengen van de automatisering door voor elke taak overal ter wereld hetzelfde stukje software te gebruiken, zodat er uiteindelijk ook van een en dezelfde database gebruik gemaakt kan worden. Zoiets is natuurlijk aanbevelenswaardig in het kader van standaardisatie en het terugbrengen van de trendy Total Cost of Ownership, maar het voorkomt ook het nodige tijdverlies door inwerkperiodes. Gespecialiseerde technici worden nog wel eens van hot naar her gestuurd en dat is op zich al duur genoeg, maar om dan ook nog eens tijd te verliezen met het inwerken in onbekende software is wel wat veel van het goede. “De werkplek”, zo stelde de plant manager van de Nederlandse vestiging kleurrijk, “moet een beetje als McDonald’s zijn: waar ook ter wereld je aanlogt, je weet altijd wat je kunt verwachten.”. McWindows, dus?

 

Het probleem

Voor het deel dat de reparatiewerkzaamheden moet administreren heeft men het systeem gekozen dat in Nederland draait en dat betekende, naast de eer, weer wat werk aan de winkel voor de maker ervan: de schrijver van dit stukje.

 

Qua functionaliteit kon het meeste bij het oude blijven - in Azerbeidzjan en Venezuela had men wat extra wensen, maar dat mocht geen naam hebben -, maar de taal bleek een heikel punt. In mijn naïviteit had ik aangenomen dat Engels de standaard zou worden, maar met een hoofdkantoor in Frankrijk had ik natuurlijk beter moeten weten. Elk land zou om te beginnen  een programma moeten krijgen in de eigen taal. Maar daarmee begon het pas…

 

Op zich zou dit namelijk allemaal geen probleem moeten zijn, want Delphi kan natuurlijk standaard meerdere talen aan en ik ging ervan uit dat een mailtje naar Borland genoeg zou moeten zijn om een “language pack” in het Azeri rijker te worden. Toch zag ik daar al tegenop: de taal zit bij Delphi in de applicatie meegebakken en daar zat ik niet zo op te wachten. Bovendien werd het al snel duidelijk dat sommige landen (om te beginnen al Luxemburg, al kon ik me niet voorstellen wat een offshore bedrijf daar te zoeken heeft, maar dat terzijde) meerdere talen eisten, afhankelijk van de gebruiker die er inlogde. Dat zou sowieso al moeten voor de rondreizende technici: natuurlijk, een schroevendraaier is een schroevendraaier, maar wat is “zoeken” in het Oezbekistaans? In een soort angstdroom zag ik me al een stuk of twintig EXEs met een veelvoud daarvan aan DLLetjes naast elkaar in de lucht houden. En dan iedere keer maar weer een (dure) vertaler inschakelen.

 

Het antwoord

Uiteindelijk bleek maar één enkele oplossing mogelijk. Alle vertalingen in een tabel en de hele interface (d.w.z. alle caption properties van forms, labels en buttons) in een database. Om de gegevens zelf hoefde ik me niet zo druk te maken: dit waren voornamelijk codes die overal binnen het bedrijf (en deels zelfs binnen de branche) standaard waren. Als we dan de taal vervolgens weer aan de gebruikersgegevens knoopten, kon het programma, zodra het wist wie zich had aangemeld, gaan babbelen in diens eigen taal. Het kip-en-ei probleem dat dan nog overbleef (in welke taal moest de interface staan zolang er nog niet was ingelogd) kon worden opgelost door bij de systeemgegevens ook nog een landstaal vast te leggen. Zodra het programma werd opgestart, babbelde het al direct de taal van het land. Voordeel van deze aanpak was ook dat ik maar een beperkt aantal talen (eigenlijk alleen Nederlands en Engels) hoefde te leveren: de overige talen kon men a.d.h.v. het Engelse origineel in het land zelf aan de tabel toevoegen. Eventuele fouten kwamen zo voor hun eigen rekening en ik hoefde me zo ook niet druk te maken om taalkundige problemen als het ontbreken van een eenduidig “Ja” of “Nee” in Keltische talen (die is daar afhankelijk van de vraag die beantwoord wordt). Per slot van rekening ben je programmeur, geen linguïst.

 

Alle vertalingen in een tabel en de hele interface in een database

 

De oplossing [1]: de vertaaltabel

Allereerst had ik dus een tabel nodig met daarin alle kreten die in de interface naar voren kwamen. In sommige pakketten zit die er standaard in en wordt op de form zelf een code gebruikt die naar de aldaar benodigde frase verwijst, maar om te beginnen had ik die methode niet gebruikt (in mijn systeem stond alles “hard” in de component) en heb ik persoonlijk ook iets tegen het gebruik van ondoorzichtige codes. Als je dan later de broncode nog eens induikt, weet je op een gegeven moment echt niet meer wat er geacht wordt te staan en om nu steeds maar weer een tabel met daarin een paar honderd frases te moeten raadplegen, daar zit ik niet echt op te wachten. Ik besloot dus maar als code de oorspronkelijke (Nederlandse) kreet te gebruiken. Zo kom je dan op een tabel met de volgende (karakter) velden:

 

Veld

Omschrijving

NL

De Nederlandse frase

UK

De (Brits) Engelse frase

DE

De Duitse frase

FR

De Franse frase

IT

De Italiaanse frase

IR

De Ierse (Irish Gaelic) frase)

Enz.

 

 

Een DBA kon zo naar hartelust kolommen toevoegen, voor elke taal die hij/zij nodig achtte en men kon ook, zolang er nog geen centrale database was, naar hartelust kolommen onderling uitwisselen. Toen deze oplossing eenmaal bekend werd, was meteen de beer los. In Brest vroeg een Bretons nationalist maar meteen de lijst met frasen op, om vast te vertalen in het Bretons, in Arbroath (Schotland) was het van hetzelfde laken een pak voor Schots Gaelic. En in IJmuiden greep de werkverdeler zijn kans om zijn vakantiecursus Spaans maar meteen in zijn werk te integreren. Eh … nee, ze hadden geen vestiging in Franeker.

 

De interface voor het onderhoud van de tabel kon eenvoudig blijven:

 

 

Alleen de eerste kolom hoefde bevroren te zijn, de overige konden door een systeembeheerder naar believen worden aangepast zonder dat er verder ergens iets hoefde te worden gecontroleerd, want taalfouten waren voor eigen rekening. Zolang toevoegen en verwijderen maar onmogelijk waren gemaakt ging het allemaal wel goed. En als de datagrid alleen maar een link naar de datasource heeft, zonder een verdere kolomspecificatie, wordt een eventueel nieuw toegevoegde kolom zonder meer meegenomen.

 

De oplossing [2]: de gebruikersgegevens

 

Hierna was het een kwestie van de taal als attribuut toevoegen aan een gebruiker:

 

 

 

De in de combobox mogelijke items worden bepaald door het aantal kolommen van de vertaaltabel:

 

procedure TfrmUser.FormActivate(Sender: TObject);

var i: integer;

begin

  cmbTaal.Items.Clear;

 

  for i:=0 to frmMain.tblTrans.FieldCount-1 do begin

    cmbTaal.Items.add(frmMain.tblTrans.Fields.Fields[i].fieldname);

  end;

end;

 

De oplossing [3]: de vertaling

 

Natuurlijk zou het bij het helemaal opnieuw opzetten van het pakket te overwegen zijn geweest om van elk form, label en button een child af te leiden, waarin een methode voor het vertalen standaard was meegebakken, maar dat ging dus nu even niet. Gelukkig was ik wel zo slim geweest om elk form af te leiden van een enkele ancestor. In die ancestor form (frmLINQS in het voorbeeld) kon ik nu in het On Activate event de volgende code toevoegen:

 

procedure TfrmLINQS.FormActivate(Sender: TObject);

var I: integer;

begin

  for i:=0 to frmLINQS.ComponentCount-1 do

  begin

    caption:= Vertaal(caption, strTaal);

 

    if frmLINQS.Components[i] is TLabel then

      (frmLINQS.Components[i] as TLabel).caption :=

       Vertaal((frmLINQS.Components[i] as TLabel).caption, strTaal);                        

 

    { hetzelfde geldt natuurlijk voor buttons }

 

  end;

end;

 

function TfrmLINQS.Vertaal(strInvoer: string; strTaal: string): string;

begin

  if strTaal<>'' then

    with dmMain do

    begin

      tblTrans.SetKey;

      tblTrans.FieldByName('NL').AsString := strInvoer;

      if tblTrans.gotokey then

        result:=tblTrans.fieldbyname(strTaal).asstring

      else

        result:=strInvoer;

    end

  else

    result:=strInvoer;

 

end;

 

 

NB: De globale variabelestrTaalwordt in een ander deel van het programma gelezen uit de systeeminstellingen of de actieve regel van de User tabel.

 

Ik heb ervoor gekozen om het On Activate event te gebruiken en niet het On Create, omdat dit een eventuele wijziging in de taal van een gebruiker nog tijdens dezelfde sessie zichtbaar maakt. De performance bleek, mits de tabel op ‘klassieke’ manier (d.w.z. middels een index en eengotokeyopdracht) werd benaderd, er niet onder te leiden.

 

De les

 

Basis voor het voorafgaande was het idee dat er voor (schijnbaar) complexe vraagstukken vaak zeer eenvoudige oplossingen gevonden kunnen worden die met een minimum aan middelen het gewenste resultaat opleveren. Te vaak wordt er met DLLs gesmeten en objecten gegoocheld waar een eenvoudige (en ook voor een relatief beginnende programmeur eenvoudig te doorgronden en dus makkelijk onderhoudbare) functie al zou volstaan.

 

Just KISS (Keep It Simple and Stupid) and life becomes easy

 

De bovenstaande code zou natuurlijk nog aanzienlijk kunnen worden verfijnd. Zo zou de gevonden regel in de vertaaltabel eerst tot object kunnen worden vermalen alvorens de gegevens aan het verdere programma worden aangeboden. Dat zou echter een hoeveelheid code hebben opgeleverd die niets toevoegt aan de les die ik erin heb willen stoppen: hoe kan ik op eenvoudige wijze een flexibel meertalige interface opzetten zonder dat ik er erg veel voor moet doen? Tevens zou het dan wel erg Delphi-specifiek gaan worden, terwijl de hierboven geschetste oplossing natuurlijk met hetzelfde gemak in VB of FoxPro toegepast zou kunnen worden. Of, zoals ik een Amerikaanse kennis van me het laatste hoorde zeggen: just KISS (Keep It Simple, Stupid!) and life becomes easy.

 

 

Ed van Akkeren

Commentaar van anderen:
ChristianLouboutin op 16-8-2010 om 4:55
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Christian Louboutin comfortable shoes are women best resolution Whoever you, Drafted this think you can expect to take pleasure in Christian Louboutin Shoes, Wedding Shoes, Christian Louboutin, Christian Louboutin Shoes. This sneakers experience women charm additionally sexy. Wedding Shoes, Discount Christian Louboutin, Christian, Louboutin, Christian Louboutin Sale This is usually fantastic Louboutin Shoes, Louboutin Sale, Cheap Christian Louboutin, Christian Louboutin Discount, Christian Louboutin Boots. As a result exist to help opt designed for style, you cherish it is usually to help opt in order for most of the eye-catching Christian Louboutin Pumps, Christian Louboutin Sandals, Christian Louboutin Flats, Christian Louboutin Evening, Christian Louboutin Wedges taht can acquire inspiration designed for his fatal stiletto investigation connected with an incident that will occurred as part of his the beginning of the twenties. Christian Louboutin Pumps, Christian Louboutin Boots, Christian Louboutin Sandals, Christian Louboutin Flats, Manolo Blahnik Shoes He visited a museum and furthermore, saw a warning that will forbade women in order to really act, Yves Saint Laurent Shoes, Yves Saint Laurent Boots, YSL Shoes, Miu Miu Shoes during bearing stilettos ready, fearing damage in order to this extensive wood floors. Herve Leger V Neck Dress, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress This image stayed in their head, along with he used this idea later in his louboutin shoes.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar