Reduce memory for language translation strings

Just starting out? Need help? Post your questions and find answers here.
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Reduce memory for language translation strings

Post by AZJIO »

I am using array for strings for GUI

Code: Select all

Lng(1) = "Set"
Lng(2) = "domain"
Lng(3) = "Hide"
Am I getting 3 copies of the data for each row?
1. The text itself is directly in the binary executable.
2. Creating a string in a dynamic array.
3. The copy that the GUI keeps on the button.

How can I minimize the memory cost while still being able to process strings in a loop so that they can be read from a file in a loop.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Reduce memory for language translation strings

Post by skywalk »

Convenience consumes more memory. Pointers are your friend.
Otherwise, you must swap exe memory for an external file of your strings, which also takes up space, unless you compress the strings?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
mk-soft
Always Here
Always Here
Posts: 6204
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Reduce memory for language translation strings

Post by mk-soft »

Perhaps via language dll's.

ProcedureDLL.s Lng(Index) ...
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
NicTheQuick
Addict
Addict
Posts: 1504
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Reduce memory for language translation strings

Post by NicTheQuick »

AZJIO wrote: Fri Jun 17, 2022 1:52 pmAm I getting 3 copies of the data for each row?
1. The text itself is directly in the binary executable.
2. Creating a string in a dynamic array.
3. The copy that the GUI keeps on the button.
You can not change anything about point 3, the copy of the string in the GUI.
And inside your binary you could use pointers to a datasection or something like this:

Code: Select all

EnableExplicit

DataSection
	text:
		Data.s "Set", "domain", "Hide"
	text_end:
EndDataSection

Procedure fillArrayFromDatasection(*start, *end, Array Lng.String(1), initialCapacity.i = 10)
	Protected capacity = initialCapacity
	Protected pos.i = 0, tmp.s
	
	If capacity <= 0
		capacity = 1
	EndIf
	
	ReDim Lng(capacity - 1)
	
	While *start < *end
		If pos = capacity
			capacity * 2
			ReDim Lng(capacity - 1)
		EndIf
		tmp = PeekS(*start)
		PokeI(@Lng(pos), *start)
		*start + SizeOf(Character) * (Len(tmp) + 1)
		pos + 1
	Wend
	
	If pos > 0
		ReDim Lng(pos)
	EndIf
	
EndProcedure

Dim a.String(0)

fillArrayFromDatasection(?text, ?text_end, a())

Define i.i
For i = 0 To ArraySize(a()) - 1
	Debug a(i)\s
Next
But why are you concerned about the size of a few strings nowadays?
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Denis
Enthusiast
Enthusiast
Posts: 778
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: Reduce memory for language translation strings

Post by Denis »

You can use stringtable resource, so a rc file must be created.
Only needed string are loaded from resource in the desired language.

For example, i use this way with PureIconManager (French + English)

Strings are identified with an id (also used with PB to lauch the string), discrimination occurs with the language/sublanguage id
This way is a bit more complicated, you have to clearly understand how it works.

with a resouce loader, you can see the strings
(2057 id lang english/sublang english UK, 1036 id lang French/sublang French)
So, string with lang neutral (no translation, for example with "Ok")

Image
The strings in English
Image
The same strings in French

Procedure i use to load a string in resource

Code: Select all

      Procedure.s Load_ResourceString(String_id, langId)
            ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            ;// FONCTION/FUNCTION : Load_ResourceString()
            ;// BUT/PURPOSE : Charger depuis la ressource (exe en cours) la chaîne identifiée par String_id pour la langue langId
            ;// PARAMETRES/PARAMETERS : String_id - Identifiant de la chaîne à retrouver depuis la StringTable
            ;//                                     Id of the string to find from the StringTable
            ;//                         langId - langue au format MS (Macro Common::_MakeLangID)
            ;// RETOURNE/RETURNS : La chaîne correspondante ou une chaîne vide si erreur
            ;//                    Errors::#Return_No_Error en cas de succès
            ;//                    Errors::#Return_Error en cas d'erreur
            ;//                    Errors::#Return_No_Error if successful
            ;//                    Errors::#Return_Error in case of failure
            ;//REMARQUE/NOTE :     http://blogs.msdn.com/oldnewthing/archive/2004/01/30/65013.aspx
            ;//                    http://msdn.microsoft.com/en-us/library/aa381050(VS.85).aspx
            ;//                    http://msdn.microsoft.com/en-us/library/aa381043(VS.85).aspx
            ;//                    http://support.microsoft.com/kb/Q20011/en-us/   ; length de la stringTable
            ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            ;// Dernière adresse mémoire valide du fichier/Last valid memory address of file
            Protected *lastMemoryAdress
            ;// Pointeur sur la ressource spécifiée en mémoire (chaîne)/Pointer to the specified resource in memory (string)
            Protected *lockResource
            ;// Détermine l'emplacement de la ressource (chaîne)/Determines the location of the resource (string)
            Protected findResource
            ;// compteur de boucle/loop counter
            Protected i
            ;// Handle utilisé pour obtenir un pointeur sur le premier octet de la ressource spécifiée en mémoire (chaîne)
            ;// Handle used to obtain a pointer to the first byte of the specified resource in memory (string)
            Protected loadResource_RT_STRING
            ;// Taille des données de la ressource RT_String pour la langue choisie/size of RT_String resource data for the chosen language
            Protected sizeOfResource
            ;// Longueur de la chaîne lue/length of readen string
            Protected stringLength
            ;// chaîne à retourner/String to return
            Protected stringToReturn$ = #Empty$
            findResource = FindResourceEx_(Common::GetModuleHandle(), #RT_STRING, Common::_MakeIntResource(((string_id)>> 4) + 1), langId)
            If findResource  = Errors::#Return_Error
                  ProcedureReturn stringToReturn$
            EndIf
            loadResource_RT_STRING = LoadResource_(Common::GetModuleHandle(), findResource)
            If  loadResource_RT_STRING = Errors::#Return_Error
                  ProcedureReturn stringToReturn$
            EndIf
            *lockResource = LockResource_(loadResource_RT_STRING)
            If *lockResource = Errors::#Return_Error
                  ProcedureReturn stringToReturn$
            EndIf
            sizeOfResource = SizeofResource_(Common::GetModuleHandle(), findResource)
            If sizeOfResource = Errors::#Return_Error
                  ProcedureReturn stringToReturn$
            EndIf
            *lastMemoryAdress = *lockResource + sizeOfResource - 1
            ;// Boucle sur les 16 chaînes du bloc pour trouver celle identifiée par String_id
            ;// Loop on the 16 strings of the block to find the one identified by String_id
            For i = 0 To 15
                  If *lockResource > *lastMemoryAdress
                        Break
                  EndIf
                  ;// longueur de la chaîne sans le 0 terminal/String length without null terminal character
                  stringLength = PeekU(*LockResource)
                  ;// positionne le pointeur sur la longueur de la 1ere chaîne du bloc pour la langue choisie
                  ;// positions the pointer on the length of the block first string for the chosen language
                  *lockResource + 2
                  If *lockResource > *lastMemoryAdress
                        Break
                  EndIf
                  If i = String_id % 16
                        stringToReturn$ = PeekS(*LockResource, stringLength, #PB_Unicode)
                        Break
                  EndIf
                  *lockResource + (stringLength * 2)
            Next i
            ProcedureReturn stringToReturn$
      EndProcedure
Rc file i Use
// PureIconManager : Les déclarations Stringtables ressources
// Création automatique du fichier le 24/09/2019 à 17:15:41

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
1000, "Keep PNG compression"
1001, "Uncompress PNG icons"
1002, "Save as ico file - "
1003, "Save as dll file - PE 32 bits - "
1004, "Save as icl file - PE 32 bits - "
1005, "Save as icl file - NE 16 bits - "
1006, "Remove selection"
1007, "Delete selected file"
1008, "Select all"
1009, "Unselect all"
1010, "Toggle selection"
1011, "Add current file to favorites"
1012, "Add current folder to favorites"
1013, "Manage favorites"
1014, "Open Setting window"
1015, "Open preferences folder"
1016, "Batch conversion"
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
1000, "Conserve la compression PNG"
1001, "Décompresse les icônes PNG"
1002, "Enregistrer au format ico - "
1003, "Enregistrer au format dll - PE 32 bits - "
1004, "Enregistrer au format icl - PE 32 bits - "
1005, "Enregistrer au format icl - NE 16 bits - "
1006, "Supprimer la sélection"
1007, "Supprimer le fichier sélectionné"
1008, "Tout sélectionner"
1009, "Tout désélectionner"
1010, "Inverser la sélection"
1011, "Ajouter le fichier courant aux favoris"
1012, "Ajouter le dossier courant aux favoris"
1013, "Gérer les favoris"
1014, "Ouvrir la fenêtre de Configuration"
1015, "Ouvrir le dossier des préférences"
1016, "Conversion par lots"
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
2000, "- monochrome"
2001, "- 16 colors"
2002, "- 256 colors"
2003, "- RGB"
2004, "- RGBA"
2005, "- \?"
2006, " (packed)"
2007, " Ico file has 1 icon"
2008, " Ico file has "
2009, " icons"
2010, " The group has 1 icon"
2011, " The group has "
2012, "File :"
2013, " Groups number : "
2014, " (ICO format)"
2015, " (ICO format, file contains 1 icon / group"
2016, " (ICO format, file contains "
2017, " (PE format)"
2018, " (PE format, file contains 1 icon / group"
2019, " (PE format, file contains "
2020, " (NE format)"
2021, " (NE format, file contains 1 icon / 1 group"
2022, " (NE format, file contains "
2023, "groups"
2024, "group"
2025, "( No selection )"
2026, "( 1 selection )"
2027, " selections )"
2028, "BMP image :"
2029, "JPEG image :"
2030, "JPEG2000 image :"
2031, "PNG image :"
2032, "TGA image :"
2033, "TIFF image :"
2034, "size - "
2035, "resolution - "
2036, " dpi"
2037, "[image with transparency]"
2038, "[image without transparency]"
2039, "Color picker"
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
2000, "- monochrome"
2001, "- 16 couleurs"
2002, "- 256 couleurs"
2003, "- RVB"
2004, "- RVBA"
2005, "- \?"
2006, " (compressé)"
2007, " Le fichier ico contient 1 icône"
2008, " Le fichier ico contient "
2009, " icônes"
2010, " Le groupe contient 1 icône"
2011, " Le groupe contient "
2012, "Fichier :"
2013, " Nombre de groupes : "
2014, " (format ICO)"
2015, " (format ICO, le fichier contient 1 icône / 1 groupe"
2016, " (format ICO, le fichier contient "
2017, " (format PE)"
2018, " (format PE, le fichier contient 1 icône / 1 groupe"
2019, " (format PE, le fichier contient "
2020, " (format NE)"
2021, " (format NE, le fichier contient 1 icône / 1 groupe"
2022, " (format NE, le fichier contient "
2023, "groupes"
2024, "groupe"
2025, "( Pas de sélection )"
2026, "( 1 sélection )"
2027, " sélections )"
2028, "Image BMP :"
2029, "Image JPEG :"
2030, "Image JPEG2000 :"
2031, "Image PNG :"
2032, "Image TGA :"
2033, "Image TIFF :"
2034, "dimensions - "
2035, "résolution - "
2036, " ppp"
2037, "[image avec transparence]"
2038, "[image sans transparence]"
2039, "Sélecteur de couleur"
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
3000, "https://www.purebasic.fr/english/viewto ... 14&t=40350"
3001, "http://www.purebasic.com/"
3002, "http://www.purebasic.fr/blog/"
3003, "http://www.purebasic.fr/english/"
3004, "http://www.purebasic.fr/french/"
3005, "http://www.purebasic.fr/german/"
3006, "http://purebasic.developpez.com/"
3007, "http://www.purebasic.fr/english/viewtopic.php?t=31381"
3008, "http://www.libpng.org/pub/png/libpng.html"
3009, "http://www.zlib.net/"
3010, "About "
3011, "Author"
3012, "Version"
3013, "Platform"
3014, "Compilation"
3015, "3.00 (2009-2019)"
3016, "Supported Windows : From Vista up to Windows 10"
3017, "Architecture 32/64 bit"
3018, "Denis Labarre"
3019, "(Denis on PureBasic english forum)"
3020, "PureBasic Compiler version 5.71 beta 1"
3021, "PureIconManager on english forum"
3022, "Preferences for "
3023, "PureIconManager license :"
3024, "Free, unrestricted, without any guarantee. Use it at your own risk."
3025, "PureBasic :"
3026, "Visit the PureBasic web site :"
3027, "PureBasic team blog (english) :"
3028, "English forum (official forum) :"
3029, "French forum :"
3030, "German forum :"
3031, "PureBasic on developpez.com (french) :"
3032, "Purebasic third party libraries :"
3033, "PureIconManager uses opensource 'Nexus-library' from Srod"
3034, "Third party libraries"
3035, "PNG images are decoded and encoded using libpng.\nTake a look on web site to licensing details."
3036, "Compression and decompression operations are done using Zlib.\nTake a look on web site to licensing details."
3037, "Thanks :"
3038, "I would like to thank for their direct or indirect help :\nFred, Flype, Nico, blueznl, chi, Danilo, netmaestro, SFSxOI, sverson, Sparkie, Srod, ts-soft."
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
3000, "https://www.purebasic.fr/english/viewto ... 14&t=40350"
3001, "http://www.purebasic.com/french/"
3002, "http://www.purebasic.fr/blog/"
3003, "http://www.purebasic.fr/english/"
3004, "http://www.purebasic.fr/french/"
3005, "http://www.purebasic.fr/german/"
3006, "http://purebasic.developpez.com/"
3007, "http://www.purebasic.fr/english/viewtopic.php?t=31381"
3008, "http://www.libpng.org/pub/png/libpng.html"
3009, "http://www.zlib.net/"
3010, "A propos de "
3011, "Auteur"
3012, "Version"
3013, "Plateforme"
3014, "Compilation"
3015, "3.00 (2009-2019)"
3016, "Windows pris en charge : Vista à Windows 10"
3017, "Architecture 32/64 bit"
3018, "Denis Labarre"
3019, "(Denis sur le forum PureBasic anglais)"
3020, "Compilateur PureBasic version 5.71 beta 1"
3021, "PureIconManager sur le forum anglais"
3022, "Préférences de "
3023, "License PureIconManager :"
3024, "Gratuit, sans restriction, sans aucune garantie. L'utilisation est à vos risques pleins et entiers."
3025, "PureBasic :"
3026, "Visitez le site web PureBasic :"
3027, "Le blog de l\'équipe PureBasic (en anglais) :"
3028, "Le forum anglais (forum officiel) :"
3029, "Le forum français :"
3030, "Le forum allemand :"
3031, "PureBasic sur le site developpez.com :"
3032, "Librairie PureBasic tierces :"
3033, "PureIconManager utilise la librairie opensource 'Nexus' de Srod"
3034, "Librairie tierces :"
3035, "Les images PNG sont décodées et encodées avec la librairie libpng.\nVisitez le site web pour les détails de la license."
3036, "Les opérations de compression et de décompression sont réalisées avec la librairie Zlib.\nVisitez le site web pour les détails de la license."
3037, "Remerciements :"
3038, "Je voudrais remercier pour leur aide directe ou indirecte :\nFred, Flype, Nico, blueznl, chi, Danilo, netmaestro, SFSxOI, sverson, Sparkie, Srod, ts-soft."
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
4000, " Open "
4001, " Save As "
4002, " Remove "
4003, " Conversion "
4004, " Favorites "
4005, " Setting "
4006, " Credits "
4007, " About "
4008, " Help "
4009, " Exit "
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
4000, " Ouvrir "
4001, " Enregistrer sous "
4002, " Supprimer "
4003, " Conversion "
4004, " Favoris "
4005, " Configuration "
4006, " Crédits "
4007, " A propos de "
4008, " Aide "
4009, " Quitter "
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
5000, "Restore"
5001, "Move"
5002, "Size"
5003, "Minimize"
5004, "Maximize"
5005, "Close Alt+F4"
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
5000, "Restaurer"
5001, "Déplacer"
5002, "Taille"
5003, "Réduire"
5004, "Agrandir"
5005, "Fermer Alt+F4"
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
6000, "initialization ..."
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
6000, "Initialisation ..."
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
16000, "All icon formats"
16001, "CPL (*.cpl)"
16002, "DLL (*.dll)"
16003, "DRV (*.drv)"
16004, "EXE (*.exe)"
16005, "ICL (*.icl)"
16006, "ICO (*.ico)"
16007, "OCX (*.ocx)"
16008, "SCR (*.scr)"
16009, "All image files(*.bmp to Tiff)"
16010, "BMP (*.bmp)"
16011, "JPEG (*.jpg; *.jpeg)"
16012, "JPEG2000 (*.jp2; *.j2k; *.j2c)"
16013, "PNG (*.png)"
16014, "TARGA (*.tga; *.targa)"
16015, "TIFF (*.tif; *.tiff)"
16016, "All files formats (*.*)"
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
16000, "Tous formats d\'icônes"
16001, "CPL (*.cpl)"
16002, "DLL (*.dll)"
16003, "DRV (*.drv)"
16004, "EXE (*.exe)"
16005, "ICL (*.icl)"
16006, "ICO (*.ico)"
16007, "OCX (*.ocx)"
16008, "SCR (*.scr)"
16009, "Tous fichiers images(*.bmp à Tiff)"
16010, "BMP (*.bmp)"
16011, "JPEG (*.jpg; *.jpeg)"
16012, "JPEG2000 (*.jp2; *.j2k; *.j2c)"
16013, "PNG (*.png)"
16014, "TARGA (*.tga; *.targa)"
16015, "TIFF (*.tif; *.tiff)"
16016, "Tous fichiers (*.*)"
}

LANGUAGE LANG_NEUTRAL,SUBLANG_NEUTRAL
STRINGTABLE
{
16000, "*.cpl;*.dll;*.drv;*.exe;*.icl;*.ico;*.ocx;*.scr"
16001, "*.cpl"
16002, "*.dll"
16003, "*.drv"
16004, "*.exe"
16005, "*.icl"
16006, "*.ico"
16007, "*.ocx"
16008, "*.scr"
16009, "*.bmp;*.jpg;*.jpeg;*.jp2;*.j2k;*.j2C;*.png;*.tga;*.targa;*.tif;*.tiff"
16010, "*.bmp"
16011, "*.jpg;*.jpeg"
16012, "*.jp2;*.j2k;*.j2C"
16013, "*.png"
16014, "*.tga;*.targa"
16015, "*.tif;*.tiff"
16016, "*.*"
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
17000, "Close"
17001, "Cancel"
17002, "Ok"
17003, "Apply"
17004, "Open preferences folder"
17005, "Files"
17006, "Recent"
17007, "Files options"
17008, "Confirms file modification"
17009, "Confirms for Control Panel file (.cpl)"
17010, "Confirms for Dynamic Link Library file (.dll)"
17011, "Confirms for Device driver file (.drv)"
17012, "Confirms for Executable file (.exe)"
17013, "Confirms for Icons file (.ico)"
17014, "Confirms for Icons library file (.icl)"
17015, "Confirms for ActiveX file (.ocx)"
17016, "Confirms for Screensaver file (.scr)"
17017, "Limit the number of recent files to :"
17018, "Display created file"
17019, "Display created icon file (.ico)"
17020, "Display created icons libray file (.icl)"
17021, "Display created Dynamic Link Library file (.dll)"
17022, "Display the deleting file confirmation window"
17023, "Display setting"
17024, "PureIconManager window"
17025, "Start not with full screen"
17026, "Start with full screen"
17027, "Memorize size when closing"
17028, "Recent folders setting"
17029, "Move selected row up"
17030, "Date and time settings (files date/time)"
17031, "French format"
17032, "English format"
17033, "US format"
17034, "ISO standard 8601"
17035, "French format : JJ-MM-AAAA / 24 hour clock"
17036, "English format : MM-JJ-AAAA / 12 hour clock"
17037, "US format : JJ-MM-AAAA / 12 hour clock"
17038, "ISO standard 8601 format : AAAA-MM-JJ / 24 hour clock)"
17039, " KB"
17040, "Automatic favorites sorting"
17041, "Batch conversion"
17042, "Convert one or all files from a folder"
17043, "Convert all subfolders/files from a folder"
17044, "Source files format"
17045, "Target files format"
17046, "Select one or more source folders"
17047, "Folders"
17048, "Current folder :"
17049, "Selected folder (target folder) :"
17050, "Selected folder :"
17051, " Selected files : "
17052, " Files in the List : "
17053, "PE 32 bits NE 16 bits Icon"
17054, "Files to process :"
17055, "Choose files to convert"
17056, "Choose target folder"
17057, "Misc"
17058, "French"
17059, "English"
// 17060, "German"
// 17061, "Italian"
// 17062, "Portuguese"
// 17063, "Rumanian"
// 17064, "Russian"
// 17065, "Spanish"
// 17066, "Swedish"
// 17067, "Turkish"
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
17000, "Fermer"
17001, "Annuler"
17002, "Ok"
17003, "Appliquer"
17004, "Ouvrir le dossier des préférences"
17005, "Fichiers"
17006, "Récents"
17007, "Options des fichiers"
17008, "Confirmer la modification des fichiers"
17009, "Confirmer pour les fichiers Panneau de configuration (.cpl)"
17010, "Confirmer pour les fichiers Librairie (.dll)"
17011, "Confirmer pour les fichiers Pilote de Périphérique (.drv)"
17012, "Confirmer pour les fichiers Exécutables (.exe)"
17013, "Confirmer pour les fichiers icônes (.ico)"
17014, "Confirmer pour les fichiers Librairie d\'icônes (.icl)"
17015, "Confirmer pour les fichiers ActiveX (.ocx)"
17016, "Confirmer pour les fichiers Ecran de veille (.scr)"
17017, "Limiter le nombre de dossiers récents à :"
17018, "Afficher les fichiers créés"
17019, "Afficher les fichiers icônes créés (.ico)"
17020, "Afficher les fichiers librairie d\'icônes créés (.icl)"
17021, "Afficher les fichiers librairie créés (.dll)"
17022, "Afficher la fenêtre de suppression des fichiers"
17023, "Options d'affichage"
17024, "Fenêtre PureIconManager"
17025, "Démarrer non en plein écran"
17026, "Démarrer en plein écran"
17027, "Mémorise la taille à la fermeture"
17028, "Options des dossiers récents"
17029, "Déplace la ligne sélectionnée vers le haut"
17030, "Paramétrage de la date et de l'heure (date/heure fichiers)"
17031, "Format francais"
17032, "Format anglais"
17033, "Format américain"
17034, "Format ISO 8601"
17035, "Format francais : JJ-MM-AAAA / format 24 heures"
17036, "Format anglais : JJ-MM-AAAA / format 12 heures"
17037, "Format américain : MM-JJ-AAAA / format 12 heures"
17038, "Format ISO 8601 : AAAA-MM-JJ / format 24 heures)"
17039, " Ko"
17040, "Tri automatique des favoris"
17041, "Conversion par lots"
17042, "Convertir un ou tous les fichiers d'un dossier"
17043, "Convertir tous les sous-dossiers/fichiers d'un dossier"
17044, "Format des fichiers sources"
17045, "Format des fichiers cibles"
17046, "Rechercher un ou plusieurs dossiers"
17047, "Dossiers"
17048, "Dossier courant :"
17049, "Dossier sélectionné (dossier destination) :"
17050, "Dossier sélectionné :"
17051, " Fichiers sélectionnés : "
17052, " Fichiers dans la liste : "
17053, "PE 32 bits NE 16 bits icône"
17054, "Fichiers à traiter :"
17055, "Choisissez les fichiers à convertir"
17056, "Choisissez le dossier cible"
17057, "Divers"
17058, "Français"
17059, "Anglais"
// 17060, "Allemand"
// 17061, "Italien"
// 17062, "Portugais"
// 17063, "Roumain"
// 17064, "Russe"
// 17065, "Espagnol"
// 17066, "Suédois"
// 17067, "Turc"
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
18000, "Do you want to remove selected icons from file \?"
18001, "Do you want to replace selected file \?"
18002, "Ico file will be modified (no backup avalaible)"
18003, "Don\'t ask again for ico files"
18004, "Icl file will be modified (no backup avalaible)"
18005, "Don\'t ask again for icl files"
18006, "Dll file will be modified (no backup avalaible)"
18007, "Don\'t ask again for dll files"
18008, "Don\'t ask again for drv files"
18009, "Drv file will be modified (no backup avalaible)"
18010, "Exe file will be modified (no backup avalaible)"
18011, "Don\'t ask again for exe files"
18012, "Ocx file will be modified (no backup avalaible)"
18013, "Don\'t ask again for ocx files"
18014, "Cpl file will be modified (no backup avalaible)"
18015, "Don\'t ask again for cpl files"
18016, "Scr file will be modified (no backup avalaible)"
18017, "Don\'t ask again for scr files"
18018, "Do you want to delete this file \?\n(no backup avalaible)"
18019, "Don\'t ask it again "
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
18000, "Voulez-vous supprimer les icônes sélectionnées du fichier \?"
18001, "Voulez-vous remplacer le fichier sélectionné \?"
18002, "Le fichier ico sera modifié (pas de récupération possible)"
18003, "Ne plus demander pour les fichiers ico"
18004, "Le fichier icl sera modifié (pas de récupération possible)"
18005, "Ne plus demander pour les fichiers icl"
18006, "Le fichier dll sera modifié (pas de récupération possible)"
18007, "Ne plus demander pour les fichiers dll"
18008, "Ne plus demander pour les fichiers drv"
18009, "Le fichier drv sera modifié (pas de récupération possible)"
18010, "Le fichier exe sera modifié (pas de récupération possible)"
18011, "Ne plus demander pour les fichiers exe"
18012, "Le fichier ocx sera modifié (pas de récupération possible)"
18013, "Ne plus demander pour les fichiers ocx"
18014, "Le fichier cpl sera modifié (pas de récupération possible)"
18015, "Ne plus demander pour les fichiers cpl"
18016, "Le fichier scr sera modifié (pas de récupération possible)"
18017, "Ne plus demander pour les fichiers scr"
18018, "Voulez-vous supprimer ce fichier \?\n(pas de récupération possible)"
18019, "Ne plus demander"
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
19000, "The file is a system file and is a read-only file :"
19001, "The file is a system file :"
19002, "The file is a read-only file :"
19003, "Overwrite file"
19004, "Show concise"
19005, "More information"
19006, "The file is a system file protected against writing operations.\nModification of such files can involve instabilities of the system.\nPureIconManager can allow modifications.\nAfter those, the file will be a system file in read-only mode."
19007, "The file is a system file.\nModification of such files can involve instabilities of the system.\nPureIconManager can allow modifications.\nAfter those, After those, the file will be a system file."
19008, "The file is protected against writing operations.\nPureIconManager can allow modifications.\nAfter those, the file will be in read-only mode."
19009, "Do you want to overwrite it \?"
19010, "About 16-bit NE file format other than .icl :"
19011, "Only visualization and recording in another format is allowed."
19012, "It isn\'t possible to remove or add icons."
19013, "The file is protected against writing operations.\nPureIconManager can allow modifications to delete it."
19014, "The file is a system file.\nDeleting such files can involve instabilities of the system.\nPureIconManager can allow modifications to delete it."
19015, "The file is a system file protected against writing operations.\nDeleting such files can involve instabilities of the system.\nPureIconManager can allow modifications to delete it."
19016, "Delete"
19017, "Do you want to delete it \?"
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
19000, "Le fichier est un fichier système en lecture seule :"
19001, "Le fichier est un fichier système :"
19002, "Le fichier est en lecture seule :"
19003, "Ecraser le fichier"
19004, "Masquer les informations"
19005, "Informations complémentaires"
19006, "Le fichier est un fichier système protégé en écriture.\nLa modification de ces fichiers peut entraîner des instabilités du système.\nPureIconManager peut modifier la protection pour autoriser les modifications.\nAprès celles-ci, le fichier sera un fichier système en lecture seule."
19007, "Le fichier est un fichier système.\nLa modification de ces fichiers peut entraîner des instabilités du système.\nPureIconManager peut modifier la protection pour autoriser les modifications.\nAprès celles-ci, le fichier sera un fichier système."
19008, "Le fichier est un fichier protégé en écriture.\nPureIconManager peut modifier la protection pour autoriser les modifications.\nAprès celles-ci, le fichier sera en lecture seule."
19009, "Voulez-vous le modifier \?"
19010, "A propos des fichiers au format NE 16 bits autres que .icl:"
19011, "Seule la visualisation et l\'enregistrement dans un autre format sont autorisés."
19012, "La suppression ou l\'ajout d\'icônes est impossible."
19013, "Le fichier est un fichier protégé en écriture.\nPureIconManager peut modifier la protection pour le supprimer."
19014, "Le fichier est un fichier système.\nLa suppression de ces fichiers peut entraîner des instabilités du système.\nPureIconManager peut modifier la protection pour le supprimer."
19015, "Le fichier est un fichier système protégé en écriture.\nLa suppression de ces fichiers peut entraîner des instabilités du système.\nPureIconManager peut modifier la protection pour le supprimer."
19016, "Supprimer"
19017, "Voulez-vous le supprimer \?"
}

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_UK
STRINGTABLE
{
20000, "Names"
20001, "Attributes"
20002, "Size"
20003, "Type"
20004, "Date modified"
20005, "Details"
20006, "Small Icons"
20007, "Tiles"
20008, "Error "
20009, "Sub Error "
20010, "Files formats "
20011, " Recent "
}

LANGUAGE LANG_FRENCH,SUBLANG_FRENCH
STRINGTABLE
{
20000, "Noms"
20001, "Attributs"
20002, "Taille"
20003, "Type"
20004, "Date de modification"
20005, "Détails"
20006, "Petites icônes"
20007, "Mosaïques"
20008, "Erreur "
20009, "Erreur secondaire "
20010, "Formats des fichiers "
20011, " Récents "
}

LANGUAGE LANG_NEUTRAL,SUBLANG_NEUTRAL
STRINGTABLE
{
30000, "PureIconManager version 3.00"
30001, "icl (*.icl)|*.icl"
30002, "ico (*.ico)|*.ico"
30003, "dll (*.dll)|*.dll"
30004, "(32 bit)"
30005, "(64 bit)"
30006, "Explorer"
30007, "ARIAL"
30008, "COURRIER NEW"
30009, "GEORGIA"
30010, "VERDANA"
30011, "Copy displayed path (recent) to clipboard"
30012, "Paste clipboard to recent path list"
30013, "Customize files view"
30014, "Change language"
30015, "Copier le chemin affiché (récents) dans le presse-papiers"
30016, "Coller le presse-papiers dans la liste des chemins affichés"
30017, "Changer le mode d\'affichage des fichiers"
30018, "Changer la langue"

// cryptage nom fichier PureIconManager_Conversion.txt
30019, "TQByAG8AYgBGAGAAbABrAEoAXgBrAF4AZABiAG8AXABAAGwAawBzAGIAbwBwAGYAbABrACsAcQB1AHEA"
30020, "TQByAG8AYgBGAGAAbABrAEoAXgBrAF4AZABiAG8AXABJAGYAcABxAGIAXABAAGwAawBzAGIAbwBwAGYAbABrACsAcQB1AHEA"
}
A+
Denis
Denis
Enthusiast
Enthusiast
Posts: 778
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: Reduce memory for language translation strings

Post by Denis »

Here is a little example with resources : Windows Only

and the rc file you have to link with pb example (with menu compiler/compiler options/Ressources)
The format of rc file is a text field and could be ANSI, UTF8 or UTF Bom format (use Notpad + if you want)

The code detects the default user language (value returned in English) and compares it to French (my language) and if it is French, everything is displayed in French, otherwise the interface is in English.

There is a button to invert the language.

Only the used strings are loaded, there are also some error strings that will be used if needed.

The procedure Get__USER_DEFAULT_Language() :
If the OS is < to #PB_OS_Windows_Vista, I don't know if the result will be correct.

Code: Select all

EnableExplicit
;- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

;-            Enumerations/Constants

#PB_Window_Main = 1

#Menu = 1

Enumeration MenuItem 1
      #MenuTitle_File_MenuItem_Save
      #MenuTitle_File_MenuItem_Save_AS
      #MenuTitle_File_MenuItem_Close
      #MenuTitle_Edition_MenuItem_1
      #MenuTitle_Edition_MenuItem_2
      #MenuTitle_Edition_MenuItem_3
EndEnumeration

Enumeration Button 1
      #Bt_Change_lang
      #Bt_Close
EndEnumeration


#LANG_NEUTRAL = $00
#LANG_GERMAN  = $07
#LANG_ENGLISH = $09
#LANG_FRENCH  = $0c
#LANG_ITALIAN = $10

#SUBLANG_NEUTRAL = $00

;-
; all PB id Resource String used by PB to load string
#RC_String_WindowTitle             = 100

; error string
#RC_StringError_RequesterTitle           = 200
#RC_StringError_UnableToOpenMainWindow   = 201
#RC_StringError_UnableToCreateMainMenu   = 202

#RC_String_MenuTitle_File = 300
#RC_String_MenuItem_1     = 301
#RC_String_MenuItem_2     = 302
#RC_String_MenuItem_3     = 303

#RC_String_MenuTitle_Edition       = 320
#RC_String_MenuTitle_Edition_Copy  = 321
#RC_String_MenuTitle_Edition_Cut   = 322
#RC_String_MenuTitle_Edition_Paste = 323

#RC_String_ButtonGadget_Change_language = 400
#RC_String_ButtonGadget_CloseAPP        = 401


#Return_Error    = #False
#Return_No_Error = #True

#LOCALE_NAME_USER_DEFAULT         = #Null
#LOCALE_NAME_INVARIANT            = ""
#LOCALE_NAME_SYSTEM_DEFAULT       = "!x-sys-default-locale"
#LOCALE_USER_DEFAULT              = $0400
#LOCALE_SNATIVELANGUAGENAME       = #LOCALE_SNATIVELANGNAME
#LOCALE_SENGLISHLANGUAGENAME      = $00001001

;- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
;- .            Microsoft Macros

Macro _MakeIntResource(Value)
      Value & $FFFF
EndMacro
Macro _MakeLangID(primary, sublang)
      (((sublang)<<10) | (primary))
EndMacro

;- .            Macros
Macro _LCTYPE
      ; https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
      ; typedef DWORD LCTYPE
      l
EndMacro
Macro _Int
      ; https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
      ;// A 32-bit signed integer
      l
EndMacro
Macro _cchData
      ; https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
      ;// A 32-bit signed integer
      _Int
EndMacro
;- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Import "Kernel32.lib"
      ;       Syntax C++
      ; int GetLocaleInfoEx(
      ;   [in, optional]  LPCWSTR lpLocaleName,
      ;   [in]            LCTYPE  LCType,
      ;   [out, optional] LPWSTR  lpLCData,
      ;   [in]            int     cchData
      ;   );
      GetLocaleInfoEx.i(*lpLocaleName, LCType._LCTYPE, *lpLCData, cchData.i)
EndImport
;- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
;-
Procedure.i GetModuleHandle()
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// FONCTION/FUNCTION : GetModuleHandle()
      ;// BUT/PURPOSE :  GetModuleHandle retourne le handle vers le fichier utilisé pour créer le processus appelant (fichier .exe).
      ;//                GetModuleHandle returns a handle to the file used to create the calling process (.exe file).
      ;// PARAMETRES/PARAMETERS : Aucun/None
      ;// RETOURNE/RETURNS : retourne le handle/returns a handle en cas de succès/if successful
      ;//                    retourne  #Return_Error/returns #Return_Error    en cas d'erreur/in case of failure
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// Mémorise l'instance
      Static hInstance
      
      Select hInstance
            Case 0
                  hInstance = GetModuleHandle_(0)
      EndSelect
      
      ProcedureReturn hInstance
EndProcedure

Procedure.s Load_ResourceString(String_id, langId)
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// FONCTION/FUNCTION : Load_ResourceString()
      ;// BUT/PURPOSE : Charger depuis la ressource (exe en cours) la chaîne identifiée par String_id pour la langue langId
      ;// PARAMETRES/PARAMETERS : String_id - Identifiant de la chaîne à retrouver depuis la StringTable
      ;//                                     Id of the string to find from the StringTable
      ;//                         langId - langue au format MS (Macro _MakeLangID)
      ;// RETOURNE/RETURNS : La chaîne correspondante ou une chaîne vide si erreur
      ;//                    Errors::#Return_No_Error en cas de succès
      ;//                    #Return_Error en cas d'erreur
      ;//                    Errors::#Return_No_Error if successful
      ;//                    #Return_Error in case of failure
      ;//REMARQUE/NOTE :     http://blogs.msdn.com/oldnewthing/archive/2004/01/30/65013.aspx
      ;//                    http://msdn.microsoft.com/en-us/library/aa381050(VS.85).aspx
      ;//                    http://msdn.microsoft.com/en-us/library/aa381043(VS.85).aspx
      ;//                    http://support.microsoft.com/kb/Q20011/en-us/   ; length de la stringTable
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// Dernière adresse mémoire valide du fichier/Last valid memory address of file
      Protected *lastMemoryAdress
      ;// Pointeur sur la ressource spécifiée en mémoire (chaîne)/Pointer to the specified resource in memory (string)
      Protected *lockResource
      ;// Détermine l'emplacement de la ressource (chaîne)/Determines the location of the resource (string)
      Protected findResource
      ;// compteur de boucle/loop counter
      Protected i
      ;// Handle utilisé pour obtenir un pointeur sur le premier octet de la ressource spécifiée en mémoire (chaîne)
      ;// Handle used to obtain a pointer to the first byte of the specified resource in memory (string)
      Protected loadResource_RT_STRING
      ;// Taille des données de la ressource RT_String pour la langue choisie/size of RT_String resource data for the chosen language
      Protected sizeOfResource
      ;// Longueur de la chaîne lue/length of readen string
      Protected stringLength
      ;// chaîne à retourner/String to return
      Protected stringToReturn$ = #Empty$
      
      
      findResource = FindResourceEx_(GetModuleHandle(), #RT_STRING, _MakeIntResource(((string_id)>> 4) + 1), langId)
      If findResource  = #Return_Error
            ProcedureReturn stringToReturn$
      EndIf
      
      loadResource_RT_STRING = LoadResource_(GetModuleHandle(), findResource)
      If  loadResource_RT_STRING = #Return_Error
            ProcedureReturn stringToReturn$
      EndIf
      
      *lockResource = LockResource_(loadResource_RT_STRING)
      If *lockResource = #Return_Error
            ProcedureReturn stringToReturn$
      EndIf
      
      sizeOfResource = SizeofResource_(GetModuleHandle(), findResource)
      If sizeOfResource = #Return_Error
            ProcedureReturn stringToReturn$
      EndIf
      
      *lastMemoryAdress = *lockResource + sizeOfResource - 1
      ;// Boucle sur les 16 chaînes du bloc pour trouver celle identifiée par String_id
      ;// Loop on the 16 strings of the block to find the one identified by String_id
      For i = 0 To 15
            If *lockResource > *lastMemoryAdress
                  Break
            EndIf
            ;// longueur de la chaîne sans le 0 terminal/String length without null terminal character
            stringLength = PeekU(*LockResource)
            ;// positionne le pointeur sur la longueur de la 1ere chaîne du bloc pour la langue choisie
            ;// positions the pointer on the length of the block first string for the chosen language
            *lockResource + SizeOf(Unicode)
            If *lockResource > *lastMemoryAdress
                  Break
            EndIf
            If i = String_id % 16
                  stringToReturn$ = PeekS(*LockResource, stringLength, #PB_Unicode)
                  Break
            EndIf
            *lockResource + (stringLength * SizeOf(Unicode))
      Next i
      
      ProcedureReturn stringToReturn$
EndProcedure
;-
Procedure.s Get__USER_DEFAULT_Language()
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// FONCTION/FUNCTION : Get__USER_DEFAULT_Language()
      ;// BUT/PURPOSE : Retourne une chaîne correspondant à la langue de l'utilisateur local
      ;//               J'ai choisi que le nom de la langue soit en Anglais, c'est-à-dire que la fonction retourne
      ;//               le mot 'French' au lieu du mot 'Français'
      ;//               Returns a string corresponding to the language of the local user
      ;//               I've chosen to have the language name in English, i.e. the function returns the word 'French'
      ;//               instead of the french word 'Français'
      ;//               Utilisation API GetLocaleInfoEx
      ;//               https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getlocaleinfoex
      ;// PARAMETRES/PARAMETERS : Aucun/None
      ;//
      ;// RETOURNE/RETURNS : La langue (en anglais) pour l'utilisateur local par défaut   en cas de succès
      ;//                    LOCALE NAME USER DEFAULT language  if successful
      ;//                    Stoppe le programme/Stops the program   en cas d'erreur/in case of failure
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      Protected *lpLCData
      Protected cchData._int
      Protected lang.s = #Empty$
      
      ; int GetLocaleInfoEx(
      ;   [in, optional]  LPCWSTR lpLocaleName,
      ;   [in]            LCTYPE  LCType,
      ;   [out, optional] LPWSTR  lpLCData,
      ;   [in]            int     cchData
      ;   );
      
      
      ; détecte la version de window
      Select  OSVersion()
                  
            Case #PB_OS_Windows_Vista To #PB_OS_Windows_Future
                  ; use GetLocaleInfoEx instead of GetLocaleInfo
                  
                  *lpLCData = 0
                  cchData = GetLocaleInfoEx(#LOCALE_NAME_USER_DEFAULT, #LOCALE_SENGLISHLANGUAGENAME, *lpLCData, 0)
                  
                  If cchData > 0
                        ; *lpLCData is a pointer to a null-terminated string of 16-bit Unicode characters.
                        *lpLCData = AllocateMemory(cchData * SizeOf(Unicode))
                        If *lpLCData = #Null
                              ;Erreur/Error
                              MessageRequester("Erreur/Error","Erreur AllocateMemory/AllocateMemory Error", #PB_MessageRequester_Error)
                              End
                        EndIf
                        
                  Else
                        ;Erreur/Error
                        MessageRequester("Erreur/Error","Impossible de déterminer la langue par défaut"+ #CRLF$ + #CRLF$ +
                        "Unable to get the default language, #PB_MessageRequester_Error", #PB_MessageRequester_Error)
                        End
                  EndIf
                  
                  If GetLocaleInfoEx(#LOCALE_NAME_USER_DEFAULT, #LOCALE_SENGLISHLANGUAGENAME, *lpLCData, cchData)
                        lang = PeekS(*lpLCData)
                        FreeMemory(*lpLCData)
                        ProcedureReturn lang
                  Else
                        ;Erreur/Error
                        MessageRequester("Erreur/Error","Impossible de déterminer la langue par défaut"+ #CRLF$ + #CRLF$ +
                        "Unable to get the default language, #PB_MessageRequester_Error", #PB_MessageRequester_Error)
                        FreeMemory(*lpLCData)
                        End
                  EndIf
                  
                  
                  
            Default   ; ( < #PB_OS_Windows_Vista)
                  ; use GetLocaleInfo instead of GetLocaleInfoEx
                  ; MS: The application should call GetLocaleInfoEx function in preference To GetLocaleInfo If designed To run only on Windows Vista And later. But it works
                  
                  ; int GetLocaleInfoW(
                  ;   [in]            LCID   Locale,
                  ;   [in]            LCTYPE LCType,
                  ;   [out, optional] LPWSTR lpLCData,
                  ;   [in]            int    cchData
                  ;   );
                  
                  *lpLCData = 0
                  cchData = GetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_SENGLISHLANGUAGENAME, *lpLCData, 0)
                  
                  If cchData > 0
                        ; *lpLCData is a pointer to a null-terminated string of 16-bit Unicode characters.
                        *lpLCData = AllocateMemory(cchData * SizeOf(Unicode))
                        If *lpLCData = #Null
                              ;Erreur/Error
                              MessageRequester("Erreur/Error","Erreur AllocateMemory/AllocateMemory Error", #PB_MessageRequester_Error)
                              End
                        EndIf
                        
                  Else
                        ;Erreur/Error
                        MessageRequester("Erreur/Error","Impossible de déterminer la langue par défaut"+ #CRLF$ + #CRLF$ +
                        "Unable to get the default language, #PB_MessageRequester_Error", #PB_MessageRequester_Error)
                        End
                  EndIf
                  
                  ; LOCALE_SENGLISHLANGUAGENAME is defined starting Win 7, with previous OS
                  If GetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_SENGLISHLANGUAGENAME, *lpLCData, cchData)
                        lang = PeekS(*lpLCData)
                        FreeMemory(*lpLCData)
                        ProcedureReturn lang
                  Else
                        ;Erreur/Error
                        MessageRequester("Erreur/Error","Impossible de déterminer la langue par défaut"+ #CRLF$ + #CRLF$ +
                        "Unable to get the default language, #PB_MessageRequester_Error", #PB_MessageRequester_Error)
                        FreeMemory(*lpLCData)
                        End
                  EndIf
                  
      EndSelect
EndProcedure

Procedure UpDateLanguage(*CurrentlangID.integer)
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// FONCTION/FUNCTION : UpDateLanguage()
      ;// BUT/PURPOSE : Mise à jour des éléments en fonctions de la langue passée en paramètre
      ;//               Dans cet exemple, il n'y a que 2 langues utilisées
      ;//               Update items according to the language passed in parameter
      ;//               In this example, there are only 2 languages used
      ;// PARAMETRES/PARAMETERS : *CurrentlangID.integer - adresse de la variable contenant l'iD de la langue
      ;//                         *CurrentlangID.integer - address of the variable wich contain the language iD
      ;// RETOURNE/RETURNS : Rien/Nothing
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// texte des Menus/Menus text, buttons etc.
      Protected Text.s = #Empty$
      
      ; UpDate Window Title
      Text = Load_ResourceString(#RC_String_WindowTitle, *CurrentlangID\i)
      SetWindowTitle(#PB_Window_Main, Text)
      
      ; UpDate Menu
      Text = Load_ResourceString(#RC_String_MenuTitle_File, *CurrentlangID\i)
      SetMenuTitleText(#Menu, 0, Text)
      Text = Load_ResourceString(#RC_String_MenuItem_1, *CurrentlangID\i)
      SetMenuItemText(#Menu, #MenuTitle_File_MenuItem_Save, Text)
      Text = Load_ResourceString(#RC_String_MenuItem_2, *CurrentlangID\i)
      SetMenuItemText(#Menu, #MenuTitle_File_MenuItem_Save_AS, Text)
      Text = Load_ResourceString(#RC_String_MenuItem_3, *CurrentlangID\i)
      SetMenuItemText(#Menu, #MenuTitle_File_MenuItem_Close, Text)
      
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition, *CurrentlangID\i)
      SetMenuTitleText(#Menu, 1, Text)
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition_Copy, *CurrentlangID\i)
      SetMenuItemText(#Menu, #MenuTitle_Edition_MenuItem_1, Text)
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition_Cut, *CurrentlangID\i)
      SetMenuItemText(#Menu, #MenuTitle_Edition_MenuItem_2, Text)
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition_Paste, *CurrentlangID\i)
      SetMenuItemText(#Menu, #MenuTitle_Edition_MenuItem_3, Text)
      
      ; UpDate Buttons
      Text = Load_ResourceString(#RC_String_ButtonGadget_Change_language, *CurrentlangID\i)
      SetGadgetText(#Bt_Change_lang, Text)
      Text = Load_ResourceString(#RC_String_ButtonGadget_CloseAPP, *CurrentlangID\i)
      SetGadgetText(#Bt_Close, Text)
      
EndProcedure

Procedure Switch_language(*CurrentlangID.integer)
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// FONCTION/FUNCTION : Switch_language()
      ;// BUT/PURPOSE : change la langue utilisée et met à jour tous les éléments affichés
      ;//               changes used language and updates all displayed items
      ;// PARAMETRES/PARAMETERS : *CurrentlangID.integer - adresse de la variable contenant l'iD de la langue
      ;//                         *CurrentlangID.integer - address of the variable wich contain the language iD
      ;// RETOURNE/RETURNS : Rien/Nothing
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      
      Select *CurrentlangID\i
                  
            Case  _MakeLangID(#LANG_FRENCH, #SUBLANG_NEUTRAL)
                  *CurrentlangID\i = _MakeLangID(#LANG_ENGLISH, #SUBLANG_NEUTRAL)
                  
            Default
                  *CurrentlangID\i = _MakeLangID(#LANG_FRENCH, #SUBLANG_NEUTRAL)
                  
      EndSelect
      UpDateLanguage(*CurrentlangID)
EndProcedure

Procedure APP()
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// FONCTION/FUNCTION : APP()
      ;// BUT/PURPOSE : Programme principal qui crée l'interface utilisateur/Main program that creates user interface
      ;// RETOURNE/RETURNS : Rien/Nothing
      ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      ;// static mainwindow ID
      ;// window Title
      Protected window_Title.s = #Empty$
      ;// message d'erreur/Error message
      Protected Error_msg.s = #Empty$
      ;// texte des Menus/Menus text, buttons etc.
      Protected Text.s = #Empty$
      ;// texte des messages d'erreur
      Protected Error_msg_Title.s = #Empty$
      ;// chaîne correspondant à la langue de l'utilisateur local
      Protected USER_DEFAULT_Language.s = #Empty$
      ;// valeur identifiant l'ID de la langue, format _MakeLangID(#LANG_FRENCH, #SUBLANG_NEUTRAL) ou
      ;// _MakeLangID(#LANG_ENGLISH, #SUBLANG_NEUTRAL)
      Protected langID
      
      
      USER_DEFAULT_Language = Get__USER_DEFAULT_Language()
      
      
      Select LCase(USER_DEFAULT_Language)
            Case "french"
                  ; use French
                  langID = _MakeLangID(#LANG_FRENCH, #SUBLANG_NEUTRAL)
                  window_Title = Load_ResourceString(#RC_String_WindowTitle, langId)
                  
            Default
                  ; use english
                  langID = _MakeLangID(#LANG_ENGLISH, #SUBLANG_NEUTRAL)
                  window_Title = Load_ResourceString(#RC_String_WindowTitle, langId)
      EndSelect
      
      If Not(OpenWindow(#PB_Window_Main, 0, 0, 800, 800/1.618, window_Title, #PB_Window_SystemMenu|#PB_Window_ScreenCentered))
            Error_msg_Title = Load_ResourceString(#RC_StringError_RequesterTitle, langId)
            Error_msg = Load_ResourceString(#RC_StringError_UnableToOpenMainWindow, langId)
            MessageRequester(Error_msg_Title, Error_msg, #PB_MessageRequester_Error)
            End
      EndIf
      
      
      If CreateMenu(#Menu, WindowID(#PB_Window_Main)) = 0
            Error_msg_Title = Load_ResourceString(#RC_StringError_RequesterTitle, langId)
            Error_msg = Load_ResourceString(#RC_StringError_UnableToCreateMainMenu, langId)
            MessageRequester(Error_msg_Title, Error_msg, #PB_MessageRequester_Error)
            End
      EndIf
      
      
      Text = Load_ResourceString(#RC_String_MenuTitle_File, langId)
      MenuTitle(Text)
      Text = Load_ResourceString(#RC_String_MenuItem_1, langId)
      MenuItem(#MenuTitle_File_MenuItem_Save, Text)
      Text = Load_ResourceString(#RC_String_MenuItem_2, langId)
      MenuItem(#MenuTitle_File_MenuItem_Save_AS, Text)
      Text = Load_ResourceString(#RC_String_MenuItem_3, langId)
      MenuItem(#MenuTitle_File_MenuItem_Close, Text)
      
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition, langId)
      MenuTitle(Text)
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition_Copy, langId)
      MenuItem(#MenuTitle_Edition_MenuItem_1, Text)
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition_Cut, langId)
      MenuItem(#MenuTitle_Edition_MenuItem_2, Text)
      Text = Load_ResourceString(#RC_String_MenuTitle_Edition_Paste, langId)
      MenuItem(#MenuTitle_Edition_MenuItem_3, Text)
      
      Text = Load_ResourceString(#RC_String_ButtonGadget_Change_language, langId)
      ButtonGadget(#Bt_Change_lang, 50, 100, 200, 40, Text)
      Text = Load_ResourceString(#RC_String_ButtonGadget_CloseAPP, langId)
      ButtonGadget(#Bt_Close, GadgetX(#Bt_Change_lang)+GadgetWidth(#Bt_Change_lang)+50,
      100, GadgetWidth(#Bt_Change_lang), 40, Text)
      
      Repeat
            Select WaitWindowEvent()
                  Case #PB_Event_CloseWindow
                        CloseWindow(#PB_Window_Main)
                        Break
                        
                  Case #PB_Event_Gadget
                        
                        Select EventGadget()
                              Case #Bt_Close
                                    PostEvent(#PB_Event_CloseWindow)
                                    
                                    
                              Case #Bt_Change_lang
                                    Switch_language(@langId)
                                    
                        EndSelect
            EndSelect
            
      ForEver
      
EndProcedure

; run program
APP()
rc file
#define LANG_NEUTRAL 0
#define SUBLANG_NEUTRAL 0

#define LANG_FRENCH 12
#define SUBLANG_FRENCH 1

#define LANG_ENGLISH 9
#define SUBLANG_ENGLISH_UK 2

#define LOCALE_USER_DEFAULT 1024


LANGUAGE LANG_ENGLISH,SUBLANG_NEUTRAL
STRINGTABLE
{
// Main Window
100, "Main Window Resource Example"

// error msg
200, "Fatal error"
201, "Unable to open main window"
202, "Unable to create main menu"

// menus text
// Menu Title
300, "File"
301, "Save"
302, "Save As"
303, "Close"

// Menu Title
320, "Edition"
321, "Copy"
322, "Cut"
323, "Paste"

// Buttons
400, "Change language"
401, "Close APP"
}

LANGUAGE LANG_FRENCH,SUBLANG_NEUTRAL
STRINGTABLE
{
// fenêtre principale
100, "Exemple de ressource de la fenêtre principale"

// error msg
200, "Erreur fatale"
201, "Impossible d'ouvrir la fenêtre principale"
202, "Impossible de créer le menu principal"

// texte des menus
// Menu Title
300, "Fichier"
301, "Enregistrer"
302, "Enregistrer sous"
303, "Fermer"

// Menu Title
320, "Edition"
321, "Copier"
322, "Couper"
323, "Coller"

// Boutons
400, "Changer la langue"
401, "Fermer l'application"
}
A+
Denis
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: Reduce memory for language translation strings

Post by AZJIO »

NicTheQuick wrote: Fri Jun 17, 2022 3:51 pm And inside your binary you could use pointers to a datasection or something like this:
I rethought your example. Initially it still has an array of strings. When I tried to understand how to get an array of pointers, I realized that the standard functions do not allow me to do this, and I redid your example.

Code: Select all

EnableExplicit

DataSection
	text:
		Data.s "Set", "domain", "Hide"
	text_end:
EndDataSection

Procedure fillArrayFromDatasection(*start, *end, Array Lng.i(1), initialCapacity.i = 10)
	Protected capacity = initialCapacity
	Protected pos.i = 0, tmp.s
	
	If capacity <= 0
		capacity = 1
	EndIf
	
	ReDim Lng(capacity - 1)
	
	While *start < *end
		If pos = capacity
			capacity * 2
			ReDim Lng(capacity - 1)
		EndIf
		Lng(pos) = *start
		*start + SizeOf(Character) * (Len(PeekS(*start)) + 1)
; 		Debug *start
		pos + 1
	Wend
	
	If pos > 0
		ReDim Lng(pos)
	EndIf
	
EndProcedure

Dim a.i(0)

fillArrayFromDatasection(?text, ?text_end, a())
; Debug "———————"
Define i.i
For i = 0 To ArraySize(a()) - 1
	Debug a(i)
	Debug PeekS(a(i))
; 	MessageRequester("", PeekS(a(i)))
Next

It would be nice to have access to the internal pointer table.

Code: Select all

Restore DataText2
Read.s tmp$
MessageRequester("", tmp$)
Read.s tmp$
MessageRequester("", tmp$)
; MessageRequester("", Read2(DataText, 3)) ; I would like this option

DataSection
    DataText:
    Data.s "Привет", "Пока", "Снова", "тест"
    DataText2:
    Data.s "Привет2"
    Data.s "Пока2"
    Data.s "Снова2"
    Data.s "тест2"
EndDataSection

Another improvement

Code: Select all

EnableExplicit

Macro Lang(i)
	PeekS(Lng(i))
EndMacro 

DataSection
	Ru:
	Data.s "Привет", "Пока", "Снова", "Тест"
	Ru_end:
	En:
	Data.s "Hello", "Bye", "Again", "Test"
	En_end:
EndDataSection

Procedure fillArrayFromDatasection(*start, *end, Array Lng(1), capacity = 10)
	Protected i, *c.Character
	
	If capacity <= 0
		capacity = 1
	EndIf
	ReDim Lng(capacity)
	
	; 	Debug ArraySize(Lng())
	
	While *start < *end
		Lng(i) = *start
		*c = *start
		While *c\c; And *c < *end
			*c + SizeOf(Character)
		Wend
		*start = *c + SizeOf(Character)
		i + 1
		If i > capacity
			capacity * 2
			ReDim Lng(capacity)
		EndIf
	Wend
	
	If i > 0
		ReDim Lng(i)
	EndIf
	
EndProcedure

Global Dim Lng(0)
Global UserIntLang, *Lang
If OpenLibrary(0, "kernel32.dll")
	*Lang = GetFunction(0, "GetUserDefaultUILanguage")
	If *Lang
		UserIntLang = CallFunctionFast(*Lang)
	EndIf
	CloseLibrary(0)
EndIf
If UserIntLang = 1049
	fillArrayFromDatasection(?Ru, ?Ru_end, Lng())
Else
	fillArrayFromDatasection(?En, ?En_end, Lng())
EndIf
; Debug "———————"
Define i.i
For i = 0 To ArraySize(Lng()) - 1
; 	Debug Lng(i)
	Debug Lang(i)
	; 	MessageRequester("", Lang(i))
Next
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: Reduce memory for language translation strings

Post by AZJIO »

Doesn't work with UTF8, because shift *c + SizeOf(Character) cannot be performed with different character widths. Had to use UTF16LE for text files.

Code: Select all

EnableExplicit

#CountStrLang = 4

Global *pLang
Global PathLang$
Global Dim pLng(#CountStrLang)

Macro Lng(i)
	PeekS(pLng(i))
EndMacro

; UTF16LE
DataSection
	Ru:
	IncludeBinary "1049.txt"
	Data.l 0
	Ru_end:

	En:
	IncludeBinary "1033.txt"
	Data.l 0
	En_end:
EndDataSection


; Procedure SplitMemory(*pLang, length, Array pLng(1), Separator$ = #CRLF$)
Procedure SplitMemory(*c.Character, Array pLng(1), Separator$ = #CRLF$)
	Protected NewList pList(), i
	Protected *S.Character = *c
	Protected *jc.Character

	; Debug PeekS(*c, -1, #PB_Unicode)
	While *c\c
		*jc = @Separator$

		While *jc\c
			If *c\c = *jc\c
				*c\c = 0
				If *S <> *c And *S\c <> ';' ; добавлен игнор закоментированного
					AddElement(pList())
					pList() = *S
					i + 1
				EndIf
				*S = *c + SizeOf(Character)
				Break
			EndIf
			*jc + SizeOf(Character)
		Wend
		If i >= #CountStrLang ; добавлен игнор лишних
			Break
		EndIf

		*c + SizeOf(Character)
	Wend
	AddElement(pList())
	pList() = *S
	; Debug ListSize(pList())
; 	ReDim pLng(ListSize(pList()))
	i = 0
	ForEach pList()
; 		If PeekC(pList()) <> ';'
			pLng(i) = pList()
			i + 1
; 		EndIf
	Next
EndProcedure


Procedure ReadLangFile()
	Protected bytes, length, File_id

	If FileSize(PathLang$) > 3
		
		File_id = ReadFile(#PB_Any, PathLang$, #PB_UTF8)
		If File_id
			length = Lof(File_id)
			*pLang = AllocateMemory(length + 2)
			If *pLang
				bytes = ReadData(File_id, *pLang, length)
				If bytes
					; SplitMemory(*pLang, bytes, pLng(), #CRLF$)
					SplitMemory(*pLang, pLng(), #CRLF$)
					; Debug bytes
				EndIf
			EndIf
			CloseFile(File_id)
		EndIf
	EndIf
EndProcedure

CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		Global UserIntLang, *Lang
		If OpenLibrary(0, "kernel32.dll")
			*Lang = GetFunction(0, "GetUserDefaultUILanguage")
			If *Lang
				UserIntLang = CallFunctionFast(*Lang)
			EndIf
			CloseLibrary(0)
		EndIf
	CompilerCase #PB_OS_Linux
		Global UserIntLang$
		If ExamineEnvironmentVariables()
			While NextEnvironmentVariable()
				If Left(EnvironmentVariableName(), 4) = "LANG"
					; LANG=ru_RU.UTF-8
					; LANGUAGE=ru
					UserIntLang$ = Left(EnvironmentVariableValue(), 2)
					Break
				EndIf
			Wend
		EndIf
CompilerEndSelect

UserIntLang = 1037 ; тест иного языка, проверка лишних или недостающих строк, а также закомментированных

CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		If UserIntLang = 1049
			; SplitMemory(?Ru, ?Ru_end - ?Ru, pLng(), #CRLF$)
			SplitMemory(?Ru, pLng(), #CRLF$)
		Else
			; SplitMemory(?En, ?En_end - ?En, pLng(), #CRLF$)
			SplitMemory(?En, pLng(), #CRLF$)
		EndIf
	CompilerCase #PB_OS_Linux
		If UserIntLang$ = "ru"
			; SplitMemory(?Ru, ?Ru_end - ?Ru, pLng(), #CRLF$)
			SplitMemory(?Ru, pLng(), #CRLF$)
		Else
			; SplitMemory(?En, ?En_end - ?En, pLng(), #CRLF$)
			SplitMemory(?En, pLng(), #CRLF$)
		EndIf
CompilerEndSelect


CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		PathLang$ = GetPathPart(ProgramFilename()) + Str(UserIntLang) + ".txt"
	CompilerCase #PB_OS_Linux
		PathLang$ = "/usr/share/locale/" + UserIntLang$ + "/LC_MESSAGES/" + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".txt"
CompilerEndSelect
; Debug PathLang$


ReadLangFile()
; Debug "———————"
Define i
; Debug ArraySize(pLng())
For i = 0 To ArraySize(pLng()) - 1
	; Debug pLng(i)
	Debug Lng(i)
	; MessageRequester("", Lang(i))
Next
If *pLang
	FreeMemory(*pLang)
EndIf
Post Reply