flowchart LR
A[Developer marks strings] --> B[Weblate extracts marked strings]
B --> C[Translators provide translations]
C --> D[JASP displays translated text at runtime]
12 Translating Your Module
JASP is used worldwide. Every user-visible string — in QML forms, R output, and C++ code — must be marked for translation so that volunteer translators can localise JASP via Weblate.
12.1 How Translation Works
- QML:
qsTr("text")→ extracted by Qt’slupdatetool. - R:
gettext("text")/gettextf("text %s", var)→ extracted by R’s translation tooling. - C++:
tr("text")→ extracted by Qt’slupdatetool.
12.2 QML Translation
Wrap every user-visible string in qsTr():
CheckBox { name: "meanDifference"; label: qsTr("Mean difference") }
Group
{
title: qsTr("Statistics")
CheckBox { name: "effectSize"; label: qsTr("Effect size") }
}12.2.1 Parameters in QML
Use %1, %2, etc. for substitutions:
text: qsTr("Group %1 vs Group %2").arg(group1Name).arg(group2Name)12.3 R Translation
12.3.1 gettext() — Simple Strings
table$addColumnInfo(name = "estimate", title = gettext("Estimate"), type = "number")12.3.2 gettextf() — Strings with Parameters
Use sprintf-style format specifiers:
message <- gettextf("Variable '%s' has %d observations.", varName, nObs)12.3.3 Numbered Arguments
When a string has multiple parameters, use numbered format specifiers (%1$s, %2$d) so translators can reorder them:
# Good — translators can reorder
gettextf("Compared %1$s with %2$s using %3$s.", group1, group2, method)
# Bad — translators cannot reorder
gettextf("Compared %s with %s using %s.", group1, group2, method)12.3.4 Plurals with ngettext()
msg <- ngettext(nObs,
"Only 1 observation. At least 2 are required.",
"Only %d observations. At least 2 are required."
)12.3.5 Unicode Characters
Use \uXXXX escapes, never raw Unicode characters or intToUtf8():
# Good
"\u2260" # ≠
# Bad
"≠" # fails on some locales
intToUtf8(8800) # not translatable12.4 Common Patterns
12.4.1 Converting paste() to gettextf()
paste() concatenation cannot be translated because word order differs across languages. Always convert to gettextf():
# Bad — untranslatable
message <- paste("Variable", varName, "has", nObs, "observations")
# Good — translatable
message <- gettextf("Variable '%1$s' has %2$d observations.", varName, nObs)12.4.2 Wrapping Expressions
If you construct expressions for display:
# Wrap the template, not the variable name
title <- gettextf("P(%1$s | data)", hypothesis)12.4.3 Table Titles and Column Names
Every title in createJaspTable() and addColumnInfo() must be wrapped:
table <- createJaspTable(title = gettext("Descriptive Statistics"))
table$addColumnInfo(name = "mean", title = gettext("Mean"), type = "number")
table$addColumnInfo(name = "sd", title = gettext("SD"), type = "number")
table$addColumnInfo(name = "n", title = gettext("N"), type = "integer")12.4.4 Footnotes and Error Messages
table$addFootnote(message = gettext("Levene's test is significant (p < .05)."))
table$setError(gettext("The dependent variable contains only missing values."))12.5 What NOT to Translate
- Option names (
name:in QML, keys inoptions[[...]]in R) - Column/row name keys (the
nameparameter inaddColumnInfo) - Internal log messages
- Code comments
- Variable names used in computation
12.6 C++ Translation
Use tr() inside QObject-derived classes:
QString message = tr("Analysis complete");For non-QObject code, use QCoreApplication::translate().
12.7 Quick Reference
| Language | Simple string | With parameters | Plural |
|---|---|---|---|
| QML | qsTr("text") |
qsTr("x %1").arg(v) |
— |
| R | gettext("text") |
gettextf("x %s", v) |
ngettext(n, "one", "many") |
| C++ | tr("text") |
tr("x %1").arg(v) |
— |