From f7f9c8059d85efd8380847aa72a6535d0a22d30e Mon Sep 17 00:00:00 2001 From: gubatron Date: Sun, 20 Jul 2014 02:55:47 -0400 Subject: [PATCH 01/11] no pdf output --- README.md | 6 +++--- ams | 20 ++++++++------------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index e761a4c..9506e0c 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ **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. ## 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. @@ -29,7 +29,7 @@ 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 text report ``` ## License diff --git a/ams b/ams index 5b40957..9b7ccf3 100755 --- a/ams +++ b/ams @@ -3,7 +3,6 @@ import re import os import sys import getopt -from fpdf import FPDF PATTERN=re.compile(r'(.*?)', re.I | re.MULTILINE) @@ -83,7 +82,7 @@ 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) @@ -136,8 +135,7 @@ def main(argv): xmlOriginalFile="./res/values/strings.xml" originalDict=MissingStringsReport.readXML(xmlOriginalFile) - pdf=FPDF() - pdf.add_page() + fd = open(outputFile,'w') for xmlFilePath in xmlFilePaths: filePath = xmlFilePath.strip() @@ -145,19 +143,17 @@ def main(argv): 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) + fd.write("Language file: " + filePath + "\n") + fd.write("%d%% completed " % (report.getCompletePercentage()) + "\n") + fd.write("Missing elements: " + "\n\n") for missingKey in report.getMissingKeys(): - pdf.set_font('Arial','I',8) tag = "%s" % (missingKey, originalDict[missingKey]) - pdf.multi_cell(190,10,tag,1,1) + fd.write(tag+"\n") - pdf.add_page() + fd.write("========================================================================\n\n") - pdf.output(outputFile,'F') + fd.close() print "\nSuccess: Android Missing String report ready at " + outputFile From 33b69987ee239b61af17ac252a2a5c0c3b9c2a33 Mon Sep 17 00:00:00 2001 From: gubatron Date: Thu, 22 Oct 2015 14:29:33 -0400 Subject: [PATCH 02/11] skip non translatable entries. --- ams | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ams b/ams index 9b7ccf3..6d8e602 100755 --- a/ams +++ b/ams @@ -48,9 +48,10 @@ class MissingStringsReport: f=open(fileName,'r') line = f.readline() while line!="": - matcher = PATTERN.search(line) - if matcher != None: - result[matcher.group(1)]=matcher.group(2) + if 'translatable="false"' not in line: + matcher = PATTERN.search(line) + if matcher != None: + result[matcher.group(1)]=matcher.group(2) line = f.readline() f.close() return result From 36e0bfbd3c3c14e6c6903445d20a9fe293ff0a88 Mon Sep 17 00:00:00 2001 From: gubatron Date: Mon, 4 Jan 2016 11:13:50 -0500 Subject: [PATCH 03/11] match CDATA --- ams | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ams b/ams index 6d8e602..4313ab5 100755 --- a/ams +++ b/ams @@ -4,7 +4,7 @@ import os import sys import getopt -PATTERN=re.compile(r'(.*?)', re.I | re.MULTILINE) +PATTERN=re.compile(r'(.*?)', re.I | re.MULTILINE) class MissingStringsReport: filePath = None From fe5f4663fd96e34fe7cb0d76af1e83d1f4d82f5f Mon Sep 17 00:00:00 2001 From: gubatron Date: Wed, 30 Nov 2016 14:53:32 -0700 Subject: [PATCH 04/11] report now includes left over keys that should be deleted --- ams | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/ams b/ams index 4313ab5..50f89b5 100755 --- a/ams +++ b/ams @@ -19,12 +19,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 @@ -41,6 +46,16 @@ 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={} @@ -146,20 +161,24 @@ def main(argv): print "Generating report for " + filePath + "..." fd.write("Language file: " + filePath + "\n") fd.write("%d%% completed " % (report.getCompletePercentage()) + "\n") - fd.write("Missing elements: " + "\n\n") - - for missingKey in report.getMissingKeys(): - tag = "%s" % (missingKey, originalDict[missingKey]) - fd.write(tag+"\n") - - fd.write("========================================================================\n\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 = "...\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 __name__ == '__main__': - main(sys.argv[1:]) From 575d33de52d25abfc33763324f28f3417f76d1f8 Mon Sep 17 00:00:00 2001 From: gubatron Date: Thu, 3 Aug 2017 09:56:25 -0600 Subject: [PATCH 05/11] prepare report only for the immediate ./res folder, otherwise report gets too long if there's multiple android projects in the same repository --- ams | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ams b/ams index 50f89b5..71a307f 100755 --- a/ams +++ b/ams @@ -74,7 +74,7 @@ class MissingStringsReport: 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 From d6b645adc087ccb0e45d6ba7a2bdf0172027ebb5 Mon Sep 17 00:00:00 2001 From: gubatron Date: Thu, 3 Aug 2017 12:30:48 -0600 Subject: [PATCH 06/11] new --cleanleftovers option deletes translations no longer used on main string.xml file from other translations --- ams | 55 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/ams b/ams index 71a307f..5623f95 100755 --- a/ams +++ b/ams @@ -3,6 +3,7 @@ import re import os import sys import getopt +from time import gmtime PATTERN=re.compile(r'(.*?)', re.I | re.MULTILINE) @@ -79,12 +80,35 @@ def getStringsFilePaths(): p.close() return filesPath +def cleanLeftoverTranslations(filePath, leftOverKeys): + print "Cleaning " + str(len(leftOverKeys)) + " leftover translations in " + filePath + "\n" + print leftOverKeys + print "="*80 + + f = open(filePath, 'r') + lines = [] + for line in f: + lines.append(line) + f.close() + + f = open(filePath, 'w') + for line in lines: + matcher = PATTERN.search(line) + if matcher == None: + f.write(line) + else: + key = matcher.group(1) + if key not in leftOverKeys: + f.write(line) + + f.close() + 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 @@ -100,20 +124,23 @@ def usage(): -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 - """ + """ % (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" usage() @@ -139,6 +166,8 @@ 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() @@ -153,11 +182,17 @@ def main(argv): 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) + if cleanLeftovers: + missingStringReports[filePath] = report print "Generating report for " + filePath + "..." fd.write("Language file: " + filePath + "\n") fd.write("%d%% completed " % (report.getCompletePercentage()) + "\n") @@ -178,7 +213,15 @@ def main(argv): 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:]) - - From 078d693deb98e1f88003d370ac657c6f7f739f3d Mon Sep 17 00:00:00 2001 From: gubatron Date: Thu, 3 Aug 2017 12:32:40 -0600 Subject: [PATCH 07/11] readme update --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9506e0c..cce519f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ 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+ @@ -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 text 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/) From f7c071de048744344266f0f9d2af3c1c2e589a35 Mon Sep 17 00:00:00 2001 From: Angel Leon Date: Thu, 20 Sep 2018 22:59:42 -0600 Subject: [PATCH 08/11] authors --- ams | 1 + 1 file changed, 1 insertion(+) diff --git a/ams b/ams index 5623f95..6b3f9a5 100755 --- a/ams +++ b/ams @@ -1,4 +1,5 @@ #!/usr/bin/env python +# authors: Angel Leon (@gubatron), Katay Santos (@cateye) import re import os import sys From cd9a8d7a73c090cd16138c416d3024d2f4033a00 Mon Sep 17 00:00:00 2001 From: Angel Leon Date: Wed, 19 Jun 2024 15:16:27 -0600 Subject: [PATCH 09/11] python3 support --- ams | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ams b/ams index 6b3f9a5..2edcdc3 100755 --- a/ams +++ b/ams @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # authors: Angel Leon (@gubatron), Katay Santos (@cateye) import re import os @@ -82,9 +82,9 @@ def getStringsFilePaths(): return filesPath def cleanLeftoverTranslations(filePath, leftOverKeys): - print "Cleaning " + str(len(leftOverKeys)) + " leftover translations in " + filePath + "\n" - print leftOverKeys - print "="*80 + print("Cleaning " + str(len(leftOverKeys)) + " leftover translations in " + filePath + "\n") + print(leftOverKeys) + print("="*80) f = open(filePath, 'r') lines = [] @@ -134,7 +134,7 @@ def usage(): Katay Santos """ % (gmtime()[0]) - print usage + print(usage) def main(argv): xmlFilePaths=[] @@ -143,13 +143,13 @@ def main(argv): try: 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: @@ -172,7 +172,7 @@ def main(argv): if outputFile == '': usage() - print "Error: missing -o (output file) parameter." + print("Error: missing -o (output file) parameter.") sys.exit(2) if len(xmlFilePaths) == 0: @@ -194,7 +194,7 @@ def main(argv): report = MissingStringsReport(filePath, originalDict) if cleanLeftovers: missingStringReports[filePath] = report - print "Generating report for " + filePath + "..." + print("Generating report for " + filePath + "...") fd.write("Language file: " + filePath + "\n") fd.write("%d%% completed " % (report.getCompletePercentage()) + "\n") @@ -212,12 +212,12 @@ def main(argv): fd.write("========================================================================\n") fd.write("\n-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.\n\n"); fd.close() - print "\nSuccess: Android Missing String report ready at " + outputFile + print("\nSuccess: Android Missing String report ready at " + outputFile) if cleanLeftovers: - print "" - print "="*80 - print "" + print("") + print("="*80) + print("") for xmlFilePath in xmlFilePaths: filePath = xmlFilePath.strip() if filePath != xmlOriginalFile: From 3bd5fe80891807067084f8061277e0a33779cfcb Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Tue, 18 Jun 2024 01:28:38 +0300 Subject: [PATCH 10/11] fix parsing multiline tags --- ams | 50 +++++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/ams b/ams index 2edcdc3..9a9d83b 100755 --- a/ams +++ b/ams @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -# authors: Angel Leon (@gubatron), Katay Santos (@cateye) +# authors: Angel Leon (@gubatron), Katay Santos (@cateye), Bohdan Horbeshko (@bodqhrohro) + import re import os import sys import getopt from time import gmtime - -PATTERN=re.compile(r'(.*?)', re.I | re.MULTILINE) +import xml.etree.ElementTree as ET class MissingStringsReport: filePath = None @@ -61,16 +61,11 @@ class MissingStringsReport: @staticmethod def readXML(fileName): result={} - global PATTERN - f=open(fileName,'r') - line = f.readline() - while line!="": - if 'translatable="false"' not in 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 @@ -86,23 +81,16 @@ def cleanLeftoverTranslations(filePath, leftOverKeys): print(leftOverKeys) print("="*80) - f = open(filePath, 'r') - lines = [] - for line in f: - lines.append(line) - f.close() + tree = ET.parse(filePath) + root = tree.getroot() - f = open(filePath, 'w') - for line in lines: - matcher = PATTERN.search(line) - if matcher == None: - f.write(line) - else: - key = matcher.group(1) - if key not in leftOverKeys: - f.write(line) + for string in root.findall('string'): + if string.attrib['name'] in leftOverKeys: + root.remove(string) - f.close() + tmpFilePath = filePath + '.tmp' + tree.write(tmpFilePath, encoding='utf-8', xml_declaration=True) + os.rename(tmpFilePath, filePath) def usage(): usage = """ @@ -202,13 +190,13 @@ def main(argv): fd.write("Missing elements: \n\n") for missingKey in report.getMissingKeys(): tag = "%s\n" % (missingKey, originalDict[missingKey]) - fd.write(tag) + fd.write(tag.encode('utf-8', 'ignore')) fd.write("========================================================================\n\n") if len(report.getLeftoverKeys()) > 0: fd.write("Leftover elements: \n\n") for leftoverKey in report.getLeftoverKeys(): - tag = "...\n" % (leftoverKey) - fd.write(tag) + tag = u"...\n" % (leftoverKey) + fd.write(tag.encode('utf-8', 'ignore')) fd.write("========================================================================\n") fd.write("\n-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.\n\n"); fd.close() From 02c15499aa1a0dbfd20b960bdb70da843a8a070e Mon Sep 17 00:00:00 2001 From: Angel Leon Date: Wed, 19 Jun 2024 15:28:12 -0600 Subject: [PATCH 11/11] encoding to utf8 screws up the output --- ams | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ams b/ams index 9a9d83b..7ef27fc 100755 --- a/ams +++ b/ams @@ -190,13 +190,13 @@ def main(argv): fd.write("Missing elements: \n\n") for missingKey in report.getMissingKeys(): tag = "%s\n" % (missingKey, originalDict[missingKey]) - fd.write(tag.encode('utf-8', 'ignore')) + 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.encode('utf-8', 'ignore')) + fd.write(tag) fd.write("========================================================================\n") fd.write("\n-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.\n\n"); fd.close()