diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b2c04c01..42eb6ebc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,7 +120,7 @@ set(KATIE_LIBRARIES_PATH "${CMAKE_INSTALL_FULL_LIBDIR}" CACHE PATH "Libraries in set(KATIE_BINARIES_PATH "${CMAKE_INSTALL_FULL_BINDIR}" CACHE PATH "Binaries installation path") set(KATIE_PLUGINS_PATH "${KATIE_LIBRARIES_PATH}/katie/plugins" CACHE PATH "Plugins installation path") set(KATIE_IMPORTS_PATH "${KATIE_LIBRARIES_PATH}/katie/imports" CACHE PATH "Declarative imports installation path") -set(KATIE_TRANSLATIONS_PATH "${CMAKE_INSTALL_FULL_LOCALEDIR}" CACHE PATH "Translations installation path") +set(KATIE_TRANSLATIONS_PATH "${CMAKE_INSTALL_FULL_DATADIR}/translations" CACHE PATH "Translations installation path") set(KATIE_CMAKE_PATH "${KATIE_LIBRARIES_PATH}/cmake/Katie" CACHE PATH "CMake aware modules installation path") set(KATIE_LDCONF_PATH "${CMAKE_INSTALL_FULL_SYSCONFDIR}/ld.so.conf.d" CACHE PATH "Run-time linker/loader configs installation path") set(KATIE_PROFILE_PATH "${CMAKE_INSTALL_FULL_SYSCONFDIR}/profile.d" CACHE PATH "Shell profile scripts installation path") @@ -147,8 +147,8 @@ add_feature_info(dbus WITH_DBUS "build D-Bus support") option(WITH_FONTCONFIG "Build Fontconfig support" ON) add_feature_info(fontconfig WITH_FONTCONFIG "build Fontconfig support") -option(WITH_INTL "Build Intl support" ON) -add_feature_info(intl WITH_INTL "build Intl support") +option(WITH_GETTEXTPO "Build Gettext support" ON) +add_feature_info(gettextpo WITH_GETTEXTPO "build Gettext support") option(WITH_EXECINFO "Build ExecInfo support" OFF) add_feature_info(execinfo WITH_EXECINFO "build ExecInfo support") @@ -256,10 +256,10 @@ set_package_properties(DBus PROPERTIES TYPE RECOMMENDED ) -find_package(Intl) -set_package_properties(Intl PROPERTIES +find_package(GettextPO) +set_package_properties(GettextPO PROPERTIES PURPOSE "Required for translations support" - DESCRIPTION "GNU gettext runtime library" + DESCRIPTION "GNU gettext PO files processsor" URL "https://www.gnu.org/software/gettext/gettext.html" TYPE RECOMMENDED ) @@ -400,8 +400,10 @@ if(NOT WITH_CUPS OR NOT CUPS_FOUND) katie_config(QT_NO_CUPS) endif() -if(NOT WITH_INTL OR NOT INTL_FOUND) +if(NOT WITH_GETTEXTPO OR NOT GETTEXTPO_FOUND) katie_config(QT_NO_TRANSLATION) +else() + set(KATIE_TOOLS "${KATIE_TOOLS} trc") endif() if(NOT WITH_EXECINFO OR NOT EXECINFO_FOUND) @@ -464,7 +466,7 @@ add_subdirectory(src/uitools) add_subdirectory(src/tools) -if(WITH_INTL AND INTL_FOUND) +if(WITH_GETTEXTPO AND GETTEXTPO_FOUND) add_subdirectory(translations) endif() diff --git a/README b/README index 0c9bbd8c4..9b35cf245 100644 --- a/README +++ b/README @@ -49,6 +49,7 @@ There are several things you should be aware before considering Katie: - some additions have been made: - custom image format + - custom translations format - custom hash algorithm - JSON document handler - standard directories handler diff --git a/cmake/modules/FindGettextPO.cmake b/cmake/modules/FindGettextPO.cmake new file mode 100644 index 000000000..e16bb281d --- /dev/null +++ b/cmake/modules/FindGettextPO.cmake @@ -0,0 +1,31 @@ +# Try to find gettextpo, once done this will define: +# +# GETTEXTPO_FOUND - system has gettextpo +# GETTEXTPO_INCLUDES - the gettextpo include directory +# GETTEXTPO_LIBRARIES - the libraries needed to use gettextpo +# +# Copyright (C) 2022 Ivailo Monev +# +# Redistribution and use is allowed according to the terms of the BSD license. + +find_package(PkgConfig REQUIRED) +include(FindPackageHandleStandardArgs) + +# gettextpo does not provide pkg-config files + +find_path(GETTEXTPO_INCLUDES + NAMES gettext-po.h + HINTS $ENV{GETTEXTPODIR}/include +) + +find_library(GETTEXTPO_LIBRARIES + NAMES gettextpo + HINTS $ENV{GETTEXTPODIR}/lib +) + +find_package_handle_standard_args(GettextPO + VERSION_VAR PC_GETTEXTPO_VERSION + REQUIRED_VARS GETTEXTPO_LIBRARIES GETTEXTPO_INCLUDES +) + +mark_as_advanced(GETTEXTPO_INCLUDES GETTEXTPO_LIBRARIES) diff --git a/cmake/modules/FindIntl.cmake b/cmake/modules/FindIntl.cmake deleted file mode 100644 index 384411373..000000000 --- a/cmake/modules/FindIntl.cmake +++ /dev/null @@ -1,50 +0,0 @@ -# Try to find intl, once done this will define: -# -# INTL_FOUND - system has intl -# INTL_INCLUDES - the intl include directory -# INTL_LIBRARIES - the libraries needed to use intl -# INTL_MSGFMT - the msgfmt binary -# -# Copyright (C) 2016 Ivailo Monev -# -# Redistribution and use is allowed according to the terms of the BSD license. - -# intl does not provide pkg-config files - -include(CMakePushCheckState) -include(FindPackageHandleStandardArgs) - -set(INTL_NAMES c intl) - -find_path(INTL_INCLUDES - NAMES libintl.h - HINTS $ENV{INTLDIR}/include -) - -set(INTL_LIBRARIES) -foreach(name ${INTL_NAMES}) - if(NOT INTL_LIBRARIES) - unset(HAVE_bind_textdomain_codeset CACHE) - cmake_reset_check_state() - set(CMAKE_REQUIRED_LIBRARIES ${name}) - katie_check_defined(bind_textdomain_codeset "libintl.h") - cmake_reset_check_state() - if(HAVE_bind_textdomain_codeset) - find_library(INTL_LIBRARIES - NAMES ${name} - HINTS $ENV{INTLDIR}/lib - ) - endif() - endif() -endforeach() - -find_program(INTL_MSGFMT - NAMES msgfmt - HINTS $ENV{INTLDIR}/bin -) - -find_package_handle_standard_args(Intl - REQUIRED_VARS INTL_LIBRARIES INTL_INCLUDES INTL_MSGFMT -) - -mark_as_advanced(INTL_INCLUDES INTL_LIBRARIES INTL_MSGFMT) diff --git a/package/debian/control b/package/debian/control index 404233800..902058184 100644 --- a/package/debian/control +++ b/package/debian/control @@ -5,7 +5,7 @@ Homepage: https://osdn.net/projects/kde/ Vcs-Git: git://git.osdn.net/gitroot/kde/Katie.git Vcs-browser: https://osdn.net/projects/kde/scm/git/Katie/tree Standards-Version: 4.12.0 -Build-Depends: debhelper (>= 9~), libdeflate-dev, +Build-Depends: debhelper (>= 9~), libdeflate-dev, libgettextpo-dev, libc6-dev, libjansson-dev, libpng-dev, libcups2-dev, libfreetype6-dev, libfontconfig1-dev, libdbus-1-dev, libicu-dev, cmake, git, xserver-xorg-dev, libxinerama-dev, libxrandr-dev, libxrender-dev, diff --git a/package/freebsd/Makefile b/package/freebsd/Makefile index 8d0e266fa..6980210a7 100644 --- a/package/freebsd/Makefile +++ b/package/freebsd/Makefile @@ -16,6 +16,7 @@ WRKSRC = ${WRKDIR}/katie.git USES = compiler:c++11-lang pkgconfig cmake xorg desktop-file-utils USE_XORG = x11 xinerama xrandr xrender xfixes xcursor xext sm ice USE_LDCONFIG = yes +USE_GETTEXT_TOOLS = build run RUN_DEPENDS = xdg-open:devel/xdg-utils \ ${LOCALBASE}/share/fonts/freefont-ttf/FreeSans.ttf:x11-fonts/freefont-ttf LIB_DEPENDS = libdeflate.so:archivers/libdeflate libicuuc.so:devel/icu \ @@ -28,9 +29,9 @@ CMAKE_ARGS = -DKATIE_TOOLS_SUFFIX="-katie" -Wno-dev OPTIONS_DEFINE = NLS OPTIONS_SUB = yes NLS_DESC = Build translator and translations -NLS_USES = gettext -NLS_CMAKE_ON = -DWITH_INTL=TRUE -NLS_CMAKE_OFF = -DWITH_INTL=FALSE +NLS_USES = gettext-tools +NLS_CMAKE_ON = -DWITH_GETTEXTPO=TRUE +NLS_CMAKE_OFF = -DWITH_GETTEXTPO=FALSE do-extract: @rm -vrf ${WRKSRC} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 06e45ac59..2cab4c09d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -319,14 +319,6 @@ foreach(lib dl rt) endif() endforeach() -if(WITH_INTL AND INTL_FOUND) - set(EXTRA_CORE_LIBS - ${EXTRA_CORE_LIBS} - ${INTL_LIBRARIES} - ) - include_directories(${INTL_INCLUDES}) -endif() - if(WITH_EXECINFO AND EXECINFO_FOUND) set(EXTRA_CORE_LIBS ${EXTRA_CORE_LIBS} diff --git a/src/core/kernel/qtranslator.cpp b/src/core/kernel/qtranslator.cpp index e3dd1310b..92175663b 100644 --- a/src/core/kernel/qtranslator.cpp +++ b/src/core/kernel/qtranslator.cpp @@ -24,22 +24,34 @@ #ifndef QT_NO_TRANSLATION #include "qcoreapplication.h" - -#include +#include "qlibraryinfo.h" +#include "qlocale.h" +#include "qdir.h" +#include "qtextcodec.h" +#include "qdebug.h" QT_BEGIN_NAMESPACE class QTranslatorPrivate { public: - QTranslatorPrivate() { } + QTranslatorPrivate(); QByteArray domain; + mutable QByteArray data; + mutable QTextConverter converter; + quint32 offset; private: Q_DISABLE_COPY(QTranslatorPrivate); }; +QTranslatorPrivate::QTranslatorPrivate() + : offset(0) +{ +} + + /*! \class QTranslator @@ -113,7 +125,6 @@ QTranslator::QTranslator() { } - /*! Destroys the object and frees any allocated resources. */ @@ -126,58 +137,172 @@ QTranslator::~QTranslator() } /*! - - Loads \a filename (with ".mo" as suffix), which is file name relative - to the translations installation directory. Returns true if the - translation is successfully loaded; otherwise returns false. + Loads translation for \a domain and \a locale, \a domain being + filename relative to the translations installation directory + without the suffix and \a locale being either empty or locale of + choice; if empty the system locale will be used. Returns true if + the translation is successfully loaded; otherwise returns false. The previous contents of this translator object are discarded. \sa QLibraryInfo */ -bool QTranslator::load(const QString &domain) +bool QTranslator::load(const QString &domain, const QString &locale) { + Q_D(QTranslator); + d->data.clear(); + d->converter = QTextConverter(); + d->offset = 0; if (domain.isEmpty()) { + qWarning("QTranslator::load: Domain is empty"); return false; } + + QString translationfilepath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + translationfilepath.append(QDir::separator()); + if (locale.isEmpty()) { + translationfilepath.append(QLocale::system().name()); + } else { + translationfilepath.append(locale); + } + translationfilepath.append(QDir::separator()); + translationfilepath.append(domain); + translationfilepath.append(QLatin1String(".tr")); + // qDebug() << Q_FUNC_INFO << translationfilepath; + + QFile translationfile(translationfilepath); + if (!translationfile.open(QFile::ReadOnly)) { + qWarning("QTranslator::load: %s", qPrintable(translationfile.errorString())); + return false; + } + + return loadFromData(translationfile.readAll()); +} + +/*! + Loads translation from \a data. Returns true if the translation + is successfully loaded; otherwise returns false. + + The previous contents of this translator object are discarded. + + \sa QLibraryInfo +*/ +bool QTranslator::loadFromData(const QByteArray &data) +{ Q_D(QTranslator); - d->domain = domain.toLatin1(); - bind_textdomain_codeset(d->domain.constData(), "UTF-8"); + d->data = data; + d->converter = QTextConverter(); + d->offset = 0; + + if (data.isEmpty()) { + qWarning("QTranslator::load: Empty data"); + d->data.clear(); + return false; + } + + QDataStream trheaderstream(&d->data, QIODevice::ReadOnly); + QByteArray trmagic; + QByteArray trcodec; + trheaderstream >> trmagic; + trheaderstream >> trcodec; + + if (trmagic != "KATIE_TRANSLATION") { + qWarning("QTranslator::load: Invalid magic"); + d->data.clear(); + return false; + } + + d->converter = QTextConverter(trcodec); + d->offset = trheaderstream.device()->pos(); return true; } /*! Returns the translation for the key (\a context, \a sourceText). - The text will be translated depending on the system locale. + The text will be translated depending on either the locale specified + when the translation was loaded or the system locale. \sa load() */ QString QTranslator::translate(const char *context, const char *sourceText) const { Q_D(const QTranslator); - if (context) { - // for reference: - // https://github.com/autotools-mirror/gettext/blob/master/gnulib-local/lib/gettext.h - QByteArray msgwithctx(context); - msgwithctx.append('\004'); - msgwithctx.append(sourceText); - const char* result = dgettext(d->domain.constData(), msgwithctx.constData()); - if (result == msgwithctx.constData()) { - return QString::fromUtf8(sourceText); + QDataStream trdatastream(&d->data, QIODevice::ReadOnly); + QIODevice* trdatadevice = trdatastream.device(); + trdatadevice->seek(d->offset); + while (!trdatadevice->atEnd()) { + QByteArray trmsgctxt; + QByteArray trmsgid; + QByteArray trmsgstr; + QByteArray trmsgid_plural; + QByteArray trmsgstr_plural; + trdatastream >> trmsgctxt; + trdatastream >> trmsgid; + trdatastream >> trmsgstr; + trdatastream >> trmsgid_plural; + trdatastream >> trmsgstr_plural; + + // this search method assumes plurals and regular messages are unique strings + if ((!context || trmsgctxt == context) && trmsgid_plural == sourceText) { + d->converter.reset(); + return d->converter.toUnicode(trmsgstr_plural.constData(), trmsgstr_plural.size()); + } + + if ((!context || trmsgctxt == context) && trmsgid == sourceText) { + d->converter.reset(); + return d->converter.toUnicode(trmsgstr.constData(), trmsgstr.size()); } - return QString::fromUtf8(result); } - return QString::fromUtf8(dgettext(d->domain.constData(), sourceText)); + return QString::fromUtf8(sourceText); +} + +/*! + Returns the translation for the key (\a context, \a sourceText). + The text will be translated depending on either the locale specified + when the translation was loaded or the system locale. + + If no translation is found empty string is returned. + + \sa load() +*/ +QString QTranslator::translateStrict(const char *context, const char *sourceText) const +{ + Q_D(const QTranslator); + QDataStream trdatastream(&d->data, QIODevice::ReadOnly); + QIODevice* trdatadevice = trdatastream.device(); + trdatadevice->seek(d->offset); + while (!trdatadevice->atEnd()) { + QByteArray trmsgctxt; + QByteArray trmsgid; + QByteArray trmsgstr; + QByteArray trmsgid_plural; + QByteArray trmsgstr_plural; + trdatastream >> trmsgctxt; + trdatastream >> trmsgid; + trdatastream >> trmsgstr; + trdatastream >> trmsgid_plural; + trdatastream >> trmsgstr_plural; + + if ((!context || trmsgctxt == context) && trmsgid_plural == sourceText) { + d->converter.reset(); + return d->converter.toUnicode(trmsgstr_plural.constData(), trmsgstr_plural.size()); + } + + if ((!context || trmsgctxt == context) && trmsgid == sourceText) { + d->converter.reset(); + return d->converter.toUnicode(trmsgstr.constData(), trmsgstr.size()); + } + } + return QString(); } /*! Returns true if this translator is empty, otherwise returns false. - This function works with stripped and unstripped translation files. */ bool QTranslator::isEmpty() const { Q_D(const QTranslator); - return d->domain.isEmpty(); + return d->data.isEmpty(); } QT_END_NAMESPACE diff --git a/src/core/kernel/qtranslator.h b/src/core/kernel/qtranslator.h index ca27f9cb7..adaeed1b9 100644 --- a/src/core/kernel/qtranslator.h +++ b/src/core/kernel/qtranslator.h @@ -37,10 +37,12 @@ public: virtual ~QTranslator(); virtual QString translate(const char *context, const char *sourceText) const; + virtual QString translateStrict(const char *context, const char *sourceText) const; virtual bool isEmpty() const; - bool load(const QString &domain); + bool load(const QString &domain, const QString &locale = QString()); + bool loadFromData(const QByteArray &data); private: Q_DISABLE_COPY(QTranslator) diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index a7d169782..592d73fb9 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -14,5 +14,8 @@ if(WITH_DBUS AND DBUS_FOUND) add_subdirectory(qdbus) add_subdirectory(qdbusviewer) endif() +if(WITH_GETTEXTPO AND GETTEXTPO_FOUND) + add_subdirectory(trc) +endif() add_subdirectory(qscript) add_subdirectory(qtconfig) diff --git a/src/tools/moc/moc.1 b/src/tools/moc/moc.1 index 3ffb125d6..e62ff5dda 100644 --- a/src/tools/moc/moc.1 +++ b/src/tools/moc/moc.1 @@ -71,7 +71,7 @@ .\" ======================================================================== .\" .IX Title "MOC 1" -.TH MOC 1 "2022-03-04" "Katie 4.12.0" "Katie Manual" +.TH MOC 1 "2022-06-15" "Katie 4.12.0" "Katie Manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/src/tools/qdbus/qdbus.1 b/src/tools/qdbus/qdbus.1 index 9d75cbacd..2a0c1500b 100644 --- a/src/tools/qdbus/qdbus.1 +++ b/src/tools/qdbus/qdbus.1 @@ -71,7 +71,7 @@ .\" ======================================================================== .\" .IX Title "QDBUS 1" -.TH QDBUS 1 "2022-03-04" "Katie 4.12.0" "Katie Manual" +.TH QDBUS 1 "2022-06-15" "Katie 4.12.0" "Katie Manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/src/tools/qdbus/qdbuscpp2xml.1 b/src/tools/qdbus/qdbuscpp2xml.1 index df475b890..df19ee1f4 100644 --- a/src/tools/qdbus/qdbuscpp2xml.1 +++ b/src/tools/qdbus/qdbuscpp2xml.1 @@ -71,7 +71,7 @@ .\" ======================================================================== .\" .IX Title "QDBUSCPP2XML 1" -.TH QDBUSCPP2XML 1 "2022-03-04" "Katie 4.12.0" "Katie Manual" +.TH QDBUSCPP2XML 1 "2022-06-15" "Katie 4.12.0" "Katie Manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/src/tools/qdbus/qdbusxml2cpp.1 b/src/tools/qdbus/qdbusxml2cpp.1 index 8676aaf2f..bf944f3b6 100644 --- a/src/tools/qdbus/qdbusxml2cpp.1 +++ b/src/tools/qdbus/qdbusxml2cpp.1 @@ -71,7 +71,7 @@ .\" ======================================================================== .\" .IX Title "QDBUSXML2CPP 1" -.TH QDBUSXML2CPP 1 "2022-03-04" "Katie 4.12.0" "Katie Manual" +.TH QDBUSXML2CPP 1 "2022-06-15" "Katie 4.12.0" "Katie Manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/src/tools/qscript/qscript.1 b/src/tools/qscript/qscript.1 index 26a314533..525af6c50 100644 --- a/src/tools/qscript/qscript.1 +++ b/src/tools/qscript/qscript.1 @@ -71,7 +71,7 @@ .\" ======================================================================== .\" .IX Title "QSCRIPT 1" -.TH QSCRIPT 1 "2022-03-04" "Katie 4.12.0" "Katie Manual" +.TH QSCRIPT 1 "2022-06-15" "Katie 4.12.0" "Katie Manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/src/tools/trc/CMakeLists.txt b/src/tools/trc/CMakeLists.txt new file mode 100644 index 000000000..a5dd296eb --- /dev/null +++ b/src/tools/trc/CMakeLists.txt @@ -0,0 +1,40 @@ +set(EXTRA_TRC_LIBS + KtCore + ${GETTEXTPO_LIBRARIES} +) + +include_directories( + ${CMAKE_BINARY_DIR}/include + ${CMAKE_BINARY_DIR}/privateinclude + ${CMAKE_BINARY_DIR}/include/QtCore + ${CMAKE_CURRENT_SOURCE_DIR} + ${GETTEXTPO_INCLUDES} +) + +set(TRC_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/trcmain.cpp +) + +katie_setup_target(trc ${TRC_SOURCES}) + +add_executable(trc ${trc_SOURCES}) +target_link_libraries(trc ${EXTRA_TRC_LIBS}) + +set_target_properties(trc PROPERTIES + EXPORT_NAME trc + OUTPUT_NAME trc${KATIE_TOOLS_SUFFIX} +) + +install( + TARGETS trc + EXPORT KatieTargets + RUNTIME DESTINATION ${KATIE_BINARIES_PATH} + COMPONENT Tools +) + +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/trc.1 + DESTINATION ${KATIE_MAN_PATH}/man1 + RENAME trc${KATIE_TOOLS_SUFFIX}.1 + COMPONENT Doc +) diff --git a/src/tools/trc/trc.1 b/src/tools/trc/trc.1 new file mode 100644 index 000000000..8f6c2efd4 --- /dev/null +++ b/src/tools/trc/trc.1 @@ -0,0 +1,109 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "TRC 1" +.TH TRC 1 "2022-11-01" "Katie 4.12.0" "Katie Manual" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +trc \- Katie Translation Compiler +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +trc [options] +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +trc reads a user interface definition (.po) file and creates corresponding +\&.tr file. +.SH "OPTIONS" +.IX Header "OPTIONS" +.Vb 3 +\& \-h +\& \-help +\& Display this help and exit. +\& +\& \-v +\& \-version +\& Display version. +\& +\& \-o +\& Place the output into . +.Ve +.SH "EXIT STATUS" +.IX Header "EXIT STATUS" +trc returns 0 on success and other on unexcepted failure. +.SH "AUTHORS" +.IX Header "AUTHORS" +Copyright (C) 2022 Ivailo Monev +.PP +Licensed through \s-1GNU\s0 Lesser General Public License. diff --git a/src/tools/trc/trc.pod b/src/tools/trc/trc.pod new file mode 100644 index 000000000..22e14f1c9 --- /dev/null +++ b/src/tools/trc/trc.pod @@ -0,0 +1,35 @@ +=head1 NAME + +trc - Katie Translation Compiler + +=head1 SYNOPSIS + +trc [options] + +=head1 DESCRIPTION + +trc reads a user interface definition (.po) file and creates corresponding +.tr file. + +=head1 OPTIONS + + -h + -help + Display this help and exit. + + -v + -version + Display version. + + -o + Place the output into . + +=head1 EXIT STATUS + +trc returns 0 on success and other on unexcepted failure. + +=head1 AUTHORS + +Copyright (C) 2022 Ivailo Monev + +Licensed through GNU Lesser General Public License. diff --git a/src/tools/trc/trcmain.cpp b/src/tools/trc/trcmain.cpp new file mode 100644 index 000000000..edfe8f297 --- /dev/null +++ b/src/tools/trc/trcmain.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Ivailo Monev +** +** This file is part of the tools applications of the Katie Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static void gettext_xerror(int severity, + po_message_t message, + const char *filename, size_t lineno, size_t column, + int multiline_p, const char *message_text) +{ + switch (severity) { + case PO_SEVERITY_WARNING: { + qWarning("%s", message_text); + break; + } + case PO_SEVERITY_ERROR: { + qCritical("%s", message_text); + break; + } + case PO_SEVERITY_FATAL_ERROR: { + qFatal("%s", message_text); + break; + } + default: { + Q_ASSERT(false); + break; + } + } +} + +static void gettext_xerror2(int severity, + po_message_t message1, + const char *filename1, size_t lineno1, size_t column1, + int multiline_p1, const char *message_text1, + po_message_t message2, + const char *filename2, size_t lineno2, size_t column2, + int multiline_p2, const char *message_text2) +{ + switch (severity) { + case PO_SEVERITY_WARNING: { + qWarning("%s, %s", message_text1, message_text2); + break; + } + case PO_SEVERITY_ERROR: { + qCritical("%s, %s", message_text1, message_text2); + break; + } + case PO_SEVERITY_FATAL_ERROR: { + qFatal("%s, %s", message_text1, message_text2); + break; + } + default: { + Q_ASSERT(false); + break; + } + } +} + +static const struct po_xerror_handler gettext_handler +{ + gettext_xerror, + gettext_xerror2 +}; + +void showHelp() +{ + fprintf(stderr, "Usage:\n" + " trc [options] \n\n" + "Options:\n" + " -h\n" + " -help\n" + " Display this help and exit.\n\n" + " -v\n" + " -version\n" + " Display version.\n\n" + " -o \n" + " Place the output into .\n"); +} + +int runTrc(int argc, char *argv[]) +{ + QByteArray inputfilepath; + QString outputfilepath; + + int arg = 1; + while (arg < argc) { + QString opt = QString::fromLocal8Bit(argv[arg]); + if (opt == QLatin1String("-h") || opt == QLatin1String("-help")) { + showHelp(); + return 0; + } else if (opt == QLatin1String("-v") || opt == QLatin1String("-version")) { + fprintf(stderr, "Katie Translation Compiler version %s\n", QT_VERSION_STR); + return 0; + } else if (opt == QLatin1String("-o") || opt == QLatin1String("-output")) { + ++arg; + if (!argv[arg]) { + showHelp(); + return 1; + } + outputfilepath = QFile::decodeName(argv[arg]); + } else if (inputfilepath.isEmpty()) { + inputfilepath = argv[arg]; + } else { + showHelp(); + return 1; + } + + ++arg; + } + + if (outputfilepath.isEmpty()) { + outputfilepath = inputfilepath; + if (outputfilepath.endsWith(QLatin1String(".po"))) { + outputfilepath.chop(3); + } + outputfilepath.append(QLatin1String(".tr")); + } + + if (inputfilepath.isEmpty()) { + showHelp(); + return 1; + } + + QFile outputfile(outputfilepath); + if (!outputfile.open(QFile::WriteOnly)) { + fprintf(stderr, "trc: Coult not open output %s\n", qPrintable(outputfilepath)); + return 1; + } + + po_file_t gettext_file = po_file_read( + inputfilepath.constData(), + &gettext_handler + ); + if (gettext_file == NULL) { + fprintf(stderr, "trc: Coult not open input %s\n", inputfilepath.constData()); + return 1; + } + + const char* gettext_domain_header = po_file_domain_header(gettext_file, NULL); + + QByteArray trdata; + QDataStream trdatastream(&trdata, QIODevice::WriteOnly); + po_message_iterator_t gettext_iterator = po_message_iterator(gettext_file, NULL); + po_message_t gettext_message = po_next_message(gettext_iterator); + while (gettext_message != NULL) { + if (po_message_is_obsolete(gettext_message) || po_message_is_fuzzy(gettext_message)) { + gettext_message = po_next_message(gettext_iterator); + continue; + } + + const QByteArray po_msgctxt = po_message_msgctxt(gettext_message); + const QByteArray po_msgid = po_message_msgid(gettext_message); + const QByteArray po_msgstr = po_message_msgstr(gettext_message); + if (po_msgstr == gettext_domain_header) { + gettext_message = po_next_message(gettext_iterator); + continue; + } + const QByteArray po_msgid_plural = po_message_msgid_plural(gettext_message); + const QByteArray po_msgstr_plural = po_message_msgstr_plural(gettext_message, 1); + + trdatastream << po_msgctxt; + trdatastream << po_msgid; + trdatastream << po_msgstr; + trdatastream << po_msgid_plural; + trdatastream << po_msgstr_plural; + + gettext_message = po_next_message(gettext_iterator); + } + po_message_iterator_free(gettext_iterator); + + QByteArray trheaderdata; + QDataStream trheaderstream(&trheaderdata, QIODevice::WriteOnly); + trheaderstream << QByteArray("KATIE_TRANSLATION"); + // both Katie and Katana assume that the strings are UTF-8 + trheaderstream << QByteArray("UTF-8"); + + if (outputfile.write(trheaderdata.constData(), trheaderdata.size()) != trheaderdata.size()) { + fprintf(stderr, "trc: Coult not write header %s\n", qPrintable(outputfile.errorString())); + po_file_free(gettext_file); + return 1; + } + + if (outputfile.write(trdata.constData(), trdata.size()) != trdata.size()) { + fprintf(stderr, "trc: Coult not write data %s\n", qPrintable(outputfile.errorString())); + po_file_free(gettext_file); + return 1; + } + + po_file_free(gettext_file); + + return 0; +} + +QT_END_NAMESPACE + +int main(int argc, char *argv[]) +{ + return QT_PREPEND_NAMESPACE(runTrc)(argc, argv); +} diff --git a/src/tools/uic/uic.1 b/src/tools/uic/uic.1 index a991bfedb..40c52c094 100644 --- a/src/tools/uic/uic.1 +++ b/src/tools/uic/uic.1 @@ -71,7 +71,7 @@ .\" ======================================================================== .\" .IX Title "UIC 1" -.TH UIC 1 "2022-03-04" "Katie 4.12.0" "Katie Manual" +.TH UIC 1 "2022-06-15" "Katie 4.12.0" "Katie Manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/tests/auto/qtranslator/CMakeLists.txt b/tests/auto/qtranslator/CMakeLists.txt new file mode 100644 index 000000000..69b44c9bb --- /dev/null +++ b/tests/auto/qtranslator/CMakeLists.txt @@ -0,0 +1,3 @@ +katie_test(tst_qtranslator + ${CMAKE_CURRENT_SOURCE_DIR}/tst_qtranslator.cpp +) diff --git a/tests/auto/qtranslator/test.po b/tests/auto/qtranslator/test.po new file mode 100644 index 000000000..903bd8fe8 --- /dev/null +++ b/tests/auto/qtranslator/test.po @@ -0,0 +1,25 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-10-29 10:06+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: bg\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "foo" +msgstr "фоо" + +msgctxt "foo" +msgid "bar" +msgstr "фообар" \ No newline at end of file diff --git a/tests/auto/qtranslator/test.tr b/tests/auto/qtranslator/test.tr new file mode 100644 index 000000000..1ef3f1b9c Binary files /dev/null and b/tests/auto/qtranslator/test.tr differ diff --git a/tests/auto/qtranslator/tst_qtranslator.cpp b/tests/auto/qtranslator/tst_qtranslator.cpp new file mode 100644 index 000000000..0694edff4 --- /dev/null +++ b/tests/auto/qtranslator/tst_qtranslator.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Ivailo Monev +** +** This file is part of the test suite of the Katie Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +//TESTED_CLASS=QTranslator +//TESTED_FILES=qtranslator.cpp,qtranslator.h + +#ifndef QT_NO_TRANSLATION +class tst_QTranslator : public QObject +{ + Q_OBJECT + +public slots: + void init(); + void cleanup(); + +private slots: + void fromdata(); +}; + +void tst_QTranslator::init() +{ +} + +void tst_QTranslator::cleanup() +{ +} + +void tst_QTranslator::fromdata() +{ + const QString trfilepath = QFile::encodeName(SRCDIR "/test.tr"); + QFile trfile(trfilepath); + QVERIFY(trfile.open(QFile::ReadOnly)); + QTranslator translator; + QVERIFY(translator.loadFromData(trfile.readAll())); + QCOMPARE(translator.translate("", "foo"), QString::fromUtf8("фоо")); + QCOMPARE(translator.translate("foo", "bar"), QString::fromUtf8("фообар")); +} + +QTEST_MAIN(tst_QTranslator) + +#include "moc_tst_qtranslator.cpp" + +#else // QT_NO_TRANSLATION + +QTEST_NOOP_MAIN + +#endif // QT_NO_TRANSLATION diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index 1eac1df22..02194083d 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -4,7 +4,7 @@ foreach(tmpres ${TRANSLATIONS}) get_filename_component(resource "${tmpres}" ABSOLUTE) get_filename_component(rscname "${resource}" NAME_WE) make_directory("${CMAKE_CURRENT_BINARY_DIR}") - set(rscout "${CMAKE_CURRENT_BINARY_DIR}/${rscname}.mo") + set(rscout "${CMAKE_CURRENT_BINARY_DIR}/${rscname}.tr") if("${rscname}" MATCHES "qt_tools") set(rscbase "qt_tools") string(REPLACE "qt_tools_" "" rsclocale "${rscname}") @@ -14,14 +14,15 @@ foreach(tmpres ${TRANSLATIONS}) endif() add_custom_target( translations_${rscname} ALL - COMMAND ${INTL_MSGFMT} -v "${resource}" -o "${rscout}" - COMMENT "Generating ${rscname}.mo" + COMMAND "${CMAKE_BINARY_DIR}/exec.sh" "${CMAKE_BINARY_DIR}/bin/trc${KATIE_TOOLS_SUFFIX}" "${resource}" -o "${rscout}" + DEPENDS "trc" + COMMENT "Generating ${rscname}.tr" ) set_source_files_properties("${rscout}" PROPERTIES GENERATED TRUE) install( FILES "${rscout}" - DESTINATION "${KATIE_TRANSLATIONS_PATH}/${rsclocale}/LC_MESSAGES" - RENAME "${rscbase}.mo" + DESTINATION "${KATIE_TRANSLATIONS_PATH}/${rsclocale}" + RENAME "${rscbase}.tr" COMPONENT Runtime ) endforeach()