How to use TMSFNCCloudTranslation to translate component labels

Hi + tx for support!

What I want to do:
Having a TWebForm with some compoents and labels on it, additionally there is a combobox which allows selecting languages. Having selected a language, I want to translate the labels and text on the form.

I have set up a TMSFNCCloudTranslation successfully.

I also have a translating method _t (see below).

What I want to do is:

procedure TfrmWRMain.TranslateForm(sLng: String);
begin
  pgRezeptberechnung.Caption := _t('Rezeptberechnung');
  pgZugriffsschluessel.Caption := _t('Zugriffsschlüssel');
  lblRezeptBezeichnung.Caption := _t('Rezept-Bezeichnung');
  tabZutaten.Caption := _t('Zutaten');
  tabMemo.Caption := _t('Beschreibung');
end;

This is the translating method:

function TfrmWRMain._t(s: string):string;
var
sTranslation:String;
begin
  TMSFNCCloudTranslation.Translate(s, sFormLanguage,
    procedure(const ARequest: TTMSFNCCloudTranslationRequest; const ARequestResult: TTMSFNCCloudBaseRequestResult)
    begin
      if ARequest.Translations.count > 0 then begin
        sTranslation := ARequest.Translations[0].TranslatedText;
       Result:=sTranslation; // this does not work,  because Result is not available here
      end;
    end));
  Result:=sTranslation; // this does not work, because the callback is not finished yet
end;

However, I am unsure, how to return the translation to the calling method.

How can this be done? Or is it better to chhose anotrher approach?

The result of the translate is received asynchronously.
The translation result is available in the OnTranslate event handler, so please update the label from this OnTranslate event where the translated text will be available.

Tx, Bruno, for your answer. I know the OnTranslate-event, but in the event there is no chance to know what label or other component I have to update.

What I will try next is modifying the translation method, so that there is a parameter taking an anonymous procdure. In this procedure I can set the label.

Basically something like:

_t('Rezeptberechnung', procedure (sTranslatedText:String)
begin
pgRezeptberechnung.Caption:=sTranslatedText;
end;);

And this procedure is called inside the TMSFNCCloudTranslation.Translate method.

Is there any better way to handle this?

Yes, this works as wanted:

procedure TfrmWRMain._t0(s: string; p: TProc<string>);
var
  sTranslation: String;
begin
  TMSFNCCloudTranslation.Translate(s, sFormLanguage,
    procedure(const ARequest: TTMSFNCCloudTranslationRequest; const ARequestResult: TTMSFNCCloudBaseRequestResult)
    begin
      if ARequest.Translations.count > 0 then begin
        sTranslation := ARequest.Translations[0].TranslatedText;
        p(sTranslation);
      end;
    end);
end;

And is called:

  _t0('Rezeptberechnung',
    procedure(s: string)
    begin
      pgRezeptberechnung.Caption := s;
    end);

Is a bit clumsy, but works as needed, I think.

That is indeed also a good & valid approach.

Still I am unsure, how to deal with something like this:

if not dmWRMain.bLoggedIn then begin
 _t('Do you want to login?',
      procedure(s: string)
        begin
          sMsg := s;
       end);
       if dlgYesNo(sMsg) = mrYes then
         Login
      else
        Exit;
    end;

I can put the dialog inside the assigning procedure, but then the flow of the programm will continue unchanged. I can put the whole if/else inside the procedure, but I am sure, there is a more elegant way do handle this.

Of course I can translate the texts in advance, maybe this is an option, too.

Can you give me some hints?

Thanks a lot!

The two solutions that make the coding easier are:

  • translate this in advance
  • wrap the translation into a promise and await it before the call to show the dialog

Thank you, Bruno.

wrap the translation into a promise and await it before the call to show the dialog

Can you give me code sample?

I don't have it ready here, but the background & information is here:
https://wiki.freepascal.org/pas2js_AsyncAWait