Copyright (c) Prolog Developemnt Center SPb

Application Frame. Dictionary Support   

Typically, the user interface uses headings and texts that are part of the codes. During operation, users critically evaluate the decisions made by the developer about certain names, phrases, etc.

The AppFrame provides the ability to change the content of text elements during operation. Moreover, it is possible to use different languages. This property of the AppFrame is provided with the ability to support dictionaries, which can be changed in the application.

The need to use dictionaries is set in the application configuration  Bin\<application name>AppData\Options.xml node
<fe_options>.<group>.<language> attribute 
use-dictionary .
  

If the application configuration determines that dictionaries are not used, then the entire dictionary mechanism is blocked and the application uses the text values ​​set in the application code.

Dictionary file structure

The dictionary outside the application is an XML file whose structure is presented below

<? Xml version = "1.0" encoding = "UTF-8" standalone = "yes"?>
<language namespace = "basic" version = "06/10/2019">
  < language_list dk = "Danish" eng = "English" rus = "Russian" />
  <row id = "ribbon.default.section.basic" meaning = "Help Section with About and Features commands">
    <dk> Help </ dk >
    <rus> Help </rus>
    <eng> Help </eng>
  </row>
  <row id = "ribbon.default.section.basic.tooltip" meaning = "Ribbon.Section.Default tooltip, when no ribbon file exists ">
    <dk>The Help </ dk>
    <rus> Help </ rus>
    <eng> the Help </ eng>
  </ row>
    ...

Attribute  namespace of the node language defines the namespace name ( "basic" in this case). The version attribute specifies the name of the version of the dictionary formation rules. The name of the version is represented by the date when these rules were developed and applied ( "10.06.2019" in this case).

The language_list node  lists the languages ​​that the basic namespace supports. Moreover, the name of the attribute (as in this case - eng , dk , rus ) shows how the language is indicated for the corresponding representation of the phrase. The language attribute values ​​are explanatory.

Each row nodedefines one phrase. The id attribute of row  is the id of the phrase. The meaning attribute  defines the semantic content of the phrase and what this phrase refers to in the user interface. Formal rules for the attribute attribute meaning are not established - this is the text whose meaning is determined by the user or developer. The phrase identifier can be any text value, although a structured representation is accepted in the core of the AppFrame.

The subnodes of the row node list how the phrase should look in the corresponding language. If there is only one language, then the subnode for this language must still exist.

Editing the contents of the dictionary file, during the operation of the application, you can take into account the stylistic preferences of the user.

The general scheme of working with dictionaries

The general scheme of working with dictionaries looks like this.


Dictionary files in the xml representation are stored in the xxxAppData directory of the application. Every UI component "knows" the name of the file, which contains its namespace , and the name itself namespace .

When the application starts, if the configuration uses dictionaries, the FrontEnd of the application determines which namespace is required for the current content of the user interface.
FrontEnd and BackEnd know which user interface language is currently working (current).
FrontEnd uses the current fact base for the current language. If the user interface language changes, FrontEnd destroys the current facts database, calls BackEnd for the facts of the current language, and the process proceeds as described above.
If FrontEnd updates a new user interface element (for example, a form or dialog), then the same mechanism works. When the form or dialog becomes irrelevant, FrontEnd can delete the database of relevant facts and contact BackEnd with the request to delete the corresponding dictionary.
Thus, dictionaries can be updated and deleted as needed or remain relevant while the application is active.

If the application configuration determines that dictionaries are not used, then the entire dictionary mechanism is blocked and the application uses the text values ​​set in the application code.

On the BackEnd side, all dictionary operations are provided by an object of the be_Dictionary class .
On the FrontEnd side, all dictionary operations are provided by an object of class fe_Dictionary .

AppFrame always updates namespace "basic" at startup and uses the file basic_Dictionary.xml .
If the application developer does not want to create dynamically loaded dictionaries, he can use only one dictionary, which contains all the phrases of the application.
If the file basic_Dictionary.xml is not present, then the namespace "basic" inside FrontEnd is created for only one basic language from the facts stored in the package Common\AppFrontEnd\defaultDictionary\defaultDictionary.pack . The file basic_Dictionary.xml is automatically created for the possibility of subsequent modification.

It should be noted that if for a given namespace If a dictionary exists as an XML file, the XML file will be checked for compliance with the set of identifiers. If it is discovered that a new identifier is being used, it will be added to the dictionary file and values ​​will be added for all supported languages. Phrases for languages ​​other than the base language will repeat the meaning of the base language. The user can then edit all phrases for all languages.

Implementation and Application Details

One of the possibilities of maintaining the dictionary is to maintain the package files of the package of the base dictionary Common \ AppFrontEnd \ defaultDictionary \ defaultDictionary.pack.
Here it is important to represent the package data structure.
The basic information can be found in the files  defaultDictionary .i and defaultDictionary.pro The file defaultDictionary.i recorded constant identifiers text phrases

constants
    ribbon_default_section_basic_C = "ribbon.default.section.basic".
    ribbon_default_section_basic_tooltip_C = "ribbon.default.section.basic.tooltip".

The file defaultDictionary.pro recorded:

the name of the namespace and the name of the dictionary file. Both of these are fact-variable properties, that is, they are accessible through an interface.

facts

    nameSpace_P  :  string  : =  "basic" .
    fileName_P  :  string  : =  @ "xxxAppData\basic_Dictionary.xml" .

and then the actual definitions of phrases having these identifiers defined in the file
defaultDictionary.i .

facts
    item_F  :  ( string  ItemID ,  string  ItemString ,  string  Meaning ).

clauses
    item_F ( ribbon_default_section_basic_C ,  "Help" ,  "Help Section with About and Features commands" ).
    item_F ( ribbon_default_section_basic_tooltip_C ,  "Help" ,  "Ribbon.Section.Default tooltip, when no ribbon file exists" ).

This is enough to create and use it in the process of program operation if there is no dictionary file.

Download and use dictionaries

In order for BackEnd to load the required dictionary from the XML file,

     FrontEnd uses the predicate add_DictionaryNameSpace :( string  NameSpace , string  DictionaryXmlFile ).

Then the necessary dictionary will appear both in the object of the be_Dictionary class and in the object of the fe_Dictionary class (for the current language).

In addition, a dictionary already loaded in BackEnd can be requested by the predicate


    fe_CoreTasks () from the FrontEnd side : getDictionary (< NameSpace >)

Finally, by the predicate

    fe_CoreTasks (): getDictionary ( )

all dictionaries loaded into the BackEnd will be loaded into FrontEnd in the class object fe_Dictionary

As a result, the desired dictionary for the current language appears in the object of the fe_Dictionary class and obtaining the phrase is performed by the
simplest query

    Phrase = fe_Dictionary (): getStringByKey ( string :: concat (< NameSpace > , @ "\" , <phrase identifier> ), <default value > )


Here, a call is made to thefe_Dictionaryobject, and<NameSpace> \ isused as a prefix in the text identifier.
The last predicate parameter getStringByKey sets the default value that will be used if the definition of the text fragment by the given identifier is not found in the dictionary.

Procedure for UI elements included in the project code and having a special dictionary definition class for a given element.

To use the dictionaries of the components built into the code, a dictionary package must first be created (if it is no longer in the form of an XML file).

For this purpose, the SpbVipTools \ AppData \ SourceTemplates \ spbDictionary \ SpbDictionary.pack
 dictionary class template
that can be used to create dictionaries of any built-in entities is included in the Application Frame .

As an example, let's take a look at a not-so-simple procedure for creating a personal dictionary for a user interface element of the Dialog class (the dictionary package optionsDlg_Dictionary.pack has already been created ).

In the dialog itself, creating an object leads to the creation of an object of a specific dictionary and saving it here


    
new(Parent, FrontEnd, _SettingsList) :-
        fe_Connector::new(FrontEnd),
        dialog::new(Parent),
        generatedInitialize(),
        coreDictionary_P:=optionsDlg_Dictionary::new(). <- a dictionary object is created

And here is how this dialog is created and used externally:


clauses
    editOptions(PerformParams):-
        Dialog=fe_Options::new(convert(window,fe_AppWindow()),frontEnd_P,PerformParams),
        % prepearing the dictionary for the dialog
        Dialog:dictionary_P:=fe_Dictionary(), <-
        fe_CoreTasks():initCoreDictionary(Dialog:coreDictionary_P), <- load/create dictionary from BackEnd
        Dialog:initData(), <- all text titles are set here
        Dialog:show().

While loading dictionary from BackEnd: if dictionary not exists, then BackEnd will requests the list of facts of the dictionary from the created dialog and will create the dictionary.

In the dialog object using the loaded dictionary with the predicate initData(), text phrases will be arranged on UI elements.

clauses
    initData():-
        if fe_Dictionary():useDictionary_P=useDictionaryNo_C then
            %  If dictionary not in use, then nothing done
        else
            NameSpace=coreDictionary_P:nameSpace_P,
            setText(fe_Dictionary():getStringByKey(string::concat(NameSpace,@"\",dialog_options_title_C),"Options")),
            cancel_ctl:setText(fe_Dictionary():getStringByKey(string::concat(NameSpace,@"\",dialog_options_pb_Cancel_C),"Cancel")),
            ok_ctl:setText(fe_Dictionary():getStringByKey(string::concat(NameSpace,@"\",dialog_options_pb_ok_C),"Ok"))
        end if.

In the initData() predicate, the controls (in this case, the header and the Cancel and Ok buttons ) are set to new text values.