// run: java tools/VerifyTranslatedStringFormatting.java // Reads all localized strings and makes sure they contain valid formatting placeholders matching the original English strings to avoid crashes. import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; import java.util.*; import java.util.regex.*; public class VerifyTranslatedStringFormatting{ // %[argument_index$][flags][width][.precision][t]conversion private static final String formatSpecifier="%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])"; private static final Pattern fsPattern=Pattern.compile(formatSpecifier); private static HashMap> placeholdersInStrings=new HashMap<>(); private static int errorCount=0; public static void main(String[] args) throws Exception{ DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(false); DocumentBuilder builder=factory.newDocumentBuilder(); Document doc; try(FileInputStream in=new FileInputStream("mastodon/src/main/res/values/strings.xml")){ doc=builder.parse(in); } NodeList list=doc.getDocumentElement().getChildNodes(); // why does this stupid NodeList thing exist at all? for(int i=0;i placeholders=new ArrayList<>(); Matcher matcher=fsPattern.matcher(value); while(matcher.find()){ placeholders.add(matcher.group()); } placeholdersInStrings.put(name, placeholders); } } for(File file:new File("mastodon/src/main/res").listFiles()){ if(file.getName().startsWith("values-")){ File stringsXml=new File(file, "strings.xml"); if(stringsXml.exists()){ processFile(stringsXml); } } } if(errorCount>0){ System.err.println("Found "+errorCount+" problems in localized strings"); System.exit(1); } } private static void processFile(File file) throws Exception{ DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(false); DocumentBuilder builder=factory.newDocumentBuilder(); Document doc; try(FileInputStream in=new FileInputStream(file)){ doc=builder.parse(in); } NodeList list=doc.getDocumentElement().getChildNodes(); for(int i=0;i placeholders){ if(placeholders==null) return true; for(String placeholder:placeholders){ if(placeholder.equals("%,d")){ // %,d and %d are interchangeable but %,d provides nicer formatting if(!str.contains(placeholder) && !str.contains("%d")) return false; }else if(!str.contains(placeholder)){ return false; } } return true; } }