some dxgettext improvements

 Delphi, dxgettext  Comments Off on some dxgettext improvements
Apr 102022
 

I fixed a bug in the dxgettext executable which made it add wrong ressource string names to the po files which in turn made the msgmergePOT tool select wrong translations. You will have to compile your own executable to get this bugfix. The source code is in the project’s svn repository on SourceForge.

Also I added scripts to generate partial German and French translations for Delphi 10, 10.1, 10.2, 10.3 and 10.4 and even added those partial translations for Delphi 10, 10.1, 10.2 and 10.4 (note that 10.3 is missing) to the repository. Warning: These translations may still be faulty.

 Posted by on 2022-04-10 at 18:31

project.exe is open in another program errors

 Delphi, dxgettext  Comments Off on project.exe is open in another program errors
Jun 232019
 

I have been using two tools to add additional data to my executable files in post build scripts for years:

Both tools open the freshly built executable and append chunks of data to it. And both started to fail with the error “[project].exe is open in another program” more often lately.

I never managed to find out exactly what causes the problem. First I suspected BitDefender antivirus, but the error still occurs after I have switched to Windows Defender, so it’s not just BitDefender but probably any antivirus software that is causing the trouble.

As a first try I added a 2 seconds delay between the compiling/linking and the execution of each of these tools. It seemed to help, the errors became much less frequently but they still occurred. Also those 4 seconds always felt like eternity (the whole compile usually takes less than 10 seconds, so 4 additional seconds is a whopping 40%).

So, a few days ago I had enough. I’ve got the source code of these tools, so why not simply add several tries to opening the file? That’s what I did: Both tools now try to open the file and if that fails wait for a second and try again. Rinse and repeat up to 10 times. In my tests, it rarely took more than 2 tries, usually the first one succeeded. That means it shaved these 4 seconds off the build time again with the rare exception of adding them again at very few occasions.

The changes to assemble are in the GnuGettext svn repository.

And here is a patch to jcldebug.pas which I used for MakeJclDbg. (If somebody wants to submit this as a pull request on github, go ahead.)

Both executables are available from my dzlib+buildtools project on OSDN.

If you would like to comment on this article, go to this post in the international Delphi Praxis forum.

 Posted by on 2019-06-23 at 11:36

GnuGetText.pas using Utf8ToUnicodeString instead of Utf8ToWideString

 Delphi, dxgettext  Comments Off on GnuGetText.pas using Utf8ToUnicodeString instead of Utf8ToWideString
Mar 092019
 

A few weeks ago, Sue King contacted me because there was a problem with using dxGetText together with the Nexus DB components.

For Unicode aware Delphi versions gnugettext.pas declares a function utf8decode which calls System.UTF8ToWideString. After replacing a call to utf8decode with UTF8ToUnicodeString the problem went away.

Since I don’t want to break backwards compatibility with non Unicode Delphi versions I have now changed gnugettext.utf8decode to call UTF8ToUnicodeString instead of UTF8ToWideString.

I can’t see any problem with this change but I am far from being an expert on Unicode related issues. So, if you find any problem with this change, please comment on the corresponding topic in the international Delphi Praxis forum.

 Posted by on 2019-03-09 at 14:11

Using dxgettext on Windows 10

 Delphi, dxgettext  Comments Off on Using dxgettext on Windows 10
Dec 022018
 

The dxGetText installer available from SourceForge has been quite outdated for a while. Via this StackOverflow answer I got this link, where somebody actually went through the trouble to update the tools and create a new installer for them. He also provides the sources he used for the tools. I hope they are based on a recent version of the ones in the SVN repository.

Thanks Dr. Jürgen Rathlev for taking the time!

(Note: I haven’t yet tried them)

 Posted by on 2018-12-02 at 14:33

Enable debug logging in gnugettext.pas

 Delphi, dxgettext  Comments Off on Enable debug logging in gnugettext.pas
Sep 032018
 

gnugettext.pas has got a conditional define called DXGETTEXTDEBUG. If it is defined, various debug messages are written to a MemoryStream. That won’t help much, if you can’t read that stream, so you need a way to write that stream to a file.

Guess what, that’s easily possible. Just call DefaultInstance.DebugLogToFile passing it a file name to which you want the log to be written. An additional parameter determines whether the file should be overwritten or been appended to.

In that call, the whole memory stream gets written to that file, so no log messages get lost. Also, any subsequent log messages will be written directly to that file instead of the memory stream.

 Posted by on 2018-09-03 at 17:34

Classname with !dx suffix?

 Delphi, dxgettext  Comments Off on Classname with !dx suffix?
Nov 192015
 

Today I had an interesting bug to fix:

I used a form’s Classname (Yes, I have a reason not to use the Name.) to persist its size in the registry and wondered why it didn’t work. Looking at the registry entries I found, that there was an entry with the class name suffixed by a ‘!dx’.

So I placed an appropriate breakpoint in the constructor and destructor respectively of the form and found that everything was fine in the constructor but in the destructor there was this suffix.

On further investigation it turned out that the culprit was dxgettext. In one of the contributions that were submitted by users but which in this case I committed myself, the class name was suffixed with !dx as a debug help. It’s not necessary for the functionality.

The fix of course was to disable this code. There is now a new conditional define dx_ChangeProxyClassName in the current gnugettext.pas code, that controls whether the suffix is added or not. The default is to not add it.

 Posted by on 2015-11-19 at 16:03

Setting a default language with dxgettext

 Delphi, dxgettext, dzLib  Comments Off on Setting a default language with dxgettext
Nov 102014
 

By default, if no translation for a language is available, dxgettext will not do any translation but use the strings as they are in the source code. Sometimes this is not desirable. e.g.

  • Your customer does not understand the source language (e.g. your source language is not English but say German)
  • You are using dxgettext to convert special characters from a placeholder (e.g. “(R)” or “[deg]”) to the actual character (“®” or “°”)

In these cases you’d probably want the translation to default to a language that is actually supplied.

dxgettext doesn’t seem to have this feature (I looked quite hard) so I implemented it myself.

unit u_dzTranslator;

interface

// ... other stuff ...

///<summary>
/// Sets the language to use </summary>
procedure UseLanguage(_LanguageCode: string);

///<summary>
/// gets a list of languages for which translations are available </summary>
procedure GetListOfLanguages(const _Domain: string; _Codes: TStrings;
  _Languages: TStrings = nil);

///<summary>
/// Sets the language to use if the desired language is not available,
/// defaults to English </summary>
procedure SetDefaultLanguage(const _LanguageCode: string);

// ... other stuff ...

implementation

uses
  gnugettext;

// ... other stuff ...

const
  DEFAULT_LANGUAGE = 'en';
var
  gblDefaultLanguage: string = DEFAULT_LANGUAGE;

procedure UseLanguage(_LanguageCode: string);
var
  Codes: TStringList;
  CurLang: string;
  i: Integer;
  p: Integer;
begin
  gnugettext.UseLanguage(_LanguageCode);

  CurLang := gnugettext.GetCurrentLanguage;
  Codes := TStringList.Create;
  try
    GetListOfLanguages('default', Codes);
    for i := 0 to Codes.Count - 1 do begin
      if SameText(CurLang, Codes[i]) then begin
        // There is a translation for this language and country, everything is fine
        Exit; //-->
      end;
    end;
    // no translation found, try without the country code
    p := Pos('_', CurLang);
    if p <> 0 then begin
      CurLang := Copy(CurLang, 1, p - 1);
      for i := 0 to Codes.Count - 1 do begin
        if SameText(CurLang, Codes[i]) then begin
          // There is a translation for this language but not country, we can live with that
          Exit; //-->
        end;
      end;
    end;
  finally
    FreeAndNil(Codes);
  end;

  // we found no translation for this language, so we use the default language
  gnugettext.UseLanguage(gblDefaultLanguage);
end;

procedure SetDefaultLanguage(const _LanguageCode: string);
begin
  if _LanguageCode = '' then
    gblDefaultLanguage := DEFAULT_LANGUAGE
  else
    gblDefaultLanguage := _LanguageCode;
  UseLanguage(gnugettext.GetCurrentLanguage);
end;

procedure GetListOfLanguages(const _Domain: string; _Codes: TStrings; _Languages: TStrings = nil);
var
  i: Integer;
begin
  _Codes.Clear;
  gnugettext.DefaultInstance.GetListOfLanguages(_Domain, _Codes);
  if Assigned(_Languages) then begin
    _Languages.Clear;
    for i := 0 to _Codes.Count - 1 do begin
      _Languages.Add(languagecodes.getlanguagename(_Codes[i]));
    end;
  end;
end;

// ... other stuff ...

initialization
  SetDefaultLanguage(DEFAULT_LANGUAGE);
end.

Apart from the obvious, that is, setting a unit global variable to the desired default language, which itself defaults to English, this code changes the way UseLanguageWorks. It now does:

  • Call gnugettext.UseLanguage to let gnugettext do its stuff
  • Call gnugettext.GetCurrentLanguage to get the language that gnugettext uses (just in case gnugettext changes it from what was set with UseLanguage).
  • Gets a list of all supported translations
  • Tries to find a matching translation for the desired language and country.
  • If not found, tries to find a matching translation for the desired language, ignoring the country
  • If not found, changes the language to the default language.

Note that I just wrote this code, it might still contain bugs and is probably far from perfect. I will put it into the unit u_dzTranslator of my dzlib library and will fix any bugs I find in the future there.

 Posted by on 2014-11-10 at 13:16

GORM experimental release

 Delphi, dxgettext  Comments Off on GORM experimental release
Jul 052014
 

GORM is an editor for .po files that was originally written by Lars Dybdahl and to which I have been contributing quite a few features in recent years. Since Lars is too busy to do a release and currently even the original download doesn’t seem to work, I have compiled the current sources and provide a download here:

Gorm_2014-07-05.zip

In addition to the features described on the Gorm homepage this new version can do the following:

  • Edit the po file header.
  • Auto translate, using Google translate (not sure whether it is still available) or Microsoft Translator. Both need an application key that is not part of Gorm.
  • Auto translate from a local translation repository, which is an MS Access database, one per language, that can be shared by multiple developers
  • Auto translate using translation memory, that is a po file that is automatically generated.
  • Load and save translations to be ignored from a ignore.po file.
  • In addition to English, I provide it with German localization, that, of course, is created with Gorm itself.

The sources are available from the dxgettext repository on sourceforge.
Of course I like Gorm much better than PoEdit and other alternatives. If there are any bugs, that’s probably my fault.

 Posted by on 2014-07-05 at 18:59

Storing gnugettext translations as resources

 Delphi, dxgettext  Comments Off on Storing gnugettext translations as resources
Apr 142013
 

I always wondered why the assemble tool in dxgettext appends the translation files directly to the executable rather than using the existing mechanism of resources. This is no longer necessary. I have updated the gnugettext code to optionally use RCDATA resources named after the files.

So you e.g. create a translations.rc file as

LOCALE_DE_DEFAULT RCDATA ..\locale\de\LC_MESSAGES\default.mo
LOCALE_DE_DELPHI RCDATA ..\locale\de\LC_MESSAGES\delphi2007.mo
LOCALE_DE_DZLIB RCDATA ..\locale\de\LC_MESSAGES\dzlib.mo
LOCALE_EN_DEFAULT RCDATA ..\locale\en\LC_MESSAGES\default.mo
LOCALE_EN_DELPHI RCDATA ..\locale\en\LC_MESSAGES\delphi2007.mo
LOCALE_EN_DZLIB RCDATA ..\locale\en\LC_MESSAGES\dzlib.mo

Compile it using a resource compiler e.g. brcc32 to translations.res and add it to the executable.

The new code in gnugettext will automatically find and use these resources, all you have to do is add the conditional define dx_SupportsResources.

// if the conditional dx_SupportsResources is defined the .mo files
// can also be added to the executable as Windows resources
// Be warned: This has not been thoroughly tested.
// Default is turned off.
{.$define dx_SupportsResources}

You can find the updated gnugettext.pas in the dxgettext subversion repository on sourceforge.

Disclaimer: I have only cursorily tested this code. Use it at your own risk!

 Posted by on 2013-04-14 at 19:31