diff --git a/README.md b/README.md
index 63543b5de4c839726cee0204b53e5e2b0816d242..38e46843258af95047a013ac0a6ad039bbf7b75c 100644
--- a/README.md
+++ b/README.md
@@ -76,14 +76,29 @@ Détail de la syntaxe :
 ###### `<référence>` remplace `<référence>` par les lignes commançant par `référence::`
 **Exemple :** 
 ```
-&lt;pre&gt; de passe
-pre::mot
-pre::phrase
+mot&lt;space&gt;de&lt;space&gt;passe
+space::-
+space::_
 ```
 sera remplacé par :
 ```
-mot de passe
-phrase de passe
+mot-de-passe
+mot-de_passe
+mot_de-passe
+mot_de_passe
+```
+
+###### `=référence>` remplace de façon syncroniser pluiseurs `=référence>` par la meme variante des lignes référencées par `référence::`
+**Exemple :** 
+```
+mot=space&gt;de=space&gt;passe
+space::-
+space::_
+```
+sera remplacé par :
+```
+mot-de-passe
+mot_de_passe
 ```
 
 ###### `(variante|autre variante)` décline les séquences listées comme des combinaisons différentes.
diff --git a/public/dictionaryBuilder.js b/public/dictionaryBuilder.js
index 8eb90e5d77a8d57cf24b22cf78c4c82eb6574c16..2e6743bbe628461561858f5b940d52b0f34d53ca 100644
--- a/public/dictionaryBuilder.js
+++ b/public/dictionaryBuilder.js
@@ -1,9 +1,8 @@
-export {dedup,noAccentVariant,casesVariants,regLikeVariants}
+export {dedup,noAccentVariant,casesVariants,regLikeVariants,resetCache}
 
 function dedup(array) {
 	const res = {};
 	array.forEach(v=>res[v]=v);
-	delete res[""];
 	return Object.keys(res);
 }
 function noAccentVariant(string) {
@@ -43,7 +42,8 @@ const specialMap = {
 	'-':String.fromCharCode(0x09),
 	'<':String.fromCharCode(0x0a),
 	'>':String.fromCharCode(0x0b),
-	':':String.fromCharCode(0x0c)
+    ':':String.fromCharCode(0x0c),
+    '=':String.fromCharCode(0x0d)
 };
 const revertSpecial = swapKeyVal(specialMap);
 function escape2utfSpecial(str) {
@@ -63,12 +63,43 @@ function bracketsHandler(theString){
 	theString = theString.replace(/\[([^\]]+)\]/g,(osef,chrs)=> `(${chrs.split('').join('|')})` );
 	return theString;
 }
+function resetCache(){cache();}
+function cache(key,func,...args){
+    if(!cache.cached) cache.cached = {}; // init
+    if(arguments.length === 0) return cache.cached = {}; // reset cache
+    if(typeof cache.cached[key] === "undefined") cache.cached[key] = func(...args); // compute and cache
+    return cache.cached[key]; // answer from cache;
+}
+function labelExpressions(str) {
+    return str.slice( str.indexOf(`::`) + 2);
+}
+function computedLabel(label,allStrings) {
+	return cache(`label::${label}`,_computedLabel,label, allStrings);
+}
+function _computedLabel(label,allStrings) {
+	const matchingLabel = allStrings.filter(str=>str.trim().indexOf(`${label}::`)=== 0);
+	return flatten(matchingLabel.map(str=>_regLikeVariants(labelExpressions(str)),allStrings));
+}
 function refHandler(theString,allStrings) {
-	//TODO: allStringsPreRenderCache
-	// handle <ref>
-	if(theString.indexOf('::')>0) return "";
-	theString = theString.replace(/<([^>]+)>/g,(osef,ref)=> `(${allStrings.filter(str=>str.indexOf(`${ref}::`)!== -1).map(str=>`(${_regLikeVariants(str.slice(`${ref}::`.length),allStrings).join('|')})`).join('|')})` );
-	return theString;
+    // handle <ref>
+    theString = theString.replace(/<([^>]+)>/g,(osef,ref)=> `(${computedLabel(ref,allStrings).join('|')})` );
+    return theString;
+}
+function syncRefHandler(theString,allStrings) {
+    // handle =ref>
+	const syncRef = dedup(theString.match(/=[^>]+>/g)||[]).map(str=>str.substring(1,str.length-1));
+	let results = [theString];
+    for(let ref of syncRef){
+        const variantes = computedLabel(ref,allStrings);
+        let tmpRes = [];
+        for(let v of variantes){
+        	for(let r of results){
+                tmpRes.push(r.replace(new RegExp(`=${ref}>`,'g'),v));
+			}
+		}
+		results = tmpRes;
+    }
+    return results;
 }
 function qtyHandlerReplaceCallback(all,chr,qty){
 	const mm = qty.split(",").map(n=>n.trim()*1);
@@ -87,13 +118,17 @@ function qtyHandler(theString){
 function optionsHandler(theString,allStrings){
 	// handle (|)
 	let multiString = theString.replace(/^(.*)\(([^)]*)\)(.*)$/,(all,before,choices,after)=>choices.split('|').map(c=> before+c+after).join('=$##$=') ).split('=$##$=');
-	console.log(multiString);
 	multiString = [].concat(...multiString.map(str => (str.indexOf('(')!== -1)?_regLikeVariants(str,allStrings):str));
 	return dedup(multiString);
 }
 function _regLikeVariants(theString,allStrings) {
-	theString = bracketsHandler(theString); // handle []
+    if(theString.indexOf('::')>0) return []; // remove label definition lines.
+    theString = bracketsHandler(theString); // handle []
 	theString = refHandler(theString,allStrings);// handle <ref> and ref::
 	theString = qtyHandler(theString); // handle {qty} and {min,max}
-	return optionsHandler(theString,allStrings); // handle (|)
+	const strings = optionsHandler(theString,allStrings); // handle (|)
+    return flatten(strings.map(str=>syncRefHandler(str,allStrings)));// handle =ref> and ref::
 }
+function flatten(arrayOfArray){
+    return dedup([].concat(...arrayOfArray));
+}
\ No newline at end of file
diff --git a/public/dictionaryBuilder.test.js b/public/dictionaryBuilder.test.js
index 57cbef24d7e0ac4e6eb91d187bbfe216b75b8e66..5dcde3c6bd667be2b91406dab6bb40fb02aebb56 100644
--- a/public/dictionaryBuilder.test.js
+++ b/public/dictionaryBuilder.test.js
@@ -1,5 +1,6 @@
 import * as app from "./dictionaryBuilder.js";
 describe('dictionaryBuilder', () => {
+	beforeEach(app.resetCache);
 	it('add no accents variant', () => {
 		expect(app.noAccentVariant("Ǧ1")).toEqual(["Ǧ1","G1"]);
 	});
@@ -12,9 +13,19 @@ describe('dictionaryBuilder', () => {
 	it('regLikeVariants remove ref:: lines', () => {
 		expect(app.regLikeVariants("ref::truc")).toEqual([]);
 	});
-	it('regLikeVariants handle <ref>', () => {
-		expect(app.regLikeVariants("<ref> <ref>",["ref::truc","ref::bidule","<ref> <ref>"])).toEqual(["truc truc","bidule truc", "truc bidule", "bidule bidule"]);
-	});
+    it('regLikeVariants handle <ref>', () => {
+        expect(app.regLikeVariants("<ref> <ref>",["ref::truc","ref::bidule","<ref> <ref>"])).toEqual(["truc truc","bidule truc", "truc bidule", "bidule bidule"]);
+        expect(app.regLikeVariants("<ref> <ref>",["ref::(truc|bidule)","<ref> <ref>"])).toEqual(["truc truc","bidule truc", "truc bidule", "bidule bidule"]);
+    });
+    it('regLikeVariants handle =ref>', () => {
+        expect(app.regLikeVariants("=ref> =ref>",["ref::truc","ref::bidule","=ref> =ref>"])).toEqual(["truc truc","bidule bidule"]);
+        expect(app.regLikeVariants("=ref> =ref>",["ref::(truc|bidule)","=ref> =ref>"])).toEqual(["truc truc","bidule bidule"]);
+    });
+    it('regLikeVariants handle multiple =ref>', () => {
+        expect(app.regLikeVariants("=ref> =ref2> =ref> =ref2>",
+			["ref::(truc|bidule)","ref2::(machin|chose)","=ref> =ref>"]
+		)).toEqual(["truc machin truc machin","bidule machin bidule machin","truc chose truc chose","bidule chose bidule chose"]);
+    });
 	it('regLikeVariants handle (this|that)', () => {
 		expect(app.regLikeVariants("(this|that)")).toEqual(["this","that"]);
 	});
diff --git a/public/index.html b/public/index.html
index a3be1144014c0f5af587ed019c91a7a3c57bba4c..507a603cfef88cb08816b15f0858a6a60960f8f8 100644
--- a/public/index.html
+++ b/public/index.html
@@ -77,6 +77,7 @@ mon super login
     </div>
     <textarea id="pass">
 exemple avancé :
+Mes=space>Secrets=space>Sont=space>Bien=space>Gardés
 space::[ _-.]{0,1}
 (m[o0]t|phrase)<space>de<space>passe
     </textarea>
diff --git a/public/main.js b/public/main.js
index d1030548931576001fb056ee546bde3259f26dea..380b4bd18e5cc036ad4180cb3a62dff56cdfdf56 100644
--- a/public/main.js
+++ b/public/main.js
@@ -40,6 +40,7 @@ function combiTested(event){
 
 function genAction(event,func){
 	const textarea = event.target.parentNode.parentNode.querySelector("textArea");
+	dico.resetCache();
 	textarea.value = dico.dedup(apply(multiLineString2cleanArray(textarea.value),[func])).join("\n");
 	updateEstimate();
 }