From 72576f439ad3eebc6947a50070f1ffabe2964b32 Mon Sep 17 00:00:00 2001 From: Andras Timar Date: Tue, 20 Aug 2013 13:24:19 +0200 Subject: [PATCH] fdo#67786 pocheck tool for checking translations Pootle has many checks, but there are cases which are not covered. Therefore I wrote a tool which checked three types of translation errors: 1. Unique style names. 2. Unique spreadsheet function names. 3. Missing trailing '|' in Windows installer translation. Usage: make cmd cmd=solver/*/bin/pocheck It checks all languages and prints the report to stdout. Change-Id: I89aad66ea41c0ebe4a6f45beaaf86afd1a6439cc --- Repository.mk | 1 + l10ntools/Executable_pocheck.mk | 34 ++++ l10ntools/Module_l10ntools.mk | 1 + l10ntools/source/pocheck.cxx | 267 ++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 l10ntools/Executable_pocheck.mk create mode 100644 l10ntools/source/pocheck.cxx diff --git a/Repository.mk b/Repository.mk index ba0f2912a066..1433f1b79c70 100644 --- a/Repository.mk +++ b/Repository.mk @@ -46,6 +46,7 @@ $(eval $(call gb_Helper_register_executables,NONE, \ osl_process_child \ pdf2xml \ pdfunzip \ + pocheck \ propex \ reg2unoidl \ regsvrex \ diff --git a/l10ntools/Executable_pocheck.mk b/l10ntools/Executable_pocheck.mk new file mode 100644 index 000000000000..2619ac6f4b57 --- /dev/null +++ b/l10ntools/Executable_pocheck.mk @@ -0,0 +1,34 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Executable_Executable,pocheck)) + +$(eval $(call gb_Executable_set_include,pocheck,\ + -I$(SRCDIR)/l10ntools/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Executable_use_libraries,pocheck,\ + sal \ +)) + +$(eval $(call gb_Executable_use_static_libraries,pocheck,\ + transex \ +)) + +$(eval $(call gb_Executable_add_exception_objects,pocheck,\ + l10ntools/source/pocheck \ +)) + +$(eval $(call gb_Executable_use_externals,pocheck,\ + boost_headers \ + libxml2 \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/l10ntools/Module_l10ntools.mk b/l10ntools/Module_l10ntools.mk index 6e637b210fbd..771e717c051f 100644 --- a/l10ntools/Module_l10ntools.mk +++ b/l10ntools/Module_l10ntools.mk @@ -18,6 +18,7 @@ $(eval $(call gb_Module_add_targets_for_build,l10ntools,\ Executable_xrmex \ Executable_localize \ Executable_transex3 \ + Executable_pocheck \ Executable_propex \ Executable_treex \ Executable_stringex \ diff --git a/l10ntools/source/pocheck.cxx b/l10ntools/source/pocheck.cxx new file mode 100644 index 000000000000..b717c7d251bf --- /dev/null +++ b/l10ntools/source/pocheck.cxx @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include "po.hxx" + +// Translated style names must be unique +static void checkStyleNames(OString aLanguage) +{ + std::map aLocalizedStyleNames; + std::map aLocalizedNumStyleNames; + OString aPoPath = OString(getenv("SRC_ROOT")) + + "/translations/source/" + aLanguage + "/sw/source/ui/utlui.po"; + PoIfstream aPoInput; + aPoInput.open(aPoPath); + if( !aPoInput.isOpen() ) + std::cerr << "Warning: Cannot open " << aPoPath << std::endl; + + for(;;) + { + PoEntry aPoEntry; + aPoInput.readEntry(aPoEntry); + if( aPoInput.eof() ) + break; + if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" && + aPoEntry.getGroupId().startsWith("STR_POOLCOLL") ) + { + OString aMsgStr = aPoEntry.getMsgStr(); + if( aMsgStr.isEmpty() ) + continue; + if( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() ) + aLocalizedStyleNames[aMsgStr] = 1; + else + aLocalizedStyleNames[aMsgStr]++; + } + if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" && + aPoEntry.getGroupId().startsWith("STR_POOLNUMRULE") ) + { + OString aMsgStr = aPoEntry.getMsgStr(); + if( aMsgStr.isEmpty() ) + continue; + if( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() ) + aLocalizedNumStyleNames[aMsgStr] = 1; + else + aLocalizedNumStyleNames[aMsgStr]++; + } + } + aPoInput.close(); + + for( std::map::iterator it=aLocalizedStyleNames.begin(); it!=aLocalizedStyleNames.end(); ++it) + { + if( it->second > 1 ) + { + std::cout << "ERROR: Style name translations must be unique in:\n" << + aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first << + "\nSee STR_POOLCOLL_*\n\n"; + } + } + for( std::map::iterator it=aLocalizedNumStyleNames.begin(); it!=aLocalizedNumStyleNames.end(); ++it) + { + if( it->second > 1 ) + { + std::cout << "ERROR: Style name translations must be unique in:\n" << + aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first << + "\nSee STR_POOLNUMRULE_*\n\n"; + } + } +} + +// Translated spreadsheet function names must be unique +static void checkFunctionNames(OString aLanguage) +{ + std::map aLocalizedFunctionNames; + std::map aLocalizedCoreFunctionNames; + OString aPoPath = OString(getenv("SRC_ROOT")) + + "/translations/source/" + aLanguage + "/formula/source/core/resource.po"; + PoIfstream aPoInput; + aPoInput.open(aPoPath); + if( !aPoInput.isOpen() ) + std::cerr << "Warning: Cannot open " << aPoPath << std::endl; + + for(;;) + { + PoEntry aPoEntry; + aPoInput.readEntry(aPoEntry); + if( aPoInput.eof() ) + break; + if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_STRLIST_FUNCTION_NAMES" ) + { + OString aMsgStr = aPoEntry.getMsgStr(); + if( aMsgStr.isEmpty() ) + continue; + if( aLocalizedCoreFunctionNames.find(aMsgStr) == aLocalizedCoreFunctionNames.end() ) + aLocalizedCoreFunctionNames[aMsgStr] = 1; + if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) + aLocalizedFunctionNames[aMsgStr] = 1; + else + aLocalizedFunctionNames[aMsgStr]++; + } + } + aPoInput.close(); + + aPoPath = OString(getenv("SRC_ROOT")) + + "/translations/source/" + aLanguage + "/scaddins/source/analysis.po"; + aPoInput.open(aPoPath); + if( !aPoInput.isOpen() ) + std::cerr << "Warning: Cannot open " << aPoPath << std::endl; + + for(;;) + { + PoEntry aPoEntry; + aPoInput.readEntry(aPoEntry); + if( aPoInput.eof() ) + break; + if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_ANALYSIS_FUNCTION_NAMES" ) + { + OString aMsgStr = aPoEntry.getMsgStr(); + if( aMsgStr.isEmpty() ) + continue; + if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() ) + aMsgStr += "_ADD"; + if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) + aLocalizedFunctionNames[aMsgStr] = 1; + else + aLocalizedFunctionNames[aMsgStr]++; + } + } + aPoInput.close(); + + aPoPath = OString(getenv("SRC_ROOT")) + + "/translations/source/" + aLanguage + "/scaddins/source/datefunc.po"; + aPoInput.open(aPoPath); + if( !aPoInput.isOpen() ) + std::cerr << "Warning: Cannot open " << aPoPath << std::endl; + + for(;;) + { + PoEntry aPoEntry; + aPoInput.readEntry(aPoEntry); + if( aPoInput.eof() ) + break; + if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_DATE_FUNCTION_NAMES" ) + { + OString aMsgStr = aPoEntry.getMsgStr(); + if( aMsgStr.isEmpty() ) + continue; + if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() ) + aMsgStr += "_ADD"; + if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) + aLocalizedFunctionNames[aMsgStr] = 1; + else + aLocalizedFunctionNames[aMsgStr]++; + } + } + aPoInput.close(); + + aPoPath = OString(getenv("SRC_ROOT")) + + "/translations/source/" + aLanguage + "/scaddins/source/pricing.po"; + aPoInput.open(aPoPath); + if( !aPoInput.isOpen() ) + std::cerr << "Warning: Cannot open " << aPoPath << std::endl; + + for(;;) + { + PoEntry aPoEntry; + aPoInput.readEntry(aPoEntry); + if( aPoInput.eof() ) + break; + if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_PRICING_FUNCTION_NAMES" ) + { + OString aMsgStr = aPoEntry.getMsgStr(); + if( aMsgStr.isEmpty() ) + continue; + if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() ) + aMsgStr += "_ADD"; + if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) + aLocalizedFunctionNames[aMsgStr] = 1; + else + aLocalizedFunctionNames[aMsgStr]++; + } + } + aPoInput.close(); + for( std::map::iterator it=aLocalizedFunctionNames.begin(); it!=aLocalizedFunctionNames.end(); ++it) + { + if( it->second > 1 ) + { + std::cout << "ERROR: Spreadsheet function name translations must be unique.\n" << + "Language: " << aLanguage << + "\nDuplicated translation is: " << it->first << "\n\n"; + } + } +} + +// In instsetoo_native/inc_openoffice/windows/msi_languages.po +// where an en-US string ends with '|', translation must end +// with '|', too. +static void checkVerticalBar(OString aLanguage) +{ + OString aPoPath = OString(getenv("SRC_ROOT")) + + "/translations/source/" + aLanguage + "/instsetoo_native/inc_openoffice/windows/msi_languages.po"; + PoIfstream aPoInput; + aPoInput.open(aPoPath); + if( !aPoInput.isOpen() ) + std::cerr << "Warning: Cannot open " << aPoPath << std::endl; + + for(;;) + { + PoEntry aPoEntry; + aPoInput.readEntry(aPoEntry); + if( aPoInput.eof() ) + break; + if( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|") && + !aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|") ) + { + std::cout << "ERROR: Missing '|' character at the end of translated string.\n" << + "It causes runtime error in installer.\n" << + "File: " << aPoPath << std::endl << + "Language: " << aLanguage << std::endl << + "English: " << aPoEntry.getMsgId() << std::endl << + "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl; + } + } + aPoInput.close(); +} + +int main() +{ + OString aLanguages(getenv("ALL_LANGS")); + if( aLanguages.isEmpty() ) + { + std::cerr << "Usage: make cmd cmd=solver/*/bin/pocheck\n"; + return 1; + } + for(sal_Int32 i = 1;;++i) // skip en-US + { + OString aLanguage = aLanguages.getToken(i,' '); + if( aLanguage.isEmpty() ) + break; + if( aLanguage == "qtz" ) + continue; + checkStyleNames(aLanguage); + checkFunctionNames(aLanguage); + checkVerticalBar(aLanguage); + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */