Translating an external workbench/de

In den folgenden Beschreibungen steht "context" für den Namen deines Addons oder deines Arbeitsbereichs, z.B. "MeinSuperAddon" oder "DraftPlus" oder was auch immer. Groß- und Kleinschreibung wird hier unterschieden: So ist z.B. "Context" ist nicht dasselbe wie "context". Dieser "context" sorgt dafür, dass alle Übersetzungen des Codes unter demselben Namen zusammengefasst werden, um von Übersetzern leichter erkannt zu werden. Das heißt, sie wissen genau, zu welchem Addon oder welchem Arbeitsbereich eine bestimmte Zeichenfolge gehört.

Hinweis: Hier gibt es ein Multifunktionsskript, das den kompletten unten beschriebenen Ablauf automatisiert (Es wird trotzdem empfohlen, die Beschreibung zu lesen, um zu wissen, was das Skript erledigen soll): https://github.com/yorikvanhavre/BIM_Workbench/blob/master/utils/updateTranslations.py

Die Quellen vorbereiten

Allgemeines

In jeder Python-Datei

import FreeCAD
translate = FreeCAD.Qt.translate
print("My text")
wird zu:
print(translate("context", "My text"))
Man darf nicht vergessen, dass translate() nicht nur eine normale Funktion ist, sondern auch als "Tag" für das Textverabeitungs-Dienstprogramm lupdate dient und deshalb exakt "translate" heißen muss. Das Programm lupdate ist eine einfache Textverarbeitung; es führt keinen Code aus. Zeichenketteninhalte müssen direkt an die Funktion translate() übergeben werden: Es können keine Variablen, Konstanten usw. übergeben werden. Ein Beispiel:
# 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")
Dies kann überall verwendet werden: In print(), in FreeCAD.Console.PrintMessage(), in Qt-Dialogen, etc. Die FreeCAD.Console-Funktionen fügen das Zeichen für den Zeilenumbruch (\n) nicht automatisch hinzu, es muss bei Bedarf am Ende hinzugefügt werden. Dieses Zeichen benötigt auch keine Übersetzung und kann daher außerhalb der Übersetzungsfunktion liegen:
FreeCAD.Console.PrintMessage(translate("context", "My text") + "\n")
obj.addProperty("App::PropertyBool", "MyProperty", "PropertyGroup", QT_TRANSLATE_NOOP("App::Property", "This is what My Property does"))
Man verwendet in diesem speziellen Fall keinen eigenen "context", sondern behält "App::Property" bei.

In Der Datei InitGui.py

from PySide.QtCore import QT_TRANSLATE_NOOP
Die Funktion QT_TRANSLATE_NOOP macht nichts, außer dass sie Texte markiert, die später vom Dienstprogramm lupdate aufgesammelt werden. Sie wird nur in Sonderfällen verwendet, in denen sich FreeCAD selbst um alles kümmert.
self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "My menu"), [list of commands, ...])
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "My toolbar"), [list of commands, ...])
FreeCADGui.addLanguagePath("/path/to/translations")
FreeCADGui.updateLocale()

Die Datei InitGui.py hat kein Attribut file, so dass es nicht einfach ist, die relative Position des Übersetzungsordners zu finden. Eine einfache Möglichkeit, dies zu umgehen, besteht darin, eine andere Datei aus demselben Ordner zu importieren und in dieser Datei Folgendes zu tun:

FreeCADGui.addLanguagePath(os.path.join(os.path.dirname(__file__), "translations"))
FreeCADGui.updateLocale()

Innerhalb jeder FreeCAD-Befehlsklasse

def QT_TRANSLATE_NOOP(context, text):
    return text
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"
    }
wobei "CommandName" der Name des Befehls ist, definiert durch:
FreeCADGui.addCommand('CommandName', My_Command_Class())

Alle Zeichenketten des eigenen Moduls sammeln

lupdate *.ui -ts translations/uifiles.ts
Dies ist rekursiv und findet .ui-Dateien in der gesamten Verzeichnisstruktur.
pylupdate *.py -ts translations/pyfiles.ts
lconvert -i translations/uifiles.ts translations/pyfiles.ts -o translations/MyModule.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

Die .ts-Datei an eine Übersetzungsplattform senden

Es ist an der Zeit, die .ts-Datei übersetzen zu lassen. Dafür kann man ein Konto auf einer öffentlichen Übersetzungsplattform wie Crowdin oder Transifex einrichten, oder unser bestehendes FreeCAD-Addons-Konto bei Crowdin nutzen, das bereits viele Benutzer hat und somit eine größere Chance, die Datei schnell übersetzen zu lassen, von Leuten, die FreeCAD kennen.

Soll die Datei auf dem FreeCAD-Crowdin-Konto untergebracht werden, hilft Yorik im FreeCAD-Forum gerne weiter.

Hinweis: Einige Plattformen wie Crowdin können sich in GitHub integrieren und den gesamten Prozess von Punkt 2, 3 und 4 automatisch durchführen. Dafür kann das FreeCAD-Crowdin-Konto nicht verwendet werden; Man muss sein eigenes Konto einrichten.

Die Übersetzungen zusammenführen

Sobald deine .ts Datei übersetzt wurde, wenn auch nur teilweise, kannst du die Übersetzungen von der Internetseite herunterladen:

Die Übersetzungen kompilieren

Nun wird das Programm lrelease auf jede vorhandene Datei angewendet:

lrelease "translations/Draft_de.ts"
lrelease "translations/Draft_fr.ts"
lrelease "translations/Draft_pt-BR.ts"

Der Prozess lässt sich automatisieren:

for f in translations/*_*.ts
do
    lrelease "translations/$f"
done

Es sollte jetzt eine .qm-Datei für jede übersetzte .ts-Datei vorhanden sein. Die .qm-Dateien sind die, die von Qt und FreeCAD zur Laufzeit verwendet werden.

Das ist alles, was gebraucht wird. Man beachte, dass bestimmte Teile des eigenen Arbeitsbereichs nicht sofort übersetzt werden können, wenn man sich für einen Wechsel der Sprache entscheidet. In diesem Fall muss FreeCAD neu gestartet werden, damit die neue Sprache verwendet wird.

Übersetzungen testen

  1. In FreeCAD zu einer Sprache wechseln, die man übersetzt hat (z.B. Deutsch)
  2. Die Übersetzung in FreeCAD laden, z.B. FreeCADGui.addTranslationPath("/Pfad/zum/Verzeichnis/mit/qmfile")
  3. Eine Zeichenkette testen, z.B. FreeCAD.Qt.translate("dein Kontext","Eine Zeichenkette")

Ergebnis: Dies sollte die deutsche Übersetzung liefern. Wenn das funktioniert, ist die Basiseinstellung OK. Dann können wir uns etwas anderes ansehen. Beispielsweise sollten Befehlsnamen einen speziellen Kontext benutzen, das ist der in FreeCAD eingetragene Befehlsname.

Wichtige Hinweise

Ein nützliches Skript

Yorik unterhält ein nützliches Skript für den Arbeitsbereich BIM, das .ts-Dateien sammeln, hochladen und herunterladen kann. Dieses Skript kann einfach kopiert und für den eigenen Arbeitsbereich angepasst werden:

https://github.com/yorikvanhavre/BIM_Workbench/blob/master/utils/updateTranslations.py

Technische Einzelheiten und erweiterte Anwendung

In den obigen Beispielen wurden zwei separate Funktionen verwendet: translate() und QT_TRANSLATE_NOOP. Vielleicht stößt man auch auf tr() und QT_TR_NOOP, die automatisch das Argument "context" basierend auf dem Ort, von wo sie aufgerufen werden, bereitstellen. Diese beiden Paare von Funktionen unterscheiden sich grundlegend.

translate() und tr() erfüllen zwei separate Aufgaben: Zur Laufzeit führen sie die eigentliche Übersetzung der ihnen übergebenen Zeichenkette in die endgültige übersetzte Zeichenkette durch. Dies gilt unabhängig davon, ob ihnen eine Literalzeichenfolge, eine Variable oder eine Konstante übergeben wird: Die Suche erfolgt dynamisch und in Echtzeit während der Ausführung des Codes. Sie bieten jedoch eine zusätzliche Nicht-Laufzeitfunktion: Sie werden vom Dienstprogramm pylupdate erkannt. Wenn (und nur wenn) sie eine Literalzeichenfolge enthalten, wird diese Literalzeichenfolge vom Dienstprogramm extrahiert. NUR Literalzeichenfolgen werden von pylupdate extrahiert – wenn eine Variable übergeben wird, wird sie vom Dienstprogramm pylupdate ignoriert. Qt versucht, zur Laufzeit eine Übersetzung bereitzustellen, dies funktioniert jedoch nur, wenn ein anderer Codeabschnitt eine der Übersetzungsfunktionen mit der zu übersetzenden Zeichenfolge aufruft, damit pylupdate diese extrahieren kann. Beachte, dass der Code mit dem Zeichenketten-Literal nicht tatsächlich ausgeführt werden muss, sondern lediglich als Codezeile in einer Datei irgendwo vorhanden sein muss: pylupdate führt keine Analyse oder Codeausführung durch, sondern lediglich eine Zeichenketten-Suche und -Extraktion.

Im Gegensatz dazu führen QT_TRANSLATE_NOOP und QT_TR_NOOP zur Laufzeit überhaupt keine Aktion aus: Sie sind wörtlich „No-Ops“ und werden vom ausgeführten Code vollständig ignoriert. Ihr einziger Zweck besteht darin, eine Literalzeichenfolge für die Extraktion durch pylupdate zu markieren: Es macht niemals Sinn, eine Variable in einen Aufruf einer dieser Funktionen einzufügen, da dies keine Wirkung hat. Sie werden in Fällen verwendet, in denen translate() oder tr() mit einer Variablen aufgerufen wird, die den zu übersetzenden Text enthält. Beispielsweise verwendet jeder Code, der zum Erstellen eines Befehls oder einer Eigenschaft verwendet wird, eine NOOP-Funktion um den Befehlsmenütext oder den Tooltip oder den Eigenschafts-Docstring herum: Zur Laufzeit, wenn FreeCAD diese Elemente dem Benutzer anzeigt, ruft es translate() auf: Die Literalzeichenfolgen müssen zum Zeitpunkt der Erstellung durch pylupdate extrahiert worden sein, zum Beispiel:

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"
    }

In dieser Verwendung ist das von dieser Funktion zurückgegebene Wörterbuch zur Laufzeit wörtlich:

{ 
    'Pixmap'  : "path/to/icon.svg",
    'MenuText': "My Command",
    'ToolTip' : "Describes what the command does",
    'Accel'   : "Shift+A"
}

Es gibt keinen Verweis auf irgendeine Art von Übersetzungsinformation. Wenn FreeCAD diese Informationen tatsächlich dem Benutzer anzeigt, lautet der Pseudocode:

for command in commands:
    resources = command.GetResources()
    menu_text = translate(resources['MenuText'])

In diesem Fall kann lupdate keine Zeichenfolge aus dem Aufruf von translate() extrahieren, da dieser sich auf eine Variable bezieht. Daher ignoriert lupdate diesen Aufruf, aber zur Laufzeit sucht Qt nach der Zeichenfolge, die an ihn übergeben wurde. Solange irgendwo im Code ein Aufruf einer der Übersetzungsfunktionen mit einer übereinstimmenden Literalzeichenfolge vorhanden ist (in diesem Fall in der Funktion GetResources()), ist dieser Übersetzungsaufruf erfolgreich.

Um zu überprüfen, ob die erwarteten Zeichenfolgen extrahiert werden, kann der Befehl pylupdate manuell ausführt werden:

pylupdate myfile.py -ts outfile.ts

Die Datei outfile.ts enthält die Zeichenfolgen, die zur Übersetzung an CrowdIn hochgeladen werden.

Wichtige Verweise

Verwandte Seiten