B4J Code Snippet B4XLocalizator no longer maintained – workaround using CSV + small B4J converter tool

Hi all,

Since the tool for converting excel files to B4XLocalizator has not been maintained for years, it recently stopped working for me completely. No matter which XLSX file I tried (even old Excel 2007 files), the tool kept crashing due to missing POI classes such as:

Code

ThemeDocument$Factory

After some digging it became clear that the POI version bundled with B4J cannot read modern XLSX files that contain themes or styles.


Working Solution

The workaround that finally solved it for me:

1. Save your Excel file as .csv instead of .xlsx​

This gives you a clean, comma‑separated file without themes, styles or XML structures that POI cannot handle.

2. Use a small B4J tool to convert the CSV into SQLite

The SQLite database is generated using the same structure that B4XLocalizator expects.

I’ve attached a ZIP file containing the small B4J converter app.


Important Notes

  • Make sure to enable the jSQL library in your B4J project.
  • Ensure the correct SQLite JDBC driver is present in your Additional Libraries folder.
  • The messages in the tool are written in my native language, but they can easily be translated to English if needed.

If this helps anyone who is still using B4XLocalizator or maintaining older projects, feel free to improve or extend the tool.
Note: this program was created by Gemini bot
Note2: B4xLocaliser itself works without any problem. https://www.b4x.com/android/forum/threads/b4x-localizator-localize-your-b4x-applications.68751/
 

Attachments

  • Cvs convertor.zip
    2.9 KB · Views: 6
Last edited:

emexes

Expert
Licensed User
Longtime User
For people who haven't yet committed to a particular localisation method:

The one program I was involved with that worked with multiple languages, we used Google Sheets (spreadsheet) which can access Google Translation using the GOOGLETRANSLATE function. Actually, now I think about it, we were using Alta Vista, but the principle is the same, just less automated.

What we then did was backcheck the translation in the reverse direction, and flag for human intervention any that didn't come back the same as they went in.

eg "Now is the time" translates to "Jetzt ist die Zeit", and "Jetzt ist die Zeit" translates back to the same "Now is the time", so it's probably a good translation.

whereas "Hold your horses" translates to "Ruhig Blut!", but "Ruhig Blut!" translates back to the different "Keep calm!", so we'd double-check that manually. Actually, if the initial customer in the language was friendly, we'd just leave it as is and supply them with a printed list of the translations we weren't sure about, and they'd usually come back the next day with their native-language-speaker suggestions which we'd immediately incorporate into the translation file and somehow get it back to them.

We were using a 16-bit BASIC (Microsoft PDS 7.1) so we couldn't store the translations in memory. Instead we had a function that we called with a phrase number that would do a random-access lookup into a file, ie in the program code it looked like PRINT Phrase$(3779), and then we had a coding tool that would go through the code and add(/update) comments to all the Phrase$() calls eg PRINT Phrase$(3779) 'Hello, World!' The same coding tool would find instances like Phrase("Hello, World!") and lookup/add the text in/to the translation file, eg find that phrase/line 3779 in the translation file is "Hello, World!" and then update the code to Phrase$(3779) 'Hello, World!'

In a sample of the system that I apparently made here in 2023 and/or 2021, I included a column that stores the translations as a Map, that could be used in develop systems that don't have the memory restrictions that we worked under. ie basically using a Map instead of an SQL database.

Link to sample Google Sheet

 
Last edited:

emexes

Expert
Licensed User
Longtime User
Now that I think about it, the Phrase$() function had a cache of the last 64 accessed strings. And it was actually called LIT$() which we were already using in place of string literals in the code because of memory tightness.

Coincidentally there is a similar 64 kB limitation in (32-bit?) Java that occasionally brings people similarly unstuck here.
 

aeric

Expert
Licensed User
Longtime User
Have you checked:

this work?
 

MbedAndroid

Well-Known Member
Licensed User
Longtime User
Top