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 @@ -42,10 +42,15 @@ let _dOptionsColors = null; let _oSpellChecker = null; let _oTokenizer = null; let _aIgnoredRules = new Set(); + +function echo (x) { + console.log(x); + return true; +} var gc_engine = { //// Informations Index: gc_lang/fr/config.ini ================================================================== --- gc_lang/fr/config.ini +++ gc_lang/fr/config.ini @@ -15,26 +15,30 @@ logo = logo.png # main dictionary lexicon_src = lexicons/French.lex dic_filenames = fr-allvars,fr-classic,fr-reform -dic_name = Français,Français (Classique/Moderne),Français (Réforme 1990) +dic_name = fr-allvars,fr-classic,fr-reform +dic_description = Français (Toutes variantes),Français (Classique),Français (Réforme 1990) dic_filter = ,[*CMPX]$,[*RPX]$ dic_default_filename_py = fr-allvars dic_default_filename_js = fr-allvars # extended dictionary lexicon_extended_src = lexicons/French.extended.lex dic_extended_filename = fr.extended -dic_extended_name = Français - dictionnaire étendu +dic_extended_name = fr.extended +dic_extended_description = Français - dictionnaire étendu # community dictionary lexicon_community_src = lexicons/French.community.lex dic_community_filename = fr.community -dic_community_name = Français - dictionnaire communautaire +dic_community_name = fr.community +dic_community_description = Français - dictionnaire communautaire # personal dictionary lexicon_personal_src = lexicons/French.personal.lex dic_personal_filename = fr.personal -dic_personal_name = Français - dictionnaire personnel +dic_personal_name = fr.personal +dic_personal_description = Français - dictionnaire personnel # Finite state automaton compression: 1, 2 (experimental) or 3 (experimental) fsa_method = 1 # stemming method: S for suffixes only, A for prefixes and suffixes stemming_method = S Index: gc_lang/fr/dictionnaire/genfrdic.py ================================================================== --- gc_lang/fr/dictionnaire/genfrdic.py +++ gc_lang/fr/dictionnaire/genfrdic.py @@ -524,15 +524,10 @@ def writeGrammarCheckerLexicon (self, spfDst, version): echo(' * Lexique simplifié >> [ {} ] '.format(spfDst)) with open(spfDst[:-4]+".lex", 'w', encoding='utf-8', newline="\n") as hDst: hDst.write(MPLHEADER) hDst.write("# Lexique simplifié pour Grammalecte v{}\n# Licence : MPL v2.0\n\n".format(version)) - hDst.write("## LangCode: fr\n") - hDst.write("## LangName: Français\n") - hDst.write("## DicName: fr.commun\n") - hDst.write("## Description: Français commun (toutes variantes)\n") - hDst.write("## Author: Olivier R.\n\n") hDst.write(Flexion.simpleHeader()) for oFlex in self.lFlexions: hDst.write(oFlex.getGrammarCheckerRepr()) def createFiles (self, spDst, lDictVars, nMode, bSimplified): Index: gc_lang/fr/modules-js/lexicographe.js ================================================================== --- gc_lang/fr/modules-js/lexicographe.js +++ gc_lang/fr/modules-js/lexicographe.js @@ -459,36 +459,40 @@ } } } getListOfTokensReduc (sText, bInfo=true) { - let aTokenList = this.getListOfTokens(sText.replace("'", "’").trim(), false); - let iKey = 0; + let lToken = this.getListOfTokens(sText.replace("'", "’").trim(), false); + let iToken = 0; let aElem = []; + if (lToken.length == 0) { + return aElem; + } do { - let oToken = aTokenList[iKey]; + let oToken = lToken[iToken]; let sMorphLoc = ''; let aTokenTempList = [oToken]; if (oToken.sType == "WORD" || oToken.sType == "WORD_ELIDED"){ - let iKeyTree = iKey + 1; + let iLocEnd = iToken + 1; let oLocNode = this.oLocGraph[oToken.sValue.toLowerCase()]; while (oLocNode) { - let oTokenNext = aTokenList[iKeyTree]; - iKeyTree++; + let oTokenNext = lToken[iLocEnd]; + iLocEnd++; if (oTokenNext) { oLocNode = oLocNode[oTokenNext.sValue.toLowerCase()]; } - if (oLocNode && iKeyTree <= aTokenList.length) { + if (oLocNode && iLocEnd <= lToken.length) { sMorphLoc = oLocNode["_:_"]; aTokenTempList.push(oTokenNext); } else { break; } } } if (sMorphLoc) { + // we have a locution let sValue = ''; for (let oTokenWord of aTokenTempList) { sValue += oTokenWord.sValue+' '; } let oTokenLocution = { @@ -522,26 +526,27 @@ aSubElem: aSubElem }); } else { aElem.push(oTokenLocution); } - iKey = iKey + aTokenTempList.length; + iToken = iToken + aTokenTempList.length; } else { + // No locution, we just add information if (bInfo) { let aRes = this.getInfoForToken(oToken); if (aRes) { aElem.push(aRes); } } else { aElem.push(oToken); } - iKey++; + iToken++; } - } while (iKey < aTokenList.length); + } while (iToken < lToken.length); return aElem; } } if (typeof(exports) !== 'undefined') { exports.Lexicographe = Lexicographe; } Index: gc_lang/fr/oxt/DictOptions/DictOptions.py ================================================================== --- gc_lang/fr/oxt/DictOptions/DictOptions.py +++ gc_lang/fr/oxt/DictOptions/DictOptions.py @@ -20,11 +20,11 @@ from com.sun.star.awt.MessageBoxType import INFOBOX, ERRORBOX # MESSAGEBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX def MessageBox (xDocument, sMsg, sTitle, nBoxType=INFOBOX, nBoxButtons=BUTTONS_OK): xParentWin = xDocument.CurrentController.Frame.ContainerWindow ctx = uno.getComponentContext() - xToolkit = ctx.ServiceManager.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx) + xToolkit = ctx.ServiceManager.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx) xMsgBox = xToolkit.createMessageBox(xParentWin, nBoxType, nBoxButtons, sTitle, sMsg) return xMsgBox.execute() class DictOptions (unohelper.Base, XActionListener, XJobExecutor): @@ -32,11 +32,11 @@ def __init__ (self, ctx): self.ctx = ctx self.xSvMgr = self.ctx.ServiceManager self.xContainer = None self.xDialog = None - + def _addWidget (self, name, wtype, x, y, w, h, **kwargs): xWidget = self.xDialog.createInstance('com.sun.star.awt.UnoControl%sModel' % wtype) xWidget.Name = name xWidget.PositionX = x xWidget.PositionY = y @@ -55,11 +55,11 @@ self.xOptionNode = helpers.getConfigSetting("/org.openoffice.Lightproof_grammalecte/Other/", True) # dialog self.xDialog = self.xSvMgr.createInstanceWithContext('com.sun.star.awt.UnoControlDialogModel', self.ctx) self.xDialog.Width = 200 - self.xDialog.Height = 310 + self.xDialog.Height = 285 self.xDialog.Title = self.dUI.get('title', "#title#") xWindowSize = helpers.getWindowSize() self.xDialog.PositionX = int((xWindowSize.Width / 2) - (self.xDialog.Width / 2)) self.xDialog.PositionY = int((xWindowSize.Height / 2) - (self.xDialog.Height / 2)) @@ -66,11 +66,11 @@ # fonts xFDTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor") xFDTitle.Height = 9 xFDTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD") xFDTitle.Name = "Verdana" - + xFDSubTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor") xFDSubTitle.Height = 10 xFDSubTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD") xFDSubTitle.Name = "Verdana" @@ -77,13 +77,12 @@ # widget nX = 10 nY1 = 10 nY2 = nY1 + 60 nY3 = nY2 + 25 - nY4 = nY3 + 25 - nY5 = nY4 + 45 - nY6 = nY5 + 95 + nY4 = nY3 + 45 + nY5 = nY4 + 95 nWidth = self.xDialog.Width - 20 nHeight = 10 # Graphspell dictionary section @@ -93,24 +92,22 @@ self._addWidget('spelling', 'FixedText', nX+10, nY1+45, nWidth-80, nHeight, Label = self.dUI.get('spelling', "#err"), FontDescriptor = xFDSubTitle) self.xInfoDicButton = self._addWidget('info_dic_button', 'Button', nX+160, nY1+45, 12, 9, Label = "‹i›") self.xSelClassic = self._addWidget('classic', 'RadioButton', nX+10, nY1+55, 50, nHeight, Label = self.dUI.get('classic', "#err")) self.xSelReform = self._addWidget('reform', 'RadioButton', nX+65, nY1+55, 55, nHeight, Label = self.dUI.get('reform', "#err")) self.xSelAllvars = self._addWidget('allvars', 'RadioButton', nX+120, nY1+55, 60, nHeight, Label = self.dUI.get('allvars', "#err")) - self.xExtendedDic = self._addWidget('activate_extended', 'CheckBox', nX, nY2+15, nWidth, nHeight, Label = self.dUI.get('activate_extended', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, Enabled = False) - self._addWidget('activate_extended_descr', 'FixedText', nX+10, nY2+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_extended_descr', "#err"), MultiLine = True) - self.xCommunityDic = self._addWidget('activate_community', 'CheckBox', nX, nY3+15, nWidth, nHeight, Label = self.dUI.get('activate_community', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, Enabled = False) - self._addWidget('activate_community_descr', 'FixedText', nX+10, nY3+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_community_descr', "#err"), MultiLine = True) - self.xPersonalDic = self._addWidget('activate_personal', 'CheckBox', nX, nY4+15, nWidth, nHeight, Label = self.dUI.get('activate_personal', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088) - self._addWidget('activate_personal_descr', 'FixedText', nX+10, nY4+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_personal_descr', "#err"), MultiLine = True) - + self.xCommunityDic = self._addWidget('activate_community', 'CheckBox', nX, nY2+15, nWidth, nHeight, Label = self.dUI.get('activate_community', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, Enabled = False) + self._addWidget('activate_community_descr', 'FixedText', nX+10, nY2+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_community_descr', "#err"), MultiLine = True) + self.xPersonalDic = self._addWidget('activate_personal', 'CheckBox', nX, nY3+15, nWidth, nHeight, Label = self.dUI.get('activate_personal', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088) + self._addWidget('activate_personal_descr', 'FixedText', nX+10, nY3+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_personal_descr', "#err"), MultiLine = True) + # Spell suggestion engine section - self._addWidget("suggestion_section", 'FixedLine', nX, nY5, nWidth, nHeight, Label = self.dUI.get("suggestion_section", "#err"), FontDescriptor = xFDTitle) - self.xGraphspellSugg = self._addWidget('activate_spell_sugg', 'CheckBox', nX, nY5+15, nWidth, nHeight, Label = self.dUI.get('activate_spell_sugg', "#err")) - self._addWidget('activate_spell_sugg_descr', 'FixedText', nX, nY5+25, nWidth, nHeight*6, Label = self.dUI.get('activate_spell_sugg_descr', "#err"), MultiLine = True) + self._addWidget("suggestion_section", 'FixedLine', nX, nY4, nWidth, nHeight, Label = self.dUI.get("suggestion_section", "#err"), FontDescriptor = xFDTitle) + self.xGraphspellSugg = self._addWidget('activate_spell_sugg', 'CheckBox', nX, nY4+15, nWidth, nHeight, Label = self.dUI.get('activate_spell_sugg', "#err")) + self._addWidget('activate_spell_sugg_descr', 'FixedText', nX, nY4+25, nWidth, nHeight*6, Label = self.dUI.get('activate_spell_sugg_descr', "#err"), MultiLine = True) # Restart message - self._addWidget('restart', 'FixedText', nX, nY6, nWidth, nHeight*2, Label = self.dUI.get('restart', "#err"), FontDescriptor = xFDTitle, MultiLine = True, TextColor = 0x880000) + self._addWidget('restart', 'FixedText', nX, nY5, nWidth, nHeight*2, Label = self.dUI.get('restart', "#err"), FontDescriptor = xFDTitle, MultiLine = True, TextColor = 0x880000) # Button self._addWidget('apply_button', 'Button', self.xDialog.Width-115, self.xDialog.Height-20, 50, 14, Label = self.dUI.get('apply_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x005500) self._addWidget('cancel_button', 'Button', self.xDialog.Width-60, self.xDialog.Height-20, 50, 14, Label = self.dUI.get('cancel_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x550000) @@ -133,11 +130,10 @@ # XActionListener def actionPerformed (self, xActionEvent): try: if xActionEvent.ActionCommand == 'Apply': xChild = self.xOptionNode.getByName("o_fr") - #xChild.setPropertyValue("use_extended_dic", self.xExtendedDic.State) #xChild.setPropertyValue("use_community_dic", self.xCommunityDic.State) xChild.setPropertyValue("use_personal_dic", self.xPersonalDic.State) xChild.setPropertyValue("use_graphspell_sugg", self.xGraphspellSugg.State) sMainDicName = "classic" if self.xSelClassic.State: @@ -153,11 +149,11 @@ MessageBox(self.xDocument, self.dUI.get('spelling_descr', "#err"), "Orthographe du français", nBoxType=INFOBOX, nBoxButtons=BUTTONS_OK) else: self.xContainer.endExecute() except: traceback.print_exc() - + # XJobExecutor def trigger (self, args): try: dialog = DictOptions(self.ctx) dialog.run() @@ -167,11 +163,10 @@ def _loadOptions (self): try: xChild = self.xOptionNode.getByName("o_fr") #self.xGraphspell.State = xChild.getPropertyValue("use_graphspell") self.xGraphspellSugg.State = xChild.getPropertyValue("use_graphspell_sugg") - #self.xExtendedDic.State = xChild.getPropertyValue("use_extended_dic") #self.xCommunityDic.State = xChild.getPropertyValue("use_community_dic") self.xPersonalDic.State = xChild.getPropertyValue("use_personal_dic") sMainDicName = xChild.getPropertyValue("main_dic_name") if sMainDicName == "classic": self.xSelClassic.State = 1 Index: gc_lang/fr/oxt/DictOptions/LexiconEditor.py ================================================================== --- gc_lang/fr/oxt/DictOptions/LexiconEditor.py +++ gc_lang/fr/oxt/DictOptions/LexiconEditor.py @@ -409,11 +409,11 @@ xGridDataModel = self.xGridModelLex.GridDataModel lEntry = [] for i in range(xGridDataModel.RowCount): lEntry.append(xGridDataModel.getRowData(i)) if lEntry: - oDAWG = dawg.DAWG(lEntry, "S", "fr", "Français", "fr.personal") + oDAWG = dawg.DAWG(lEntry, "S", "fr", "Français", "fr.personal", "Dictionnaire personnel") self.oPersonalDicJSON = oDAWG.getBinaryAsJSON() self.xOptionNode.setPropertyValue("personal_dic", json.dumps(self.oPersonalDicJSON, ensure_ascii=False)) self.xSettingNode.commitChanges() self.xNumDic.Label = str(self.oPersonalDicJSON["nEntry"]) self.xDateDic.Label = self.oPersonalDicJSON["sDate"] Index: gc_lang/fr/oxt/DictOptions/do_strings.py ================================================================== --- gc_lang/fr/oxt/DictOptions/do_strings.py +++ gc_lang/fr/oxt/DictOptions/do_strings.py @@ -4,11 +4,11 @@ return dStrings["fr"] dStrings = { "fr": { "title": "Grammalecte · Options orthographiques", - + "spelling_section": "Correcteur orthographique", "activate_main": "Activer le correcteur orthographique de Grammalecte", "activate_main_descr": "Supplante le correcteur orthographique inclus dans LibreOffice (Hunspell).", "suggestion_section": "Moteur de suggestion orthographique", @@ -21,12 +21,10 @@ "spelling": "Orthographe", "spelling_descr": "Le dictionnaire “Classique” propose l’orthographe telle qu’elle est écrite aujourd’hui le plus couramment. C’est le dictionnaire recommandé. Il contient les graphies usuelles et classiques, certaines encore communément utilisées, d’autres désuètes.\n\nAvec le dictionnaire “Réforme 1990”, seule l’orthographe réformée est reconnue. Attendu que bon nombre de graphies réformées sont considérées comme erronées par beaucoup, ce dictionnaire est déconseillé. Les graphies passées dans l’usage sont déjà incluses dans le dictionnaire “Classique”.\n\nLe dictionnaire “Toutes variantes” contient toutes les graphies, classiques ou réformées, ainsi que d’autres plus rares encore. Ce dictionnaire est déconseillé à ceux qui ne connaissent pas très bien la langue française.", "allvars": "Toutes variantes", "classic": "Classique", "reform": "Réforme 1990", - "activate_extended": "Dictionnaire étendu", - "activate_extended_descr": "Fonctionnalité à venir", "activate_community": "Dictionnaire communautaire", "activate_community_descr": "Fonctionnalité à venir", "activate_personal": "Dictionnaire personnel", "activate_personal_descr": "Créable et éditable via l’éditeur lexical.", @@ -35,11 +33,11 @@ "apply_button": "Appliquer", "cancel_button": "Annuler", }, "en": { "title": "Grammalecte · Spelling options", - + "spelling_section": "Spell checker", "activate_main": "Activate the spell checker from Grammalecte", "activate_main_descr": "Overrides the spell checker included in LibreOffice (Hunspell)", "suggestion_section": "Spell suggestion engine", @@ -52,12 +50,10 @@ "spelling": "Spelling", "spelling_descr": "The dictionary “Classic” offers the French spelling as it is written nowadays most often. This is the recommended dictionary. It contains usual and classical spellings, some of them still widely used, others obsolete.\n\nWith the dictionary “Reform 1990”, only the reformed spelling is recognized. As many of reformed spellings are considered erroneous by many people, this dictionary is unadvised. Reformed spellings commonly used are already included in the “Classic” dictionary.\n\nThe dictionary “All variants” contains all spelling variants, classical and reformed, and some others even rarer. This dictionary is unadvised for those who don’t know very well the French language.", "allvars": "All variants", "classic": "Classic", "reform": "Reform 1990", - "activate_extended": "Extended dictionary", - "activate_extended_descr": "Feature to come.", "activate_community": "Community dictionary", "activate_community_descr": "Feature to come.", "activate_personal": "Personal dictionary", "activate_personal_descr": "Creatible and editable via the lexicon editor.", Index: gc_lang/fr/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -505,11 +505,11 @@ __[s>(p_Mr_Mgr_Mme_point)__ M(?:r|gr|me) [A-ZÉ]([.])(?=\W+[a-zéèêâîïû]) @@$ <<- ~1>> * # Patronyme réduit à une seule lettre __[s](p_prénom_lettre_point_patronyme)__ - ([A-ZÉÈÂÎ][\w-]+)[ ][A-ZÉÈÂ]([.])[ ]([A-ZÉÈÂ][\w-]+) @@0,$,$ + ([A-ZÉÈÂÎ][\w-]+)[ ][A-ZÉÈÂ]([.])[ ]([A-ZÉÈÂ][\w-]+) @@0,*,$ <<- morph(\1, ":M[12]") and (morph(\3, ":(?:M[12]|V)") or not spell(\3)) ~2>> * __[s>(p_prénom_lettre_point)__ ([A-ZÉÈÂÎ][\w-]+)[ ][A-ZÉÈÂ]([.]) @@0,$ <<- morph(\1, ":M[12]") and after("^\\W+[a-zéèêîïâ]") ~2>> _ @@ -741,11 +741,11 @@ !! !! __[i>/poncfin(poncfin_règle1)__ ({w_1}) *$ @@0 - <<- before("\\w+(?:[.]|[ ][!?]) +(?:[A-ZÉÈÎ]\\w+|[ÀÔ])") -1>> \1.|\1 !|\1 ? # Il semble manquer une ponctuation finale (s’il s’agit d’un titre, le point final n’est pas requis). + <<- before("\\w+(?:\\.|[ ][!?]) +(?:[A-ZÉÈÎ]\\w+|[ÀÔ])") -1>> \1.|\1 !|\1 ? # Il semble manquer une ponctuation finale (s’il s’agit d’un titre, le point final n’est pas requis). TEST: __poncfin__ Vraiment. Quel {{ennui}} TEST: Internet : le nouvel eldorado TEST: OMC-FMI : Les nouveaux maîtres du monde Index: gc_lang/fr/tb/content/lex_editor.js ================================================================== --- gc_lang/fr/tb/content/lex_editor.js +++ gc_lang/fr/tb/content/lex_editor.js @@ -476,11 +476,11 @@ build: function () { let xProgressNode = document.getElementById("wait_progress"); let lEntry = oLexiconTable.getEntries(); if (lEntry.length > 0) { - let oDAWG = new DAWG(lEntry, "S", "fr", "Français", "fr.personal", xProgressNode); + let oDAWG = new DAWG(lEntry, "S", "fr", "Français", "fr.personal", "Dictionnaire personnel", xProgressNode); let oJSON = oDAWG.createBinaryJSON(1); oFileHandler.saveFile("fr.personal.json", JSON.stringify(oJSON)); this.oIBDAWG = new IBDAWG(oJSON); this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate); //browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sType: "personal", oDict: oJSON}, dInfo: {} }); Index: gc_lang/fr/webext/background.js ================================================================== --- gc_lang/fr/webext/background.js +++ gc_lang/fr/webext/background.js @@ -117,25 +117,22 @@ dParam: { sDictionary: sDictionary, bActivate: bActivate }, dInfo: {} }); } -function initSCOptions (dSavedOptions) { - if (!dSavedOptions.hasOwnProperty("sc_options")) { +function initSCOptions (oData) { + if (!oData.hasOwnProperty("sc_options")) { browser.storage.local.set({"sc_options": { extended: true, community: true, personal: true }}); - setDictionaryOnOff("extended", true); setDictionaryOnOff("community", true); setDictionaryOnOff("personal", true); } else { - let dOptions = dSavedOptions.sc_options; - setDictionaryOnOff("extended", dOptions["extended"]); - setDictionaryOnOff("community", dOptions["community"]); - setDictionaryOnOff("personal", dOptions["personal"]); + setDictionaryOnOff("community", oData.sc_options["community"]); + setDictionaryOnOff("personal", oData.sc_options["personal"]); } } function setDictionary (sDictionary, oDictionary) { xGCEWorker.postMessage({ @@ -143,37 +140,41 @@ dParam: { sDictionary: sDictionary, oDict: oDictionary }, dInfo: {} }); } -function setSpellingDictionary (dSavedDictionary) { - if (dSavedDictionary.hasOwnProperty("oExtendedDictionary")) { - setDictionary("extended", dSavedDictionary["oExtendedDictionary"]); - } - if (dSavedDictionary.hasOwnProperty("oCommunityDictionary")) { - setDictionary("community", dSavedDictionary["oCommunityDictionary"]); - } - if (dSavedDictionary.hasOwnProperty("oPersonalDictionary")) { - setDictionary("personal", dSavedDictionary["oPersonalDictionary"]); +function setSpellingDictionaries (oData) { + if (oData.hasOwnProperty("oPersonalDictionary")) { + // deprecated (to be removed in 2020) + console.log("personal dictionary migration"); + browser.storage.local.set({ "personal_dictionary": oData["oPersonalDictionary"] }); + setDictionary("personal", oData["oPersonalDictionary"]); + browser.storage.local.remove("oPersonalDictionary"); + } + if (oData.hasOwnProperty("personal_dictionary")) { + setDictionary("personal", oData["personal_dictionary"]); + } + if (oData.hasOwnProperty("community_dictionary")) { + setDictionary("community", oData["community_dictionary"]); } } function init () { if (bChrome) { browser.storage.local.get("gc_options", initGrammarChecker); browser.storage.local.get("ui_options", initUIOptions); - browser.storage.local.get("oExtendedDictionary", setSpellingDictionary); - browser.storage.local.get("oCommunityDictionary", setSpellingDictionary); - browser.storage.local.get("oPersonalDictionary", setSpellingDictionary); + browser.storage.local.get("personal_dictionary", setSpellingDictionaries); + browser.storage.local.get("community_dictionary", setSpellingDictionaries); + browser.storage.local.get("oPersonalDictionary", setSpellingDictionaries); // deprecated browser.storage.local.get("sc_options", initSCOptions); return; } browser.storage.local.get("gc_options").then(initGrammarChecker, showError); browser.storage.local.get("ui_options").then(initUIOptions, showError); - browser.storage.local.get("oExtendedDictionary").then(setSpellingDictionary, showError); - browser.storage.local.get("oCommunityDictionary").then(setSpellingDictionary, showError); - browser.storage.local.get("oPersonalDictionary").then(setSpellingDictionary, showError); + browser.storage.local.get("personal_dictionary").then(setSpellingDictionaries, showError); + browser.storage.local.get("community_dictionary").then(setSpellingDictionaries, showError); + browser.storage.local.get("oPersonalDictionary").then(setSpellingDictionaries, showError); // deprecated browser.storage.local.get("sc_options").then(initSCOptions, showError); } init(); @@ -215,10 +216,19 @@ xGCEWorker.postMessage(oRequest); break; case "openURL": browser.tabs.create({url: dParam.sURL}); break; + case "openConjugueurTab": + openConjugueurTab(); + break; + case "openLexiconEditor": + openLexiconEditor(dParam["dictionary"]); + break; + case "openDictionaries": + openDictionaries(); + break; default: console.log("[background] Unknown command: " + sCommand); console.log(oRequest); } //sendResponse({response: "response from background script"}); @@ -226,10 +236,11 @@ browser.runtime.onMessage.addListener(handleMessage); function handleConnexion (xPort) { + // Messages from tabs let iPortId = xPort.sender.tab.id; // identifier for the port: each port can be found at dConnx[iPortId] dConnx.set(iPortId, xPort); xPort.onMessage.addListener(function (oRequest) { let {sCommand, dParam, dInfo} = oRequest; switch (sCommand) { @@ -277,13 +288,15 @@ browser.contextMenus.create({ id: "separator_editable", type: "separator", contexts: ["editable"] }); // Page browser.contextMenus.create({ id: "rightClickLxgPage", title: "Lexicographe (page)", contexts: ["all"] }); // on all parts, due to unwanted selection browser.contextMenus.create({ id: "rightClickGCPage", title: "Correction grammaticale (page)", contexts: ["all"] }); browser.contextMenus.create({ id: "separator_page", type: "separator", contexts: ["all"] }); -// Conjugueur +// Tools browser.contextMenus.create({ id: "conjugueur_window", title: "Conjugueur [fenêtre]", contexts: ["all"] }); browser.contextMenus.create({ id: "conjugueur_tab", title: "Conjugueur [onglet]", contexts: ["all"] }); +browser.contextMenus.create({ id: "dictionaries", title: "Dictionnaires", contexts: ["all"] }); +browser.contextMenus.create({ id: "lexicon_editor", title: "Éditeur lexical", contexts: ["all"] }); // Rescan page browser.contextMenus.create({ id: "separator_rescan", type: "separator", contexts: ["editable"] }); browser.contextMenus.create({ id: "rescanPage", title: "Rechercher à nouveau les zones de texte", contexts: ["editable"] }); @@ -323,10 +336,16 @@ openConjugueurWindow(); break; case "conjugueur_tab": openConjugueurTab(); break; + case "lexicon_editor": + openLexiconEditor(); + break; + case "dictionaries": + openDictionaries(); + break; // rescan page case "rescanPage": let xPort = dConnx.get(xTab.id); xPort.postMessage({sActionDone: "rescanPage"}); break; @@ -341,22 +360,50 @@ /* Keyboard shortcuts */ browser.commands.onCommand.addListener(function (sCommand) { switch (sCommand) { + case "lexicographer": + sendCommandToCurrentTab("shortcutLexicographer"); + break; + case "text_formatter": + sendCommandToCurrentTab("shortcutTextFormatter"); + break; + case "grammar_checker": + sendCommandToCurrentTab("shortcutGrammarChecker"); + break; case "conjugueur_tab": openConjugueurTab(); break; case "conjugueur_window": openConjugueurWindow(); break; - case "lex_editor": - openLexEditor(); + case "lexicon_editor": + openLexiconEditor(); + break; + case "dictionaries": + openDictionaries(); break; } }); + +/* + Tabs +*/ +let nTabLexiconEditor = null; +let nTabDictionaries = null; +let nTabConjugueur = null; + +browser.tabs.onRemoved.addListener(function (nTabId, xRemoveInfo) { + switch (nTabId) { + case nTabLexiconEditor: nTabLexiconEditor = null; break; + case nTabDictionaries: nTabDictionaries = null; break; + case nTabConjugueur: nTabConjugueur = null; break; + } +}); + /* Actions */ @@ -370,34 +417,93 @@ function sendCommandToTab (sCommand, iTab) { let xTabPort = dConnx.get(iTab); xTabPort.postMessage({sActionDone: sCommand, result: null, dInfo: null, bEnd: false, bError: false}); } -function openLexEditor () { - if (bChrome) { - browser.tabs.create({ - url: browser.extension.getURL("panel/lex_editor.html") - }); - return; - } - let xLexEditor = browser.tabs.create({ - url: browser.extension.getURL("panel/lex_editor.html") - }); - xLexEditor.then(onCreated, onError); -} - -function openConjugueurTab () { - if (bChrome) { - browser.tabs.create({ - url: browser.extension.getURL("panel/conjugueur.html") - }); - return; - } - let xConjTab = browser.tabs.create({ - url: browser.extension.getURL("panel/conjugueur.html") - }); - xConjTab.then(onCreated, onError); +function sendCommandToCurrentTab (sCommand) { + console.log(sCommand); + if (bChrome) { + browser.tabs.query({ currentWindow: true, active: true }, (lTabs) => { + for (let xTab of lTabs) { + console.log(xTab); + browser.tabs.sendMessage(xTab.id, {sActionRequest: sCommand}); + } + }); + return; + } + browser.tabs.query({ currentWindow: true, active: true }).then((lTabs) => { + for (let xTab of lTabs) { + console.log(xTab); + browser.tabs.sendMessage(xTab.id, {sActionRequest: sCommand}); + } + }, onError); +} + +function openLexiconEditor (sName="__personal__") { + if (nTabLexiconEditor === null) { + if (bChrome) { + browser.tabs.create({ + url: browser.extension.getURL("panel/lex_editor.html") + }, onLexiconEditorOpened); + return; + } + let xLexEditor = browser.tabs.create({ + url: browser.extension.getURL("panel/lex_editor.html") + }); + xLexEditor.then(onLexiconEditorOpened, onError); + } + else { + browser.tabs.update(nTabLexiconEditor, {active: true}); + } +} + +function onLexiconEditorOpened (xTab) { + nTabLexiconEditor = xTab.id; +} + +function openDictionaries () { + if (nTabDictionaries === null) { + if (bChrome) { + browser.tabs.create({ + url: browser.extension.getURL("panel/dictionaries.html") + }, onDictionariesOpened); + return; + } + let xLexEditor = browser.tabs.create({ + url: browser.extension.getURL("panel/dictionaries.html") + }); + xLexEditor.then(onDictionariesOpened, onError); + } + else { + browser.tabs.update(nTabDictionaries, {active: true}); + } +} + +function onDictionariesOpened (xTab) { + nTabDictionaries = xTab.id; +} + +function openConjugueurTab () { + if (nTabDictionaries === null) { + if (bChrome) { + browser.tabs.create({ + url: browser.extension.getURL("panel/conjugueur.html") + }, onConjugueurOpened); + return; + } + let xConjTab = browser.tabs.create({ + url: browser.extension.getURL("panel/conjugueur.html") + }); + xConjTab.then(onConjugueurOpened, onError); + } + else { + browser.tabs.update(nTabConjugueur, {active: true}); + } +} + +function onConjugueurOpened (xTab) { + nTabConjugueur = xTab.id; } function openConjugueurWindow () { if (bChrome) { browser.windows.create({ @@ -412,16 +518,11 @@ url: browser.extension.getURL("panel/conjugueur.html"), type: "popup", width: 710, height: 980 }); - xConjWindow.then(onCreated, onError); } -function onCreated (xWindowInfo) { - //console.log(`Created window: ${xWindowInfo.id}`); -} - -function onError (error) { - console.log(`Error: ${error}`); +function onError (e) { + console.error(e); } Index: gc_lang/fr/webext/content_scripts/init.js ================================================================== --- gc_lang/fr/webext/content_scripts/init.js +++ gc_lang/fr/webext/content_scripts/init.js @@ -101,13 +101,11 @@ } } }, observePage: function () { - /* - When a textarea is added via jascript we add the menu :) - */ + // When a textarea is added via jascript we add the menu let that = this; this.xObserver = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { for (let i = 0; i < mutation.addedNodes.length; i++){ if (mutation.addedNodes[i].tagName == "TEXTAREA") { @@ -255,11 +253,10 @@ */ let xGrammalectePort = browser.runtime.connect({name: "content-script port"}); xGrammalectePort.onMessage.addListener(function (oMessage) { let {sActionDone, result, dInfo, bEnd, bError} = oMessage; - let sText = ""; switch (sActionDone) { case "init": oGrammalecte.sExtensionUrl = oMessage.sUrl; // Start oGrammalecte.listenRightClick(); @@ -291,63 +288,41 @@ (Context menu are initialized in background) */ // Grammar checker commands case "rightClickGCEditableNode": if (oGrammalecte.xRightClickedNode !== null) { - oGrammalecte.startGCPanel(oGrammalecte.xRightClickedNode); - sText = (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA") ? oGrammalecte.xRightClickedNode.value : oGrammalecte.xRightClickedNode.innerText; - xGrammalectePort.postMessage({ - sCommand: "parseAndSpellcheck", - dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, - dInfo: {sTextAreaId: oGrammalecte.xRightClickedNode.id} - }); + parseAndSpellcheckEditableNode(oGrammalecte.xRightClickedNode); } else { oGrammalecte.showMessage("Erreur. Le node sur lequel vous avez cliqué n’a pas pu être identifié. Sélectionnez le texte à corriger et relancez le correcteur via le menu contextuel."); } break; case "rightClickGCPage": - oGrammalecte.startGCPanel(); - xGrammalectePort.postMessage({ - sCommand: "parseAndSpellcheck", - dParam: {sText: oGrammalecte.getPageText(), sCountry: "FR", bDebug: false, bContext: false}, - dInfo: {} - }); + parseAndSpellcheckPage(); break; case "rightClickGCSelectedText": oGrammalecte.startGCPanel(); // selected text is sent to the GC worker in the background script. break; // Lexicographer commands case "rightClickLxgEditableNode": if (oGrammalecte.xRightClickedNode !== null) { - oGrammalecte.startLxgPanel(); - sText = (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA") ? oGrammalecte.xRightClickedNode.value : oGrammalecte.xRightClickedNode.innerText; - xGrammalectePort.postMessage({ - sCommand: "getListOfTokens", - dParam: {sText: sText}, - dInfo: {sTextAreaId: oGrammalecte.xRightClickedNode.id} - }); + lexicographerEditableNode(oGrammalecte.xRightClickedNode); } else { oGrammalecte.showMessage("Erreur. Le node sur lequel vous avez cliqué n’a pas pu être identifié. Sélectionnez le texte à analyser et relancez le lexicographe via le menu contextuel."); } break; case "rightClickLxgPage": - oGrammalecte.startLxgPanel(); - xGrammalectePort.postMessage({ - sCommand: "getListOfTokens", - dParam: {sText: oGrammalecte.getPageText()}, - dInfo: {} - }); + lexicographerPage(); break; case "rightClickLxgSelectedText": oGrammalecte.startLxgPanel(); // selected text is sent to the GC worker in the background script. break; // Text formatter command case "rightClickTFEditableNode": if (oGrammalecte.xRightClickedNode !== null) { - if (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA") { + if (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA" || oGrammalecte.xRightClickedNode.tagName == "INPUT") { oGrammalecte.startFTPanel(oGrammalecte.xRightClickedNode); } else { oGrammalecte.showMessage("Cette zone de texte n’est pas réellement un champ de formulaire, mais un node HTML éditable. Le formateur de texte n’est pas disponible pour ce type de champ de saisie."); } } else { @@ -356,9 +331,86 @@ break; // rescan page command case "rescanPage": oGrammalecte.rescanPage(); break; + + } +}); + + +/* + Other messages from background +*/ +browser.runtime.onMessage.addListener(function (oMessage) { + let {sActionRequest} = oMessage; + let xActiveNode = document.activeElement; + switch (sActionRequest) { + /* + Commands received from the keyboard (shortcuts) + */ + case "shortcutLexicographer": + if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) { + lexicographerEditableNode(xActiveNode); + } else { + lexicographerPage(); + } + break; + case "shortcutTextFormatter": + if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) { + oGrammalecte.startFTPanel(xActiveNode); + } + break; + case "shortcutGrammarChecker": + if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) { + parseAndSpellcheckEditableNode(xActiveNode); + } else { + parseAndSpellcheckPage(); + } + break; default: console.log("[Content script] Unknown command: " + sActionDone); } }); + + +/* + Actions +*/ + +function parseAndSpellcheckPage () { + oGrammalecte.startGCPanel(); + xGrammalectePort.postMessage({ + sCommand: "parseAndSpellcheck", + dParam: {sText: oGrammalecte.getPageText(), sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {} + }); +} + +function parseAndSpellcheckEditableNode (xNode) { + oGrammalecte.startGCPanel(xNode); + let sText = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") ? xNode.value : xNode.innerText; + xGrammalectePort.postMessage({ + sCommand: "parseAndSpellcheck", + dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {sTextAreaId: xNode.id} + }); +} + +function lexicographerPage () { + oGrammalecte.startLxgPanel(); + xGrammalectePort.postMessage({ + sCommand: "getListOfTokens", + dParam: {sText: oGrammalecte.getPageText()}, + dInfo: {} + }); +} + +function lexicographerEditableNode (xNode) { + oGrammalecte.startLxgPanel(); + let sText = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") ? xNode.value : xNode.innerText; + xGrammalectePort.postMessage({ + sCommand: "getListOfTokens", + dParam: {sText: sText}, + dInfo: {sTextAreaId: xNode.id} + }); +} Index: gc_lang/fr/webext/content_scripts/panel_gc.js ================================================================== --- gc_lang/fr/webext/content_scripts/panel_gc.js +++ gc_lang/fr/webext/content_scripts/panel_gc.js @@ -62,11 +62,11 @@ start (xNode=null) { this.oTooltip.hide(); this.clear(); if (xNode) { this.oNodeControl.setNode(xNode); - if (xNode.tagName != "TEXTAREA") { + if (!(xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT")) { this.addMessage("Note : cette zone de texte n’est pas un champ de formulaire “textarea” mais un node HTML éditable. Une telle zone de texte est susceptible de contenir des éléments non textuels qui seront effacés lors de la correction."); } } } @@ -463,11 +463,11 @@ } setNode (xNode) { this.clear(); this.xNode = xNode; - this.bTextArea = (xNode.tagName == "TEXTAREA"); + this.bTextArea = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT"); this.xNode.disabled = true; this._loadText(); } clear () { Index: gc_lang/fr/webext/gce_worker.js ================================================================== --- gc_lang/fr/webext/gce_worker.js +++ gc_lang/fr/webext/gce_worker.js @@ -318,13 +318,10 @@ //console.log("setDictionary", sDictionary); switch (sDictionary) { case "main": oSpellChecker.setMainDictionary(oDict); break; - case "extended": - oSpellChecker.setExtendedDictionary(oDict); - break; case "community": oSpellChecker.setCommunityDictionary(oDict); break; case "personal": oSpellChecker.setPersonalDictionary(oDict); @@ -340,17 +337,10 @@ postMessage(createResponse("setDictionary", "# Error. SpellChecker not loaded.", dInfo, true)); return; } //console.log("setDictionaryOnOff", sDictionary, bActivate); switch (sDictionary) { - case "extended": - if (bActivate) { - oSpellChecker.activateExtendedDictionary(); - } else { - oSpellChecker.deactivateExtendedDictionary(); - } - break; case "community": if (bActivate) { oSpellChecker.activateCommunityDictionary(); } else { oSpellChecker.deactivateCommunityDictionary(); Index: gc_lang/fr/webext/manifest.json ================================================================== --- gc_lang/fr/webext/manifest.json +++ gc_lang/fr/webext/manifest.json @@ -78,27 +78,37 @@ "run_at": "document_idle" } ], "commands": { + "lexicographer": { + "suggested_key": { "default": "Ctrl+Shift+L" }, + "description": "Ouvre le lexicographe" + }, + "text_formatter": { + "suggested_key": { "default": "Ctrl+Shift+F" }, + "description": "Ouvre le formateur de texte" + }, + "grammar_checker": { + "suggested_key": { "default": "Ctrl+Shift+V" }, + "description": "Ouvre le correcteur grammatical" + }, "conjugueur_tab": { - "suggested_key": { - "default": "Ctrl+Shift+6" - }, + "suggested_key": { "default": "Ctrl+Shift+6" }, "description": "Ouvre le conjugueur dans un onglet" }, "conjugueur_window": { - "suggested_key": { - "default": "Ctrl+Shift+7" - }, + "suggested_key": { "default": "Ctrl+Shift+7" }, "description": "Ouvre le conjugueur dans une fenêtre" }, - "lex_editor": { - "suggested_key": { - "default": "Ctrl+Shift+8" - }, + "lexicon_editor": { + "suggested_key": { "default": "Ctrl+Shift+8" }, "description": "Ouvre l’éditeur lexical" + }, + "dictionaries": { + "suggested_key": { "default": "Ctrl+Shift+9" }, + "description": "Ouvre le gestionnaire de dictionnaires communautaires" } }, "web_accessible_resources": [ "content_scripts/panel.css", @@ -116,12 +126,15 @@ "grammalecte/fr/tests_data.json", "img/logo-16.png" ], "permissions": [ + "*://localhost/*", + "*://dic.grammalecte.net/*", "activeTab", "contextMenus", + "cookies", "downloads", "storage" ], "chrome_settings_overrides": { ADDED gc_lang/fr/webext/panel/dictionaries.css Index: gc_lang/fr/webext/panel/dictionaries.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/dictionaries.css @@ -0,0 +1,193 @@ +/* + CSS Document + White + Design par Olivier R. +*/ + +* { margin: 0; padding: 0; } +img { border: none; } + + +/* 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; } + +body { + background: hsl(0, 0%, 100%) url(../img/lines.png); + font: normal 16px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; + color: #505050; +} + +.inbox { + width: 800px; + margin: 20px auto 10px auto; + padding: 10px 30px 30px 30px; + background: hsl(0, 0%, 100%); + border: 2px solid hsl(210, 0%, 90%); + border-radius: 20px; +} + + +#message_box { + display: none; + position: fixed; + top: 33%; + left: calc(50% - 325px); +} +#message { + display: inline-block; + padding: 5px 20px; + width: 600px; + background-color: hsl(0, 50%, 50%); + color: hsl(0, 50%, 98%); + border-style: solid; + border-width: 3px 0 3px 3px; + border-color: hsla(0, 50%, 40%, .5); + border-radius: 5px 0 0 5px; +} +#message_close_button { + display: inline-block; + padding: 5px 10px; + background-color: hsl(0, 50%, 40%); + color: hsl(0, 90%, 90%); + border-style: solid; + border-width: 3px 3px 3px 0; + border-color: hsla(0, 50%, 30%, .5); + border-radius: 0 5px 5px 0; + cursor: pointer; +} + + +h1 { + margin: 5px 0 5px 0; + color: hsl(210, 50%, 50%); + font: bold 24px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; +} +h2 { + margin: 10px 0 2px 0; + color: hsl(0, 50%, 50%); + font: bold 20px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; +} + + +input[type=text].large { + display: inline-block; + width: 250px; + 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%, 20%); +} + +input[type=text].medium { + display: inline-block; + width: 175px; + padding: 2px 5px; + border: 2px solid hsl(0, 0%, 80%); + border-radius: 3px; + height: 20px; + background: transparent; + font: normal 18px Tahoma, "Ubuntu Condensed"; + color: hsl(0, 0%, 20%); +} + +input[placeholder] { + color: hsl(0, 0%, 50%); +} + + +#connect_panel { + background-color: hsl(210, 50%, 90%); + border-radius: 5px; + padding: 3px 10px; +} +#submit_button { + display: inline-block; + padding: 1px 5px; + background-color: hsl(210, 50%, 30%); + color: hsl(210, 0%, 100%); + border-radius: 3px; + cursor: pointer; +} + + +.dic_button { + margin: 2px; + display: inline-block; +} +.dic_button_close { + display: inline-block; + padding: 1px 5px; + background-color: hsl(0, 50%, 50%); + color: hsl(0, 90%, 90%); + border-style: solid; + border-width: 1px 0 1px 1px; + border-color: hsl(0, 50%, 45%); + border-radius: 3px 0 0 3px; + cursor: pointer; +} +.dic_button_label { + display: inline-block; + padding: 1px 10px; + background-color: hsl(210, 50%, 94%); + border-style: solid; + border-width: 1px 1px 1px 0; + border-color: hsl(210, 50%, 70%); + border-radius: 0 3px 3px 0; +} + +.apply { + display: none; + float: right; + padding: 2px 10px; + background: hsl(120, 50%, 30%); + color: hsl(120, 50%, 96%); + cursor: pointer; + border-radius: 3px; +} + + +/* + Table +*/ +#wait_progress { + width: 100%; + height: 4px; +} + +table { + border: 1px solid hsl(210, 10%, 50%); + width: 100%; + font-size: 14px; +} +th { + padding: 5px 10px; + border-left: 1px solid hsl(210, 10%, 90%); + text-align: left; +} +td { + padding: 0 10px; + vertical-align: top; +} +.delete_entry { + cursor: pointer; + font-weight: bold; + color: hsl(0, 100%, 50%); +} +.select_entry { + cursor: pointer; + background-color: hsl(210, 50%, 30%); + color: hsl(210, 50%, 100%); + border-radius: 3px; + text-align: center; +} ADDED gc_lang/fr/webext/panel/dictionaries.html Index: gc_lang/fr/webext/panel/dictionaries.html ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/dictionaries.html @@ -0,0 +1,57 @@ + + +
+ ++ Connecté. Identifiants : , +
+[Aucun]
+ +
Nombre d’entrées : 0.
-Il est déconseillé d’utiliser la catégorie ‹Autre› pour générer autre chose que des noms, des adjectifs, des noms propres, des verbes et des adverbes. Il n’y a aucune garantie que les étiquettes pour les autres catégories, notamment les mots grammaticaux, ne changeront pas.