Problem Translator class Clarion

Hi all,

My application needs to be multilingual. I using

Clarion’s Translator class to do so. It works fine most of the time. Matter of collecting all the strings and putting them and their translation in a .TRN file.

But now I see inconsistent behaviour: The headings of one list box are translated and those of another are not. I would expect both to be translated. In this case, it’s about the string ‘Naam’ that needs to be translated to ‘Name’. Of course, the pair ‘Name/Name’ is in the .TRN file.

The most logical thing seems to me to be the format string of the list boxes.

Left list 
![Inconsistent translation|690x296](upload://5UnaipzAaW4yjlfBROE7BdQT0HP.png)
box:
'0R|FM~Object Nr~L(2)@n10@0R|FM~Klant ID~L(2)@n14@15R(2)|FM@n3@E(0C0C0C0H,,,)180L(2)|FM~Naam~L(1)@s60@0L|FM~Ruimte ID~L(1)@n10@80L(2)|FM~Ruimte~L(1)@s60@42L(2)|FM~Code~L(0)@s30@20R(2)|FM~Aant.~R(1)@n3B@0R|FM~Soort object~L(2)@n7@0L(2)|FM~Soort~L(1)@s60@0L(2)|FM~Materiaal~L(1)@s60@0C|FM~Kritisch~C(2)@n3@15C|FM~Krit.~C(2)@s1@Q''Kritisch object''0C|FM~Buiten gebruik~C(2)@n3@0C|FM~BG~@s1@0C|FM~Omschrijving Lang~@s250@')
            
Right list box
'0R|M~Bijlage ID~L(2)@n10@0R|M~Object Nr~L(2)@n10@140L(2)|M~Naam~L(1)@s60@0L(2)|M~Omschrijving Lang~L(1)@s250@'

The difference I see is the Fixed attribute so I removed that. Makes no difference. And vica versa adding the attribute to the other listbox also makes no difference.

Any ideas?

If you are using some tool to save listboxformat, it might interfere with translation

I found that in the child window of the example above the header of the Attachments list box was not translated. So I made the format string in the child window exactly the same the parent. No result not translated. So I think it is not related to the format string.

But I found it has something to do with the Sort Header option. I disabled the sort header option in the main browse and bingo the header is translated.

So the problem is probably in the sort header. But… that is inconsistent behaviour. In almost all my list boxes I have sort header enabled, certainly in the example above where sometimes the translation works and sometimes not.

Makes no sense to me, anyone?

The sortheader option prefixes the header text, effectivly. changing text from Name to +Name and -Name (or similar. I know I have worked around that issue at some point, but not at my computer now). If you are able to add a Translator.TranslateControl(?list) for the relevant control, before the sortheader is initiated, you should be good. You can also subclass the TranslateString method to parse out leading character before translating. Or just add +Name and -Name to your trn file

That is worth a try. Will do that tomorrow.

However the behaviour is very inconsistent. On some windows the translation of the list headers is working and others it isn’t. Both have SortHeader enabled. But if I disable the SortHeader on failing window then the translation works.

But by coincidence is found some different. On two windows it makes a difference if I open the window resized or not. If the window is opened in design time size the translation is not working. But if I resize it, close it and reopen it is translated. Resize it back to design time (minimal format), close it again reopen → No translation
I tested this behaviour on other failing windows and too bad no, resizing doesn’t work there.

So disabling SortHeader does something, but other windows can work with it. sometimes.
And second resizing from design time size to a bigger size does something.

Well ehmmm… :thinking:

Hi Bjarne,

That did the trick. Thanks for the idea.

Still curious why this behaviour seems so in consistent. For some list boxes the translation is still done with SortHeader enabled. But now I have a work around so not so important to figure out the big WHY.

BTW Adding +Name in my .trn file appears not tot be needed. The string is translated now.

Thank you for thinking with me

I’ll mention my List Format Tool on GitHub will take a Format() string like above and split the Columns into lines to make it easier to read:

!Left list 
 FORMAT('0R|FM~Object Nr~L(2)@n10@' &|
        '0R|FM~Klant ID~L(2)@n14@'                  &|
        '15R(2)|FM@n3@E(0C0C0C0H,,,)'               &|
        '180L(2)|FM~Naam~L(1)@s60@'                 &|
        '0L|FM~Ruimte ID~L(1)@n10@'                 &|
        '80L(2)|FM~Ruimte~L(1)@s60@'                &|
        '42L(2)|FM~Code~L(0)@s30@'                  &|
        '20R(2)|FM~Aant.~R(1)@n3B@'                 &|
        '0R|FM~Soort object~L(2)@n7@'               &|
        '0L(2)|FM~Soort~L(1)@s60@'                  &|
        '0L(2)|FM~Materiaal~L(1)@s60@'              &|
        '0C|FM~Kritisch~C(2)@n3@'                   &|
        '15C|FM~Krit.~C(2)@s1@Q''Kritisch object''' &|
        '0C|FM~Buiten gebruik~C(2)@n3@'             &|
        '0C|FM~BG~@s1@'                             &|
        '0C|FM~Omschrijving Lang~@s250@'            ), |

!Right list box
 FORMAT('0R|M~Bijlage ID~L(2)@n10@' &|
        '0R|M~Object Nr~L(2)@n10@'             &|
        '140L(2)|M~Naam~L(1)@s60@'             &|
        '0L(2)|M~Omschrijving Lang~L(1)@s250@' ), |
	

It does many more things like show a List Preview of that Format:

Yours above was confusing until I realized it had many Width columns as Zero. It will show the Format in a List in detail:

And each PROPLIST for one column for each modifier:

1 Like

Cool tool with overwhelming options. But just the formatting of format sting to a single line each already very useful. Especially when you need to compare two format string to find differences.
Thank you for sharing.

Yeah, old habit before I started to use the hot fields. The idea was to have an overview of all fields in the view. But later I realised that looking in to the view code is much easier.

I took a look at the SortHeaderClassType. It reads out the current header when initalizing and routinely runs a method RestoreHeaderText that sets back headers to the text upon initialization. Then it applies the rules for appending [+] or [-] indicators.

My own workaround was adding code in my listboxformatter template to omit the initial sortheader initialization in ThisWindow.Init and rather initialize in ThisWindow.Open, at which point the translation is finished.

I guess you are referring to this line in ABBROWSE.tpw:

#AT(%WindowManagerMethodCodeSection,'Init','(),BYTE'),PRIORITY(9600),WHERE(%EnableSortHeader)

It is tempting to modify the the template file but that might not be the best solution. I guess in my situation it might be best to write a template which adds a line

Translator.TranslateControl(?listx)

for each BrowseBox control present. Never wrote a template that searches for instances of a control template before, so that is a new challenge. Will try tonight.

Below is a template that finds all the LISTs and makes a call AT Window Opening.

The line you need to find all lists is this:

#FOR(%CONTROL),WHERE(%ControlType='LIST' OR %ControlType='DROP' OR %ControlType='COMBO')

This is a Global extension doing all windows.

#TEMPLATE(CBWndPreviewClass,'CB Window Preview Class Related Templates'),FAMILY('ABC','CW20')
#Extension(CBWndPrvListFromQ,'For all LIST boxes set User Prop with &Queue Reference - Version 1.01')
#DISPLAY(' ')
#DISPLAY('For every LIST and COMBO add call to CBListPropFromQ(?List,Queue)')
#DISPLAY('which will set User Property FromQ. ')
#DISPLAY(' ')
#DISPLAY('In root module add CBListPropFromQ() PROCEDURE() ')
#DISPLAY(' ')
#PROMPT('Do NOT generate this Template i.e. Disable',CHECK),%DoNotGenerate,AT(10)
#!------------------------------------------------------------------------------
#!-- Legacy Init ------------------------------------------------------------------
#ATSTART
#DECLARE(%FromQ)
#ENDAT
#AT(%AfterWindowOpening),WHERE(~%DoNotGenerate)
  #! CBWndPrvListFromQ.TPL
  #FOR(%CONTROL),WHERE(%ControlType='LIST' OR %ControlType='DROP' OR %ControlType='COMBO')
    #SET(%FromQ,EXTRACT(%ControlStatement,'FROM',1)) 
    #IF(%FromQ AND 0=INSTRING(CHR(39),%FromQ,1,1))

 CBListPropFromQ(%Control,%FromQ,'%FromQ') !Tpl CBWndPrvListFromQ  #! <-- this line for each list

    #ELSE
 #!NO:  CBListPropFromQ(%Control,%FromQ,'%FromQ') this is From('string') no Q
    #ENDIF
  #ENDFOR   
#EndAT
#!-------------
#AT(%GlobalMap),WHERE(~%DoNotGenerate)
    CBListPropFromQ(LONG FEQ,*QUEUE FrmQ,<STRING NameQ>)
#EndAT
#!-------------
#AT(%ProgramProcedures),WHERE(~%DoNotGenerate)
CBListPropFromQ PROCEDURE(LONG FEQ,*QUEUE FrmQ,<STRING NameQ>)
Ref GROUP,AUTO
Q    &QUEUE
L    LONG,OVER(Q)   #! Could use INSTANCE(Q) 
  END
  CODE
  Ref.Q &=FrmQ ; FEQ{'FromQ'}=Ref.L ; FEQ{'FromWho'}=CHOOSE(~OMITTED(NameQ),NameQ,'Queue' & Ref.L)
  RETURN
#EndAT
#!------------------------------------------------------------------------------  

Thank you for this code. However, I was so stubborn that I wanted to write my own template to improve my skills . This is what I made:

#EXTENSION(Translate2,'Testen vertaling listboxes'),APPLICATION
#!
#AT(%WindowManagerMethodCodeSection,'Init','(),BYTE'),PRIORITY(9500)
#SUSPEND
#DECLARE(%LocEnableSortHeader)
#?! Explicitly translate listbox headers because the init of SortHeader interfers (translate2).
#!FOR(%Control),WHERE(%ControlTemplate='BrowseBox(ABC)' AND %EnableSortHeader)    #! <-- This condition doesn't work: "Unknown Variable '%EnableSortHeader"
#FOR(%Control),WHERE(%ControlTemplate='BrowseBox(ABC)')
!BRW%ActiveTemplateInstance                                                          #! <-- Te template instance doesn't show.
Translator.TranslateControl(%Control)
#ENDFOR
#RESUME
#ENDAT

The only thing I haven’t been able to figure out yet is how to use the %EnableSortHeader symbol. When I use it, I get an error message saying “Unknown Variable '%EnableSortHeader”. In other words, how can I import a symbol from another template chain into my own template?

And I also don’t understand why %ActiveTemplateInstance doesn’t show anything. Any ideas?

Take a look at #CONTEXT in the help.

I did, but I didn’t understand how to use it in my template. So I might some help on that one.

This isn’t tested …

#EXTENSION(Translate2,'Testen vertaling listboxes'),APPLICATION
#!
#AT(%WindowManagerMethodCodeSection,'Init','(),BYTE'),PRIORITY(9500)
  #SUSPEND
  #DECLARE(%LocEnableSortHeader)
#?! Explicitly translate listbox headers because the init of SortHeader interfers (translate2).
#!FOR(%Control),WHERE(%ControlTemplate='BrowseBox(ABC)' AND %EnableSortHeader)    #! <-- This condition doesn't work: "Unknown Variable '%EnableSortHeader"
  #FOR(%Control),WHERE(%ControlTemplate='BrowseBox(ABC)')
!BRW%ActiveTemplateInstance                                                          #! <-- Te template instance doesn't show.
    #CONTEXT(%Procedure,%ControlInstance)
    #SET(%LocEnableSortHeader,%EnableSortHeader)
    #ENDCONTEXT
    #IF(%LocEnableSortHeader=1)
Translator.TranslateControl(%Control)
    #ENDIF
  #ENDFOR
  #RESUME
#ENDAT

Thank you!
#CONTEXT(%Procedure,%ControlInstance) did the trick.
I used #CONTEXT(%Procedure,%ActiveTemplateInstance) and that wasn’t working.

If I understand this correctly this is because I am doing a loop over controls and not template instances.

Then one question left. How can I get the class name that is linked to this control? Something like BRW or BRW3 etc. Not really needed for this template, as it works no, but I just want to learn.

That depends on the template. If your context is the browse template then the symbol is %ManagerName. There is no way to know that without looking at the template in question and finding the symbol name.

When you used %ActiveTemplateInstance, that is YOUR template instance.

It was indeed the symbol %ManagerName so I got the last thing working also.Thank you for you suggestions, it improved my template skills.