Android Code Snippet Collecting all keys for translation in Localizator

I want to localize some of my apps with the Localizator class, and was looking for a good way, to find all the keys for translation. There are lots of texts in views and dialogs. So the idea was, to define and declare also a map "strings_missing" where the map "strings" is and change the sub "Localize" a bit, to collect all keys that have no value yet.

So I can start the app, go through all pages and catch the keys in the end like "Log(Main.loc.GetMissingStrings)". Copy & Paste this keys in the strings excel file for translations would be the next step.

Collecting keys without translation:
'Localizes the given key.
'If the key does not match then the key itself is returned.
'Note that the key matching is case insensitive.
Public Sub Localize(Key As String) As String
    Dim value As Object = strings.Get(Key.ToLowerCase)
    If value = Null Then
        value = Key
        strings_missing.Put(Key, "") 'Collect Keys without value in beginning of app localisation, later can be deactivated. Value is not important here
    End If
    Return value
End Sub

Public Sub GetMissingStrings() As String
    Dim sb As StringBuilder : sb.Initialize
    For Each sKey As String In strings_missing.Keys
        sb.Append(sKey & CRLF)
    Next
    Return sb.ToString
End Sub
 

emexes

Expert
Licensed User
Longtime User
Use Google Sheets instead of Excel and you have a function GoogleTranslate that might save you some time. 🍻

https://support.google.com/docs/answer/3093331

We actually did something very similar using Alta Vista, before Google translate was a thing. Lol, might even have been before Google was a thing, or at least before they went public. Then in about 2020 we had an app for a different project, and by then Alta Vista had disappeared but I'd found Google Sheets, which I was using to do geolocation for yet another project, also had translation functions.

Anyway, here's a sample I prepared earlier:


Note that there are two sheets:

Sheet1 = where the action happens, including back-translation for comparison to the original language, to pick up where something might have been lost in translation.

Eg "put down the cat" might meant that you are holding the cat and should place it down on the floor, or it might mean to euthanize the cat.

I had a similar mistranslation occur in about 2003, when I asked the Romanian neighbour to tie up the dog on New Year Eve so that she didn't escape when the fireworks went off, but it turned out the machine translation of that was to dress the dog with a (dinner suit) tie.

Sheet2 = all the various translations, published (downloadable using OkHttp) as a tab-separated-values text file eg:


Looking at that spreadsheet reminded me of two other tricks:

1/ column L is B4X code that can be pasted into a program and creates a Map of the translations
2/ the original language is just another translation, usually 1:1 but sometimes the same original text is listed twice, to handle situations where it is used in two different senses that translate differently in another language, eg if "put the cat down" is used twice in the program, then there would be two entries with keys like "put the cat down (1)" and "put the cat down (2)" which would both translate to the original language as "put the cat down".

Another trick enabled by "original language is just another translation" is to use placeholders to handle situations where words are sequenced differently in different languages eg:

"there is a red Toyoto in parking spot 27"

"there is a {color} {make} in parking spot {number}"
"il y a une {make} {color} sur la place de parking {number}"
"auf Parkplatz {number} steht ein {color} {make}"
"c'è una {make} {color} nel parcheggio {number}"
"hay un {make} {color} en la plaza de aparcamiento {number}"
"На парковочном месте {number} стоит {color} {make}"

which would be handled like:

B4X:
Dim t As String = tr("there is a {color} {make} in parking spot {number}")

t = t.Replace("{make}", VehicleMake)          'no translation for name'
t = t.Replace("{color}", tr(VehicleColor))    'translate color
t = t.Replace("{number}", VehicleLocation)    'no translation for number

Label1.Text = t

Any calls to tr that didn't find a translation in the table, would just return the untranslated string, but also log it to a to-do list, which I think is what you're doing too.

Nowadays you could probably translate it immediately if your program has access to the internet (although you might have to run it through a hidden background browser or WebView to get at the translated text) eg:

https://www.deepl.com/en/translator#en/de/B4X is a great programming language!

I have a vague recollection of using reverse apostrophes to mark text that shouldn't be translated., but I think that evolved into the placeholder method.
 
Last edited:

emexes

Expert
Licensed User
Longtime User
Not sure why, or how to undo, but the spreadsheet viewer and download links got changed into embedded views. The actual links were:

HTML:
https://docs.google.com/spreadsheets/d/1UIbEyLP14-v12rDskifypDtGyQEI7yshe7hyim6w_a4/edit?usp=sharing

https://docs.google.com/spreadsheets/d/e/2PACX-1vR30lvGdQ-rGWQ5LI3cvgdgupNxxG92gmBWUZc0hx14R3kzQ2mfyYIAkyA-iaimGkzv8s8oZy7zvRIk/pub?gid=1151276385&single=true&output=tsv
 

emexes

Expert
Licensed User
Longtime User
That's a very good suggestion. 👍

Also that you can publish and download it as a text file, ie update your program's translations if your users discover any mistakes (surely not).

But cache the downloaded translation file on the phone, for when internet access is down.
 
Top