[[PageOutline(1-100, Table of Contents)]] = Internationalizing GUI Files = == Internationalizing Captions and Tooltips == To internationalize a caption or tooltip (or any other XML attribute) of a GUI XML `object` element, remove the attribute from the `object` element that contains it, and add a new `translatableAttribute` element within the `object` element that used to contain the old attribute, with the name of the old attribute as the value of the `id` attribute of this new element, and the value of the old attribute as the content of this new element. ''' Note:''' Use "caption" as id of translatableAttribute to translate the caption of an object element. '''Original:''' {{{ Options }}} '''Internationalized:''' {{{ Options  Adjust game settings. }}} == Internationalizing Captions By Parts == You might need to internationalize only parts of a caption. This is the case when, for translators’ sake, you want to mark each paragraph of a multiline caption for translation, or when you want to strip out formatting content (such as "[font]" tags) from the translatable content. The solution here is to use the `attribute` element in combination with `keep` and `translate` elements as follows: '''Original:''' {{{ This is a paragraph. This is another paragraph. }}} '''Internationalized:''' {{{ This is a paragraph. \n\n This is another paragraph. }}} == Internationalizing !JavaScript Code Within GUI Files == Simply put: '''you cannot'''. You can call any of the global internationalization functions described below, however, strings passed to these functions are not parsed by the message extraction system, which means that translators won’t be able to actually translate the string. Move any !JavaScript code that requires internationalization of “hard-coded” strings into a function of a separate !JavaScript file. The message extraction system will successfully extract !JavaScript strings from !JavaScript files. You can then call that function from your GUI file. For example: '''Original:''' XML file: {{{ this.caption = "Build:" + ``Engine.GetBuildTime(); }}} '''Wrong:''' XML file: {{{ this.caption = sprintf(translate("Build: %(buildTime)s"), { buildTime: Engine.GetBuildTime() }); }}} '''Internationalized:''' XML file: {{{ this.caption = getBuildTimeString() }}} !JavaScript file included by the GUI file: {{{ function getBuildTimeString() { return sprintf(translate("Build: %(buildTime)s"), { buildTime: Engine.GetBuildTime() }); } }}} = Internationalizing !JavaScript Files = == Internationalizing Strings in !JavaScript == To internationalize a string in a 0 A.D. !JavaScript file, simply use the following global functions: {{{ translate(message); translatePlural(singularMessage, pluralMessage, number); translateWithContext(context, message); translatePluralWithContext(context, singularMessage, pluralMessage, number); }}} These functions return the specified message translated into the current language. If that language is the source language or if there is no translation for the specified message, these functions return the specified message. === Using Plural Functions === Plural functions require that you pass them two versions of the same message: a message in singular form (`number = 1`) and a message in plural form (`number != 1`). This is because those are the English plural forms. However, other languages may have more plural forms or no plural forms at all. That is why you must specify an integer, `number`, that is the number of items represented in your message. === Using Context Functions === Context functions are used to handle cases where an English string may have a different meaning depending on the context. When that happens, chances are other languages use different words for each one of those meanings. If you use a context-free internationalization function (`translate` or `translatePlural`) to translate two messages that contain the same text, when you generate a translation template (POT file) from the sources, both messages are treated as a single message, and translators can only translate the message one way or another. If instead you use a context function (`translateWithContext` or `translatePluralWithContext`) and specify a different context for each message, translators will be able to translate each message differently. The context string can be any short English string. The following is a real-life example, extracted from the game sources: {{{ translateWithContext("map size", "Any"); translateWithContext("player number", "Any") }}} Usually, you do not need to worry about whether or not a string needs a context. You can always use a context-free internationalization function, and if a context is necessary for one or more languages to properly translate the message, translators will let you know, and you can then switch to a context function. However, you might want to be alert for the following cases that might need a context: * '''Single words'''. When you translate a single word, you are likely to need a context. * '''Unclear verb tenses or nouns'''. More often than not, the same English word may represent two or more different tenses of a verb, and also work as a noun or other type of word. If the message string is not clear enough as to which of those tenses is being used or whether the word is working as a verb or as a different type of word, you need a context. === Using String Formatting Instead of Concatenating Strings in !JavaScript === You should '''never''' concatenate translatable strings, as the position of each member of the concatenation may change in other languages. Instead, use the `sprintf` global function for string formatting: '''Original:''' {{{ fpsCaption = "FPS: " + Engine.GetFPS(); progressCaption = "Uploading… (" + Math.floor(100*done) + "%)"; }}} '''Internationalized:''' {{{ fpsCaption = sprintf(translate("FPS: %(fps)s"), { fps: Engine.GetFPS() }); progressCaption = sprintf(translate("Uploading… (%f%%)"), Math.floor(100*done)); }}} == Formatting Dates in JavaScript == Given a date in [http://en.wikipedia.org/wiki/Unix_time UNIX time] (in milliseconds, not in seconds), you can format the date using the following engine function: {{{ dateString = Engine.FormatMillisecondsIntoDateString(unixTime, translate("yyyy-MM-dd HH:mm:ss")); }}} If you have a !JavaScript [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date Date] object, you can use [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime Date.getTime()] to obtain the UNIX time in milliseconds: {{{ dateString = Engine.FormatMillisecondsIntoDateString(date.getTime(), translate("yyyy-MM-dd HH:mm:ss")); }}} You can modify the format string (second parameter) as you wish, using any [https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table ICU date formatting symbols]. = Internationalizing Data Files = Internationalizing strings from data files that are loaded by !JavaScript files is a two-step process. You must: 1. Configure the [wiki:Implementation_of_Internationalization_and_Localization#MessageExtraction message extraction system] to extract the translatable strings from the data file. 1. Use an internationalization function on the !JavaScript side after you load the data file. == Configuring the Message Extraction System for Data Files == Our message extraction system currently supports extracting data strings from: * '''Plain text files (.txt)'''. The content of plain text files can be extracted line by line. Paragraphs in these plain text files should not contain line breaks, otherwise each line is extracted as a separate message, which is not translator-friendly. * '''JSON files'''. You can extract strings associated with a specific key or keys of any object in a given JSON file. * '''XML files''', where you can extract: * The content of specific elements (start and end tags). Currently, however, you cannot extract the value of an attribute of an XML element. For example, in `