14. Databázové aplikace II
  1. Dříve než se budeme zabývat dalšími databázovými komponentami, seznámíme se s vytvářením přezdívek BDE. U našich příkladů jsme se zatím přezdívkám BDE vyhnuli. Při dalším seznamování s databázovými aplikacemi je ale již budeme potřebovat. Přezdívku lze vytvořit několika způsoby:
  2. Nyní se budeme zabývat pouze první z těchto možností.
    Při vývoji aplikace je obvykle zapotřebí vytvořit jednu nebo více přezdívek BDE. Administrátora BDE spustíme volbou Programs | Borland C++ Builder 3 | BDE Administrator. V okně administrátora je uveden seznam nadefinovaných přezdívek. Je možno zde měnit parametry přezdívek a také vytvářet přezdívky nové. Prohlédněte si, jaké přezdívky jsou nadefinované.
    V dalších zadáních se pokusíme připojit ke cvičné databázi Sybase SQL Anywhere. Ukážeme si tedy jak vytvořit potřebnou přezdívku pro tuto databázi. Budeme ale postupovat jiným způsobem.
    Nejdříve je nutno překopírovat soubor této cvičné databáze do adresáře C:\PRAC. Jedná se o soubor SADEMO.DB. Nalezněte jej a překopírujte. Dále je nutno nadefinovat datový zdroj ODBC. V administrátoru BDE zvolíme Object | ODBC Administrator, čímž zobrazíme dialogové okno Administrátora datových zdrojů ODBC. Na stránce User DSN stiskneme tlačítko Add a vybereme ovladač, pro který chceme datový zdroj vytvářet. My vybereme Sybase SQL Anywhere 5.0 a stiskneme tlačítko Finish. Je zobrazeno okno konfigurace ODBC. Pomocí tlačítka Browse do části Database File zadáme C:\PRAC\SADEMO.DB, označíme volič Custom a stiskneme tlačítko Options. Tím zobrazíme další okno, kde v části Start Command je nutno zadat úplnou specifikaci souboru DBENG50.EXE (např. C:\SQLAny50\Win32\DBENG50.EXE). Nalezněte tuto specifikaci a zadejte ji. V tomto okně již není zapotřebí nic nastavovat a stiskem OK jej uzavřeme. Okno konfigurace uzavřeme stiskem OK. Na stránce User DSN Administrátora datových zdrojů ODBC si povšimněte, že v seznamu datových zdrojů je již námi vytvořený datový zdroj (Sademo). Okno Administrátora ODBC také uzavřeme stiskem OK. Až budete používat tuto databázi, pak je nutno při připojování k ní zadat identifikaci uživatele a heslo. K databázi se budeme hlásit jako uživatel DBA a tento uživatel má heslo SQL. Tyto informace si zapamatujte.
    Seznam přezdívek v Administrátoru BDE se zatím nezměnil (seznam se načítá vždy při spuštění administrátora). Pokud ukončíme práci Administrátora BDE a spustíme jej znova, pak v seznamu přezdívek již uvidíme přezdívku Sademo pro náš nově vytvořený datový zdroj ODBC. Přezdívka tedy byla vytvořena sama.
    To co bylo uvedeno o Administrátoru BDE platí i o samotném C++ Builderu. Přezdívky jsou načítány pouze při jeho spuštění. Pokud provedeme nějakou změnu v přezdívkách BDE, pak tato změna se v C++ Builderu projeví až po jeho dalším spuštění.
  3. V předchozím zadání jsme si vytvořili přezdívku BDE Sademo, kterou nyní budeme používat při dalším seznamování s databázovými komponentami. Komponenta Query slouží pro zpřístupňování dat v databázích typu klient/server. Pokud bychom i pro tento typ databází používali komponentu Table, pak některé tabulky se nám nepodaří otevřít (např. ve cvičné databázi Sademo neotevřeme tabulku Customer - tabulky jsou provázané a databáze obsahuje také různé příkazy). Nyní se seznámíme s komponentou Query.

  4. Tato komponenta nemá vlastnost TableName. Nelze tedy přímo zjistit, ze kterých tabulek se databáze skládá. Pokud se potřebujeme dozvědět, které tabulky jsou v databázi, pak to lze provést dvěma způsoby. Jednak lze na formulář umístit dočasnou komponentu Table, nastavit u ní DatabaseName na správnou přezdívku a v rozbalovacím seznamu vlastnosti TableName si seznam tabulek můžeme prohlédnout. Druhou možností je, vybrat komponentu Query na formuláři a v její místní nabídce zvolit Explore. Tím zobrazíme Průzkumníka databáze, ve kterém si můžeme zjistit různé věci o databázi (včetně seznamu tabulek databáze).
    Jednou z hlavních vlastností komponenty Query je vlastnost SQL. Je typu TStringList a obsahuje prováděné příkazy SQL. Hodnotu této vlastnosti lze nastavit při návrhu v Inspektoru objektů nebo za běhu aplikace pomocí kódu. Vytvoříme nyní jednoduchou aplikaci, ve které např. zobrazíme obsah tabulky Customer ze cvičné databáze Sademo. Začneme vývojem nové aplikace, na formulář umístíme komponentu Query, vlastnost DatabaseName nastavíme na Sademo, vlastnost SQL na
    SELECT * FROM Customer
    a vlastnost Active na true. Na formulář je ještě nutno vložit komponenty DataSource a DBGrid. Vlastnosti u těchto komponent nastavíme již známým způsobem (u DataSource vlastnost DataSet na Query1 a u DBGrid vlastnost DataSource na DataSource1). Aplikace je hotova a vidíme, že mřížka zobrazuje obsah tabulky Customer (pokud bychom použili komponentu Table, pak se nám to nepodaří).
    Když budeme vlastnost SQL nastavovat za běhu, pak nesmíme zapomenout vyprázdnit její předchozí obsah, tzn. je nutno použít např. příkazy:
    Query1->SQL->Clear();
    Query1->SQL->Add("SELECT * FROM Customer");
    Vlastnost je seznam řetězců a ne samotný řetězec.
    Příkazy ve vlastnosti SQL budou provedeny, při volání metody Open nebo metody ExecSQL. Pokud vlastnost SQL obsahuje příkaz SELECT, pak k jeho použití voláme metodu Open, pokud používáme příkazy INSERT, UPDATE nebo DELETE, pak je nutno k provedení příkazu použít metodu ExecSQL. Nastavení vlastnosti Active na true má stejný efekt jako volání metody Open.
  5. V další aplikaci si ukážeme změnu vlastnosti SQL za běhu aplikace. Začneme vývoj nové aplikace, na formulář umístíme komponentu Query (změníme u ní DatabaseName na Sademo), komponenty DataSource a DBGrid (nastavíme u nich vlastnosti obvyklým způsobem) a dvě tlačítka (s texty Customer a Employee). Obsluha stisku tlačítka Customer bude tvořena příkazy:

  6. Query1->SQL->Clear();
    Query1->SQL->Add("SELECT * FROM Customer");
    Query1->Open();
    a obsluhu stisku druhého tlačítka tvoří:
    Query1->SQL->Clear();
    Query1->SQL->Add("SELECT * FROM Employee");
    Query1->Open();
    Nyní při stisku některého z tlačítek se zobrazí příslušná tabulka.
    Příkaz SQL SELECT získává data z databáze. Můžeme používat i jeho složitější tvary. Např.
    Query1->SQL->Clear();
    Query1->SQL->Add("SELECT id, lname, city FROM Customer");
    Query1->SQL->Add("WHERE city = 'Santa Fe'");
    Query1->Open();
    Příkaz SQL DELETE ruší záznamy z datové množiny (můžeme rušit pouze záznamy, jejichž zrušení neporuší integritu databáze). Můžeme použít např. tento kód:
    Query1->SQL->Clear();
    Query1->SQL->Add("DELETE FROM Contact WHERE first_name = 'Rose'");
    Query1->ExecSQL();
    V tomto případě je nutno volat metodu ExecSQL. Totéž platí i o dalších příkladech. Příkaz SQL INSERT vkládá záznam do datové množiny. Např.
    Query1->SQL->Clear();
    Query1->SQL->Add("INSERT INTO Department");
    Query1->SQL->Add("(Dept_id, Dept_name, Dept_head_id)");
    Query1->SQL->Add("VALUES (600, 'Nic', 501)");
    Query1->ExecSQL();
    Tento záznam lze vložit pouze jednou, při druhém vložení již nastává shoda hodnot primárního klíče (je signalizována chyba). Obdobně lze použít i příkaz UPDATE. Např.
    Query1->SQL->Clear();
    Query1->SQL->Add("UPDATE Department");
    Query1->SQL->Add("SET Dept_name = 'xxxxx'");
    Query1->SQL->Add("WHERE Dept_id = 500");
    Query1->ExecSQL();
    Vidíme, že příkazy SQL můžeme používat jak potřebujeme.
  7. Příkazy SQL mohou také používat parametry. Parametry v příkazech SQL se podobají proměnným v C++. Parametr v příkazu SQL předchází dvojtečka. Podívejte se na následující příkaz SQL:

  8. SELECT * FROM Contact WHERE first_name = :Param1
    Parametr se jmenuje Param1. Při provádění tohoto příkazu hodnota Param1 ve vlastnosti Params je použita místo jména parametru. Např.
    Query1->SQL->Add("SELECT * FROM Contact WHERE first_name = :Param1");
    Query1->ParamByName("Param1")->AsString = "John";
    Query1->Open();
    Hodnoty parametrů lze nastavovat během návrhu ve vlastnosti Params pomocí dialogového okna parametrů, ale větší význam má nastavování parametrů za běhu aplikace. V předchozí ukázce je použita metoda ParamByName k nastavení hodnoty parametru Param1. Parametr lze nastavit i příkazem:
    Query1->Params->Items[0]->AsString = "John";
    Zde k nastavení hodnoty parametru je použita vlastnost Items třídy TParams. Výhodnější je ale použití prvního způsobu, protože si nemusíme pamatovat pořadí parametrů.
    Ne všechny prvky příkazů SQL mohou být parametrizovatelné. Např. většina serverů SQL neumožňuje použít parametr místo jména tabulky.
  9. Komponenta StoredProc reprezentuje uloženou proceduru na databázovém serveru. Uložené procedury jsou množiny příkazů SQL, které jsou prováděny jako jeden celek (zaobalují často prováděné databázové úlohy). Příkazy uložené procedury jsou prováděny na serveru.

  10. Některé uložené procedury používají parametry a jiné ne. Pro uloženou proceduru bez parametru, nastavíme jméno procedury a provedeme ji:
    StoredProc1->StoredProcName = "sp_proc";
    StoredProc1->Prepare();
    StoredProc1->ExecProc();
    Pro uloženou proceduru s parametry je nutno nejprve nastavit parametry a potom proceduru provést:
    StoredProc1->StoredProcName = "sp_proc1";
    StoredProc1->ParamByName("cislo")->Value = 6665;
    StoredProc1->Prepare();
    StoredProc1->ExecProc();
    Jelikož cvičná databáze SQL Anywhere neobsahuje vhodnou uloženou proceduru, neukážeme si jejich použití.
  11. Komponenta UpdateSQL poskytuje možnost aplikování změn na datovou množinu určenou pro čtení při povolené odložené aktualizaci. Normálně datová množina určená pouze pro čtení dovoluje údaje pouze číst. Když povolíme odloženou aktualizaci, pak je možno takovouto datovou množinu modifikovat a výsledky těchto modifikací zapsat do databáze. Většina databází typu klient/server má implicitní akce, které jsou prováděny, když jsou aplikovány změny v aktualizační vyrovnávací paměti. Komponenta UpdateSQL umožňuje poskytnout naše vlastní příkazy SQL, pro případ, když má být aktualizován, vložen nebo zrušen záznam v datové množině určené pouze pro čtení. Např. komponenta UpdateSQL umožňuje specifikaci implicitních hodnot pro jisté položky v datové množině.

  12. Vlastnost DeleteSQL umožňuje definovat dotaz SQL, který bude proveden, když odložené aktualizace jsou aplikovány a vyrovnávací paměť aktualizací obsahuje rušení záznamů. Obdobně je možno ve vlastnostech InsertSQL a ModifySQL nadefinovat dotaz SQL pro vkládání a modifikaci.
  13. Komponenta DataSource poskytuje mechanismus k propojení komponent datových množin (Table, Query a StoredProc) s vizuálními komponentami, které zobrazují data (DBGrid, DBEdit, DBListBox atd.). Základní funkcí této komponenty je usnadnění provádění změn v naší aplikaci. Všechny datové komponenty na formuláři jsou propojeny s DataSource, který je pak propojen s datovými množinami. Protože datové komponenty nejsou propojeny přímo s datovou množinou, lze snadno změnit datový zdroj a není nutno se zabývat všemi datovými komponentami. Umožňuje to také snadnou změnu např. z datové množiny Table na Query.

  14. DataSource má jen několik vlastností. Vlastnost DataSet je použita k určení připojené datové množiny. Vlastnost Enabled určuje zda připojené datové komponenty budou zobrazovat data. Je-li tato vlastnost nastavena na true, pak data jsou zobrazována, má-li ale hodnotu false, pak datové komponenty jsou prázdné.
  15. Komponenta Session spravuje databázové sezení. Pokaždé, když spouštíme databázovou aplikaci, pak BDE vytváří globální objekt Session. Můžeme jej použít pro přístup k současnému databázovému sezení. Pokud nevytváříme vícevláknové aplikace, pak není nutno vytvářet svůj vlastní objekt třídy TSession, ale vystačí nám objekt vytvořený implicitně. TSession má několik zajímavých metod. Metody AddAlias a AddStandardAlias lze použít k vytvoření přezdívky BDE za běhu aplikace. Metody GetAliasNames a GetDatabaseNames mohou být použity k získání seznamu databází. Použijeme je, když chceme nabídnout uživateli možnost volit databázi ze seznamu. Seznam lze např. vložit do kombinovaného okna:

  16. Session1->GetDatabaseNames(ComboBox1->Items);
    Obdobně lze použít i metody GetTableNames a GetStoredProcNames.
  17. Komponenta Database umožňuje provádět některé databázové operace. Potřebujeme ji pouze, když chceme tyto speciální operace provádět. Následuje popis těchto situací.

  18. Vlastnost KeepConnections je použita k řízení zpracování databázového připojení při uzavření datové množiny. Pokud tato vlastnost má hodnotu false, pak při uzavření poslední datové množiny, je připojení k databázi zrušeno. To vyžaduje opětovné připojení k databázi při dalším otevření datové množiny. Nastavíme-li tuto vlastnost na true, pak připojení k databázi je stále udržováno.
    Další význam použití komponenty Database je řízení operace přihlašování. Pokud vlastnost LoginPrompt má hodnotu false, pak lze nastavovat přihlašovací parametry. Např.
    Database1->Params->Values["user name"] = "DBA";
    Database1->Params->Values["password"] = "SQL";
    Tyto příkazy je možno použít i jako obsluhu události OnLogin.
    Další důvod k použití komponenty Database je řízení transakcí. Normálně jsou transakce řízeny BDE. Někdy ale transakce potřebujeme řídit sami. Transakce je zahájena voláním metody StartTransaction. Provedené změny jsou do databáze zapsány až při volání metody Commit. Zatím nezapsané změny je možno zrušit metodou Rollback. Úroveň izolace transakcí je řízena hodnotou vlastnosti TransIsolation.
  19. Komponenta BatchMove je používána ke kopírování záznamů z jedné datové množiny do jiné. Vlastnost Source specifikuje zdrojovou datovou množinu a vlastnost Destination cílovou datovou množinu. Vlastnost Mapping je vyžadována, pokud datové množiny nemají identické sloupce. Tato vlastnost je seznam řetězců tvaru:

  20. FirstName = FName
    LastName = LName
    Notes = Comments
    Jména sloupců na levé straně jsou cílové sloupce a sloupce napravo jsou zdrojové sloupce. Kopírování je spouštěno metodou Execute. Vlastnost Mode určuje režim kopírování.
  21. Třída TField reprezentuje položky (sloupce) v databázi. Tato třída nám umožní nastavit atributy položek. Tyto atributy zahrnují datový typ (string, integer, float, atd.), velikost položky, index apod. Hodnotu položky můžeme také číst nebo nastavovat prostřednictvím vlastností typu AsString, AsVariant a AsInteger.

  22. TField je předek více specializovaných tříd položek. Potomci TField zahrnují TStringField, TIntegerField, TSmallIntField, TWordField, TFloatField, TCurrencyField, TBCDField, TBooleanField, TDataTimeField, TTimeField, TBlobField, TBytesField, TVarBytesField, TMemoField a TGraphicField. Tyto odvozené třídy rozšiřují funkčnost základní třídy jen nepatrně. Např. třídy číselných položek mají vlastnost DisplayFormat, která určuje způsob zobrazení čísla a vlastnost EditFormat, určující způsob zobrazení čísla při editaci. Každý potomek TField odpovídá specifickému typu databázové položky. Třída TIntegerField je použita, když typ položky je Integer, atd.
    K vlastnostem TField můžeme přistupovat během návrhu pomocí Editoru položek. Vlastnosti vybrané položky v Editoru položek jsou zobrazeny v Inspektoru objektů.
    Dříve než můžeme položku číst nebo nastavovat musíme nějakým způsobem položku lokalizovat. To lze udělat třemi způsoby. Zpřístupňování položky pomocí jejího ukazatele je nejsnadněji použitelná metoda. Lze ji ale použít pouze tehdy, když máme přidané položky pomocí Editoru položek. Při přidávání položek pomocí Editoru položek, C++ Builder vytváří ukazatel na každou položku spojením jména tabulky a jména položky. Pro tabulku Table1 a řetězcovou položku FirstName,  C++ Builder vytvoří ukazatel typu TStringField nazvaný Table1FirstName. Tento ukazatel lze použít pro přístup k položce:
    Table1FirstName->Value = "John";
    Vlastnost Fields nabízí další možnost přístupu k položce - podle pozice. Pokud víme, že položka LastName je první položkou v tabulce, pak k ní lze přistupovat takto:
    Edit1->Text = Table1->Fields[0]->Value;
    Třetí způsob zpřístupnění položky používá metodu FieldByName. Zde potřebujeme znát pouze jméno položky:
    Table1->FieldByName("LastName")->AsString = Edit1->Text;
    FieldByName vrací ukazatel TField.
    Když již máme ukazatel na jistou položku, pak lze změnit její hodnotu pomocí vlastnosti Value nebo některou z vlastností As (AsString, AsInteger, AsBoolean, atd.). Tyto vlastnosti provádějí převod z jednoho datového typu na jiný. Nastavení hodnoty položky je jednoduché, např.
    Table1->Edit();
    Table1->FieldByName("LastName")->AsString = Edit1->Text;
    Table1->Post();
    Metoda Edit uvede tabulku do editačního režimu, tabulku je možno editovat a metodou Post zapíšeme změny do databáze.
    TField má události OnChange a OnValidate. Událost OnChange je generována vždy při změně hodnoty položky (po zápisu změny do databáze). Tuto událost lze použít k oznámení změny položky. Událost OnValidate vzniká před odesláním dat do databáze. V její obsluze je možno kontrolovat přípustnost dat. Např.
    void __fastcall TForm1::Table1ACCT_NBRValidate(TField *Sender)
    {
      if (Sender->AsInteger < 3000)
        throw (EDBEditError("Chybné číslo."));
    }
    Když generujeme výjimku, pak zápis dat do databáze je zrušen.
    Tím je naše seznamování s databázovými aplikacemi zatím dokončeno. Podrobný popis všech databázových komponent bude uveden později ve 4. části příručky.
  23. Nyní se budeme zabývat vývojem aplikací, které chceme uvést na mezinárodní trh. U těchto aplikací je zapotřebí se zaměřit na internacionalizaci a lokalizaci. Internacionalizace je proces umožňující naší aplikaci pracovat ve více zemích (respektování kulturních konvencí cílového státu a také jazyka). Lokalizace je proces transformace aplikace na funkce ve specifickém státě. Např. aplikaci je nutno modifikovat k respektování jistých zákonů v různých státech.
  24. Vytvořit internacionalizovanou aplikaci není obtížné. Musíme umožnit našemu kódu zpracovávat řetězce z mezinárodní znakové množiny, musíme navrhnout naše uživatelské rozhraní, aby umožňovalo změnit lokalizaci a je také nutno izolovat všechny zdroje, které musí být lokalizovány.

  25. Musíme se ujistit, že kód naší aplikace může zpracovávat řetězce v různých jazycích. Verze US Windows 95 a Windows NT používá znakovou množinu ANSI Latin-1 (1252). Jiné verze Windows používají jiné znakové množiny. Např. Japonská verze Windows používá znakovou množinu Shift-Jis (kódová stránka 932), ve které japonské znaky jsou reprezentovány jedno nebo dvou slabikovými kódy.
    Někdy je zapotřebí provést převod mezi znakovou množinou Windows (ANSI) a znakovou množinou specifikovanou kódovou stránkou uživatelova počítače (nazvanou znaková množina OEM). Např. BDE očekává znakovou množinu OEM ve voláních funkcí API. Pokud použijeme databázové komponenty VCL, pak není nutno se zabývat převodem mezi znakovými množinami OEM a ANSI. Tyto komponenty provedou všechny nutné převody automaticky. Při přímém volání API BDE ale musíme provést převod mezi znakovou množinou ANSI používanou VCL a znakovou množinou OEM používanou BDE.
    Některé jazyky mají více znaků, než je možno uložit do jedné slabiky. Tyto jazyky mají znaky reprezentované jednoslabikovým kódem a jiné znaky reprezentované dvouslabikovým kódem. Když zapisujeme kód pro tyto jazyky, pak musíme zajistit, zpracování všech řetězců pomocí funkcí, které počítají s jedno i dvouslabikovými znaky. V nápovědě C++ Builderu pod heslem International API nalezneme seznam všech funkcí RTL, které umožňují pracovat s víceslabikovými znaky. Nesmíme zapomenout ani na to, že délka řetězce ve slabikách neodpovídá délce řetězce ve znacích. Je také nutno být opatrný při předávání znaku jako parametru funkci, neboť velikost znaku obecně není známa. V tomto případě je vhodné znaky předávat jako ukazatel na znak nebo jako řetězec.
    Jinou možností práce s těmito znakovými množinami je převedení všech znaků na široké znaky (např. Unicode). Široké znaky jsou vždy dvouslabikové. Použití dekódovacího schématu širokých znaků má řadu výhod. Je zde přímý vztah mezi počtem slabik řetězce a počtem znaků řetězce. Nemusíme mít také obavu, že znak budeme hledat od poloviny jiného znaku. Velkou nevýhodou práce s širokými znaky je to, že Windows 95 nepodporuje široké znaky ve voláních funkcí API.
    K aplikacím můžeme přidávat speciální služby. Např. pro mnohoznakové jazyky, můžeme požadovat řízení IME (Input Method Editor), které je použito pro převod stisku kláves uživatelem na znakový řetězec. Komponenty VCL často nabízejí podporu pro programování IME. Většina Windowsovských ovladačů, které přímo pracují se vstupem textu mají vlastnost ImeName umožňující specifikaci potřebného IME. Globální proměnná Screen poskytuje informace o IME dostupných na našem systému.
  26. Nyní se budeme zabývat návrhem uživatelského rozhraní internacionalizovaných aplikací. Všechen text zobrazovaný uživatelským rozhraním musí být přeložen. Texty v jednom jazyku mohou být kratší než jejich překlad. Musíme tedy počítat s tím, že délka textů se může zvětšit. Dialogová okna, stavové řádky apod. musí mít dostatek místa pro zobrazení delších textů. Pokud aplikace používá obrázky, pak je vhodné používat takové, které nevyžadují překlad (nepoužíváme na obrázcích text). Je vhodné také nepoužívat obrázky, které jsou specifické pro jistou kulturu.

  27. Formáty datumu, času, finančních částek jsou také závislé na cílové zemi. Pokud používáme pouze formáty Windows, pak se jejich převodem nemusíme zabývat. Pořadí řazení řetězců se také v různých zemích liší. Mnoho evropských jazyků používá diakritická znaménka, která se v různých zemích řadí různě. V některých zemích existují dvouznakové kombinace, které jsou chápány jako jeden znak. Někdy jeden znak je řazen jako dva znaky.
  28. Nejobvyklejší úloha lokalizace aplikace je překlad řetězců, které se zobrazují v uživatelském rozhraní. Je vhodné všechny řetězce uživatelského rozhraní uložit do samostatného modulu. C++ Builder automaticky vytváří DFM soubor obsahující zdroje pro naši nabídku, dialogová okna a bitové mapy. Mimo těchto prvků uživatelského rozhraní, je také vhodné izolovat všechny řetězce (např. chybové zprávy). Zdroje řetězců nejsou vkládány do souboru DFM, ale můžeme je uložit do RD souboru.

  29. Izolované zdroje zjednodušují proces překladu do jiného jazyka. Zdroje mohou být přeloženy a vytvořena DLL zdrojů. DLL zdrojů umožňuje vytvořit program, který podporuje mnoho jazyků, a to pouhou výměnou DLL zdrojů. Toto jsme viděli v aplikaci textového editoru v kapitole Textový editor II.
    Průvodce Resource DLL použijeme k vytvoření DLL zdrojů pro náš program. Při použití tohoto průvodce musíme mít otevřený uložený projekt. Můžeme vytvořit DLL zdrojů pro každý požadovaný jazyk. Přípony těchto DLL určují cílové místo (první dva znaky určují jazyk a třetí znak zemi). Následující kód může být použit k získání kódu místa pro cílový překlad:
    /* Plnění ListBoxu řetězci a jejich přiřazenými jazyky se zeměmi */
    BOOL __stdcall EnumLocalesProc(char* lpLocaleString)
    {
      AnsiString LocaleName, LanguageName, CountryName;
      LCID lcid;
      lcid = StrToInt("$" + AnsiString(lpLocaleString));
      LocaleName = GetLocaleStr(lcid, LOCALE_SABBREVLANGNAME, "");
      LanguageName = GetLocaleStr(lcid, LOCALE_SNATIVELANGNAME, "");
      CountryName = GetLocaleStr(lcid, LOCALE_SNATIVECTRYNAME, "");
      if (lstrlen(LocaleName.c_str()) > 0)
        Form1->ListBox1->Items->Add(LocaleName + ":" + LanguageName + "-" + CountryName);
      return TRUE;
    }
    /* Toto volání způsobí provedení zpětného volání pro každé místo */
    EnumSystemLocales((LOCALE_ENUMPROC)EnumLocalesProc, LCID_SUPPORTED);
  30. EXE soubory, DLL a balíčky, které tvoří naší aplikaci obsahují všechny potřebné zdroje. K nahrazení těchto zdrojů jejich lokalizovanou verzí, stačí pouze nabídnout lokalizovanou DLL zdrojů, která má stejné jméno jako soubory EXE, DLL nebo BPL. Při spuštění aplikace je testováno místo lokálního systému. Pokud je nalezena DLL zdrojů se stejným jménem, pak je testována její přípona. Pokud přípona vyhovuje jazyku a zemi lokálního systému, pak naše aplikace používá zdroje z této DLL a to místo zdrojů ze souborů EXE, DLL nebo BPL. Není-li nalezena vyhovující DLL zdrojů, pak jsou použity původní zdroje.

  31. Když naši aplikaci chceme použít s jinou DLL zdrojů, než je určeno lokálním systémem, musíme nastavit položku v registrech Windows. Pod klíč HKEY_CURRENT_USER\Software\Borland\Locales přidáme úplnou specifikaci naší aplikace jako řetězcovou hodnotu a nastavíme datovou hodnotu na příponu požadované DLL zdrojů. Tím je možno dosáhnout změny jazyka aplikace bez nutnosti měnit místo našeho systému.
    Např. následující funkce může být použita v instalačním programu k nastavení hodnoty klíče registru, která určuje místo použité při spuštění aplikace.
    void SetLocalOverrides(char* FileName, char* LocaleOverride)
    {
      HKEY Key;
      const char* LocaleOverrideKey = "Software\\Borland\\Locales";
      if (RegOpenKeyEx(HKEY_CURRENT_USER, LocaleOverrideKey, 0,
           KEY_ALL_ACCESS, &Key) == ERROR_SUCCESS) {
        if (lstrlen(LocaleOverride) == 3)
          RegSetValueEx(Key, FileName, 0, REG_SZ, (const BYTE*)LocaleOverride, 4);
        RegCloseKey(Key);
      }
    }
    V naší aplikaci použijeme globální funkci FindResourceHInstance k získání madla současného modulu zdrojů. Např.
    LoadString(FindResourceHInstance(HInstance), IDS_AmountDueName,
               szQuery, sizeof(szQuery));
  32. Když již máme aplikaci internacializovanou, pak můžeme vytvořit lokalizované verze pro různé země, do kterých ji chceme distribuovat. Naše prvky rozhraní jsou izolovány v souborech DFM a RC. DFM soubor si zobrazíme jako text (volbou View As Text v místní nabídce návrhového formuláře) a provedeme požadovaný překlad. Soubory RC jsou textové soubory a můžeme je tedy snadno převést do cílového jazyka. Je také nutno provést další úpravy, týkající se různých specifik cílové země.
14. Databázové aplikace II