Localization of xTuple ERP has two facets:
- The application text can be translated into languages other than English.
- Input and display of numbers, dates, and colors can be changed to meet local conventions.
The process of using translation files is described elsewhere.
In the sections below you'll learn how, as a script or core application developer, you can make your work translatable and localizable. Some of the tools you'll use are part of Qt's application framework and others have been built by xTuple.
Making Text Translatable
As a developer it is your job to mark text strings in such a way that Qt's translation management tools can find them.
The .ui files that describe user interface layouts are easy - Qt Linguist knows how to extract label text from those with no problems and no effort on your part.
In C++ files you should wrap all translatable text with calls to the tr() method. You can find complete details in Qt's documentation. Keep in mind the following issues:
- tr() is only available in classes that inherit from QObject. If you have a class that does not inherit from QObject, you'll need to refer to QObject::tr() explicitly.
- You should not build translatable text with string concatenation. Something that reads OK in English probably won't in other languages. Instead you should write your full English text and use % placeholders and the many QString::arg() methods to do your substitutions.
Here are some examples of things you should and should not do. All four examples ask the same question. The first three are for C++ code and the fourth is a script example.
StandardButton ans; // This is bad because it doesn't translate the dialog title and // builds the main question the wrong way from translated parts ans = QMessageBox::question(this, "Are you sure?", tr("Are you sure you wanted to click the ") + _button->text() + tr(" button?"), QMessageBox::Yes | QMessageBox::No); // Do this instead: ans = QMessageBox::question(this, tr("Are you sure?"), tr("Are you sure you wanted to click the %1 button?") .arg(_button->text()), QMessageBox::Yes | QMessageBox::No); // In a class that does not inherit from QObject: ans = QMessageBox::question(this, QObject::tr("Are you sure?"), QObject::tr("Are you sure you wanted to click the %1 button?") .arg(_button->text()), QMessageBox::Yes | QMessageBox::No); // In a script (xTuple has enabled use of QMessageBox in scripts; note the . instead of ::): var ans = QMessageBox.question(mywindow, qsTr("Are you sure?"), qsTr("Are you sure you want to click the %1 button?") .arg(_button->text()), QMessageBox.Yes | QMessageBox.No);
Translation of Common Error Messages
To be written: describe storedProcErrorLookup in C++ and scripts, along with script package lookup tables
Localizing Numeric Input and Output
Different localities have different conventions for representing numeric values. Some separate the integer (whole number) portion from the fractional portion using a period ('.', the US convention) while others use a comma (','). xTuple ERP allows entering and displaying numeric values using these different conventions.
Users of xTuple ERP can set their language and refine the locale presentation by setting the country. These settings in turn use Qt's built-in rules for formatting numbers. Users do not have direct control over presentation.
As a developer you need to be aware of four different contexts in which numeric values appear:
- Data entry fields
- Single-value data display fields
- XTreeWidgets (tabular displays)
When designing a user interface that will allow the user to enter a numeric value, use xTuple's XLineEdit widget, not Qt's QLineEdit. Then set a QValidator on the widget. If the numeric values of the field should be restricted to integers, create a QIntValidator. If they should be fractional, use a QDoubleValidator. Keep in mind that the xTuple ERP application has pre-defined validators for xTuple-specific types, such as quantities, costs, and prices. For example, the Sales Order Item window allows entering the order quantity and the customer discount:
// use pre-existing validators: _qtyOrdered->setValidator(omfgThis->qtyVal()); _discountFromCust->setValidator(omfgThis->percentVal());
Setting these validators allows the xTuple ERP application to automatically adjust to the user's locale setting, including using the right group and decimal separator characters and restricting display and input to the right number of decimal places.
Handling single-value data display fields is similar. Instead of using Qt's QLabel widget, use xTuple's XLabel widget. Since the XLabel does not allow data entry, you don't set the Validator on the XLabel but instead set the precision based on an existing validator. You can also specify the number of decimal places directly or ask the application for an appropriate setting with the decimalPlaces function:
// set the precision explicitly: _numberOfChecks->setPrecision(0); // let the application check the locale: _qtyOrdered->setPrecision(decimalPlaces("qty")); // use a pre-existing QValidator: _orderQty->setPrecision(omfgThis->qtyVal());
When you want to display data in a table form on screen, use the XTreeWidget. There is documentation on the wiki describing how to use it, including a section called Numeric Formatting. Also search for 'xtnumericrole' on the wiki page. The widget uses the decimalPlaces function internally, so any of the special strings you see in the XTreeWidget documentation for xtnumericrole can also be used if you use XLabel::setPrecision(decimalPlaces()).
Reports have special requirements because OpenRPT is a separate open-source project from xTuple ERP/PostBooks. OpenRPT cannot have too many dependencies on the other xTuple projects or it won't be able to stand alone. When writing queries for OpenRPT, you'll need to explicitly format the numeric values as text strings for presentation. xTuple databases all have special stored procedures to help you based on the user's locale:
Keep in mind when writing reports that the subtotal feature works on numeric values but not on formatted numbers. If you want to subtotal a column, write your query to select both the raw number from the database and the formatted value. In the line detail write the formatted value but calculate the subtotal on the raw value.
For example, if you write a MetaSQL query that will be shared by a display window and a report definition, write something like this:
SELECT coitem_qtyord, -- raw value formatQty(coitem_qtyord) AS f_qtyord, -- format for OpenRPT 'qty' AS coitem_qtyord_xtnumericrole, -- format for XTreeWidget ... FROM coitem ...
Set the XTreeWidget's column name to coitem_qtyord, set the report definition to subtotal on coitem_qtyord, and set the report definition to write f_qtyord on the report's detail output lines.
Dates, like numeric values, are handled by a combination of Qt and xTuple code. For date display and entry, use either xTuple's DLineEdit for a single date or DateCluster for a pair of start and end dates. These automatically adjust to display and accept localized dates. The XTreeWidget also adjusts to the current user's locale to format dates appropriately.
OpenRPT reports require special handling for dates for the same reasons as stated above for numeric values. Again, xTuple supplies formatting functions to help:
For now see the XTreeWidget's description and search for 'Colors'.