aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/widgets/linguist/example_de.ts40
-rw-r--r--examples/widgets/linguist/linguist.pyproject3
-rw-r--r--examples/widgets/linguist/linguist_rc.py68
-rw-r--r--examples/widgets/linguist/main.py93
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/translations.rst82
5 files changed, 286 insertions, 0 deletions
diff --git a/examples/widgets/linguist/example_de.ts b/examples/widgets/linguist/example_de.ts
new file mode 100644
index 000000000..447d1ceb5
--- /dev/null
+++ b/examples/widgets/linguist/example_de.ts
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE" sourcelanguage="de_DE">
+<context>
+ <name>Window</name>
+ <message>
+ <location filename="main.py" line="56"/>
+ <source>&amp;File</source>
+ <translation>&amp;Datei</translation>
+ </message>
+ <message>
+ <location filename="main.py" line="57"/>
+ <source>Quit</source>
+ <translation>Beenden</translation>
+ </message>
+ <message>
+ <location filename="main.py" line="58"/>
+ <source>CTRL+Q</source>
+ <translation>CTRL+B</translation>
+ </message>
+ <message>
+ <location filename="main.py" line="60"/>
+ <source>&amp;Help</source>
+ <translation>Hilfe</translation>
+ </message>
+ <message>
+ <location filename="main.py" line="61"/>
+ <source>About Qt</source>
+ <translation>Über Qt</translation>
+ </message>
+ <message numerus="yes">
+ <location filename="main.py" line="75"/>
+ <source>%n language(s) selected</source>
+ <translation>
+ <numerusform>Eine Sprache ausgewählt</numerusform>
+ <numerusform>%n Sprachen ausgewählt</numerusform>
+ </translation>
+ </message>
+</context>
+</TS>
diff --git a/examples/widgets/linguist/linguist.pyproject b/examples/widgets/linguist/linguist.pyproject
new file mode 100644
index 000000000..43a13b036
--- /dev/null
+++ b/examples/widgets/linguist/linguist.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "linguist.qrc", "example_de.ts"]
+}
diff --git a/examples/widgets/linguist/linguist_rc.py b/examples/widgets/linguist/linguist_rc.py
new file mode 100644
index 000000000..72575e549
--- /dev/null
+++ b/examples/widgets/linguist/linguist_rc.py
@@ -0,0 +1,68 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.2.0
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x01\xcd\
+<\
+\xb8d\x18\xca\xef\x9c\x95\xcd!\x1c\xbf`\xa1\xbd\xdd\xa7\
+\x00\x00\x00\x05de_DEB\x00\x00\x000\x00\x05\
+\x8c\x04\x00\x00\x01E\x00*\xd0%\x00\x00\x00\x91\x00*\
+\xec0\x00\x00\x00\xbd\x04\x89o\x01\x00\x00\x01\x18\x05\xbc\
+\xad\xd4\x00\x00\x00\x00\x09la\xf4\x00\x00\x00\xe7i\x00\
+\x00\x01r\x03\x00\x00\x00.\x00E\x00i\x00n\x00e\
+\x00 \x00S\x00p\x00r\x00a\x00c\x00h\x00e\
+\x00 \x00a\x00u\x00s\x00g\x00e\x00w\x00\xe4\
+\x00h\x00l\x00t\x03\x00\x00\x00,\x00%\x00n\x00\
+ \x00S\x00p\x00r\x00a\x00c\x00h\x00e\x00\
+n\x00 \x00a\x00u\x00s\x00g\x00e\x00w\x00\
+\xe4\x00h\x00l\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x17%n language(s) \
+selected\x07\x00\x00\x00\x06Win\
+dow\x01\x03\x00\x00\x00\x0c\x00&\x00D\x00a\x00\
+t\x00e\x00i\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05&\
+File\x07\x00\x00\x00\x06Window\x01\
+\x03\x00\x00\x00\x0a\x00H\x00i\x00l\x00f\x00e\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x05&Help\x07\x00\
+\x00\x00\x06Window\x01\x03\x00\x00\x00\x0e\x00\
+\xdc\x00b\x00e\x00r\x00 \x00Q\x00t\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x08About Qt\x07\
+\x00\x00\x00\x06Window\x01\x03\x00\x00\x00\x0c\
+\x00C\x00T\x00R\x00L\x00+\x00B\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x06CTRL+Q\x07\x00\x00\x00\
+\x06Window\x01\x03\x00\x00\x00\x0e\x00B\x00\
+e\x00e\x00n\x00d\x00e\x00n\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x04Quit\x07\x00\x00\x00\x06Wi\
+ndow\x01\x88\x00\x00\x00\x02\x01\x01\
+"
+
+qt_resource_name = b"\
+\x00\x0c\
+\x0d\xfc\x11\x13\
+\x00t\
+\x00r\x00a\x00n\x00s\x00l\x00a\x00t\x00i\x00o\x00n\x00s\
+\x00\x0d\
+\x02\x8b\x0a\x9d\
+\x00e\
+\x00x\x00a\x00m\x00p\x00l\x00e\x00_\x00d\x00e\x00.\x00q\x00m\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01z\x80\x03\xea\xc1\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/examples/widgets/linguist/main.py b/examples/widgets/linguist/main.py
new file mode 100644
index 000000000..03783d046
--- /dev/null
+++ b/examples/widgets/linguist/main.py
@@ -0,0 +1,93 @@
+#############################################################################
+##
+## Copyright (C) 2021 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import (QItemSelection, QLibraryInfo, QLocale, QTranslator,
+ Qt, Slot)
+from PySide6.QtWidgets import (QAbstractItemView, QApplication, QListWidget,
+ QMainWindow, QWidget)
+
+
+import linguist_rc
+
+
+class Window(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ file_menu = self.menuBar().addMenu(self.tr("&File"))
+ quit_action = file_menu.addAction(self.tr("Quit"))
+ quit_action.setShortcut(self.tr("CTRL+Q"))
+ quit_action.triggered.connect(self.close)
+ help_menu = self.menuBar().addMenu(self.tr("&Help"))
+ about_qt_action = help_menu.addAction(self.tr("About Qt"))
+ about_qt_action.triggered.connect(qApp.aboutQt)
+
+ self._list_widget = QListWidget()
+ self._list_widget.setSelectionMode(QAbstractItemView.MultiSelection)
+ self._list_widget.selectionModel().selectionChanged.connect(self.selection_changed)
+ self._list_widget.addItem("C++")
+ self._list_widget.addItem("Java")
+ self._list_widget.addItem("Python")
+ self.setCentralWidget(self._list_widget)
+
+ @Slot(QItemSelection, QItemSelection)
+ def selection_changed(self, selected, deselected):
+ count = len(self._list_widget.selectionModel().selectedRows())
+ message = self.tr("%n language(s) selected", "", count)
+ self.statusBar().showMessage(message)
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+
+ path = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
+ translator = QTranslator(app)
+ if translator.load(QLocale.system(), 'qtbase', '_', path):
+ app.installTranslator(translator)
+ translator = QTranslator(app)
+ path = ':/translations'
+ if translator.load(QLocale.system(), 'example', '_', path):
+ app.installTranslator(translator)
+
+ window = Window()
+ window.show()
+ sys.exit(app.exec())
diff --git a/sources/pyside6/doc/tutorials/basictutorial/translations.rst b/sources/pyside6/doc/tutorials/basictutorial/translations.rst
index eb51baac3..44280580c 100644
--- a/sources/pyside6/doc/tutorials/basictutorial/translations.rst
+++ b/sources/pyside6/doc/tutorials/basictutorial/translations.rst
@@ -3,6 +3,88 @@
Translating Applications
========================
+Qt Linguist
+-----------
+
+`Qt Linguist <https://doc.qt.io/qt-6/qtlinguist-index.html>`_ and
+its related tools can be used to provide translations for applications.
+
+The ``examples/widgets/linguist`` example illustrates this. The example is
+very simple, it has a menu and shows a list of programming languages with
+multiselection.
+
+Translation works by passing the message strings through function calls that
+look up the translation. Each ``QObject`` instance provides a ``tr()``
+function for that purpose. There is also ``QCoreApplication.translate()``
+for adding translated texts to non-QObject classes.
+
+Qt ships its own translations containing the error messages and standard
+dialog captions.
+
+The linguist example has a number of messages enclosed in ``self.tr()``.
+The status bar message shown in response to a selection change uses
+a plural form depending on a count:
+
+ .. code-block:: python
+
+ count = len(self._list_widget.selectionModel().selectedRows())
+ message = self.tr("%n language(s) selected", "", count)
+
+The translation workflow for the example is as follows:
+The translated messages are extracted using the ``lupdate`` tool,
+producing XML-based ``.ts`` files:
+
+ .. code-block:: bash
+
+ pyside6-lupdate main.py -ts example_de.ts
+
+If ``example_de.ts`` already exists, it will be updated with the new
+messages added to the code in-between.
+
+``.ts`` files are translated using *Qt Linguist*. Once this is complete,
+the files are converted to a binary form (``.qm`` files):
+
+ .. code-block:: bash
+
+ mkdir translations
+ pyside6-lrelease example_de.ts -qm translations/example_de.qm
+
+To avoid having to ship the ``.qm`` files, it is recommend
+to put them into a Qt resource file along with icons and other
+applications resources (see :ref:`using_qrc_files`).
+The resource file ``linguist.qrc`` provides the ``example_de.qm``
+under ``:/translations``:
+
+ .. code-block:: xml
+
+ <!DOCTYPE RCC><RCC version="1.0">
+ <qresource>
+ <file>translations/example_de.qm</file>
+ </qresource>
+ </RCC>
+
+At runtime, the translations need to be loaded using the ``QTranslator`` class:
+
+ .. code-block:: python
+
+ path = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
+ translator = QTranslator(app)
+ if translator.load(QLocale.system(), 'qtbase', '_', path):
+ app.installTranslator(translator)
+ translator = QTranslator(app)
+ path = ':/translations'
+ if translator.load(QLocale.system(), 'example', '_', path):
+ app.installTranslator(translator)
+
+The code first loads the translations shipped for Qt and then
+the translations of the applications loaded from resources.
+
+The example can then be run in German:
+
+ .. code-block:: bash
+
+ LANG=de python main.py
+
GNU gettext
-----------