diff options
author | Andras Timar <atimar@suse.com> | 2013-08-20 13:24:19 +0200 |
---|---|---|
committer | Andras Timar <atimar@suse.com> | 2013-08-20 13:29:39 +0200 |
commit | 72576f439ad3eebc6947a50070f1ffabe2964b32 (patch) | |
tree | 0488d4e1dd9677b8f4e863144e880227fe19f8d7 /l10ntools | |
parent | ab55228f6c524ddefde2d7e1d6688b8a407da3ad (diff) |
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
Diffstat (limited to 'l10ntools')
-rw-r--r-- | l10ntools/Executable_pocheck.mk | 34 | ||||
-rw-r--r-- | l10ntools/Module_l10ntools.mk | 1 | ||||
-rw-r--r-- | l10ntools/source/pocheck.cxx | 267 |
3 files changed, 302 insertions, 0 deletions
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 <iostream> +#include <map> +#include <rtl/string.hxx> +#include "po.hxx" + +// Translated style names must be unique +static void checkStyleNames(OString aLanguage) +{ + std::map<OString,sal_uInt16> aLocalizedStyleNames; + std::map<OString,sal_uInt16> 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<OString,sal_uInt16>::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<OString,sal_uInt16>::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<OString,sal_uInt16> aLocalizedFunctionNames; + std::map<OString,sal_uInt16> 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<OString,sal_uInt16>::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: */ |