Compatibility settings – Use period and comma as placeholders (Part 2)

Automatically translated from English

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!

Thomas Maul

• VP of Strategy, 4D Product Line •

When 4D's German subsidiary was created in 1988, Thomas joined the company as a Technical Director, helping to build the 4D developer community in both Germany and Austria.

After many years supporting customers with technical problems and being increasingly involved in sales and management issues, he was promoted to Managing Director for 4D Germany in 1999.

As a member of the executive board since 2005, he became part of worldwide strategy of the company, leading to his current position as Vice President of Strategy, 4D Product Line, responsible for defining and executing the overall strategy for the 4D product line in relation to the Program, R&D, Sales and Marketing teams.