mirror of
https://bitbucket.org/smil3y/katie.git
synced 2025-02-23 10:22:55 +00:00
reimplement QTranslator via gettext/libintl
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
1e006a2c35
commit
36c364fbe9
128 changed files with 118376 additions and 213131 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -34,3 +34,6 @@ package/debian/katie-*/
|
|||
# tarballs from tarball.sh and oldmake.sh
|
||||
*.tar.xz
|
||||
scripts/cmake-*.tar.gz
|
||||
|
||||
# temporary files used to update the translations
|
||||
translations/*.pot
|
|
@ -80,7 +80,7 @@ endif()
|
|||
# used by the the designer tool. if it is ever needed for something it can just be added here
|
||||
# and that's it - the rest of the build system has everything in place.
|
||||
set(KATIE_COMPONENTS "Core Gui Declarative Designer Network Sql Svg Xml Script ScriptTools Test UiTools")
|
||||
set(KATIE_TOOLS "moc uic rcc lupdate lrelease lconvert designer qscript qtconfig")
|
||||
set(KATIE_TOOLS "moc uic rcc designer qscript qtconfig")
|
||||
|
||||
# for config.cpp.cmake
|
||||
string(TIMESTAMP KATIE_DATE "%Y-%m-%d")
|
||||
|
@ -95,7 +95,7 @@ set(KATIE_BINARIES_PATH "${CMAKE_INSTALL_FULL_BINDIR}" CACHE PATH "Binaries inst
|
|||
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_DATA_PATH "${CMAKE_INSTALL_FULL_DATADIR}" CACHE PATH "Generic data installation path")
|
||||
set(KATIE_TRANSLATIONS_PATH "${KATIE_DATA_PATH}/katie/translations" CACHE PATH "Translations installation path")
|
||||
set(KATIE_TRANSLATIONS_PATH "${CMAKE_INSTALL_FULL_LOCALEDIR}" 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")
|
||||
|
@ -137,6 +137,9 @@ add_feature_info(mysql WITH_MYSQL "build MySQL support")
|
|||
option(WITH_RESOLV "Build resolv support" ON)
|
||||
add_feature_info(resolv WITH_RESOLV "build resolv support")
|
||||
|
||||
option(WITH_INTL "Build Intl support" ON)
|
||||
add_feature_info(intl WITH_INTL "build Intl support")
|
||||
|
||||
option(WITH_UNWIND "Build Unwind support" OFF)
|
||||
add_feature_info(unwind WITH_UNWIND "build Unwind support")
|
||||
|
||||
|
@ -330,6 +333,14 @@ set_package_properties(MySQL PROPERTIES
|
|||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
find_package(Intl)
|
||||
set_package_properties(Intl PROPERTIES
|
||||
PURPOSE "Required for translations support"
|
||||
DESCRIPTION "GNU gettext runtime library"
|
||||
URL "https://www.gnu.org/software/gettext/gettext.html"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
find_package(Unwind)
|
||||
set_package_properties(Unwind PROPERTIES
|
||||
PURPOSE "Required for stack traces on assert and crash"
|
||||
|
@ -472,6 +483,10 @@ if(NOT WITH_TIFF OR NOT TIFF_FOUND)
|
|||
katie_definition(-DQT_NO_IMAGEFORMAT_TIFF)
|
||||
endif()
|
||||
|
||||
if(NOT WITH_INTL OR NOT INTL_FOUND)
|
||||
katie_definition(-DQT_NO_TRANSLATION)
|
||||
endif()
|
||||
|
||||
if(NOT WITH_UNWIND OR NOT UNWIND_FOUND OR NOT HAVE_CXXABI)
|
||||
katie_definition(-DQT_NO_UNWIND)
|
||||
endif()
|
||||
|
|
2
README
2
README
|
@ -16,7 +16,7 @@ There are several things you should be aware before considering Katie:
|
|||
required for building
|
||||
- OpenSSL, D-Bus, CUPS and resolv must be linked to during build
|
||||
- QtUiTools is build as shared library by default
|
||||
- moc, uic, rcc, lrelease, lupdate, etc. are linked to components
|
||||
- moc, uic, rcc, etc. are linked to components
|
||||
- updated bundled Harfbuzz from upstream (not Harfbuzz-NG)
|
||||
- updated bundled FFTW from upstream
|
||||
- versioned and extended classes have been merged into the base class,
|
||||
|
|
49
cmake/modules/FindIntl.cmake
Normal file
49
cmake/modules/FindIntl.cmake
Normal file
|
@ -0,0 +1,49 @@
|
|||
# - 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-2020, 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
|
||||
|
||||
set(INTL_NAMES c intl)
|
||||
|
||||
find_path(INTL_INCLUDES
|
||||
NAMES libintl.h
|
||||
HINTS $ENV{INTLDIR}/include
|
||||
)
|
||||
|
||||
set(INTL_LIBRARIES)
|
||||
foreach(name ${INTL_NAMES})
|
||||
unset(HAVE_bind_textdomain_codeset CACHE)
|
||||
if(NOT INTL_LIBRARIES)
|
||||
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
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Intl
|
||||
REQUIRED_VARS INTL_LIBRARIES INTL_INCLUDES INTL_MSGFMT
|
||||
)
|
||||
|
||||
mark_as_advanced(INTL_INCLUDES INTL_LIBRARIES INTL_MSGFMT)
|
|
@ -4,7 +4,6 @@
|
|||
set(KATIE_UIC "uic")
|
||||
set(KATIE_RCC "rcc")
|
||||
set(KATIE_MOC "bootstrap_moc")
|
||||
set(KATIE_LRELEASE "lrelease")
|
||||
|
||||
include(CMakePushCheckState)
|
||||
include(CheckStructHasMember)
|
||||
|
@ -258,18 +257,27 @@ function(KATIE_SETUP_TARGET FORTARGET)
|
|||
OUTPUT "${rscout}"
|
||||
)
|
||||
endif()
|
||||
elseif("${rscext}" MATCHES ".ts")
|
||||
elseif("${rscext}" MATCHES ".po" AND WITH_INTL AND INTL_FOUND)
|
||||
make_directory("${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(rscout "${CMAKE_CURRENT_BINARY_DIR}/${rscname}.qm")
|
||||
set(rscout "${CMAKE_CURRENT_BINARY_DIR}/${rscname}.mo")
|
||||
if(rscout MATCHES "qt_tools")
|
||||
set(rsbase "qt_tools")
|
||||
string(REPLACE "qt_tools_" "" rslocale "${rscname}")
|
||||
else()
|
||||
set(rsbase "qt")
|
||||
string(REPLACE "qt_" "" rslocale "${rscname}")
|
||||
endif()
|
||||
add_custom_target(
|
||||
${FORTARGET}_${rscname} ALL
|
||||
COMMAND "${CMAKE_BINARY_DIR}/exec.sh" "${CMAKE_BINARY_DIR}/bin/${KATIE_LRELEASE}${KATIE_TOOLS_SUFFIX}" "${resource}" -qm "${rscout}"
|
||||
COMMAND ${INTL_MSGFMT} -v "${resource}" -o "${rscout}"
|
||||
DEPENDS "${KATIE_LRELEASE}"
|
||||
COMMENT "Generating ${rscname}.mo"
|
||||
)
|
||||
set_source_files_properties("${rscout}" PROPERTIES GENERATED TRUE)
|
||||
install(
|
||||
FILES "${rscout}"
|
||||
DESTINATION "${KATIE_TRANSLATIONS_PATH}"
|
||||
DESTINATION "${KATIE_TRANSLATIONS_PATH}/${rslocale}/LC_MESSAGES"
|
||||
RENAME "${rsbase}.mo"
|
||||
COMPONENT Runtime
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -80,25 +80,6 @@ macro(KATIE_DBUS_INTERFACE SRCIN)
|
|||
set_property(SOURCE "${SRCIN}" APPEND PROPERTY OBJECT_DEPENDS "${rscout}")
|
||||
endmacro()
|
||||
|
||||
macro(KATIE_TRANSLATIONS TRANSLATIONS)
|
||||
if(NOT KATIE_TRANSLATIONS_PATH)
|
||||
message(SEND_ERROR "Directory where the translation should be installed is not set")
|
||||
endif()
|
||||
foreach(translation ${TRANSLATIONS} ${ARGN})
|
||||
get_filename_component(trname "${translation}" NAME_WE)
|
||||
get_filename_component(trdir "${translation}" DIRECTORY)
|
||||
string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" trdir ${trdir})
|
||||
make_directory("${trdir}")
|
||||
set(trout "${trdir}/${trname}.qm")
|
||||
add_custom_target(
|
||||
${trname}_translation ALL
|
||||
COMMAND "${KATIE_LRELEASE}" "${translation}" -qm "${trout}"
|
||||
)
|
||||
set_source_files_properties(${trout} PROPERTIES GENERATED TRUE)
|
||||
install(FILES "${trout}" DESTINATION "${KATIE_TRANSLATIONS_PATH}")
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
macro(KATIE_DEFINITION DEF)
|
||||
set(KATIE_DEFINITIONS ${KATIE_DEFINITIONS} ${DEF} ${ARGN})
|
||||
add_definitions(${DEF} ${ARGN})
|
||||
|
|
|
@ -27,8 +27,8 @@ Section: x11
|
|||
Depends: ${shlibs:Depends}, ${misc:Depends}, katie-runtime
|
||||
Description: C++ toolkit derived from the Qt 4.8 framework
|
||||
This package is pre-release version of the Katie tookit (includes designer,
|
||||
lconvert, lrelease, lupdate, moc, qdbus, qdbuscpp2xml, qdbusviewer,
|
||||
qdbusxml2cpp, qscript, qtconfig, rcc and uic)
|
||||
moc, qdbus, qdbuscpp2xml, qdbusviewer, qdbusxml2cpp, qscript, qtconfig, rcc
|
||||
and uic)
|
||||
|
||||
Package: katie-dev
|
||||
Architecture: all
|
||||
|
|
|
@ -16,7 +16,7 @@ DISTFILES = master.tar.gz
|
|||
DIST_SUBDIR = ${PORTNAME}
|
||||
WRKSRC = ${WRKDIR}/katie-master
|
||||
|
||||
USES = compiler:c++11-lang pkgconfig cmake ssl xorg sqlite:3 desktop-file-utils
|
||||
USES = compiler:c++11-lang pkgconfig cmake gettext ssl xorg sqlite:3 desktop-file-utils
|
||||
USE_XORG = xinerama xrandr xrender xfixes xcursor xext sm ice
|
||||
USE_LDCONFIG = yes
|
||||
LIB_DEPENDS = libzstd.so:archivers/zstd libicuuc.so:devel/icu libicui18n.so:devel/icu \
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
bin/designer-katie
|
||||
bin/lconvert-katie
|
||||
bin/lrelease-katie
|
||||
bin/lupdate-katie
|
||||
bin/moc-katie
|
||||
bin/qdbus-katie
|
||||
bin/qdbuscpp2xml-katie
|
||||
|
@ -1556,9 +1553,6 @@ libdata/pkgconfig/KtSvg.pc
|
|||
libdata/pkgconfig/KtTest.pc
|
||||
libdata/pkgconfig/KtUiTools.pc
|
||||
libdata/pkgconfig/KtXml.pc
|
||||
man/man1/lconvert-katie.1.gz
|
||||
man/man1/lrelease-katie.1.gz
|
||||
man/man1/lupdate-katie.1.gz
|
||||
man/man1/moc-katie.1.gz
|
||||
man/man1/qdbus-katie.1.gz
|
||||
man/man1/qdbuscpp2xml-katie.1.gz
|
||||
|
@ -1569,43 +1563,6 @@ man/man1/uic-katie.1.gz
|
|||
share/applications/designer-katie.desktop
|
||||
share/applications/qdbusviewer-katie.desktop
|
||||
share/applications/qtconfig-katie.desktop
|
||||
%%DATADIR%%/translations/qt_ar.qm
|
||||
%%DATADIR%%/translations/qt_cs.qm
|
||||
%%DATADIR%%/translations/qt_da.qm
|
||||
%%DATADIR%%/translations/qt_de.qm
|
||||
%%DATADIR%%/translations/qt_es.qm
|
||||
%%DATADIR%%/translations/qt_eu.qm
|
||||
%%DATADIR%%/translations/qt_fa.qm
|
||||
%%DATADIR%%/translations/qt_fr.qm
|
||||
%%DATADIR%%/translations/qt_gl.qm
|
||||
%%DATADIR%%/translations/qt_he.qm
|
||||
%%DATADIR%%/translations/qt_hu.qm
|
||||
%%DATADIR%%/translations/qt_ja.qm
|
||||
%%DATADIR%%/translations/qt_ko.qm
|
||||
%%DATADIR%%/translations/qt_lt.qm
|
||||
%%DATADIR%%/translations/qt_pl.qm
|
||||
%%DATADIR%%/translations/qt_pt.qm
|
||||
%%DATADIR%%/translations/qt_ru.qm
|
||||
%%DATADIR%%/translations/qt_sk.qm
|
||||
%%DATADIR%%/translations/qt_sl.qm
|
||||
%%DATADIR%%/translations/qt_sv.qm
|
||||
%%DATADIR%%/translations/qt_tools_CN.qm
|
||||
%%DATADIR%%/translations/qt_tools_TW.qm
|
||||
%%DATADIR%%/translations/qt_tools_cs.qm
|
||||
%%DATADIR%%/translations/qt_tools_de.qm
|
||||
%%DATADIR%%/translations/qt_tools_eu.qm
|
||||
%%DATADIR%%/translations/qt_tools_fr.qm
|
||||
%%DATADIR%%/translations/qt_tools_he.qm
|
||||
%%DATADIR%%/translations/qt_tools_hu.qm
|
||||
%%DATADIR%%/translations/qt_tools_ja.qm
|
||||
%%DATADIR%%/translations/qt_tools_ko.qm
|
||||
%%DATADIR%%/translations/qt_tools_pl.qm
|
||||
%%DATADIR%%/translations/qt_tools_ru.qm
|
||||
%%DATADIR%%/translations/qt_tools_sl.qm
|
||||
%%DATADIR%%/translations/qt_tools_uk.qm
|
||||
%%DATADIR%%/translations/qt_uk.qm
|
||||
%%DATADIR%%/translations/qt_zh_CN.qm
|
||||
%%DATADIR%%/translations/qt_zh_TW.qm
|
||||
share/pixmaps/designer-katie.png
|
||||
share/pixmaps/qdbusviewer-katie.png
|
||||
share/pixmaps/qtconfig-katie.png
|
||||
|
|
|
@ -21,11 +21,25 @@ for c in components:
|
|||
for t in glob.glob('src/tools/*'):
|
||||
tfiles.extend(list_files(t))
|
||||
|
||||
for t in glob.glob('translations/qt*.ts'):
|
||||
command = ['minsize/bin/lupdate', '-locations', 'relative', '-no-ui-lines', '-no-obsolete']
|
||||
if not 'tools' in t:
|
||||
command.extend(cfiles)
|
||||
else:
|
||||
command.extend(tfiles)
|
||||
command.extend(['-ts', t])
|
||||
subprocess.check_call(command)
|
||||
qtcommand = ['xgettext', '--from-code=UTF-8', '-o', 'translations/qt.pot',
|
||||
'-kQT_TR_NOOP:1', '-kQT_TR_NOOP_UTF8:1',
|
||||
'-kQT_TRANSLATE_NOOP:1c,2','-kQT_TRANSLATE_NOOP_UTF8:1c,2',
|
||||
'-kQT_TRANSLATE_NOOP3:1c,2,3', '-kQT_TRANSLATE_NOOP3_UTF8:1c,2,3',
|
||||
'-ktranslate', '-ktr', '-ktrUtf8']
|
||||
qtcommand.extend(cfiles)
|
||||
subprocess.check_call(qtcommand)
|
||||
|
||||
qttoolscommand = ['xgettext', '--from-code=UTF-8', '-o', 'translations/qt_tools.pot',
|
||||
'-kQT_TR_NOOP:1', '-kQT_TR_NOOP_UTF8:1',
|
||||
'-kQT_TRANSLATE_NOOP:1c,2','-kQT_TRANSLATE_NOOP_UTF8:1c,2',
|
||||
'-kQT_TRANSLATE_NOOP3:1c,2,3', '-kQT_TRANSLATE_NOOP3_UTF8:1c,2,3',
|
||||
'-ktranslate', '-ktr', '-ktrUtf8']
|
||||
qttoolscommand.extend(tfiles)
|
||||
subprocess.check_call(qttoolscommand)
|
||||
|
||||
for t in glob.glob('translations/*.po'):
|
||||
potfile = 'translations/qt.pot'
|
||||
if t.startswith('qt_tools'):
|
||||
potfile = 'translations/qt_tools.pot'
|
||||
subprocess.check_call(['msgmerge', '--verbose', '--update', t, potfile])
|
||||
subprocess.check_call(['msgattrib', '--no-fuzzy', '--no-obsolete', t, '-o', t])
|
||||
|
|
|
@ -216,7 +216,6 @@ set(CORE_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qsocketnotifier.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qtimer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qtranslator.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qtranslator_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qvariant.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qabstracteventdispatcher_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qcoreapplication_p.h
|
||||
|
@ -463,6 +462,13 @@ elseif(KATIE_PLATFORM STREQUAL "hurd")
|
|||
)
|
||||
endif()
|
||||
|
||||
if(WITH_INTL AND INTL_FOUND)
|
||||
set(EXTRA_CORE_LIBS
|
||||
${EXTRA_CORE_LIBS}
|
||||
${INTL_LIBRARIES}
|
||||
)
|
||||
include_directories(${INTL_INCLUDES})
|
||||
endif()
|
||||
|
||||
if(WITH_UNWIND AND UNWIND_FOUND AND HAVE_CXXABI)
|
||||
set(EXTRA_CORE_LIBS
|
||||
|
|
|
@ -1615,11 +1615,11 @@ int qrand()
|
|||
|
||||
\snippet doc/src/snippets/code/src_corelib_global_qglobal.cpp 34
|
||||
|
||||
The macro QT_TR_NOOP_UTF8() is identical except that it tells lupdate
|
||||
The macro QT_TR_NOOP_UTF8() is identical except that it notes
|
||||
that the source string is encoded in UTF-8. Corresponding variants
|
||||
exist in the QT_TRANSLATE_NOOP() family of macros, too. Note that
|
||||
using these macros is not required if \c CODECFORTR is already set to
|
||||
UTF-8 in the qmake project file.
|
||||
using these macros is not required if UTF-8 codec is used for
|
||||
translations.
|
||||
|
||||
\sa QT_TRANSLATE_NOOP(), {Internationalization with Qt}
|
||||
*/
|
||||
|
|
|
@ -31,200 +31,25 @@
|
|||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qplatformdefs.h"
|
||||
|
||||
#include "qtranslator.h"
|
||||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
|
||||
#include "qfileinfo.h"
|
||||
#include "qstring.h"
|
||||
#include "qstringlist.h"
|
||||
#include "qcoreapplication.h"
|
||||
#include "qcoreapplication_p.h"
|
||||
#include "qdatastream.h"
|
||||
#include "qdir.h"
|
||||
#include "qfile.h"
|
||||
#include "qmap.h"
|
||||
#include "qalgorithms.h"
|
||||
#include "qhash.h"
|
||||
#include "qtranslator_p.h"
|
||||
#include "qlocale.h"
|
||||
#include "qresource.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "qobject_p.h"
|
||||
|
||||
#include <libintl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum Tag { Tag_End = 1, Tag_SourceText16, Tag_Translation, Tag_Context16, Tag_Obsolete1,
|
||||
Tag_SourceText, Tag_Context, Tag_Comment, Tag_Obsolete2 };
|
||||
/*
|
||||
$ mcookie
|
||||
3cb86418caef9c95cd211cbf60a1bddd
|
||||
$
|
||||
*/
|
||||
|
||||
// magic number for the file
|
||||
static const int MagicLength = 16;
|
||||
static const uchar magic[MagicLength] = {
|
||||
0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
|
||||
0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
|
||||
};
|
||||
|
||||
static bool match(const uchar* found, const char* target, uint len)
|
||||
{
|
||||
// catch the case if \a found has a zero-terminating symbol and \a len includes it.
|
||||
// (normalize it to be without the zero-terminating symbol)
|
||||
if (len > 0 && found[len-1] == '\0')
|
||||
--len;
|
||||
return (memcmp(found, target, len) == 0 && target[len] == '\0');
|
||||
}
|
||||
|
||||
static uint elfHash(const char *name)
|
||||
{
|
||||
const uchar *k;
|
||||
uint h = 0;
|
||||
uint g;
|
||||
|
||||
if (name) {
|
||||
k = (const uchar *) name;
|
||||
while (*k) {
|
||||
h = (h << 4) + *k++;
|
||||
if ((g = (h & 0xf0000000)) != 0)
|
||||
h ^= g >> 24;
|
||||
h &= ~g;
|
||||
}
|
||||
}
|
||||
if (!h)
|
||||
h = 1;
|
||||
return h;
|
||||
}
|
||||
|
||||
static int numerusHelper(int n, const uchar *rules, int rulesSize)
|
||||
{
|
||||
#define CHECK_RANGE \
|
||||
do { \
|
||||
if (i >= rulesSize) \
|
||||
return -1; \
|
||||
} while (0)
|
||||
|
||||
int result = 0;
|
||||
int i = 0;
|
||||
|
||||
if (rulesSize == 0)
|
||||
return 0;
|
||||
|
||||
for (;;) {
|
||||
bool orExprTruthValue = false;
|
||||
|
||||
for (;;) {
|
||||
bool andExprTruthValue = true;
|
||||
|
||||
for (;;) {
|
||||
bool truthValue = true;
|
||||
|
||||
CHECK_RANGE;
|
||||
int opcode = rules[i++];
|
||||
|
||||
int leftOperand = n;
|
||||
if (opcode & Q_MOD_10) {
|
||||
leftOperand %= 10;
|
||||
} else if (opcode & Q_MOD_100) {
|
||||
leftOperand %= 100;
|
||||
} else if (opcode & Q_LEAD_1000) {
|
||||
while (leftOperand >= 1000)
|
||||
leftOperand /= 1000;
|
||||
}
|
||||
|
||||
int op = opcode & Q_OP_MASK;
|
||||
|
||||
CHECK_RANGE;
|
||||
int rightOperand = rules[i++];
|
||||
|
||||
switch (op) {
|
||||
default:
|
||||
return -1;
|
||||
case Q_EQ:
|
||||
truthValue = (leftOperand == rightOperand);
|
||||
break;
|
||||
case Q_LT:
|
||||
truthValue = (leftOperand < rightOperand);
|
||||
break;
|
||||
case Q_LEQ:
|
||||
truthValue = (leftOperand <= rightOperand);
|
||||
break;
|
||||
case Q_BETWEEN:
|
||||
int bottom = rightOperand;
|
||||
CHECK_RANGE;
|
||||
int top = rules[i++];
|
||||
truthValue = (leftOperand >= bottom && leftOperand <= top);
|
||||
}
|
||||
|
||||
if (opcode & Q_NOT)
|
||||
truthValue = !truthValue;
|
||||
|
||||
andExprTruthValue = andExprTruthValue && truthValue;
|
||||
|
||||
if (i == rulesSize || rules[i] != Q_AND)
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
orExprTruthValue = orExprTruthValue || andExprTruthValue;
|
||||
|
||||
if (i == rulesSize || rules[i] != Q_OR)
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (orExprTruthValue)
|
||||
return result;
|
||||
|
||||
++result;
|
||||
|
||||
if (i == rulesSize)
|
||||
return result;
|
||||
|
||||
if (rules[i++] != Q_NEWRULE)
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
class QTranslatorPrivate : public QObjectPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QTranslator)
|
||||
public:
|
||||
enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 };
|
||||
|
||||
QTranslatorPrivate()
|
||||
: dataPointer(0), dataLength(0), resource(0),
|
||||
messageArray(0), offsetArray(0), contextArray(0), numerusRulesArray(0),
|
||||
messageLength(0), offsetLength(0), contextLength(0), numerusRulesLength(0) {}
|
||||
QTranslatorPrivate() : didload(false) {}
|
||||
|
||||
uchar *dataPointer;
|
||||
unsigned int dataLength;
|
||||
|
||||
// The resource object in case we loaded the translations from a resource
|
||||
QResource *resource;
|
||||
|
||||
// for squeezed but non-file data, this is what needs to be deleted
|
||||
const uchar *messageArray;
|
||||
const uchar *offsetArray;
|
||||
const uchar *contextArray;
|
||||
const uchar *numerusRulesArray;
|
||||
uint messageLength;
|
||||
uint offsetLength;
|
||||
uint contextLength;
|
||||
uint numerusRulesLength;
|
||||
|
||||
bool do_load(const QString &filename);
|
||||
bool do_load(const uchar *data, int len);
|
||||
QString do_translate(const char *context, const char *sourceText, const char *comment,
|
||||
int n) const;
|
||||
void clear();
|
||||
bool didload;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -323,545 +148,30 @@ QTranslator::~QTranslator()
|
|||
{
|
||||
if (QCoreApplication::instance())
|
||||
QCoreApplication::removeTranslator(this);
|
||||
Q_D(QTranslator);
|
||||
d->clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
Loads \a filename + \a suffix (".qm" if the \a suffix is not
|
||||
specified), which may be an absolute file name or relative to \a
|
||||
directory. Returns true if the translation is successfully loaded;
|
||||
otherwise returns false.
|
||||
|
||||
If \a directory is not specified, the directory of the
|
||||
application's executable is used (i.e., as
|
||||
\l{QCoreApplication::}{applicationDirPath()}).
|
||||
|
||||
The previous contents of this translator object are discarded.
|
||||
|
||||
If the file name does not exist, other file names are tried
|
||||
in the following order:
|
||||
|
||||
\list 1
|
||||
\o File name without \a suffix appended.
|
||||
\o File name with text after a character in \a search_delimiters
|
||||
stripped ("_." is the default for \a search_delimiters if it is
|
||||
an empty string) and \a suffix.
|
||||
\o File name stripped without \a suffix appended.
|
||||
\o File name stripped further, etc.
|
||||
\endlist
|
||||
|
||||
For example, an application running in the fr_CA locale
|
||||
(French-speaking Canada) might call load("foo.fr_ca",
|
||||
"/opt/foolib"). load() would then try to open the first existing
|
||||
readable file from this list:
|
||||
|
||||
\list 1
|
||||
\o \c /opt/foolib/foo.fr_ca.qm
|
||||
\o \c /opt/foolib/foo.fr_ca
|
||||
\o \c /opt/foolib/foo.fr.qm
|
||||
\o \c /opt/foolib/foo.fr
|
||||
\o \c /opt/foolib/foo.qm
|
||||
\o \c /opt/foolib/foo
|
||||
\endlist
|
||||
*/
|
||||
|
||||
bool QTranslator::load(const QString & filename, const QString & directory,
|
||||
const QString & search_delimiters,
|
||||
const QString & suffix)
|
||||
{
|
||||
Q_D(QTranslator);
|
||||
d->clear();
|
||||
|
||||
QString fname = filename;
|
||||
QString prefix;
|
||||
if (QFileInfo(filename).isRelative()) {
|
||||
prefix = directory;
|
||||
if (prefix.length() && !prefix.endsWith(QLatin1Char('/')))
|
||||
prefix += QLatin1Char('/');
|
||||
}
|
||||
|
||||
|
||||
QString realname;
|
||||
QString delims;
|
||||
delims = search_delimiters.isNull() ? QString::fromLatin1("_.") : search_delimiters;
|
||||
|
||||
for (;;) {
|
||||
QFileInfo fi;
|
||||
|
||||
|
||||
realname = prefix + fname + (suffix.isNull() ? QString::fromLatin1(".qm") : suffix);
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
break;
|
||||
|
||||
realname = prefix + fname;
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
break;
|
||||
|
||||
int rightmost = 0;
|
||||
for (int i = 0; i < (int)delims.length(); i++) {
|
||||
int k = fname.lastIndexOf(delims[i]);
|
||||
if (k > rightmost)
|
||||
rightmost = k;
|
||||
}
|
||||
|
||||
// no truncations? fail
|
||||
if (rightmost == 0)
|
||||
return false;
|
||||
|
||||
fname.truncate(rightmost);
|
||||
}
|
||||
|
||||
// realname is now the fully qualified name of a readable file.
|
||||
return d->do_load(realname);
|
||||
}
|
||||
|
||||
bool QTranslatorPrivate::do_load(const QString &realname)
|
||||
{
|
||||
QTranslatorPrivate *d = this;
|
||||
bool ok = false;
|
||||
|
||||
const bool isResourceFile = realname.startsWith(QLatin1Char(':'));
|
||||
if (isResourceFile) {
|
||||
// If the translation is in a non-compressed resource file, the data is already in
|
||||
// memory, so no need to use QFile to copy it again.
|
||||
Q_ASSERT(!d->resource);
|
||||
d->resource = new QResource(realname);
|
||||
if (d->resource->isValid() && !d->resource->isCompressed()) {
|
||||
d->dataLength = d->resource->size();
|
||||
d->dataPointer = const_cast<uchar *>(d->resource->data());
|
||||
ok = true;
|
||||
} else {
|
||||
delete d->resource;
|
||||
d->resource = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
QFile file(realname);
|
||||
d->dataLength = file.size();
|
||||
if (!d->dataLength)
|
||||
return false;
|
||||
d->dataPointer = new uchar[d->dataLength];
|
||||
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
if (d->dataLength != (uint)file.read(reinterpret_cast<char*>(d->dataPointer), d->dataLength)) {
|
||||
delete [] d->dataPointer;
|
||||
d->dataPointer = 0;
|
||||
d->dataLength = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d->do_load(d->dataPointer, d->dataLength);
|
||||
}
|
||||
|
||||
static QString find_translation(const QLocale & locale,
|
||||
const QString & filename,
|
||||
const QString & prefix,
|
||||
const QString & directory,
|
||||
const QString & suffix)
|
||||
{
|
||||
QString path;
|
||||
if (QFileInfo(filename).isRelative()) {
|
||||
path = directory;
|
||||
if (!path.isEmpty() && !path.endsWith(QLatin1Char('/')))
|
||||
path += QLatin1Char('/');
|
||||
}
|
||||
|
||||
QFileInfo fi;
|
||||
QString realname;
|
||||
QStringList fuzzyLocales;
|
||||
|
||||
// see http://www.unicode.org/reports/tr35/#LanguageMatching for inspiration
|
||||
QStringList languages = locale.uiLanguages();
|
||||
for (int i = languages.size()-1; i >= 0; --i) {
|
||||
QString lang = languages.at(i);
|
||||
QString lowerLang = lang.toLower();
|
||||
if (lang != lowerLang)
|
||||
languages.insert(i+1, lowerLang);
|
||||
}
|
||||
|
||||
// try explicit locales names first
|
||||
foreach (QString localeName, languages) {
|
||||
localeName.replace(QLatin1Char('-'), QLatin1Char('_'));
|
||||
|
||||
realname = path + filename + prefix + localeName + (suffix.isNull() ? QLatin1String(".qm") : suffix);
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
return realname;
|
||||
|
||||
realname = path + filename + prefix + localeName;
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
return realname;
|
||||
|
||||
fuzzyLocales.append(localeName);
|
||||
}
|
||||
|
||||
// start guessing
|
||||
foreach (QString localeName, fuzzyLocales) {
|
||||
for (;;) {
|
||||
int rightmost = localeName.lastIndexOf(QLatin1Char('_'));
|
||||
// no truncations? fail
|
||||
if (rightmost <= 0)
|
||||
break;
|
||||
localeName.truncate(rightmost);
|
||||
|
||||
realname = path + filename + prefix + localeName + (suffix.isNull() ? QLatin1String(".qm") : suffix);
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
return realname;
|
||||
|
||||
realname = path + filename + prefix + localeName;
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
return realname;
|
||||
}
|
||||
}
|
||||
|
||||
if (!suffix.isNull()) {
|
||||
realname = path + filename + suffix;
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
return realname;
|
||||
}
|
||||
|
||||
realname = path + filename + prefix;
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
return realname;
|
||||
|
||||
realname = path + filename;
|
||||
fi.setFile(realname);
|
||||
if (fi.isReadable() && fi.isFile())
|
||||
return realname;
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.8
|
||||
|
||||
Loads \a filename + \a prefix + \l{QLocale::uiLanguages()}{ui language
|
||||
name} + \a suffix (".qm" if the \a suffix is not specified), which may be
|
||||
an absolute file name or relative to \a directory. Returns true if the
|
||||
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.
|
||||
|
||||
The previous contents of this translator object are discarded.
|
||||
|
||||
If the file name does not exist, other file names are tried
|
||||
in the following order:
|
||||
For example, an application running in the fr_CA locale
|
||||
(French-speaking Canada) might call load("foo", "fr_ca".
|
||||
|
||||
\list 1
|
||||
\o File name without \a suffix appended.
|
||||
\o File name with ui language part after a "_" character stripped and \a suffix.
|
||||
\o File name with ui language part stripped without \a suffix appended.
|
||||
\o File name with ui language part stripped further, etc.
|
||||
\endlist
|
||||
|
||||
For example, an application running in the locale with the following
|
||||
\l{QLocale::uiLanguages()}{ui languages} - "es", "fr-CA", "de" might call
|
||||
load(QLocale::system(), "foo", ".", "/opt/foolib", ".qm"). load() would
|
||||
replace '-' (dash) with '_' (underscore) in the ui language and then try to
|
||||
open the first existing readable file from this list:
|
||||
|
||||
\list 1
|
||||
\o \c /opt/foolib/foo.es.qm
|
||||
\o \c /opt/foolib/foo.es
|
||||
\o \c /opt/foolib/foo.fr_CA.qm
|
||||
\o \c /opt/foolib/foo.fr_CA
|
||||
\o \c /opt/foolib/foo.de.qm
|
||||
\o \c /opt/foolib/foo.de
|
||||
\o \c /opt/foolib/foo.fr.qm
|
||||
\o \c /opt/foolib/foo.fr
|
||||
\o \c /opt/foolib/foo.qm
|
||||
\o \c /opt/foolib/foo.
|
||||
\o \c /opt/foolib/foo
|
||||
\endlist
|
||||
|
||||
On operating systems where file system is case sensitive, QTranslator also
|
||||
tries to load a lower-cased version of the locale name.
|
||||
\sa QLibraryInfo
|
||||
*/
|
||||
bool QTranslator::load(const QLocale & locale,
|
||||
const QString & filename,
|
||||
const QString & prefix,
|
||||
const QString & directory,
|
||||
const QString & suffix)
|
||||
|
||||
bool QTranslator::load(const QString &domain, const QLocale &locale)
|
||||
{
|
||||
Q_D(QTranslator);
|
||||
d->clear();
|
||||
QString fname = find_translation(locale, filename, prefix, directory, suffix);
|
||||
return !fname.isEmpty() && d->do_load(fname);
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload load()
|
||||
\fn bool QTranslator::load(const uchar *data, int len)
|
||||
|
||||
Loads the QM file data \a data of length \a len into the
|
||||
translator.
|
||||
|
||||
The data is not copied. The caller must be able to guarantee that \a data
|
||||
will not be deleted or modified.
|
||||
*/
|
||||
bool QTranslator::load(const uchar *data, int len)
|
||||
{
|
||||
Q_D(QTranslator);
|
||||
d->clear();
|
||||
return d->do_load(data, len);
|
||||
}
|
||||
|
||||
static quint8 read8(const uchar *data)
|
||||
{
|
||||
return *data;
|
||||
}
|
||||
|
||||
static quint16 read16(const uchar *data)
|
||||
{
|
||||
return (data[0] << 8) | (data[1]);
|
||||
}
|
||||
|
||||
static quint32 read32(const uchar *data)
|
||||
{
|
||||
return (data[0] << 24)
|
||||
| (data[1] << 16)
|
||||
| (data[2] << 8)
|
||||
| (data[3]);
|
||||
}
|
||||
|
||||
bool QTranslatorPrivate::do_load(const uchar *data, int len)
|
||||
{
|
||||
if (!data || len < MagicLength || memcmp(data, magic, MagicLength))
|
||||
return false;
|
||||
|
||||
bool ok = true;
|
||||
const uchar *end = data + len;
|
||||
|
||||
data += MagicLength;
|
||||
|
||||
while (data < end - 4) {
|
||||
quint8 tag = read8(data++);
|
||||
quint32 blockLen = read32(data);
|
||||
data += 4;
|
||||
if (!tag || !blockLen)
|
||||
break;
|
||||
if (data + blockLen > end) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == QTranslatorPrivate::Contexts) {
|
||||
contextArray = data;
|
||||
contextLength = blockLen;
|
||||
} else if (tag == QTranslatorPrivate::Hashes) {
|
||||
offsetArray = data;
|
||||
offsetLength = blockLen;
|
||||
} else if (tag == QTranslatorPrivate::Messages) {
|
||||
messageArray = data;
|
||||
messageLength = blockLen;
|
||||
} else if (tag == QTranslatorPrivate::NumerusRules) {
|
||||
numerusRulesArray = data;
|
||||
numerusRulesLength = blockLen;
|
||||
}
|
||||
|
||||
data += blockLen;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static QString getMessage(const uchar *m, const uchar *end, const char *context,
|
||||
const char *sourceText, const char *comment, int numerus)
|
||||
{
|
||||
const uchar *tn = 0;
|
||||
uint tn_length = 0;
|
||||
int currentNumerus = -1;
|
||||
|
||||
for (;;) {
|
||||
uchar tag = 0;
|
||||
if (m < end)
|
||||
tag = read8(m++);
|
||||
switch((Tag)tag) {
|
||||
case Tag_End:
|
||||
goto end;
|
||||
case Tag_Translation: {
|
||||
int len = read32(m);
|
||||
if (len % 1)
|
||||
return QString();
|
||||
m += 4;
|
||||
if (++currentNumerus == numerus) {
|
||||
tn_length = len;
|
||||
tn = m;
|
||||
}
|
||||
m += len;
|
||||
break;
|
||||
}
|
||||
case Tag_Obsolete1:
|
||||
m += 4;
|
||||
break;
|
||||
case Tag_SourceText: {
|
||||
quint32 len = read32(m);
|
||||
m += 4;
|
||||
if (!match(m, sourceText, len))
|
||||
return QString();
|
||||
m += len;
|
||||
}
|
||||
break;
|
||||
case Tag_Context: {
|
||||
quint32 len = read32(m);
|
||||
m += 4;
|
||||
if (!match(m, context, len))
|
||||
return QString();
|
||||
m += len;
|
||||
}
|
||||
break;
|
||||
case Tag_Comment: {
|
||||
quint32 len = read32(m);
|
||||
m += 4;
|
||||
if (*m && !match(m, comment, len))
|
||||
return QString();
|
||||
m += len;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
end:
|
||||
if (!tn)
|
||||
return QString();
|
||||
QString str = QString((const QChar *)tn, tn_length/2);
|
||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
str[i] = QChar((str.at(i).unicode() >> 8) + ((str.at(i).unicode() << 8) & 0xff00));
|
||||
}
|
||||
#endif
|
||||
return str;
|
||||
}
|
||||
|
||||
QString QTranslatorPrivate::do_translate(const char *context, const char *sourceText,
|
||||
const char *comment, int n) const
|
||||
{
|
||||
if (context == 0)
|
||||
context = "";
|
||||
if (sourceText == 0)
|
||||
sourceText = "";
|
||||
if (comment == 0)
|
||||
comment = "";
|
||||
|
||||
if (!offsetLength)
|
||||
return QString();
|
||||
|
||||
/*
|
||||
Check if the context belongs to this QTranslator. If many
|
||||
translators are installed, this step is necessary.
|
||||
*/
|
||||
if (contextLength) {
|
||||
quint16 hTableSize = read16(contextArray);
|
||||
uint g = elfHash(context) % hTableSize;
|
||||
const uchar *c = contextArray + 2 + (g << 1);
|
||||
quint16 off = read16(c);
|
||||
c += 2;
|
||||
if (off == 0)
|
||||
return QString();
|
||||
c = contextArray + (2 + (hTableSize << 1) + (off << 1));
|
||||
|
||||
for (;;) {
|
||||
quint8 len = read8(c++);
|
||||
if (len == 0)
|
||||
return QString();
|
||||
if (match(c, context, len))
|
||||
break;
|
||||
c += len;
|
||||
}
|
||||
}
|
||||
|
||||
size_t numItems = offsetLength / (2 * sizeof(quint32));
|
||||
if (!numItems)
|
||||
return QString();
|
||||
|
||||
int numerus = 0;
|
||||
if (n >= 0)
|
||||
numerus = numerusHelper(n, numerusRulesArray, numerusRulesLength);
|
||||
|
||||
for (;;) {
|
||||
quint32 h = elfHash(QByteArray(QByteArray(sourceText) + comment).constData());
|
||||
|
||||
const uchar *start = offsetArray;
|
||||
const uchar *end = start + ((numItems-1) << 3);
|
||||
while (start <= end) {
|
||||
const uchar *middle = start + (((end - start) >> 4) << 3);
|
||||
uint hash = read32(middle);
|
||||
if (h == hash) {
|
||||
start = middle;
|
||||
break;
|
||||
} else if (hash < h) {
|
||||
start = middle + 8;
|
||||
} else {
|
||||
end = middle - 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (start <= end) {
|
||||
// go back on equal key
|
||||
while (start != offsetArray && read32(start) == read32(start-8))
|
||||
start -= 8;
|
||||
|
||||
while (start < offsetArray + offsetLength) {
|
||||
quint32 rh = read32(start);
|
||||
start += 4;
|
||||
if (rh != h)
|
||||
break;
|
||||
quint32 ro = read32(start);
|
||||
start += 4;
|
||||
QString tn = getMessage(messageArray + ro, messageArray + messageLength, context,
|
||||
sourceText, comment, numerus);
|
||||
if (!tn.isNull())
|
||||
return tn;
|
||||
}
|
||||
}
|
||||
if (!comment[0])
|
||||
break;
|
||||
comment = "";
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
/*!
|
||||
Empties this translator of all contents.
|
||||
|
||||
This function works with stripped translator files.
|
||||
*/
|
||||
|
||||
void QTranslatorPrivate::clear()
|
||||
{
|
||||
Q_Q(QTranslator);
|
||||
if (dataPointer && dataLength) {
|
||||
if (!resource)
|
||||
delete [] dataPointer;
|
||||
}
|
||||
|
||||
delete resource;
|
||||
resource = 0;
|
||||
dataPointer = 0;
|
||||
dataLength = 0;
|
||||
messageArray = 0;
|
||||
contextArray = 0;
|
||||
offsetArray = 0;
|
||||
numerusRulesArray = 0;
|
||||
messageLength = 0;
|
||||
contextLength = 0;
|
||||
offsetLength = 0;
|
||||
numerusRulesLength = 0;
|
||||
|
||||
if (QCoreApplicationPrivate::isTranslatorInstalled(q))
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(),
|
||||
new QEvent(QEvent::LanguageChange));
|
||||
Q_UNUSED(locale);
|
||||
QByteArray latindomain(domain.toLatin1());
|
||||
bind_textdomain_codeset(latindomain.constData(), "UTF-8");
|
||||
d->didload = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -877,8 +187,8 @@ void QTranslatorPrivate::clear()
|
|||
QString QTranslator::translate(const char *context, const char *sourceText, const char *disambiguation,
|
||||
int n) const
|
||||
{
|
||||
Q_D(const QTranslator);
|
||||
return d->do_translate(context, sourceText, disambiguation, n);
|
||||
Q_UNUSED(disambiguation);
|
||||
return QString::fromUtf8(ngettext(context, sourceText, n));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -888,8 +198,7 @@ QString QTranslator::translate(const char *context, const char *sourceText, cons
|
|||
bool QTranslator::isEmpty() const
|
||||
{
|
||||
Q_D(const QTranslator);
|
||||
return !d->dataPointer && !d->dataLength && !d->messageArray &&
|
||||
!d->offsetArray && !d->contextArray;
|
||||
return d->didload;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define QTRANSLATOR_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qlocale.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
|
@ -43,7 +44,6 @@ QT_BEGIN_NAMESPACE
|
|||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
|
||||
class QLocale;
|
||||
class QTranslatorPrivate;
|
||||
|
||||
class Q_CORE_EXPORT QTranslator : public QObject
|
||||
|
@ -59,16 +59,7 @@ public:
|
|||
|
||||
virtual bool isEmpty() const;
|
||||
|
||||
bool load(const QString & filename,
|
||||
const QString & directory = QString(),
|
||||
const QString & search_delimiters = QString(),
|
||||
const QString & suffix = QString());
|
||||
bool load(const QLocale & locale,
|
||||
const QString & filename,
|
||||
const QString & prefix = QString(),
|
||||
const QString & directory = QString(),
|
||||
const QString & suffix = QString());
|
||||
bool load(const uchar *data, int len);
|
||||
bool load(const QString &domain, const QLocale &locale = QLocale::system());
|
||||
|
||||
|
||||
private:
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtCore module 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTRANSLATOR_P_H
|
||||
#define QTRANSLATOR_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of qfontencodings_x11.cpp and qfont_x11.cpp. This header file may
|
||||
// change from version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
enum {
|
||||
Q_EQ = 0x01,
|
||||
Q_LT = 0x02,
|
||||
Q_LEQ = 0x03,
|
||||
Q_BETWEEN = 0x04,
|
||||
|
||||
Q_NOT = 0x08,
|
||||
Q_MOD_10 = 0x10,
|
||||
Q_MOD_100 = 0x20,
|
||||
Q_LEAD_1000 = 0x40,
|
||||
|
||||
Q_AND = 0xFD,
|
||||
Q_OR = 0xFE,
|
||||
Q_NEWRULE = 0xFF,
|
||||
|
||||
Q_OP_MASK = 0x07,
|
||||
|
||||
Q_NEQ = Q_NOT | Q_EQ,
|
||||
Q_GT = Q_NOT | Q_LEQ,
|
||||
Q_GEQ = Q_NOT | Q_LT,
|
||||
Q_NOT_BETWEEN = Q_NOT | Q_BETWEEN
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,4 +1,3 @@
|
|||
add_subdirectory(findwidget)
|
||||
add_subdirectory(linguist)
|
||||
add_subdirectory(qtgradienteditor)
|
||||
add_subdirectory(qtpropertybrowser)
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
set(SHAREDLINGUIST_INCLUDES
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/privateinclude
|
||||
${CMAKE_BINARY_DIR}/include/QtCore
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtCore
|
||||
${CMAKE_BINARY_DIR}/include/QtXml
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtXml
|
||||
${CMAKE_SOURCE_DIR}/src/shared/linguist
|
||||
)
|
||||
|
||||
set(SHAREDLINGUIST_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/simtexth.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/numerus.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translatormessage.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qm.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qph.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/po.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ts.cpp
|
||||
)
|
||||
|
||||
set(SHAREDLINGUIST_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/simtexth.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translator.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translatormessage.h
|
||||
)
|
||||
|
||||
katie_setup_target(sharedlinguist ${SHAREDLINGUIST_SOURCES} ${SHAREDLINGUIST_HEADERS})
|
||||
|
||||
add_library(sharedlinguist OBJECT ${sharedlinguist_SOURCES})
|
||||
target_include_directories(sharedlinguist PUBLIC ${SHAREDLINGUIST_INCLUDES})
|
|
@ -1,404 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
#include "qtranslator_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static const uchar englishStyleRules[] =
|
||||
{ Q_EQ, 1 };
|
||||
static const uchar frenchStyleRules[] =
|
||||
{ Q_LEQ, 1 };
|
||||
static const uchar latvianRules[] =
|
||||
{ Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
|
||||
Q_NEQ, 0 };
|
||||
static const uchar icelandicRules[] =
|
||||
{ Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11 };
|
||||
static const uchar irishStyleRules[] =
|
||||
{ Q_EQ, 1, Q_NEWRULE,
|
||||
Q_EQ, 2 };
|
||||
static const uchar slovakStyleRules[] =
|
||||
{ Q_EQ, 1, Q_NEWRULE,
|
||||
Q_BETWEEN, 2, 4 };
|
||||
static const uchar macedonianRules[] =
|
||||
{ Q_MOD_10 | Q_EQ, 1, Q_NEWRULE,
|
||||
Q_MOD_10 | Q_EQ, 2 };
|
||||
static const uchar lithuanianRules[] =
|
||||
{ Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
|
||||
Q_MOD_10 | Q_NEQ, 0, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
|
||||
static const uchar russianStyleRules[] =
|
||||
{ Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
|
||||
Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
|
||||
static const uchar polishRules[] =
|
||||
{ Q_EQ, 1, Q_NEWRULE,
|
||||
Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
|
||||
static const uchar romanianRules[] =
|
||||
{ Q_EQ, 1, Q_NEWRULE,
|
||||
Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 19 };
|
||||
static const uchar slovenianRules[] =
|
||||
{ Q_MOD_100 | Q_EQ, 1, Q_NEWRULE,
|
||||
Q_MOD_100 | Q_EQ, 2, Q_NEWRULE,
|
||||
Q_MOD_100 | Q_BETWEEN, 3, 4 };
|
||||
static const uchar malteseRules[] =
|
||||
{ Q_EQ, 1, Q_NEWRULE,
|
||||
Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 10, Q_NEWRULE,
|
||||
Q_MOD_100 | Q_BETWEEN, 11, 19 };
|
||||
static const uchar welshRules[] =
|
||||
{ Q_EQ, 0, Q_NEWRULE,
|
||||
Q_EQ, 1, Q_NEWRULE,
|
||||
Q_BETWEEN, 2, 5, Q_NEWRULE,
|
||||
Q_EQ, 6 };
|
||||
static const uchar arabicRules[] =
|
||||
{ Q_EQ, 0, Q_NEWRULE,
|
||||
Q_EQ, 1, Q_NEWRULE,
|
||||
Q_EQ, 2, Q_NEWRULE,
|
||||
Q_MOD_100 | Q_BETWEEN, 3, 10, Q_NEWRULE,
|
||||
Q_MOD_100 | Q_GEQ, 11 };
|
||||
static const uchar tagalogRules[] =
|
||||
{ Q_LEQ, 1, Q_NEWRULE,
|
||||
Q_MOD_10 | Q_EQ, 4, Q_OR, Q_MOD_10 | Q_EQ, 6, Q_OR, Q_MOD_10 | Q_EQ, 9 };
|
||||
static const uchar catalanRules[] =
|
||||
{ Q_EQ, 1, Q_NEWRULE,
|
||||
Q_LEAD_1000 | Q_EQ, 11 };
|
||||
|
||||
static const char * const japaneseStyleForms[] = { "Universal Form", 0 };
|
||||
static const char * const englishStyleForms[] = { "Singular", "Plural", 0 };
|
||||
static const char * const frenchStyleForms[] = { "Singular", "Plural", 0 };
|
||||
static const char * const icelandicForms[] = { "Singular", "Plural", 0 };
|
||||
static const char * const latvianForms[] = { "Singular", "Plural", "Nullar", 0 };
|
||||
static const char * const irishStyleForms[] = { "Singular", "Dual", "Plural", 0 };
|
||||
static const char * const slovakStyleForms[] = { "Singular", "Paucal", "Plural", 0 };
|
||||
static const char * const macedonianForms[] = { "Singular", "Dual", "Plural", 0 };
|
||||
static const char * const lithuanianForms[] = { "Singular", "Paucal", "Plural", 0 };
|
||||
static const char * const russianStyleForms[] = { "Singular", "Dual", "Plural", 0 };
|
||||
static const char * const polishForms[] = { "Singular", "Paucal", "Plural", 0 };
|
||||
static const char * const romanianForms[] = { "Singular", "Paucal", "Plural", 0 };
|
||||
static const char * const slovenianForms[] = { "Singular", "Dual", "Trial", "Plural", 0 };
|
||||
static const char * const malteseForms[] =
|
||||
{ "Singular", "Paucal", "Greater Paucal", "Plural", 0 };
|
||||
static const char * const welshForms[] =
|
||||
{ "Nullar", "Singular", "Dual", "Sexal", "Plural", 0 };
|
||||
static const char * const arabicForms[] =
|
||||
{ "Nullar", "Singular", "Dual", "Minority Plural", "Plural", "Plural (100-102, ...)", 0 };
|
||||
static const char * const tagalogForms[] =
|
||||
{ "Singular", "Plural (consonant-ended)", "Plural (vowel-ended)", 0 };
|
||||
static const char * const catalanForms[] = { "Singular", "Undecal (11)", "Plural", 0 };
|
||||
|
||||
#define EOL QLocale::C
|
||||
|
||||
static const QLocale::Language japaneseStyleLanguages[] = {
|
||||
QLocale::Bislama,
|
||||
QLocale::Burmese,
|
||||
QLocale::Chinese,
|
||||
QLocale::Fijian,
|
||||
QLocale::Guarani,
|
||||
QLocale::Hungarian,
|
||||
QLocale::Indonesian,
|
||||
QLocale::Japanese,
|
||||
QLocale::Javanese,
|
||||
QLocale::Korean,
|
||||
QLocale::Malay,
|
||||
QLocale::NauruLanguage,
|
||||
QLocale::Persian,
|
||||
QLocale::Sundanese,
|
||||
QLocale::Tatar,
|
||||
QLocale::Thai,
|
||||
QLocale::Tibetan,
|
||||
QLocale::Turkish,
|
||||
QLocale::Vietnamese,
|
||||
QLocale::Yoruba,
|
||||
QLocale::Zhuang,
|
||||
EOL
|
||||
};
|
||||
|
||||
static const QLocale::Language englishStyleLanguages[] = {
|
||||
QLocale::Abkhazian,
|
||||
QLocale::Afar,
|
||||
QLocale::Afrikaans,
|
||||
QLocale::Albanian,
|
||||
QLocale::Amharic,
|
||||
QLocale::Assamese,
|
||||
QLocale::Aymara,
|
||||
QLocale::Azerbaijani,
|
||||
QLocale::Bashkir,
|
||||
QLocale::Basque,
|
||||
QLocale::Bulgarian,
|
||||
QLocale::Cornish,
|
||||
QLocale::Corsican,
|
||||
QLocale::Danish,
|
||||
QLocale::Dutch,
|
||||
QLocale::English,
|
||||
QLocale::Esperanto,
|
||||
QLocale::Estonian,
|
||||
QLocale::Faroese,
|
||||
QLocale::Finnish,
|
||||
QLocale::Friulian,
|
||||
QLocale::WesternFrisian,
|
||||
QLocale::Galician,
|
||||
QLocale::Georgian,
|
||||
QLocale::German,
|
||||
QLocale::Greek,
|
||||
QLocale::Gujarati,
|
||||
QLocale::Hausa,
|
||||
QLocale::Hebrew,
|
||||
QLocale::Hindi,
|
||||
QLocale::Interlingua,
|
||||
QLocale::Interlingue,
|
||||
QLocale::Italian,
|
||||
QLocale::Kannada,
|
||||
QLocale::Kashmiri,
|
||||
QLocale::Kazakh,
|
||||
QLocale::Khmer,
|
||||
QLocale::Kinyarwanda,
|
||||
QLocale::Kirghiz,
|
||||
QLocale::Kurdish,
|
||||
QLocale::Lao,
|
||||
QLocale::Latin,
|
||||
QLocale::Lingala,
|
||||
QLocale::Luxembourgish,
|
||||
QLocale::Malagasy,
|
||||
QLocale::Malayalam,
|
||||
QLocale::Marathi,
|
||||
QLocale::Mongolian,
|
||||
QLocale::Nepali,
|
||||
QLocale::NorthernSotho,
|
||||
QLocale::NorwegianBokmal,
|
||||
QLocale::NorwegianNynorsk,
|
||||
QLocale::Occitan,
|
||||
QLocale::Pashto,
|
||||
QLocale::Portuguese,
|
||||
QLocale::Punjabi,
|
||||
QLocale::Quechua,
|
||||
QLocale::Romansh,
|
||||
QLocale::Rundi,
|
||||
QLocale::Shona,
|
||||
QLocale::Sindhi,
|
||||
QLocale::Sinhala,
|
||||
QLocale::Somali,
|
||||
QLocale::SouthernSotho,
|
||||
QLocale::Spanish,
|
||||
QLocale::Swahili,
|
||||
QLocale::Swati,
|
||||
QLocale::Swedish,
|
||||
QLocale::Tajik,
|
||||
QLocale::Tamil,
|
||||
QLocale::Telugu,
|
||||
QLocale::Tongan,
|
||||
QLocale::Tsonga,
|
||||
QLocale::Tswana,
|
||||
QLocale::Turkmen,
|
||||
QLocale::Twi,
|
||||
QLocale::Urdu,
|
||||
QLocale::Uzbek,
|
||||
QLocale::Volapuk,
|
||||
QLocale::Wolof,
|
||||
QLocale::Xhosa,
|
||||
QLocale::Yiddish,
|
||||
QLocale::Zulu,
|
||||
EOL
|
||||
};
|
||||
static const QLocale::Language frenchStyleLanguages[] = {
|
||||
// keep synchronized with frenchStyleCountries
|
||||
QLocale::Armenian,
|
||||
QLocale::Breton,
|
||||
QLocale::French,
|
||||
QLocale::Portuguese,
|
||||
QLocale::Filipino,
|
||||
QLocale::Tigrinya,
|
||||
QLocale::Walloon,
|
||||
EOL
|
||||
};
|
||||
static const QLocale::Language latvianLanguage[] = { QLocale::Latvian, EOL };
|
||||
static const QLocale::Language icelandicLanguage[] = { QLocale::Icelandic, EOL };
|
||||
static const QLocale::Language irishStyleLanguages[] = {
|
||||
QLocale::Divehi,
|
||||
QLocale::Inuktitut,
|
||||
QLocale::Irish,
|
||||
QLocale::Manx,
|
||||
QLocale::Maori,
|
||||
QLocale::NorthernSami,
|
||||
QLocale::Samoan,
|
||||
QLocale::Sanskrit,
|
||||
EOL
|
||||
};
|
||||
static const QLocale::Language slovakStyleLanguages[] = { QLocale::Slovak, QLocale::Czech, EOL };
|
||||
static const QLocale::Language macedonianLanguage[] = { QLocale::Macedonian, EOL };
|
||||
static const QLocale::Language lithuanianLanguage[] = { QLocale::Lithuanian, EOL };
|
||||
static const QLocale::Language russianStyleLanguages[] = {
|
||||
QLocale::Bosnian,
|
||||
QLocale::Belarusian,
|
||||
QLocale::Croatian,
|
||||
QLocale::Russian,
|
||||
QLocale::Serbian,
|
||||
QLocale::SerboCroatian,
|
||||
QLocale::Ukrainian,
|
||||
EOL
|
||||
};
|
||||
static const QLocale::Language polishLanguage[] = { QLocale::Polish, EOL };
|
||||
static const QLocale::Language romanianLanguages[] = {
|
||||
QLocale::Moldavian,
|
||||
QLocale::Romanian,
|
||||
EOL
|
||||
};
|
||||
static const QLocale::Language slovenianLanguage[] = { QLocale::Slovenian, EOL };
|
||||
static const QLocale::Language malteseLanguage[] = { QLocale::Maltese, EOL };
|
||||
static const QLocale::Language welshLanguage[] = { QLocale::Welsh, EOL };
|
||||
static const QLocale::Language arabicLanguage[] = { QLocale::Arabic, EOL };
|
||||
static const QLocale::Language tagalogLanguage[] = { QLocale::Tagalog, EOL };
|
||||
static const QLocale::Language catalanLanguage[] = { QLocale::Catalan, EOL };
|
||||
|
||||
static const QLocale::Country frenchStyleCountries[] = {
|
||||
// keep synchronized with frenchStyleLanguages
|
||||
QLocale::AnyCountry,
|
||||
QLocale::AnyCountry,
|
||||
QLocale::AnyCountry,
|
||||
QLocale::Brazil,
|
||||
QLocale::AnyCountry,
|
||||
QLocale::AnyCountry,
|
||||
QLocale::AnyCountry
|
||||
};
|
||||
struct NumerusTableEntry {
|
||||
const uchar *rules;
|
||||
int rulesSize;
|
||||
const char * const *forms;
|
||||
const QLocale::Language *languages;
|
||||
const QLocale::Country *countries;
|
||||
const char * const gettextRules;
|
||||
};
|
||||
|
||||
static const NumerusTableEntry numerusTable[] = {
|
||||
{ 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0, "nplurals=1; plural=0;" },
|
||||
{ englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0,
|
||||
"nplurals=2; plural=(n != 1);" },
|
||||
{ frenchStyleRules, sizeof(frenchStyleRules), frenchStyleForms, frenchStyleLanguages,
|
||||
frenchStyleCountries, "nplurals=2; plural=(n > 1);" },
|
||||
{ latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0,
|
||||
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" },
|
||||
{ icelandicRules, sizeof(icelandicRules), icelandicForms, icelandicLanguage, 0,
|
||||
"nplurals=2; plural=(n%10==1 && n%100!=11 ? 0 : 1);" },
|
||||
{ irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0,
|
||||
"nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);" },
|
||||
{ slovakStyleRules, sizeof(slovakStyleRules), slovakStyleForms, slovakStyleLanguages, 0,
|
||||
"nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);" },
|
||||
{ macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0,
|
||||
"nplurals=3; plural=(n%100==1 ? 0 : n%100==2 ? 1 : 2);" },
|
||||
{ lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0,
|
||||
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" },
|
||||
{ russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0,
|
||||
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
|
||||
{ polishRules, sizeof(polishRules), polishForms, polishLanguage, 0,
|
||||
"nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
|
||||
{ romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0,
|
||||
"nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);" },
|
||||
{ slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0,
|
||||
"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" },
|
||||
{ malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0,
|
||||
"nplurals=4; plural=(n==1 ? 0 : (n==0 || (n%100>=1 && n%100<=10)) ? 1 : (n%100>=11 && n%100<=19) ? 2 : 3);" },
|
||||
{ welshRules, sizeof(welshRules), welshForms, welshLanguage, 0,
|
||||
"nplurals=5; plural=(n==0 ? 0 : n==1 ? 1 : (n>=2 && n<=5) ? 2 : n==6 ? 3 : 4);" },
|
||||
{ arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0,
|
||||
"nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : (n%100>=3 && n%100<=10) ? 3 : n%100>=11 ? 4 : 5);" },
|
||||
{ tagalogRules, sizeof(tagalogRules), tagalogForms, tagalogLanguage, 0,
|
||||
"nplurals=3; plural=(n==1 ? 0 : (n%10==4 || n%10==6 || n%10== 9) ? 1 : 2);" },
|
||||
{ catalanRules, sizeof(catalanRules), catalanForms, catalanLanguage, 0,
|
||||
"nplurals=3; plural=(n==1 ? 0 : (n==11 || n/1000==11 || n/1000000==11 || n/1000000000==11) ? 1 : 2);" },
|
||||
};
|
||||
|
||||
static const int NumerusTableSize = sizeof(numerusTable) / sizeof(numerusTable[0]);
|
||||
|
||||
bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
|
||||
QByteArray *rules, QStringList *forms, const char **gettextRules)
|
||||
{
|
||||
while (true) {
|
||||
for (int i = 0; i < NumerusTableSize; ++i) {
|
||||
const NumerusTableEntry &entry = numerusTable[i];
|
||||
for (int j = 0; entry.languages[j] != EOL; ++j) {
|
||||
if (entry.languages[j] == language
|
||||
&& ((!entry.countries && country == QLocale::AnyCountry)
|
||||
|| (entry.countries && entry.countries[j] == country))) {
|
||||
if (rules) {
|
||||
*rules = QByteArray::fromRawData(reinterpret_cast<const char *>(entry.rules),
|
||||
entry.rulesSize);
|
||||
}
|
||||
if (gettextRules)
|
||||
*gettextRules = entry.gettextRules;
|
||||
if (forms) {
|
||||
forms->clear();
|
||||
for (int k = 0; entry.forms[k]; ++k)
|
||||
forms->append(QLatin1String(entry.forms[k]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (country == QLocale::AnyCountry)
|
||||
break;
|
||||
country = QLocale::AnyCountry;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString getNumerusInfoString()
|
||||
{
|
||||
QStringList langs;
|
||||
|
||||
for (int i = 0; i < NumerusTableSize; ++i) {
|
||||
const NumerusTableEntry &entry = numerusTable[i];
|
||||
for (int j = 0; entry.languages[j] != EOL; ++j) {
|
||||
QLocale loc(entry.languages[j], entry.countries ? entry.countries[j] : QLocale::AnyCountry);
|
||||
QString lang = QLocale::languageToString(entry.languages[j]);
|
||||
if (loc.language() == QLocale::C)
|
||||
lang += QLatin1String(" (!!!)");
|
||||
else if (entry.countries && entry.countries[j] != QLocale::AnyCountry)
|
||||
lang += QLatin1String(" (") + QLocale::countryToString(loc.country()) + QLatin1Char(')');
|
||||
else
|
||||
lang += QLatin1String(" [") + QLocale::countryToString(loc.country()) + QLatin1Char(']');
|
||||
langs << QString::fromLatin1("%1 %2 %3\n").arg(lang, -40).arg(loc.name(), -8)
|
||||
.arg(QString::fromLatin1(entry.gettextRules));
|
||||
}
|
||||
}
|
||||
langs.sort();
|
||||
return langs.join(QString());
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,894 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QTextCodec>
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
// Uncomment if you wish to hard wrap long lines in .po files. Note that this
|
||||
// affects only msg strings, not comments.
|
||||
//#define HARD_WRAP_LONG_WORDS
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static const int MAX_LEN = 79;
|
||||
|
||||
static QString poEscapedString(const QString &prefix, const QString &keyword,
|
||||
bool noWrap, const QString &ba)
|
||||
{
|
||||
QStringList lines;
|
||||
int off = 0;
|
||||
QString res;
|
||||
while (off < ba.length()) {
|
||||
ushort c = ba[off++].unicode();
|
||||
switch (c) {
|
||||
case '\n':
|
||||
res += QLatin1String("\\n");
|
||||
lines.append(res);
|
||||
res.clear();
|
||||
break;
|
||||
case '\r':
|
||||
res += QLatin1String("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
res += QLatin1String("\\t");
|
||||
break;
|
||||
case '\v':
|
||||
res += QLatin1String("\\v");
|
||||
break;
|
||||
case '\a':
|
||||
res += QLatin1String("\\a");
|
||||
break;
|
||||
case '\b':
|
||||
res += QLatin1String("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
res += QLatin1String("\\f");
|
||||
break;
|
||||
case '"':
|
||||
res += QLatin1String("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
res += QLatin1String("\\\\");
|
||||
break;
|
||||
default:
|
||||
if (c < 32) {
|
||||
res += QLatin1String("\\x");
|
||||
res += QString::number(c, 16);
|
||||
if (off < ba.length() && isxdigit(ba[off].unicode()))
|
||||
res += QLatin1String("\"\"");
|
||||
} else {
|
||||
res += QChar(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!res.isEmpty())
|
||||
lines.append(res);
|
||||
if (!lines.isEmpty()) {
|
||||
if (!noWrap) {
|
||||
if (lines.count() != 1 ||
|
||||
lines.first().length() > MAX_LEN - keyword.length() - prefix.length() - 3)
|
||||
{
|
||||
QStringList olines = lines;
|
||||
lines = QStringList(QString());
|
||||
const int maxlen = MAX_LEN - prefix.length() - 2;
|
||||
foreach (const QString &line, olines) {
|
||||
int off = 0;
|
||||
while (off + maxlen < line.length()) {
|
||||
int idx = line.lastIndexOf(QLatin1Char(' '), off + maxlen - 1) + 1;
|
||||
if (idx == off) {
|
||||
#ifdef HARD_WRAP_LONG_WORDS
|
||||
// This doesn't seem too nice, but who knows ...
|
||||
idx = off + maxlen;
|
||||
#else
|
||||
idx = line.indexOf(QLatin1Char(' '), off + maxlen) + 1;
|
||||
if (!idx)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
lines.append(line.mid(off, idx - off));
|
||||
off = idx;
|
||||
}
|
||||
lines.append(line.mid(off));
|
||||
}
|
||||
}
|
||||
} else if (lines.count() > 1) {
|
||||
lines.prepend(QString());
|
||||
}
|
||||
}
|
||||
return prefix + keyword + QLatin1String(" \"") +
|
||||
lines.join(QLatin1String("\"\n") + prefix + QLatin1Char('"')) +
|
||||
QLatin1String("\"\n");
|
||||
}
|
||||
|
||||
static QString poEscapedLines(const QString &prefix, bool addSpace, const QStringList &lines)
|
||||
{
|
||||
QString out;
|
||||
foreach (const QString &line, lines) {
|
||||
out += prefix;
|
||||
if (addSpace && !line.isEmpty())
|
||||
out += QLatin1Char(' ' );
|
||||
out += line;
|
||||
out += QLatin1Char('\n');
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static QString poEscapedLines(const QString &prefix, bool addSpace, const QString &in0)
|
||||
{
|
||||
QString in = in0;
|
||||
if (in.endsWith(QLatin1Char('\n')))
|
||||
in.chop(1);
|
||||
return poEscapedLines(prefix, addSpace, in.split(QLatin1Char('\n')));
|
||||
}
|
||||
|
||||
static QString poWrappedEscapedLines(const QString &prefix, bool addSpace, const QString &line)
|
||||
{
|
||||
const int maxlen = MAX_LEN - prefix.length();
|
||||
QStringList lines;
|
||||
int off = 0;
|
||||
while (off + maxlen < line.length()) {
|
||||
int idx = line.lastIndexOf(QLatin1Char(' '), off + maxlen - 1);
|
||||
if (idx < off) {
|
||||
#if 0 //def HARD_WRAP_LONG_WORDS
|
||||
// This cannot work without messing up semantics, so do not even try.
|
||||
#else
|
||||
idx = line.indexOf(QLatin1Char(' '), off + maxlen);
|
||||
if (idx < 0)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
lines.append(line.mid(off, idx - off));
|
||||
off = idx + 1;
|
||||
}
|
||||
lines.append(line.mid(off));
|
||||
return poEscapedLines(prefix, addSpace, lines);
|
||||
}
|
||||
|
||||
struct PoItem
|
||||
{
|
||||
public:
|
||||
PoItem()
|
||||
: isPlural(false), isFuzzy(false)
|
||||
{}
|
||||
|
||||
|
||||
public:
|
||||
QByteArray id;
|
||||
QByteArray context;
|
||||
QByteArray tscomment;
|
||||
QByteArray oldTscomment;
|
||||
QByteArray lineNumber;
|
||||
QByteArray fileName;
|
||||
QByteArray references;
|
||||
QByteArray translatorComments;
|
||||
QByteArray automaticComments;
|
||||
QByteArray msgId;
|
||||
QByteArray oldMsgId;
|
||||
QList<QByteArray> msgStr;
|
||||
bool isPlural;
|
||||
bool isFuzzy;
|
||||
QHash<QString, QString> extra;
|
||||
};
|
||||
|
||||
|
||||
static bool isTranslationLine(const QByteArray &line)
|
||||
{
|
||||
return line.startsWith("#~ msgstr") || line.startsWith("msgstr");
|
||||
}
|
||||
|
||||
static QByteArray slurpEscapedString(const QList<QByteArray> &lines, int &l,
|
||||
int offset, const QByteArray &prefix, ConversionData &cd)
|
||||
{
|
||||
QByteArray msg;
|
||||
int stoff;
|
||||
|
||||
for (; l < lines.size(); ++l) {
|
||||
const QByteArray &line = lines.at(l);
|
||||
if (line.isEmpty() || !line.startsWith(prefix))
|
||||
break;
|
||||
while (isspace(line[offset])) // No length check, as string has no trailing spaces.
|
||||
offset++;
|
||||
if (line[offset] != '"')
|
||||
break;
|
||||
offset++;
|
||||
forever {
|
||||
if (offset == line.length())
|
||||
goto premature_eol;
|
||||
uchar c = line[offset++];
|
||||
if (c == '"') {
|
||||
if (offset == line.length())
|
||||
break;
|
||||
while (isspace(line[offset]))
|
||||
offset++;
|
||||
if (line[offset++] != '"') {
|
||||
cd.appendError(QString::fromLatin1(
|
||||
"PO parsing error: extra characters on line %1.")
|
||||
.arg(l + 1));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c == '\\') {
|
||||
if (offset == line.length())
|
||||
goto premature_eol;
|
||||
c = line[offset++];
|
||||
switch (c) {
|
||||
case 'r':
|
||||
msg += '\r'; // Maybe just throw it away?
|
||||
break;
|
||||
case 'n':
|
||||
msg += '\n';
|
||||
break;
|
||||
case 't':
|
||||
msg += '\t';
|
||||
break;
|
||||
case 'v':
|
||||
msg += '\v';
|
||||
break;
|
||||
case 'a':
|
||||
msg += '\a';
|
||||
break;
|
||||
case 'b':
|
||||
msg += '\b';
|
||||
break;
|
||||
case 'f':
|
||||
msg += '\f';
|
||||
break;
|
||||
case '"':
|
||||
msg += '"';
|
||||
break;
|
||||
case '\\':
|
||||
msg += '\\';
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
stoff = offset - 1;
|
||||
while ((c = line[offset]) >= '0' && c <= '7')
|
||||
if (++offset == line.length())
|
||||
goto premature_eol;
|
||||
msg += line.mid(stoff, offset - stoff).toUInt(0, 8);
|
||||
break;
|
||||
case 'x':
|
||||
stoff = offset;
|
||||
while (isxdigit(line[offset]))
|
||||
if (++offset == line.length())
|
||||
goto premature_eol;
|
||||
msg += line.mid(stoff, offset - stoff).toUInt(0, 16);
|
||||
break;
|
||||
default:
|
||||
cd.appendError(QString::fromLatin1(
|
||||
"PO parsing error: invalid escape '\\%1' (line %2).")
|
||||
.arg(QChar((uint)c)).arg(l + 1));
|
||||
msg += '\\';
|
||||
msg += c;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
msg += c;
|
||||
}
|
||||
}
|
||||
offset = prefix.size();
|
||||
}
|
||||
--l;
|
||||
return msg;
|
||||
|
||||
premature_eol:
|
||||
cd.appendError(QString::fromLatin1(
|
||||
"PO parsing error: premature end of line %1.").arg(l + 1));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
static void slurpComment(QByteArray &msg, const QList<QByteArray> &lines, int & l)
|
||||
{
|
||||
QByteArray prefix = lines.at(l);
|
||||
for (int i = 1; ; i++) {
|
||||
if (prefix.at(i) != ' ') {
|
||||
prefix.truncate(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; l < lines.size(); ++l) {
|
||||
const QByteArray &line = lines.at(l);
|
||||
if (line.startsWith(prefix))
|
||||
msg += line.mid(prefix.size());
|
||||
else if (line != "#")
|
||||
break;
|
||||
msg += '\n';
|
||||
}
|
||||
--l;
|
||||
}
|
||||
|
||||
static void splitContext(QByteArray *comment, QByteArray *context)
|
||||
{
|
||||
char *data = comment->data();
|
||||
int len = comment->size();
|
||||
int sep = -1, j = 0;
|
||||
|
||||
for (int i = 0; i < len; i++, j++) {
|
||||
if (data[i] == '~' && i + 1 < len)
|
||||
i++;
|
||||
else if (data[i] == '|')
|
||||
sep = j;
|
||||
data[j] = data[i];
|
||||
}
|
||||
if (sep >= 0) {
|
||||
QByteArray tmp = comment->mid(sep + 1, j - sep - 1);
|
||||
comment->truncate(sep);
|
||||
*context = *comment;
|
||||
*comment = tmp;
|
||||
} else {
|
||||
comment->truncate(j);
|
||||
}
|
||||
}
|
||||
|
||||
static QString makePoHeader(const QString &str)
|
||||
{
|
||||
return QLatin1String("po-header-") + str.toLower().replace(QLatin1Char('-'), QLatin1Char('_'));
|
||||
}
|
||||
|
||||
static QByteArray QByteArrayList_join(const QList<QByteArray> &that, char sep)
|
||||
{
|
||||
int totalLength = 0;
|
||||
const int size = that.size();
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
totalLength += that.at(i).size();
|
||||
|
||||
if (size > 0)
|
||||
totalLength += size - 1;
|
||||
|
||||
QByteArray res;
|
||||
if (totalLength == 0)
|
||||
return res;
|
||||
res.reserve(totalLength);
|
||||
for (int i = 0; i < that.size(); ++i) {
|
||||
if (i)
|
||||
res += sep;
|
||||
res += that.at(i);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
QTextCodec *codec = QTextCodec::codecForName(
|
||||
cd.m_codecForSource.isEmpty() ? QByteArray("UTF-8") : cd.m_codecForSource);
|
||||
bool error = false;
|
||||
|
||||
// format of a .po file entry:
|
||||
// white-space
|
||||
// # translator-comments
|
||||
// #. automatic-comments
|
||||
// #: reference...
|
||||
// #, flag...
|
||||
// #~ msgctxt, msgid*, msgstr - used for obsoleted messages
|
||||
// #| msgctxt, msgid* previous untranslated-string - for fuzzy message
|
||||
// msgctx string-context
|
||||
// msgid untranslated-string
|
||||
// -- For singular:
|
||||
// msgstr translated-string
|
||||
// -- For plural:
|
||||
// msgid_plural untranslated-string-plural
|
||||
// msgstr[0] translated-string
|
||||
// ...
|
||||
|
||||
// we need line based lookahead below.
|
||||
QList<QByteArray> lines;
|
||||
while (!dev.atEnd())
|
||||
lines.append(dev.readLine().trimmed());
|
||||
lines.append(QByteArray());
|
||||
|
||||
int l = 0, lastCmtLine = -1;
|
||||
bool qtContexts = false;
|
||||
PoItem item;
|
||||
for (; l != lines.size(); ++l) {
|
||||
QByteArray line = lines.at(l);
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
if (isTranslationLine(line)) {
|
||||
bool isObsolete = line.startsWith("#~ msgstr");
|
||||
const QByteArray prefix = isObsolete ? "#~ " : "";
|
||||
while (true) {
|
||||
int idx = line.indexOf(' ', prefix.length());
|
||||
QByteArray str = slurpEscapedString(lines, l, idx, prefix, cd);
|
||||
item.msgStr.append(str);
|
||||
if (l + 1 >= lines.size() || !isTranslationLine(lines.at(l + 1)))
|
||||
break;
|
||||
++l;
|
||||
line = lines.at(l);
|
||||
}
|
||||
if (item.msgId.isEmpty()) {
|
||||
QHash<QString, QByteArray> extras;
|
||||
QList<QByteArray> hdrOrder;
|
||||
QByteArray pluralForms;
|
||||
foreach (const QByteArray &hdr, item.msgStr.first().split('\n')) {
|
||||
if (hdr.isEmpty())
|
||||
continue;
|
||||
int idx = hdr.indexOf(':');
|
||||
if (idx < 0) {
|
||||
cd.appendError(QString::fromLatin1("Unexpected PO header format '%1'")
|
||||
.arg(QString::fromLatin1(hdr)));
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
QByteArray hdrName = hdr.left(idx).trimmed();
|
||||
QByteArray hdrValue = hdr.mid(idx + 1).trimmed();
|
||||
hdrOrder << hdrName;
|
||||
if (hdrName == "X-Language") {
|
||||
translator.setLanguageCode(QString::fromLatin1(hdrValue));
|
||||
} else if (hdrName == "X-Source-Language") {
|
||||
translator.setSourceLanguageCode(QString::fromLatin1(hdrValue));
|
||||
} else if (hdrName == "X-Qt-Contexts") {
|
||||
qtContexts = (hdrValue == "true");
|
||||
} else if (hdrName == "Plural-Forms") {
|
||||
pluralForms = hdrValue;
|
||||
} else if (hdrName == "MIME-Version") {
|
||||
// just assume it is 1.0
|
||||
} else if (hdrName == "Content-Type") {
|
||||
if (cd.m_codecForSource.isEmpty()) {
|
||||
if (!hdrValue.startsWith("text/plain; charset=")) {
|
||||
cd.appendError(QString::fromLatin1("Unexpected Content-Type header '%1'")
|
||||
.arg(QString::fromLatin1(hdrValue)));
|
||||
error = true;
|
||||
// This will avoid a flood of conversion errors.
|
||||
codec = QTextCodec::codecForName("latin1");
|
||||
} else {
|
||||
QByteArray cod = hdrValue.mid(20);
|
||||
QTextCodec *cdc = QTextCodec::codecForName(cod);
|
||||
if (!cdc) {
|
||||
cd.appendError(QString::fromLatin1("Unsupported codec '%1'")
|
||||
.arg(QString::fromLatin1(cod)));
|
||||
error = true;
|
||||
// This will avoid a flood of conversion errors.
|
||||
codec = QTextCodec::codecForName("latin1");
|
||||
} else {
|
||||
codec = cdc;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (hdrName == "Content-Transfer-Encoding") {
|
||||
if (hdrValue != "8bit") {
|
||||
cd.appendError(QString::fromLatin1("Unexpected Content-Transfer-Encoding '%1'")
|
||||
.arg(QString::fromLatin1(hdrValue)));
|
||||
return false;
|
||||
}
|
||||
} else if (hdrName == "X-Virgin-Header") {
|
||||
// legacy
|
||||
} else {
|
||||
extras[makePoHeader(QString::fromLatin1(hdrName))] = hdrValue;
|
||||
}
|
||||
}
|
||||
if (!pluralForms.isEmpty()) {
|
||||
if (translator.languageCode().isEmpty()) {
|
||||
extras[makePoHeader(QLatin1String("Plural-Forms"))] = pluralForms;
|
||||
} else {
|
||||
// FIXME: have fun with making a consistency check ...
|
||||
}
|
||||
}
|
||||
// Eliminate the field if only headers we added are present in standard order.
|
||||
// Keep in sync with savePO
|
||||
static const char * const dfltHdrs[] = {
|
||||
"MIME-Version", "Content-Type", "Content-Transfer-Encoding",
|
||||
"Plural-Forms", "X-Language", "X-Source-Language", "X-Qt-Contexts"
|
||||
};
|
||||
uint cdh = 0;
|
||||
for (int cho = 0; cho < hdrOrder.length(); cho++) {
|
||||
for (;; cdh++) {
|
||||
if (cdh == sizeof(dfltHdrs)/sizeof(dfltHdrs[0])) {
|
||||
extras[QLatin1String("po-headers")] =
|
||||
QByteArrayList_join(hdrOrder, ',');
|
||||
goto doneho;
|
||||
}
|
||||
if (hdrOrder.at(cho) == dfltHdrs[cdh]) {
|
||||
cdh++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
doneho:
|
||||
if (lastCmtLine != -1)
|
||||
extras[QLatin1String("po-header_comment")] =
|
||||
QByteArrayList_join(lines.mid(0, lastCmtLine + 1), '\n');
|
||||
for (QHash<QString, QByteArray>::ConstIterator it = extras.constBegin(),
|
||||
end = extras.constEnd();
|
||||
it != end; ++it)
|
||||
translator.setExtra(it.key(), codec->toUnicode(it.value()));
|
||||
item = PoItem();
|
||||
continue;
|
||||
}
|
||||
// build translator message
|
||||
TranslatorMessage msg;
|
||||
msg.setContext(codec->toUnicode(item.context));
|
||||
if (!item.references.isEmpty()) {
|
||||
QString xrefs;
|
||||
foreach (const QString &ref,
|
||||
codec->toUnicode(item.references).split(
|
||||
QRegExp(QLatin1String("\\s")), QString::SkipEmptyParts)) {
|
||||
int pos = ref.indexOf(QLatin1Char(':'));
|
||||
int lpos = ref.lastIndexOf(QLatin1Char(':'));
|
||||
if (pos != -1 && pos == lpos) {
|
||||
bool ok;
|
||||
int lno = ref.mid(pos + 1).toInt(&ok);
|
||||
if (ok) {
|
||||
msg.addReference(ref.left(pos), lno);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!xrefs.isEmpty())
|
||||
xrefs += QLatin1Char(' ');
|
||||
xrefs += ref;
|
||||
}
|
||||
if (!xrefs.isEmpty())
|
||||
item.extra[QLatin1String("po-references")] = xrefs;
|
||||
}
|
||||
msg.setId(codec->toUnicode(item.id));
|
||||
msg.setSourceText(codec->toUnicode(item.msgId));
|
||||
msg.setOldSourceText(codec->toUnicode(item.oldMsgId));
|
||||
msg.setComment(codec->toUnicode(item.tscomment));
|
||||
msg.setOldComment(codec->toUnicode(item.oldTscomment));
|
||||
msg.setExtraComment(codec->toUnicode(item.automaticComments));
|
||||
msg.setTranslatorComment(codec->toUnicode(item.translatorComments));
|
||||
msg.setPlural(item.isPlural || item.msgStr.size() > 1);
|
||||
QStringList translations;
|
||||
foreach (const QByteArray &bstr, item.msgStr) {
|
||||
QString str = codec->toUnicode(bstr);
|
||||
str.replace(QChar(Translator::TextVariantSeparator),
|
||||
QChar(Translator::BinaryVariantSeparator));
|
||||
translations << str;
|
||||
}
|
||||
msg.setTranslations(translations);
|
||||
if (isObsolete)
|
||||
msg.setType(TranslatorMessage::Obsolete);
|
||||
else if (item.isFuzzy || (!msg.sourceText().isEmpty() && !msg.isTranslated()))
|
||||
msg.setType(TranslatorMessage::Unfinished);
|
||||
else
|
||||
msg.setType(TranslatorMessage::Finished);
|
||||
msg.setExtras(item.extra);
|
||||
|
||||
//qDebug() << "WRITE: " << context;
|
||||
//qDebug() << "SOURCE: " << msg.sourceText();
|
||||
//qDebug() << flags << msg.m_extra;
|
||||
translator.append(msg);
|
||||
item = PoItem();
|
||||
} else if (line.startsWith('#')) {
|
||||
switch (line.size() < 2 ? 0 : line.at(1)) {
|
||||
case ':':
|
||||
item.references += line.mid(3);
|
||||
item.references += '\n';
|
||||
break;
|
||||
case ',': {
|
||||
QStringList flags =
|
||||
QString::fromLatin1(line.mid(2)).split(
|
||||
QRegExp(QLatin1String("[, ]")), QString::SkipEmptyParts);
|
||||
if (flags.removeOne(QLatin1String("fuzzy")))
|
||||
item.isFuzzy = true;
|
||||
flags.removeOne(QLatin1String("qt-format"));
|
||||
TranslatorMessage::ExtraData::const_iterator it =
|
||||
item.extra.constFind(QLatin1String("po-flags"));
|
||||
if (it != item.extra.constEnd())
|
||||
flags.prepend(*it);
|
||||
if (!flags.isEmpty())
|
||||
item.extra[QLatin1String("po-flags")] = flags.join(QLatin1String(", "));
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
item.translatorComments += '\n';
|
||||
break;
|
||||
case ' ':
|
||||
slurpComment(item.translatorComments, lines, l);
|
||||
break;
|
||||
case '.':
|
||||
if (line.startsWith("#. ts-context ")) { // legacy
|
||||
item.context = line.mid(14);
|
||||
} else if (line.startsWith("#. ts-id ")) {
|
||||
item.id = line.mid(9);
|
||||
} else {
|
||||
item.automaticComments += line.mid(3);
|
||||
item.automaticComments += '\n';
|
||||
}
|
||||
break;
|
||||
case '|':
|
||||
if (line.startsWith("#| msgid ")) {
|
||||
item.oldMsgId = slurpEscapedString(lines, l, 9, "#| ", cd);
|
||||
} else if (line.startsWith("#| msgid_plural ")) {
|
||||
QByteArray extra = slurpEscapedString(lines, l, 16, "#| ", cd);
|
||||
if (extra != item.oldMsgId)
|
||||
item.extra[QLatin1String("po-old_msgid_plural")] =
|
||||
codec->toUnicode(extra);
|
||||
} else if (line.startsWith("#| msgctxt ")) {
|
||||
item.oldTscomment = slurpEscapedString(lines, l, 11, "#| ", cd);
|
||||
if (qtContexts)
|
||||
splitContext(&item.oldTscomment, &item.context);
|
||||
} else {
|
||||
cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'"))
|
||||
.arg(l + 1).arg(codec->toUnicode(lines[l])));
|
||||
error = true;
|
||||
}
|
||||
break;
|
||||
case '~':
|
||||
if (line.startsWith("#~ msgid ")) {
|
||||
item.msgId = slurpEscapedString(lines, l, 9, "#~ ", cd);
|
||||
} else if (line.startsWith("#~ msgid_plural ")) {
|
||||
QByteArray extra = slurpEscapedString(lines, l, 16, "#~ ", cd);
|
||||
if (extra != item.msgId)
|
||||
item.extra[QLatin1String("po-msgid_plural")] =
|
||||
codec->toUnicode(extra);
|
||||
item.isPlural = true;
|
||||
} else if (line.startsWith("#~ msgctxt ")) {
|
||||
item.tscomment = slurpEscapedString(lines, l, 11, "#~ ", cd);
|
||||
if (qtContexts)
|
||||
splitContext(&item.tscomment, &item.context);
|
||||
} else {
|
||||
cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'"))
|
||||
.arg(l + 1).arg(codec->toUnicode(lines[l])));
|
||||
error = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'"))
|
||||
.arg(l + 1).arg(codec->toUnicode(lines[l])));
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
lastCmtLine = l;
|
||||
} else if (line.startsWith("msgctxt ")) {
|
||||
item.tscomment = slurpEscapedString(lines, l, 8, QByteArray(), cd);
|
||||
if (qtContexts)
|
||||
splitContext(&item.tscomment, &item.context);
|
||||
} else if (line.startsWith("msgid ")) {
|
||||
item.msgId = slurpEscapedString(lines, l, 6, QByteArray(), cd);
|
||||
} else if (line.startsWith("msgid_plural ")) {
|
||||
QByteArray extra = slurpEscapedString(lines, l, 13, QByteArray(), cd);
|
||||
if (extra != item.msgId)
|
||||
item.extra[QLatin1String("po-msgid_plural")] = codec->toUnicode(extra);
|
||||
item.isPlural = true;
|
||||
} else {
|
||||
cd.appendError(QString(QLatin1String("PO-format error in line %1: '%2'"))
|
||||
.arg(l + 1).arg(codec->toUnicode(lines[l])));
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
return !error && cd.errors().isEmpty();
|
||||
}
|
||||
|
||||
static void addPoHeader(Translator::ExtraData &headers, QStringList &hdrOrder,
|
||||
const char *name, const QString &value)
|
||||
{
|
||||
QString qName = QLatin1String(name);
|
||||
if (!hdrOrder.contains(qName))
|
||||
hdrOrder << qName;
|
||||
headers[makePoHeader(qName)] = value;
|
||||
}
|
||||
|
||||
static QString escapeComment(const QString &in, bool escape)
|
||||
{
|
||||
QString out = in;
|
||||
if (escape) {
|
||||
out.replace(QLatin1Char('~'), QLatin1String("~~"));
|
||||
out.replace(QLatin1Char('|'), QLatin1String("~|"));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
QString str_format = QLatin1String("-format");
|
||||
|
||||
bool ok = true;
|
||||
QTextStream out(&dev);
|
||||
out.setCodec(cd.m_outputCodec.isEmpty() ? QByteArray("UTF-8") : cd.m_outputCodec);
|
||||
|
||||
bool qtContexts = false;
|
||||
foreach (const TranslatorMessage &msg, translator.messages())
|
||||
if (!msg.context().isEmpty()) {
|
||||
qtContexts = true;
|
||||
break;
|
||||
}
|
||||
|
||||
QString cmt = translator.extra(QLatin1String("po-header_comment"));
|
||||
if (!cmt.isEmpty())
|
||||
out << cmt << '\n';
|
||||
out << "msgid \"\"\n";
|
||||
Translator::ExtraData headers = translator.extras();
|
||||
QStringList hdrOrder = translator.extra(QLatin1String("po-headers")).split(
|
||||
QLatin1Char(','), QString::SkipEmptyParts);
|
||||
QString hdrCharset = QLatin1String("text/plain; charset=") + QLatin1String(out.codec()->name());
|
||||
// Keep in sync with loadPO
|
||||
addPoHeader(headers, hdrOrder, "MIME-Version", QLatin1String("1.0"));
|
||||
addPoHeader(headers, hdrOrder, "Content-Type", hdrCharset);
|
||||
addPoHeader(headers, hdrOrder, "Content-Transfer-Encoding", QLatin1String("8bit"));
|
||||
if (!translator.languageCode().isEmpty()) {
|
||||
QLocale::Language l;
|
||||
QLocale::Country c;
|
||||
Translator::languageAndCountry(translator.languageCode(), &l, &c);
|
||||
const char *gettextRules;
|
||||
if (getNumerusInfo(l, c, 0, 0, &gettextRules))
|
||||
addPoHeader(headers, hdrOrder, "Plural-Forms", QLatin1String(gettextRules));
|
||||
addPoHeader(headers, hdrOrder, "X-Language", translator.languageCode());
|
||||
}
|
||||
if (!translator.sourceLanguageCode().isEmpty())
|
||||
addPoHeader(headers, hdrOrder, "X-Source-Language", translator.sourceLanguageCode());
|
||||
if (qtContexts)
|
||||
addPoHeader(headers, hdrOrder, "X-Qt-Contexts", QLatin1String("true"));
|
||||
QString hdrStr;
|
||||
foreach (const QString &hdr, hdrOrder) {
|
||||
hdrStr += hdr;
|
||||
hdrStr += QLatin1String(": ");
|
||||
hdrStr += headers.value(makePoHeader(hdr));
|
||||
hdrStr += QLatin1Char('\n');
|
||||
}
|
||||
out << poEscapedString(QString(), QString::fromLatin1("msgstr"), true, hdrStr);
|
||||
|
||||
foreach (const TranslatorMessage &msg, translator.messages()) {
|
||||
out << endl;
|
||||
|
||||
if (!msg.translatorComment().isEmpty())
|
||||
out << poEscapedLines(QLatin1String("#"), true, msg.translatorComment());
|
||||
|
||||
if (!msg.extraComment().isEmpty())
|
||||
out << poEscapedLines(QLatin1String("#."), true, msg.extraComment());
|
||||
|
||||
if (!msg.id().isEmpty())
|
||||
out << QLatin1String("#. ts-id ") << msg.id() << '\n';
|
||||
|
||||
QString xrefs = msg.extra(QLatin1String("po-references"));
|
||||
if (!msg.fileName().isEmpty() || !xrefs.isEmpty()) {
|
||||
QStringList refs;
|
||||
foreach (const TranslatorMessage::Reference &ref, msg.allReferences())
|
||||
refs.append(QString(QLatin1String("%2:%1"))
|
||||
.arg(ref.lineNumber()).arg(ref.fileName()));
|
||||
if (!xrefs.isEmpty())
|
||||
refs << xrefs;
|
||||
out << poWrappedEscapedLines(QLatin1String("#:"), true, refs.join(QLatin1String(" ")));
|
||||
}
|
||||
|
||||
bool noWrap = false;
|
||||
bool skipFormat = false;
|
||||
QStringList flags;
|
||||
if (msg.type() == TranslatorMessage::Unfinished && msg.isTranslated())
|
||||
flags.append(QLatin1String("fuzzy"));
|
||||
TranslatorMessage::ExtraData::const_iterator itr =
|
||||
msg.extras().constFind(QLatin1String("po-flags"));
|
||||
if (itr != msg.extras().constEnd()) {
|
||||
QStringList atoms = itr->split(QLatin1String(", "));
|
||||
foreach (const QString &atom, atoms)
|
||||
if (atom.endsWith(str_format)) {
|
||||
skipFormat = true;
|
||||
break;
|
||||
}
|
||||
if (atoms.contains(QLatin1String("no-wrap")))
|
||||
noWrap = true;
|
||||
flags.append(*itr);
|
||||
}
|
||||
if (!skipFormat) {
|
||||
QString source = msg.sourceText();
|
||||
// This is fuzzy logic, as we don't know whether the string is
|
||||
// actually used with QString::arg().
|
||||
for (int off = 0; (off = source.indexOf(QLatin1Char('%'), off)) >= 0; ) {
|
||||
if (++off >= source.length())
|
||||
break;
|
||||
if (source.at(off) == QLatin1Char('n') || source.at(off).isDigit()) {
|
||||
flags.append(QLatin1String("qt-format"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!flags.isEmpty())
|
||||
out << "#, " << flags.join(QLatin1String(", ")) << '\n';
|
||||
|
||||
QString prefix = QLatin1String("#| ");
|
||||
if (!msg.oldComment().isEmpty())
|
||||
out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap,
|
||||
escapeComment(msg.oldComment(), qtContexts));
|
||||
if (!msg.oldSourceText().isEmpty())
|
||||
out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.oldSourceText());
|
||||
QString oldplural = msg.extra(QLatin1String("po-old_msgid_plural"));
|
||||
if (!oldplural.isEmpty())
|
||||
out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, oldplural);
|
||||
prefix = QLatin1String((msg.type() == TranslatorMessage::Obsolete) ? "#~ " : "");
|
||||
if (!msg.context().isEmpty())
|
||||
out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap,
|
||||
escapeComment(msg.context(), true) + QLatin1Char('|')
|
||||
+ escapeComment(msg.comment(), true));
|
||||
else if (!msg.comment().isEmpty())
|
||||
out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap,
|
||||
escapeComment(msg.comment(), qtContexts));
|
||||
out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.sourceText());
|
||||
if (!msg.isPlural()) {
|
||||
QString transl = msg.translation();
|
||||
transl.replace(QChar(Translator::BinaryVariantSeparator),
|
||||
QChar(Translator::TextVariantSeparator));
|
||||
out << poEscapedString(prefix, QLatin1String("msgstr"), noWrap, transl);
|
||||
} else {
|
||||
QString plural = msg.extra(QLatin1String("po-msgid_plural"));
|
||||
if (plural.isEmpty())
|
||||
plural = msg.sourceText();
|
||||
out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
|
||||
const QStringList &translations = msg.translations();
|
||||
for (int i = 0; i != translations.size(); ++i) {
|
||||
QString str = translations.at(i);
|
||||
str.replace(QChar(Translator::BinaryVariantSeparator),
|
||||
QChar(Translator::TextVariantSeparator));
|
||||
out << poEscapedString(prefix, QString::fromLatin1("msgstr[%1]").arg(i), noWrap,
|
||||
str);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool savePOT(const Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
Translator ttor = translator;
|
||||
ttor.dropTranslations();
|
||||
return savePO(ttor, dev, cd);
|
||||
}
|
||||
|
||||
int initPO()
|
||||
{
|
||||
Translator::FileFormat format;
|
||||
format.extension = QLatin1String("po");
|
||||
format.description = QObject::tr("GNU Gettext localization files");
|
||||
format.loader = &loadPO;
|
||||
format.saver = &savePO;
|
||||
format.fileType = Translator::FileFormat::TranslationSource;
|
||||
format.priority = 1;
|
||||
Translator::registerFileFormat(format);
|
||||
format.extension = QLatin1String("pot");
|
||||
format.description = QObject::tr("GNU Gettext localization template files");
|
||||
format.loader = &loadPO;
|
||||
format.saver = &savePOT;
|
||||
format.fileType = Translator::FileFormat::TranslationSource;
|
||||
format.priority = -1;
|
||||
Translator::registerFileFormat(format);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Q_CONSTRUCTOR_FUNCTION(initPO)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,779 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QTextCodec>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// magic number for the file
|
||||
static const int MagicLength = 16;
|
||||
static const uchar magic[MagicLength] = {
|
||||
0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
|
||||
0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
enum Tag {
|
||||
Tag_End = 1,
|
||||
Tag_SourceText16 = 2,
|
||||
Tag_Translation = 3,
|
||||
Tag_Context16 = 4,
|
||||
Tag_Obsolete1 = 5,
|
||||
Tag_SourceText = 6,
|
||||
Tag_Context = 7,
|
||||
Tag_Comment = 8,
|
||||
Tag_Obsolete2 = 9
|
||||
};
|
||||
|
||||
enum Prefix {
|
||||
NoPrefix,
|
||||
Hash,
|
||||
HashContext,
|
||||
HashContextSourceText,
|
||||
HashContextSourceTextComment
|
||||
};
|
||||
|
||||
} // namespace anon
|
||||
|
||||
static uint elfHash(const QByteArray &ba)
|
||||
{
|
||||
const uchar *k = (const uchar *)ba.data();
|
||||
uint h = 0;
|
||||
uint g;
|
||||
|
||||
if (k) {
|
||||
while (*k) {
|
||||
h = (h << 4) + *k++;
|
||||
if ((g = (h & 0xf0000000)) != 0)
|
||||
h ^= g >> 24;
|
||||
h &= ~g;
|
||||
}
|
||||
}
|
||||
if (!h)
|
||||
h = 1;
|
||||
return h;
|
||||
}
|
||||
|
||||
class ByteTranslatorMessage
|
||||
{
|
||||
public:
|
||||
ByteTranslatorMessage(
|
||||
const QByteArray &context,
|
||||
const QByteArray &sourceText,
|
||||
const QByteArray &comment,
|
||||
const QStringList &translations) :
|
||||
m_context(context),
|
||||
m_sourcetext(sourceText),
|
||||
m_comment(comment),
|
||||
m_translations(translations)
|
||||
{}
|
||||
const QByteArray &context() const { return m_context; }
|
||||
const QByteArray &sourceText() const { return m_sourcetext; }
|
||||
const QByteArray &comment() const { return m_comment; }
|
||||
const QStringList &translations() const { return m_translations; }
|
||||
bool operator<(const ByteTranslatorMessage& m) const;
|
||||
|
||||
private:
|
||||
QByteArray m_context;
|
||||
QByteArray m_sourcetext;
|
||||
QByteArray m_comment;
|
||||
QStringList m_translations;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(ByteTranslatorMessage, Q_MOVABLE_TYPE);
|
||||
|
||||
bool ByteTranslatorMessage::operator<(const ByteTranslatorMessage& m) const
|
||||
{
|
||||
if (m_context != m.m_context)
|
||||
return m_context < m.m_context;
|
||||
if (m_sourcetext != m.m_sourcetext)
|
||||
return m_sourcetext < m.m_sourcetext;
|
||||
return m_comment < m.m_comment;
|
||||
}
|
||||
|
||||
class Releaser
|
||||
{
|
||||
public:
|
||||
struct Offset {
|
||||
Offset()
|
||||
: h(0), o(0)
|
||||
{}
|
||||
Offset(uint hash, uint offset)
|
||||
: h(hash), o(offset)
|
||||
{}
|
||||
|
||||
bool operator<(const Offset &other) const {
|
||||
return (h != other.h) ? h < other.h : o < other.o;
|
||||
}
|
||||
bool operator==(const Offset &other) const {
|
||||
return h == other.h && o == other.o;
|
||||
}
|
||||
uint h;
|
||||
uint o;
|
||||
};
|
||||
|
||||
enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 };
|
||||
|
||||
Releaser() : m_codec(0) {}
|
||||
|
||||
void setCodecName(const QByteArray &codecName)
|
||||
{
|
||||
m_codec = QTextCodec::codecForName(codecName);
|
||||
}
|
||||
|
||||
bool save(QIODevice *iod);
|
||||
|
||||
void insert(const TranslatorMessage &msg, const QStringList &tlns, bool forceComment);
|
||||
void insertIdBased(const TranslatorMessage &message, const QStringList &tlns);
|
||||
|
||||
void squeeze(TranslatorSaveMode mode);
|
||||
|
||||
void setNumerusRules(const QByteArray &rules);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(Releaser)
|
||||
|
||||
// This should reproduce the byte array fetched from the source file, which
|
||||
// on turn should be the same as passed to the actual tr(...) calls
|
||||
QByteArray originalBytes(const QString &str, bool isUtf8) const;
|
||||
|
||||
void insertInternal(const TranslatorMessage &message, const QStringList &tlns,
|
||||
bool forceComment, bool isUtf8);
|
||||
|
||||
static Prefix commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2);
|
||||
|
||||
static uint msgHash(const ByteTranslatorMessage &msg);
|
||||
|
||||
void writeMessage(const ByteTranslatorMessage & msg, QDataStream & stream,
|
||||
TranslatorSaveMode strip, Prefix prefix) const;
|
||||
|
||||
// for squeezed but non-file data, this is what needs to be deleted
|
||||
QByteArray m_messageArray;
|
||||
QByteArray m_offsetArray;
|
||||
QByteArray m_contextArray;
|
||||
QMap<ByteTranslatorMessage, void *> m_messages;
|
||||
QByteArray m_numerusRules;
|
||||
|
||||
// Used to reproduce the original bytes
|
||||
QTextCodec *m_codec;
|
||||
};
|
||||
|
||||
QByteArray Releaser::originalBytes(const QString &str, bool isUtf8) const
|
||||
{
|
||||
if (str.isEmpty()) {
|
||||
// Do not use QByteArray() here as the result of the serialization
|
||||
// will be different.
|
||||
return QByteArray("");
|
||||
}
|
||||
if (isUtf8)
|
||||
return str.toUtf8();
|
||||
return m_codec ? m_codec->fromUnicode(str) : str.toLatin1();
|
||||
}
|
||||
|
||||
uint Releaser::msgHash(const ByteTranslatorMessage &msg)
|
||||
{
|
||||
return elfHash(msg.sourceText() + msg.comment());
|
||||
}
|
||||
|
||||
Prefix Releaser::commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2)
|
||||
{
|
||||
if (msgHash(m1) != msgHash(m2))
|
||||
return NoPrefix;
|
||||
if (m1.context() != m2.context())
|
||||
return Hash;
|
||||
if (m1.sourceText() != m2.sourceText())
|
||||
return HashContext;
|
||||
if (m1.comment() != m2.comment())
|
||||
return HashContextSourceText;
|
||||
return HashContextSourceTextComment;
|
||||
}
|
||||
|
||||
void Releaser::writeMessage(const ByteTranslatorMessage &msg, QDataStream &stream,
|
||||
TranslatorSaveMode mode, Prefix prefix) const
|
||||
{
|
||||
for (int i = 0; i < msg.translations().count(); ++i)
|
||||
stream << quint8(Tag_Translation) << msg.translations().at(i);
|
||||
|
||||
if (mode == SaveEverything)
|
||||
prefix = HashContextSourceTextComment;
|
||||
|
||||
// lrelease produces "wrong" QM files for QByteArrays that are .isNull().
|
||||
switch (prefix) {
|
||||
default:
|
||||
case HashContextSourceTextComment:
|
||||
stream << quint8(Tag_Comment) << msg.comment();
|
||||
// fall through
|
||||
case HashContextSourceText:
|
||||
stream << quint8(Tag_SourceText) << msg.sourceText();
|
||||
// fall through
|
||||
case HashContext:
|
||||
stream << quint8(Tag_Context) << msg.context();
|
||||
break;
|
||||
}
|
||||
|
||||
stream << quint8(Tag_End);
|
||||
}
|
||||
|
||||
|
||||
bool Releaser::save(QIODevice *iod)
|
||||
{
|
||||
QDataStream s(iod);
|
||||
s.writeRawData((const char *)magic, MagicLength);
|
||||
|
||||
if (!m_offsetArray.isEmpty()) {
|
||||
quint32 oas = quint32(m_offsetArray.size());
|
||||
s << quint8(Hashes) << oas;
|
||||
s.writeRawData(m_offsetArray.constData(), oas);
|
||||
}
|
||||
if (!m_messageArray.isEmpty()) {
|
||||
quint32 mas = quint32(m_messageArray.size());
|
||||
s << quint8(Messages) << mas;
|
||||
s.writeRawData(m_messageArray.constData(), mas);
|
||||
}
|
||||
if (!m_contextArray.isEmpty()) {
|
||||
quint32 cas = quint32(m_contextArray.size());
|
||||
s << quint8(Contexts) << cas;
|
||||
s.writeRawData(m_contextArray.constData(), cas);
|
||||
}
|
||||
if (!m_numerusRules.isEmpty()) {
|
||||
quint32 nrs = m_numerusRules.size();
|
||||
s << quint8(NumerusRules) << nrs;
|
||||
s.writeRawData(m_numerusRules.constData(), nrs);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Releaser::squeeze(TranslatorSaveMode mode)
|
||||
{
|
||||
if (m_messages.isEmpty() && mode == SaveEverything)
|
||||
return;
|
||||
|
||||
QMap<ByteTranslatorMessage, void *> messages = m_messages;
|
||||
|
||||
// re-build contents
|
||||
m_messageArray.clear();
|
||||
m_offsetArray.clear();
|
||||
m_contextArray.clear();
|
||||
m_messages.clear();
|
||||
|
||||
QMap<Offset, void *> offsets;
|
||||
|
||||
QDataStream ms(&m_messageArray, QIODevice::WriteOnly);
|
||||
QMap<ByteTranslatorMessage, void *>::const_iterator it, next;
|
||||
int cpPrev = 0, cpNext = 0;
|
||||
for (it = messages.constBegin(); it != messages.constEnd(); ++it) {
|
||||
cpPrev = cpNext;
|
||||
next = it;
|
||||
++next;
|
||||
if (next == messages.constEnd())
|
||||
cpNext = 0;
|
||||
else
|
||||
cpNext = commonPrefix(it.key(), next.key());
|
||||
offsets.insert(Offset(msgHash(it.key()), ms.device()->pos()), (void *)0);
|
||||
writeMessage(it.key(), ms, mode, Prefix(qMax(cpPrev, cpNext + 1)));
|
||||
}
|
||||
|
||||
QMap<Offset, void *>::const_iterator offset = offsets.constBegin();
|
||||
QDataStream ds(&m_offsetArray, QIODevice::WriteOnly);
|
||||
while (offset != offsets.constEnd()) {
|
||||
Offset k = offset.key();
|
||||
++offset;
|
||||
ds << quint32(k.h) << quint32(k.o);
|
||||
}
|
||||
|
||||
if (mode == SaveStripped) {
|
||||
QMap<QByteArray, int> contextSet;
|
||||
for (it = messages.constBegin(); it != messages.constEnd(); ++it)
|
||||
++contextSet[it.key().context()];
|
||||
|
||||
quint16 hTableSize;
|
||||
if (contextSet.size() < 200)
|
||||
hTableSize = (contextSet.size() < 60) ? 151 : 503;
|
||||
else if (contextSet.size() < 2500)
|
||||
hTableSize = (contextSet.size() < 750) ? 1511 : 5003;
|
||||
else
|
||||
hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2;
|
||||
|
||||
QMultiMap<int, QByteArray> hashMap;
|
||||
QMap<QByteArray, int>::const_iterator c;
|
||||
for (c = contextSet.constBegin(); c != contextSet.constEnd(); ++c)
|
||||
hashMap.insert(elfHash(c.key()) % hTableSize, c.key());
|
||||
|
||||
/*
|
||||
The contexts found in this translator are stored in a hash
|
||||
table to provide fast lookup. The context array has the
|
||||
following format:
|
||||
|
||||
quint16 hTableSize;
|
||||
quint16 hTable[hTableSize];
|
||||
quint8 contextPool[...];
|
||||
|
||||
The context pool stores the contexts as Pascal strings:
|
||||
|
||||
quint8 len;
|
||||
quint8 data[len];
|
||||
|
||||
Let's consider the look-up of context "FunnyDialog". A
|
||||
hash value between 0 and hTableSize - 1 is computed, say h.
|
||||
If hTable[h] is 0, "FunnyDialog" is not covered by this
|
||||
translator. Else, we check in the contextPool at offset
|
||||
2 * hTable[h] to see if "FunnyDialog" is one of the
|
||||
contexts stored there, until we find it or we meet the
|
||||
empty string.
|
||||
*/
|
||||
m_contextArray.resize(2 + (hTableSize << 1));
|
||||
QDataStream t(&m_contextArray, QIODevice::WriteOnly);
|
||||
|
||||
quint16 *hTable = new quint16[hTableSize];
|
||||
memset(hTable, 0, hTableSize * sizeof(quint16));
|
||||
|
||||
t << hTableSize;
|
||||
t.device()->seek(2 + (hTableSize << 1));
|
||||
t << quint16(0); // the entry at offset 0 cannot be used
|
||||
uint upto = 2;
|
||||
|
||||
QMap<int, QByteArray>::const_iterator entry = hashMap.constBegin();
|
||||
while (entry != hashMap.constEnd()) {
|
||||
int i = entry.key();
|
||||
hTable[i] = quint16(upto >> 1);
|
||||
|
||||
do {
|
||||
const char *con = entry.value().constData();
|
||||
uint len = uint(entry.value().length());
|
||||
len = qMin(len, 255u);
|
||||
t << quint8(len);
|
||||
t.writeRawData(con, len);
|
||||
upto += 1 + len;
|
||||
++entry;
|
||||
} while (entry != hashMap.constEnd() && entry.key() == i);
|
||||
if (upto & 0x1) {
|
||||
// offsets have to be even
|
||||
t << quint8(0); // empty string
|
||||
++upto;
|
||||
}
|
||||
}
|
||||
t.device()->seek(2);
|
||||
for (int j = 0; j < hTableSize; j++)
|
||||
t << hTable[j];
|
||||
delete [] hTable;
|
||||
|
||||
if (upto > 131072) {
|
||||
qWarning("Releaser::squeeze: Too many contexts");
|
||||
m_contextArray.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Releaser::insertInternal(const TranslatorMessage &message, const QStringList &tlns,
|
||||
bool forceComment, bool isUtf8)
|
||||
{
|
||||
ByteTranslatorMessage bmsg(originalBytes(message.context(), isUtf8),
|
||||
originalBytes(message.sourceText(), isUtf8),
|
||||
originalBytes(message.comment(), isUtf8),
|
||||
tlns);
|
||||
if (!forceComment) {
|
||||
ByteTranslatorMessage bmsg2(
|
||||
bmsg.context(), bmsg.sourceText(), QByteArray(""), bmsg.translations());
|
||||
if (!m_messages.contains(bmsg2)) {
|
||||
m_messages.insert(bmsg2, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_messages.insert(bmsg, 0);
|
||||
}
|
||||
|
||||
void Releaser::insert(const TranslatorMessage &message, const QStringList &tlns, bool forceComment)
|
||||
{
|
||||
insertInternal(message, tlns, forceComment, message.isUtf8());
|
||||
if (message.isUtf8() && message.isNonUtf8())
|
||||
insertInternal(message, tlns, forceComment, false);
|
||||
}
|
||||
|
||||
void Releaser::insertIdBased(const TranslatorMessage &message, const QStringList &tlns)
|
||||
{
|
||||
ByteTranslatorMessage bmsg("", originalBytes(message.id(), false), "", tlns);
|
||||
m_messages.insert(bmsg, 0);
|
||||
}
|
||||
|
||||
void Releaser::setNumerusRules(const QByteArray &rules)
|
||||
{
|
||||
m_numerusRules = rules;
|
||||
}
|
||||
|
||||
static quint8 read8(const uchar *data)
|
||||
{
|
||||
return *data;
|
||||
}
|
||||
|
||||
static quint32 read32(const uchar *data)
|
||||
{
|
||||
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
|
||||
}
|
||||
|
||||
static void fromBytes(const char *str, int len, QTextCodec *codec, QTextCodec *utf8Codec,
|
||||
QString *out, QString *utf8Out,
|
||||
bool *isSystem, bool *isUtf8, bool *needs8Bit)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
if (str[i] & 0x80) {
|
||||
if (utf8Codec) {
|
||||
QTextCodec::ConverterState cvtState;
|
||||
*utf8Out = utf8Codec->toUnicode(str, len, &cvtState);
|
||||
*isUtf8 = !cvtState.invalidChars;
|
||||
}
|
||||
QTextCodec::ConverterState cvtState;
|
||||
*out = codec->toUnicode(str, len, &cvtState);
|
||||
*isSystem = !cvtState.invalidChars;
|
||||
*needs8Bit = true;
|
||||
return;
|
||||
}
|
||||
*out = QString::fromLatin1(str, len);
|
||||
*isSystem = true;
|
||||
if (utf8Codec) {
|
||||
*utf8Out = *out;
|
||||
*isUtf8 = true;
|
||||
}
|
||||
*needs8Bit = false;
|
||||
}
|
||||
|
||||
bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
QByteArray ba = dev.readAll();
|
||||
const uchar *data = reinterpret_cast<const uchar*>(ba.constData());
|
||||
int datalen = ba.size();
|
||||
if (datalen < MagicLength || memcmp(data, magic, MagicLength) != 0) {
|
||||
cd.appendError(QLatin1String("QM-Format error: magic marker missing"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 };
|
||||
|
||||
// for squeezed but non-file data, this is what needs to be deleted
|
||||
const uchar *messageArray = 0;
|
||||
const uchar *offsetArray = 0;
|
||||
uint offsetLength = 0;
|
||||
|
||||
bool ok = true;
|
||||
const uchar *end = data + datalen;
|
||||
|
||||
data += MagicLength;
|
||||
|
||||
while (data < end - 4) {
|
||||
quint8 tag = read8(data++);
|
||||
quint32 blockLen = read32(data);
|
||||
//qDebug() << "TAG:" << tag << "BLOCKLEN:" << blockLen;
|
||||
data += 4;
|
||||
if (!tag || !blockLen)
|
||||
break;
|
||||
if (data + blockLen > end) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == Hashes) {
|
||||
offsetArray = data;
|
||||
offsetLength = blockLen;
|
||||
//qDebug() << "HASHES: " << blockLen << QByteArray((const char *)data, blockLen).toHex();
|
||||
} else if (tag == Messages) {
|
||||
messageArray = data;
|
||||
//qDebug() << "MESSAGES: " << blockLen << QByteArray((const char *)data, blockLen).toHex();
|
||||
}
|
||||
|
||||
data += blockLen;
|
||||
}
|
||||
|
||||
|
||||
size_t numItems = offsetLength / (2 * sizeof(quint32));
|
||||
//qDebug() << "NUMITEMS: " << numItems;
|
||||
|
||||
QTextCodec *codec = QTextCodec::codecForName(
|
||||
cd.m_codecForSource.isEmpty() ? QByteArray("Latin1") : cd.m_codecForSource);
|
||||
QTextCodec *utf8Codec = 0;
|
||||
if (codec->name() != "UTF-8")
|
||||
utf8Codec = QTextCodec::codecForName("UTF-8");
|
||||
|
||||
QString strProN = QLatin1String("%n");
|
||||
QLocale::Language l;
|
||||
QLocale::Country c;
|
||||
Translator::languageAndCountry(translator.languageCode(), &l, &c);
|
||||
QStringList numerusForms;
|
||||
bool guessPlurals = true;
|
||||
if (getNumerusInfo(l, c, 0, &numerusForms, 0))
|
||||
guessPlurals = (numerusForms.count() == 1);
|
||||
|
||||
QString context, contextUtf8;
|
||||
bool contextIsSystem, contextIsUtf8, contextNeeds8Bit;
|
||||
QString sourcetext, sourcetextUtf8;
|
||||
bool sourcetextIsSystem, sourcetextIsUtf8, sourcetextNeeds8Bit;
|
||||
QString comment, commentUtf8;
|
||||
bool commentIsSystem, commentIsUtf8, commentNeeds8Bit;
|
||||
QStringList translations;
|
||||
|
||||
for (const uchar *start = offsetArray; start != offsetArray + (numItems << 3); start += 8) {
|
||||
//quint32 hash = read32(start);
|
||||
quint32 ro = read32(start + 4);
|
||||
//qDebug() << "\nHASH:" << hash;
|
||||
const uchar *m = messageArray + ro;
|
||||
|
||||
for (;;) {
|
||||
uchar tag = read8(m++);
|
||||
//qDebug() << "Tag:" << tag << " ADDR: " << m;
|
||||
switch(tag) {
|
||||
case Tag_End:
|
||||
goto end;
|
||||
case Tag_Translation: {
|
||||
int len = read32(m);
|
||||
if (len % 1) {
|
||||
cd.appendError(QLatin1String("QM-Format error"));
|
||||
return false;
|
||||
}
|
||||
m += 4;
|
||||
QString str = QString((const QChar *)m, len/2);
|
||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
str[i] = QChar((str.at(i).unicode() >> 8) +
|
||||
((str.at(i).unicode() << 8) & 0xff00));
|
||||
}
|
||||
#endif
|
||||
translations << str;
|
||||
m += len;
|
||||
break;
|
||||
}
|
||||
case Tag_Obsolete1:
|
||||
m += 4;
|
||||
//qDebug() << "OBSOLETE";
|
||||
break;
|
||||
case Tag_SourceText: {
|
||||
quint32 len = read32(m);
|
||||
m += 4;
|
||||
//qDebug() << "SOURCE LEN: " << len;
|
||||
//qDebug() << "SOURCE: " << QByteArray((const char*)m, len);
|
||||
fromBytes((const char*)m, len, codec, utf8Codec,
|
||||
&sourcetext, &sourcetextUtf8,
|
||||
&sourcetextIsSystem, &sourcetextIsUtf8, &sourcetextNeeds8Bit);
|
||||
m += len;
|
||||
break;
|
||||
}
|
||||
case Tag_Context: {
|
||||
quint32 len = read32(m);
|
||||
m += 4;
|
||||
//qDebug() << "CONTEXT LEN: " << len;
|
||||
//qDebug() << "CONTEXT: " << QByteArray((const char*)m, len);
|
||||
fromBytes((const char*)m, len, codec, utf8Codec,
|
||||
&context, &contextUtf8,
|
||||
&contextIsSystem, &contextIsUtf8, &contextNeeds8Bit);
|
||||
m += len;
|
||||
break;
|
||||
}
|
||||
case Tag_Comment: {
|
||||
quint32 len = read32(m);
|
||||
m += 4;
|
||||
//qDebug() << "COMMENT LEN: " << len;
|
||||
//qDebug() << "COMMENT: " << QByteArray((const char*)m, len);
|
||||
fromBytes((const char*)m, len, codec, utf8Codec,
|
||||
&comment, &commentUtf8,
|
||||
&commentIsSystem, &commentIsUtf8, &commentNeeds8Bit);
|
||||
m += len;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
//qDebug() << "UNKNOWN TAG" << tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
end:;
|
||||
TranslatorMessage msg;
|
||||
msg.setType(TranslatorMessage::Finished);
|
||||
if (translations.count() > 1) {
|
||||
// If guessPlurals is not false here, plural form discard messages
|
||||
// will be spewn out later.
|
||||
msg.setPlural(true);
|
||||
} else if (guessPlurals) {
|
||||
// This might cause false positives, so it is a fallback only.
|
||||
if (sourcetext.contains(strProN))
|
||||
msg.setPlural(true);
|
||||
}
|
||||
msg.setTranslations(translations);
|
||||
translations.clear();
|
||||
if (contextNeeds8Bit || sourcetextNeeds8Bit || commentNeeds8Bit) {
|
||||
if (utf8Codec && contextIsUtf8 && sourcetextIsUtf8 && commentIsUtf8) {
|
||||
// The message is utf-8, but file is not.
|
||||
msg.setUtf8(true);
|
||||
msg.setContext(contextUtf8);
|
||||
msg.setSourceText(sourcetextUtf8);
|
||||
msg.setComment(commentUtf8);
|
||||
translator.append(msg);
|
||||
continue;
|
||||
}
|
||||
if (!(contextIsSystem && sourcetextIsSystem && commentIsSystem)) {
|
||||
cd.appendError(QLatin1String(
|
||||
"Cannot read file with specified input codec"));
|
||||
return false;
|
||||
}
|
||||
// The message is 8-bit in the file's encoding (utf-8 or not).
|
||||
}
|
||||
msg.setContext(context);
|
||||
msg.setSourceText(sourcetext);
|
||||
msg.setComment(comment);
|
||||
translator.append(msg);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool containsStripped(const Translator &translator, const TranslatorMessage &msg)
|
||||
{
|
||||
foreach (const TranslatorMessage &tmsg, translator.messages())
|
||||
if (tmsg.sourceText() == msg.sourceText()
|
||||
&& tmsg.context() == msg.context()
|
||||
&& tmsg.comment().isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
Releaser releaser;
|
||||
QLocale::Language l;
|
||||
QLocale::Country c;
|
||||
Translator::languageAndCountry(translator.languageCode(), &l, &c);
|
||||
QByteArray rules;
|
||||
if (getNumerusInfo(l, c, &rules, 0, 0))
|
||||
releaser.setNumerusRules(rules);
|
||||
releaser.setCodecName(translator.codecName());
|
||||
|
||||
int finished = 0;
|
||||
int unfinished = 0;
|
||||
int untranslated = 0;
|
||||
int missingIds = 0;
|
||||
int droppedData = 0;
|
||||
|
||||
for (int i = 0; i != translator.messageCount(); ++i) {
|
||||
const TranslatorMessage &msg = translator.message(i);
|
||||
TranslatorMessage::Type typ = msg.type();
|
||||
if (typ != TranslatorMessage::Obsolete) {
|
||||
if (cd.m_idBased && msg.id().isEmpty()) {
|
||||
++missingIds;
|
||||
continue;
|
||||
}
|
||||
if (typ == TranslatorMessage::Unfinished) {
|
||||
if (msg.translation().isEmpty() && !cd.m_idBased && cd.m_unTrPrefix.isEmpty()) {
|
||||
++untranslated;
|
||||
continue;
|
||||
} else {
|
||||
if (cd.ignoreUnfinished())
|
||||
continue;
|
||||
++unfinished;
|
||||
}
|
||||
} else {
|
||||
++finished;
|
||||
}
|
||||
QStringList tlns = msg.translations();
|
||||
if (msg.type() == TranslatorMessage::Unfinished
|
||||
&& (cd.m_idBased || !cd.m_unTrPrefix.isEmpty()))
|
||||
for (int j = 0; j < tlns.size(); ++j)
|
||||
if (tlns.at(j).isEmpty())
|
||||
tlns[j] = cd.m_unTrPrefix + msg.sourceText();
|
||||
if (cd.m_idBased) {
|
||||
if (!msg.context().isEmpty() || !msg.comment().isEmpty())
|
||||
++droppedData;
|
||||
releaser.insertIdBased(msg, tlns);
|
||||
} else {
|
||||
// Drop the comment in (context, sourceText, comment),
|
||||
// unless the context is empty,
|
||||
// unless (context, sourceText, "") already exists or
|
||||
// unless we already dropped the comment of (context,
|
||||
// sourceText, comment0).
|
||||
bool forceComment =
|
||||
msg.comment().isEmpty()
|
||||
|| msg.context().isEmpty()
|
||||
|| containsStripped(translator, msg);
|
||||
releaser.insert(msg, tlns, forceComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingIds)
|
||||
cd.appendError(QCoreApplication::translate("LRelease",
|
||||
"Dropped %n message(s) which had no ID.", 0,
|
||||
QCoreApplication::CodecForTr, missingIds));
|
||||
if (droppedData)
|
||||
cd.appendError(QCoreApplication::translate("LRelease",
|
||||
"Excess context/disambiguation dropped from %n message(s).", 0,
|
||||
QCoreApplication::CodecForTr, droppedData));
|
||||
|
||||
releaser.squeeze(cd.m_saveMode);
|
||||
bool saved = releaser.save(&dev);
|
||||
if (saved && cd.isVerbose()) {
|
||||
int generatedCount = finished + unfinished;
|
||||
cd.appendError(QCoreApplication::translate("LRelease",
|
||||
" Generated %n translation(s) (%1 finished and %2 unfinished)", 0,
|
||||
QCoreApplication::CodecForTr, generatedCount).arg(finished).arg(unfinished));
|
||||
if (untranslated)
|
||||
cd.appendError(QCoreApplication::translate("LRelease",
|
||||
" Ignored %n untranslated source text(s)", 0,
|
||||
QCoreApplication::CodecForTr, untranslated));
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
int initQM()
|
||||
{
|
||||
Translator::FileFormat format;
|
||||
|
||||
format.extension = QLatin1String("qm");
|
||||
format.description = QObject::tr("Compiled Qt translations");
|
||||
format.fileType = Translator::FileFormat::TranslationBinary;
|
||||
format.priority = 0;
|
||||
format.loader = &loadQM;
|
||||
format.saver = &saveQM;
|
||||
Translator::registerFileFormat(format);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Q_CONSTRUCTOR_FUNCTION(initQM)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,199 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTextCodec>
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
#include <QtXml/QXmlStreamReader>
|
||||
#include <QtXml/qxmlstream.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QPHReader : public QXmlStreamReader
|
||||
{
|
||||
public:
|
||||
QPHReader(QIODevice &dev)
|
||||
: QXmlStreamReader(&dev)
|
||||
{}
|
||||
|
||||
// the "real thing"
|
||||
bool read(Translator &translator);
|
||||
|
||||
private:
|
||||
bool isWhiteSpace() const
|
||||
{
|
||||
return isCharacters() && text().toString().trimmed().isEmpty();
|
||||
}
|
||||
|
||||
enum DataField { NoField, SourceField, TargetField, DefinitionField };
|
||||
DataField m_currentField;
|
||||
QString m_currentSource;
|
||||
QString m_currentTarget;
|
||||
QString m_currentDefinition;
|
||||
};
|
||||
|
||||
bool QPHReader::read(Translator &translator)
|
||||
{
|
||||
m_currentField = NoField;
|
||||
QString result;
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isStartElement()) {
|
||||
if (name() == QLatin1String("source")) {
|
||||
m_currentField = SourceField;
|
||||
} else if (name() == QLatin1String("target")) {
|
||||
m_currentField = TargetField;
|
||||
} else if (name() == QLatin1String("definition")) {
|
||||
m_currentField = DefinitionField;
|
||||
} else {
|
||||
m_currentField = NoField;
|
||||
if (name() == QLatin1String("QPH")) {
|
||||
QXmlStreamAttributes atts = attributes();
|
||||
translator.setLanguageCode(atts.value(QLatin1String("language")).toString());
|
||||
translator.setSourceLanguageCode(atts.value(QLatin1String("sourcelanguage")).toString());
|
||||
}
|
||||
}
|
||||
} else if (isWhiteSpace()) {
|
||||
// ignore these
|
||||
} else if (isCharacters()) {
|
||||
if (m_currentField == SourceField)
|
||||
m_currentSource += text();
|
||||
else if (m_currentField == TargetField)
|
||||
m_currentTarget += text();
|
||||
else if (m_currentField == DefinitionField)
|
||||
m_currentDefinition += text();
|
||||
} else if (isEndElement() && name() == QLatin1String("phrase")) {
|
||||
m_currentTarget.replace(QChar(Translator::TextVariantSeparator),
|
||||
QChar(Translator::BinaryVariantSeparator));
|
||||
TranslatorMessage msg;
|
||||
msg.setSourceText(m_currentSource);
|
||||
msg.setTranslation(m_currentTarget);
|
||||
msg.setComment(m_currentDefinition);
|
||||
translator.append(msg);
|
||||
m_currentSource.clear();
|
||||
m_currentTarget.clear();
|
||||
m_currentDefinition.clear();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &)
|
||||
{
|
||||
translator.setLocationsType(Translator::NoLocations);
|
||||
QPHReader reader(dev);
|
||||
return reader.read(translator);
|
||||
}
|
||||
|
||||
static QString protectQph(const QString &str)
|
||||
{
|
||||
QString result;
|
||||
result.reserve(str.length() * 12 / 10);
|
||||
for (int i = 0; i != str.size(); ++i) {
|
||||
uint c = str.at(i).unicode();
|
||||
switch (c) {
|
||||
case '\"':
|
||||
result += QLatin1String(""");
|
||||
break;
|
||||
case '&':
|
||||
result += QLatin1String("&");
|
||||
break;
|
||||
case '>':
|
||||
result += QLatin1String(">");
|
||||
break;
|
||||
case '<':
|
||||
result += QLatin1String("<");
|
||||
break;
|
||||
case '\'':
|
||||
result += QLatin1String("'");
|
||||
break;
|
||||
default:
|
||||
if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
|
||||
result += QString(QLatin1String("&#%1;")).arg(c);
|
||||
else // this also covers surrogates
|
||||
result += QChar(c);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData &)
|
||||
{
|
||||
QTextStream t(&dev);
|
||||
t.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
t << "<!DOCTYPE QPH>\n<QPH";
|
||||
QString languageCode = translator.languageCode();
|
||||
if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
|
||||
t << " language=\"" << languageCode << "\"";
|
||||
languageCode = translator.sourceLanguageCode();
|
||||
if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
|
||||
t << " sourcelanguage=\"" << languageCode << "\"";
|
||||
t << ">\n";
|
||||
foreach (const TranslatorMessage &msg, translator.messages()) {
|
||||
t << "<phrase>\n";
|
||||
t << " <source>" << protectQph(msg.sourceText()) << "</source>\n";
|
||||
QString str = msg.translations().join(QLatin1String("@"));
|
||||
str.replace(QChar(Translator::BinaryVariantSeparator),
|
||||
QChar(Translator::TextVariantSeparator));
|
||||
t << " <target>" << protectQph(str)
|
||||
<< "</target>\n";
|
||||
if (!msg.comment().isEmpty())
|
||||
t << " <definition>" << protectQph(msg.comment()) << "</definition>\n";
|
||||
t << "</phrase>\n";
|
||||
}
|
||||
t << "</QPH>\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
int initQPH()
|
||||
{
|
||||
Translator::FileFormat format;
|
||||
|
||||
format.extension = QLatin1String("qph");
|
||||
format.description = QObject::tr("Qt Linguist 'Phrase Book'");
|
||||
format.fileType = Translator::FileFormat::TranslationSource;
|
||||
format.priority = 0;
|
||||
format.loader = &loadQPH;
|
||||
format.saver = &saveQPH;
|
||||
Translator::registerFileFormat(format);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Q_CONSTRUCTOR_FUNCTION(initQPH)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,203 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "simtexth.h"
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QList>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*
|
||||
How similar are two texts? The approach used here relies on co-occurrence
|
||||
matrices and is very efficient.
|
||||
|
||||
Let's see with an example: how similar are "here" and "hither"? The
|
||||
co-occurrence matrix M for "here" is M[h,e] = 1, M[e,r] = 1, M[r,e] = 1, and 0
|
||||
elsewhere; the matrix N for "hither" is N[h,i] = 1, N[i,t] = 1, ...,
|
||||
N[h,e] = 1, N[e,r] = 1, and 0 elsewhere. The union U of both matrices is the
|
||||
matrix U[i,j] = max { M[i,j], N[i,j] }, and the intersection V is
|
||||
V[i,j] = min { M[i,j], N[i,j] }. The score for a pair of texts is
|
||||
|
||||
score = (sum of V[i,j] over all i, j) / (sum of U[i,j] over all i, j),
|
||||
|
||||
a formula suggested by Arnt Gulbrandsen. Here we have
|
||||
|
||||
score = 2 / 6,
|
||||
|
||||
or one third.
|
||||
|
||||
The implementation differs from this in a few details. Most importantly,
|
||||
repetitions are ignored; for input "xxx", M[x,x] equals 1, not 2.
|
||||
*/
|
||||
|
||||
/*
|
||||
Every character is assigned to one of 20 buckets so that the co-occurrence
|
||||
matrix requires only 20 * 20 = 400 bits, not 256 * 256 = 65536 bits or even
|
||||
more if we want the whole Unicode. Which character falls in which bucket is
|
||||
arbitrary.
|
||||
|
||||
The second half of the table is a replica of the first half, because of
|
||||
laziness.
|
||||
*/
|
||||
static const int indexOf[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// ! " # $ % & ' ( ) * + , - . /
|
||||
0, 2, 6, 7, 10, 12, 15, 19, 2, 6, 7, 10, 12, 15, 19, 0,
|
||||
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
|
||||
1, 3, 4, 5, 8, 9, 11, 13, 14, 16, 2, 6, 7, 10, 12, 15,
|
||||
// @ A B C D E F G H I J K L M N O
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
|
||||
// P Q R S T U V W X Y Z [ \ ] ^ _
|
||||
15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
|
||||
// ` a b c d e f g h i j k l m n o
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
|
||||
// p q r s t u v w x y z { | } ~
|
||||
15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 2, 6, 7, 10, 12, 15, 19, 2, 6, 7, 10, 12, 15, 19, 0,
|
||||
1, 3, 4, 5, 8, 9, 11, 13, 14, 16, 2, 6, 7, 10, 12, 15,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
|
||||
15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
|
||||
15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0
|
||||
};
|
||||
|
||||
/*
|
||||
The entry bitCount[i] (for i between 0 and 255) is the number of bits used to
|
||||
represent i in binary.
|
||||
*/
|
||||
static const int bitCount[256] = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
||||
};
|
||||
|
||||
struct CoMatrix
|
||||
{
|
||||
/*
|
||||
The matrix has 20 * 20 = 400 entries. This requires 50 bytes, or 13
|
||||
words. Some operations are performed on words for more efficiency.
|
||||
*/
|
||||
union {
|
||||
quint8 b[52];
|
||||
quint32 w[13];
|
||||
};
|
||||
|
||||
CoMatrix() { memset( b, 0, 52 ); }
|
||||
|
||||
CoMatrix(const QString &str)
|
||||
{
|
||||
QByteArray ba = str.toUtf8();
|
||||
const char *text = ba.constData();
|
||||
char c = '\0', d;
|
||||
memset( b, 0, 52 );
|
||||
/*
|
||||
The Knuth books are not in the office only for show; they help make
|
||||
loops 30% faster and 20% as readable.
|
||||
*/
|
||||
while ( (d = *text) != '\0' ) {
|
||||
setCoOccurence( c, d );
|
||||
if ( (c = *++text) != '\0' ) {
|
||||
setCoOccurence( d, c );
|
||||
text++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setCoOccurence( char c, char d ) {
|
||||
int k = indexOf[(uchar) c] + 20 * indexOf[(uchar) d];
|
||||
b[k >> 3] |= (1 << (k & 0x7));
|
||||
}
|
||||
|
||||
int worth() const {
|
||||
int w = 0;
|
||||
for ( int i = 0; i < 50; i++ )
|
||||
w += bitCount[b[i]];
|
||||
return w;
|
||||
}
|
||||
};
|
||||
|
||||
static inline CoMatrix reunion(const CoMatrix &m, const CoMatrix &n)
|
||||
{
|
||||
CoMatrix p;
|
||||
for (int i = 0; i < 13; ++i)
|
||||
p.w[i] = m.w[i] | n.w[i];
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline CoMatrix intersection(const CoMatrix &m, const CoMatrix &n)
|
||||
{
|
||||
CoMatrix p;
|
||||
for (int i = 0; i < 13; ++i)
|
||||
p.w[i] = m.w[i] & n.w[i];
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks how similar two strings are.
|
||||
* The return value is the score, and a higher score is more similar
|
||||
* than one with a low score.
|
||||
* Linguist considers a score over 190 to be a good match.
|
||||
*/
|
||||
int getSimilarityScore(const QString &str1, const QString &str2)
|
||||
{
|
||||
CoMatrix cmTarget(str2);
|
||||
CoMatrix cm(str1);
|
||||
int delta = qAbs(str1.size() - str2.size());
|
||||
|
||||
int score = ( (intersection(cm, cmTarget).worth() + 1) << 10 )
|
||||
/ ( reunion(cm, cmTarget).worth() + (delta << 1) + 1 );
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,47 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SIMTEXTH_H
|
||||
#define SIMTEXTH_H
|
||||
|
||||
const int textSimilarityThreshold = 190;
|
||||
|
||||
#include <QString>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
int getSimilarityScore(const QString &str1, const QString &str2);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,726 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
#include "simtexth.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QTextCodec>
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
#include "qtranslator_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Translator::Translator() :
|
||||
m_codec(QTextCodec::codecForName("ISO-8859-1")),
|
||||
m_locationsType(AbsoluteLocations),
|
||||
m_indexOk(true)
|
||||
{
|
||||
}
|
||||
|
||||
void Translator::registerFileFormat(const FileFormat &format)
|
||||
{
|
||||
//qDebug() << "Translator: Registering format " << format.extension;
|
||||
QList<Translator::FileFormat> &formats = registeredFileFormats();
|
||||
for (int i = 0; i < formats.size(); ++i)
|
||||
if (format.fileType == formats[i].fileType && format.priority < formats[i].priority) {
|
||||
formats.insert(i, format);
|
||||
return;
|
||||
}
|
||||
formats.append(format);
|
||||
}
|
||||
|
||||
QList<Translator::FileFormat> &Translator::registeredFileFormats()
|
||||
{
|
||||
static QList<Translator::FileFormat> theFormats;
|
||||
return theFormats;
|
||||
}
|
||||
|
||||
void Translator::addIndex(int idx, const TranslatorMessage &msg) const
|
||||
{
|
||||
if (msg.sourceText().isEmpty() && msg.id().isEmpty()) {
|
||||
m_ctxCmtIdx[msg.context()] = idx;
|
||||
} else {
|
||||
m_msgIdx[TMMKey(msg)] = idx;
|
||||
if (!msg.id().isEmpty())
|
||||
m_idMsgIdx[msg.id()] = idx;
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::delIndex(int idx) const
|
||||
{
|
||||
const TranslatorMessage &msg = m_messages.at(idx);
|
||||
if (msg.sourceText().isEmpty() && msg.id().isEmpty()) {
|
||||
m_ctxCmtIdx.remove(msg.context());
|
||||
} else {
|
||||
m_msgIdx.remove(TMMKey(msg));
|
||||
if (!msg.id().isEmpty())
|
||||
m_idMsgIdx.remove(msg.id());
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::ensureIndexed() const
|
||||
{
|
||||
if (!m_indexOk) {
|
||||
m_indexOk = true;
|
||||
m_ctxCmtIdx.clear();
|
||||
m_idMsgIdx.clear();
|
||||
m_msgIdx.clear();
|
||||
for (int i = 0; i < m_messages.count(); i++)
|
||||
addIndex(i, m_messages.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::replaceSorted(const TranslatorMessage &msg)
|
||||
{
|
||||
int index = find(msg);
|
||||
if (index == -1) {
|
||||
appendSorted(msg);
|
||||
} else {
|
||||
delIndex(index);
|
||||
m_messages[index] = msg;
|
||||
addIndex(index, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::extend(const TranslatorMessage &msg)
|
||||
{
|
||||
int index = find(msg);
|
||||
if (index == -1) {
|
||||
append(msg);
|
||||
} else {
|
||||
TranslatorMessage &emsg = m_messages[index];
|
||||
emsg.addReferenceUniq(msg.fileName(), msg.lineNumber());
|
||||
if (!msg.extraComment().isEmpty()) {
|
||||
QString cmt = emsg.extraComment();
|
||||
if (!cmt.isEmpty())
|
||||
cmt.append(QLatin1String("\n----------\n"));
|
||||
cmt.append(msg.extraComment());
|
||||
emsg.setExtraComment(cmt);
|
||||
}
|
||||
if (msg.isUtf8() != emsg.isUtf8()) {
|
||||
emsg.setUtf8(true);
|
||||
emsg.setNonUtf8(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::insert(int idx, const TranslatorMessage &msg)
|
||||
{
|
||||
addIndex(idx, msg);
|
||||
m_messages.insert(idx, msg);
|
||||
}
|
||||
|
||||
void Translator::append(const TranslatorMessage &msg)
|
||||
{
|
||||
insert(m_messages.count(), msg);
|
||||
}
|
||||
|
||||
void Translator::appendSorted(const TranslatorMessage &msg)
|
||||
{
|
||||
int msgLine = msg.lineNumber();
|
||||
if (msgLine < 0) {
|
||||
append(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
int bestIdx = 0; // Best insertion point found so far
|
||||
int bestScore = 0; // Its category: 0 = no hit, 1 = pre or post, 2 = middle
|
||||
int bestSize = 0; // The length of the region. Longer is better within one category.
|
||||
|
||||
// The insertion point to use should this region turn out to be the best one so far
|
||||
int thisIdx = 0;
|
||||
int thisScore = 0;
|
||||
int thisSize = 0;
|
||||
// Working vars
|
||||
int prevLine = 0;
|
||||
int curIdx = 0;
|
||||
foreach (const TranslatorMessage &mit, m_messages) {
|
||||
bool sameFile = mit.fileName() == msg.fileName() && mit.context() == msg.context();
|
||||
int curLine;
|
||||
if (sameFile && (curLine = mit.lineNumber()) >= prevLine) {
|
||||
if (msgLine >= prevLine && msgLine < curLine) {
|
||||
thisIdx = curIdx;
|
||||
thisScore = thisSize ? 2 : 1;
|
||||
}
|
||||
++thisSize;
|
||||
prevLine = curLine;
|
||||
} else {
|
||||
if (thisSize) {
|
||||
if (!thisScore) {
|
||||
thisIdx = curIdx;
|
||||
thisScore = 1;
|
||||
}
|
||||
if (thisScore > bestScore || (thisScore == bestScore && thisSize > bestSize)) {
|
||||
bestIdx = thisIdx;
|
||||
bestScore = thisScore;
|
||||
bestSize = thisSize;
|
||||
}
|
||||
thisScore = 0;
|
||||
thisSize = sameFile ? 1 : 0;
|
||||
prevLine = 0;
|
||||
}
|
||||
}
|
||||
++curIdx;
|
||||
}
|
||||
if (thisSize && !thisScore) {
|
||||
thisIdx = curIdx;
|
||||
thisScore = 1;
|
||||
}
|
||||
if (thisScore > bestScore || (thisScore == bestScore && thisSize > bestSize))
|
||||
insert(thisIdx, msg);
|
||||
else if (bestScore)
|
||||
insert(bestIdx, msg);
|
||||
else
|
||||
append(msg);
|
||||
}
|
||||
|
||||
static QString guessFormat(const QString &filename, const QString &format)
|
||||
{
|
||||
if (format != QLatin1String("auto"))
|
||||
return format;
|
||||
|
||||
foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
|
||||
if (filename.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive))
|
||||
return fmt.extension;
|
||||
}
|
||||
|
||||
// the default format.
|
||||
// FIXME: change to something more widely distributed later.
|
||||
return QLatin1String("ts");
|
||||
}
|
||||
|
||||
bool Translator::load(const QString &filename, ConversionData &cd, const QString &format)
|
||||
{
|
||||
cd.m_sourceDir = QFileInfo(filename).absoluteDir();
|
||||
cd.m_sourceFileName = filename;
|
||||
|
||||
QFile file;
|
||||
if (filename.isEmpty() || filename == QLatin1String("-")) {
|
||||
if (!file.open(stdin, QIODevice::ReadOnly)) {
|
||||
cd.appendError(QString::fromLatin1("Cannot open stdin!? (%1)")
|
||||
.arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
file.setFileName(filename);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
cd.appendError(QString::fromLatin1("Cannot open %1: %2")
|
||||
.arg(filename, file.errorString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString fmt = guessFormat(filename, format);
|
||||
|
||||
foreach (const FileFormat ®format, registeredFileFormats()) {
|
||||
if (fmt == regformat.extension) {
|
||||
if (regformat.loader)
|
||||
return (*regformat.loader)(*this, file, cd);
|
||||
cd.appendError(QString(QLatin1String("No loader for format %1 found"))
|
||||
.arg(fmt));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cd.appendError(QString(QLatin1String("Unknown format %1 for file %2"))
|
||||
.arg(format, filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Translator::save(const QString &filename, ConversionData &cd, const QString &format) const
|
||||
{
|
||||
QFile file;
|
||||
if (filename.isEmpty() || filename == QLatin1String("-")) {
|
||||
if (!file.open(stdout, QIODevice::WriteOnly)) {
|
||||
cd.appendError(QString::fromLatin1("Cannot open stdout!? (%1)")
|
||||
.arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
file.setFileName(filename);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
cd.appendError(QString::fromLatin1("Cannot create %1: %2")
|
||||
.arg(filename, file.errorString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString fmt = guessFormat(filename, format);
|
||||
cd.m_targetDir = QFileInfo(filename).absoluteDir();
|
||||
|
||||
foreach (const FileFormat ®format, registeredFileFormats()) {
|
||||
if (fmt == regformat.extension) {
|
||||
if (regformat.saver)
|
||||
return (*regformat.saver)(*this, file, cd);
|
||||
cd.appendError(QString(QLatin1String("Cannot save %1 files")).arg(fmt));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cd.appendError(QString(QLatin1String("Unknown format %1 for file %2"))
|
||||
.arg(format).arg(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
QString Translator::makeLanguageCode(QLocale::Language language, QLocale::Country country)
|
||||
{
|
||||
QLocale locale(language, country);
|
||||
if (country == QLocale::AnyCountry) {
|
||||
QString languageCode = locale.name().section(QLatin1Char('_'), 0, 0);
|
||||
if (languageCode.length() <= 3)
|
||||
return languageCode;
|
||||
return QString();
|
||||
} else {
|
||||
return locale.name();
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::languageAndCountry(const QString &languageCode,
|
||||
QLocale::Language *lang, QLocale::Country *country)
|
||||
{
|
||||
QLocale locale(languageCode);
|
||||
if (lang)
|
||||
*lang = locale.language();
|
||||
|
||||
if (country) {
|
||||
if (languageCode.indexOf(QLatin1Char('_')) != -1)
|
||||
*country = locale.country();
|
||||
else
|
||||
*country = QLocale::AnyCountry;
|
||||
}
|
||||
}
|
||||
|
||||
int Translator::find(const TranslatorMessage &msg) const
|
||||
{
|
||||
ensureIndexed();
|
||||
if (msg.id().isEmpty())
|
||||
return m_msgIdx.value(TMMKey(msg), -1);
|
||||
int i = m_idMsgIdx.value(msg.id(), -1);
|
||||
if (i >= 0)
|
||||
return i;
|
||||
i = m_msgIdx.value(TMMKey(msg), -1);
|
||||
// If both have an id, then find only by id.
|
||||
return i >= 0 && m_messages.at(i).id().isEmpty() ? i : -1;
|
||||
}
|
||||
|
||||
int Translator::find(const QString &context,
|
||||
const QString &comment, const TranslatorMessage::References &refs) const
|
||||
{
|
||||
if (!refs.isEmpty()) {
|
||||
for (TMM::ConstIterator it = m_messages.constBegin(); it != m_messages.constEnd(); ++it) {
|
||||
if (it->context() == context && it->comment() == comment)
|
||||
foreach (const TranslatorMessage::Reference &itref, it->allReferences())
|
||||
foreach (const TranslatorMessage::Reference &ref, refs)
|
||||
if (itref == ref)
|
||||
return it - m_messages.constBegin();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Translator::find(const QString &context) const
|
||||
{
|
||||
ensureIndexed();
|
||||
return m_ctxCmtIdx.value(context, -1);
|
||||
}
|
||||
|
||||
void Translator::stripObsoleteMessages()
|
||||
{
|
||||
for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); )
|
||||
if (it->type() == TranslatorMessage::Obsolete)
|
||||
it = m_messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
m_indexOk = false;
|
||||
}
|
||||
|
||||
void Translator::stripFinishedMessages()
|
||||
{
|
||||
for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); )
|
||||
if (it->type() == TranslatorMessage::Finished)
|
||||
it = m_messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
m_indexOk = false;
|
||||
}
|
||||
|
||||
void Translator::stripEmptyContexts()
|
||||
{
|
||||
for (TMM::Iterator it = m_messages.begin(); it != m_messages.end();)
|
||||
if (it->sourceText() == QLatin1String(ContextComment))
|
||||
it = m_messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
m_indexOk = false;
|
||||
}
|
||||
|
||||
void Translator::stripNonPluralForms()
|
||||
{
|
||||
for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); )
|
||||
if (!it->isPlural())
|
||||
it = m_messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
m_indexOk = false;
|
||||
}
|
||||
|
||||
void Translator::stripIdenticalSourceTranslations()
|
||||
{
|
||||
for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); ) {
|
||||
// we need to have just one translation, and it be equal to the source
|
||||
if (it->translations().count() == 1 && it->translation() == it->sourceText())
|
||||
it = m_messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
m_indexOk = false;
|
||||
}
|
||||
|
||||
void Translator::dropTranslations()
|
||||
{
|
||||
for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
|
||||
if (it->type() == TranslatorMessage::Finished)
|
||||
it->setType(TranslatorMessage::Unfinished);
|
||||
it->setTranslation(QString());
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::dropUiLines()
|
||||
{
|
||||
QString uiXt = QLatin1String(".ui");
|
||||
QString juiXt = QLatin1String(".jui");
|
||||
for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
|
||||
QHash<QString, int> have;
|
||||
QList<TranslatorMessage::Reference> refs;
|
||||
foreach (const TranslatorMessage::Reference &itref, it->allReferences()) {
|
||||
const QString &fn = itref.fileName();
|
||||
if (fn.endsWith(uiXt) || fn.endsWith(juiXt)) {
|
||||
if (++have[fn] == 1)
|
||||
refs.append(TranslatorMessage::Reference(fn, -1));
|
||||
} else {
|
||||
refs.append(itref);
|
||||
}
|
||||
}
|
||||
it->setReferences(refs);
|
||||
}
|
||||
}
|
||||
|
||||
struct TranslatorMessageIdPtr {
|
||||
explicit TranslatorMessageIdPtr(const TranslatorMessage &tm)
|
||||
{
|
||||
ptr = &tm;
|
||||
}
|
||||
|
||||
inline const TranslatorMessage *operator->() const
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const TranslatorMessage *ptr;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(TranslatorMessageIdPtr, Q_MOVABLE_TYPE);
|
||||
|
||||
inline int qHash(TranslatorMessageIdPtr tmp)
|
||||
{
|
||||
return qHash(tmp->id());
|
||||
}
|
||||
|
||||
inline bool operator==(TranslatorMessageIdPtr tmp1, TranslatorMessageIdPtr tmp2)
|
||||
{
|
||||
return tmp1->id() == tmp2->id();
|
||||
}
|
||||
|
||||
struct TranslatorMessageContentPtr {
|
||||
explicit TranslatorMessageContentPtr(const TranslatorMessage &tm)
|
||||
{
|
||||
ptr = &tm;
|
||||
}
|
||||
|
||||
inline const TranslatorMessage *operator->() const
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const TranslatorMessage *ptr;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(TranslatorMessageContentPtr, Q_MOVABLE_TYPE);
|
||||
|
||||
inline int qHash(TranslatorMessageContentPtr tmp)
|
||||
{
|
||||
int hash = qHash(tmp->context()) ^ qHash(tmp->sourceText());
|
||||
if (!tmp->sourceText().isEmpty())
|
||||
// Special treatment for context comments (empty source).
|
||||
hash ^= qHash(tmp->comment());
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline bool operator==(TranslatorMessageContentPtr tmp1, TranslatorMessageContentPtr tmp2)
|
||||
{
|
||||
if (tmp1->context() != tmp2->context() || tmp1->sourceText() != tmp2->sourceText())
|
||||
return false;
|
||||
// Special treatment for context comments (empty source).
|
||||
if (tmp1->sourceText().isEmpty())
|
||||
return true;
|
||||
return tmp1->comment() == tmp2->comment();
|
||||
}
|
||||
|
||||
Translator::Duplicates Translator::resolveDuplicates()
|
||||
{
|
||||
Duplicates dups;
|
||||
QHash<TranslatorMessageIdPtr, int> idRefs;
|
||||
QHash<TranslatorMessageContentPtr, int> contentRefs;
|
||||
for (int i = 0; i < m_messages.count();) {
|
||||
const TranslatorMessage &msg = m_messages.at(i);
|
||||
TranslatorMessage *omsg;
|
||||
int oi;
|
||||
QSet<int> *pDup;
|
||||
if (!msg.id().isEmpty()) {
|
||||
QHash<TranslatorMessageIdPtr, int>::ConstIterator it =
|
||||
idRefs.constFind(TranslatorMessageIdPtr(msg));
|
||||
if (it != idRefs.constEnd()) {
|
||||
oi = *it;
|
||||
omsg = &m_messages[oi];
|
||||
pDup = &dups.byId;
|
||||
goto gotDupe;
|
||||
}
|
||||
}
|
||||
{
|
||||
QHash<TranslatorMessageContentPtr, int>::ConstIterator it =
|
||||
contentRefs.constFind(TranslatorMessageContentPtr(msg));
|
||||
if (it != contentRefs.constEnd()) {
|
||||
oi = *it;
|
||||
omsg = &m_messages[oi];
|
||||
if (msg.id().isEmpty() || omsg->id().isEmpty()) {
|
||||
if (!msg.id().isEmpty() && omsg->id().isEmpty()) {
|
||||
omsg->setId(msg.id());
|
||||
idRefs[TranslatorMessageIdPtr(*omsg)] = oi;
|
||||
}
|
||||
pDup = &dups.byContents;
|
||||
goto gotDupe;
|
||||
}
|
||||
// This is really a content dupe, but with two distinct IDs.
|
||||
}
|
||||
}
|
||||
if (!msg.id().isEmpty())
|
||||
idRefs[TranslatorMessageIdPtr(msg)] = i;
|
||||
contentRefs[TranslatorMessageContentPtr(msg)] = i;
|
||||
++i;
|
||||
continue;
|
||||
gotDupe:
|
||||
if (omsg->isUtf8() != msg.isUtf8() && !omsg->isNonUtf8()) {
|
||||
// Dual-encoded message
|
||||
omsg->setUtf8(true);
|
||||
omsg->setNonUtf8(true);
|
||||
} else {
|
||||
// Duplicate
|
||||
pDup->insert(oi);
|
||||
}
|
||||
if (!omsg->isTranslated() && msg.isTranslated())
|
||||
omsg->setTranslations(msg.translations());
|
||||
m_indexOk = false;
|
||||
m_messages.removeAt(i);
|
||||
}
|
||||
return dups;
|
||||
}
|
||||
|
||||
void Translator::reportDuplicates(const Duplicates &dupes,
|
||||
const QString &fileName, bool verbose)
|
||||
{
|
||||
if (!dupes.byId.isEmpty() || !dupes.byContents.isEmpty()) {
|
||||
std::cerr << "Warning: dropping duplicate messages in '" << qPrintable(fileName);
|
||||
if (!verbose) {
|
||||
std::cerr << "'\n(try -verbose for more info).\n";
|
||||
} else {
|
||||
std::cerr << "':\n";
|
||||
foreach (int i, dupes.byId)
|
||||
std::cerr << "\n* ID: " << qPrintable(message(i).id()) << std::endl;
|
||||
foreach (int j, dupes.byContents) {
|
||||
const TranslatorMessage &msg = message(j);
|
||||
std::cerr << "\n* Context: " << qPrintable(msg.context())
|
||||
<< "\n* Source: " << qPrintable(msg.sourceText()) << std::endl;
|
||||
if (!msg.comment().isEmpty())
|
||||
std::cerr << "* Comment: " << qPrintable(msg.comment()) << std::endl;
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used by lupdate to be able to search using absolute paths during merging
|
||||
void Translator::makeFileNamesAbsolute(const QDir &originalPath)
|
||||
{
|
||||
for (TMM::iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
|
||||
TranslatorMessage &msg = *it;
|
||||
TranslatorMessage::References refs = msg.allReferences();
|
||||
msg.setReferences(TranslatorMessage::References());
|
||||
foreach (const TranslatorMessage::Reference &ref, refs) {
|
||||
QString fileName = ref.fileName();
|
||||
QFileInfo fi (fileName);
|
||||
if (fi.isRelative())
|
||||
fileName = originalPath.absoluteFilePath(fileName);
|
||||
msg.addReference(fileName, ref.lineNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<TranslatorMessage> Translator::messages() const
|
||||
{
|
||||
return m_messages;
|
||||
}
|
||||
|
||||
QStringList Translator::normalizedTranslations(const TranslatorMessage &msg, int numPlurals)
|
||||
{
|
||||
QStringList translations = msg.translations();
|
||||
int numTranslations = msg.isPlural() ? numPlurals : 1;
|
||||
|
||||
// make sure that the stringlist always have the size of the
|
||||
// language's current numerus, or 1 if its not plural
|
||||
if (translations.count() > numTranslations) {
|
||||
for (int i = translations.count(); i > numTranslations; --i)
|
||||
translations.removeLast();
|
||||
} else if (translations.count() < numTranslations) {
|
||||
for (int i = translations.count(); i < numTranslations; ++i)
|
||||
translations.append(QString());
|
||||
}
|
||||
return translations;
|
||||
}
|
||||
|
||||
void Translator::normalizeTranslations(ConversionData &cd)
|
||||
{
|
||||
bool truncated = false;
|
||||
QLocale::Language l;
|
||||
QLocale::Country c;
|
||||
languageAndCountry(languageCode(), &l, &c);
|
||||
int numPlurals = 1;
|
||||
if (l != QLocale::C) {
|
||||
QStringList forms;
|
||||
if (getNumerusInfo(l, c, 0, &forms, 0))
|
||||
numPlurals = forms.count(); // includes singular
|
||||
}
|
||||
for (int i = 0; i < m_messages.count(); ++i) {
|
||||
const TranslatorMessage &msg = m_messages.at(i);
|
||||
QStringList tlns = msg.translations();
|
||||
int ccnt = msg.isPlural() ? numPlurals : 1;
|
||||
if (tlns.count() != ccnt) {
|
||||
while (tlns.count() < ccnt)
|
||||
tlns.append(QString());
|
||||
while (tlns.count() > ccnt) {
|
||||
tlns.removeLast();
|
||||
truncated = true;
|
||||
}
|
||||
m_messages[i].setTranslations(tlns);
|
||||
}
|
||||
}
|
||||
if (truncated)
|
||||
cd.appendError(QLatin1String(
|
||||
"Removed plural forms as the target language has less "
|
||||
"forms.\nIf this sounds wrong, possibly the target language is "
|
||||
"not set or recognized."));
|
||||
}
|
||||
|
||||
QString Translator::guessLanguageCodeFromFileName(const QString &filename)
|
||||
{
|
||||
QString str = filename;
|
||||
foreach (const FileFormat &format, registeredFileFormats()) {
|
||||
if (str.endsWith(format.extension)) {
|
||||
str = str.left(str.size() - format.extension.size() - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
static QRegExp re(QLatin1String("[\\._]"));
|
||||
while (true) {
|
||||
QLocale locale(str);
|
||||
//qDebug() << "LANGUAGE FROM " << str << "LANG: " << locale.language();
|
||||
if (locale.language() != QLocale::C) {
|
||||
//qDebug() << "FOUND " << locale.name();
|
||||
return locale.name();
|
||||
}
|
||||
int pos = str.indexOf(re);
|
||||
if (pos == -1)
|
||||
break;
|
||||
str = str.mid(pos + 1);
|
||||
}
|
||||
//qDebug() << "LANGUAGE GUESSING UNSUCCESSFUL";
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool Translator::hasExtra(const QString &key) const
|
||||
{
|
||||
return m_extra.contains(key);
|
||||
}
|
||||
|
||||
QString Translator::extra(const QString &key) const
|
||||
{
|
||||
return m_extra[key];
|
||||
}
|
||||
|
||||
void Translator::setExtra(const QString &key, const QString &value)
|
||||
{
|
||||
m_extra[key] = value;
|
||||
}
|
||||
|
||||
void Translator::setCodecName(const QByteArray &name)
|
||||
{
|
||||
QTextCodec *codec = QTextCodec::codecForName(name);
|
||||
if (!codec) {
|
||||
if (!name.isEmpty())
|
||||
std::cerr << "No QTextCodec for " << name.constData() << " available. Using Latin1.\n";
|
||||
m_codec = QTextCodec::codecForName("ISO-8859-1");
|
||||
} else {
|
||||
m_codec = codec;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Translator::codecName() const
|
||||
{
|
||||
return m_codec->name();
|
||||
}
|
||||
|
||||
void Translator::dump() const
|
||||
{
|
||||
for (int i = 0; i != messageCount(); ++i)
|
||||
message(i).dump();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,247 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef METATRANSLATOR_H
|
||||
#define METATRANSLATOR_H
|
||||
|
||||
#include "translatormessage.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QList>
|
||||
#include <QLocale>
|
||||
#include <QMultiHash>
|
||||
#include <QString>
|
||||
#include <QSet>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QIODevice;
|
||||
|
||||
// A struct of "interesting" data passed to and from the load and save routines
|
||||
class ConversionData
|
||||
{
|
||||
public:
|
||||
ConversionData() :
|
||||
m_verbose(false),
|
||||
m_ignoreUnfinished(false),
|
||||
m_sortContexts(false),
|
||||
m_noUiLines(false),
|
||||
m_idBased(false),
|
||||
m_saveMode(SaveEverything)
|
||||
{}
|
||||
|
||||
// tag manipulation
|
||||
const QStringList &dropTags() const { return m_dropTags; }
|
||||
QStringList &dropTags() { return m_dropTags; }
|
||||
const QDir &targetDir() const { return m_targetDir; }
|
||||
bool isVerbose() const { return m_verbose; }
|
||||
bool ignoreUnfinished() const { return m_ignoreUnfinished; }
|
||||
bool sortContexts() const { return m_sortContexts; }
|
||||
|
||||
void appendError(const QString &error) { m_errors.append(error); }
|
||||
QString error() const { return m_errors.isEmpty() ? QString() : m_errors.join(QLatin1String("\n")) + QLatin1Char('\n'); }
|
||||
QStringList errors() const { return m_errors; }
|
||||
void clearErrors() { m_errors.clear(); }
|
||||
|
||||
public:
|
||||
QString m_defaultContext;
|
||||
QByteArray m_codecForSource; // CPP, PO & QM specific
|
||||
QByteArray m_outputCodec; // CPP & PO specific
|
||||
QString m_unTrPrefix; // QM specific
|
||||
QString m_sourceFileName;
|
||||
QString m_targetFileName;
|
||||
QDir m_sourceDir;
|
||||
QDir m_targetDir; // FIXME: TS specific
|
||||
QSet<QString> m_projectRoots;
|
||||
QMultiHash<QString, QString> m_allCSources;
|
||||
QStringList m_includePath;
|
||||
QStringList m_dropTags; // tags to be dropped
|
||||
QStringList m_errors;
|
||||
bool m_verbose;
|
||||
bool m_ignoreUnfinished;
|
||||
bool m_sortContexts;
|
||||
bool m_noUiLines;
|
||||
bool m_idBased;
|
||||
TranslatorSaveMode m_saveMode;
|
||||
};
|
||||
|
||||
class TMMKey {
|
||||
public:
|
||||
TMMKey(const TranslatorMessage &msg)
|
||||
{ context = msg.context(); source = msg.sourceText(); comment = msg.comment(); }
|
||||
bool operator==(const TMMKey &o) const
|
||||
{ return context == o.context && source == o.source && comment == o.comment; }
|
||||
QString context, source, comment;
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(TMMKey, Q_MOVABLE_TYPE);
|
||||
inline uint qHash(const TMMKey &key) { return qHash(key.context) ^ qHash(key.source) ^ qHash(key.comment); }
|
||||
|
||||
class Translator
|
||||
{
|
||||
public:
|
||||
Translator();
|
||||
|
||||
bool load(const QString &filename, ConversionData &err, const QString &format /* = "auto" */);
|
||||
bool save(const QString &filename, ConversionData &err, const QString &format /* = "auto" */) const;
|
||||
|
||||
int find(const TranslatorMessage &msg) const;
|
||||
int find(const QString &context,
|
||||
const QString &comment, const TranslatorMessage::References &refs) const;
|
||||
|
||||
int find(const QString &context) const;
|
||||
|
||||
void replaceSorted(const TranslatorMessage &msg);
|
||||
void extend(const TranslatorMessage &msg); // Only for single-location messages
|
||||
void append(const TranslatorMessage &msg);
|
||||
void appendSorted(const TranslatorMessage &msg);
|
||||
|
||||
void stripObsoleteMessages();
|
||||
void stripFinishedMessages();
|
||||
void stripEmptyContexts();
|
||||
void stripNonPluralForms();
|
||||
void stripIdenticalSourceTranslations();
|
||||
void dropTranslations();
|
||||
void dropUiLines();
|
||||
void makeFileNamesAbsolute(const QDir &originalPath);
|
||||
|
||||
struct Duplicates { QSet<int> byId, byContents; };
|
||||
Duplicates resolveDuplicates();
|
||||
void reportDuplicates(const Duplicates &dupes, const QString &fileName, bool verbose);
|
||||
|
||||
void setCodecName(const QByteArray &name);
|
||||
void setCodec(QTextCodec *codec) { m_codec = codec; }
|
||||
QByteArray codecName() const;
|
||||
QTextCodec *codec() const { return m_codec; }
|
||||
|
||||
QString languageCode() const { return m_language; }
|
||||
QString sourceLanguageCode() const { return m_sourceLanguage; }
|
||||
|
||||
enum LocationsType { DefaultLocations, NoLocations, RelativeLocations, AbsoluteLocations };
|
||||
void setLocationsType(LocationsType lt) { m_locationsType = lt; }
|
||||
LocationsType locationsType() const { return m_locationsType; }
|
||||
|
||||
static QString makeLanguageCode(QLocale::Language language, QLocale::Country country);
|
||||
static void languageAndCountry(const QString &languageCode,
|
||||
QLocale::Language *lang, QLocale::Country *country);
|
||||
void setLanguageCode(const QString &languageCode) { m_language = languageCode; }
|
||||
void setSourceLanguageCode(const QString &languageCode) { m_sourceLanguage = languageCode; }
|
||||
static QString guessLanguageCodeFromFileName(const QString &fileName);
|
||||
QList<TranslatorMessage> messages() const;
|
||||
static QStringList normalizedTranslations(const TranslatorMessage &m, int numPlurals);
|
||||
void normalizeTranslations(ConversionData &cd);
|
||||
QStringList normalizedTranslations(const TranslatorMessage &m, ConversionData &cd, bool *ok) const;
|
||||
|
||||
int messageCount() const { return m_messages.size(); }
|
||||
TranslatorMessage &message(int i) { return m_messages[i]; }
|
||||
const TranslatorMessage &message(int i) const { return m_messages.at(i); }
|
||||
const TranslatorMessage &constMessage(int i) const { return m_messages.at(i); }
|
||||
void dump() const;
|
||||
|
||||
// additional file format specific data
|
||||
// note: use '<fileformat>:' as prefix for file format specific members,
|
||||
// e.g. "po-flags", "po-msgid_plural"
|
||||
typedef TranslatorMessage::ExtraData ExtraData;
|
||||
QString extra(const QString &ba) const;
|
||||
void setExtra(const QString &ba, const QString &var);
|
||||
bool hasExtra(const QString &ba) const;
|
||||
const ExtraData &extras() const { return m_extra; }
|
||||
void setExtras(const ExtraData &extras) { m_extra = extras; }
|
||||
|
||||
// registration of file formats
|
||||
typedef bool (*SaveFunction)(const Translator &, QIODevice &out, ConversionData &data);
|
||||
typedef bool (*LoadFunction)(Translator &, QIODevice &in, ConversionData &data);
|
||||
struct FileFormat {
|
||||
FileFormat() : loader(0), saver(0), priority(-1) {}
|
||||
QString extension; // such as "ts", "xlf", ...
|
||||
QString description; // human-readable description
|
||||
LoadFunction loader;
|
||||
SaveFunction saver;
|
||||
enum FileType { TranslationSource, TranslationBinary } fileType;
|
||||
int priority; // 0 = highest, -1 = invisible
|
||||
};
|
||||
static void registerFileFormat(const FileFormat &format);
|
||||
static QList<FileFormat> ®isteredFileFormats();
|
||||
|
||||
enum {
|
||||
TextVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
|
||||
BinaryVariantSeparator = 0x9c // unicode "STRING TERMINATOR"
|
||||
};
|
||||
|
||||
private:
|
||||
void insert(int idx, const TranslatorMessage &msg);
|
||||
void addIndex(int idx, const TranslatorMessage &msg) const;
|
||||
void delIndex(int idx) const;
|
||||
void ensureIndexed() const;
|
||||
|
||||
typedef QList<TranslatorMessage> TMM; // int stores the sequence position.
|
||||
|
||||
TMM m_messages;
|
||||
QTextCodec *m_codec;
|
||||
LocationsType m_locationsType;
|
||||
|
||||
// A string beginning with a 2 or 3 letter language code (ISO 639-1
|
||||
// or ISO-639-2), followed by the optional country variant to distinguish
|
||||
// between country-specific variations of the language. The language code
|
||||
// and country code are always separated by '_'
|
||||
// Note that the language part can also be a 3-letter ISO 639-2 code.
|
||||
// Legal examples:
|
||||
// 'pt' portuguese, assumes portuguese from portugal
|
||||
// 'pt_BR' Brazilian portuguese (ISO 639-1 language code)
|
||||
// 'por_BR' Brazilian portuguese (ISO 639-2 language code)
|
||||
QString m_language;
|
||||
QString m_sourceLanguage;
|
||||
ExtraData m_extra;
|
||||
|
||||
mutable bool m_indexOk;
|
||||
mutable QHash<QString, int> m_ctxCmtIdx;
|
||||
mutable QHash<QString, int> m_idMsgIdx;
|
||||
mutable QHash<TMMKey, int> m_msgIdx;
|
||||
};
|
||||
|
||||
bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
|
||||
QByteArray *rules, QStringList *forms, const char **gettextRules);
|
||||
|
||||
QString getNumerusInfoString();
|
||||
|
||||
bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd);
|
||||
|
||||
/*
|
||||
This is a quick hack. The proper way to handle this would be
|
||||
to extend Translator's interface.
|
||||
*/
|
||||
#define ContextComment "QT_LINGUIST_INTERNAL_CONTEXT_COMMENT"
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,179 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translatormessage.h"
|
||||
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
TranslatorMessage::TranslatorMessage()
|
||||
: m_lineNumber(-1), m_type(Unfinished), m_utf8(false), m_nonUtf8(false), m_plural(false)
|
||||
{
|
||||
}
|
||||
|
||||
TranslatorMessage::TranslatorMessage(const QString &context,
|
||||
const QString &sourceText, const QString &comment,
|
||||
const QString &userData,
|
||||
const QString &fileName, int lineNumber, const QStringList &translations,
|
||||
Type type, bool plural)
|
||||
: m_context(context), m_sourcetext(sourceText), m_comment(comment),
|
||||
m_userData(userData),
|
||||
m_translations(translations), m_fileName(fileName), m_lineNumber(lineNumber),
|
||||
m_type(type), m_utf8(false), m_nonUtf8(false), m_plural(plural)
|
||||
{
|
||||
}
|
||||
|
||||
void TranslatorMessage::addReference(const QString &fileName, int lineNumber)
|
||||
{
|
||||
if (m_fileName.isEmpty()) {
|
||||
m_fileName = fileName;
|
||||
m_lineNumber = lineNumber;
|
||||
} else {
|
||||
m_extraRefs.append(Reference(fileName, lineNumber));
|
||||
}
|
||||
}
|
||||
|
||||
void TranslatorMessage::addReferenceUniq(const QString &fileName, int lineNumber)
|
||||
{
|
||||
if (m_fileName.isEmpty()) {
|
||||
m_fileName = fileName;
|
||||
m_lineNumber = lineNumber;
|
||||
} else {
|
||||
if (fileName == m_fileName && lineNumber == m_lineNumber)
|
||||
return;
|
||||
if (!m_extraRefs.isEmpty()) // Rather common case, so special-case it
|
||||
foreach (const Reference &ref, m_extraRefs)
|
||||
if (fileName == ref.fileName() && lineNumber == ref.lineNumber())
|
||||
return;
|
||||
m_extraRefs.append(Reference(fileName, lineNumber));
|
||||
}
|
||||
}
|
||||
|
||||
void TranslatorMessage::clearReferences()
|
||||
{
|
||||
m_fileName.clear();
|
||||
m_lineNumber = -1;
|
||||
m_extraRefs.clear();
|
||||
}
|
||||
|
||||
void TranslatorMessage::setReferences(const TranslatorMessage::References &refs0)
|
||||
{
|
||||
if (!refs0.isEmpty()) {
|
||||
References refs = refs0;
|
||||
const Reference &ref = refs.takeFirst();
|
||||
m_fileName = ref.fileName();
|
||||
m_lineNumber = ref.lineNumber();
|
||||
m_extraRefs = refs;
|
||||
} else {
|
||||
clearReferences();
|
||||
}
|
||||
}
|
||||
|
||||
TranslatorMessage::References TranslatorMessage::allReferences() const
|
||||
{
|
||||
References refs;
|
||||
if (!m_fileName.isEmpty()) {
|
||||
refs.append(Reference(m_fileName, m_lineNumber));
|
||||
refs += m_extraRefs;
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
static bool needs8BitHelper(const QString &ba)
|
||||
{
|
||||
for (int i = ba.size(); --i >= 0; )
|
||||
if (ba.at(i).unicode() >= 0x80)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TranslatorMessage::needs8Bit() const
|
||||
{
|
||||
//dump();
|
||||
return needs8BitHelper(m_sourcetext)
|
||||
|| needs8BitHelper(m_comment)
|
||||
|| needs8BitHelper(m_context);
|
||||
}
|
||||
|
||||
|
||||
bool TranslatorMessage::hasExtra(const QString &key) const
|
||||
{
|
||||
return m_extra.contains(key);
|
||||
}
|
||||
|
||||
QString TranslatorMessage::extra(const QString &key) const
|
||||
{
|
||||
return m_extra[key];
|
||||
}
|
||||
|
||||
void TranslatorMessage::setExtra(const QString &key, const QString &value)
|
||||
{
|
||||
m_extra[key] = value;
|
||||
}
|
||||
|
||||
void TranslatorMessage::unsetExtra(const QString &key)
|
||||
{
|
||||
m_extra.remove(key);
|
||||
}
|
||||
|
||||
void TranslatorMessage::dump() const
|
||||
{
|
||||
qDebug()
|
||||
<< "\nId : " << m_id
|
||||
<< "\nContext : " << m_context
|
||||
<< "\nSource : " << m_sourcetext
|
||||
<< "\nComment : " << m_comment
|
||||
<< "\nUserData : " << m_userData
|
||||
<< "\nExtraComment : " << m_extraComment
|
||||
<< "\nTranslatorComment : " << m_translatorComment
|
||||
<< "\nTranslations : " << m_translations
|
||||
<< "\nFileName : " << m_fileName
|
||||
<< "\nLineNumber : " << m_lineNumber
|
||||
<< "\nType : " << m_type
|
||||
<< "\nPlural : " << m_plural
|
||||
<< "\nExtra : " << m_extra;
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_TRANSLATION
|
|
@ -1,173 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TRANSLATORMESSAGE_H
|
||||
#define TRANSLATORMESSAGE_H
|
||||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum TranslatorSaveMode { SaveEverything, SaveStripped };
|
||||
|
||||
class TranslatorMessage
|
||||
{
|
||||
public:
|
||||
enum Type { Unfinished, Finished, Obsolete };
|
||||
typedef QHash<QString, QString> ExtraData;
|
||||
class Reference
|
||||
{
|
||||
QString m_fileName;
|
||||
int m_lineNumber;
|
||||
public:
|
||||
Reference(const QString &n, int l) : m_fileName(n), m_lineNumber(l) {}
|
||||
bool operator==(const Reference &other) const
|
||||
{ return fileName() == other.fileName() && lineNumber() == other.lineNumber(); }
|
||||
QString fileName() const { return m_fileName; }
|
||||
int lineNumber() const { return m_lineNumber; }
|
||||
};
|
||||
typedef QList<Reference> References;
|
||||
|
||||
TranslatorMessage();
|
||||
TranslatorMessage(const QString &context, const QString &sourceText,
|
||||
const QString &comment, const QString &userData,
|
||||
const QString &fileName, int lineNumber,
|
||||
const QStringList &translations = QStringList(),
|
||||
Type type = Unfinished, bool plural = false);
|
||||
|
||||
uint hash() const;
|
||||
|
||||
QString id() const { return m_id; }
|
||||
void setId(const QString &id) { m_id = id; }
|
||||
|
||||
QString context() const { return m_context; }
|
||||
void setContext(const QString &context) { m_context = context; }
|
||||
|
||||
QString sourceText() const { return m_sourcetext; }
|
||||
void setSourceText(const QString &sourcetext) { m_sourcetext = sourcetext; }
|
||||
QString oldSourceText() const { return m_oldsourcetext; }
|
||||
void setOldSourceText(const QString &oldsourcetext) { m_oldsourcetext = oldsourcetext; }
|
||||
|
||||
QString comment() const { return m_comment; }
|
||||
void setComment(const QString &comment) { m_comment = comment; }
|
||||
QString oldComment() const { return m_oldcomment; }
|
||||
void setOldComment(const QString &oldcomment) { m_oldcomment = oldcomment; }
|
||||
|
||||
QStringList translations() const { return m_translations; }
|
||||
void setTranslations(const QStringList &translations) { m_translations = translations; }
|
||||
QString translation() const { return m_translations.value(0); }
|
||||
void setTranslation(const QString &translation) { m_translations = QStringList(translation); }
|
||||
void appendTranslation(const QString &translation) { m_translations.append(translation); }
|
||||
bool isTranslated() const
|
||||
{
|
||||
foreach (const QString &trans, m_translations)
|
||||
if (!trans.isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
QString fileName() const { return m_fileName; }
|
||||
void setFileName(const QString &fileName) { m_fileName = fileName; }
|
||||
int lineNumber() const { return m_lineNumber; }
|
||||
void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
|
||||
void clearReferences();
|
||||
void setReferences(const References &refs);
|
||||
void addReference(const QString &fileName, int lineNumber);
|
||||
void addReference(const Reference &ref) { addReference(ref.fileName(), ref.lineNumber()); }
|
||||
void addReferenceUniq(const QString &fileName, int lineNumber);
|
||||
References extraReferences() const { return m_extraRefs; }
|
||||
References allReferences() const;
|
||||
QString userData() const { return m_userData; }
|
||||
void setUserData(const QString &userData) { m_userData = userData; }
|
||||
QString extraComment() const { return m_extraComment; }
|
||||
void setExtraComment(const QString &extraComment) { m_extraComment = extraComment; }
|
||||
QString translatorComment() const { return m_translatorComment; }
|
||||
void setTranslatorComment(const QString &translatorComment) { m_translatorComment = translatorComment; }
|
||||
|
||||
bool isNull() const { return m_sourcetext.isNull() && m_lineNumber == -1 && m_translations.isEmpty(); }
|
||||
|
||||
Type type() const { return m_type; }
|
||||
void setType(Type t) { m_type = t; }
|
||||
bool isUtf8() const { return m_utf8; } // codecForTr override
|
||||
void setUtf8(bool on) { m_utf8 = on; }
|
||||
bool isNonUtf8() const { return m_nonUtf8; } // codecForTr override
|
||||
void setNonUtf8(bool on) { m_nonUtf8 = on; }
|
||||
bool isPlural() const { return m_plural; }
|
||||
void setPlural(bool isplural) { m_plural = isplural; }
|
||||
|
||||
// note: use '<fileformat>:' as prefix for file format specific members,
|
||||
// e.g. "po-msgid_plural"
|
||||
QString extra(const QString &ba) const;
|
||||
void setExtra(const QString &ba, const QString &var);
|
||||
bool hasExtra(const QString &ba) const;
|
||||
const ExtraData &extras() const { return m_extra; }
|
||||
void setExtras(const ExtraData &extras) { m_extra = extras; }
|
||||
void unsetExtra(const QString &key);
|
||||
|
||||
bool needs8Bit() const;
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
QString m_id;
|
||||
QString m_context;
|
||||
QString m_sourcetext;
|
||||
QString m_oldsourcetext;
|
||||
QString m_comment;
|
||||
QString m_oldcomment;
|
||||
QString m_userData;
|
||||
ExtraData m_extra; // PO flags, PO plurals
|
||||
QString m_extraComment;
|
||||
QString m_translatorComment;
|
||||
QStringList m_translations;
|
||||
QString m_fileName;
|
||||
int m_lineNumber;
|
||||
References m_extraRefs;
|
||||
|
||||
Type m_type;
|
||||
bool m_utf8;
|
||||
bool m_nonUtf8;
|
||||
bool m_plural;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(TranslatorMessage, Q_MOVABLE_TYPE);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_TRANSLATION
|
||||
|
||||
#endif // TRANSLATORMESSAGE_H
|
|
@ -1,773 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTextCodec>
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
#include <QtXml/QXmlStreamReader>
|
||||
#include <QtXml/qxmlstream.h>
|
||||
|
||||
#define STRINGIFY_INTERNAL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
|
||||
#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s)))
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*
|
||||
* The encodings are a total mess.
|
||||
* A Translator has a codecForTr(). Each message's text will be passed to tr()
|
||||
* in that encoding or as UTF-8 to trUtf8() if it is flagged as such.
|
||||
* For ts 2.0, the file content is always uniformly in UTF-8. The file stores
|
||||
* the codecForTr default and marks deviating messages accordingly.
|
||||
* For ts 1.1, the file content is in mixed encoding. Each message is encoded
|
||||
* the way it will be passed to tr() (with 8-bit characters encoded as numeric
|
||||
* entities) or trUtf8(). The file stores the encoding and codecForTr in one
|
||||
* attribute, for both the default and each deviating message.
|
||||
*/
|
||||
|
||||
|
||||
QDebug &operator<<(QDebug &d, const QXmlStreamAttribute &attr)
|
||||
{
|
||||
return d << "[" << attr.name().toString() << "," << attr.value().toString() << "]";
|
||||
}
|
||||
|
||||
|
||||
class TSReader : public QXmlStreamReader
|
||||
{
|
||||
public:
|
||||
TSReader(QIODevice &dev, ConversionData &cd)
|
||||
: QXmlStreamReader(&dev), m_cd(cd)
|
||||
{}
|
||||
|
||||
// the "real thing"
|
||||
bool read(Translator &translator);
|
||||
|
||||
private:
|
||||
bool elementStarts(const QString &str) const
|
||||
{
|
||||
return isStartElement() && name() == str;
|
||||
}
|
||||
|
||||
bool isWhiteSpace() const
|
||||
{
|
||||
return isCharacters() && text().toString().trimmed().isEmpty();
|
||||
}
|
||||
|
||||
// needed to expand <byte ... />
|
||||
QString readContents();
|
||||
// needed to join <lengthvariant>s
|
||||
QString readTransContents();
|
||||
|
||||
void handleError();
|
||||
|
||||
ConversionData &m_cd;
|
||||
};
|
||||
|
||||
void TSReader::handleError()
|
||||
{
|
||||
if (isComment())
|
||||
return;
|
||||
if (hasError() && error() == CustomError) // raised by readContents
|
||||
return;
|
||||
|
||||
const QString loc = QString::fromLatin1("at %3:%1:%2")
|
||||
.arg(lineNumber()).arg(columnNumber()).arg(m_cd.m_sourceFileName);
|
||||
|
||||
switch (tokenType()) {
|
||||
case NoToken: // Cannot happen
|
||||
default: // likewise
|
||||
case Invalid:
|
||||
raiseError(QString::fromLatin1("Parse error %1: %2").arg(loc, errorString()));
|
||||
break;
|
||||
case StartElement:
|
||||
raiseError(QString::fromLatin1("Unexpected tag <%1> %2").arg(name().toString(), loc));
|
||||
break;
|
||||
case Characters:
|
||||
{
|
||||
QString tok = text().toString();
|
||||
if (tok.length() > 30)
|
||||
tok = tok.left(30) + QLatin1String("[...]");
|
||||
raiseError(QString::fromLatin1("Unexpected characters '%1' %2").arg(tok, loc));
|
||||
}
|
||||
break;
|
||||
case EntityReference:
|
||||
raiseError(QString::fromLatin1("Unexpected entity '&%1;' %2").arg(name().toString(), loc));
|
||||
break;
|
||||
case ProcessingInstruction:
|
||||
raiseError(QString::fromLatin1("Unexpected processing instruction %1").arg(loc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static QString byteValue(QString value)
|
||||
{
|
||||
int base = 10;
|
||||
if (value.startsWith(QLatin1String("x"))) {
|
||||
base = 16;
|
||||
value.remove(0, 1);
|
||||
}
|
||||
int n = value.toUInt(0, base);
|
||||
return (n != 0) ? QString(QChar(n)) : QString();
|
||||
}
|
||||
|
||||
QString TSReader::readContents()
|
||||
{
|
||||
STRING(byte);
|
||||
STRING(value);
|
||||
|
||||
QString result;
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isEndElement()) {
|
||||
break;
|
||||
} else if (isCharacters()) {
|
||||
result += text();
|
||||
} else if (elementStarts(strbyte)) {
|
||||
// <byte value="...">
|
||||
result += byteValue(attributes().value(strvalue).toString());
|
||||
readNext();
|
||||
if (!isEndElement()) {
|
||||
handleError();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
handleError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
//qDebug() << "TEXT: " << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
QString TSReader::readTransContents()
|
||||
{
|
||||
STRING(lengthvariant);
|
||||
STRING(variants);
|
||||
STRING(yes);
|
||||
|
||||
if (attributes().value(strvariants) == stryes) {
|
||||
QString result;
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isEndElement()) {
|
||||
break;
|
||||
} else if (isWhiteSpace()) {
|
||||
// ignore these, just whitespace
|
||||
} else if (elementStarts(strlengthvariant)) {
|
||||
if (!result.isEmpty())
|
||||
result += QChar(Translator::BinaryVariantSeparator);
|
||||
result += readContents();
|
||||
} else {
|
||||
handleError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return readContents();
|
||||
}
|
||||
}
|
||||
|
||||
bool TSReader::read(Translator &translator)
|
||||
{
|
||||
STRING(both);
|
||||
STRING(byte);
|
||||
STRING(comment);
|
||||
STRING(context);
|
||||
STRING(defaultcodec);
|
||||
STRING(encoding);
|
||||
STRING(extracomment);
|
||||
STRING(filename);
|
||||
STRING(id);
|
||||
STRING(language);
|
||||
STRING(line);
|
||||
STRING(location);
|
||||
STRING(message);
|
||||
STRING(name);
|
||||
STRING(numerus);
|
||||
STRING(numerusform);
|
||||
STRING(obsolete);
|
||||
STRING(oldcomment);
|
||||
STRING(oldsource);
|
||||
STRING(source);
|
||||
STRING(sourcelanguage);
|
||||
STRING(translation);
|
||||
STRING(translatorcomment);
|
||||
STRING(true);
|
||||
STRING(TS);
|
||||
STRING(type);
|
||||
STRING(unfinished);
|
||||
STRING(userdata);
|
||||
STRING(utf8);
|
||||
STRING(value);
|
||||
//STRING(version);
|
||||
STRING(yes);
|
||||
|
||||
static const QString strextrans(QLatin1String("extra-"));
|
||||
static const QString strUtf8(QLatin1String("UTF-8"));
|
||||
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isStartDocument()) {
|
||||
// <!DOCTYPE TS>
|
||||
//qDebug() << attributes();
|
||||
} else if (isEndDocument()) {
|
||||
// <!DOCTYPE TS>
|
||||
//qDebug() << attributes();
|
||||
} else if (isDTD()) {
|
||||
// <!DOCTYPE TS>
|
||||
//qDebug() << tokenString();
|
||||
} else if (elementStarts(strTS)) {
|
||||
// <TS>
|
||||
//qDebug() << "TS " << attributes();
|
||||
QHash<QString, int> currentLine;
|
||||
QString currentFile;
|
||||
bool maybeRelative = false, maybeAbsolute = false;
|
||||
|
||||
QXmlStreamAttributes atts = attributes();
|
||||
//QString version = atts.value(strversion).toString();
|
||||
translator.setLanguageCode(atts.value(strlanguage).toString());
|
||||
translator.setSourceLanguageCode(atts.value(strsourcelanguage).toString());
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isEndElement()) {
|
||||
// </TS> found, finish local loop
|
||||
break;
|
||||
} else if (isWhiteSpace()) {
|
||||
// ignore these, just whitespace
|
||||
} else if (elementStarts(strdefaultcodec)) {
|
||||
// <defaultcodec>
|
||||
const QString &codec = readElementText();
|
||||
if (!codec.isEmpty())
|
||||
translator.setCodecName(codec.toLatin1());
|
||||
// </defaultcodec>
|
||||
} else if (isStartElement()
|
||||
&& name().toString().startsWith(strextrans)) {
|
||||
// <extra-...>
|
||||
QString tag = name().toString();
|
||||
translator.setExtra(tag.mid(6), readContents());
|
||||
// </extra-...>
|
||||
} else if (elementStarts(strcontext)) {
|
||||
// <context>
|
||||
QString context;
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isEndElement()) {
|
||||
// </context> found, finish local loop
|
||||
break;
|
||||
} else if (isWhiteSpace()) {
|
||||
// ignore these, just whitespace
|
||||
} else if (elementStarts(strname)) {
|
||||
// <name>
|
||||
context = readElementText();
|
||||
// </name>
|
||||
} else if (elementStarts(strmessage)) {
|
||||
// <message>
|
||||
TranslatorMessage::References refs;
|
||||
QString currentMsgFile = currentFile;
|
||||
|
||||
TranslatorMessage msg;
|
||||
msg.setId(attributes().value(strid).toString());
|
||||
msg.setContext(context);
|
||||
msg.setType(TranslatorMessage::Finished);
|
||||
msg.setPlural(attributes().value(strnumerus) == stryes);
|
||||
const QStringRef &utf8Attr = attributes().value(strutf8);
|
||||
msg.setNonUtf8(utf8Attr == strboth);
|
||||
msg.setUtf8(msg.isNonUtf8() || utf8Attr == strtrue
|
||||
|| attributes().value(strencoding) == strUtf8);
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isEndElement()) {
|
||||
// </message> found, finish local loop
|
||||
msg.setReferences(refs);
|
||||
translator.append(msg);
|
||||
break;
|
||||
} else if (isWhiteSpace()) {
|
||||
// ignore these, just whitespace
|
||||
} else if (elementStarts(strsource)) {
|
||||
// <source>...</source>
|
||||
msg.setSourceText(readContents());
|
||||
} else if (elementStarts(stroldsource)) {
|
||||
// <oldsource>...</oldsource>
|
||||
msg.setOldSourceText(readContents());
|
||||
} else if (elementStarts(stroldcomment)) {
|
||||
// <oldcomment>...</oldcomment>
|
||||
msg.setOldComment(readContents());
|
||||
} else if (elementStarts(strextracomment)) {
|
||||
// <extracomment>...</extracomment>
|
||||
msg.setExtraComment(readContents());
|
||||
} else if (elementStarts(strtranslatorcomment)) {
|
||||
// <translatorcomment>...</translatorcomment>
|
||||
msg.setTranslatorComment(readContents());
|
||||
} else if (elementStarts(strlocation)) {
|
||||
// <location/>
|
||||
maybeAbsolute = true;
|
||||
QXmlStreamAttributes atts = attributes();
|
||||
QString fileName = atts.value(strfilename).toString();
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = currentMsgFile;
|
||||
maybeRelative = true;
|
||||
} else {
|
||||
if (refs.isEmpty())
|
||||
currentFile = fileName;
|
||||
currentMsgFile = fileName;
|
||||
}
|
||||
const QString lin = atts.value(strline).toString();
|
||||
if (lin.isEmpty()) {
|
||||
refs.append(TranslatorMessage::Reference(fileName, -1));
|
||||
} else {
|
||||
bool bOK;
|
||||
int lineNo = lin.toInt(&bOK);
|
||||
if (bOK) {
|
||||
if (lin.startsWith(QLatin1Char('+')) || lin.startsWith(QLatin1Char('-'))) {
|
||||
lineNo = (currentLine[fileName] += lineNo);
|
||||
maybeRelative = true;
|
||||
}
|
||||
refs.append(TranslatorMessage::Reference(fileName, lineNo));
|
||||
}
|
||||
}
|
||||
readContents();
|
||||
} else if (elementStarts(strcomment)) {
|
||||
// <comment>...</comment>
|
||||
msg.setComment(readContents());
|
||||
} else if (elementStarts(struserdata)) {
|
||||
// <userdata>...</userdata>
|
||||
msg.setUserData(readContents());
|
||||
} else if (elementStarts(strtranslation)) {
|
||||
// <translation>
|
||||
QXmlStreamAttributes atts = attributes();
|
||||
QStringRef type = atts.value(strtype);
|
||||
if (type == strunfinished)
|
||||
msg.setType(TranslatorMessage::Unfinished);
|
||||
else if (type == strobsolete)
|
||||
msg.setType(TranslatorMessage::Obsolete);
|
||||
if (msg.isPlural()) {
|
||||
QStringList translations;
|
||||
while (!atEnd()) {
|
||||
readNext();
|
||||
if (isEndElement()) {
|
||||
break;
|
||||
} else if (isWhiteSpace()) {
|
||||
// ignore these, just whitespace
|
||||
} else if (elementStarts(strnumerusform)) {
|
||||
translations.append(readTransContents());
|
||||
} else {
|
||||
handleError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
msg.setTranslations(translations);
|
||||
} else {
|
||||
msg.setTranslation(readTransContents());
|
||||
}
|
||||
// </translation>
|
||||
} else if (isStartElement()
|
||||
&& name().toString().startsWith(strextrans)) {
|
||||
// <extra-...>
|
||||
QString tag = name().toString();
|
||||
msg.setExtra(tag.mid(6), readContents());
|
||||
// </extra-...>
|
||||
} else {
|
||||
handleError();
|
||||
}
|
||||
}
|
||||
// </message>
|
||||
} else {
|
||||
handleError();
|
||||
}
|
||||
}
|
||||
// </context>
|
||||
} else {
|
||||
handleError();
|
||||
}
|
||||
translator.setLocationsType(maybeRelative ? Translator::RelativeLocations :
|
||||
maybeAbsolute ? Translator::AbsoluteLocations :
|
||||
Translator::NoLocations);
|
||||
} // </TS>
|
||||
} else {
|
||||
handleError();
|
||||
}
|
||||
}
|
||||
if (hasError()) {
|
||||
m_cd.appendError(errorString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static QString numericEntity(int ch)
|
||||
{
|
||||
return QString(ch <= 0x20 ? QLatin1String("<byte value=\"x%1\"/>")
|
||||
: QLatin1String("&#x%1;")) .arg(ch, 0, 16);
|
||||
}
|
||||
|
||||
static QString protectTs(const QString &str)
|
||||
{
|
||||
QString result;
|
||||
result.reserve(str.length() * 12 / 10);
|
||||
for (int i = 0; i != str.size(); ++i) {
|
||||
uint c = str.at(i).unicode();
|
||||
switch (c) {
|
||||
case '\"':
|
||||
result += QLatin1String(""");
|
||||
break;
|
||||
case '&':
|
||||
result += QLatin1String("&");
|
||||
break;
|
||||
case '>':
|
||||
result += QLatin1String(">");
|
||||
break;
|
||||
case '<':
|
||||
result += QLatin1String("<");
|
||||
break;
|
||||
case '\'':
|
||||
result += QLatin1String("'");
|
||||
break;
|
||||
default:
|
||||
if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
|
||||
result += numericEntity(c);
|
||||
else // this also covers surrogates
|
||||
result += QChar(c);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString evilBytes(const QString& str,
|
||||
bool isUtf8, int format, const QByteArray &codecName)
|
||||
{
|
||||
//qDebug() << "EVIL: " << str << isUtf8 << format << codecName;
|
||||
if (isUtf8)
|
||||
return protectTs(str);
|
||||
if (format == 20)
|
||||
return protectTs(str);
|
||||
if (codecName == "UTF-8")
|
||||
return protectTs(str);
|
||||
QTextCodec *codec = QTextCodec::codecForName(codecName);
|
||||
if (!codec)
|
||||
return protectTs(str);
|
||||
QString t = QString::fromLatin1(codec->fromUnicode(protectTs(str)).data());
|
||||
int len = (int) t.length();
|
||||
QString result;
|
||||
// FIXME: Factor is sensible only for latin scripts, probably.
|
||||
result.reserve(t.length() * 2);
|
||||
for (int k = 0; k < len; k++) {
|
||||
if (t[k].unicode() >= 0x7f)
|
||||
result += numericEntity(t[k].unicode());
|
||||
else
|
||||
result += t[k];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void writeExtras(QTextStream &t, const char *indent,
|
||||
const TranslatorMessage::ExtraData &extras, const QRegExp &drops)
|
||||
{
|
||||
for (Translator::ExtraData::ConstIterator it = extras.begin(); it != extras.end(); ++it) {
|
||||
if (!drops.exactMatch(it.key())) {
|
||||
t << indent << "<extra-" << it.key() << '>'
|
||||
<< protectTs(it.value())
|
||||
<< "</extra-" << it.key() << ">\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void writeVariants(QTextStream &t, const char *indent, const QString &input)
|
||||
{
|
||||
int offset;
|
||||
if ((offset = input.indexOf(QChar(Translator::BinaryVariantSeparator))) >= 0) {
|
||||
t << " variants=\"yes\">";
|
||||
int start = 0;
|
||||
forever {
|
||||
t << "\n " << indent << "<lengthvariant>"
|
||||
<< protectTs(input.mid(start, offset - start))
|
||||
<< "</lengthvariant>";
|
||||
if (offset == input.length())
|
||||
break;
|
||||
start = offset + 1;
|
||||
offset = input.indexOf(QChar(Translator::BinaryVariantSeparator), start);
|
||||
if (offset < 0)
|
||||
offset = input.length();
|
||||
}
|
||||
t << "\n" << indent;
|
||||
} else {
|
||||
t << ">" << protectTs(input);
|
||||
}
|
||||
}
|
||||
|
||||
bool saveTS(const Translator &translator, QIODevice &dev, ConversionData &cd, int format)
|
||||
{
|
||||
bool result = true;
|
||||
QTextStream t(&dev);
|
||||
t.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
bool trIsUtf8 = (translator.codecName() == "UTF-8");
|
||||
//qDebug() << translator.codecName();
|
||||
bool fileIsUtf8 = (format == 20 || trIsUtf8);
|
||||
|
||||
// The xml prolog allows processors to easily detect the correct encoding
|
||||
t << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n";
|
||||
|
||||
if (format == 11)
|
||||
t << "<TS version=\"1.1\"";
|
||||
else
|
||||
t << "<TS version=\"2.0\"";
|
||||
|
||||
QString languageCode = translator.languageCode();
|
||||
if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
|
||||
t << " language=\"" << languageCode << "\"";
|
||||
if (format == 20) {
|
||||
languageCode = translator.sourceLanguageCode();
|
||||
if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
|
||||
t << " sourcelanguage=\"" << languageCode << "\"";
|
||||
}
|
||||
t << ">\n";
|
||||
|
||||
QByteArray codecName = translator.codecName();
|
||||
if (codecName != "ISO-8859-1")
|
||||
t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
|
||||
|
||||
QRegExp drops(cd.dropTags().join(QLatin1String("|")));
|
||||
|
||||
if (format == 20)
|
||||
writeExtras(t, " ", translator.extras(), drops);
|
||||
|
||||
QHash<QString, QList<TranslatorMessage> > messageOrder;
|
||||
QList<QString> contextOrder;
|
||||
foreach (const TranslatorMessage &msg, translator.messages()) {
|
||||
// no need for such noise
|
||||
if (msg.type() == TranslatorMessage::Obsolete && msg.translation().isEmpty())
|
||||
continue;
|
||||
|
||||
QList<TranslatorMessage> &context = messageOrder[msg.context()];
|
||||
if (context.isEmpty())
|
||||
contextOrder.append(msg.context());
|
||||
context.append(msg);
|
||||
}
|
||||
if (cd.sortContexts())
|
||||
qSort(contextOrder);
|
||||
|
||||
QHash<QString, int> currentLine;
|
||||
QString currentFile;
|
||||
foreach (const QString &context, contextOrder) {
|
||||
const TranslatorMessage &firstMsg = messageOrder[context].first();
|
||||
t << "<context" << ((!fileIsUtf8 && firstMsg.isUtf8()) ? " encoding=\"UTF-8\"" : "") << ">\n";
|
||||
|
||||
t << " <name>"
|
||||
<< evilBytes(context, firstMsg.isUtf8() || fileIsUtf8, format, codecName)
|
||||
<< "</name>\n";
|
||||
foreach (const TranslatorMessage &msg, messageOrder[context]) {
|
||||
//msg.dump();
|
||||
|
||||
bool isUtf8 = msg.isUtf8();
|
||||
bool second = false;
|
||||
forever {
|
||||
|
||||
t << " <message";
|
||||
if (!msg.id().isEmpty())
|
||||
t << " id=\"" << msg.id() << "\"";
|
||||
if (!trIsUtf8) {
|
||||
if (format == 11) {
|
||||
if (isUtf8)
|
||||
t << " encoding=\"UTF-8\"";
|
||||
} else {
|
||||
if (msg.isUtf8()) {
|
||||
if (msg.isNonUtf8())
|
||||
t << " utf8=\"both\"";
|
||||
else
|
||||
t << " utf8=\"true\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (msg.isPlural())
|
||||
t << " numerus=\"yes\"";
|
||||
t << ">\n";
|
||||
if (translator.locationsType() != Translator::NoLocations) {
|
||||
QString cfile = currentFile;
|
||||
bool first = true;
|
||||
foreach (const TranslatorMessage::Reference &ref, msg.allReferences()) {
|
||||
QString fn = cd.m_targetDir.relativeFilePath(ref.fileName())
|
||||
.replace(QLatin1Char('\\'),QLatin1Char('/'));
|
||||
int ln = ref.lineNumber();
|
||||
QString ld;
|
||||
if (translator.locationsType() == Translator::RelativeLocations) {
|
||||
if (ln != -1) {
|
||||
int dlt = ln - currentLine[fn];
|
||||
if (dlt >= 0)
|
||||
ld.append(QLatin1Char('+'));
|
||||
ld.append(QString::number(dlt));
|
||||
currentLine[fn] = ln;
|
||||
}
|
||||
|
||||
if (fn != cfile) {
|
||||
if (first)
|
||||
currentFile = fn;
|
||||
cfile = fn;
|
||||
} else {
|
||||
fn.clear();
|
||||
}
|
||||
first = false;
|
||||
} else {
|
||||
if (ln != -1)
|
||||
ld = QString::number(ln);
|
||||
}
|
||||
t << " <location";
|
||||
if (!fn.isEmpty())
|
||||
t << " filename=\"" << fn << "\"";
|
||||
if (!ld.isEmpty())
|
||||
t << " line=\"" << ld << "\"";
|
||||
t << "/>\n";
|
||||
}
|
||||
}
|
||||
|
||||
t << " <source>"
|
||||
<< evilBytes(msg.sourceText(), isUtf8, format, codecName)
|
||||
<< "</source>\n";
|
||||
|
||||
if (format != 11 && !msg.oldSourceText().isEmpty())
|
||||
t << " <oldsource>" << protectTs(msg.oldSourceText()) << "</oldsource>\n";
|
||||
|
||||
if (!msg.comment().isEmpty()) {
|
||||
t << " <comment>"
|
||||
<< evilBytes(msg.comment(), isUtf8, format, codecName)
|
||||
<< "</comment>\n";
|
||||
}
|
||||
|
||||
if (format != 11) {
|
||||
|
||||
if (!msg.oldComment().isEmpty())
|
||||
t << " <oldcomment>" << protectTs(msg.oldComment()) << "</oldcomment>\n";
|
||||
|
||||
if (!msg.extraComment().isEmpty())
|
||||
t << " <extracomment>" << protectTs(msg.extraComment())
|
||||
<< "</extracomment>\n";
|
||||
|
||||
if (!msg.translatorComment().isEmpty())
|
||||
t << " <translatorcomment>" << protectTs(msg.translatorComment())
|
||||
<< "</translatorcomment>\n";
|
||||
|
||||
}
|
||||
|
||||
t << " <translation";
|
||||
if (msg.type() == TranslatorMessage::Unfinished)
|
||||
t << " type=\"unfinished\"";
|
||||
else if (msg.type() == TranslatorMessage::Obsolete)
|
||||
t << " type=\"obsolete\"";
|
||||
if (msg.isPlural()) {
|
||||
t << ">";
|
||||
const QStringList &translns = msg.translations();
|
||||
for (int j = 0; j < translns.count(); ++j) {
|
||||
t << "\n <numerusform";
|
||||
writeVariants(t, " ", translns[j]);
|
||||
t << "</numerusform>";
|
||||
}
|
||||
t << "\n ";
|
||||
} else {
|
||||
writeVariants(t, " ", msg.translation());
|
||||
}
|
||||
t << "</translation>\n";
|
||||
|
||||
if (format != 11)
|
||||
writeExtras(t, " ", msg.extras(), drops);
|
||||
|
||||
if (!msg.userData().isEmpty())
|
||||
t << " <userdata>" << msg.userData() << "</userdata>\n";
|
||||
t << " </message>\n";
|
||||
|
||||
if (format != 11 || second || !msg.isUtf8() || !msg.isNonUtf8())
|
||||
break;
|
||||
isUtf8 = false;
|
||||
second = true;
|
||||
}
|
||||
}
|
||||
t << "</context>\n";
|
||||
}
|
||||
|
||||
t << "</TS>\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
bool loadTS(Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
TSReader reader(dev, cd);
|
||||
return reader.read(translator);
|
||||
}
|
||||
|
||||
bool saveTS11(const Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
return saveTS(translator, dev, cd, 11);
|
||||
}
|
||||
|
||||
bool saveTS20(const Translator &translator, QIODevice &dev, ConversionData &cd)
|
||||
{
|
||||
return saveTS(translator, dev, cd, 20);
|
||||
}
|
||||
|
||||
int initTS()
|
||||
{
|
||||
Translator::FileFormat format;
|
||||
|
||||
format.extension = QLatin1String("ts11");
|
||||
format.fileType = Translator::FileFormat::TranslationSource;
|
||||
format.priority = -1;
|
||||
format.description = QObject::tr("Qt translation sources (format 1.1)");
|
||||
format.loader = &loadTS;
|
||||
format.saver = &saveTS11;
|
||||
Translator::registerFileFormat(format);
|
||||
|
||||
format.extension = QLatin1String("ts20");
|
||||
format.fileType = Translator::FileFormat::TranslationSource;
|
||||
format.priority = -1;
|
||||
format.description = QObject::tr("Qt translation sources (format 2.0)");
|
||||
format.loader = &loadTS;
|
||||
format.saver = &saveTS20;
|
||||
Translator::registerFileFormat(format);
|
||||
|
||||
// "ts" is always the latest. right now it's ts20.
|
||||
format.extension = QLatin1String("ts");
|
||||
format.fileType = Translator::FileFormat::TranslationSource;
|
||||
format.priority = 0;
|
||||
format.description = QObject::tr("Qt translation sources (latest format)");
|
||||
format.loader = &loadTS;
|
||||
format.saver = &saveTS20;
|
||||
Translator::registerFileFormat(format);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Q_CONSTRUCTOR_FUNCTION(initTS)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,100 +0,0 @@
|
|||
<!--
|
||||
!
|
||||
! Some notes to the DTD:
|
||||
!
|
||||
! The location element is set as optional since it was introduced first in Qt 4.2.
|
||||
! The userdata element is set as optional since it was introduced first in Qt 4.4.
|
||||
!
|
||||
-->
|
||||
<!--
|
||||
! Macro used in order to escape byte entities not allowed in an xml document
|
||||
! for instance, only #x9, #xA and #xD are allowed characters below #x20.
|
||||
-->
|
||||
<!ENTITY % evilstring '(#PCDATA | byte)*' >
|
||||
<!ELEMENT byte EMPTY>
|
||||
<!-- value contains decimal (e.g. 1000) or hex (e.g. x3e8) unicode encoding of one char -->
|
||||
<!ATTLIST byte
|
||||
value CDATA #REQUIRED>
|
||||
<!--
|
||||
! This element wildcard is no valid DTD. No better solution available.
|
||||
! extra elements may appear in TS and message elements. Each element may appear
|
||||
! only once within each scope. The contents are preserved verbatim; any
|
||||
! attributes are dropped. Currently recognized extra tags include:
|
||||
! extra-po-msgid_plural, extra-po-old_msgid_plural
|
||||
! extra-po-flags (comma-space separated list)
|
||||
! extra-loc-layout_id
|
||||
! extra-loc-feature
|
||||
! extra-loc-blank
|
||||
-->
|
||||
<!ELEMENT extra-* %evilstring; >
|
||||
<!ELEMENT TS (defaultcodec?, extra-**, (context|message)+) >
|
||||
<!ATTLIST TS
|
||||
version CDATA #IMPLIED
|
||||
sourcelanguage CDATA #IMPLIED
|
||||
language CDATA #IMPLIED>
|
||||
<!-- The encoding to use in the QM file by default. Default is ISO-8859-1. -->
|
||||
<!ELEMENT defaultcodec (#PCDATA) >
|
||||
<!ELEMENT context (name, comment?, (context|message)+) >
|
||||
<!ATTLIST context
|
||||
encoding CDATA #IMPLIED>
|
||||
<!ELEMENT name %evilstring; >
|
||||
<!-- This is "disambiguation" in the (new) API, or "msgctxt" in gettext speak -->
|
||||
<!ELEMENT comment %evilstring; >
|
||||
<!-- Previous content of comment (result of merge) -->
|
||||
<!ELEMENT oldcomment %evilstring; >
|
||||
<!-- The real comment (added by developer/designer) -->
|
||||
<!ELEMENT extracomment %evilstring; >
|
||||
<!-- Comment added by translator -->
|
||||
<!ELEMENT translatorcomment %evilstring; >
|
||||
<!ELEMENT message (location*, source?, oldsource?, comment?, oldcomment?, extracomment?, translatorcomment?, translation?, userdata?, extra-**) >
|
||||
<!--
|
||||
! If utf8 is "true", the defaultcodec is overridden and the message is encoded
|
||||
! in UTF-8 in the QM file. If it is "both", both source encodings are stored
|
||||
! in the QM file.
|
||||
-->
|
||||
<!ATTLIST message
|
||||
id CDATA #IMPLIED
|
||||
utf8 (true|false|both) "false"
|
||||
numerus (yes|no) "no">
|
||||
<!ELEMENT location EMPTY>
|
||||
<!--
|
||||
! If the line is omitted, the location specifies only a file.
|
||||
!
|
||||
! location supports relative specifications as well. Line numbers are
|
||||
! relative (explicitly positive or negative) to the last reference to a
|
||||
! given filename; each file starts with current line 0. If the filename
|
||||
! is omitted, the "current" one is used. For the 1st location in a message,
|
||||
! "current" is the filename used for the 1st location of the previous message.
|
||||
! For subsequent locations, it is the filename used for the previous location.
|
||||
! A single TS file has either all absolute or all relative locations.
|
||||
-->
|
||||
<!ATTLIST location
|
||||
filename CDATA #IMPLIED
|
||||
line CDATA #IMPLIED>
|
||||
<!ELEMENT source %evilstring;>
|
||||
<!-- Previous content of source (result of merge) -->
|
||||
<!ELEMENT oldsource %evilstring;>
|
||||
<!--
|
||||
! The following should really say one evilstring macro or several
|
||||
! numerusform or lengthvariant elements, but the DTD can't express this.
|
||||
-->
|
||||
<!ELEMENT translation (#PCDATA|byte|numerusform|lengthvariant)* >
|
||||
<!--
|
||||
! If no type is set, the message is "finished".
|
||||
! Length variants must be ordered by falling display length.
|
||||
! variants may not be yes if the message has numerus yes.
|
||||
-->
|
||||
<!ATTLIST translation
|
||||
type (unfinished|obsolete) #IMPLIED
|
||||
variants (yes|no) "no">
|
||||
<!-- Deprecated. Use extra-* -->
|
||||
<!ELEMENT userdata (#PCDATA)* >
|
||||
<!--
|
||||
! The following should really say one evilstring macro or several
|
||||
! lengthvariant elements, but the DTD can't express this.
|
||||
! Length variants must be ordered by falling display length.
|
||||
-->
|
||||
<!ELEMENT numerusform (#PCDATA|byte|lengthvariant)* >
|
||||
<!ATTLIST numerusform
|
||||
variants (yes|no) "no">
|
||||
<!ELEMENT lengthvariant %evilstring; >
|
|
@ -8,9 +8,6 @@ add_subdirectory(moc)
|
|||
# essential tools
|
||||
add_subdirectory(rcc)
|
||||
add_subdirectory(uic)
|
||||
add_subdirectory(lrelease)
|
||||
add_subdirectory(lupdate)
|
||||
add_subdirectory(lconvert)
|
||||
|
||||
# not so much
|
||||
if(WITH_DBUS AND DBUS_FOUND)
|
||||
|
|
|
@ -46,8 +46,6 @@
|
|||
#include <QtGui/QErrorMessage>
|
||||
#include <QtCore/QMetaObject>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QLibraryInfo>
|
||||
#include <QtCore/QLocale>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QTranslator>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
@ -134,7 +132,7 @@ QDesignerWorkbench *QDesigner::workbench() const
|
|||
return m_workbench;
|
||||
}
|
||||
|
||||
bool QDesigner::parseCommandLineArgs(QStringList &fileNames, QString &resourceDir)
|
||||
bool QDesigner::parseCommandLineArgs(QStringList &fileNames)
|
||||
{
|
||||
const QStringList args = arguments();
|
||||
const QStringList::const_iterator acend = args.constEnd();
|
||||
|
@ -148,15 +146,6 @@ bool QDesigner::parseCommandLineArgs(QStringList &fileNames, QString &resourceDi
|
|||
fileNames.append(argument);
|
||||
break;
|
||||
}
|
||||
// Options
|
||||
if (argument == QLatin1String("-resourcedir")) {
|
||||
if (++it == acend) {
|
||||
qWarning("** WARNING The option -resourcedir requires an argument");
|
||||
return false;
|
||||
}
|
||||
resourceDir = QFile::decodeName(it->toLocal8Bit());
|
||||
break;
|
||||
}
|
||||
if (argument == QLatin1String("-enableinternaldynamicproperties")) {
|
||||
QDesignerPropertySheet::setInternalDynamicPropertiesEnabled(true);
|
||||
break;
|
||||
|
@ -172,22 +161,16 @@ void QDesigner::initialize()
|
|||
{
|
||||
// initialize the sub components
|
||||
QStringList files;
|
||||
QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
|
||||
parseCommandLineArgs(files, resourceDir);
|
||||
parseCommandLineArgs(files);
|
||||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
QTranslator *translator = new QTranslator(this);
|
||||
QTranslator *qtTranslator = new QTranslator(this);
|
||||
|
||||
const QString localSysName = QLocale::system().name();
|
||||
QString translatorFileName = QLatin1String("qt_tools_");
|
||||
translatorFileName += localSysName;
|
||||
translator->load(translatorFileName, resourceDir);
|
||||
|
||||
translatorFileName = QLatin1String("qt_");
|
||||
translatorFileName += localSysName;
|
||||
qtTranslator->load(translatorFileName, resourceDir);
|
||||
translator->load(QLatin1String("qt_tools"));
|
||||
qtTranslator->load(QLatin1String("qt"));
|
||||
installTranslator(translator);
|
||||
installTranslator(qtTranslator);
|
||||
#endif
|
||||
|
||||
m_workbench = new QDesignerWorkbench();
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ private slots:
|
|||
void callCreateForm();
|
||||
|
||||
private:
|
||||
bool parseCommandLineArgs(QStringList &fileNames, QString &resourceDir);
|
||||
bool parseCommandLineArgs(QStringList &fileNames);
|
||||
void showErrorMessageBox(const QString &);
|
||||
|
||||
QDesignerWorkbench *m_workbench;
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
add_definitions(
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_CAST_TO_ASCII
|
||||
)
|
||||
set(EXTRA_LCONVERT_LIBS
|
||||
KtCore KtXml
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/privateinclude
|
||||
${CMAKE_BINARY_DIR}/include/QtCore
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtCore
|
||||
${CMAKE_BINARY_DIR}/include/QtXml
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtXml
|
||||
)
|
||||
|
||||
add_executable(lconvert
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lconvert.cpp
|
||||
$<TARGET_OBJECTS:sharedlinguist>
|
||||
)
|
||||
target_link_libraries(lconvert ${EXTRA_LCONVERT_LIBS})
|
||||
|
||||
set_target_properties(lconvert PROPERTIES
|
||||
EXPORT_NAME lconvert
|
||||
OUTPUT_NAME lconvert${KATIE_TOOLS_SUFFIX}
|
||||
)
|
||||
|
||||
katie_setup_object(lconvert sharedlinguist)
|
||||
|
||||
install(
|
||||
TARGETS lconvert
|
||||
EXPORT KatieBinaryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}
|
||||
RUNTIME DESTINATION ${KATIE_BINARIES_PATH}
|
||||
COMPONENT Tools
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/lconvert.1
|
||||
DESTINATION ${KATIE_MAN_PATH}/man1
|
||||
RENAME lconvert${KATIE_TOOLS_SUFFIX}.1
|
||||
COMPONENT Doc
|
||||
)
|
|
@ -1,187 +0,0 @@
|
|||
.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
|
||||
.\"
|
||||
.\" 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 "LCONVERT 1"
|
||||
.TH LCONVERT 1 "2020-11-11" "Katie 4.9.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"
|
||||
lconvert \- part of Katie's Linguist tool chain
|
||||
.SH "SYNOPSIS"
|
||||
.IX Header "SYNOPSIS"
|
||||
lconvert [options] <infile> [<infile>] ...
|
||||
.SH "DESCRIPTION"
|
||||
.IX Header "DESCRIPTION"
|
||||
lconvert can be used as a stand-alone tool to convert and filter translation
|
||||
data files. The following file formats are supported:
|
||||
.PP
|
||||
.Vb 7
|
||||
\& ts11 \- Qt translation sources (format 1.1)
|
||||
\& ts20 \- Qt translation sources (format 2.0)
|
||||
\& pot \- GNU Gettext localization template files
|
||||
\& ts \- Qt translation sources (latest format)
|
||||
\& qph \- Qt Linguist \*(AqPhrase Book\*(Aq
|
||||
\& po \- GNU Gettext localization files
|
||||
\& qm \- Compiled Qt translations
|
||||
.Ve
|
||||
.PP
|
||||
If multiple input files are specified, they are merged with translations from
|
||||
later files taking precedence.
|
||||
.SH "OPTIONS"
|
||||
.IX Header "OPTIONS"
|
||||
.Vb 3
|
||||
\& \-h
|
||||
\& \-\-help
|
||||
\& Display this information and exit.
|
||||
\&
|
||||
\& \-i <infile>
|
||||
\& \-\-input\-file <infile>
|
||||
\& Specify input file. Use if <infile> might start with a dash.
|
||||
\& This option can be used several times to merge inputs.
|
||||
\& May be \*(Aq\-\*(Aq (standard input) for use in a pipe.
|
||||
\&
|
||||
\& \-o <outfile>
|
||||
\& \-\-output\-file <outfile>
|
||||
\& Specify output file. Default is \*(Aq\-\*(Aq (standard output).
|
||||
\&
|
||||
\& \-if <informat>
|
||||
\& \-\-input\-format <format>
|
||||
\& Specify input format for subsequent <infile>s.
|
||||
\& The format is auto\-detected from the file name and defaults to \*(Aqts\*(Aq.
|
||||
\&
|
||||
\& \-of <outformat>
|
||||
\& \-\-output\-format <outformat>
|
||||
\& Specify output format. See \-if.
|
||||
\&
|
||||
\& \-\-input\-codec <codec>
|
||||
\& Specify encoding for QM and PO input files. Default is \*(AqLatin1\*(Aq
|
||||
\& for QM and \*(AqUTF\-8\*(Aq for PO files. UTF\-8 is always tried as well for
|
||||
\& QM, corresponding to the possible use of the trUtf8() function.
|
||||
\&
|
||||
\& \-\-output\-codec <codec>
|
||||
\& Specify encoding for PO output files. Default is \*(AqUTF\-8\*(Aq.
|
||||
\&
|
||||
\& \-\-drop\-tags <regexp>
|
||||
\& Drop named extra tags when writing TS or XLIFF files.
|
||||
\& May be specified repeatedly.
|
||||
\&
|
||||
\& \-\-drop\-translations
|
||||
\& Drop existing translations and reset the status to \*(Aqunfinished\*(Aq.
|
||||
\& Note: this implies \-\-no\-obsolete.
|
||||
\&
|
||||
\& \-\-source\-language <language>[_<region>]
|
||||
\& Specify/override the language of the source strings. Defaults to
|
||||
\& POSIX if not specified and the file does not name it yet.
|
||||
\&
|
||||
\& \-\-target\-language <language>[_<region>]
|
||||
\& Specify/override the language of the translation.
|
||||
\& The target language is guessed from the file name if this option
|
||||
\& is not specified and the file contents name no language yet.
|
||||
\&
|
||||
\& \-\-no\-obsolete
|
||||
\& Drop obsolete messages.
|
||||
\&
|
||||
\& \-\-no\-finished
|
||||
\& Drop finished messages.
|
||||
\&
|
||||
\& \-\-sort\-contexts
|
||||
\& Sort contexts in output TS file alphabetically.
|
||||
\&
|
||||
\& \-\-locations {absolute|relative|none}
|
||||
\& Override how source code references are saved in TS files.
|
||||
\& Default is absolute.
|
||||
\&
|
||||
\& \-\-no\-ui\-lines
|
||||
\& Drop line numbers from references to UI files.
|
||||
\&
|
||||
\& \-\-verbose
|
||||
\& be a bit more verbose
|
||||
.Ve
|
||||
.PP
|
||||
Long options can be specified with only one leading dash, too.
|
||||
.SH "EXIT STATUS"
|
||||
.IX Header "EXIT STATUS"
|
||||
lconvert returns 0 on success, 2 on read failure, 3 on write failure and other
|
||||
on unexcepted failure.
|
||||
.SH "AUTHORS"
|
||||
.IX Header "AUTHORS"
|
||||
The Qt Company Ltd.
|
||||
.PP
|
||||
Copyright (C) 2015 The Qt Company Ltd.
|
||||
Copyright (C) 2016\-2020 Ivailo Monev
|
||||
.PP
|
||||
Licensed through \s-1GNU\s0 Lesser General Public License/GNU General Public License.
|
||||
.SH "SEE ALSO"
|
||||
.IX Header "SEE ALSO"
|
||||
\&\fBlrelease\fR\|(1) \fBlupdate\fR\|(1)
|
|
@ -1,287 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTranslator>
|
||||
#include <QtCore/QLibraryInfo>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class LC {
|
||||
Q_DECLARE_TR_FUNCTIONS(LConvert)
|
||||
};
|
||||
|
||||
static int usage(const QStringList &args)
|
||||
{
|
||||
Q_UNUSED(args);
|
||||
|
||||
QString loaders;
|
||||
QString line(QLatin1String(" %1 - %2\n"));
|
||||
foreach (Translator::FileFormat format, Translator::registeredFileFormats())
|
||||
loaders += line.arg(format.extension, -5).arg(format.description);
|
||||
|
||||
std::cout << qPrintable(LC::tr("\nUsage:\n"
|
||||
" lconvert [options] <infile> [<infile>] ...\n\n"
|
||||
"lconvert is part of Katie's Linguist tool chain. It can be used as a\n"
|
||||
"stand-alone tool to convert and filter translation data files.\n"
|
||||
"The following file formats are supported:\n\n%1\n"
|
||||
"If multiple input files are specified, they are merged with\n"
|
||||
"translations from later files taking precedence.\n\n"
|
||||
"Options:\n"
|
||||
" -h\n"
|
||||
" --help\n"
|
||||
" Display this information and exit.\n\n"
|
||||
" -i <infile>\n"
|
||||
" --input-file <infile>\n"
|
||||
" Specify input file. Use if <infile> might start with a dash.\n"
|
||||
" This option can be used several times to merge inputs.\n"
|
||||
" May be '-' (standard input) for use in a pipe.\n\n"
|
||||
" -o <outfile>\n"
|
||||
" --output-file <outfile>\n"
|
||||
" Specify output file. Default is '-' (standard output).\n\n"
|
||||
" -if <informat>\n"
|
||||
" --input-format <format>\n"
|
||||
" Specify input format for subsequent <infile>s.\n"
|
||||
" The format is auto-detected from the file name and defaults to 'ts'.\n\n"
|
||||
" -of <outformat>\n"
|
||||
" --output-format <outformat>\n"
|
||||
" Specify output format. See -if.\n\n"
|
||||
" --input-codec <codec>\n"
|
||||
" Specify encoding for QM and PO input files. Default is 'Latin1'\n"
|
||||
" for QM and 'UTF-8' for PO files. UTF-8 is always tried as well for\n"
|
||||
" QM, corresponding to the possible use of the trUtf8() function.\n\n"
|
||||
" --output-codec <codec>\n"
|
||||
" Specify encoding for PO output files. Default is 'UTF-8'.\n\n"
|
||||
" --drop-tags <regexp>\n"
|
||||
" Drop named extra tags when writing TS or XLIFF files.\n"
|
||||
" May be specified repeatedly.\n\n"
|
||||
" --drop-translations\n"
|
||||
" Drop existing translations and reset the status to 'unfinished'.\n"
|
||||
" Note: this implies --no-obsolete.\n\n"
|
||||
" --source-language <language>[_<region>]\n"
|
||||
" Specify/override the language of the source strings. Defaults to\n"
|
||||
" POSIX if not specified and the file does not name it yet.\n\n"
|
||||
" --target-language <language>[_<region>]\n"
|
||||
" Specify/override the language of the translation.\n"
|
||||
" The target language is guessed from the file name if this option\n"
|
||||
" is not specified and the file contents name no language yet.\n\n"
|
||||
" --no-obsolete\n"
|
||||
" Drop obsolete messages.\n\n"
|
||||
" --no-finished\n"
|
||||
" Drop finished messages.\n\n"
|
||||
" --sort-contexts\n"
|
||||
" Sort contexts in output TS file alphabetically.\n\n"
|
||||
" --locations {absolute|relative|none}\n"
|
||||
" Override how source code references are saved in TS files.\n"
|
||||
" Default is absolute.\n\n"
|
||||
" --no-ui-lines\n"
|
||||
" Drop line numbers from references to UI files.\n\n"
|
||||
" --verbose\n"
|
||||
" be a bit more verbose\n\n"
|
||||
"Long options can be specified with only one leading dash, too.\n").arg(loaders));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct File
|
||||
{
|
||||
QString name;
|
||||
QString format;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
QTranslator translator;
|
||||
QTranslator qtTranslator;
|
||||
QString sysLocale = QLocale::system().name();
|
||||
QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
|
||||
if (translator.load(QLatin1String("qt_tools_") + sysLocale, resourceDir)
|
||||
&& qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
|
||||
app.installTranslator(&translator);
|
||||
app.installTranslator(&qtTranslator);
|
||||
}
|
||||
|
||||
QStringList args = app.arguments();
|
||||
QList<File> inFiles;
|
||||
QString inFormat(QLatin1String("auto"));
|
||||
QString outFileName;
|
||||
QString outFormat(QLatin1String("auto"));
|
||||
QString targetLanguage;
|
||||
QString sourceLanguage;
|
||||
bool dropTranslations = false;
|
||||
bool noObsolete = false;
|
||||
bool noFinished = false;
|
||||
bool verbose = false;
|
||||
bool noUiLines = false;
|
||||
Translator::LocationsType locations = Translator::DefaultLocations;
|
||||
|
||||
ConversionData cd;
|
||||
Translator tr;
|
||||
|
||||
for (int i = 1; i < args.size(); ++i) {
|
||||
if (args[i].startsWith(QLatin1String("--")))
|
||||
args[i].remove(0, 1);
|
||||
if (args[i] == QLatin1String("-o")
|
||||
|| args[i] == QLatin1String("-output-file")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
outFileName = args[i];
|
||||
} else if (args[i] == QLatin1String("-of")
|
||||
|| args[i] == QLatin1String("-output-format")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
outFormat = args[i];
|
||||
} else if (args[i] == QLatin1String("-i")
|
||||
|| args[i] == QLatin1String("-input-file")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
File file;
|
||||
file.name = args[i];
|
||||
file.format = inFormat;
|
||||
inFiles.append(file);
|
||||
} else if (args[i] == QLatin1String("-if")
|
||||
|| args[i] == QLatin1String("-input-format")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
inFormat = args[i];
|
||||
} else if (args[i] == QLatin1String("-input-codec")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
cd.m_codecForSource = args[i].toLatin1();
|
||||
} else if (args[i] == QLatin1String("-output-codec")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
cd.m_outputCodec = args[i].toLatin1();
|
||||
} else if (args[i] == QLatin1String("-drop-tag")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
cd.m_dropTags.append(args[i]);
|
||||
} else if (args[i] == QLatin1String("-drop-translations")) {
|
||||
dropTranslations = true;
|
||||
} else if (args[i] == QLatin1String("-target-language")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
targetLanguage = args[i];
|
||||
} else if (args[i] == QLatin1String("-source-language")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
sourceLanguage = args[i];
|
||||
} else if (args[i].startsWith(QLatin1String("-h"))) {
|
||||
usage(args);
|
||||
return 0;
|
||||
} else if (args[i] == QLatin1String("-no-obsolete")) {
|
||||
noObsolete = true;
|
||||
} else if (args[i] == QLatin1String("-no-finished")) {
|
||||
noFinished = true;
|
||||
} else if (args[i] == QLatin1String("-sort-contexts")) {
|
||||
cd.m_sortContexts = true;
|
||||
} else if (args[i] == QLatin1String("-locations")) {
|
||||
if (++i >= args.size())
|
||||
return usage(args);
|
||||
if (args[i] == QLatin1String("none"))
|
||||
locations = Translator::NoLocations;
|
||||
else if (args[i] == QLatin1String("relative"))
|
||||
locations = Translator::RelativeLocations;
|
||||
else if (args[i] == QLatin1String("absolute"))
|
||||
locations = Translator::AbsoluteLocations;
|
||||
else
|
||||
return usage(args);
|
||||
} else if (args[i] == QLatin1String("-no-ui-lines")) {
|
||||
noUiLines = true;
|
||||
} else if (args[i] == QLatin1String("-verbose")) {
|
||||
verbose = true;
|
||||
} else if (args[i].startsWith(QLatin1Char('-'))) {
|
||||
return usage(args);
|
||||
} else {
|
||||
File file;
|
||||
file.name = args[i];
|
||||
file.format = inFormat;
|
||||
inFiles.append(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (inFiles.isEmpty())
|
||||
return usage(args);
|
||||
|
||||
tr.setLanguageCode(Translator::guessLanguageCodeFromFileName(inFiles[0].name));
|
||||
|
||||
if (!tr.load(inFiles[0].name, cd, inFiles[0].format)) {
|
||||
std::cerr << qPrintable(cd.error());
|
||||
return 2;
|
||||
}
|
||||
tr.reportDuplicates(tr.resolveDuplicates(), inFiles[0].name, verbose);
|
||||
|
||||
for (int i = 1; i < inFiles.size(); ++i) {
|
||||
Translator tr2;
|
||||
if (!tr2.load(inFiles[i].name, cd, inFiles[i].format)) {
|
||||
std::cerr << qPrintable(cd.error());
|
||||
return 2;
|
||||
}
|
||||
tr2.reportDuplicates(tr2.resolveDuplicates(), inFiles[i].name, verbose);
|
||||
for (int j = 0; j < tr2.messageCount(); ++j)
|
||||
tr.replaceSorted(tr2.message(j));
|
||||
}
|
||||
|
||||
if (!targetLanguage.isEmpty())
|
||||
tr.setLanguageCode(targetLanguage);
|
||||
if (!sourceLanguage.isEmpty())
|
||||
tr.setSourceLanguageCode(sourceLanguage);
|
||||
if (noObsolete)
|
||||
tr.stripObsoleteMessages();
|
||||
if (noFinished)
|
||||
tr.stripFinishedMessages();
|
||||
if (dropTranslations)
|
||||
tr.dropTranslations();
|
||||
if (noUiLines)
|
||||
tr.dropUiLines();
|
||||
if (locations != Translator::DefaultLocations)
|
||||
tr.setLocationsType(locations);
|
||||
|
||||
tr.normalizeTranslations(cd);
|
||||
if (!cd.errors().isEmpty()) {
|
||||
std::cerr << qPrintable(cd.error());
|
||||
cd.clearErrors();
|
||||
}
|
||||
if (!tr.save(outFileName, cd, outFormat)) {
|
||||
std::cerr << qPrintable(cd.error());
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
=head1 NAME
|
||||
|
||||
lconvert - part of Katie's Linguist tool chain
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
lconvert [options] <infile> [<infile>] ...
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
lconvert can be used as a stand-alone tool to convert and filter translation
|
||||
data files. The following file formats are supported:
|
||||
|
||||
ts11 - Qt translation sources (format 1.1)
|
||||
ts20 - Qt translation sources (format 2.0)
|
||||
pot - GNU Gettext localization template files
|
||||
ts - Qt translation sources (latest format)
|
||||
qph - Qt Linguist 'Phrase Book'
|
||||
po - GNU Gettext localization files
|
||||
qm - Compiled Qt translations
|
||||
|
||||
If multiple input files are specified, they are merged with translations from
|
||||
later files taking precedence.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-h
|
||||
--help
|
||||
Display this information and exit.
|
||||
|
||||
-i <infile>
|
||||
--input-file <infile>
|
||||
Specify input file. Use if <infile> might start with a dash.
|
||||
This option can be used several times to merge inputs.
|
||||
May be '-' (standard input) for use in a pipe.
|
||||
|
||||
-o <outfile>
|
||||
--output-file <outfile>
|
||||
Specify output file. Default is '-' (standard output).
|
||||
|
||||
-if <informat>
|
||||
--input-format <format>
|
||||
Specify input format for subsequent <infile>s.
|
||||
The format is auto-detected from the file name and defaults to 'ts'.
|
||||
|
||||
-of <outformat>
|
||||
--output-format <outformat>
|
||||
Specify output format. See -if.
|
||||
|
||||
--input-codec <codec>
|
||||
Specify encoding for QM and PO input files. Default is 'Latin1'
|
||||
for QM and 'UTF-8' for PO files. UTF-8 is always tried as well for
|
||||
QM, corresponding to the possible use of the trUtf8() function.
|
||||
|
||||
--output-codec <codec>
|
||||
Specify encoding for PO output files. Default is 'UTF-8'.
|
||||
|
||||
--drop-tags <regexp>
|
||||
Drop named extra tags when writing TS or XLIFF files.
|
||||
May be specified repeatedly.
|
||||
|
||||
--drop-translations
|
||||
Drop existing translations and reset the status to 'unfinished'.
|
||||
Note: this implies --no-obsolete.
|
||||
|
||||
--source-language <language>[_<region>]
|
||||
Specify/override the language of the source strings. Defaults to
|
||||
POSIX if not specified and the file does not name it yet.
|
||||
|
||||
--target-language <language>[_<region>]
|
||||
Specify/override the language of the translation.
|
||||
The target language is guessed from the file name if this option
|
||||
is not specified and the file contents name no language yet.
|
||||
|
||||
--no-obsolete
|
||||
Drop obsolete messages.
|
||||
|
||||
--no-finished
|
||||
Drop finished messages.
|
||||
|
||||
--sort-contexts
|
||||
Sort contexts in output TS file alphabetically.
|
||||
|
||||
--locations {absolute|relative|none}
|
||||
Override how source code references are saved in TS files.
|
||||
Default is absolute.
|
||||
|
||||
--no-ui-lines
|
||||
Drop line numbers from references to UI files.
|
||||
|
||||
--verbose
|
||||
be a bit more verbose
|
||||
|
||||
Long options can be specified with only one leading dash, too.
|
||||
|
||||
=head1 EXIT STATUS
|
||||
|
||||
lconvert returns 0 on success, 2 on read failure, 3 on write failure and other
|
||||
on unexcepted failure.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
The Qt Company Ltd.
|
||||
|
||||
Copyright (C) 2015 The Qt Company Ltd.
|
||||
Copyright (C) 2016-2020 Ivailo Monev
|
||||
|
||||
Licensed through GNU Lesser General Public License/GNU General Public License.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
lrelease(1) lupdate(1)
|
|
@ -1,42 +0,0 @@
|
|||
add_definitions(
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_CAST_TO_ASCII
|
||||
)
|
||||
set(EXTRA_LRELEASE_LIBS
|
||||
KtCore KtXml
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/privateinclude
|
||||
${CMAKE_BINARY_DIR}/include/QtCore
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtCore
|
||||
${CMAKE_BINARY_DIR}/include/QtXml
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtXml
|
||||
)
|
||||
|
||||
add_executable(lrelease ${CMAKE_CURRENT_SOURCE_DIR}/lrelease.cpp
|
||||
$<TARGET_OBJECTS:sharedlinguist>
|
||||
)
|
||||
target_link_libraries(lrelease ${EXTRA_LRELEASE_LIBS})
|
||||
|
||||
set_target_properties(lrelease PROPERTIES
|
||||
EXPORT_NAME lrelease
|
||||
OUTPUT_NAME lrelease${KATIE_TOOLS_SUFFIX}
|
||||
)
|
||||
|
||||
katie_setup_object(lrelease sharedlinguist)
|
||||
|
||||
install(
|
||||
TARGETS lrelease
|
||||
EXPORT KatieBinaryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}
|
||||
RUNTIME DESTINATION ${KATIE_BINARIES_PATH}
|
||||
COMPONENT Tools
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/lrelease.1
|
||||
DESTINATION ${KATIE_MAN_PATH}/man1
|
||||
RENAME lrelease${KATIE_TOOLS_SUFFIX}.1
|
||||
COMPONENT Doc
|
||||
)
|
|
@ -1,132 +0,0 @@
|
|||
.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
|
||||
.\"
|
||||
.\" 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 "LRELEASE 1"
|
||||
.TH LRELEASE 1 "2020-11-11" "Katie 4.9.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"
|
||||
lrelease \- part of Katie's Linguist tool chain
|
||||
.SH "SYNOPSIS"
|
||||
.IX Header "SYNOPSIS"
|
||||
lrelease [options] ts-files [\-qm qm\-file]
|
||||
.SH "DESCRIPTION"
|
||||
.IX Header "DESCRIPTION"
|
||||
lrelease can be used as a stand-alone tool to convert XML-based translations
|
||||
files in the \s-1TS\s0 format into the 'compiled' \s-1QM\s0 format used by QTranslator
|
||||
objects.
|
||||
.SH "OPTIONS"
|
||||
.IX Header "OPTIONS"
|
||||
.Vb 2
|
||||
\& \-help
|
||||
\& Display this information and exit.
|
||||
\&
|
||||
\& \-idbased
|
||||
\& Use IDs instead of source strings for message keying.
|
||||
\&
|
||||
\& \-compress
|
||||
\& Compress the QM files.
|
||||
\&
|
||||
\& \-nounfinished
|
||||
\& Do not include unfinished translations.
|
||||
\&
|
||||
\& \-removeidentical
|
||||
\& If the translated text is the same as the source text,
|
||||
\& do not include the message.
|
||||
\&
|
||||
\& \-markuntranslated <prefix>
|
||||
\& If a message has no real translation, use the source text
|
||||
\& prefixed with the given string instead.
|
||||
\&
|
||||
\& \-silent
|
||||
\& Do not explain what is being done.
|
||||
\&
|
||||
\& \-version
|
||||
\& Display the version of lrelease and exit.
|
||||
.Ve
|
||||
.SH "EXIT STATUS"
|
||||
.IX Header "EXIT STATUS"
|
||||
lrelease returns 0 on success, 1 on wrong usage and other on unexpected
|
||||
failure.
|
||||
.SH "AUTHORS"
|
||||
.IX Header "AUTHORS"
|
||||
The Qt Company Ltd.
|
||||
.PP
|
||||
Copyright (C) 2015 The Qt Company Ltd.
|
||||
Copyright (C) 2016\-2020 Ivailo Monev
|
||||
.PP
|
||||
Licensed through \s-1GNU\s0 Lesser General Public License/GNU General Public License.
|
||||
.SH "SEE ALSO"
|
||||
.IX Header "SEE ALSO"
|
||||
\&\fBlconvert\fR\|(1) \fBlupdate\fR\|(1)
|
|
@ -1,241 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QTranslator>
|
||||
#include <QtCore/QLibraryInfo>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QRegExp>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class LR {
|
||||
Q_DECLARE_TR_FUNCTIONS(LRelease)
|
||||
};
|
||||
|
||||
static void printOut(const QString & out)
|
||||
{
|
||||
QTextStream stream(stdout);
|
||||
stream << out;
|
||||
}
|
||||
|
||||
static void printErr(const QString & out)
|
||||
{
|
||||
QTextStream stream(stderr);
|
||||
stream << out;
|
||||
}
|
||||
|
||||
static void printUsage()
|
||||
{
|
||||
printOut(LR::tr(
|
||||
"Usage:\n"
|
||||
" lrelease [options] ts-files [-qm qm-file]\n\n"
|
||||
"lrelease is part of Katie's Linguist tool chain. It can be used as a\n"
|
||||
"stand-alone tool to convert XML-based translations files in the TS\n"
|
||||
"format into the 'compiled' QM format used by QTranslator objects.\n\n"
|
||||
"Options:\n"
|
||||
" -help\n"
|
||||
" Display this information and exit.\n\n"
|
||||
" -idbased\n"
|
||||
" Use IDs instead of source strings for message keying.\n\n"
|
||||
" -compress\n"
|
||||
" Compress the QM files.\n\n"
|
||||
" -nounfinished\n"
|
||||
" Do not include unfinished translations.\n\n"
|
||||
" -removeidentical\n"
|
||||
" If the translated text is the same as the source text,\n"
|
||||
" do not include the message.\n\n"
|
||||
" -markuntranslated <prefix>\n"
|
||||
" If a message has no real translation, use the source text\n"
|
||||
" prefixed with the given string instead.\n\n"
|
||||
" -silent\n"
|
||||
" Do not explain what is being done.\n\n"
|
||||
" -version\n"
|
||||
" Display the version of lrelease and exit.\n"
|
||||
));
|
||||
}
|
||||
|
||||
static bool loadTsFile(Translator &tor, const QString &tsFileName, bool /* verbose */)
|
||||
{
|
||||
ConversionData cd;
|
||||
bool ok = tor.load(tsFileName, cd, QLatin1String("auto"));
|
||||
if (!ok) {
|
||||
printErr(LR::tr("lrelease error: %1").arg(cd.error()));
|
||||
} else {
|
||||
if (!cd.errors().isEmpty())
|
||||
printOut(cd.error());
|
||||
}
|
||||
cd.clearErrors();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool releaseTranslator(Translator &tor, const QString &qmFileName,
|
||||
ConversionData &cd, bool removeIdentical)
|
||||
{
|
||||
tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose());
|
||||
|
||||
if (cd.isVerbose())
|
||||
printOut(LR::tr("Updating '%1'...\n").arg(qmFileName));
|
||||
if (removeIdentical) {
|
||||
if (cd.isVerbose())
|
||||
printOut(LR::tr("Removing translations equal to source text in '%1'...\n").arg(qmFileName));
|
||||
tor.stripIdenticalSourceTranslations();
|
||||
}
|
||||
|
||||
QFile file(qmFileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
printErr(LR::tr("lrelease error: cannot create '%1': %2\n")
|
||||
.arg(qmFileName, file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
tor.normalizeTranslations(cd);
|
||||
bool ok = saveQM(tor, file, cd);
|
||||
file.close();
|
||||
|
||||
if (!ok) {
|
||||
printErr(LR::tr("lrelease error: cannot save '%1': %2")
|
||||
.arg(qmFileName, cd.error()));
|
||||
} else if (!cd.errors().isEmpty()) {
|
||||
printOut(cd.error());
|
||||
}
|
||||
cd.clearErrors();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool releaseTsFile(const QString& tsFileName,
|
||||
ConversionData &cd, bool removeIdentical)
|
||||
{
|
||||
Translator tor;
|
||||
if (!loadTsFile(tor, tsFileName, cd.isVerbose()))
|
||||
return false;
|
||||
|
||||
QString qmFileName = tsFileName;
|
||||
foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
|
||||
if (qmFileName.endsWith(QLatin1Char('.') + fmt.extension)) {
|
||||
qmFileName.chop(fmt.extension.length() + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
qmFileName += QLatin1String(".qm");
|
||||
|
||||
return releaseTranslator(tor, qmFileName, cd, removeIdentical);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
ConversionData cd;
|
||||
cd.m_verbose = true; // the default is true starting with Qt 4.2
|
||||
bool removeIdentical = false;
|
||||
Translator tor;
|
||||
QStringList inputFiles;
|
||||
QString outputFile;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "-compress")) {
|
||||
cd.m_saveMode = SaveStripped;
|
||||
continue;
|
||||
} else if (!strcmp(argv[i], "-idbased")) {
|
||||
cd.m_idBased = true;
|
||||
continue;
|
||||
} else if (!strcmp(argv[i], "-nocompress")) {
|
||||
cd.m_saveMode = SaveEverything;
|
||||
continue;
|
||||
} else if (!strcmp(argv[i], "-removeidentical")) {
|
||||
removeIdentical = true;
|
||||
continue;
|
||||
} else if (!strcmp(argv[i], "-nounfinished")) {
|
||||
cd.m_ignoreUnfinished = true;
|
||||
continue;
|
||||
} else if (!strcmp(argv[i], "-markuntranslated")) {
|
||||
if (i == argc - 1) {
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
cd.m_unTrPrefix = QString::fromLocal8Bit(argv[++i]);
|
||||
} else if (!strcmp(argv[i], "-silent")) {
|
||||
cd.m_verbose = false;
|
||||
continue;
|
||||
} else if (!strcmp(argv[i], "-verbose")) {
|
||||
cd.m_verbose = true;
|
||||
continue;
|
||||
} else if (!strcmp(argv[i], "-version")) {
|
||||
printOut(LR::tr("lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR)));
|
||||
return 0;
|
||||
} else if (!strcmp(argv[i], "-qm")) {
|
||||
if (i == argc - 1) {
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
outputFile = QString::fromLocal8Bit(argv[++i]);
|
||||
} else if (!strcmp(argv[i], "-help")) {
|
||||
printUsage();
|
||||
return 0;
|
||||
} else if (argv[i][0] == '-') {
|
||||
printUsage();
|
||||
return 1;
|
||||
} else {
|
||||
inputFiles << QString::fromLocal8Bit(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (inputFiles.isEmpty()) {
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
foreach (const QString &inputFile, inputFiles) {
|
||||
if (outputFile.isEmpty()) {
|
||||
if (!releaseTsFile(inputFile, cd, removeIdentical))
|
||||
return 1;
|
||||
} else {
|
||||
if (!loadTsFile(tor, inputFile, cd.isVerbose()))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputFile.isEmpty())
|
||||
return releaseTranslator(tor, outputFile, cd, removeIdentical) ? 0 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
=head1 NAME
|
||||
|
||||
lrelease - part of Katie's Linguist tool chain
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
lrelease [options] ts-files [-qm qm-file]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
lrelease can be used as a stand-alone tool to convert XML-based translations
|
||||
files in the TS format into the 'compiled' QM format used by QTranslator
|
||||
objects.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-help
|
||||
Display this information and exit.
|
||||
|
||||
-idbased
|
||||
Use IDs instead of source strings for message keying.
|
||||
|
||||
-compress
|
||||
Compress the QM files.
|
||||
|
||||
-nounfinished
|
||||
Do not include unfinished translations.
|
||||
|
||||
-removeidentical
|
||||
If the translated text is the same as the source text,
|
||||
do not include the message.
|
||||
|
||||
-markuntranslated <prefix>
|
||||
If a message has no real translation, use the source text
|
||||
prefixed with the given string instead.
|
||||
|
||||
-silent
|
||||
Do not explain what is being done.
|
||||
|
||||
-version
|
||||
Display the version of lrelease and exit.
|
||||
|
||||
=head1 EXIT STATUS
|
||||
|
||||
lrelease returns 0 on success, 1 on wrong usage and other on unexpected
|
||||
failure.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
The Qt Company Ltd.
|
||||
|
||||
Copyright (C) 2015 The Qt Company Ltd.
|
||||
Copyright (C) 2016-2020 Ivailo Monev
|
||||
|
||||
Licensed through GNU Lesser General Public License/GNU General Public License.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
lconvert(1) lupdate(1)
|
|
@ -1,53 +0,0 @@
|
|||
# add_definitions()
|
||||
set(EXTRA_LUPDATE_LIBS
|
||||
KtCore KtXml
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/privateinclude
|
||||
${CMAKE_BINARY_DIR}/include/QtCore
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtCore
|
||||
${CMAKE_BINARY_DIR}/include/QtXml
|
||||
${CMAKE_BINARY_DIR}/privateinclude/QtXml
|
||||
)
|
||||
|
||||
set(LUPDATE_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lupdate.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/merge.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cpp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qscript.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui.cpp
|
||||
)
|
||||
|
||||
set(LUPDATE_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lupdate.h
|
||||
)
|
||||
|
||||
katie_setup_target(lupdate ${LUPDATE_SOURCES} ${LUPDATE_HEADERS})
|
||||
|
||||
add_executable(lupdate ${lupdate_SOURCES}
|
||||
$<TARGET_OBJECTS:sharedlinguist>
|
||||
)
|
||||
target_link_libraries(lupdate ${EXTRA_LUPDATE_LIBS})
|
||||
|
||||
set_target_properties(lupdate PROPERTIES
|
||||
EXPORT_NAME lupdate
|
||||
OUTPUT_NAME lupdate${KATIE_TOOLS_SUFFIX}
|
||||
)
|
||||
|
||||
katie_setup_object(lupdate sharedlinguist)
|
||||
|
||||
install(
|
||||
TARGETS lupdate
|
||||
EXPORT KatieBinaryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}
|
||||
RUNTIME DESTINATION ${KATIE_BINARIES_PATH}
|
||||
COMPONENT Tools
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/lupdate.1
|
||||
DESTINATION ${KATIE_MAN_PATH}/man1
|
||||
RENAME lupdate${KATIE_TOOLS_SUFFIX}.1
|
||||
COMPONENT Doc
|
||||
)
|
File diff suppressed because it is too large
Load diff
|
@ -1,165 +0,0 @@
|
|||
.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
|
||||
.\"
|
||||
.\" 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 "LUPDATE 1"
|
||||
.TH LUPDATE 1 "2020-11-11" "Katie 4.9.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"
|
||||
lupdate \- part of Katie's Linguist tool chain
|
||||
.SH "SYNOPSIS"
|
||||
.IX Header "SYNOPSIS"
|
||||
lupdate [options] [source\-file|path|@lst\-file] ... \-ts ts\-files|@lst\-file
|
||||
.SH "DESCRIPTION"
|
||||
.IX Header "DESCRIPTION"
|
||||
lupdate extracts translatable messages from Qt \s-1UI\s0 files, \*(C+ and
|
||||
JavaScript/QtScript source code. Extracted messages are stored in textual
|
||||
translation source files (typically Qt \s-1TS XML\s0). New and modified messages can
|
||||
be merged into existing \s-1TS\s0 files.
|
||||
.SH "OPTIONS"
|
||||
.IX Header "OPTIONS"
|
||||
.Vb 4
|
||||
\& \-h
|
||||
\& \-help Display this information and exit.
|
||||
\& \-no\-obsolete
|
||||
\& Drop all obsolete strings.
|
||||
\&
|
||||
\& \-extensions <ext>[,<ext>]...
|
||||
\& Process files with the given extensions only.
|
||||
\& The extension list must be separated with commas, not with whitespace.
|
||||
\& Default: \*(Aqui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml\*(Aq.
|
||||
\&
|
||||
\& \-pluralonly
|
||||
\& Only include plural form messages.
|
||||
\&
|
||||
\& \-silent
|
||||
\& Do not explain what is being done.
|
||||
\&
|
||||
\& \-no\-sort
|
||||
\& Do not sort contexts in TS files.
|
||||
\&
|
||||
\& \-no\-recursive
|
||||
\& Do not recursively scan the following directories.
|
||||
\&
|
||||
\& \-recursive
|
||||
\& Recursively scan the following directories (default).
|
||||
\&
|
||||
\& \-I <includepath> or \-I<includepath>
|
||||
\& Additional location to look for include files.
|
||||
\& May be specified multiple times.
|
||||
\&
|
||||
\& \-locations {absolute|relative|none}
|
||||
\& Specify/override how source code references are saved in TS files.
|
||||
\& Default is absolute.
|
||||
\& \-no\-ui\-lines
|
||||
\& Do not record line numbers in references to UI files.
|
||||
\&
|
||||
\& \-disable\-heuristic {sametext|similartext|number}
|
||||
\& Disable the named merge heuristic. Can be specified multiple times.
|
||||
\&
|
||||
\& \-source\-language <language>[_<region>]
|
||||
\& Specify the language of the source strings for new files.
|
||||
\& Defaults to POSIX if not specified.
|
||||
\&
|
||||
\& \-target\-language <language>[_<region>]
|
||||
\& Specify the language of the translations for new files.
|
||||
\& Guessed from the file name if not specified.
|
||||
\&
|
||||
\& \-ts <ts\-file>...
|
||||
\& Specify the output file(s). This will override the TRANSLATIONS
|
||||
\& and nullify the CODECFORTR from possibly specified project files.
|
||||
\&
|
||||
\& \-codecfortr <codec>
|
||||
\& Specify the codec assumed for tr() calls. Effective only with \-ts.
|
||||
\&
|
||||
\& \-version
|
||||
\& Display the version of lupdate and exit.
|
||||
\&
|
||||
\& @lst\-file
|
||||
\& Read additional file names (one per line) from lst\-file.
|
||||
.Ve
|
||||
.SH "EXIT STATUS"
|
||||
.IX Header "EXIT STATUS"
|
||||
lupdate returns 0 on success and other on unexcepted failure.
|
||||
.SH "AUTHORS"
|
||||
.IX Header "AUTHORS"
|
||||
The Qt Company Ltd.
|
||||
.PP
|
||||
Copyright (C) 2015 The Qt Company Ltd.
|
||||
Copyright (C) 2016\-2020 Ivailo Monev
|
||||
.PP
|
||||
Licensed through \s-1GNU\s0 Lesser General Public License/GNU General Public License.
|
||||
.SH "SEE ALSO"
|
||||
.IX Header "SEE ALSO"
|
||||
\&\fBlconvert\fR\|(1) \fBlrelease\fR\|(1)
|
|
@ -1,512 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "lupdate.h"
|
||||
|
||||
#include <translator.h>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTextCodec>
|
||||
#include <QtCore/QTranslator>
|
||||
#include <QtCore/QLibraryInfo>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
static QString m_defaultExtensions;
|
||||
|
||||
static void printOut(const QString & out)
|
||||
{
|
||||
QTextStream stream(stdout);
|
||||
stream << out;
|
||||
}
|
||||
|
||||
static void printErr(const QString & out)
|
||||
{
|
||||
QTextStream stream(stderr);
|
||||
stream << out;
|
||||
}
|
||||
|
||||
static void recursiveFileInfoList(const QDir &dir,
|
||||
const QSet<QString> &nameFilters, QDir::Filters filter,
|
||||
QFileInfoList *fileinfolist)
|
||||
{
|
||||
foreach (const QFileInfo &fi, dir.entryInfoList(filter))
|
||||
if (fi.isDir())
|
||||
recursiveFileInfoList(QDir(fi.absoluteFilePath()), nameFilters, filter, fileinfolist);
|
||||
else if (nameFilters.contains(fi.suffix()))
|
||||
fileinfolist->append(fi);
|
||||
}
|
||||
|
||||
static void printUsage()
|
||||
{
|
||||
printOut(LU::tr(
|
||||
"Usage:\n"
|
||||
" lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file\n\n"
|
||||
"lupdate is part of Katie's Linguist tool chain. It extracts translatable\n"
|
||||
"messages from Qt UI files, C++ and JavaScript/QtScript source code.\n"
|
||||
"Extracted messages are stored in textual translation source files (typically\n"
|
||||
"Qt TS XML). New and modified messages can be merged into existing TS files.\n\n"
|
||||
"Options:\n"
|
||||
" -h\n"
|
||||
" -help Display this information and exit.\n"
|
||||
" -no-obsolete\n"
|
||||
" Drop all obsolete strings.\n\n"
|
||||
" -extensions <ext>[,<ext>]...\n"
|
||||
" Process files with the given extensions only.\n"
|
||||
" The extension list must be separated with commas, not with whitespace.\n"
|
||||
" Default: '%1'.\n\n"
|
||||
" -pluralonly\n"
|
||||
" Only include plural form messages.\n\n"
|
||||
" -silent\n"
|
||||
" Do not explain what is being done.\n\n"
|
||||
" -no-sort\n"
|
||||
" Do not sort contexts in TS files.\n\n"
|
||||
" -no-recursive\n"
|
||||
" Do not recursively scan the following directories.\n\n"
|
||||
" -recursive\n"
|
||||
" Recursively scan the following directories (default).\n\n"
|
||||
" -I <includepath> or -I<includepath>\n"
|
||||
" Additional location to look for include files.\n"
|
||||
" May be specified multiple times.\n\n"
|
||||
" -locations {absolute|relative|none}\n"
|
||||
" Specify/override how source code references are saved in TS files.\n"
|
||||
" Default is absolute.\n\n"
|
||||
" -no-ui-lines\n"
|
||||
" Do not record line numbers in references to UI files.\n\n"
|
||||
" -disable-heuristic {sametext|similartext|number}\n"
|
||||
" Disable the named merge heuristic. Can be specified multiple times.\n\n"
|
||||
" -source-language <language>[_<region>]\n"
|
||||
" Specify the language of the source strings for new files.\n"
|
||||
" Defaults to POSIX if not specified.\n\n"
|
||||
" -target-language <language>[_<region>]\n"
|
||||
" Specify the language of the translations for new files.\n"
|
||||
" Guessed from the file name if not specified.\n\n"
|
||||
" -ts <ts-file>...\n"
|
||||
" Specify the output file(s). This will override the TRANSLATIONS\n"
|
||||
" and nullify the CODECFORTR from possibly specified project files.\n\n"
|
||||
" -codecfortr <codec>\n"
|
||||
" Specify the codec assumed for tr() calls. Effective only with -ts.\n\n"
|
||||
" -version\n"
|
||||
" Display the version of lupdate and exit.\n\n"
|
||||
" @lst-file\n"
|
||||
" Read additional file names (one per line) from lst-file.\n"
|
||||
).arg(m_defaultExtensions));
|
||||
}
|
||||
|
||||
static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames,
|
||||
bool setCodec, const QString &sourceLanguage, const QString &targetLanguage,
|
||||
UpdateOptions options, bool *fail)
|
||||
{
|
||||
QDir dir;
|
||||
QString err;
|
||||
foreach (const QString &fileName, tsFileNames) {
|
||||
QString fn = dir.relativeFilePath(fileName);
|
||||
ConversionData cd;
|
||||
Translator tor;
|
||||
cd.m_sortContexts = !(options & NoSort);
|
||||
if (QFile(fileName).exists()) {
|
||||
if (!tor.load(fileName, cd, QLatin1String("auto"))) {
|
||||
printErr(cd.error());
|
||||
*fail = true;
|
||||
continue;
|
||||
}
|
||||
tor.resolveDuplicates();
|
||||
cd.clearErrors();
|
||||
if (setCodec && fetchedTor.codec() != tor.codec())
|
||||
printErr(LU::tr("lupdate warning: Codec for tr() '%1' disagrees with"
|
||||
" existing file's codec '%2'. Expect trouble.\n")
|
||||
.arg(QString::fromLatin1(fetchedTor.codecName()),
|
||||
QString::fromLatin1(tor.codecName())));
|
||||
if (!targetLanguage.isEmpty() && targetLanguage != tor.languageCode())
|
||||
printErr(LU::tr("lupdate warning: Specified target language '%1' disagrees with"
|
||||
" existing file's language '%2'. Ignoring.\n")
|
||||
.arg(targetLanguage, tor.languageCode()));
|
||||
if (!sourceLanguage.isEmpty() && sourceLanguage != tor.sourceLanguageCode())
|
||||
printErr(LU::tr("lupdate warning: Specified source language '%1' disagrees with"
|
||||
" existing file's language '%2'. Ignoring.\n")
|
||||
.arg(sourceLanguage, tor.sourceLanguageCode()));
|
||||
} else {
|
||||
if (setCodec)
|
||||
tor.setCodec(fetchedTor.codec());
|
||||
if (!targetLanguage.isEmpty())
|
||||
tor.setLanguageCode(targetLanguage);
|
||||
else
|
||||
tor.setLanguageCode(Translator::guessLanguageCodeFromFileName(fileName));
|
||||
if (!sourceLanguage.isEmpty())
|
||||
tor.setSourceLanguageCode(sourceLanguage);
|
||||
}
|
||||
tor.makeFileNamesAbsolute(QFileInfo(fileName).absoluteDir());
|
||||
if (options & NoLocations)
|
||||
tor.setLocationsType(Translator::NoLocations);
|
||||
else if (options & RelativeLocations)
|
||||
tor.setLocationsType(Translator::RelativeLocations);
|
||||
else if (options & AbsoluteLocations)
|
||||
tor.setLocationsType(Translator::AbsoluteLocations);
|
||||
if (options & Verbose)
|
||||
printOut(LU::tr("Updating '%1'...\n").arg(fn));
|
||||
|
||||
UpdateOptions theseOptions = options;
|
||||
if (tor.locationsType() == Translator::NoLocations) // Could be set from file
|
||||
theseOptions |= NoLocations;
|
||||
Translator out = merge(tor, fetchedTor, theseOptions, err);
|
||||
if (setCodec)
|
||||
out.setCodec(fetchedTor.codec());
|
||||
|
||||
if ((options & Verbose) && !err.isEmpty()) {
|
||||
printOut(err);
|
||||
err.clear();
|
||||
}
|
||||
if (options & PluralOnly) {
|
||||
if (options & Verbose)
|
||||
printOut(LU::tr("Stripping non plural forms in '%1'...\n").arg(fn));
|
||||
out.stripNonPluralForms();
|
||||
}
|
||||
if (options & NoObsolete)
|
||||
out.stripObsoleteMessages();
|
||||
out.stripEmptyContexts();
|
||||
|
||||
out.normalizeTranslations(cd);
|
||||
if (!cd.errors().isEmpty()) {
|
||||
printErr(cd.error());
|
||||
cd.clearErrors();
|
||||
}
|
||||
if (!out.save(fileName, cd, QLatin1String("auto"))) {
|
||||
printErr(cd.error());
|
||||
*fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void processSources(Translator &fetchedTor,
|
||||
const QStringList &sourceFiles, ConversionData &cd)
|
||||
{
|
||||
QStringList sourceFilesCpp;
|
||||
for (QStringList::const_iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) {
|
||||
if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive))
|
||||
loadUI(fetchedTor, *it, cd);
|
||||
else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive)
|
||||
|| it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive))
|
||||
loadQScript(fetchedTor, *it, cd);
|
||||
else
|
||||
sourceFilesCpp << *it;
|
||||
}
|
||||
loadCPP(fetchedTor, sourceFilesCpp, cd);
|
||||
if (!cd.error().isEmpty())
|
||||
printErr(cd.error());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
QTranslator translator;
|
||||
QTranslator qtTranslator;
|
||||
QString sysLocale = QLocale::system().name();
|
||||
QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
|
||||
if (translator.load(QLatin1String("qt_tools_") + sysLocale, resourceDir)
|
||||
&& qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
|
||||
app.installTranslator(&translator);
|
||||
app.installTranslator(&qtTranslator);
|
||||
}
|
||||
|
||||
m_defaultExtensions = QLatin1String("ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml");
|
||||
|
||||
QStringList args = app.arguments();
|
||||
QStringList tsFileNames;
|
||||
QMultiHash<QString, QString> allCSources;
|
||||
QSet<QString> projectRoots;
|
||||
QStringList sourceFiles;
|
||||
QStringList includePath;
|
||||
QString targetLanguage;
|
||||
QString sourceLanguage;
|
||||
QByteArray codecForTr;
|
||||
|
||||
UpdateOptions options =
|
||||
Verbose | // verbose is on by default starting with Qt 4.2
|
||||
HeuristicSameText | HeuristicSimilarText | HeuristicNumber;
|
||||
int numFiles = 0;
|
||||
bool metTsFlag = false;
|
||||
bool recursiveScan = true;
|
||||
|
||||
QString extensions = m_defaultExtensions;
|
||||
QSet<QString> extensionsNameFilters;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
QString arg = args.at(i);
|
||||
if (arg == QLatin1String("-help")
|
||||
|| arg == QLatin1String("--help")
|
||||
|| arg == QLatin1String("-h")) {
|
||||
printUsage();
|
||||
return 0;
|
||||
} else if (arg == QLatin1String("-list-languages")) {
|
||||
printOut(getNumerusInfoString());
|
||||
return 0;
|
||||
} else if (arg == QLatin1String("-pluralonly")) {
|
||||
options |= PluralOnly;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-noobsolete")
|
||||
|| arg == QLatin1String("-no-obsolete")) {
|
||||
options |= NoObsolete;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-silent")) {
|
||||
options &= ~Verbose;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-target-language")) {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
printErr(LU::tr("The option -target-language requires a parameter.\n"));
|
||||
return 1;
|
||||
}
|
||||
targetLanguage = args[i];
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-source-language")) {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
printErr(LU::tr("The option -source-language requires a parameter.\n"));
|
||||
return 1;
|
||||
}
|
||||
sourceLanguage = args[i];
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-disable-heuristic")) {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
printErr(LU::tr("The option -disable-heuristic requires a parameter.\n"));
|
||||
return 1;
|
||||
}
|
||||
arg = args[i];
|
||||
if (arg == QLatin1String("sametext")) {
|
||||
options &= ~HeuristicSameText;
|
||||
} else if (arg == QLatin1String("similartext")) {
|
||||
options &= ~HeuristicSimilarText;
|
||||
} else if (arg == QLatin1String("number")) {
|
||||
options &= ~HeuristicNumber;
|
||||
} else {
|
||||
printErr(LU::tr("Invalid heuristic name passed to -disable-heuristic.\n"));
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-locations")) {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
printErr(LU::tr("The option -locations requires a parameter.\n"));
|
||||
return 1;
|
||||
}
|
||||
if (args[i] == QLatin1String("none")) {
|
||||
options |= NoLocations;
|
||||
} else if (args[i] == QLatin1String("relative")) {
|
||||
options |= RelativeLocations;
|
||||
} else if (args[i] == QLatin1String("absolute")) {
|
||||
options |= AbsoluteLocations;
|
||||
} else {
|
||||
printErr(LU::tr("Invalid parameter passed to -locations.\n"));
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-no-ui-lines")) {
|
||||
options |= NoUiLines;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-verbose")) {
|
||||
options |= Verbose;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-no-recursive")) {
|
||||
recursiveScan = false;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-recursive")) {
|
||||
recursiveScan = true;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-no-sort")
|
||||
|| arg == QLatin1String("-nosort")) {
|
||||
options |= NoSort;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-version")) {
|
||||
printOut(QObject::tr("lupdate version %1\n").arg(QLatin1String(QT_VERSION_STR)));
|
||||
return 0;
|
||||
} else if (arg == QLatin1String("-codecfortr")) {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
printErr(LU::tr("The -codecfortr option should be followed by a codec name.\n"));
|
||||
return 1;
|
||||
}
|
||||
codecForTr = args[i].toLatin1();
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-ts")) {
|
||||
metTsFlag = true;
|
||||
continue;
|
||||
} else if (arg == QLatin1String("-extensions")) {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
printErr(LU::tr("The -extensions option should be followed by an extension list.\n"));
|
||||
return 1;
|
||||
}
|
||||
extensions = args[i];
|
||||
continue;
|
||||
} else if (arg.startsWith(QLatin1String("-I"))) {
|
||||
if (arg.length() == 2) {
|
||||
++i;
|
||||
if (i == argc) {
|
||||
printErr(LU::tr("The -I option should be followed by a path.\n"));
|
||||
return 1;
|
||||
}
|
||||
includePath += args[i];
|
||||
} else {
|
||||
includePath += args[i].mid(2);
|
||||
}
|
||||
continue;
|
||||
} else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) {
|
||||
printErr(LU::tr("Unrecognized option '%1'.\n").arg(arg));
|
||||
return 1;
|
||||
}
|
||||
|
||||
QStringList files;
|
||||
if (arg.startsWith(QLatin1String("@"))) {
|
||||
QFile lstFile(arg.mid(1));
|
||||
if (!lstFile.open(QIODevice::ReadOnly)) {
|
||||
printErr(LU::tr("lupdate error: List file '%1' is not readable.\n")
|
||||
.arg(lstFile.fileName()));
|
||||
return 1;
|
||||
}
|
||||
while (!lstFile.atEnd())
|
||||
files << QString::fromLocal8Bit(lstFile.readLine().trimmed());
|
||||
} else {
|
||||
files << arg;
|
||||
}
|
||||
if (metTsFlag) {
|
||||
foreach (const QString &file, files) {
|
||||
bool found = false;
|
||||
foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
|
||||
if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
|
||||
QFileInfo fi(file);
|
||||
if (!fi.exists() || fi.isWritable()) {
|
||||
tsFileNames.append(QFileInfo(file).absoluteFilePath());
|
||||
} else {
|
||||
printErr(LU::tr("lupdate warning: For some reason, '%1' is not writable.\n")
|
||||
.arg(file));
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
printErr(LU::tr("lupdate error: File '%1' has no recognized extension.\n")
|
||||
.arg(file));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
numFiles++;
|
||||
} else {
|
||||
foreach (const QString &file, files) {
|
||||
QFileInfo fi(file);
|
||||
if (!fi.exists()) {
|
||||
printErr(LU::tr("lupdate error: File '%1' does not exist.\n").arg(file));
|
||||
return 1;
|
||||
}
|
||||
if (fi.isDir()) {
|
||||
if (options & Verbose)
|
||||
printOut(LU::tr("Scanning directory '%1'...\n").arg(file));
|
||||
QDir dir = QDir(fi.filePath());
|
||||
projectRoots.insert(dir.absolutePath() + QLatin1Char('/'));
|
||||
if (extensionsNameFilters.isEmpty()) {
|
||||
foreach (QString ext, extensions.split(QLatin1Char(','))) {
|
||||
ext = ext.trimmed();
|
||||
if (ext.startsWith(QLatin1Char('.')))
|
||||
ext.remove(0, 1);
|
||||
extensionsNameFilters.insert(ext);
|
||||
}
|
||||
}
|
||||
QDir::Filters filters = QDir::Files | QDir::NoSymLinks;
|
||||
if (recursiveScan)
|
||||
filters |= QDir::AllDirs | QDir::NoDotAndDotDot;
|
||||
QFileInfoList fileinfolist;
|
||||
recursiveFileInfoList(dir, extensionsNameFilters, filters, &fileinfolist);
|
||||
int scanRootLen = dir.absolutePath().length();
|
||||
foreach (const QFileInfo &fi, fileinfolist) {
|
||||
QString fn = QDir::cleanPath(fi.absoluteFilePath());
|
||||
sourceFiles << fn;
|
||||
|
||||
if (!fn.endsWith(QLatin1String(".ui"))
|
||||
&& !fn.endsWith(QLatin1String(".js"))
|
||||
&& !fn.endsWith(QLatin1String(".qs"))
|
||||
&& !fn.endsWith(QLatin1String(".qml"))) {
|
||||
int offset = 0;
|
||||
int depth = 0;
|
||||
do {
|
||||
offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1);
|
||||
QString ffn = fn.mid(offset + 1);
|
||||
allCSources.insert(ffn, fn);
|
||||
} while (++depth < 3 && offset > scanRootLen);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sourceFiles << QDir::cleanPath(fi.absoluteFilePath());;
|
||||
projectRoots.insert(fi.absolutePath() + QLatin1Char('/'));
|
||||
}
|
||||
}
|
||||
numFiles++;
|
||||
}
|
||||
} // for args
|
||||
|
||||
if (numFiles == 0) {
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!targetLanguage.isEmpty() && tsFileNames.count() != 1)
|
||||
printErr(LU::tr("lupdate warning: -target-language usually only"
|
||||
" makes sense with exactly one TS file.\n"));
|
||||
if (!codecForTr.isEmpty() && tsFileNames.isEmpty())
|
||||
printErr(LU::tr("lupdate warning: -codecfortr has no effect without -ts.\n"));
|
||||
|
||||
bool fail = false;
|
||||
if (tsFileNames.isEmpty())
|
||||
printErr(LU::tr("lupdate warning:"
|
||||
" no TS files specified. Only diagnostics will be produced.\n"));
|
||||
|
||||
Translator fetchedTor;
|
||||
ConversionData cd;
|
||||
cd.m_noUiLines = options & NoUiLines;
|
||||
cd.m_projectRoots = projectRoots;
|
||||
cd.m_includePath = includePath;
|
||||
cd.m_allCSources = allCSources;
|
||||
fetchedTor.setCodecName(codecForTr);
|
||||
processSources(fetchedTor, sourceFiles, cd);
|
||||
updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
|
||||
sourceLanguage, targetLanguage, options, &fail);
|
||||
return fail ? 1 : 0;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef LUPDATE_H
|
||||
#define LUPDATE_H
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class ConversionData;
|
||||
class QString;
|
||||
class QStringList;
|
||||
class Translator;
|
||||
class TranslatorMessage;
|
||||
|
||||
enum UpdateOption {
|
||||
Verbose = 1,
|
||||
NoObsolete = 2,
|
||||
PluralOnly = 4,
|
||||
NoSort = 8,
|
||||
HeuristicSameText = 16,
|
||||
HeuristicSimilarText = 32,
|
||||
HeuristicNumber = 64,
|
||||
AbsoluteLocations = 256,
|
||||
RelativeLocations = 512,
|
||||
NoLocations = 1024,
|
||||
NoUiLines = 2048
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(UpdateOptions, UpdateOption)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateOptions)
|
||||
|
||||
Translator merge(const Translator &tor, const Translator &virginTor,
|
||||
UpdateOptions options, QString &err);
|
||||
|
||||
void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context);
|
||||
void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd);
|
||||
bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd);
|
||||
bool loadUI(Translator &translator, const QString &filename, ConversionData &cd);
|
||||
|
||||
class LU {
|
||||
Q_DECLARE_TR_FUNCTIONS(LUpdate);
|
||||
};
|
||||
|
||||
static QString MagicComment(QLatin1String("TRANSLATOR"));
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,92 +0,0 @@
|
|||
=head1 NAME
|
||||
|
||||
lupdate - part of Katie's Linguist tool chain
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
lupdate [options] [source-file|path|@lst-file] ... -ts ts-files|@lst-file
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
lupdate extracts translatable messages from Qt UI files, C++ and
|
||||
JavaScript/QtScript source code. Extracted messages are stored in textual
|
||||
translation source files (typically Qt TS XML). New and modified messages can
|
||||
be merged into existing TS files.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-h
|
||||
-help Display this information and exit.
|
||||
-no-obsolete
|
||||
Drop all obsolete strings.
|
||||
|
||||
-extensions <ext>[,<ext>]...
|
||||
Process files with the given extensions only.
|
||||
The extension list must be separated with commas, not with whitespace.
|
||||
Default: 'ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml'.
|
||||
|
||||
-pluralonly
|
||||
Only include plural form messages.
|
||||
|
||||
-silent
|
||||
Do not explain what is being done.
|
||||
|
||||
-no-sort
|
||||
Do not sort contexts in TS files.
|
||||
|
||||
-no-recursive
|
||||
Do not recursively scan the following directories.
|
||||
|
||||
-recursive
|
||||
Recursively scan the following directories (default).
|
||||
|
||||
-I <includepath> or -I<includepath>
|
||||
Additional location to look for include files.
|
||||
May be specified multiple times.
|
||||
|
||||
-locations {absolute|relative|none}
|
||||
Specify/override how source code references are saved in TS files.
|
||||
Default is absolute.
|
||||
-no-ui-lines
|
||||
Do not record line numbers in references to UI files.
|
||||
|
||||
-disable-heuristic {sametext|similartext|number}
|
||||
Disable the named merge heuristic. Can be specified multiple times.
|
||||
|
||||
-source-language <language>[_<region>]
|
||||
Specify the language of the source strings for new files.
|
||||
Defaults to POSIX if not specified.
|
||||
|
||||
-target-language <language>[_<region>]
|
||||
Specify the language of the translations for new files.
|
||||
Guessed from the file name if not specified.
|
||||
|
||||
-ts <ts-file>...
|
||||
Specify the output file(s). This will override the TRANSLATIONS
|
||||
and nullify the CODECFORTR from possibly specified project files.
|
||||
|
||||
-codecfortr <codec>
|
||||
Specify the codec assumed for tr() calls. Effective only with -ts.
|
||||
|
||||
-version
|
||||
Display the version of lupdate and exit.
|
||||
|
||||
@lst-file
|
||||
Read additional file names (one per line) from lst-file.
|
||||
|
||||
=head1 EXIT STATUS
|
||||
|
||||
lupdate returns 0 on success and other on unexcepted failure.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
The Qt Company Ltd.
|
||||
|
||||
Copyright (C) 2015 The Qt Company Ltd.
|
||||
Copyright (C) 2016-2020 Ivailo Monev
|
||||
|
||||
Licensed through GNU Lesser General Public License/GNU General Public License.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
lconvert(1) lrelease(1)
|
|
@ -1,44 +0,0 @@
|
|||
#!/bin/sh
|
||||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2015 The Qt Company Ltd.
|
||||
##
|
||||
## This file is the build configuration utility 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.
|
||||
##
|
||||
## As a special exception, The Qt Company gives you certain additional
|
||||
## rights. These rights are described in The Qt Company LGPL Exception
|
||||
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
##
|
||||
## GNU General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU
|
||||
## General Public License version 3.0 as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.GPL included in the
|
||||
## packaging of this file. Please review the following information to
|
||||
## ensure the GNU General Public License version 3.0 requirements will be
|
||||
## met: http://www.gnu.org/copyleft/gpl.html.
|
||||
##
|
||||
## $QT_END_LICENSE$
|
||||
##
|
||||
#############################################################################
|
||||
|
||||
me=$(dirname $0)
|
||||
mkdir -p $me/out
|
||||
(cd $me/out && ../../../../build/bin/qlalr --qt --no-debug --no-lines ../qscript.g)
|
||||
|
||||
for f in $me/out/*.h; do
|
||||
n=$(basename $f)
|
||||
cp $f $n
|
||||
done
|
||||
|
||||
git diff .
|
||||
|
|
@ -1,505 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "lupdate.h"
|
||||
|
||||
#include "simtexth.h"
|
||||
#include "translator.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTextCodec>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static inline bool isDigitFriendly(QChar c)
|
||||
{
|
||||
return c.isPunct() || c.isSpace();
|
||||
}
|
||||
|
||||
static int numberLength(const QString &s, int i)
|
||||
{
|
||||
if (i >= s.size() || !s.at(i).isDigit())
|
||||
return 0;
|
||||
|
||||
int pos = i;
|
||||
do {
|
||||
++i;
|
||||
} while (i < s.size()
|
||||
&& (s.at(i).isDigit()
|
||||
|| (isDigitFriendly(s[i])
|
||||
&& i + 1 < s.size()
|
||||
&& (s[i + 1].isDigit()
|
||||
|| (isDigitFriendly(s[i + 1])
|
||||
&& i + 2 < s.size()
|
||||
&& s[i + 2].isDigit())))));
|
||||
return i - pos;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Returns a version of 'key' where all numbers have been replaced by zeroes. If
|
||||
there were none, returns "".
|
||||
*/
|
||||
static QString zeroKey(const QString &key)
|
||||
{
|
||||
QString zeroed;
|
||||
bool metSomething = false;
|
||||
|
||||
for (int i = 0; i < key.size(); ++i) {
|
||||
int len = numberLength(key, i);
|
||||
if (len > 0) {
|
||||
i += len;
|
||||
zeroed.append(QLatin1Char('0'));
|
||||
metSomething = true;
|
||||
} else {
|
||||
zeroed.append(key.at(i));
|
||||
}
|
||||
}
|
||||
return metSomething ? zeroed : QString();
|
||||
}
|
||||
|
||||
static QString translationAttempt(const QString &oldTranslation,
|
||||
const QString &oldSource, const QString &newSource)
|
||||
{
|
||||
int p = zeroKey(oldSource).count(QLatin1Char('0'));
|
||||
QString attempt;
|
||||
QStringList oldNumbers;
|
||||
QStringList newNumbers;
|
||||
QVector<bool> met(p);
|
||||
QVector<int> matchedYet(p);
|
||||
int i, j;
|
||||
int k = 0, ell, best;
|
||||
int m, n;
|
||||
int pass;
|
||||
|
||||
/*
|
||||
This algorithm is hard to follow, so we'll consider an example
|
||||
all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0"
|
||||
and newSource is "XeT 3.1".
|
||||
|
||||
First, we set up two tables: oldNumbers and newNumbers. In our
|
||||
example, oldNumber[0] is "3.0" and newNumber[0] is "3.1".
|
||||
*/
|
||||
for (i = 0, j = 0; i < oldSource.size(); i++, j++) {
|
||||
m = numberLength(oldSource, i);
|
||||
n = numberLength(newSource, j);
|
||||
if (m > 0) {
|
||||
oldNumbers.append(oldSource.mid(i, m + 1));
|
||||
newNumbers.append(newSource.mid(j, n + 1));
|
||||
i += m;
|
||||
j += n;
|
||||
met[k] = false;
|
||||
matchedYet[k] = 0;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
We now go over the old translation, "XeT 3.0", one letter at a
|
||||
time, looking for numbers found in oldNumbers. Whenever such a
|
||||
number is met, it is replaced with its newNumber equivalent. In
|
||||
our example, the "3.0" of "XeT 3.0" becomes "3.1".
|
||||
*/
|
||||
for (i = 0; i < oldTranslation.length(); i++) {
|
||||
attempt += oldTranslation[i];
|
||||
for (k = 0; k < p; k++) {
|
||||
if (oldTranslation[i] == oldNumbers[k][matchedYet[k]])
|
||||
matchedYet[k]++;
|
||||
else
|
||||
matchedYet[k] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Let's find out if the last character ended a match. We make
|
||||
two passes over the data. In the first pass, we try to
|
||||
match only numbers that weren't matched yet; if that fails,
|
||||
the second pass does the trick. This is useful in some
|
||||
suspicious cases, flagged below.
|
||||
*/
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
best = p; // an impossible value
|
||||
for (k = 0; k < p; k++) {
|
||||
if ((!met[k] || pass > 0) &&
|
||||
matchedYet[k] == oldNumbers[k].length() &&
|
||||
numberLength(oldTranslation, i + 1 - matchedYet[k]) == matchedYet[k]) {
|
||||
// the longer the better
|
||||
if (best == p || matchedYet[k] > matchedYet[best])
|
||||
best = k;
|
||||
}
|
||||
}
|
||||
if (best != p) {
|
||||
attempt.truncate(attempt.length() - matchedYet[best]);
|
||||
attempt += newNumbers[best];
|
||||
met[best] = true;
|
||||
for (k = 0; k < p; k++)
|
||||
matchedYet[k] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
We flag two kinds of suspicious cases. They are identified as
|
||||
such with comments such as "{2000?}" at the end.
|
||||
|
||||
Example of the first kind: old source text "TeX 3.0" translated
|
||||
as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the
|
||||
new text is.
|
||||
*/
|
||||
for (k = 0; k < p; k++) {
|
||||
if (!met[k])
|
||||
attempt += QLatin1String(" {") + newNumbers[k] + QLatin1String("?}");
|
||||
}
|
||||
|
||||
/*
|
||||
Example of the second kind: "1 of 1" translated as "1 af 1",
|
||||
with new source text "1 of 2", generates "1 af 2 {1 or 2?}"
|
||||
because it's not clear which of "1 af 2" and "2 af 1" is right.
|
||||
*/
|
||||
for (k = 0; k < p; k++) {
|
||||
for (ell = 0; ell < p; ell++) {
|
||||
if (k != ell && oldNumbers[k] == oldNumbers[ell] &&
|
||||
newNumbers[k] < newNumbers[ell])
|
||||
attempt += QLatin1String(" {") + newNumbers[k] + QLatin1String(" or ") +
|
||||
newNumbers[ell] + QLatin1String("?}");
|
||||
}
|
||||
}
|
||||
return attempt;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Augments a Translator with translations easily derived from
|
||||
similar existing (probably obsolete) translations.
|
||||
|
||||
For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1"
|
||||
has no translation, "XeT 3.1" is added to the translator and is
|
||||
marked Unfinished.
|
||||
|
||||
Returns the number of additional messages that this heuristic translated.
|
||||
*/
|
||||
int applyNumberHeuristic(Translator &tor)
|
||||
{
|
||||
QMap<QString, QPair<QString, QString> > translated;
|
||||
QVector<bool> untranslated(tor.messageCount());
|
||||
int inserted = 0;
|
||||
|
||||
for (int i = 0; i < tor.messageCount(); ++i) {
|
||||
const TranslatorMessage &msg = tor.message(i);
|
||||
bool hasTranslation = msg.isTranslated();
|
||||
if (msg.type() == TranslatorMessage::Unfinished) {
|
||||
if (!hasTranslation)
|
||||
untranslated[i] = true;
|
||||
} else if (hasTranslation && msg.translations().count() == 1) {
|
||||
const QString &key = zeroKey(msg.sourceText());
|
||||
if (!key.isEmpty())
|
||||
translated.insert(key, qMakePair(msg.sourceText(), msg.translation()));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < tor.messageCount(); ++i) {
|
||||
if (untranslated[i]) {
|
||||
TranslatorMessage &msg = tor.message(i);
|
||||
const QString &key = zeroKey(msg.sourceText());
|
||||
if (!key.isEmpty()) {
|
||||
QMap<QString, QPair<QString, QString> >::ConstIterator t =
|
||||
translated.constFind(key);
|
||||
if (t != translated.constEnd() && t->first != msg.sourceText()) {
|
||||
msg.setTranslation(translationAttempt(t->second, t->first,
|
||||
msg.sourceText()));
|
||||
inserted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Augments a Translator with trivially derived translations.
|
||||
|
||||
For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no
|
||||
matter the context or the comment, "Eingeschaltet:" is added as the
|
||||
translation of any untranslated "Enabled:" text and is marked Unfinished.
|
||||
|
||||
Returns the number of additional messages that this heuristic translated.
|
||||
*/
|
||||
|
||||
int applySameTextHeuristic(Translator &tor)
|
||||
{
|
||||
QMap<QString, QStringList> translated;
|
||||
QMap<QString, bool> avoid; // Want a QTreeSet, in fact
|
||||
QVector<bool> untranslated(tor.messageCount());
|
||||
int inserted = 0;
|
||||
|
||||
for (int i = 0; i < tor.messageCount(); ++i) {
|
||||
const TranslatorMessage &msg = tor.message(i);
|
||||
if (!msg.isTranslated()) {
|
||||
if (msg.type() == TranslatorMessage::Unfinished)
|
||||
untranslated[i] = true;
|
||||
} else {
|
||||
const QString &key = msg.sourceText();
|
||||
QMap<QString, QStringList>::ConstIterator t = translated.constFind(key);
|
||||
if (t != translated.constEnd()) {
|
||||
/*
|
||||
The same source text is translated at least two
|
||||
different ways. Do nothing then.
|
||||
*/
|
||||
if (*t != msg.translations()) {
|
||||
translated.remove(key);
|
||||
avoid.insert(key, true);
|
||||
}
|
||||
} else if (!avoid.contains(key)) {
|
||||
translated.insert(key, msg.translations());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < tor.messageCount(); ++i) {
|
||||
if (untranslated[i]) {
|
||||
TranslatorMessage &msg = tor.message(i);
|
||||
QMap<QString, QStringList>::ConstIterator t = translated.constFind(msg.sourceText());
|
||||
if (t != translated.constEnd()) {
|
||||
msg.setTranslations(*t);
|
||||
++inserted;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Merges two Translator objects. The first one
|
||||
is a set of source texts and translations for a previous version of
|
||||
the internationalized program; the second one is a set of fresh
|
||||
source texts newly extracted from the source code, without any
|
||||
translation yet.
|
||||
*/
|
||||
|
||||
Translator merge(const Translator &tor, const Translator &virginTor,
|
||||
UpdateOptions options, QString &err)
|
||||
{
|
||||
int known = 0;
|
||||
int neww = 0;
|
||||
int obsoleted = 0;
|
||||
int similarTextHeuristicCount = 0;
|
||||
|
||||
Translator outTor;
|
||||
outTor.setLanguageCode(tor.languageCode());
|
||||
outTor.setSourceLanguageCode(tor.sourceLanguageCode());
|
||||
outTor.setLocationsType(tor.locationsType());
|
||||
outTor.setCodecName(tor.codecName());
|
||||
|
||||
/*
|
||||
The types of all the messages from the vernacular translator
|
||||
are updated according to the virgin translator.
|
||||
*/
|
||||
foreach (TranslatorMessage m, tor.messages()) {
|
||||
TranslatorMessage::Type newType = TranslatorMessage::Finished;
|
||||
|
||||
if (m.sourceText().isEmpty() && m.id().isEmpty()) {
|
||||
// context/file comment
|
||||
int mvi = virginTor.find(m.context());
|
||||
if (mvi >= 0)
|
||||
m.setComment(virginTor.constMessage(mvi).comment());
|
||||
} else {
|
||||
const TranslatorMessage *mv;
|
||||
int mvi = virginTor.find(m);
|
||||
if (mvi < 0) {
|
||||
if (!(options & HeuristicSimilarText)) {
|
||||
makeObsolete:
|
||||
newType = TranslatorMessage::Obsolete;
|
||||
if (m.type() != TranslatorMessage::Obsolete)
|
||||
obsoleted++;
|
||||
m.clearReferences();
|
||||
} else {
|
||||
mvi = virginTor.find(m.context(), m.comment(), m.allReferences());
|
||||
if (mvi < 0) {
|
||||
// did not find it in the virgin, mark it as obsolete
|
||||
goto makeObsolete;
|
||||
} else {
|
||||
mv = &virginTor.constMessage(mvi);
|
||||
// Do not just accept it if its on the same line number,
|
||||
// but different source text.
|
||||
// Also check if the texts are more or less similar before
|
||||
// we consider them to represent the same message...
|
||||
if (getSimilarityScore(m.sourceText(), mv->sourceText()) >= textSimilarityThreshold) {
|
||||
// It is just slightly modified, assume that it is the same string
|
||||
|
||||
// Mark it as unfinished. (Since the source text
|
||||
// was changed it might require re-translating...)
|
||||
newType = TranslatorMessage::Unfinished;
|
||||
++similarTextHeuristicCount;
|
||||
neww++;
|
||||
|
||||
outdateSource:
|
||||
m.setOldSourceText(m.sourceText());
|
||||
m.setSourceText(mv->sourceText());
|
||||
const QString &oldpluralsource = m.extra(QLatin1String("po-msgid_plural"));
|
||||
if (!oldpluralsource.isEmpty()) {
|
||||
m.setExtra(QLatin1String("po-old_msgid_plural"), oldpluralsource);
|
||||
m.unsetExtra(QLatin1String("po-msgid_plural"));
|
||||
}
|
||||
goto copyAttribs; // Update secondary references
|
||||
} else {
|
||||
// The virgin and vernacular sourceTexts are so
|
||||
// different that we could not find it.
|
||||
goto makeObsolete;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mv = &virginTor.message(mvi);
|
||||
if (!mv->id().isEmpty()
|
||||
&& (mv->context() != m.context()
|
||||
|| mv->sourceText() != m.sourceText()
|
||||
|| mv->comment() != m.comment())) {
|
||||
known++;
|
||||
newType = TranslatorMessage::Unfinished;
|
||||
m.setContext(mv->context());
|
||||
m.setComment(mv->comment());
|
||||
if (mv->sourceText() != m.sourceText())
|
||||
goto outdateSource;
|
||||
} else {
|
||||
switch (m.type()) {
|
||||
case TranslatorMessage::Finished:
|
||||
default:
|
||||
if (m.isPlural() == mv->isPlural()) {
|
||||
newType = TranslatorMessage::Finished;
|
||||
} else {
|
||||
newType = TranslatorMessage::Unfinished;
|
||||
}
|
||||
known++;
|
||||
break;
|
||||
case TranslatorMessage::Unfinished:
|
||||
newType = TranslatorMessage::Unfinished;
|
||||
known++;
|
||||
break;
|
||||
case TranslatorMessage::Obsolete:
|
||||
newType = TranslatorMessage::Unfinished;
|
||||
neww++;
|
||||
}
|
||||
}
|
||||
|
||||
// Always get the filename and linenumber info from the
|
||||
// virgin Translator, in case it has changed location.
|
||||
// This should also enable us to read a file that does not
|
||||
// have the <location> element.
|
||||
// why not use operator=()? Because it overwrites e.g. userData.
|
||||
copyAttribs:
|
||||
m.setReferences(mv->allReferences());
|
||||
m.setPlural(mv->isPlural());
|
||||
m.setUtf8(mv->isUtf8());
|
||||
m.setExtraComment(mv->extraComment());
|
||||
m.setId(mv->id());
|
||||
}
|
||||
}
|
||||
|
||||
m.setType(newType);
|
||||
outTor.append(m);
|
||||
}
|
||||
|
||||
/*
|
||||
Messages found only in the virgin translator are added to the
|
||||
vernacular translator.
|
||||
*/
|
||||
foreach (const TranslatorMessage &mv, virginTor.messages()) {
|
||||
if (mv.sourceText().isEmpty() && mv.id().isEmpty()) {
|
||||
if (tor.find(mv.context()) >= 0)
|
||||
continue;
|
||||
} else {
|
||||
if (tor.find(mv) >= 0)
|
||||
continue;
|
||||
if (options & HeuristicSimilarText) {
|
||||
int mi = tor.find(mv.context(), mv.comment(), mv.allReferences());
|
||||
if (mi >= 0) {
|
||||
if (getSimilarityScore(tor.constMessage(mi).sourceText(), mv.sourceText())
|
||||
>= textSimilarityThreshold)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options & NoLocations)
|
||||
outTor.append(mv);
|
||||
else
|
||||
outTor.appendSorted(mv);
|
||||
if (!mv.sourceText().isEmpty() || !mv.id().isEmpty())
|
||||
++neww;
|
||||
}
|
||||
|
||||
/*
|
||||
The same-text heuristic handles cases where a message has an
|
||||
obsolete counterpart with a different context or comment.
|
||||
*/
|
||||
int sameTextHeuristicCount = (options & HeuristicSameText) ? applySameTextHeuristic(outTor) : 0;
|
||||
|
||||
/*
|
||||
The number heuristic handles cases where a message has an
|
||||
obsolete counterpart with mostly numbers differing in the
|
||||
source text.
|
||||
*/
|
||||
int sameNumberHeuristicCount = (options & HeuristicNumber) ? applyNumberHeuristic(outTor) : 0;
|
||||
|
||||
if (options & Verbose) {
|
||||
int totalFound = neww + known;
|
||||
err += LU::tr(" Found %n source text(s) (%1 new and %2 already existing)\n", 0, totalFound).arg(neww).arg(known);
|
||||
|
||||
if (obsoleted) {
|
||||
if (options & NoObsolete) {
|
||||
err += LU::tr(" Removed %n obsolete entries\n", 0, obsoleted);
|
||||
} else {
|
||||
err += LU::tr(" Kept %n obsolete entries\n", 0, obsoleted);
|
||||
}
|
||||
}
|
||||
|
||||
if (sameNumberHeuristicCount)
|
||||
err += LU::tr(" Number heuristic provided %n translation(s)\n",
|
||||
0, sameNumberHeuristicCount);
|
||||
if (sameTextHeuristicCount)
|
||||
err += LU::tr(" Same-text heuristic provided %n translation(s)\n",
|
||||
0, sameTextHeuristicCount);
|
||||
if (similarTextHeuristicCount)
|
||||
err += LU::tr(" Similar-text heuristic provided %n translation(s)\n",
|
||||
0, similarTextHeuristicCount);
|
||||
}
|
||||
return outTor;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,192 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016-2020 Ivailo Monev
|
||||
**
|
||||
** This file is part of the Qt Linguist 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.
|
||||
**
|
||||
** As a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "lupdate.h"
|
||||
|
||||
#include <translator.h>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QString>
|
||||
#include <QtXml/qxml.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class UiReader : public QXmlDefaultHandler
|
||||
{
|
||||
public:
|
||||
UiReader(Translator &translator, ConversionData &cd)
|
||||
: m_translator(translator), m_cd(cd), m_lineNumber(-1), m_isTrString(false),
|
||||
m_needUtf8(translator.codecName() != "UTF-8")
|
||||
{}
|
||||
|
||||
bool startElement(const QString &namespaceURI, const QString &localName,
|
||||
const QString &qName, const QXmlAttributes &atts);
|
||||
bool endElement(const QString &namespaceURI, const QString &localName,
|
||||
const QString &qName);
|
||||
bool characters(const QString &ch);
|
||||
bool fatalError(const QXmlParseException &exception);
|
||||
|
||||
void setDocumentLocator(QXmlLocator *locator) { m_locator = locator; }
|
||||
|
||||
private:
|
||||
void flush();
|
||||
|
||||
Translator &m_translator;
|
||||
ConversionData &m_cd;
|
||||
QString m_context;
|
||||
QString m_source;
|
||||
QString m_comment;
|
||||
QString m_extracomment;
|
||||
QXmlLocator *m_locator;
|
||||
|
||||
QString m_accum;
|
||||
int m_lineNumber;
|
||||
bool m_isTrString;
|
||||
bool m_needUtf8;
|
||||
};
|
||||
|
||||
bool UiReader::startElement(const QString &namespaceURI,
|
||||
const QString &localName, const QString &qName, const QXmlAttributes &atts)
|
||||
{
|
||||
Q_UNUSED(namespaceURI);
|
||||
Q_UNUSED(localName);
|
||||
|
||||
if (qName == QLatin1String("item")) { // UI3 menu entries
|
||||
flush();
|
||||
if (!atts.value(QLatin1String("text")).isEmpty()) {
|
||||
m_source = atts.value(QLatin1String("text"));
|
||||
m_isTrString = true;
|
||||
if (!m_cd.m_noUiLines)
|
||||
m_lineNumber = m_locator->lineNumber();
|
||||
}
|
||||
} else if (qName == QLatin1String("string")) {
|
||||
flush();
|
||||
if (atts.value(QLatin1String("notr")).isEmpty() ||
|
||||
atts.value(QLatin1String("notr")) != QLatin1String("true")) {
|
||||
m_isTrString = true;
|
||||
m_comment = atts.value(QLatin1String("comment"));
|
||||
m_extracomment = atts.value(QLatin1String("extracomment"));
|
||||
if (!m_cd.m_noUiLines)
|
||||
m_lineNumber = m_locator->lineNumber();
|
||||
} else {
|
||||
m_isTrString = false;
|
||||
}
|
||||
}
|
||||
m_accum.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiReader::endElement(const QString &namespaceURI,
|
||||
const QString &localName, const QString &qName)
|
||||
{
|
||||
Q_UNUSED(namespaceURI);
|
||||
Q_UNUSED(localName);
|
||||
|
||||
m_accum.replace(QLatin1String("\r\n"), QLatin1String("\n"));
|
||||
|
||||
if (qName == QLatin1String("class")) { // UI "header"
|
||||
if (m_context.isEmpty())
|
||||
m_context = m_accum;
|
||||
} else if (qName == QLatin1String("string") && m_isTrString) {
|
||||
m_source = m_accum;
|
||||
} else if (qName == QLatin1String("comment")) { // FIXME: what's that?
|
||||
m_comment = m_accum;
|
||||
flush();
|
||||
} else if (qName == QLatin1String("function")) { // UI3 embedded code
|
||||
fetchtrInlinedCpp(m_accum, m_translator, m_context);
|
||||
} else {
|
||||
flush();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiReader::characters(const QString &ch)
|
||||
{
|
||||
m_accum += ch;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiReader::fatalError(const QXmlParseException &exception)
|
||||
{
|
||||
QString msg = LU::tr("XML error: Parse error at line %1, column %2 (%3).")
|
||||
.arg(exception.lineNumber()).arg(exception.columnNumber())
|
||||
.arg(exception.message());
|
||||
m_cd.appendError(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
void UiReader::flush()
|
||||
{
|
||||
if (!m_context.isEmpty() && !m_source.isEmpty()) {
|
||||
TranslatorMessage msg(m_context, m_source,
|
||||
m_comment, QString(), m_cd.m_sourceFileName,
|
||||
m_lineNumber, QStringList());
|
||||
msg.setExtraComment(m_extracomment);
|
||||
if (m_needUtf8 && msg.needs8Bit())
|
||||
msg.setUtf8(true);
|
||||
m_translator.extend(msg);
|
||||
}
|
||||
m_source.clear();
|
||||
m_comment.clear();
|
||||
m_extracomment.clear();
|
||||
}
|
||||
|
||||
bool loadUI(Translator &translator, const QString &filename, ConversionData &cd)
|
||||
{
|
||||
cd.m_sourceFileName = filename;
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
|
||||
return false;
|
||||
}
|
||||
QXmlInputSource in(&file);
|
||||
QXmlSimpleReader reader;
|
||||
reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false);
|
||||
reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true);
|
||||
reader.setFeature(QLatin1String(
|
||||
"http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
|
||||
UiReader handler(translator, cd);
|
||||
reader.setContentHandler(&handler);
|
||||
reader.setErrorHandler(&handler);
|
||||
bool result = reader.parse(in);
|
||||
if (!result)
|
||||
cd.appendError(LU::tr("Parse error in UI file"));
|
||||
reader.setContentHandler(0);
|
||||
reader.setErrorHandler(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -33,8 +33,6 @@
|
|||
|
||||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QLibraryInfo>
|
||||
#include <QLocale>
|
||||
#include <QTranslator>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
@ -45,15 +43,15 @@ int main(int argc, char **argv)
|
|||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
QTranslator translator;
|
||||
QTranslator qtTranslator;
|
||||
QString sysLocale = QLocale::system().name();
|
||||
QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
|
||||
if (translator.load(QLatin1String("qt_tools_") + sysLocale, resourceDir)
|
||||
&& qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
|
||||
if (translator.load(QLatin1String("qt_tools"))
|
||||
&& qtTranslator.load(QLatin1String("qt"))) {
|
||||
app.installTranslator(&translator);
|
||||
app.installTranslator(&qtTranslator);
|
||||
}
|
||||
#endif
|
||||
|
||||
MainWindow mw;
|
||||
mw.show();
|
||||
|
|
|
@ -8046,7 +8046,6 @@
|
|||
qtimer.h
|
||||
qtranslator.cpp
|
||||
qtranslator.h
|
||||
qtranslator_p.h
|
||||
qvariant.cpp
|
||||
qvariant.h
|
||||
qvariant_p.h
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
file(GLOB TRANSLATIONS "*.ts")
|
||||
file(GLOB TRANSLATIONS "*.po")
|
||||
katie_setup_target(translations ${TRANSLATIONS})
|
||||
|
|
3528
translations/qt_ar.po
Normal file
3528
translations/qt_ar.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1904
translations/qt_cs.po
Normal file
1904
translations/qt_cs.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
3011
translations/qt_da.po
Normal file
3011
translations/qt_da.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1926
translations/qt_de.po
Normal file
1926
translations/qt_de.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
3135
translations/qt_es.po
Normal file
3135
translations/qt_es.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1906
translations/qt_eu.po
Normal file
1906
translations/qt_eu.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1901
translations/qt_fa.po
Normal file
1901
translations/qt_fa.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
2059
translations/qt_fr.po
Normal file
2059
translations/qt_fr.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1908
translations/qt_gl.po
Normal file
1908
translations/qt_gl.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1898
translations/qt_he.po
Normal file
1898
translations/qt_he.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
2303
translations/qt_hu.po
Normal file
2303
translations/qt_hu.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1922
translations/qt_ja.po
Normal file
1922
translations/qt_ja.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1916
translations/qt_ko.po
Normal file
1916
translations/qt_ko.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
2391
translations/qt_lt.po
Normal file
2391
translations/qt_lt.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1911
translations/qt_pl.po
Normal file
1911
translations/qt_pl.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
3428
translations/qt_pt.po
Normal file
3428
translations/qt_pt.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1182
translations/qt_ru.po
Normal file
1182
translations/qt_ru.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1903
translations/qt_sk.po
Normal file
1903
translations/qt_sk.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1945
translations/qt_sl.po
Normal file
1945
translations/qt_sl.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
3445
translations/qt_sv.po
Normal file
3445
translations/qt_sv.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
4653
translations/qt_tools_CN.po
Normal file
4653
translations/qt_tools_CN.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
4653
translations/qt_tools_TW.po
Normal file
4653
translations/qt_tools_TW.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
4653
translations/qt_tools_cs.po
Normal file
4653
translations/qt_tools_cs.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue