use custom format for translations

the code for translating strings in kdelibs (see
kdelibs/kdecore/localization/kcatalog.cpp) has finally made me roll my
own - locking global mutex, saving and restoring environment variables
on every string translation is not very good, is it?

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-11-02 04:26:43 +02:00
parent ab43571cc1
commit d0742df56c
25 changed files with 717 additions and 107 deletions

View file

@ -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()

1
README
View file

@ -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

View file

@ -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 <xakepa10@gmail.com>
#
# 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)

View file

@ -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 <xakepa10@gmail.com>
#
# 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)

View file

@ -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,

View file

@ -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}

View file

@ -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}

View file

@ -24,22 +24,34 @@
#ifndef QT_NO_TRANSLATION
#include "qcoreapplication.h"
#include <libintl.h>
#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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
)

109
src/tools/trc/trc.1 Normal file
View file

@ -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] <translation\-file>
.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 <file>
\& Place the output into <file>.
.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.

35
src/tools/trc/trc.pod Normal file
View file

@ -0,0 +1,35 @@
=head1 NAME
trc - Katie Translation Compiler
=head1 SYNOPSIS
trc [options] <translation-file>
=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 <file>
Place the output into <file>.
=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.

222
src/tools/trc/trcmain.cpp Normal file
View file

@ -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 <QFile>
#include <QTextCodec>
#include <QDebug>
#include <gettext-po.h>
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] <translation-file>\n\n"
"Options:\n"
" -h\n"
" -help\n"
" Display this help and exit.\n\n"
" -v\n"
" -version\n"
" Display version.\n\n"
" -o <file>\n"
" Place the output into <file>.\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);
}

View file

@ -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

View file

@ -0,0 +1,3 @@
katie_test(tst_qtranslator
${CMAKE_CURRENT_SOURCE_DIR}/tst_qtranslator.cpp
)

View file

@ -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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 "фообар"

Binary file not shown.

View file

@ -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 <QtTest/QtTest>
#include <QtCore/QtCore>
//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

View file

@ -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()