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

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.
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.
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
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.
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.
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.
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?
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.