Tłumaczenie interfejsu zewnętrznych środowisk pracy
W poniższych uwagach, " kontekst"
powinien być nazwą twojego dodatku lub środowiska pracy, na przykład, "MySuperAddon"
lub "DraftPlus"
, lub cokolwiek innego. Ten kontekst sprawia, że wszystkie tłumaczenia twojego kodu będą zebrane pod tą samą nazwą, aby tłumacze mogli je łatwiej zidentyfikować. Oznacza to, że będą oni dokładnie wiedzieli, do którego dodatku lub środowiska pracy należy dany ciąg znaków.
Uwaga: Oto skrypt all-in-one, który automatyzuje całą procedurę opisaną poniżej (zalecamy jednak przeczytanie procedury, aby wiedzieć, co skrypt powinien zrobić): https://github.com/yorikvanhavre/BIM_Workbench/blob/master/utils/updateTranslations.py
Przygotowywanie źródeł
Informacje ogólne
- Dodaj folder
translations/
. Możesz nadać mu inną nazwę, ale tak będzie łatwiej, ponieważ jest on taki sam w całym FreeCAD. W tym folderze umieścisz pliki.ts
(pliki tłumaczenia "źródła") i.qm
(skompilowane pliki tłumaczenia). - Należy tłumaczyć tylko tekst, który jest wyświetlany użytkownikowi w interfejsie użytkownika programu FreeCAD. Tekst, wyświetlany w konsoli Python nie powinien być tłumaczony.
- Tekst, który jest wyświetlany w
FreeCAD.Console
, jest wyświetlany w oknie "Widok raportu" i dlatego powinien zostać przetłumaczony. Okno "Widok raportu" jest czymś innym niż konsola Python.
W każdym pliku .py Python
- W każdym pliku, w którym trzeba przetłumaczyć tekst, należy zdefiniować funkcję
translate()
. Możesz użyć w pełni kwalifikowanej nazwy z Qt, ale to jest trochę bardziej przejrzyste w użyciu:
import FreeCAD translate = FreeCAD.Qt.translate
- Cały tekst, który musi zostać przetłumaczony, musi zostać przekazany przez funkcję
translate()
:
print("My text")
- staje się:
print(translate("context", "My text"))
- Pamiętaj, że
translate()
nie jest zwykłą funkcją: służy również jako "tag" dla narzędzia do przetwarzania tekstulupdate
, więc musi mieć nazwę dokładnie "translate". Programlupdate
jest prostym procesorem tekstu, nie wykonuje Twojego kodu. Do funkcjitranslate()
musisz przekazywać bezpośrednio literały łańcuchowe: nie możesz przekazywać zmiennych, stałych, itp. Na przykład:
# 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")
Może to być użyte wszędzie: w print()
, w FreeCAD.Console.PrintMessage()
, w oknach dialogowych Qt, itd. Funkcje FreeCAD.Console
nie dodają automatycznie znaku nowej linii (\n
), więc musi on być dodany na końcu, jeśli jest to pożądane. Ten znak również nie wymaga tłumaczenia, więc może znajdować się poza funkcją tłumaczącą:
FreeCAD.Console.PrintMessage(translate("context", "My text") + "\n")
- Jeśli używasz plików
.ui
wykonanych za pomocą QtDesigner, nie trzeba z nimi robić nic specjalnego. - Podczas tworzenia nowych obiektów, nie tłumacz "Nazwy" obiektu. Należy raczej tłumaczyć "Etykietę" obiektu. Różnica polega na tym, że "Nazwa" jest unikalna; pozostaje taka sama przez całe życie obiektu; z drugiej strony, "Etykieta" może być zmieniana przez użytkownika wedle życzenia.
- Kiedy tworzysz właściwości dla swoich obiektów, nie tłumacz nazwy właściwości. Ale umieść opis wewnątrz
QT_TRANSLATE_NOOP
:
obj.addProperty("App::PropertyBool", "MyProperty", "PropertyGroup", QT_TRANSLATE_NOOP("App::Property", "This is what My Property does"))
Nie używaj swojego własnego "kontekstu"
w tym konkretnym przypadku. Zachowaj "App::Property"
.
- Nie tłumacz tekstu operacji na dokumentach wykonanych za pomocą
Document.openTransaction()
FreeCAD.Console.PrintMessage(translate("context", "My text") + "\n")
- If you are using
.ui
files made with QtDesigner, nothing special needs to be done with them. - When creating new objects, do not translate the object's "Name". Rather, translate the object's "Label". The difference is that a "Name" is unique; it stays the same throughout the life of the object; on the other hand, a "Label" can be changed by the user as desired.
- When creating properties for your objects, don't translate the property name. But place the description inside
QT_TRANSLATE_NOOP
:
obj.addProperty("App::PropertyBool", "MyProperty", "PropertyGroup", QT_TRANSLATE_NOOP("App::Property", "This is what My Property does"))
- Don't use your own
"context"
in this specific case. Keep"App::Property"
.
- Do not translate the text of document transactions made with
Document.openTransaction()
Wewnątrz InitGui.py
- Dodaj następującą linię, blisko początku pliku:
def QT_TRANSLATE_NOOP(scope, text):
return text
- Aby przetłumaczyć nazwy menu:
self.appendMenu(QT_TRANSLATE_NOOP("context", "My menu"), [list of commands, ...])
- Makro
QT_TRANSLATE_NOOP
nie robi nic, ale zaznacza teksty, które później zostaną pobrane przez narzędzielupdate
. Ponieważ nie robi ono w zasadzie nic, używamy go tylko w szczególnych przypadkach, gdy FreeCAD sam się wszystkim zajmuje. - Dodaj ścieżkę do folderu
translations/
w funkcji Initialized:
FreeCADGui.addLanguagePath("/path/to/translations")
Plik InitGui.py
nie ma atrybutu plik, więc nie jest łatwo znaleźć względną lokalizację folderu tłumaczeń. Łatwym sposobem na obejście tego problemu jest zaimportowanie innego pliku z tego samego folderu, a następnie wykonanie w tym pliku polecenia
FreeCADGui.addLanguagePath(os.path.join(os.path.dirname(__file__), "translations"))
def QT_TRANSLATE_NOOP(scope, text): return text
- To translate menu names:
self.appendMenu(QT_TRANSLATE_NOOP("context", "My menu"), [list of commands, ...])
- The
QT_TRANSLATE_NOOP
macro doesn't do anything, but it marks texts to be picked up by thelupdate
utility later on. Since it doesn't actually do anything, we only use it in special cases where FreeCAD itself takes care of everything. - Add the path to your
translations/
folder in the Initialized function:
FreeCADGui.addLanguagePath("/path/to/translations")
- The
InitGui.py
file has no file attribute, so it is not easy to find the translations folder's relative location. An easy way to work around this is to make it import another file from the same folder, and in that file do:
FreeCADGui.addLanguagePath(os.path.join(os.path.dirname(__file__), "translations"))
Wewnątrz każdej klasy poleceń FreeCAD
- Dodaj następującą linię, na początku pliku:
def QT_TRANSLATE_NOOP(context, text):
return text
- Przetłumacz
'MenuText'
i'Tooltip'
polecenia w ten sposób:
def QT_TRANSLATE_NOOP(context, text): return text
- Translate the
'MenuText'
and'Tooltip'
of the command like this:
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" }
gdzie "CommandName"
jest nazwą polecenia, zdefiniowaną przez
FreeCADGui.addCommand('CommandName', My_Command_Class())
FreeCADGui.addCommand('CommandName', My_Command_Class())
Zbierz wszystkie ciągi z twojego modułu
- Będziesz potrzebował zainstalowanych w systemie narzędzi
lupdate
,lconvert
,lrelease
ipylupdate
. W dystrybucjach Linuksa są one zwykle dostępne w pakietach o nazwachpyside-tools
lubpyside2-tools
. W niektórych systemachlupdate
nazywa sięlupdate4
lublupdate5
lublupdate-qt4
lub podobnie. To samo dotyczy innych narzędzi. Możesz użyć wersji Qt4 lub Qt5 według własnego uznania. - Jeśli masz pliki
.ui
, musisz najpierw uruchomićlupdate
:
lupdate *.ui -ts translations/uifiles.ts
To działa rekurencyjnie i znajdzie pliki .ui
wewnątrz wszystkich twoich katalogów.
- Jeśli masz pliki
.py
, musisz również uruchomićpylupdate
:
pylupdate *.py -ts translations/pyfiles.ts
- Jeśli wykonałeś obie operacje, musisz teraz połączyć te dwa pliki w jeden:
lconvert -i translations/uifiles.ts translations/pyfiles.ts -o translations/MyModule.ts
- Sprawdź zawartość trzech plików
.ts
, aby upewnić się, że zawierają one łańcuchy znaków, a następnie możesz usunąć zarównopyfiles.ts
, jak iuifiles.ts
. - Możesz to wszystko zrobić w jednym skrypcie bash, tak jak poniżej:
lupdate *.ui -ts translations/uifiles.ts
- This is recursive and will find
.ui
files inside your whole directory structure.
- If you have
.py
files, you need to runpylupdate
too:
pylupdate *.py -ts translations/pyfiles.ts
- If you ran both operations, you now need to unify these two files into one:
lconvert -i translations/uifiles.ts translations/pyfiles.ts -o translations/MyModule.ts
- Check the contents of the three
.ts
files to make sure that they contain the strings, then you can delete bothpyfiles.ts
anduifiles.ts
. - You can do it all in one bash script like this:
#!/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
Przesłanie pliku .ts do platformy tłumaczeniowej
Nadszedł czas, aby zlecić tłumaczenie Twojego pliku .ts
. Możesz założyć konto na publicznej platformie tłumaczeniowej, takiej jak Crowdin lub Transifex, lub możesz skorzystać z naszego istniejącego konta FreeCAD-addons na Crowdin, które ma już wielu użytkowników, a zatem jest większa szansa, że Twój plik zostanie przetłumaczony szybko i przez ludzi, którzy znają FreeCAD.
Jeśli chcesz udostępnić swój plik na koncie FreeCAD Crowdin, skontaktuj się z użytkownikiem Yorik na forum FreeCAD.
Uwaga: niektóre platformy, takie jak Crowdin, mogą zintegrować się z GitHubem i wykonać wszystkie procesy z punktów 2, 3 i 4 automatycznie. W tym celu nie można korzystać z konta FreeCAD Crowdin. Będziesz musiał założyć własne konto.
Scalanie tłumaczeń
Kiedy Twój plik .ts
został przetłumaczony, choćby częściowo, możesz pobrać tłumaczenia ze strony:
- Zazwyczaj pobierasz plik
.zip
zawierający jeden plik.ts
na każdy język. - Umieść wszystkie przetłumaczone pliki
.ts
, razem ze swoim podstawowym plikiem.ts
, w folderzetranslations/
.
Kompilacja tłumaczeń
Teraz uruchom program lrelease
dla każdego pliku, który posiadasz:
lrelease "translations/Draft_de.ts"
lrelease "translations/Draft_fr.ts"
lrelease "translations/Draft_pt-BR.ts"
Możesz zautomatyzować ten proces:
for f in translations/*_*.ts
do
lrelease "translations/$f"
done
Powinieneś znaleźć jeden plik .qm
dla każdego przetłumaczonego pliku .ts
. Pliki .qm
będą używane przez Qt i FreeCAD w czasie pracy.
To wszystko, czego potrzebujesz. Zauważ, że niektóre części Twojego środowiska pracy nie mogą być tłumaczone w locie, jeśli zdecydujesz się na zmianę języka. Jeśli tak jest, będziesz musiał ponownie uruchomić FreeCAD, aby nowy język został zastosowany.
Testowanie tłumaczeń
- Przełącz FreeCAD na język, który przetłumaczyłeś (np. niemiecki)
- Załaduj tłumaczenie do FreeCAD, np.
FreeCADGui.addTranslationPath("/path/to/the/folder/containing/qmfile")
- Przetestuj coś, np.
FreeCAD.Qt.translate("twój kontekst", "jakiś ciąg znaków")
Wynik: To powinno dać ci niemieckie tłumaczenie. Jeśli to działa dobrze, to znaczy, że podstawowa konfiguracja jest w porządku. Wtedy możemy przyjrzeć się czemuś innemu. Na przykład, nazwy poleceń powinny zawsze używać specjalnego kontekstu, który jest nazwą polecenia zarejestrowanego we FreeCAD.
Istotne uwagi
- Upewnij się, że używasz *kontekstu* i *ciągu znaków*, które rzeczywiście znajdują się w pliku ts/qm.
Wygodniejszy skrypt
Yorik utrzymuje wygodny skrypt dla środowiska pracy BIM, który może zbierać, wysyłać i pobierać pliki ts. Możesz po prostu skopiować i dostosować ten skrypt do swojego środowiska pracy:
https://github.com/yorikvanhavre/BIM_Workbench/blob/master/utils/updateTranslations.py
Technical details and advanced usage
In the above examples there are two separate functions being used: translate()
and QT_TRANSLATE_NOOP
. You may also run across tr()
and QT_TR_NOOP
, which automatically provide the "context" argument based on their calling location. These two pairs of functions are fundamentally different.
translate()
and tr()
accomplish two separate tasks: at runtime they perform the actual translation from the string passed into them to the final translated string. This is true whether they are provided a literal string, or a variable, or a constant: the lookup is dynamic and real-time during the run of the code. However, they provide an additional non-runtime function: they are recognized by the pylupdate
utility. If (and only if) they contain a string literal, that literal is extracted by the utility. ONLY string literals are extracted by pylupdate
-- if a variable is passed it is ignored by the pylupdate
utility. Qt will attempt to provide a translation at runtime, but this will only work if some other piece of code called one of the translation functions with the literal string that needs to be translated, so that pylupdate
can extract it. Note that the code with the string literal need not actually ever execute, it must simply exist as a line of code in a file somewhere: pylupdate
performs no analysis or code execution, it is simply performing a string search and extraction.
In contrast, QT_TRANSLATE_NOOP
and QT_TR_NOOP
do nothing at all at runtime: they are literal "no-ops", and are completely ignored by running code. Their only use is to mark a literal string for extraction by pylupdate
: it never makes sense to place a variable within a call to one of these functions, it will have no effect. They are used in circumstances where translate()
or tr()
will be called with a variable containing the text to translate. For example, any code that is used to create a Command or a Property will use a NOOP-type function around the command menu text or tooltip, or the property docstring: at runtime when FreeCAD displays these items to the user it calls translate()
: the literal strings must have been extracted by pylupdate
at the point of creation, for example:
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 this usage, at runtime the dictionary returned by this function is literally:
{
'Pixmap' : "path/to/icon.svg",
'MenuText': "My Command",
'ToolTip' : "Describes what the command does",
'Accel' : "Shift+A"
}
There is no reference to any type of translation information. When FreeCAD actually displays this information to the user, the pseudo-code is:
for command in commands:
resources = command.GetResources()
menu_text = translate(resources['MenuText'])
In this case, lupdate
cannot extract any string from the call to translate()
because it refers to a variable. So lupdate
ignores that call, but at runtime Qt searches for the string that's passed to it. As long as someplace in the code there is a call to one of the translation functions with a matching literal string (in this case, in the GetResources()
function), this translation call will succeed.
To verify that the expected strings are being extracted, you can manually run the pylupdate
command:
pylupdate myfile.py -ts outfile.ts
The file outfile.ts
will contain the set of strings that are uploaded to CrowdIn for translation.
Ważne odnośniki
- Dlaczego warto i jak tłumaczyć funkcje
openCommand()
(forum thread).
Related Pages
Strony pokrewne
- Zewnętrzne środowiska pracy
- Lokalizacja - tłumaczenie interfejsu i dokumentacji
- Aby uzyskać więcej informacji, złóż zapytanie tutaj Tłumaczenie zewnętrznych środowisk pracy.
- Jak zacząć
- Instalacja: Pobieranie programu, Windows, Linux, Mac, Dodatkowych komponentów, Docker, AppImage, Ubuntu Snap
- Podstawy: Informacje na temat FreeCAD, Interfejs użytkownika, Profil nawigacji myszką, Metody wyboru, Nazwa obiektu, Edytor ustawień, Środowiska pracy, Struktura dokumentu, Właściwości, Pomóż w rozwoju FreeCAD, Dotacje
- Pomoc: Poradniki, Wideo poradniki
- Środowiska pracy: Strona Startowa, Złożenie, BIM, CAM, Rysunek Roboczy, MES, Inspekcja, Siatka, OpenSCAD, Część, Projekt Części, Punkty, Inżynieria Wsteczna, Robot, Szkicownik, Arkusz Kalkulacyjny, Powierzchnia 3D, Rysunek Techniczny, Test Framework