/* * Copyright 2004 - 2008 Christian Sprajc. All rights reserved. * * This file is part of PowerFolder. * * PowerFolder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * PowerFolder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PowerFolder. If not, see <http://www.gnu.org/licenses/>. * * $Id$ */ package de.dal33t.powerfolder.util; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Properties; /** * A Translation file cleaner * * @version $Revision: 1.3 $ */ public class CleanupTranslationFiles { /** A table of hex digits */ private static final char[] hexDigit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** * Removes the translation of the following keys to force retranslation */ private static final String[] RETRANSLATE = {}; private static final String headerText = "#\n# PowerFolder translation file\n" + "#\n" + "# Guide:\n" + "# Missing translations are marked with ## in front of the line\n" + "# You just need to translate the english text behind the =\n" + "# Then remove the ## in front of the line.\n" + "#\n" + "# Original English texts for existing translations are\n" + "# indicated with a proceeding #orig#.\n" + "# Please check if the meaning is still the same.\n" + "#\n" + "# Pro version translations start with the prefix 'pro.'\n" + "#\n" + "# There is also a guide on our offical webpage under\n" + "# http://www.powerfolder.com/development.html\n" + "#\n" + "# When you are completed please send your translation file to\n" + "# translation@powerfolder.com\n" + "#\n" + "# Thank you,\n" + "# Your PowerFolder Team\n" + "# http://www.powerfolder.com\n" + "#"; private static final String baseName = "src/etc/Translation"; private static final String outputName = "src/etc/Translation"; private Properties originals; private boolean deep = false; public void run() throws IOException { originals = loadTranslationFile(baseName + ".properties"); List<String> keys = new ArrayList<String>(); List<String> searchContents = new ArrayList<String>(); for (Object string : originals.keySet()) { String key = (String) string; keys.add(key); if (key.startsWith("action_")) { int i = key.lastIndexOf('.'); if (i > 0) { key = key.substring(0, i); } } if (!key.startsWith("transfer_mode.")) { searchContents.add(key); } } Collections.sort(keys); if (deep) { Collection<String> usedOriginals = new HashSet<String>(); findContent(searchContents, new File("src/main"), usedOriginals); findContent(searchContents, new File("../PowerFolder-Pro/src/pro"), usedOriginals); findContent(searchContents, new File( "../PowerFolder-Pro/src/server"), usedOriginals); System.out.println("Found " + usedOriginals.size() + "/" + keys.size() + ". " + (keys.size() - usedOriginals.size()) + " unused translations. Removing: "); Collection<String> unused = new HashSet<String>(searchContents); unused.removeAll(usedOriginals); for (String key : unused) { System.out.println(key); if (!keys.remove(key)) { boolean r = keys.remove(key + ".key") || keys.remove(key + ".label") || keys.remove(key + ".description"); if (!r) { System.err.println("Unable to remove " + key); } } } } // writeTranslationFile(null, keys, originals); List<Locale> supportedLocales = Translation.getSupportedLocales(); for (Locale locale : supportedLocales) { if (locale.getLanguage().equals("en")) { // Skip en continue; } Properties foreignProperties = loadTranslationFile(baseName + "_" + locale.getLanguage() + ".properties"); writeTranslationFile(locale.getLanguage(), keys, foreignProperties); } writeTranslationFile(null, keys, originals); System.out.println("Streamlined " + originals.size() + " translations. " + (supportedLocales.size() - 1) + " Translation files"); } private void writeTranslationFile(String theLocaleName, List<String> keys, Properties translations) throws IOException { boolean original; String localeName = theLocaleName; if (localeName == null) { localeName = ""; original = true; } else { localeName = "_" + localeName; original = false; } // Now write the stuff FileOutputStream fOut; try { fOut = new FileOutputStream(outputName + localeName + ".properties"); } catch (FileNotFoundException e) { throw new RuntimeException(e); } BufferedWriter out; try { out = new BufferedWriter(new OutputStreamWriter( new BufferedOutputStream(fOut), "8859_1")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } String lastPrefix = null; out.write(headerText); out.newLine(); int missing = 0; for (String key : keys) { String val = translations.getProperty(key); String originalVal = originals.getProperty(key); if (Arrays.asList(RETRANSLATE).contains(key)) { val = null; } boolean translationFound = val != null; if (val == null) { val = originalVal; } // Get prefix String prefix; int prefixEnd = key.indexOf('.'); if (prefixEnd >= 0) { prefix = key.substring(0, prefixEnd); } else { prefix = key; System.err.println("Check translation key: " + key); } try { if (!prefix.equals(lastPrefix)) { out.newLine(); } // if (translationFound) { // out.write("#orig#"); // out.write(saveConvert(key, true) + "=" // + saveConvert(originalVal, false)); // out.newLine(); // } if (!translationFound && !original) { out.write("##"); missing++; } out.write(saveConvert(key, true) + "=" + saveConvert(val, false)); out.newLine(); } catch (IOException e) { throw new RuntimeException(e); } lastPrefix = prefix; } try { out.flush(); out.close(); fOut.close(); } catch (IOException e) { throw new RuntimeException(e); } System.out.println("Result for " + localeName + ": " + missing + " missing translations."); } private void findContent(Collection<String> contents, File f, Collection<String> foundContents) throws FileNotFoundException { if (f.isDirectory()) { for (File innerFile : f.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith("java") || new File(dir, name).isDirectory(); } })) { findContent(contents, innerFile, foundContents); } } else { InputStream in = new FileInputStream(f); try { byte[] buf = StreamUtils.readIntoByteArray(in); String input = new String(buf, Convert.UTF8); for (String content : contents) { if (input.contains(content)) { foundContents.add(content); } } System.out.println(f.getAbsolutePath()); } catch (Exception e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { } } } } private Properties loadTranslationFile(String fileName) { ExtendedProperties props = new ExtendedProperties(); try { props.load(new FileInputStream(fileName)); } catch (FileNotFoundException e) { System.err.print(fileName); try { System.err.println(": Creating new translation file"); new File(fileName).createNewFile(); return props; } catch (IOException e1) { e1.printStackTrace(); } } catch (IOException e) { System.err.println(fileName); e.printStackTrace(); } catch (IllegalArgumentException e) { System.err.println(fileName); e.printStackTrace(); } return props; } /** * Copied from SUN Properties * * @param theString * @param escapeSpace * @return */ private String saveConvert(String theString, boolean escapeSpace) { int len = theString.length(); int bufLen = len * 2; if (bufLen < 0) { bufLen = Integer.MAX_VALUE; } StringBuffer outBuffer = new StringBuffer(bufLen); for (int x = 0; x < len; x++) { char aChar = theString.charAt(x); // Handle common case first, selecting largest block that // avoids the specials below if ((aChar > 61) && (aChar < 127)) { if (aChar == '\\') { outBuffer.append('\\'); outBuffer.append('\\'); continue; } outBuffer.append(aChar); continue; } switch (aChar) { case ' ' : if (x == 0 || escapeSpace) outBuffer.append('\\'); outBuffer.append(' '); break; case '\t' : outBuffer.append('\\'); outBuffer.append('t'); break; case '\n' : outBuffer.append('\\'); outBuffer.append('n'); break; case '\r' : outBuffer.append('\\'); outBuffer.append('r'); break; case '\f' : outBuffer.append('\\'); outBuffer.append('f'); break; case '=' : // Fall through case ':' : // Fall through case '#' : // Fall through case '!' : outBuffer.append('\\'); outBuffer.append(aChar); break; default : if ((aChar < 0x0020) || (aChar > 0x007e)) { outBuffer.append('\\'); outBuffer.append('u'); outBuffer.append(toHex((aChar >> 12) & 0xF)); outBuffer.append(toHex((aChar >> 8) & 0xF)); outBuffer.append(toHex((aChar >> 4) & 0xF)); outBuffer.append(toHex(aChar & 0xF)); } else { outBuffer.append(aChar); } } } return outBuffer.toString(); } /** * Convert a nibble to a hex character * * @param nibble * the nibble to convert. */ private static char toHex(int nibble) { return hexDigit[(nibble & 0xF)]; } /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { CleanupTranslationFiles instance = new CleanupTranslationFiles(); instance.run(); } }