Hello,
I've been working in a project for a while and I'm not sure if the multilingual architecture I am currently implementing is really good idea. Sometime ago I had a look to I18n and PO files, but I remember it as a bit confusing and painful to edit, mantein, etc. For some reason I didn't felt confortable using it. Maybe I shall give it a try again someday, but I decided to develop my own method as an alternative and to learn more about pascal. Check it please and let me know what you think.
The method works as follows:
We define a file called constants.inc that contains several constants like LANGUAGES_AVAIBLE = 9 (i.e)
and ES = 1, CA = 2, EU = 3, GA = 4 ... up to the number you need.
The lpr/ppr file will hold a global variable (LANGUAGE : integer) to define a common default language for
all forms in the application:
(...)
{$R *.res}
{$INCLUDE ../include/constants.inc}
(...)
var
// crear variables globales
LANGUAGE : integer; cvar; export;
(...)
begin
LANGUAGE := ES; // default language
(...)
Then, for every form in the application we must create an include file (i.e unit_mainform_language.inc)
with the following structure (i.e for eight local languages in Spain and english), that will contain
translations for all the labels, captions, titles, list items, hints, etc, for *that* particular form:
{ 1 2 3 4 5 6 7 8 9
+---------------------------------------------------------------------------------------------------------------------+
| | | | | | | | | |
| Español (es_ES) | Catalán | Valenciano | Mallorquín | Aranés | Euskera | Gallego | Asturiano | English |
| | | | | | | | | |
+---------------------------------------------------------------------------------------------------------------------+
}
// the blank ones have not yet been traslated or can be interpreted as equivalent to other's language word (#2 OR #1)
uEButton_close_Caption: Array[1..LANGUAGES_AVAIBLE] of String = (
'Cerrar',
'Tancar',
'',
'',
'',
'Itxi',
'Pechar',
'',
'Close');
Label_copyrightLogo_Caption: Array[1..LANGUAGES_AVAIBLE] of String = (
'Todos los derechos reservados.',
'Tots els drets reservats.',
'',
'',
'',
'Eskubide guztiak erreserbatuta.',
'Todos os dereitos reservados.',
'',
'All rights reserved.');
(...)
Items in lists, comboboxes and such are declared in two dimensional arrays, something like this:
ComboBox_AccessControlType_ListItems: Array[1..LANGUAGES_AVAIBLE,1..7] of String = (
('Identificador Empleado','PIN','Persona-Clave','Máquina-Clave','Tarjeta','SmartCard','DNI Electrónico'),
('Identificador Empleat', 'PIN', 'Persona-Clau', 'Màquina-Clau', 'Targeta', 'SmartCard', 'DNI Electrònic'),
('','','','','','',''),
('','','','','','',''),
('','','','','','',''),
('Langilearen Identifikatzailea', 'PIN', 'Pertsona-Pasahitza', 'Makina-Pasahitza', 'Txartela', 'SmartCard', 'NAN elektronikoa'),
('Identificador de empregado', 'PIN', 'Contrasinal de persoa', 'Contrasinal de máquina', 'Tarxeta', 'SmartCard', 'Identificación electrónica'),
('','','','','','',''),
('Employee Identifier', 'PIN', 'Person-Password', 'Machine-Password', 'Card', 'SmartCard', 'Electronic ID')
);
Then in the form's code (i.e unit_whateverform.pas) we must define:
(...)
var
whateverForm: TwhateverForm;
LANGUAGE : Integer; cvar; external; // it's a global variable
(...)
We must place {$INCLUDE ../include/constants.inc} somewere to access the language codes or we may declare those constants as global at the lpr/ppr
file.
Then we will load language translation on creation time to the form:
procedure TwhateverForm.FormCreate(Sender: TObject);
var
//LANGUAGE : Integer; uncommenting this will give an Access violation under linux but not under Windows.
i : integer;
{$INCLUDE ../languages/unit_whateverform_languages.inc}
begin // start to assign translated words
uEButton_Help.Caption := BitBtn_Help_Caption[LANGUAGE];
uEButton_Help.Hint := BitBtn_Help_Hint[LANGUAGE];
uEButton_Configuration.Caption := BitBtn_Configuration_Caption[LANGUAGE];
uEButton_Configuration.Hint := BitBtn_Configuration_Hint[LANGUAGE];
uEButton_Start.Caption := BitBtn_Start_Caption[LANGUAGE];
uEButton_Start.Hint := BitBtn_Start_Hint[LANGUAGE];
uEButton_Quit.Caption := BitBtn_Quit_Caption[LANGUAGE];
(...)
And we also add items to lists, etc in the selected language:
// we then load translated items to lists, comboboxes, etc
for i := 1 to length(ComboBox_AccessControlType_ListItems[LANGUAGE]) do
begin
ComboBox_AccessControlType.Items.Add(
ComboBox_AccessControlType_ListItems[LANGUAGE][i]);
end;
for i := 1 to length(ComboBox_SessionType_ListItems[LANGUAGE]) do
begin
ComboBox_SessionType.Items.Add(
ComboBox_SessionType_ListItems[LANGUAGE][i]);
end;
(...)
Ok, so that's all. Let me know what you think please.
The main benefits I see this way are:
1) Adding new languages to a *_language.inc file is as easy as using any text editor, copy paste a line "a","b","c"... into Google Translator (if that language is supported) and paste back the result to the inc file at the right position in the array.
2) I believe this can be scripted into an automathed process to add as many languages as supported by Google or other
online services.
3) Easier to distribute plain text files and easier for contributors to edit (I think so)
4) Easier to read and mantein (imho). One file for form with all supported languages. Any one can read the line in english and translate to it's own language.
5) Easier to distribute as inc files are compiled into the executable. No need to distribute and install *.po files
6) You can change language on the fly, creating a loadLanguage() procedure to be called on creation time and at any moment the user wants to. Interesting in a country with more than one oficial language, a POS terminal shared with several operators, etc
7) add your own if you see any...
Bad things I see:
1) words are stored in arrays and data structures that consume memory resources.
2) all languages shall be loaded into memory (probably you dont need to have portuguese, chinese, dutch and arab at the same time, but yes for bilingual regions: finnish-swedish, spanish-catalan, spanish-euskara...)
3) all languages shall be kept in memory while running, at least co-oficial languages, to allow on-the-fly switching. Non required elements can be freed from the array.
4) add what you see here
regards,