# Better Translation Manager

The Better Translation Manager (BTM) is a replacement for the Delphi Translation Manager a.k.a. the Integrated Translation Environment (ITE) and External Translation Manager (ETM).

Why is it better? Well, for one thing, it's free but more important; It actually works - unlike the ITE/ETM.

## Why?

The standard Translation Manager that ships with Delphi today was originally an individual product known as the Borland Translation Suite. With Delphi 5 it became a part of the enterprise edition.
The Borland Translation Suite showed great promise but unfortunately it never evolved from its roots as an external tool and has always been hampered by severe bugs that made it completely unusable in practice. As a result nobody uses it. This can be witnessed by the plethora of homegrown and commercial alternatives.

The great benefit of the standard translation system is that it just works (this is the system itself I'm talking about, not the tools. The tools suck). Apart from the requirement that you must use resourcestrings you don't need to do anything special when writing your code. At run time you just place the compiled resource modules in the same folder as your application and the Delphi Run Time Library automatically takes care of loading and using the translations based on the current Windows user interface language.

Anyway, since Embarcadero has now finally admitted that they are never going to fix the Delphi Translation Manager and instead recommend that we find alternative solutions, I decided that it was time I solved this little problem once and for all.

The core functionality of the Better Translation Manager was written in two weeks during my summer vacation in Italy 2019. Amazing what you can do with a little pasta!

## Features

• Does not require any changes to the source code of the application being translated.
• Works with the existing standard Delphi localization system.
• Translates resourcestrings and all strings in forms regardless of any 3rd party components used.
• Works on compiled application. Source code is never used.
• Generates localized binary resource modules (resource DLLs). Does not use an external compiler.
• Can import existing translations from compiled application and resource modules or from XLIFF localization source files (dfn, rcn files).
• Read and save TMX and TBX translation memory files.
• Import Translation Memory from TMX (Translation Memory eXchange), TBX (TermBase eXchange), Microsoft Glossary and CSV.
• Machine Translation using Translation Memory, Microsoft Translation Service or Microsoft Terminology Service.
• Forms, Components, Types and Values that should be ignored can be specified in a Stop List.
• Translations are Spell Checked.
• Validation Rules to catch common translation mistakes.
• Supports Right To Left (RTL) editing based on translation language.
• Translation project is stored in a single XML file.
• Command line interface for use in automated build systems.
• Fast! Refreshing a large project typically takes less than a second vs. many minutes with the ITE/ETM.
• Supports all Unicode versions of Delphi (i.e. Delphi 9 and later).
• Resource modules contain the version resource of the source application.

## What it doesn't do

There's one task that BTM, by design, doesn't attempt to solve: Localizing the placement and size of controls.

Since it has been my experience that it is a far better idea to design the user interface in such a way that the layout automatically accommodates changes in font- and text size and shorter/longer texts due to translation, I decided from the start that I would not be supporting localization of size and position of controls. This also relieved me of having to create a run time form designer, supporting 3rd party controls visually (something that nobody so far has managed to find a foolproof solution to) and deciding what individual properties constitutes size/position values.

Instead I just localize all string values - and only string values.

## But wait... There's More!

Yup, you not only get this little wonder for free. You get the full source code too.

Grab it at the repository linked at top. More details at the repository.

Enjoy

/ Anders Melander

Edited by Anders Melander
dog ate the image
• 4
• 22

Anders,

thats an amazing work and effort you did here. I am looking forward to put that into use.

Thanks,

Julian

15 minutes ago, wpcubed said:

In case you just want to use BTM as-is, and don't care to compile it yourself, you can download the application installer here: http://melander.dk/download/

Feel free to comment or ask for help about anything here or at the issue tracker.

I know it could do with some documentation but the readme is too big already and I have other projects in need of some love (also, who likes writing documentation? ).

Thanks for this great tool.

It would be nice if it could be used optionally portable. For example via a file PORTABLE in the program directory.

I don't own the DevExpress components, so I can't make the changes myself.

Edited by ringli

8 hours ago, ringli said:

It would be nice if it could be used o﻿ptionally portable. For example via a file PORTABLE in the program directory.

I've thought about a portable version but the framework I use for configuration persistence is highly dependent on the features provided by the registry so I would have to rewrite most of that.

The use of %appdata% is easy enough to redirect to the application folder. I already do that for debug builds.

One solution would be to have the application export the content of its registry branch on termination and restore it again on start. Would that be satisfactory?

Another solution would be to package it with an application virtualization system, like WMware ThinApp, but I'd rather not go down that road.

On 10/27/2019 at 5:30 PM, Anders Melander said:

One solution would be to have the application export the content of its registry branch on termination and restore it again on start. Would that be satisfactory? ﻿﻿

Exporting the settings every time I quit and restoring them the next time I start up I don't think so much. A backup / restore function would certainly be helpful, with which you can save the complete settings if a purely portable version is not or only with difficulty possible.

6 minutes ago, ringli said:

Exporting the settings every time I quit and restoring them the next time I start up I don't think so much.

The application would do this for you automagically, so you wouldn't notice that it's using the registry internally.

Anyway I just finished implementing it. It's available in version 1.0.7241.65070
You can now run in portable mode either by specifying it with a command line switch: "amTranslationManager.exe -portable" or by placing a file named "portable" in the application folder. The application settings dialog can also do this for you.

When in portable mode, settings are read from and written to a "amTranslationManager.portable" file located in the same folder as the application. Application data files (Translation Memory, user spell check dictionaries, etc.) are also stored in or under that directory.

One problem that I haven't solved is how to get the Inno Setup installer to create a portable installation.

• 1

13 hours ago, Anders Melander said:

The application would do this for you automagically, so you wouldn't notice that it's using the registry internally.

Anyway I just finished implementing it. It's available in version 1.0.7241.65070
You can now run in portable mode either by specifying it with a command line switch: "amTranslationManager.exe -portable" or by placing a file named "portable" in the application folder. The application settings dialog can also do this for you.

When in portable mode, settings are read from and written to a "amTranslationManager.portable" file located in the same folder as the application. Application data files (Translation Memory, user spell check dictionaries, etc.) are also stored in or under that directory.

One problem that I haven't solved is how to get the Inno Setup installer to create a portable installation.

Inno Setup - just not creating uninstaller and not writing to the registry? Some notice is here: http://www.jrsoftware.org/ishelp/index.php?topic=setup_uninstallable

48 minutes ago, Vandrovnik said:

Inno Setup - just not creating uninstaller and not writing to the registry?

It's no problem creating a separate installer for portable mode. I could even just zip the files and leave it at that.

The problem is getting the existing installer to have a "portable mode" option.

Something like this?

[Setup]
Uninstallable=not WizardIsComponentSelected('PortableComponent')

[Types]
Name: normal; Description: "Normal setup"
Name: portable; Description: "Portable"

[Components]
Name: "Main"; Description: "Test files"
Name: "PortableComponent"; Description: "Portable"; Types: portable

[Files]
Source: "TestFile.jpg"; DestDir: "{app}"; Flags: replacesameversion

• 1

21 minutes ago, Vandrovnik said:

Some﻿thing like this?

Well yes, but there's the existing installer types and components to take into account.

I mean I currently have the default three Types: Full, Compact and Custom. I don't mind getting rid of Compact but if I must chose between Full and Custom then it's Full that goes.

Anyway, let's say I go with your suggestion: Normal and Portable, both of which will probably then install the Full component set.

Now I then need to copy some files into {userappdata}\foo in Portable mode but into {app}\bar in Normal mode. How do I do that without having to duplicate the [files] entries?

And then there's the [Icons] section. I should probably not create any shortcuts in Portable mode but as fare as I can see it's not possible to control [Icons] based on Type - only based on Components.

Can't you habe empty components for both types that you can refer to?

For files, you can use something like this:

[Files]
Source: "TestFile.jpg"; DestDir: {code:MyDestDir}; Flags: replacesameversion

[Code]
function MyDestDir(Dummy: string): string;
begin
if WizardIsComponentSelected('PortableComponent')
then result:=ExpandConstant('{app}')+'\A'
else result:=ExpandConstant('{app}')+'\B';
end;

Icons - I would base them on Components, so create a component, which is enabled just for Normal type, and assign all icons to this type.

• 1

1 minute ago, Vandrovnik said:

For files, you can use something like this:

Thanks.

I had a feeling I might end up having to script my way out of it.

Please is it possible to translate one text in different ways? For example, in one form "Volume" means something like "hard disk" (svazek), but in another form "Volume" means something like "Level of sound intensity" (hlasitost). Is it possible to tell BTM to automatically translate this text in different ways in these two forms? (Or have one term as default and the other term selected for the second form, so that BTM will keep this selection for automatic translation in the future for this form).

57 minutes ago, Vandrovnik said:

Please is it possible to translate one text in different ways? For example, in one form "Volume" means something like "hard disk" (svazek), but in another form "Volume" means something like "Level of sound intensity" (hlasitost). Is it possible to tell BTM to automatically translate this text in different ways in these two forms? (Or have one term as default and the other term selected for the second form, so that BTM will keep this selection for automatic translation in the future for this form).

Huh, in every programmer's life there's a moment to realize that natural keys suck and surrogate keys rule xD

47 minutes ago, Vandrovnik said:

is it possible to translate one text in different ways?

Sure it's possible.

Translations are always associated with a context, the context being the module (form), the component and the property.

If you're doing automatic translation (i.e. machine translation), then the context is not used, only the source language and source text.

However if you have more than one translation of a term/text in your Translation Memory, then BTM will prompt you to choose a translation for each of the different source terms. You can choose to use the same for all or use a different one for each of them.

For example here I have added the three different Czech translations of the term "Volume" to my Translation Memory (forgive me if I mistranslated - I don't speak Czech 🙂

):

If I now perform a machine translation from Translation Memory then I will be prompted:

If doing manual translation then BTM will indicate to you that it already know these translations (even if you haven't added them to the Translation Memory) so they can be easily reused.
For example the grid will mark the terms that has known translations and display them as a hint:

and during manual edit you can choose from these translations in a drop down list:

The principle is the same if I perform machine translation using one of the web services.
For example here's the prompt I get when using Microsoft Terminology Service:

Strangely, if I use Microsoft Translation Service then I only get one term back: "Objem". but that might be caused by a misconfiguration on my part. I'll investigate.

Thank you very much for your explanation, I will investigate it.

And... 🙂 Please could you add buttons to remove lines from translation memory? I will import data from TMX. Sometimes I find a mistake in it, but do not know how to remove the line (in cases when there are two versions, one correct and one wrong).

6 minutes ago, Vandrovnik said:

Just use the [Delete] key...

• 1

27 minutes ago, Anders Melander said:

Strangely, if I use Microsoft Translation Service then I only get one term back: "Objem". but that might be caused by a misconfiguration on my part.

No. The Microsoft Translator Text service only support a single translation per language. That sucks. I guess they're not dogfooding this API.

The strange thing is that they claim it powers Bing translate which can find multiple translations per source value.

May I have a few other questions? (Hopefully not so stupid as deleting row in TM...)

1. Is it possible to have TranslationMemory.dat in user defined folder? Something like C:\BTM1\TranslationMemory.dat for App1 and C:\BTM2\TranslationMemory.dat for App2. Or to tell amTranslationManager.exe using command line switch, where to look for TranslationMemory.dat. I need to keep separate translation memories for these applications.

2. Has .xlat file to be in the same folder as App.exe? I saved it to another folder, but next time I opened this project, BTM complains it cannot find App.exe (because it does not save full path to the App.exe in .xlat file).

3. In Translation Memory, when data is sorted by a column: please could you add a button, which will find next duplicity from current position to the end? For example, standing on the first row and pressing the button, it should find line "E&xport", because I have it on two rows (E&xport = E&xport and E&xport = Export). This would be very helpfull when eliminating errors in the translations.

6 minutes ago, Vandrovnik said:

Is it possible to have TranslationMemory.dat in user defined folder

No not yet.

The plan is to have support for multiple TM files and per project TMs.

For now you can only change the TM via the registry: HKCU\Software\Melander\TranslationManager\Providers\TranslationMemory\Filename

9 minutes ago, Vandrovnik said:

Has .xlat file to be in the same folder as App.exe?

No. The file can be saved anywhere.

Inside the xlat file (it's an XML file) there should be a reference to the source application and this should contain a relative filename. Like this:

<xlat>
<project sourcefile=".\Win32\Debug\HelloWorld.exe" stringsymbolfile=".\HelloWorld.drc" language="1033">
...

The sourcefile value is relative to the xlat file. The stringsymbolfile is relative to the source file.

There was a bug in an earlier version that broke relative file names but they should work in the latest release (v1.0.7241.65070).

24 minutes ago, Vandrovnik said:

In Translation Memory, when data is sorted by a column: please could you add a button, which will find next duplicity from current position to the end?

Yes. I can add something like that.

I already do duplicate detection when adding terms from the translation editor, but I don't do anything when importing translations in bulk (e.g. from TMX).

1 hour ago, Anders Melander said:
2 hours ago, Vandrovnik said:

Is it possible to have TranslationMemory.dat in user defined folder

No not yet.

The plan is to have support for multiple TM files and per project TMs.

For now you can only change the TM via the registry: HKCU\Software\Melander\TranslationManager\Providers\TranslationMemory\Filename

Actually I just realized that you should have (*) been able to use the "portable mode" to get per-project TM.

If you copy amTranslationManager.exe to the project folder and then start with the -portable switch then BTM will look for the TM in the project folder.
Just make sure that the above registry key value contains the value "%DATA%\TranslationMemory.dat" so the portable config file is initialized with the correct default value.

*) Unfortunately it seems I managed to break portable mode just before release so I'll have to get a new version released before that solution can be used. I'll get that done this week end.