Replace all strings in sourcecode to constants for language translation?

Just starting out? Need help? Post your questions and find answers here.
User avatar
Caronte3D
Addict
Addict
Posts: 1055
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Replace all strings in sourcecode to constants for language translation?

Post by Caronte3D »

Hi
Anyone has already done a tool to generate different language versions of an aplication?

I mean something like:

Search throug all the source code for "" and replace it with auto-generated constants, so then we can translate every string (at the begining) without need to search throug all the soucecode manually.

I know would be better to do it from scratch, but my application is already done and I'm looking for an easy way.

Any idea?
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4663
Joined: Sun Apr 12, 2009 6:27 am

Re: Replace all strings in sourcecode to constants for language translation?

Post by RASHAD »

You can use Multi Array for your strings
Use the Array contents instead of your strings
All you need in the start is the Array branch
I hope I understood you correctly :)

Code: Select all

dim title.s(30,3)
title(0,0) = "English"
title(0,1) = "German"
title(0,2 = "Arabic"
.
.
.
Egypt my love
normeus
Enthusiast
Enthusiast
Posts: 415
Joined: Fri Apr 20, 2012 8:09 pm
Contact:

Re: Replace all strings in sourcecode to constants for language translation?

Post by normeus »

Use a regular expresion to find all text encoded in quotes:

Code: Select all

\"(.)?\"
I would use an editor that displays all regEx lines matched like notepad++, just to see what you are up against.

Norm.
google Translate;Makes my jokes fall flat- Fait mes blagues tombent à plat- Machte meine Witze verpuffen- Eh cumpari ci vo sunari
BarryG
Addict
Addict
Posts: 3324
Joined: Thu Apr 18, 2019 8:17 am

Re: Replace all strings in sourcecode to constants for language translation?

Post by BarryG »

Don't forget that the size of gadgets will need to be increased when supporting multiple gadgets. For example, a ButtonGadget() that is just the right size for English will have cropped text for Asian languages, etc. I once read you need to increase them by at least 33% in width so all languages can fit (this was in my Visual Basic 5 manual, so I assume Microsoft knows best).

If your app is a small tool or utility that is easy to use, then a possible alternative is just to use icons instead of text for everything.
Saboteur
Enthusiast
Enthusiast
Posts: 272
Joined: Fri Apr 25, 2003 7:09 pm
Location: (Madrid) Spain
Contact:

Re: Replace all strings in sourcecode to constants for language translation?

Post by Saboteur »

This is a little example I use with preferences command.
This way you can write all languages you need, I think it is easy to understand:
· write language files to a subfolder
· load texts in a map variable
· write the text assigned to a code

And be careful about BarryG warning, text with changes in other languages.
I'm not sure if this is what you are looking for.

Code: Select all

---------------------------
FILE: LANG.PB
---------------------------

Structure appsettings
  LANG.s                      ; App language
EndStructure

Global G_SETTINGS.appsettings       ; Structure with app settings
G_SETTINGS\LANG="es"                ; default language, change it
Global NewMap G_TEXTS.s()           ; Array with app texts, stored in "lang/lang-xx.txt"

Procedure OpenLang()
  If(OpenPreferences("lang/lang-"+G_SETTINGS\LANG+".txt"))
    ExaminePreferenceGroups()
    
    While NextPreferenceGroup()
      If(PreferenceGroupName()="AppTexts")
        ClearMap(G_TEXTS())
        ExaminePreferenceKeys()
        While NextPreferenceKey()
          G_TEXTS(PreferenceKeyName())=PreferenceKeyValue()
        Wend
      EndIf
    Wend
    ClosePreferences()
  EndIf
EndProcedure

Debug G_TEXTS("T001")
Debug G_TEXTS("T002")
Debug G_TEXTS("T003")
Debug G_TEXTS("T004")

---------------------------
FILE: lang/lang-en.txt
---------------------------

[AppTexts]
T001 = File
T002 = Open
T003 = Save
T004 = Exit

---------------------------
FILE: lang/lang-es.txt
---------------------------

[AppTexts]
T001 = Archivo
T002 = Abrir
T003 = Guardar
T004 = Salir
[:: PB Registered ::]

Win10 Intel core i5-3330 8GB RAM Nvidia GTX 1050Ti
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4663
Joined: Sun Apr 12, 2009 6:27 am

Re: Replace all strings in sourcecode to constants for language translation?

Post by RASHAD »

Hi Caronte3D
From your request I understood that you don't want to use any external file but you need everything included in your code so
#1 : Use Multi Array as my first post
#2 : Or include your strings in DataSection

Code: Select all

DataSection
english:
data.s "english",
englishend:

spanish:
data.s .......
spanishend:

EndDataSection

Egypt my love
User avatar
Caronte3D
Addict
Addict
Posts: 1055
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Replace all strings in sourcecode to constants for language translation?

Post by Caronte3D »

Thanks to everyone for the ideas and code examples, but... :? I'm looking for a "tool" to do the dirty job:

1.- Scan the sourcecode for texts inside " "
2.- Create a constant for each string text
3.- Put all the constants at begining of the source code

Then I only need to replace the constants at sourcecode begining (before compile) for each language.

I know it's posible to create a tool to do the major part of work, my question is if someone have a tool like that already done :wink:

P.D:
Mi app is not small tool, is a big project with many, many controls and any other texts on screen.
I don't want to use variables for this, because is a threaded application and is already a pain of global variables and mutex.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 586
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Replace all strings in sourcecode to constants for language translation?

Post by spikey »

Personally I'd recommend not going in this particular direction for implementing localisation. I know from past experience it's a huge pain in the butt for maintenance purposes as strings get added or modified over time. Either you end up with time consuming synchronisation problems between the base source and translation resources if a string gets inserted in the middle of the table; or you only ever add strings to the end of the table to avoid sync problems which means that you can end up with a muddled layout for the table which can be almost as bad. My resources were in separate files and could be managed separately to some extent, yours will be mixed up in the real source too which would make things worse I think! The bigger the project gets the worse the problem is...

The group/key = value implementation used by the PureBasic IDE itself is a much easier system to maintain in the long run even if it is more work to set up in the first place and uses slightly more memory in operation. See viewtopic.php?f=12&t=26729&p=191073 or 'Language.pb' if you've downloaded the IDE source code.

However if you really want to go that way, this is a section of code taken from a larger project that hunts for strings and tries to be intelligent about it, maybe you could modify it:-

Code: Select all

;- EXAMINESOURCE Structure
Structure EXAMINESOURCE
  LineNum.I
  Source.S
  StringText.S
  Action.I
  Remark.S
  ExistingGroup.S
  ExistingKey.S
  NewGroup.S
  NewKey.S
  GroupID.I
  KeyID.I
  StringID.I
EndStructure

;- Action types
Enumeration ActionTypes
  #ActionNoneDefined 
  #ActionIgnoreString
  #ActionIgnoreDuplicate
  #ActionExistingGroupExistingKey
  #ActionExistingGroupNewKey
  #ActionNewGroupNewKey
  #ActionReuseGroupNewKey
  #ActionReuseGroupReuseKey
EndEnumeration

Enumeration ErrorCode
  #ErrSuccess                  ; Success.
  #ErrCantOpenFile             ; A file could not be opened.   
  #ErrUnterminated             ; A file contains an unterminated string.
EndEnumeration

Procedure.I fSourceScanFile(aFile.S, List aList.EXAMINESOURCE())
  ; Scan a source file aFile for strings, building a list of candidates in aList.
  ; The list must use the EXAMINESOURCE structure.
  ; Returns an ErrorCode enum value.
  
  ; 1.00.00 spikey 21/10/2019 Created.
  
  Protected.B lbytUnterminated
  Protected.I lintFile, lintStart, lintEnd, lintLength, lintLineNum, lintResult
  Protected.S lstrLine, lstrString
  NewMap lmapStrings.I()
  
  ; Open the file.
  lintFile = ReadFile(#PB_Any, aFile)
  If lintFile = 0
    ProcedureReturn #ErrCantOpenFile
  EndIf
  
  ; Iterate the file.
  While Eof(lintFile) = 0
    
    ; Reinitialise.
    lintStart = 0
    lintEnd = 0
    lintLength = 0
    lstrString = #Empty$
    
    ; Get the next line from the file.
    lintLineNum + 1
    lstrLine = Trim(ReadString(lintFile))
    
    ; If there's no double quote, skip the line.
    If FindString(lstrLine, #DQUOTE$) = 0
      Continue
    EndIf
    
    ; If it's a comment, skip the line.
    If Mid(Trim(lstrLine), 1, 1) = ";"
      Continue
    EndIf
    
    ; Look for strings in the line.
    Repeat 
      
      ; Look for an opening quote.
      lintStart = FindString(lstrLine, #DQUOTE$, lintEnd + 1)
      
      If lintStart
        lintStart + 1
        lintEnd = FindString(lstrLine, #DQUOTE$, lintStart)
        
        ; Check for unterminated strings.
        If lintEnd = 0
          AddElement(aList())
          aList()\LineNum = lintLineNum
          aList()\StringText = "WARNING: Unterminated string."
          lbytUnterminated = #True
          Break
        EndIf
        
        ; Extract the string.
        lintLength = lintEnd - lintStart
        lstrString = Trim(Mid(lstrLine, lintStart, lintLength))
        
        ; Skip if empty.
        If lstrString = #Empty$
          Continue
        EndIf
        
        ; Check for redundancies.
        If Right(lstrString, 1) = "." Or Right(lstrString, 1) = ":" Or Right(lstrString, 1) = "?"
          lstrString = Left(lstrString, Len(lstrString) - 1)
        EndIf
        
        ; Add it to the list.
        AddElement(aList())
        aList()\LineNum = lintLineNum
        aList()\Source = lstrLine
        aList()\StringText = lstrString
        
        ; Check for accelerators.
        If Left(lstrString, 4) = "Ctrl" Or Left(lstrString, 3) = "Alt"
          aList()\Action = #ActionIgnoreString          
        EndIf
        
        ; Check for duplicates.
        If FindMapElement(lmapStrings(), lstrString)
          aList()\Action = #ActionIgnoreDuplicate
          aList()\Remark = "See line " + StrU(lmapStrings(lstrString)) + "."
          
        Else
          lmapStrings(lstrString) = lintLineNum
          
        EndIf
        
      EndIf
      
    Until lintStart = 0
    
  Wend
  
  ; Clear the map.
  ClearMap(lmapStrings())
  
  ; Close the file.
  CloseFile(lintFile)               
  
  ; Set the return value.
  If lbytUnterminated
    ProcedureReturn #ErrUnterminated
    
  Else
    ProcedureReturn #ErrSuccess
    
  EndIf
  
EndProcedure

;- Demo
NewList Results.EXAMINESOURCE()
fSourceScanFile("<a source name here>", Results())
ShowVariableViewer()
CallDebugger
eck49
Enthusiast
Enthusiast
Posts: 153
Joined: Sat Nov 14, 2020 10:08 pm
Location: England

Re: Replace all strings in sourcecode to constants for language translation?

Post by eck49 »

Something else to watch out for, especially with single words...

Words which look the same but mean something different depending on the context.

English examples: Ball, which could be the round object used in football, one of a series of plays in cricket, or a social gathering for dancing.
Pound, which could be hitting a door to demand entry or the UK unit of currency.
Ubuntu 22.04 64-bit
Purebasic 6.00 (as of 5 Sep 2022)
(native tongue: English)
User avatar
darius676
Enthusiast
Enthusiast
Posts: 280
Joined: Thu Jan 31, 2019 12:59 am
Contact:

Re: Replace all strings in sourcecode to constants for language translation?

Post by darius676 »

Why not generate external files for each supported language?
The strings used are known at the start of the program? So why not check at program start, which language is used by system. Or let the user set the language at program start..
So you fill the strings with the data of the language file.
User avatar
Caronte3D
Addict
Addict
Posts: 1055
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Replace all strings in sourcecode to constants for language translation?

Post by Caronte3D »

darius676 wrote: Sat May 01, 2021 9:41 pm Why not generate external files for each supported language?...
My application is not aimed to several languages from begining, so now I have near 60.000 lines of code and many strings spreaded over all the source.
I only want to find an easy solution to translate to another language.
eck49
Enthusiast
Enthusiast
Posts: 153
Joined: Sat Nov 14, 2020 10:08 pm
Location: England

Re: Replace all strings in sourcecode to constants for language translation?

Post by eck49 »

However you do it, keep a good record of where every string is (or, better(?), how you derived the list).

If you find your translation needs amendment because it turns out to be unclear or inaccurate, you'll need to go easily to the right place to make the change.
Ubuntu 22.04 64-bit
Purebasic 6.00 (as of 5 Sep 2022)
(native tongue: English)
User avatar
darius676
Enthusiast
Enthusiast
Posts: 280
Joined: Thu Jan 31, 2019 12:59 am
Contact:

Re: Replace all strings in sourcecode to constants for language translation?

Post by darius676 »

My solution for this problem was:

https://mark-dowen.itch.io/the-quest-of ... arfes-king
https://www.youtube.com/watch?v=HnYGI_CoU5M

Code: Select all

Procedure.i E_GET_NPC_TEXT_RANDOM()
  ;german random default conversation textoutput, make the npcs a bit more  different
  
  Define _file.i=0
  Define _count.b=0
  Define _text_random_id.b=Random(e_NPC_maximum_text_alternative.b)
  Define _text_random_id_string.s=Str(_text_random_id.b)
  
; 
;   If ChangeCurrentElement(world_object(),*e_object_system_id2)=0
;     ProcedureReturn #False
;   EndIf
  
  
  
  e_npc_debugging_dummy_text_var.s=v_engine_base.s+world_object()\object_NPC_text_path+_text_random_id_string.s+e_npc_language_file_suffix.s
  e_npc_dummy_text_for_debugging.s=v_engine_base.s+world_object()\object_NPC_text_path
  _file.i=ReadFile(#PB_Any,e_npc_debugging_dummy_text_var.s)
  ProcedureReturn _file.i
  
  
  
  

EndProcedure






Procedure E_GET_NPC_TEXT(_mode.l)
  
  ;_mode.l=randomm language yes /no 
  
  Define _file.i=0
  Define _count.b=0
  
  If ChangeCurrentElement(world_object(),*e_object_system_id2)=0
  ProcedureReturn #False  
  EndIf
  
  
  Select e_npc_language.l
    Case #DE
      e_npc_language_file_suffix.s=".de"
    Case #EN
      e_npc_language_file_suffix.s=""
  EndSelect
  


  
    _count.b=E_NPC_SHOW_CORE_INFO_SYSTEM()
  
  
    If _mode.l=#NPC_USE_RANDOM_TEXT
      _file.i= E_GET_NPC_TEXT_RANDOM()
      If IsFile(_file.i)=0
     e_npc_dummy_text_for_debugging.s=v_engine_base.s+world_object()\object_NPC_text_path
    e_npc_debugging_dummy_text_var.s=v_engine_base.s+world_object()\object_NPC_text_path+e_npc_language_file_suffix.s
    _file.i=ReadFile(#PB_Any,  e_npc_debugging_dummy_text_var.s,#PB_File_SharedRead) 
      EndIf
      
    EndIf
    
    
    ;no random conversation : 

  
 
  If IsFile(_file.i)
    
    While Not Eof(_file.i)
      
      npc_text\text_text[_count.b]=ReadString(_file.i)
      If Len(npc_text\text_text[_count.b])>0  ;no empty lines (workaround for old text files, which used emty lines as buffer for lines displayed)
        npc_text\text_text[_count.b]= E_INTERPRETER_GET_KEYWORD(npc_text\text_text[_count.b]) 
        _count.b+1
      EndIf
      
    Wend
    
   
    
    CloseFile(_file.i)
    
     npc_text\text_text[_count.b]="[B]"
     npc_text\text_last_line=_count.b
    
  Else
    
    ;no german/alternative language pack found? we try To use english version
    _file.i=ReadFile(#PB_Any,v_engine_base.s+world_object()\object_NPC_text_path,#PB_File_SharedRead)
    
   
    If IsFile(_file.i)
      
      While Not Eof(_file.i)
        npc_text\text_text[_count.b]=ReadString(_file.i)
        
        If Len(npc_text\text_text[_count.b])>0  ;no empty lines (workaround for old text files, which used emty lines as buffer for lines displayed)
         npc_text\text_text[_count.b]= E_INTERPRETER_GET_KEYWORD(npc_text\text_text[_count.b])  ;here we inject some dynamic text, if a keyword is given
        _count.b+1
      EndIf
        
      Wend
      CloseFile(_file.i)
        npc_text\text_text[_count.b]="[B]"
        npc_text\text_last_line=_count.b
      
    EndIf
    
  EndIf



EndProcedure

https://mark-dowen.itch.io/the-quest-of ... arfes-king

https://www.youtube.com/watch?v=HnYGI_CoU5M
AZJIO
Addict
Addict
Posts: 1364
Joined: Sun May 14, 2017 1:48 am

Re: Replace all strings in sourcecode to constants for language translation?

Post by AZJIO »

download
This code was written by me in the AutoIt3 programming language. I adapted it to translate programs to PureBasic.

1. Drag the pb file into the program window. You will get a list of strings.
2. Remove strings from the list that you do not want to translate. Click the "Reread List" button.
3. Make a translation on the same list. You can do it in another editor, and then paste it into the program window. The number of lines must match.
4. Click the "Run" button. The finished code will appear in the clipboard.

In the settings, you can specify the use of an array or replacement by location.
You can see an example of how it looks in my StopwatchTimer program
User avatar
Caronte3D
Addict
Addict
Posts: 1055
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Replace all strings in sourcecode to constants for language translation?

Post by Caronte3D »

AZJIO wrote: Sun May 16, 2021 7:10 pm download
This code was written by me in the AutoIt3 programming language...
Thanks!
I think your solution may be the one I'm waiting for, but...
I can't download that file, every web browser says it contains a virus. :?
Post Reply