Development/Tutorials/Localization/i18n: Difference between revisions

From KDE TechBase
 
(38 intermediate revisions by 21 users not shown)
Line 1: Line 1:
{{TutorialBrowser|
{{TutorialBrowser|


Line 20: Line 21:
* textual messages that are displayed to the user
* textual messages that are displayed to the user
* data input from the user, files and other sources
* data input from the user, files and other sources
* format of dates, numbers, currency, dates, etc.
* format of dates, numbers, currency, etc.


Localization, or l10n ('l', followed by 10 characters, then an 'n'), is the process of taking an internationalized application and adapting it for a specific locale.
Localization, or l10n ('l', followed by 10 characters, then an 'n'), is the process of taking an internationalized application and adapting it for a specific locale.


Generaly speaking, programmers internationalize their applications and translation teams localize them.
Generally speaking, programmers internationalize their applications and translation teams localize them.


== Why is This Important? ==
== Why is This Important? ==
Line 30: Line 31:
KDE development happens primarily in English as this allows the broadest reach into the development and translation communities. However, English is not the primary language of most people on the planet. In fact, fewer than 8% of humanity speaks English and less than 5% speak it as their mother tongue. Even on the Internet, only 35% people who are online use English as their primary language and as more and more of the world gets wired this number is only decreasing. Additionally most languages, including 9 out of the 10 most common languages, use non-ASCII characters in their written form. It is easy to see, then, why it has become a necessity to provide localized software.  
KDE development happens primarily in English as this allows the broadest reach into the development and translation communities. However, English is not the primary language of most people on the planet. In fact, fewer than 8% of humanity speaks English and less than 5% speak it as their mother tongue. Even on the Internet, only 35% people who are online use English as their primary language and as more and more of the world gets wired this number is only decreasing. Additionally most languages, including 9 out of the 10 most common languages, use non-ASCII characters in their written form. It is easy to see, then, why it has become a necessity to provide localized software.  


As an international project that spans the globe such localization is a core value within the KDE culture. In fact, while many KDE developers write their software in English they use the desktop in their native locale.
As an international project that spans the globe, such localization is a core value within the KDE culture. In fact, while many KDE developers write their software in English they use the desktop in their native locale.


== Translatable Code Using i18n() ==
== Translatable Code Using i18n() ==
Line 38: Line 39:
KDE provides the <tt>KLocale</tt> class as part of <tt>libkdecore</tt> to facilitate the technical details of localization. KLocale makes it as easy as possible for developers to make their code i18n aware, but there are some things you need to be aware of so that applications are usable in other languages and countries.
KDE provides the <tt>KLocale</tt> class as part of <tt>libkdecore</tt> to facilitate the technical details of localization. KLocale makes it as easy as possible for developers to make their code i18n aware, but there are some things you need to be aware of so that applications are usable in other languages and countries.


Access to a global <tt>KLocale</tt> object is provided via <tt>KGlobal::locale()</tt>. This <tt>KLocale</tt> object is created automatically by <tt>KInstance</tt> for you and take care of all user i18n related settings. It is deleted automatically on application exit.
Access to a global <tt>KLocale</tt> object is provided via <tt>KGlobal::locale()</tt>. This <tt>KLocale</tt> object is created automatically by <tt>KInstance</tt> and takes care of all user i18n related settings. It is deleted automatically on application exit.


Translations are made possible by the <tt>QString i18n(const char*)</tt> method which you must wrap all strings that should be displayed in. The QString returned by <tt>i18n()</tt> is the translated (if necessary) string. This makes creating translatable widgets as simple as in this example:
Translations are made possible by the <tt>QString i18n(const char*)</tt> method, defined in <tt>klocalizedstring.h</tt>, which you must wrap all strings that should be displayed in. The QString returned by <tt>i18n()</tt> is the translated (if necessary) string. This makes creating translatable widgets as simple as in this example:


<code cppqt>
<syntaxhighlight lang="cpp-qt">
QPushButton* myButton = new QPushButton(i18n("Translate this!"));</code>
#include <klocalizedstring.h>
[...]
QPushButton* myButton = new QPushButton(i18n("Translate this!"));
</syntaxhighlight>


QString's native Unicode support ensures that all translations are represented correctly. All string handling done by your application should therefore use QString.
QString's native Unicode support ensures that all translations are represented correctly. All string handling done by your application should therefore use QString.


{{tip|If the string to be translated contains any non-UTF8 characters, use the utf8() method to get a char*.}}
{{tip|If the string to be translated contains any non-UTF8 characters, use the <tt>utf8()</tt> method to get a <tt>char*</tt>.}}


=== I18N_NOOP ===
=== ki18n ===


The <tt>i18n()</tt> method requires that a <tt>KInstance</tt> (e.g. <tt>KApplication</tt>) has been created. For any strings that are created prior  to this there is a macro provided: I18N_NOOP(). This allows one to mark strings that should be translated later as such.
The <tt>i18n()</tt> method requires that a <tt>KInstance</tt> (e.g. <tt>KApplication</tt>) has been created. For any strings that are created prior  to this there is another method provided: <tt>ki18n()</tt>. This allows one to mark strings that should be translated later as such. The ki18n() will return a <tt>KLocalizedString</tt>, which can be finalized into a QString (i.e. translated for real) after the KInstance has been created, using its toString() method.


When you want to actually translate the string at runtime, you still have to use i18n() with exactly the same string. I18N_NOOP() is typically used for strings given to KAboutData, because it is constructed before the KApplication and you can use i18n() only after the construction of the KApplication. Other than these special cases, it is always safe to use i18n() if you are sure that the code will be executed after construction of KApplication or some other KInstance.  
<tt>ki18n()</tt> is typically used for strings given to KAboutData, because it is constructed before the KApplication and you can use i18n() only after the construction of the KApplication. Other than these special cases, it is always safe to use i18n() if you are sure that the code will be executed after construction of KApplication or some other KInstance.


=== Adding Context ===
=== Adding Context with i18nc() ===


There is an extended version of <tt>i18n()</tt>, <tt>i18nc()</tt> which takes two <tt>const char*</tt> arguments. The first argument is an additional contextual description of the second string which will be translated. The first  string is used to find the proper corresponding translation at run-time and is shown to translators to help them understand the meaning of the string.  
There is an extended method, <tt>i18nc()</tt> which takes two <tt>const char*</tt> arguments. The first argument is an additional contextual description of the second string which will be translated. The first  string is used to find the proper corresponding translation at run-time and is shown to translators to help them understand the meaning of the string.  


Use <tt>i18nc()</tt> whenever the purpose of the text might be ambiguous without further context. For example, consider a context menu in a file manager with an entry called "View" which opens a viewer for the currently selected file. In this context "View" is a verb. However, the same application also may have a menu called "View" in the menubar. In that context "View" is a noun. In the English version of the application everything looks fine, but in most other languages one of the two "View" strings will be incorrect.
Use <tt>i18nc()</tt> whenever the purpose of the text might be ambiguous without further context. For example, consider a context menu in a file manager with an entry called "View" which opens a viewer for the currently selected file. In this context "View" is a verb. However, the same application also may have a menu called "View" in the menubar. In that context "View" is a noun. In the English version of the application everything looks fine, but in most other languages one of the two "View" strings will be incorrect.


Additionally, translators sometimes need extra help in understanding what the text is actually referring to during the translation purpose.
Additionally, translators sometimes need extra help in understanding what the text is actually referring to during the translation process.


In the file manager example above, one might therefore write:
In the file manager example above, one might therefore write:
    
    
<code cppqt>contextMenu->addAction(i18nc("verb, to view something", "View"));
<syntaxhighlight lang="cpp-qt">
viewMenu->addAction(i18nc("noun, the view", "View"));</code>
contextMenu->addAction(i18nc("verb, to view something", "View"));
viewMenu->addAction(i18nc("noun, the view", "View"));
</syntaxhighlight>


Now the two strings will be properly translatable, both by the human translators and at runtime by KLocale.
Now the two strings will be properly translatable, both by the human translators and at runtime by KLocale.
Line 72: Line 78:
Use this form of i18n whenever the string to translate is short or the meaning is hard to discern when the context is not exactly known. For example:
Use this form of i18n whenever the string to translate is short or the meaning is hard to discern when the context is not exactly known. For example:


<code cppqt>QString up = i18nc("Go one directory up in the hierarchy", "Up");
<syntaxhighlight lang="cpp-qt">
QString relation = i18nc("A person's name and their familial relationship to you.", "%1 is your %2", name, relationship);</code>
QString up = i18nc("Go one directory up in the hierarchy", "Up");
QString relation = i18nc("A person's name and their familial relationship to you.", "%1 is your %2", name, relationship);
</syntaxhighlight>


{{note|There is also a I18N_NOOP2("context","text") macro for providing untranslated strings with a context. You must, however, use i18nc later to retrieve the translated string.}}
{{note|There is also a <tt>ki18nc("context","text")</tt> method for providing context to strings which are constructed before the KInstance. It returns a KLocalizedString, so use the <tt>toString()</tt> method afterwards to convert it into a <tt>QString</tt>.}}
 
Contexts can also be added when building forms in Qt Designer. Each widget label, including tooltips and whatsthis texts, has a "disambiguation" attribute (named "comment" prior to Qt 4.5), which will serve the same purpose as first argument to <tt>i18nc()</tt> call.
 
KDE provides a standard set of strings to identify the semantic context of translatable strings. These are defined in the [[Development/Tutorials/Localization/i18n_Semantics|KUIT Semantic Markup scheme]].


=== Standard Context For Common Phrases ===
=== Standard Context For Common Phrases ===
Line 81: Line 93:
Below is a chart showing some common words and phrases in English and the context that must be used with them to ensure proper translation of them in other languages.
Below is a chart showing some common words and phrases in English and the context that must be used with them to ensure proper translation of them in other languages.


{|
{| border="1"
|+ Standard Contexts
|+ Standard Contexts
|-
|-
! Phrase !! Context !! i18nc Call
! Phrase !! Context !! i18nc Call !! Example
|-
| Busy || Referring to a person || <tt>i18nc("A person is busy", "Busy")</tt>
|-
| Busy || Referring to a thing || <tt>i18nc("A thing is busy", "Busy")</tt>
|-
| Color || Color mode, as opposed to Grayscale || <tt>i18nc("Not Grayscale", "Color")</tt>
|-
| Creator || Referring to a person || <tt>i18nc("A person who creates", "Creator")</tt>
|-
| Creator || Referring to software || <tt>i18nc("Software", "Creator")</tt>
|-
| Display || Referring to hardware || <tt>i18nc("Hardware display", "Display")</tt>
|-
| Editor || Referring to a person || <tt>i18nc("A person who edits", "Editor")</tt>
|-
| Editor || Referring to software || <tt>i18nc("Software", "Editor")</tt>
|-
| Line || Referring to drawing || <tt>i18nc("Draw a line", "Line")</tt>
|-
| Line || Referring to text || <tt>i18nc("Line of text", "Line")</tt>
|-
| Name || Referring to a name of thing || <tt>i18nc("A thing's name", "Name")</tt> || In theme change dialog: <tt>i18nc("Theme name", "Name")</tt>
|-
| Name || Referring to first name and last name of person || <tt>i18nc("Person's first and last name", "Name")</tt> || In KAddessbook contact edit dialog: <tt>i18nc("Person's first and last name", "Name")</tt>
|-
| New || Create something || <tt>i18nc("Action", "New")</tt>
|-
| New || Status || <tt>i18nc("New mail message", "New")</tt>
|-
| No || Answer to a question || <tt>i18nc("Answer to a question", "No")</tt>
|-
| No || Availability of a thing || <tt>i18nc("Availability", "No")</tt>
|-
| (Re)load || (Re)load a document, medium etc. || <tt>i18nc("(Re)load a document", "(Re)load")</tt>
|-
| (Re)load || (Re)start a program, daemon etc. || <tt>i18nc("(Re)start a program", "(Re)load")</tt>
|-
| Title || Referring to a person || <tt>i18nc("A person's title", "Title")</tt>
|-
| Title || Referring to a thing || <tt>i18nc("A thing's title", "Title")</tt>
|-
| Trash || Referring to the action of emptying || <tt>i18nc("The trash is not empty. Empty it", "Empty")</tt>
|-
| Trash || Referring to the state of being empty || <tt>i18nc("The trash is empty. This is not an action, but a state", "Empty")</tt>
|-
|-
| Title || Refering to a person || <tt>i18nc("A person's title", "Title")</tt>
| Volume || Referring to sound || <tt>i18nc("Sound volume", "Volume")</tt>
|-
|-
| Title || Refering to a thing || <tt>i18nc("A thing's title", "Title")</tt>
| Volume || Referring to a filesystem || <tt>i18nc("Filesystem volume", "Volume")</tt>
|-
|-
| Name || Refering to a name of thing || <tt>i18nc("A thing's name", "Name")</tt>
| Volume || Referring to books || <tt>i18nc("Book volume", "Volume")</tt>
|-
|-
| Name || Refering to first name and last name of person || <tt>i18nc("Person's first and last name", "Name")</tt>
| Yes || Answer to a question || <tt>i18nc("Answer to a question", "Yes")</tt>
|-
| Yes || Availability of a thing|| <tt>i18nc("Availability", "Yes")</tt>
|}
|}
=== Standard Context for Special Format Strings ===
* The context '''(qtdt-format)''' should be used for strings that are used as '''displayFormat''' in '''QDateTimeEdit''' or '''QTimeEdit'''.
* The context '''(qtundo-format)''' should be used for strings that are used as '''text''' in '''QUndoCommand'''. If the application depends on Qt 4.8+, translators can use the "double-form functionality": if you put two strings separated by "\n" in that text, the first line will be used in "Undo History" panel, and the second line will be used in "Edit -> Undo/Redo %1" menu items.


=== Plurals ===
=== Plurals ===


Plurals are handled differently from language to language. Many languages have different plurals for 2, 10, 20, 100, etc. When the string you want translated refers to more than one item, you must use the third form of <tt>i18n</tt>,  the <tt>i18np()</tt>. It takes the singular and plural English forms as its first two arguments, followed by a number for <tt>%n</tt> placeholder, and then the arguments for other placeholders. For example:
Plurals are handled differently from language to language. Many languages have different plurals for 2, 10, 20, 100, etc. When the string you want translated refers to more than one item, you must use the third form of <tt>i18n</tt>,  the <tt>i18np()</tt>. It takes the singular and plural English forms as its first two arguments, followed by any substitution arguments as usual, but at least one of which should be integer-valued. For example:


<code cppqt>msgStr = i18np("1 image in album %1", "%n images in album $1", numImages, albumName);</code>
<syntaxhighlight lang="cpp-qt">
msgStr = i18np("1 image in album %2", "%1 images in album %2", numImages, albumName);
msgStr = i18np("Delete Group", "Delete Groups", numGroups);
</syntaxhighlight>


<tt>i18np()</tt> gets expanded to as many cases as required by the user's language. In English, this is just two forms while in other languages it may be more depending on the value of the <tt>number</tt> parameter.
<tt>i18np()</tt> gets expanded to as many cases as required by the user's language. In English, this is just two forms while in other languages it may be more, depending on the value of the first integer-valued argument.


Note that this form should be used even if the string always refers to more than one item as languages use a singular form even when referring to a multiple (typically for 21, 31, etc.). This code:
Note that this form should be used even if the string always refers to more than one item as some languages use a singular form even when referring to a multiple (typically for 21, 31, etc.). This code:


<code cppqt>i18n("%1 files were deleted", numFilesDeleted);</code>
<syntaxhighlight lang="cpp-qt">
i18n("%1 files were deleted", numFilesDeleted);
</syntaxhighlight>


is therefore incorrect and should instead be:
is therefore incorrect and should instead be:


<code cppqt>i18np("%1 files were deleted",  
<syntaxhighlight lang="cpp-qt">
i18np("1 file was deleted",  
     "%1 files were deleted",
     "%1 files were deleted",
     numFilesDeleted);</code>
     numFilesDeleted);
</syntaxhighlight>


To provide context as well as pluralization, use <tt>i18ncp</tt> as in this example:
To provide context as well as pluralization, use <tt>i18ncp</tt> as in this example:


<code cppqt>i18ncp("Personal file", "1 file", "%n files", numFiles);</code>
<syntaxhighlight lang="cpp-qt">
i18ncp("Personal file", "1 file", "%1 files", numFiles);
</syntaxhighlight>
 
In some cases pluralization is needed even if English does not need it. For example:
 
<syntaxhighlight lang="cpp-qt">
i18nc("%1 is a comma separated list of platforms the software is available on.",
      "Available on %1", platforms);
</syntaxhighlight>
 
One reason for this is that in some languages the preposition "on" needs to be replaced with a descriptive noun in a certain form, which essentially in English means the same as this:
 
<syntaxhighlight lang="cpp-qt">
i18nc("%1 is a comma separated list of platforms the software is available on.",
      "Available on platforms %1", platforms);
</syntaxhighlight>
 
And that is why the correct way is to use pluralization here as well:
 
<syntaxhighlight lang="cpp-qt">
i18ncp("%2 is a comma separated list of platforms the software is available on.",
      "Available on %2", "Available on %2", numberOfPlatforms, platforms);
</syntaxhighlight>


== Formatting Dates and Numbers ==
== Formatting Dates and Numbers ==
Line 126: Line 219:
! Formats&nbsp;a.. !! From&nbsp;a.. !! Function&nbsp;Prototype
! Formats&nbsp;a.. !! From&nbsp;a.. !! Function&nbsp;Prototype
|-
|-
| Number || String || <pre>QString formatNumber( const QString & numStr )</pre>
| Number || String || <syntaxhighlight lang="cpp-qt">QString formatNumber( const QString & numStr )</syntaxhighlight>
|-
|-
| Number || Integer,&nbsp;double || <pre>formatNumber( double num,  
| Number || Integer,&nbsp;double || <syntaxhighlight lang="cpp-qt">formatNumber( double num,  
               int precision = -1 )</pre>
               int precision = -1 )</syntaxhighlight>
|-
|-
| Money || String || <pre>formatMoney( const QString & numStr )</pre>
| Money || String || <syntaxhighlight lang="cpp-qt">formatMoney( const QString & numStr )</syntaxhighlight>
|-
|-
| Money || Number || <pre>formatMoney( double num,  
| Money || Number || <syntaxhighlight lang="cpp-qt">formatMoney( double num,  
             const QString & currency,
             const QString & currency,
             int digits = -1 )</pre>
             int digits = -1 )</syntaxhighlight>
|-
|-
| Date || String || <pre>formatDate( const QDate & pDate,
| Date || String || <syntaxhighlight lang="cpp-qt">formatDate( const QDate & pDate,
             bool shortFormat=false )</pre>
             bool shortFormat=false )</syntaxhighlight>
|-
|-
| Time || QTime || <pre>formatTime( const QTime & pTime,  
| Time || QTime || <syntaxhighlight lang="cpp-qt">formatTime( const QTime & pTime,  
             bool includeSecs=false)</pre>
             bool includeSecs=false)</syntaxhighlight>
|-
|-
| Date&nbsp;and&nbsp;time || QDateTime || <pre>formatDateTime( const QDateTime &pDateTime,
| Date&nbsp;and&nbsp;time || QDateTime || <syntaxhighlight lang="cpp-qt">formatDateTime( const QDateTime &pDateTime,
                 bool shortFormat = true,
                 bool shortFormat = true,
                 bool includeSecs = false )</pre>
                 bool includeSecs = false )</syntaxhighlight>
|}   
|}   
   
   
Similar functions exist to read information provided by the user at runtime in their localized format, e.g. readNumber() or readMoney().
Similar functions exist to read information provided by the user at runtime in their localized format, e.g. <tt>readNumber()</tt> or <tt>readMoney()</tt>.


== Calendaring ==
== Calendaring ==


Developing applications dealing with dates and time, such as calendars, is a very complex area. Not only may the displayed string containing a date or time may look different based on locale, but one also has to take care of other aspects such as:
Developing applications dealing with dates and time, such as calendars, is a very complex area. Not only may the displayed string containing a date or time look different based on locale, but one also has to take care of other aspects such as:
* what calendar system is being used (e.g. Hebrew, Hijri)
* which day in the week is the first one (cf int weekStartDay())  
* which day in the week is the first one (cf int weekStartDay())  
* how many months in a year there are  
* how many months in a year there are  
* "era"-based calendars  
* "era"-based calendars  
* whether to use 24-hour time format (cf bool use12Clock())  
* whether to use 24-hour time format (cf bool use12Clock())  
QDate only implements a hybrid Julian/Gregorian calendar system, if the user's locale has a different calendar system then any direct calls to QDate will result in incorrect dates being read and displayed, and incorrect date maths being performed.  All date calculations and formatting must be performed through the KLocale and KCalendarSystem methods which provide a full set of methods matching those available in QDate.  The current locale calendar system can be accessed via KGlobal::locale()->calendar().  The KLocale date formatting methods will always point to the current global calendar system.


KLocale provides, among others, these methods:  
KLocale provides, among others, these methods:  
Line 165: Line 261:
! Formats&nbsp;a.. !! From&nbsp;a.. !! Function Prototype
! Formats&nbsp;a.. !! From&nbsp;a.. !! Function Prototype
|-
|-
| Date || QDate || <pre>formatDate( const QDate & pDate,
| Date || QDate || <syntaxhighlight lang="cpp-qt">formatDate( const QDate & pDate,
             bool shortFormat=false )</pre>
             bool shortFormat=false )</syntaxhighlight>
|-
|-
| Time || QTime || <pre>formatTime( const QTime & pTime,
| Time || QTime || <syntaxhighlight lang="cpp-qt">formatTime( const QTime & pTime,
             bool includeSecs=false )</pre>
             bool includeSecs=false )</syntaxhighlight>
|-
|-
| Date&nbsp;and&nbsp;time || QDateTime || <pre>formatDateTime( const QDateTime &pDateTime,
| Date&nbsp;and&nbsp;time || QDateTime || <syntaxhighlight lang="cpp-qt">formatDateTime( const QDateTime &pDateTime,
                 bool shortFormat=true,
                 bool shortFormat=true,
                 bool includeSecs=false )</pre>
                 bool includeSecs=false )</syntaxhighlight>
|}
|}


{{improve|provide more info on the different calendar systems}}
{{improve|provide more info on the different calendar systems}}
== QML ==
To make QML code translatable, <tt>KDeclarative </tt> provides the same <tt>i18n()</tt> calls described above. To enable parsing at runtime, you need to install a <tt>KDeclarative</tt> object in the <tt>QQmlEngine</tt>:
<syntaxhighlight lang="cpp-qt">
KDeclarative kdeclarative;
//view refers to the QQmlView
kdeclarative.setDeclarativeEngine(view.engine());
kdeclarative.initialize();
//binds things like kconfig and icons
kdeclarative.setupBindings();
</syntaxhighlight>
The application also needs to link to <tt>libkdeclarative</tt>.
If you don't need the rest of features in KDeclarative, you can use ki18n right away:
<syntaxhighlight lang="cpp-qt">
    engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
</syntaxhighlight>


== Avoiding Common Traps ==
== Avoiding Common Traps ==

Latest revision as of 19:41, 10 February 2019

Writing Applications With Localization In Mind
Tutorial Series   Localization
Previous   Introduction to Unicode is recommended, though not required
What's Next   Avoiding Common Localization Pitfalls
Further Reading   n/a

Abstract

Reaching a broad audience of users and developers requires that your software can be translated and otherwise shaped at runtime to be linguistically and culturally relevant to whomever is sitting in front of the computer. This is the realm of localization and this tutorial steps you through what is needed to make your application localizable.

What is Internationalization and Localization?

Internationalization, or i18n ('i', followed by 18 letters, then an 'n'), is the process of writing your application so that it can be run in any locale. This means taking into account such things as:

  • textual messages that are displayed to the user
  • data input from the user, files and other sources
  • format of dates, numbers, currency, etc.

Localization, or l10n ('l', followed by 10 characters, then an 'n'), is the process of taking an internationalized application and adapting it for a specific locale.

Generally speaking, programmers internationalize their applications and translation teams localize them.

Why is This Important?

KDE development happens primarily in English as this allows the broadest reach into the development and translation communities. However, English is not the primary language of most people on the planet. In fact, fewer than 8% of humanity speaks English and less than 5% speak it as their mother tongue. Even on the Internet, only 35% people who are online use English as their primary language and as more and more of the world gets wired this number is only decreasing. Additionally most languages, including 9 out of the 10 most common languages, use non-ASCII characters in their written form. It is easy to see, then, why it has become a necessity to provide localized software.

As an international project that spans the globe, such localization is a core value within the KDE culture. In fact, while many KDE developers write their software in English they use the desktop in their native locale.

Translatable Code Using i18n()

To ensure your application is ready to be localized you have to follow a few simple rules. All user-visible strings in your application should be translated before they are displayed on the user's screen, exceptions to this being debugging messages, configuration keys and similar types of text data.

KDE provides the KLocale class as part of libkdecore to facilitate the technical details of localization. KLocale makes it as easy as possible for developers to make their code i18n aware, but there are some things you need to be aware of so that applications are usable in other languages and countries.

Access to a global KLocale object is provided via KGlobal::locale(). This KLocale object is created automatically by KInstance and takes care of all user i18n related settings. It is deleted automatically on application exit.

Translations are made possible by the QString i18n(const char*) method, defined in klocalizedstring.h, which you must wrap all strings that should be displayed in. The QString returned by i18n() is the translated (if necessary) string. This makes creating translatable widgets as simple as in this example:

#include <klocalizedstring.h>
[...]
QPushButton* myButton = new QPushButton(i18n("Translate this!"));

QString's native Unicode support ensures that all translations are represented correctly. All string handling done by your application should therefore use QString.

Tip
If the string to be translated contains any non-UTF8 characters, use the utf8() method to get a char*.


ki18n

The i18n() method requires that a KInstance (e.g. KApplication) has been created. For any strings that are created prior to this there is another method provided: ki18n(). This allows one to mark strings that should be translated later as such. The ki18n() will return a KLocalizedString, which can be finalized into a QString (i.e. translated for real) after the KInstance has been created, using its toString() method.

ki18n() is typically used for strings given to KAboutData, because it is constructed before the KApplication and you can use i18n() only after the construction of the KApplication. Other than these special cases, it is always safe to use i18n() if you are sure that the code will be executed after construction of KApplication or some other KInstance.

Adding Context with i18nc()

There is an extended method, i18nc() which takes two const char* arguments. The first argument is an additional contextual description of the second string which will be translated. The first string is used to find the proper corresponding translation at run-time and is shown to translators to help them understand the meaning of the string.

Use i18nc() whenever the purpose of the text might be ambiguous without further context. For example, consider a context menu in a file manager with an entry called "View" which opens a viewer for the currently selected file. In this context "View" is a verb. However, the same application also may have a menu called "View" in the menubar. In that context "View" is a noun. In the English version of the application everything looks fine, but in most other languages one of the two "View" strings will be incorrect.

Additionally, translators sometimes need extra help in understanding what the text is actually referring to during the translation process.

In the file manager example above, one might therefore write:

contextMenu->addAction(i18nc("verb, to view something", "View"));
viewMenu->addAction(i18nc("noun, the view", "View"));

Now the two strings will be properly translatable, both by the human translators and at runtime by KLocale.

Use this form of i18n whenever the string to translate is short or the meaning is hard to discern when the context is not exactly known. For example:

QString up = i18nc("Go one directory up in the hierarchy", "Up");
QString relation = i18nc("A person's name and their familial relationship to you.", "%1 is your %2", name, relationship);
Note
There is also a ki18nc("context","text") method for providing context to strings which are constructed before the KInstance. It returns a KLocalizedString, so use the toString() method afterwards to convert it into a QString.


Contexts can also be added when building forms in Qt Designer. Each widget label, including tooltips and whatsthis texts, has a "disambiguation" attribute (named "comment" prior to Qt 4.5), which will serve the same purpose as first argument to i18nc() call.

KDE provides a standard set of strings to identify the semantic context of translatable strings. These are defined in the KUIT Semantic Markup scheme.

Standard Context For Common Phrases

Below is a chart showing some common words and phrases in English and the context that must be used with them to ensure proper translation of them in other languages.

Standard Contexts
Phrase Context i18nc Call Example
Busy Referring to a person i18nc("A person is busy", "Busy")
Busy Referring to a thing i18nc("A thing is busy", "Busy")
Color Color mode, as opposed to Grayscale i18nc("Not Grayscale", "Color")
Creator Referring to a person i18nc("A person who creates", "Creator")
Creator Referring to software i18nc("Software", "Creator")
Display Referring to hardware i18nc("Hardware display", "Display")
Editor Referring to a person i18nc("A person who edits", "Editor")
Editor Referring to software i18nc("Software", "Editor")
Line Referring to drawing i18nc("Draw a line", "Line")
Line Referring to text i18nc("Line of text", "Line")
Name Referring to a name of thing i18nc("A thing's name", "Name") In theme change dialog: i18nc("Theme name", "Name")
Name Referring to first name and last name of person i18nc("Person's first and last name", "Name") In KAddessbook contact edit dialog: i18nc("Person's first and last name", "Name")
New Create something i18nc("Action", "New")
New Status i18nc("New mail message", "New")
No Answer to a question i18nc("Answer to a question", "No")
No Availability of a thing i18nc("Availability", "No")
(Re)load (Re)load a document, medium etc. i18nc("(Re)load a document", "(Re)load")
(Re)load (Re)start a program, daemon etc. i18nc("(Re)start a program", "(Re)load")
Title Referring to a person i18nc("A person's title", "Title")
Title Referring to a thing i18nc("A thing's title", "Title")
Trash Referring to the action of emptying i18nc("The trash is not empty. Empty it", "Empty")
Trash Referring to the state of being empty i18nc("The trash is empty. This is not an action, but a state", "Empty")
Volume Referring to sound i18nc("Sound volume", "Volume")
Volume Referring to a filesystem i18nc("Filesystem volume", "Volume")
Volume Referring to books i18nc("Book volume", "Volume")
Yes Answer to a question i18nc("Answer to a question", "Yes")
Yes Availability of a thing i18nc("Availability", "Yes")

Standard Context for Special Format Strings

  • The context (qtdt-format) should be used for strings that are used as displayFormat in QDateTimeEdit or QTimeEdit.
  • The context (qtundo-format) should be used for strings that are used as text in QUndoCommand. If the application depends on Qt 4.8+, translators can use the "double-form functionality": if you put two strings separated by "\n" in that text, the first line will be used in "Undo History" panel, and the second line will be used in "Edit -> Undo/Redo %1" menu items.

Plurals

Plurals are handled differently from language to language. Many languages have different plurals for 2, 10, 20, 100, etc. When the string you want translated refers to more than one item, you must use the third form of i18n, the i18np(). It takes the singular and plural English forms as its first two arguments, followed by any substitution arguments as usual, but at least one of which should be integer-valued. For example:

msgStr = i18np("1 image in album %2", "%1 images in album %2", numImages, albumName);
msgStr = i18np("Delete Group", "Delete Groups", numGroups);

i18np() gets expanded to as many cases as required by the user's language. In English, this is just two forms while in other languages it may be more, depending on the value of the first integer-valued argument.

Note that this form should be used even if the string always refers to more than one item as some languages use a singular form even when referring to a multiple (typically for 21, 31, etc.). This code:

i18n("%1 files were deleted", numFilesDeleted);

is therefore incorrect and should instead be:

i18np("1 file was deleted", 
     "%1 files were deleted",
     numFilesDeleted);

To provide context as well as pluralization, use i18ncp as in this example:

i18ncp("Personal file", "1 file", "%1 files", numFiles);

In some cases pluralization is needed even if English does not need it. For example:

i18nc("%1 is a comma separated list of platforms the software is available on.",
      "Available on %1", platforms);

One reason for this is that in some languages the preposition "on" needs to be replaced with a descriptive noun in a certain form, which essentially in English means the same as this:

i18nc("%1 is a comma separated list of platforms the software is available on.",
      "Available on platforms %1", platforms);

And that is why the correct way is to use pluralization here as well:

i18ncp("%2 is a comma separated list of platforms the software is available on.",
       "Available on %2", "Available on %2", numberOfPlatforms, platforms);

Formatting Dates and Numbers

When displaying a number to the user, your program must take care of the decimal separator, thousand separator and currency symbol (if any) being used. These symbols differ from region to region. In English speaking countries a dot (.) is used to separate the fractional part of a number, while in some European countries a comma (,) is used instead. Below is a short summary of functions that will help you format the numbers correctly, taking the local conventions into account for you.

Functions to Format Numbers
Formats a.. From a.. Function Prototype
Number String
QString formatNumber( const QString & numStr )
Number Integer, double
formatNumber( double num, 
              int precision = -1 )
Money String
formatMoney( const QString & numStr )
Money Number
formatMoney( double num, 
             const QString & currency,
             int digits = -1 )
Date String
formatDate( const QDate & pDate,
            bool shortFormat=false )
Time QTime
formatTime( const QTime & pTime, 
            bool includeSecs=false)
Date and time QDateTime
formatDateTime( const QDateTime &pDateTime,
                bool shortFormat = true,
                bool includeSecs = false )

Similar functions exist to read information provided by the user at runtime in their localized format, e.g. readNumber() or readMoney().

Calendaring

Developing applications dealing with dates and time, such as calendars, is a very complex area. Not only may the displayed string containing a date or time look different based on locale, but one also has to take care of other aspects such as:

  • what calendar system is being used (e.g. Hebrew, Hijri)
  • which day in the week is the first one (cf int weekStartDay())
  • how many months in a year there are
  • "era"-based calendars
  • whether to use 24-hour time format (cf bool use12Clock())

QDate only implements a hybrid Julian/Gregorian calendar system, if the user's locale has a different calendar system then any direct calls to QDate will result in incorrect dates being read and displayed, and incorrect date maths being performed. All date calculations and formatting must be performed through the KLocale and KCalendarSystem methods which provide a full set of methods matching those available in QDate. The current locale calendar system can be accessed via KGlobal::locale()->calendar(). The KLocale date formatting methods will always point to the current global calendar system.

KLocale provides, among others, these methods:

Calendar Data Functions
Formats a.. From a.. Function Prototype
Date QDate
formatDate( const QDate & pDate,
            bool shortFormat=false )
Time QTime
formatTime( const QTime & pTime,
            bool includeSecs=false )
Date and time QDateTime
formatDateTime( const QDateTime &pDateTime,
                bool shortFormat=true,
                bool includeSecs=false )
Warning
This section needs improvements: Please help us to

cleanup confusing sections and fix sections which contain a todo


provide more info on the different calendar systems

QML

To make QML code translatable, KDeclarative provides the same i18n() calls described above. To enable parsing at runtime, you need to install a KDeclarative object in the QQmlEngine:

KDeclarative kdeclarative;
//view refers to the QQmlView
kdeclarative.setDeclarativeEngine(view.engine());
kdeclarative.initialize();
//binds things like kconfig and icons
kdeclarative.setupBindings();

The application also needs to link to libkdeclarative.

If you don't need the rest of features in KDeclarative, you can use ki18n right away:

    engine.rootContext()->setContextObject(new KLocalizedContext(&engine));

Avoiding Common Traps

There are a number of common problems that may prevent an application being properly localized. See Avoiding Common Localization Pitfalls to learn more about them, and how to avoid them.

Note
Thanks to Lukáš Tinkl, Matthias Kiefer and Gary Cramblitt for writing the original version of this tutorial.