Index: tmp/org.txm.setups/index.html
===================================================================
--- tmp/org.txm.setups/index.html	(revision 1222)
+++ tmp/org.txm.setups/index.html	(revision 1223)
@@ -3,27 +3,27 @@
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 <meta name="generator" content="ReText 4.1.2">
 <link rel="stylesheet" type="text/css" href="index_files/sf.css">
-<title>Documentation</title>
+<title>Téléchargement de TXM 0.8.0 α2</title>
 </head>
 <body>
+<hr/>
+
+<h1 align="center">Téléchargement de <span class="big">&nbsp;TXM 0.8.0&nbsp;</span> α<br>
+<!--<img src="index_files/txm-icon.jpg"><br>-->
+<!-- <small><a href="#fr">(instructions d'installation en français ci-dessous - FR FR FR)</a></small> -->
+</h1>
+<div style="background-image:url('index_files/TXM-background.jpg');background-repeat: no-repeat;background-position: right;">
+
+<table >
+<td><center><a href="TXM_0.8.0a3_Win64_installer.exe"><img width="125" src="index_files/windows-icon.png"/><br>Windows 7 et plus (64 bit)</a></center></td>
+<td><center ><a href="TXM_0.8.0a3_Linux64_installer.deb"><img width="125" src="index_files/ubuntu-icon.png"/><br>Ubuntu 16.04 et plus</a><br></center></li><td>
+<td><center ><a href="TXM_0.8.0a3_MacOSX_installer.pkg"><img width="125" src="index_files/macosx-icon.png"/><br>Mac OS X 10.10 et plus</a><br></center></li><td>
+</table>
+
+<h2>Recettes</h2>
 <ul>
-<li> <a href="Archives">ArchivesArchives</a></li>
-<li> <a href="Command_Line_Interface_Java_API_Wrapper_FR.pdf">Command_Line_Interface_Java_API_Wrapper_FR.pdf</a></li>
-<li> <a href="Guide de Transcription d_entretiens Transcriber-TXM 0.3_FR.pdf">Guide de Transcription d_entretiens Transcriber-TXM 0.3_FR.pdf</a></li>
-<li> <a href="Manuel de reference TXMWEB Dev 0.3_FR.pdf">Manuel de reference TXMWEB Dev 0.3_FR.pdf</a></li>
-<li> <a href="Manuel de TXM 0.7 FR.pdf">Manuel de TXM 0.7 FR.pdf</a></li>
-<li> <a href="Manuel de TXM 0.7 FR 22-12-2017.pdf">Manuel de TXM 0.7 FR 22-12-2017.pdf</a></li>
-<li> <a href="Manuel de TXM 0.7 FR-2015.pdf">Manuel de TXM 0.7 FR-2015.pdf</a></li>
-<li> <a href="Manuel du portail TXM 0.6.1 - admin.pdf">Manuel du portail TXM 0.6.1 - admin.pdf</a></li>
-<li> <a href="Manuel editeur XML de TXM.pdf">Manuel editeur XML de TXM.pdf</a></li>
-<li> <a href="Tutorial A Gentle Introduction To TXM Key Concepts In One And An Half Hour.pdf">Tutorial A Gentle Introduction To TXM Key Concepts In One And An Half Hour.pdf</a></li>
-<li> <a href="Tutoriel import de transcriptions entretiens TXT-ODT-RTF-DOC dans TXM.pdf">Tutoriel import de transcriptions entretiens TXT-ODT-RTF-DOC dans TXM.pdf</a></li>
-<li> <a href="TXM leaflet FR.pdf">TXM leaflet FR.pdf</a></li>
-<li> <a href="TXM Leaftlet EN.pdf">TXM Leaftlet EN.pdf</a></li>
-<li> <a href="TXM Manual 0.7.pdf">TXM Manual 0.7.pdf</a></li>
-<li> <a href="TXM-portal-KNIC-tutorial-EN.pdf">TXM-portal-KNIC-tutorial-EN.pdf</a></li>
-<li> <a href="TXM Reference Manual 0.5_EN.pdf">TXM Reference Manual 0.5_EN.pdf</a></li>
-<li> <a href="TXM Reference Manual 0.5_EN.zip">TXM Reference Manual 0.5_EN.zip</a></li>
-</ul>
+<li><a href="https://groupes.renater.fr/wiki/txm-info/public/roadmap_0.8#recette_0_-_installation_multiple">Recette 0 </a>: installation</li>
+<li><a href="https://groupes.renater.fr/wiki/txm-info/public/roadmap_0.8#recette_1_verification_de_toutes_les_fonctionnalites_de_txm_080" >Recette 1</a> : vérification des fonctionnalités</li>
+
 </body>
 </html>
Index: tmp/org.txm.translate.rcp/.settings/org.eclipse.jdt.groovy.core.prefs
===================================================================
--- tmp/org.txm.translate.rcp/.settings/org.eclipse.jdt.groovy.core.prefs	(revision 0)
+++ tmp/org.txm.translate.rcp/.settings/org.eclipse.jdt.groovy.core.prefs	(revision 1223)
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+groovy.compiler.level=25

Property changes on: tmp/org.txm.translate.rcp/.settings/org.eclipse.jdt.groovy.core.prefs
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tmp/org.txm.translate.rcp/.classpath
===================================================================
--- tmp/org.txm.translate.rcp/.classpath	(revision 1222)
+++ tmp/org.txm.translate.rcp/.classpath	(revision 1223)
@@ -7,5 +7,7 @@
 		</accessrules>
 	</classpathentry>
 	<classpathentry kind="src" path="src"/>
+	<classpathentry exported="true" kind="con" path="GROOVY_SUPPORT"/>
+	<classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
Index: tmp/org.txm.translate.rcp/.project
===================================================================
--- tmp/org.txm.translate.rcp/.project	(revision 1222)
+++ tmp/org.txm.translate.rcp/.project	(revision 1223)
@@ -22,6 +22,7 @@
 		</buildCommand>
 	</buildSpec>
 	<natures>
+		<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
 		<nature>org.eclipse.pde.PluginNature</nature>
 		<nature>org.eclipse.jdt.core.javanature</nature>
 	</natures>
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/FindUnusedTranslations.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/FindUnusedTranslations.java	(revision 1222)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/FindUnusedTranslations.java	(revision 1223)
@@ -1,5 +0,0 @@
-package org.txm.rcp.translate;
-
-public class FindUnusedTranslations {
-
-}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/FindMissingTranslations.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/FindMissingTranslations.java	(revision 1222)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/FindMissingTranslations.java	(revision 1223)
@@ -1,5 +0,0 @@
-package org.txm.rcp.translate;
-
-public class FindMissingTranslations {
-
-}
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 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ExportToOneFile.java	(revision 1223)
@@ -0,0 +1,5 @@
+package org.txm.rcp.translate.devtools;
+
+public class ExportToOneFile {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ExportToOneFile.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedTranslations.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedTranslations.java	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedTranslations.java	(revision 1223)
@@ -0,0 +1,5 @@
+package org.txm.rcp.translate.devtools;
+
+public class FindUnusedTranslations {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedTranslations.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindMissingTranslations.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindMissingTranslations.java	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindMissingTranslations.java	(revision 1223)
@@ -0,0 +1,5 @@
+package org.txm.rcp.translate.devtools;
+
+public class FindMissingTranslations {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindMissingTranslations.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MoveDedicatedStringsToPlugins.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MoveDedicatedStringsToPlugins.java	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MoveDedicatedStringsToPlugins.java	(revision 1223)
@@ -0,0 +1,5 @@
+package org.txm.rcp.translate.devtools;
+
+public class MoveDedicatedStringsToPlugins {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MoveDedicatedStringsToPlugins.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

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 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/NormalizeKeys.java	(revision 1223)
@@ -0,0 +1,19 @@
+package org.txm.rcp.translate.devtools;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Normalizes the keys using the message words
+ * 
+ * @author mdecorde
+ *
+ */
+public class NormalizeKeys {
+	static {
+		
+	}
+	
+	public static void main(String[] args) {
+		System.out.println(NLS.bind("lol {0}", 1));
+	}
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/NormalizeKeys.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MergeDuplicatedStringsInAPlugin.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MergeDuplicatedStringsInAPlugin.java	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MergeDuplicatedStringsInAPlugin.java	(revision 1223)
@@ -0,0 +1,5 @@
+package org.txm.rcp.translate.devtools;
+
+public class MergeDuplicatedStringsInAPlugin {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/MergeDuplicatedStringsInAPlugin.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedKeys.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedKeys.java	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedKeys.java	(revision 1223)
@@ -0,0 +1,5 @@
+package org.txm.rcp.translate.devtools;
+
+public class FindUnusedKeys {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindUnusedKeys.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

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 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ImportFromOneFile.java	(revision 1223)
@@ -0,0 +1,12 @@
+package org.txm.rcp.translate.devtools;
+
+/**
+ * 
+ * @author mdecorde
+ *
+ *	Reads one properties file per lang and uses the key to dispatch the strings in the right files
+ *
+ */
+public class ImportFromOneFile {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/ImportFromOneFile.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindCommonMessages.java
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindCommonMessages.java	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindCommonMessages.java	(revision 1223)
@@ -0,0 +1,5 @@
+package org.txm.rcp.translate.devtools;
+
+public class FindCommonMessages {
+
+}

Property changes on: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/devtools/FindCommonMessages.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

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 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizeStringFile.groovy	(revision 1223)
@@ -0,0 +1,262 @@
+package org.txm.rcp.translate.i18n
+
+import javax.swing.*;
+
+/**
+ * Scan a java file to find unexternalized Strings
+ * create the Messages.java entries and the *.properties entries too
+ * replace externalized Strings in the java file
+ * 
+ * uses a ReverseI18nDict
+ * 
+ * @author mdecorde
+ */
+class ExternalizeStringFile {
+
+	File propFile;
+	File messageFile
+
+	ReverseI18nDict dict
+
+	String prefix
+	int iprefix = 0;
+	
+	boolean mustSaveChanges = false;
+
+	public ExternalizeStringFile(File propFile, File messageFile) {
+		dict = new ReverseI18nDict(propFile, messageFile);		
+	}
+	
+	private ArrayList<String> getLineStrings(String l) {
+		def strings = []
+		boolean inS = false;
+		int start = 0;
+		int comment = 0;
+		for(int i = 0 ; i < l.length() ; i++) {
+			def c = l[i]
+			if (inS) {
+				comment = 0
+				if (c == "\\") {
+					i++;
+				} else if (c == "\"") {
+					inS = false;
+					strings << l.substring(start, i+1);
+				}
+			} else {
+				if (c == "\"") {
+					inS = true;
+					start = i;
+					comment = 0
+				} else if (c == "/") {
+					comment++;
+					if (comment == 2) break;
+				} else {
+					comment = 0
+				}
+			}
+		}
+		return strings;
+	}
+	
+	BufferedReader reader;
+	String line;
+	String previousLine;
+	boolean ignore = false;
+	int nline = 1;
+
+	def nlserrors = []
+	def linesToWrite = []
+
+	int nTotalChanges = 0;
+	
+	def strings;
+	def nonnls;
+	def toExternalize = []
+	def toNLSize = []
+	
+	private int processSourceFile(File sourceFile, boolean processMode) {
+		
+		prefix = sourceFile.getName();
+		prefix = prefix.substring(0, prefix.indexOf("."))+"_" ;
+		iprefix = 0;
+		
+		reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), "UTF-8"));
+		line = reader.readLine();
+		previousLine;
+		ignore = false;
+		nline = 1;
+
+		nlserrors = []
+		linesToWrite = []
+
+		nTotalChanges = 0;
+		
+		while (line != null) {
+			String l = line.trim()
+			if (ignore) {
+				if (l.startsWith("*/") || l.endsWith("*/")) {
+					ignore = false
+				} else if (l.startsWith("*")) {
+
+				} else {
+//					println "ERROR LINE $nline : not a comment ? "+l
+//					return -1;
+				}
+			} else {
+				if (l.startsWith("//")) {
+					// ignore
+				} else if (l.startsWith("@")) {
+					// ignore
+				} else if (l.startsWith("/*")) {
+					ignore = true;
+					if (l.endsWith("*/")) ignore=false
+				} else {
+					strings = getLineStrings(l);
+					nonnls = getNONNLS(l);
+					toExternalize = []
+					toNLSize = []
+
+					// fix NON-NLS
+					if (nonnls.size() > 0 && nonnls.size() != strings.size()) {
+						println "NLS error line $nline: "+l
+						nlserrors << nline
+					}
+
+					if (nlserrors.size() == 0) {
+						for (int i = 0 ; i < strings.size() ; i++) {
+							if (nonnls.contains(i)) continue; // ignore
+
+							if (strings[i] == "\"\"" || strings[i] == "\" \"" || strings[i] == "\"\\n\"") {
+								toNLSize << i;
+							} else {
+								toExternalize << i
+							}
+						}
+
+						if (processMode) {
+							nTotalChanges += processLine();
+						} else {
+							nTotalChanges += (toExternalize.size() + toNLSize.size());
+						}
+					}
+				}
+			}
+
+			linesToWrite << line
+			previousLine = line;
+			line = reader.readLine();
+			nline++;
+		}
+
+		if (nlserrors.size() > 0) {
+			println "NLS ERROR "+sourceFile.getName()+" lines $nlserrors"
+			return -1;
+		} else if (processMode && nTotalChanges > 0) {
+			mustSaveChanges = true;
+			File oldFile = new File(sourceFile.getParentFile(), sourceFile.getName()+".cpy");
+			if (!sourceFile.renameTo(oldFile)) {
+				println "Failed to backup old source file $sourceFile to $oldFile"
+				return 0;
+			} else {
+				sourceFile.withWriter("UTF-8") { writer ->
+					for(String l : linesToWrite) {
+						writer.println l
+					}
+				}
+			}
+		}
+		return nTotalChanges
+	}
+	
+	private int processLine() {
+		int nChanges = 0;
+		int ichar = 0;
+		for (int i = 0 ; i < strings.size() ; i++) {
+			ichar = line.indexOf(strings[i], ichar);
+			if (toExternalize.contains(i)) {
+				println "Line $nline : $strings $nonnls $toNLSize $toExternalize"
+				nChanges++;
+				String value = strings[i].substring(1,strings[i].length() -1)
+				if (dict.containsValue(value)) {
+					String str = "Messages."+dict.getKey(value)
+					println " automatic replace "+strings[i]+" with Messages."+dict.getKey(value);
+					String p1 = line.substring(0, ichar);
+					String p2 = line.substring(ichar+strings[i].length(), line.length())
+					println " old: $line"
+					line = p1+str+p2
+					println " new: $line"
+				} else {
+					String key = getNextKey();
+					String ok = JOptionPane.showInputDialog(null, "Key for value=$value\n\n$previousLine\n$line\n", key);
+					if(ok == null) {
+						println " add new NON-NLS "+(i+1)
+						println " old: $line"
+						line += " //\$NON-NLS-"+(i+1)+"\$"
+						println " new: $line"
+					} else {
+						println " add new key: $key"
+						dict.put(key, value);
+
+						String p1 = line.substring(0, ichar);
+						String p2 = line.substring(ichar+strings[i].length(), line.length())
+						String str = "Messages."+key
+						println " old: $line"
+						line = p1+str+p2
+						println " new: $line"
+					}
+				}
+			} else if (toNLSize.contains(i)) {
+				println "Line $nline : $strings $nonnls $toNLSize $toExternalize"
+				nChanges++;
+				println " add automatic new NON-NLS "+(i+1)
+				println " old: $line"
+				line += " //\$NON-NLS-"+(i+1)+"\$"
+				println " new: $line"
+			}
+		}
+		return nChanges;
+	}
+
+	private def getNONNLS(String l) {
+		def idx = []
+		def m = l =~ /\/\/.NON-NLS-[0-9]\$/
+		m.each { idx << Integer.parseInt(it[11])-1 }
+		return idx.sort()
+	}
+
+	private def getNextKey() {
+		String tmp = prefix+(iprefix++);
+		while (dict.containsKey(tmp)) {
+			tmp = prefix+(iprefix++);
+		}
+		return tmp;
+	}
+	
+	int getNbOfChanges(File sourceFile) {
+		return processSourceFile(sourceFile, false);
+	}
+	
+	int process(File sourceFile) {
+		return processSourceFile(sourceFile, true);
+	}
+	
+	public boolean saveChanges() {
+		if (mustSaveChanges ) {
+			mustSaveChanges = false;
+			dict.saveChanges();
+			return true;
+		}
+		return false;
+	}
+
+	public static void main(String[] args) {
+		File sourceFile = new File("/home/mdecorde/workspace37/org.txm.core/src/java/org/txm/functions/cooccurrences/Cooccurrence.java");
+		File propFile = new File("/home/mdecorde/workspace37/org.txm.core/src/java/org/txm/messages.properties")
+		File messageFile = new File("/home/mdecorde/workspace37/org.txm.core/src/java/org/txm/Messages.java")
+
+		ExternalizeStringFile externalizer = new ExternalizeStringFile(propFile, messageFile)
+		println externalizer.getNbOfChanges(sourceFile);
+		println externalizer.process(sourceFile);
+		externalizer.saveChanges();
+	}
+}
\ No newline at end of file
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/IsStringMissingInPlugin.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/IsStringMissingInPlugin.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/IsStringMissingInPlugin.groovy	(revision 1223)
@@ -0,0 +1,54 @@
+package org.txm.rcp.translate.i18n
+
+/**
+ * Display the missing i18n keys of the plugin.xml file 
+ * 
+ * @author mdecorde
+ *
+ */
+class IsStringMissingInPlugin {
+	
+	public static void main(String[] args) {
+		String userhome = System.getProperty("user.home")
+		File propertyFile = new File(userhome, "workspace43/org.txm.rcp/OSGI-INF/l10n/bundle_fr.properties");
+		File copyFile = new File(userhome, "workspace43/org.txm.rcp/OSGI-INF/l10n/bundle_copy.properties");
+		File pluginFile = new File(userhome, "workspace43/org.txm.rcp/plugin.xml");
+		if (!(propertyFile.exists() && propertyFile.canRead() && propertyFile.canWrite() && propertyFile.isFile()))
+		{
+			println "error file : "+propertyFile
+			return
+		}
+
+		println "Get properties of "+propertyFile
+		Properties props = new Properties();
+		propertyFile.withReader("iso-8859-1") { input->
+			props.load(input);
+			input.close()
+		}
+
+		
+		String pluginXMLcontent = pluginFile.getText();
+		def pluginKeys = [];
+		def matcher = pluginXMLcontent =~ /"%([.A-Za-z0-9]+)+"/ // %key.of.the.field
+		println "Comparing keys "+matcher.pattern() +" of "+pluginFile+" with those declared"
+		matcher.each { // iterates of %key in the plugin.xml content
+			def key = it[1]
+			pluginKeys << key
+			if (!props.containsKey(key)) {
+				println "Prop file does not contains the key: $key"
+				//println " adding default value."
+				props.setProperty(key, "NA")
+			}
+		} 
+		
+		for (String key : props.keySet()) {
+			if (!pluginKeys.contains(key)) {
+				println "plugin.xml does not use the key: "+key
+			}
+		}
+		
+		propertyFile.withWriter("iso-8859-1") { output ->
+			props.store(output, "")
+		}
+	}
+}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FindDuplicatedKeyProperties.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FindDuplicatedKeyProperties.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FindDuplicatedKeyProperties.groovy	(revision 1223)
@@ -0,0 +1,115 @@
+package org.txm.rcp.translate.i18n
+
+import org.txm.utils.LS;
+import org.txm.utils.io.IOUtils;
+
+
+def javaFileToPropertieFiles = [:]
+def propertieFileToJavaFile = [:]
+
+def langs = ["", "_fr", "_ru"] // lang pattern
+
+public File getSRCDirectory(File projectDirectory) {
+	def commons = ["src/main/java", "src/java", "src"]
+	for (String root : commons) {
+		if (new File(projectDirectory, root).exists()) {
+			return new File(projectDirectory, root)
+		}
+	}
+	return projectDirectory;
+}
+
+File workspace = new File(System.getProperty("user.home"), "workspace079");
+def allProjectDirectories = LS.list(workspace, false, false)
+def allProjectSRCDirectories = []
+for (File projectDirectory : allProjectDirectories) {
+	allProjectSRCDirectories << getSRCDirectory(projectDirectory)
+}
+
+// find Messages.java files
+def allJavaFiles = []
+for (File projectSRCDirectory : allProjectSRCDirectories) {
+	allJavaFiles.addAll(LS.list(projectSRCDirectory, true, false))
+}
+allJavaFiles = allJavaFiles.findAll { it -> return it.getName().endsWith("Messages.java") }
+
+// for each Java file find its properties files
+for (File javaFile : allJavaFiles) {
+	println javaFile
+	def otherFiles = LS.list(javaFile.getParentFile(), false, false)
+	otherFiles = otherFiles.findAll { it -> return it.getName().endsWith(".properties") && it.getName().startsWith("messages") }
+	println otherFiles
+	javaFileToPropertieFiles[javaFile] = otherFiles
+	for (File o : otherFiles) {
+		propertieFileToJavaFile[o] = javaFile
+	}
+}
+
+// for each properties file, load its properties
+def propertiesToMessageFile = [:]
+def messagesFileToProperties = [:]
+
+def messageToProperties = [:]
+def messageKeyToProperties = [:]
+
+for (def file : propertieFileToJavaFile.keySet()) {
+	Properties newProperties = new Properties();
+	newProperties.load(IOUtils.getReader(file, "UTF-8"));
+	
+	propertiesToMessageFile[file] = newProperties;
+	messagesFileToProperties[newProperties] = file;
+	
+	for (String kmessage : newProperties.keySet()) {
+		def message = newProperties[kmessage]
+		if (!messageToProperties.containsKey(message)) messageToProperties[message] = []
+		messageToProperties[message] << newProperties
+		if (!messageKeyToProperties.containsKey(message)) messageKeyToProperties[message] = []
+		messageKeyToProperties << newProperties
+	}
+}
+
+
+//////////////////
+for (def lang : langs) {
+	def pattenr = /messages$lang\.properties/
+
+	def allPropertiesFiles = []
+	for (File projectSRCDirectory : allProjectSRCDirectories) {
+		allPropertiesFiles.addAll(LS.list(projectSRCDirectory, true, false))
+	}
+	def propertiesFiles = []
+	for (def file : allPropertiesFiles) {
+		if (file.getName() ==~ pattenr) {
+			propertiesFiles << file
+		}
+	}
+	println "\n*** '$lang' properties files in $workspace***\n"
+
+	def messages = [:]
+	for (def file : propertiesFiles) {
+		Properties newProperties = new Properties();
+		newProperties.load(IOUtils.getReader(file, "UTF-8"));
+		messages[file] = newProperties;
+	}
+
+	def allKeys = new HashSet();
+	for (def file : propertiesFiles) {
+		for (def keys : messages[file].keySet()) {
+			allKeys.addAll(keys)
+		}
+	}
+	allKeys = allKeys.sort()
+
+	for (def key : allKeys) {
+		def values = []
+		for (def file : propertiesFiles) {
+			def v = messages[file][key];
+			if (v != null) {
+				values << file.getAbsolutePath()
+			}
+		}
+		if (values.size() > 1) {
+			println "'$key': "+values.join("\t")
+		}
+	}
+}
\ No newline at end of file
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizationFilesUpdater.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizationFilesUpdater.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizationFilesUpdater.groovy	(revision 1223)
@@ -0,0 +1,313 @@
+// Copyright © 2010-2013 ENS de Lyon.
+// Copyright © 2007-2010 ENS de Lyon, CNRS, INRP, University of
+// Lyon 2, University of Franche-Comté, University of Nice
+// Sophia Antipolis, University of Paris 3.
+// 
+// The TXM platform 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,
+// either version 2 of the License, or (at your option) any
+// later version.
+// 
+// The TXM platform 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 the TXM platform. If not, see
+// http://www.gnu.org/licenses.
+
+//
+// This file is part of the TXM platform.
+//
+// The TXM platform is free software: you can redistribute it and/or modif y
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The TXM platform 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 the TXM platform.  If not, see <http://www.gnu.org/licenses/>.
+// 
+// 
+// 
+// $LastChangedDate: 2013-07-25 16:24:13 +0200 (jeu. 25 juil. 2013) $
+// $LastChangedRevision: 2490 $
+// $LastChangedBy: mdecorde $ 
+//
+package org.txm.rcp.translate.i18n;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+
+// TODO: Auto-generated Javadoc
+/**
+ * Tool to update the key of externalization files xxxx_fr.properties
+ * adds _NA to missing keys
+ * 
+ * @author mdecorde
+ * 
+ */
+public class ExternalizationFilesUpdater {
+
+	/** The dirfiles. */
+	LinkedList<File> dirfiles = new LinkedList<File>();
+	
+	/** The propertyfiles. */
+	HashMap<String, File> propertyfiles = new HashMap<String, File>();
+	
+	/** The fileentries. */
+	HashMap<File, List<String>> fileentries = new HashMap<String, List<String>>();
+	
+	/** The fileentriesvalues. */
+	HashMap<File, HashMap<String, String>> fileentriesvalues = new HashMap<String, HashMap<String, String>>();
+
+	/**
+	 * Gets the entries.
+	 *
+	 * @param file the file
+	 * @return the entries
+	 */
+	public List<String> getEntries(File file)
+	{
+		List<String> entries = [];
+		BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file) , "iso-8859-1"));
+		String line = reader.readLine();
+		while (line != null) {
+			String[] split = line.split("=", 2);
+			if (split.length > 0) {
+				String key = split[0];
+				entries.add(key);
+			}
+			line = reader.readLine();
+		}
+		return entries;
+	}
+
+	/**
+	 * Gets the values.
+	 *
+	 * @param file the file
+	 * @return the values
+	 */
+	public HashMap<String, String> getValues(File file) {
+		HashMap<String, String> values = new HashMap<String, String>();
+		BufferedReader reader = new BufferedReader(new InputStreamReader(
+				new FileInputStream(file), "iso-8859-1"));
+		String line = reader.readLine();
+		while (line != null) {
+			String[] split = line.split("=");
+			if (split.length > 0) {
+				String key = split[0];
+				String concat = "";
+				for (int i = 1; i < split.length; i++)
+					concat += split[i];
+				values.put(key, concat);
+			}
+			line = reader.readLine();
+		}
+		return values;
+	}
+
+	/**
+	 * Process found directories.
+	 */
+	public void processFoundDirectories()
+	{
+		for (String dirfile : dirfiles) {
+			println "DIRECTORY : "+dirfile
+			List<File> files = propertyfiles.get(dirfile)
+			for (File f : files) {
+				this.fileentries.put(f, getEntries(f));
+				this.fileentriesvalues.put(f, getValues(f));
+			}
+			File reference = null;
+			if (files.get(0).getName().startsWith("m")) {
+				reference = new File(dirfile,"messages.properties");
+			}
+			else if (files.get(0).getName().startsWith("b"))
+				reference = new File(dirfile,"bundle.properties");
+			
+			if (reference != null && reference.exists()) {
+				for (File f : files) {
+					if (f != reference) {
+						List<String> refentries = this.fileentries.get(reference).clone();
+						List<String> tmp1 = this.fileentries.get(f).clone();
+						tmp1.removeAll(refentries);
+						
+						refentries = this.fileentries.get(reference).clone();
+						List<String> tmp = this.fileentries.get(f);
+						refentries.removeAll(tmp);
+						for (String missing : refentries) {
+							this.fileentriesvalues.get(f).put(missing,"N/A_"+this.fileentriesvalues.get(reference).get(missing)); // put entry's value
+						}
+						
+						this.fileentries.put(f, this.fileentries.get(reference)); // update file entries
+						
+						if (tmp1.size() > 0 || refentries.size() > 0)
+							println " "+f.getName()
+						if (tmp1.size() > 0)
+							println "  Removed keys : "+tmp1;
+						if (refentries.size() > 0)
+							println "  Added keys : "+refentries;
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Update files.
+	 */
+	public void updateFiles()
+	{
+		for(String dirfile : dirfiles)
+		{
+			List<File> files = propertyfiles.get(dirfile);
+			for(File f : files)
+			{
+				List<String> entries =  this.fileentries.get(f);
+				HashMap<String, String> values = this.fileentriesvalues.get(f);
+				
+				Writer writer = new OutputStreamWriter(new FileOutputStream(f) , "iso-8859-1");
+				for(String entry : entries)
+				{
+					writer.write(entry+"="+values.get(entry)+"\n");
+				}
+				writer.close()
+			}
+		}
+	}
+
+	/**
+	 * Creates the missing files.
+	 *
+	 * @param suffix the suffix
+	 */
+	public void createMissingFiles(String suffix)
+	{
+		println "Looking for missing messages files "+ suffix;
+		for(String dirfile : dirfiles)
+		{
+			//println "DIRECTORY : "+dirfile
+			File reference = null;
+			String lookingname = "";
+			List<File> files = propertyfiles.get(dirfile)
+			if(files.get(0).getName().startsWith("m"))
+			{
+				reference = new File(dirfile,"messages.properties");
+				lookingname = "messages_"+suffix+".properties"
+			}
+			else if (files.get(0).getName().startsWith("b"))
+			{
+				reference = new File(dirfile,"bundle.properties");
+				lookingname = "bundle_"+suffix+".properties"
+			}
+			
+			boolean create = true;
+			if(reference != null && reference.exists())
+			{
+				for(File f : files)
+				{
+					if(f.getName() == lookingname)
+						create = false;
+				}
+			}
+			if(create)
+			{
+				new File(dirfile,lookingname).createNewFile();
+				println "Create file " +new File(dirfile,lookingname)
+			}
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString()
+	{
+		String rez = "";
+		for(String dirfile : dirfiles)
+		{
+			rez += dirfile+"\n";
+			for(File f : propertyfiles.get(dirfile))
+				rez += "  "+f.getName()+"\n"
+		}
+		return rez;
+	}
+
+	/**
+	 * Scan directory.
+	 *
+	 * @param directory the directory
+	 */
+	public void scanDirectory(File directory)
+	{
+		if(!directory.exists())
+		{
+			println "directory '$directory' does not exists"
+			return;
+		}
+		
+		println "scan directory : "+directory.getAbsolutePath();
+		LinkedList<File> files = new LinkedList<File>();
+		files.add(directory);
+		
+		while(!files.isEmpty())
+		{
+			File current = files.removeFirst();
+			if(current.isDirectory())
+			{
+				List<String> currentpfiles = [];
+				for(File sfile : current.listFiles())
+				{
+					if(sfile.isDirectory())
+						files.add(sfile);
+					else if(sfile.getName().endsWith(".properties") && ( sfile.getName().startsWith("messages") || sfile.getName().startsWith("bundle")) )
+						currentpfiles.add(sfile)
+				}
+				if(currentpfiles.size() > 0)
+				{
+					dirfiles.add(current.getAbsolutePath());
+					propertyfiles.put(current.getAbsolutePath(), currentpfiles);
+				}
+			}
+		}
+	}
+
+	/**
+	 * The main method.
+	 *
+	 * @param args the arguments
+	 */
+	public static void main(String[] args)
+	{
+		String userdir = System.getProperty("user.home");
+
+		println "\nRCP\n";
+		
+		ExternalizationFilesUpdater scanner = new ExternalizationFilesUpdater();
+//		scanner.scanDirectory(new File(userdir, "workspace37/org.txm.rcp")); // find directories with a messages.properties file
+//		scanner.createMissingFiles("fr"); // create messages_fr.properties files when a messages.properties is found
+//		scanner.processFoundDirectories(); // find missing and obsolets keys
+//		scanner.updateFiles(); // update messages files content
+		
+		println "\nTOOLBOX\n";
+		
+		scanner = new ExternalizationFilesUpdater();
+		scanner.scanDirectory(new File(userdir, "workspace43/org.txm.rcp/src/main/java"));
+		scanner.createMissingFiles("fr");
+		scanner.createMissingFiles("ru");
+		scanner.processFoundDirectories();
+		scanner.updateFiles();
+	}
+}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/I18nDiff.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/I18nDiff.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/I18nDiff.groovy	(revision 1223)
@@ -0,0 +1,84 @@
+package org.txm.rcp.translate.i18n
+
+import java.io.File;
+import java.util.HashMap;
+/**
+ * Display a diff of 2 message files
+ * 
+ * @author mdecorde
+ *
+ */
+class I18nDiff {
+	Properties values1, values2;
+	File file1;
+	File file2;
+
+	def notInValues1, notInValues2
+	I18nDiff(File file1, File file2) {
+		this.file1 = file1
+		this.file2 = file2
+		values1 = getValues(file1)
+		values2 = getValues(file2)
+
+		def inter = values1.keySet().intersect(values2.keySet());
+		notInValues1 = values2.keySet().minus(values1.keySet());
+		notInValues2 = values1.keySet().minus(values2.keySet());
+
+		println "not in $file1"
+		for(String key : notInValues1)
+			if (key.trim().length() > 0)
+				println key+" = "+values2.get(key)
+
+		println "not in $file2"
+		for(String key : notInValues2)
+			if (key.trim().length() > 0)
+				println key+" = "+values1.get(key)
+
+		//		println "values diff"
+		//
+		//		for(String key : inter ) {
+		//			if(values1.get(key) != values2.get(key))
+		//				println key+" = "+values1.get(key)+" != "+values2.get(key)
+		//		}
+	}
+
+	public void synchronizeFromF1toF2() {
+		
+		for(String key : notInValues2) {
+			if (key.trim().length() > 0)
+				values2.setProperty(key, "NA_"+values1.get(key));
+		}
+		
+		for(String key : notInValues1)
+			if (key.trim().length() > 0)
+				values2.remove(key)
+				
+		file2.withWriter("iso-8859-1") { writer ->
+			values2.store(writer, "")
+		}
+	}
+
+	/**
+	 * Gets the values.
+	 *
+	 * @param file the file
+	 * @return the values
+	 */
+	public Properties getValues(File file) {
+		Properties props = new Properties();
+		file.withReader("iso-8859-1") { input->
+			props.load(input);
+			input.close()
+		}
+		return props;
+	}
+
+	public static void main(String[] args) {
+		String userhome = System.getProperty("user.home");
+		File f1 = new File(userhome, "workspace43/org.txm.rcp/OSGI-INF/l10n/bundle.properties")
+		File f2 = new File(userhome, "workspace43/org.txm.rcp/OSGI-INF/l10n/bundle_fr.properties")
+		
+		I18nDiff diff = new I18nDiff(f1, f2);
+		diff.synchronizeFromF1toF2()
+	}
+}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/MergeExternalizedStringFiles.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/MergeExternalizedStringFiles.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/MergeExternalizedStringFiles.groovy	(revision 1223)
@@ -0,0 +1,293 @@
+package org.txm.rcp.translate.i18n
+
+import java.io.File;
+import org.eclipse.osgi.util.NLS;
+import org.txm.utils.DeleteDir;
+
+/**
+ * Scan for *.properties files and merge them into one
+ * @author mdecorde
+ *
+ */
+class MergeExternalizedStringFiles {
+	
+	ArrayList<File> alldirs;
+	LinkedHashMap<File, ArrayList> data = [:];
+	
+	public MergeExternalizedStringFiles(File projectDir)
+	{
+		alldirs = scanDirectory(projectDir);
+		alldirs.sort{it.getAbsolutePath();}
+		//System.out.println("All dirs size: "+alldirs.size);
+		findMessageClasses();
+		mergeAll()
+	}
+	
+	public findMessageClasses()
+	{
+		File messageClass;
+		ArrayList<File> externalizeMessages;
+		for(int i = 0 ; i < alldirs.size() ; i++) {
+			File dir = alldirs.get(i)
+			if (!dir.isDirectory())
+				continue;
+			//System.out.println("Dir: "+dir);
+			externalizeMessages = new ArrayList<File>();
+			for (File f : dir.listFiles()) {
+				if (f.getName() == "Messages.java")
+					messageClass = f
+				if (f.getName() =~ "messages(_..)?\\.properties")
+					externalizeMessages << f
+			}
+			
+			if (messageClass != null) {
+				String bundle = findBundle(messageClass);
+				data.put(dir, [messageClass, bundle, externalizeMessages])
+			} else {
+				alldirs.remove(i);
+				i--;
+			}
+		}
+		println "all dirs size: "+alldirs.size()
+		//		for(def f : alldirs)
+		//			println f
+	}
+	
+	public String findBundle(File message)
+	{
+		String ret;
+		message.eachLine("iso-8859-1") {
+			if (it.contains("private static final String BUNDLE_NAME = ")) {
+				int start = it.indexOf("\"")
+				int end = it.lastIndexOf("\"")
+				ret = it.substring(start+1, end)
+			}
+		}
+		return ret;
+	}
+	
+	/**
+	 * Scan directory.
+	 *
+	 * @param directory the directory
+	 */
+	public ArrayList<File> scanDirectory(File directory)
+	{
+		ArrayList<File> dirfiles = [];
+		
+		if (!directory.exists()) {
+			println "directory '$directory' does not exists"
+			return;
+		}
+		
+		println "scan directory : "+directory.getAbsolutePath();
+		LinkedList<File> files = new LinkedList<File>();
+		files.add(directory);
+		
+		while (!files.isEmpty()) {
+			File current = files.removeFirst();
+			if (current.isDirectory() && !current.getName().startsWith(".")) {
+				//println "add dir: "+current
+				List<String> currentpfiles = [];
+				for (File sfile : current.listFiles()) {
+					if (sfile.isDirectory())
+						files.add(sfile);
+				}
+				
+				dirfiles.add(current);
+			}
+		}
+		return dirfiles;
+	}
+	
+	public void mergeAll()
+	{
+		def files = new ArrayList<File>(data.keySet());
+		println files
+		if (files.size() < 1)
+			return;
+		
+		File refDir = files.get(0);
+		File refClass = data.get(refDir)[0];
+		String refBundle = data.get(refDir)[1];
+		def externalized = data.get(refDir)[2];
+		
+		System.out.println("ref dir: "+refDir);
+		System.out.println("ref class: "+refClass);
+		System.out.println("ref bundle: "+refBundle);
+		System.out.println("ref files: "+externalized);
+		
+		// get externalized ref strings
+		//      Lang            Key     externalized string 
+		HashMap<String, HashMap<String, String>> externalizedLines = [:];
+		for (File source : externalized) {
+			def lines = new HashMap<String, String>();
+			externalizedLines.put(source.getName(), lines);
+			source.eachLine("iso-8859-1"){
+				if (it =~ ".+=.+") {
+					int idx = it.indexOf("=");
+					lines.put(it.substring(0, idx), it.substring(idx+1))
+				} else if(it =~ ".+=\$") {
+					int idx = it.indexOf("=")
+					String key = it.substring(0, idx)
+					String value = ""
+					lines.put(key, value)
+				}
+			}
+		}
+		
+		// get references keys
+		HashSet<String> refKeys = new HashSet<String>();
+		refClass.eachLine("iso-8859-1") {
+			if (it =~ ".+public static String .+")
+				refKeys << it.trim()
+		}
+		
+		println "BEFORE"
+		println "refKeys size: "+refKeys.size();
+		println "fr externalizedLines size: "+externalizedLines.get("messages_fr.properties").size();
+		println "en externalizedLines size: "+externalizedLines.get("messages.properties").size();
+		
+		// for all sub dir
+		for (int i = 1 ; i < files.size() ; i++) {
+			File srcDir = files.get(i);
+			File srcClass = data.get(srcDir)[0];
+			String srcBundle = data.get(srcDir)[1];
+			
+			def srcExternalized = data.get(srcDir)[2];
+			// merge externalized strings
+			for (File source : srcExternalized) {
+				def lines = externalizedLines.get(source.getName());
+				source.eachLine("iso-8859-1") {
+					if (it =~ ".+=.+") {
+						int idx = it.indexOf("=")
+						String key = it.substring(0, idx)
+						String value = it.substring(idx+1)
+						if (lines.containsKey(key) && lines.get(key) != value)
+							println "merged message of "+source.getName()+": "+it+ " >> "+lines.get(key)
+						lines.put(key, value)
+					} else if(it =~ ".+=\$") {
+						int idx = it.indexOf("=")
+						String key = it.substring(0, idx)
+						String value = ""
+						lines.put(key, value)
+					}
+				}
+				source.delete(); // bye bye *.properties files
+			}
+						
+			// merge static String
+			if(!srcClass.exists())
+			{
+				println "file removed: "+srcClass
+				println  srcDir
+				println  srcClass
+				println  srcBundle
+				println  srcExternalized
+				continue;
+			}
+			srcClass.eachLine("iso-8859-1"){
+				if(it =~ ".+public static String .+")
+				{
+//					if(refKeys.contains(it.trim()))
+//						println "Externalized key merged: "+it
+					refKeys << it.trim()
+				}
+			}
+			srcClass.delete(); // bye bye Messages.java
+		}
+		
+		println "AFTER"
+		println "refKeys size: "+refKeys.size();
+		println "fr externalizedLines size: "+externalizedLines.get("messages_fr.properties").size();
+		println "en externalizedLines size: "+externalizedLines.get("messages.properties").size();
+		
+		//check unused externalized strings
+		def keysDeclared = refKeys.collect{it.substring(21, it.length() -1) };
+		System.out.println("number of keys declared in Message.java: "+keysDeclared.size());
+		def missingDeclarations = new HashMap<String, HashSet<String>>();
+		def missingExternalizations = new HashMap<String, HashSet<String>>();
+		for(def lang : externalizedLines.keySet())
+		{
+			def keys = externalizedLines.get(lang).keySet(); // keys  in propeties files
+			missingExternalizations.put(lang, new HashSet<String>());
+			missingDeclarations.put(lang, new HashSet<String>());
+			for(String key : keys)
+				if(!keysDeclared.contains(key))
+					missingDeclarations.get(lang) << key
+					
+			for(String key : keysDeclared)
+				if(!keys.contains(key))
+					missingExternalizations.get(lang) << key
+		}
+		
+		println "fix missing key declaration and externalization"
+		for(String lang: missingExternalizations.keySet())
+		{
+			println "Lang: "+lang
+			println " missingDeclarations: "+missingDeclarations.get(lang)
+			for(String key : missingDeclarations.get(lang))
+				refKeys << "public static String "+key+";"
+			println " missingExternalizations: "+missingExternalizations.get(lang)
+			for(String key : missingExternalizations.get(lang))
+				externalizedLines.get(lang).put(key, "N/A")
+		}
+		
+		println "AFTER FIX"
+		println "refKeys size: "+refKeys.size();
+		println "fr externalizedLines size: "+externalizedLines.get("messages_fr.properties").size();
+		println "en externalizedLines size: "+externalizedLines.get("messages.properties").size();
+		
+	
+		// write externalized lines
+		for(File source : externalized)
+		{
+			source.withWriter("iso-8859-1") { out ->
+				def hash = externalizedLines.get(source.getName())
+				def keys = hash.keySet();
+				keys = keys.sort()
+				keys.each() { 
+					out.writeLine(it+"="+hash.get(it))
+				}
+			}
+		}
+
+		// write classRef File
+		refClass.withWriter("iso-8859-1") { out ->			
+			// write start
+			out.writeLine('''
+package org.txm.scripts.;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+
+	// The Constant BUNDLE_NAME. 
+	private static final String BUNDLE_NAME = "org.txm.messages"; //$NON-NLS-1$
+	
+				''')
+			
+			//write keys
+			for(String key : refKeys.sort())
+				out.writeLine("\t"+key);
+			
+			// write end
+			out.writeLine('''
+	static {
+		// initialize resource bundle
+		NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+	}
+
+	private Messages() {
+	}
+}
+				''')
+		}
+	}
+	
+	public static void main(String[] args)
+	{
+		File projectdir = new File("/home/mdecorde/workspace37/org.txm.core/src/java")
+		def rez = new MergeExternalizedStringFiles(projectdir);
+	}
+}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/IsStringUsedInPlugin.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/IsStringUsedInPlugin.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/IsStringUsedInPlugin.groovy	(revision 1223)
@@ -0,0 +1,63 @@
+package org.txm.rcp.translate.i18n
+
+/**
+ * Display the String and key not used in the plugin messages
+ * @author mdecorde
+ *
+ */
+class IsStringUsedInPlugin {
+	Properties props;
+
+	public boolean scan(File propertyFile, File pluginFile) {
+		if(!(propertyFile.exists() && propertyFile.canRead() && propertyFile.canWrite() && propertyFile.isFile()))
+		{
+			println "error file : "+propertyFile
+			return false;
+		}
+
+		props = new Properties();
+		propertyFile.withReader("iso-8859-1") { input->
+			props.load(input);
+			input.close()
+		}
+
+		String content = pluginFile.getText();
+		def toRemove = [];
+		for(def key : props.keySet()) {
+			if (!content.contains("\"%"+key+"\"")) {
+				println "not used $key "+props.get(key);
+				toRemove << key
+			}
+		}
+
+		for(String key : toRemove) {
+			props.remove(key);
+		}
+
+
+		if (toRemove == 0) {
+			println "nothing to do"
+			return false;
+		}
+		return true
+	}
+
+	public boolean writeFixes(File propertyFile) {
+		if (props == null) return false;
+		propertyFile.withWriter("iso-8859-1") { output ->
+			props.store(output, "")
+		}
+		return true;
+	}
+
+	public static void main(String[] args) {
+		String userhome = System.getProperty("user.home")
+		File propertyFile = new File(userhome, "workspace43/org.txm.rcp/OSGI-INF/l10n/bundle.properties");
+		File pluginFile = new File(userhome, "workspace43/org.txm.rcp/plugin.xml");
+		IsStringUsedInPlugin fixer = new IsStringUsedInPlugin();
+		//uncomment to remove unused Strings
+		if (fixer.scan(propertyFile, pluginFile)) {
+			fixer.writeFixes(propertyFile)
+		}
+	}
+}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizerUI.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizerUI.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ExternalizerUI.groovy	(revision 1223)
@@ -0,0 +1,145 @@
+package org.txm.rcp.translate.i18n;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.table.*
+import java.awt.GridLayout
+import java.awt.event.*
+
+import org.txm.utils.LS;
+/**
+ * This replace the eclipse externalisation interface
+ * Scan the java files for not translated Strings and update the messages.properties file
+ * 
+ * uses ExternalizeStringFile
+ * @author mdecorde
+ *
+ */
+class ExternalizeUI extends JPanel {
+	ExternalizeStringFile externalizer;
+
+	ExternalizeUI() {
+		super(new FlowLayout());
+		File userDir = new File(System.getProperty("user.home"))
+		File srcDir = new File(userDir, "workspace47/org.txm.rcp/src/main/java")
+		File propFile = new File(srcDir, "org/txm/rcp/messages.properties")
+		File messageFile = new File(srcDir, "org/txm/rcp/Messages.java")
+
+		println userDir
+		println srcDir
+		println propFile
+		println messageFile
+
+		String[] columnNames = ["Files in "+srcDir.getPath(), "Nb of changes"];
+
+
+		def files = LS.list(srcDir, true, false);
+		files = files.findAll { file -> file.getName().endsWith(".java") }
+		files = files.sort()
+		println files
+
+		externalizer = new ExternalizeStringFile(propFile, messageFile);
+		println "externalizer ready"
+		def data = []
+		def selectedFiles = []
+		for (File file : files) {
+			int count = externalizer.getNbOfChanges(file)
+			if (count != 0)	{
+				selectedFiles << file
+				data << [file.getPath().substring(srcDir.getPath().length()), count]
+			}
+		}
+		Object[][] arrayData = data;
+		JTable table = new JTable(arrayData, columnNames);
+		table.getColumnModel().getColumn(0).setPreferredWidth(600);
+		table.getColumnModel().getColumn(1).setPreferredWidth(100);
+
+		table.addMouseListener(new MouseAdapter() {
+					@Override
+					public void mouseClicked(MouseEvent e) {
+						if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 1) {
+							int selectedRowIndex = table.getSelectedRow();
+							int c = externalizer.process(selectedFiles[selectedRowIndex])
+							println ""+c
+							int after = table.getValueAt(table.getSelectedRow(), 1);
+							table.setValueAt(after - c, table.getSelectedRow(), 1);
+							//show your dialog with the selected row's contents
+						}
+					}
+				});
+
+		JScrollPane scrollPane = new JScrollPane(table);
+		table.setFillsViewportHeight(true);
+
+		add(scrollPane)
+
+		JButton cancelButton = new JButton("CANCEL");
+		JButton okButton = new JButton("OK + SAVE");
+		okButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e)
+			{
+				ExternalizeUI.this.doSave();
+				System.exit(0);
+			}
+		});
+		
+		cancelButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e)
+			{
+				println "Changes not saved: "
+				System.exit(0);
+			}
+		});
+		cancelButton.setSize(100, 20);
+		okButton.setSize(100, 20);
+		add(cancelButton)
+		add(okButton)
+
+		println "end of constructor"
+	}
+
+	public void doSave() {
+		if (externalizer != null && externalizer.saveChanges()) {
+			JOptionPane.showMessageDialog(this, "Changes saved");
+		}
+	}
+
+	/**
+	 * Create the GUI and show it.  For thread safety,
+	 * this method should be invoked from the
+	 * event-dispatching thread.
+	 */
+	private static void createAndShowGUI() {
+		//Create and set up the window.
+		JFrame frame = new JFrame("ExternalizeUI");
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+		//Create and set up the content pane.
+		ExternalizeUI newContentPane = new ExternalizeUI();
+		newContentPane.setOpaque(true); //content panes must be opaque
+
+		//		frame.addWindowListener(new java.awt.event.WindowAdapter() {
+		//			public void windowClosing(WindowEvent winEvt) {
+		//				newContentPane.doSave();
+		//				System.exit(0);
+		//			}
+		//		});
+		frame.setContentPane(newContentPane);
+
+		println "display"
+		//Display the window.
+		frame.pack();
+		frame.setVisible(true);
+	}
+
+
+	public static void main(String[] args) {
+		//Schedule a job for the event-dispatching thread:
+		//creating and showing this application's GUI.
+		javax.swing.SwingUtilities.invokeLater(new Runnable() {
+					public void run() {
+						createAndShowGUI();
+					}
+				});
+	}
+}
\ No newline at end of file
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/GetStringsFromPropertiesFile.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/GetStringsFromPropertiesFile.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/GetStringsFromPropertiesFile.groovy	(revision 1223)
@@ -0,0 +1,43 @@
+package org.txm.rcp.translate.i18n;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+
+import org.txm.utils.io.IOUtils;
+
+File newProjectMessagesPackage= new File(System.getProperty("user.home"), "workspace079/org.txm.concordance.core/src/org/txm/concordances/messages");
+File oldProjectMessagesPackage= new File(System.getProperty("user.home"), "workspace079/org.txm.core/src/java/org/txm/core/messages")
+File newNLSClassFile = new File(newProjectMessagesPackage, "ConcordanceMessages.java");
+File oldNLSClassFile = new File(oldProjectMessagesPackage, "TBXMessages.java");
+def langs = ["_fr", "", "_ru"]
+for (def lang : langs) {
+	println "LANG=$lang"
+	Properties oldProperties = new Properties();
+	File oldMessageFile = new File(oldProjectMessagesPackage, "messages${lang}.properties")
+	oldProperties.load(IOUtils.getReader(oldMessageFile, "UTF-8"));
+	Properties newProperties = new Properties();
+	File newMessageFile = new File(newProjectMessagesPackage, "messages${lang}.properties")
+	if (newMessageFile.exists())
+		newProperties.load(IOUtils.getReader(newMessageFile, "UTF-8"));
+		
+	newNLSClassFile.getText("UTF-8").findAll("public static String [^;]+;") { key ->
+		key = key.substring(21, key.length()-1)
+		
+		if (oldProperties.get(key) == null) {
+			println "Key $key is missing from old messages file: "+oldMessageFile.getName()
+			newProperties.put(key, "MISSING")
+		} else {
+			if (newProperties.get(key) != null) {
+				println "update $key = "+oldProperties.get(key)
+			} else {
+				println "add $key = "+oldProperties.get(key)
+			}
+			newProperties.put(key, oldProperties.get(key))
+		}
+	}
+	newProperties.store(IOUtils.getWriter(newMessageFile, "UTF-8"), "created/updated with GetStringsFromPropertiesFile.groovy")
+	println "Done"		
+}
\ No newline at end of file
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FindUntranslatedMessages.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FindUntranslatedMessages.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FindUntranslatedMessages.groovy	(revision 1223)
@@ -0,0 +1,15 @@
+package org.txm.rcp.translate.i18n
+
+import org.txm.rcp.translate.i18n.ExternalizationFilesUpdater
+
+def userdir = System.getProperty("user.home")
+def workspace = new File(userdir, "workspace47")
+def langs = ["fr", "ru"]
+
+		scanner = new ExternalizationFilesUpdater();
+		scanner.scanDirectory(new File(workspace, "org.txm.rcp"));
+		for (def lang : langs) {
+			scanner.createMissingFiles(lang);
+		}
+		scanner.processFoundDirectories();
+		scanner.updateFiles();
\ No newline at end of file
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FixExternalizedDuplicatedStrings.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FixExternalizedDuplicatedStrings.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/FixExternalizedDuplicatedStrings.groovy	(revision 1223)
@@ -0,0 +1,135 @@
+package org.txm.rcp.translate.i18n
+
+import org.txm.utils.DeleteDir;
+
+/**
+ * Browse messages files and look for duplicated values
+ * @author mdecorde
+ *
+ */
+class FixExternalizedDuplicatedStrings {
+	
+	static main(args) {
+//		File propertyFile = new File("/home/mdecorde/Bureau/Experiences/rcp/messages.properties");
+//		File messageClass = new File("/home/mdecorde/Bureau/Experiences/rcp/Messages.java");
+//		File srcdirectory = new File("/home/mdecorde/Bureau/Experiences/rcp");
+		File propertyFile = new File("/home/mdecorde/workspace37/org.txm.rcp/src/main/java/org/txm/rcp/messages.properties");
+		File messageClass = new File("/home/mdecorde/workspace37/org.txm.rcp/src/main/java/org/txm/rcp/Messages.java");
+		File srcdirectory = new File("/home/mdecorde/workspace37/org.txm.rcp/src/main/java/org/txm/rcp");
+		if (!(propertyFile.exists() && propertyFile.canRead() && propertyFile.canWrite() && propertyFile.isFile())) {
+			println "error file : "+propertyFile
+			return
+		}
+		
+		// build hash of externalised Strings
+		//						  key	  externalized string
+		def entries = new HashMap<String, String>()
+		propertyFile.eachLine("iso-8859-1"){
+			if(it =~ ".+=.+")
+			{
+				int idx = it.indexOf("=")
+				String key = it.substring(0, idx)
+				String value = it.substring(idx+1)
+				entries.put(key, value)
+			}
+			else if(it =~ ".+=\$")
+			{
+				int idx = it.indexOf("=")
+				String key = it.substring(0, idx)
+				String value = ""
+				entries.put(key, value)
+			}
+		}
+		
+		// invert hash
+		def hash = entries
+		def reverse = [:]
+		hash.each{
+			if(reverse[it.value] == null)
+				reverse[it.value] = []
+			reverse[it.value] << it.key
+		};
+		
+		for(def key : reverse.keySet())
+			if(reverse[key].size() > 1)
+				println "doublons of $key: "+reverse[key]
+		
+		// translate dictionnary
+		def dictionnary = [:]
+		for(def value : reverse.keySet())
+		{
+			def keys = reverse[value];
+			def first = keys[0];
+			for(int i = 1 ; i < keys.size() ; i++) // the first key will not be removed
+			{
+				def key = keys.get(i)
+				if(key != first)
+				{
+					dictionnary.put(key , first)
+					println key+ " >> "+first
+				}
+			}
+		}
+		
+		// patch source files : iterates over all java files
+		
+		/*def files = DeleteDir.scanDirectory(srcdirectory, true);
+		for(File f : files)
+		{
+			if(f.getName().endsWith(".java") && f.getName() != "Messages.java")
+			{
+				def lines = f.readLines("UTF-8")
+				f.withWriter("UTF-8"){ writer ->
+					
+					for(String line : lines)
+					{
+						for(String key : dictionnary.keySet())
+						{
+							String old = line;
+							//line = line.replaceAll(, "Messages."+dictionnary[key]);
+							line = (line =~ /Messages\.$key([^0-9]|$)/).replaceAll("Messages."+dictionnary[key]+'$1')
+
+							if(old != line)
+								System.out.println(f.getAbsolutePath()+":"+old+": replaced "+"Messages\\."+key+"[^0-9] Messages."+dictionnary[key]);
+						}
+						writer.writeLine(line);
+					}	
+				}
+			}
+		}
+		
+		// remove key declaration from Messages.java
+		String txt = messageClass.getText("UTF-8")
+		String[] split = txt.split("\n");
+		messageClass.withWriter "UTF-8", { writer -> 
+			for(String line : split)
+			{
+				if(line.contains("public static String "))
+				{
+					for(String key : dictionnary.keySet())
+					{
+						if(line.contains("public static String "+key+";"))
+						{
+							line = line.trim()
+							line = null
+							println "remove key: "+key
+							break;
+						}
+					}
+					if(line != null)
+						writer.writeLine(line);
+				}
+				else
+					writer.writeLine(line);
+			}
+		}*/
+		
+		// rewrite messages.properties
+		def keytoRemove = dictionnary.keySet();
+		propertyFile.withWriter("UTF-8") { writer ->
+			for(String key : entries.keySet().sort())
+				if(!keytoRemove.contains(key))
+					writer.writeLine(key+"="+entries[key])
+		}
+	}
+}
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/Merge2PropertiesFile.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/Merge2PropertiesFile.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/Merge2PropertiesFile.groovy	(revision 1223)
@@ -0,0 +1,20 @@
+package org.txm.rcp.translate.i18n
+
+File propFile1 = new File("/home/mdecorde/workspace43/org.eclipse.equinox.p2.ui.nl_fr/org/eclipse/equinox/internal/p2/ui/messages_fr.properties")
+File propFile2 = new File("/home/mdecorde/workspace43/org.txm.rcp.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages_fr.properties")
+
+Properties props1 = new Properties()
+props1.load(propFile1.newReader("iso-8859-1"))
+Properties props2 = new Properties()
+props2.load(propFile2.newReader("iso-8859-1"))
+
+println "props1.size = "+props1.size()
+println "props2.size = "+props2.size()
+
+for( def key : props1.keySet()) {
+	println "update: "+key
+	props2.put(key, props1.get(key))
+}
+
+println "props2.size AFTER = "+props2.size()
+props2.store(propFile2.newWriter("iso-8859-1"), "$propFile1 + $propFile2")
\ No newline at end of file
Index: tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ReverseI18nDict.groovy
===================================================================
--- tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ReverseI18nDict.groovy	(revision 0)
+++ tmp/org.txm.translate.rcp/src/org/txm/rcp/translate/i18n/ReverseI18nDict.groovy	(revision 1223)
@@ -0,0 +1,125 @@
+package org.txm.rcp.translate.i18n
+
+import org.txm.utils.BiHashMap
+
+/**
+ * Bihashmap 
+ * Check any missing key in both of messages.properties and Message.java files
+ * @author mdecorde
+ *
+ */
+class ReverseI18nDict extends BiHashMap<String, String> {
+	File propFile;
+	File messageFile;
+	
+	def messageKeys = new HashSet<String>()
+	
+	public ReverseI18nDict(File propFile) {
+		this(propFile, null)
+	}
+	
+	public ReverseI18nDict(File propFile, File messageFile) {
+		this.propFile = propFile
+		this.messageFile = messageFile
+		BufferedReader reader = new BufferedReader(new InputStreamReader(
+				new FileInputStream(propFile), "iso-8859-1"));
+		String line = reader.readLine();
+		while (line != null) {
+			if (!line.startsWith("#")) {
+				String[] split = line.split("=", 2);
+				if (split.length == 2) {
+					this.put(split[0], split[1]);
+				}
+			}
+			line = reader.readLine();
+		}
+		
+		if (messageFile != null) {
+			BufferedReader reader2 = new BufferedReader(new InputStreamReader(new FileInputStream(messageFile), "iso-8859-1"));
+			String line2 = reader2.readLine();
+			while (line2 != null) {
+				line2 = line2.trim();
+				if (line2.startsWith("public static String")) {
+					line2 = line2.substring(21, line2.length() -1);
+					messageKeys << line2
+				}
+				line2 = reader2.readLine();
+			}
+		}
+	}
+	
+	public def getMissingsMessageKeys() {
+		def missing = []
+		for (String pKey : this.getKeys())
+			if (!messageKeys.contains(pKey))
+				missing << pKey
+				
+		return missing
+	}
+	
+	public getMissingPropKeys() {
+		def missing = []
+		def pKeys = this.getKeys()
+		for (String mKey : messageKeys)
+			if (!pKeys.contains(mKey))
+				missing << mKey
+				
+		return missing
+	}
+	
+	public void saveChanges() {
+
+		//Write prop File
+		File oldFile = new File(propFile.getParentFile(), propFile.getName()+".cpy");
+		propFile.renameTo(oldFile);
+		propFile.withWriter("iso-8859-1") { out ->
+			for(String key : this.getKeys().sort()) {
+				out.println(key+"="+this.get(key))
+			}
+		}
+		
+		// write message File
+		if (messageFile == null) return;
+		
+		File oldFile2 = new File(messageFile.getParentFile(), messageFile.getName()+".cpy");
+		messageFile.renameTo(oldFile2); // back up
+		messageFile.withWriter("iso-8859-1") { out ->
+			// write start
+			out.println('''package org.txm;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+	
+	// The Constant BUNDLE_NAME. 
+	private static final String BUNDLE_NAME = "org.txm.messages"; //$NON-NLS-1$
+				''')
+			
+			//write keys
+			for(String key : this.getKeys().sort())
+				out.println("\tpublic static String $key;");
+			
+			// write end
+			out.println('''
+	static {
+		// initialize resource bundle
+		NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+	}
+
+	private Messages() {
+	}
+}
+				''')
+		}
+	}
+	
+	public static void main(String[] args) {
+		File propFile = new File("/home/mdecorde/workspace43/org.txm.core/src/java/org/txm/messages.properties")
+		File messageFile = new File("/home/mdecorde/workspace43/org.txm.core/src/java/org/txm/Messages.java")
+		ReverseI18nDict dict = new ReverseI18nDict(propFile, messageFile);
+		println dict.getMissingsMessageKeys()
+		println dict.getMissingPropKeys();
+		println dict.toString();
+		//dict.saveChanges();
+	}
+}
