Best practices for internationalization of script tools

To create a script tool that works well across different languages and locales, it is important to follow best practices.

Avoid common locale-related problems

In a tool's source code, there are two functions for obtaining a parameter value:

When the data type is one of the following types, arcpy.GetParameter is the preferred method as native Python types are returned.

  • Date (GPDate)—Returns a datetime.datetime object.
  • Double (GPDouble)—Returns a float type.
  • Long (GPLong)—Returns an integer type.

Using GetParameter lessens the potential for a parsing error. The GetParameterAsText function is preferred if the data type is not one of the three aforementioned. However, if any string parsing is required, you should be cautious that the return value is correctly parsed to avoid errors.

Avoid expressing numeric values as strings when writing ArcPy code. When expressing a coordinate value as a string, you are likely to encounter an error when you run the code in a different locale. For example, if the first following example is used in a French locale, the code will likely fail since the code uses periods for decimal separators and most French locales use commas. If expressing numeric values as strings is necessary, refer to the following Python locale module section for best practices.

Expressing numeric values as a string. This is not recommended.

x_coordinate = "-118.24982695975127"
y_coordinate = "34.04771282043386"

Expressing numeric values as a numeric type.

x_coordinate = -118.24982695975127
y_coordinate = 34.04771282043386

Error handling in ArcPy

If your script tool includes error handling for potential geoprocessing errors, use the error ID instead of the entire error string. In geoprocessing tool error messages, the content after the colon is translated for different languages.

Check the messages for the entire error string. This will only work when the messages are in English.

try:
    arcpy.analysis.Buffer("cities", "cities_buffer", "500 meters")
except Exception:
    # Catch "ERROR 000732: Input Features: Dataset cities does not exist or is not supported"
    message = "ERROR 000732: Input Features: Dataset cities does not exist or is not supported"
    if message in arcpy.GetMessages(2):
        # Take some action

Check the messages for the entire error string. This approach works regardless of the language or locale. Since the ERROR 000732 component of the message is not translated, the comparison will be valid on all locales and languages.

try:
    arcpy.analysis.Buffer("cities", "cities_buffer", "500 meters")
except arcpy.ExecuteError as err:
    # Catch "ERROR 000732: Input Features: Dataset cities does not exist or is not supported"
    message_id = "ERROR 000732"
    if message_id in arcpy.GetMessages(2):
        # Take some action

Python locale module

The Python locale module includes localization functionality that can be implemented in a script tool.

The locale.atof function converts a string to a float, and it parses localized numbers into a normalized format. This is useful when converting strings with decimal separators from a non-English locale to an English locale.

# Import the locale module
import locale

# Use user's default locale settings
locale.setlocale(locale.LC_ALL, '')

# These are coordinates returned assuming the user's locale is French (France).
x_coordinate = "-118,24982695975127"
y_coodinate = "34,04771282043386"

# The output is normalized.
# The following value will be -118.24982695975127 (float).
x_coord_localized = locale.atof(x_coordinate)

# The following value will be 34.04771282043386 (float).
y_coord_localized = locale.atof(y_coordinate)

Similar to locale.atof, the locale.atoi function converts a string to an integer and parses localized numbers into a normalized format.

# Import the locale module
import locale

# Use user's default locale settings
locale.setlocale(locale.LC_ALL, '')

num_books = "737"

num_books_localized = locale.atoi(num_books)

Avoid hard-coding separators by using locale.localeconv() to identify decimal and thousand separators. This ensures that the input will always have the locale-appropriate thousands separator and decimal separator.

# Import the locale module
import locale

# Use user's default locale settings
locale.setlocale(locale.LC_ALL, '')

# Obtain the current locale's separators
thousands_sep = locale.localeconv()['thousands_sep']
decimal_sep = locale.localeconv()['decimal_point']

input_linear_unit = f"2{thousands_sep}259{decimal_sep}50 Miles"

SpatialReference object

A SpatialReference object can be created using three types of inputs.

  • Using the coordinate system's factory code (or authority code or WKID).

    This method is preferred since it's locale independent (meaning the number used to reference this spatial reference will remain the same regardless of locale).

    # EPSG:4301 Tokyo
    sr = arcpy.SpatialReference(4301)

  • Using the canonical name of the coordinate system.

    This method is similar to using a factory code since it's also locale independent.

    # EPSG:4301 Tokyo
    sr = arcpy.SpatialReference("GCS_Tokyo")
    
    # To obtain a spatial reference's canonical name, you can use the factory code along with the command below
    sr_canonical_name = arcpy.SpatialReference(4301).name

  • Using the display name of the coordinate system.

    This method should be avoided if the code will be run on different locales or language packs.

    # EPSG:4301 Tokyo
    sr = arcpy.SpatialReference("Tokyo")

    The following depicts code run on a Japanese language pack. Using an English spatial reference display name does not work and returns an error. Each language pack will have a uniquely translated display name; however, the canonical name and factory code will remain constant regardless of language or locale. Python window in Japanese language pack with spatial reference example