You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: technical/translation.md
+51-7Lines changed: 51 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,11 +13,56 @@ In most cases, any user-visible text in FreeCAD should be made translatable. Exc
13
13
14
14
Some general guidelines when constructing strings for translation:
15
15
* Not all languages use the same word order, so it's best to write complete sentences (and sometimes complete paragraphs), using the `QString::args()` function, or the Python `format()` function to do replacements where necessary.
16
-
* The script that extracts translatable strings from Python does not currently (as of Qt 6.4) support Python string concatenation, so to follow the above guideline you *must* write your translatable string on a single line of code.
17
-
* The NOOP-versions of the Qt translation functions extract the string for translation purposes, but *do not* replace the string in place with its translated equivalent. They should generally only be used in classes derived from Gui::Command for things like the menu entry and tooltip. The Command class handles actually loading the translated string.
16
+
* The NOOP-versions of the Qt translation functions extract the string for translation purposes, but *do not* replace the string in place with its translated equivalent. A later call to `tr()` or `translate()` is always required in these cases. See more detail below.
18
17
* In a non-QObject-derived class, use `Q_DECLARE_TR_FUNCTIONS(MyClass)` to give your class access to the `tr()` function. See also [the Qt documentation](https://doc.qt.io/qt-5/i18n-source-translation.html).
19
18
20
-
## Qt Translation Functions
19
+
## Qt Translation Process
20
+
21
+
Qt translation takes place in four steps (only two of which developers need concern themselves with, steps 1 and 4 below):
22
+
23
+
1. A source-code pre-processor called `lupdate` examines the source code, looking for specific strings that mark string literals to be translated. This is **not** a compiler or interpreter, and does not understand all of the details of C++ or Python code. Its only purpose is to generate a `*.ts` file that can be uploaded to our translation platform, CrowdIn. That upload is not currently automated, and must be done manually by a CrowdIn Manager. See [https://crowdin.com/project/freecad](https://crowdin.com/project/freecad).
24
+
2. Translators on CrowdIn are presented with these strings (plus some metadata about where they are in the code, and optional comments that developers can leave for them). For each language, translators work to develop appropriate translated strings.
25
+
3. A CrowdIn Manager exports those strings, processes them to `*.qm` files, and commits them to the FreeCAD repository.
26
+
4. When FreeCAD is compiled, those files become part of FreeCAD's Qt resources and are available to the translation functions at runtime. The translation functions `tr()` and `translate()` do the actual work of looking up the string in the translation table and extracting the correct user-visible translation.
27
+
28
+
### lupdate extraction
29
+
30
+
The critical first step above is the use of `lupdate` to extract strings marked for translation. From a developer point of view, the most critical thing to
31
+
note is that *only string literals are extracted*. A variable containing a string is **not**. There are many different functions that can be used to mark strings
32
+
for extraction: the four most important are:
33
+
*`tr(string)` All QObject-derived classes have access to this function. It serves *both* to mark a string for extraction (if a string literal is provided) *and* to do the actual translation lookup at runtime. In classes *not* derived from `QObject`, the `Q_DECLARE_TR_FUNCTIONS` macro can be used to define this function in your class.
34
+
*`translate(context, string)` It's sometimes necessary to manually specify the "context" of a string. Otherwise this does the same thing that `tr()` does.
35
+
*`QT_TR_NOOP(string)` This marks a string literal for extraction, *but does not translate it in place*. In compiled/interpreted code, this is a literal no-op, it does nothing and is completely ignored. A later call to one of the above translation functions must do the actual user-visible translation.
36
+
*`QT_TRANSLATE_NOOP(context, string)` The same as `QT_TR_NOOP` but with manual specification of context.
37
+
38
+
The most basic usage is:
39
+
```cpp
40
+
QPushButton myButton(tr("Do great things"));
41
+
```
42
+
In this case, `tr()` functions both as a marker for the `lupdate` tool to extract the string literal "Do great things", as well as an actual function call at
43
+
runtime to look up the appropriate translated string. The string's context is the name of the class that this code is exectuded from, provided automatically
44
+
by the `tr()` function.
45
+
46
+
In some cases it's necessary or useful to split up the string extraction and the translation calls, for example:
47
+
```cpp
48
+
// The following vector ALWAYS contains the English language strings, regardless of the current
49
+
// language setting. The macro just serves as a marker for lupdate.
In that case, the `translate()` function call cannot provide any information to `lupdate` because it is not being called on a string literal. Therefore
65
+
a separate mechanism must be used to provide the string to translators, and `translate()` only serves to do the final database lookup at runtime.
21
66
22
67
### C++
23
68
@@ -70,12 +115,11 @@ It then gets used similarly to `tr()` in C++, but with a manually-specified cont
70
115
print(translate("MyMod", "This will get translated"))
71
116
```
72
117
73
-
Note that "translate" is not a true function: it is really a tag that the lupdate utility uses to identify strings for translation. You *cannot* rename it, or use it with non-literal strings, etc. You also cannot use f-strings, or Python string concatenation:
118
+
Note that "translate" is not a true function: it is really a tag that the lupdate utility uses to identify strings for translation. You *cannot* rename it, or use it with non-literal strings, etc. You also cannot use f-strings:
print(translate("MyMod", "This"" also ""won't work")) # NO!
79
123
```
80
124
To do argument replacement use the `format` function of Python's string class:
81
125
@@ -128,7 +172,7 @@ To determine whether the string is being replaced correctly, you can create a du
128
172
129
173
For testing purposes, choose a language you are familiar with (or at least, can navigate well enough to deactivate when you are done testing!), and copy the file generated by lupdate into the appropriate filename. Locate the string you want to test:
0 commit comments