summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFriedrich W. H. Kossebau <kossebau@kde.org>2016-10-20 06:33:14 (GMT)
committerFriedrich W. H. Kossebau <kossebau@kde.org>2016-10-20 06:34:44 (GMT)
commit87c0182d3b0a38c58c11249870d5202f944d9acf (patch)
treec916ce82c04fab38e82d67e340af11009b825a11
parentef7f429151b9ae07fa245663d595c6e0a6164cfd (diff)
Support for translations with bundled apps, for now Marble Maps
Summary: A new Script download-pos.sh allows to get marble_qt po files either for trunk, stable or a tag, either all or a certain language. Example: data/lang/download-pos.sh trunk de To be used optionally with packaging or development builds. Additionally a target "bundle_translations" is added which can be explicitly invoked to generate qm files for the po files fetched before with download-pos.sh Also remove outdated scripts from qt4 times. Reviewers: shentey, nienhueser, #marble Reviewed By: nienhueser, #marble Differential Revision: https://phabricator.kde.org/D3094
-rw-r--r--data/lang/.gitignore2
-rw-r--r--data/lang/CMakeLists.txt104
-rw-r--r--data/lang/README60
-rwxr-xr-xdata/lang/download-pos.sh85
-rwxr-xr-xdata/lang/download-translations.bash45
-rwxr-xr-xdata/lang/marble_i18n.sh18
-rw-r--r--data/lang/merge_ts_po.cpp103
-rw-r--r--src/apps/marble-maps/main.cpp52
8 files changed, 258 insertions, 211 deletions
diff --git a/data/lang/.gitignore b/data/lang/.gitignore
index 6d609ce..6cddb31 100644
--- a/data/lang/.gitignore
+++ b/data/lang/.gitignore
@@ -1 +1 @@
-*.po
+po
diff --git a/data/lang/CMakeLists.txt b/data/lang/CMakeLists.txt
index edab714..e50841a 100644
--- a/data/lang/CMakeLists.txt
+++ b/data/lang/CMakeLists.txt
@@ -1,50 +1,66 @@
-SET (TARGET merge_ts_po)
-PROJECT (${TARGET})
-
-include_directories(
- ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR}
-)
-
-set( ${TARGET}_SRC merge_ts_po.cpp )
-add_executable( ${TARGET} ${${TARGET}_SRC} )
-
-target_link_libraries( ${TARGET} Qt5::Core )
-
-# MARBLE_WRAP_PO(qmfiles pofile1 pofile2 ... )
-MACRO(MARBLE_WRAP_PO qmfiles)
- FILE(GLOB_RECURSE all_sources RELATIVE "${CMAKE_SOURCE_DIR}/src/" "${CMAKE_SOURCE_DIR}/src/*.cpp" "${CMAKE_SOURCE_DIR}/src/*.h" "${CMAKE_SOURCE_DIR}/src/*.ui")
- SET(tstemplate ${CMAKE_CURRENT_BINARY_DIR}/template.ts)
- ADD_CUSTOM_COMMAND(OUTPUT ${tstemplate}
- COMMAND ${QT_LUPDATE_EXECUTABLE}
- ARGS ${all_sources} -ts ${tstemplate}
- WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/src"
- )
- FOREACH (pofile ${ARGN})
- GET_FILENAME_COMPONENT(pofile ${pofile} ABSOLUTE)
- GET_FILENAME_COMPONENT(basename ${pofile} NAME_WE)
- SET(tsfile "${CMAKE_CURRENT_BINARY_DIR}/${basename}.ts")
- SET(qmfile "${CMAKE_CURRENT_BINARY_DIR}/${basename}.qm")
- ADD_CUSTOM_COMMAND(OUTPUT ${tsfile}
- COMMAND ${CMAKE_CURRENT_BINARY_DIR}/merge_ts_po
- ARGS ${tstemplate} ${pofile} > ${tsfile}
- OUTPUT ${qmfile}
- COMMAND ${QT_LRELEASE_EXECUTABLE}
- ARGS -silent ${tsfile} -qm ${qmfile}
- DEPENDS ${TARGET} ${tstemplate} ${pofile}
+# custom translation catalog , for creation of bundled binary packages
+# TODO: disable for builds in normal linux distri builds, to not confuse people
+
+
+# Find Qt translation tools
+find_package(Qt5LinguistTools CONFIG)
+
+# enable target in any case
+add_custom_target(bundle_translations)
+
+if(NOT Qt5LinguistTools_FOUND)
+ return()
+endif()
+
+
+if(TARGET Qt5::lconvert)
+ set(lconvert_executable Qt5::lconvert)
+else()
+ # Qt < 5.3.1 does not define Qt5::lconvert
+ get_target_property(lrelease_location Qt5::lrelease LOCATION)
+ get_filename_component(lrelease_path ${lrelease_location} PATH)
+ find_program(lconvert_executable
+ NAMES lconvert-qt5 lconvert
+ PATHS ${lrelease_path}
+ NO_DEFAULT_PATH
)
- SET(${qmfiles} ${${qmfiles}} "${qmfile}")
- ENDFOREACH(pofile)
-ENDMACRO(MARBLE_WRAP_PO)
+endif()
+
+function(marble_process_po_files_as_qm lang po_file)
+ # Create commands to turn po files into qm files
+ get_filename_component(po_file ${po_file} ABSOLUTE)
+ get_filename_component(filename_base ${po_file} NAME_WE)
+ # Include ${lang} in build dir because we might be called multiple times
+ # with the same ${filename_base}
+ set(build_dir ${CMAKE_CURRENT_BINARY_DIR}/locale/${lang})
+ set(ts_file ${build_dir}/${filename_base}.ts)
+ set(qm_file ${build_dir}/${filename_base}.qm)
-FILE(GLOB LANG_SRC "*.po")
+ file(MAKE_DIRECTORY ${build_dir})
-# Create our translation files.
-MARBLE_WRAP_PO(LANG_FILES ${LANG_SRC})
+ # lconvert from .po to .ts, then lrelease from .ts to .qm.
+ add_custom_command(OUTPUT ${qm_file}
+ COMMAND ${lconvert_executable}
+ ARGS -i ${po_file} -o ${ts_file} -target-language ${lang}
+ COMMAND Qt5::lrelease
+ ARGS -removeidentical -nounfinished -silent ${ts_file} -qm ${qm_file}
+ DEPENDS ${po_file}
+ )
+ install(
+ FILES ${qm_file}
+ DESTINATION ${data_dir}/locale/${lang}
+ OPTIONAL # if not build, ignore it
+ )
-ADD_CUSTOM_TARGET(translations ALL
- DEPENDS ${LANG_FILES}
-)
+ set(target_name translation_${lang}_${filename_base})
+ add_custom_target(${target_name} DEPENDS ${qm_file})
+ add_dependencies(bundle_translations ${target_name})
+endfunction()
-install(FILES ${LANG_FILES} DESTINATION ${MARBLE_DATA_INSTALL_PATH}/lang)
+file(GLOB po_files "po/*/*.po")
+foreach(po_file ${po_files})
+ get_filename_component(po_dir ${po_file} DIRECTORY)
+ get_filename_component(lang ${po_dir} NAME)
+ marble_process_po_files_as_qm(${lang} ${po_file})
+endforeach()
diff --git a/data/lang/README b/data/lang/README
new file mode 100644
index 0000000..1089b65
--- /dev/null
+++ b/data/lang/README
@@ -0,0 +1,60 @@
+Handling translation catalogs for app bundles
+=============================================
+
+Usage
+-----
+A package build is done as usual, just with 2 extra steps.
+
+# Provide marble sources, e.g. working copy of repo or tarball untarred
+[...]
+
+# EXTRA STEP: Add translation sources to the sources (here for stable):
+cd <toplevel_sources>
+data/lang/download-pos.sh stable
+
+# Configure build, run cmake as usual:
+cd <toplevel_build>
+cmake [...]
+
+# Build as usual
+make
+
+# EXTRA STEP: Build translations for bundle package optionally
+make bundle_translations
+
+# Install as usual
+# If bundle_translations was called before, this will also install the qm files
+make install
+
+# Package as usual
+[...]
+
+
+Challenge
+---------
+Translation catalogs are not part of the Marble source repository, instead
+are in a different repository.
+So on packaging time the catalogs need to be added to the working copy, so
+they can be processed as needed and become part of the package product.
+
+The usual KDE source packaging scripts are fetching all po files belonging to
+the repo and are storing them in the toplevel dir in a subfolder po/$lang/*.po.
+Additionally either the buildsystem has some if-po/-exists-process-and-install-po-files
+or the packaging script will inject such logic into the buildsystem.
+
+When creating binary packages, this is often done directly from a checkout of the
+sources and not from the released source tarball. So the step of fetching all po
+files has to be done here as well as making sure the processing and installation is done.
+
+Not all products are build for all platforms. So e.g. for Android only Marble Maps
+will be created (separate packaging done for Behaim app, ignored for now). Right now
+the apps only or also released as bundles (Maps, Behaim, MarbleQt) or the lib only need
+the one single marble_qt catalog. The other (ki18n) catalogs are only used on platforms
+where binary packaging is done by the distributions and also unbundled.
+
+
+So supporting translations with binary packaging needs to work independently:
+* separate script to download po files, in separate folder:
+ download-pos.sh
+* separate target name for creating the qm files:
+ bundle_translations
diff --git a/data/lang/download-pos.sh b/data/lang/download-pos.sh
new file mode 100755
index 0000000..2b545f5
--- /dev/null
+++ b/data/lang/download-pos.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+# A script to download Marble Qt translations from KDE's SVN.
+#
+# To be used before cmake is called on the sources.
+#
+# This program is free software licensed under the GNU LGPL. You can
+# find a copy of this license in LICENSE.txt in the top directory of
+# the source code.
+#
+# Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+#
+
+# TODO: support updates properly (e.g. removing no longer existing po files)
+
+has_subdirs=true
+case "$1" in
+ trunk)
+ echo "Downloading from trunk"
+ svn_path_prefix="svn://anonsvn.kde.org/home/kde/trunk/l10n-kf5"
+ ;;
+ stable)
+ echo "Downloading from stable"
+ svn_path_prefix="svn://anonsvn.kde.org/home/kde/branches/stable/l10n-kf5"
+ ;;
+ "")
+ echo "Syntax: $0 trunk|stable|\"KDE Applications tag\" [lang]"
+ exit 1
+ ;;
+ *)
+ TAG=$1
+ echo "Downloading from KDE Applications tag $1"
+ svn_path_prefix="svn://anonsvn.kde.org/home/kde/tags/Applications/${TAG}/kde-l10n/5"
+ has_subdirs=false
+ ;;
+esac
+
+if [ ! -e data/lang ]; then
+ echo "$0 needs to be invoked from the toplevel source dir."
+ exit 1
+fi
+
+
+workdir="$(mktemp -d)"
+pofile="${workdir}//marble_qt.po"
+
+if [ $# -gt 1 ] ; then
+ languages=$2
+else
+ subdirs="${workdir}/subdirs"
+ if [ "$has_subdirs" = true ] ; then
+ svn -q export "${svn_path_prefix}/subdirs" ${subdirs}
+ else
+ # check if tag exists
+ if svn info ${svn_path_prefix} > /dev/null 2>&1 ; then
+ # tags don't have a subdirs file, so create one from the dirs present
+ svn list ${svn_path_prefix} | egrep "/$" | sed "s,/$,," > ${subdirs}
+ else
+ echo "There seems to be no tag $TAG."
+ exit 1
+ fi
+ fi
+ languages=$(cat ${subdirs})
+fi
+
+for i in ${languages}
+do
+ svn -q export "${svn_path_prefix}/${i}/messages/kdeedu/marble_qt.po" ${pofile} > /dev/null 2>&1
+ # some languages might not have a catalog
+ if [ -e ${pofile} ]; then
+ chmod a-w ${pofile} # mark as not-to-be-edited
+ target_dir="data/lang/po/${i}"
+ mkdir -p ${target_dir}
+ mv -f ${pofile} ${target_dir}
+ echo "Downloaded for language $i"
+ fi
+done
+
+if [ -e data/lang/po ]; then
+ # TODO: think about some way to check if there are local modifications to prevent their loss
+ touch data/lang/po/WARNING_CHANGES_TO_PO_FILES_WILL_BE_LOST
+ # language files are only picked up at configuration time by a glob expression
+ # TODO: think about some way to trigger this automatically, by detecting the need and changing some file
+ echo "For picking up new languages, the CMake config needs to be redone, e.g. by calling: make rebuild_cache"
+fi
diff --git a/data/lang/download-translations.bash b/data/lang/download-translations.bash
deleted file mode 100755
index feeaf30..0000000
--- a/data/lang/download-translations.bash
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-
-# A script to download Marble Qt translations from KDE's SVN.
-#
-# This program is free software licensed under the GNU LGPL. You can
-# find a copy of this license in LICENSE.txt in the top directory of
-# the source code.
-#
-# Copyright 2011 Dennis Nienhüser <nienhueser@kde.org>
-# Copyright 2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
-#
-
-set -e
-
-workdir="$(mktemp -d)"
-
-#prefix="svn://anonsvn.kde.org/home/kde/branches/stable/l10n-kde4/"
-#TAG="4.7.0"
-#prefix="svn://anonsvn.kde.org/home/kde/tags/KDE/${TAG}/l10n-kde4/"
-# Translations can also be loaded from SVN trunk, uncomment below.
-prefix="svn://anonsvn.kde.org/home/kde/trunk/l10n-kde4"
-
-echo "Downloading translations, please wait. This may take some time..."
-
-svn -q export "${prefix}/subdirs" "${workdir}/subdirs"
-for i in $(cat "${workdir}/subdirs")
-do
- if svn -q export --force "${prefix}/${i}/messages/kdeedu/marble_qt.po" "${workdir}/marble_qt.po" 2>/dev/null
- then
- if svn -q export --force "${prefix}/${i}/messages/kdeedu/marble.po" "${workdir}/marble.po" 2>/dev/null
- then
- echo >> "${workdir}/marble_qt.po"
- cat "${workdir}/marble.po" >> "${workdir}/marble_qt.po"
- mv "${workdir}/marble_qt.po" marble-${i}.po
- fi
- fi
-done
-
-rm "${workdir}/marble.po"
-rm "${workdir}/subdirs"
-rmdir "${workdir}"
-
-test -e CMakeLists.txt && touch CMakeLists.txt
-
-echo "Done."
diff --git a/data/lang/marble_i18n.sh b/data/lang/marble_i18n.sh
deleted file mode 100755
index fa9b9e2..0000000
--- a/data/lang/marble_i18n.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-TAG=$1
-if [ "${TAG}" = "" ]; then
- TAG=4.7.3
- echo "Syntax: marble_i18n KDE-tag"
- echo "E.g.: marble_i18n ${TAG}"
- echo "-------------------------"
- echo "Assuming ${TAG} as a KDE-tag version for now."
-fi
-
-svn export svn://anonsvn.kde.org/home/kde/tags/KDE/${TAG}/kde-l10n/subdirs
-for i in $(cat subdirs)
-do
- echo "Processing Language $i"
- svn export svn://anonsvn.kde.org/home/kde/tags/KDE/${TAG}/kde-l10n/${i}/messages/kdeedu/marble.po
- perl -pi -e 's/^[[:space:]]*#.*$//g' marble.po
- lconvert marble.po -o marble_${i}.qm
-done
diff --git a/data/lang/merge_ts_po.cpp b/data/lang/merge_ts_po.cpp
deleted file mode 100644
index cd91cb2..0000000
--- a/data/lang/merge_ts_po.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// This file is part of the Marble Virtual Globe.
-//
-// This program is free software licensed under the GNU LGPL. You can
-// find a copy of this license in LICENSE.txt in the top directory of
-// the source code.
-//
-// Copyright 2011 Dennis Nienhüser <nienhueser@kde.org>
-//
-
-#include <QString>
-#include <QFileInfo>
-#include <QTextStream>
-#include <QDebug>
-
-void usage( const QString &app )
-{
- qDebug() << "Usage: " << app << " input.ts input.po";
-}
-
-int main( int argc, char** argv )
-{
- if ( argc != 3 ) {
- usage( argv[0] );
- return 0;
- }
-
- QFileInfo const ts = QFileInfo( QString( argv[1] ) );
- if ( !ts.exists() ) {
- usage( argv[0] );
- return 1;
- }
-
- QFileInfo const po = QFileInfo( QString( argv[2] ) );
- if ( !po.exists() ) {
- usage( argv[0] );
- return 2;
- }
-
- QMap<QString,QString> translations;
-
- // Open the .po file and build a map of translations
- QFile poFile( po.absoluteFilePath() );
- poFile.open( QFile::ReadOnly );
- QTextStream poStream( &poFile );
- poStream.setCodec( "UTF-8" );
- poStream.setAutoDetectUnicode( true );
- QString source;
- bool ignore = false;
- while( !poStream.atEnd() ) {
- QString line = poStream.readLine();
- if ( line.startsWith( QLatin1String( "#, fuzzy" ) ) ) {
- ignore = true;
- } else if ( line.startsWith( QLatin1String( "msgid " ) ) ) {
- source = line.mid( 7, line.size() - 8 );
- } else if ( !source.isEmpty() && line.startsWith( QLatin1String( "msgstr " ) ) ) {
- if ( ignore ) {
- ignore = false;
- } else {
- QString translation = line.mid( 8, line.size() - 9 );
- source.replace( QLatin1Char( '&' ), QLatin1String( "&amp;" ) );
- translation.replace( QLatin1Char( '&' ), QLatin1String( "&amp; ") );
- source.replace( QLatin1Char( '<' ), QLatin1String( "&lt;" ) );
- translation.replace( QLatin1Char( '<' ), QLatin1String( "&lt;" ) );
- source.replace( QLatin1Char( '>' ), QLatin1String( "&gt;" ) );
- translation.replace( QLatin1Char( '>' ), QLatin1String( "&gt;" ) );
- if ( !translation.isEmpty() ) {
- translations[source] = translation;
- }
- }
- }
- }
-
- QTextStream console( stdout );
- console.setCodec( "UTF-8" );
-
- // Open the .ts file and replace source strings with translations
- // The modified .to file is dumped to stdout
- QFile tsFile( ts.absoluteFilePath() );
- tsFile.open( QFile::ReadOnly );
- QTextStream tsStream( &tsFile );
- tsStream.setCodec( "UTF-8" );
- tsStream.setAutoDetectUnicode( true );
- source.clear();
- while( !tsStream.atEnd() ) {
- QString line = tsStream.readLine().trimmed();
- if ( line.startsWith( QLatin1String( "<source>" ) ) ) {
- source = line.mid( 8, line.size() - 17 );
- console << line << "\n";
- } else if ( !source.isEmpty() &&
- line == "<translation type=\"unfinished\"></translation>" &&
- translations.contains( source ) ) {
- console << "<translation>" << translations[source] << "</translation>\n";
- } else if ( !source.isEmpty() &&
- line == "<translation type=\"unfinished\"></translation>" ) {
- console << line << "\n";
- } else {
- console << line << "\n";
- }
- }
-
- return 0;
-}
diff --git a/src/apps/marble-maps/main.cpp b/src/apps/marble-maps/main.cpp
index 4d77661..ebe69ac 100644
--- a/src/apps/marble-maps/main.cpp
+++ b/src/apps/marble-maps/main.cpp
@@ -15,10 +15,59 @@
#include "declarative/MarbleDeclarativePlugin.h"
#include <MarbleGlobal.h>
#include "MarbleMaps.h"
+#include "MarbleDirs.h"
#include "TextToSpeechClient.h"
using namespace Marble;
+static bool loadTranslation(const QString &localeDirName, QApplication &app)
+{
+ // TODO: check if any translations for Qt modules have to be loaded,
+ // as they need to be explicitely loaded as well by the Qt-using app
+
+#ifdef Q_OS_ANDROID
+ // load translation file from bundled packaging installation
+ const QString fullPath = MarbleDirs::systemPath() + QLatin1String("/locale/") + localeDirName + QLatin1String("/marble_qt.qm");
+#else
+ // load translation file from normal "KDE Applications" packaging installation
+ const QString subPath = QLatin1String("locale/") + localeDirName + QLatin1String("/LC_MESSAGES/marble_qt.qm");
+ const QString fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath);
+ if (fullPath.isEmpty()) {
+ return false;
+ }
+#endif
+
+ QTranslator* translator = new QTranslator(&app);
+ if (!translator->load(fullPath)) {
+ delete translator;
+ return false;
+ }
+
+ app.installTranslator(translator);
+
+ return true;
+}
+
+// load KDE translators system based translations
+static void loadTranslations(QApplication &app)
+{
+ // Quote from ecm_create_qm_loader created code:
+ // The way Qt translation system handles plural forms makes it necessary to
+ // have a translation file which contains only plural forms for `en`.
+ // That's why we load the `en` translation unconditionally, then load the
+ // translation for the current locale to overload it.
+ const QString en(QStringLiteral("en"));
+
+ loadTranslation(en, app);
+
+ QLocale locale = QLocale::system();
+ if (locale.name() != en) {
+ if (!loadTranslation(locale.name(), app)) {
+ loadTranslation(locale.bcp47Name(), app);
+ }
+ }
+}
+
#ifdef Q_OS_ANDROID
// Declare symbol of main method as exported as needed by Qt-on-Android,
// where the Dalvik-native QtActivity class needs to find and invoke it
@@ -35,6 +84,9 @@ int main(int argc, char ** argv)
app.setDesktopFileName(QStringLiteral("org.kde.marble.maps"));
#endif
+ // Load Qt translation system catalog for libmarblewidget, the plugins and this app
+ loadTranslations(app);
+
#ifdef Q_OS_ANDROID
MarbleGlobal::Profiles profiles = MarbleGlobal::SmallScreen | MarbleGlobal::HighResolution;
MarbleGlobal::getInstance()->setProfiles( profiles );