Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindMessagesToBind.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindMessagesToBind.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindMessagesToBind.java	(revision 1276)
@@ -10,7 +10,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.txm.rcp.translate.i18n.PluginMessages;
+import org.txm.rcp.translate.i18n.PluginMessagesManager;
 import org.txm.rcp.translate.i18n.WorkspaceMessagesManager;
 import org.txm.utils.DeleteDir;
 import org.txm.utils.io.IOUtils;
@@ -130,7 +130,7 @@
 
 	public void run() throws UnsupportedEncodingException, FileNotFoundException, IOException {
 		WorkspaceMessagesManager wmm = new WorkspaceMessagesManager();
-		LinkedHashMap<File, PluginMessages> h = wmm.getPluginMessages();
+		LinkedHashMap<File, PluginMessagesManager> h = wmm.getPluginMessages();
 
 		writer = IOUtils.getWriter(report);
 		
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/NormalizeKeys.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/NormalizeKeys.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/NormalizeKeys.java	(revision 1276)
@@ -5,7 +5,7 @@
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 
-import org.txm.rcp.translate.i18n.PluginMessages;
+import org.txm.rcp.translate.i18n.PluginMessagesManager;
 import org.txm.rcp.translate.i18n.WorkspaceMessagesManager;
 import org.txm.utils.BiHashMap;
 
@@ -45,7 +45,7 @@
 	 * @param pmManager the PluginMessages to update
 	 * @return the number of modifications done
 	 */
-	public int normalize(PluginMessages pmManager)	{
+	public int normalize(PluginMessagesManager pmManager)	{
 
 		BiHashMap<String, String> messages = pmManager.getMessagesForDefaultLang();
 
@@ -231,7 +231,7 @@
 		NormalizeKeys keysNormalizer = new NormalizeKeys(false);
 
 		WorkspaceMessagesManager wmm = new WorkspaceMessagesManager();
-		for (PluginMessages pm : wmm.getPluginMessages().values()) {
+		for (PluginMessagesManager pm : wmm.getPluginMessages().values()) {
 			keysNormalizer.normalize(pm);
 		}
 
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/RemoveUnusedKeys.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/RemoveUnusedKeys.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/RemoveUnusedKeys.java	(revision 1276)
@@ -8,7 +8,7 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 
-import org.txm.rcp.translate.i18n.PluginMessages;
+import org.txm.rcp.translate.i18n.PluginMessagesManager;
 import org.txm.rcp.translate.i18n.WorkspaceMessagesManager;
 
 /**
@@ -23,9 +23,10 @@
 		WorkspaceMessagesManager wmm = new WorkspaceMessagesManager();
 		
 		TreeMap<String, TreeSet<File>> usedKeys = wmm.getUsedKeysFilesIndex();
-		System.out.println("all keys: "+usedKeys.keySet());
+		//System.out.println("all keys: "+usedKeys.keySet());
 		int total = 0;
-		for (PluginMessages pm : wmm.getPluginMessages().values()) {
+		for (PluginMessagesManager pm : wmm.getPluginMessages().values()) {
+			//System.out.println("pm keys: "+pm.getProjectDirectory()+" "+pm.getUsedKeysFilesIndex().keySet());
 			int n = 0;
 			ArrayList<String> keys = new ArrayList<String>(pm.getMessageKeys());
 //			System.out.println("N keys="+keys.size());
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ImportFromOneFile.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ImportFromOneFile.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ImportFromOneFile.java	(revision 1276)
@@ -8,7 +8,7 @@
 import java.util.LinkedHashMap;
 import java.util.Properties;
 
-import org.txm.rcp.translate.i18n.PluginMessages;
+import org.txm.rcp.translate.i18n.PluginMessagesManager;
 import org.txm.rcp.translate.i18n.WorkspaceMessagesManager;
 import org.txm.utils.BiHashMap;
 import org.txm.utils.io.IOUtils;
@@ -25,7 +25,7 @@
 	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
 
 		WorkspaceMessagesManager wmm = new WorkspaceMessagesManager();
-		LinkedHashMap<File, PluginMessages> h = wmm.getPluginMessages();
+		LinkedHashMap<File, PluginMessagesManager> h = wmm.getPluginMessages();
 
 		String langs[] = {"", "_fr", "_ru"};
 
@@ -47,7 +47,7 @@
 
 			for (File project : h.keySet()) {
 
-				PluginMessages messages = h.get(project);
+				PluginMessagesManager messages = h.get(project);
 
 				//messages.put(mergedLang, key, value);
 				for (String key : messages.getMessageKeys()) {
@@ -69,7 +69,7 @@
 			total_update += update;
 		}
 		
-		for (PluginMessages messages : h.values()) {
+		for (PluginMessagesManager messages : h.values()) {
 			//messages.saveChanges();
 		}
 		
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/RemoveUnusedTranslations.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/RemoveUnusedTranslations.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/RemoveUnusedTranslations.java	(revision 1276)
@@ -8,20 +8,20 @@
 import java.util.LinkedHashSet;
 import java.util.TreeSet;
 
-import org.txm.rcp.translate.i18n.PluginMessages;
+import org.txm.rcp.translate.i18n.PluginMessagesManager;
 import org.txm.rcp.translate.i18n.WorkspaceMessagesManager;
 import org.txm.utils.BiHashMap;
 
 public class RemoveUnusedTranslations {
 	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
 		WorkspaceMessagesManager wmm = new WorkspaceMessagesManager();
-		LinkedHashMap<File, PluginMessages> h = wmm.getPluginMessages();
+		LinkedHashMap<File, PluginMessagesManager> h = wmm.getPluginMessages();
 
 		String langs[] = {"", "_fr", "_ru"};
 
 		for (File project : h.keySet()) {
 
-			PluginMessages messages = h.get(project);
+			PluginMessagesManager messages = h.get(project);
 			TreeSet<String> keys = messages.getMessageKeys();
 			for (String lang : langs) {
 				BiHashMap<String, String> lmessages = messages.getMessagesForLang(lang);
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ExportToOneFile.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ExportToOneFile.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ExportToOneFile.java	(revision 1276)
@@ -8,7 +8,7 @@
 import java.util.LinkedHashMap;
 import java.util.Properties;
 
-import org.txm.rcp.translate.i18n.PluginMessages;
+import org.txm.rcp.translate.i18n.PluginMessagesManager;
 import org.txm.rcp.translate.i18n.WorkspaceMessagesManager;
 import org.txm.utils.BiHashMap;
 import org.txm.utils.io.IOUtils;
@@ -25,7 +25,7 @@
 
 	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
 		WorkspaceMessagesManager wmm = new WorkspaceMessagesManager();
-		LinkedHashMap<File, PluginMessages> h = wmm.getPluginMessages();
+		LinkedHashMap<File, PluginMessagesManager> h = wmm.getPluginMessages();
 
 		HashMap<String, Properties> mergedProps = new HashMap<String, Properties>();
 		String langs[] = {"", "_fr", "_ru"};
@@ -38,7 +38,7 @@
 		System.out.println("Fetching messages...");
 		for (File project : h.keySet()) {
 			
-			PluginMessages messages = h.get(project);
+			PluginMessagesManager messages = h.get(project);
 			//System.out.println(" "+messages.getMessageFullName()+" -> "+messages.getMessageKeys().size());
 			
 			for (String lang : langs) {
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/PluginMessages.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/PluginMessages.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/PluginMessages.java	(revision 1276)
@@ -1,782 +0,0 @@
-package org.txm.rcp.translate.i18n;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang.StringUtils;
-import org.eclipse.osgi.util.NLS;
-import org.txm.utils.BiHashMap;
-import org.txm.utils.io.IOUtils;
-
-/**
- *  
- * Check any missing key in both of messages.properties and Message.java files
- *
- * @author mdecorde
- * @author sjacquot
- *
- */
-public class PluginMessages {
-	/**
-	 * Files encoding.
-	 */
-	public static String ENCODING = "UTF-8";
-
-	/**
-	 * Debug state.
-	 */
-	protected boolean debug;
-
-	/**
-	 * Project root directory.
-	 */
-	protected File projectDirectory;
-
-	/**
-	 * Java message file, eg.: TXMCoreMessages.java, ConcordanceUIMessages.java, etc.
-	 */
-	protected File javaMessageFile;
-
-	/**
-	 * Source files of the project.
-	 */
-	protected ArrayList<File> srcFiles;
-
-	/**
-	 * Extensions to use when creating the source files list.
-	 */
-	protected String[] srcFilesExtensions = new String[] {"java", "groovy"};
-
-
-	/**
-	 * TODO Global REGEX containing all keys to later use for search in files.
-	 */
-	protected Pattern keysGlobalREGEX = Pattern.compile("...");
-
-	/**
-	 * Index of the keys used and their associated files.
-	 */
-	protected TreeMap<String, TreeSet<File>> usedKeysFilesIndex;
-
-	/**
-	 * the XXXMessages.java messages keys
-	 */
-	TreeSet<String> messageKeys = new TreeSet<String>();
-
-	/**
-	 * plugin.xml file -> contains eventually keys
-	 */
-	protected File pluginXMLFile;
-
-	public File getPluginXMLFile() {
-		return pluginXMLFile;
-	}
-
-	public TreeSet<String> getOsgiKeys() {
-		return osgiKeys;
-	}
-
-	public TreeMap<File, BiHashMap<String, String>> getOsgiLangs() {
-		return osgiLangs;
-	}
-
-	/**
-	 * the OSGI-INF/l10n/bundle[_lang].properties OSGI message keys
-	 */
-	TreeSet<String> osgiKeys = new TreeSet<String>();
-
-	/**
-	 * The OSGI messages stored by properties file
-	 */
-	TreeMap<File, BiHashMap<String, String>> osgiLangs = new TreeMap<File, BiHashMap<String, String>>();
-
-	/**
-	 * Stores the key modifications for further saves in the source files
-	 */
-	HashMap<String, String> keyModifications = new HashMap<String, String>();
-
-	/**
-	 * The messages stored by properties file
-	 */
-	TreeMap<File, BiHashMap<String, String>> langs = new TreeMap<File, BiHashMap<String, String>>();
-
-	/**
-	 * the properties files langs
-	 */
-	BiHashMap<File, String> file2lang = new BiHashMap<File, String>();
-
-	/**
-	 * 
-	 * @param projectDirectory
-	 * @param javaMessageFile
-	 * @throws UnsupportedEncodingException
-	 * @throws FileNotFoundException
-	 * @throws IOException
-	 */
-	public PluginMessages(File projectDirectory) throws UnsupportedEncodingException, FileNotFoundException, IOException {
-		this(projectDirectory, findMessageFile(projectDirectory), false);
-	}
-
-	public static File findMessageFile(File projectDirectory2) {
-		File messagesPackageDir = new File(projectDirectory2, "src/"+projectDirectory2.getName().replaceAll("\\.", "/")+"/messages");
-
-		if (!messagesPackageDir.exists()) {
-			messagesPackageDir = new File(projectDirectory2, "src/java/"+projectDirectory2.getName().replaceAll("\\.", "/")+"/messages");
-		}
-
-		if (!messagesPackageDir.exists()) {
-			messagesPackageDir = new File(projectDirectory2, "src/main/java/"+projectDirectory2.getName().replaceAll("\\.", "/")+"/messages");
-		}
-
-		if (!messagesPackageDir.exists()) {
-			return null;
-		}
-
-		for (File messagesJavaFile : messagesPackageDir.listFiles()) {
-			if (messagesJavaFile.isDirectory()) continue;
-			if (!messagesJavaFile.getName().endsWith("Messages.java")) continue;
-			return messagesJavaFile;
-		}
-		return null;
-	}
-
-	/**
-	 * 
-	 * @param projectDirectory
-	 * @param javaMessageFile
-	 * @throws UnsupportedEncodingException
-	 * @throws FileNotFoundException
-	 * @throws IOException
-	 */
-	public PluginMessages(File projectDirectory, File javaMessageFile) throws UnsupportedEncodingException, FileNotFoundException, IOException {
-		this(projectDirectory, javaMessageFile, false);
-	}
-
-	/**
-	 * 
-	 * @param projectDirectory
-	 * @param javaMessageFile
-	 * @throws UnsupportedEncodingException
-	 * @throws FileNotFoundException
-	 * @throws IOException
-	 */
-	public PluginMessages(File projectDirectory, File javaMessageFile, boolean debug) throws UnsupportedEncodingException, FileNotFoundException, IOException {
-
-		this.javaMessageFile = javaMessageFile;
-		this.projectDirectory = projectDirectory;
-		this.pluginXMLFile = new File(projectDirectory, "plugin.xml");
-		this.srcFiles = new ArrayList<File>();
-		this.debug = debug;
-
-		if (debug) {
-			System.out.println("********************************************************************************************************************");
-			System.out.println("PluginMessages.PluginMessages(): project root directory = " + this.projectDirectory);
-			System.out.println("PluginMessages.PluginMessages(): java message file = " + this.javaMessageFile);
-		}
-		File[] propFiles = javaMessageFile.getParentFile().listFiles();
-		for (File propFile : propFiles) {
-			String name = propFile.getName();
-			if (!name.endsWith(".properties")) continue;
-			//messages.properties
-			//messages_xx.properties
-			String lang = name.substring(8, name.indexOf(".properties"));
-			langs.put(propFile, new BiHashMap<String, String>());
-			file2lang.put(propFile, lang);
-
-			BiHashMap<String, String> hash = langs.get(propFile);
-
-			Properties props1 = new Properties();
-			props1.load(IOUtils.getReader(propFile, ENCODING));
-			for (Object k : props1.keySet()) {
-				hash.put(k.toString(), props1.get(k).toString());
-			}
-		}
-
-		if (this.javaMessageFile != null) {
-			BufferedReader reader2 = new BufferedReader(new InputStreamReader(new FileInputStream(javaMessageFile), ENCODING));
-			String line2 = reader2.readLine();
-			while (line2 != null) {
-				line2 = line2.trim();
-				if (line2.startsWith("public static String") && line2.endsWith(";") && !line2.contains("=")) {
-					line2 = line2.substring(21, line2.length() -1);
-					messageKeys.add(line2);
-				}
-				line2 = reader2.readLine();
-			}
-			reader2.close();
-		}
-
-		// create the project source files list
-		this.createSourceFilesList();
-
-		//this.createKeysGlobalREGEX();
-		this.createUsedKeysFilesIndex();
-
-		// try loading OSGI messages 
-		if (pluginXMLFile.exists()) {
-			ArrayList<String> result = IOUtils.findWithGroup(pluginXMLFile, "\"%([^\"]+)\"");
-			osgiKeys.addAll(result);
-
-			File osgiInf = new File(projectDirectory, "OSGI-INF/l10n");
-			if (osgiInf.exists()) {
-				for (File propFile : osgiInf.listFiles()) {
-					if (propFile.getName().startsWith("bundle") && propFile.getName().endsWith(".properties")) {
-						Properties props = new Properties();
-						props.load(IOUtils.getReader(propFile));
-						osgiLangs.put(propFile, new BiHashMap<String, String>(props));
-					}
-				}
-			}
-//			System.out.println(osgiKeys);
-//			System.out.println(osgiLangs);
-		}
-
-		if(debug)	{
-			this.dumpUsedKeysFilesIndex();
-		}
-	}
-
-	//	/**
-	//	 * Creates a global REGEX containing all keys to later search in files.
-	//	 */
-	//	public void createKeysGlobalREGEX()	{
-	//		StringBuffer buffer = new StringBuffer();
-	//		for (String key : messageKeys) {
-	//			buffer.append("|" + key);
-	//		}
-	//		
-	//		// remove first pipe
-	//		buffer.deleteCharAt(0);
-	//		
-	//		this.keysGlobalREGEX = Pattern.compile("(" + buffer + ")");
-	//		
-	//		if(debug)	{
-	//			System.out.println("PluginMessages.createKeysGlobalREGEX(): REGEX created: " + this.keysGlobalREGEX + ".");
-	//		}
-	//		
-	//	}
-
-	/**
-	 * Creates/updates the index of the keys used and their associated files.
-	 * @throws IOException 
-	 */
-	public void createUsedKeysFilesIndex() throws IOException	{
-		this.usedKeysFilesIndex = new TreeMap<String, TreeSet<File>>();
-
-		//for (String key : messageKeys) {
-
-		//		if(debug) {
-		//			System.out.println("PluginMessages.createUsedKeysIndex(): looking for key " + this.getKeyFullName(key) + " in project source files...");
-		//		}
-
-		for (File file : this.srcFiles) {
-			if (!file.exists()) continue;
-			String lines = IOUtils.getText(file, PluginMessages.ENCODING);
-
-			//			for (String line : lines) {
-			//				//					if(line.contains(this.getKeyFullName(key)))	{
-			//				//						files.add(file);
-			//				//					};
-
-			Matcher m = WorkspaceMessagesManager.KEY_REGEX.matcher(lines);
-			while (m.find()) {
-				// Get the matching string
-				String key = m.group();
-				if (!this.usedKeysFilesIndex.containsKey(key)) {
-					this.usedKeysFilesIndex.put(key, new TreeSet<File>());
-				}
-				this.usedKeysFilesIndex.get(key).add(file);
-			}
-			//			}
-		}
-		//}
-	}
-
-
-	/**
-	 * Dumps in console the index of the keys used and their associated files.
-	 */
-	public void dumpUsedKeysFilesIndex()	{
-
-		System.out.println("PluginMessages.dumpUsedKeysIndex(): dumping used keys files index...");
-
-		for (Map.Entry<String, TreeSet<File>> entry : this.usedKeysFilesIndex.entrySet()) {
-			System.out.println("key: " + this.getKeyFullName(entry.getKey()));
-			if(entry.getValue().isEmpty())	{
-				System.out.println("   file(s): -not used in any file of the project " + this.getProjectDirectory() + "-");
-			}
-			else	{
-				for(File file : entry.getValue())	{
-					System.out.println("   file(s): " + file);
-				}
-			}
-		}
-	}
-
-	/***
-	 * @param key
-	 * @return all source files using the specified message key (except the main Java message file).
-	 */
-	public TreeSet<File> getFilesUsingKey(String key) {
-		if (usedKeysFilesIndex.containsKey(key)) {
-			return this.usedKeysFilesIndex.get(key);
-		} else {
-			return new TreeSet<File>();
-		}
-	}
-
-	/***
-	 * @param key local key name
-	 * @return all source files using the specified local message key (except the main Java message file).
-	 */
-	public TreeSet<File> getFilesUsingLocalKey(String key) {
-		return getFilesUsingKey(getKeyFullName(key));
-	}
-
-
-	/**
-	 * Returns the keys of all messages that are not used in the project itself.
-	 * @return a sorted list containing all unused keys
-	 */
-	public ArrayList<String> getUnusedKeys()	{
-		return getUnusedKeys(usedKeysFilesIndex, debug);
-	}
-
-	/**
-	 * @param usedKeysFilesIndex
-	 * 
-	 * @return a sorted list containing all unused keys of all messages that are not used in the used key files index.
-	 */
-	public static ArrayList<String> getUnusedKeys(TreeMap<String, TreeSet<File>> usedKeysFilesIndex, boolean debug)	{
-		ArrayList<String> unusedKeys = new ArrayList<String>();
-		for (Map.Entry<String, TreeSet<File>> entry : usedKeysFilesIndex.entrySet()) {
-			if(entry.getValue().isEmpty())	{
-
-				if(debug)	{
-					System.out.println("PluginMessages.getUnusedKeys(): unused key found: " + entry.getKey() + ".");
-				}
-
-				unusedKeys.add(entry.getKey());
-			}
-		}
-
-		Collections.sort(unusedKeys);
-		return unusedKeys;
-	}
-
-	/**
-	 * @param lang
-	 * @return the map of the messages keys and values for the specified language.
-	 */
-	public BiHashMap<String, String> getMessagesForLang(String lang) {
-		File p = new File(javaMessageFile.getParentFile(), "messages" + lang + ".properties");
-		return langs.get(p);
-	}
-
-	/**
-	 * @return the map of the messages keys and values for the main language.
-	 */
-	public BiHashMap<String, String> getMessagesForDefaultLang() {
-		return this.getMessagesForLang(""); //$NON-NLS-1$
-	}
-
-
-	public void put(String lang, String key, String message) {
-		messageKeys.add(key);
-
-		File p = new File(javaMessageFile.getParentFile(), "messages"+lang+".properties");
-
-		if (!langs.containsKey(p)) {
-			langs.put(p, new BiHashMap<String, String>());
-			file2lang.put(p, lang);
-		}
-
-		if (message == null) {
-			langs.get(p).removeByKey(key);
-		} else {
-			langs.get(p).put(key, message);
-		}
-	}
-
-	public String get(String lang, String key) {
-		File p = new File(javaMessageFile.getParentFile(), "messages"+lang+".properties");
-
-		if (!file2lang.containsKey(p)) return null;
-
-		return langs.get(p).get(key);
-	}
-
-	public TreeSet<String> getMessageKeys() {
-		return messageKeys;
-	}
-
-	public File getMessageFile() {
-		return javaMessageFile;
-	}
-
-	public String getMessageClassName() {
-		return javaMessageFile.getName().substring(0, javaMessageFile.getName().length()-5);
-	}
-
-	public String getMessageFullClassName() {
-		return javaMessageFile.getAbsolutePath().substring(javaMessageFile.getAbsolutePath().lastIndexOf("org/txm/"), javaMessageFile.getAbsolutePath().length()-5).replace("/", ".");
-	}
-
-	/**
-	 * @return the XXXMessages.java class name without the 'Messages' part
-	 */
-	public String getMessageName() {
-		return javaMessageFile.getName().substring(0, javaMessageFile.getName().length()-5-8);
-	}
-
-	public String getMessageFullName() {
-		return javaMessageFile.getAbsolutePath().substring(javaMessageFile.getAbsolutePath().lastIndexOf("org/txm/")+8, javaMessageFile.getAbsolutePath().length()-5-8).replace("/", ".");
-	}
-
-	/**
-	 * Returns the full name of the specified key including the Java message class name.
-	 * @param key
-	 * @return
-	 */
-	public String getKeyFullName(String key)	{
-		return this.getMessageClassName() + "." + key;
-	}
-
-	public static Collator col = Collator.getInstance(Locale.FRANCE);
-	public static Comparator comp = new Comparator<String>() {
-		@Override
-		public int compare(String arg0, String arg1) {
-			return col.compare(arg0, arg1);
-		}
-	};
-	static {
-		col.setStrength(Collator.TERTIARY);
-	}
-	public void saveChanges(boolean replaceFiles) throws IOException {
-
-		if (javaMessageFile == null) return;
-		
-		//Write prop File
-		for (File propFile : langs.keySet()) {
-
-			Properties props = new Properties() {
-				@Override
-				public synchronized Enumeration<Object> keys() {
-					TreeSet<Object> set = new TreeSet<Object>(comp);
-					set.addAll(super.keySet());
-					return Collections.enumeration(set);
-				}
-			};
-			BiHashMap<String, String> h = langs.get(propFile);
-			for (String k : h.getKeys()) {
-				props.setProperty(k, h.get(k));
-			}
-
-			File newPropFile = new File(propFile.getParentFile(), propFile.getName()+".new");
-			if (replaceFiles) {
-				propFile.delete();
-				newPropFile = propFile;
-			}
-			props.store(IOUtils.getWriter(newPropFile, ENCODING), "TXM messages generated by the PluginMessages class");
-		}
-
-		// write message File
-		File newJavaMessageFile = new File(javaMessageFile.getParentFile(), javaMessageFile.getName()+".new");
-		if (replaceFiles) {
-			javaMessageFile.delete();
-			newJavaMessageFile = javaMessageFile;
-		}
-
-		String className = getMessageClassName();
-		String name = getMessageName();
-		String classPackage = getMessageFullClassName().substring(0, getMessageFullClassName().length() - getMessageClassName().length() - 1);
-
-		PrintWriter out = IOUtils.getWriter(newJavaMessageFile, ENCODING);
-
-		// write start
-		out.println("package "+classPackage+";\n");
-
-		out.println("import org.eclipse.osgi.util.NLS;");
-		out.println("import org.txm.utils.messages.Utf8NLS;\n");
-
-		out.println("/**\n * "+name+" messages.\n * @author sjacquot, mdecorde \n */");
-
-		out.println("public class "+className+" extends NLS {\n");
-
-		out.println("	private static final String BUNDLE_NAME = \""+classPackage+".messages\"; //$NON-NLS-1$\n");
-
-		//write keys
-		ArrayList<String> keys = new ArrayList<String>(this.getMessageKeys());
-		Collections.sort(keys, comp);
-		for (String key : keys) {
-			out.println("	public static String "+key+";");
-		}
-
-		// write end
-		out.println("\n	/**");
-		out.println("	 * @param message the message containing parameters {0}...{N}");
-		out.println("	 * @param bindings the parameters ");
-		out.println("	 * @return the given message's substitution locations with the given string values.");
-		out.println("	 */");
-		out.println("	public static String bind(String message, Object... bindings) {");
-		out.println("		return NLS.bind(message, bindings);");
-		out.println("	}");
-
-		out.println("\n	static {");
-		out.println("		// initialize resource bundle");
-		out.println("		Utf8NLS.initializeMessages(BUNDLE_NAME, "+className+".class);");
-		out.println("	}");
-
-		out.println("	private "+className+"() { }");
-		out.println("}");
-
-		out.close();
-	}
-
-	public void summary() {
-
-		System.out.println(getMessageClassName());
-
-		System.out.println("Messages keys");
-		for (String key : messageKeys) {
-			System.out.println(key);
-		}
-
-		System.out.println("Langs");
-		for (File lang : langs.keySet()) {
-			System.out.println("  ------------------------------------------------------------------------------------------------------------------------------");
-			System.out.println("  " + lang);
-			System.out.println("  ------------------------------------------------------------------------------------------------------------------------------");
-			BiHashMap<String, String> hash = langs.get(lang);
-			System.out.println(" keys="+ hash.getKeys());
-			for (String key : hash.getKeys()) {
-				System.out.println("  " + key + "="+hash.get(key));
-			}
-		}
-	}
-
-	/**
-	 * Parses the project and stores a list of the source files. 
-	 */
-	private void createSourceFilesList(File path)	{
-		File[] list = path.listFiles();
-
-		if (list == null) {
-			return;
-		}
-
-		for (File file : list) {
-			if (file.isDirectory()) {
-				this.createSourceFilesList(file);
-			}
-			else if (!file.equals(this.javaMessageFile)
-					&& Arrays.asList(this.srcFilesExtensions).contains(FilenameUtils.getExtension(file.getName())) 
-					) {
-
-				if (debug) {
-					System.out.println("PluginMessages.createSourceFilesList(): adding source files " + file + ".");
-				}
-				else	{
-					//System.out.print(".");
-				}
-
-				this.srcFiles.add(file);
-			}
-		}
-	}
-
-	/**
-	 * Parses the project and stores a list of the source files.
-	 */
-	public void createSourceFilesList()	{
-
-		if (debug) {
-			System.out.println("PluginMessages.loadSourceFilesList(): creating source files list for extensions [" + StringUtils.join(this.srcFilesExtensions, ", ") + "]...");
-		}
-
-		this.createSourceFilesList(this.projectDirectory);
-
-
-		if(debug) {
-			System.out.println("PluginMessages.loadSourceFilesList(): done. Source files count = " + this.srcFiles.size() + ".");
-		}
-	}
-
-	/**
-	 * Rename a key and update in the properties file the keys
-	 * 
-	 * stores the modification to rename the keys later in the Workspace source files
-	 * 
-	 * @param oldName
-	 * @param newName
-	 */
-	public void renameKey(String oldName, String newName) {
-
-		if (!messageKeys.contains(oldName)) {
-			System.err.println("PluginMessages.renameKey(): key=" + oldName + " not found, aborting renaming to " + newName + ".");
-			return;
-		}
-
-		// continue only if key name has changed
-		if(oldName.equals(newName))	{
-			if(debug)	{
-				System.out.println("PluginMessages.renameKey(): no changes, skipped key: " + oldName + " = " + newName);
-			}
-			return;
-		}
-
-
-		messageKeys.remove(oldName);
-		messageKeys.add(newName);
-
-		for (File p : langs.keySet()) {
-			BiHashMap<String, String> h = langs.get(p);
-			if (h.containsKey(oldName)) {
-				h.put(newName, h.get(oldName));
-				h.removeByKey(oldName);
-			}
-		}
-
-		keyModifications.put(oldName, newName);
-	}
-
-	/**
-	 * Remove a messages from XXMessages.java and messages.properties files
-	 * 
-	 * @param key the local name of the key to remove
-	 */
-	public void removeKey(String key) {
-		if (!messageKeys.contains(key)) {
-			System.err.println("PluginMessages.removeKey(): key=" + key + " not found, aborting removing.");
-			return;
-		}
-
-		messageKeys.remove(key);
-
-		for (File p : langs.keySet()) {
-			BiHashMap<String, String> h = langs.get(p);
-			if (h.containsKey(key)) {
-				h.removeByKey(key);
-			}
-		}
-
-		keyModifications.remove(key); // if any modifications was done
-	}
-
-	/**
-	 * 
-	 * @return the renamed keys
-	 */
-	public HashMap<String, String> getKeyModifications() {
-		return keyModifications;
-	}
-
-	public BiHashMap<File, String> getFile2lang() {
-		return file2lang;
-	}
-
-	public TreeMap<File, BiHashMap<String, String>> getLangs() {
-		return langs;
-	}
-
-	/**
-	 * Gets the project root directory.
-	 * @return the projectDirectory
-	 */
-
-	public File getProjectDirectory() {
-		return projectDirectory;
-	}
-
-
-	/**
-	 * Dumps the specified BiHashMap<String, String> to standard output.
-	 */
-	public void dump(BiHashMap<String, String> messages)	{
-		for (String key : messages.getKeys()) {
-			System.out.println("PluginMessages.dump(): " + key + "=" + messages.get(key));
-		}
-	}
-
-
-	/**
-	 * Dumps the keys replacements table.
-	 */
-	public void dumpKeysReplacements()	{
-		System.out.println("PluginMessages.dumpKeysReplacements(): keys replacements table");
-		for (String key : this.keyModifications.keySet()) {
-			System.out.println("PluginMessages.dumpKeysReplacements(): " + key + "=>" + this.keyModifications.get(key));
-		}
-	}
-
-	/**
-	 * @return the usedKeysIndex
-	 */
-	public TreeMap<String, TreeSet<File>> getUsedKeysFilesIndex() {
-		return usedKeysFilesIndex;
-	}
-
-	/**
-	 * 
-	 * @param args
-	 * @throws UnsupportedEncodingException
-	 * @throws FileNotFoundException
-	 * @throws IOException
-	 */
-	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
-
-		File projectFile = new File(new File(System.getProperty("user.dir")).getParentFile().getAbsolutePath() + "/org.txm.rcp");
-
-		PluginMessages pmManager = new PluginMessages(projectFile);
-
-		// test to find files using the specified key
-		/*
-		String key = "TXMCoreMessages.Base_0";
-		TreeSet<File> files = pmManager.getFilesUsingKey(key);
-		System.out.println("getFilesUsingKey: files using key: " + key);
-		for(File file : files)	{
-			System.out.println("   file: " + file);
-		}
-		key = "TXMCoreMessages.common_frequency";
-		files = pmManager.getFilesUsingKey(key);
-		System.out.println("getFilesUsingKey: files using key: " + key);
-		for(File file : files)	{
-			System.out.println("   file: " + file);
-		}
-
-		// test to find unused keys
-		ArrayList<String> unusedKeys = pmManager.getUnusedKeys();
-		for (int i = 0; i < unusedKeys.size(); i++) {
-			System.out.println("findUnusedKeys: key " + unusedKeys.get(i) + " is unused in project " + pmManager.getProjectDirectory() + " (main language value = " + pmManager.getMessagesForLang("").get(unusedKeys.get(i)) + ")");	
-		}
-		 */
-		//dict.summary();
-
-
-		//pmManager.saveChanges(true);
-
-		System.out.println("PluginMessages.main(): done.");
-	}
-}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/PluginMessagesManager.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/PluginMessagesManager.java	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/PluginMessagesManager.java	(revision 1276)
@@ -0,0 +1,785 @@
+package org.txm.rcp.translate.i18n;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang.StringUtils;
+import org.eclipse.osgi.util.NLS;
+import org.txm.utils.BiHashMap;
+import org.txm.utils.io.IOUtils;
+
+/**
+ *  
+ * Check any missing key in both of messages.properties and Message.java files
+ *
+ * @author mdecorde
+ * @author sjacquot
+ *
+ */
+public class PluginMessagesManager {
+	/**
+	 * Files encoding.
+	 */
+	public static String ENCODING = "UTF-8";
+
+	/**
+	 * Debug state.
+	 */
+	protected boolean debug;
+
+	/**
+	 * Project root directory.
+	 */
+	protected File projectDirectory;
+
+	/**
+	 * Java message file, eg.: TXMCoreMessages.java, ConcordanceUIMessages.java, etc.
+	 */
+	protected File javaMessageFile;
+
+	/**
+	 * Source files of the project.
+	 */
+	protected ArrayList<File> srcFiles;
+
+	/**
+	 * Extensions to use when creating the source files list.
+	 */
+	protected String[] srcFilesExtensions = new String[] {"java", "groovy"};
+
+
+	/**
+	 * TODO Global REGEX containing all keys to later use for search in files.
+	 */
+	protected Pattern keysGlobalREGEX = Pattern.compile("...");
+
+	/**
+	 * Index of the keys used and their associated files.
+	 */
+	protected TreeMap<String, TreeSet<File>> usedKeysFilesIndex;
+
+	/**
+	 * the XXXMessages.java messages keys
+	 */
+	TreeSet<String> messageKeys = new TreeSet<String>();
+
+	/**
+	 * plugin.xml file -> contains eventually keys
+	 */
+	protected File pluginXMLFile;
+
+	public File getPluginXMLFile() {
+		return pluginXMLFile;
+	}
+
+	public TreeSet<String> getOsgiKeys() {
+		return osgiKeys;
+	}
+
+	public TreeMap<File, BiHashMap<String, String>> getOsgiLangs() {
+		return osgiLangs;
+	}
+
+	/**
+	 * the OSGI-INF/l10n/bundle[_lang].properties OSGI message keys
+	 */
+	TreeSet<String> osgiKeys = new TreeSet<String>();
+
+	/**
+	 * The OSGI messages stored by properties file
+	 */
+	TreeMap<File, BiHashMap<String, String>> osgiLangs = new TreeMap<File, BiHashMap<String, String>>();
+
+	/**
+	 * Stores the key modifications for further saves in the source files
+	 */
+	HashMap<String, String> keyModifications = new HashMap<String, String>();
+
+	/**
+	 * The messages stored by properties file
+	 */
+	TreeMap<File, BiHashMap<String, String>> langs = new TreeMap<File, BiHashMap<String, String>>();
+
+	/**
+	 * the properties files langs
+	 */
+	BiHashMap<File, String> file2lang = new BiHashMap<File, String>();
+
+	/**
+	 * 
+	 * @param projectDirectory
+	 * @param javaMessageFile
+	 * @throws UnsupportedEncodingException
+	 * @throws FileNotFoundException
+	 * @throws IOException
+	 */
+	public PluginMessagesManager(File projectDirectory) throws UnsupportedEncodingException, FileNotFoundException, IOException {
+		this(projectDirectory, findMessageFile(projectDirectory), false);
+	}
+
+	public static File findMessageFile(File projectDirectory2) {
+		File messagesPackageDir = new File(projectDirectory2, "src/"+projectDirectory2.getName().replaceAll("\\.", "/")+"/messages");
+
+		if (!messagesPackageDir.exists()) {
+			messagesPackageDir = new File(projectDirectory2, "src/java/"+projectDirectory2.getName().replaceAll("\\.", "/")+"/messages");
+		}
+
+		if (!messagesPackageDir.exists()) {
+			messagesPackageDir = new File(projectDirectory2, "src/main/java/"+projectDirectory2.getName().replaceAll("\\.", "/")+"/messages");
+		}
+
+		if (!messagesPackageDir.exists()) {
+			return null;
+		}
+
+		for (File messagesJavaFile : messagesPackageDir.listFiles()) {
+			if (messagesJavaFile.isDirectory()) continue;
+			if (!messagesJavaFile.getName().endsWith("Messages.java")) continue;
+			return messagesJavaFile;
+		}
+		return null;
+	}
+
+	/**
+	 * 
+	 * @param projectDirectory
+	 * @param javaMessageFile
+	 * @throws UnsupportedEncodingException
+	 * @throws FileNotFoundException
+	 * @throws IOException
+	 */
+	public PluginMessagesManager(File projectDirectory, File javaMessageFile) throws UnsupportedEncodingException, FileNotFoundException, IOException {
+		this(projectDirectory, javaMessageFile, false);
+	}
+
+	/**
+	 * 
+	 * @param projectDirectory
+	 * @param javaMessageFile
+	 * @throws UnsupportedEncodingException
+	 * @throws FileNotFoundException
+	 * @throws IOException
+	 */
+	public PluginMessagesManager(File projectDirectory, File javaMessageFile, boolean debug) throws UnsupportedEncodingException, FileNotFoundException, IOException {
+
+		this.javaMessageFile = javaMessageFile;
+		this.projectDirectory = projectDirectory;
+		this.pluginXMLFile = new File(projectDirectory, "plugin.xml");
+		this.srcFiles = new ArrayList<File>();
+		this.debug = debug;
+
+		if (debug) {
+			System.out.println("********************************************************************************************************************");
+			System.out.println("PluginMessages.PluginMessages(): project root directory = " + this.projectDirectory);
+			System.out.println("PluginMessages.PluginMessages(): java message file = " + this.javaMessageFile);
+		}
+		
+
+		if (this.javaMessageFile != null) {
+			
+			File[] propFiles = javaMessageFile.getParentFile().listFiles();
+			for (File propFile : propFiles) {
+				String name = propFile.getName();
+				if (!name.endsWith(".properties")) continue;
+				//messages.properties
+				//messages_xx.properties
+				String lang = name.substring(8, name.indexOf(".properties"));
+				langs.put(propFile, new BiHashMap<String, String>());
+				file2lang.put(propFile, lang);
+
+				BiHashMap<String, String> hash = langs.get(propFile);
+
+				Properties props1 = new Properties();
+				props1.load(IOUtils.getReader(propFile, ENCODING));
+				for (Object k : props1.keySet()) {
+					hash.put(k.toString(), props1.get(k).toString());
+				}
+			}
+			
+			BufferedReader reader2 = new BufferedReader(new InputStreamReader(new FileInputStream(javaMessageFile), ENCODING));
+			String line2 = reader2.readLine();
+			while (line2 != null) {
+				line2 = line2.trim();
+				if (line2.startsWith("public static String") && line2.endsWith(";") && !line2.contains("=")) {
+					line2 = line2.substring(21, line2.length() -1);
+					messageKeys.add(line2);
+				}
+				line2 = reader2.readLine();
+			}
+			reader2.close();
+		}
+
+		// create the project source files list
+		this.createSourceFilesList();
+
+		//this.createKeysGlobalREGEX();
+		this.createUsedKeysFilesIndex();
+
+		// try loading OSGI messages 
+		if (pluginXMLFile.exists()) {
+			ArrayList<String> result = IOUtils.findWithGroup(pluginXMLFile, "\"%([^\"]+)\"");
+			osgiKeys.addAll(result);
+
+			File osgiInf = new File(projectDirectory, "OSGI-INF/l10n");
+			if (osgiInf.exists()) {
+				for (File propFile : osgiInf.listFiles()) {
+					if (propFile.getName().startsWith("bundle") && propFile.getName().endsWith(".properties")) {
+						Properties props = new Properties();
+						props.load(IOUtils.getReader(propFile));
+						osgiLangs.put(propFile, new BiHashMap<String, String>(props));
+					}
+				}
+			}
+//			System.out.println(osgiKeys);
+//			System.out.println(osgiLangs);
+		}
+
+		if(debug)	{
+			this.dumpUsedKeysFilesIndex();
+		}
+	}
+
+	//	/**
+	//	 * Creates a global REGEX containing all keys to later search in files.
+	//	 */
+	//	public void createKeysGlobalREGEX()	{
+	//		StringBuffer buffer = new StringBuffer();
+	//		for (String key : messageKeys) {
+	//			buffer.append("|" + key);
+	//		}
+	//		
+	//		// remove first pipe
+	//		buffer.deleteCharAt(0);
+	//		
+	//		this.keysGlobalREGEX = Pattern.compile("(" + buffer + ")");
+	//		
+	//		if(debug)	{
+	//			System.out.println("PluginMessages.createKeysGlobalREGEX(): REGEX created: " + this.keysGlobalREGEX + ".");
+	//		}
+	//		
+	//	}
+
+	/**
+	 * Creates/updates the index of the keys used and their associated files.
+	 * @throws IOException 
+	 */
+	public void createUsedKeysFilesIndex() throws IOException	{
+		this.usedKeysFilesIndex = new TreeMap<String, TreeSet<File>>();
+
+		//for (String key : messageKeys) {
+
+		//		if(debug) {
+		//			System.out.println("PluginMessages.createUsedKeysIndex(): looking for key " + this.getKeyFullName(key) + " in project source files...");
+		//		}
+
+		for (File file : this.srcFiles) {
+			if (!file.exists()) continue;
+			String lines = IOUtils.getText(file, PluginMessagesManager.ENCODING);
+
+			//			for (String line : lines) {
+			//				//					if(line.contains(this.getKeyFullName(key)))	{
+			//				//						files.add(file);
+			//				//					};
+
+			Matcher m = WorkspaceMessagesManager.KEY_REGEX.matcher(lines);
+			while (m.find()) {
+				// Get the matching string
+				String key = m.group();
+				if (!this.usedKeysFilesIndex.containsKey(key)) {
+					this.usedKeysFilesIndex.put(key, new TreeSet<File>());
+				}
+				this.usedKeysFilesIndex.get(key).add(file);
+			}
+			//			}
+		}
+		//}
+	}
+
+
+	/**
+	 * Dumps in console the index of the keys used and their associated files.
+	 */
+	public void dumpUsedKeysFilesIndex()	{
+
+		System.out.println("PluginMessages.dumpUsedKeysIndex(): dumping used keys files index...");
+
+		for (Map.Entry<String, TreeSet<File>> entry : this.usedKeysFilesIndex.entrySet()) {
+			System.out.println("key: " + this.getKeyFullName(entry.getKey()));
+			if(entry.getValue().isEmpty())	{
+				System.out.println("   file(s): -not used in any file of the project " + this.getProjectDirectory() + "-");
+			}
+			else	{
+				for(File file : entry.getValue())	{
+					System.out.println("   file(s): " + file);
+				}
+			}
+		}
+	}
+
+	/***
+	 * @param key
+	 * @return all source files using the specified message key (except the main Java message file).
+	 */
+	public TreeSet<File> getFilesUsingKey(String key) {
+		if (usedKeysFilesIndex.containsKey(key)) {
+			return this.usedKeysFilesIndex.get(key);
+		} else {
+			return new TreeSet<File>();
+		}
+	}
+
+	/***
+	 * @param key local key name
+	 * @return all source files using the specified local message key (except the main Java message file).
+	 */
+	public TreeSet<File> getFilesUsingLocalKey(String key) {
+		return getFilesUsingKey(getKeyFullName(key));
+	}
+
+
+	/**
+	 * Returns the keys of all messages that are not used in the project itself.
+	 * @return a sorted list containing all unused keys
+	 */
+	public ArrayList<String> getUnusedKeys()	{
+		return getUnusedKeys(usedKeysFilesIndex, debug);
+	}
+
+	/**
+	 * @param usedKeysFilesIndex
+	 * 
+	 * @return a sorted list containing all unused keys of all messages that are not used in the used key files index.
+	 */
+	public static ArrayList<String> getUnusedKeys(TreeMap<String, TreeSet<File>> usedKeysFilesIndex, boolean debug)	{
+		ArrayList<String> unusedKeys = new ArrayList<String>();
+		for (Map.Entry<String, TreeSet<File>> entry : usedKeysFilesIndex.entrySet()) {
+			if(entry.getValue().isEmpty())	{
+
+				if(debug)	{
+					System.out.println("PluginMessages.getUnusedKeys(): unused key found: " + entry.getKey() + ".");
+				}
+
+				unusedKeys.add(entry.getKey());
+			}
+		}
+
+		Collections.sort(unusedKeys);
+		return unusedKeys;
+	}
+
+	/**
+	 * @param lang
+	 * @return the map of the messages keys and values for the specified language.
+	 */
+	public BiHashMap<String, String> getMessagesForLang(String lang) {
+		File p = new File(javaMessageFile.getParentFile(), "messages" + lang + ".properties");
+		return langs.get(p);
+	}
+
+	/**
+	 * @return the map of the messages keys and values for the main language.
+	 */
+	public BiHashMap<String, String> getMessagesForDefaultLang() {
+		return this.getMessagesForLang(""); //$NON-NLS-1$
+	}
+
+
+	public void put(String lang, String key, String message) {
+		messageKeys.add(key);
+
+		File p = new File(javaMessageFile.getParentFile(), "messages"+lang+".properties");
+
+		if (!langs.containsKey(p)) {
+			langs.put(p, new BiHashMap<String, String>());
+			file2lang.put(p, lang);
+		}
+
+		if (message == null) {
+			langs.get(p).removeByKey(key);
+		} else {
+			langs.get(p).put(key, message);
+		}
+	}
+
+	public String get(String lang, String key) {
+		File p = new File(javaMessageFile.getParentFile(), "messages"+lang+".properties");
+
+		if (!file2lang.containsKey(p)) return null;
+
+		return langs.get(p).get(key);
+	}
+
+	public TreeSet<String> getMessageKeys() {
+		return messageKeys;
+	}
+
+	public File getMessageFile() {
+		return javaMessageFile;
+	}
+
+	public String getMessageClassName() {
+		return javaMessageFile.getName().substring(0, javaMessageFile.getName().length()-5);
+	}
+
+	public String getMessageFullClassName() {
+		return javaMessageFile.getAbsolutePath().substring(javaMessageFile.getAbsolutePath().lastIndexOf("org/txm/"), javaMessageFile.getAbsolutePath().length()-5).replace("/", ".");
+	}
+
+	/**
+	 * @return the XXXMessages.java class name without the 'Messages' part
+	 */
+	public String getMessageName() {
+		return javaMessageFile.getName().substring(0, javaMessageFile.getName().length()-5-8);
+	}
+
+	public String getMessageFullName() {
+		return javaMessageFile.getAbsolutePath().substring(javaMessageFile.getAbsolutePath().lastIndexOf("org/txm/")+8, javaMessageFile.getAbsolutePath().length()-5-8).replace("/", ".");
+	}
+
+	/**
+	 * Returns the full name of the specified key including the Java message class name.
+	 * @param key
+	 * @return
+	 */
+	public String getKeyFullName(String key)	{
+		return this.getMessageClassName() + "." + key;
+	}
+
+	public static Collator col = Collator.getInstance(Locale.FRANCE);
+	public static Comparator comp = new Comparator<String>() {
+		@Override
+		public int compare(String arg0, String arg1) {
+			return col.compare(arg0, arg1);
+		}
+	};
+	static {
+		col.setStrength(Collator.TERTIARY);
+	}
+	public void saveChanges(boolean replaceFiles) throws IOException {
+
+		if (javaMessageFile == null) return;
+		
+		//Write prop File
+		for (File propFile : langs.keySet()) {
+
+			Properties props = new Properties() {
+				@Override
+				public synchronized Enumeration<Object> keys() {
+					TreeSet<Object> set = new TreeSet<Object>(comp);
+					set.addAll(super.keySet());
+					return Collections.enumeration(set);
+				}
+			};
+			BiHashMap<String, String> h = langs.get(propFile);
+			for (String k : h.getKeys()) {
+				props.setProperty(k, h.get(k));
+			}
+
+			File newPropFile = new File(propFile.getParentFile(), propFile.getName()+".new");
+			if (replaceFiles) {
+				propFile.delete();
+				newPropFile = propFile;
+			}
+			props.store(IOUtils.getWriter(newPropFile, ENCODING), "TXM messages generated by the PluginMessages class");
+		}
+
+		// write message File
+		File newJavaMessageFile = new File(javaMessageFile.getParentFile(), javaMessageFile.getName()+".new");
+		if (replaceFiles) {
+			javaMessageFile.delete();
+			newJavaMessageFile = javaMessageFile;
+		}
+
+		String className = getMessageClassName();
+		String name = getMessageName();
+		String classPackage = getMessageFullClassName().substring(0, getMessageFullClassName().length() - getMessageClassName().length() - 1);
+
+		PrintWriter out = IOUtils.getWriter(newJavaMessageFile, ENCODING);
+
+		// write start
+		out.println("package "+classPackage+";\n");
+
+		out.println("import org.eclipse.osgi.util.NLS;");
+		out.println("import org.txm.utils.messages.Utf8NLS;\n");
+
+		out.println("/**\n * "+name+" messages.\n * @author sjacquot, mdecorde \n */");
+
+		out.println("public class "+className+" extends NLS {\n");
+
+		out.println("	private static final String BUNDLE_NAME = \""+classPackage+".messages\"; //$NON-NLS-1$\n");
+
+		//write keys
+		ArrayList<String> keys = new ArrayList<String>(this.getMessageKeys());
+		Collections.sort(keys, comp);
+		for (String key : keys) {
+			out.println("	public static String "+key+";");
+		}
+
+		// write end
+		out.println("\n	/**");
+		out.println("	 * @param message the message containing parameters {0}...{N}");
+		out.println("	 * @param bindings the parameters ");
+		out.println("	 * @return the given message's substitution locations with the given string values.");
+		out.println("	 */");
+		out.println("	public static String bind(String message, Object... bindings) {");
+		out.println("		return NLS.bind(message, bindings);");
+		out.println("	}");
+
+		out.println("\n	static {");
+		out.println("		// initialize resource bundle");
+		out.println("		Utf8NLS.initializeMessages(BUNDLE_NAME, "+className+".class);");
+		out.println("	}");
+
+		out.println("	private "+className+"() { }");
+		out.println("}");
+
+		out.close();
+	}
+
+	public void summary() {
+
+		System.out.println(getMessageClassName());
+
+		System.out.println("Messages keys");
+		for (String key : messageKeys) {
+			System.out.println(key);
+		}
+
+		System.out.println("Langs");
+		for (File lang : langs.keySet()) {
+			System.out.println("  ------------------------------------------------------------------------------------------------------------------------------");
+			System.out.println("  " + lang);
+			System.out.println("  ------------------------------------------------------------------------------------------------------------------------------");
+			BiHashMap<String, String> hash = langs.get(lang);
+			System.out.println(" keys="+ hash.getKeys());
+			for (String key : hash.getKeys()) {
+				System.out.println("  " + key + "="+hash.get(key));
+			}
+		}
+	}
+
+	/**
+	 * Parses the project and stores a list of the source files. 
+	 */
+	private void createSourceFilesList(File path)	{
+		File[] list = path.listFiles();
+
+		if (list == null) {
+			return;
+		}
+
+		for (File file : list) {
+			if (file.isDirectory()) {
+				this.createSourceFilesList(file);
+			}
+			else if (!file.equals(this.javaMessageFile)
+					&& Arrays.asList(this.srcFilesExtensions).contains(FilenameUtils.getExtension(file.getName())) 
+					) {
+
+				if (debug) {
+					System.out.println("PluginMessages.createSourceFilesList(): adding source files " + file + ".");
+				}
+				else	{
+					//System.out.print(".");
+				}
+
+				this.srcFiles.add(file);
+			}
+		}
+	}
+
+	/**
+	 * Parses the project and stores a list of the source files.
+	 */
+	public void createSourceFilesList()	{
+
+		if (debug) {
+			System.out.println("PluginMessages.loadSourceFilesList(): creating source files list for extensions [" + StringUtils.join(this.srcFilesExtensions, ", ") + "]...");
+		}
+
+		this.createSourceFilesList(this.projectDirectory);
+
+
+		if(debug) {
+			System.out.println("PluginMessages.loadSourceFilesList(): done. Source files count = " + this.srcFiles.size() + ".");
+		}
+	}
+
+	/**
+	 * Rename a key and update in the properties file the keys
+	 * 
+	 * stores the modification to rename the keys later in the Workspace source files
+	 * 
+	 * @param oldName
+	 * @param newName
+	 */
+	public void renameKey(String oldName, String newName) {
+
+		if (!messageKeys.contains(oldName)) {
+			System.err.println("PluginMessages.renameKey(): key=" + oldName + " not found, aborting renaming to " + newName + ".");
+			return;
+		}
+
+		// continue only if key name has changed
+		if(oldName.equals(newName))	{
+			if(debug)	{
+				System.out.println("PluginMessages.renameKey(): no changes, skipped key: " + oldName + " = " + newName);
+			}
+			return;
+		}
+
+
+		messageKeys.remove(oldName);
+		messageKeys.add(newName);
+
+		for (File p : langs.keySet()) {
+			BiHashMap<String, String> h = langs.get(p);
+			if (h.containsKey(oldName)) {
+				h.put(newName, h.get(oldName));
+				h.removeByKey(oldName);
+			}
+		}
+
+		keyModifications.put(oldName, newName);
+	}
+
+	/**
+	 * Remove a messages from XXMessages.java and messages.properties files
+	 * 
+	 * @param key the local name of the key to remove
+	 */
+	public void removeKey(String key) {
+		if (!messageKeys.contains(key)) {
+			System.err.println("PluginMessages.removeKey(): key=" + key + " not found, aborting removing.");
+			return;
+		}
+
+		messageKeys.remove(key);
+
+		for (File p : langs.keySet()) {
+			BiHashMap<String, String> h = langs.get(p);
+			if (h.containsKey(key)) {
+				h.removeByKey(key);
+			}
+		}
+
+		keyModifications.remove(key); // if any modifications was done
+	}
+
+	/**
+	 * 
+	 * @return the renamed keys
+	 */
+	public HashMap<String, String> getKeyModifications() {
+		return keyModifications;
+	}
+
+	public BiHashMap<File, String> getFile2lang() {
+		return file2lang;
+	}
+
+	public TreeMap<File, BiHashMap<String, String>> getLangs() {
+		return langs;
+	}
+
+	/**
+	 * Gets the project root directory.
+	 * @return the projectDirectory
+	 */
+
+	public File getProjectDirectory() {
+		return projectDirectory;
+	}
+
+
+	/**
+	 * Dumps the specified BiHashMap<String, String> to standard output.
+	 */
+	public void dump(BiHashMap<String, String> messages)	{
+		for (String key : messages.getKeys()) {
+			System.out.println("PluginMessages.dump(): " + key + "=" + messages.get(key));
+		}
+	}
+
+
+	/**
+	 * Dumps the keys replacements table.
+	 */
+	public void dumpKeysReplacements()	{
+		System.out.println("PluginMessages.dumpKeysReplacements(): keys replacements table");
+		for (String key : this.keyModifications.keySet()) {
+			System.out.println("PluginMessages.dumpKeysReplacements(): " + key + "=>" + this.keyModifications.get(key));
+		}
+	}
+
+	/**
+	 * @return the usedKeysIndex
+	 */
+	public TreeMap<String, TreeSet<File>> getUsedKeysFilesIndex() {
+		return usedKeysFilesIndex;
+	}
+
+	/**
+	 * 
+	 * @param args
+	 * @throws UnsupportedEncodingException
+	 * @throws FileNotFoundException
+	 * @throws IOException
+	 */
+	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
+
+		File projectFile = new File(new File(System.getProperty("user.dir")).getParentFile().getAbsolutePath() + "/org.txm.rcp");
+
+		PluginMessagesManager pmManager = new PluginMessagesManager(projectFile);
+
+		// test to find files using the specified key
+		/*
+		String key = "TXMCoreMessages.Base_0";
+		TreeSet<File> files = pmManager.getFilesUsingKey(key);
+		System.out.println("getFilesUsingKey: files using key: " + key);
+		for(File file : files)	{
+			System.out.println("   file: " + file);
+		}
+		key = "TXMCoreMessages.common_frequency";
+		files = pmManager.getFilesUsingKey(key);
+		System.out.println("getFilesUsingKey: files using key: " + key);
+		for(File file : files)	{
+			System.out.println("   file: " + file);
+		}
+
+		// test to find unused keys
+		ArrayList<String> unusedKeys = pmManager.getUnusedKeys();
+		for (int i = 0; i < unusedKeys.size(); i++) {
+			System.out.println("findUnusedKeys: key " + unusedKeys.get(i) + " is unused in project " + pmManager.getProjectDirectory() + " (main language value = " + pmManager.getMessagesForLang("").get(unusedKeys.get(i)) + ")");	
+		}
+		 */
+		//dict.summary();
+
+
+		//pmManager.saveChanges(true);
+
+		System.out.println("PluginMessages.main(): done.");
+	}
+}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/WorkspaceMessagesManager.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/WorkspaceMessagesManager.java	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/WorkspaceMessagesManager.java	(revision 1276)
@@ -30,7 +30,7 @@
 	/**
 	 * the plugins discovered and their messages
 	 */
-	LinkedHashMap<File, PluginMessages> pluginsMessagesPerProject = new LinkedHashMap<File, PluginMessages>();
+	LinkedHashMap<File, PluginMessagesManager> pluginsMessagesPerProject = new LinkedHashMap<File, PluginMessagesManager>();
 
 	/**
 	 * the Eclipse RCP workspace location containing the projects
@@ -112,7 +112,7 @@
 
 //			if (messagesJavaFile != null && messagesJavaFile.exists()) {
 
-				PluginMessages messages = new PluginMessages(project);
+				PluginMessagesManager messages = new PluginMessagesManager(project);
 
 				pluginsMessagesPerProject.put(project, messages);
 
@@ -132,7 +132,7 @@
 			System.out.println("WorkspaceMessagesManager.WorkspaceMessagesManager(): ------------------ process summary --------------------------------------------------------------");
 			System.out.println("WorkspaceMessagesManager.WorkspaceMessagesManager(): numbers of projects: " + this.pluginsMessagesPerProject.size() + ".");
 			System.out.println("WorkspaceMessagesManager.WorkspaceMessagesManager(): numbers of used keys: " + this.usedKeysFilesIndex.size() + ".");
-			System.out.println("WorkspaceMessagesManager.WorkspaceMessagesManager(): numbers of unused keys: " + PluginMessages.getUnusedKeys(this.usedKeysFilesIndex, false).size() + ".");
+			System.out.println("WorkspaceMessagesManager.WorkspaceMessagesManager(): numbers of unused keys: " + PluginMessagesManager.getUnusedKeys(this.usedKeysFilesIndex, false).size() + ".");
 			System.out.println("WorkspaceMessagesManager.WorkspaceMessagesManager(): done.");
 		}
 	}
@@ -167,19 +167,19 @@
 		HashMap<String, String> modifications = new HashMap<String, String>();
 
 		// stores all modifications
-		ArrayList<PluginMessages> plugins = new ArrayList<PluginMessages>(pluginsMessagesPerProject.values());
+		ArrayList<PluginMessagesManager> plugins = new ArrayList<PluginMessagesManager>(pluginsMessagesPerProject.values());
 
 		// sort plugins by name length. Needed to not erase a replacement: for messages BMessages.xx and ABMessages.xx, ABMessages.xx should be replaced first
-		Collections.sort(plugins, new Comparator<PluginMessages>() {
+		Collections.sort(plugins, new Comparator<PluginMessagesManager>() {
 			@Override
-			public int compare(PluginMessages arg0, PluginMessages arg1) {
+			public int compare(PluginMessagesManager arg0, PluginMessagesManager arg1) {
 				return arg0.getMessageName().length() - arg1.getMessageName().length();
 			}
 
 		});
 
 		// build the global keys used in sources
-		for (PluginMessages pm : plugins) {
+		for (PluginMessagesManager pm : plugins) {
 			HashMap<String, String> modifs = pm.getKeyModifications();
 			String name = pm.getMessageClassName();
 
@@ -200,7 +200,7 @@
 		});
 
 		ArrayList<String> updated = new ArrayList<String>();
-		for (PluginMessages pm : pluginsMessagesPerProject.values()) {
+		for (PluginMessagesManager pm : pluginsMessagesPerProject.values()) {
 			TreeMap<String, TreeSet<File>> index = pm.getUsedKeysFilesIndex();
 			
 			for (String oldKey : keys) {
@@ -218,7 +218,7 @@
 	/**
 	 * @return the Project directory associated with their PluginMessages
 	 */
-	public LinkedHashMap<File, PluginMessages> getPluginMessages() {
+	public LinkedHashMap<File, PluginMessagesManager> getPluginMessages() {
 		return pluginsMessagesPerProject;
 	}
 
@@ -231,10 +231,10 @@
 	 * @param key local key (without the 'XXXMessages.' part)
 	 * @return the PluginMessages that uses the key in source files
 	 */
-	public ArrayList<PluginMessages> getPluginMessagesManagersUsingKey(String key)	{
-		ArrayList<PluginMessages> pms = new ArrayList<>();
+	public ArrayList<PluginMessagesManager> getPluginMessagesManagersUsingKey(String key)	{
+		ArrayList<PluginMessagesManager> pms = new ArrayList<>();
 
-		for (PluginMessages pm : this.getPluginMessages().values()) {
+		for (PluginMessagesManager pm : this.getPluginMessages().values()) {
 			if (pm.getUsedKeysFilesIndex().containsKey(key)) {
 				pms.add(pm);
 			}
@@ -251,7 +251,7 @@
 		this.usedKeysFilesIndex = new TreeMap<String, TreeSet<File>>();
 
 		for (File p : this.pluginsMessagesPerProject.keySet()) {
-			PluginMessages pmManager = this.pluginsMessagesPerProject.get(p);
+			PluginMessagesManager pmManager = this.pluginsMessagesPerProject.get(p);
 
 			TreeMap<String, TreeSet<File>> index = pmManager.getUsedKeysFilesIndex();
 
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizeStringFile.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizeStringFile.groovy	(revision 1275)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizeStringFile.groovy	(revision 1276)
@@ -16,7 +16,7 @@
 	File propFile;
 	File messageFile
 
-	PluginMessages dict
+	PluginMessagesManager dict
 
 	String prefix
 	int iprefix = 0;
@@ -24,7 +24,7 @@
 	boolean mustSaveChanges = false;
 
 	public ExternalizeStringFile(File propFile, File messageFile) {
-		dict = new PluginMessages(propFile, messageFile);		
+		dict = new PluginMessagesManager(propFile, messageFile);		
 	}
 	
 	private ArrayList<String> getLineStrings(String l) {
