We’ve started a series of blog posts on compatibility settings and the secret options that can drastically help you improve the performance and the behavior of your applications. The first post was about QUERY BY FORMULA.
The second part of the compatibility series is about international usage of your business applications. This could mean selling to other countries – or having coworkers coming from other countries to work for a while, but using their local systems.
If you’ve ever seen or gotten reports like “the numbers are displayed as >>>>>>>>>”, this blog post is for you.
Unsure how your numbers will be displayed? Check your database settings:
If the above option is already enabled or not displayed at all (applications created with 4D v11 or later), all is well and there’s no need to read further.
How it should work
The 4D developer designs for international usage, while the end user can use any regional settings, even mixed settings in client/server mode.
Example: “###,##0.00” displayed in Germany as “1.234,56”, in the USA as “1,234.56”, in Switzerland as “1’234.56”
How it used to work (compatibility mode)
The 4D developer designed for a given regional setting (usually the local one) and the end user needed to have exactly the same settings.
Example: “###,##0.00” displayed in Germany as “>>>>>>>>”, in the USA as “1,234.56”, in Switzerland as “>>>>>>>>>”
how it works now (Developing internationalLY)
In recent 4D versions, everything in the 4D methods is in an international format:
- A constant numeric value is defined using a dot as the international decimal delimiter (for example, 5.3).
- Dates are defined using the international “yyyy-mm-dd” (2018-07-31) format. Note the “-” used as the delimiter and the order of the Year/Month/Day.
- Times are defined in 24 hour format.
As soon as the value is displayed (or formatted), the current regional settings are used.
For example, I’m writing this blog post on a German system, so String(!2018-07-31!) results in “31.07.2018” and String(5.3) results in 5,3. Different results with different regional settings – for the same application.
This requires a change in thinking for long-time developers, but as result you get international working applications without extra effort.
Migration hints
To change the regional settings behavior, you need to change every place where local settings are hard coded. This could be a nearly impossible mission. This is why a compatibility option exists and why it’s set to “compatible” by default.
But there’s a lot to earn, so let’s see how difficult it really is.
Hard coded constants in methods
Good news! There’s nothing for you to do. When you create a 4D method in v2004 and write $i:=5,3 (German system) and open that method in 4D v16 or newer, you’ll see $i:=5.3.
It’s the same for dates and times. Problem solved!
Using numeric formats – filters.
While 4D does allow hard coding formats (i.e., “###.##0,00”) in methods or forms, we’ve been advising against it for 20+ years.
If you’ve used Filters everywhere, changing them will be extremely easy. You might have 5 or 10 filters, but probably not more than 20. So you’ll need to modify your filters from local format to international format (i.e., using dots (“.”) for decimals and commas (“,”) for thousands separator).
Once that’s finished, enable the compatibility checkbox and you’re done!
How to find hard coded filters in methods
This step isn’t as easy as those mentioned above. While you might’ve used filters in your applications, you may not be certain whether or not you (occassionally) skipped the filters and created “quick & dirty” code in some cases.
Example:
$text := String(5300;"### ##0") // to be used in Sweden with a space for thousands
How to find them?
Since there aren’t that many different formats, it’s simple!
##0 ^^^0 0,00 ***0 are typical formats. Only a part (as unique as possible) of the format is needed. If you find one of these text fragments in your methods, chances are pretty good that they’re hard-coded. Use the Find tool in design mode to search for these bits of code and browse the results.
If you only find a few, go through them manually and replace them with filters, such as:
$text := String(5300;"|mynumberformat") // vertical line before filter name
If you find a lot, browse through them manually and verify that your search condition was correct. Fine-tune it if necessary.
It might take some work, but you’ll be able to define some patterns to hande most of the cases. These patterns should allow an automatic “search & replace” operation.
Depending on the amount of effort required, you could even write a method to run through your code and search for you, using the METHOD GET CODE and METHOD SET CODE commands.
How to find all hard-coded filters in forms
4D doesn’t provide a search option for forms, so you can’t automatically check for hard-coded formats in them. But since we’re developers, we can do that ourselves …
First, we’ll need to get a list of all forms for all tables and loop through them. Then for each form, we’ll get a list of all objects and loop through them, too. And finally, for each object, we’ll check if it uses a numeric format and if it does, we’ll see if it’s hard-coded. If it is, we’ll report it.
The good news: this code will provide a list similar to the Find option in design mode for methods. The bad news: you’ll need to open each result and manually assign the format, so it might take some time.
$report:=""
For ($table;0;Get last table number)
ARRAY TEXT($arr_Names;0)
If($table=0) // project forms
FORM GET NAMES($arr_Names)
Else
If (Is table number valid($table))
FORM GET NAMES(Table($table)->;$arr_Names)
End if
End if
For ($forms;1;Size of array($arr_Names))
If ($table=0)
FORM LOAD($arr_Names{$forms})
Else
FORM LOAD(Table($table)->;$arr_Names{$forms})
End if
FORM GET OBJECTS(arrObjNames;arrObjPtrs;arrPages;Form all pages)
For ($i;1;Size of array(arrObjNames))
$type:=OBJECT Get type(*;arrObjNames{$i})
If (($type=Object type text input) | ($type=Object type listbox column) \
| ($type=Object type listbox footer) | ($type=Object type static text))
$format:=OBJECT Get format(*;arrObjNames{$i})
If (($format#"") & (($format#"|@")))
If(($format="@,@")|($format="@.@"))
$report:=$report+$arr_Names{$forms}+char(9)+"object name: "+arrObjNames{$i}+char(9)+"format: "+$format+char(13)
End if
End if
End if
End for
FORM UNLOAD
End for
End for
If ($report="")
$report:="All is well!"
End if
ALERT($report)
SET TEXT TO PASTEBOARD($report)
order of operations
In your current application:
- Check your filters and create new ones (if necessary)
- Let all filters continue to use regional settings… no changes yet!
- Change all methods and forms to use filters
- Deploy
At the same time, with a copy of your application:
- Either internally or for selected beta testers:
- Change filters to use international format
- Enable compatibility check box
- Compile and deploy for testers for several weeks
- Once everything is tested and verified, deploy for everyone (replace the existing)
Related commands
Even though in nearly all cases, you’ll want numbers to be formatted to follow regional settings, there are some exceptions. For example, you may use different rules when you import or export data. It depends on who will use or who has provided the data. Another example is data in an XML or JSON format, which always uses a dot (“.”) as a decimal separator.
Your job is only to handle these exceptions, not the normal behavior.
To convert a number to string using a dot as a decimal separator, anywhere in the world, independent from the user’s regional setting, use:
String($mynumber; "&xml")
Note: Even the format is named XML, so it can be used for any use case, as long you want to use a dot as a decimal separator.
On the other hand, when you want to parse a string and you know the string uses a dot as a decimal separator( because you imported it from an XML or JSON document), use:
Num($mystring;".")
Conclusion
For older, well-established applications, some work may be required, but results in applications that you can deploy anywhere with correct numeric formats. This will take pressure off of your tech support and help your customers, too. It’s a win-win!