Localisation de l'application

Comme vu dans l'article traitant de l'usage de la classe Application, un paramètre déterminant à passer au constructeur de cette classe est le paramètre de localisation. Par défaut, ce paramètre a la valeur 'en' (pour lnague anglaise). Mais on peut l'utiliser pour d'autres langues comme 'fr' pour le français.
Mais de quoi s'agit-il ?
L'un des problèmes apparu avec le développement des applications fenêtrées et de leur diffusion par Internet sur le continent européen et ailleurs, c'est leur traduction dans le language de l'utilisateur. Des expressions dans le langage du développeurs telles que icônes et chaines de caractères se trouvent dissiminées un peu partout dans la programmation. Cela oblige à une revue détaillée de tout le code pour traduire ces expressions. Ce qui peut entrainer des décalages d'affichage puisque que le texte de chaque langue n'occupe pas exactement le même espace.
Pour régler ce problème, WinLib offre la possibilité de localiser les applications en rassemblant toutes les informations localisées dans un fichier unique, le fichier de ocalisation. Ce fichier doit se trouver à la racine du projet Python. Les fichiers sont rédigés en JSON (JavaScript Object Notation) et nommé en fonction de la langue utilisée : en.json pour l'anglais, fr.json pour le français, etc. La diffusion d'une application dans une langue donnée consiste à traduire le fichier de localisation et de le nommer avec l'abréviation du pays concerné. Pour cela, même si ce n'est pas obligatoire avec WinLib, il est conseiller d'utilise les codes ISO-639-1 des pays. Par exemple, pour créer une version en allemand de l'application, il suffit de traduire l'un des fichiers de localisation dans cette langue et lui donner comme nom de.json. Une dernière précision, pour ne pas créer d'anomalie d'affichage avec les caractères accentués, le fichier doit être encodé UTF-8.

Les fichiers de localisation

Voici deux exemples de fichier de localisation. Il est possible ainsi de comparer leur contenu respectif.

en.json

Fichier utilisé par WinLib pour la langue anglaise :

{
  "application":
  {
    "title": "My Application Title",
    "geometry": "1200x800",
    "not_implemented_command" :
    {
      "title": "Application information",
      "message": "This command is not yet implemented."
    },
  },
  "menu":
  [
    {
      "title" : "File",
      "ul" : "0",
      "icon" : "icon_menu_file.png",
      "options" :
      [
        { "type" : "command", "title" : "New...", "ul" : "0", "command" : "menu_file_new", "icon" : "icon_menu_file_new16.png",
          "accel" : ["Ctrl+N", "<Control-KeyPress-n>", "<Control-KeyPress-N>"] },
        { "type" : "command", "title" : "Open...", "ul" : "0", "command" : "menu_file_open", "icon" : "icon_menu_file_open16.png",
          "accel" : ["Ctrl+O", "<Control-KeyPress-o>", "<Control-KeyPress-O>"] },
        { "type" : "command", "title" : "Save as...", "ul" : "5", "command" : "menu_file_save_as", "icon" : "icon_menu_file_save_as16.png",
          "accel" : ["Ctrl+Alt+S", "<Control-Alt-KeyPress-s>", "<Control-Alt-KeyPress-S>"] },
        { "type" : "command", "title" : "Save", "ul" : "0", "command" : "menu_file_save", "icon" : "icon_menu_file_save16.png",
          "accel" : ["Ctrl+S", "<Control-KeyPress-s>", "<Control-KeyPress-S>"] },
        { "type" : "separator" },
        { "type" : "command", "title" : "Print...", "ul" : "0", "command" : "menu_file_print", "icon" : "icon_menu_file_print16.png",
          "accel" : ["Ctrl+P", "<Control-KeyPress-p>", "<Control-KeyPress-P>"] },
        { "type" : "command", "title" : "Print preview...", "ul" : "0" , "command" : "menu_file_print_preview", "icon" : "icon_menu_page_setup16.png" },
        { "type" : "separator" },
        { "type" : "command", "title" : "Exit", "ul" : "0", "command" : "menu_application_exit", "icon" : "icon_menu_app_exit16.png",
          "accel" : ["Alt+F4", "<Alt-F4>"] }
      ]
    },
    {
      "title" : "Edit",
      "ul" : "0",
      "options" :
      [
        { "type" : "command", "title" : "Undo", "ul" : "0", "command" : "menu_edit_undo", "icon" : "icon_menu_edit_undo16.png",
          "accel" : ["Ctrl+Z", "<Control-KeyPress-z>", "<Control-KeyPress-Z>"] },
        { "type" : "command", "title" : "Redo", "ul" : "0", "command" : "menu_edit_redo", "icon" : "icon_menu_edit_redo16.png",
          "accel" : ["Ctrl+Y", "<Control-KeyPress-y>", "<Control-KeyPress-Y>"] },
        { "type" : "separator" },
        { "type" : "command", "title" : "Cut", "ul" : "2", "command" : "menu_edit_cut", "icon" : "icon_menu_edit_cut16.png",
          "accel" : ["Ctrl+X", "<Control-KeyPress-x>", "<Control-KeyPress-X>"] },
        { "type" : "command", "title" : "Copy", "ul" : "0", "command" : "menu_edit_copy", "icon" : "icon_menu_edit_copy16.png",
          "accel" : ["Ctrl+C", "<Control-KeyPress-c>", "<Control-KeyPress-C>"] },
        { "type" : "command", "title" : "Paste", "ul" : "0", "command" : "menu_edit_paste", "icon" : "icon_menu_edit_paste16.png",
          "accel" : ["Ctrl+V", "<Control-KeyPress-v>", "<Control-KeyPress-V>"] },
        { "type" : "separator" },
        { "type" : "command", "title" : "Delete", "ul" : "0", "command" : "menu_edit_delete", "icon" : "icon_menu_edit_delete16.png",
          "accel" : ["DEL", "<Delete>"] },
        { "type" : "command", "title" : "Select All", "ul" : "7", "command" : "menu_edit_select_all", "icon" : "icon_menu_edit_select_all16.png",
          "accel" : ["Ctrl+A", "<Control-KeyPress-a>", "<Control-KeyPress-A>"] }
      ]
    },
    {
      "title": "Help",
      "ul" : 0,
      "options" :
      [
        { "type" : "command", "title" : "Help...", "ul" : "0" , "accel" : ["F1", "<F1>"] , "command" : "menu_help" },
        { "type" : "separator" },
        { "type" : "command", "title" : "About...", "ul" : "2" , "command" : "menu_about" }
      ]
    }
  ],
  "on_closing_message_box":
  {
    "title": "Modified document !",
    "message": "Do you want to save changes to {}?"
  },
  "file_dialog":
  {
    "open_title": "Open a file ",
    "saveas_title": "Save a file as ",
    "filestypes": [["Application files (*.obj)","*.obj"],["All files (*.*)","*.*"]],
    "extension": ".obj",
    "error_message": "Accessing {} caused an error."
  }
}

fr.json

Fichier utilisé par WinLib pour la langue anglaise :
{
  "application":
  {
    "title": "Titre de mon application",
    "geometry": "1200x800",
    "not_implemented_command" :
    {
      "title": "Information sur l'application",
      "message": "Cette commande n'est pas encore implémentée."
    },
  },
  "menu":
  [
    {
      "title" : "Fichier",
      "ul" : "0",
      "options" :
      [
        { "type" : "command", "title" : "Nouveau...", "ul" : "0", "command" : "menu_file_new", "icon" : "icon_menu_file_new16.png",
          "accel" : ["Ctrl+N", "<Control-KeyPress-n>", "<Control-KeyPress-N>"] },
        { "type" : "command", "title" : "Ouvrir...", "ul" : "0", "command" : "menu_file_open", "icon" : "icon_menu_file_open16.png",
          "accel" : ["Ctrl+O", "<Control-KeyPress-o>", "<Control-KeyPress-O>"] },
        { "type" : "command", "title" : "Enregistrer sous...", "ul" : "5", "command" : "menu_file_save_as", "icon" : "icon_menu_file_save_as16.png",
          "accel" : ["Ctrl+Alt+S", "<Control-Alt-KeyPress-s>", "<Control-Alt-KeyPress-S>"] },
        { "type" : "command", "title" : "Enregistrer", "ul" : "0", "command" : "menu_file_save", "icon" : "icon_menu_file_save16.png",
          "accel" : ["Ctrl+S", "<Control-KeyPress-s>", "<Control-KeyPress-S>"] },
        { "type" : "separator" },
        { "type" : "command", "title" : "Imprimer...", "ul" : "0", "command" : "menu_file_print", "icon" : "icon_menu_file_print16.png",
          "accel" : ["Ctrl+P", "<Control-KeyPress-p>", "<Control-KeyPress-P>"] },
        { "type" : "command", "title" : "Mise en page...", "ul" : "0" , "command" : "menu_file_print_preview", "icon" : "icon_menu_page_setup16.png" },
        { "type" : "separator" },
        { "type" : "command", "title" : "Quitter", "ul" : "0", "command" : "menu_application_exit", "icon" : "icon_menu_app_exit16.png",
          "accel" : ["Alt+F4", "<Alt-F4>"] }
      ]
    },
    {
      "title" : "Edition",
      "ul" : "0",
      "options" :
      [
        { "type" : "command", "title" : "Annuler", "ul" : "0", "command" : "menu_edit_undo", "icon" : "icon_menu_edit_undo16.png",
          "accel" : ["Ctrl+Z", "<Control-KeyPress-z>", "<Control-KeyPress-Z>"] },
        { "type" : "command", "title" : "Rétablir", "ul" : "0", "command" : "menu_edit_redo", "icon" : "icon_menu_edit_redo16.png",
          "accel" : ["Ctrl+Y", "<Control-KeyPress-y>", "<Control-KeyPress-Y>"] },
        { "type" : "separator" },
        { "type" : "command", "title" : "Couper", "ul" : "3", "command" : "menu_edit_cut", "icon" : "icon_menu_edit_cut16.png",
          "accel" : ["Ctrl+X", "<Control-KeyPress-x>", "<Control-KeyPress-X>"] },
        { "type" : "command", "title" : "Copier", "ul" : "0", "command" : "menu_edit_copy", "icon" : "icon_menu_edit_copy16.png",
          "accel" : ["Ctrl+C", "<Control-KeyPress-c>", "<Control-KeyPress-C>"] },
        { "type" : "command", "title" : "Coller", "ul" : "0", "command" : "menu_edit_paste", "icon" : "icon_menu_edit_paste16.png",
          "accel" : ["Ctrl+V", "<Control-KeyPress-y>", "<Control-KeyPress-Vgt;"] },
        { "type" : "separator" },
        { "type" : "command", "title" : "Supprimer", "ul" : "0", "command" : "menu_edit_delete", "icon" : "icon_menu_edit_delete16.png",
          "accel" : ["SUP", "<Delete>"] },
        { "type" : "command", "title" : "Selectionner tout", "ul" : "13", "command" : "menu_edit_select_all", "icon" : "icon_menu_edit_select_all16.png",
          "accel" : ["Ctrl+A", "<Control-KeyPress-a>", "<Control-KeyPress-Agt;"] }
      ]
    },
    {
      "title": "Aide",
      "ul" : 0,
      "options" :
      [
        { "type" : "command", "title" : "Aide...", "ul" : "0" , "accel" : ["F1", "<F1>"] , "command" : "menu_help" },
        { "type" : "separator" },
        { "type" : "command", "title" : "A propos de...", "ul" : "2" , "command" : "menu_about" }
      ]
    }
  ],
  "on_closing_message_box":
  {
    "title": "Document modifié",
    "message": "Voulez-vous enregistrer les modifications apportées à {} ?"
  },
  "file_dialog":
  {
    "open_title": "Ouvrir un fichier ",
    "saveas_title": "Enregistrer un fichier ",
    "filestypes": [["Fichier d'application (*.obj)","*.obj"],["Tous les fichiers (*.*)","*.*"]],
    "extension": ".obj",
    "error_message": "L'accès à {} a provoqué une erreur."
  }
}

Les rubriques des fichiers de localisation

Au debut WinLib propose quatre rubriques dans le fichier JSON :
  • "application" contient les données propres à l'application.
  • "menu" contient les données relatives aux menus de commandes de l'application.
  • "on_closing_message_box" contient les données relatives à la boite d'alerte affichée lorsque que l'utilisateur tente de fermer l'application lorsque le document n'a pas été enregistré.
  • "file_dialog" contient les données relatives aux boites de dialogue afficher pour choisir un fichier dans les commandes d'ouverture ou d'enregistrement des documents.
Des rubriques supplémentaires pourront ou devront être ajoutées au fur et à mesure des besoins lors du développement de l'application, et ceci sur plusieurs niveaux hiérarchiques. Il conviendra de maintenir tous les fichiers de localisation en parallèle pour ne pas causer de dysfonctionnements en fonction de la loccalisation choisie.

Rubrique "application"

  • "title" : Titre de l'application affiché dans la barre de titre de la fenêtre principale de l'application.
  • "geometry" : Chaine de caractère précisant la taille en pixels écran de la fenêtre principale de l'application. Le format de la chaine doit être "[largeur]x[hauteur]" conformémentà la documentation de TkInter.
  • "not_implemented_command" : Données affichées dans la boite d'alerte lorsqu'une commande du menu non encore implémentée est sollicitée par l'utilisateur.
    • "title" : Titre de la boite d'alerte.
    • "message" : Texte affiché dans la boite d'alerte.

Rubrique "menu"

La structure de la rubrique "menu" est riche et complexe. Elle est utilisée par la méthode parseMenu() de la classe Application. Elle est étudiée et son usage décrit dans l'article Gestion des menus.

Rubrique "on_closing_message_box"

  • "title" : Titre de la boite d'alerte.
  • "message" : Texte affiché dans la boite d'alerte. Ce texte doit prévoir un champ {} pour y insérer le nom du document à enregistrer.

Rubrique "file_dialog"

  • "open_title" : Titre de la boite de dialogue pour choisir le fichier à ouvrir.
  • "saveas_title" : Titre de la boite de dialogue pour choisir le nom du fichier à enregistrer.
  • "filestypes" : Tableau des extensions des fichiers gérés par l'application.
  • "extension" : Extension par défaut des fichiers gérés par l'application.
  • "error_message" : Texte du message d'erreur à afficher lorsqu'une exception est levée pendant l'accès aux fichiers. Le texte doit prévoir un champ {} pour y insérer le nom du fichier qui a causé l'erreur.
Par défaut, l'extension ajoutée au nom des fichier est .obj. Il conviendra de remplacer cet acronyme par un plus significatif en relation avec le type des fichiers gérés par l'application. Par exemple, .txt pour traitement de textes ou .img pouur un traitement d'image.

Usage des données de localisation dans le code Python

Les données du fichier sont gérées en Python comme un dictionnaire. Plus précisément, les données entre accolades {...} seront vue dans Python comme des dictionnaires dont les élements sont indicés par leur clef, et les données entre crochets [...] seront vues comme des tableaux et sont indicées par leur rang numérique. Le format JSON a été choisi justement pour cette distinction en conformité avec la syntaxe du langage Python.
Lors de la construction le fichier de localisation passé en paramètre est chargé dans la propriété self.local de la classe Application. Cette propriété peut être utilisée selon la syntaxe Python pour accéder à une donnée précise. Par exemple, self.local['application']['title'] fournit la chaine de caractères contenant le titre de l'application tel qu'il sera affiché dans la barre de titre de la fenêtre principale.
Plus compliqué, mais tout aussi rigoureux, self.local['menu'][1]['title'] fournit le texte du titre du menu Edition. En effet, la rubrique "menu" est structurée comme un tableau indexé à partir de 0 et le menu Edition est le deuxième menu du tableau, donc indexé à 1.

Conclusion

Le code de l'application ne doit contenir aucun litéral pouvant être localisé. Toutes les données localisées doivent être rassembées dans un unique fichier de localisation au format JSON, encodé UTF-8 placé dans le répertoire racine du projet Python. Ce fichier peut être dupliqué et traduit dans autant de langues que nécessaire. Il appartient à l'équipe de développement de maintenir tous ces fichier en parallèle pour ne pas créer de dysfonctionnement selon la localisation choisie.
Au cours du developpement de l'application, ces fichiers devront être enrichis de nouvelles rubriques. Des exemples pourront être trouvés, notamment lors d'ajout de boites de dialogue ou de commande de menus suplémentaires. Des articles dédiés à ce sujets seront publiés utltérieurement.

Commentaires

Posts les plus consultés de ce blog

Développer une application fenêtrée en langage Python.

Créer ma première application