Mise à jour du 24/07/10. — Le code de YALT a été réécrit de A à Z. Le présent article ne reflète pas cette évolution. YALT 2 produit désormais l'objet L10N de façon beaucoup plus compact, il améliore la compatibilité avec des scripts de portée session (#targetengine session) et permet au développeur d'échapper les caractères Unicode via la syntaxe \u.... (afin de conserver tout le script en encodage Ascii). Vous pouvez également consulter la locale active en interrogeant la propriété L10N.locale (qui retourne une chaîne de caractères). Enfin, YALT 2.1 abandonne totalement la technique du « fichier qui se lit lui-même » et offre la possibilité de composer avec des paramètres additionnels via des arguments formels %1, %2, etc.
Accéder à l'article actualisé.

BILLET ARCHIVÉ :

Notre syntaxe est encodée sous la forme de commentaires JavaScript introduits par des doubles slashes :

//======================================
// <L10N> :: FRENCH_LOCALE :: GERMAN_LOCALE
//======================================
// Yes :: Oui :: Ja
// I love :: J'aime :: Ich liebe
// </L10N> ::
 

Supposez qu'un fichier texte F contienne une table de localisation formatée comme ci-dessus. À partir d'un script S, il serait aisé d'ouvrir F, d'extraire le contenu utile et de retourner les données dans un tableau associatif :

Analyseur de syntaxe L10N

Tout d'abord, nous devons capturer les lignes de commentaires situées entre les balises <L10N> et </L10N>, puis analyser les chaînes séparées par ' :: '.

__sep = ' :: ';
__beg = ' <L10N>';
__end = ' </L10N>';
 
/*arr*/File.prototype.getCommentsContaining = function(/*str*/sep_)
//--------------------------------------
// Ouvrir ce fichier et extraire les lignes amorcees
// par '//' ET contenant la chaine <sep_>
    {
    var r = [];
    if ( this.open('r') )
        {
        var line;
        while(!this.eof)
            {
            line = this.readln();
            if ( ( line.substr(0,2) == '//' )
                && ( line.indexOf(sep_) >= 0 ) )
                r.push(line.substr(2));
            }
        this.close();
        }
    return(r);
    }
 
/*arr*/Array.prototype.parseL10N = function(/*str*/locale_)
//--------------------------------------
// Analyse ce tableau d'entrees L10N et retourne
// un tableau associatif pour une <locale_> spécifique
// <locale_> est fourni sous la forme d'un nom
// d'enum. Locale: "ENGLISH_LOCALE", "FRENCH_LOCALE", etc.
    {
    var r = [];
    var sz = this.length;
    var lm, ss;
    var st = -1
    var rg = 0;
    for ( var i=0 ; i < sz ; i++ )
        {
        ss = this[i].split(__sep, lm);
        if ( ( st == -1 ) && ( ss[0] == __beg ) )
            {
            lm = ss.length;
            for ( var j = 1 ; j < lm ; j++ )
                if ( ss[j] == locale_ ) rg = j;
            st = 0;
            continue;
            }
        if ( st == 0 )
            {
            if ( ( rg == 0 ) || ( ss[0] == __end ) ) break;
            if ( ss.length <= rg ) continue;
            r[ss[0].substr(1)] = ss[rg];
            }
        }
    return(r);
    }
 
 
// echantillon de code
 
var F = File("l10n_strings.txt");
var lines = F.getCommentsContaining(__sep);
var frStrings = lines.parseL10N("FRENCH_LOCALE");
 
// ici, <frStrings> contient un tableau associatif des
// traductions françaises extraites du fichier "l10n_strings.txt"
alert( frStrings["Yes"] ); // "Oui"
 

La méthode File.getCommentsContaining(<sep>) récupère le contenu utile du fichier cible, puis Array.parseL10N(<locale>) extrait les correspondances entre une clé donnée dans la langue par défaut (telle que "Yes") et la chaîne associée dans la langue cible.

Rendre les noms de « locale » accessibles

À ce point, nous disposons d'un code générique réutilisable capable d'analyser n'importe quel fichier encodé dans notre syntaxe <L10N>. Gardez à l'esprit que cette procédure ne renvoie pas la table de localisation complète, mais seulement la colonne correspondant à la langue recherchée. Le paramètre que nous utiliserons dans le futur est le nom de l'identifiant app.locale (c'est-à-dire, la langue de l'utilisateur). Comme la propriété app.locale renvoie un id (Number) à l'intérieur de la classe d'énumération Locale, nous devons extraire de ce nombre son nom accessible (tel que "FRENCH_LOCALE"), ainsi que le réclame notre syntaxe. Pour ce faire, nous greffons une méthode toLocaleName à l'objet Number :

/*str*/Number.prototype.toLocaleName = function()
//--------------------------------------
// renvoie le nom accessible de cette
// Locale (connue par son id)
{
for ( var p in Locale )
    if ( Locale[p] == this ) return(p);
return(null);
}
 
// test:
alert( app.locale.toLocaleName );
 

Pour information, voici la liste des Locale InDesign de la version 3 (CS) à la version 6 (CS4) :

Nom de Locale ID InDesign
ARABIC_LOCALE 0x4C434172 5, 6
CZECH_LOCALE 0x4C43437A 5, 6
DANISH_LOCALE 0x4C43446E 3, 4, 5, 6
ENGLISH_LOCALE 0x4C43456E 3, 4, 5, 6
FINNISH_LOCALE 0x4C43466E 3, 4, 5, 6
FRENCH_LOCALE 0x4C434672 3, 4, 5, 6
GERMAN_LOCALE 0x4C43476D 3, 4, 5, 6
GREEK_LOCALE 0x4C434772 5, 6
HEBREW_LOCALE 0x4C434862 5, 6
HUNGARIAN_LOCALE 0x4C434875 5, 6
INTERNATIONAL_ENGLISH_LOCALE 0x4C434569 3, 4, 5, 6
ITALIAN_LOCALE 0x4C434974 3, 4, 5, 6
JAPANESE_LOCALE 0x4C434A70 3, 4, 5, 6
KOREAN_LOCALE 0x4C434B6F 6
POLISH_LOCALE 0x4C43506C 5, 6
PORTUGUESE_LOCALE 0x4C435067 3, 4, 5, 6
ROMANIAN_LOCALE 0x4C43526F 5, 6
RUSSIAN_LOCALE 0x4C435275 5, 6
SIMPLIFIED_CHINESE_LOCALE 0x4C43436E 6
SPANISH_LOCALE 0x4C435370 3, 4, 5, 6
SWEDISH_LOCALE 0x4C435377 3, 4, 5, 6
TRADITIONAL_CHINESE_LOCALE 0x4C435477 6
TURKISH_LOCALE 0x4C435472 5, 6
UKRAINIAN_LOCALE 0x4C43556B 5, 6

Étape d'auto-analyse

L'ultime étape de notre technique consiste à utiliser le même fichier, et pour la localisation, et pour le processus principal. Telle est l'idée-clé de la « technologie YALT » : le projet localisé réside dans un fichier unique contenant à la fois la table L10N et le script. Il nous reste alors à ouvrir le fichier du script à partir du script lui-même afin d'analyser les lignes commentées selon la syntaxe définie plus haut.

Nous pouvons à présent assurer cette opération en une seule instruction, stockant les données utiles de localisation dans une propriété statique (.L10N) de l'objet String :

String.L10N = File(app.activeScript).
    getCommentsContaining(__sep).
    parseL10N(app.locale.toLocaleName());
 

Une fois créé, le tableau String.L10N permet de convertir toute chaîne prise en charge de la langue par défaut vers la langue cible. Nous gérons cette traduction à travers une fonction nommée __ (double underscore) :

function __(s)
{
return(String.L10N[s]||s);
}
 

ainsi, en tout point du script, un code du genre __("Yes") renverra le message localisé correspondant s'il existe, sinon la chaîne "Yes" inchangée. De cette façon, le développeur peut construire rapidement et de façon générique l'interface complète de son script sans se préoccuper de traduction, en utilisant la syntaxe __(<myDefaultLocaleString>). Pour localiser ensuite <myDefaultLocaleString>, il vous suffira de saisir les champs correspondants dans la table <L10N>.

Nous souhaitons enfin que le mécanisme YALT soit chargé une fois au tout début du script, sans être compromis par d'éventuelles conditions de persistance induites par la directive #targetengine. Une solution consiste à encapsuler tout le code dans un bloc if testant l'existence de la fonction __ par typeof(__).

Voici donc la structure résultante d'un projet :

 
// 1. Créez vos messages localisés en utilisant
//    la syntaxe commentée ci-dessous:
 
//======================================
// <L10N> :: FRENCH_LOCALE :: GERMAN_LOCALE
//======================================
// Yes :: Oui :: Ja
// I love :: J'aime :: Ich liebe
// </L10N> ::
 
 
// 2. Conservez le bloc ci-dessous dans votre script:
 
if ( typeof __ == 'undefined' )
{
__sep = ' :: ';
__beg = ' <L10N>';
__end = ' </L10N>';
 
/*arr*/File.prototype.getCommentsContaining = function(/*str*/k_)
//--------------------------------------
    {
    // code of File.getCommentsContaining 
    }
 
/*arr*/Array.prototype.parseL10N = function(/*str*/locale_)
//--------------------------------------
    {
    // code of Array.parseL10N 
    }
 
/*str*/Number.prototype.toLocaleName = function()
//--------------------------------------
  {
    // code of Number.toLocaleName 
  }
 
String.L10N = File(app.activeScript).
    getCommentsContaining(__sep).
    parseL10N(app.locale.toLocaleName());
 
function __(s){return(String.L10N[s]||s);}
}
 
 
// 3. Utilisez la syntaxe __(<string>) partout où
//     vous avez besoin de localiser votre texte:
 
alert( __("I love") + " Indiscripts.com!");
 
 

Le fichier YaltSample.js fournit le code générique complet à partir duquel élaborer votre propre projet.

Partie 1