Table des matières

The System localization/translation of the Ryzom Core

inlinetoc

General Information

There are basically “two” distinct parts for localization in Ryzom. The first part(and the simplest one) concerns the server-side “static localization”(that is, interface names, error messages). The second part is for the dynamically generated server texts.

As you can see in the diagram, there are four types of files that operate the location-system. Each of these files must come in there own “localized(Locale)” language. If you look closely, you can see(In bold text) that each file includes the “language-code” embedded in the name.

Detail about the file formats follow below:

Code Language

The languages in Ryzom are identified by their “language-code” as defined in ISO 639-1 PLUS a country code defined in ISO 3166 if necessary.

ISO 639-1 is a two-character language-code(eg 'en', or 'fr'). This is(for the most part) enough for most languages that we want to manage.

But.. there are some exceptions, as for ex the Chinese writing. The Chinese language can be written in two forms: “traditional” or “simplified”. However, there is only one language-code for Chinese: 'hz'.

Therefor we need to add a country code to indicate what form of Chinese writing we are talking about. The language-code for simplified Chinese becomes: 'hz-CN'(ie Chinese language, Country China), and for traditional Chinese, it is: 'hz', this happens because of all other Asian languages, like: (Taiwan, Hong Kong?) only uses “traditional” Chinese as the spoken language.

Definitions of identifiers

The translated strings are associated with an identifier. The identifiers are strings of text that must follow the constraint of “<wrap hi> the identifier C </ wrap>”, with a small difference. An identifier C must contain only the following characters: A-Z , a-z , 0-9 , @ and _ . A real identifier C can not start with a number, but an string identifier can.

Example of “GOOD” identifiers:

These are regarded as good identifiers: 
CeciEstUnBonIdentifiant
_Ceci@est@unautreBonId
1234_est_un_bonId
Ceci_est_un_Bon_1234

Example of “BAD” identifiers:

These are regarded as bad identifiers:
é#()|{[_IdPASBON

File Formats

There are three different formats for translation files. But we do only need to know two of them:

Format 1

This format is used for the static text on the client side and also for text with server-side clauses. This file is a list of associations between identifiers and strings(also called “value chains”). The identifier MUST obey the constraint of the identifier C, and the value chain is also delimited by [ 'and' ']' '. The formatting of the text is “read;”. But you do have complete freedom when it comes to skipping “lines and indents”.

identifier1 [text value]
identifier2 [The other text value]

Note: It is permitted to include C-style comments.

// This is just a comment line. Continue to the end of the line
identifier1 [text value]
/* this is
a comment
on several lines */
identifier2 /* Comment on several lines here! */ [Other text value]

Text values can be formatted for better legibility. The new “line and tabs” are deleted in the final value of the string.

identifier [value
text
with a
new line
And tabs for the readability only]
identifier [Other text value]

If you want to specify new rows or tabs in a string-value, you must use the C \t escape sequence for tabs and \n for new lines. To write \ in a string-value, double the backslash: \\. To write ] in the string, precede it with a backslash : \].

identifier1 [tabulation : \This text is tabulated]
identifier2 [New line \nText on a new line]
identifier3 [Backslash: \\]
identifier4 [A closing hook: \] ]

For easier use and maintaining, you can split up the original file into a “series of smaller files”. This is done by using a pre-processor command in the C-programing language: “#include”.

#include "path/filename.txt"

You can have any number of “include-commands”, you can even have “include”-commands in include files. The “path” can be either “absolute” or “relative” to the location of the “master file”.

Format 2

This format is used for the translation of “phrase files”. This format has a rather complicated grammar that could be described as a syntax close to LALR :

identifier: [A-Za-z0-9_@]+

phrase : identifier ‘(‘ parameterList ‘)’
‘{‘
clauseList
‘}’

parameterList : parameterList ‘,’ parameterDesc
| parameterDesc

parameterDesc : parameterType parameterName

parameterName : identifier

parameterType : ‘item’
| ‘place’
| ‘creature’
| ‘skill’
| ‘role’
| ‘ecosystem’
| ‘race’
| ‘brick’
| ‘tribe’
| ‘guild’
| ‘player’
| ‘int’
| ‘bot’
| ‘time’
| ‘money’
| ‘compass’
| ‘dyn_string_id’
| ‘string_id’
| ‘self’
| ‘creature_model’
| ‘entity’
| ‘bot_name’
| ‘bodypart’
| ‘score’
| ‘sphrase’
| ‘characteristic’
| ‘damage_type’
| ‘literal’

clauseList : clauseList clause
| clause

clause : conditionList identifier textValue
| identifier textValue
| conditionList identifier
| identifier
| textValue

conditionList : conditionList condition
| condition

condition : ‘(‘ testList ‘)’

testList : testList ‘&’ test
| test

test : operand1 operator reference

operand1 : parameterName
| parameterName’.’propertyName

propertyName : identifier

operator: ‘=’
| ‘!=’
| ‘<’
| ‘<=’
| ‘>’
| ‘<=’

reference: identifier

textValue: ‘[‘ .* ‘]’

As in format 1, you can insert “C style” comments in the text, identifier with enclosure marks, and use the include command.

Format 3 : Export the Unicode Worksheet

This format is the result of a “spreadsheet export” to Unicode text. The encoding MUST be 16-bit unicode format. The columns are separated by tabs, and the lines simply by new lines.

IMPORTANT: You must NOT modify them manually, use only the worksheet for this task.

The first row will/should always contain the column names.

Information Columns

For ex: If the name of a column starts with '*', then the entire column will be ignored. This is very useful when you want to add a “column of information” that can help with the translation.

Erase characters

It is possible to insert a 'delete' command in a field: '\ d'. This is useful for translating articles.

Example: You have a string with the following replacements(in English):

“Report me $ item.da $ $ item.name $

And the file will contain the following names:

 Item name da
 Hammer hammer
 L’ échelle l’ échelle

If the item is 'hammer', there is no problem. The replacement will give the following result:

“Report me hammer

But for 'l’ échelle', there is an extra space in the result:

“Report me l’ échelle

To remove this extra space, you can add a 'delete' marker in the article definition, like this:

item name da
Hammer hammer
echelle échelle l’\d

This will give a correct string, as the result shows below:

“Report me l’échelle

Working with the translation files with the point of view from the translator

File types of "* .uxt" in the client

These files contain all the “static texts” available directly in the client. The text should follow the format 1(as described above).

There is an additional constraint to consider: you MUST provide the name of the language as the “first entry”, as it is written in the considered language(ie English for English, 'French' for French …).

For example, the en.uxt file should start with:

 LanguageName [English]

The Server-side files

The server-side translation is a bit more complicated. We will see how the server-side translations is done in four steps(We will start with the simplest to the most complicated problems, Yay!).

Step 1: A single string

To “pull this of”, you only need the sentence file. Let's say we want a string that says “hello world!”, Identified by HelloWorld. Let's create a phrase entry in phrase_en.txt:

HelloWorld()
{
[Hello world!]
}

And there you go! That's all there is to it.

Of course, you will also have to provide the same sentence in all supported languages, for example in file phrase_en.txt:

HelloWorld()
{
[Bonjour le monde!]
}

Notice that only the value of the text has changed. The sentence identifier MUST be the same in all translation files.

Step 2: Workaround for "clause_<lang>.txt"

In the 4th step, we will see that the file-sentences can become very complex. Therefore is these kind of files not very suitable if you(for example) want to give it to a professional translator without the proficiency in complex file grammatics. Even worse, the complexity of the file may hide the translation work itself.

One can therefore separate the grammar of the sentence into sentence-files and the textual value into a file of clauses.

To do this, you must assign a unique identifier to each text value. Let's go back to the previous, above example.. with a “workaround”.

In phrase_en.txt, create the sentence entry like this:

HelloWorld ()
{
Hello
}

As you can see, we only need to put an identifier in the sentence block. This means that the phrase refers to a string identified as Hello in the clause file.

Now we can create the text value in clause_en.txt:

Hello [Hello world!]

As in the first step, you will have to do this for each language.

To facilitate the translation work, it is possible to specify the string identifier AND the value identifier. This can be useful for automatically building a translation file from the original file. Exemple :

HelloWorld ()
{
Hello [Bonjour le monde!]
}

In a case like this, the translation system always looks first in the clause file, and then falls back on the text value in the sentence file ONLY if it does NOT find a string in the clause file.

The other advantage is that the person who wrote the phrase file can give a simplified version of the channel which a professional translator can improve.

The 3rd step: Using parameters - the basics

This is where we are gonna attack the complicated part!

Each sentence can receive a list of parameters.

These parameters can be of different types:

Each parameter receives a name (or id) from its declaration. We call it paramName.

Each type of parameter MAY be associated with a 'word' file. This file is an excel sheet (in unicode text export format) containing the translation of the parameter: its name, an indefinite or defined article( 'a', 'le' …), the plural of the name and The article, and any useful property or grammatical element necessary for translation.

The first column is very important because it associates a data line with a particular value of the parameter.

Let's start with an example: we want to build a dynamic sentence with a variable of species name of the creature.

First, we need to create an excel sheet that defines the words for the species of the creature. This will be saved as race_words_ <lang> .txt in exporting unicode text from excel. As always, you will need to provide a version of this file for each language.

NB: The first column MUST always be the association field and you will need to have a name column as it is the default value for the parameters. Any other column is optional and can vary from one language to another to match the various specific grammatical constraints.

Here is an example of race_words_en.txt : <Code> race name ia da p pia pda Kitifly Kitifly has the Kitiflys the Varynx in the Varynx Etc … </ code>

As stated above, the first column gives the identifier of the race, as defined in the game dev sheets. The second column is the 'highly recommended' column to store the name of the breed. The column 'p' is the plural name. 'Ia' and 'da' indicate the indefinite and defined items.

Next, create a sentence with a creature parameter in sentence_ <lang> .txt :

KILL_A_CREATURE (race crea)
{}

As you can see, after the phrase identifier KILL_THIS_CREATURE , we have the list of parameters in parentheses. We declare a race parameter named crea . Note that you freely choose the name of your parameter, but each parameter must have a unique name (at least for the sentence).

Now we can build the string value. To insert the parameter in the string, we must specify the replacement point using the $ sign (ie $ crea $ ) directly in the string value:

KILL_A_CREATURE (race crea)
{
[Would you please kill a $crea$ for me ?]
}

As you can see, it's not too complicated. $ Crea $ will be replaced by the field content from the word file in the name column and the row corresponding to the race identifier.

It is possible to recall any column of the word file in the value chain. For example, we can dynamize the indefinite article:

KILL_A_CREATURE (race crea)
{
[Would you please kill $crea.ia$ $crea$ for me ?]
}

Some types of parameters have special replacement rules: int are replaced by their text representation, time are converted to a ryzomian date, as are money .

Finally, last but not least, the rules for identifiers and workarounds seen in steps 1 and 2 that are still valid.

Step 4: Using Parameters - Conditional Clause

It is now time to unveil the conditional clause system.

Let's say that the identifier and the string value that we used in a sentence in the previous step are a clause. And let's even say that a sentence can contain more than one clause, which can be chosen by the translation engine on the fly according to the value of the parameter. This is what the conditional clause system is all about!

Let's start with a first example. As for step 3, we want to kill a creature, but this time we add a variable for the number of animals to kill, from 0 to n. What we need is the condition from which to choose between the three clauses: no creature to kill, one creature to kill, or several.

First, let us write the sentence, its parameters and its three clauses:

KILL_A_CREATURE (race crea, int count)
{
// no creature to kill (No creature to kill)
[There is no creature to kill today.]
// 1 creature to kill (1 creature to kill)
    [Would you please kill a $crea$ for me ?]
// more than one (More than one)
[Would you please kill $count$ $crea$ for me ?]
}

We have written three versions of the text with a very different meaning and grammatical structure. Now let's add the conditions. The conditions are placed before the identifier and / or the value chain, and are indicated by parentheses.

KILL_A_CREATURE (race crea, int count)
{
// no creature to kill (No creature to kill)
(count = 0) [There is no creature to kill today.]
// 1 creature to kill (1 creature to kill)
(count = 1) [Would you please kill a $crea$ for me ?]
// more than one (More than one)
(count > 1) [Would you please kill $count$ $crea$ for me ?]
}

Easy, right?

Now let's take a more complicated case: Let's say we want to write a different sentence depending on whether the player is male or female. This is a perfect opportunity to introduce the self parameter. The Self parameter is a hidden parameter, because it is always available and represents the recipient of the sentence(the one to whom it is addressed).

The Self parameter carries the gender and name properties. You can create a self_words_ <lang> .txt file to handle special cases (an admin player with a translatable name, for example).

Rewrite the query to kill animals taking into account the kind of player:

KILL_A_CREATURE (race crea, int count)
{
// -- Male Player
// No creature to kill, male player
(count = 0 & self.gender = Male)
[Hi man, there is no creature to kill today .]
// 1 creature to kill, male player
(count = 1 & self.gender = Male)
[Hi man, would you please kill a $crea$ for me ?]
// More than one, male player
(count > 1 & self.gender = Male)
[Hi man, Would you please kill $count$ $crea$ for me ?]

// -- Female Player
// No creature to kill, female player
(count = 0 & self.gender = Female)
[Hi girl, There is no creature to kill today.]
// 1 creature to kill, female player
(count = 1 & self.gender = Female)
[Hi girl, Would you please kill a $crea$ for me ?]
// More than one, female player
(count > 1 & self.gender = Female)
[Hi girl, Would you please kill $count$ $crea$ for me ?]
}

As you can see, we now have six clauses. Three for the number of creatures, multiplied by the two genres of the player.

As you can see, conditional tests can be combined with the & character. This means that all tests must be valid to select the clause.

You can use any parameter as the left operator for the test. You can also specify a parameter property (from the word file) as an operand.

On the other hand, the right-hand operand of the test must be a constant value (textual or numerical). The available operators are:

=
!=
<
<=
>
>=

In some cases, you may need to do combinations of tests with OR (or). This is possible simply by indicating a multiple condition list before a clause:

FOO_PHRASE (int c1, int c2)
{
(c1 = 0 & c2 = 10)
(c1 = 10 & c2 = 0)
[One passe in this clause if :
c1 Equal to zero and c2 equal ten
or
c1 Equal ten and c2 equal zero]
}

Detailed rules for selecting clauses:

Property-list of hard-coded parameters

Here you will find an exhaustive list of the properties of the hard-coded parameters. These properties are always available even when no word file has been provided. In addition, hard-coded properties can not be replaced by a name file that would use a column with the same name.

Parameter Property
Item
Place
Creature “name : Name of the model for the creature
gender : Creature type (drawn?) From the model”
Skill
Ecosystem
Race
Brick
Tribe
Guild
Player “name : Player name
gender : Kind of the player in the mirror”
Bot “career : Career of bot
role : Role of the bot as defined in creature.basics.chatProfile
name : Name of the creature
gender : Creature type (drawn?) From the model”
Integer
Time
Money
Compass
dyn_string_id Only tests != and == Are possible. Essentially to compare a parameter with 0.
string_id Only tests != and == Are possible. Essentially to compare a parameter with 0.
self “name : Player name
gender : Kind of the player in the mirror”
creature_model NB : Use the creature_words translation file !
entity “== 0, != 0 : Tests whether the entity is Unknown (unknown) or not
name : Name of the creature or name of player
gender : Creature type (drawn?) Of the player's model or gender (based on player info)”
bodypart
score
sphrase
characteristic
damage_type
bot_name

Translation Workflow

In the translation workflow below, we consider that the reference language is English.

There is a series of tools and beats files to help synchronize translations. Here is a step-by-step description of how to work on a translation file, which tool to use and when.

Only additions to existing translation files are supported by the translation tools. If you want to edit or delete an existing translation string or phrase, it will be done 'by hand' with maximum attention in all language versions.

In most cases, it is better to create a new translation entry, rather than manage a change in all translation files, and it is almost safe to leave the old unused strings in the files.

In any case, you must NEVER make changes when there are pending diff files.

It is strongly recommended that you strictly follow the described workflow to avoid translation problems, missing strings, or other bizarre problems that might occur when working with multiple language versions of a set of files.

The translation work is done in collaboration between the publisher, who is the 'technical' part of the work, and a professional translator subcontracts that translates only simple strings into rich chains and of very high quality while respecting the context.

The tool that generates the diff file for translation keeps the comments of the reference version. This can be useful to provide additional information to the translator about the context of the text.

In addition, for the sentences file, the diff file automatically includes comments that describe the parameter list.

By default it seems that the system works with ISO-8859-15 files and not UTF8 so if you get strange results with commands, it's probably due to a UTF8 encoding of your file.

Structure of stored translation

All files to be translated are stored in a well-defined directory structure called translation warehouse . All translation work is done in this directory.

Tools are provided to install the translated file in the client and the server directory after the end of the translation cycle.

Name translation of bots

The command extract_bot_names parses the file bot_names.txt according to the Primitives and if some names are used several times in it, it must transform the name of the bot into a generic name of the type gn_genericname Whose match is created in the corresponding title_words.

Test to perform cf HexChatlog kerv30/12/2015 - 14:30 — Yann K 2015/12/30 14:42

Static string on the client

Initial task

Editor :

Translator:

Editor:

After the initial task is completed, the workflow enters incremental mode.

Incremental spot

Editor * Add strings to the reference file en.uxt in the work directory. * Generate diff files from static strings with the command make_string_diff. This will create the diff files for each language in the diff folder. * Send diff files to the translator.

Translator

Editor

Dynamic strings on the server

Initial task

Editor * Create the reference file phrase_wk.uxt in the work directory and add all sentence entries for the English version.

Translator

Editor * Deposit translated diff files in the diff directory(which will overwrite the untranslated diff clause files and replace them with the translated files).

Once the initial task has been completed, the workflow enters incremental mode.

Incremental spot

Editor * * Add the new sentence to phrase_wk.uxt in the work folder.

Translator

Editor

Server-side word files

This part is obsolete.

We must now also use tools, as for others:

https://bitbucket.org/ryzom/ryzomcore/src/d69a3cd7fbd0757f7acbef68649229016b0d361c/code/ryzom/tools/translation/?at=compatibility-develop

How to update translations in words files :

1. Update original texts in “work” directory 2. Launch 5_make_words_diff script 3. Open files in “diff” directory 4. Replace original text with translation (separators are <tab>) 5. The 2 last lines : REMOVE THE FOLLOWING TWO LINES WHEN TRANSLATION IS DONE and DIFF NOT TRANSLATED 6. Save files 7. Launch 6_merge_words_diff to merge your translations in “translated”

NB: 'word' (N.d.T. 'words', in the original text) has no relation to the Microsoft Word program!

Word files are always updated manually because they are rarely updated by the editor(and usually only for large updates). In addition, when new sentences are translated, it may be necessary to create and fill a new column in one of the word files to fit the translation.

So there's just a workflow, but no tools.

Initial tasks

Editor * Create the initial sheet for each parameter type with all possible identifiers in the reference language in the translated directory.

Translator

Editor

After this initial task, there are two possible events:

Need a new column

Translator

Editor

New sheet contents

Editor

Translator

Editor

Install translated filess

At the end of a translation cycle, the translated files must be installed in the client and server directory structures.

To do this, we use the command install_translation. The <lang>.uxt files are copied into the client structure in Ryzom/gamedev/language. All other files are copied to Ryzom/data_shard.

To apply the translation to the client, the publisher must make a patch.

To apply the translation to the server, just enter the reload command on the InputOutputService.

Working with translation files, from the point of view of the programr

NeL / Ryzom programmers can use the translation system with a restricted number of calls.

Accessing static client chains

To get the unicode string from an identifier string, use the class NLMISC::CI18N.

First, make sure that the translation files * .uxt are available in the search path.

Then, it is possible to load a set of language strings by calling NLMISC :: CI18N :: load (languageCode) .

The languageCode parameter is the language code as defined in Chapter 2 'Language code'. Then, call the method NLMISC::CI18N::get(identifier) to get the unicode string associated with the identifier.

Dynamic Chains

A dynamic string requires a little more work and a complete instance infrastructure.

Dynamic String Management involves a query service (RQS), the InputOutputService (IOS), the FrontEnd (FE), the ryzom client, plus the basic services to run The others(naming, tick, mirror).

RQS is a service that requires sending a dynamic string to the client.

RQS also sends the dynamic string identifier to the client using the database or any other method.

The proxy is a small piece of code that builds and sends the PHRASE message to the IOS service.

IOS has the heavy burden of parsing(parsing) the parameters, choosing the right clause and constructing the resulting string, and then sending a minimum amount of data to the client.

The client receives the phrase and requests any missing element from the string to the IOS.

Build a list of string sending parameters

To access the proxy, you must include game_share/string_manager_sender.h and link it with game_share.lib.

The list of parameters must first be constructed. This is done by filling a vector of STRING_MANAGER::TParam struture. For each parameter, define the “Type” field and write the appropriate data for the member.

You must respect exactly the definition of the sentence parameters of the sentence file.

We can then call:

uint32 STRING_MANAGER::sendStringToClient(
NLMISC::CEntityId destClientId,
const std::string &phraseIdentifier,
const std::vector<STRING_MANAGER::TParam> parameters

) DestClientId is the identifier of the destination client, phraseIdentifier is the identifier as written in the phrase file, parameters is the parameter vector you built before.

This function returns the dynamic ID assigned to this phrase.

Example: send the phrase 'kill a creature'(see § 5.2, step 4):

<Code> include the definition of the proxy string manager #include “game_share / string_manager_sender.h” uint32 killCreatureMessage( EntityId destClient, GSPEOPLE::EPeople race, uint32 nbToKill) { std::vector<STRING_MANAGER::TParam> params; STRING_MANAGER::TParam param; First, we need the race of the creature param.Type = STRING_MANAGER::creature; param.Enum = race; params.push_back(param);

Then the number of creatures to kill param.Type = STRING_MANAGER::integer; param.Int = nbToKill; params.push_back(param); And now send the message uint32 dynId = STRING_MANAGER::sendStringToClient( destClient, “KILL_A_CREATURE”, params);

return dynId; }</code>

Member to fill in TParam depending on the parameter type

* Compass: not yet defined

Accessing dynamic strings from the client

On the client side, accessing dynamic strings is quite simple. You just have to watch out for the delay in some cases.

Once the dynamic string identifier is retrieved from anywhere(for example, from the database), you just have to probe(pol?) The getDynString method of STRING_MANAGER::CStringManagerClient .

The method returns false as long as 1) the requested string is incomplete or unknown.

Even if the method returns false, it can bring back a partial text, with missing replacement text.

Dynamic strings are dynamically stored, and if the code is smart enough, it can free memory from dynamic strings when they are no longer needed by calling releaseDynString.

To be more efficient, it is possible to call each frame() the getDynString method until it returns true, then store the string and call releaseDynString.

STRING_MANAGER::CStringManagerClient is based on a single scheme, so you need to call STRING_MANAGER::CStringManagerClient::instance() to get a pointer to the single instance.

Voici un exemple de code simple.

// Include the definition of the proxy string manager
#include “string_manager_client.h”

using namespace STRING_MANAGER;

/ ** A method that receives the dynamic string identifier
* And prints the dynamic string in the log.
* Call it to each frame / frame until true value
* /
bool foo(uint32 dynStringId)
{
ucstring result;
bool ret;
CStringManagerClient *smc = CStringManagerClient::instance();

ret = smc->getDynamicString(dynStringId, result)

if (!ret)
nlinfo(“Incomplete string : %s”, result.toString().c_str());
else
{
nlinfo(“Complete string : %s”, result.toString().c_str());
// libérer la chaîne dynamique
smc->releaseDynString(dynStringId);
}

return ret;
}

Text creation guide for Ryzom

There are plenty of places for text in Ryzom, this page will clarify the text identification conventions, the context available for text insertion and contextual text settings.

Conventions for identifiers

String identifiers in en.uxt

These identifiers are written in lowercase, with a capital letter for each new word. Exemple:

unIdentifierSimple
unOtherIdentifier

Identifiers of sentences in phrase_en.txt

These identifiers are written in uppercase, and the words are separated by “underscore”.

Example:

 UN_IDENTIFIER_SIMPLE
 UN_OTHER_IDENTIFIER

Identifiers of strings(or clauses) in clause_en.txt

These identifiers are written as string identifiers in en.uxt .

But, as they are inside a sentence definition, they must contain the name of the phrases as the base name. The name of the sentences is in lower case to respect the convention of the string identifiers.

Example, in a sentence called AN_PHRASE_SIMPLE, the identifier must be:

 anPhraseSimple

In addition, when there is more than one clause for a given sentence, the clause identifier must be followed by a few labels that will give the translator a clue as to the meaning of each clause. Example, in a sentence named AN_PHRASE_SIMPLE and containing two clauses, one for the singular and one for the plural, we could have the following identifiers:

anPhraseSimpleS
anPhraseSimpleP

Contexts of the texts

Context "Chat"

The “Chat” context encompasses all texts that come from an NPC via a chat window and text bubbles.

The bot says / shouts in the neighborhood

There is only one parameter available: the NPC entity that speaks/cries.

The sentence name starts with SAY_

Sample sentence:

SAY_XXX (bot b)
{
     sayXxx    [Hello here, my name is $ b $, someone hears me ?]
}
The bot talks to the escort leader (one player)

Two parameters: the talking bot, and the player.

The sentence name starts with TALK_

Sample sentence:

     TALK_XXX (bot b, player p)
{
     talkXxx   [Hi $ p $, my name is $ b $, I need help !]
}
The bot speaks/cries in response to a "click" on him

Two parameters: the bot clicked and the player.

The sentence name starts with CLICK_

Sample sentence

     CLICK_XXX (bot b, player p)
{
     clickXxx  [Hi $ p $, my name is $ b $, you clicked on me ?]
}

Interactive context (also called botchat)

The botchat covers all the texts in the interactive dialogue with an NPC.

Static missions

All mission-related sentence names have a root defined by the name of the mission as positioned in the mision node of the world editor.

From this root, several extensions can be added to form sentence names:

Exemple:

From a mission called INSTRUCTOR_MIS_1
The title will be INSTRUCTOR_MIS_1_TITLE
The text of Stage 1 of the mission will be INSTRUCTOR_MIS_1_STEP_1
The text of Stage 2 of the Mission will be INSTRUCTOR_MIS_1_STEP_2
The end-of-mission text will be INSTRUCTOR_MIS_1_END

Settings :

The parameters of the log's step texts depend on the nature of the stage(see Nicolas Brigand).

For mission progress texts in the context menu, there are two:

The first is the standard text, the second is displayed when you have to give something to the bot.

Additional context for menu entries

It is possible to add informative simple entries in the context menu of the bot. This part consists of two sentences: the name displayed for the menu entry and the text content displayed after clicking on the menu entry.

Two parameters: the context menu bot bot and the player.

The sentence name starts with BC_MENU_ for the menu entry and BC_MENUTEXT_

System Messages(combat info)

The parameters depend on the sentences, but there are a few well defined types of sentences:

1)
N.d.T.: The original text says “until”, “until”