forked from qt-creator/qt-creator
		
	GPL-3.0 is deprecated by SPDX.
Change done by
 find . -type f -exec perl -pi -e 's/LicenseRef-Qt-Commercial OR GPL-3.0(?!-)/LicenseRef-Qt-Commercial OR GPL-3.0-only/g' {} \;
Change-Id: If316a498e3f27d2030b86d4e7743b3237ce09939
Reviewed-by: Lucie Gerard <lucie.gerard@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
		
	
		
			
				
	
	
		
			194 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/usr/bin/env python
 | 
						|
 | 
						|
# Copyright (C) 2022 The Qt Company Ltd.
 | 
						|
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
 | 
						|
 | 
						|
# See argparse description in main
 | 
						|
#
 | 
						|
# Run on all .ts files in Qt Creator from qtcreator root dir:
 | 
						|
#  for tsfile in share/qtcreator/translations/qtcreator_*.ts; do python scripts/scrubts.py $tsfile -context FooBar; done
 | 
						|
 | 
						|
import argparse
 | 
						|
import pathlib
 | 
						|
import sys
 | 
						|
from dataclasses import dataclass
 | 
						|
from enum import Enum, auto
 | 
						|
 | 
						|
def rewriteLines(input, scrubbedContext, tsFilePath):
 | 
						|
    result = []
 | 
						|
    previouslyInContext = False
 | 
						|
    contextWasPresent = False
 | 
						|
    messageHashes = []
 | 
						|
    mergedContextsCount = 0
 | 
						|
    removedDuplicatesCount = 0
 | 
						|
 | 
						|
    lineIter = iter(input)
 | 
						|
    for line in lineIter:
 | 
						|
        # Context merging
 | 
						|
        if line.count(r"</name>") == 1: # Any new context
 | 
						|
            if line.count(scrubbedContext + r"</name>") == 1: # It the context being scrubbed
 | 
						|
                contextWasPresent = True
 | 
						|
                if previouslyInContext: # Previous context was a scrubbed context, so merge them
 | 
						|
                    mergedContextsCount += 1
 | 
						|
                    result = result[ : -2] # Remove recent:   </context>\n<context>
 | 
						|
                    continue               # ...and skip this input line
 | 
						|
                else:
 | 
						|
                    previouslyInContext = True
 | 
						|
            else:
 | 
						|
                previouslyInContext = False
 | 
						|
 | 
						|
        # Message de-duplicating
 | 
						|
        if previouslyInContext and line.count(r"<message") == 1: # message in scrubbed context
 | 
						|
            # Iterate through message
 | 
						|
            messageLines = [line]
 | 
						|
            for messageLine in lineIter:
 | 
						|
                messageLines.append(messageLine)
 | 
						|
                if messageLine.count(r"</message>") == 1: # message finished
 | 
						|
                    break
 | 
						|
 | 
						|
            # Duplication check
 | 
						|
            messageHash = hash(str(messageLines))
 | 
						|
            if messageHash not in messageHashes:
 | 
						|
                result = result + messageLines
 | 
						|
                messageHashes.append(messageHash) # Append if not a duplicate
 | 
						|
            else:
 | 
						|
                removedDuplicatesCount += 1
 | 
						|
 | 
						|
            continue
 | 
						|
 | 
						|
        result.append(line)
 | 
						|
 | 
						|
    if not contextWasPresent:
 | 
						|
        error = f"Context \"{scrubbedContext}\" was not found in {tsFilePath}"
 | 
						|
        sys.exit(error)
 | 
						|
 | 
						|
    print (f"{tsFilePath}:")
 | 
						|
    print (f"  {removedDuplicatesCount} identical duplicate message(s) removed.")
 | 
						|
    print (f"  {mergedContextsCount} occurrence(s) of context \"{scrubbedContext}\" merged.")
 | 
						|
 | 
						|
    return result
 | 
						|
 | 
						|
 | 
						|
def findDistinctDuplicates(input, scrubbedContext, tsFilePath):
 | 
						|
    inContext = False
 | 
						|
    inputLineNr = 0
 | 
						|
 | 
						|
    @dataclass
 | 
						|
    class Translation:
 | 
						|
        lineNr: int
 | 
						|
        translationXml: []
 | 
						|
 | 
						|
    @dataclass
 | 
						|
    class Source:
 | 
						|
        sourceXml: str
 | 
						|
        translations: []
 | 
						|
 | 
						|
    messages = {}
 | 
						|
 | 
						|
    lineIter = iter(input)
 | 
						|
    for line in lineIter:
 | 
						|
        inputLineNr += 1
 | 
						|
        if line.count(r"</name>") == 1: # Any new context
 | 
						|
            inContext = (line.count(scrubbedContext + r"</name>") == 1)
 | 
						|
            continue
 | 
						|
        if line.count(r"<message") == 0:
 | 
						|
            continue
 | 
						|
        if inContext:
 | 
						|
            sourceXml = []
 | 
						|
            translationXml = []
 | 
						|
            lineNr = inputLineNr
 | 
						|
 | 
						|
            class Position(Enum):
 | 
						|
                MESSAGESTART = auto()
 | 
						|
                LOCATION = auto()
 | 
						|
                SOURCE = auto()
 | 
						|
                COMMENT = auto()
 | 
						|
                EXTRACOMMENT = auto()
 | 
						|
                TRANSLATORCOMMENT = auto()
 | 
						|
                TRANSLATION = auto()
 | 
						|
                MESSAGEOVER = auto()
 | 
						|
 | 
						|
            pos = Position.MESSAGESTART
 | 
						|
 | 
						|
            for messageLine in lineIter:
 | 
						|
                inputLineNr += 1
 | 
						|
                if messageLine.count(r"<location") == 1:
 | 
						|
                    pos = Position.LOCATION
 | 
						|
                elif messageLine.count(r"<source") == 1:
 | 
						|
                    pos = Position.SOURCE
 | 
						|
                elif messageLine.count(r"<comment") == 1:
 | 
						|
                    pos = Position.COMMENT
 | 
						|
                elif messageLine.count(r"<extracomment") == 1:
 | 
						|
                    pos = Position.EXTRACOMMENT
 | 
						|
                elif messageLine.count(r"<translatorcomment") == 1:
 | 
						|
                    pos = Position.TRANSLATORCOMMENT
 | 
						|
                elif messageLine.count(r"<translation") == 1:
 | 
						|
                    pos = Position.TRANSLATION
 | 
						|
                elif messageLine.count(r"</message>") == 1:
 | 
						|
                    pos = Position.MESSAGEOVER
 | 
						|
 | 
						|
                if pos == Position.SOURCE or pos == Position.COMMENT:
 | 
						|
                    sourceXml.append(messageLine)
 | 
						|
                elif pos == Position.TRANSLATION or pos == Position.EXTRACOMMENT or pos == Position.TRANSLATORCOMMENT:
 | 
						|
                    translationXml.append(messageLine)
 | 
						|
                elif pos == Position.MESSAGEOVER:
 | 
						|
                    break
 | 
						|
 | 
						|
            sourceXmlHash = hash(str(sourceXml))
 | 
						|
            translation = Translation(lineNr, translationXml)
 | 
						|
            if sourceXmlHash in messages:
 | 
						|
                messages[sourceXmlHash].translations.append(translation)
 | 
						|
            else:
 | 
						|
                messages[sourceXmlHash] = Source(sourceXml, [translation])
 | 
						|
 | 
						|
    for sourceId in messages:
 | 
						|
        source = messages[sourceId]
 | 
						|
        translationsCount = len(source.translations)
 | 
						|
        if translationsCount > 1:
 | 
						|
            print (f"\n==========================================")
 | 
						|
            print (f"\n{translationsCount} duplicates for source:")
 | 
						|
            for sourceXmlLine in source.sourceXml:
 | 
						|
                print (sourceXmlLine.rstrip())
 | 
						|
            for translation in source.translations:
 | 
						|
                print (f"\n{tsFilePath}:{translation.lineNr}")
 | 
						|
                for translationXmlLine in translation.translationXml:
 | 
						|
                    print (translationXmlLine.rstrip())
 | 
						|
 | 
						|
 | 
						|
def processTsFile(tsFilePath, scrubbedContext):
 | 
						|
    with open(tsFilePath, 'r') as tsInputFile:
 | 
						|
        lines = tsInputFile.readlines()
 | 
						|
 | 
						|
    result = rewriteLines(lines, scrubbedContext, tsFilePath)
 | 
						|
    if lines != result:
 | 
						|
        with open(tsFilePath, 'w') as tsOutputFile:
 | 
						|
            for line in result:
 | 
						|
                tsOutputFile.write(line)
 | 
						|
 | 
						|
    findDistinctDuplicates(result, scrubbedContext, tsFilePath)
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
    parser = argparse.ArgumentParser(
 | 
						|
        description='''Rewrites a .ts file, removing identical duplicate messages of a specified
 | 
						|
                       translation context and joining adjacent occurrences of that context.
 | 
						|
                       Unlike lrelease and lconvert, this script does an exact comparison of the
 | 
						|
                       whole <message/> xml tag when removing duplicates.
 | 
						|
                       Subsequently, the remaining duplicate messages with identical source but
 | 
						|
                       different translation are listed with filename:linenumber.''')
 | 
						|
    parser.add_argument('tsfile',
 | 
						|
                        help='The .ts file to be processed.',
 | 
						|
                        type=pathlib.Path)
 | 
						|
    parser.add_argument('-context',
 | 
						|
                        help='Translation context to scrubbed.',
 | 
						|
                        required=True)
 | 
						|
    args = parser.parse_args()
 | 
						|
 | 
						|
    processTsFile(args.tsfile, args.context)
 | 
						|
 | 
						|
    return 0
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    sys.exit(main())
 |