Java tutorial
/* * Copyright 2015 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.apiman.tools.i18n; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import org.apache.commons.io.FileUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import org.jsoup.select.Elements; /** * Used to scan all of the UI templates looking for strings that require * translation. The output from this scanner is a messages.properties file with * all of the strings that require translation. This should then be compared * with the messages.properties file included in the API Manager UI project to * see if anything is missing. * * TODO: integrate this into a maven plugin of some kind to automatically detect * missing strings during the build */ @SuppressWarnings("nls") public class TemplateScanner { private static final Set<String> TRANSLATABLE_ATTRIBUTES = new HashSet<>(); static { TRANSLATABLE_ATTRIBUTES.add("title"); TRANSLATABLE_ATTRIBUTES.add("placeholder"); } public static void main(String[] args) throws IOException { if (args == null || args.length != 1) { System.out.println("Template directory not provided (no path provided)."); System.exit(1); } File templateDir = new File(args[0]); if (!templateDir.isDirectory()) { System.out.println("Template directory not provided (provided path is not a directory)."); System.exit(1); } if (!new File(templateDir, "dash.html").isFile()) { System.out.println("Template directory not provided (dash.html not found)."); System.exit(1); } File outputDir = new File(templateDir, "../../../../../../tools/i18n/target"); if (!outputDir.isDirectory()) { System.out.println("Output directory not found: " + outputDir); System.exit(1); } File outputFile = new File(outputDir, "scanner-messages.properties"); if (outputFile.isFile() && !outputFile.delete()) { System.out.println("Couldn't delete the old messages.properties: " + outputFile); System.exit(1); } System.out.println("Starting scan."); System.out.println("Scanning template directory: " + templateDir.getAbsolutePath()); String[] extensions = { "html", "include" }; Collection<File> files = FileUtils.listFiles(templateDir, extensions, true); TreeMap<String, String> strings = new TreeMap<>(); for (File file : files) { System.out.println("\tScanning file: " + file); scanFile(file, strings); } outputMessages(strings, outputFile); System.out.println("Scan complete. Scanned " + files.size() + " files and discovered " + strings.size() + " translation strings."); } /** * Scan the given html template using jsoup and find all strings that require translation. This is * done by finding all elements with a "apiman-i18n-key" attribute. * @param file * @param strings * @throws IOException */ private static void scanFile(File file, TreeMap<String, String> strings) throws IOException { Document doc = Jsoup.parse(file, "UTF-8"); // First, scan for elements with the 'apiman-i18n-key' attribute. These require translating. Elements elements = doc.select("*[apiman-i18n-key]"); for (Element element : elements) { String i18nKey = element.attr("apiman-i18n-key"); boolean isNecessary = false; // Process the element text (if the element has no children) if (strings.containsKey(i18nKey)) { if (hasNoChildren(element)) { isNecessary = true; String elementVal = element.text(); if (elementVal.trim().length() > 0 && !elementVal.contains("{{")) { String currentValue = strings.get(i18nKey); if (!currentValue.equals(elementVal)) { throw new IOException("Duplicate i18n key found with different default values. Key=" + i18nKey + " Value1=" + elementVal + " Value2=" + currentValue); } } } } else { if (hasNoChildren(element)) { String elementVal = element.text(); if (elementVal.trim().length() > 0 && !elementVal.contains("{{")) { isNecessary = true; strings.put(i18nKey, elementVal); } } } // Process the translatable attributes for (String tattr : TRANSLATABLE_ATTRIBUTES) { if (element.hasAttr(tattr)) { String attrValue = element.attr(tattr); if (attrValue.contains("{{")) { continue; } String attrI18nKey = i18nKey + '.' + tattr; String currentAttrValue = strings.get(attrI18nKey); if (currentAttrValue == null) { isNecessary = true; strings.put(attrI18nKey, attrValue); } else if (!currentAttrValue.equals(attrValue)) { throw new IOException( "Duplicate i18n key found with different default values (for attribute '" + tattr + "'). Key=" + attrI18nKey + " Value1=" + attrValue + " Value2=" + currentAttrValue); } else { isNecessary = true; } } } if (!isNecessary) { throw new IOException("Detected an unnecessary apiman-i18n-key attribute in file '" + file.getName() + "' on element: " + element); } } // Next, scan all elements to see if the element *should* be marked for translation elements = doc.select("*"); for (Element element : elements) { if (element.hasAttr("apiman-i18n-key") || element.hasAttr("apiman-i18n-skip")) { continue; } if (hasNoChildren(element)) { String value = element.text(); if (value != null && value.trim().length() > 0) { if (!value.contains("{{")) { throw new IOException("Found an element in '" + file.getName() + "' that should be translated: " + element); } } } } // Next scan elements with a translatable attribute and fail if any of those elements // are missing the apiman-i18n-key attribute. for (String tattr : TRANSLATABLE_ATTRIBUTES) { elements = doc.select("*[" + tattr + "]"); for (Element element : elements) { if (element.hasAttr("apiman-i18n-key") || element.hasAttr("apiman-i18n-skip") || element.attr(tattr).contains("{{")) { continue; } else { throw new IOException("In template '" + file.getName() + "', found an element with a '" + tattr + "' attribute but missing 'apiman-i18n-key': " + element); } } } } /** * @param element * @return true if the element doesn't have any child elements */ private static boolean hasNoChildren(Element element) { List<Node> childNodes = element.childNodes(); for (Node node : childNodes) { if (node instanceof Element) { return false; } } return true; } /** * Output the sorted map of strings to the specified output file. * @param strings * @param outputFile * @throws FileNotFoundException */ private static void outputMessages(TreeMap<String, String> strings, File outputFile) throws FileNotFoundException { PrintWriter writer = new PrintWriter(new FileOutputStream(outputFile)); for (Entry<String, String> entry : strings.entrySet()) { String key = entry.getKey(); String val = entry.getValue(); writer.append(key); writer.append('='); writer.append(val); writer.append("\n"); } writer.flush(); writer.close(); } }