diff --git a/README.md b/README.md index e761a4c..cce519f 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,13 @@ **ams** (android-missing-strings) is a simple command line reporting tool to know what strings need to be translated on an android project. -The output of ams is a pdf file friendly to a non-coding person who can be in charge of managing the translators involved in the project. +The output of ams is a text file friendly to a non-coding person who can be in charge of managing the translators involved in the project. + +It can also be used to delete left over translation entries that have been deleted from your main string.xml ## Requirements - Python 2.6+ - - [pyPDF](https://code.google.com/p/pyfpdf/) + ## Installation Make sure you have the **ams** source folder on your $PATH environment variable. @@ -15,7 +17,10 @@ Make sure you have the **ams** source folder on your $PATH environment variable. from within any android project, just execute ```bash -ams [-l xx[,yy,zz...]] -o +ams - Android Missing Strings reporting tool. + + Usage: + ams [-l xx[,yy,zz...]] [--cleanleftovers] -o Options: -h --help Print this help @@ -29,11 +34,19 @@ ams [-l xx[,yy,zz...]] -o If this parameter is ommited, a report with every language file found will be created. - -o --oFile Specify the output file name for the PDF report + -o --oFile Specify the output file name for the report + + --cleanleftovers Removes left over translations on other language files + + Copyright (c) 2014-2017 - The Mit License (MIT) + + Authors: + Angel Leon + Katay Santos ``` ## License -Copyright (c) 2014 - The Mit License (MIT) +Copyright (c) 2014-2017 - The Mit License (MIT) ## Authors - [Angel Leon](https://github.com/gubatron/) diff --git a/ams b/ams index 5b40957..7ef27fc 100755 --- a/ams +++ b/ams @@ -1,11 +1,12 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# authors: Angel Leon (@gubatron), Katay Santos (@cateye), Bohdan Horbeshko (@bodqhrohro) + import re import os import sys import getopt -from fpdf import FPDF - -PATTERN=re.compile(r'(.*?)', re.I | re.MULTILINE) +from time import gmtime +import xml.etree.ElementTree as ET class MissingStringsReport: filePath = None @@ -20,12 +21,17 @@ class MissingStringsReport: self.stringsFilePath = stringsFilePath self.originalStringsDict = originalStringsDict self.myStringsDict = MissingStringsReport.readXML(self.stringsFilePath) - self.missingKeys = MissingStringsReport.findMissingKeys(self.originalStringsDict, self.myStringsDict) + self.missingKeys = MissingStringsReport.findMissingKeys(self.originalStringsDict, self.myStringsDict) + self.leftOverKeys = MissingStringsReport.findLeftoverKeys(self.originalStringsDict, self.myStringsDict) def getMissingKeys(self): '''Returns a list with the missing keys.''' return self.missingKeys + def getLeftoverKeys(self): + '''Returns a list of left over keys that should be deleted''' + return self.leftOverKeys + def getMyStringsDict(self): return self.myStringsDict @@ -42,34 +48,56 @@ class MissingStringsReport: missingKeys.append(k) return missingKeys + @staticmethod + def findLeftoverKeys(fullDict, incompleteDict): + '''Given the original (fullDict) strings dictionary, this returns a list of the keys that no longer exist on the + full dictionary but still exist on the incompleteDict''' + leftOverKeys = [] + for k in incompleteDict: + if k not in fullDict: + leftOverKeys.append(k) + return leftOverKeys + @staticmethod def readXML(fileName): result={} - global PATTERN - f=open(fileName,'r') - line = f.readline() - while line!="": - matcher = PATTERN.search(line) - if matcher != None: - result[matcher.group(1)]=matcher.group(2) - line = f.readline() - f.close() + tree = ET.parse(fileName) + root = tree.getroot() + for string in root.findall('string'): + if 'translatable' not in string.attrib or string.attrib['translatable'] != 'False': + result[string.attrib['name']] = string.text return result def getStringsFilePaths(): '''Gets all the relative paths to strings.xml files in a list.''' - p = os.popen("find . | grep strings.xml") + p = os.popen("find ./res | grep strings.xml") filesPath = p.readlines() p.close() return filesPath +def cleanLeftoverTranslations(filePath, leftOverKeys): + print("Cleaning " + str(len(leftOverKeys)) + " leftover translations in " + filePath + "\n") + print(leftOverKeys) + print("="*80) + + tree = ET.parse(filePath) + root = tree.getroot() + + for string in root.findall('string'): + if string.attrib['name'] in leftOverKeys: + root.remove(string) + + tmpFilePath = filePath + '.tmp' + tree.write(tmpFilePath, encoding='utf-8', xml_declaration=True) + os.rename(tmpFilePath, filePath) + def usage(): usage = """ ams - Android Missing Strings reporting tool. Usage: - ams [-l xx[,yy,zz...]] -o + ams [-l xx[,yy,zz...]] [--cleanleftovers] -o Options: -h --help Print this help @@ -83,30 +111,33 @@ def usage(): If this parameter is ommited, a report with every language file found will be created. - -o --oFile Specify the output file name for the PDF report + -o --oFile Specify the output file name for the report - Copyright (c) 2014 - The Mit License (MIT) + --cleanleftovers Removes left over translations on other language files + + Copyright (c) 2014-%d - The Mit License (MIT) Authors: Angel Leon Katay Santos - """ - print usage + """ % (gmtime()[0]) + print(usage) def main(argv): xmlFilePaths=[] outputFile = '' + cleanLeftovers = False try: - opts, args = getopt.getopt(argv, "ho:l:",["ofile=","lang="]) + opts, args = getopt.getopt(argv, "ho:l:",["ofile=","lang=","cleanleftovers"]) except getopt.GetoptError: - print "unhandled option" + print("unhandled option") usage() sys.exit(2) if len(opts) == 0: usage() - print "Error: missing parameters." + print("Error: missing parameters.") sys.exit(2) for opt, arg in opts: @@ -124,10 +155,12 @@ def main(argv): xmlFilePaths.append("./res/values-" + l + "/strings.xml") else: xmlFilePaths=["./res/values-" + arg + "/strings.xml"] + if opt in ("--cleanleftovers"): + cleanLeftovers = True if outputFile == '': usage() - print "Error: missing -o (output file) parameter." + print("Error: missing -o (output file) parameter.") sys.exit(2) if len(xmlFilePaths) == 0: @@ -136,33 +169,48 @@ def main(argv): xmlOriginalFile="./res/values/strings.xml" originalDict=MissingStringsReport.readXML(xmlOriginalFile) - pdf=FPDF() - pdf.add_page() + fd = open(outputFile,'w') + + # in case we're going to clean up, we don't need to re-calculate reports + # if we keep them in this dictionary (path -> MissingStringReport) + missingStringReports = {} for xmlFilePath in xmlFilePaths: filePath = xmlFilePath.strip() if filePath != xmlOriginalFile: report = MissingStringsReport(filePath, originalDict) - print "Generating report for " + filePath + "..." - pdf.set_font('Arial','B',12) - pdf.cell(100,10,"Language file: " + filePath,0,0) - pdf.cell(40,10,"%d%% completed " % (report.getCompletePercentage()),0,1,'R') - pdf.cell(40,10,"Missing elements: ",0,1) - - for missingKey in report.getMissingKeys(): - pdf.set_font('Arial','I',8) - tag = "%s" % (missingKey, originalDict[missingKey]) - pdf.multi_cell(190,10,tag,1,1) - - pdf.add_page() - - pdf.output(outputFile,'F') - print "\nSuccess: Android Missing String report ready at " + outputFile - + if cleanLeftovers: + missingStringReports[filePath] = report + print("Generating report for " + filePath + "...") + fd.write("Language file: " + filePath + "\n") + fd.write("%d%% completed " % (report.getCompletePercentage()) + "\n") + + if len(report.getMissingKeys()) > 0: + fd.write("Missing elements: \n\n") + for missingKey in report.getMissingKeys(): + tag = "%s\n" % (missingKey, originalDict[missingKey]) + fd.write(tag) + fd.write("========================================================================\n\n") + if len(report.getLeftoverKeys()) > 0: + fd.write("Leftover elements: \n\n") + for leftoverKey in report.getLeftoverKeys(): + tag = u"...\n" % (leftoverKey) + fd.write(tag) + fd.write("========================================================================\n") + fd.write("\n-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.\n\n"); + fd.close() + print("\nSuccess: Android Missing String report ready at " + outputFile) + + if cleanLeftovers: + print("") + print("="*80) + print("") + for xmlFilePath in xmlFilePaths: + filePath = xmlFilePath.strip() + if filePath != xmlOriginalFile: + report = missingStringReports[filePath] + cleanLeftoverTranslations(filePath, report.getLeftoverKeys()) if __name__ == '__main__': - main(sys.argv[1:]) - -