Komponent TLangcb
Borland C++ Builder v6.
Wstęp.
Komponent TLangcb został napisany w środowisku Borland C++ Builder w wersji 6.0. Ponieważ komponent wykorzystuje biblioteki z tego środowiska, mogą wystąpić problemy z użytkowaniem komponentu we wcześniejszych wersjach BCB.
Zastosowanie.
Komponent TLangcb służy do tworzenia programów w różnych wersjach językowych, komponent działa w dwóch trybach, pierwszy tryb to wygenerowanie plików językowych na podstawie listy języków, z tym że generując nawet kilka plików językowych, np. dla języków angielskiego, niemieckiego i polskiego zostaną w nich zapisane takie nazwy etykiet jakie mają komponenty na formularzu, więc wyjaśniam tutaj, że komponent nie tłumaczy nazw etykiet, a tylko w trybie pierwszym generuje pliki językowe i w każdym pliku umieszcza to samo, tłumaczenia należy dokonać ręcznie, otwierając pliki np. w Notatniku, ponieważ są to pliki tekstowe (niezależnie od rozszerzenia jaki zostanie im nadane) i posiadają strukturę plików *.ini. Drugi tryb pracy - główny - jest bardziej złożony, polega on na wczytywaniu wybranego pliku i zmianie nazw etykiet komponentów znajdujących się na formularzu, nazwy etykiet są zmieniane na podstawie wygenerowanego pliku językowego, zmiany te są dokonywane automatycznie przez komponent, poprzez wywołanie pojedynczego polecenia. Oprócz zmian nazw etykiet na wybrany język komponent może wygenerować własne menu językowe i podłączyć je pod wybraną pozycję w MainMenu, w ten sposób pojawi się automatycznie lista języków w menu, gdzie po kliknięciu na wybraną pozycję można dokonać zmiany języka. Komponent może obsługiwać teoretycznie nieograniczoną liczbę formularzy, poprzez umieszczenie na każdym formularzu odrębnego komponentu, można je jednak ze sobą połączyć poprze wybranie odpowiedniej właściwości w komponencie i w ten sposób sterować wszystkimi komponentami w programie poprzez tylko jeden komponent.
Komponent obsługuje głównie właściwości Caption komponentów znajdujących się na formularzu, oraz właściwości Text większości komponentów z wyjątkiem komponentu TMemo i TRichEdit. Obsługiwany jest również w całości komponent TRadioGroup. W przypadku komponentu TLabeledEdit obsługiwane są zarówno właściwość Caption jak i Text. Komponent obsługuje również właściwość Caption formularza.
Ważna uwaga!
Po wygenerowaniu plików językowych
nie można w żadnym razie zmieniać nazwy formularza (można zmieniać
właściwość Caption)! Nie należy również umieszczać na formularzu
nowych komponentów, ponieważ komponent TLangcb nie weźmie ich pod uwagę, nie
należy również zmieniać nazw komponentów, ponieważ mogą zostać nie rozpoznane,
nie należy również niczego usuwać z formularza, można za to wprowadzać zmiany we
właściwościach Caption. Tak więc komponent należy stosować po zakończeniu prac
nad aplikacją.
Jeżeli jednak dokonamy zmian po wygenerowaniu plików językowych, należy usunąć
te pliki językowe i wygenerować nowe, po takim zabiegu komponent znów będzie
działał prawidłowo.
Instalacja komponentu.
Pobierz komponent
TLangcb z przykładowym programem - 370 KB
komponent dostępny
poprzez klikomierz, jeśli link nie działa
daj mi znać.
W archiwum razem z komponentem znajduje się wersja demonstracyjna programu napisana w BCB 6, przedstawiająca kilka możliwości programu. Przed uruchomieniem projektu trzeba zainstalować komponent. Należy rozpakować archiwum do wybranego folderu, przy czym nazwa folderu i wszystkich podfolderów powinna składać się z pojedynczego wyrazu. Po wypakowaniu trzeba odszukać paczkę PackLangcb.bpk, wystarczy więc po uruchomieniu środowiska BCB zamknąć otwarty projekt poprzez menu File | Close ALL, a następnie poprzez menu File | Open Project... odszukujemy plik PackLangcb.bpk. W oknie które się naciskamy przycisk Install, kompilowanie paczki nie jest wskazane, chyba, że występują problemy z instalacją. Jeżeli wszystko zostało zrobione prawidłowo, komponent powinien się zainstalować na nowo utworzonej palecie Cyfbar. Teraz wystarczy tylko zrestartować BCB.
Jeżeli przy próbie użycia komponentu wyskocz komunikat w stylu „[Linker Error] Unable to open file 'LANGCB.RES'” trzeba wejść w menu 'Tools | Environment Options...' następnie przechodzimy na zakładkę 'Library' i klikamy na przycisku [...] znajdującym się przy pozycji 'Library path:', w oknie 'Directories' które powinno wyskoczyć klikamy na przycisku [...] znajdującym się przy pozycji 'Greyed items denote invalid path' i wybieramy katalog w którym umieściliśmy nasz komponent np: 'C:\Components\Langcb', nastęnie klikamy na przycisk 'Add', potem przycisk 'OK' i zamykamy wszystkie okna. Jeżeli komunikat błędu nadal się pojawia trzeba dodać tą samą ścieżkę dostępu (np. E:\Components\Langcb) do menu: 'Project | Options' następnie przechodzimy na zakładkę 'Directories/Conditional' i klikamy na przycisku [...] znajdującym się przy pozycji 'Library path' i postępujemy dokładnie tak samo jak poprzednio w menu 'Tools | Environment Options...'. Jeżeli błąd nadal występuje, napisz do mnie opisując dokładnie wszystkie komunikaty błędów i sytuacje w których się one pojawiają.
Może się zdarzyć, że paczka dołączona do komponentu TLangcb, nie będzie chciała się skompilować i będą zgłaszane komunikaty o brakujących bibliotekach. W takiej sytuacji trzeba utworzyć własną paczkę. W menu File wybieramy New a następnie Other... W oknie, które wyskoczy wybieramy Package. Wyskoczy okno tworzenia nowej paczki. Zapisujemy paczkę pod nową nazwą (tylko znaki języka angielskiego i jednoczłonowa nazwa paczki) w katalogu z komponentem TLangcb, w tym samym oknie naciskamy przycisk Add, a następnie w nowo otwartym oknie naciskamy przycisk Browse i odszukujemy plik komponentu Langcb.cpp. Gdy plik komponentu zostanie dołączony do paczki, kompilujemy paczkę. Teraz nie powinno być już z tym żadnych problemów, po skompilowaniu instalujemy paczkę.
Jeżeli masz problemy z instalacją komponentów napisz. Opisz problem bardzo dokładnie, podając wszystkie komunikaty zgłaszane przez kompilator.
Tworzenie programu wielojęzykowego z pojedynczym formularzem.
Zanim przystąpisz do czytania tego artykułu, otwórz przykładowy projekt znajdujący się w katalogu z komponentem. Jest to przykład aplikacji składającej się z wielu formularzy, ale na razie skup swoją uwagę na formularzu głównym Form1 i jednostce Unit1.cpp.
Generowanie plików językowych.
Umieszczamy na formularzu kilka komponentów i nadajemy im np. polskie nazwy. Gdy już zaprogramujemy wszystko co chcemy i aplikacja jest już gotowa, umieszczamy na formularzu komponent Langcb1, na jednym formularzu można umieszczać tylko jeden taki komponent, następnie klikamy na właściwości 'LangList' komponentu i tworzymy listę językową. Lista taka składa się z dwóch elementów: nazwy języka i nazwy pliku językowego.
Przykładowa lista może wyglądać tak:
deutsch=deutsch.lng
english=english.lng
polski=polski.lngJak widać najpierw występuje nazwa języka, potem stawiamy znak równości (=), a na końcu umieszczamy nazwę pliku językowego. Nazwa pliku jak również jego rozszerzenie mogą być dowolne, ale nie mogą się powtarzać, podobnie jak nazwa języka. W podanym przykładzie pliki językowe zostaną wygenerowane w tym samym katalogu, w którym znajduje się projekt aplikacji i z tego katalogu będą wczytywane. Zasada jest taka, że program zawsze (z jednym wyjątkiem, ale o tym później) będzie tworzył i wczytywał pliki językowe w katalogu z programem lub w podkatalogu znajdującym się w katalogu programu. Można w katalogu programu utworzyć podkatalog np. o nazwie 'Language' (nazwa dowolna) i w nim utworzyć pliki językowe, będą one również z niego wczytywane, wtedy lista powinna wyglądać tak:
Przykładowa lista z podkatalogiem w katalogu programu:
deutsch=language\deutsch.lng
english=language\english.lng
polski=language\polski.lngGdy lista jest już gotowa, w programie wywołujemy np. zdarzenie OnShow (nigdy OnCreate, ani OnPaint, czy podobne) i umieszczamy w nim kod generujący pliki językowe:
void __fastcall TForm1::FormShow(TObject *Sender)
{
Langcb1->CreateFilesLang(); // funkcja wywoływana tylko raz do stworzenia plików językowych
}Po skompilowaniu i uruchomieniu aplikacji w katalogu programu, lub podkatalogu powinny pojawić się pliki językowe. Funkcję CreateFilesLang można wywołać również w dowolnym innym zdarzeniu np. OnClick dla dowolnego przycisku, ważne jest tylko to, żeby po wygenerowaniu plików, usunąć tą funkcję z programu i dla bezpieczeństwa tychże plików skompilować program jeszcze raz bez tej funkcji, w przeciwnym razie przy każdorazowym wywołaniu funkcji CreateFilesLang będzie ona generowała pliki językowe, zastępując stare i np. już przetłumaczone.
Przykład wygenerowanych plików językowych: polski.lng; przetłumaczone: english.lgn; deutsch.lng.
Struktura i tłumaczenia plików językowych.
Jak wcześniej wspomniałem pliki językowe mają taką samą konstrukcję jak pliki *.INI, dlatego tłumaczeniu podlegają tylko wartości przypisane do nazwy wartości, zarówno nazwa sekcji jak i nazwy wartości nie mogą zostać zmienione.
Przykładowy plik językowy:
[FORM1]
form1=Program demonstracyjny
0=Program prezentuje możliwości komponentu TLangcb
1=Okno edycji
2=Przycisk
4=Pole wyboru
6=Stwórz
7=Przycisk3
9= Co sądzisz o komponencie TLangcb?
12=Cofnij
13=Powtórz
15=Wytnij
16=Kopiuj
17=Wklej
18=Usuń
20=&PlikNa zielono została zaznaczona nazwa sekcji, nie podlega ona zmianie, nazwa sekcji to nazwa formularza dla którego generuje się pliki językowe, na niebieską są zaznaczone nazwy wartości ich również nie można zmieniać, natomiast wartości mogą być zmieniane dowolnie.
Przykładowy przetłumaczony na angielski plik językowy:
[FORM1]
form1=Demonstrative Programme
0=The Programme presents the possibility of component the TLangcb
1=Field of edition
2=Button
4=CheckBox
6=Create
7=Button3
9= What do you judge about component TLangcb?
12=Undo
13=Redo
15=Cut
16=Copy
17=Paste
18=Delete
20=&FileW przypadku pliku językowego dla wielu formularzy znajdzie się w nim wiele sekcji, a każda z nich będzie miała taką nazwę jak formularz dla którego została utworzona.
Jeżeli na formularzu będzie znajdował się komponent typu TRadioGroup to zostanie dla niego utworzona odrębna sekcja powiązana z sekcją formularza.Przykład sekcji dla komponentu typu TRadioGroup:
[FORM1]
form1=Program demonstracyjny
0=Program prezentuje możliwości komponentu TLangcb
1=Okno edycji
2=Przycisk
4=Pole wyboru
6=Stwórz
7=Przycisk3
9= Co sądzisz o komponencie TLangcb?
12=Cofnij
13=Powtórz
15=Wytnij
16=Kopiuj
17=Wklej
18=Usuń
20=&Plik
[R9FORM1]
0=beznadziejny
1=kiepski
2=słaby
3=dobry
4=bardzo dobry
5=świetny
6=wspaniałyJak widać komponent RadioGroup1 ma nazwę wartości 9 i znajduje się w sekcji FORM1, dlatego utworzona dla niego sekcja ma nazwę R9FORM1, w ten sposób sekcja została powiązana z formularzem na którym znajduje się komponent oraz z samym komponentem, jeżeli umieścimy na formularzu kilka komponentów typu TRadioGroup to zostanie utworzonych dla nich kilka odrębnych sekcji. W każdej takiej sekcji znajduje się lista elementów obiektu RadioGroup.
Podobna kombinacja, lecz znacznie prostsza występuje w przypadku komponentu TLabeledEdit, nie tworzy się dla niego odrębnej sekcji, lecz ten obiekt posiada dwie właściwości, które zostają zapisane w pliku językowym są to właściwości Caption i Text.
Przykład zapisu dla komponentu typu TLabeledEdit:
[FORM1]
4=Tekst w LabeledEdit
L4=Etykieta okna edycjiWłaściwość Text tego obiektu zostaje zapisana w takiej samej formie jak właściwość Caption innych komponentów, natomiast nazwa wartość dla właściwości Caption zostaje zawsze poprzedzona dużą literą L.
Właściwość Text obiektów typu TEdit jest traktowana jak właściwość Caption. Komponent Langcb generuje również w pliku językowym wartości dla właściwości Caption obiektów typu TMainMenu i TPopupMenu.
Gdy pliki zostaną już wygenerowane otwieramy je np. w Notatniku systemowym i poddajemy tłumaczeniu. Po przetłumaczeniu zapisujemy je zawsze jako tekstowe, przy czym nie zmieniamy im rozszerzenia ani też nazwy, bo program ich nie rozpozna, więc jeśli mamy np. plik english.lng to po przetłumaczeniu zapisujemy go jako plik tekstowy ale dokładnie pod tą samą nazwą i z tym samym rozszerzeniem. Po przetłumaczeniu dobrze jest wykonać kopię zapasową takiego pliku na wypadek przypadkowego wywołania funkcji CreateFilesLang.
Dodawanie menu językowego do własnego MainMenu oraz zmiana języka.
Gdy pliki językowe zostają już przez nas przetłumaczone, zarówno komponent jak i cały program są już gotowe do działania i można dowolnie zmieniać język. Język programu można wybierać na trzy sposoby.
Tworzenie listy języków w menu.
Najlepszym rozwiązaniem jest utworzenie listy języków w menu utworzonym za pomocą komponentu typu TMainMenu, w tym celu umieszczamy na formularzu komponent TMainMenu a następnie tworzymy dla niego menu, nie tworzymy jednak menu dla listy języków, ponieważ zostanie ona utworzona automatycznie przez komponent Langcb1, we właściwości MenuLang komponentu Langcb1 wybieramy z rozwijanej listy pozycję w MainMenu pod którą chcemy podłączyć menu listy języków. Załóżmy, że w MainMenu utworzyliśmy pozycję o nazwie AppLang1 i właściwości Caption = 'Język' (patrz przykładowy program), teraz wystarczy we właściwości MenuLang komponentu Langcb1 wybrać właśnie AppLang1.
Menu listy języków nigdy nie pojawi się na etapie programowania, ponieważ tworzy się je poprzez wywołanie funkcji CreateMenu komponentu Langcb1. Funkcję CreateMenu można wywołać tylko raz i najlepiej w zdarzeniu OnCreate dla formularza głównego:void __fastcall TForm1::FormCreate(TObject *Sender)
{
Langcb1->CreateMenu(); // Tworzenie w MainMenu listy języków
}Jeżeli teraz skompilujemy i uruchomimy program to w menu pod pozycją 'Języki' powinna pojawić się lista języków. Klikając na tej liście zmieniamy język.
Komponent Langcb1 zapamięta raz wybrany język, ale nie załaduje go przy uruchomieniu programu chyba, że w zdarzeniu OnShow formularza głównego umieścimy funkcję Execute(), ta funkcja jest odpowiedzialna za ładowanie domyślnego języka aplikacji, a domyślnym językiem jest zawsze język wybrany jako ostatni. Funkcji nie należy umieszczać w zdarzeniu OnCreate dla formularz bo nie zadziała:void __fastcall TForm1::FormShow(TObject *Sender)
{
Langcb1->Execute();
}Jeżeli chodzi o tworzenie menu języków i zmianę języka to to jest wszystko, komponent sam tym zarządza i nie wymaga w tym celu żadnego dodatkowego kodu, nadmienię tutaj, że lista menu języków nigdy nie zostanie wygenerowana do plików językowych, a to oznacza, że nie podlega ona tłumaczeniu.
Tworzenie listy języków w obiektach typu ListBox i ComboBox.
Innym sposobem na utworzenie menu listy języków mogą być obiekty typu TComboBox i TListBox. Do utworzenia listy języków w tych obiektach służy funkcja LanguageList można ją wywołać np. w zdarzeniu OnShow dla formularza głównego, ale nigdy w zdarzeniu OnCreate.
void __fastcall TForm1::FormShow(TObject *Sender)
{
Langcb1->LanguageList(ListBox1); // Ładowanie listy języków do ListBox1
Langcb1->Execute(); // Ustawianie wybranego wcześniej języka
}Zmiana języka poprze kliknięcie na wybranej pozycji obiektu ListBox1 (lub ComboBox1) może się odbywać w zdarzeniu OnClick dla tegoż obiektu poprzez wywołanie funkcji SetLanguage i przekazanie jako parametru elementu listy:
void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
Langcb1->SetLanguage(ListBox1->Items->Strings[ListBox1->ItemIndex]); // Ładowanie języka z listy ListBox1
}I to jest wszystko jeśli chodzi o tworzenie listy języków w obiektach typu TListBox lub ComboBox, możliwe jest również załadowanie listy do obiektu typy TStringList:
void __fastcall TForm1::FormShow(TObject *Sender)
{
TStringList *Lista = new TStringList;
Langcb1->LanguageList(Lista);
Langcb1->Execute(); // Ustawianie wybranego wcześniej języka
}
Ładowanie języka bezpośrednio z pliku.
Ostatnim sposobem na zmianę języka programu jest jego załadowanie bezpośrednio z pliku, umożliwia to zmianę języka programu poprzez wywołanie funkcji LoadFileLang, ładującej język z dowolnego pliku. Ta funkcja ma zastosowanie tylko w aplikacjach składających się z pojedynczego formularza, ponieważ zmienia steruje językiem tylko tego komponentu z którego została wywołana, nie działa w jej przypadku połączenie szeregowe z innymi komponentami, tak więc jeśli mamy kilka formularzy, a na każdym komponent Langcb i chcemy skorzystać z tej funkcji, to należy ją wywołać dla każdego komponentu oddzielnie:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Langcb1->LoadFileLang("c:\\windows\\english.lng");
}Tej funkcji nie towarzyszy żadna lista języków.
Tworzenie programu wielojęzykowego z wieloma formularzami.
Jeżeli tworzymy aplikację wielojęzykową na którą składa się wiele formularzy, to zaczynamy od tego, że na każdym formularzu umieszczamy jeden (tylko jeden) komponent Langcb1, następnie w komponencie Langcb1 znajdującym się na pierwszym, a więc głównym formularzu we właściwości 'LangList' tworzymy listę językową tak jak to zostało opisane wyżej, gdy lista jest już gotowa przystępujemy do szeregowego łączenia ze sobą wszystkich komponentów. Załóżmy, że mamy aplikację składającą się z 4 formularzy, Form1 - główny; Form2; Form3 i Form4. Na każdym formularzu umieściliśmy po jednym komponencie Langcb1, najpierw w plikach źródłowych dla każdego formularza (Unit1.cpp; Unit2.cpp; Unit3.cpp i Unit4.cpp) w sekcji include umieszczamy odwołanie do pozostałych formularzy, czyli w każdym pliku źródłowym powinien znaleźć się następujący wpis:
#include "Unit1.h"
#include "Unit2.h"
#include "Unit3.h"
#include "Unit4.h"
To jest ważne ponieważ formularze, a co za tym idzie komponenty Langcb1 "muszą o sobie wiedzieć". Gdy zostanie to już zrobione zapisujemy projekt, a następnie przechodzimy do komponentu Langcb1 znajdującego się na formularzu Form1 i w jego właściwości 'OtherLangcb' wybieramy 'Form2->Langcb1' (wybieramy nie wpisujemy) łącząc w ten sposób Langcb1 z Form1 z komponentem Langcb1 z Form2, jeśli na liście wyboru nie ma podanego wpisu to znaczy, że zapomnieliśmy połączyć ze sobą pliki źródłowe poprzez sekcję include. Następnie przechodzimy do komponentu Langcb1 znajdującego się na formularzu Form2 i w jego właściwości 'OtherLangcb' wybieramy 'Form3->Langcb1', potem przechodzimy do komponentu Langcb1 znajdującego się na formularzu Form3 i w jego właściwości 'OtherLangcb' wybieramy 'Form4->Langcb1', ostatniego komponentu Langcb1 znajdującego się na formularzu Form4 nie łączymy z niczym, w żadnym razie nie podłączamy go pod 'Form1->Langcb1', ani pod żaden inny komponent typu TLangcb, ponieważ spowodowałoby to zapętlenie programu, a w efekcie jego zawieszenie. Jest to klasyczne połączenie szeregowe, tworzące łańcuch w którym komponent Langcb1 znajdujący się na Form1 automatycznie steruje pozostałymi komponentami, komponent znajdujący się na końcu łańcucha nie jest do niczego podłączany.
Po połączeniu komponentów musimy jeszcze zaktualizować właściwości 'LangList' we wszystkich komponentach by były identyczne jak w komponencie znajdującym się na początku łańcucha, czyli w Langcb1 znajdującym się na formularzu głównym Form1. Podczas podłączania do siebie komponentów listy językowe (właściwość 'LangList') powinna kopiować się z pierwszego komponentu, ale należy to sprawdzić, ponieważ jeśli pomyliliśmy kolejności łączenia, to lista się nie przekopiuje i wtedy należy ją przekopiować ręcznie z pierwszego komponentu.
UWAGA!
Listy językowe - właściwość 'LangList' muszą być identyczne we wszystkich komponentach.
Pierwszym komponentem jest zawsze ten znajdujący się na początku łańcucha.
Komponenty nie mogą wzajemnie się do siebie odwoływać, czyli komponent Langcb1 z formularza Form1 może się odwoływać do komponentu Langcb1 z formularza Form2, ale wtedy komponent Langcb1 z formularza Form2 nie może się odwoływać do komponentu Langcb1 z formularza Form1.
Ostatni komponent w łańcuchy nie może się odwoływać do żadnego innego komponentu.
Po szeregowym
połączeniu ze sobą komponentów postępujemy dalej tak samo jak przy tworzeniu
aplikacji z jednym formularzem, czyli programujemy tylko pierwszy komponent
Langcb1 i to on będzie sterował pozostałymi komponentami znajdującymi się na
innych formularzach.
Przy takim połączeniu zmiana języka na formularzu Form1 powoduje równoczesną
zmianę języków na pozostałych formularzach.
Rozwiązywanie problemów:
Podczas instalacji komponentu otrzymuję komunikat: „[Linker Error] Unable to open file 'LANGCB.RES'” - czytaj tutaj.
Pomimo rozwiązania problemu z komponentem z punktu 1, podczas instalacji komponentu otrzymuję komunikat o brakujących bibliotekach - czytaj tutaj.
Podczas instalacji komponentu otrzymuję komunikaty o błędach, punkty 1 i 2 nie rozwiązały problemu.
Komponent TLangcb został stworzony w środowisku Borland C++ Builder w wersji 6, w związku z tym może występować niezgodność bibliotek we wcześniejszych wersjach BCB, szczególnie w tych poniżej wersji 4.
Komponent nie zmienia języka programu.
Usuń komponent z formularza, zapisz zmiany w projekcie, umieść ponownie komponent na formularzu, utwórz listę językową we właściwości LangList komponentu Langcb wygeneruj ponownie pliki językowe.
W aplikacji z wieloma formularzami język jest zmieniany tylko na formularzu głównym.
Sprawdź połączenia we wszystkich komponentach, na wszystkich formularzach, zwróć uwagę na to, czy gdzieś nie występuje przerwa w połączeniu łańcucha, następnie sprawdź czy we wszystkich komponentach Langcb na wszystkich formularzach listy językowe umieszczone we właściwości LangList są identyczne.
Po uruchomieniu aplikacji z wieloma formularzami i zmianie języka program zawiesza się.
Sprawdź czy gdzieś komponenty nie odwołują się wzajemnie, oraz czy ostatni komponent w łańcuchu nie odwołuje się do innego komponentu, czy nie nastąpiło zamknięcie, zapętlenie się łańcucha.
Opracował:
Cyfrowy Baron
Prawa autorskie do komponentu: Cyfrowy Baron.