Outils du site

fr:rc_localisation_traduction

Système de localisation/traduction sur Ryzom Core

Généralités

Il y a, en gros, deux parties distinctes pour la localisation dans Ryzom. La première partie (et la plus simple) concerne la localisation statique côté serveur (autrement dit, les noms d'interface, les messages d'erreur). La seconde partie est pour les textes générés dynamiquement depuis les serveurs.

Comme vous pouvez le voir sur le diagramme, il y a quatre types de fichiers qui font fonctionner le système de localisation. Chacun de ces fichiers doit venir dans chaque langue localisée. En gras, vous pouvez voir que chaque fichier inclut le code de la langue dans son nom.

Les formats de fichiers sont détaillés ci-dessous.

Code Langue

Les langues, dans Ryzom, sont identifiées par leur code langue tel que défini dans ISO 639-1 plus un code pays défini en ISO 3166 si nécessaire.

ISO 639-1 est un code langue sur deux caractères (par exemple 'en', ou 'fr'). Ce qui suffit pour la plupart des langues que nous voulons gérer.

Mais il y a quelques exceptions, comme le chinois écrit. Le chinois peut s'écrire sous deux formes : traditionnelle ou simplifiée. Cependant, il n'y a qu'un seul code pour le chinois : 'hz'.

Nous devons donc ajouter un code pays pour indiquer de quelle forme de chinois écrit nous parlons. Le code langue pour le chinois simplifié devient 'hz-CN' (c'est-à-dire Langue chinoises, Pays Chine), et pour le chinois traditionnel, c'est 'hz' tout seul parce que tous les autres pays de langue chinoise (Taiwan, Hong Kong ?) utilisent le chinois traditionnel.

Définition de l'identifiant

Les chaînes traduites sont associées à un identifiant. Les identifiants sont des chaînes de texte qui doivent suivent la contrainte de l'identifiant C, à une petite différence près. Un identifiant C ne doit contenir que les caractères suivants : A-Z, a-z, 0-9, @ et _. Un véritable identifiant C ne peut pas commencer par un nombre, un identifiant de chaîne le peut.

Quelques bons identifiants :

Ceci_est_un_bon_identifiant
CeciEstUnBonIdentifiant
_Ceci@est@unautreBonId
1234_est_un_bonId
Ceci_est_un_Bon_1234

Quelques mauvais identifiants :

Ceci est un mauvais identifiant
é#()|{[_IdPASBON

Formats de fichier

Il y a trois formats différents pour les fichiers de traduction. Mais on n'a besoin d'en connaître que deux.

Format 1

Ce format est utilisé pour le texte statique côté client et pour le texte à clauses côté serveur. Ce fichier est une liste d'associations entre identifiants et chaînes (qu'on appelle aussi des chaînes de valeur). L'identifiant doit obéir à la contrainte de l'identifiant C, et la chaîne de valeur est délimitée par [ et ]. La mise en forme du texte est lire ; vous pouvez sauter des lignes et indentités comme vous voulez.

identifiant1 [valeur de texte]
identifiant2 [autre valeur de texte]

Le fichier peut inclure des commentaires de style C.

// Ceci est juste une ligne de commentaire. Continuer jusqu'à la fin de la ligne
identifiant1 [valeur de texte]
/* Ceci est 
un commentaire
sur plusieurs lignes */
identifiant2 /* commentaire sur plusieurs lignes ici ! */ [autre valeur de texte]

Les valeurs de texte peuvent être mises en forme pour une meilleure lisibilité. La nouvelle ligne et les tabulations sont supprimées dans la valeur finale de la chaîne.

identifiant1 [valeur
de texte
avec une
nouvelle ligne
et des tabulations rien que pour la lisibilité]
identifiant2 [autre valeur de texte]

Si vous voulez indiquer de nouvelles lignes ou des tabulations dans une chaîne de valeur, vous devez utilisers la séquence d'échappement de style C \t pour les tabulations et \n pour les nouvelles lignes. Pour écrire \ dans une chaîne de valeur, doublez le backslash : \\. Pour écrire ] dans la chaîne, faîtes le précéder d'un backslah : \].

identifiant1 [tabulation : \tCe texte est tabulé]
identifiant2 [Nouvelle ligne \nTexte sur une nouvelle ligne]
identifiant3 [Backslash : \\]
identifiant4 [un crochet fermant : \] ]

Vous pouvez découper le fichier d'origine en une série de fichiers plus petits, plus simples à maintenir et à utiliser. Ceci se fait en utilisant une commande de préprocesseur de type C “#include.

#include "path/filename.txt"

Vous pouvez avoir un nombre quelconque de commandes “include”. Les fichiers inclus peuvent également contenir des commandes “include”. Le chemin peut être aussi bien absolu que relatif à l'emplacement du fichier maître.

Format 2

Ce format est utilisé pour les fichiers de phrases de traduction. Ce format a une grammaire assez compliquée qui va être décrite dans une syntaxe proche de LALR :

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

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

parameterList : parameterList ‘,’ parameterDesc
| parameterDesc

parameterDesc : parameterType parameterName

parameterName : identifiant

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 identifiant textValue
| identifiant textValue
| conditionList identifiant
| identifiant
| textValue

conditionList : conditionList condition
| condition

condition : ‘(‘ testList ‘)’

testList : testList ‘&’ test
| test

test : operand1 operator reference

operand1 : parameterName
| parameterName’.’propertyName

propertyName : identifiant

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

reference : identifiant

textValue : ‘[‘ .* ‘]’

Comme dans le format 1, vous pouvez insérer des commentaires en style C dans le text, indenter à loisir, et utiliser la commande include.

Format 3 : Export de feuille de calcul Unicode

Ce format est le résultat d'un export de feuille de calcul en texte Unicode. L'encodage doit être unicode 16 bits. Les colonnes sont séparées par des tabulations, et les lignes par de nouvelles lignes.

Vous ne devez pas les modifier manuellement, mais uniquement avec la feuille de calcul.

La première ligne contiendra le nom des colonnes.

Colonnes d'information

Si le nom d'une colonne commence par '*', alors toute la colonne sera ignorée. Ceci est utile pour ajouter une colonne d'information qui pourra aider à la traduction.

Caractère d'effacement

Il est possible d'insérer une commande 'delete' (effacer) dans un champ : '\d'. C'est utile pour la traduction des articles.

Exemple : vous avez une chaîne avec les remplacements suivants (en français) :

Rapporte moi $item.da$ $item.name$

Et le fichier des noms d'items contient ce qui suit :

item name da
marteau marteau le
echelle échelle l’

Si l'item est 'marteau', pas de problème. Le remplacement donnera :

“Rapporte moi le marteau

Mais pour 'échelle', il y a un espace supplémentaire dans le résultat :

“Rapporte moi l’ échelle

Pour supprimer cet espace supplémentaire, vous pouvez ajouter un marqueur 'delete' dans la définition de l'article :

item name da
marteau marteau le
echelle échelle l’\d

Ceci donnera une chaîne correcte comme résultat :

“Rapporte moi l’échelle

Travailler avec des fichiers de traduction, du point de vue du traducteur

Fichiers “*.uxt” sur le client

Ce fichier contient tous les textes statiques disponibles directement sur le client. Le texte doit suivre le format 1 décrit ci-dessus.

Il y a une contrainte supplémentaire : vous DEVEZ fournir le nom de la langue comme première entrée, telle qu'elle s'écrit dans la langue considérée (c'est-à-dire ‘English’ pour l'anglais, ‘Français’ pour le français…).

Par exemple, le fichier en.uxt doit commencer par :

languageName [English]

Fichiers sur le serveur

La traduction côté serveur est un peu plus compliquée. Nous allons voir comment écrire des traductions côté serveur en quatre étapes (devinez quoi : des problèmes les plus simples au plus compliqués !).

1ère étape : Une chaîne simple

Pour cela, vous n'avez besoin que du fichier de phrases. Disons que nous voulons une chaîne qui dise “hello world!”, identifiée par HelloWorld. Créez une entrée de phrase dans phrase_en.txt :

HelloWorld ()
{
[Hello world!]
}

Et voilà ! C'est tout.

Bien sûr, vous devrez aussi fournir la même phrase dans toutes les langues supportées, par exemple dans phrase_fr.txt :

HelloWorld ()
{
[Bonjour le monde!]
}

Remarquez que seule la valeur du texte a changé. L'identifiant de phrase DOIT être le même dans tous les fichiers de traduction.

2ème étape : Contournement pour "clause_<lang>.txt"

Dans la 4ème étape, nous verrons que le fichier de phrases peut devenir très complexe. Par conséquent, ce fichier n'est pas très adapté si on veut le donner à un traducteur professionnel sans compétence en grammaire complexe de fichier. Pire, la complexité du fichier peut masquer le travail à faire pour la traduction.

On peut donc séparer la grammaire de la phrase en fichiers de phrases et la valeur textuelle en fichier de clauses.

Pour cela, vous devez assigner un identifiant unique à chaque valeur de texte. Reprenons l'exemple précédent avec contournement.

Dans phrase_en.txt, créer l'entrée de phrase comme ceci :

HelloWorld ()
{
Hello
}

Nous avons juste à mettre un identifiant dans le bloc de phrase. Ce qui signifie que la phrase se réfère à une chaîne identifiée comme Hello dans le fichier de clause.

Maintenant, nous pouvons créer la valeur textuelle dans clause_en.txt :

Hello [Hello world!]

Comme dans la première étape, vous devrez faire ça pour chaque langue.

Pour faciliter le travail de traduction, il est possible de spécifier l'identifiant de chaîne ET l'identifiant de valeur. Ceci peut être utile pour construire automatiquement un fichier de traduction à partir du fichier d'origine.

Exemple :

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

Dans un cas comme celui-ci, le système de traduction regarde toujours d'abord dans le fichier de clause, et se rabat sur la valeur textuelle dans le fichier de phrase uniquement s'il ne trouve pas de chaîne dans le fichier de clause.

L'autre avantage, c'est que la personne qui a écrit le fichier de phrase peut donner une version simplifiée de la chaîne, qu'un traducteur professionnel pourra améliorer.

3ème étape : Utiliser des paramètres – les bases

C'est là qu'on attaque la partie compliquée !

Chaque phrase peut recevoir une liste de paramètres.

Ces paramètres peuvent être de différents types :

  • item, (objet)
  • place, (emplacement)
  • creature, (créature, animal)
  • skill, (compétence)
  • ecosystem, (écosystème)
  • race, (race)
  • brick, (brique)
  • tribe, (tribu)
  • guild, (guilde)
  • player, (joueur)
  • int, (nombre entier)
  • bot, (robot)
  • time, (date)
  • money, (argent)
  • compass, (compas)
  • dyn_string_id, (identifiant de chaîne dynamique)
  • string_id, (identifiant de chaîne)
  • creature_model, (modèle de créature)
  • entity, (entité)
  • body_part, (partie du corps)
  • score, (score)
  • sphrase, (sous-phrase ?)
  • characteristic, (caractéristique)
  • damage_type, (type de dégât)
  • bot_name, (nom du robot)
  • literal. (?)

Chaque paramètre reçoit un nom (ou identifiant) à sa déclaration. Nous l’appelons paramName.

Chaque type de paramètre PEUT être associé avec un fichier de 'mot'. Ce fichier est une feuille excel (au format d'export de texte unicode) qui contient la traduction du paramètre : son nom, un article indéfini ou défini ('un', 'le'…), le pluriel du nom et de l'article, et toute propriété utile ou élément grammatical nécessaire pour la traduction.

La première colonne est très importante car elle associe une ligne de donnée avec une valeur particulière du paramètre.

Commençons par un exemple : nous voulons construire une phrase dynamique avec une variable de nom d'espèce de la créature.

D'abord, nous devons créer une feuille excel qui définit les mots pour l'espèce de la créature. Celle-ci sera sauvegardée en tant que race_words_<lang>.txt en export de texte unicode depuis excel. Comme toujours, vous devrez fournir une version de ce fichier pour chaque langue.

NB : La première colonne DOIT toujours être le champ d'association et vous devrez avoir une colonne name (nom) puisque c'est la valeur par défaut pour les paramètres. Toute autre colonne est facultative et peut varier d'une langue à l'autre pour correspondre aux diverses contraintes grammaticales spécifiques.

Voici un exemple de race_words_en.txt :

race name ia da p pia pda
kitifly Kitifly a the Kitiflys the
varynx Varynx a the Varynx the
etc…

Comme énoncé ci-dessus, la première colonne donne l'identifiant de la race, tel que défini dans les fiches de développement du jeu (game dev sheets). La deuxième colonne est la colonne 'hautement recommandée' pour stocker le nom de la race. La colonne 'p' est le nom au pluriel. 'ia' et 'da' indiquent les articles indéfinis et définis.

Ensuite, il faut créer une phrase avec un paramètre créature dans phrase_<lang>.txt:

KILL_A_CREATURE (race crea)
{}

Comme vous pouvez le voir, après l'identifiant de phrase KILL_THIS_CREATURE, nous avons la liste de paramètres entre parenthèses. Nous déclarons un paramètre de type race nommé crea. Remarquez que vous choisissez librement le nom de votre paramètre mais que chaque paramètre doit avoir un nom unique (au moins pour une phrase).

Maintenant, nous pouvons construire la valeur de chaîne. Pour insérer le paramètre dans la chaîne, nous devons préciser le point de remplacement à l'aide du signe $ (c'est-à-dire $crea$) directement dans la valeur de chaîne :

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

Comme vous le voyez, ce n'est pas trop compliqué. $crea$ sera remplacé par le contenu de champ issu du fichier de mots dans la colonne name et pour la ligne correspondant à l'identifier de la race.

Il est possible de rappeler n'importe quelle colonne du fichier de mots dans la chaîne de valeur. Nous pouvons par exemple dynamiser l'article indéfini :

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

Certains types de paramètres ont des règles de remplacement spéciales : les int sont remplacés par leur représentation en texte, les time sont converties en date ryzomienne, de même que les money.

Enfin, un dernier point mais non des moindres, les règles pour les identifiants et les contournements vues aux étapes 1 et 2 sont toujours valides.

4ème étape : Utiliser les paramètres – clause conditionnelle

Il est temps maintenant de dévoiler le système de clause conditionnelle.

Disons que l'identifiant et la valeur de chaîne que nous avons mis dans une phrase à l'étape précédentes sont une clause. Et disons qu'une phrase peut contenir plus d'une clause, celle-ci pouvant être choisie par le moteur de traduction à la volée en fonction de la valeur du paramètre. C'est le système de clause conditionnelle.

Commençons par un premier exemple. Comme pour l'étape 3, nous voulons tuer une créature, mais cette fois, nous ajoutons une variable pour le nombre d'animaux à tuer, de 0 à n. Ce dont nous avons besoin, c'est la condition à partir de laquelle choisir entre les trois clauses : pas de créature à tuer, une créature à tuer, ou plusieurs.

D'abord, écrivons la phrase, ses paramètres et ses trois clauses :

KILL_A_CREATURE (race crea, int count)
{
// no creature to kill (Pas de créature à tuer)
[There is no creature to kill today.]
// 1 creature to kill (1 créature à tuer)
    [Would you please kill a $crea$ for me ?]
// more than one (plus d'une)
[Would you please kill $count$ $crea$ for me ?]
}

Nous avons écrit trois versions du texte avec un sens et une structure grammaticale très différents. Maintenant, ajoutons les conditions. Les conditions sont placées avant l'identifiant et/ou la chaîne de valeur et sont indiquées par des parenthèses.

KILL_A_CREATURE (race crea, int count)
{
// no creature to kill (Pas de créature à tuer)
(count = 0) [There is no creature to kill today.]
// 1 creature to kill (1 créature à tuer)
(count = 1) [Would you please kill a $crea$ for me ?]
// more than one (plus d'une)
(count > 1) [Would you please kill $count$ $crea$ for me ?]
}

Facile, non ?

Maintenant, prenons un cas un peu plus compliqué : nous voulons écrire une phrase différente selon que le joueur soit masculin ou féminin. Voici une occasion d'introduire le paramètre self. Le paramètre Self est un paramètre caché, car il est toujours disponible et représente le destinataire de la phrase (celui à qui elle s'adresse).

Le paramètre Self porte les propriétés de genre et de nom. Vous pouvez créer un fichier self_words_<lang>.txt pour gérer les cas spéciaux (un joueur admin avec un nom traduisible, par exemple).

Réécrivons la requête pour tuer des animaux en tenant compte du genre du joueur :

KILL_A_CREATURE (race crea, int count)
{
// -- Joueur masculin
// pas de créature à tuer, joueur masculin
(count = 0 & self.gender = Male)
[Hi man, there is no creature to kill today .]
// 1 créature à tuer, joueur masculin
(count = 1 & self.gender = Male)
[Hi man, would you please kill a $crea$ for me ?]
// plus d'une, joueur masculin
(count > 1 & self.gender = Male)
[Hi man, Would you please kill $count$ $crea$ for me ?]

// -- Joueur féminin
// pas de créature à tuer, joueur féminin
(count = 0 & self.gender = Female)
[Hi girl, There is no creature to kill today.]
// 1 créature à tuer, joueur féminin
(count = 1 & self.gender = Female)
[Hi girl, Would you please kill a $crea$ for me ?]
// plus d'une, joueur féminin
(count > 1 & self.gender = Female)
[Hi girl, Would you please kill $count$ $crea$ for me ?]
}

Nous avons six clauses maintenant. Trois pour le nombre de créatures, multipliées par les deux genres du joueur.

Comme vous pouvez le voir, les tests conditionnels peuvent être combinés avec le caractère &. Ce qui signifie que tous les tests doivent être valides pour sélectionner la clause.

Vous pouvez utiliser n'importe quel paramètre comme opérateur gauche pour le test. Vous pouvez également spécifier une propriété de paramètre (issue du fichier de mots) comme opérande.

De l'autre côté, l'opérande de droite du test doit être une valeur constante (textuelle ou numérique). Les opérateurs disponibles sont

=
!=
<
<=
>
>=

Dans certains cas, vous pourriez avoir besoin de faire des combinaisons de tests avec OR (ou). Ceci est possible simplement en indiquant une liste de condition multiple devant une clause :

FOO_PHRASE (int c1, int c2)
{
(c1 = 0 & c2 = 10)
(c1 = 10 & c2 = 0)
[On passe dans cette clause si :
c1 égal zéro et c2 égal dix
ou
c1 égal dix et c2 égal zéro]
}

Règles détaillées de sélection des clauses :

  • Une clause valide est une clause où les combinaisons conditionnelles sont vraies pour un ensemble donné de valeurs du paramètre.
  • Les clauses sont évaluées dans l'ordre dans lequel elles sont écrites dans le fichier de phrase. La première clause valide sera retenue.
  • Si la première clause n'a aucune condition, elle sera utilisée comme clause par défaut si aucune clause conditionnelle n'a été choisie.
  • S'il n'y a pas de clause par défaut, et qu'aucune des clauses n'est retenue, alors c'est la première clause qui est choisie.
  • Pour la même phrase, chaque langue peut fournir son propre ensemble de conditions et de clauses pour s'adapter à ses besoins grammaticaux.

Liste des propriétés des paramètres codés en dur

Vous trouverez ici une liste exhaustive des propriétés des paramètres codés en dur. Ces propriétés sont toujours disponbible, même quand aucun fichier de mots n'a été fourni. De plus, les propriétés codées en dur ne peuvent pas être remplacées par un fichier de nom qui utiliserait une colonne du même nom.

Paramètre Propriété
Item
Place
Creature “name : nom du modèle pour la créature
gender : genre de la créature (tiré ?) du modèle”
Skill
Ecosystem
Race
Brick
Tribe
Guild
Player “name : nom du joueur
gender : genre du joueur dans le miroir”
Bot “career : carrière du bot
role : rôle du bot tel que défini dans creature.basics.chatProfile
name : nom de la créature
gender : genre de la créature (tiré ?) du modèle”
Integer
Time
Money
Compass
dyn_string_id Seuls les tests != et == sont possibles. Essentiellement pour comparer un paramètre avec 0.
string_id Seuls les tests != et == sont possibles. Essentiellement pour comparer un paramètre avec 0.
self “name : nom du joueur
gender : genre du joueur dans le miroir”
creature_model NB : utiliser le fichier de traduction creature_words !
entity ”== 0, != 0 : teste si l'entité est Unknown (inconnue) ou non
name : nom de la créature ou nom du joueur
gender : genre de la créature (tiré ?) du modèle ou genre du joueur (d'après l'info du joueur)“
bodypart
score
sphrase
characteristic
damage_type
bot_name

Workflow de traduction

Dans le worklow de traduction ci-dessous, nous considérons que la langue de référence est l'anglais.

Il y a une série d'outils et de fichiers bat pour aider à synchroniser les traductions. Voici une description point par point de comment travailler sur un fichier de traduction, quel outil utiliser et quand.

Seuls les ajouts aux fichiers de traduction existants sont supportés par les outils de traduction. Si vous souhaitez modifier ou supprimer une chaîne ou une phrase de traduction existante, ce sera à faire 'à la main' avec un maximum d'attention dans toutes les versions linguistiques.

Dans la plupart des cas, il vaut mieux créer une nouvelle entrée de traduction, plutôt que de gérer une modification dans tous les fichiers de traduction, et il est à peu près sûr de laisser les vieilles chaînes inutilisées dans les fichiers.

En tous cas, vous ne devez JAMAIS faire de modifications quand il y a des fichiers diff en attente.

Il est fortement recommandé de respecter scrupuleusement le workflow décrit pour éviter les problèmes de traduction, les chaînes manquantes ou autres problèmes bizarres qui pourraient se produire en travaillant avec plusieurs versions linguistiques d'un ensemble de fichiers.

Le travail de traduction se fait en collaboration entre l'éditeur, qui fait la partie 'technique' du travail, et un traducteur professionnel sous-contrat qui traduit uniquement des chaînes simples en chaînes riches et de très haute qualité en respectant le contexte.

L'outil qui génère le fichier diff pour la traduction garde les commentaires de la version de référence. Ceci peut être utile pour fournir des informations supplémentaires au traducteur au sujet du contexte du texte.

De plus, pour le fichier de phrases, le fichier diff inclut automatiquement les commentaires qui décrivent la liste de paramètres.

Par défaut il semble que le système travaille avec des fichiers ISO-8859-15 etpas UTF8 donc si vous obtenez des résultats étranges avec les commandes, c'est sûrement dû à un encodage UTF8 de votre fichier.

Structure de l'entrepôt de traduction

Tous les fichiers à traduire sont stockés dans une structure de répertoire bien définie, appelée entrepôt de traduction. Tout le travail de traduction est fait dans ce répertoire.

Des outils sont fournis pour installer le fichier traduit dans le client et le répertoire du serveur après la fin du cycle de traduction.

  • translation/ Répertoire racine pour la traduction
  • languages.txt Un simple fichier texte qui contient tous les codes langues (ISO 639-2) avec lesquels nous travaillons (par exemple : en, fr, etc…).
  • work/ C'est le point de départ pour l'ajout de nouveau contenu. Ce répertoire contient tous les fichiers qui peuvent être modifiés pour ajout.
  • diff/ Contient tous les fichiers diff générés par les outils. C'est là que se fait l'essentiel du travail. Après l'opération de fusion qui intègre les diff traduits dans les fichiers de traduction, les fichiers diff sont déplacés dans le répertoire history. Quand des fichiers diff sont générés, ils sont préfixés avec un numéro de version calculé automatiquement à partir de l'horodatage Unix courant. Ceci permet de générer de nouveaux fichiers diff sans attendre que la traduction du diff soit faite. De plus, quand vous générez un nouveau fichier diff sans fichier diff fusionné, le nouveau diff ne contient que les différences.
  • history/ Contient tous les fichiers diff qui ont été traduits et fusionnés. Utilisé pour des raisons de sauvegarde et de sécurité.
  • translated/ Contient tous les fichiers traduits. Le contenu de ce répertoire est installé sur les entrepôts du client et du serveur lorsque le cycle de traduction est terminé. Vous ne devez jamais rien changer à la main dans ce repertoire à moins de savoir exactement ce que vous faites.

Traduction des noms de bot

La commande extract_bot_names analyse le fichier bot_names.txt en fonction des Primitives et si certains noms sont utilisés plusieurs fois dans celles-ci, il doit transformer le nom du bot en un nom générique du type gn_nomgénérique dont la correspondance est crée dans le title_words correspondant.

Test à effectuer cf HexChatlog kerv30/12/2015 - 14:30 — Yann K 2015/12/30 14:42

Chaîne statique sur le client

Tâche initiale

Editeur :

  • Créer le fichier de référence en.uxt' dans le répertoire work', remplir la première chaîne nécessaire LanguageName puis ajouter toutes les chaînes pour la version anglaise.
  • Générer les fichiers diff de chaînes statiques avec la commande make_string_diff. Ceci créera les fichiers diff pour tous les langages dans le répertoire diff.
  • Envoyer les fichiers diff au traducteur.

Traducteur :

  • Traduire les fichiers diff.
  • Les renvoyer à l'éditeur.

Editeur :

  • Mettre les fichiers diff traduits dans le répertoire diff (ce qui écrasera les fichiers diff non traduits et les remplacera par les fichiers traduits).
  • Fusionner les fichiers diff traduits à l'aide de la commande merge_string_diff. Cela créera les fichiers <lang>.uxt pour chaque langue dans le répertoire translated et déplacera le diff dans le dossier history.

Une fois la tâche initiale terminée, le workflow entre en mode incrémental.

Tache incrémentale

Editeur

  • Ajouter des chaînes dans le fichier de référence en.uxt dans le répertoire work.
  • Générer les fichiers diff de chaînes statiques avec la commande make_string_diff. Ceci créera les fichiers diff pour chaque langue dans le dossier diff.
  • Envoyer les fichiers diff au traducteur.

Traducteur

  • Traduire les fichiers diff.
  • Les renvoyer à l'éditeur.

Editeur

  • Déposer les fichiers diff traduits dans le répertoire diff (ce qui écrasera les fichiers diff non traduits et les remplacera par les fichiers traduits).
  • Fusionner les fichiers diff avec la commande merge_string_diff. Ceci appliquera les fichiers sdiff aux fichiers <lang>.uxt pour chaque langue dans le dossier translated et déplacera les diff dans le dossier history.

Chaînes dynamiques sur le serveur

Tâche initiale

Editeur

  • Créer le fichier de référence phrase_wk.uxt dans le répertoire work et ajouter toutes les entrées de phrases pour la version anglaise.
  • Générer les fichiers de phrase diff avec la commande make_phrase_diff. Ceci créera les fichiers diff pour chaque langue dans le répertoire diff.
  • Traduire le fichier de phrases diff. Ceci implique de bonnes connaissances de la structure grammaticale de chaque langue pour s'adapter aux règles des clauses de sélection.
  • Fusionner le fichier diff traduit à l'aide de la commande merge_phrase_diff. Ceci créera tous les fichiers phrase_<lang>.txt dans le répertoire translated pour le fichier diff traduit, puis déplacera les fichiers diff dans le répertoire history.
  • Générer le fichier de clauses diff avec la commande make_clause_diff. Ceci créera le fichier de clauses diff dans le répertoire diff pour toutes les langues.
  • Envoyer les fichiers de clauses diff au traducteur.

Traducteur

  • Traduire les fichiers de clauses diff.
  • Les renvoyer à l'éditeur.

Editeur

  • Déposer les fichiers de clause diff traduits dans le répertoire diff (ce qui écrasera les fichiers de clause diff non traduits et les remplacera par les fichiers traduits).
  • Fusionner les fichiers de clause diff avec la commande merge_clause_diff. Ceci créera les fichiers clause_<lang>.txt pour chaque langue dans le répertoire translated et déplacera les diff dans le répertoire history.

Une fois la tâche initiale accomplie, le workflow entre en mode incrémental.

Tache incrémentale

Editeur *

  • Ajouter la nouvelle phrase dans phrase_wk.uxt dans le dossier work.
  • Générer les fichiers de phrase diff avec la commande make_phrase_diff. Ceci créera les fichiers diff pour chaque langue dans le répertoire diff.
  • Traduire le fichier de phrase diff. Ceci implique de bonnes connaissances de la structure grammaticale de chaque langue pour s'adapter aux règles de sélection des clauses.
  • Fusionner les fichiers diff traduits à l'aide de la commande merge_phrase_diff. Ceci ajoutera les diff à leurs fichiers phrase_<lang>.txt respectifs dans le répertoire translated puis déplacera les fichiers diff dans le répertoire history.
  • Générer les fichiers de clauses diff avec la commande make_clause_diff. Ceci créera le fichier de clause diff dans le répertoire diff.
  • Envoyer les fichiers de clause diff au traducteur.

Traducteur

  • Traduire les fichiers de clauses diff.
  • Les renvoyer à l'éditeur.

Editeur

  • Déposer les fichiers de clause diff traduits dans le répertoire diff (ce qui écrasera les fichiers de clause non traduits et les remplacera par les fichiers traduits).
  • Fusionner les fichiers de clause traduits avec la commande merge_clause_diff. Ceci ajoutera les fichiers diff à leurs fichiers clause_<lang>.txt respectifs pour chaque langue dans le répertoire translated et déplacera les diff dans le répertoire history.

Fichiers de mot côté serveur

Cette partie est obsolète.

Il faut désormais recourir à des outils également, comme pour les autres:

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 FOLOWING TWO LINE WHEN TRANSLATION IS DONE and DIFF NOT TRANSLATED 6. Save files 7. Launch 6_merge_words_diff to merge your translations in “translated”

NB : ‘mot’ (N.d.T. 'words', dans le texte d'origine) n'a aucun rapport avec le program Word de microsoft !

Les fichiers de mots sont toujours mis à jour à la main parce qu'ils sont rarement mis à jour par l'éditeur (et, en général, uniquement pour de grosses mises à jour). De plus, quand de nouvelles phrases sont traduites, il peut être nécessaire de créer et de remplir une nouvelle colonne dans l'un des fichiers de mots pour s'adapter à la traduction.

Il y a donc juste un workflow, mais pas d'outils.

Taches initiales

Editeur

  • Créer la feuille initiale pour chaque type de paramètre avec tous les identifiants possibles dans le langae de référence dans le répertoire translated.
  • Créer et remplire les colonnes par défaut name et p.
  • Créer les feuilles pour toutes les langues en copiant les feuilles de la langue de référence.
  • Créer et remplir toutes les colonnes de bases dépendantes de la langue.
  • Envoyer toutes les feuilles au traducteur.

Traducteur

  • Traduire toutes les feuilles dans toutes les langues, en ajoutant éventuellement toutes les colonnes nécessaires.
  • Renvoyer les feuilles traduites à l'éditeur.
  • Garder une copie des feuilles traduites comme référence pour la traduction des phrases et des clauses.

Editeur

  • Déposer les feuilles traduites dans le répertoire translated, ce qui écrasera les anciennes.

Après cette tache initiale, il y a deux événements possibles :

Besoin d'une nouvelle colonne

Traducteur

  • Au moment de traduire un diff de phrase ou de clause, il apparaît qu'il manque une ou plusieurs nouvelles colonnes pour l'une des langues et un type de paramètre.
  • Définir les colonnes nécessaires.
  • Contacter l'éditeur pour vérifier qu'aucune mise à jour de feuille n'est en attente. Si oui, appliquer d'abord le workflow pour le nouveau contenu de feuilles.
  • Ajouter les nouvelles colonnes et les remplir dans les feuilles / langues concernées.
  • Envoyer les feuilles à l'éditeur.

Editeur

  • Déposer les nouvelles feuilles dans le répertoire translated, ce qui écrasera les anciennes.
Nouveau contenu de feuilles

Editeur

  • Le nouveau contenu de jeu est à intégrer dans le jeu.
  • Contacter le traducteur pour vérifier qu'aucune mise à jour de feuilles n'est en cours. Si oui, appliquer d'abord le workflow pour besoin d'une nouvelle colonne.
  • Créer de nouvelles feuilles pour le langage de référence, et ne contenant que le nouveau contenu.
  • Ajouter et remplir les colonnes par défaut pour les nouvelles feuilles (voir Taches initiales).
  • Créer les nouvelles feuilles pour toutes les langues en copiant les feuilles de la langue de référence.
  • Ajouter toutes les colonnes pour respecter le format de feuille en cours, pour chaque type et chaque langue, mais NE PAS LES REMPLIR.
  • Envoyer les nouvelles feuilles au traducteur.

Traducteur

  • Traduire les nouvelles feuilles.
  • Ajouter les nouvelles feuilles à la fin des feuilles existantes.
  • Envoyer le résultat de la fusion à l'éditeur.
  • Conserver le résultat de la fusion pour référence pour la traduction des phrases et des clauses, et les futurs ajouts de contenu.

Editeur

  • Déposer les nouvelles feuilles dans le répertoire translated, ce qui écrasera les anciennes.

Installer les fichiers traduits

A la fin d'un cycle de traduction, il faut installer les fichiers traduits dans les structures d'annuaire du client et du serveur.

Pour cela, on utilise la commande install_translation. Les fichiers <lang>.uxt sont copiés dans la structure du client dans Ryzom/gamedev/language. Tous les autres fichiers sont copiés dans Ryzom/data_shard.

Pour appliquer la traduction sur le client, l'éditeur doit faire un patch.

Pour appliquer la traduction sur le serveur, il faut juste entrer la commande reload sur le InputOutputService.

Travailler avec des fichiers de traduction, du point de vue du programmeur

Les programmeurs NeL/Ryzom peuvent utiliser le système de traduction avec un nombre d'appels restreint.

Accéder aux chaînes statiques du client

Pour obtenir la chaîne unicode à partir d'une chaîne d'identifiant, utiliser la classe NLMISC::CI18N.

Tout d'abord, il faut s'assurer que les fichiers de traduction *.uxt sont disponibles dans le chemin de recherche.

Ensuite, il est possible de charger un ensemble de chaînes de langue en appelant NLMISC::CI18N::load(languageCode).

Le paramètre languageCode est le code langue tel que défini au chapitre 2 “Code langue”. Ensuite, appeler la méthode NLMISC::CI18N::get(identifier) pour obtenir la chaîne unicode associée à l'identifiant.

Chaînes dynamiques

Une chaîne dynamique nécessite un peu plus de travail et une infrastructure d'instance complète.

La gestion des chaînes dynamiques implique un service de requête (RQS), le InputOutputService (IOS), le FrontEnd (FE), le client ryzom, plus les services de bases pour faire tourner les autres (nommage, tick, miroir).

RQS est un service qui nécessite d'envoyer une chaîne dynamique au client.

RQS envoie également l'identifiant de chaîne dynamique au client en utilisant la base de données ou n'importe quelle autre méthode.

Le proxy est un petit morceau de code qui construit et envoie le message de PHRASE au service IOS.

IOS a la lourde charge de parser (analyser ?) les paramètres, en choisissant la bonne clause et en construisant la chaîne résultante, puis en envoyant un minimum de données au client.

Le client reçoit la phrase et demande tout élément manquant de la chaîne à l'IOS.

Construire une liste de paramètres en envoyer la chaîne

Pour accéder au proxy, il faut inclure game_share/string_manager_sender.h et le lier avec game_share.lib.

Il faut d'abord construire la liste des paramètres. Ce qui se fait en remplissant un vecteur de STRING_MANAGER::TParam struture. Pour chaque paramètre, il faut définir le champ Type, puis écrire la donnée appropriée pour le membre.

Il FAUT respecter exactement la définition des paramètres de phrase du fichier de phrases.

On peut alors appeler :

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

)

destClientId est l'identifiant du client de destination, phraseIdentifier est l'identifiant tel qu'écrit dans le fichier de phrase, parameters est le vecteur de paramètres que vous avez construit auparavant.

Cette fonction renvoie l'ID dynamique attribué à cette phrase.

Exemple : envoyer la phrase ‘kill a creature’ (cf. § 5.2, étape 4) :

// inclure la définition du gestionnaire de chaîne du proxy
#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;

// d'abord, il nous faut la race de la créature
param.Type = STRING_MANAGER::creature;
param.Enum = race;
params.push_back(param);

// ensuite, le nombre de créatures à tuer
param.Type = STRING_MANAGER::integer;
param.Int = nbToKill;
params.push_back(param);

// et maintenant, envoyer le message
uint32 dynId = STRING_MANAGER::sendStringToClient(
destClient,
“KILL_A_CREATURE”,
params);

return dynId;
}

Membre à remplir dans TParam en fonction du type de paramètre

  • item: Remplit le SheetId avec l'identifiant de l'item
  • place: Remplit l'identifiant de la chaîne avec l'identifiant de l'emplacement
  • creature: Remplit EId avec l'id de l'entité créature
  • skill: Remplit Enum avec la valeur enum de SKILLS::ESkills
  • ecosystem: Remplit Enum avec la valeur enum de ECOSYSTEM::EEcosystem
  • race: Remplit Enum avec la valeur enum de GSPEOPLE::EPeople
  • brick: Remplit SheetId avec l'identifiant de la brique
  • tribe: pas encore défini
  • guild: pas encore défini
  • player: Remplit EId avec l'identifiant du joueur
  • bot: Remplit EId avec l'identifiant du bot
  • integer: Remplit Int avec la valeur integer/entière (sint32)
  • time: Remplit Time avec la valeur time/temporelle (uint32)
  • money: Remplit Money avec la valeur money/monétaire (uint64)
  • compass: pas encore défini
  • dyn_string_id: Remplit StringId avec un identifiant de chaîne dynamique
  • string_id: Remplit StringId avec un identifiant de chaîne
  • creature_model: Remplit SheetId avec l'identifiant de la créature
  • entity: Remplit EId avec l'entité de la créature, du PNJ ou du joueur
  • body_part: Remplit Enum avec la valeur enum value de BODY::TBodyPart
  • score: Remplit Enum avec la valeur enum de SCORES::TScores
  • sphrase: Remplit SheetId avec l'identifiant de la phrase
  • characteristic: Remplit Enum avec la valeur enum de CHARACTERISTICS::TCharacteristic
  • damage_type: Remplit Enum avec la valeur enum de DMGTYPE::EDamageType
  • bot_name: Remplit Identifier avec le nom du bot sans la fonction
  • literal: Remplit Literal avec la chaîne Unicode littérale

Accéder aux chaînes dynamiques depuis le client

Côté client, accéder aux chaînes dynamiques est assez simple. Il faut juste faire attention au délai de transmission dans certains cas.

Une fois l'identifiant de chaîne dynamique récupéré d'où que ce soit (par exemple, de la base de données), il faut juste sonder (pol ?) le getDynString method de STRING_MANAGER::CStringManagerClient.

La méthode renvoie faux tant que1) la chaîne demandée est incomplète ou inconnue.

Même si la méthode renvoie faux, elle peut ramener un texte partiel, avec du texte de remplacement manquant.

Les chaînes dynamiques sont stockées dynamiquement, et si le code est suffisamment intelligent, il peut libérer la mémoire des chaînes dynamiques quand celles-ci ne sont plus utiles en appelant releaseDynString.

Pour être plus efficace, on peut appeler chaque cadre2) la méthode getDynString jusqu'à ce que celle-ci renvoie vrai, puis stocker la chaîne et appeler releaseDynString.

STRING_MANAGER::CStringManagerClient est basé sur un schéma unique, donc il faut appeler STRING_MANAGER::CStringManagerClient::instance() pour obtenir un pointeur vers l'instance unique.

Voici un exemple de code simple.

// inclure la définition du gestionnaire de chaîne du proxy
#include “string_manager_client.h”

using namespace STRING_MANAGER;

/** Une méthode qui reçoit l'identifiant de chaîne dynamique
* et qui imprime la chaîne dynamique dans le log.
* L'appeler à chaque frame/cadre jusqu'à valeur vraie
*/
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;
}

Guide de création de texte pour Ryzom

Il y a beaucoup de places pour le texte dans Ryzom, cette page va clarifier les conventions d'identification de texte, le contexte disponible pour l'insertion de texte et les paramètres contextuels de texte.

Conventions pour les identifiants

Identifiants de chaînes dans en.uxt

Ces identifiants sont écrits en minuscule, avec une majuscule pour chaque nouveau mot. Exemple:

unIdentifiantSimple
unAutreIdentifiant

 

Identifiants de phrases dans phrase_en.txt

Ces identifiants sont écrits en majuscules, et les mots sont séparés par “underscore”.

Exemple:

UN_IDENTIFIANT_SIMPLE
UN_AUTRE_IDENTIFIANT

Identifiants de chaînes (ou de clauses) dans clause_en.txt

Ces identifiants sont écrits comme des identifiants de chaîne dans en.uxt.

Mais, comme ils sont à l'intérieure d'une définition de phrase, ils doivent contenir le nom des phrases comme nom de base. Le nom des phrases est en minuscule pour respecter la convention des identifiants de chaîne.

Exemple, dans une phrase appelée UNE_PHRASE_SIMPLE, l'identifiant doit être :

unePhraseSimple

De plus, lorsqu'il y a plus d'une clause pour une phrase donnée, l'identifiant de clause doit être suivi par quelques étiquettes qui donneront un indice au traducteur sur la signification de chaque clause. Exemple, dans une phrase nommée UNE_PHRASE_SIMPLE et qui contient deux clauses, une pour le singulier et une pour le pluriel, nous pourrions avoir les identifiants suivants :

unePhraseSimpleS
unePhraseSimpleP

Contextes des textes

Contexte “Chat”

Le contexte “Chat” englobe tous les textes qui viennent d'un PNJ via une fenêtre de chat et des bulles de texte.

Le bot dit/crie en alentours

Il n'y a qu'un paramètre disponible : l'entité PNJ qui parle/crie.

Le nom de la phrase commence par SAY_

Exemple de phrase :

SAY_XXX (bot b)
{
     sayXxx    [Bonjour ici, je m'appelle $b$, quelqu'un m'entend ?]
}
Le bot parle au chef de l'escorte (un joueur)

Deux paramètres : le bot qui parle, et le joueur.

Le nom de la phrase commence par TALK_

Exemple de phrase :

     TALK_XXX (bot b, player p)
{
     talkXxx   [Bonjour $p$, je m'appelle $b$, j'ai besoin d'aide !]
}
Le bot parle/crie en réponse à un clic sur lui

Deux paramètres : le bot cliqué et le joueur.

Le nom de la phrase commence par CLICK_

Exemple de phrase

     CLICK_XXX (bot b, player p)
{
     clickXxx  [Bonjour $p$, je m'appelle $b$, vous avez cliqué sur moi ?]
}

Contexte interactif (également appelé botchat)

Le botchat couvre tous les textes dans le dialogue interactif avec un PNJ.

Phrases liées aux missions

Missions statiques

Tous les noms de phrases liées à des missions ont une racine définie par le nom de la mission telle que positionnée dans le noeud de mision de l'éditeur de monde.

A partir de cette racine, plusieurs extensions peuvent être ajoutées pour former des noms de phrase :

  • _TITLE : pour le titre de la mission
  • _STEP_X : pour le texte de l'étape X (les étapes de mission commencent à 1)
  • _END : pour le texte de fin de mission.

Exemple:

A partir d'une mission appelée INSTRUCTEUR_MIS_1,
Le titre sera INSTRUCTEUR_MIS_1_TITLE
Le texte de l'étape 1 de la mission sera INSTRUCTEUR_MIS_1_STEP_1
Le texte de l'étape 2 de la mission sera INSTRUCTEUR_MIS_1_STEP_2
Le texte de fin de mission sera INSTRUCTEUR_MIS_1_END

Paramètres :

  • XXXXXXX_TITLE (bot b, player p)
    • b est le bot à qui le joueur parle
    • p est le joueur
  • XXXXXXX_STEP_X (bot giver, bot current, bot target, bot previous, player p)
    • giver est le donneur de la mission
    • current est le bot à qui le joueur parle
    • target est le bot à aller voir pour la prochaine etape
    • previous est le bot vu à l'étape précédente
    • p est le joueur
  • XXXXX_END (bot current, bot giver, bot previous, player p)
    • giver est le donneur de la mission
    • current est le bot à qui le joueur parle
    • previous est le bot vu à l'étape précédente
    • p est le joueur

Les paramètres des textes d'étapes du journal dépendent de la nature de l'étape (voir avec Nicolas Brigand).   Pour les textes de progression de mission dans le menu contextuel, il en existe deux :

  • MISSION_STEP_GIVE_ITEM_CONTEXT (bot giver, bot previous, item i)
  • MISSION_STEP_TALK_CONTEXT (bot giver, bot previous)

Le premier est le texte standard, le second est affiché quand on doit donner quelque chose au bot.

Contexte supplémentaire pour les entrées de menu

Il est possible d'ajouter des entrées informatives simples dans le menu contextuel du bot. Cette partie est composée de deux phrases : le nom affiché pour l'entrée de menu et le contenu de texte affiché après avoir cliqué sur l'entrée de menu.

Deux paramètres : le bot support du menu de contexte et le joueur.

Le nom de la phrase commence par BC_MENU_ pour l'entrée de menu et BC_MENUTEXT_

Messages système (info de combat)

Les paramètres dépendent des phrases, mais il y a quelques types de phrases bien définis :

  • COMBAT_
  • MAGIC_
  • HARVEST_
  • CRAFT_
  • DEATH_
  • PROGRESS_

 

1)
N.d.T. : le texte d'origine dit “jusqu'à ce que”, “until”
2)
(NDT : each frame ?
fr/rc_localisation_traduction.txt · Dernière modification: 2016/11/17 21:35 par Domperss