/**************/ /* transdlg.c */ /**************************************************************************** PROGRAM: transdlg.c PURPOSE: Kääntää dialogit kieleltä toiselle AUTHOR: Vesa Lappalainen 29.8.1993 UPDATE: 21.10.1994: + SetNoTranslate("* - [*;[*]"); USAGE: Projektiin laitettava: transdlg.c, language.c, mjonot.c Jos halutaan kääntää dialogeja eri kielien välillä toimitaan seuraavasti: 1) Kirjoitetaan kaikki dialogit ja menut tietyllä kielellä (vaikkapa suomeksi kun siinä on pisimmät sanat). 2) Niiden dialogien ja menujen tunnukset, joiden ei haluta kääntyvän, nimetään numerolle joka on suurempi kuin kutsulla SetTranslateLimit(raja) asetettu raja. Oletuksena raja on 5000. Kääntymättömiksi kannattaa usein laittaa ainakin Edit-kentät ja Combo-boxit. Tosin jos kieltä ei vaihdella edestakaisin ohjelman ajon aikana, vaan vaihto tehdään tasan yhden kerran ohjelma alussa, ei kääntymättömiä kenttiä välttämättä tarvita. Tietysti edit-kenttäkin voi olla käännettävä, mutta tällöin kieleten edestakaisissa vaihdoissa voi sanasto lisääntyä kohtuuttoman suureksi. 3) Ohjelman aluksi tai ennen ensimmäisen dialogin kännistymistä lisätään kutsu: ReadLanguage(oma_inifile,kaannos_polku,oletus_kaannos) joka lukee omasta .INI-tiedostosta määritykset: [General] Language=0 Translate=C:\WINDOWS\WINCD\wincd.tra Kielten numeroissa 0 on oletuskieli, -1 on oletuskieli ja kieltää samalla kielen vaihdon (säästää muistia, jos kieltä ei haluta vaihtaa) ja muut ovat vieraita kieliä. Käytännössä on language.h:ssa on määritelty KIELIA=2, joten vain numerot -1,0 ja 1 ovat käytössä Jos riviä Translate= ei ole, luetaan käännös tiedostosta kaannospolku\oletus_kaannos 4) Aina kunkin dialogin WM_INITDIALOG-viestiin liitetään kutsu: TranslateDialog(hDlg,0) Tämä kääntää ikkunan (ei tavitse olla dialogi) otsikon ja kaikkien sen lapsi-ikkunoiden otsikot (dialogissa mm. static.kentät) sekä menut aktiiviselle kielelle. Modeless-dialogien tapauksessa voidaan myös (jos ei haluta muuttaa valmista dialogin ikkunafunktiota) tehdä: hDlg = CreteDialog(... TranslateDialog(hDlg); Modal-dialogeille tämä ei onnistu, koska niille ei palata luomiskutsusta ennenkuin dialogi on hävinnyt. Toinen tapa välttää valmiin ikkunafunktion muuttaminen (jos esim. jopa saatu vain .OBJ tai .LIB) on uuden ikkunafunktion tekeminen, joka ensin kutsuu alkuperäistä funktiota ja sitten WM_INITDIALOG tapauksessa vaihtaa vielä kielen. Tämä voidaan helpoiten tehdä alla olevalla makrolla TrINITPROC Olemassa olevaan dialogin funktioon voidaan myös lisätä alkuun kutsu: TranslateHandler(hWnd,message,wParam,lParam); joka tarkistaa onko kyseessä dialogin alustus ja jos on, niin kääntää dialogin. 5) Muihin tulostettaviin teksteihin laitetaan kutsu T(teksti). Esim. jos normaalisti kirjoitettaisiin: MessageBox(hWnd, "Tallennus epäonnistui!",WTITLE,... kirjoitetaan muodossa: MessageBox(hWnd, T("Tallennus epäonnistui!"),T(WTITLE),... 6) Ohjelman loppuun lisätään kutsu: SaveLanguageAndFree(oma_inifile,kaannos_polku,oletus_kaannos) 7) Jos halutaan kielen vaihto kesken ohjelman suorituksen, vaihdetaan käynnissä olevat dialogit ensin suomen kielelle takaisin kutsulla TranslateDialog(hDlg,1) sitten vaihdetaan kieli halutuksi set_language(kieli) ja käännetään dialogit jälleen. Jos vaihdellaan vain kahden kielen välillä, ja aktiivisena on vain yksi dialogi (tai ketju, jolle voidaan ilmaista yhteinen esi-isä) voidaan käyttää kutsua: FlipDialogLanguage(hDlg,back) joka tuo dialogin vieraalta kieleltä suomen kielelle tai vaihtaa suomen kielisen dialogin kielelle kieli. Tämä toimii hyvin, jos on vain yksi aktiivinen dialogi. Jos aktiivisia (rinnakkaisia, isättömiä) dialogeja on useita, pitää menetellä ensin mainitulla tavalla tai sulkea kaikki aktiiviset dialogit ja avata ne uudelleen. 8) Edellisen tilalla ehkä kaikkein helpointa on käyttää TranslateProgram(hWnd,back) joka kääntää kaikki ohjelman ikkunat joko vieraalle kielelle (back=0) tai takaisin (back=1). Jos käännetään takaisin, pitää kieli olla ennen kääntämistä 1 ja kääntämisen jälkeen kieli muuttuu automaattisesti 0:ksi. Vielä on olemassa flip-kutsuja, jotka vaihtavat kahden kielen välillä. Ks. seuraava luettelo. ============ Aliohjelmat: (transdlg.c) ============ int SetTranslateLimit(int l) ---------------------------- - asettaa sen tunnusnumerorajan, jota isompien tunnusten ikkunoita ei käännetä, oletus ilman kutsua on 5000 int SetNoTranslate(LPCSTR *s) ---------------------------- - asettaa maskin, jonka mukaisia sanoja ei käännetä IKKUNOISTA. (T kääntää riippumatta tästä). Esim jos s = "*a;*b", niin ei käännetä a- ja b-loppuisia sanoja. int ChangeWindowTitle(HWND hWnd,LPARAM back) ---------------------------------------------- - kääntää ikkunan otsikon vieraalle kielelle jos back==0 ja suomeksi jos back==1. Yleensä tätä ei käytetä, vaan myöhemmät aliohjelmat kutsuvat tätä. int TranslateMenu(HWND hWnd,int back) ------------------------------------- - kääntää menun vieraalle kielelle tai suomeksi int TranslateDialog(HWND hDlg,int back) --------------------------------------- - kääntää ikkunan ja sen kaikkien lapsi-ikkunoiden tekstit ja menut back==0 => vieras kieli, back==1 => suomen kieli int TranslateProgram(HWND hDlg,int back) --------------------------------------- - kääntää kaikki ohjelman ikkunat, muuten ks ed. int TranslateProgramAndRead(HWND hWnd,const char *transfile) ------------------------------------------------------------ - kääntää ohjelman kaikki ikkunat ja lukee tarvittaessa transfile nimisen käännöstiedoston. Jos == NULL, niin käytetään sisäisesti talletettua tiedoston nimeä, ks. ReadTranslate void FlipDialogLanguage(HWND hDlg,int lang) ------------------------------------------- - vaihtaa dialogin kielen joko suomeksi tai vieraaksi kieleksi int FlipProgramLanguage(HWND hDlg,int lang) ------------------------------------------------- - vaihtaa koko ohjelman kaikkien ikkunoiden kielen, muuten ks. edellä int SaveLanguageAndFree(char *inifile,char *path,char *def) ------------------------------------------------- - tallettaa nykyisen kieliasetuksen ja käänöstiedoston nimen .INI-tiedostoon. file on .INI-tiedoston nimi. path\def on käännöstiedoson nimi. Jos .INI-tiedostosa on jo ennestään kielitiedoston nimi, ei nimeä talleta. .INI-tiedoston muoto: [General] Language=0 Translate=C:\WINDOWS\WINCD\wincd.tra Jos path ja def == NULL, niin nimenä käytetään ini-nimestä muodostettua nimeä (.ini => .tra). Jos inifile == NULL, niin käytetään sisäisesti tallettua nimeä ( ReadLanguage... ) int ReadLanguage(char *inifile,char *path,char *def) ------------------------------------------------- - lukee .INI-tiedostosta kielitiedot ja lukee tämän jälkeen joko .INI-tiedostosta löytyneen käännöslistan tai jollei siellä ole nimeä, niin path\def-nimisen käännöslistan. int ReadLanguageIfNeeded(HWND hWnd,const char *inifile) -------------------------------------------------------- - lukee .INI-tiedostosta kielitiedot ja lukee tämän jälkeen käännöslistan JOS vieras kieli on käytössä, muuten vain sisäisesti tallettaa käännöstiedoston nimen. Jos vieraskieli on käytössä, niin vaihtaa myös sille kutsulla TranslateProgram(hWnd,0); int ReadTranslate(const char *transfile,int lang) -------------------------------------------------------- - kuten read_translate, mutta jos transfile == NULL, niin käytetään sisäisesti talletettua nimeä (ReadLanguage...) LONG TranslateHandler(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam) -------------------------------------------------------- - käännöksen käsittely jos message on WM_INITDIALOG, eli tämän kutsu voidaan lisätä jokaisen dialogin ikkunafunktion alkuun tai tehdä uusi ikkunafunktio makrolla TrINITPROC(old,new) Muut aliohjelmat: ks. language.c ============ Makrot: (language.h) ============ T("jono") - kääntää jonon oletuskielelle, jos jonoa ei ole, lisää käännöstiedostoon rivin "jono" "@@jono" ja palauttaa samalla tekstin "@@jono", josta helposti huomaa, ettei sanaa ole vielä käytetty. TA("jono",a) - kuten edellä, muutta a määrää lisätäänkö sana vai ei TB("jono") - kääntää jonon oletuskielestä suomen kielelle, ei lisää käännöstiedostoon. ============ Makrot: (transdlg.h) ============ TrINITPROC(of,nf,type) - tekee uuden ikkunafunktion uf, joka hoitaa alustuksessa tarvittavat hommat kun alkuperäinen ikkunafuktio on ne hoitanut. Esim. jos on valmis ikkunafunktio About-dialogin käsittelemiseksi, niin dialogin kielen vaihto voidaan tehdä vaikkapa seuraavasti: BOOL CALLBACK export_ About(....) ... TrINITPROC(About,IAbout,BOOL) ja sitten dialogin luomisessa ikkunafunktioksi ilmoitetaan IAbout. ============ Toiminnasta: ============ Kääntäminen tehdään ajon aikana, eikä se muuta varsinaisia Windowsin sisäisiä tietorakenteita (.RES-osassa olevia). Näin ollen jos käännetty dialogi suljetaan ja avataan uudelleen ilman että sen WM_INIT.. osassa on TranslateDialog-kutsua, tulee dialogi aina suomen (alkuperäisellä) kielellä. Kääntäminen perustuu sanastoon ja makroihin T, TA ja TB. Sanastotiedosto on muotoa: "lause 1 suomeksi" "sentence 1 in english" "lause 2 suomeksi" "sentence 2 in english" Jos sana puuttuu sanastotiedostosta, niin makrot T ja TA lisäävät tiedostoon rivin "lause 1 suomeksi" "@@lause 1 suomeksi" Nämä @@-alkuiset sanonnat pitää sitten joskus muistaa päivittää. Siis itse käännöstyön tekeminen voidaan tehdä vasta kun ohjelmaa on jo ajettu useita kertoja ja näin sanastosta puuttuvat sanat saatu merkityksi. Ohjelman kehityksessä riittää vain kirjoittaa jokainen tulostuva teksti T-makron sisälle. Jos kielenä pidetään -1, ei itse käännöstä yritetäkään tehdä. Tietysti T-makro voidaan korvata makrolla #define T(sana) sana jolloin itse käännösaliohjelmia ei edes tarvita ohjelman kehityksen aikana. C-kielen %-merkit säilytetään sellaisenaan, eli kutsua sprintf(s,"Olen %d vuotta vanha.",ika) vastaava kääntyvä kutsu on sprintf(s,T("Olen %d vuotta vanha."),ika) ja vastaava käännöstiedoston rivi on muotoa: "Olen %d vuotta vanha." "My age is %d" Näin sanajärjestys riippuavat asiat voidaan kiertää suht. helposti. HUOM! Muista teksteissä C-kielen % säännöt, eli % tulostuslauseen tullessa pitää kirjoittaa %%. Muissa teksteissä kuin ?printf( ja ?scanf- teksteissä %-kirjoitetaan normaalisti. \ kirjoitetaan \\ ja rivinvaihto \n jne.. Mitään ongelmia ei kuitenkaan tule, kun vain matkitaan käännöstiedoston suomenkielistä saraketta ja erikoismerkkien muotoa siinä. ======== Kojattu: 21.10.1994 ======== Jos dialogin otsikossa on muuttuvaa tekstiä, lisäytyy sekin aina käännöslistaan (esim. tiedoston nimi.) Miten tämä voitaisiin välttää. Tarvitaan varmaan tekstifiltteri, jolla sanotaan että minkälaisia asioita EI saa kääntää (Jos otsikko on EDIT - [koe.txt], niin filtteri voisi olla "* - [*" tms.). ======== Kojattu: 13.11.1996 /vl ======== 32-bittisessä TranslateProgram muutettu kasittelemaan saikeen ikkunat Samoin 0xFFFF vaihdettu -1 (koska 32-bittisessä piti olla 0xffffffff ======== Vikoja: ======== Em. maskilla otsikko jää kokonaan kääntymättä ja se pitää pyytää uudelleen! ****************************************************************************/ #include #include #include #include "portable.h" #include "mjonot.h" #include "transdlg.h" static unsigned int TranslateLimit = TRANSLATE_LIMIT; static LPCSTR NoTranslate = ""; /***************************************************************************/ static int IsNoTranslate(const char *s) { LPCSTR pe,ps = NoTranslate; char nt[100]; int nc,n,mn = sizeof(nt)-1; if ( NoTranslate == NULL ) return 0; do { pe = _fstrchr(ps,';'); n = (int)(pe ? pe - ps : _fstrlen(ps)); nc = n < mn ? n : mn; _fstrncpy(nt,ps,nc); nt[nc] = 0; if ( wildmat(s,nt) == 0 ) return 1; ps += n+1; } while ( pe ); return 0; } /***************************************************************************/ int SetNoTranslate(LPCSTR s) { NoTranslate = s ? s : ""; return 0; } /***************************************************************************/ int SetTranslateLimit(int l) { int old_val = TranslateLimit; TranslateLimit = l; return old_val; } /***************************************************************************/ int ComboBoxChild(HWND hWnd) /* ComboBoxin lapsi on Edit ikkuna ja sitä ei käännetä! */ { char s[20]; HWND phWnd; GetClassName(hWnd,N_S(s)); if ( strcmp(s,"Edit") != 0 ) return 0; phWnd = GetParent(hWnd); if ( !phWnd ) return 0; GetClassName(phWnd,N_S(s)); if ( strcmp(s,"ComboBox") != 0 ) return 0; return 1; } /***************************************************************************/ int ChangeWindowTitle(HWND hWnd,LPARAM back) /* Vaihdetaan otsikko */ /* Paluu 0 = vaihdettiin ** 1 = ei tekstiä ** 2 = ei saa kääntää ** 3 = ComboBoxin lapsi => ei käännetä! ** Jos ikkunan id on laiton ja kyseessä on lapsi-ikkuna, niin ei käännetä. ** Ei-lapsi-ikkunan id voi olla sen menun kahva, ja tällaisen ikkunan ** otsikko kyllä käännetään. */ { char s[200]; UINT id; if ( !GetWindowText(hWnd,N_S(s)) ) return 1; id = GetWindowID(hWnd); if ( ( id >= TranslateLimit && (id&0xffff) != 0xffff && GetParent(hWnd) ) || IsNoTranslate(s) ) return 2; if ( ComboBoxChild(hWnd) ) return 3; SetWindowText(hWnd,( back ? TB(s) : TA(s,1) )); return 0; } /***************************************************************************/ static int IsHelpText(char *s) { char st[10],*p; strncpy(st,s,sizeof(st)-1); st[sizeof(st)-1]=0; p = strchr(st,'&'); if ( p ) /* Delete & */ while ( *p ) { *p=*(p+1); p++; } return ( strcmpi(st,"Help") == 0 || strcmpi(st,"Apua") == 0 ); } static char s[200]; /***************************************************************************/ static int translate_hmenu(HMENU hMenu,int back) { int i,n; UINT id; HMENU hSub; UINT state; if ( !hMenu ) return 1; n = GetMenuItemCount(hMenu); if ( n<=0 ) return 1; for (i=0; i 0 ) { if ( IsHelpText(s) ) state |= MF_HELP; if ( !IsNoTranslate(s) ) ModifyMenu(hMenu, i, MF_BYPOSITION | state, (UINT)hSub, ( back ? TB(s): T(s)) ); } } else if ( id < TranslateLimit && GetMenuString(hMenu,i,N_S(s),MF_BYPOSITION) > 0 && !IsNoTranslate(s) ) ModifyMenu(hMenu, i, MF_BYPOSITION | state, id,( back ? TB(s): T(s)) ); } return 0; } /***************************************************************************/ int TranslateMenu(HWND hWnd,int back) { HMENU hMenu=GetMenu(hWnd); translate_hmenu(hMenu,back); DrawMenuBar(hWnd); return 0; } /***************************************************************************/ BOOL CALLBACK _export EnumChilds(HWND hWnd,LPARAM back) { ChangeWindowTitle(hWnd,back); return TRUE; } /***************************************************************************/ int TranslateDialog(HWND hDlg,int back) { FARPROC EnumProc = MakeProcInstance((FARPROC)EnumChilds,GetWindowInstance(hDlg)); if ( language()<=0 ) return 0; ChangeWindowTitle(hDlg,back); EnumChildWindows(hDlg,(WNDENUMPROC)EnumProc,back); (void)FreeProcInstance(EnumProc); TranslateMenu(hDlg,back); InvalidateRect(hDlg,NULL,TRUE); return 0; } /***************************************************************************/ BOOL CALLBACK _export EnumProgs(HWND hWnd,LPARAM back) { TranslateDialog(hWnd,(int)back); return TRUE; } /***************************************************************************/ int TranslateProgram(HWND hDlg,int back) { FARPROC EnumProc = MakeProcInstance((FARPROC)EnumProgs,GetWindowInstance(hDlg)); #ifndef __WIN32__ HTASK hTask = GetCurrentTask(); (void)hDlg; if ( language()<=0 ) return 0; EnumTaskWindows(hTask,(WNDENUMPROC)EnumProc,back); #else DWORD thNr = GetWindowThreadProcessId(hDlg,NULL); if ( language()<=0 ) return 0; EnumThreadWindows(thNr,(WNDENUMPROC)EnumProc,back); #endif (void)FreeProcInstance(EnumProc); if ( back ) set_language(0); return 0; } /***************************************************************************/ int FlipDialogLanguage(HWND hDlg,int lang) { int l = language(); if ( l < 0 ) return 0; if ( l ) { TranslateDialog(hDlg,1); set_language(0); } else { set_language(lang); TranslateDialog(hDlg,0); } return 0; } /***************************************************************************/ int FlipProgramLanguage(HWND hDlg,int lang) { int l = language(); if ( l < 0 ) return 0; if ( l ) { TranslateProgram(hDlg,1); set_language(0); } else { set_language(lang); TranslateProgram(hDlg,0); } return 0; } /***************************************************************************/ char *FullName(const char *path,const char *name, const char *file,const char *ext) { char *p; kopioi_jono(N_S(s),path); if ( *s && s[strlen(s)-1] != '\\' ) liita_jono(N_S(s),"\\"); liita_jono(N_S(s),name); if ( *s ) return s; kopioi_jono(N_S(s),file); p = strrchr(s,'.'); if ( p ) *p = 0; liita_jono(N_S(s),ext); return s; } static char tra_name[100]; static char ini_name[100]; #define INI(ini) (ini?ini:ini_name) /***************************************************************************/ static int SaveUsedLanguage(const char *inifile) /* Talletetaan kieli ja käännöstiedoston paikka, jollei sitä jo ole. */ { int lang = language(); char str[10]; sprintf(str,"%i",lang); WritePrivateProfileString("General","Language",str,INI(inifile)); return 0; } /***************************************************************************/ int SaveLanguageAndFree(const char *inifile,const char *path,const char *def) /* Talletetaan kieli ja käännöstiedoston paikka, jollei sitä jo ole. */ { char str[10]; const char *p = ""; SaveUsedLanguage(inifile); if ( GetPrivateProfileString("General","Translate","",N_S(str),INI(inifile)) == 0 ) { if ( def && def[0] ) p = FullName(path,def,INI(inifile),".tra"); WritePrivateProfileString("General","Translate",p,INI(inifile)); } free_translate(); return 0; } /***************************************************************************/ static int GetTranslateName(const char *inifile,const char *path,const char *def) { int lang=GetPrivateProfileInt("General","Language",0,inifile); GetPrivateProfileString("General","Translate", FullName(path,def,inifile,".tra"),N_S(tra_name),inifile); if ( tra_name[0] == 0 ) kopioi_jono(N_S(tra_name),FullName(path,def,inifile,".tra")); kopioi_jono(N_S(ini_name),inifile); return lang; } /***************************************************************************/ int ReadLanguage(const char *inifile,const char *path,const char *def) { int lang = GetTranslateName(inifile,path,def); return read_translate(tra_name,lang); } /***************************************************************************/ int ReadLanguageIfNeeded(HWND hWnd,const char *inifile) { int ret, lang = GetTranslateName(inifile,NULL,NULL); if ( lang <= 0 ) return 0; ret = read_translate(tra_name,lang); TranslateProgram(hWnd,0); return ret; } /***************************************************************************/ int ReadTranslate(const char *transfile,int lang) { const char *p = transfile; if ( p == NULL ) p = tra_name; return read_translate(p,lang); } /****************************************************************************/ int TranslateProgramAndRead(HWND hWnd,const char *transfile) { if ( language() == 1 ) return 0; ReadTranslate(transfile,1); TranslateProgram(hWnd,0); return 0; } /****************************************************************************/ int FlipProgramLanguageAndRead(HWND hWnd,const char *transfile) { int ret,l = language(); if ( l < 0 ) return 0; if ( l ) ret = TranslateProgram(hWnd,1); else ret = TranslateProgramAndRead(hWnd,transfile); SaveUsedLanguage(NULL); return ret; } /***************************************************************************/ LONG TranslateHandler(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam) { #pragma argsused if ( message == WM_INITDIALOG || message == WM_CREATE ) { TranslateDialog(hWnd,0); } return -1; }