pocheck.cxx 13.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/* -*- 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>
12
#include <vector>
13
#include <rtl/string.hxx>
14 15
#include <rtl/ustring.hxx>
#include <osl/file.hxx>
16
#include <po.hxx>
17 18

// Translated style names must be unique
19
static void checkStyleNames(const OString& aLanguage)
20 21 22
{
    std::map<OString,sal_uInt16> aLocalizedStyleNames;
    std::map<OString,sal_uInt16> aLocalizedNumStyleNames;
23
    std::vector<PoEntry> repeatedEntries;
24

25
    OString aPoPath = OString(getenv("SRC_ROOT")) +
Stephan Bergmann's avatar
Stephan Bergmann committed
26
                      "/translations/source/" +
27
                      aLanguage + "/sw/messages.po";
28 29 30
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
31
    {
32
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
33 34
        return;
    }
35 36 37

    for(;;)
    {
Matteo Casalin's avatar
Matteo Casalin committed
38 39
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
40
        bool bRepeated = false;
41
        if( aPoInput.eof() )
42
        {
43
            break;
44 45
        }

46
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith("STR_POOLCOLL") )
47
        {
48
            const OString& aMsgStr = aPoEntry.getMsgStr();
49 50 51 52
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() )
                aLocalizedStyleNames[aMsgStr] = 1;
53
            else {
54
                aLocalizedStyleNames[aMsgStr]++;
55 56
                bRepeated = true;
            }
57
        }
58
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith("STR_POOLNUMRULE") )
59
        {
60
            const OString& aMsgStr = aPoEntry.getMsgStr();
61 62 63 64
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() )
                aLocalizedNumStyleNames[aMsgStr] = 1;
65
            else {
66
                aLocalizedNumStyleNames[aMsgStr]++;
67 68
                bRepeated = true;
            }
69
        }
70
        if (bRepeated)
Matteo Casalin's avatar
Matteo Casalin committed
71
            repeatedEntries.push_back(aPoEntry);
72 73 74
    }
    aPoInput.close();

75
    for (auto const& localizedStyleName : aLocalizedStyleNames)
76
    {
77
        if( localizedStyleName.second > 1 )
78 79
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
80
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << localizedStyleName.first <<
81 82 83
                "\nSee STR_POOLCOLL_*\n\n";
        }
    }
84
    for (auto const& localizedNumStyleName : aLocalizedNumStyleNames)
85
    {
86
        if( localizedNumStyleName.second > 1 )
87 88
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
89
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << localizedNumStyleName.first <<
90 91 92
                "\nSee STR_POOLNUMRULE_*\n\n";
        }
    }
93 94
    OString sPoHdrMsg;
    aPoInput.open(aPoPath, sPoHdrMsg);
95
    if( !aPoInput.isOpen() )
96
    {
97
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
98 99
        return;
    }
100 101
    PoOfstream aPoOutput;
    aPoOutput.open(aPoPath+".new");
102
    PoHeader aTmp("sw/inc", sPoHdrMsg);
103 104 105 106 107 108 109 110 111 112
    aPoOutput.writeHeader(aTmp);
    bool bAnyError = false;

    for(;;)
    {
        PoEntry aPoEntry;
        bool bError = false;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
113 114
        for (auto const& repeatedEntry : repeatedEntries)
        {
115
            if (repeatedEntry.getMsgId() == aPoEntry.getMsgId() && repeatedEntry.getMsgCtxt() == aPoEntry.getMsgCtxt()) {
116 117 118 119 120 121 122 123 124 125 126 127 128
                bError = true;
                break;
            }
        }
        if (bError) {
            bAnyError = true;
        } else {
            aPoOutput.writeEntry(aPoEntry);
        }
    }
    aPoInput.close();
    aPoOutput.close();
    OUString aPoPathURL;
129
    osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
130 131 132 133
    if( bAnyError )
        osl::File::move(aPoPathURL + ".new", aPoPathURL);
    else
        osl::File::remove(aPoPathURL + ".new");
134 135 136
}

// Translated spreadsheet function names must be unique
137
static void checkFunctionNames(const OString& aLanguage)
138 139 140
{
    std::map<OString,sal_uInt16> aLocalizedFunctionNames;
    std::map<OString,sal_uInt16> aLocalizedCoreFunctionNames;
141

142
    std::vector<PoEntry> repeatedEntries;
143

144
    OString aPoPaths[2];
145 146 147
    OUString aPoPathURL;

    aPoPaths[0] = OString(getenv("SRC_ROOT")) +
Stephan Bergmann's avatar
Stephan Bergmann committed
148 149
                      "/translations/source/" +
                      aLanguage +
150
                      "/formula/messages.po";
151
    PoIfstream aPoInput;
152
    aPoInput.open(aPoPaths[0]);
153
    if( !aPoInput.isOpen() )
154
    {
155
        std::cerr << "Warning: Cannot open " << aPoPaths[0] << std::endl;
156 157
        return;
    }
158 159 160

    for(;;)
    {
Matteo Casalin's avatar
Matteo Casalin committed
161 162
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
163 164
        if( aPoInput.eof() )
            break;
165
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt() == "RID_STRLIST_FUNCTION_NAMES" )
166
        {
167
            const OString& aMsgStr = aPoEntry.getMsgStr();
168 169 170 171
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) == aLocalizedCoreFunctionNames.end() )
                aLocalizedCoreFunctionNames[aMsgStr] = 1;
172
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
173
                aLocalizedFunctionNames[aMsgStr] = 1;
174
            } else {
175
                aLocalizedFunctionNames[aMsgStr]++;
176 177
                repeatedEntries.push_back(aPoEntry);
            }
178 179 180 181
        }
    }
    aPoInput.close();

182
    aPoPaths[1] = OString(getenv("SRC_ROOT")) +
Stephan Bergmann's avatar
Stephan Bergmann committed
183 184
        "/translations/source/" +
        aLanguage +
185
        "/scaddins/messages.po";
186
    aPoInput.open(aPoPaths[1]);
187
    if( !aPoInput.isOpen() )
188
    {
189
        std::cerr << "Warning: Cannot open " << aPoPaths[1] << std::endl;
190 191
        return;
    }
192 193 194

    for(;;)
    {
Matteo Casalin's avatar
Matteo Casalin committed
195 196
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
197 198
        if( aPoInput.eof() )
            break;
199
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith("ANALYSIS_FUNCNAME") )
200
        {
Matteo Casalin's avatar
Matteo Casalin committed
201
            OString aMsgStr = aPoEntry.getMsgStr();
202 203 204 205
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
206
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
207
                aLocalizedFunctionNames[aMsgStr] = 1;
208
            } else {
209
                aLocalizedFunctionNames[aMsgStr]++;
210 211
                repeatedEntries.push_back(aPoEntry);
            }
212 213 214 215
        }
    }
    aPoInput.close();

216
    for (auto const& localizedFunctionName : aLocalizedFunctionNames)
217
    {
218
        if( localizedFunctionName.second > 1 )
219
        {
220 221 222
            std::cout
                << ("ERROR: Spreadsheet function name translations must be"
                    " unique.\nLanguage: ")
223
                << aLanguage << "\nDuplicated translation is: " << localizedFunctionName.first
224
                << "\n\n";
225 226
        }
    }
227

228
    for (int i=0;i<2;i++)
229
    {
230 231 232 233 234 235
        aPoInput.open(aPoPaths[i]);
        if( !aPoInput.isOpen() )
            std::cerr << "Warning: Cannot open " << aPoPaths[i] << std::endl;
        PoOfstream aPoOutput;
        aPoOutput.open(aPoPaths[i]+".new");

236 237 238 239
        switch (i)
        {
        case 0:
        {
240
            PoHeader hd("formula/inc");
241 242 243 244 245
            aPoOutput.writeHeader(hd);
            break;
        }
        case 1:
        {
246
            PoHeader hd("scaddins/inc");
247 248 249
            aPoOutput.writeHeader(hd);
            break;
        }
250 251 252 253 254 255 256 257 258 259
        }
        bool bAnyError = false;

        for(;;)
        {
            PoEntry aPoEntry;
            bool bError = false;
            aPoInput.readEntry(aPoEntry);
            if( aPoInput.eof() )
                break;
260
            for (auto const& repeatedEntry : repeatedEntries)
261
            {
262
                if (repeatedEntry.getMsgId() == aPoEntry.getMsgId() && repeatedEntry.getMsgCtxt() == aPoEntry.getMsgCtxt())
263
                {
264 265 266 267
                    bError = true;
                    break;
                }
            }
268 269
            if (bError)
            {
270
                bAnyError = true;
271 272 273
            }
            else
            {
274 275 276 277 278
                aPoOutput.writeEntry(aPoEntry);
            }
        }
        aPoInput.close();
        aPoOutput.close();
279
        osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPaths[i], RTL_TEXTENCODING_UTF8), aPoPathURL);
280 281 282 283 284
        if( bAnyError )
            osl::File::move(aPoPathURL + ".new", aPoPathURL);
        else
            osl::File::remove(aPoPathURL + ".new");
    }
285 286 287 288 289
}

// In instsetoo_native/inc_openoffice/windows/msi_languages.po
// where an en-US string ends with '|', translation must end
// with '|', too.
290
static void checkVerticalBar(const OString& aLanguage)
291 292
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
Stephan Bergmann's avatar
Stephan Bergmann committed
293 294
                      "/translations/source/" +
                      aLanguage +
295 296 297 298
                      "/instsetoo_native/inc_openoffice/windows/msi_languages.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
299
    {
300
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
301 302 303 304
        return;
    }
    PoOfstream aPoOutput;
    aPoOutput.open(aPoPath+".new");
305 306 307
    PoHeader aTmp("instsetoo_native/inc_openoffice/windows/msi_languages");
    aPoOutput.writeHeader(aTmp);
    bool bError = false;
308 309 310 311 312 313 314 315 316 317

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|") &&
            !aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|") )
        {
318 319 320 321 322 323 324 325
            std::cout
                << ("ERROR: Missing '|' character at the end of translated"
                    " string.\nIt causes runtime error in installer.\nFile: ")
                << aPoPath << std::endl
                << "Language: " << aLanguage << std::endl
                << "English:   " << aPoEntry.getMsgId() << std::endl
                << "Localized: " << aPoEntry.getMsgStr() << std::endl
                << std::endl;
326
            bError = true;
327
        }
328 329
        else
            aPoOutput.writeEntry(aPoEntry);
330 331
    }
    aPoInput.close();
332 333
    aPoOutput.close();
    OUString aPoPathURL;
334
    osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
335 336 337 338
    if( bError )
        osl::File::move(aPoPathURL + ".new", aPoPathURL);
    else
        osl::File::remove(aPoPathURL + ".new");
339 340
}

341 342
// In starmath/source.po Math symbol names (from symbol.src)
// must not contain spaces
343
static void checkMathSymbolNames(const OString& aLanguage)
344 345 346 347
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
348
                      "/starmath/messages.po";
349 350 351
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
352
    {
353
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
354 355 356 357
        return;
    }
    PoOfstream aPoOutput;
    aPoOutput.open(aPoPath+".new");
358
    PoHeader aTmp("starmath/inc");
359 360
    aPoOutput.writeHeader(aTmp);
    bool bError = false;
361 362 363 364 365 366 367 368 369 370

    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) )
        {
371 372 373 374 375 376 377
            std::cout
                << "ERROR: Math symbol names must not contain spaces.\nFile: "
                << aPoPath << std::endl
                << "Language: " << aLanguage << std::endl
                << "English:   " << aPoEntry.getMsgId() << std::endl
                << "Localized: " << aPoEntry.getMsgStr() << std::endl
                << std::endl;
378
            bError = true;
379
        }
380 381
        else
            aPoOutput.writeEntry(aPoEntry);
382 383
    }
    aPoInput.close();
384 385
    aPoOutput.close();
    OUString aPoPathURL;
386
    osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
387 388 389 390
    if( bError )
        osl::File::move(aPoPathURL + ".new", aPoPathURL);
    else
        osl::File::remove(aPoPathURL + ".new");
391 392
}

393 394
int main()
{
395
    try
396
    {
397 398 399
        OString aLanguages(getenv("ALL_LANGS"));
        if( aLanguages.isEmpty() )
        {
400
            std::cerr << "Usage: LD_LIBRARY_PATH=instdir/program make cmd cmd=workdir/LinkTarget/Executable/pocheck\n";
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
            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);
        }
        return 0;
416
    }
417
    catch (std::exception& e)
418
    {
419 420
        std::cerr << "pocheck: exception " << e.what() << std::endl;
        return 1;
421 422 423 424
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */