Dans les notes suivantes, "context"
doit être le nom de votre extension ou de votre atelier, par exemple, "MySuperAddon"
ou "DraftPlus"
, ou autre. Les majuscules ont leur importance ici : "Context"
n'est pas la même chose que "context"
par exemple. Le contexte fait en sorte que toutes les traductions de votre code seront rassemblées sous le même nom, pour être plus facilement identifiées par les traducteurs. En d'autres termes, ils sauront exactement à quelle extension ou atelier appartient une chaîne de caractères particulière.
Remarque : Voici un script tout-en-un qui automatise la procédure complète mentionnée ci-dessous (il est tout de même conseillé de lire la procédure pour savoir ce que le script doit faire) : https://github.com/yorikvanhavre/BIM_Workbench/blob/master/utils/updateTranslations.py.
translations/
. Vous pouvez le nommer autrement, mais ce sera plus facile car c'est la même chose dans FreeCAD. Dans ce dossier, vous placerez les fichiers .ts
(les fichiers de traduction "source") et les fichiers .qm
(fichiers de traduction compilés).FreeCAD.Console
est affiché dans la "vue rapport" et doit donc être traduit. La "vue rapport" est différente depuis la console Python.
translate()
. Elle doit être nommée exactement translate
: l'extracteur de chaînes de caractères s'appuie sur ce nom exact. Vous pouvez utiliser le nom entièrement qualifié de Qt, mais c'est un peu plus propre à utiliser :import FreeCAD
translate = FreeCAD.Qt.translate
translate()
:print("My text")
print(translate("context", "My text"))
translate()
n'est pas seulement une fonction normale : elle sert également de "balise" (tag) pour l'utilitaire de traitement de texte lupdate
, et doit donc être nommée exactement "translate". Le programme lupdate
est un simple processeur de texte, il n'exécute pas votre code. Vous devez passer des chaînes de caractères directement à la fonction translate()
: vous ne pouvez pas passer de variables, de constantes, etc. Par exemple :# This works:
FreeCAD.Console.PrintMessage(translate("context", "My text") + "\n")
# This does not, lupdate only sees the word "a_variable", and doesn't know what that is:
a_variable = "My text"
FreeCAD.Console.PrintMessage(translate("context", a_variable ) + "\n")
# But this works -- a_variable will contain the translated string:
a_variable = translate("context", "My text")
FreeCAD.Console.PrintMessage(a_variable + "\n")
print()
, dans FreeCAD.Console.PrintMessage()
, dans les dialogues Qt, etc. Les fonctions FreeCAD.Console
n'ajoutent pas automatiquement le caractère de nouvelle ligne (\n
), qui doit donc être ajouté à la fin si vous le souhaitez. Ce caractère n'a pas besoin d'être traduit non plus, il peut donc se trouver en dehors de la fonction de traduction :FreeCAD.Console.PrintMessage(translate("context", "My text") + "\n")
.ui
réalisés avec QtDesigner, vous n'avez rien à faire de spécial avec eux.QT_TRANSLATE_NOOP
:obj.addProperty("App::PropertyBool", "MyProperty", "PropertyGroup", QT_TRANSLATE_NOOP("App::Property", "This is what My Property does"))
"context"
dans ce cas précis. Gardez "App::Property"
.
def QT_TRANSLATE_NOOP(context, text):
return text
QT_TRANSLATE_NOOP
ne fait rien, mais elle marque les textes qui seront repris plus tard par l'utilitaire lupdate
. Nous ne l'utilisons que dans des cas particuliers où FreeCAD s'occupe lui-même de tout."Workbench"
comme contexte :self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "My menu"), [list of commands, ...])
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "My toolbar"), [list of commands, ...])
translations/
et mettez à jour la locale dans la fonction Initialized :FreeCADGui.addLanguagePath("/path/to/translations")
FreeCADGui.updateLocale()
InitGui.py
n'a pas d'attribut file, il n'est donc pas facile de trouver l'emplacement relatif du dossier des traductions. Une façon simple de contourner ce problème est de faire en sorte qu'il importe un autre fichier du même dossier, et de faire dans ce fichier :FreeCADGui.addLanguagePath(os.path.join(os.path.dirname(__file__), "translations"))
FreeCADGui.updateLocale()
def QT_TRANSLATE_NOOP(context, text):
return text
'MenuText'
et 'Tooltip'
de la commande comme ceci :def GetResources(self):
return {'Pixmap' : "path/to/icon.svg"),
'MenuText': QT_TRANSLATE_NOOP("CommandName", "My Command"),
'ToolTip' : QT_TRANSLATE_NOOP("CommandName", "Describes what the command does"),
'Accel' : "Shift+A"
}
"CommandName"
est le nom de la commande, défini par :FreeCADGui.addCommand('CommandName', My_Command_Class())
lupdate
, lconvert
, lrelease
et pylupdate
installés sur votre système. Dans les distributions Linux, ils sont généralement fournis dans des paquets nommés pyside-tools
ou pyside2-tools
. Sur certains systèmes, lupdate
est nommé lupdate4
ou lupdate5
ou lupdate-qt4
ou similaire. Idem pour les autres outils. Vous pouvez utiliser la version Qt4 ou Qt5 à votre choix. Dans Qt6 il n'y a pas de système de traduction séparé pour les fichiers Python, lupdate
est utilisé pour extraire les chaînes de caractères de tous les types de fichiers sources..ui
, vous devez d'abord exécuter lupdate
:lupdate *.ui -ts translations/uifiles.ts
.ui
dans toute la structure de votre répertoire..py
, vous devez aussi exécuter pylupdate
:pylupdate *.py -ts translations/pyfiles.ts
lconvert -i translations/uifiles.ts translations/pyfiles.ts -o translations/MyModule.ts
.ts
pour vous assurer qu'ils contiennent les chaînes de caractères, puis vous pouvez supprimer pyfiles.ts
et uifiles.ts
.#!/bin/sh
lupdate *.ui -ts translations/uifiles.ts
pylupdate *.py -ts translations/pyfiles.ts
lconvert -i translations/uifiles.ts translations/pyfiles.ts -o translations/MyModule.ts
rm translations/pyfiles.ts
rm translations/uifiles.ts
Il est temps de faire traduire votre fichier .ts
. Vous pouvez choisir de créer un compte sur une plateforme de traduction publique telle que Crowdin ou Transifex, ou vous pouvez bénéficier de notre compte FreeCAD existant sur Crowdin, qui compte déjà de nombreux utilisateurs et qui a donc plus de chance de faire traduire votre fichier rapidement par des personnes connaissant FreeCAD.
Si vous souhaitez héberger votre fichier sur le compte FreeCAD Crowdin, contactez Yorik sur le forum FreeCAD.
Note: certaines plates-formes comme Crowdin peuvent s'intégrer à GitHub et effectuer automatiquement tout le processus à partir des points 2, 3 et 4. Pour cela, vous ne pouvez pas utiliser le compte FreeCAD Crowdin ; vous devrez créer votre propre compte.
Une fois que votre fichier .ts
a été traduit, même partiellement, vous pouvez télécharger les traductions à partir du site :
.zip
contenant un .ts
par langue.ts
traduits, ainsi que votre fichier .ts
de base, dans le dossier translations/
Maintenant, lancez le programme lrelease
sur chaque fichier que vous avez:
lrelease "translations/Draft_de.ts"
lrelease "translations/Draft_fr.ts"
lrelease "translations/Draft_pt-BR.ts"
Vous pouvez automatiser le processus:
for f in translations/*_*.ts
do
lrelease "translations/$f"
done
Vous devriez trouver un fichier .qm
pour chaque fichier .ts
traduit. Les fichiers .qm
sont ceux qui seront utilisés par Qt et FreeCAD au moment de l'exécution.
C'est tout ce dont vous avez besoin. Notez que certaines parties de votre atelier ne peuvent pas être traduites à la volée si vous décidez de changer de langue. Si tel est le cas, vous devrez redémarrer FreeCAD pour que la nouvelle langue prenne effet.
FreeCADGui.addTranslationPath("/path/to/the/folder/containing/qmfile")
FreeCAD.Qt.translate("your context", "some string")
Résultat : Cela devrait vous donner la traduction allemande. Si cela fonctionne bien, alors la configuration de base est correcte. Alors nous pouvons regarder autre chose. Par exemple, les noms des commandes devraient toujours utiliser un contexte spécial qui est le nom de la commande telle qu'elle est enregistrée dans FreeCAD.
Yorik maintient un script pratique pour l'atelier BIM, qui peut rassembler, charger et télécharger des fichiers ts. Vous pouvez simplement copier et adapter ce script à votre atelier :
https://github.com/yorikvanhavre/BIM_Workbench/blob/master/utils/updateTranslations.py
Dans les exemples ci-dessus, deux fonctions distinctes sont utilisées : translate()
et QT_TRANSLATE_NOOP
. Vous pouvez également rencontrer et
QT_TR_NOOP
, qui fournissent automatiquement l'argument "context" en fonction de l'emplacement de l'appel. Ces deux paires de fonctions sont fondamentalement différentes.
translate()
et tr()
accomplissent deux tâches distinctes : au moment de l'exécution, elles effectuent la traduction réelle de la chaîne qui leur est passée en chaîne traduite finale. Ceci est vrai qu'on leur fournisse une chaîne littérale, une variable ou une constante : la recherche est dynamique et en temps réel pendant l'exécution du code. Toutefois, elles fournissent une fonction supplémentaire hors temps d'exécution : elles sont reconnues par l'utilitaire pylupdate
. Si (et seulement si) elles contiennent une chaîne de caractères littérale, cette chaîne est extraite par l'utilitaire. SEULS les chaînes de caractères sont extraites par pylupdate
. Si une variable est passée, elle est ignorée par l'utilitaire pylupdate
. Qt essaiera de fournir une traduction au moment de l'exécution, mais cela ne fonctionnera que si un autre morceau de code a appelé une des fonctions de traduction avec la chaîne littérale qui doit être traduite, afin que pylupdate
puisse l'extraire. Notez que le code contenant la chaîne littérale ne doit jamais être exécuté, il doit simplement exister en tant que ligne de code dans un fichier quelque part : pylupdate
n'effectue aucune analyse ou exécution de code, il effectue simplement une recherche et une extraction de chaîne.
En revanche, QT_TRANSLATE_NOOP
et QT_TR_NOOP
ne font rien du tout à l'exécution : ce sont des "no-ops" littérales et sont complètement ignorées par le code en cours d'exécution. Leur seule utilité est de marquer une chaîne littérale pour qu'elle soit extraite par pylupdate
: il est inutile de placer une variable dans un appel à l'une de ces fonctions, cela n'aura aucun effet. Elles sont utilisées dans les cas où translate()
ou tr()
seront appelées avec une variable contenant le texte à traduire. Par exemple, tout code utilisé pour créer une commande ou une propriété utilisera une fonction de type NOOP autour du texte ou de l'info-bulle du menu de la commande, ou de la docstring de la propriété : à l'exécution, lorsque FreeCAD affiche ces éléments à l'utilisateur, il appelle translate()
: les chaînes littérales doivent avoir été extraites par pylupdate
au moment de la création, par exemple :
def GetResources(self):
return {'Pixmap' : "path/to/icon.svg",
'MenuText': QT_TRANSLATE_NOOP("CommandName", "My Command"),
'ToolTip' : QT_TRANSLATE_NOOP("CommandName", "Describes what the command does"),
'Accel' : "Shift+A"
}
Dans cette utilisation, au moment de l'exécution, le dictionnaire renvoyé par cette fonction est littéralement :
{
'Pixmap' : "path/to/icon.svg",
'MenuText': "My Command",
'ToolTip' : "Describes what the command does",
'Accel' : "Shift+A"
}
Il n'y a aucune référence à un quelconque type d'information de traduction. Lorsque FreeCAD affiche réellement ces informations à l'utilisateur, le pseudo-code est le suivant :
for command in commands:
resources = command.GetResources()
menu_text = translate(resources['MenuText'])
Dans ce cas, lupdate
ne peut pas extraire de chaîne de caractères de l'appel à translate()
car il fait référence à une variable. Ainsi, lupdate
ignore cet appel, mais au moment de l'exécution, Qt recherche la chaîne qui lui est passée. Tant que quelque part dans le code, il y a un appel à l'une des fonctions de traduction avec une chaîne littérale correspondante (dans ce cas, dans la fonction GetResources()
), cet appel de traduction réussira.
Pour vérifier que les chaînes de caractères attendues sont extraites, vous pouvez exécuter manuellement la commande pylupdate
:
pylupdate myfile.py -ts outfile.ts
Le fichier outfile.ts
contiendra l'ensemble des chaînes de caractères qui sont téléchargées vers CrowdIn pour être traduites.
openCommand()
(fil de discussion)