/* -*- 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 <sal/config.h>
#include <cassert>
#include <iostream>
#include <map>
#include <vector>
#include <rtl/string.hxx>
#include <rtl/ustring.hxx>
#include <osl/file.hxx>
#include <po.hxx>
// Translated style names must be unique
static void checkStyleNames(
const OString& aLanguage)
{
std::map<OString,sal_uInt16> aLocalizedStyleNames;
std::map<OString,sal_uInt16> aLocalizedNumStyleNames;
std::vector<PoEntry> repeatedEntries;
OString aPoPath = OString::Concat(getenv(
"SRC_ROOT" )) +
"/translations/source/" +
aLanguage +
"/sw/messages.po" ;
PoIfstream aPoInput;
aPoInput.open(aPoPath);
if ( !aPoInput.isOpen() )
{
std::cerr <<
"Warning: Cannot open " << aPoPath << std::endl;
return ;
}
for (;;)
{
PoEntry aPoEntry;
aPoInput.readEntry(aPoEntry);
bool bRepeated =
false ;
if ( aPoInput.eof() )
{
break ;
}
if ( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith(
"STR_POOLCOLL" ) )
{
const OString& aMsgStr = aPoEntry.getMsgStr();
if ( aMsgStr.isEmpty() )
continue ;
if ( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() )
aLocalizedStyleNames[aMsgStr] = 1;
else {
aLocalizedStyleNames[aMsgStr]++;
bRepeated =
true ;
}
}
if ( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith(
"STR_POOLNUMRULE" ) )
{
const OString& aMsgStr = aPoEntry.getMsgStr();
if ( aMsgStr.isEmpty() )
continue ;
if ( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() )
aLocalizedNumStyleNames[aMsgStr] = 1;
else {
aLocalizedNumStyleNames[aMsgStr]++;
bRepeated =
true ;
}
}
if (bRepeated)
repeatedEntries.push_back(aPoEntry);
}
aPoInput.close();
for (
auto const & localizedStyleName : aLocalizedStyleNames)
{
if ( localizedStyleName.second > 1 )
{
std::cout <<
"ERROR: Style name translations must be unique in:\n" <<
aPoPath <<
"\nLanguage: " << aLanguage <<
"\nDuplicated translation is: " << localizedStyleName.
first <<
"\nSee STR_POOLCOLL_*\n\n" ;
}
}
for (auto const & localizedNumStyleName : aLocalizedNumStyleNames)
{
if ( localizedNumStyleName.second > 1 )
{
std::cout << "ERROR: Style name translations must be unique in:\n" <<
aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << localizedNumStyleName.first <<
"\nSee STR_POOLNUMRULE_*\n\n" ;
}
}
OString sPoHdrMsg;
aPoInput.open(aPoPath, sPoHdrMsg);
if ( !aPoInput.isOpen() )
{
std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
return ;
}
PoOfstream aPoOutput;
aPoOutput.open(aPoPath+".new" );
PoHeader aTmp("sw/inc" , sPoHdrMsg);
aPoOutput.writeHeader(aTmp);
bool bAnyError = false ;
for (;;)
{
PoEntry aPoEntry;
bool bError = false ;
aPoInput.readEntry(aPoEntry);
if ( aPoInput.eof() )
break ;
for (auto const & repeatedEntry : repeatedEntries)
{
if (repeatedEntry.getMsgId() == aPoEntry.getMsgId() && repeatedEntry.getMsgCtxt() == aPoEntry.getMsgCtxt()) {
bError = true ;
break ;
}
}
if (bError) {
bAnyError = true ;
} else {
aPoOutput.writeEntry(aPoEntry);
}
}
aPoInput.close();
aPoOutput.close();
OUString aPoPathURL;
osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
if ( bAnyError )
osl::File::move(aPoPathURL + ".new" , aPoPathURL);
else
osl::File::remove(aPoPathURL + ".new" );
}
// Translated spreadsheet function names must be unique
static void checkFunctionNames(const OString& aLanguage)
{
std::map<OString,sal_uInt16> aLocalizedFunctionNames;
std::map<OString,sal_uInt16> aLocalizedCoreFunctionNames;
std::vector<PoEntry> repeatedEntries;
OString aPoPaths[2];
OUString aPoPathURL;
aPoPaths[0] = OString::Concat(getenv("SRC_ROOT" )) +
"/translations/source/" +
aLanguage +
"/formula/messages.po" ;
PoIfstream aPoInput;
OString sPoHdrMsg;
aPoInput.open(aPoPaths[0], sPoHdrMsg);
if ( !aPoInput.isOpen() )
{
std::cerr << "Warning: Cannot open " << aPoPaths[0] << std::endl;
return ;
}
for (;;)
{
PoEntry aPoEntry;
aPoInput.readEntry(aPoEntry);
if ( aPoInput.eof() )
break ;
if ( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt() == "RID_STRLIST_FUNCTION_NAMES" )
{
const 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]++;
repeatedEntries.push_back(aPoEntry);
}
}
}
aPoInput.close();
aPoPaths[1] = OString::Concat(getenv("SRC_ROOT" )) +
"/translations/source/" +
aLanguage +
"/scaddins/messages.po" ;
aPoInput.open(aPoPaths[1]);
if ( !aPoInput.isOpen() )
{
std::cerr << "Warning: Cannot open " << aPoPaths[1] << std::endl;
return ;
}
for (;;)
{
PoEntry aPoEntry;
aPoInput.readEntry(aPoEntry);
if ( aPoInput.eof() )
break ;
if ( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith("ANALYSIS_FUNCNAME" ) )
{
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]++;
repeatedEntries.push_back(aPoEntry);
}
}
}
aPoInput.close();
for (auto const & localizedFunctionName : aLocalizedFunctionNames)
{
if ( localizedFunctionName.second > 1 )
{
std::cout
<< ("ERROR: Spreadsheet function name translations must be"
" unique.\nLanguage: " )
<< aLanguage << "\nDuplicated translation is: " << localizedFunctionName.first
<< "\n\n" ;
}
}
for (int i=0;i<2;i++)
{
aPoInput.open(aPoPaths[i]);
if ( !aPoInput.isOpen() )
std::cerr << "Warning: Cannot open " << aPoPaths[i] << std::endl;
PoOfstream aPoOutput;
aPoOutput.open(aPoPaths[i]+".new" );
switch (i)
{
case 0:
{
PoHeader hd("formula/inc" , sPoHdrMsg);
aPoOutput.writeHeader(hd);
break ;
}
case 1:
{
PoHeader hd("scaddins/inc" , sPoHdrMsg);
aPoOutput.writeHeader(hd);
break ;
}
}
bool bAnyError = false ;
for (;;)
{
PoEntry aPoEntry;
bool bError = false ;
aPoInput.readEntry(aPoEntry);
if ( aPoInput.eof() )
break ;
for (auto const & repeatedEntry : repeatedEntries)
{
if (repeatedEntry.getMsgId() == aPoEntry.getMsgId() && repeatedEntry.getMsgCtxt() == aPoEntry.getMsgCtxt())
{
bError = true ;
break ;
}
}
if (bError)
{
bAnyError = true ;
}
else
{
aPoOutput.writeEntry(aPoEntry);
}
}
aPoInput.close();
aPoOutput.close();
osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPaths[i], RTL_TEXTENCODING_UTF8), aPoPathURL);
if ( bAnyError )
osl::File::move(aPoPathURL + ".new" , aPoPathURL);
else
osl::File::remove(aPoPathURL + ".new" );
}
}
static void printError(const OString& rPoPath, const OString& rLanguage, const PoEntry& rPoEntry, const OString& rError)
{
std::cout << "ERROR: " << rError << std::endl
<< "File: " << rPoPath << std::endl
<< "Language: " << rLanguage << std::endl
<< "English: " << rPoEntry.getMsgId() << std::endl
<< "Localized: " << rPoEntry.getMsgStr() << std::endl
<< std::endl;
}
// In instsetoo_native/inc_openoffice/windows/msi_languages.po
// where an en-US string ends with '|', translation must end
// with '|', too.
static void checkVerticalBar(const OString& aLanguage)
{
OString aPoPath = OString::Concat(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;
return ;
}
PoOfstream aPoOutput;
aPoOutput.open(aPoPath+".new" );
PoHeader aTmp("instsetoo_native/inc_openoffice/windows/msi_languages" );
aPoOutput.writeHeader(aTmp);
bool bError = false ;
for (;;)
{
PoEntry aPoEntry;
aPoInput.readEntry(aPoEntry);
if ( aPoInput.eof() )
break ;
if ( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|" ) &&
!aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|" ) )
{
OString aError("Missing '|' character at the end of translated"
" string.\nIt causes runtime error in installer." _ostr);
printError(aPoPath, aLanguage, aPoEntry, aError);
bError = true ;
}
else
aPoOutput.writeEntry(aPoEntry);
}
aPoInput.close();
aPoOutput.close();
OUString aPoPathURL;
osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
if ( bError )
osl::File::move(aPoPathURL + ".new" , aPoPathURL);
else
osl::File::remove(aPoPathURL + ".new" );
}
static void checkMalformedString(const OString& aLanguage)
{
OString aPoPath = OString::Concat(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;
return ;
}
PoOfstream aPoOutput;
aPoOutput.open(aPoPath+".new" );
PoHeader aTmp("instsetoo_native/inc_openoffice/windows/msi_languages" );
aPoOutput.writeHeader(aTmp);
bool bError = false ;
for (;;)
{
PoEntry aPoEntry;
aPoInput.readEntry(aPoEntry);
if ( aPoInput.eof() )
break ;
if (!aPoEntry.isFuzzy() && !aPoEntry.getMsgStr().isEmpty())
{
if (aPoEntry.getMsgId().indexOf("[ProductName]" ) != -1 &&
aPoEntry.getMsgStr().indexOf("[ProductName]" ) == -1)
{
// just a warning. bError = false
printError(aPoPath, aLanguage, aPoEntry, "Incorrect [ProductName]." _ostr);
}
OString aStr = aPoEntry.getMsgStr();
sal_Int32 nPos(0);
sal_Int32 nOpeningBracketsCount(0);
sal_Int32 nClosingBracketsCount(0);
sal_Int32 nOpeningSquareBracketsCount(0);
sal_Int32 nClosingSquareBracketsCount(0);
sal_Int32 nOpeningParenthesesCount(0);
sal_Int32 nClosingParenthesesCount(0);
// Check the number of opening and closing characters for
// brackets, square brackets and parentheses match
while (nPos < aStr.getLength())
{
const sal_Unicode cCurrentChar = aStr[nPos];
if (cCurrentChar == '{' )
++nOpeningBracketsCount;
else if (cCurrentChar == '}' )
++nClosingBracketsCount;
else if (cCurrentChar == '[' )
++nOpeningSquareBracketsCount;
else if (cCurrentChar == ']' )
++nClosingSquareBracketsCount;
else if (cCurrentChar == '(' )
++nOpeningParenthesesCount;
else if (cCurrentChar == ')' )
++nClosingParenthesesCount;
++nPos;
}
// See instsetoo_native/inc_openoffice/windows/msi_templates/TextStyl.idt
if ((aPoEntry.getMsgId().startsWith("{&DialogDefaultBold}" ) &&
!aPoEntry.getMsgStr().startsWith("{&DialogDefaultBold}" )) ||
(aPoEntry.getMsgId().startsWith("{&DialogHeading}" ) &&
!aPoEntry.getMsgStr().startsWith("{&DialogHeading}" )))
{
printError(aPoPath, aLanguage, aPoEntry, "Incorrect TextStyle." _ostr);
bError = true ;
}
else if (nOpeningBracketsCount != nClosingBracketsCount)
{
printError(aPoPath, aLanguage, aPoEntry, "Number of Opening and Closing brackets doesn't match." _ostr);
bError = true ;
}
else if (nOpeningSquareBracketsCount != nClosingSquareBracketsCount)
{
printError(aPoPath, aLanguage, aPoEntry, "Number of Opening and Closing square brackets doesn't match." _ostr);
bError = true ;
}
else if (nOpeningParenthesesCount != nClosingParenthesesCount)
{
printError(aPoPath, aLanguage, aPoEntry, "Number of Opening and Closing parentheses doesn't match." _ostr);
bError = true ;
}
else
aPoOutput.writeEntry(aPoEntry);
}
else
aPoOutput.writeEntry(aPoEntry);
}
aPoInput.close();
aPoOutput.close();
OUString aPoPathURL;
osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
if ( bError )
osl::File::move(aPoPathURL + ".new" , aPoPathURL);
else
osl::File::remove(aPoPathURL + ".new" );
}
// In starmath/source.po Math symbol names (from symbol.src)
// must not contain spaces
static void checkMathSymbolNames(const OString& aLanguage)
{
OString aPoPath = OString::Concat(getenv("SRC_ROOT" )) +
"/translations/source/" +
aLanguage +
"/starmath/messages.po" ;
PoIfstream aPoInput;
aPoInput.open(aPoPath);
if ( !aPoInput.isOpen() )
{
std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
return ;
}
PoOfstream aPoOutput;
aPoOutput.open(aPoPath+".new" );
PoHeader aTmp("starmath/inc" );
aPoOutput.writeHeader(aTmp);
bool bError = false ;
for (;;)
{
PoEntry aPoEntry;
aPoInput.readEntry(aPoEntry);
if ( aPoInput.eof() )
break ;
if ( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_UI_SYMBOL_NAMES" &&
!aPoEntry.getMsgStr().isEmpty() && (aPoEntry.getMsgStr().indexOf(" " ) != -1) )
{
printError(aPoPath, aLanguage, aPoEntry, "Math symbol names must not contain spaces." _ostr);
bError = true ;
}
else
aPoOutput.writeEntry(aPoEntry);
}
aPoInput.close();
aPoOutput.close();
OUString aPoPathURL;
osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
if ( bError )
osl::File::move(aPoPathURL + ".new" , aPoPathURL);
else
osl::File::remove(aPoPathURL + ".new" );
}
int main()
{
try
{
auto const env = getenv("ALL_LANGS" );
assert(env != nullptr);
OString aLanguages(env);
if ( aLanguages.isEmpty() )
{
std::cerr << "Usage: LD_LIBRARY_PATH=instdir/program make cmd cmd=workdir/LinkTarget/Executable/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);
checkMathSymbolNames(aLanguage);
checkMalformedString(aLanguage);
}
return 0;
}
catch (std::exception& e)
{
std::cerr << "pocheck: exception " << e.what() << std::endl;
return 1;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=96 H=95 G=95
¤ Dauer der Verarbeitung: 0.31 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland