SpaceHawks' WorldNews
Issue #16

Lokalizáció

Na, itten írkálok egyet lokalizálható programok írkálásáról.

Nemolyanrég (fél éve...) megkértek egy Battle Isle stílusú játék pályaszerkeszőjének elkészítésére. A srácok vidékiek (valami dojcsland nevű helyen laknak), és nem kommunikálnak megfelelően semelyik kulturált emberi nyelven (= angol, esetleg magyar), ezért úgy kérték, hogy lokalizálható legyen. Sajna a gépem "hibája" miatt (Winner kártya rulez...) miatt csak most(anában) álltam neki. Szinte minden megvolt a cuca elkészítéséhez (ld. honlapom), csak ezzel a locale dologgal nem foglalkoztam még igazán. Ezirányú tapasztalataimat kívánom most megosztani véletek:

A lokalizáláshoz kell:

  • forráskód.(c|ass|mittomén)
    • Na ez aztán mandatory...
  • program.cd
    • Ez ideális esetben a programmal egyidőben készül.
  • CatComp
    • Ez csinál üres katalógus fordítás filét (.ct), c/asm/modula2 forrást, és ez gyártja le a .catalog fájlokat is.

A (beilleszthető) források elkészítésére én a saját programomat használom, amelyről e czikk végén ejtek néhány keresetlen sort.

Amúgy maguk a katalógusok (.catalog) elkészítésére biztos vannak sokkal jobb utility-k is, de ezekkel én nem kívánok foglalkozni.

A lokalizálásra (vagy katalogizálás, esetleg internacionalizálé stb.) három módot látok (kb., inkább számoljátok meg ti is):

1. Nem lokalizáljuk a programot.

Ez nem tekinthető igazi lokalizálásnak. Illetve mégis, hiszen az amineten megtalálható "c2local.lha" arhívum (dev/c könyvtárban) ezt megcsinálja helyettünk. Helyből legyártja a .cd (katalógusleíró) fájlokat, és módosítja a forrást úgy hogy automatikusan használja az esetleges katalógust.

A probléma ezzel a stuffal, hogy a kódban lévő összes szöveget lefordítaná alapból (pl. "PROGDIR:" -> "PROGKÖNYVTR:", "locale.library" -> "helyi.könyvtár" ?), ezt lehet opcionálisan kikapcsolni stringenként. A másik gondom, hogy mondjuk én rühelleném ha egy program belenyúlkálna a forráskódomba. Harmadszor pedig: nehézkes a .cd fájlba kerülő sztringek azonosítóinak megadása, ami a programozó részéről végülis nem probléma, csak a szerencsétlen fordítók dolgát nehezítjük meg vele (jójó, végülis ez senkit sem érdekel...) (Ez az, látom megvan a helyes hozzáállás a dologhoz-Emeric SH). Végül pedig a programba beégetett szöveges változók és konstansok bizonyos esetben nem lokalizálhatók automatikusan...

Ha van egy jó régi összegányolt forráskódod, akkor talán egyszerűbb ezt a c2local-t ráereszteni mind átírni rendesen, de új programnál nem javasolom.

2. A programban a sztringek helyére locale.library/GetCatalogStr() (vagy hasonló funkciójú) hívásokat rakunk.

pl.:
Printf("Hello"); -> Printf(GetCatalogStr(catalog, MSG_HELLO, "Hello"));

Ez lenne a nonpluszultra megoldás, ahogy Júzer Móriczka elképzeli, de több gond van vele mind gondolnánk. Először is sokat kell gépelni (ez csak akkor gond ha nem diktálod a programot), ráadásul minden üzenet és üzenetazonosító kétszer fordul elő (a forráskódban és a katalógusleíró fájlban). Végül kevésbé profi fordító esetén egy adott sztring többször békerülhet a kódba fölöslegesen.

Egyetlen előnye, hogy minden infó adott a forráskódban, így egy

#define GetCatalogStr(x,y,z) z

makróval minden külső modul nélkül kiszedhető belőle a tényleges locale.library hívás. Szóval ezt nem javaslom...

3. A programban a sztringek helyére CatComp-féle (lásd Native SDK) GetString() hívásokat teszünk.

pl.:
Printf("Hello"); -> Printf(GetString(localeinf, MSG_HELLO));

Ez lenne az előző megoldás "bé" verziója. Ez már frankóbb, kevesebb gépelés, az azonosító csak 1szer szerepel... Itt program írásával párhuzamosan pötyögjük magukat az üzeneteket a katalógusleíró fájlba, mivel csak ott szerepelnek a tényleges szövegek (a példában "Hello"), és hogy nehogy elfelejtsük őket (ez sajnos szükséges a program teszteléséhez, így nem igazán gyártható le utólag).

4. A programban a sztringek helyett definíciókat teszünk.

pl.:
Printf("Hello"); -> Printf(MSG_HELLO);

Itt program írásával párhuzamosan pötyögjük magukat az üzeneteket a katalógusleíró fájlba.

A dolog előnye hogy nem feltétlenül kell lokalizálni a programot, ha meggondolod magad, mert a .cd file alapján csinálsz egy inklúdos filét, amibe #define-al benne vannak a sztringek. A gond csak az, hogy ez a megoldás nem kompatibilis a CatComp output C filejával, de szerintem ez a legpraktikásabb, így írtam is erre egy programot...

Persze mindhárom utóbbi módozatnál ügyelni kell arra, hogy nem lehetnek statikus szövegek a programban, ezeket valahogy dinamikussá kell tenni. Például a menü elkészítésére nem használhattsz statikus NewMenu struktúrákat, hiszen ezekben a szövegek lecserélését meg kell oldani... Mondjuk nem egy nagy macera, de ügyelni kell erre is.

A negyedik módozat megvalósítása a CatComp-al amúgy igen macerás, itt jön a képbe az én programom. A gond az, hogy a CatComp nem generál makrókat, csak számokat és sztringeket. Ezeket mondjuk fel lehetne már használni a makrók elkészítéséhez, de ez nem fehér embernek való meló, ezért inkább a magam részéről a gépre bíztam.

A program feladata távirati stílusban a .cd-ben talált azonosítókhoz C-makrók rendelése. Persze a hatékonyság érdekében több dolog is belekerül a C forrásba (ezek beillesztéskor bekapcsolhatók), lássuk mik ezek:

A forráskód eleje ilyesmi lesz:

/*
** Macro definitions for program.cd
** - this is a machine generated source - do not edit by hand
** created by Localizer © 1999 Att1s
*/

Szóval rizsa. De ilyet a CatComp is belerak.

Ez és néhány #ifdef/#ifndef után jönnek a katalógusleíró fájlban talált azonosítók és azok számértéke. (Ezek egyértelműen adottak a .cd fájlban, lásd CatComp.doc). Na ilyet a CatComp is csinál. pl.:

#define ID_HELLO 1000

Ezután jön egy tömb, ami kb. ugyanaz mint a CatComp féle CatCompArray. Itt a tömb elemei egy longból és egy sztringből álló struktúrák, az id a sztring azonosító számértéke, a sztring meg a .cd fájlban talált szöveg. pl.:

struct loc_arr {
ULONG id;
STRPTR msg;
};

static struct loc_arr builtin_strings[ ] = {
ID_HELLO, "Hello",
...
~0
};

 

Leghátul jönnek maguk a makrók, amely az előző tömbre vonatkozó indexeket tartalmaznak. pl.:

#define MSG_HELLO GetStr(0)

Ennyi. Ahhoz hogy ezek az utóbbi makrók működjenek, kell a következő kódrészlet is:

STRPTR GetStr(ULONG idx)
{
register struct loc_arr *pt = builtin_strings + idx;

return GetCatalogStr(catalog, pt->id, pt->msg);
}

 

aholis a struct Catalog *catalog változót inicializálni kell.

Egyébként a programot boldoggá teheted azzal, ha a sztring azonosítók az MSG_ előtaggal kezdődnek (ezt javasolja a style guide is egyébként), de ez nem szükséges.

Ennyi.

re, Att1s

Ha teccett a cikk, kukkancs be a honlapomra: http://sunny.pmmf.hu/~hilandr
vagy írj az att1s@freemail.c3.hu címre.

Ha nem teccett, a flameket az emeric@abakusz.matav.hu címre várom.

(Minden hátsó szándék nélkül hívnám fel a figyelmet arra, hogy az abakusz percenként körülbelül kétszáz levél elküldésére képes, már abban az esetben, ha a fentebb említett flamekre válaszolva néhány biztonsági másolatot is utána gondolnék szalajtani, természetesen egyidôben az att1s címére indított forward üzenetekkel együtt, úgyhogy csak szolídan-Emeric SH)

 

 
(C) Copyright 1999 SpaceHawks