Index: gc_core/js/helpers.js
==================================================================
--- gc_core/js/helpers.js
+++ gc_core/js/helpers.js
@@ -1,88 +1,97 @@
 
 // HELPERS
 
 "use strict";
 
-// In Firefox, there is no console.log in PromiseWorker, but there is worker.log.
-// In Thunderbird, you can’t access to console directly. So it’s required to pass a log function.
-let funcOutput = null;
-
-function setLogOutput (func) {
-    funcOutput = func;
-}
-
-function echo (obj) {
-    if (funcOutput !== null) {
-        funcOutput(obj);
-    } else {
-        console.log(obj);
-    }
-    return true;
-}
-
-function logerror (e, bStack=false) {
-    let sMsg = "\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message;
-    if (bStack) {
-        sMsg += "\n--- Stack ---\n" + e.stack;
-    }
-    if (funcOutput !== null) {
-        funcOutput(sMsg);
-    } else {
-        console.error(sMsg);
-    }
-}
-
-
-// load ressources in workers (suggested by Mozilla extensions reviewers)
-// for more options have a look here: https://gist.github.com/Noitidart/ec1e6b9a593ec7e3efed
-// if not in workers, use sdk/data.load() instead
-function loadFile (spf) {
-    try {
-        let xRequest;
-        if (typeof XMLHttpRequest !== "undefined") {
-            xRequest = new XMLHttpRequest();
-        }
-        else {
-            // JS bullshit again… necessary for Thunderbird
-            let { Cc, Ci } = require("chrome");
-            xRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
-            xRequest.QueryInterface(Ci.nsIXMLHttpRequest);
-        }
-        xRequest.open('GET', spf, false); // 3rd arg is false for synchronous, sync is acceptable in workers
-        xRequest.send();
-        return xRequest.responseText;
-    }
-    catch (e) {
-        logerror(e);
-        return null
-    }
-}
-
-
-    
-
-
-// conversions
-function objectToMap (obj) {
-    let m = new Map();
-    for (let param in obj) {
-        //console.log(param + " " + obj[param]);
-        m.set(param, obj[param]);
-    }
-    return m;
-}
-
-function mapToObject (m) {
-    let obj = {};
-    for (let [k, v] of m) {
-        obj[k] = v;
-    }
-    return obj;
-}
-
-exports.echo = echo;
-exports.logerror = logerror;
-exports.objectToMap = objectToMap;
-exports.mapToObject = mapToObject;
-exports.setLogOutput = setLogOutput;
-exports.loadFile = loadFile;
+const helpers = {
+    // In Firefox, there is no console.log in PromiseWorker, but there is worker.log.
+    // In Thunderbird, you can’t access to console directly. So it’s required to pass a log function.
+    _funcOutput: null,
+
+    setLogOutput: function (func) {
+        this._funcOutput = func;
+    },
+
+    echo: function (obj) {
+        if (this._funcOutput !== null) {
+            this._funcOutput(obj);
+        } else {
+            console.log(obj);
+        }
+        return true;
+    },
+
+    logerror: function (e, bStack=false) {
+        let sMsg = "\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message;
+        if (bStack) {
+            sMsg += "\n--- Stack ---\n" + e.stack;
+        }
+        if (this._funcOutput !== null) {
+            this._funcOutput(sMsg);
+        } else {
+            console.error(sMsg);
+        }
+    },
+
+    inspect: function (o) {
+        let sMsg = "__inspect__: " + typeof o;
+        for (let sParam in o) {
+            sMsg += "\n" + sParam + ": " + o.sParam;
+        }
+        sMsg += "\n" + JSON.stringify(o) + "\n__end__";
+        this.echo(sMsg);
+    },
+
+    loadFile: function (spf) {
+        // load ressources in workers (suggested by Mozilla extensions reviewers)
+        // for more options have a look here: https://gist.github.com/Noitidart/ec1e6b9a593ec7e3efed
+        // if not in workers, use sdk/data.load() instead
+        try {
+            let xRequest;
+            if (typeof XMLHttpRequest !== "undefined") {
+                xRequest = new XMLHttpRequest();
+            }
+            else {
+                // JS bullshit again… necessary for Thunderbird
+                let { Cc, Ci } = require("chrome");
+                xRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
+                xRequest.QueryInterface(Ci.nsIXMLHttpRequest);
+            }
+            xRequest.open('GET', spf, false); // 3rd arg is false for synchronous, sync is acceptable in workers
+            xRequest.send();
+            return xRequest.responseText;
+        }
+        catch (e) {
+            this.logerror(e);
+            return null
+        }
+    },
+
+    objectToMap: function (obj) {
+        let m = new Map();
+        for (let param in obj) {
+            //console.log(param + " " + obj[param]);
+            m.set(param, obj[param]);
+        }
+        return m;
+    },
+
+    mapToObject: function (m) {
+        let obj = {};
+        for (let [k, v] of m) {
+            obj[k] = v;
+        }
+        return obj;
+    }
+}
+
+
+if (typeof(exports) !== 'undefined') {
+    exports.setLogOutput = helpers.setLogOutput;
+    exports.echo = helpers.echo;
+    exports.logerror = helpers.logerror;
+    exports.inspect = helpers.inspect;
+    exports.loadFile = helpers.loadFile;
+    exports.objectToMap = helpers.objectToMap;
+    exports.mapToObject = helpers.mapToObject;
+}

Index: gc_core/js/ibdawg.js
==================================================================
--- gc_core/js/ibdawg.js
+++ gc_core/js/ibdawg.js
@@ -286,6 +286,8 @@
         // to do
     };
 }
 
 
-exports.IBDAWG = IBDAWG;
+if (typeof(exports) !== 'undefined') {
+    exports.IBDAWG = IBDAWG;
+}

Index: gc_core/js/lang_core/gc_engine.js
==================================================================
--- gc_core/js/lang_core/gc_engine.js
+++ gc_core/js/lang_core/gc_engine.js
@@ -16,11 +16,10 @@
 
 const ibdawg = require("resource://grammalecte/ibdawg.js");
 const helpers = require("resource://grammalecte/helpers.js");
 const gc_options = require("resource://grammalecte/${lang}/gc_options.js");
 const cr = require("resource://grammalecte/${lang}/cregex.js");
-const text = require("resource://grammalecte/text.js");
 const echo = require("resource://grammalecte/helpers.js").echo;
 
 const lang = "${lang}";
 const locales = ${loc};
 const pkg = "${implname}";
@@ -604,20 +603,21 @@
 
 ${callablesJS}
 
 
 
-
-exports.load = load;
-exports.parse = parse;
-exports.lang = lang;
-exports.version = version;
-exports.getDictionary = getDictionary;
-exports.setOption = setOption;
-exports.setOptions = setOptions;
-exports.getOptions = getOptions;
-exports.getDefaultOptions = getDefaultOptions;
-exports.resetOptions = resetOptions;
-exports.ignoreRule = ignoreRule;
-exports.reactivateRule = reactivateRule;
-exports.resetIgnoreRules = resetIgnoreRules;
-exports.listRules = listRules;
+if (typeof(exports) !== 'undefined') {
+    exports.load = load;
+    exports.parse = parse;
+    exports.lang = lang;
+    exports.version = version;
+    exports.getDictionary = getDictionary;
+    exports.setOption = setOption;
+    exports.setOptions = setOptions;
+    exports.getOptions = getOptions;
+    exports.getDefaultOptions = getDefaultOptions;
+    exports.resetOptions = resetOptions;
+    exports.ignoreRule = ignoreRule;
+    exports.reactivateRule = reactivateRule;
+    exports.resetIgnoreRules = resetIgnoreRules;
+    exports.listRules = listRules;
+}

Index: gc_core/js/lang_core/gc_options.js
==================================================================
--- gc_core/js/lang_core/gc_options.js
+++ gc_core/js/lang_core/gc_options.js
@@ -17,8 +17,11 @@
     "Thunderbird": new Map (${dOptThunderbird}),
 }
 
 const dOptLabel = ${dOptLabel};
 
-exports.getOptions = getOptions;
-exports.lStructOpt = lStructOpt;
-exports.dOptLabel = dOptLabel;
+
+if (typeof(exports) !== 'undefined') {
+	exports.getOptions = getOptions;
+	exports.lStructOpt = lStructOpt;
+	exports.dOptLabel = dOptLabel;
+}

Index: gc_core/js/lang_core/gc_rules.js
==================================================================
--- gc_core/js/lang_core/gc_rules.js
+++ gc_core/js/lang_core/gc_rules.js
@@ -7,7 +7,9 @@
 const lParagraphRules = ${paragraph_rules_JS};
 
 const lSentenceRules = ${sentence_rules_JS};
 
 
-exports.lParagraphRules = lParagraphRules;
-exports.lSentenceRules = lSentenceRules;
+if (typeof(exports) !== 'undefined') {
+	exports.lParagraphRules = lParagraphRules;
+	exports.lSentenceRules = lSentenceRules;
+}

Index: gc_core/js/str_transform.js
==================================================================
--- gc_core/js/str_transform.js
+++ gc_core/js/str_transform.js
@@ -1,58 +1,33 @@
 //// STRING TRANSFORMATION
 
-var dSimilarChars = new Map ([
-    ["a", "aàâáä"],
-    ["à", "aàâáä"],
-    ["â", "aàâáä"],
-    ["á", "aàâáä"],
-    ["ä", "aàâáä"],
-    ["c", "cç"],
-    ["ç", "cç"],
-    ["e", "eéêèë"],
-    ["é", "eéêèë"],
-    ["ê", "eéêèë"],
-    ["è", "eéêèë"],
-    ["ë", "eéêèë"],
-    ["i", "iîïíì"],
-    ["î", "iîïíì"],
-    ["ï", "iîïíì"],
-    ["í", "iîïíì"],
-    ["ì", "iîïíì"],
-    ["o", "oôóòö"],
-    ["ô", "oôóòö"],
-    ["ó", "oôóòö"],
-    ["ò", "oôóòö"],
-    ["ö", "oôóòö"],
-    ["u", "uûùüú"],
-    ["û", "uûùüú"],
-    ["ù", "uûùüú"],
-    ["ü", "uûùüú"],
-    ["ú", "uûùüú"]
-]);
-
-// Note: 48 is the ASCII code for "0"
-
-// Suffix only
-function getStemFromSuffixCode (sFlex, sSfxCode) {
-    if (sSfxCode == "0") {
-        return sFlex;
-    }
-    return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1);
-}
-
-// Prefix and suffix
-function getStemFromAffixCode (sFlex, sAffCode) {
-    if (sAffCode == "0") {
-        return sFlex;
-    }
-    if (!sAffCode.includes("/")) {
-        return "# error #";
-    }
-    var [sPfxCode, sSfxCode] = sAffCode.split('/');
-    sFlex = sPfxCode.slice(1) + sFlex.slice(sPfxCode.charCodeAt(0)-48);
-    return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1);
-}
-
-
-exports.getStemFromSuffixCode = getStemFromSuffixCode;
-exports.getStemFromAffixCode = getStemFromAffixCode;
+// Note: 48 is the ASCII code for "0"
+
+const str_transform = {
+
+    getStemFromSuffixCode: function (sFlex, sSfxCode) {
+        // Suffix only
+        if (sSfxCode == "0") {
+            return sFlex;
+        }
+        return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1);
+    },
+
+    getStemFromAffixCode: function (sFlex, sAffCode) {
+        // Prefix and suffix
+        if (sAffCode == "0") {
+            return sFlex;
+        }
+        if (!sAffCode.includes("/")) {
+            return "# error #";
+        }
+        let [sPfxCode, sSfxCode] = sAffCode.split('/');
+        sFlex = sPfxCode.slice(1) + sFlex.slice(sPfxCode.charCodeAt(0)-48);
+        return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1);
+    }
+}
+
+
+if (typeof(exports) !== 'undefined') {
+    exports.getStemFromSuffixCode = str_transform.getStemFromSuffixCode;
+    exports.getStemFromAffixCode = str_transform.getStemFromAffixCode;
+}

Index: gc_core/js/tests.js
==================================================================
--- gc_core/js/tests.js
+++ gc_core/js/tests.js
@@ -145,6 +145,9 @@
         return [" ".repeat(sLine.length), ""];
     };
 
 }
 
-exports.TestGrammarChecking = TestGrammarChecking;
+
+if (typeof(exports) !== 'undefined') {
+    exports.TestGrammarChecking = TestGrammarChecking;
+}

Index: gc_core/js/text.js
==================================================================
--- gc_core/js/text.js
+++ gc_core/js/text.js
@@ -2,176 +2,179 @@
 
 "use strict";
 
 const helpers = require("resource://grammalecte/helpers.js");
 
-
-function* getParagraph (sText) {
-    // generator: returns paragraphs of text
-    let iStart = 0;
-    let iEnd = 0;
-    sText = sText.replace("\r", "");
-    while ((iEnd = sText.indexOf("\n", iStart)) !== -1) {
-        yield sText.slice(iStart, iEnd);
-        iStart = iEnd + 1;
-    }
-    yield sText.slice(iStart);
-}
-
-function* wrap (sText, nWidth=80) {
-    // generator: returns text line by line
-    while (sText) {
-        if (sText.length >= nWidth) {
-            let nEnd = sText.lastIndexOf(" ", nWidth) + 1;
-            if (nEnd > 0) {
-                yield sText.slice(0, nEnd);
-                sText = sText.slice(nEnd);
-            } else {
-                yield sText.slice(0, nWidth);
-                sText = sText.slice(nWidth);
-            }
-        } else {
-            break;
-        }
-    }
-    yield sText;
-}
-
-function getReadableError (oErr) {
-    // Returns an error oErr as a readable error
-    try {
-        let s = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] + "  # " + oErr['sRuleId']+":\n";
-        s += "  " + oErr["sMessage"];
-        if (oErr["aSuggestions"].length > 0) {
-            s += "\n  > Suggestions : " + oErr["aSuggestions"].join(" | ");
-        }
-        if (oErr["URL"] !== "") {
-            s += "\n  > URL: " + oErr["URL"];
-        }
-        return s;
-    }
-    catch (e) {
-        helpers.logerror(e);
-        return "\n# Error. Data: " + oErr.toString();
-    }
-}
-
-function addHtmlEntities (sParagraph) {
-    if (sParagraph.includes("&")) {
-        sParagraph = sParagraph.replace(/&/g, "&");
-    }
-    if (sParagraph.includes("<")) {
-        sParagraph = sParagraph.replace(/</g, "&lt;").replace(/>/g, "&gt;");
-    }
-    return sParagraph;
-}
-
-function createHTMLBlock (sParagraph, iParagraph) {
-    let sClassName = (sParagraph.includes('<u id="err')) ? " softred" : "";
-    return '<p id="paragr'+iParagraph.toString()+'" class="paragraph' + sClassName + '" lang="fr" spellcheck="false">' + sParagraph + '</p>\n'
-           + '<div class="actions"><div id="end'+iParagraph.toString()+'" class="button red">×</div>'
-           + '<div id="edit'+iParagraph.toString()+'" class="button">Éditer</div>'
-           + '<div id="check'+iParagraph.toString()+'" class="button green">Réanalyser</div>&nbsp;</div>\n';
-}
-
-function tagParagraph (sParagraph, iParagraph, aGrammErr, aSpellErr, bDebug=false) {
-    // Returns a text with with underlined errors and messages in tooltip
-    try {
-        if (aGrammErr.length === 0  &&  aSpellErr.length === 0) {
-            return sParagraph;
-        }
-        aGrammErr.push(...aSpellErr);
-        aGrammErr.sort(function (a, b) {
-            if (a["nStart"] < b["nStart"])
-                return 1;
-            if (a["nStart"] > b["nStart"])
-                return -1;
-            return 0;
-        });
-
-        let nErr = aGrammErr.length - 1; // we count errors to give them an identifier
-        let nStartLastErr = sParagraph.length;
-        for (let oErr of aGrammErr) {
-            let sErrId = iParagraph.toString() + "_" + nErr.toString();
-            let nStart = oErr["nStart"];
-            let nEnd = oErr["nEnd"];
-            if (nEnd <= nStartLastErr) {
-                oErr["sId"] = sErrId;
-                if (oErr['sType'] !== 'WORD') {
-                    // Grammar Error
-                    sParagraph = sParagraph.slice(0, nStart)
-                               + '<u id="err' + sErrId + '" class="error '+oErr['sType']+'" href="#" onclick="return false;">'
-                               + sParagraph.slice(nStart, nEnd)
-                               + '<span id="tooltip' + sErrId + '" class="tooltip" contenteditable="false">'
-                               + getGrammarErrorHTML(oErr, bDebug) + '</span>'
-                               + "</u><!-- err_end -->" + sParagraph.slice(nEnd);
-                } else {
-                    // Spelling error
-                    sParagraph = sParagraph.slice(0, nStart)
-                               + '<u id="err' + sErrId + '" class="error spell" href="#" onclick="return false;">'
-                               + sParagraph.slice(nStart, nEnd)
-                               + '<span id="tooltip' + sErrId + '" class="tooltip" contenteditable="false">'
-                               + getSpellingErrorHTML(oErr) + '</span>'
-                               + "</u><!-- err_end -->" + sParagraph.slice(nEnd);
-                }
-                nStartLastErr = nStart;
-            }
-            nErr -= 1;
-        }
-        return sParagraph;
-    }
-    catch (e) {
-        helpers.logerror(e);
-        return "# Error.";
-    }
-}
-
-function getGrammarErrorHTML (oErr, bDebug=false) {
-    // Returns an error oErr as a readable error in HTML
-    try {
-        let sErr = '';
-        if (bDebug) {
-            sErr += '<em class="debug">'
-              + '<i id="data'+oErr['sId']+'" class="data" hidden>'
-              + oErr['nStart'] + ":" + oErr['nEnd'] + ' · #' + oErr['sRuleId'] + " </i>+</em>";
-        }
-        sErr += oErr["sMessage"];
-        sErr += ' <a id="ignore'+oErr['sId']+'" class="ignore" href="#" onclick="return false;">IGNORER</a>';
-        if (oErr["URL"] !== "") {
-            sErr += ' <a href="' + oErr["URL"] + '" onclick="return false;">Infos…</a>';
-        }
-        if (oErr["aSuggestions"].length > 0) {
-            sErr += '<br/><s>Suggestions :</s><br/>';
-            let iSugg = 0;
-            for (let sSugg of oErr["aSuggestions"]) {
-                sErr += '<a id="sugg' + oErr['sId'] + "-" + iSugg + '" class="sugg" href="#" onclick="return false;">' + sSugg + '</a> ';
-                iSugg += 1;
-            }
-        }
-        return sErr;
-    }
-    catch (e) {
-        helpers.logerror(e);
-        return '# Error. Data: ' + oErr.toString();
-    }
-}
-
-function getSpellingErrorHTML (oErr) {
-    // Returns an error oErr as a readable error in HTML
-    try {
-        let sErr = 'Mot inconnu du dictionnaire.';
-        sErr += ' <a id="ignore' + oErr['sId'] + '" class="ignore" href="#" onclick="return false;">IGNORER</a>';
-        sErr += '<br/><s>Suggestions :</s><br/>';
-        return sErr;
-    }
-    catch (e) {
-        helpers.logerror(e);
-        return '# Error. Data: ' + oErr.toString();
-    }
-}
-
-
-exports.getParagraph = getParagraph;
-exports.addHtmlEntities = addHtmlEntities;
-exports.createHTMLBlock = createHTMLBlock;
-exports.tagParagraph = tagParagraph;
-exports.getReadableError = getReadableError;
+const text = {
+    getParagraph: function* (sText) {
+        // generator: returns paragraphs of text
+        let iStart = 0;
+        let iEnd = 0;
+        sText = sText.replace("\r", "");
+        while ((iEnd = sText.indexOf("\n", iStart)) !== -1) {
+            yield sText.slice(iStart, iEnd);
+            iStart = iEnd + 1;
+        }
+        yield sText.slice(iStart);
+    },
+
+    wrap: function* (sText, nWidth=80) {
+        // generator: returns text line by line
+        while (sText) {
+            if (sText.length >= nWidth) {
+                let nEnd = sText.lastIndexOf(" ", nWidth) + 1;
+                if (nEnd > 0) {
+                    yield sText.slice(0, nEnd);
+                    sText = sText.slice(nEnd);
+                } else {
+                    yield sText.slice(0, nWidth);
+                    sText = sText.slice(nWidth);
+                }
+            } else {
+                break;
+            }
+        }
+        yield sText;
+    },
+
+    getReadableError: function (oErr) {
+        // Returns an error oErr as a readable error
+        try {
+            let s = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] + "  # " + oErr['sRuleId']+":\n";
+            s += "  " + oErr["sMessage"];
+            if (oErr["aSuggestions"].length > 0) {
+                s += "\n  > Suggestions : " + oErr["aSuggestions"].join(" | ");
+            }
+            if (oErr["URL"] !== "") {
+                s += "\n  > URL: " + oErr["URL"];
+            }
+            return s;
+        }
+        catch (e) {
+            helpers.logerror(e);
+            return "\n# Error. Data: " + oErr.toString();
+        }
+    },
+
+    addHtmlEntities: function (sParagraph) {
+        if (sParagraph.includes("&")) {
+            sParagraph = sParagraph.replace(/&/g, "&amp;");
+        }
+        if (sParagraph.includes("<")) {
+            sParagraph = sParagraph.replace(/</g, "&lt;").replace(/>/g, "&gt;");
+        }
+        return sParagraph;
+    },
+
+    createHTMLBlock: function (sParagraph, iParagraph) {
+        let sClassName = (sParagraph.includes('<u id="err')) ? " softred" : "";
+        return '<p id="paragr'+iParagraph.toString()+'" class="paragraph' + sClassName + '" lang="fr" spellcheck="false">' + sParagraph + '</p>\n'
+               + '<div class="actions"><div id="end'+iParagraph.toString()+'" class="button red">×</div>'
+               + '<div id="edit'+iParagraph.toString()+'" class="button">Éditer</div>'
+               + '<div id="check'+iParagraph.toString()+'" class="button green">Réanalyser</div>&nbsp;</div>\n';
+    },
+
+    tagParagraph: function (sParagraph, iParagraph, aGrammErr, aSpellErr, bDebug=false) {
+        // Returns a text with with underlined errors and messages in tooltip
+        try {
+            if (aGrammErr.length === 0  &&  aSpellErr.length === 0) {
+                return sParagraph;
+            }
+            aGrammErr.push(...aSpellErr);
+            aGrammErr.sort(function (a, b) {
+                if (a["nStart"] < b["nStart"])
+                    return 1;
+                if (a["nStart"] > b["nStart"])
+                    return -1;
+                return 0;
+            });
+
+            let nErr = aGrammErr.length - 1; // we count errors to give them an identifier
+            let nStartLastErr = sParagraph.length;
+            for (let oErr of aGrammErr) {
+                let sErrId = iParagraph.toString() + "_" + nErr.toString();
+                let nStart = oErr["nStart"];
+                let nEnd = oErr["nEnd"];
+                if (nEnd <= nStartLastErr) {
+                    oErr["sId"] = sErrId;
+                    if (oErr['sType'] !== 'WORD') {
+                        // Grammar Error
+                        sParagraph = sParagraph.slice(0, nStart)
+                                   + '<u id="err' + sErrId + '" class="error '+oErr['sType']+'" href="#" onclick="return false;">'
+                                   + sParagraph.slice(nStart, nEnd)
+                                   + '<span id="tooltip' + sErrId + '" class="tooltip" contenteditable="false">'
+                                   + this._getGrammarErrorHTML(oErr, bDebug) + '</span>'
+                                   + "</u><!-- err_end -->" + sParagraph.slice(nEnd);
+                    } else {
+                        // Spelling error
+                        sParagraph = sParagraph.slice(0, nStart)
+                                   + '<u id="err' + sErrId + '" class="error spell" href="#" onclick="return false;">'
+                                   + sParagraph.slice(nStart, nEnd)
+                                   + '<span id="tooltip' + sErrId + '" class="tooltip" contenteditable="false">'
+                                   + this._getSpellingErrorHTML(oErr) + '</span>'
+                                   + "</u><!-- err_end -->" + sParagraph.slice(nEnd);
+                    }
+                    nStartLastErr = nStart;
+                }
+                nErr -= 1;
+            }
+            return sParagraph;
+        }
+        catch (e) {
+            helpers.logerror(e);
+            return "# Error.";
+        }
+    },
+
+    _getGrammarErrorHTML: function (oErr, bDebug=false) {
+        // Returns an error oErr as a readable error in HTML
+        try {
+            let sErr = '';
+            if (bDebug) {
+                sErr += '<em class="debug">'
+                  + '<i id="data'+oErr['sId']+'" class="data" hidden>'
+                  + oErr['nStart'] + ":" + oErr['nEnd'] + ' · #' + oErr['sRuleId'] + " </i>+</em>";
+            }
+            sErr += oErr["sMessage"];
+            sErr += ' <a id="ignore'+oErr['sId']+'" class="ignore" href="#" onclick="return false;">IGNORER</a>';
+            if (oErr["URL"] !== "") {
+                sErr += ' <a href="' + oErr["URL"] + '" onclick="return false;">Infos…</a>';
+            }
+            if (oErr["aSuggestions"].length > 0) {
+                sErr += '<br/><s>Suggestions :</s><br/>';
+                let iSugg = 0;
+                for (let sSugg of oErr["aSuggestions"]) {
+                    sErr += '<a id="sugg' + oErr['sId'] + "-" + iSugg + '" class="sugg" href="#" onclick="return false;">' + sSugg + '</a> ';
+                    iSugg += 1;
+                }
+            }
+            return sErr;
+        }
+        catch (e) {
+            helpers.logerror(e);
+            return '# Error. Data: ' + oErr.toString();
+        }
+    },
+
+    _getSpellingErrorHTML: function (oErr) {
+        // Returns an error oErr as a readable error in HTML
+        try {
+            let sErr = 'Mot inconnu du dictionnaire.';
+            sErr += ' <a id="ignore' + oErr['sId'] + '" class="ignore" href="#" onclick="return false;">IGNORER</a>';
+            sErr += '<br/><s>Suggestions :</s><br/>';
+            return sErr;
+        }
+        catch (e) {
+            helpers.logerror(e);
+            return '# Error. Data: ' + oErr.toString();
+        }
+    }
+}
+
+
+if (typeof(exports) !== 'undefined') {
+    exports.getParagraph = text.getParagraph;
+    exports.addHtmlEntities = text.addHtmlEntities;
+    exports.createHTMLBlock = text.createHTMLBlock;
+    exports.tagParagraph = text.tagParagraph;
+    exports.getReadableError = text.getReadableError;
+}

Index: gc_core/js/tokenizer.js
==================================================================
--- gc_core/js/tokenizer.js
+++ gc_core/js/tokenizer.js
@@ -70,6 +70,9 @@
             sText = sText.slice(nCut);
         }
     }
 }
 
-exports.Tokenizer = Tokenizer;
+
+if (typeof(exports) !== 'undefined') {
+    exports.Tokenizer = Tokenizer;
+}

Index: gc_lang/fr/build.py
==================================================================
--- gc_lang/fr/build.py
+++ gc_lang/fr/build.py
@@ -8,12 +8,23 @@
 
 
 def build (sLang, dVars, spLangPack):
     "complementary build launched from make.py"
     createFirefoxExtension(sLang, dVars)
+    createWebExtension(sLang, dVars)
     createThunderbirdExtension(sLang, dVars, spLangPack)
 
+
+def createWebExtension (sLang, dVars):
+    "create Web-extension"
+    print("Building Web-extension")
+    helpers.createCleanFolder("_build/webext/"+sLang)
+    dir_util.copy_tree("gc_lang/"+sLang+"/webext/", "_build/webext/"+sLang)
+    dir_util.copy_tree("grammalecte-js", "_build/webext/"+sLang+"/grammalecte")
+    with helpers.cd("_build/webext/"+sLang):
+        os.system("web-ext build")
+
 
 def createFirefoxExtension (sLang, dVars):
     "create extension for Firefox"
     print("Building extension for Firefox")
     helpers.createCleanFolder("_build/xpi/"+sLang)

Index: gc_lang/fr/config.ini
==================================================================
--- gc_lang/fr/config.ini
+++ gc_lang/fr/config.ini
@@ -3,11 +3,11 @@
 lang_name = French
 locales = fr_FR fr_BE fr_CA fr_CH fr_LU fr_MC fr_BF fr_CI fr_SN fr_ML fr_NE fr_TG fr_BJ
 country_default = FR
 name = Grammalecte
 implname = grammalecte
-version = 0.5.17.2
+version = 0.6.0
 author = Olivier R.
 provider = Dicollecte
 link = http://grammalecte.net
 description = Correcteur grammatical pour le français.
 extras = README_fr.txt
@@ -28,10 +28,15 @@
 oxt_identifier = French.linguistic.resources.from.Dicollecte.by.OlivierR
 
 # Firefox
 fx_identifier = French-GC@grammalecte.net
 fx_name = Grammalecte [fr]
+
+fx_standard_path = C:\Program Files\Mozilla Firefox\firefox.exe
+fx_beta_path = C:\Program Files\Mozilla Firefox Beta\firefox.exe
+fx_nightly_path = C:\Program Files (x86)\Nightly\firefox.exe
+
 
 # Thunderbird
 tb_identifier = French-GC-TB@grammalecte.net
 tb_name = Grammalecte [fr]
 tb_debug_extension_path = _build/tb-debug.profile/extensions/French-GC-TB@grammalecte.net

Index: gc_lang/fr/data/phonet_simil.txt
==================================================================
--- gc_lang/fr/data/phonet_simil.txt
+++ gc_lang/fr/data/phonet_simil.txt
@@ -666,10 +666,11 @@
 sommet sommets sommer
 son sons sont
 sonnet sonnets sonner
 sors sort sorts
 sortie sorties sortis sortit
+sou sous soue soues
 souci soucis soucie soucies soucient
 souper soupers sous-paie sous-paies sous-paient
 soupir soupirs soupire soupires soupirent
 soutien soutiens soutient
 soufflet soufflets soufflé soufflés souffler

Index: gc_lang/fr/modules-js/conj.js
==================================================================
--- gc_lang/fr/modules-js/conj.js
+++ gc_lang/fr/modules-js/conj.js
@@ -4,185 +4,173 @@
 "use strict";
 
 ${map}
 
 
-let helpers = null; // module not loaded in Firefox content script
-
-let _oData = {};
-let _lVtyp = null;
-let _lTags = null;
-let _dPatternConj = {};
-let _dVerb = {};
-
-
-if (typeof(exports) !== 'undefined') {
-    // used within Grammalecte library
-    helpers = require("resource://grammalecte/helpers.js");
-    _oData = JSON.parse(helpers.loadFile("resource://grammalecte/fr/conj_data.json"));
-    _lVtyp = _oData.lVtyp;
-    _lTags = _oData.lTags;
-    _dPatternConj = _oData.dPatternConj;
-    _dVerb = _oData.dVerb;
-} else {
-    // used within Firefox content script (conjugation panel).
-    // can’t load JSON from here, so we do it in ui.js and send it here.
-    self.port.on("provideConjData", function (sData) {
-        _oData = JSON.parse(sData);
-        _lVtyp = _oData.lVtyp;
-        _lTags = _oData.lTags;
-        _dPatternConj = _oData.dPatternConj;
-        _dVerb = _oData.dVerb;
-    });
-}
-
-
-const _zStartVoy = new RegExp("^[aeéiouœê]");
-const _zNeedTeuph = new RegExp("[tdc]$");
-
-const _dProSuj = new Map ([ [":1s", "je"], [":1ś", "je"], [":2s", "tu"], [":3s", "il"], [":1p", "nous"], [":2p", "vous"], [":3p", "ils"] ]);
-const _dProObj = new Map ([ [":1s", "me "], [":1ś", "me "], [":2s", "te "], [":3s", "se "], [":1p", "nous "], [":2p", "vous "], [":3p", "se "] ]);
-const _dProObjEl = new Map ([ [":1s", "m’"], [":1ś", "m’"], [":2s", "t’"], [":3s", "s’"], [":1p", "nous "], [":2p", "vous "], [":3p", "s’"] ]);
-const _dImpePro = new Map ([ [":2s", "-toi"], [":1p", "-nous"], [":2p", "-vous"] ]);
-const _dImpeProNeg = new Map ([ [":2s", "ne te "], [":1p", "ne nous "], [":2p", "ne vous "] ]);
-const _dImpeProEn = new Map ([ [":2s", "-t’en"], [":1p", "-nous-en"], [":2p", "-vous-en"] ]);
-const _dImpeProNegEn = new Map ([ [":2s", "ne t’en "], [":1p", "ne nous en "], [":2p", "ne vous en "] ]);
-
-const _dGroup = new Map ([ ["0", "auxiliaire"], ["1", "1ᵉʳ groupe"], ["2", "2ᵉ groupe"], ["3", "3ᵉ groupe"] ]);
-
-const _dTenseIdx = new Map ([ [":PQ", 0], [":Ip", 1], [":Iq", 2], [":Is", 3], [":If", 4], [":K", 5], [":Sp", 6], [":Sq", 7], [":E", 8] ]);
-
-
-function isVerb (sVerb) {
-    return _dVerb.hasOwnProperty(sVerb);
-}
-
-function getConj (sVerb, sTense, sWho) {
-    // returns conjugation (can be an empty string)
-    if (!_dVerb.hasOwnProperty(sVerb)) {
-        return null;
-    }
-    if (!_dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) {
-        return "";
-    }
-    return _modifyStringWithSuffixCode(sVerb, _dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]][sWho]);
-}
-
-function hasConj (sVerb, sTense, sWho) {
-    // returns false if no conjugation (also if empty) else true
-    if (!_dVerb.hasOwnProperty(sVerb)) {
-        return false;
-    }
-    if (_dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]].hasOwnProperty(sWho)
-            && _dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]][sWho]) {
-        return true;
-    }
-    return false;
-}
-
-function getVtyp (sVerb) {
-    // returns raw informations about sVerb
-    if (!_dVerb.hasOwnProperty(sVerb)) {
-        return null;
-    }
-    return _lVtyp[_dVerb[sVerb][0]];
-}
-
-function getSimil (sWord, sMorph, sFilter=null) {
-    if (!sMorph.includes(":V")) {
-        return new Set();
-    }
-    let sInfi = sMorph.slice(1, sMorph.indexOf(" "));
-    let tTags = _getTags(sInfi);
-    let aSugg = new Set();
-    if (sMorph.includes(":Q") || sMorph.includes(":Y")) {
-        // we suggest conjugated forms
-        if (sMorph.includes(":V1")) {
-            aSugg.add(sInfi);
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":3s"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":2p"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Iq", ":1s"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Iq", ":3s"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Iq", ":3p"));
-        } else if (sMorph.includes(":V2")) {
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":1s"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":3s"));
-        } else if (sMorph.includes(":V3")) {
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":1s"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":3s"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Is", ":1s"));
-            aSugg.add(_getConjWithTags(sInfi, tTags, ":Is", ":3s"));
-        } else if (isMorph.includes(":V0a")) {
-            aSugg.add("eus");
-            aSugg.add("eut");
-        } else {
-            aSugg.add("étais");
-            aSugg.add("était");
-        }
-        aSugg.delete("");
-    } else {
-        // we suggest past participles
-        aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q1"));
-        aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q2"));
-        aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q3"));
-        aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q4"));
-        aSugg.delete("");
-        // if there is only one past participle (epi inv), unreliable.
-        if (aSugg.size === 1) {
-            aSugg.clear();
-        }
-        if (sMorph.includes(":V1")) {
-            aSugg.add(sInfi);
-        }
-    }
-    return aSugg;
-}
-
-
-function _getTags (sVerb) {
-    // returns tuple of tags (usable with functions _getConjWithTags and _hasConjWithTags)
-    if (!_dVerb.hasOwnProperty(sVerb)) {
-        return null;
-    }
-    return _lTags[_dVerb[sVerb][1]];
-}
-
-function _getConjWithTags (sVerb, tTags, sTense, sWho) {
-    // returns conjugation (can be an empty string)
-    if (!_dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) {
-        return "";
-    }
-    return _modifyStringWithSuffixCode(sVerb, _dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]][sWho]);
-}
-
-function _hasConjWithTags (tTags, sTense, sWho) {
-    // returns false if no conjugation (also if empty) else true
-    if (_dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]].hasOwnProperty(sWho)
-            && _dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]][sWho]) {
-        return true;
-    }
-    return false;
-}
-
-function _modifyStringWithSuffixCode (sWord, sSfx) {
-    // returns sWord modified by sSfx
-    if (sSfx === "") {
-        return "";
-    }
-    if (sSfx === "0") {
-        return sWord;
-    }
-    try {
-        if (sSfx[0] !== '0') {
-            return sWord.slice(0, -(sSfx.charCodeAt(0)-48)) + sSfx.slice(1); // 48 is the ASCII code for "0"
-        } else {
-            return sWord + sSfx.slice(1);
-        }
-    }
-    catch (e) {
-        console.log(e);
-        return "## erreur, code : " + sSfx + " ##";
+const conj = {
+    _lVtyp: null,
+    _lTags: null,
+    _dPatternConj: {},
+    _dVerb: {},
+
+    init: function (sJSONData) {
+        try {
+            let _oData = JSON.parse(sJSONData);
+            this._lVtyp = _oData.lVtyp;
+            this._lTags = _oData.lTags;
+            this._dPatternConj = _oData.dPatternConj;
+            this._dVerb = _oData.dVerb;
+        }
+        catch (e) {
+            console.error(e);
+        }
+    },
+
+    _zStartVoy: new RegExp("^[aeéiouœê]"),
+    _zNeedTeuph: new RegExp("[tdc]$"),
+
+    _dProSuj: new Map ([ [":1s", "je"], [":1ś", "je"], [":2s", "tu"], [":3s", "il"], [":1p", "nous"], [":2p", "vous"], [":3p", "ils"] ]),
+    _dProObj: new Map ([ [":1s", "me "], [":1ś", "me "], [":2s", "te "], [":3s", "se "], [":1p", "nous "], [":2p", "vous "], [":3p", "se "] ]),
+    _dProObjEl: new Map ([ [":1s", "m’"], [":1ś", "m’"], [":2s", "t’"], [":3s", "s’"], [":1p", "nous "], [":2p", "vous "], [":3p", "s’"] ]),
+    _dImpePro: new Map ([ [":2s", "-toi"], [":1p", "-nous"], [":2p", "-vous"] ]),
+    _dImpeProNeg: new Map ([ [":2s", "ne te "], [":1p", "ne nous "], [":2p", "ne vous "] ]),
+    _dImpeProEn: new Map ([ [":2s", "-t’en"], [":1p", "-nous-en"], [":2p", "-vous-en"] ]),
+    _dImpeProNegEn: new Map ([ [":2s", "ne t’en "], [":1p", "ne nous en "], [":2p", "ne vous en "] ]),
+
+    _dGroup: new Map ([ ["0", "auxiliaire"], ["1", "1ᵉʳ groupe"], ["2", "2ᵉ groupe"], ["3", "3ᵉ groupe"] ]),
+
+    _dTenseIdx: new Map ([ [":PQ", 0], [":Ip", 1], [":Iq", 2], [":Is", 3], [":If", 4], [":K", 5], [":Sp", 6], [":Sq", 7], [":E", 8] ]),
+
+    isVerb: function (sVerb) {
+        return this._dVerb.hasOwnProperty(sVerb);
+    },
+
+    getConj: function (sVerb, sTense, sWho) {
+        // returns conjugation (can be an empty string)
+        if (!this._dVerb.hasOwnProperty(sVerb)) {
+            return null;
+        }
+        if (!this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) {
+            return "";
+        }
+        return this._modifyStringWithSuffixCode(sVerb, this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]][sWho]);
+    },
+
+    hasConj: function (sVerb, sTense, sWho) {
+        // returns false if no conjugation (also if empty) else true
+        if (!this._dVerb.hasOwnProperty(sVerb)) {
+            return false;
+        }
+        if (this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho)
+                && this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]][sWho]) {
+            return true;
+        }
+        return false;
+    },
+
+    getVtyp: function (sVerb) {
+        // returns raw informations about sVerb
+        if (!this._dVerb.hasOwnProperty(sVerb)) {
+            return null;
+        }
+        return this._lVtyp[this._dVerb[sVerb][0]];
+    },
+
+    getSimil: function (sWord, sMorph, sFilter=null) {
+        if (!sMorph.includes(":V")) {
+            return new Set();
+        }
+        let sInfi = sMorph.slice(1, sMorph.indexOf(" "));
+        let tTags = this._getTags(sInfi);
+        let aSugg = new Set();
+        if (sMorph.includes(":Q") || sMorph.includes(":Y")) {
+            // we suggest conjugated forms
+            if (sMorph.includes(":V1")) {
+                aSugg.add(sInfi);
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":3s"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":2p"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Iq", ":1s"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Iq", ":3s"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Iq", ":3p"));
+            } else if (sMorph.includes(":V2")) {
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":1s"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":3s"));
+            } else if (sMorph.includes(":V3")) {
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":1s"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":3s"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Is", ":1s"));
+                aSugg.add(this._getConjWithTags(sInfi, tTags, ":Is", ":3s"));
+            } else if (isMorph.includes(":V0a")) {
+                aSugg.add("eus");
+                aSugg.add("eut");
+            } else {
+                aSugg.add("étais");
+                aSugg.add("était");
+            }
+            aSugg.delete("");
+        } else {
+            // we suggest past participles
+            aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q1"));
+            aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q2"));
+            aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q3"));
+            aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q4"));
+            aSugg.delete("");
+            // if there is only one past participle (epi inv), unreliable.
+            if (aSugg.size === 1) {
+                aSugg.clear();
+            }
+            if (sMorph.includes(":V1")) {
+                aSugg.add(sInfi);
+            }
+        }
+        return aSugg;
+    },
+
+    _getTags: function (sVerb) {
+        // returns tuple of tags (usable with functions _getConjWithTags and _hasConjWithTags)
+        if (!this._dVerb.hasOwnProperty(sVerb)) {
+            return null;
+        }
+        return this._lTags[this._dVerb[sVerb][1]];
+    },
+
+    _getConjWithTags: function (sVerb, tTags, sTense, sWho) {
+        // returns conjugation (can be an empty string)
+        if (!this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) {
+            return "";
+        }
+        return this._modifyStringWithSuffixCode(sVerb, this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]][sWho]);
+    },
+
+    _hasConjWithTags: function (tTags, sTense, sWho) {
+        // returns false if no conjugation (also if empty) else true
+        if (this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho)
+                && this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]][sWho]) {
+            return true;
+        }
+        return false;
+    },
+
+    _modifyStringWithSuffixCode: function (sWord, sSfx) {
+        // returns sWord modified by sSfx
+        if (sSfx === "") {
+            return "";
+        }
+        if (sSfx === "0") {
+            return sWord;
+        }
+        try {
+            if (sSfx[0] !== '0') {
+                return sWord.slice(0, -(sSfx.charCodeAt(0)-48)) + sSfx.slice(1); // 48 is the ASCII code for "0"
+            } else {
+                return sWord + sSfx.slice(1);
+            }
+        }
+        catch (e) {
+            console.log(e);
+            return "## erreur, code : " + sSfx + " ##";
+        }
     }
 }
 
 
 class Verb {
@@ -191,107 +179,107 @@
         if (typeof sVerb !== "string" || sVerb === "") {
             throw new TypeError ("The value should be a non-empty string");
         }
         this.sVerb = sVerb;
         this.sVerbAux = "";
-        this._sRawInfo = getVtyp(this.sVerb);
+        this._sRawInfo = conj.getVtyp(this.sVerb);
         this.sInfo = this._readableInfo(this._sRawInfo);
-        this._tTags = _getTags(sVerb);
-        this._tTagsAux = _getTags(this.sVerbAux);
+        this._tTags = conj._getTags(sVerb);
+        this._tTagsAux = conj._getTags(this.sVerbAux);
         this.bProWithEn = (this._sRawInfo[5] === "e");
         this.dConj = new Map ([
             [":Y", new Map ([
                 ["label", "Infinitif"],
                 [":Y", sVerb]
             ])],
             [":PQ", new Map ([
                 ["label", "Participes passés et présent"],
-                [":Q1", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q1")],
-                [":Q2", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q2")],
-                [":Q3", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q3")],
-                [":Q4", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q4")],
-                [":P", _getConjWithTags(sVerb, this._tTags, ":PQ", ":P")]
+                [":Q1", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q1")],
+                [":Q2", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q2")],
+                [":Q3", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q3")],
+                [":Q4", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q4")],
+                [":P", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":P")]
             ])],
             [":Ip", new Map ([
                 ["label", "Présent"],
-                [":1s", _getConjWithTags(sVerb, this._tTags, ":Ip", ":1s")],
-                [":1ś", _getConjWithTags(sVerb, this._tTags, ":Ip", ":1ś")],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":Ip", ":2s")],
-                [":3s", _getConjWithTags(sVerb, this._tTags, ":Ip", ":3s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":Ip", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":Ip", ":2p")],
-                [":3p", _getConjWithTags(sVerb, this._tTags, ":Ip", ":3p")]
+                [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":1s")],
+                [":1ś", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":1ś")],
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":2s")],
+                [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":3s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":2p")],
+                [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":3p")]
             ])],
             [":Iq", new Map ([
                 ["label", "Imparfait"],
-                [":1s", _getConjWithTags(sVerb, this._tTags, ":Iq", ":1s")],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":Iq", ":2s")],
-                [":3s", _getConjWithTags(sVerb, this._tTags, ":Iq", ":3s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":Iq", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":Iq", ":2p")],
-                [":3p", _getConjWithTags(sVerb, this._tTags, ":Iq", ":3p")]
+                [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":1s")],
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":2s")],
+                [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":3s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":2p")],
+                [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":3p")]
             ])],
             [":Is", new Map ([
                 ["label", "Passé simple"],
-                [":1s", _getConjWithTags(sVerb, this._tTags, ":Is", ":1s")],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":Is", ":2s")],
-                [":3s", _getConjWithTags(sVerb, this._tTags, ":Is", ":3s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":Is", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":Is", ":2p")],
-                [":3p", _getConjWithTags(sVerb, this._tTags, ":Is", ":3p")]
+                [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":1s")],
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":2s")],
+                [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":3s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":2p")],
+                [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":3p")]
             ])],
             [":If", new Map ([
                 ["label", "Futur"],
-                [":1s", _getConjWithTags(sVerb, this._tTags, ":If", ":1s")],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":If", ":2s")],
-                [":3s", _getConjWithTags(sVerb, this._tTags, ":If", ":3s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":If", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":If", ":2p")],
-                [":3p", _getConjWithTags(sVerb, this._tTags, ":If", ":3p")]
+                [":1s", conj._getConjWithTags(sVerb, this._tTags, ":If", ":1s")],
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":If", ":2s")],
+                [":3s", conj._getConjWithTags(sVerb, this._tTags, ":If", ":3s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":If", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":If", ":2p")],
+                [":3p", conj._getConjWithTags(sVerb, this._tTags, ":If", ":3p")]
             ])],
             [":Sp", new Map ([
                 ["label", "Présent subjonctif"],
-                [":1s", _getConjWithTags(sVerb, this._tTags, ":Sp", ":1s")],
-                [":1ś", _getConjWithTags(sVerb, this._tTags, ":Sp", ":1ś")],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":Sp", ":2s")],
-                [":3s", _getConjWithTags(sVerb, this._tTags, ":Sp", ":3s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":Sp", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":Sp", ":2p")],
-                [":3p", _getConjWithTags(sVerb, this._tTags, ":Sp", ":3p")]
+                [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":1s")],
+                [":1ś", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":1ś")],
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":2s")],
+                [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":3s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":2p")],
+                [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":3p")]
             ])],
             [":Sq", new Map ([
                 ["label", "Imparfait subjonctif"],
-                [":1s", _getConjWithTags(sVerb, this._tTags, ":Sq", ":1s")],
-                [":1ś", _getConjWithTags(sVerb, this._tTags, ":Sq", ":1ś")],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":Sq", ":2s")],
-                [":3s", _getConjWithTags(sVerb, this._tTags, ":Sq", ":3s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":Sq", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":Sq", ":2p")],
-                [":3p", _getConjWithTags(sVerb, this._tTags, ":Sq", ":3p")]
+                [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":1s")],
+                [":1ś", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":1ś")],
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":2s")],
+                [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":3s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":2p")],
+                [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":3p")]
             ])],
             [":K", new Map ([
                 ["label", "Conditionnel"],
-                [":1s", _getConjWithTags(sVerb, this._tTags, ":K", ":1s")],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":K", ":2s")],
-                [":3s", _getConjWithTags(sVerb, this._tTags, ":K", ":3s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":K", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":K", ":2p")],
-                [":3p", _getConjWithTags(sVerb, this._tTags, ":K", ":3p")]
+                [":1s", conj._getConjWithTags(sVerb, this._tTags, ":K", ":1s")],
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":K", ":2s")],
+                [":3s", conj._getConjWithTags(sVerb, this._tTags, ":K", ":3s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":K", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":K", ":2p")],
+                [":3p", conj._getConjWithTags(sVerb, this._tTags, ":K", ":3p")]
             ])],
             [":E", new Map ([
                 ["label", "Impératif"],
-                [":2s", _getConjWithTags(sVerb, this._tTags, ":E", ":2s")],
-                [":1p", _getConjWithTags(sVerb, this._tTags, ":E", ":1p")],
-                [":2p", _getConjWithTags(sVerb, this._tTags, ":E", ":2p")]
+                [":2s", conj._getConjWithTags(sVerb, this._tTags, ":E", ":2s")],
+                [":1p", conj._getConjWithTags(sVerb, this._tTags, ":E", ":1p")],
+                [":2p", conj._getConjWithTags(sVerb, this._tTags, ":E", ":2p")]
             ])]
         ]);
     };
 
     _readableInfo () {
         // returns readable infos
         this.sVerbAux = (this._sRawInfo.slice(7,8) == "e") ? "être" : "avoir";
-        let sGroup = _dGroup.get(this._sRawInfo[0]);
+        let sGroup = conj._dGroup.get(this._sRawInfo[0]);
         let sInfo = "";
         if (this._sRawInfo.slice(3,4) == "t") {
             sInfo = "transitif";
         } else if (this._sRawInfo.slice(4,5) == "n") {
             sInfo = "transitif indirect";
@@ -323,11 +311,11 @@
         }
         if (bPro) {
             if (this.bProWithEn) {
                 sInfi = "s’en " + sInfi;
             } else {
-                sInfi = (_zStartVoy.test(sInfi)) ? "s’" + sInfi : "se " + sInfi;
+                sInfi = (conj._zStartVoy.test(sInfi)) ? "s’" + sInfi : "se " + sInfi;
             }
         }
         if (bNeg) {
             sInfi = "ne pas " + sInfi;
         }
@@ -348,18 +336,18 @@
         if (!this.dConj.get(":PQ").get(":P")) {
             return "";
         }
         let sPartPre;
         if (bTpsCo) {
-            sPartPre = (!bPro) ? _getConjWithTags(this.sVerbAux, this._tTagsAux, ":PQ", ":P") : getConj("être", ":PQ", ":P");
+            sPartPre = (!bPro) ? conj._getConjWithTags(this.sVerbAux, this._tTagsAux, ":PQ", ":P") : getConj("être", ":PQ", ":P");
         } else {
             sPartPre = this.dConj.get(":PQ").get(":P");
         }
         if (sPartPre === "") {
             return "";
         }
-        let bEli = _zStartVoy.test(sPartPre);
+        let bEli = conj._zStartVoy.test(sPartPre);
         if (bPro) {
             if (this.bProWithEn) {
                 sPartPre = "s’en " + sPartPre;
             } else {
                 sPartPre = (bEli) ? "s’" + sPartPre : "se " + sPartPre;
@@ -384,30 +372,30 @@
         let sConj;
         if (!bTpsCo && bInt && sWho == ":1s" && this.dConj.get(sTemps)._get(":1ś", false)) {
             sWho = ":1ś";
         }
         if (bTpsCo) {
-            sConj = (!bPro) ? _getConjWithTags(this.sVerbAux, this._tTagsAux, sTemps, sWho) : getConj("être", sTemps, sWho);
+            sConj = (!bPro) ? conj._getConjWithTags(this.sVerbAux, this._tTagsAux, sTemps, sWho) : getConj("être", sTemps, sWho);
         } else {
             sConj = this.dConj.get(sTemps).get(sWho);
         }
         if (sConj === "") {
             return "";
         }
-        let bEli = _zStartVoy.test(sConj);
+        let bEli = conj._zStartVoy.test(sConj);
         if (bPro) {
             if (!this.bProWithEn) {
-                sConj = (bEli) ? _dProObjEl.get(sWho) + sConj : _dProObj.get(sWho) + sConj;
+                sConj = (bEli) ? conj._dProObjEl.get(sWho) + sConj : conj._dProObj.get(sWho) + sConj;
             } else {
-                sConj = _dProObjEl.get(sWho) + "en " + sConj;
+                sConj = conj._dProObjEl.get(sWho) + "en " + sConj;
             }
         }
         if (bNeg) {
             sConj = (bEli && !bPro) ? "n’" + sConj : "ne " + sConj;
         }
         if (bInt) {
-            if (sWho == ":3s" && !_zNeedTeuph.test(sConj)) {
+            if (sWho == ":3s" && !conj._zNeedTeuph.test(sConj)) {
                 sConj += "-t";
             }
             sConj += "-" + this._getPronom(sWho, bFem);
         } else {
             if (sWho == ":1s" && bEli && !bNeg && !bPro) {
@@ -436,39 +424,39 @@
                 return "elle";
             }
         } else if (sWho == ":3p" && bFem) {
             return "elles";
         }
-        return _dProSuj.get(sWho);
+        return conj._dProSuj.get(sWho);
     };
 
     imperatif (sWho, bPro, bNeg, bTpsCo, bFem) {
         if (!this.dConj.get(":E").get(sWho)) {
             return "";
         }
         let sImpe;
         if (bTpsCo) {
-            sImpe = (!bPro) ? _getConjWithTags(this.sVerbAux, this._tTagsAux, ":E", sWho) : getConj("être", ":E", sWho);
+            sImpe = (!bPro) ? conj._getConjWithTags(this.sVerbAux, this._tTagsAux, ":E", sWho) : getConj("être", ":E", sWho);
         } else {
             sImpe = this.dConj.get(":E").get(sWho);
         }
         if (sImpe === "") {
             return "";
         }
-        let bEli = _zStartVoy.test(sImpe);
+        let bEli = conj._zStartVoy.test(sImpe);
         if (bNeg) {
             if (bPro) {
                 if (!this.bProWithEn) {
-                    sImpe = (bEli && sWho == ":2s") ? "ne t’" + sImpe + " pas" : _dImpeProNeg.get(sWho) + sImpe + " pas";
+                    sImpe = (bEli && sWho == ":2s") ? "ne t’" + sImpe + " pas" : conj._dImpeProNeg.get(sWho) + sImpe + " pas";
                 } else {
-                    sImpe = _dImpeProNegEn.get(sWho) + sImpe + " pas";
+                    sImpe = conj._dImpeProNegEn.get(sWho) + sImpe + " pas";
                 }
             } else {
                 sImpe = (bEli) ? "n’" + sImpe + " pas" : "ne " + sImpe + " pas";
             }
         } else if (bPro) {
-            sImpe = (this.bProWithEn) ? sImpe + _dImpeProEn.get(sWho) : sImpe + _dImpePro.get(sWho);
+            sImpe = (this.bProWithEn) ? sImpe + conj._dImpeProEn.get(sWho) : sImpe + conj._dImpePro.get(sWho);
         }
         if (bTpsCo) {
             return sImpe + " " + this._seekPpas(bPro, bFem, sWho.endsWith("p") || this._sRawInfo[5] == "r");
         }
         return sImpe;
@@ -485,19 +473,31 @@
             return (this.dConj.get(":PQ").get(":Q3")) ? this.dConj.get(":PQ").get(":Q3") : this.dConj.get(":PQ").get(":Q1");
         }
         return (this.dConj.get(":PQ").get(":Q4")) ? this.dConj.get(":PQ").get(":Q4") : this.dConj.get(":PQ").get(":Q1");
     }
 }
+
 
 if (typeof(exports) !== 'undefined') {
-    // Used for Grammalecte library.
-    // In content scripts, these variable are directly reachable
+    // used within Grammalecte library
+    let helpers = require("resource://grammalecte/helpers.js");
+    conj.init(helpers.loadFile("resource://grammalecte/fr/conj_data.json"));
+} else {
+    // used within Firefox content script (conjugation panel).
+    // can’t load JSON from here, so we do it in ui.js and send it here.
+    self.port.on("provideConjData", function (sJSONData) {
+        conj.init(sJSONData);
+    });
+}
+
+
+if (typeof(exports) !== 'undefined') {
     exports.Verb = Verb;
-    exports.isVerb = isVerb;
-    exports.getConj = getConj;
-    exports.hasConj = hasConj;
-    exports.getVtyp = getVtyp;
-    exports.getSimil = getSimil;
-    exports._getTags = _getTags;
-    exports._hasConjWithTags = _hasConjWithTags;
-    exports._getConjWithTags = _getConjWithTags;
+    exports.isVerb = conj.isVerb;
+    exports.getConj = conj.getConj;
+    exports.hasConj = conj.hasConj;
+    exports.getVtyp = conj.getVtyp;
+    exports.getSimil = conj.getSimil;
+    exports._getTags = conj._getTags;
+    exports._hasConjWithTags = conj._hasConjWithTags;
+    exports._getConjWithTags = conj._getConjWithTags;
 }

Index: gc_lang/fr/modules-js/cregex.js
==================================================================
--- gc_lang/fr/modules-js/cregex.js
+++ gc_lang/fr/modules-js/cregex.js
@@ -262,39 +262,40 @@
     }
     return lMorph.some(s  =>  zNPm.test(s));
 }
 
 
-
-exports.getLemmaOfMorph = getLemmaOfMorph;
-exports.checkAgreement = checkAgreement;
-exports.checkConjVerb = checkConjVerb;
-exports.getGender = getGender;
-exports.getNumber = getNumber;
-exports.isNom = isNom;
-exports.isNomNotAdj = isNomNotAdj;
-exports.isAdj = isAdj;
-exports.isNomAdj = isNomAdj;
-exports.isNomVconj = isNomVconj;
-exports.isInv = isInv;
-exports.isSg = isSg;
-exports.isPl = isPl;
-exports.isEpi = isEpi;
-exports.isMas = isMas;
-exports.isFem = isFem;
-exports.mbNom = mbNom;
-exports.mbAdj = mbAdj;
-exports.mbAdjNb = mbAdjNb;
-exports.mbNomAdj = mbNomAdj;
-exports.mbNomNotAdj = mbNomNotAdj;
-exports.mbPpasNomNotAdj = mbPpasNomNotAdj;
-exports.mbVconj = mbVconj;
-exports.mbVconj123 = mbVconj123;
-exports.mbMG = mbMG;
-exports.mbInv = mbInv;
-exports.mbSg = mbSg;
-exports.mbPl = mbPl;
-exports.mbEpi = mbEpi;
-exports.mbMas = mbMas;
-exports.mbFem = mbFem;
-exports.mbNpr = mbNpr;
-exports.mbNprMasNotFem = mbNprMasNotFem;
+if (typeof(exports) !== 'undefined') {
+    exports.getLemmaOfMorph = getLemmaOfMorph;
+    exports.checkAgreement = checkAgreement;
+    exports.checkConjVerb = checkConjVerb;
+    exports.getGender = getGender;
+    exports.getNumber = getNumber;
+    exports.isNom = isNom;
+    exports.isNomNotAdj = isNomNotAdj;
+    exports.isAdj = isAdj;
+    exports.isNomAdj = isNomAdj;
+    exports.isNomVconj = isNomVconj;
+    exports.isInv = isInv;
+    exports.isSg = isSg;
+    exports.isPl = isPl;
+    exports.isEpi = isEpi;
+    exports.isMas = isMas;
+    exports.isFem = isFem;
+    exports.mbNom = mbNom;
+    exports.mbAdj = mbAdj;
+    exports.mbAdjNb = mbAdjNb;
+    exports.mbNomAdj = mbNomAdj;
+    exports.mbNomNotAdj = mbNomNotAdj;
+    exports.mbPpasNomNotAdj = mbPpasNomNotAdj;
+    exports.mbVconj = mbVconj;
+    exports.mbVconj123 = mbVconj123;
+    exports.mbMG = mbMG;
+    exports.mbInv = mbInv;
+    exports.mbSg = mbSg;
+    exports.mbPl = mbPl;
+    exports.mbEpi = mbEpi;
+    exports.mbMas = mbMas;
+    exports.mbFem = mbFem;
+    exports.mbNpr = mbNpr;
+    exports.mbNprMasNotFem = mbNprMasNotFem;
+}

Index: gc_lang/fr/modules-js/lexicographe.js
==================================================================
--- gc_lang/fr/modules-js/lexicographe.js
+++ gc_lang/fr/modules-js/lexicographe.js
@@ -262,6 +262,8 @@
         return _dAD.get(s.slice(0, nPos)) + " +" + _dAD.get(s.slice(nPos+1));
     };
 }
 
 
-exports.Lexicographe = Lexicographe;
+if (typeof(exports) !== 'undefined') {
+    exports.Lexicographe = Lexicographe;
+}

Index: gc_lang/fr/modules-js/mfsp.js
==================================================================
--- gc_lang/fr/modules-js/mfsp.js
+++ gc_lang/fr/modules-js/mfsp.js
@@ -77,9 +77,11 @@
         return "## erreur, code : " + sSfx + " ##";
     }
 }
 
 
-exports.isFemForm = isFemForm;
-exports.getMasForm = getMasForm;
-exports.hasMiscPlural = hasMiscPlural;
-exports.getMiscPlural = getMiscPlural;
+if (typeof(exports) !== 'undefined') {
+    exports.isFemForm = isFemForm;
+    exports.getMasForm = getMasForm;
+    exports.hasMiscPlural = hasMiscPlural;
+    exports.getMiscPlural = getMiscPlural;
+}

Index: gc_lang/fr/modules-js/phonet.js
==================================================================
--- gc_lang/fr/modules-js/phonet.js
+++ gc_lang/fr/modules-js/phonet.js
@@ -66,8 +66,10 @@
     }
     return aSelect;
 }
 
 
-exports.hasSimil = hasSimil;
-exports.getSimil = getSimil;
-exports.selectSimil = selectSimil;
+if (typeof(exports) !== 'undefined') {
+    exports.hasSimil = hasSimil;
+    exports.getSimil = getSimil;
+    exports.selectSimil = selectSimil;
+}

Index: gc_lang/fr/modules-js/textformatter.js
==================================================================
--- gc_lang/fr/modules-js/textformatter.js
+++ gc_lang/fr/modules-js/textformatter.js
@@ -276,10 +276,11 @@
 
     getDefaultOptions () {
         return dDefaultOptions;
     }
 }
+
 
 if (typeof(exports) !== 'undefined') {
     exports.TextFormatter = TextFormatter;
     exports.oReplTable = oReplTable;
 }

ADDED   gc_lang/fr/oxt/_img/Algoo_logo.png
Index: gc_lang/fr/oxt/_img/Algoo_logo.png
==================================================================
--- /dev/null
+++ gc_lang/fr/oxt/_img/Algoo_logo.png
cannot compute difference between binary files

ADDED   gc_lang/fr/oxt/_img/logo120_text.png
Index: gc_lang/fr/oxt/_img/logo120_text.png
==================================================================
--- /dev/null
+++ gc_lang/fr/oxt/_img/logo120_text.png
cannot compute difference between binary files

Index: gc_lang/fr/rules.grx
==================================================================
--- gc_lang/fr/rules.grx
+++ gc_lang/fr/rules.grx
@@ -3158,11 +3158,11 @@
     <<- __else__ -2>> _                                                                             # Incohérence : “\1” est une préposition. “\2” est un verbe conjugué.
 __[i]/conf(conf_a_à_locutions1)__
     (a) (?:nouveau|présent|(?:bonne distance|bord|cause|contre-courant|côté|court|défaut|droite|gauche|l’(?:arrière|autre bout|écart|égard|extérieur|aune|avant|encontre|ins(?:u|tar)|intérieur|opposé)|la (?:portée|suite)|partir|portée|propos|rebours) d(?:es?|u))  @@0
     <<- -1>> à                                                                                      # Confusion. Utilisez la préposition “à”.
 __[s]/conf(conf_a_à_locutions2)__
-    (a) (?:califourchon|contre(?:cœur|temps)|côté d(?:e|’\w[\w-]+)|demi-mot|nouveau|présent|rebrousse-poil|regret|travers|tout-va|l’(?:heure (?:actuelle|qu il est)|accoutumée|amiable|avance|aven(?:ir|ant)|air libre|aveuglette|emporte-pièce|évidence|exclusion de toute autre chose|improviste|inverse|ordre du jour|œil nu|en croire|un(?:animité| (?:d’entre eux|des leurs)|e (?:d’entre elles|des leurs)))|la (?:bonne franquette|con|dér(?:ive|obée)|diable|fois|limite du supportable|lumière de tout ce(?:ci|la)|mords-moi-le-nœud|papa|petite semaine|pointe du progrès|queue leu leu|rescousse|sauvette|surprise générale|virgule près|volée)|partir (?:de (?:demain|là|maintenant|rien)|d’(?:aujourd’hui|hier|ici))|au(?:cun prix|trui|tre chose)|bas co[ûu]t|bâ(?:bord|tons rompus)|beaucoup près|belles dents|bien (?:des égards|y (?:penser|réfléchir|songer))|bon (?:compte|escient|droit)|bout (?:de (?:bras|souffle|forces?)|portant|touchant)|bras (?:ouverts|le corps)|brève échéance|but non lucratif|cause d(?:e (?:ça|[mt]oi|lui|[nv]ous)|’e(?:lles?|ux))|ce (?:compte-là|moment-là|titre)|cet (?:égard|instant(?: précis|))|cette (?:époque(?: de l’année|)|heure de la (?:journée|nuit))|chaque (?:fois|instant)|chaudes larmes|cœur (?:joie|ouvert|perdu)|ciel ouvert|contre-cœur|corps perdu|cou(?:p sûr|per le souffle|rt terme|rte (?:échéance|portée))|de nombreuses reprises|des kilomètres à la ronde|dose homéopathique|double (?:titre|tranchant)|durée limitée|en (?:juger par (?:[mts]on|[nv]otre|leur) expérience|perdre (?:haleine|la tête))|faible (?:allure|revenu)|feu et à sang|flanc de (?:colline|montagne)|fleur de peau|géométrie variable|grand(?:-peine|e échelle)|haut risque|hue et à dia|huis clos|intervalles (?:ir|)réguliers|juste (?:raison|titre)|long terme|longue(?: échéance| portée|ur (?:de (?:temps|journée))|d’année)|loyer modéré|main (?:armée|droite|gauche|levée)|maint(?:s égards|es reprises)|marche forcée|merveille|mi-(?:course|distance|temps)|mi(?:di|nuit)(?: pile|)|moindres frais|moyen(?: terme|ne échéance)|mots couverts|ne (?:jamais|pas|rien|guère)|n’en (?:pas douter|point douter|plus finir)|outrance|parler franc|part (?:entière|ça|ce(?:la|ci))|partir de là|part(?:ir de rien|s égales)|pas de (?:géant|loup|tortue|velours)|personne en danger|perte de vue|petit(?: feu|e (?:dose|échelle))|peu (?:de choses? |)près|pieds joints|pile ou face|plat(?: ventre|e couture)|plein(?: (?:régime|temps|nez)|s poumons)|plus (?:forte raison|d’un titre)|point nommé|portée de main|première vue|prix (?:cassé|modique)s?|proprement parler|qui (?:mieux mieux|que ce soit|de droit)|quelque distance|quelques exceptions près|ras bords?|rude épreuve|tel point|temps (?:plein|partiel|complet)|tête reposée|tire[ -]d’aile|titre (?:conservatoire|d’exemple|indicatif|informatif|grâcieux|personnel|posthume)|tombeau ouvert|tort (?:ou à raison|et à travers)|tour de (?:bras|rôle)|tous (?:crins|points de vue)|tout(?: (?:âge|bout de champ|crin|instant|jamais|le (?:moins|monde)|moment|point de vue|prix|un chacun)|e (?:allure|bride|épreuve|force|heure|vitesse|volée))|toutes (?:fins utiles|jambes)|tribord|un moment donné|usage interne|visage découvert|vive allure|voix (?:haute|basse)|vol d’oiseau|vrai dire|vue d’œil|y (?:regarder de plus près|réfléchir))  @@0
+    (a) (?:califourchon|contre(?:cœur|temps)|côté d(?:e|’\w[\w-]+)|demi-mot|nouveau|présent|rebrousse-poil|regret|travers|tout-va|l’(?:heure (?:actuelle|qu il est)|accoutumée|amiable|avance|aven(?:ir|ant)|air libre|aveuglette|emporte-pièce|évidence|exclusion de toute autre chose|improviste|inverse|ordre du jour|œil nu|en croire|un(?:animité| (?:d’entre eux|des leurs)|e (?:d’entre elles|des leurs)))|la (?:bonne franquette|con|dér(?:ive|obée)|diable|fois|limite du supportable|lumière de tout ce(?:ci|la)|mords-moi-le-nœud|papa|petite semaine|pointe du progrès|queue leu leu|rescousse|sauvette|surprise générale|virgule près|volée)|partir (?:de (?:demain|là|maintenant|rien)|d’(?:aujourd’hui|hier|ici))|au(?:cun prix|trui|tre chose)|bas co[ûu]t|bâ(?:bord|tons rompus)|beaucoup près|belles dents|bien (?:des égards|y (?:penser|réfléchir|songer))|bon (?:compte|escient|droit)|bout (?:de (?:bras|souffle|forces?)|portant|touchant)|bras (?:ouverts|le corps)|brève échéance|but non lucratif|cause d(?:e (?:ça|[mt]oi|lui|[nv]ous)|’e(?:lles?|ux))|ce (?:compte-là|moment-là|titre)|cet (?:égard|instant(?: précis|))|cette (?:époque(?: de l’année|)|heure de la (?:journée|nuit))|chaque (?:fois|instant)|chaudes larmes|cœur (?:joie|ouvert|perdu)|ciel ouvert|contre-cœur|corps perdu|cou(?:p sûr|per le souffle|rt terme|rte (?:échéance|portée))|couilles rabattues|de nombreuses reprises|des kilomètres à la ronde|dose homéopathique|double (?:titre|tranchant)|durée limitée|en (?:juger par (?:[mts]on|[nv]otre|leur) expérience|perdre (?:haleine|la tête))|faible (?:allure|revenu)|feu et à sang|flanc de (?:colline|montagne)|fleur de peau|géométrie variable|grand(?:-peine|e échelle)|haut risque|hue et à dia|huis clos|intervalles (?:ir|)réguliers|juste (?:raison|titre)|long terme|longue(?: échéance| portée|ur (?:de (?:temps|journée))|d’année)|loyer modéré|main (?:armée|droite|gauche|levée)|maint(?:s égards|es reprises)|marche forcée|merveille|mi-(?:course|distance|temps)|mi(?:di|nuit)(?: pile|)|moindres frais|moyen(?: terme|ne échéance)|mots couverts|ne (?:jamais|pas|rien|guère)|n’en (?:pas douter|point douter|plus finir)|outrance|parler franc|part (?:entière|ça|ce(?:la|ci))|partir de là|part(?:ir de rien|s égales)|pas de (?:géant|loup|tortue|velours)|personne en danger|perte de vue|petit(?: feu|e (?:dose|échelle))|peu (?:de choses? |)près|pieds joints|pile ou face|plat(?: ventre|e couture)|plein(?: (?:régime|temps|nez)|s poumons)|plus (?:forte raison|d’un titre)|point nommé|portée de main|première vue|prix (?:cassé|modique)s?|proprement parler|qui (?:mieux mieux|que ce soit|de droit)|quelque distance|quelques exceptions près|ras bords?|rude épreuve|tel point|temps (?:plein|partiel|complet)|tête reposée|tire[ -]d’aile|titre (?:conservatoire|d’exemple|indicatif|informatif|grâcieux|personnel|posthume)|tombeau ouvert|tort (?:ou à raison|et à travers)|tour de (?:bras|rôle)|tous (?:crins|points de vue)|tout(?: (?:âge|bout de champ|crin|instant|jamais|le (?:moins|monde)|moment|point de vue|prix|un chacun)|e (?:allure|bride|épreuve|force|heure|vitesse|volée))|toutes (?:fins utiles|jambes)|tribord|un moment donné|usage interne|visage découvert|vive allure|voix (?:haute|basse)|vol d’oiseau|vrai dire|vue d’œil|y (?:regarder de plus près|réfléchir))  @@0
     <<- not before(r"(?i)[ln]’$|(?<!-)\b(?:il|elle|on|y|n’en) +$")
     -1>> à                  # Confusion probable : “a” est la conjugaison du verbe “avoir”. Utilisez la préposition “à”.|http://fr.wiktionary.org/wiki/%C3%A0
 __[s]/conf(conf_a_à_locutions3)__
     (a) (?:confesse|mi(?:di|nuit)|r(?:allonge|eculons|enverse|isque)|tâtons|vélo|la (?:manque|ramasse|re(?:dresse|nverse)))  @@0
     <<- not before(r"(?i)(?:\bque? |[ln]’$|(?<!-)\b(?:il|elle|on|y|n’en) +$)")
@@ -4387,11 +4387,11 @@
 __<i](p_loc_de_début_phrase)__  ^ *(?:et(?: puis|)|puis|car|mais|or donc|m’est avis que) <<- ~>> *
 __[i](p_à_côté_de)__            à côté (?:de (?:ça|lui|[mt]oi|[nv]ous)|d’(?:elles|eux))(?! et) <<- ~>> *
 __[i](p_à_la_qqch)__            à la (?:bo(?:nne franquette|urre)|con|dér(?:ive|obée)|diable|fois|leur|limite du supportable|longue|lumière de tout ce(?:ci|la)|manque|mords-moi-le-nœud|papa|petite semaine|pointe du progrès|queue leu leu|ramasse|re(?:nverse|dresse|scousse)|sauvette|surprise générale|virgule près|volée) <<- ~>> *
 __[i](p_à_heure)__              à \d\d? ?h(?: ?\d\d|)(?: (?:du (?:matin|soir)|de l’après-midi|ce (?:matin|soir)|cet après-midi|demain (?:matin|soir|après-midi)|)|) <<- ~>> *
 __[i](p_à_loc_qqch1)__          à (?:califourchon|chacun|confesse|contre(?:cœur|temps)|demi-mot|foison|grand-peine|loisir|merveille|moitié|nouveau|outrance|peine|perpétuité|présent|raison|rallonge|rebrousse-poil|reculons|regret|renverse|risque|tâtons|tort|tout-va) <<- ~>> *
-__[i](p_à_loc_qqch2)__          à (?:au(?:cun prix|trui|tre chose)|bas (?:co[ûu]t|prix)|bâ(?:bord|tons rompus)|beaucoup près|belles dents|bien (?:des égards|pire|y (?:penser|réfléchir|songer))|bon (?:compte|escient|droit)|bout (?:de (?:bras|souffle|forces?)|nerfs|portant|touchant)|bras (?:ouverts|le corps)|brève échéance|but (?:non |)lucratif|cause d(?:e (?:ça|[mt]oi|lui|[nv]ous)|’e(?:lles?|ux))|ce (?:compte-là|moment-là|titre)|cet (?:égard|instant(?: précis|))|cette (?:date|époque(?: de l’année|)|heure de la (?:journée|nuit)|occasion)|chaque (?:fois|instant)|chaudes larmes|cœur (?:joie|ouvert|perdu)|ciel ouvert|contre-cœur|corps perdu|cou(?:p sûr|per le souffle|rt terme|rte (?:échéance|portée))|de (?:nombreuses|multiples) reprises|des kilomètres à la ronde|défaut d’autre chose|dose homéopathique|double (?:titre|tranchant)|durée limitée|en (?:juger par (?:[mts]on|[nv]otre|leur) expérience|perdre (?:haleine|la tête))|faible (?:allure|revenu)|feu et à sang|flanc de (?:colline|montagne)|fleur de peau|géométrie variable|grand(?:-peine|e échelle)|haut risque|hue et à dia|huis clos|intervalles (?:ir|)réguliers|juste (?:raison|titre)|long terme|longue(?: échéance| portée|ur (?:de (?:temps|journée))|d’année)|loyer modéré|main(?: (?:armée|droite|gauche|levée)|s nues)|maint(?:s égards|es reprises)|marche forcée|merveille|mi-(?:course|distance|temps)|mi(?:di|nuit)(?: pile|)|moindres frais|mots couverts|moyen(?: terme|ne échéance)|n’en (?:pas douter|point douter|plus finir)|outrance|parler franc|part (?:entière|ça|ce(?:la|ci))|partir de là|part(?:ir de rien|s égales)|pas de (?:géant|loup|tortue|velours)|personne en danger|perte de vue|petit(?: feu|e (?:dose|échelle))|peu (?:de (?:distance|choses près|frais)|près)|pieds joints|pile ou face|plat(?: ventre|e couture)|plein(?: (?:régime|temps|nez)|s poumons)|plus (?:forte raison|d’un titre)|point nommé|portée de (?:main|tir)|première vue|prix (?:cassé|modique)s?|proprement parler|qui (?:mieux mieux|que ce soit|de droit)|quelque(?: distance|s (?:exceptions|nuances) près)|ras bords?|rude épreuve|s’y méprendre|somme nulle|tel point|temps (?:plein|partiel|complet)|tête reposée|tire[ -]d’aile|titre (?:conservatoire|d’exemple|expérimental|indicatif|informatif|grâcieux|personnel|posthume)|tombeau ouvert|tort (?:ou à raison|et à travers)|tour de (?:bras|rôle)|tous (?:crins|points de vue)|toutes (?:fins utiles|jambes)|tribord|tu et à toi|un moment donné|usage interne|visage (?:découvert|humain)|vive allure|voix (?:haute|basse)|vol d’oiseau|vrai dire|vue d’œil|y (?:regarder de plus près|réfléchir)) <<- ~>> *
+__[i](p_à_loc_qqch2)__          à (?:au(?:cun prix|trui|tre chose)|bas (?:co[ûu]t|prix)|bâ(?:bord|tons rompus)|beaucoup près|belles dents|bien (?:des égards|pire|y (?:penser|réfléchir|songer))|bon (?:compte|escient|droit)|bout (?:de (?:bras|souffle|forces?)|nerfs|portant|touchant)|bras (?:ouverts|le corps)|brève échéance|but (?:non |)lucratif|cause d(?:e (?:ça|[mt]oi|lui|[nv]ous)|’e(?:lles?|ux))|ce (?:compte-là|moment-là|titre)|cet (?:égard|instant(?: précis|))|cette (?:date|époque(?: de l’année|)|heure de la (?:journée|nuit)|occasion)|chaque (?:fois|instant)|chaudes larmes|cœur (?:joie|ouvert|perdu)|ciel ouvert|contre-cœur|corps perdu|cou(?:p sûr|per le souffle|rt terme|rte (?:échéance|portée))|couilles rabattues|de (?:nombreuses|multiples) reprises|des kilomètres à la ronde|défaut d’autre chose|dose homéopathique|double (?:titre|tranchant)|durée limitée|en (?:juger par (?:[mts]on|[nv]otre|leur) expérience|perdre (?:haleine|la tête))|faible (?:allure|revenu)|feu et à sang|flanc de (?:colline|montagne)|fleur de peau|géométrie variable|grand(?:-peine|e échelle)|haut risque|hue et à dia|huis clos|intervalles (?:ir|)réguliers|juste (?:raison|titre)|long terme|longue(?: échéance| portée|ur (?:de (?:temps|journée))|d’année)|loyer modéré|main(?: (?:armée|droite|gauche|levée)|s nues)|maint(?:s égards|es reprises)|marche forcée|merveille|mi-(?:course|distance|temps)|mi(?:di|nuit)(?: pile|)|moindres frais|mots couverts|moyen(?: terme|ne échéance)|n’en (?:pas douter|point douter|plus finir)|outrance|parler franc|part (?:entière|ça|ce(?:la|ci))|partir de là|part(?:ir de rien|s égales)|pas de (?:géant|loup|tortue|velours)|personne en danger|perte de vue|petit(?: feu|e (?:dose|échelle))|peu (?:de (?:distance|choses près|frais)|près)|pieds joints|pile ou face|plat(?: ventre|e couture)|plein(?: (?:régime|temps|nez)|s poumons)|plus (?:forte raison|d’un titre)|point nommé|portée de (?:main|tir)|première vue|prix (?:cassé|modique)s?|proprement parler|qui (?:mieux mieux|que ce soit|de droit)|quelque(?: distance|s (?:exceptions|nuances) près)|ras bords?|rude épreuve|s’y méprendre|somme nulle|tel point|temps (?:plein|partiel|complet)|tête reposée|tire[ -]d’aile|titre (?:conservatoire|d’exemple|expérimental|indicatif|informatif|grâcieux|personnel|posthume)|tombeau ouvert|tort (?:ou à raison|et à travers)|tour de (?:bras|rôle)|tous (?:crins|points de vue)|toutes (?:fins utiles|jambes)|tribord|tu et à toi|un moment donné|usage interne|visage (?:découvert|humain)|vive allure|voix (?:haute|basse)|vol d’oiseau|vrai dire|vue d’œil|y (?:regarder de plus près|réfléchir)) <<- ~>> *
 __[i](p_à_partir_de)__          à partir (?:de (?:demain(?: matin| midi| soir|)|là|maintenant|rien)|d’(?:aujourd’hui|hier(?: matin| midi| soir|)|ici)) <<- ~>> *
 __[i](p_à_quelques_uns)__       à quelques-un(?:s d’entre (?:eux|nous|vous)|es d’entre (?:nous|vous|elles)) <<- ~>> *
 __[i](p_à_tout_qqch)__          à tout(?: (?:âge|bout de champ|crin|instant|jamais|le (?:moins|monde)|moment|point de vue|prix|un chacun)|e (?:allure|bride|épreuve|force|heure(?: d(?:u jour|e la nuit)|)|vitesse|volée)) <<- ~>> *
 __[i](p_à_l_qqch)__             à l’(?:heure (?:actuelle|qu il est)|accoutumée|amiable|avance|aven(?:ir(?: incertain)|ant)|air libre|aveuglette|emporte-pièce|échelle (?:nationale|mondiale|régionale|départementale|cantonale|locale|galactique|universelle)|évidence|exclusion de toute autre chose|improviste|inverse|occasion|ordre du jour|œil nu|en croire|un(?:animité| (?:d’entre eux|des leurs)|e (?:d’entre elles|des leurs))) <<- ~>> *
 __[i](p_à_det_plur_qqch)__      à (?:[mts]es|[nv]os|leurs) (?:côtés|dépens|risques et périls|trousses) <<- ~>> *
@@ -4467,11 +4467,11 @@
 __[i](p_du_même_ordre_coup)__   du même (?:ordre|coup) <<- ~>> *
 __[i](p_en_nombre_années)__     en \d\d+(?: ans| années|) <<- ~>> *
 __[i](p_en_cours)__             en cours(?! d[e’]) <<- ~>> *
 __[i](p_en_pronom)__            en (?:[mt]oi|eux|elles?) <<- ~>> *
 __[i](p_en_qqch1)__             en (?:aparté|apparence|arrière|avance|avant|cachette|ceci|cela|clair|commun|conséquence|continu|contrepartie|définitive|détail|direct|douce|effet|émoi|filigrane|général|goguette|hâte|majorité|outre|pâmoison|parallèle|partie|particulier|permanence|personne|pratique|prime|privé|principe|priorité|public|réalité|retour|revanche|rien|rogne|route|secret|silence|somme|suspens|théorie|trompe-l’œil|vain|vérité|ville|vitesse) <<- ~>> *
-__[i](p_en_qqch2)__             en (?:aucun(?: cas|e (?:circonstance|façon|manière))|bon(?: état|ne (?:compagnie|et due forme|posture|santé(?: physique| mentale|)|voie))|bout de course|cas d(?:e (?:besoin|doute)|’urgence)|chacune? d(?:e [nv]ous|’(?:eux|elles))|chair et en os|chute libre|comparution immédiate|connaissance de cause|coupe réglée|cours de route|d’autres (?:circonstances|termes|temps)|de telles circonstances|début d(?:e (?:journée|matinée|soirée)|’après-midi)|définitive|dehors de (?:tout|)(?:ça|cela|ceci)|dents de scie|dernier (?:lieu|recours|ressort)|désespoir de cause|détention provisoire|direction d(?:u (?:nord|sud)(?:-est|-ouest|)|e l’(?:est|ouest))|état (?:de (?:choc(?: circulatoire|)|marche)|d’ébriété(?: avancée|))|excellent état|file indienne|fin d(?:e (?:compte|journée|matinée|soirée)|’après-midi)|forte (?:baisse|hausse)|garde à vue(?: prolongée|)|grand(?: nombre|e (?:difficulté|majorité|partie|pompe))|haut lieu|l’occurrence|lieu sûr|ligne de (?:compte|mire)|mains propres|mauvais(?: état|e (?:posture|santé))|même temps|milieu d(?:e (?:journée|matinée|soirée)|’après-midi)|nombre (?:plus que |)suffisant|partant de zéro|plein(?: air| cœur| jour|e (?:gueule|figure|forme|nuit))|perte de vitesse|peu de temps|piteux état|point de mire|position de force|premi(?:er lieu|ère (?:instance|ligne))|pure perte|quantité (?:plus que |)suffisante|quelque sorte|queue de peloton|rangs serrés|rase campagne|règle générale|roue libre|sens inverse|si peu de temps|sous-main|tête à tête|temps (?:et en heure|normal|opportun|ordinaire|utile|voulu)|termes choisis|toile de fond|tous (?:les cas|sens)|tout (?:bien tout honneur|cas|genre|lieu|et pour tout|état de cause|premier lieu|sens|temps)|toute(?: (?:bonne foi|circonstance|connaissance de cause|confiance|discrétion|franchise|hâte|impartialité|impunité|innocence|légalité|liberté|logique|sécurité|simplicité)|s circonstances)|un (?:clin d’œil|rien de temps)|une autre occasion|vase clos|voie de développement|y réfléchissant bien) <<- ~>> *
+__[i](p_en_qqch2)__             en (?:aucun(?: cas|e (?:circonstance|façon|manière))|bon(?: état|ne (?:compagnie|et due forme|posture|santé(?: physique| mentale|)|voie))|bout de course|cas d(?:e (?:besoin|doute)|’urgence)|chacune? d(?:e [nv]ous|’(?:eux|elles))|chair et en os|chute libre|comparution immédiate|connaissance de cause|coupe réglée|cours de route|d’autres (?:circonstances|termes|temps)|de telles circonstances|début d(?:e (?:journée|matinée|soirée)|’après-midi)|définitive|dehors de (?:tout|)(?:ça|cela|ceci)|dents de scie|dernier (?:lieu|recours|ressort)|désespoir de cause|détention provisoire|direction d(?:u (?:nord|sud)(?:-est|-ouest|)|e l’(?:est|ouest))|état (?:de (?:choc(?: circulatoire|)|marche)|d’ébriété(?: avancée|))|excellent état|file indienne|fin d(?:e (?:compte|journée|matinée|soirée)|’après-midi)|forte (?:baisse|hausse)|garde à vue(?: prolongée|)|grand(?: nombre|e (?:difficulté|majorité|partie|pompe))|haut lieu|l’occurrence|lieu sûr|ligne de (?:compte|mire)|mains propres|mauvais(?: état|e (?:posture|santé))|même temps|milieu d(?:e (?:journée|matinée|soirée)|’après-midi)|nombre (?:plus que |)suffisant|partant de zéro|plein(?: air| cœur| jour|e (?:gueule|figure|forme|poire|nuit|tronche))|perte de vitesse|peu de temps|piteux état|point de mire|position de force|premi(?:er lieu|ère (?:instance|ligne))|pure perte|quantité (?:plus que |)suffisante|quelque sorte|queue de peloton|rangs serrés|rase campagne|règle générale|roue libre|sens inverse|si peu de temps|sous-main|tête à tête|temps (?:et en heure|normal|opportun|ordinaire|utile|voulu)|termes choisis|toile de fond|tous (?:les cas|sens)|tout (?:bien tout honneur|cas|genre|lieu|et pour tout|état de cause|premier lieu|sens|temps)|toute(?: (?:bonne foi|circonstance|connaissance de cause|confiance|discrétion|franchise|hâte|impartialité|impunité|innocence|légalité|liberté|logique|sécurité|simplicité)|s circonstances)|un (?:clin d’œil|rien de temps)|une autre occasion|vase clos|voie de développement|y réfléchissant bien) <<- ~>> *
 __[i](p_en_mois_dernier)__      en (?:janvier|février|mars|avril|mai|jui(?:n|llet)|ao[ûu]t|septembre|octobre|novembre|décembre) dernier <<- ~>> *
 __[i](p_en_dat_mas_qqch)__      en (?:[mts]on|leur|[nv]otre) (?:âme et conscience|for intérieur|nom propre) <<- ~>> *
 __[i](p_en_ce_qqch)__           en ce(?: (?:moment|temps-là|qui (?:[mt]e|l(?:es?|a)|[nv]ous) concern(?:e|ait))|t instant) <<- ~>> *
 __[i](p_encore_qqch)__          encore (?:une fois|et (?:encore|toujours)) <<- ~>> *
 __[i](p_envers_qqch)__          envers (?:autrui|et contre tout|les uns et les autres|tout le monde) <<- ~>> *
@@ -4729,10 +4729,11 @@
 __[i](p_salle)__                        salles? (à manger|d’attente|de (?:bains?|conférence)) @@$ <<- ~1>> *
 __[i](p_sain_de_corps)__                saine?s? (d(?:e corps et d|)’esprit) @@$ <<- ~1>> *
 __[i](p_sclérose_en_plaques)__          scléroses? (en plaques) @@$  <<- ~1>> *
 __[i](p_sembler_paraitre_être)__        (sembl\w+|par[au]\w+) +(être|avoir été) +({w_2}) @@0,w,$ <<- morph(\1, ">(?:sembler|para[îi]tre) ") and morphex(\3, ":A", ":G") ~2>> *
 __[i](p_silo)__                         silos? (à (?:grains?|blé)) @@$ <<- ~1>> *
+__[i](p_soue_à_cochons)__               soues? (à cochons?) @@$ <<- ~1>> *
 __[u](p_système)__                      systèmes? (d’exploitation|D) @@$ <<- ~1>> *
 __[i](p_taille)__                       taille (\d+) @@$ <<- ~1>> *
 __[i](p_taux_de_qqch)__                 taux (d’(?:abstention|absorption|alcool|alphabétisation|endettement|inflation|intérêt|imposition|occupation|ouverture|œstrogène|urée|usure)|de (?:change|cholest[ée]rol|glycémie|fécondité|participation|testostérone|TVA)) @@$ <<- ~1>> *
 __[i](p_tête_de_déterré)__              têtes? (de déterrée?s?) @@$ <<- ~1>> *
 __[i](p_tenir_compte)__                 (t[eiî]\w+) +(compte) d(?:es?|u) @@0,w <<- morph(\1, ">tenir ", False) ~2>> *

ADDED   gc_lang/fr/tb/skin/Algoo_logo.png
Index: gc_lang/fr/tb/skin/Algoo_logo.png
==================================================================
--- /dev/null
+++ gc_lang/fr/tb/skin/Algoo_logo.png
cannot compute difference between binary files

ADDED   gc_lang/fr/webext/README.md
Index: gc_lang/fr/webext/README.md
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/README.md
@@ -0,0 +1,22 @@
+# Grammalecte
+
+**French Grammar Checker**
+
+écrit en JavaScript ES6/ES7
+par Olivier R.
+
+## Fonctionnalités ##
+
+* correcteur grammatical
+* conjugueur
+* formateur de texte
+* lexicographe
+
+## Site web ##
+
+https://grammalecte.net
+
+## Licence ##
+
+GNU GPL 3.0+
+http://www.gnu.org/copyleft/gpl.html

ADDED   gc_lang/fr/webext/content_scripts/modify_page.js
Index: gc_lang/fr/webext/content_scripts/modify_page.js
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/content_scripts/modify_page.js
@@ -0,0 +1,28 @@
+import { echo } from "../mymodule";
+
+echo("CONTENT SCRIPRT!!!");
+
+function handleMessage2 (oRequest, xSender, sendResponse) {
+  console.log(`[Content script] received: ${oRequest.content}`);
+  change(request.myparam);
+  //browser.runtime.onMessage.removeListener(handleMessage);
+  sendResponse({response: "response from content script"});
+}
+
+function removeEverything () {
+  while (document.body.firstChild) {
+    document.body.firstChild.remove();
+  }
+}
+
+function change (param) {
+  document.getElementById("title").setAttribute("background-color", "#809060");
+  console.log("param: " + param);
+  document.getElementById("title").setAttribute("background-color", "#FF0000");
+}
+
+
+/*
+  Assign do_something() as a listener for messages from the extension.
+*/
+browser.runtime.onMessage.addListener(handleMessage2);

ADDED   gc_lang/fr/webext/gce_worker.js
Index: gc_lang/fr/webext/gce_worker.js
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/gce_worker.js
@@ -0,0 +1,136 @@
+
+
+/*
+try {
+    console.log("BEFORE");
+    //var myhelpers = require('./grammalecte/helpers.js');
+    require(['./grammalecte/helpers.js'], function (foo) {
+        console.log("LOADING");
+        echo("MODULE LOADED2");
+    });
+    console.log("AFTER");
+}
+catch (e) {
+    console.log("\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message);
+    console.error(e);
+}*/
+
+
+let gce = null; // module: grammar checker engine
+let text = null;
+let tkz = null; // module: tokenizer
+let lxg = null; // module: lexicographer
+let helpers = null;
+
+let oTokenizer = null;
+let oDict = null;
+let oLxg = null;
+
+function loadGrammarChecker (sGCOptions="", sContext="JavaScript") {
+    if (gce === null) {
+        try {
+            gce = require("resource://grammalecte/fr/gc_engine.js");
+            helpers = require("resource://grammalecte/helpers.js");
+            text = require("resource://grammalecte/text.js");
+            tkz = require("resource://grammalecte/tokenizer.js");
+            lxg = require("resource://grammalecte/fr/lexicographe.js");
+            oTokenizer = new tkz.Tokenizer("fr");
+            helpers.setLogOutput(console.log);
+            gce.load(sContext);
+            oDict = gce.getDictionary();
+            oLxg = new lxg.Lexicographe(oDict);
+            if (sGCOptions !== "") {
+                gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions)));
+            }
+            // we always retrieve options from the gce, for setOptions filters obsolete options
+            return gce.getOptions()._toString();
+        }
+        catch (e) {
+            console.log("# Error: " + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message);
+        }
+    }
+}
+
+function parse (sText, sLang, bDebug, bContext) {
+    let aGrammErr = gce.parse(sText, sLang, bDebug, bContext);
+    return JSON.stringify(aGrammErr);
+}
+
+function parseAndSpellcheck (sText, sLang, bDebug, bContext) {
+    let aGrammErr = gce.parse(sText, sLang, bDebug, bContext);
+    let aSpellErr = oTokenizer.getSpellingErrors(sText, oDict);
+    return JSON.stringify({ aGrammErr: aGrammErr, aSpellErr: aSpellErr });
+}
+
+function getOptions () {
+    return gce.getOptions()._toString();
+}
+
+function getDefaultOptions () {
+    return gce.getDefaultOptions()._toString();
+}
+
+function setOptions (sGCOptions) {
+    gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions)));
+    return gce.getOptions()._toString();
+}
+
+function setOption (sOptName, bValue) {
+    gce.setOptions(new Map([ [sOptName, bValue] ]));
+    return gce.getOptions()._toString();
+}
+
+function resetOptions () {
+    gce.resetOptions();
+    return gce.getOptions()._toString();
+}
+
+function fullTests (sGCOptions="") {
+    if (!gce || !oDict) {
+        return "# Error: grammar checker or dictionary not loaded."
+    }
+    let dMemoOptions = gce.getOptions();
+    if (sGCOptions) {
+        gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions)));
+    }
+    let tests = require("resource://grammalecte/tests.js");
+    let oTest = new tests.TestGrammarChecking(gce);
+    let sAllRes = "";
+    for (let sRes of oTest.testParse()) {
+        dump(sRes+"\n");
+        sAllRes += sRes+"\n";
+    }
+    gce.setOptions(dMemoOptions);
+    return sAllRes;
+}
+
+
+// Lexicographer
+
+function getListOfElements (sText) {
+    try {
+        let aElem = [];
+        let aRes = null;
+        for (let oToken of oTokenizer.genTokens(sText)) {
+            aRes = oLxg.getInfoForToken(oToken);
+            if (aRes) {
+                aElem.push(aRes);
+            }
+        }
+        return JSON.stringify(aElem);
+    }
+    catch (e) {
+        helpers.logerror(e);
+    }
+    return JSON.stringify([]);
+}
+
+
+function handleMessage (oRequest, xSender, sendResponse) {
+  console.log(`[background] received: ${oRequest.content}`);
+  sendResponse({response: "response from background script"});
+}
+
+browser.runtime.onMessage.addListener(handleMessage);
+
+

ADDED   gc_lang/fr/webext/img/logo-16.png
Index: gc_lang/fr/webext/img/logo-16.png
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/img/logo-16.png
cannot compute difference between binary files

ADDED   gc_lang/fr/webext/img/logo-32.png
Index: gc_lang/fr/webext/img/logo-32.png
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/img/logo-32.png
cannot compute difference between binary files

ADDED   gc_lang/fr/webext/img/logo-48.png
Index: gc_lang/fr/webext/img/logo-48.png
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/img/logo-48.png
cannot compute difference between binary files

ADDED   gc_lang/fr/webext/img/logo-64.png
Index: gc_lang/fr/webext/img/logo-64.png
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/img/logo-64.png
cannot compute difference between binary files

ADDED   gc_lang/fr/webext/img/logo-96.png
Index: gc_lang/fr/webext/img/logo-96.png
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/img/logo-96.png
cannot compute difference between binary files

ADDED   gc_lang/fr/webext/manifest.json
Index: gc_lang/fr/webext/manifest.json
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/manifest.json
@@ -0,0 +1,43 @@
+{
+  "manifest_version": 2,
+  "name": "Grammalecte [fr]",
+  "short_name": "Grammalecte [fr]",
+  "version": "0.6",
+
+  "applications": {
+    "gecko": {
+      "id": "French-GC@grammalecte.net",
+      "strict_min_version": "54.0"
+    }
+  },
+
+  "author": "Olivier R.",
+  "homepage_url": "https://grammalecte.net",
+  "offline_enabled": true,
+
+  "description": "Correcteur grammatical pour le français.",
+
+  "icons": { "16": "img/logo-16.png",
+             "32": "img/logo-32.png",
+             "48": "img/logo-48.png",
+             "64": "img/logo-64.png",
+             "96": "img/logo-96.png" },
+
+  "browser_action": {
+    "default_icon": "img/logo-32.png",
+    "default_popup": "panel/main.html",
+    "default_title": "Grammalecte [fr]",
+    "browser_style": false
+  },
+  "background": {
+    "scripts": ["require.js", "grammalecte/helpers.js", "gce_worker.js"]
+  },
+  "web_accessible_resources": [
+    "beasts/frog.jpg",
+    "beasts/turtle.jpg",
+    "beasts/snake.jpg"
+  ],
+  "permissions": [
+    "activeTab"
+  ]
+}

ADDED   gc_lang/fr/webext/panel/main.css
Index: gc_lang/fr/webext/panel/main.css
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/panel/main.css
@@ -0,0 +1,457 @@
+/*
+    flexbox:
+    https://css-tricks.com/snippets/css/a-guide-to-flexbox/
+*/
+
+
+/* reset */
+
+* { margin: 0; padding: 0; }
+img { border: none; }
+
+
+/* Selection */
+
+::-moz-selection {
+    background-color: hsl(210, 50%, 60%);
+    color: hsl(210, 20%, 100%);
+    text-shadow: 0 0 2px hsl(210, 80%, 20%);
+    border-radius: 2px;
+}
+::selection {
+    background-color: hsl(210, 50%, 60%);
+    color: hsl(210, 20%, 100%);
+    text-shadow: 0 0 2px hsl(210, 80%, 20%);
+    border-radius: 2px;
+}
+
+
+/* Generic classes */
+
+.fleft { float: left; }
+.fright { float: right; }
+
+.center { text-align: center; }
+.right { text-align: right; }
+.left { text-align: left; }
+.justify { text-align: justify; }
+
+.hidden { display: none; }
+.clearer { clear: both; font-size: 0; height: 0; }
+
+.red {          background-color: hsl(0, 50%, 50%);   color: hsl(0, 0%, 96%); }
+.red:hover {    background-color: hsl(0, 60%, 40%);   color: hsl(0, 0%, 100%); }
+.cyan {         background-color: hsl(180, 50%, 50%); color: hsl(0, 0%, 96%); }
+.cyan:hover {   background-color: hsl(180, 60%, 40%); color: hsl(0, 0%, 100%); }
+.green {        background-color: hsl(120, 50%, 40%); color: hsl(120, 10%, 96%); }
+.green:hover {  background-color: hsl(120, 60%, 30%); color: hsl(120, 10%, 96%); }
+.blue {         background-color: hsl(210, 50%, 50%); color: hsl(210, 10%, 96%); }
+.blue:hover {   background-color: hsl(210, 60%, 40%); color: hsl(210, 10%, 96%); }
+
+
+/* links */
+
+a:link, a:visited {
+    color: hsl(210, 70%, 40%);
+    /*text-decoration: none;*/
+}
+a:hover, a:active {
+    text-shadow: 0 0 2px hsl(210, 80%, 60%);
+}
+
+a.extlink:hover:after {
+    content: " >";
+}
+
+
+/* Main classes */
+
+html {
+    box-sizing: border-box;
+    width: 530px;
+    height: 880px;
+    font-family: "Trebuchet MS", "Liberation Sans", sans-serif;
+}
+body {
+    width: 530px;
+    height: 880px;
+}
+
+#main {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: nowrap;
+    align-items: stretch;
+    background-color: hsl(210, 0%, 100%);
+    min-height: 100%;
+}
+
+#left {
+    width: 54px;
+    background-color: hsl(210, 10%, 96%);
+    border-right: solid 1px hsl(210, 0%, 70%);
+    color: hsl(210, 10%, 96%);
+}
+#logo {
+  padding: 10px;
+}
+#left li {
+  padding: 10px 5px;
+  border-bottom: 1px solid hsl(210, 10%, 90%);
+  text-align: center;
+  cursor: pointer;
+  color: hsl(210, 10%, 50%);
+  list-style-type: none;
+}
+#left li:hover {
+  background-color: hsl(210, 10%, 92%);
+
+}
+
+#page {
+    background-color: hsl(210, 0%, 100%);
+}
+#page h1 {
+    margin: 0 0 10px 0;
+    color: hsl(210, 70%, 70%);
+    font: bold 30px 'Yanone Kaffeesatz', "Liberation Sans Narrow", sans-serif;
+}
+#page p {
+    margin: 10px 0 5px 0;
+}
+
+#home_page {
+  display: block;
+  padding: 20px;
+}
+
+#tf_page {
+  display: none;
+  padding: 20px;
+}
+#gc_page {
+  display: none;
+  padding: 20px 20px 30px 20px;
+}
+#gc_options_page {
+  display: none;
+  padding: 20px;
+}
+#sc_options_page {
+  display: none;
+  padding: 20px;
+}
+#lxg_page {
+  display: none;
+  padding: 20px;
+}
+
+
+/*
+  Conjugueur page
+*/
+
+#conj_page {
+  display: none;
+  padding: 10px;
+}
+
+#conj_page h2 {
+    margin: 5px 0 2px 0;
+    color: hsl(210, 50%, 50%);
+    font: bold 30px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif;
+}
+#conj_page h3 {
+    margin: 5px 0 2px 0;
+    color: hsl(0, 50%, 50%);
+    font: bold 16px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif;
+}
+#conj_page h4 {
+    margin: 5px 0 2px 0;
+    color: hsl(210, 50%, 50%);
+    font: bold 14px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif;
+}
+
+#conj_page .colonne {
+    float: left;
+    width: 240px;
+}
+#conj_page .colsep {
+    float: left;
+    width: 20px;
+}
+
+#conj_page .colonne p {
+    font-size: 12px;
+}
+
+
+#conj_page input#verb {
+    display: inline-block;
+    width: 230px;
+    margin-left: 5px;
+    padding: 5px 10px;
+    border: 2px solid hsl(0, 0%, 80%);
+    border-radius: 3px;
+    height: 24px;
+    background: transparent;
+    font: normal 20px Tahoma, "Ubuntu Condensed";
+    color: hsl(0, 0%, 30%);
+}
+#conj_page input[placeholder]#verb {
+    color: hsl(0, 0%, 70%);
+}
+
+#conj_page a#conjugate {
+    display: inline-block;
+    padding: 7px 10px;
+    font-size: 20px;
+    background-color: hsl(0, 30%, 30%);
+    color: hsl(0, 30%, 60%);
+    border-radius: 3px;
+    text-transform: uppercase;
+    text-align: center;
+    text-decoration: none;
+}
+#conj_page a#conjugate:hover {
+    background-color: hsl(0, 60%, 40%);
+    color: hsl(0, 60%, 70%);
+    box-shadow: 0 0 2px hsl(0, 60%, 50%);
+}
+
+#conj_options {
+    margin: 10px 5px 0 5px;
+    font-size: 16px;
+    text-align: center;
+}
+
+#conj_smallnote {
+    float: right;
+    width: 190px;
+    margin: 15px 0 0 0;
+    padding: 0 5px;
+    font-size: 8.5px;
+    color: hsl(0, 0%, 60%);
+    text-align: center;
+}
+
+
+/*
+  Test page
+*/
+
+#test_page {
+  display: none;
+}
+#test_cmd {
+    padding: 15px;
+    background-color: hsl(0, 0%, 92%);
+    border-bottom: 1px solid hsl(0, 0%, 86%);
+}
+#test_cmd textarea {
+    width: 100%;
+    border: 2px solid hsl(0, 0%, 89%);
+    border-radius: 3px;
+    resize: vertical;
+}
+
+#test_results {
+    padding: 15px;
+    background-color: hsl(0, 0%, 96%);
+}
+
+#test_page .button {
+    display: inline-block;
+    padding: 5px 10px;
+    width: 120px;
+    border-radius: 3px;
+    font-size: 12px;
+    text-align: center;
+    cursor: pointer;
+}
+
+
+/*
+  Text formatter
+*/
+
+#tf_options {
+    
+}
+
+#tf_options fieldset {
+    margin: 5px 0;
+    padding: 5px 10px 10px 10px;
+    background-color: hsl(0, 0%, 92%);
+    border-radius: 3px;
+}
+
+#tf_options legend {
+    font-size: 20px;
+    color: hsla(210, 20%, 50%, .8);
+    font-weight: bold;
+}
+#tf_options legend span {
+    display: none;
+}
+
+#tf_options fieldset h2 {
+    color: hsl(210, 80%, 40%);
+}
+
+#tf_options fieldset .blockopt {
+    padding: 2px 3px;
+    font-size: 12.5px;
+}
+#tf_options fieldset .underline:hover {
+    background-color: hsl(180, 10%, 86%);
+    border-radius: 2px;
+}
+
+#tf_options fieldset .option {
+    margin: 1px 3px 0 0;
+    float: left;
+}
+#tf_options legend .option {
+    margin: 7px 5px 0 3px;
+    float: left;
+}
+
+#tf_options fieldset .opt_lbl {
+    display: inline-block;
+    color: hsl(0, 0%, 20%);
+}
+
+
+#tf_options fieldset .largew {
+    width: 300px;
+}
+#tf_options fieldset .reducedw {
+    width: 200px;
+}
+#tf_options fieldset .smallw {
+    width: 90px;
+}
+
+#tf_options fieldset .secondoption {
+    display: inline-block;
+}
+
+#tf_options fieldset label span {
+    display: none;
+}
+
+#tf_options .groupblock {
+    opacity: 0.3;
+}
+
+#tf_options .inlineblock {
+    display: inline-block;
+}
+#tf_options .indent {
+    margin-left: 15px;
+}
+
+#tf_actions {
+    background-color: hsl(120, 10%, 92%);
+    padding: 15px;
+    border-top: 1px solid hsl(120, 20%, 86%);
+}
+
+#tf_options .button {
+    display: inline-block;
+    padding: 5px 10px;
+    width: 100px;
+    border-radius: 3px;
+    font-size: 16px;
+    font-weight: bold;
+    text-align: center;
+    cursor: pointer;
+}
+
+#tf_progressbarbox {
+    display: inline-block;
+    padding: 10px 20px;
+}
+
+
+/* 
+  Other elements
+*/
+
+#movewindow {
+    position: fixed;
+    right: 0;
+    top: 50;
+    width: 16px;
+    margin-top: 60px;
+    z-index: 100;
+}
+#movewindow .arrow {
+    background-color: hsl(180, 60%, 50%);
+    cursor: pointer;
+    padding: 1px 3px;
+    font-size: 10px;
+    font-weight: bold;
+    text-align: center;
+    color: hsl(180, 50%, 90%);
+}
+#movewindow .arrow:hover {
+    background-color: hsl(180, 70%, 40%);
+    cursor: hsl(180, 50%, 96%);
+}
+
+#rightcorner {
+    position: absolute;
+    top: 0;
+    right: 0;
+}
+a.rightcornerbutton1 {
+    float: right;
+    padding: 2px 10px 5px 10px;
+    border-radius: 0 0 0 3px;
+    font-size: 18px;
+    text-decoration: none;
+}
+a.rightcornerbutton {
+    float: right;
+    padding: 2px 10px 5px 10px;
+    font-size: 18px;
+    text-decoration: none;
+}
+
+
+/*
+    CSS Spinner
+    Double bounce
+    http://tobiasahlin.com/spinkit/
+*/
+.spinner {
+    width: 40px;
+    height: 40px;
+    position: absolute;
+    top: 2px;
+    right: 120px;
+}
+.double-bounce1, .double-bounce2 {
+    width: 100%;
+    height: 100%;
+    border-radius: 50%;
+    background-color: hsl(180, 50%, 75%);
+    opacity: 0.6;
+    position: absolute;
+    top: 0;
+    left: 0;
+    animation: sk-bounce 2.0s infinite ease-in-out;
+}
+.double-bounce2 {
+    animation-delay: -1.0s;
+}
+
+@keyframes sk-bounce {
+    0%, 100% { 
+        transform: scale(0.0);
+    } 50% { 
+        transform: scale(1.0);
+    }
+}

ADDED   gc_lang/fr/webext/panel/main.html
Index: gc_lang/fr/webext/panel/main.html
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/panel/main.html
@@ -0,0 +1,495 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <meta charset="utf-8">
+    <link rel="stylesheet" href="main.css"/>
+  </head>
+
+  <body>
+    <div id="main">
+
+      <header id="left">
+        <nav id="menu">
+          <header id="logo">
+            <img src="../img/logo-32.png">
+          </header>
+          <ul>
+            <li class="select" data-page="home_page"><i class="fa fa-home icon"></i> 1.</li>
+            <li class="select" data-page="conj_page"><i class="fa fa-star icon"></i> CJ</li>
+            <li class="select" data-page="tf_page"><i class="fa fa-photo icon"></i> TF</li>
+            <li class="select" data-page="gc_page"><i class="fa fa-question-circle icon"></i> CG</li>
+            <li class="select" data-page="gc_options_page"><i class="fa fa-coffee icon"></i> OP1</li>
+            <li class="select" data-page="sc_options_page"><i class="fa fa-keyboard-o icon"></i> OP2</li>
+            <li class="select" data-page="lxg_page"><i class="fa fa-keyboard-o icon"></i> LXG</li>
+            <li class="select" data-page="test_page"><i class="fa fa-keyboard-o icon"></i> TST</li>
+          </ul>
+        </nav>
+      </header> <!-- #left -->
+
+      <div id="movewindow">
+        <div id="resize_h_bigger" class="arrow" style="border-radius: 2px 0 0 0">↓</div>
+        <div id="resize_h_smaller" class="arrow">↑</div>
+        <div id="resize_w_bigger" class="arrow">←</div>
+        <div id="resize_w_smaller" class="arrow" style="border-radius: 0 0 0 2px">→</div>
+      </div>
+
+      <div id="page">
+
+        <section id="home_page" class="page">
+          <h1>GRAMMALECTE</h1>
+        </section>
+
+        <section id="gc_page" class="page">
+          <h1>CORRECTEUR GRAMMATICAL</h1>
+          <div id="paragraphs_list"></div>
+        </section>
+
+        <section id="gc_options_page" class="page">
+          <h1>OPTIONS GRAMMATICALES</h1>
+        </section>
+
+        <section id="sc_options_page" class="page">
+          <h1>OPTIONS ORTHOGRAPHIQUES</h1>
+        </section>
+
+        <section id="lxg_page" class="page">
+          <h1>LEXICOGRAPHE</h1>
+          <div id="tokens_list"></div>
+        </section>
+
+        <section id="test_page" class="page">
+          <div id="test_cmd">
+            <h1>TESTS</h1>
+            <textarea id="text" rows="10"></textarea>
+            <div id="testall" class="button blue">Tests complets</div> <div id="parse" class="button green fright">Analyser</div>
+          </div>
+          <div id="test_results">
+          </div>
+        </section>
+
+        <section id="conj_page" class="page">
+          <h1>CONJUGUEUR</h1>
+          <p class="right" style="margin: 10px 30px 0 0">
+            <input type="text" id="verb" name="verb" maxlength="40" value="" placeholder="entrez un verbe" autofocus />
+            <a id="conjugate" href="#" onclick="return false;">Conjuguer</a>
+          <p>
+
+          <div class="clearer"></div>
+
+          <p id="conj_smallnote" hidden>Ce verbe n’a pas encore été vérifié. C’est pourquoi les options “pronominal” et “temps composés” sont désactivées.</p>
+          <p id="conj_options">
+            <label for="oneg">Négation</label> <input type="checkbox" id="oneg" name="oneg" value="ON"  /> 
+            · <label id="opro_lbl" for="opro">Pronominal</label> <input type="checkbox" id="opro" name="opro" value="ON"  />
+            · <label for="ofem">Féminin</label> <input type="checkbox" id="ofem" name="ofem" value="ON"  />
+            <br/> <label for="oint">Interrogatif</label> <input type="checkbox" id="oint" name="oint" value="ON"  />
+            · <label id="otco_lbl" for="otco">Temps composés</label> <input type="checkbox" id="otco" name="otco" value="ON"  />
+          </p>
+
+          <h2 id="verb_title" class="center">&nbsp;</h2>
+          <p id="info" class="center">&nbsp;</p>
+
+          <!-- section 1 -->
+          <div class="colonne">
+            <div id="infinitif" class="box">
+              <h3 id="infinitif_title">Infinitif</h3>
+              <p id="infi">&nbsp;</p>
+            </div>
+            <div id="imperatif" class="box">
+              <h3 id="imperatif_title">Impératif</h3>
+              <h4 id="impe_temps">Présent</h4>
+              <p id="impe1">&nbsp;</p>
+              <p id="impe2">&nbsp;</p>
+              <p id="impe3">&nbsp;</p>
+            </div>
+          </div>
+          
+          <div class="colsep">&nbsp;</div>
+          
+          <div class="colonne">
+            <div id="partpre" class="box">
+              <h3 id="partpre_title">Participe présent</h3>
+              <p id="ppre">&nbsp;</p>
+            </div>
+            <div id="partpas" class="box">
+              <h3 id="partpas_title">Participes passés</h3>
+              <p id="ppas1">&nbsp;</p>
+              <p id="ppas2">&nbsp;</p>
+              <p id="ppas3">&nbsp;</p>
+              <p id="ppas4">&nbsp;</p>
+            </div>
+          </div>
+
+          <div class="clearer"></div>
+
+          <!-- section 2 -->
+          <div class="colonne">
+            <div id="indicatif" class="box">
+              <h3 id="indicatif_title">Indicatif</h3>
+              <div id="ipre">
+                <h4 id="ipre_temps">Présent</h4>
+                <p id="ipre1">&nbsp;</p>
+                <p id="ipre2">&nbsp;</p>
+                <p id="ipre3">&nbsp;</p>
+                <p id="ipre4">&nbsp;</p>
+                <p id="ipre5">&nbsp;</p>
+                <p id="ipre6">&nbsp;</p>
+              </div>
+              <div id="iimp">
+                <h4 id="iimp_temps">Imparfait</h4>
+                <p id="iimp1">&nbsp;</p>
+                <p id="iimp2">&nbsp;</p>
+                <p id="iimp3">&nbsp;</p>
+                <p id="iimp4">&nbsp;</p>
+                <p id="iimp5">&nbsp;</p>
+                <p id="iimp6">&nbsp;</p>
+              </div>
+              <div id="ipsi">
+                <h4 id="ipsi_temps">Passé simple</h4>
+                <p id="ipsi1">&nbsp;</p>
+                <p id="ipsi2">&nbsp;</p>
+                <p id="ipsi3">&nbsp;</p>
+                <p id="ipsi4">&nbsp;</p>
+                <p id="ipsi5">&nbsp;</p>
+                <p id="ipsi6">&nbsp;</p>
+              </div>
+              <div id="ifut">
+                <h4 id="ifut_temps">Futur</h4>
+                <p id="ifut1">&nbsp;</p>
+                <p id="ifut2">&nbsp;</p>
+                <p id="ifut3">&nbsp;</p>
+                <p id="ifut4">&nbsp;</p>
+                <p id="ifut5">&nbsp;</p>
+                <p id="ifut6">&nbsp;</p>
+              </div>
+            </div>
+          </div>
+          
+          <div class="colsep">&nbsp;</div>
+          
+          <div class="colonne">
+            <div id="subjonctif" class="box">
+              <h3 id="subjontif_title">Subjonctif</h3>
+              <div id="spre">
+                <h4 id="spre_temps">Présent</h4>
+                <p id="spre1">&nbsp;</p>
+                <p id="spre2">&nbsp;</p>
+                <p id="spre3">&nbsp;</p>
+                <p id="spre4">&nbsp;</p>
+                <p id="spre5">&nbsp;</p>
+                <p id="spre6">&nbsp;</p>
+              </div>
+              <div id="simp">
+                <h4 id="simp_temps">Imparfait</h4>
+                <p id="simp1">&nbsp;</p>
+                <p id="simp2">&nbsp;</p>
+                <p id="simp3">&nbsp;</p>
+                <p id="simp4">&nbsp;</p>
+                <p id="simp5">&nbsp;</p>
+                <p id="simp6">&nbsp;</p>
+              </div>
+            </div>
+            <div id="conditionnel" class="box">
+              <h3 id="conditionnel_title">Conditionnel</h3>
+              <div id="conda">
+                <h4 id="conda_temps">Présent</h4>
+                <p id="conda1">&nbsp;</p>
+                <p id="conda2">&nbsp;</p>
+                <p id="conda3">&nbsp;</p>
+                <p id="conda4">&nbsp;</p>
+                <p id="conda5">&nbsp;</p>
+                <p id="conda6">&nbsp;</p>
+              </div>
+              <div id="condb">
+                <h4 id="condb_temps">&nbsp;</h4>
+                <p id="condb1">&nbsp;</p>
+                <p id="condb2">&nbsp;</p>
+                <p id="condb3">&nbsp;</p>
+                <p id="condb4">&nbsp;</p>
+                <p id="condb5">&nbsp;</p>
+                <p id="condb6">&nbsp;</p>
+              </div>
+            </div>
+          </div>
+
+          <div class="clearer"></div>
+        </section> <!-- conjugueur -->
+
+        <section id="tf_page" class="page">
+          <h1>FORMATEUR DE TEXTE</h1>
+          <div id="tf_options">
+
+            <!-- Supernumerary spaces -->
+            <fieldset>
+              <legend><input type="checkbox" id="o_group_ssp" class="option" data-default="true" /><label for="o_group_ssp" data-l10n-en="tf_ssp">${tf_ssp}</label></legend>
+              <div id="group_ssp" class="groupblock">
+                <div class="blockopt underline">
+                  <div id="res_o_start_of_paragraph" class="result fright"></div>
+                  <input type="checkbox" id="o_start_of_paragraph" class="option" data-default="true" />
+                  <label for="o_start_of_paragraph" class="opt_lbl largew" data-l10n-en="tf_start_of_paragraph">${tf_start_of_paragraph}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_end_of_paragraph" class="result fright"></div>
+                  <input type="checkbox" id="o_end_of_paragraph" class="option" data-default="true" />
+                  <label for="o_end_of_paragraph" class="opt_lbl largew" data-l10n-en="tf_end_of_paragraph">${tf_end_of_paragraph}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_between_words" class="result fright"></div>
+                  <input type="checkbox" id="o_between_words" class="option" data-default="true" />
+                  <label for="o_between_words" class="opt_lbl largew" data-l10n-en="tf_between_words">${tf_between_words}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_before_punctuation" class="result fright"></div>
+                  <input type="checkbox" id="o_before_punctuation" class="option" data-default="true" />
+                  <label for="o_before_punctuation" class="opt_lbl largew" data-l10n-en="tf_before_punctuation">${tf_before_punctuation}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_within_parenthesis" class="result fright"></div>
+                  <input type="checkbox" id="o_within_parenthesis" class="option" data-default="true" />
+                  <label for="o_within_parenthesis" class="opt_lbl largew" data-l10n-en="tf_within_parenthesis">${tf_within_parenthesis}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_within_square_brackets" class="result fright"></div>
+                  <input type="checkbox" id="o_within_square_brackets" class="option" data-default="true" />
+                  <label for="o_within_square_brackets" class="opt_lbl largew" data-l10n-en="tf_within_square_brackets">${tf_within_square_brackets}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_within_quotation_marks" class="result fright"></div>
+                  <input type="checkbox" id="o_within_quotation_marks" class="option" data-default="true" />
+                  <label for="o_within_quotation_marks" class="opt_lbl largew" data-l10n-en="tf_within_quotation_marks">${tf_within_quotation_marks}</label>
+                </div>
+              </div>
+            </fieldset>
+
+            <!-- Missing spaces -->
+            <fieldset>
+              <legend><input type="checkbox" id="o_group_space" class="option" data-default="true" /><label for="o_group_space" data-l10n-en="tf_space">${tf_space}</label></legend>
+              <div id="group_space" class="groupblock">
+                <div class="blockopt underline">
+                  <div id="res_o_add_space_after_punctuation" class="result fright"></div>
+                  <input type="checkbox" id="o_add_space_after_punctuation" class="option" data-default="true" />
+                  <label for="o_add_space_after_punctuation" class="opt_lbl reducedw" data-l10n-en="tf_add_space_after_punctuation">${tf_add_space_after_punctuation}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_add_space_around_hyphens" class="result fright"></div>
+                  <input type="checkbox" id="o_add_space_around_hyphens" class="option" data-default="true" />
+                  <label for="o_add_space_around_hyphens" class="opt_lbl largew" data-l10n-en="tf_add_space_around_hyphens">${tf_add_space_around_hyphens}</label>
+                </div>
+              </div>
+            </fieldset>
+
+            <!-- Non breaking spaces -->
+            <fieldset>
+              <legend><input type="checkbox" id="o_group_nbsp" class="option" data-default="true" /><label for="o_group_nbsp" data-l10n-en="tf_nbsp">${tf_nbsp}</label></legend>
+              <div id="group_nbsp" class="groupblock">
+                <div class="blockopt underline">
+                  <div id="res_o_nbsp_before_punctuation" class="result fright"></div>
+                  <input type="checkbox" id="o_nbsp_before_punctuation" class="option" data-default="true" />
+                  <label for="o_nbsp_before_punctuation" class="opt_lbl reducedw" data-l10n-en="tf_nbsp_before_punctuation">${tf_nbsp_before_punctuation}</label>
+                  <!--<div class="secondoption">
+                      <input type="checkbox" id="o_nnbsp_before_punctuation" class="option" />
+                      <label for="o_nnbsp_before_punctuation" class="opt_lbl smallw">fines<span>sauf avec “:”</span></label>
+                  </div>-->
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_nbsp_within_quotation_marks" class="result fright"></div>
+                  <input type="checkbox" id="o_nbsp_within_quotation_marks" class="option" data-default="true" />
+                  <label for="o_nbsp_within_quotation_marks" class="opt_lbl reducedw" data-l10n-en="tf_nbsp_within_quotation_marks">${tf_nbsp_within_quotation_marks}</label>
+                  <!--<div class="secondoption">
+                      <input type="checkbox" id="o_nnbsp_within_quotation_marks" class="option" />
+                      <label for="o_nnbsp_within_quotation_marks" class="opt_lbl smallw">fines</label>
+                  </div>-->
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_nbsp_before_symbol" class="result fright"></div>
+                  <input type="checkbox" id="o_nbsp_before_symbol" class="option" data-default="true" />
+                  <label for="o_nbsp_before_symbol" class="opt_lbl largew" data-l10n-en="tf_nbsp_before_symbol">${tf_nbsp_before_symbol}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_nbsp_within_numbers" class="result fright"></div>
+                  <input type="checkbox" id="o_nbsp_within_numbers" class="option" data-default="true" />
+                  <label for="o_nbsp_within_numbers" class="opt_lbl reducedw" data-l10n-en="tf_nbsp_within_numbers">${tf_nbsp_within_numbers}</label>
+                  <!--<div class="secondoption">
+                      <input type="checkbox" id="o_nnbsp_within_numbers" class="option" />
+                      <label for="o_nnbsp_within_numbers" class="opt_lbl smallw">fines</label>
+                  </div>-->
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_nbsp_before_units" class="result fright"></div>
+                  <input type="checkbox" id="o_nbsp_before_units" class="option" data-default="true" />
+                  <label for="o_nbsp_before_units" class="opt_lbl largew" data-l10n-en="tf_nbsp_before_units">${tf_nbsp_before_units}</label>
+                </div>
+              </div>
+            </fieldset>
+
+            <!-- Deletions -->
+            <fieldset>
+              <legend><input type="checkbox" id="o_group_delete" class="option" data-default="true" /><label for="o_group_delete" data-l10n-en="tf_delete">${tf_delete}</label></legend>
+              <div id="group_delete" class="groupblock">
+                <div class="blockopt underline">
+                  <div id="res_o_erase_non_breaking_hyphens" class="result fright"></div>
+                  <input type="checkbox" id="o_erase_non_breaking_hyphens" class="option" data-default="true" />
+                  <label for="o_erase_non_breaking_hyphens" class="opt_lbl largew" data-l10n-en="tf_erase_non_breaking_hyphens">${tf_erase_non_breaking_hyphens}</label>
+                </div>
+              </div>
+            </fieldset>
+
+            <!-- Typographical signs -->
+            <fieldset>
+              <legend><input type="checkbox" id="o_group_typo" class="option" data-default="true" /><label for="o_group_typo" data-l10n-en="tf_typo">${tf_typo}</label></legend>
+              <div id="group_typo" class="groupblock">
+                <div class="blockopt underline">
+                  <div id="res_o_ts_apostrophe" class="result fright"></div>
+                  <input type="checkbox" id="o_ts_apostrophe" class="option" data-default="true" />
+                  <label for="o_ts_apostrophe" class="opt_lbl largew" data-l10n-en="tf_ts_apostrophe">${tf_ts_apostrophe}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ts_ellipsis" class="result fright"></div>
+                  <input type="checkbox" id="o_ts_ellipsis" class="option" data-default="true" />
+                  <label for="o_ts_ellipsis" class="opt_lbl largew" data-l10n-en="tf_ts_ellipsis">${tf_ts_ellipsis}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ts_dash_middle" class="result fright"></div>
+                  <input type="checkbox" id="o_ts_dash_middle" class="option" data-default="true" />
+                  <label for="o_ts_dash_middle" class="opt_lbl largew" data-l10n-en="tf_ts_dash_middle">${tf_ts_dash_middle}</label>
+                </div>
+                <div class="blockopt">
+                  <div class="inlineblock indent">
+                    <input type="radio" name="hyphen1" id="o_ts_m_dash_middle" class="option" data-default="false" /><label for="o_ts_m_dash_middle" class="opt_lbl" data-l10n-en="tf_emdash">${tf_emdash}</label>
+                  </div>
+                  <div class="inlineblock indent">
+                    <input type="radio" name="hyphen1" id="o_ts_n_dash_middle" class="option" data-default="true" /><label for="o_ts_n_dash_middle" class="opt_lbl" data-l10n-en="tf_endash">${tf_endash}</label>
+                  </div>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ts_dash_start" class="result fright"></div>
+                  <input type="checkbox" id="o_ts_dash_start" class="option" data-default="true" />
+                  <label for="o_ts_dash_start" class="opt_lbl largew" data-l10n-en="tf_ts_dash_start">${tf_ts_dash_start}</label>
+                </div>
+                <div class="blockopt">
+                  <div class="inlineblock indent">
+                    <input type="radio" name="hyphen2" id="o_ts_m_dash_start" class="option"  data-default="true" /><label for="o_ts_m_dash_start" class="opt_lbl" data-l10n-en="tf_emdash">${tf_emdash}</label>
+                  </div>
+                  <div class="inlineblock indent">
+                    <input type="radio" name="hyphen2" id="o_ts_n_dash_start" class="option" data-default="false" /><label for="o_ts_n_dash_start" class="opt_lbl" data-l10n-en="tf_endash">${tf_endash}</label>
+                  </div>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ts_quotation_marks" class="result fright"></div>
+                  <input type="checkbox" id="o_ts_quotation_marks" class="option" data-default="true" />
+                  <label for="o_ts_quotation_marks" class="opt_lbl largew" data-l10n-en="tf_ts_quotation_marks">${tf_ts_quotation_marks}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ts_units" class="result fright"></div>
+                  <input type="checkbox" id="o_ts_units" class="option" data-default="true" />
+                  <label for="o_ts_units" class="opt_lbl largew" data-l10n-en="tf_ts_units">${tf_ts_units}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ts_spell" class="result fright"></div>
+                  <input type="checkbox" id="o_ts_spell" class="option" data-default="true" />
+                  <label for="o_ts_spell" class="opt_lbl largew" data-l10n-en="tf_ts_spell">${tf_ts_spell}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ts_ligature" class="result fright"></div>
+                  <div class="inlineblock">
+                    <input type="checkbox" id="o_ts_ligature" class="option" data-default="false" />
+                    <label for="o_ts_ligature" class="opt_lbl" data-l10n-en="tf_ts_ligature">${tf_ts_ligature}</label>
+                  </div>
+                  <div class="inlineblock indent">
+                    <input type="radio" id="o_ts_ligature_do" name="liga" class="option" data-default="false" />
+                    <label for="o_ts_ligature_do" class="opt_lbl" data-l10n-en="tf_ts_ligature_do">${tf_ts_ligature_do}</label>
+                  </div>
+                  <div class="inlineblock indent">
+                    <input type="radio" id="o_ts_ligature_undo" name="liga" class="option" data-default="true" />
+                    <label for="o_ts_ligature_undo" class="opt_lbl" data-l10n-en="tf_ts_ligature_undo">${tf_ts_ligature_undo}</label>
+                  </div>
+                </div>
+
+                <div class="blockopt">
+                  <div class="inlineblock indent"><input type="checkbox" id="o_ts_ligature_ff" class="option" data-default="true" /><label for="o_ts_ligature_ff" class="opt_lbl">ff</label></div>
+                  &nbsp; <div class="inlineblock"><input type="checkbox" id="o_ts_ligature_fi" class="option" data-default="true" /><label for="o_ts_ligature_fi" class="opt_lbl">fi</label></div>
+                  &nbsp; <div class="inlineblock"><input type="checkbox" id="o_ts_ligature_ffi" class="option" data-default="true" /><label for="o_ts_ligature_ffi" class="opt_lbl">ffi</label></div>
+                  &nbsp; <div class="inlineblock"><input type="checkbox" id="o_ts_ligature_fl" class="option" data-default="true" /><label for="o_ts_ligature_fl" class="opt_lbl">fl</label></div>
+                  &nbsp; <div class="inlineblock"><input type="checkbox" id="o_ts_ligature_ffl" class="option" data-default="true" /><label for="o_ts_ligature_ffl" class="opt_lbl">ffl</label></div>
+                  &nbsp; <div class="inlineblock"><input type="checkbox" id="o_ts_ligature_ft" class="option" data-default="true" /><label for="o_ts_ligature_ft" class="opt_lbl">ft</label></div>
+                  &nbsp; <div class="inlineblock"><input type="checkbox" id="o_ts_ligature_st" class="option" data-default="false" /><label for="o_ts_ligature_st" class="opt_lbl">st</label></div>
+                </div>
+              </div>
+            </fieldset>
+
+            <!-- Misc -->
+            <fieldset>
+              <legend><input type="checkbox" id="o_group_misc" class="option" data-default="true" /><label for="o_group_misc" data-l10n-en="tf_misc">${tf_misc}</label></legend>
+              <div id="group_misc" class="groupblock">
+                <div class="blockopt underline">
+                  <div id="res_o_ordinals_no_exponant" class="result fright"></div>
+                  <input type="checkbox" id="o_ordinals_no_exponant" class="option" data-default="true" />
+                  <label for="o_ordinals_no_exponant" class="opt_lbl reducedw" data-l10n-en="tf_ordinals_no_exponant">${tf_ordinals_no_exponant}</label>
+                  <div class="secondoption">
+                    <input type="checkbox" id="o_ordinals_exponant" class="option" data-default="true" />
+                    <label for="o_ordinals_exponant" class="opt_lbl smallw" data-l10n-en="tf_ordinals_exponant">${tf_ordinals_exponant}</label>
+                  </div>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_etc" class="result fright"></div>
+                  <input type="checkbox" id="o_etc" class="option" data-default="true" />
+                  <label for="o_etc" class="opt_lbl largew" data-l10n-en="tf_etc">${tf_etc}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_missing_hyphens" class="result fright"></div>
+                  <input type="checkbox" id="o_missing_hyphens" class="option" data-default="true" />
+                  <label for="o_missing_hyphens" class="opt_lbl largew" data-l10n-en="tf_missing_hyphens">${tf_missing_hyphens}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_ma_word" class="result fright"></div>
+                  <input type="checkbox" id="o_ma_word" class="option" data-default="true" />
+                  <label for="o_ma_word" class="opt_lbl largew" data-l10n-en="tf_ma_word">${tf_ma_word}</label>
+                </div>
+                <div class="blockopt">
+                  <div class="inlineblock indent">
+                    <input type="checkbox" id="o_ma_1letter_lowercase" class="option" />
+                    <label for="o_ma_1letter_lowercase" class="opt_lbl" data-l10n-en="tf_ma_1letter_lowercase">${tf_ma_1letter_lowercase}</label>
+                  </div>
+                  <div class="inlineblock indent">
+                    <input type="checkbox" id="o_ma_1letter_uppercase" class="option" />
+                    <label for="o_ma_1letter_uppercase" class="opt_lbl" data-l10n-en="tf_ma_1letter_uppercase">${tf_ma_1letter_uppercase}</label>
+                  </div>
+                </div>
+              </div>
+            </fieldset>
+
+            <!-- Restructuration -->
+            <fieldset>
+              <legend><input type="checkbox" id="o_group_struct" class="option" data-default="false" /><label for="o_group_struct" data-l10n-en="tf_struct">${tf_struct}</label></legend>
+              <div id="group_struct" class="groupblock">
+                <div class="blockopt underline">
+                  <div id="res_o_remove_hyphens_at_end_of_paragraphs" class="result fright"></div>
+                  <input type="checkbox" id="o_remove_hyphens_at_end_of_paragraphs" class="option" data-default="false" />
+                  <label for="o_remove_hyphens_at_end_of_paragraphs" class="opt_lbl largew"  data-l10n-en="tf_remove_hyphens_at_end_of_paragraphs">${tf_remove_hyphens_at_end_of_paragraphs}</label>
+                </div>
+                <div class="blockopt underline">
+                  <div id="res_o_merge_contiguous_paragraphs" class="result fright"></div>
+                  <input type="checkbox" id="o_merge_contiguous_paragraphs" class="option" data-default="false" />
+                  <label for="o_merge_contiguous_paragraphs" class="opt_lbl largew" data-l10n-en="tf_merge_contiguous_paragraphs">${tf_merge_contiguous_paragraphs}</label>
+                </div>
+              </div>
+            </fieldset>
+          </div>
+
+          <div id="tf_actions">
+              <div id="tf_reset" class="button blue" data-l10n-en="Default">Par défaut</div>
+              <div id="tf_apply" class="button green fright" data-l10n-en="Apply">Appliquer</div>
+              <div id="tf_progressbarbox"><progress id="progressbar" style="width: 400px;"></progress> <span id="time_res"></span></div>
+              <!--<div class="clearer"></div>
+              <div id="infomsg" data-l10n-id="tf_infomsg"></div>-->
+          </div>
+        </section> <!-- text formatter -->
+
+      </div> <!-- #page -->
+
+    </div> <!-- #main -->
+
+    <script src="main.js"></script>
+  </body>
+
+</html>

ADDED   gc_lang/fr/webext/panel/main.js
Index: gc_lang/fr/webext/panel/main.js
==================================================================
--- /dev/null
+++ gc_lang/fr/webext/panel/main.js
@@ -0,0 +1,70 @@
+
+function showError (e) {
+  console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message);
+}
+
+function beastNameToURL(beastName) {
+  switch (beastName) {
+    case "Frog":
+      return browser.extension.getURL("beasts/frog.jpg");
+    case "Snake":
+      return browser.extension.getURL("beasts/snake.jpg");
+    case "Turtle":
+      return browser.extension.getURL("beasts/turtle.jpg");
+  }
+}
+
+window.addEventListener(
+  "click",
+  function (xEvent) {
+    let xElem = xEvent.target;
+    if (xElem.id) {
+      if (xElem.id) {
+
+      }
+    } else if (xElem.className === "select") {
+      showPage(xElem.dataset.page);
+    } else if (xElem.tagName === "A") {
+      openURL(xElem.getAttribute("href"));
+    }
+  },
+  false
+);
+
+function showPage (sPageName) {
+  try {
+    // hide them all
+    for (let xNodePage of document.getElementsByClassName("page")) {
+      xNodePage.style.display = "None";
+    }
+    // show the one
+    document.getElementById(sPageName).style.display = "block";
+    sendMessage("Mon message");
+    // specific modifications
+    if (sPageName === "conj_page") {
+      document.body.style.width = "600px";
+      document.documentElement.style.width = "600px";
+      document.getElementById("movewindow").style.display = "none";
+    } else {
+      document.body.style.width = "530px";
+      document.documentElement.style.width = "530px";
+      document.getElementById("movewindow").style.display = "block";
+    }
+  }
+  catch (e) {
+    showError(e);
+  }
+}
+
+function handleResponse(message) {
+  console.log(`[Panel] received: ${message.response}`);
+}
+
+function handleError(error) {
+  console.log(`[Panel] Error: ${error}`);
+}
+
+function sendMessage (sMessage) {
+  let sending = browser.runtime.sendMessage({content: sMessage});
+  sending.then(handleResponse, handleError);  
+}

Index: gc_lang/fr/xpi/data/conj_panel.js
==================================================================
--- gc_lang/fr/xpi/data/conj_panel.js
+++ gc_lang/fr/xpi/data/conj_panel.js
@@ -69,11 +69,11 @@
             if (sVerb.endsWith("?")) {
                 document.getElementById('oint').checked = true;
                 sVerb = sVerb.slice(0,-1).trim();
             }
 
-            if (!isVerb(sVerb)) {
+            if (!conj.isVerb(sVerb)) {
                 document.getElementById('verb').style = "color: #BB4411;";
             } else {
                 self.port.emit("show");
                 document.getElementById('verb_title').textContent = sVerb;
                 document.getElementById('verb').style = "color: #999999;";

Index: gc_lang/fr/xpi/data/gc_panel.html
==================================================================
--- gc_lang/fr/xpi/data/gc_panel.html
+++ gc_lang/fr/xpi/data/gc_panel.html
@@ -2,19 +2,10 @@
 <html>
     <head>
         <link rel="stylesheet" type="text/css" href="gc_panel.css" />
         <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
     </head>
-    <script type="text/javascript">
-        // for some reason, onclick onmouseover onmouseout can’t launch function in attached script, so here it is.
-        function showData (sIdErr) {
-            document.getElementById("data"+sIdErr).hidden = false;
-        }
-        function hideData (sIdErr) {
-            document.getElementById("data"+sIdErr).hidden = true;
-        }
-    </script>
     <body class="panel">
         <div style="position: fixed; width: 100%">
             <div id="rightcorner">
                 <a class="rightcornerbutton red" id="close" href="#" title="Fermer"><b>×</b></a>
                 <a class="rightcornerbutton cyan" id="expand_reduce" href="#" title="Réduire/Agrandir"><b>−</b></a>

ADDED   gc_lang/fr/xpi/data/img/Algoo_logo.png
Index: gc_lang/fr/xpi/data/img/Algoo_logo.png
==================================================================
--- /dev/null
+++ gc_lang/fr/xpi/data/img/Algoo_logo.png
cannot compute difference between binary files

Index: gc_lang/fr/xpi/package.json
==================================================================
--- gc_lang/fr/xpi/package.json
+++ gc_lang/fr/xpi/package.json
@@ -1,10 +1,10 @@
 {
   "name": "grammalecte-fr",
   "title": "Grammalecte [fr]",
   "id": "French-GC@grammalecte.net",
-  "version": "0.5.17.2",
+  "version": "0.6.0",
   "description": "Correcteur grammatical pour le français",
   "homepage": "http://www.dicollecte.org/grammalecte",
   "main": "ui.js",
   "icon": "data/img/icon-48.png",
   "scripts": {

Index: make.py
==================================================================
--- make.py
+++ make.py
@@ -286,10 +286,11 @@
     xParser.add_argument("-t", "--tests", help="run unit tests", action="store_true")
     xParser.add_argument("-p", "--perf", help="run performance tests", action="store_true")
     xParser.add_argument("-pm", "--perf_memo", help="run performance tests and store results in perf_memo.txt", action="store_true")
     xParser.add_argument("-js", "--javascript", help="JavaScript build for Firefox", action="store_true")
     xParser.add_argument("-fx", "--firefox", help="Launch Firefox Nightly for XPI testing", action="store_true")
+    xParser.add_argument("-we", "--web_ext", help="Launch Firefox Nightly for WebExtension testing", action="store_true")
     xParser.add_argument("-tb", "--thunderbird", help="Launch Thunderbird", action="store_true")
     xParser.add_argument("-i", "--install", help="install the extension in Writer (path of unopkg must be set in config.ini)", action="store_true")
     xArgs = xParser.parse_args()
 
     if xArgs.build_data:
@@ -349,14 +350,18 @@
             # Firefox
             if xArgs.firefox:
                 with helpers.cd("_build/xpi/"+sLang):
                     os.system("jpm run -b nightly")
 
+            if xArgs.web_ext:
+                with helpers.cd("_build/webext/"+sLang):
+                    os.system(r'web-ext run --firefox="' + dVars['fx_beta_path'] + '" --browser-console')            
+
             # Thunderbird
             if xArgs.thunderbird:
                 os.system("thunderbird -jsconsole -P debug")
         else:
             print("Folder not found: gc_lang/"+sLang)
 
 
 if __name__ == '__main__':
     main()