I had an interesting software development problem the other day. I was working on a NodeJS application, doing general maintenance work, which led me down an interesting rabbit hole. I could’ve used an online knowledge tool such as the various LLM-based tools available now. But the grunt of the search was much more useful.
It started off by upgrading the base image of the Docker file for the application to Node 20, as well as the base image for the CI pipeline runners. When the code was pushed to the git repository, the pipeline started running the unit tests, and they started failing because numbers were not formatting correctly as strings: the number 45993.33 was expected to format as 45 993,33 for the South African locale. Instead, it was now formatting as 45,993.33 with the comma as a thousand separator and the point as the decimal separator. The code that formatted the number was as follows:
const myValue = 45993.3332342;
const formattedValue = myValue.toLocaleString(
'en-ZA', {
maximumFractionDigits: 2,
minimumFractionDigits: 2
});
There had been changes in the runners used for the pipeline, so my first thought was that it was because they didn’t have the appropriate locales installed. So I started the internet searches for the differences between bullseye, bookworm and buster Docker images, and eventually found the right commands to install the South African locale. This didn’t work. The tests still failed.
I then ran the tests on my local machine to see if I can replicate the problem. They failed. I switched my local NodeJS version to 18. They passed. I switched back to 20. They failed. Then I started the journey of understanding how the NodeJS formatter worked. I changed the implementation to use Intl.NumberFormat but that didn’t work. I read how NodeJS has supported internalisation over different versions, but the default is what the documentation called full-icu, which is full international support. So then it wasn’t that.
Eventually, after frustrated DuckDuckGo-ing, I found out that someone in the world logged an issue on this Unicode site stating that “Decimal and grouping separator for en_ZA does not align with in-country usage”. They listed a few examples of how various sites (including the South African Reserve Bank) format their numbers, and stated that a comma should separate thousands instead of a space. This was then accepted and merged on 4 May 2023. Because, according to the NodeJs docs, “Node.js and the underlying V8 engine use International Components for Unicode (ICU)” for internationalisation, this change then appeared in newer versions of NodeJS, leading to my dilemma.
Now I could go on a rant about the governance of such a project, the breaking changes it introduces, as well as what is “official” or not. But instead, I want to focus on the fruitfulness of this journey. All of this took several hours of my time. Of course I could’ve opened up ChatGPT or one of its friends to tell me what the answer was and move on with my life (if they gave me the right response). But this journey took me through mountains of different Docker base images. Through footpaths of Linux locales. Through valleys of NodeJS and internalisation. Through forests of Unicode and its governance. All which are now in my toolbox for future use. All which would’ve been missed had I taken the plane flight straight to the destination.
The growth of knowledge based tools promises to take away the grunt work from developers. But maybe sometimes, the grunt work is worth doing.