Le bot fonctionne de façon semi automatique sur AWB avec un module programmé en C# (tool, make module)

Ci-dessous le code du bot au 09 juin 2023 Il utilise un fichier texte local contenant une liste de prénoms, postnom, responsabilités, organisation... Le nom et le chemin de ce fichier est dans la première variable ListsFile. Le fichier peut être vide lors de la première utilisation mais il doit exister. Pour les prénoms non reconnu le bot demande à l’utilisateur confirmation, et les sauvegarde dans le fichier.

	private static string ListsFile = @"D:\Docs\Programmation\ListePrenoms.txt";
    
	public string ProcessArticle(string ArticleText, string ArticleTitle, int wikiNamespace, out string Summary, out bool Skip) {
		Summary = "";

		ArticleTextClass article = new ArticleTextClass(ArticleTitle, ArticleText, wikiNamespace, false);
		article.fix();

		string newText = article.ToString();

		if (newText != Regex.Replace(ArticleText, @"\r", "")) {
			Summary = article.getSumary();
			Skip = false; // || article.FlagSkip;
			if (article.FlagSkip)
				newText = "Article skipped\n\n" + newText;
		} else
			Skip = true;

		// Skip = article.FlagSkip;

		return newText; // + "\n<!-- \n" + article.getSumary() + "\n-->"
	}


	private static string[] wikiTag = { "categorytree", "ce", "charinsert", "chem", "gallery", "graph", "hiero", "imagemap", "indicator", "inputbox", "mapframe", "maplink", "math", "nowiki", "poem", "pre", "ref", "references", "score", "section", "source", "syntaxhighlight", "templatedata", "timeline" };
	private static string[] blockTag = { "div", "center", "pre", "p", "blockquote", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6" };
	private static string[] tableTag = { "table", "tr", "th", "td", "caption" };
	private static string[] inlineTag = { "span", "font", "small", "big", "b", "u", "i", "s", "code" };

	class WikitextClass {
		public static ArticleTextClass article;
		public static Dictionary<string, bool> listPages;
		public static bool flagAnalyseUnknownTemplates = false;

		public string name { get; set; }
		public readonly string initialString;
		public virtual string parsedString { get; set; }
		public WikitextClass parent { get; set; }
		public string strip { get; set; }
		public List<WikitextClass> subWikitextList = new List<WikitextClass>();

		public bool FlagSkip {
			get { return _flagSkip; }
			set {
				_flagSkip = value && _flagSkip;
				if (parent != null)
					parent.FlagSkip = _flagSkip;
			}
		}
		public bool StopProcessing {
			get {
				return _stopProcessing;
			}
			set {
				_stopProcessing = value;
				if (parent != null)
					parent.StopProcessing = value;
				FlagSkip = !value;
			}
		}

		private bool _flagSkip = true;
		private bool _stopProcessing = false;

		public WikitextClass() {
			name = "";
			initialString = "";
			parsedString = "";
		}
		public WikitextClass(string nname, string contenu, WikitextClass nparent) {
			name = nname;
			initialString = contenu;
			parsedString = contenu;
			parent = nparent;
		}
		public WikitextClass(WikitextClass wikiText) {
			name = wikiText.name;
			initialString = wikiText.initialString;
			parsedString = wikiText.parsedString;
			subWikitextList = wikiText.subWikitextList;
			parent = wikiText.parent;
			_flagSkip = wikiText.FlagSkip;
		}

		public bool isNumber(string s) {
			float output;
			return float.TryParse(s, out output);
		}

		// ---
		// - parse & compose
		// ---
		public string ucFirst(string str) {
			if (str != null) {
				if (str.Length > 1)
					return str.Substring(0, 1).ToUpper() + str.Substring(1);
				else
					return str.ToUpper();
			}
			return null;
		}

		protected void parse(int level) {
			if (level < 1)
				// parse les commentaires et les <nowiki>, <pre> et <ref>
				parsedString = Regex.Replace(
					parsedString, 
					"(?si)"
						+ "(<!--|<(?<name>" + string.Join("|", wikiTag) + ")( [^>]+)?(?<!/)(?<close>/)?>)"
						+ @"(?(close)|.*?((?(name)</\k<name> *>|-->)|$))",
					ReplaceNowiki);

			if (level < 2) {
				// parse les modèles
				Regex templateRegex = new Regex("(?xs)"
						+ @"{{(\s*[\S-[<>\[\]{}|]][ \S-[<>\[\]{}|]]* )"
						+ @"(?=\s*\| | \s*: | \s*\})"
						+ "( (?: (?<param>{{{) | (?<-param>}}}) | (?<acc>{{) | (?<-acc>}}) |. )*? (?(acc)(?!))(?(param)(?!)) )"
						+ "}}");
				parsedString = templateRegex.Replace(parsedString, parseTemplate); // New MatchEvaluator(AddressOf parseTemplate))
			}

			if (level < 3) {
				// parse les tableaux
				Regex tableRegex = new Regex("(?x)"
					+ @" (?<=(\n:*|^) (¤§c\d+§¤)? [ \t]* ) "
					+ @" \{\| .* "
					+ @" ( ( (?<tab>\n [ \t]* \{\| ) .* | (?<-tab>\n [ \t]* \|\} ) .* | (?>\n.*) )*? (?(tab)(?!)) ) "
					+ @" \n [ \t]* \|\} ");
				parsedString = tableRegex.Replace(parsedString, parseTable);
			}

			// parse les élément html (je ne sais pas encore comment gérer le code invalide, omniprésent)
			Regex htmlBlocTagRegex = new Regex("(?xni)"
					+ "<(?<name>" + string.Join("|", blockTag) + @"|table|(?<tr>tr)|(?<td>t[dh]))\b"
					+ "(?<value> [^<>]*> (?(?<= />) |"
					+ @"( (?<tag> <\k<name>) | (?<-tag> </\k<name>\ *> ) | [^<]* | < )*?"
					+ @"( (?(tag)(?!) | </ \k<name>\ *> ) | (?(td)(?=</?(td|th|tr|table)\b) | (?(tr)(?=<(tr|/table)\b)|$) ) | $ )"
					+ ") ) #fin de value");
			parsedString = htmlBlocTagRegex.Replace(parsedString, parseHtmlTag);

			Regex htmlInlineTagRegex = new Regex("(?xni)"
					+ "<(?<name>" + string.Join("|", inlineTag) + @")\b"
					+ "(?<value> (?>[^<>]*>) (?(?<= />) |"
						+ @"( (?<tag> <\k<name>) | (?<-tag> </\k<name>\ *> ) | (?>[^<]+) | < )*?"
						+ @"( (?(tag) $ | </ \k<name>\ *> ) | $ | (?= </?(" + string.Join("|", blockTag.Concat(tableTag).ToArray()) + @")\b ) )"
					+ ") ) #fin de value");
			parsedString = htmlInlineTagRegex.Replace(parsedString, parseHtmlTag);
		}
		protected string ReplaceNowiki(Match m) {
			if (isNumber(m.Groups[1].Value))
				return article.replacementTab[System.Convert.ToInt32(m.Groups[1].Value)];
			else
				switch (m.Groups["name"].Value) {
					case "gallery":
					case "poem":
					case "ref":
					case "references": {
							return parseHtmlTag(m);
						}

					default: {
							article.replacementTab.Add(m.Value);
							string balise = "c";
							if (m.Groups["name"].Value.Length > 0)
								balise = "w";

							return ("¤§" + balise + (article.replacementTab.Count - 1).ToString() + "§¤");
						}
				}
		}
		private string parseTemplate(Match m) {
			string TemplateName = Regex.Replace(m.Groups[1].Value, @"¤§c\d+§¤", "");
			TemplateClass templObj;
			TemplateName = (Regex.Replace(ucFirst(TemplateName), " +", " ")).Trim();

			switch (TemplateName) {
				case "Ancre":
				case "Anchor": {
						templObj = new Ancre(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Article":
				case "Cite journal":
				case "Lien news":
				case "Périodique":
				case "Cita noticia": {
						templObj = new Article(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Chapitre":
				case "Cite encyclopedia": {
						templObj = new Chapitre(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Citation":
				case "Cita":
				case "Citation2":
				case "Citation3":
				case "\"":
				case "Citation étrangère": {
						templObj = new Citation(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Citation bloc":
				case "Quote":
				case "Cquote":
				case "Bloc citation":
				case "Citation étrangère bloc":
				case "Citation bloc étrangère": {
						templObj = new CitationBloc(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Citeref": {
						templObj = new Citeref(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Date":
				case "Date-": {
						templObj = new ModeleDate(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Google Buch":
				case "Google Livres":
				case "Google books":
				case "Google Books":
				case "Google livres":
				case "Google livre": {
						templObj = new GoogleLivres(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Harvsp":
				case "Harv":
				case "Sfn":
				case "Référence Harvard sans parenthèses":
				case "Référence Harvard":
				case "Harvnb":
				case "Harvard": {
						templObj = new Harvard(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Infobox Avion militaire": {
						templObj = new InfoboxAvionMilitaire(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Infobox Élection":
				case "Infobox Élection générale":
				case "Infobox_Élection_générale": {
						templObj = new InfoboxÉlection(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Infobox Gare":
				case "Infobox gare": {
						templObj = new InfoboxGare(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Infobox Musique (œuvre)": {
						templObj = new InfoboxMusiqueOeuvre(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Lang":
				case "Langue": {
						templObj = new Langue(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Lien web":
				case "Référence web":
				case "Cite web":
				case "Cite press release":
				case "Lien Web":
				case "Lienweb":
				case "Cita web":
				case "Lien brisé": {
						templObj = new LienWeb(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Ouvrage":
				case "Cite document":
				case "Cite book": {
						templObj = new Ouvrage(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Références nombreuses":
				case "Références":
				case "Reflist":
				case "References": {
						templObj = new References(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Traduction": {
						templObj = new Traduction(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Tri":
				case "Tri1":
				case "Sort": {
						templObj = new tri(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				case "Unité":
				case "Nombre":
				case "Num":
				case "NaU":
				case "Nau":
				case "Unit": {
						templObj = new Unité(m.Groups[1].Value, m.Groups[2].Value, this);
						break;
					}

				default: {
						templObj = new TemplateClass(m.Groups[1].Value, m.Groups[2].Value, this, new string[] { "" });
						break;
					}
			}

			if (flagAnalyseUnknownTemplates || !templObj.GetType().Equals(typeof(TemplateClass)))
				templObj.analyseParameters();
			else
				templObj.parse(1);
			subWikitextList.Add(templObj);
			templObj.strip = "¤§t" + article.templateList.IndexOf(templObj).ToString() + "§¤";

			return templObj.strip;
		}
		private string parseHtmlTag(Match m) {
			HtmlTag tag = new HtmlTag(m.Groups["name"].Value, m.Value, this);
			subWikitextList.Add(tag);
			article.htmlElementList.Add(tag);
			tag.strip = "¤§h" + article.htmlElementList.IndexOf(tag).ToString() + "§¤";
			switch (m.Groups["name"].Value) {
				case "gallery":
				case "poem":
				case "ref":
				case "references": {
						tag.parse(0);
						break;
					}

				default: {
						tag.parse(5);
						break;
					}
			}
			return tag.strip;
		}
		private string parseTable(Match m) {
			TableClass tableObject = new TableClass(m.Value, this);
			subWikitextList.Add(tableObject);
			article.htmlElementList.Add(tableObject);
			tableObject.strip = "¤§a" + article.htmlElementList.IndexOf(tableObject).ToString() + "§¤";
			tableObject.parseTable();

			return tableObject.strip;
		}

		public override string ToString() {
			foreach (WikitextClass wikiText in subWikitextList)
				FlagSkip = wikiText.FlagSkip;
			return wikitextToString(parsedString);
		}
		public string wikitextToString(string textToAnalyse) {
			string newText = Regex.Replace(textToAnalyse, @"¤§[ah](\d+)§¤", composehtmlElement);
			return Regex.Replace(newText, @"¤§t(\d+)§¤", composeTemplate);
		}
		private string composeTemplate(Match m) {
			return article.templateList[System.Convert.ToInt32(m.Groups[1].Value)].ToString();
		}
		private string composehtmlElement(Match m) {
			return article.htmlElementList[System.Convert.ToInt32(m.Groups[1].Value)].ToString();
		}

		public virtual void fix() {
			for (int i = 0; i <= subWikitextList.Count - 1; i++) {
				if (StopProcessing)
					break;
				subWikitextList[i].fix();
			}
			MatchCollection anchoredLinks = Regex.Matches(parsedString, @"\[\[[^#|\]]+#[^\]]+\]\]");
			foreach (Match m in anchoredLinks) {
				string newValue = m.Value
										.Replace(".C3.A0", "à")
										.Replace(".C3.A2", "â")
										.Replace(".C3.82", "Â")
										.Replace(".C3.89", "É")
										.Replace(".C3.A9", "é")
										.Replace(".C3.A8", "è")
										.Replace(".C5.93", "œ")
										.Replace(".C2.A0", " ")
										.Replace(".C2.AB", "«")
										.Replace(".C2.BB", "»")
										.Replace(".E2.80.99", "’")
										.Replace(".27", "'")
										.Replace(".28", "(")
										.Replace(".29", ")")
										.Replace(".2C", ",")
										.Replace(".2F", "/");
				if (m.Value != newValue)
					parsedString = parsedString.Replace(m.Value, newValue);
			}

			removeLonlyHtmlCloseTag();
		}

		public void removeLonlyHtmlCloseTag() {
			parsedString = Regex.Replace(parsedString, "(?i)</(div|center|p|span|small|font|big|b|u|i|s)>", "");
		}

		public bool isExistingPage(string pageTitle) {
			if (Regex.IsMatch(pageTitle, @"^\s*$"))
				return false;
			if (!listPages.ContainsKey(pageTitle)) {
				string querry = WikiFunctions.Variables.URLLong + "api.php?action=query&format=json&titles=" + pageTitle;
				string result = new System.Net.WebClient().DownloadString(querry);
				listPages[pageTitle] = Regex.IsMatch(result, "pageid");
			}
			return listPages[pageTitle];
		}

		public bool isExistingFile(string fileTitle) {
			if (!Regex.IsMatch(fileTitle, "(?i)^(file|fichier):"))
				fileTitle = "Fichier:" + fileTitle;
			if (Regex.IsMatch(fileTitle, @"^\s*$"))
				return false;
			if (!listPages.ContainsKey(fileTitle)) {
				string querry = WikiFunctions.Variables.URLLong + "api.php?action=query&format=json&prop=imageinfo&titles=" + fileTitle;
				string result = new System.Net.WebClient().DownloadString(querry);
				listPages[fileTitle] = Regex.IsMatch(result, "imageinfo");
			}
			return listPages[fileTitle];
		}

		public void loadListPages() {
			List<string> exist = new List<string>();
			List<string> nopage = new List<string>();
			try {
				string fichierPrenom = System.IO.File.ReadAllText(CustomModule.ListsFile);
				deserialize(fichierPrenom, exist, "pages existantes");
				deserialize(fichierPrenom, nopage, "pages inexistantes");
			} catch {
			}
			foreach (string elmt in exist)
				listPages.Add(elmt, true);
			foreach (string elmt in nopage)
				listPages.Add(elmt, false);
		}
		public static void SaveListPages() {
			List<string> exist = new List<string>();
			List<string> nopage = new List<string>();
			foreach (string page in listPages.Keys) {
				if (listPages[page])
					exist.Add(page);
				else
					nopage.Add(page);
			}

			string fichier;
			try {
				fichier = System.IO.File.ReadAllText(CustomModule.ListsFile);
			} catch (Exception) {
				fichier = "";
			}
			string nouveauFichier = fichier;

			nouveauFichier = serialize(nouveauFichier, exist, "pages existantes", false);
			nouveauFichier = serialize(nouveauFichier, nopage, "pages inexistantes", false);

			if (nouveauFichier != fichier)
				System.IO.File.WriteAllText(CustomModule.ListsFile, nouveauFichier);
		}

		public static void deserialize(string fichier, List<string> list, string name) {
			list.Clear();
			Int32 start = fichier.IndexOf("[" + name + "]");
			if (start == -1)
				return;
			start += 2 + name.Length;
			Regex regexLine = new Regex(@"\G\s*([\S -[\[\]]]+)[\r\n]+");
			foreach (Match line in regexLine.Matches(fichier, start))
				list.Add(line.Groups[1].Value);
		}
		public static string serialize(string fichier, List<string> list, string name, bool confirm) {
			// génère la liste des nouveaux éléments
			List<string> oldList = new List<string>();
			deserialize(fichier, oldList, name);
			StringBuilder nouveauxElements = new StringBuilder();
			foreach (string elmt in list) {
				if (!oldList.Contains(elmt))
					nouveauxElements.Append(elmt).AppendLine();
			}

			if (nouveauxElements.Length > 0) {
				System.Windows.Forms.DialogResult save = System.Windows.Forms.DialogResult.Yes;

				// détermine s'il faut sauver les nouveau éléments
				if (confirm)
					save = System.Windows.Forms.MessageBox.Show(
						"Ajout à la liste des " + name + " : " + Environment.NewLine + nouveauxElements.ToString(),
						"Sauvegarde ?",
						System.Windows.Forms.MessageBoxButtons.YesNo
					);

				if (save == System.Windows.Forms.DialogResult.Yes) {
					// recherche de la section correspondante dans le ficher
					Int32 start = fichier.IndexOf("[" + name + "]");
					if (start == -1) {
						start = 0;
						fichier = "[" + name + "]" + Environment.NewLine + Environment.NewLine + fichier;
					}
					start += 2 + name.Length;
					// ajout des nouveaux éléments
					Regex regexLine = new Regex(@"\G( *\r?\n?(\s*[\S -[\[\]]]+\r?\n?)*)");
					fichier = regexLine.Replace(fichier, "$1" + nouveauxElements.ToString(), 1, start);
				} else
					deserialize(fichier, list, name);
			}

			return fichier;
		}
		public static string serialize(string fichier, List<string> list, string name) {
			return serialize(fichier, list, name, true);
		}
	}



	class ArticleTextClass : WikitextClass {
		public readonly bool jeuxVideo;
		public readonly int ns;
		public List<string> replacementTab = new List<string>();
		public List<TemplateClass> templateList = new List<TemplateClass>();
		public List<HtmlElement> htmlElementList = new List<HtmlElement>();
		public List<string> modifiedTemplates = new List<string>();
		public List<string> modifiedTables = new List<string>();
		public List<string> modifiedhtmlTag = new List<string>();
		private int tableEndAdded = 0;

		const string interuptedProcessWarning = "<!-- Il faut annuler la modification du bot qui a ajouté ce message !\nCette modification n'aurait pas du être enregistrée. -->\n";

		const string unClosedTableMessage = "<!-- Zebulon84bot à l'impression que cette page contient une table mal fermée : à vérifier – %s -->\n";

		public ArticleTextClass(string nname, string articleText, int wikiNamespace, bool AnalyseUnknownTemplates)
			: base(nname, articleText, null) {
			article = this;
			ns = wikiNamespace;
			flagAnalyseUnknownTemplates = AnalyseUnknownTemplates;
			listPages = new Dictionary<string, bool>();
			loadListPages();

			parsedString = interuptedProcessWarning + articleText;
			parsedString = Regex.Replace(parsedString, "¤", "&#164;");
			parsedString = Regex.Replace(parsedString, "§", "&#167;");
			parsedString = Regex.Replace(parsedString, @"\r", "");

			fermeLesTables();

			parse(0);
			listPages[nname] = true;
			jeuxVideo = Regex.IsMatch(articleText, "(?i)jeux? vid[ée]o");
		}

		public override string ToString() {
			string ArticleText = base.ToString();
			if (tableEndAdded > 0 && ArticleText.EndsWith("|}"))
				ArticleText = ArticleText.Remove(ArticleText.Length - 3 * tableEndAdded) + "\n" + unClosedTableMessage.Replace("%s", DateTime.Today.ToString("D"));
			ArticleText = Regex.Replace(ArticleText, @"¤§[wpcm](\d+)§¤", ReplaceNowiki);
			ArticleText = Regex.Replace(ArticleText, "&#164;", "¤");
			ArticleText = Regex.Replace(ArticleText, "&#167;", "§");
			if (!StopProcessing)
				ArticleText = ArticleText.Substring(interuptedProcessWarning.Length);

			return ArticleText;
		}

		public string getSumary() {
			StringBuilder buildSummary = new StringBuilder();
			if (modifiedTemplates.Count > 0) {
				buildSummary.Append("maintenance modèle");
				List<string> unique = new List<string>();
				foreach (string tmpl in modifiedTemplates) {
					if (!unique.Contains(ucFirst(tmpl)))
						unique.Add(ucFirst(tmpl));
				}
				if (unique.Count > 1) {
					buildSummary.Append("s ").Append(unique[0]);
					for (int i = 1; i <= unique.Count - 2; i++)
						buildSummary.Append(", ").Append(unique[i]);
					buildSummary.Append(" et ").Append(unique[unique.Count - 1]);
				} else
					buildSummary.Append(" ").Append(unique[0]);
			}
			if (modifiedTables.Count > 0) {
				if (buildSummary.Length > 0)
					buildSummary.Append(" ; ");
				buildSummary.Append("maintenance table");
				if (modifiedTables.Count > 1)
					buildSummary.Append("s");
			}
			if (modifiedhtmlTag.Count > 0) {
				if (buildSummary.Length > 0)
					buildSummary.Append(" ; ");
				buildSummary.Append("maintenance html");
			}

			return buildSummary.ToString();
		}

		private void fermeLesTables() {
			while (Regex.Matches(parsedString, @"(?m)^\s*\{\|").Count > Regex.Matches(parsedString, @"(?m)^\s*\|\}").Count) {
				parsedString = parsedString + "\n|}";
				tableEndAdded = tableEndAdded + 1;
			}
		}

		private class idChange {
			public string oldId;
			public string newId;
			public bool isValidChange = true;
			public List<Harvard> changeTemplate = new List<Harvard>();
			public List<Harvard> conflictTemplate = new List<Harvard>();

			public idChange(string old, Harvard harv) {
				oldId = old;
				newId = harv.Href;
				changeTemplate.Add(harv);
			}

			public void addTemplate(Harvard hrv) {
				if (hrv.Href != newId)
					addConflict(hrv);
				changeTemplate.Add(hrv);
			}

			public void addConflict(Harvard hrv) {
				isValidChange = false;
				foreach (Harvard template in changeTemplate) {
					template.Parametres[0].prefix = "<!-- !! En conflit avec " + hrv.initialString + "!! -->" + template.Parametres[0].prefix;
					hrv.Parametres[0].prefix = "<!-- !! En conflit avec " + template.initialString + "!! -->" + template.Parametres[0].prefix;
				}
			}
		}
		private Dictionary<string, idChange> harvardIdChangeList = new Dictionary<string, idChange>();

		public override void fix() {
			Biblio.loadListNomPrenom(true);

			base.fix();

			if (StopProcessing)
				return;

			foreach (TemplateClass template in templateList)
				testHarvard(template as Harvard);
			fixHarvard();
			Biblio.saveListNomPrenom();
			SaveListPages();
		}

		private void testHarvard(Harvard harvardTemplate) {
			// supprime les paramètre en trop. Ne peut pas être fait pendant le fixTemplate pour éviter les boucles infinie.
			if (harvardTemplate != null) {
				string oldId = harvardTemplate.Href;
				harvardTemplate.integreAnnee();
				harvardTemplate.suprimmeParamEnTrop();
				if (harvardTemplate.Href != oldId) {
					if (harvardIdChangeList.ContainsKey(oldId))
						harvardIdChangeList[oldId].addTemplate(harvardTemplate);
					else
						harvardIdChangeList.Add(oldId, new idChange(oldId, harvardTemplate));
				}
			}
		}

		private void fixHarvard() {
			foreach (TemplateClass template in templateList) {
				Harvard harvardTemplate = template as Harvard;
				if (harvardTemplate != null) {
					if (harvardIdChangeList.ContainsKey(harvardTemplate.Href))
						harvardIdChangeList[harvardTemplate.Href].addConflict(harvardTemplate);
				}
			}
			foreach (TemplateClass template in templateList) {
				idTemplate idTempl = template as idTemplate;
				if (idTempl != null && idTempl.id != null) {
					if (harvardIdChangeList.ContainsKey(idTempl.id) || harvardIdChangeList.ContainsKey(idTempl.id2)) {
						if (harvardIdChangeList[idTempl.id].isValidChange && !idTempl.hasId(harvardIdChangeList[idTempl.id].newId))
							idTempl.changeId(harvardIdChangeList[idTempl.id].newId);
					}
				}
			}
		}
	}


	// <summary>
	// - Class Template ans associated 
	// </summary>
	class TemplateClass : WikitextClass {
		public string templateName { get { return templateNames[0]; } }
		public bool isFixed { get; private set; }
		public List<parametre> Parametres { get; private set; }

		public parmetresStandardDictionary tmplParams = new parmetresStandardDictionary();

		public bool hasBeenModified = false;
		public List<string> templateNames = new List<string>();

		protected List<parametre> invalidParameters = new List<parametre>();
		protected string addBeforeTemplage = "";
		protected string addAfterTemplage = "";
		protected string templateEndString = null;
		protected int unnamedIndex = 0;

		protected bool isLowerOnly = false;
		protected bool flagTestInversion = true;
		protected bool flagTestWrongKeypress = true;
		protected bool flagTestMissedKeypress = true;
		protected bool flagTestAddedKeypress = true;
		protected bool flagRemoveEmptyKnownParam = false;
		protected bool flagOnlyNamedParam = false;
		protected bool falgRemoveDoubloonsEvenIfDifferent = false;
		protected bool flagRemoveEmptyUnnamedInvalidParam = true;
		protected bool flagRemoveEmptyNamedInvalidParam = true;



		public TemplateClass(string nname, string contenuModele, WikitextClass nparent, string[] ntemplateNames)
			: base(nname, contenuModele, nparent) {
			templateNames.AddRange(ntemplateNames);
			article.templateList.Add(this);
			Parametres = new List<parametre>();
			isFixed = false;
		}
		public TemplateClass(TemplateClass template, string[] ntemplateNames)
			: base(template) {
			templateNames.AddRange(ntemplateNames);
			name = templateNames[0];
		}

		public override string ToString() {
			StringBuilder buildTemplate = new StringBuilder();
			buildTemplate.Append(addBeforeTemplage);
			buildTemplate.Append("{{");
			buildTemplate.Append(name);
			if (templateEndString == null)
				buildTemplate.Append(parsedString);
			else {
				foreach (parametre param in Parametres) {
					{
						var withBlock = param;
						buildTemplate.Append(withBlock.prefix);
						if (withBlock.isNamed) {
							buildTemplate.Append(withBlock.name);
							buildTemplate.Append(withBlock.sep);
						}
						buildTemplate.Append(withBlock.value);
						buildTemplate.Append(withBlock.sufix);
					}
				}
				buildTemplate.Append(templateEndString);
			}
			buildTemplate.Append("}}");
			buildTemplate.Append(addAfterTemplage);
			string templateString = wikitextToString(buildTemplate.ToString());
			if (hasBeenModified)
				article.modifiedTemplates.Add(name.Trim());
			return templateString;
		}

		public void analyseParameters() {
			Regex detectionParametresRegex = new Regex(@"(?xsn) \G" + "(?<named>" + @"(?<prefix> \s*\|\s*)" + @"(?<name> [^=|{}\[\]]*)(?<!\s)" + @"(?<sep> \s*= [ \t]*)" + @"(?<value> ( {{((?<acc>{{)|(?<-acc>}})|.)*?(?(acc)(?!))}} | \[\[ [^\[\]|]+ (\| ((?<cro>\[\[)|(?<-cro>\]\])|[^\[\]]+)*?(?(cro)(?!)))? \]\] | [^|] )+ (?<!\s) )?" + @"(?<sufix> [ \t]*)(?=\s*\||(?<end> \s*)$)" + ")" + @" | \G" + "(?<unnamed>" + @"(?<prefix> \s*\|)" + @"(?<value> ( {{((?<acc>{{)|(?<-acc>}})|.)*?(?(acc)(?!))}} | \[\[ [^\[\]|]+ (\| ((?<cro>\[\[)|(?<-cro>\]\])|[^\[\]]+)*?(?(cro)(?!)))? \]\] | [^|] )+ (?<!\s) )?" + @"(?<sufix> [ \t]*)(?=\s*\||(?<end> \s*)$)" + ")");

			// parsing of the different parameters
			foreach (Match match in detectionParametresRegex.Matches(parsedString)) {
				if (match.Success) {
					parametre newParam;
					if (match.Groups["named"].Success)
						newParam = new parametre(this, match.Groups["name"].Value, match.Groups["value"].Value, match.Groups["prefix"].Value, match.Groups["sep"].Value, match.Groups["sufix"].Value
);
					else {
						unnamedIndex = unnamedIndex + 1;
						newParam = new parametre(this, unnamedIndex, match.Groups["value"].Value, match.Groups["prefix"].Value, match.Groups["sufix"].Value
						);
					}
					Parametres.Add(newParam);
					subWikitextList.Add(newParam);
					templateEndString = match.Groups["end"].Value;
				}
			}

			assignParameters();
		}

		// check valid parameters and defined used values
		private void assignParameters() {
			// Cancel previous values
			invalidParameters.Clear();
			foreach (parametreStandard stdParam in tmplParams.Values) {
				stdParam.parametre = null;
				stdParam.doubloons.Clear();
			}

			foreach (parametre param in Parametres) {
				// Cancel previous values
				param.isdoubloon = false;
				param.isActive = false;
				// check if valid parameter
				if (tmplParams.ContainsKey(param.name)) {
					parametreStandard stdParam = tmplParams[param.name];
					param.standardName = stdParam;
					if (stdParam.hasParametre) {
						stdParam.parametre.isdoubloon = true;
						stdParam.doubloons.Add(stdParam.parametre);
					}
					stdParam.parametre = param;
				} else
					invalidParameters.Add(param);
			}

			// define the active parameters
			foreach (parametreStandard stdParam in tmplParams.Values) {
				if (stdParam.hasParametre) {
					if (stdParam.isAlias) {
						if (!stdParam.mainParam.hasParametre) {
							stdParam.mainParam.parametre = stdParam.parametre;
							stdParam.parametre.isActive = true;
						} else {
							parametre currentMainParamValue = stdParam.mainParam.parametre;
							if (currentMainParamValue.standardName.isAlias && currentMainParamValue.standardName.aliasPriority > stdParam.aliasPriority) {
								currentMainParamValue.isActive = false;
								currentMainParamValue.isdoubloon = true;
								stdParam.doubloons.Add(currentMainParamValue);
								stdParam.mainParam.parametre = stdParam.parametre;
								stdParam.parametre.isActive = true;
							} else {
								stdParam.doubloons.Add(stdParam.parametre);
								stdParam.parametre.isdoubloon = true;
							}
						}
					} else
						stdParam.parametre.isActive = true;
				}
			}
		}


		public override void fix() {
			if (!isFixed && !StopProcessing) {
				base.fix();

				fixName();
				if (tmplParams.Count > 0 && !StopProcessing) {
					toLower();
					if (invalidParameters.Count > 0)
						detectMissingEqual();
					if (invalidParameters.Count > 0)
						fixLetterMix();
					if (invalidParameters.Count > 0)
						detectEqualPipe();
					if (invalidParameters.Count > 0)
						detectEqualAtEnd();
					// If invalidParameters.Count > 0 Then
					// detectPipeBeforeValue()
					// End If


					// remplace les espaces insécable par des espace normaux dans les séparateur et préfixes
					foreach (parametre param in Parametres) {
						if (param.sep != null)
							param.sep = param.sep.Replace(@"\u00a0", " ").Replace(@"\u202f", " ");
						param.prefix = param.prefix.Replace(@"\u00a0", " ").Replace(@"\u202f", " ");
					}
					assignParameters();

					isFixed = true;

					otherFix();

					if (StopProcessing)
						return;

					removeUselessDoubloons();
					replaceObsoleteParam();

					removeEmptyKnownParam();

					if (flagRemoveEmptyUnnamedInvalidParam)
						removeEmptyUnnamedInvalidParam();
					if (flagRemoveEmptyNamedInvalidParam)
						removeEmptyNamedInvalidParam();
					trimParam();
					fixParamName();
				} else {
					falgRemoveDoubloonsEvenIfDifferent = true;
					removeUselessDoubloons();
					fixFormatNum();
				}

			}
		}

		private string _paramToFind;
		private bool findParamPredicate(parametre param) {
			return param.name.Equals(_paramToFind);
		}
		public parametre findParam(string name, params string[] names) {
			_paramToFind = name;
			parametre param = Parametres.FindLast(findParamPredicate);
			if (param == null) {
				foreach (string n in names) {
					_paramToFind = n;
					param = Parametres.FindLast(findParamPredicate);
					if (param != null)
						break;
				}
			}
			return param;
		}

		public void getSep(parametre param) {
			getSep(param, null);
		}
		public void getSep(parametre param, parametre paramExemple) {
			int id = 0;
			if (param != null && paramExemple == null) {
				id = Parametres.IndexOf(param);
				if (id > 0)
					id = id - 1;
				else if (Parametres.Count > 1)
					id = 1;
			} else
				id = Parametres.IndexOf(paramExemple);
			if (Parametres[id].isNamed) {
				string alignment = Regex.Match(Parametres[id].sep, "^ *").Value;
				if (alignment.Length > 1) {
					int newLength = alignment.Length + Parametres[id].name.Length - param.name.Length;
					if (newLength < 0)
						newLength = 0;
					param.sep = Regex.Replace(Parametres[id].sep, "^( *)", new string(' ', newLength));
				} else
					param.sep = Parametres[id].sep;
			} else
				param.sep = " = ";
		}

		public void addParameter(parametre param) {
			addParameter(param, Parametres.Count);
		}
		public void addParameter(parametre param, parametre after) {
			addParameter(param, Parametres.IndexOf(after) + 1);
		}
		public void addParameterBefore(parametre param, parametre before) {
			addParameter(param, Parametres.IndexOf(before));
		}
		public void addParameter(parametre param, int position) {
			if (param != null) {
				Parametres.Insert(position, param);
				if (tmplParams.ContainsKey(param.name))
					assignParameters();
				hasBeenModified = true;
			}
		}

		public void removeParameter(string name) {
			bool hasChanged = false;
			List<parametre> paramList = new List<parametre>(Parametres);
			foreach (parametre param in paramList) {
				if (param.name == name)
					removeParameter(param);
			}
			if (hasChanged)
				assignParameters();
		}
		public void removeParameter(parametre param) {
			if (param != null) {
				if (param.prefix.StartsWith("\n")) {
					int id = Parametres.IndexOf(param);
					if ((id + 1) < Parametres.Count) {
						if (Parametres[id + 1].isNamed && !Parametres[id + 1].prefix.StartsWith("\n"))
							Parametres[id + 1].prefix = param.prefix;
					}
				}
				Parametres.Remove(param);
				if (!param.isNamed) {
					foreach (parametre unamedParam in Parametres) {
						if (!unamedParam.isNamed && Convert.ToInt32(unamedParam.name) > Convert.ToInt32(param.name))
							unamedParam.changeName((Convert.ToInt32(unamedParam.name) - 1).ToString());
					}
					unnamedIndex = unnamedIndex - 1;
				}
				if (!param.isEmpty)
					hasBeenModified = true;
				assignParameters();
			}
		}

		public void renameParameter(string name, string newName) {
			renameParameter(name, newName, false);
		}
		public void renameParameter(string name, string newName, bool minor) {
			List<parametre> paramList = new List<parametre>(Parametres);
			foreach (parametre param in paramList) {
				if (param.name == name)
					renameParameter(param, newName, minor);
			}
		}
		public bool renameParameter(parametre param, string newName) {
			return renameParameter(param, newName, false);
		}
		public bool renameParameter(parametre param, string newName, bool minor) {
			if (param == null)
				return false;
			else {
				string odlName = param.name;
				bool haschanged = false;
				if (!tmplParams.ContainsKey(newName)) {
					param.changeName(newName);
					haschanged = true;
					if (param.standardName != null) {
						if (param.standardName.parametre == param)
							param.standardName.parametre = null;
						else
							assignParameters();
					}
				} else {
					parametreStandard stdParam = tmplParams[newName];
					if (stdParam.mainParam.hasParametre && !param.Equals(stdParam.mainParam.parametre) && !stdParam.mainParam.parametre.isEmpty) {
						if (falgRemoveDoubloonsEvenIfDifferent || param.value == stdParam.mainParam.value) {
							removeParameter(param);
						} else if (stdParam.hasParametre) {
							if (param.value == stdParam.value) {
								removeParameter(param);
							}
						}
					} else {
						param.changeName(newName);
						haschanged = true;
						if (!param.isEmpty && !minor)
							FlagSkip = false;
						assignParameters();
					}
				}
				if (haschanged) {
					if (param.isNamed) {
						if (Regex.IsMatch(param.sep, "^  +")) {
							string alignment = Regex.Match(param.sep, "^ *").Value;
							int newLength = alignment.Length + odlName.Length - param.name.Length;
							if (newLength < 0)
								newLength = 0;
							param.sep = Regex.Replace(param.sep, "^( *)", new string(' ', newLength));
						}
					} else {
						param.prefix = param.prefix + Regex.Match(param.value, @"^\s*").Value;
						param.value = Regex.Match(param.value, @"^\s*(.*)").Groups[1].Value;
						getSep(param);
					}
					if (!minor)
						hasBeenModified = true;
				}
				return haschanged;
			}
		}


		public void removeDateTemplate(string paramName) {
			parametre param = findParam(paramName);
			if (param != null) {
				if (Regex.IsMatch(param.value, @"^¤§t\d+§¤")) {
					TemplateClass tmpl = article.templateList[System.Convert.ToInt32(Regex.Match(param.value, @"^¤§t(\d+)").Groups[1].Value)];
					if (tmpl is ModeleDate)
						param.value = param.value.Replace(tmpl.strip, ((ModeleDate)tmpl).getDate());
				} else
					param.value = Regex.Replace(param.value, @"\G\[\[([^\]|]+\|)?([^\]]*\d)\]\]( ?)", "$2$3");
			}
		}
		public void fixName() {
			string sanitizedName = Regex.Replace(name, @"¤§c\d+§¤", "").Trim();
			sanitizedName = ucFirst(sanitizedName);
			if (templateName != "" && !templateNames.Contains(sanitizedName))
				name = name.Replace(name.Trim(), templateName);
		}

		public virtual void otherFix() { }

		private void toLower() {
			List<parametre> invalides = new List<parametre>(invalidParameters);
			foreach (parametre param in invalides) {
				string lowerName = param.name.ToLower();
				if (lowerName != param.name && tmplParams.ContainsKey(lowerName)) {
					parametreStandard stdParam = tmplParams[lowerName];
					if (stdParam.mainParam.hasValue) {
						if (falgRemoveDoubloonsEvenIfDifferent || param.value == stdParam.mainParam.value) {
							removeParameter(param);
							hasBeenModified = true;
						} else if (stdParam.hasParametre) {
							if (param.value == stdParam.value) {
								removeParameter(param);
								hasBeenModified = true;
							}
						}
					} else if (stdParam.mainParam.hasParametre && param.isEmpty)
						removeParameter(param);
					else {
						if (stdParam.mainParam.hasParametre)
							removeParameter(stdParam.parametre);
						param.changeName(lowerName);
						param.standardName = tmplParams[param.name];
						hasBeenModified = true;
						if (!param.isEmpty)
							FlagSkip = false;
					}
				}
			}
		}

		private void detectMissingEqual() {
			List<parametre> invalides = new List<parametre>(invalidParameters);
			foreach (parametre param in invalides) {
				if (!param.isNamed) {
					List<parametreStandard> candidateList = new List<parametreStandard>();
					foreach (parametreStandard stdParam in tmplParams.Values) {
						if (param.value.Trim().ToLower().StartsWith(stdParam.name.ToLower()))
							candidateList.Add(stdParam);
					}
					if (candidateList.Count > 1) {
						do {
							if (candidateList[0].name.Length > candidateList[1].name.Length)
								candidateList.RemoveAt(1);
							else
								candidateList.RemoveAt(0);
						}
						while (candidateList.Count != 1);
					}
					if (candidateList.Count == 1) {
						if (candidateList[0].hasValue) {
							if (falgRemoveDoubloonsEvenIfDifferent || candidateList[0].parametre.isEmpty || candidateList[0].value == param.value.Remove(0, candidateList[0].parametre.name.Length).Trim()) {
								removeParameter(param);
								hasBeenModified = true;
							}
						} else {
							param.changeName(candidateList[0].name);
							param.prefix = param.prefix + Regex.Match(param.value, @"^\s*").Value;
							param.value = Regex.Replace(param.value, @"(?i)^\s*" + Regex.Escape(param.name) + @"\s*[:+)]?\s*(.*)$", "$1");
							getSep(param);
							hasBeenModified = true;
							FlagSkip = false;
						}
					}
				} else {
					// check if url, often containing equal signs
					Match match = Regex.Match(param.name, "^(.*)(http.*)$", RegexOptions.IgnoreCase);
					if (match.Success) {
						string name = match.Groups[1].Value.Trim();
						if (name.Length > 0) {
							if (tmplParams.ContainsKey(name) || tmplParams.ContainsKey(name)) {
								parametreStandard paramStd = tmplParams[name];
								if (paramStd.isUrl && !paramStd.hasParametre) {
									param.changeName(name);
									param.value = match.Groups[2].Value + param.sep + param.value;
									getSep(param);
									hasBeenModified = true;
									FlagSkip = false;
								}
							}
						} else
							foreach (parametreStandard paramStd in tmplParams.Values) {
								if (paramStd.isUrl && !paramStd.mainParam.hasParametre) {
									param.value = match.Groups[2].Value + param.sep + param.value;
									param.changeName(paramStd.name);
									getSep(param);
									hasBeenModified = true;
									FlagSkip = false;
									if (isNumber(param.name) && Parametres.IndexOf(param) < (Parametres.Count - 1)) {
										for (int i = Parametres.IndexOf(param); i <= Parametres.Count - 1; i++) {
											parametre param_i = Parametres[i];
											if (!param_i.isNamed)
												getSep(param_i);
										}
									}
									break;
								}
							}
					}
				}
			}
		}

		private void detectEqualPipe() {
			List<parametre> invalides = new List<parametre>(invalidParameters);
			foreach (parametre param in invalides) {
				if (!param.isNamed && !param.isEmpty) {
					int index = Parametres.IndexOf(param);
					if (index > 0) {
						parametre previousParam = Parametres[index - 1];
						if (previousParam.isNamed && previousParam.isEmpty && previousParam.isValid) {
							previousParam.value = param.value;
							removeParameter(param);
							hasBeenModified = true;
							FlagSkip = false;
						}
					}
				}
			}
		}

		private void detectEqualAtEnd() {
			List<parametre> invalides = new List<parametre>(invalidParameters);
			foreach (parametre param in invalides) {
				if (param.isNamed && param.isEmpty) {
					List<parametreStandard> candidateList = new List<parametreStandard>();
					foreach (parametreStandard stdParam in tmplParams.Values) {
						bool stdParamIsEmpty = true;
						if (stdParam.hasParametre)
							stdParamIsEmpty = stdParam.parametre.isEmpty;
						if (stdParamIsEmpty && param.name.StartsWith(stdParam.name))
							candidateList.Add(stdParam);
					}
					if (candidateList.Count > 1) {
						do {
							if (candidateList[0].name.Length > candidateList[1].name.Length)
								candidateList.RemoveAt(1);
							else
								candidateList.RemoveAt(0);
						}
						while (candidateList.Count != 1);
					}
					if (candidateList.Count == 1) {
						param.value = param.name.Remove(0, candidateList[0].name.Length).Trim();
						param.changeName(candidateList[0].name);
						int id = Parametres.IndexOf(param);
						if (id > 0 && Parametres[id - 1].isNamed)
							param.sep = Parametres[id - 1].sep;

						hasBeenModified = true;
						FlagSkip = false;
					}
				}
			}
		}

		private void detectPipeBeforeValue() {
			List<parametre> invalides = new List<parametre>(invalidParameters);
			foreach (parametre param in invalides) {
				int index = Parametres.IndexOf(param) - 1;
				if (index > 0) {
					parametre previousParam = Parametres[index];
					if (param.isNamed && previousParam.isNamed && previousParam.isEmpty) {
						foreach (parametreStandard paramStd in tmplParams.Values) {
							if (!paramStd.hasValue && (param.name.EndsWith(paramStd.name) || (isLowerOnly && param.name.ToLower().EndsWith(paramStd.name)))) {
								previousParam.value = param.name.Remove(param.name.Length - paramStd.name.Length).Trim();
								param.changeName(paramStd.name);
								hasBeenModified = true;
								FlagSkip = false;
							}
						}
					}
				}
			}
		}

		private void fixLetterMix() {
			List<parametre> invalides = new List<parametre>(invalidParameters);
			foreach (parametre param in invalides) {
				if (param.isNamed) {
					List<parametreStandard> candidateList = new List<parametreStandard>();
					char[] paramNameArray;
					string paramName;
					if (isLowerOnly)
						paramName = param.name.ToLower();
					else
						paramName = param.name;
					paramNameArray = paramName.ToCharArray();
					char[] sortedParamNameArray = paramName.ToCharArray();
					Array.Sort(sortedParamNameArray);

					// for each standandard parameter, test different typing errors : inversion (up to 2 caracters), addition, omission, wrong letter
					foreach (parametreStandard stdParam in tmplParams.Values) {
						char[] stdNameArray = stdParam.name.ToCharArray();
						char[] sortedStdNameArray = stdParam.name.ToCharArray();
						Array.Sort(sortedStdNameArray);
						if (flagTestInversion && sortedParamNameArray == sortedStdNameArray) {
							// teste s'il y a des lettres inversées
							int diff = 0;
							for (int i = 0; i <= paramNameArray.Length - 1; i++) {
								if (paramNameArray[i] != stdNameArray[i]) {
									if (diff == 1) {
										diff = 2;
										break;
									}
									if (paramNameArray[i] == stdNameArray[i + 1] && paramNameArray[i + 1] == stdNameArray[i])
										i = i + 1;
									else if (
										paramNameArray[i] == stdNameArray[i + 1]
										&& paramNameArray[i + 1] == stdNameArray[i + 2]
										&& paramNameArray[i + 2] == stdNameArray[i]
									)
										i = i + 2;
									else if (
										paramNameArray[i] == stdNameArray[i + 2]
										&& paramNameArray[i + 1] == stdNameArray[i]
										&& paramNameArray[i + 2] == stdNameArray[i + 1]
									)
										i = i + 2;
									else {
										diff = 2;
										break;
									}
									diff = 1;
								}
							}
							if (diff == 1)
								candidateList.Add(stdParam);
						} else if (flagTestWrongKeypress && paramNameArray.Length == stdNameArray.Length) {
							// teste s'il y a une lettre à la place d'une autre
							int diff = 0;
							for (int i = 0; i <= paramNameArray.Length - 1; i++) {
								if (paramNameArray[i] != stdNameArray[i])
									diff = diff + 1;
							}
							if (diff == 1)
								candidateList.Add(stdParam);
						} else if (flagTestMissedKeypress && paramNameArray.Length == stdNameArray.Length - 1) {
							// teste s'il y a une lettre manquante
							int diff = 0;
							for (int i = 0; i <= paramNameArray.Length - 1; i++) {
								if (paramNameArray[i] != stdNameArray[i + diff]) {
									diff = diff + 1;
									if (diff > 1)
										break;
									if (paramNameArray[i] != stdNameArray[i + diff]) {
										diff = 2;
										break;
									}
								}
							}
							if (diff == 1 || (diff == 0 && !Char.IsNumber(stdNameArray[stdNameArray.Length - 1])))
								candidateList.Add(stdParam);
						} else if (flagTestAddedKeypress && paramNameArray.Length == stdNameArray.Length + 1) {
							// test s'il y a une lettre en trop
							int diff = 0;
							for (int i = 0; i <= stdNameArray.Length - 1; i++) {
								if (paramNameArray[i + diff] != stdNameArray[i]) {
									if (Char.IsNumber(paramNameArray[i + diff]) || !(paramNameArray[i + 1] == stdNameArray[i]))
										diff = 2;
									diff = diff + 1;
									if (diff > 1)
										break;
								}
							}
							if (diff == 0 || diff == 1)
								candidateList.Add(stdParam);
						}
					}

					// if several possibilities, eliminate existing parameters
					if (candidateList.Count > 1) {
						// look candidate values
						List<parametreStandard> candidateToBeRemoved = new List<parametreStandard>();
						foreach (parametreStandard candidate in candidateList) {
							if (candidate.hasParametre) {
								if (param.value == candidate.value) {
									removeParameter(param);
									hasBeenModified = true;
								} else
									candidateToBeRemoved.Add(candidate);
							}
						}
						foreach (parametreStandard candidate in candidateToBeRemoved)
							candidateList.Remove(candidate);

						// if still several possibilities, look candidate main Parmeter values
						if (candidateList.Count > 1) {
							candidateToBeRemoved.Clear();
							foreach (parametreStandard candidate in candidateList) {
								if (candidate.mainParam.hasParametre) {
									if (param.value == candidate.mainParam.value) {
										removeParameter(param);
										hasBeenModified = true;
									} else
										candidateToBeRemoved.Add(candidate);
								}
							}
							foreach (parametreStandard candidate in candidateToBeRemoved)
								candidateList.Remove(candidate);
						}

						// if still several possibilities, check if is only alias of one parameter
						if (candidateList.Count > 1) {
							parametreStandard MainP = candidateList[0].mainParam;
							foreach (parametreStandard candidate in candidateList) {
								if (!MainP.Equals(candidate.mainParam)) {
									MainP = null;
									break;
								}
							}
							if (MainP != null) {
								candidateList.Clear();
								candidateList.Add(MainP);
							}
						}

						// if still several possibilities, check if only one param name with different number at the end (keep the smallest)
						if (candidateList.Count > 1) {
							Regex regexMultiParam = new Regex(@"^(.*\D)(\d+)$");
							int i = 0;
							int j;
							string baseName;
							while (i + 1 < candidateList.Count) {
								baseName = regexMultiParam.Match(candidateList[i].name).Groups[1].Value;
								if (baseName != "") {
									j = i + 1;
									while (j < candidateList.Count) {
										if (regexMultiParam.Match(candidateList[j].name).Groups[1].Value == baseName) {
											int indexI = System.Convert.ToInt32(regexMultiParam.Match(candidateList[i].name).Groups[2].Value);
											int indexJ = System.Convert.ToInt32(regexMultiParam.Match(candidateList[j].name).Groups[2].Value);
											if (indexI > indexJ) {
												candidateList.RemoveAt(i);
												i = i - 1;
												break;
											} else
												candidateList.RemoveAt(j);
										} else
											j = j + 1;
									}
								}
								i = i + 1;
							}
						}
					}

					// If only one candidate, param is renamaned exept if this parameter already exist.
					if (candidateList.Count == 1)
						hasBeenModified = renameParameter(param, candidateList[0].name);
				}
			}
		}

		private void replaceObsoleteParam() {
			foreach (parametre param in Parametres) {
				if (param.isActive && param.standardName.isDeprecated) {
					renameParameter(param, param.standardName.mainParam.name, true);
					if (!param.isEmpty && param.standardName.isObsolete) {
						FlagSkip = false;
						hasBeenModified = true;
					}
				}
			}
		}

		private void removeEmptyKnownParam() {
			List<parametre> paramList = new List<parametre>(Parametres);
			foreach (parametre param in paramList) {
				if (param.isEmpty && param.isNamed && param.isValid && param.standardName.mainParam.deleteIfEmpty)
					removeParameter(param);
			}
		}

		private void removeEmptyUnnamedInvalidParam() {
			int index = unnamedIndex;
			while (index > 0) {
				foreach (parametre param in Parametres) {
					if (param.name == index.ToString()) {
						if (param.isValid)
							break;
						if (param.isEmpty) {
							removeParameter(param);
							unnamedIndex = unnamedIndex - 1;
						}
						break;
					}
				}
				index = index - 1;
			}
		}

		private void removeEmptyNamedInvalidParam() {
			List<parametre> paramList = new List<parametre>(Parametres);
			foreach (parametre param in paramList) {
				if (!param.isValid && param.isEmpty && !isNumber(param.name))
					removeParameter(param);
			}
		}

		private void removeUselessDoubloons() {
			foreach (parametreStandard paramStd in tmplParams.Values) {
				if (paramStd.doubloons.Count > 0)
					testDoubloons(paramStd, null);
			}
		}
		private void testDoubloons(parametreStandard paramStd, parametre doubloon) {
			if (doubloon == null)
				doubloon = paramStd.doubloons[0];

			if (doubloon.isEmpty || paramStd.mainParam.value == doubloon.value)
				removeParameter(doubloon);
			else if (paramStd.mainParam.parametre.isEmpty)
				removeParameter(paramStd.mainParam.parametre);
			else if (falgRemoveDoubloonsEvenIfDifferent) {
				removeParameter(doubloon);
				if (paramStd.doubloons.Count > 0)
					testDoubloons(paramStd, null);
			} else if (paramStd.doubloons.Count > paramStd.doubloons.IndexOf(doubloon) + 1)
				testDoubloons(paramStd, paramStd.doubloons[paramStd.doubloons.IndexOf(doubloon) + 1]);
		}

		public void changeClassTo(Type templateClass) {
			foreach (parametre param in Parametres) {
				if (tmplParams.ContainsKey(param.name))
					param.standardName = null;
			}

			int index = article.templateList.IndexOf(this);
			TemplateClass newTemplate = (TemplateClass)Activator.CreateInstance(templateClass, this);
			newTemplate.Parametres = this.Parametres;
			newTemplate.templateEndString = this.templateEndString;
			newTemplate.unnamedIndex = this.unnamedIndex;
			newTemplate.strip = this.strip;

			newTemplate.assignParameters();
			article.templateList[index] = newTemplate;
			newTemplate.fix();
			hasBeenModified = true;
		}

		public void trimParam() {
			// retire les tabulation et les après les signes = et à la fin des parmètres
			foreach (parametre param in Parametres) {
				if (param.isNamed) {
					param.sep = Regex.Replace(param.sep, @"= *\t+", "= ");
					param.sufix = Regex.Replace(param.sufix, @"^\t+", "");
				}
			}
		}

		public void fixParamName() {
			// retire les espaces insécables avant ou après le nom du paramètre
			foreach (parametre param in Parametres) {
				if (param.isNamed && (Regex.IsMatch(param.prefix, @"[\u00A0\u200B-\u200F]") || Regex.IsMatch(param.sep, @"[\u00A0\u200B-\u200F]"))) {
					param.prefix = Regex.Replace(param.prefix, "\u00A0", " ");
					param.sep = Regex.Replace(param.sep, "\u00A0", " ");
					param.prefix = Regex.Replace(param.prefix, @"[\u200B-\u200F]", "");
					param.sep = Regex.Replace(param.sep, @"[\u200B-\u200F]", "");
					hasBeenModified = true;
				}
			}
		}

		public void fixFormatNum() {
			if (name.StartsWith("formatnum:")) {
				Match followingNum = Regex.Match(parent.parsedString, "(" + strip + @") *(\d[\d ]*)(?<=\d)");
				if (followingNum.Success) {
					name = name + Regex.Replace(followingNum.Groups[2].Value, " |&nbsp;", "");
					parent.parsedString = parent.parsedString.Replace(followingNum.Groups[0].Value, followingNum.Groups[1].Value);
				}
			}
		}
	}


	class parmetresStandardDictionary : Dictionary<string, parametreStandard> {
		public void addAlias(parametreStandard mainP, parametreStandard paramStd) {
			paramStd.isAlias = true;
			paramStd.mainParam = mainP;
			base.Add(paramStd.name, paramStd);
			paramStd.aliasPriority = mainP.aliases.Count;
			mainP.aliases.Add(paramStd);
		}

		public void add(params string[] stringTab) {
			parametreStandard mainP = new parametreStandard(stringTab[0]);
			base.Add(mainP.name, mainP);
			for (int i = 1; i <= stringTab.Length - 1; i++)
				addAlias(mainP, new parametreStandard(stringTab[i]));
		}

		public void add(string paramName, string aliasName, params parametreStandard[] paramTab) {
			parametreStandard mainP = new parametreStandard(paramName);
			base.Add(paramName, mainP);
			addAlias(mainP, new parametreStandard(aliasName));
			foreach (parametreStandard paramStd in paramTab)
				addAlias(mainP, paramStd);
		}

		public void add(string paramName, parametreStandard aliasParam) {
			parametreStandard mainP = new parametreStandard(paramName);
			base.Add(paramName, mainP);
			addAlias(mainP, aliasParam);
		}

		public void add(string paramName, params parametreStandard[] paramTab) {
			parametreStandard mainP = new parametreStandard(paramName);
			base.Add(paramName, mainP);
			foreach (parametreStandard paramStd in paramTab)
				addAlias(mainP, paramStd);
		}

		public void add(params parametreStandard[] paramTab) {
			parametreStandard mainP = paramTab[0];
			base.Add(mainP.name, mainP);
			for (int i = 1; i <= paramTab.Length - 1; i++)
				addAlias(mainP, paramTab[i]);
		}

		//Avec isnumeric
		public void add(string paramName, bool numeric) {
			parametreStandard mainP = new parametreStandard(paramName);
			mainP.isNumeric = numeric;
			base.Add(mainP.name, mainP);
		}

		public void add(string paramName, bool numeric, params string[] stringTab) {
			parametreStandard mainP = new parametreStandard(paramName);
			mainP.isNumeric = numeric;
			base.Add(mainP.name, mainP);
			for (int i = 1; i <= stringTab.Length - 1; i++)
				addAlias(mainP, new parametreStandard(stringTab[i]));
		}

		public void add(string paramName, bool numeric, params parametreStandard[] paramTab) {
			parametreStandard mainP = new parametreStandard(paramName);
			mainP.isNumeric = numeric;
			base.Add(paramName, mainP);
			foreach (parametreStandard paramStd in paramTab)
				addAlias(mainP, paramStd);
		}

		public void parametreToDeleteIfEmpty(params string[] paramNames) {
			foreach (string paramName in paramNames) {
				if (ContainsKey(paramName))
					this[paramName].deleteIfEmpty = true;
			}
		}
	}


	class parametreStandard {
		public string name;

		private parametre _parametre;
		public parametre parametre {
			get { return _parametre; }
			set {
				_parametre = value;
				hasParametre = _parametre != null;
			}
		}
		public List<parametre> doubloons { get; private set; }
		public List<parametreStandard> aliases { get; private set; }

		public bool isAlias;
		public bool isObsolete = false;
		public bool isDeprecated = false;
		public bool isUrl = false;
		public int aliasPriority;
		public bool isNumeric = false;
		public bool deleteIfEmpty = false;

		public bool hasParametre { get; private set; }
		private parametreStandard _mainParam;
		public parametreStandard mainParam {
			get { return _mainParam; }
			set {
				if (value.mainParam != null) {
					value = value.mainParam;
					aliases = value.aliases;
					doubloons = value.doubloons;
					isUrl = isUrl || value.isUrl;
				}
				_mainParam = value;
				if (!value.Equals(this))
					isAlias = true;
			}
		}
		public bool hasValue {
			get {
				if (hasParametre)
					return !parametre.isEmpty;
				else
					return false;
			}
		}
		public string value {
			get {
				if (parametre != null)
					return parametre.value;
				else
					return null;
			}
			set {
				parametre.value = value;
			}
		}

	
		public parametreStandard(string n) {
			_mainParam = this;
			name = n;
			doubloons = new List<parametre>();
			aliases = new List<parametreStandard>();
			aliases.Add(this);
		}	
		public parametreStandard(string n, string options) : this(n) {
			if (options.Contains("d"))
				isDeprecated = true;
			if (options.Contains("o")) {
				isDeprecated = true;
				isObsolete = true;
			}
			if (options.Contains("u"))
				isUrl = true;
		}

		public override string ToString() {
			StringBuilder paramStrBld = new StringBuilder();
			paramStrBld.Append(name);
			if (isAlias) {
				paramStrBld.Append(" (isAlias");
				if (isObsolete)
					paramStrBld.Append(", isObsolete)");
				else if (isDeprecated)
					paramStrBld.Append(", isDeprecated)");
				else
					paramStrBld.Append(")");
			}
			return paramStrBld.ToString();
		}
	}

	class parametre : WikitextClass {

		// Private _value As String
		public string value {
			get { return parsedString; }
			set {
				if (value != parsedString) {
					parsedString = value;
					TemplateClass tmpl = parent as TemplateClass;
					if (tmpl != null)
						tmpl.hasBeenModified = true;
				}
				parse(1);
				// fix()
				isEmpty = value == "" || Regex.IsMatch(value, @"^\s*(?:¤§c(\d+)§¤)*\s*$");
			}
		}
		public bool isEmpty { get; private set; }
		public string prefix { get; set; }
		public string sep { get; set; }
		public string sufix { get; set; }
		public bool isNamed { get { return sep.Contains("="); } }
		public bool isValid { get { return standardName != null; } }
		public bool isActive { get; set; }
		public parametreStandard standardName { get; set; }
		public bool isdoubloon { get; set; }

		public parametre(TemplateClass template, string nname, string nvalue, string nprefix, string nsep, string nsufix)
			: base(nname, nvalue, template) {
			value = nvalue;
			prefix = nprefix;
			sep = nsep;
			sufix = nsufix;
		}
		public parametre(TemplateClass template, int index, string nvalue, string nprefix, string nsufix)
			: base(index.ToString(), nvalue, template) {
			value = nvalue;
			prefix = nprefix;
			sufix = nsufix;
			sep = "";
		}
		public parametre(parametre paramExemple, string nname, string nvalue)
			: base(nname, nvalue, paramExemple.parent) {
			value = nvalue;
			prefix = paramExemple.prefix;
			if (paramExemple.isNamed || nvalue.Contains("=") || isNamed || !isNumber(nname)) {
				TemplateClass parentTemplate = paramExemple.parent as TemplateClass;
				parentTemplate.getSep(this, paramExemple);
			} else
				sep = "";
			sufix = paramExemple.sufix;
		}

		public override void fix() {
			base.fix();

			if (standardName != null && standardName.mainParam.isNumeric) {
				if (Regex.IsMatch(value, @"^¤§t\d+§¤$")) {
					TemplateClass tmpl = article.templateList[System.Convert.ToInt32(Regex.Match(value, @"\d+").Value)];
					if (tmpl.templateNames[0] == "Unité")
						value = tmpl.Parametres[0].value;
					else if (Regex.IsMatch(tmpl.name, "formatnum:"))
						value = Regex.Match(tmpl.name, @"formatnum:\s*([\d,.  ]+)").Groups[1].Value;
				}
			}
		}

		public override string ToString() {
			StringBuilder paramStrBld = new StringBuilder();
			paramStrBld.Append(prefix);
			if (isNamed) {
				paramStrBld.Append(name);
				paramStrBld.Append(sep);
			}
			paramStrBld.Append(value);
			paramStrBld.Append(sufix);
			return paramStrBld.ToString();
		}

		public void changeName(string newName) {
			name = newName;
			if (!isNumber(newName) && !isNamed)
				sep = "=";
		}
	}

	// ---
	// - Class Html
	// ---
	class HtmlElement : WikitextClass {
		public bool isBloc = false;
		public bool isTableElement = false;
		public bool isInline = false;
		public bool isWiki = false;
		public HtmlAttributes attributes;
		public AttributeClass classes {
			get {
				return attributes.classes;
			}
			set {
				attributes.classes = value;
			}
		}
		public StyleAttribute style {
			get {
				return attributes.style;
			}
			set {
				attributes.style = value;
			}
		}

		public virtual HtmlElement tableParent {
			get {
				switch (name) {
					case "table": {
							return this;
						}

					case "tr":
					case "caption": {
							return parent as HtmlElement;
						}

					case "td":
					case "th": {
							return parent.parent as HtmlElement;
						}
				}
				return null;
			}
		}
		public bool isWikitable {
			get {
				if (tableParent != null)
					return tableParent.hasClass("wikitable");
				return false;
			}
			set {
				if (tableParent != null) {
					if (value)
						tableParent.addClass("wikitable", true);
					else
						removeClass("wikitable");
				}
			}
		}

		public HtmlElement(string nname, string content, WikitextClass nparent)
			: base(nname, content, nparent) {
			bool t;
			t = Array.Exists(blockTag, s => { return s == name; });
			t = Array.Exists(blockTag, name.Equals);
			t = Array.IndexOf(blockTag, name) != -1;

			if (Array.Exists(blockTag, name.Equals))
				isBloc = true;
			else if (Array.Exists(tableTag, name.Equals))
				isTableElement = true;
			else if (Array.Exists(inlineTag, name.Equals))
				isInline = true;
			else if (Array.Exists(wikiTag, name.Equals))
				isWiki = true;
		}

		public override void fix() {
			base.fix();
			mergeWithChild();
		}

		public void mergeWithChild() {
			if (isWiki)
				return;
			switch (name) {
				case "table":
				case "tr":
				case "ul":
				case "ol": {
						return;
					}
			}

			Match childMach = Regex.Match(parsedString, @"^(\s*)¤§h(\d+)§¤(\s*)$");
			if (childMach.Success) {
				HtmlElement child = article.htmlElementList[System.Convert.ToInt32(childMach.Groups[2].Value)];
				if (child.isWiki)
					return;
				switch (child.name) {
					case "tr":
					case "td":
					case "th":
					case "li":
					case "h1":
					case "h2":
					case "h3":
					case "h4":
					case "h5":
					case "h6":
					case "small":
					case "big":
					case "s":
					case "u": {
							return;
						}
				}
				if (child.hasAttribute("class") || child.hasStyle("position") || child.hasStyle("width") || hasAttribute("class") || child.getStyleValue("background") != "" || child.getStyleValue("background-color") != "")
					return;

				string deletedName = child.name;
				List<string> elementWithStyle = new List<string>(new string[] { "small", "big", "b", "i", "u", "s" });
				if (isInline && child.isBloc) {
					deletedName = name;
					name = child.name;
					isBloc = true;
				}
				if (name == "span" && elementWithStyle.Contains(child.name)) {
					deletedName = "span";
					name = child.name;
				}
				if (elementWithStyle.Contains(deletedName)) {
					switch (deletedName) {
						case "small": {
								if (name == "small")
									addStyle("font-size", "smaller", true, true);
								else if (name == "big") {
									name = "span";
									break;
								}
								addStyle("font-size", "smaller", true, true);
								break;
							}

						case "big": {
								if (name == "big")
									addStyle("font-size", "larger", true, true);
								else if (name == "small") {
									name = "span";
									break;
								}
								addStyle("font-size", "larger", true, true);
								break;
							}

						case "b": {
								addStyle("font-weight", "bold", true, true);
								break;
							}

						case "i": {
								addStyle("font-style", "italic", true, true);
								break;
							}

						case "u": {
								addStyle("text-decoration", "underline", true, true);
								break;
							}

						case "s": {
								addStyle("text-decoration", "line-through", true, true);
								break;
							}
					}
				}

				parsedString = childMach.Groups[1].Value + child.parsedString + childMach.Groups[3].Value;
				if (child.style != null) {
					foreach (StyleComponent styleC in child.style.styleList)
						addStyle(styleC.name, styleC.value, true, true);
				}
			}
		}

		public bool hasAttribute(string name) {
			return attributes.hasAttribute(name);
		}

		public string getAttributeValue(string name) {
			return attributes.getAttributeValue(name);
		}

		public bool hasStyle(string name) {
			if (isWiki)
				return false;
			return attributes.hasStyle(name);
		}

		public string getStyleValue(string name) {
			return attributes.getStyleValue(name);
		}

		public void addStyle(string name, string value, bool @override, bool atTheEnd) {
			if (isWiki)
				return;
			attributes.addStyle(name, value, @override, atTheEnd);
		}
		public void addStyle(string name, string value, bool @override) {
			addStyle(name, value, @override, false);
		}
		public void addStyle(string name, string value) {
			addStyle(name, value, false, false);
		}

		public void removeStyle(string name) {
			attributes.removeStyle(name);
		}

		public bool hasClass(string name) {
			return attributes.hasClass(name);
		}

		public void addClass(string value, bool insert) {
			if (isWiki)
				return;
			attributes.addClass(value, insert);
		}
		public void addClass(string value) {
			addClass(value, true);
		}

		public void removeClass(string name) {
			attributes.removeClass(name);
		}
	}

	class HtmlAttributes : WikitextClass {
		public HtmlElement element;
		public List<AttributeClass> attributeList = new List<AttributeClass>();
		public string attributeEndString;
		public AttributeClass classes;
		public StyleAttribute style;
		protected List<string> validAttribues = new List<string>(new string[] { "class", "style", "id", "title", "lang", "dir", "hidden", "accesskey", "tabindex", "align", "valign", "bgcolor", "width" });

		public HtmlAttributes(string s, HtmlElement p)
			: base("attribute", s, p) {
			p.subWikitextList.Add(this);
			element = p;
			element.attributes = this;

			switch (element.name) {
				case "gallery": {
						validAttribues = new List<string>(new string[] { "mode", "caption", "widths", "heights", "perrow", "showfilename" });
						break;
					}
				case "ol": {
						validAttribues.AddRange(new string[] { "start", "reversed", "type" });
						break;
					}
				case "ref": {
						validAttribues = new List<string>(new string[] { "name", "group", "refines" });
						break;
					}
				case "references": {
						validAttribues = new List<string>(new string[] { "name", "group", "responsive" });
						break;
					}
				case "table": {
						validAttribues.AddRange(new string[] { "border", "cellpadding", "cellspacing", "frame", "rules", "sortable", "summary" });
						break;
					}
				case "td":
				case "th": {
						validAttribues.AddRange(new string[] { "colspan", "rowspan", "scope", "height", "headers", "nowrap" });
						break;
					}
			}

			parseAttributes();
		}

		private void parseAttributes() {
			// gestion d'un erreur particulière trouvé sur certaines page, difficile à parser correctement
			if (Regex.IsMatch(parsedString, "style ?= ?\"? ?bgcolor"))
				parsedString = Regex.Replace(parsedString, @"style *= *""? ?bgcolor ?= ?""?(#?\w+)""?;?", "style=\"background:$1;");

			// parse standard
			Regex startEnd = new Regex(@"\s*\|?$");
			attributeEndString = startEnd.Match(parsedString).Value;
			Regex attrRegex = new Regex(@"(?xni) \G (?<before> \s* )("
				+ @"  ( (?<name> [a-z][\w-]* )(?<sep> \s* = \s* )(?<value> ""[^""]*(""|$) (?<! ="") | '[^']*('|$) (?<! =') | \S* ) )"
				+ @"| (?<name> \S+ )"
				+ ")");
			foreach (Match m in attrRegex.Matches(startEnd.Replace(parsedString, ""))) {
				if (m.Groups["name"].Value == "style") {
					style = new StyleAttribute(m.Groups["value"].Value, m.Groups["sep"].Value, m.Groups["before"].Value);
					attributeList.Add(style);
				} else {
					AttributeClass attr = new AttributeClass(m.Groups["name"].Value, m.Groups["value"].Value, m.Groups["sep"].Value, m.Groups["before"].Value);
					attributeList.Add(attr);
					if (m.Groups["name"].Value == "class")
						classes = attr;
				}
			}
		}

		public bool hasAttribute(string name) {
			foreach (AttributeClass attribute in attributeList) {
				if (attribute.name == name)
					return true;
			}
			return false;
		}

		public string getAttributeValue(string name) {
			foreach (AttributeClass attribute in attributeList) {
				if (attribute.name == name)
					return attribute.value;
			}
			return "";
		}

		public bool hasStyle(string name) {
			return style != null && style.hasStyle(name);
		}

		public string getStyleValue(string name) {
			if (style == null)
				return "";
			return style.getStyleValue(name);
		}

		public void addStyle(string name, string value, bool @override, bool atTheEnd) {
			value = value.Replace("=", ":").TrimEnd();
			if (style == null) {
				style = new StyleAttribute(name + ":" + value + ";");
				style.hasQuotes = true;
				attributeList.Add(style);
				if (attributeEndString == "|")
					attributeEndString = " |";
			} else
				style.addStyle(name, value, @override, atTheEnd);
		}
		public void addStyle(string name, string newvalue) {
			addStyle(name, newvalue, false, false);
		}

		public void removeStyle(string name) {
			if (hasStyle(name))
				style.removeStyle(name);
		}

		public bool hasClass(string name) {
			return classes != null && Regex.IsMatch(classes.value, @"\b" + Regex.Escape(name) + @"\b");
		}

		public void addClass(string value, bool insert) {
			if (classes == null) {
				classes = new AttributeClass("class", value);
				classes.hasQuotes = true;
				if (insert) {
					attributeList.Insert(0, classes);
					if (attributeList.Count > 1 && attributeList[1].before == "")
						attributeList[1].before = " ";
				}
			} else if (!Regex.IsMatch(classes.value, @"\b" + Regex.Escape(value) + @"\b"))
				classes.value = classes.value + " " + value;
		}

		public void removeClass(string name) {
			if (hasClass(name)) {
				classes.value = Regex.Replace(classes.value, @"\b" + Regex.Escape(name) + @"\b", "").Trim();
				if (!Regex.IsMatch(classes.value, @"\S")) {
					attributeList.Remove(classes);
					classes = null;
				}
			}
		}

		public override void fix() {
			fixDoublon();
			base.fix();
			fixAttributes();
			if (style != null)
				style.fix(element);
		}

		public void fixDoublon() {
			List<string> listSingleAttributes = new List<string>();
			for (int i = attributeList.Count - 1; i >= 0; i += -1) {
				if (listSingleAttributes.Contains(attributeList[i].name))
					attributeList.RemoveAt(i);
				else
					listSingleAttributes.Add(attributeList[i].name);
			}
		}

		public void fixAttributes() {
			bool removeAttr;
			for (int i = attributeList.Count - 1; i >= 0; i += -1) {
				removeAttr = true;
				AttributeClass attribute = attributeList[i];
				string value = attribute.value;
				attribute.name = attribute.name.ToLower();
				switch (attribute.name) {
					case "style": {
							if (attribute.hasQuotes == false) {
								attribute.hasQuotes = true;
								attribute.value = Regex.Replace(attribute.value, "(?<!;)[; ]*$", ";");
							}
							attribute.value = attribute.value.Replace("=", ":");
							removeAttr = false;
							break;
						}

					case "align": {
							if (element.name == "table") {
								string width = getStyleValue("width");
								if (width == "")
									width = getAttributeValue("width");
								if (width == "100%")
									break;
								if (value.ToLower() == "center")
									addClass("centre", false);
								else if (value.ToLower() == "right")
									addClass("right", false);
								else if (value.ToLower() == "left")
									addStyle("float", "left");
								FlagSkip = false;
							} else if (element.isWikitable == false || !(element is TableCell) || ((TableCell)element).isHeader == false) {
								addStyle("text-align", value.ToLower());
								FlagSkip = false;
							}
							break;
						}

					case "bgcolor": {
							value = value.Replace(" ", "");
							if (Regex.IsMatch(value, @"(?i)^([\dA-F]{3}){1,2}"))
								value = "#" + value;

							if (Regex.IsMatch(value, @"(?i)^(#([\dA-F]{3}){1,2}|[a-z]+)")) {
								addStyle("background", Regex.Match(value, @"(?i)^(#([\dA-F]{3}){1,2}|[a-z]+)").Value);
								FlagSkip = false;
							}

							if (Regex.IsMatch(value, @"¤§t\d+§¤")) {
								string templateType = article.templateList[System.Convert.ToInt32(Regex.Match(value, @"¤§t(\d+)§¤").Groups[1].ToString())].name;
								if (Regex.IsMatch(templateType.ToLower(), @"^\s*(tennis couleur|fc|sport couleur)\s*$"))
									addStyle("background", value);
							}

							break;
						}

					case "border": {
							if (element.name != "table")
								break;
							string cellspacing = getAttributeValue("cellspacing");
							if (cellspacing == "")
								cellspacing = getStyleValue("border-spacing");
							cellspacing = cellspacing.Replace("px", "");

							if (isNumber(value) && value != "0" && element.name == "table") {
								if ((value == "1" && (!isNumber(cellspacing) || System.Convert.ToInt32(cellspacing) < 3)) || getStyleValue("border-collapse") == "collapse") {
									addClass("wikitable", false);
									removeStyle("border-collapse");
									removeStyle("border-spacing");
								} else if (classes == null || Regex.IsMatch(classes.value, @"\bwikitable\b"))
									removeAttr = false;
							}

							break;
						}

					case "cellpadding": {
							if (element.name != "table")
								break;
							if (value is string)
								value = Regex.Match(value, @"\d+").Value;
							if (isNumber(value) && value != "1" && value != "2") {
								if (classes == null || !Regex.IsMatch(classes.value, @"\bwikitable\b")) {
									// à faire après avoir ajouter les classes nécessaire à Common.css
									if (value == "0")
										element.tableParent.addClass("nocellpadding");
									else
										removeAttr = false;
								}
							}

							break;
						}

					case "cellspacing": {
							if (element.name != "table")
								break;
							if (value is string)
								value = Regex.Match(value, @"\d+").Value;
							if (isNumber(value) && value != "2" && value != "1" && value != "3" && !(element.isWikitable || element.tableParent.getStyleValue("border-collapse") == "collapse")) {
								if (value == "0")
									addStyle("border-spacing", value);
								else
									addStyle("border-spacing", value + "px");
							}

							break;
						}

					case "color": {
							if (element.name == "font") {
								value = value.Replace(" ", "");
								if (Regex.IsMatch(value, @"(?i)^([\dA-F]{3}){1,2}$"))
									value = "#" + value;

								if (Regex.IsMatch(value, @"(?xi)^ ( \#([\dA-F]{3}){1,2} | [a-z]+ | \{? ¤§t\d+§¤ \}? ) $"))
									addStyle("color", value);
							}

							break;
						}

					case "colspan": {
							if (attribute.value != "1")
								removeAttr = false;
							break;
						}

					case "face": {
							if (element.name == "font")
								addStyle("font-family", value);
							break;
						}

					case "frame": {
							if (element.name != "table")
								break;
							if (classes == null || !Regex.IsMatch(classes.value, @"\bwikitable\b")) {
								switch (value) {
									case "box":
									case "border": {
											addStyle("border", "1px solid grey");
											break;
										}

									case "above": {
											addStyle("border-top", "1px solid grey");
											break;
										}

									case "below": {
											addStyle("border-bottom", "1px solid grey");
											break;
										}

									case "lhs": {
											addStyle("border-left", "1px solid grey");
											break;
										}

									case "rhs": {
											addStyle("border-right", "1px solid grey");
											break;
										}

									case "hsides": {
											addStyle("border-top", "1px 0");
											addStyle("border-style", "solid");
											addStyle("border-color", "grey");
											break;
										}

									case "vsides": {
											addStyle("border-top", "0 1px");
											addStyle("border-style", "solid");
											addStyle("border-color", "grey");
											break;
										}
								}
							}

							break;
						}

					case "height": {
							if (element.name == "td" || element.name == "th") {
								if (isNumber(value))
									value = value.Trim() + "px";
								addStyle("height", value);
							}

							break;
						}

					case "nowrap": {
							addClass("nowrap", false);
							break;
						}

					case "rowspan": {
							if (attribute.value != "1")
								removeAttr = false;
							break;
						}

					case "rules": {
							if (element.name != "table")
								break;
							switch (value) {
								case "all": {
										addClass("wikitable", false);
										removeStyle("border-collapse");
										removeStyle("border-spacing");
										break;
									}

								case "rows": {
										addClass("wikitable", false);
										addClass("alternance", false);
										removeStyle("border-collapse");
										removeStyle("border-spacing");
										break;
									}

								case "cols": {
										if (classes == null || !Regex.IsMatch(classes.value, @"\bwikitable\b"))
											// à faire après avoir ajouter les classes nécessaires à Common.css
											removeAttr = false;
										break;
									}
							}

							break;
						}

					case "size": {
							if (element.name == "font") {
								switch (value) {
									case "1": {
											value = "x-small";
											break;
										}

									case "2": {
											value = "small";
											break;
										}

									case "3": {
											value = "normal";
											break;
										}

									case "4": {
											value = "large";
											break;
										}

									case "5": {
											value = "x-large";
											break;
										}

									case "6": {
											value = "xx-large";
											break;
										}

									case "+1": {
											value = "120%";
											break;
										}

									case "+2": {
											value = "140%";
											break;
										}

									case "+3": {
											value = "170%";
											break;
										}

									case "+4": {
											value = "200%";
											break;
										}

									case "+5": {
											value = "240%";
											break;
										}

									case "-1": {
											value = "83%";
											break;
										}

									case "-2": {
											value = "70%";
											break;
										}

									default: {
											value = "<!-- taille de police à vérifier-->";
											break;
										}
								}
								addStyle("font-size", value);
							}

							break;
						}

					case "sortable": {
							if (element.name == "table")
								addClass("sortable", false);
							break;
						}

					case "valign": {
							if (value != "middle" && value != "center" && Regex.IsMatch(element.name, "^t[rdh]$"))
								addStyle("vertical-align", value);
							break;
						}

					case "width": {
							if (isNumber(value))
								value = value.Trim() + "px";
							if (Regex.IsMatch(value, @"\d"))
								addStyle("width", value);
							break;
						}

					default: {
							if (validAttribues.Contains(attribute.name) || attribute.name.StartsWith("data-") || Regex.IsMatch(attribute.name, @"¤§\w+§¤"))
								removeAttr = false;
							break;
						}
				}
				if (removeAttr)
					attributeList.Remove(attribute);
			}
			if (classes != null) {
				if (!attributeList.Contains(classes))
					attributeList.Insert(0, classes);
				classes.value = Regex.Replace(classes.value, @"\bprettytable\b", "").Trim();
			}
		}

		public virtual bool fixOtherAttributes(AttributeClass attribute) {
			if (validAttribues.Contains(attribute.name) || attribute.name.StartsWith("data-") || Regex.IsMatch(attribute.name, @"¤§\w+§¤"))
				return false;
			return true;
		}

		public override string ToString() {
			StringBuilder builAttributes = new StringBuilder();
			foreach (AttributeClass attr in attributeList)
				builAttributes.Append(attr.ToString());
			if (Regex.IsMatch(builAttributes.ToString(), @"\S")) {
				builAttributes.Append(attributeEndString);
				return builAttributes.ToString();
			} else
				return "";
		}
	}

	class AttributeClass {
		public string before = " ";
		public string name;
		public string sep = "=";
		public bool hasQuotes = false;
		public string quoteType = "\"";
		protected string _value = "";
		public virtual string value {
			get {
				return _value;
			}
			set {
				_value = checquote(value);
			}
		}

		public AttributeClass(string attrName, string attrValue, string attrSep, string attrBefore) {
			before = attrBefore;
			name = attrName;
			sep = attrSep;
			_value = checquote(attrValue);
		}
		public AttributeClass(string attrName, string attrValue) {
			name = attrName;
			_value = checquote(attrValue);
		}

		public string checquote(string str) {
			if (Regex.IsMatch(str, "^[\"']")) {
				hasQuotes = true;
				quoteType = str.Substring(0, 1);
				return Regex.Match(str, "(?s)^[\"'](.*)(?<![\"'])[\"']?$").Groups[1].Value;
			} else {
				if (str.Contains(" "))
					hasQuotes = true;
				return str;
			}
		}

		public override string ToString() {
			if (hasQuotes)
				return before + name + sep + quoteType + value.Trim() + quoteType;
			else
				return before + name + sep + value;
		}
	}

	class StyleAttribute : AttributeClass {
		public List<StyleComponent> styleList = new List<StyleComponent>();
		public Dictionary<string, StyleComponent> styles = new Dictionary<string, StyleComponent>();
		public override string value {
			get {
				StringBuilder strb = new StringBuilder();
				foreach (StyleComponent style in styleList)
					strb.Append(style.ToString());
				string result = strb.ToString();
				if (result != _value)
					result = result.Trim();
				_value = result;
				return result;
			}
			set {
				base.value = value;
				if (styleList != null) {
					styleList.Clear();
					styles.Clear();
					parseStyle();
				}
			}
		}

		public StyleAttribute(string attrValue, string attrSep, string attrBefore)
			: base("style", attrValue, attrSep, attrBefore) {
			parseStyle();
		}
		public StyleAttribute(string attrValue)
			: base("style", attrValue) {
			parseStyle();
		}


		public void parseStyle() {
			MatchCollection styleMatches = Regex.Matches(_value, @"\G\s*(?:(?<name>[^:; ]+)(?<sep>:\s*)(?<value>[^;]+)|(?<alt>[^;]*))(?<after>(;|(?<=§¤))\s*|$)");
			foreach (Match m in styleMatches) {
				if (m.Groups["name"].Length > 0) {
					StyleComponent item = new StyleComponent(m.Groups["name"].Value, m.Groups["value"].Value, m.Groups["sep"].Value, m.Groups["after"].Value);
					styleList.Add(item);
					styles[item.name] = item;
				} else if (Regex.IsMatch(m.Groups["alt"].Value, @"¤§.\d+§¤")) {
					StyleComponent item = new StyleComponent(m.Groups[5].Value, "", "", m.Groups["after"].Value);
					styleList.Add(item);
					styles[item.name] = item;
				}
			}
		}

		public bool hasStyle(string name) {
			return styles.ContainsKey(name);
		}
		public string getStyleValue(string name, string defaultValue) {
			if (styles.ContainsKey(name))
				return styles[name].value;
			return defaultValue;
		}
		public string getStyleValue(string name) {
			return getStyleValue(name, "");
		}


		public void addStyle(string name, string newValue, bool @override, bool atTheEnd) {
			if (styles.ContainsKey(name)) {
				if (@override) {
					if (name == "font-size") {
						double size = fontSize(styles["font-size"].value) * fontSize(newValue);
						styles[name].value = size.ToString("F2", System.Globalization.NumberFormatInfo.InvariantInfo) + "em";
					} else
						styles[name].value = newValue;
				}
			} else {
				StyleComponent newStyle = new StyleComponent(name, newValue);
				if (atTheEnd) {
					styleList[styleList.Count - 1].after = "; ";
					styleList.Add(newStyle);
				} else
					styleList.Insert(0, newStyle);
				styles[name] = newStyle;
			}
			hasQuotes = true;
		}
		public void addStyle(string name, string newValue) {
			addStyle(name, newValue, false, false);
		}

		public double fontSize(string value) {
			if (value.EndsWith("%"))
				return double.Parse(Regex.Match(value, @"\d+").Value) / 100;
			else if (value.EndsWith("em"))
				return double.Parse(Regex.Match(value, @"[\d.]+").Value, System.Globalization.NumberFormatInfo.InvariantInfo);
			else if (value.EndsWith("px"))
				return double.Parse(Regex.Match(value, @"\d+").Value) / 14;
			else
				switch (value) {
					case "normal": {
							return 1;
						}
					case "smaller": {
							return (1 / 1.2);
						}
					case "larger": {
							return 1.2;
						}
					case "small": {
							return 1 / (1.2 * 0.875);
						}
					case "x-small": {
							return 1 / (1.44 * 0.875);
						}
					case "xx-small": {
							return 1 / (Math.Pow(1.2, 3) * 0.875);
						}
					case "large": {
							return 1.2 / 0.875;
						}
					case "x-large": {
							return 1.44 / 0.875;
						}
					case "xx-large": {
							return Math.Pow(1.2, 3) / 0.875;
						}
				}
			return 100;
		}

		public void removeStyle(string name) {
			if (styles.ContainsKey(name)) {
				styleList.Remove(styles[name]);
				styles.Remove(name);
				hasQuotes = true;
			}
		}
		public void replaceStyle(string oldName, string newName, string newValue, bool @override) {
			if (!hasStyle(oldName))
				return;
			if (hasStyle(newName)) {
				if (@override)
					removeStyle(newName);
				else {
					removeStyle(oldName);
					return;
				}
			}
			StyleComponent style = styles[oldName];
			style.name = newName;
			styles[newName] = style;
			styles.Remove(oldName);
			if (newValue.Length > 0)
				style.value = newValue;
		}
		public void replaceStyle(string oldName, string newName) {
			replaceStyle(oldName, newName, "", false);
		}


		public void fix(HtmlElement element) {
			// supprime les doublons
			for (int i = styleList.Count - 2; i >= 0; i += -1) {
				if (!styles.ContainsValue(styleList[i]))
					styleList.RemoveAt(i);
			}

			// border-radius et autre vieux préfixe
			removeStyle("-moz-border-radius");
			removeStyle("-moz-border-top-left-radius");
			removeStyle("-moz-border-top-right-radius");
			removeStyle("-moz-border-bottom-right-radius");
			removeStyle("-moz-border-bottom-left-radius");
			removeStyle("-moz-border-radius-topleft");
			removeStyle("-moz-border-radius-topright");
			removeStyle("-moz-border-radius-bottomright");
			removeStyle("-moz-border-radius-bottomleft");
			removeStyle("-o-border-radius");
			removeStyle("-khtml-border-radius");
			replaceStyle("-webkit-border-radius", "border-radius");
			replaceStyle("-webkit-border-top-left-radius", "border-top-left-radius");
			replaceStyle("-webkit-border-top-right-radius", "border-top-right-radius");
			replaceStyle("-webkit-border-bottom-right-radius", "border-bottom-right-radius");
			replaceStyle("-webkit-border-bottom-left-radius", "border-bottom-left-radius");
			if (hasStyle("border-top-left-radius")) {
				string combineRadius = getStyleValue("border-top-left-radius");
				if (!(getStyleValue("border-top-right-radius") == combineRadius && getStyleValue("border-bottom-right-radius") == combineRadius && getStyleValue("border-bottom-left-radius") == combineRadius))
					combineRadius = combineRadius + " "
	+ getStyleValue("border-top-right-radius", "0") + " "
	+ getStyleValue("border-bottom-right-radius", "0") + " "
	+ getStyleValue("border-bottom-left-radius", "0");
				replaceStyle("border-top-left-radius", "border-radius", combineRadius, false);
				removeStyle("border-top-right-radius");
				removeStyle("border-bottom-right-radius");
				removeStyle("border-bottom-left-radius");
			}
			replaceStyle("-moz-column-count", "column-count");
			replaceStyle("-webkit-column-count", "column-count");
			removeStyle("-o-column-count");
			removeStyle("-khtml-column-count");
			removeStyle("-moz-box-shadow");
			replaceStyle("-webkit-box-shadow", "box-shadow");
			replaceStyle("-moz-column-width", "column-width");
			replaceStyle("-webkit-column-width", "column-width");

			// supprime les float:center
			if (getStyleValue("float") == "center")
				removeStyle("float");

			// margin pour les cellules
			if (Regex.IsMatch(element.name, "^t[rdh]$"))
				removeStyle("margin");

			// Les régles suivante peuvent être inapropriées s'il y a une classe
			if (element.hasAttribute("class"))
				return;

			// font-size:100%
			if (getStyleValue("font-size") == "100%")
				removeStyle("font-size");

			// display block et width:100% inutiles
			string eType = element.name;
			if (element.isBloc) {
				if (getStyleValue("display") == "block")
					removeStyle("display");
				if (getStyleValue("width") == "100%")
					removeStyle("width");
			}

			// vertical-align:top
			if (!Regex.IsMatch(element.name, "^t[rdh]$") && !hasStyle("display"))
				removeStyle("vertical-align");

			// background:transparent
			if ((getStyleValue("background") == "transparent" || getStyleValue("background-color") == "transparent")) {
				removeStyle("background");
				removeStyle("background-color");
			}

			// inline elements
			if (!hasStyle("display") && element.isInline) {
				removeStyle("margin-top");
				removeStyle("margin-bottom");
				removeStyle("text-align");
				removeStyle("width");
				removeStyle("height");
				removeStyle("overflow");
			}

			// supprime les éléments vide
			List<string> toBeRemoved = new List<string>();
			foreach (StyleComponent s in styleList) {
				if (Regex.IsMatch(s.value, @"^\s*$"))
					toBeRemoved.Add(s.name);
			}
			foreach (string s in toBeRemoved)
				removeStyle(s);
		}

		public override string ToString() {
			if (value == "")
				return "";
			return base.ToString();
		}
	}

	class StyleComponent {
		public string name = "";
		public string sep = ":";
		public string value = "";
		public string after = "; ";

		public StyleComponent() {
		}

		public StyleComponent(string nname, string nvalue, string nsep, string nafter) {
			name = nname;
			value = nvalue;
			sep = nsep;
			after = nafter;
		}
		public StyleComponent(string nname, string nvalue) {
			name = nname;
			value = nvalue;
		}

		public override string ToString() {
			if (name.Length > 0)
				return name + sep + value + after;
			return "";
		}
	}

	class HtmlTag : HtmlElement {
		public bool selfClosing = false;
		public bool closingTag = false;
		public HtmlTag(string nname, string content, WikitextClass nparent)
			: base(nname.ToLower(), content, nparent) {
			parseTag();
		}

		public void parseTag() {
			Regex tagRegex = new Regex("(?xni) ^<" + name
				+ "(?<attributes> [^>]* (?<! / ) )"
				+ "(?<selfclosing> /)? >"
				+ "(?<content> ( [^<]* | <)*?  )"
				+ "(?<closing> </" + name + @"\s*>)? $"
				);

			Match m = tagRegex.Match(parsedString);
			if (!m.Success)
				throw new System.Exception("HtmlTag " + name + " n'a pas pas été parsé correctement");

			attributes = new HtmlAttributes(m.Groups["attributes"].Value, this);
			if (m.Groups["selfclosing"].Value == "/")
				selfClosing = true;
			if (m.Groups["closing"].Value.Length > 0)
				closingTag = true;
			parsedString = m.Groups["content"].Value;
		}

		public override void fix() {
			// Les balises gras et italique en sytaxe wiki à l'intèrieur du tag (facilite le regroupement de balises, préférable pour les balise block)
			Match boldM = Regex.Match(parent.parsedString, "('{2,5})( *)" + strip + @"( *)\1");
			if (!isWiki && !isTableElement && article.ns != 0 && boldM.Success) {
				parsedString = boldM.Groups[1].Value + parsedString + boldM.Groups[1].Value;
				parent.parsedString = parent.parsedString.Replace(boldM.Value, boldM.Groups[2].Value + strip + boldM.Groups[3].Value);
			}

			// ferme les balises qui n'ont qui ne sont qu'ouverte
			if (closingTag == false && !selfClosing) {
				Match closeMatch = Regex.Match(parsedString, "(?si)</(div|center|p|span|small|font|big|b|u|i|s)>(.+)");
				if (closeMatch.Success) {
					// détection d'une balise fermente avec un autre nom
					parent.parsedString = parent.parsedString.Replace(strip, strip + closeMatch.Groups[2].Value);
					parsedString = parsedString.Replace(closeMatch.Value, "");
					closingTag = true;
				} else if (parsedString == "")
					parent.parsedString = parent.parsedString.Replace(strip, "");
				else if (isInline && Regex.IsMatch(parsedString, @"^[^\n]*\n\s*\z")) {
					parent.parsedString = parent.parsedString.Replace(strip, strip + Regex.Match(parsedString, @"(?<=\S)\s+\z").Value);
					parsedString = Regex.Match(parsedString, @"^.+\S").Value;
				}
			}

			base.fix();

			if (isInline)
				fixInlineWithBlock();


			if (name == "font") {
				name = "span";
				FlagSkip = false;
			}

			if (name == "center") {
				foreach (Match m in Regex.Matches(parsedString, @"¤§a(\d+)§¤")) {
					TableClass t = (TableClass)article.htmlElementList[System.Convert.ToInt32(m.Groups[1].Value)];
					if (t.hasStyle("margin")) {
						MatchCollection marginMatch = Regex.Matches(t.getStyleValue("margin"), @"\S+");
						switch (marginMatch.Count) {
							case 1:
							case 2: {
									t.addStyle("margin", marginMatch[0].Value + " auto", true);
									break;
								}

							case 3:
							case 4: {
									t.addStyle("margin", marginMatch[0].Value + " auto " + marginMatch[2].Value, true);
									break;
								}

							default: {
									t.addStyle("margin-left", "auto", true);
									t.addStyle("margin-right", "auto", true);
									break;
								}
						}
					} else
						t.addClass("centre");
				}
				if (Regex.IsMatch(parsedString, @"^\s*(¤§a\d+§¤\s*)+$"))
					parent.parsedString = Regex.Replace(parent.parsedString, @"(?:(?<=\n)\n)? *" + strip, parsedString.TrimEnd());
				else if (hasAttribute("style") || hasAttribute("class") || hasAttribute("title")) {
					addStyle("text-align", "center");
					name = "div";
				} else if (parent is TableCell && Regex.IsMatch(parent.parsedString, @"^\s*" + strip + @"\s*$")) {
					TableCell cell = (TableCell)parent;
					cell.parsedString = Regex.Replace(parent.parsedString, strip, parsedString.Trim());
					cell.addStyle("text-align", "center");
				} else if (article.ns == 0) {
					Match m = Regex.Match(parsedString, @"(?s)^\s*(<gallery.+</gallery>)\s*$");
					if (m.Success)
						parent.parsedString = parent.parsedString.Replace(strip, m.Groups[1].Value);
					else {
						string @params = "|" + parsedString;
						if (wikitextToString(parsedString).Contains("="))
							@params = "|1 = " + parsedString;
						TemplateClass centreTempl = new TemplateClass("centrer", @params, parent, new string[] { "Centrer" });
						parent.subWikitextList.Add(centreTempl);
						centreTempl.strip = "¤§t" + article.templateList.IndexOf(centreTempl).ToString() + "§¤";
						parent.parsedString = parent.parsedString.Replace(strip, centreTempl.strip);
						if (!article.modifiedhtmlTag.Contains(strip))
							article.modifiedhtmlTag.Add(strip);
					}
				} else {
					addClass("center");
					name = "div";
				}
				FlagSkip = false;
			}

			if (name == "span" && attributes.ToString() == "")
				parent.parsedString = parent.parsedString.Replace(strip, parsedString);

			// évite de terminer un balise dans une liste, une définition...
			if (!isWiki && Regex.IsMatch(parsedString, @"\n[:;*#][^\n]+\z"))
				parsedString = parsedString + "\n";

			if (selfClosing == false && closingTag == false)
				closingTag = true;
		}

		public void fixInlineWithBlock() {
			if (Regex.IsMatch(parsedString.ToString(), "(?i)<(div|p|center) .*?>") || Regex.IsMatch(parsedString, @"\n( *\n|[*#:;]|\s*¤§a)")) {
				isBloc = true;
				isInline = false;
				switch (name) {
					case "small": {
							if (!hasStyle("font-size"))
								addStyle("font-size", "smaller");
							break;
						}

					case "big": {
							if (!hasStyle("font-size"))
								addStyle("font-size", "larger");
							break;
						}

					case "b": {
							addStyle("font-weight", "bold");
							break;
						}

					case "i": {
							addStyle("font-style", "italic");
							break;
						}

					case "u": {
							addStyle("text-decoration", "underline");
							break;
						}

					case "s": {
							addStyle("text-decoration", "line-through");
							break;
						}
				}
				name = "div";
				if (Regex.IsMatch(parsedString, @"(?m)^[*#].+\z"))
					parsedString = parsedString + "\n";
				if (Regex.IsMatch(parent.parsedString, "(?m)^[*#:;] *" + strip)) {
					parsedString = "\n" + Regex.Match(parent.parsedString, "(?m)^[*#:;] *(?=" + strip + ")").Value + parsedString;
					parent.parsedString = Regex.Replace(parent.parsedString, "(?m)^[*#] *" + strip, strip);
				}
			}
		}

		public override string ToString() {
			string tagInit = "<" + name + attributes.ToString();
			if (selfClosing)
				tagInit = tagInit + "/";
			tagInit = tagInit + ">";

			string tagEnd = "";
			if (closingTag)
				tagEnd = "</" + name + ">";

			// ajout de la modification de l'élément dans le résumé de modif
			bool modified = !(initialString.StartsWith(tagInit) && initialString.EndsWith(tagEnd));
			if (modified && !article.modifiedhtmlTag.Contains(strip))
				article.modifiedhtmlTag.Add(strip);

			return tagInit + wikitextToString(parsedString) + tagEnd;
		}
	}

	// ---
	// - Class Table
	// ---

	class TableClass : HtmlElement {
		public List<TableRow> rows = new List<TableRow>();
		public TableCell caption;
		public bool wasWikitable = false;

		public TableClass(string tableContent, WikitextClass nparent)
			: base("table", tableContent, nparent) {
		}

		public void parseTable() {
			Regex attributesRegex = new Regex(@"^{\|(.*(\n[ \t]*(?=\n))*)");
			Regex captionRegex = new Regex("(?xns) ^ ( "
					+ @"(?<init> (\n [ \t]* \|\-)? \n [ \t]* \|\+ )"
					+ @"(?<captionContent> .*? (?= \n [ \t]* \| | \n [ \t]* ! | $ ) )"
					+ ")? (?<rows> .+ $ ) ");
			Regex rowRegex = new Regex(@"(?xn) \G"
				+ @"(?<init> \n [ \t]* \|\-+ [ ]* | ^ )"
				+ @"(?<rowContent> (?> (?<tab>\n [ \t]* \{\| .* ) | (?<-tab>\n [ \t]* \|\} ) | .+ | \n )*? (?(tab)(?!)) (?= \n [ \t]* \|\- | \n [ \t]* \|\} ) )");

			attributes = new HtmlAttributes(attributesRegex.Match(parsedString).Groups[1].Value, this);

			Match captionMatch = captionRegex.Match(attributesRegex.Replace(parsedString, ""));
			if (captionMatch.Groups["captionContent"].Value.Trim() != "") {
				caption = new TableCell(captionMatch.Groups["init"].Value, captionMatch.Groups["captionContent"].Value, this);
				subWikitextList.Add(caption);
			}

			foreach (Match m in rowRegex.Matches(captionMatch.Groups["rows"].Value)) {
				TableRow row = new TableRow(m.Groups["init"].Value, m.Groups["rowContent"].Value, this);
				if (row.cells.Count > 0 || row.parsedString != "") {
					rows.Add(row);
					subWikitextList.Add(row);
				}
			}
			if (classes != null && Regex.IsMatch(classes.value, @"\bwikitable\b")) {
				isWikitable = true;
				wasWikitable = true;
			}
		}

		public override void fix() {
			// fixCenter()

			// On applique attributes.fixAttributes deux fois (la deuxième fois dans MyBase.fix) pour supprimer les attributs qui n'ont plus lieu d'être car class=wikitable a été appliqué après leur évaluation
			attributes.fixAttributes();

			base.fix();

			// fixCaption()
			fixFirstRowHeader();
			fixStyle();
		}

		public void fixStyle() {
			int nbCell = 0;
			int nbCenter = 0;
			int nbRight = 0;
			int nbRowCenter = 0;
			int nbRowRight = 0;

			// retire les style qui n'ont aucun effet sur une table
			removeStyle("vertical-align");
			if (!hasAttribute("class") && (getStyleValue("background") == "transparent" || getStyleValue("background-color") == "transparent")) {
				removeStyle("background");
				removeStyle("background-color");
			}

			// suppression des styles redondants
			string tableAlign = getStyleValue("text-align");
			string rowAlign;
			string cellAlign;
			foreach (TableRow row in rows) {
				rowAlign = row.getStyleValue("text-align");
				// décompte de l'alignement des lignes
				switch (rowAlign) {
					case "center": {
							nbRowCenter = nbRowCenter + 1;
							break;
						}

					case "right": {
							nbRowRight = nbRowRight + 1;
							break;
						}
				}
				// retire le style si c'est le même que la table
				if (rowAlign == tableAlign)
					row.removeStyle("text-align");
				else if (tableAlign == "" && rowAlign == "left") {
					row.removeStyle("text-align");
					rowAlign = "";
				} else if (rowAlign == "")
					rowAlign = tableAlign;
				foreach (TableCell cell in row.cells) {
					cellAlign = cell.getStyleValue("text-align");
					// décompte de l'alignement des cellules
					if (!cell.isEmpty && (!cell.isHeader || cell.isScopeRow)) {
						nbCell = nbCell + 1;
						switch (cellAlign + rowAlign) {
							case "center": {
									nbCenter = nbCenter + 1;
									break;
								}

							case "right": {
									nbRight = nbRight + 1;
									break;
								}
						}
					}
					// gestion des wikitable th, dont l'alignemetn est imposé par css
					if (isWikitable && cell.isHeader) {
						if (cell.isScopeRow && cellAlign == "left" || !cell.isScopeRow && cellAlign == "center")
							cell.removeStyle("text-align");
					} else if (rowAlign == cellAlign || rowAlign == "" && cellAlign == "left")
						// retrait du style s'il est identique à celui de la ligne ou la table
						cell.removeStyle("text-align");
				}
			}

			// changement d'alignement par défaut si plus de la moitié des céllules à le même alignement différent.
			string changeto = "";
			if (tableAlign == "") {
				if (nbRowCenter >= Math.Max(rows.Count - 2, 4))
					changeto = "center";
				else if (nbRowRight >= Math.Max(rows.Count - 2, 4))
					changeto = "right";
				if (changeto != "") {
					addStyle("text-align", changeto, true);
					foreach (TableRow row in rows) {
						rowAlign = row.getStyleValue("text-align");
						if (rowAlign == "") {
							if (row.cells.Count > 1 && !row.cells[1].isHeader)
								row.addStyle("text-align", "left");
						} else if (row.getStyleValue("text-align") == changeto)
							row.removeStyle("text-align");
					}
				} else {
					if (nbCenter > (nbCell * 0.55 + 1))
						changeto = "center";
					else if (nbRight > (nbCell * 0.55 + 1))
						changeto = "right";
					if (changeto != "") {
						addStyle("text-align", changeto, true);
						foreach (TableRow row in rows) {
							rowAlign = row.getStyleValue("text-align");
							if (rowAlign == changeto)
								row.removeStyle("text-align");
							if (rowAlign == "") {
								foreach (TableCell cell in row.cells) {
									cellAlign = cell.getStyleValue("text-align");
									if (cellAlign == "") {
										if (cell.isHeader && !cell.isScopeRow) {
											if (changeto == "right" && !cell.isEmpty && !isWikitable)
												cell.addStyle("text-align", "center");
										} else if (!cell.isEmpty)
											cell.addStyle("text-align", "left");
									} else if (cellAlign == changeto
																				  && (!cell.isHeader || changeto == "center"))
										cell.removeStyle("text-align");
								}
							}
						}
					}
				}
			}
		}

		public void fixCaption() {
			if (rows.Count < 3 || hasClass("sortable"))
				return;
			if (caption == null && rows[0].cells.Count == 1 && rows[1].cells.Count > 1 && !rows[0].cells[0].hasAttribute("rowspan")) {
				caption = new TableCell("\n|+ ", rows[0].cells[0].parsedString.Trim(), this);
				subWikitextList.Add(caption);
				rows.RemoveAt(0);
			}
		}

		public void fixCenter() {
			// table juste entouré de center
			if (Regex.IsMatch(parent.parsedString, @"<center>\s*\n" + strip + @"\s*</center>")) {
				addClass("centre");
				parent.parsedString = Regex.Replace(parent.parsedString, @"(?:(?<=\n)\n)?<center>\s*\n" + strip + @"\s*</center>", "\n" + strip);
			}

			// table entouré de center avec une note avant la fermeture de la balise
			if (Regex.IsMatch(parent.parsedString, @"<center>\s*\n" + strip + @"\s*(([^¤=]|(?<!=)=(?!=))+?)</center>")) {
				addClass("centre");
				parent.parsedString = Regex.Replace(parent.parsedString, @"(?:(?<=\n)\n)?<center>\s*\n" + strip + @"\s*(([^¤=]|(?<!=)=(?!=))+?)</center>", "\n" + strip + "\n<div class=\"center\">$1</div>"
				);
			}
		}

		public void fixFirstRowHeader() {
			// essaye de déterminer si la première et la seconde ligne sont des lignes de titre
			if (!isWikitable || rows.Count < 3 || rows[0].cells.Count < 2 || rows[1].cells.Count < 2)
				return;
			string color = rows[0].getStyleValue("background") + rows[0].getStyleValue("background-color");
			if (color == "") {
				foreach (TableCell cell in rows[0].cells) {
					if (cell.isHeader)
						return;
					color = cell.getStyleValue("background") + cell.getStyleValue("background-color");
					if (color != "" || Regex.IsMatch(cell.parsedString, @"\S"))
						break;
				}
			}
			if (testRowHeader(rows[0], color))
				testRowHeader(rows[1], color);
		}

		public bool testRowHeader(TableRow row, string baseColor) {
			string rowColor = row.getStyleValue("background") + row.getStyleValue("background-color");
			foreach (TableCell cell in row.cells) {
				string cellColor = rowColor;
				if (cellColor == "")
					cellColor = cell.getStyleValue("background") + cell.getStyleValue("background-color");
				if (cell.isHeader || !Regex.IsMatch(cell.parsedString, @"^\s*(&nbsp;\s*)?$") && (cellColor != baseColor || baseColor == "" && !cell.parsedString.Contains("'''")))
					return false;
			}
			// Si on est pas sorti de la fonction, la ligne est une ligne de titre
			foreach (TableCell cell in row.cells) {
				cell.isHeader = true;
				cell.attributes.attributeList.Insert(0, new AttributeClass("scope", "col"));
				if (cell.attributes.attributeList.Count > 1)
					cell.attributes.attributeList[1].before = " ";
				if (cell.parsedString.Contains("'''"))
					cell.parsedString = Regex.Replace(cell.parsedString, "'''", "");
				else if (!Regex.IsMatch(cell.parsedString, @"^\s*(&nbsp;\s*)?$"))
					cell.addStyle("font-weight", "normal");
				if (isWikitable && !Regex.IsMatch(baseColor, @"(?i)#(e[\da-f]?)\1\1|lightgr[ae]y")) {
					cell.removeStyle("background");
					cell.removeStyle("background-color");
				}
			}
			if (baseColor != "" && (!isWikitable || !Regex.IsMatch(baseColor, @"(?i)#(e[\da-f]?)\1\1|lightgr[ae]y"))) {
				row.removeStyle("background-color");
				row.addStyle("background", baseColor);
				if (isWikitable)
					tableParent.addClass("titre-en-couleur");
			}
			return true;
		}

		public override string ToString() {
			StringBuilder builTable = new StringBuilder();
			builTable.Append("{|");
			builTable.Append(attributes.ToString());
			if (caption != null)
				builTable.Append(caption.ToString());
			foreach (TableRow row in rows)
				builTable.Append(row.ToString());
			builTable.Append("\n|}");
			string result = builTable.ToString();

			// ajout de la modification de table dans le résumé de modif
			if (result != wikitextToString(initialString) && !article.modifiedTables.Contains(strip))
				article.modifiedTables.Add(strip);

			// suppression dernière ligne vide
			result = Regex.Replace(result, @"\n\|-\s*\n\|\}", "\n|}");

			return result;
		}
	}

	class TableRow : HtmlElement {
		public List<TableCell> cells = new List<TableCell>();
		public string init;

		public TableRow(string initString, string rowContent, WikitextClass nparent)
			: base("tr", rowContent, nparent) {
			init = initString;
			parseRow();
		}

		private void parseRow() {
			Regex attributesRegex = new Regex(@"^(.*(\n[ \t]*(?=\n))*)");
			Regex cellRegex = new Regex(@"(?xn) \G"
				+ @"(?<init> \n [ \t]* \| | \|\| | \n [ \t]* ! | !! | \n ¤§t\d+§¤ | (?= \n \{\|) )"
				+ "(?<cellContent> "
				+ @"(?> (?<tab>\n[ \t]*\{\| .* ) | (?<-tab>\n[ \t]*\|\} ) | \[\[ [^\[\]|]+ (\| ([^\[\]]+|(?<cro>\[\[)|(?<-cro>\]\])|[\[\]])*? (?(cro)(?!)))? \]\]  | [^|!\n\[]+ | [|!\n\[] )*?"
				+ @"(?(tab)(?!))  (?= \n[ \t]*\| | \|\| | \n[ \t]*\! | !! | \z ) )");

			attributes = new HtmlAttributes(attributesRegex.Match(parsedString).Groups[1].Value, this);
			parsedString = Regex.Replace(parsedString, @"\A" + Regex.Escape(attributesRegex.Match(parsedString).Value), "");

			foreach (Match m in cellRegex.Matches(parsedString)) {
				TableCell cell = new TableCell(m.Groups["init"].Value, m.Groups["cellContent"].Value, this);
				cells.Add(cell);
				subWikitextList.Add(cell);
				parsedString = Regex.Replace(parsedString, @"\A" + Regex.Escape(m.Value), "");
			}
			if (cells.Count > 1 && cells[0].isHeader) {
				for (int i = 1; i <= cells.Count - 1; i++) {
					if (cells[i].init.Contains("||"))
						cells[i].isHeader = true;
					else
						break;
				}
			}
		}

		public override void fix() {
			base.fix();

			// suppression des styles redondants
			removeDuplicateStyles("text-align", false);
			removeDuplicateStyles("vertical-align", true);

			// retire la couleur si ligne de titre et wikitable
			if (isWikitable && hasStyle("background") || hasStyle("background-color")) {
				bool headerline = true;
				foreach (TableCell cell in cells) {
					if (!cell.isHeader) {
						headerline = false;
						break;
					}
				}
				if (headerline) {
					if (((TableClass)tableParent).wasWikitable) {
						removeStyle("background");
						removeStyle("background-color");
					} else
						tableParent.addClass("titre-en-couleur");
				}
			}

			// ajout de scope=row dans quelque cas
			// Dim tableP As TableClass = DirectCast(tableParent, TableClass)
			// If tableP.rows.IndexOf(Me) > 0 _
			// And cells.Count > 1 _
			// AndAlso (cells(0).isHeader And cells(0).getStyleValue("text-align") = "left") _
			// AndAlso Not tableP.rows(tableP.rows.IndexOf(Me) - 1).cells(0).isHeader _
			// Then
			// cells(0).attributes.attributeList.Add(New AttributeClass("scope", "row", quotes:=True))
			// If Not hasStyle("text-align") And (isWikitable Or Not tableP.hasStyle("text-align")) Then
			// cells(0).removeStyle("text-align")
			// End If
			// End If

			// style="vertical-align:top;" si toutes les cellules l'ont
			int valignTop = 0;
			foreach (TableCell cell in cells) {
				if (cell.getStyleValue("vertical-align") == "top")
					valignTop = valignTop + 1;
				else if (!cell.isEmpty) {
					valignTop = 0;
					break;
				}
			}
			if (valignTop == 2 && hasAttribute("style") || valignTop > 2) {
				addStyle("vertical-align", "top", true);
				foreach (TableCell cell in cells)
					cell.removeStyle("vertical-align");
			} else if (cells.Count == 1)
				cells[0].removeStyle("vertical-align");

			// retire les class spécifiques à de.wiki
			removeClass("hintergrundfarbe1");
			removeClass("hintergrundfarbe5");
		}


		public void removeDuplicateStyles(string style, bool includeHeader) {
			if (cells.Count > (2 + System.Convert.ToInt32(includeHeader))) {
				string rowStyle = getStyleValue(style);
				// Dim temp As String
				if (rowStyle == "") {
				} else
					foreach (TableCell cell in cells) {
						if (cell.getStyleValue(style) == rowStyle && (includeHeader || !cell.isHeader))
							cell.removeStyle(style);
					}
			}
		}

		public override string ToString() {
			string attributeString = attributes.ToString();
			if (init == "" && attributeString != "")
				init = "\n|-";
			else if (init.EndsWith(" ") && attributeString.StartsWith(" "))
				attributeString = attributeString.Trim();

			StringBuilder builRow = new StringBuilder();
			builRow.Append(init);
			builRow.Append(attributeString);
			foreach (TableCell cell in cells)
				builRow.Append(cell.ToString());
			builRow.Append(wikitextToString(parsedString));

			return builRow.ToString();
		}
	}

	class TableCell : HtmlElement {
		public string init;
		public bool _isHeader = false;
		public bool isHeader {
			get {
				return _isHeader;
			}
			set {
				_isHeader = value;
				init = Regex.Replace(init, @"(\n\s*)\|", "$1!");
			}
		}

		public bool isScopeRow = false;
		public bool isCaption {
			get {
				return init.Contains("|+");
			}
			set {
				if (value == true && !init.Contains("|+"))
					init = "\n|+ ";
			}
		}
		public bool isEmpty = false;

		public TableCell(string initString, string cellContent, HtmlElement nparent)
			: base("td", cellContent, nparent) {
			init = initString;
			if (initString.Contains("!")) {
				_isHeader = true;
				name = "th";
			}
			if (isCaption)
				name = "caption";
			parseCell();
			checkIfEmpty();
		}

		private void parseCell() {
			Regex attributesRegex = new Regex(@"(?xs) ^ ( (?> \[\[ [^\[\]|]+ (\| ((?<cro>\[\[)|(?<-cro>\]\])|.*?)(?(cro)(?!)))? \]\] | [^\n\|] )* \| )");
			if (attributesRegex.IsMatch(parsedString)) {
				attributes = new HtmlAttributes(attributesRegex.Match(parsedString).Groups[1].Value, this);
				parsedString = attributesRegex.Replace(parsedString, "");
			} else
				attributes = new HtmlAttributes(" |", this);
			if (isHeader) {
				foreach (AttributeClass attr in attributes.attributeList) {
					if (attr.name == "scope") {
						if (attr.value == "row")
							isScopeRow = true;
						break;
					}
				}
			}
			foreach (Match m in Regex.Matches(parsedString, @"¤§t(\d+)§¤"))
				article.templateList[System.Convert.ToInt32(m.Groups[1].Value)].parent = this;
			parse(2);
		}

		public void checkIfEmpty() {
			if (Regex.IsMatch(parsedString, @"^\s*(&nbsp;\s*)?$"))
				isEmpty = true;
			else if (Regex.IsMatch(parsedString, @"^\s*''+\s*$")) {
				parsedString = "";
				isEmpty = true;
			}
		}

		public override void fix() {
			// ajoute un vrai début de cellule s'il n'y en a pas (cas nouvelle ligne suivi par un tableau)
			if (init == "" && !isEmpty)
				init = "\n|";

			// supprime bgcolor pour les headers de wikitable car il sont déjà ignorés.
			if (isHeader && ((TableClass)tableParent).wasWikitable) {
				foreach (AttributeClass attr in attributes.attributeList) {
					if (attr.name == "bgcolor") {
						attributes.attributeList.Remove(attr);
						break;
					}
				}
			}

			base.fix();

			// supprime la couleur si elle est proche de la couleur par défaut
			if (isHeader && isWikitable) {
				string color = getStyleValue("background") + getStyleValue("background-color");
				if (Regex.IsMatch(color, @"(?i)#(e[\da-f]?)\1\1|lightgr[ae]y")) {
					removeStyle("background-color");
					removeStyle("background");
				}
			}

			// suppression des balises <center>
			if (Regex.IsMatch(parsedString, @"^\s*('''?)?<center>")) {
				parsedString = Regex.Replace(parsedString, "</?center>", "");
				if (Regex.IsMatch(parsedString, @"\S"))
					addStyle("text-align", "center");
			}

			// ajout des ''' manquant
			if (Regex.IsMatch(parsedString, @"^\s*('''?)(.(?!\1))+?$"))
				parsedString = parsedString + Regex.Match(parsedString, "('''?)").Value;

			// ajout des </small> manquant
			if (Regex.IsMatch(parsedString, @"^\s*<small>(.(?!</small>))+?$"))
				parsedString = parsedString + "</small>";

			// suppression de |- avant le titre du tableau
			if (isCaption)
				init = Regex.Replace(init, @"\|-\s*", "");
		}

		public override string ToString() {
			string attributeString = attributes.ToString();
			if (attributeString == "" && Regex.IsMatch(parsedString, "^[-+}]"))
				parsedString = " " + parsedString;

			return wikitextToString(init + attributeString + parsedString);
		}
	}


	// ---
	// - dialog classes
	// ---
	class dialog : System.Windows.Forms.Form {
		public dialog(string titre, int width, int height) {
			Text = titre;
			Size = new System.Drawing.Size(width, height);
			FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
			StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
		}
	}

	class Button : System.Windows.Forms.Button {
		public Button(string buttonText, int x, int y, int returnValue) {
			Text = buttonText;
			Location = new System.Drawing.Point(x, y);
			DialogResult = (System.Windows.Forms.DialogResult)returnValue;
		}
	}

	class RichLabel : System.Windows.Forms.RichTextBox {
		public RichLabel() {
			BorderStyle = System.Windows.Forms.BorderStyle.None;
		}
	}

	// ---
	// - Templates definitions
	// ---

	abstract class idTemplate : TemplateClass {
		public abstract string id { get; }
		public abstract string id2 { get; }
		public abstract void changeId(string newId);

		public idTemplate(string name, string contenuModele, WikitextClass parent, string[] ntemplateNames)
			: base(name, contenuModele, parent, ntemplateNames) {
		}
		public idTemplate(TemplateClass template, string[] ntemplateNames)
			: base(template, ntemplateNames) {
		}

		public bool hasId(string idToCheck) {
			return (idToCheck == id) || (idToCheck == id2);
		}
	}

	abstract class Biblio : idTemplate {
		public override string id {
			get {
				return idTab[0];
			}
		}
		public override string id2 {
			get {
				return id2Tab[0];
			}
		}

		protected string[] idTab = new string[7];
		protected string[] id2Tab = new string[7];
		protected bool supprAuteur = false;
		protected int maxAuteurs = 70;
		private static List<string> _listPrenom;
		private static List<string> _listMiddle;
		private static List<string> _listNom;
		private static List<string> _listPostnom;
		private static List<string> _listResp;
		private static List<string> _listOrg;
		private static List<string> _listMotsOrg;
		private static List<string> _listInconnu;

		public Biblio(string name, string contenuModele, WikitextClass parent, string[] ntemplateNames)
			: base(name, contenuModele, parent, ntemplateNames) {
			addStandardParametres();
			loadListNomPrenom(false);
		}
		public Biblio(TemplateClass template, string[] ntemplateNames)
			: base(template, ntemplateNames) {
			addStandardParametres();
			loadListNomPrenom(false);
		}

		protected virtual void addStandardParametres() {
			isLowerOnly = true;
			flagRemoveEmptyKnownParam = true;
			falgRemoveDoubloonsEvenIfDifferent = true;

			tmplParams.add("langue", new parametreStandard("lang", "d"), new parametreStandard("lien langue", "d"), new parametreStandard("language", "o"));

			tmplParams.add("nom1", "nom", new parametreStandard("last", "o"), new parametreStandard("last1", "o"));
			tmplParams.add("prénom1", new parametreStandard("prénom"), new parametreStandard("first", "o"), new parametreStandard("first1", "o"));
			tmplParams.add("postnom1");
			tmplParams.add("auteur1", "auteur", new parametreStandard("author1", "o"), new parametreStandard("author", "o"));
			tmplParams.add("lien auteur1", "lien auteur", new parametreStandard("authorlink", "o"), new parametreStandard("author-link", "o"), new parametreStandard("authorlink1", "o"), new parametreStandard("author1-link", "o"), new parametreStandard("enllaçautor", "o"));

			tmplParams.add("directeur1", "directeur", new parametreStandard("direction1", "o"));
			tmplParams.add("responsabilité1");

			for (int i = 2; i <= maxAuteurs; i++) {
				tmplParams.add("nom" + i.ToString(), new parametreStandard("last" + i.ToString(), "o"));
				tmplParams.add("prénom" + i.ToString(), new parametreStandard("first" + i.ToString(), "o"));
				tmplParams.add("postnom" + i.ToString());
				tmplParams.add("auteur" + i.ToString(), new parametreStandard("author" + i.ToString(), "o"));
				tmplParams.add("lien auteur" + i.ToString(), new parametreStandard("authorlink" + i.ToString(), "o"), new parametreStandard("author" + i.ToString() + "-link", "o"));
				tmplParams.add("directeur" + i.ToString(), new parametreStandard("direction" + i.ToString(), "o"));
				tmplParams.add("responsabilité" + i.ToString());
			}

			tmplParams.add("auteurs", new parametreStandard("authors", "o"));
			tmplParams.add("et al.", "et alii");
			tmplParams.add("auteur institutionnel");
			tmplParams.add("co-auteur", new parametreStandard("coauteurs", "d"), new parametreStandard("coauteur", "o"), new parametreStandard("coauthor", "o"), new parametreStandard("coauthors", "o"));

			tmplParams.add("traduction", "traducteur", new parametreStandard("trad", "d"));

			tmplParams.add("préface");
			tmplParams.add("postface");
			tmplParams.add("illustrateur", new parametreStandard("illustration", "o"));
			tmplParams.add("photographe", new parametreStandard("photo", "o"));
			tmplParams.add("champ libre");

			tmplParams.add("titre", new parametreStandard("title", "o"));
			tmplParams.add("sous-titre");
			tmplParams.add("lien titre");
			tmplParams.add("titre original", new parametreStandard("titre vo", "d"));
			tmplParams.add("traduction titre");

			tmplParams.add("tome");
			tmplParams.add("volume", new parametreStandard("vol", "o"));
			tmplParams.add("titre volume", "titre tome");

			tmplParams.add("lieu", new parametreStandard("location", "o"), new parametreStandard("lieu édition", "o"));
			tmplParams.add("éditeur", new parametreStandard("publisher", "o"), new parametreStandard("édition", "o"));
			tmplParams.add("lien éditeur");

			tmplParams.add("série");
			tmplParams.add("jour", new parametreStandard("day", "o"));
			tmplParams.add("mois", "saison", new parametreStandard("month", "o"));
			tmplParams.add("année", new parametreStandard("year", "o"));
			tmplParams.add("date");
			tmplParams.add("format");
			tmplParams.add("format livre");
			tmplParams.add("format électronique");
			tmplParams.add("isbn", new parametreStandard("isbn1", "d"), new parametreStandard("ISBN", "d"));
			tmplParams.add("isbn2", new parametreStandard("ISBN2", "d"));
			tmplParams.add("isbn3");
			tmplParams.add("isbn4");
			tmplParams.add("isbn5");
			tmplParams.add("isbn6");
			tmplParams.add("isbn10");
			tmplParams.add("isbn erroné");
			tmplParams.add("issn", new parametreStandard("ISSN", "d"));
			tmplParams.add("issn2", new parametreStandard("ISSN2", "d"));
			tmplParams.add("issn3", new parametreStandard("ISSN3", "d"));
			tmplParams.add("ismn");
			tmplParams.add("ean");
			tmplParams.add("oclc");
			tmplParams.add("bnf");
			tmplParams.add("lccn");
			tmplParams.add("dnb");
			tmplParams.add("pmid", new parametreStandard("PMID", "d"));
			tmplParams.add("pmcid", new parametreStandard("pmc", "o"));
			tmplParams.add("doi", new parametreStandard("DOI", "d"));
			tmplParams.add("jstor");
			tmplParams.add("numdam");
			tmplParams.add("bibcode");
			tmplParams.add("math reviews", new parametreStandard("mr", "o"));
			tmplParams.add("zbl");
			tmplParams.add("arxiv");
			tmplParams.add(new parametreStandard("archiveurl", "u"), new parametreStandard("archive-url", "u"));
			tmplParams.add("archivedate", "archive-date");

			tmplParams.add(new parametreStandard("présentation en ligne", "u"), new parametreStandard("résumé"));

			tmplParams.add("consulté le", new parametreStandard("accessdate", "d"), new parametreStandard("consulté", "d"));
			tmplParams.add("id");
			tmplParams.add("libellé");
			tmplParams.add("extrait", "citation", new parametreStandard("quote", "o"));
			tmplParams.add("commentaire");
			tmplParams.add("plume");

			tmplParams.parametreToDeleteIfEmpty("nom1", "prénom1", "postnom1", "lien auteur1", "directeur1", "responsabilité1", "auteurs", "et al.", "auteur institutionnel", "co-auteur", "traduction", "préface", "postface", "illustrateur", "photographe", "champ libre", "sous-titre", "lien titre", "titre original", "traduction titre", "tome", "volume", "titre volume", "lien éditeur", "série", "jour", "mois", "format", "format livre", "format électronique", "isbn", "isbn2", "isbn3", "isbn erroné", "issn", "issn2", "issn3", "ismn", "ean", "oclc", "bnf", "lccn", "dnb", "pmid", "pmcid", "doi", "jstor", "numdam", "bibcode", "math reviews", "zbl", "arxiv", "archiveurl", "archivedate", "présentation en ligne", "consulté le", "id", "libellé", "extrait", "commentaire", "plume");
		}

		public override void fix() {
			GenerateId();

			renameParameter("an", "année");
			renameParameter("anno", "année");
			renameParameter("año", "année");
			renameParameter("Jahr", "année");
			renameParameter("fecha", "année");
			renameParameter("any", "année");
			renameParameter("Vuosi", "année");
			renameParameter("date d'édition", "année");
			renameParameter("date d'édition", "année");
			renameParameter("parution", "année");
			renameParameter("publication-date", "année");
			renameParameter("год", "année");
			renameParameter("autor", "auteur");
			renameParameter("Autor", "auteur");
			renameParameter("автор", "auteur");
			renameParameter("coautores", "co-auteur");
			renameParameter("commection", "collection");
			renameParameter("серия", "collection");
			renameParameter("kommentar", "commentaire");
			renameParameter("note", "commentaire");
			renameParameter("remarque", "commentaire");
			renameParameter("consulté", "consulté le", true);
			renameParameter("date de consultation", "consulté le", true);
			renameParameter("fechaacceso", "consulté le", true);
			renameParameter("date d'accès", "consulté le", true);
			renameParameter("Date d'accès", "consulté le", true);
			renameParameter("consultation", "consulté le", true);
			renameParameter("conseultation", "consulté le", true);
			renameParameter("page consultée", "consulté le", true);
			renameParameter("editorial", "éditeur");
			renameParameter("edición", "éditeur");
			renameParameter("editeurr", "éditeur");
			renameParameter("Verlag", "éditeur");
			renameParameter("Julkaisija", "éditeur");
			renameParameter("издательство", "éditeur");
			renameParameter("diteurr", "éditeur");
			renameParameter("Edieteur", "éditeur");
			renameParameter("édititeur", "éditeur");
			renameParameter("éditeur 1", "éditeur");
			renameParameter("quotes", "extrait");
			renameParameter("dimensions", "format");
			renameParameter("EAN 13", "isbn");
			renameParameter("EAN13", "isbn");
			renameParameter("ean13", "isbn");
			renameParameter("ean13", "isbn");
			renameParameter("Print ISBN", "isbn");
			renameParameter("code langue", "langue");
			renameParameter("idioma", "langue");
			renameParameter("Online", "lire en ligne");
			renameParameter("contribution-url", "lire en ligne");
			renameParameter("chapter-url", "lire en ligne");
			renameParameter("chapterurl", "lire en ligne");
			renameParameter("url chapitre", "lire en ligne");
			renameParameter("lien web", "lire en ligne");
			renameParameter("lien ouvrage", "lire en ligne");
			renameParameter("lien en ligne", "lire en ligne");
			renameParameter("lien url", "lire en ligne");
			renameParameter("lecture en ligne", "lire en ligne");
			renameParameter("lire", "lire en ligne");
			renameParameter("A lire en ligne", "lire en ligne");
			renameParameter("urlcapítol", "lire en ligne");
			renameParameter("urlcapítulo", "lire en ligne");
			renameParameter("ссылка", "lire en ligne");
			renameParameter("lien nom", "lien auteur1");
			renameParameter("lieu éditeur", "lieu");
			renameParameter("lieu édition", "lieu");
			renameParameter("lieu d'édition", "lieu");
			renameParameter("ubicación", "lieu");
			renameParameter("emplacement", "lieu");
			renameParameter("place", "lieu");
			renameParameter("endroit", "lieu");
			renameParameter("publication-place", "lieu");
			renameParameter("lugar", "lieu");
			renameParameter("localité", "lieu");
			renameParameter("localité", "lieu");
			renameParameter("Selite", "lieu");
			renameParameter("ville", "lieu");
			renameParameter("Ville", "lieu");
			renameParameter("city", "lieu");
			renameParameter("Ort", "lieu");
			renameParameter("lloc", "lieu");
			renameParameter("loc", "lieu");
			renameParameter("localisation", "lieu");
			renameParameter("место", "lieu");
			renameParameter("mes", "mois");
			renameParameter("dernier", "nom");
			renameParameter("famille", "nom");
			renameParameter("nom auteur1", "nom1");
			renameParameter("nom&amp;", "nom1");
			renameParameter(". nom1", "nom1");
			renameParameter("nomé", "nom2");
			renameParameter("numéro de chapitre", "numéro chapitre");
			renameParameter("numéro d’édition", "numéro d'édition");
			renameParameter("Auflage", "numéro d'édition");
			renameParameter("pages total", "pages totales");
			renameParameter("page totale", "pages totales");
			renameParameter("total pages", "pages totales");
			renameParameter("nombre de pages", "pages totales");
			renameParameter("nombre pages", "pages totales");
			renameParameter("nombre", "pages totales");
			renameParameter("passage totales", "pages totales");
			renameParameter("страниц", "pages totales");
			renameParameter("pages passage", "passage");
			renameParameter("pagine", "passage");
			renameParameter(", page", "passage");
			renameParameter("at", "passage");
			renameParameter("страницы", "passage");
			renameParameter("photographies", "photographe");
			renameParameter("préfacier", "préface");
			renameParameter("introduction", "préface");
			renameParameter("Introduction et notes", "préface");
			renameParameter("premier", "prénom");
			renameParameter("prénom auteur1", "nom1");
			renameParameter("présentation", "présentation en ligne");
			renameParameter("aperçu en ligne", "présentation en ligne");
			renameParameter("Recension en ligne", "présentation en ligne");
			renameParameter("description en ligne", "présentation en ligne");
			renameParameter("consulter en ligne", "présentation en ligne");
			renameParameter("résumé en ligne", "présentation en ligne");
			renameParameter("url présentation", "présentation en ligne");
			renameParameter("url résumé", "présentation en ligne");
			renameParameter("url Résumé", "présentation en ligne");
			renameParameter("url texte / résumé", "présentation en ligne");
			renameParameter("url texte - résumé", "présentation en ligne");
			renameParameter("sous", "sous-titre");
			renameParameter(". titre", "titre");
			renameParameter("título", "titre");
			renameParameter("том", "tome");
			renameParameter("titre du volume", "titre volume");
			renameParameter("titre vol", "titre volume");
			renameParameter("traductrice", "traduction");
			renameParameter("Übersetzer", "traduction");
			renameParameter("auteurstrad", "traduction");
			renameParameter("trans_title", "traduction titre");
			renameParameter("Band", "volume");
			renameParameter("numéro de volume", "volume");
			renameParameter("numéro volume", "volume");

			renameParameter("authorlink", "authorlink1");
			renameParameter("author-link", "authorlink1");

			removeParameter("postscript");
			removeParameter("lastauthoramp");

			removeParameter("droits");
			removeParameter("source");

			parametre @ref = findParam("ref");
			if (@ref != null && (@ref.value == "harv" || @ref.value.Contains("¤§t")))
				removeParameter(@ref);
			ouvrageConsulteLe();

			isxn();
			modeleP();  // transforme un paramètre non nommé avec un modèle {{p.}} en parametre passage.

			if (!tmplParams.ContainsKey("sous-titre ouvrage"))
				renameParameter("sous-titre ouvrage", "sous-titre");

			// ---
			// - Base fix
			base.fix();

			supprimerLangDeTitre(tmplParams["titre"].parametre);
			supprimerLangDeTitre(tmplParams["extrait"].parametre);
			checkGeneratedId();
		}

		public override void otherFix() {
			removeParameter("coins");
			removeParameter("nopp");

			base.otherFix();

			validerAuteurs();
			fusionJourMoisAnnee();
			inverserAnneeDate();
			removeDateTemplate("date");
			vérificationLangue();
			// retireItaliqueDuTitre("titre")  'désactivé, car le projet jeux vidéo veux le conservé pour les article en lang -> retiré dans chaque modèle
			retireVolDeVolume();
			fusionlien("éditeur", "lien éditeur");
			isbn10();
			separerBnf();
			supprimerGoogleLivreVide("lire en ligne");
			supprimerGoogleLivreVide("présentation en ligne");
			simplifierFormat();
			paragraphe();
			traductionLieu();

			if (tmplParams["éditeur"].hasValue) {
				parametre editeur = tmplParams["éditeur"].parametre;
				// editeur.value = Regex.Replace(editeur.value, "(?x) (\[\[ ([^|()\[\]]+) \ \( [^|()\[\]]+ \) \|) \2 \]\]", "$1]]")
				editeur.value = editeur.value.Replace("[[Éditions L'Harmattan|L'Harmattan]]", "[[L'Harmattan]]");
				editeur.value = editeur.value.Replace("[[Éditions Gallimard|Gallimard]]", "[[Gallimard]]");
				editeur.value = editeur.value.Replace("[[Groupe Flammarion|Flammarion]]", "[[Flammarion]]");
				editeur.value = editeur.value.Replace("[[Presses universitaires de France|PUF]]", "[[PUF]]");
				editeur.value = editeur.value.Replace("[[Presses de l'université Paris-Sorbonne|Presses de l'Université Paris-Sorbonne]]", "[[Presses de l'Université Paris-Sorbonne]]");
				editeur.value = editeur.value.Replace("[[Librairie philosophique J. Vrin|J. Vrin]]", "[[J. Vrin]]");
				editeur.value = editeur.value.Replace("[[Librairie philosophique J. Vrin|Vrin]]", "[[J. Vrin]]");
				editeur.value = editeur.value.Replace("[[Éditions Dunod|Dunod]]", "[[Dunod]]");
				editeur.value = editeur.value.Replace("[[Tallandier|édition Tallandier]]", "[[éditions Tallandier]]");
				editeur.value = editeur.value.Replace("[[Milan Presse|Éditions Milan]]", "[[Éditions Milan]]");
				editeur.value = editeur.value.Replace("[[Autrement|Éditions Autrement]]", "[[Éditions Autrement]]");
				editeur.value = editeur.value.Replace("[[Éditions Autrement|Autrement]]", "[[Autrement]]");
				editeur.value = editeur.value.Replace("[[Éditions Desclée de Brouwer|Desclée de Brouwer]]", "[[Desclée de Brouwer]]");
				editeur.value = editeur.value.Replace("[[Greenwood Publishing Group|Greenwood Press]]", "[[Greenwood Press]]");
				editeur.value = editeur.value.Replace("[[Imprimerie nationale|Imprimerie Impériale]]", "[[Imprimerie Impériale]]");
			}

			if (tmplParams["année"].hasValue) {
				// suppression des liens sur l'année
				parametre annee = tmplParams["année"].parametre;
				annee.value = Regex.Replace(annee.value, @"^\[\[(.+\|)?(\d+)\]\]$", "$2");
			}

			removeDateTemplate("consulté le");
			supprimePointConsultéLe();
		}

		// Id
		public void GenerateId() {
			Array.Clear(idTab, 0, idTab.Length);
			Array.Clear(id2Tab, 0, id2Tab.Length);
			parametre paramId = tmplParams["id"].parametre;
			int i = 1;
			if (paramId != null) {
				idTab[0] = paramId.value;
				idTab[6] = paramId.value;
				id2Tab = idTab;
			} else {
				string nom = tmplParams["nom1"].value;
				string prenom = tmplParams["prénom1"].value;
				string postnom = tmplParams["postnom1"].value;
				string auteur = tmplParams["auteur1"].value;
				while (nom != null || auteur != null) {
					if (nom == null) {
						Regex regexNom = new Regex(@"^(\[\[([^|\[\]]*\|)?)?([^ ]+ ([^ ]\.? )?)?(?<nom>.+?)(?(1)\]\]|$)");
						nom = regexNom.Match(auteur).Groups["nom"].Value;
					} else {
						Regex regexNom = new Regex(@"^(\[\[([^|\[\]]+\|)?)?(?<nom>.+)(?(1)\]\])$");
						nom = regexNom.Match(nom).Groups["nom"].Value;
					}
					if (auteur == null) {
						if (prenom != null && postnom != null)
							auteur = prenom + " " + nom + " " + postnom;
						else if (prenom != null)
							auteur = prenom + " " + nom;
						else if (postnom != null)
							auteur = nom + " " + postnom;
						else
							auteur = nom;
					}
					idTab[i] = nom.Trim();
					id2Tab[i] = Regex.Match(auteur, @"^(\[\[([^|\[\]]+\|)?)?(?<auteur>.+)(?(1)\]\])$").Groups["auteur"].Value.Trim();
					i = i + 1;
					if (i > 4)
						break;
					else {
						nom = tmplParams["nom" + i].value;
						prenom = tmplParams["prénom" + i].value;
						postnom = tmplParams["postnom" + i].value;
						auteur = tmplParams["auteur" + i].value;
					}
				}
				if (i < 4 && tmplParams["auteur institutionnel"].hasValue) {
					auteur = tmplParams["auteur institutionnel"].value;
					auteur = Regex.Replace(auteur, @"\[\[([^|\[\]]+\|)?([^\[\]]+)\]\]", "$2");
					if (auteur != null) {
						idTab[i] = auteur;
						id2Tab[i] = auteur;
						i = i + 1;
					}
				}
				idTab[i] = tmplParams["année"].value;
				if (idTab[i] == null && tmplParams["date"].hasValue)
					idTab[i] = Regex.Match(tmplParams["date"].value, @"\d{4}").Value;
				id2Tab[i] = idTab[i];
				idTab[0] = idTab[1] + idTab[2] + idTab[3] + idTab[4] + idTab[5];
				id2Tab[0] = id2Tab[1] + id2Tab[2] + id2Tab[3] + id2Tab[4] + id2Tab[5];
			}
		}

		public void checkGeneratedId() {
			// si l'identifiant à été modifié vérifie s'il y a des modèles Harvsp... avec cet identifiant. Si oui ajoute l'ancien identifiant
			string odlId = id;
			string oldId2 = id2;
			GenerateId();

			if (id != odlId || id2 != oldId2) {
				foreach (TemplateClass templ in article.templateList) {
					Harvard haravardTempl = templ as Harvard;
					if (haravardTempl != null) {
						string href = haravardTempl.Href;
						if (href == "")
							href = haravardTempl.generateHref();
						if ((href == odlId || href == oldId2) && !hasId(href)) {
							haravardTempl.fix();
							if (supprAuteur)
								changeId(href);
							else if (haravardTempl.tmplParams["id"].hasValue)
								haravardTempl.tmplParams["id"].value = id;
							else {
								string[] idT = new string[7];
								if (href.Length - id.Length < id2.Length - href.Length)
									idT = idTab;
								else
									idT = id2Tab;
								parametre previousParam = null;
								for (int i = 1; i <= 5; i++) {
									if (haravardTempl.tmplParams[i.ToString()].hasParametre) {
										parametre param = haravardTempl.tmplParams[i.ToString()].parametre;
										if (idT[i] == "")
											haravardTempl.removeParameter(param);
										else {
											param.value = idT[i];
											previousParam = param;
										}
									} else if (idT[i] != "") {
										parametre newParam = new parametre(haravardTempl, i, idT[i], "|", "");
										if (i == 1) {
											haravardTempl.addParameter(newParam, 0);
											// haravardTempl.unnamedIndex = 1
											previousParam = newParam;
										} else {
											int previousIndex = haravardTempl.Parametres.IndexOf(previousParam);
											haravardTempl.addParameter(newParam, previousIndex + 1);
											previousParam = newParam;
										}
									}
								}
							}
							haravardTempl.Href = haravardTempl.generateHref();
						}
					}
				}
			}
		}

		public override void changeId(string newId) {
			if (tmplParams["id"].hasParametre) {
				parametre IdParam = tmplParams["id"].parametre;
				int IdParamIndex = Parametres.IndexOf(IdParam);
				removeParameter(IdParam);
				GenerateId();
				if (!hasId(newId)) {
					IdParam.value = newId;
					addParameter(IdParam, IdParamIndex);
					GenerateId();
				}
			} else {
				addParameter(new parametre(Parametres[Parametres.Count - 1], "id", newId));
			}
		}

		// langue
		private void vérificationLangue() {
			includePrecedibgLang();
			if (tmplParams["langue"].hasParametre) {
				parametre paramLangue = tmplParams["langue"].parametre;
				retireModeleDeLangue(paramLangue);
				ajouteLatn(paramLangue);
				paramLangue.value = Regex.Replace(paramLangue.value, "(anglais|allemand|espagnol)e", "$1");
				paramLangue.value = paramLangue.value.Replace("inglés", "en");
			}
		}

		private void retireModeleDeLangue(parametre paramLangue) {
			Match langueMatch = Regex.Match(wikitextToString(paramLangue.value), @"^{{(\w\w\w?)}}$");
			if (langueMatch.Success) {
				paramLangue.value = langueMatch.Groups[1].Value;
				FlagSkip = false;
			}
		}
		private void ajouteLatn(parametre paramLangue) {
			if (tmplParams["titre"].mainParam.hasParametre) {
				string titre = tmplParams["titre"].mainParam.value;
				string oldValue = paramLangue.value;
				paramLangue.value = Langue.checkRtlScript(oldValue, titre, this);
				FlagSkip = (paramLangue.value == oldValue);
			}
		}

		public string aliasLang(string lang) {
			switch (lang.ToLower()) {
				case "anglais":
				case "english": { return "en"; }
				case "allemand":
				case "deutch":
				case "german": { return "de"; }
				case "espagnol":
				case "spanish": { return "es"; }
				case "français":
				case "francais":
				case "française":
				case "french": { return "fr"; }
				case "polonais":
				case "polish": { return "pl"; }
				case "japonais":
				case "japanese": { return "ja"; }
				default: {
						return lang.ToLower();
					}
			}
		}

		public void includePrecedibgLang() {
			// Ouvrage/Article précédé par indication de langue
			string parentText = parent.parsedString;
			Regex precedingTemplateRegex = new Regex(@"(¤§t\d+§¤) *(\s*" + strip + ")");
			string precidingTemplateString = precedingTemplateRegex.Match(parentText).Groups[1].ToString();
			if (precidingTemplateString.Length > 0) {
				string lang = Regex.Match(wikitextToString(precidingTemplateString), @"^{{\s*(\w\w\w?(\+\w\w\w?)?)\s*}}$").Groups[1].Value;
				if (lang.Length > 0) {
					List<string> formatElectroniques = new List<string>();
					string[] formatElectronique = new[] { "pdf", "djvu", "doc", "epub", "html", "txt", "img", "mov", "mp3", "odt", "ogg", "ppt", "perl", "php", "png", "postscript", "rar", "rtf", "svg", "tex", "vid", "xls", "xml", "xlsx", "zip" };
					formatElectroniques.AddRange(formatElectronique);
					if (formatElectroniques.Contains(lang.ToLower())) {
						if (tmplParams["format électronique"].hasParametre) {
							parametre paramformat = tmplParams["format électronique"].parametre;
							if (!paramformat.value.Contains(lang))
								paramformat.value = lang + ", " + paramformat.value;
						} else {
							parametre paramformat = new parametre(Parametres[0], "format électronique", lang);
							if (tmplParams["lire en ligne"].hasParametre)
								addParameter(paramformat, tmplParams["lire en ligne"].parametre);
							else
								Parametres.Add(paramformat);
						}
					} else if (tmplParams["langue"].hasParametre) {
						parametre paramLangue = tmplParams["langue"].parametre;
						if (paramLangue.isEmpty)
							paramLangue.value = lang;
						else if (!aliasLang(paramLangue.value).Contains(lang))
							paramLangue.value = paramLangue.value + ", " + lang;
					} else {
						parametre paramLangue = new parametre(Parametres[0], "langue", lang);
						addParameter(paramLangue, 0);
					}
					parent.parsedString = precedingTemplateRegex.Replace(parentText, "$2");
					includePrecedibgLang();
				}
			}
		}

		public void supprimerLangDeTitre(parametre paramTitre) {
			if (paramTitre != null) {
				string idModele = Regex.Match(paramTitre.value, @"^[""'« ]*¤§t(\d+)§¤[""'» ]*$").Groups[1].Value;
				if (idModele != "" && article.templateList[System.Convert.ToInt32(idModele)] is Langue) {
					Langue langTmpl = (Langue)article.templateList[System.Convert.ToInt32(idModele)];
					parametreStandard paramLangP = langTmpl.tmplParams["1"];
					parametreStandard paramTexte = langTmpl.tmplParams["texte"];
					if (paramLangP.hasValue && paramTexte.hasParametre && tmplParams["langue"].hasValue && !langTmpl.tmplParams["trans"].hasValue && aliasLang(tmplParams["langue"].value) == aliasLang(paramLangP.value))
						paramTitre.value = paramTitre.value.Replace(langTmpl.strip, Regex.Replace(paramTexte.value, "^''(.*)''$", "$1"));
				}
			}
		}

		// consulté le
		private void ouvrageConsulteLe() {
			// renomme les paramètres  |ouvrage consulté le 10 mai 2010 =
			foreach (parametre param in invalidParameters) {
				if (param.isEmpty) {
					Match match = Regex.Match(param.name, @"(?i)ouvrage consulté le\s*(.*)");
					if (match.Success) {
						param.value = match.Groups[1].Value;
						renameParameter(param, "consulté le", true);
						break;
					}
				}
			}
		}
		private void supprimePointConsultéLe() {
			if (tmplParams["consulté le"].hasValue) {
				parametre paramConsulté = tmplParams["consulté le"].parametre;
				paramConsulté.value = Regex.Replace(paramConsulté.value, @"\.$", "");
			}
		}

		// auteurs
		private void validerAuteurs() {

			// séparations des "auteurs"
			parametre paramAuteur = findParam("auteurs", "auteur", "auteur1", "authors", "author1", "author");
			if (paramAuteur != null && !paramAuteur.isEmpty) {
				separerEtAl(paramAuteur);
				if (isOrganisation(paramAuteur.value)) {
					if (paramAuteur.name == "auteurs")
						renameParameter(paramAuteur, "auteur institutionnel");
					else
						renommerAuteurInstitutionnel(paramAuteur, 1);
				} else {
					// séparation d'auteurs multiples
					separerResponsabiliteSecondaire(paramAuteur);
					if ((paramAuteur.name != "auteur1" && paramAuteur.name != "auteur") || !tmplParams["auteur2"].hasParametre) {
						if (Regex.Matches(paramAuteur.value, ",").Count < 2 && !paramAuteur.value.Contains(";"))
							separerResponsabilité(paramAuteur);
						separerAuteurs(paramAuteur);
					}
				}
			}

			// séparations des "co-auteurs"
			parametre paramCoauteur = tmplParams["co-auteur"].parametre;
			if (paramCoauteur != null && !paramCoauteur.isEmpty) {
				separerEtAl(paramCoauteur);
				paramCoauteur.value = Regex.Replace(paramCoauteur.value, "^(et|and|&) ", "");
				if (isOrganisation(paramCoauteur.value))
					renameParameter(paramCoauteur, "auteur institutionnel");
				else {
					separerResponsabiliteSecondaire(paramCoauteur);
					separerAuteurs(paramCoauteur);
				}
			}

			// pour tous les "auteur9" séparation responsabilité, inversion des "noms, prénom", séparation en prénom9 et nom9 s'il y a plusieur prénoms
			int i = 1;
			while (!StopProcessing && (tmplParams["auteur" + i].hasValue || tmplParams["nom" + i].hasValue) && i < maxAuteurs) {

				// suppression lien auteur s'il n'y a pas de page à ce nom
				parametre paramLienAuteur = tmplParams["lien auteur" + i].parametre;
				if (paramLienAuteur != null) {
					if (!isExistingPage(paramLienAuteur.value)) {
						removeParameter(paramLienAuteur);
						FlagSkip = false;
					}
				}

				if (tmplParams["auteur" + i].hasValue)
					analyseAuteurI(tmplParams["auteur" + i].parametre);
				else {
				}

				i = i + 1;
			}
		}

		private void separerEtAl(parametre paramauteur) {
			// si le paramètre co-auteurs ou auteurs contien et al., ajoute ce paramètre
			if (paramauteur != null) {
				Match match = Regex.Match(wikitextToString(paramauteur.value), @"^(?i)(.*)(?<m>\{\{)?(?<i>'*)(&|et)[\. ]+(?<ii>'*)(al(\.|l?ii)?|autres?)\k<ii>\k<i>(?(m)\}\})[,.]?$");
				if (match.Success) {
					paramauteur.value = match.Groups[1].Value.Trim();
					parametre etAl = new parametre(paramauteur, "et al.", "oui");
					addParameter(etAl, paramauteur);
					FlagSkip = false;
				}
			}
		}

		private void separerResponsabiliteSecondaire(parametre paramAuteur) {
			Dictionary<string, Regex> regexList = new Dictionary<string, Regex>();
			string postfix = @"\s*(:|de|du)?\s*(?<resp>[\w '-]+)(?(1)\))";
			regexList.Add("traduction", new Regex(@"(\()?(traduction|trad\.|traducteurs?|traductrices?)" + postfix));
			regexList.Add("préface", new Regex(@"(\()?(préface|pref\.|préfacier|introduction|avant propos)" + postfix));
			regexList.Add("postface", new Regex(@"(\()?(postface|épilogue|conclusion)" + postfix));
			regexList.Add("illustrateur", new Regex(@"(\()?(illustrateurs?|\bill\.|illustratrices?|illustrations?)" + postfix));
			regexList.Add("photographe", new Regex(@"(\()?(photographes?|photos?|photographies?)" + postfix));

			foreach (string name in regexList.Keys) {
				Match m = regexList[name].Match(paramAuteur.value);
				if (m.Success) {
					parametre paramResp = new parametre(paramAuteur, name, m.Groups["resp"].Value);
					addParameter(paramResp, paramAuteur);
					paramAuteur.value = paramAuteur.value.Replace(m.Value, "").Trim();
				}
			}
		}

		private void separerAuteurs(parametre param) {
			// retrait éventuelle virgule, point virgule ou point finale
			string paramValue = param.value;
			if (paramValue.Trim() == "") {
				removeParameter(param);
				return;
			}
			param.value = Regex.Replace(param.value, "[,;]$", "");
			FlagSkip = (paramValue == param.value);
			MatchCollection matchAuteurs;


			// nom, initiale(s),
			Regex nomVirguleInitialeViguleRegex = new Regex(@"(?xin) \G  (?> "
				+ "(?#capture|    nom       |    initiale        |   junior    )"
				+ @"(?<auteur>  \w[-\w'’ ]+ , \ ? (\p{Lu}\b[. ]*)+ (,\ ?jr\.?)? )"
				+ @"( \( (?<resp>[-\w'’. ]*) \) )?                     (?#  responsabilité )"
				+ @"( ,?\ (et|and|&)\ | , | (?<end>$) ) \s*            (?#  séparateur ou fin )"
				+ ")");
			matchAuteurs = nomVirguleInitialeViguleRegex.Matches(param.value);

			if (matchAuteurs.Count == 0 || !matchAuteurs[matchAuteurs.Count - 1].Groups["end"].Success) {
				// auteurs séparés par des virgules (attention ce peut être nom, prénom)
				Regex auteurVirguleRegex = new Regex(@"(?xni) \G  (?> "
					+ "(?#capture|       lien sur l'auteur              |     prénom   |    nom     |  junior       |    fermeture lien    )"
					+ @"(?<auteur> (?<lien>\[\[\ * ( [^{}|\[\]]+ \| )? )? [-\w'.]+ [. ]+ \w[-\w'’. ]*? (,\ ?jr\.?\ *)? (?(lien)\ *\]\]\ *)?  )"
					+ @"( \( (?<resp>[-\w'’. ]*) \) )?                     (?#  responsabilité )"
					+ @"( ,?\ (et|and|&)\ | , | (?<end>$) )(?!\ ?jr) \s*   (?#  séparateur ou fin )"
					+ ")");
				matchAuteurs = auteurVirguleRegex.Matches(param.value);
			}

			if (matchAuteurs.Count == 0 || !matchAuteurs[matchAuteurs.Count - 1].Groups["end"].Success) {
				// auteurs sous forme nom, prénom, prénom nom ... et prénom nom;
				Regex nomVigulePrenomEtPrenomNomRegex = new Regex("(?xni) (^ (?>                                       (?# partie nom, prénom )"
					+ @"(?<auteur> \w[-\w'’]+\b                           (?# nom  )"
					+ @"\ * , \s*                                         (?# espace virgule espace )"
					+ @"\w[-\w'. ]*\b \.? (?<!\w\w\.) (,?\ ?jr\.?)? )     (?# prénom pas suivit d'un point sauf initiale ou jr.)"
					+ @"\s* ( (,|;)?\ (et|and|&)\ | (,|;) ) \s*           (?#  séparateur )"
					+ @") | ( \G                                          (?#  début de la partie prenom nom )"
					+ @"(?<auteur> [-\w'’.]+ [. ]+                         (?# prénom)"
					+ @"\w[-\w'. ]*? (,\ ?jr\.?\ *)?  ) \s*               (?# nom & junior)"
					+ @"( (,|;)?\ (et|and|&)\ | (,|;) | (?<end>\.?\s*$) ) (?#  séparateur ou fin )"
					+ @"\s* ) )");
				matchAuteurs = nomVigulePrenomEtPrenomNomRegex.Matches(param.value);
			}

			if (matchAuteurs.Count == 0 || !matchAuteurs[matchAuteurs.Count - 1].Groups["end"].Success) {
				// auteurs sous forme nom, prénom;
				Regex nomVigulePrenomPointVirguleRegex = new Regex(@"(?xni) \G  (?> "
					+ @"(?<auteur> \w[-\w'’ ]+\b                          (?# nom  )"
					+ @"\ * (, \s*                                        (?# espace virgule espace )"
					+ @"\w[-\w'. ]*\b \.? (?<!\w\w\.) (,?\ ?jr\.?)?       (?# prénom pas suivit d'un point sauf initiale ou jr.)"
					+ ")? )                                              (?# prénom facultatif, fin d'auteur )"
					+ @"( \s* \( (?<resp>[-\w'’. ]*) \) )?                (?#  responsabilité )"
					+ @"\s* ( ;?\ (et|and|&)\ | ; | (?<end>\.?\s*$) ) \s* (?#  séparateur ou fin )"
					+ ")");
				matchAuteurs = nomVigulePrenomPointVirguleRegex.Matches(param.value);
			}

			if (matchAuteurs.Count == 0 || !matchAuteurs[matchAuteurs.Count - 1].Groups["end"].Success) {
				// auteurs séparés par des points virgules
				Regex auteurPointVirguleRegex = new Regex(@"(?xni) \G  (?> "
					+ "(?#capture|       lien sur l'auteur           |  prénom   |    nom       |  junior       | fermeture lien    )"
					+ @"(?<auteur> (?<lien>\[\[ ( [^{}|\[\]]+ \| )? )? [-\w'’.]+ \  \w[-\w'’. ]*? (,\ ?jr\.?\ *)? (?(lien)\]\]\ *)?  )"
					+ @"( \( (?<resp>[-\w'’. ]*) \) )?                     (?#  responsabilité )"
					+ @"( ;?\ (et|and|&)\ | ; | (?<end>$) ) \s*            (?#  séparateur ou fin )"
					+ ")");
				matchAuteurs = auteurPointVirguleRegex.Matches(param.value);
			}

			if (matchAuteurs.Count == 0 || !matchAuteurs[matchAuteurs.Count - 1].Groups["end"].Success) {
				// Un seul mot
				Regex auteurPointVirguleRegex = new Regex("(?xn) ^ "
					+ "(?#capture|       lien sur l'auteur               |   nom     |    fermeture lien    )"
					+ @"(?<auteur> (?<lien>\[\[\ * ( [^{}|\[\]]+ \| )? )?\ * [-\w'’.]+ (?(lien)\ *\]\]\ *)?  )"
					+ @"( \( (?<resp>[-\w'’. ]*) \) )?                     (?#  responsabilité )"
					+ "(?<end>$)");
				matchAuteurs = auteurPointVirguleRegex.Matches(param.value);
			}

			if (matchAuteurs.Count > 0) {
				if (matchAuteurs[matchAuteurs.Count - 1].Groups["end"].Success) {
					int i = 1;
					if (param.name == "auteurs" || param.name.StartsWith("co")) {
						while (tmplParams["auteur" + i.ToString()].hasValue || tmplParams["nom" + i.ToString()].hasValue) {
							i = i + 1;
							if (i == maxAuteurs)
								return;
						}
					}

					parametre auteurParam, respParam;
					List<parametre> listeAuteurs = new List<parametre>();

					foreach (Match matchAuteur in matchAuteurs) {
						if (matchAuteurs.Count == 2 && listeAuteurs.Count == 1 && !matchAuteur.Groups["resp"].Success && Regex.IsMatch(matchAuteur.Groups["auteur"].Value, @"^[\w. ]+$")) {
							if (Regex.IsMatch(param.value, @"^[\w. -]+,[\w. -]+$")) {
								CaptureCollection matchMots = Regex.Match(matchAuteur.Groups["auteur"].Value, @"(?n)^(?<mots>[-\w'’.]+( +|$))+$").Groups["mots"].Captures;
								// teste si ce n'est pas un groupe de prénom
								bool testPrénom = true;
								foreach (Capture mot in matchMots)
									testPrénom = testPrénom && _listPrenom.Contains(mot.Value.Trim().ToLower());
								if (testPrénom) {
									CaptureCollection matchMotsPrev = Regex.Match(listeAuteurs[0].value, @"(?n)^(?<mots>[-\w'’.]+( +|$))+$").Groups["mots"].Captures;
									bool testPrénomPrev = false;
									foreach (Capture mot in matchMotsPrev)
										// test s'il y a déjà un prénom dans l'entrée précédente
										testPrénomPrev = testPrénomPrev || _listPrenom.Contains(mot.Value.Trim().ToLower()) || isInitiale(mot.Value.Trim());
									if (!testPrénomPrev) {
										auteurParam = new parametre(param, "prénom" + (i - 1).ToString(), matchAuteur.Groups["auteur"].Value);
										listeAuteurs[listeAuteurs.Count - 1].changeName("nom" + (i - 1).ToString());
										listeAuteurs.Add(auteurParam);
										continue;
									}
								} else {
									// teste si ce n'est pas un groupe de responsabilité
									bool testResp = true;
									foreach (Capture mot in matchMots)
										testResp = testResp && _listResp.Contains(mot.Value.Trim());
									if (testResp) {
										auteurParam = new parametre(param, "responsabilité" + (i - 1).ToString(), matchAuteur.Groups["auteur"].Value);
										listeAuteurs.Add(auteurParam);
										continue;
									}
								}
							} else if (Regex.IsMatch(param.value, @"^[\w. -]+ (et|&) [\w. -]+$")) {
								string prenom1 = Regex.Match(matchAuteur.Groups["auteur"].Value, @"^\p{Lu}(\p{Ll}+|\.?([- ]?\p{Lu}\.?)?)").Value;
								string nom1 = "";
								if (prenom1 != null)
									nom1 = matchAuteur.Groups["auteur"].Value.Replace(prenom1 + " ", "");
								if (Regex.IsMatch(nom1, @"^[\w-]+$") && (_listPrenom.Contains(prenom1) && _listPrenom.Contains(listeAuteurs[0].value) || isInitiale(prenom1) && isInitiale(listeAuteurs[0].value)))
									listeAuteurs[0].value = listeAuteurs[0].value + " " + nom1;
							}
						}
						auteurParam = new parametre(param, "auteur" + i.ToString(), matchAuteur.Groups["auteur"].Value);
						listeAuteurs.Add(auteurParam);
						if (matchAuteur.Groups["resp"].Success) {
							string respValue = matchAuteur.Groups["resp"].Value;
							string typeParam = "responsabilité";
							if (Regex.IsMatch(respValue, @"(?i)^(sous la )?dir(\w*).?( de)?$")) {
								typeParam = "directeur";
								respValue = "oui";
							}
							respParam = new parametre(param, typeParam + i.ToString(), respValue);
							listeAuteurs.Add(respParam);
						}
						i = i + 1;
					}
					if (param.value != listeAuteurs[0].value || (param.name != "auteur" && param.name != "auteur1")) {
						Parametres.InsertRange(Parametres.IndexOf(param), listeAuteurs);
						removeParameter(param);
						FlagSkip = false;
					}
				} else if (param.name != "auteur" && param.name != "auteur1")
					param.value = param.value + "<!-- !!! CORRECTION auteurs / co-auteur A COMPLETER !!! (détection partielle) -->";
			} else if (param.name != "auteur" && param.name != "auteur1")
				param.value = param.value + "<!-- !!! CORRECTION auteurs / co-auteur A COMPLETER !!! -->";
		}

		private void separerResponsabilité(parametre paramAuteur) {
			// séparation de certaine responsabilité (éditeur ou abrégé)
			string respValue = "";
			Regex regexAuteurResp = new Regex("(?xn) ^" + @"(?<auteur> \w+ ,\  \w+\.? | [^,;(){}¤]*? )" + "(" + @",?\s+ \(? (?<editeur> [EÉeé]d ( it(eu|o)r | \.? ) ) \)?" + "|" + @",\s* (?<resp2> \w+\p{Ll} \. )" + "|" + @"\s* \( (?<resp2> [\w ]+ \.? ) \)" + ") $");
			Regex regexRespAuteur = new Regex("(?xn) ^ " + @"(?<resp> [\w. ]+\b )" + @"\s* :? \s*" + @"(?<auteur> \[\[ ( [^{}|\[\]]+ \| )? ( \w+ ,\  \w+\.? | [^,;(){}¤]*? ) \]\] ) $");
			Match respMatch = regexAuteurResp.Match(paramAuteur.value);
			if (respMatch.Success) {
				if (respMatch.Groups["editeur"].Success)
					respValue = "éd.";
				else {
					respValue = respMatch.Groups["resp"].Value;
					respValue = Regex.Replace(respValue, @"(?i)[eé]d(it(eu|o)r|\.?)", "éd.");
				}
			} else if (!isOrganisation(paramAuteur.value)) {
				respMatch = regexRespAuteur.Match(paramAuteur.value);
				if (respMatch.Success)
					respValue = respMatch.Groups["resp"].Value;
			}

			if (respValue != "") {
				string numAuteur = Regex.Match(paramAuteur.name, @"\d+").Value;
				if (numAuteur == "")
					numAuteur = "1";
				string typeParam = "responsabilité";
				if (Regex.IsMatch(respValue, @"(?i)^(sous la )?dir(\w*).?( de)?$")) {
					typeParam = "directeur";
					respValue = "oui";
				}
				parametre respParam = new parametre(paramAuteur, typeParam + numAuteur, respValue);
				addParameter(respParam, paramAuteur);
				paramAuteur.value = respMatch.Groups["auteur"].Value;
			}
		}

		public static void loadListNomPrenom(bool reload) {
			if (_listPrenom == null || reload) {
				if (_listPrenom == null) {
					_listPrenom = new List<string>();
					_listMiddle = new List<string>();
					_listNom = new List<string>();
					_listPostnom = new List<string>();
					_listResp = new List<string>();
					_listOrg = new List<string>();
					_listMotsOrg = new List<string>();
					_listInconnu = new List<string>();
				} else {
					_listPrenom.Clear();
					_listResp.Clear();
				}

				try {
					string fichierPrenom = System.IO.File.ReadAllText(CustomModule.ListsFile);
					deserialize(fichierPrenom, _listPrenom, "prénoms");
					deserialize(fichierPrenom, _listResp, "responsabilités");
					deserialize(fichierPrenom, _listPostnom, "postnoms");
					deserialize(fichierPrenom, _listMiddle, "middle");
					deserialize(fichierPrenom, _listNom, "noms");
					deserialize(fichierPrenom, _listOrg, "organisations");
					deserialize(fichierPrenom, _listMotsOrg, "mots organisations");
				} catch (Exception ex) {
					System.Windows.Forms.MessageBox.Show("listes de prénoms et responsabilités non chargée : " + ex.Message);
				}
			}
		}

		public static void saveListNomPrenom() {
			string fichierPrenom;
			try {
				fichierPrenom = System.IO.File.ReadAllText(CustomModule.ListsFile);
			} catch (Exception) {
				fichierPrenom = "";
			}
			string nouveauFichier = fichierPrenom;

			nouveauFichier = serialize(nouveauFichier, _listPrenom, "prénoms");
			nouveauFichier = serialize(nouveauFichier, _listPostnom, "postnoms");
			nouveauFichier = serialize(nouveauFichier, _listResp, "responsabilités");
			nouveauFichier = serialize(nouveauFichier, _listMiddle, "middle");
			nouveauFichier = serialize(nouveauFichier, _listOrg, "organisations");

			if (nouveauFichier != fichierPrenom)
				System.IO.File.WriteAllText(CustomModule.ListsFile, nouveauFichier);
		}


		const int _btnCancel = 1;
		const int _btnInitiale = 23;
		const int _btnPrenom = 2;
		const int _btnMiddle = 2;
		const int _btnNom = 4;
		const int _btnPostnom = 5;
		const int _btnResponsabilité = 6;
		const int _btnOrganisation = 7;
		const int _btnJeNeSaisPas = 3;
		private int dialogueNom(string mot, string auteur) {
			dialog dialogue = new dialog("", 450, 300);

			RichLabel label = new RichLabel();
			{
				var withBlock = label;
				withBlock.Location = new System.Drawing.Point(12, 12);
				withBlock.Width = dialogue.Width - 24;
				withBlock.Height = 150;
				withBlock.Rtf = @"{\rtf1\ansi {\fonttbl{\f0\fswiss\fprq2\fcharset1 Calibri;}{\f1\fmodern\fprq1\fcharset0 Calibri;}}\f0\fs18 " + @"Dans \'ab {\b " + escapeRTF(auteur) + @"} \'bb,\par est-ce que \'ab {\b " + escapeRTF(mot) + @"} \'bb est un pr\'e9nom ?\par\par " + @"Mod\'e8le complet : \par {\f1 {" + escapeRTF(this.templateName + this.initialString) + "}}}";
			}
			dialogue.Controls.Add(label);

			// Bouton Cancel.
			Button boutonCancel = new Button("Cancel", dialogue.Width - 90, dialogue.Height - 60, _btnCancel);
			dialogue.CancelButton = boutonCancel;
			dialogue.Controls.Add(boutonCancel);

			// Bouton Prénom.
			Button boutonPrenom = new Button("Prénom", 12, dialogue.Height - 120, _btnPrenom);
			dialogue.Controls.Add(boutonPrenom);

			// Bouton Middle.
			Button boutonMiddle = new Button("Middle", 12, dialogue.Height - 90, _btnMiddle);
			dialogue.Controls.Add(boutonMiddle);

			// Bouton Postnom.
			Button boutonPostnom = new Button("Postnom", 12, dialogue.Height - 60, _btnPostnom);
			dialogue.Controls.Add(boutonPostnom);

			// Bouton Nom.
			Button boutonNom = new Button("Nom", boutonPrenom.Location.X + boutonPrenom.Width + 12, dialogue.Height - 120, _btnNom);
			dialogue.Controls.Add(boutonNom);

			// Bouton Je ne sais pas.
			Button boutonJeNeSaisPas = new Button("Je ne sais pas", boutonPrenom.Location.X + boutonPrenom.Width + 12, dialogue.Height - 90, _btnJeNeSaisPas);
			dialogue.Controls.Add(boutonJeNeSaisPas);

			// Bouton Responsabilité.
			Button boutonResponsabilite = new Button("Responsabilité", boutonNom.Location.X + boutonNom.Width + 12, dialogue.Height - 120, _btnResponsabilité);
			boutonResponsabilite.Width = 100;
			dialogue.Controls.Add(boutonResponsabilite);

			// Bouton Organisation.
			Button boutonOrganisation = new Button("Organisation", boutonResponsabilite.Location.X + boutonResponsabilite.Width + 12, dialogue.Height - 120, _btnOrganisation);
			boutonOrganisation.Width = 100;
			dialogue.Controls.Add(boutonOrganisation);

			// Display the form as a modal dialog box.
			dialogue.ShowDialog();

			int retourDialogue = (int)dialogue.DialogResult;
			dialogue.Dispose();
			switch (retourDialogue) {
				case _btnPrenom: { // prénom
						_listPrenom.Add(mot.ToLower());
						break;
					}
				//case  _btnMiddle: { // middle name
				//        _listMiddle.Add(mot.ToLower());
				//        break;
				//    }
				case _btnNom: { // nom
						_listNom.Add(mot.ToLower());
						break;
					}
				case _btnPostnom: {
						_listPostnom.Add(mot.ToLower());
						break;
					}
				case _btnResponsabilité: { // responsabilité
						_listResp.Add(mot.ToLower());
						break;
					}
				case _btnOrganisation: {
						_listOrg.Add(Regex.Replace(auteur.ToLower(), @"\[\[([^|\[\]]+\|)?([^\[\]]+)\]\]", "$2"));
						break;
					}
				case _btnJeNeSaisPas: {
						_listInconnu.Add(mot.ToLower());
						break;
					}
			}
			return retourDialogue;
		}
		public string escapeRTF(string texte) {
			StringBuilder newTexte = new StringBuilder();
			foreach (char letter in texte) {
				if (letter == '\\' || letter == '{' || letter == '}')
					newTexte.Append(@"\" + letter);
				else if (Convert.ToUInt32(letter) <= 0x7F)
					newTexte.Append(letter);
				else
					newTexte.Append(@"\u" + Convert.ToUInt32(letter).ToString() + "?");
			}
			return newTexte.ToString();
		}

		private int evaluateMot(string mot, string auteur, bool silent) {
			mot = mot.Trim();
			string motMinuscules = mot.ToLower();
			if (isInitiale(mot))
				return _btnInitiale; // initiale
			else if (_listPrenom.Contains(motMinuscules))
				return _btnPrenom; // prénom
			else if (_listMiddle.Contains(motMinuscules))
				return _btnMiddle; // middle name
			else if (_listPostnom.Contains(motMinuscules))
				return _btnPostnom; // nom
			else if (_listNom.Contains(motMinuscules))
				return _btnNom; // nom
			else if (_listResp.Contains(motMinuscules))
				return _btnResponsabilité; // responsabilité
			else if (_listOrg.Contains(Regex.Replace(auteur.ToLower(), @"\[\[([^|\[\]]+\|)?([^\[\]]+)\]\]", "$2")))
				return _btnOrganisation;
			else if (mot.Contains("-") && _listPrenom.Contains(Regex.Match(motMinuscules, "^[^-]+").ToString()))
				return _btnPrenom; // prénom
			else if (_listInconnu.Contains(motMinuscules))
				return _btnJeNeSaisPas;
			else if (!silent)
				return dialogueNom(mot, auteur);
			return _btnJeNeSaisPas;
		}
		private bool isInitiale(string prenom) {
			return Regex.IsMatch(prenom.Trim(), @"^\p{Lu}\.?([- ]?\p{Lu}\.?){0,2}$");
		}
		private bool isResponsabilite(string resp, string auteur) {
			resp = resp.Trim();
			string respMinuscules = resp.ToLower();
			if (_listResp.Contains(respMinuscules))
				return true;
			else
				return false;
		}
		private bool isOrganisation(string auteur) {
			if (Regex.IsMatch(wikitextToString(auteur), @"\d"))
				return true;
			auteur = Regex.Replace(auteur.ToLower(), @"\[\[([^|\[\]]+\|)?([^\[\]]+)\]\]", "$2");
			if (_listOrg.Contains(auteur))
				return true;
			foreach (Match mot in Regex.Matches(auteur, @"\G[^. ]+([. ]+|$)")) {
				if (_listMotsOrg.Contains(mot.ToString().Trim().ToLower()))
					return true;
			}
			return false;
		}

		private void analyseAuteurI(parametre paramAuteur) {
			if (paramAuteur == null || paramAuteur.value == "")
				return;
			string numAuteur = Regex.Match(paramAuteur.name, @"\d+$").Value;
			if (numAuteur == "")
				numAuteur = "1";

			separerResponsabilité(paramAuteur);
			// suppresion virgule finale
			paramAuteur.value = Regex.Replace(paramAuteur.value, "[,;]$", "");
			// suppression Sir
			paramAuteur.value = Regex.Replace(paramAuteur.value, "^Sir ", "");

			if (isOrganisation(paramAuteur.value) && !tmplParams["nom" + numAuteur].hasValue) {
				renommerAuteurInstitutionnel(paramAuteur, System.Convert.ToInt32(numAuteur));
				return;
			}

			// séparation lien
			string auteur = paramAuteur.value;
			string page = "";
			Regex regexLienAuteur = new Regex("(?xn) ^ " + @"\[\[" + @"(?<page> [^{}|\[\]]+ \| )?       " + @"(?<auteur> [^\[\]]+ )            " + @"\]\] $ ");
			Match matchLien = regexLienAuteur.Match(auteur);
			bool lien = matchLien.Success;
			if (lien) {
				page = matchLien.Groups["page"].Value;
				auteur = matchLien.Groups["auteur"].Value.Trim();
				if (page == "")
					lien = isExistingPage(auteur);
				else
					lien = isExistingPage(page);
			}

			// remplacement de quelques noms courrant sur wikipédia
			if (auteur == "Jean Baptiste Pierre Jullien de Courcelles" || auteur == "Jean Baptiste Pierre Jullien Courcelles")
				auteur = "Jean-Baptiste-Pierre Jullien de Courcelles";
			else if (auteur == "Agricole Joseph François Xavier Pierre Esprit Simon Paul Antoine Fortia d'Urban")
				auteur = "Agricol-Joseph Fortia d'Urban";

			// inversion nom, prénom
			if (auteur.Contains(",")) {
				string post = Regex.Match(auteur, ", *(.*)").Groups[1].Value.ToLower();
				if (_listPostnom.Contains(post) || _listResp.Contains(post))
					auteur = Regex.Replace(auteur, ", ?", " ");
				else
					auteur = Regex.Replace(auteur, @"^([^,]+),\ *([^,]+)$", "$2 $1");
			}

			// identification des prénoms
			Regex regexAuteur = new Regex(@"(?n)^(?<mots>[-\w'’.]+( +|$))+$");
			Match matchAuteur = regexAuteur.Match(auteur);
			if (matchAuteur.Success) {
				string prenom = "";
				int nbPrenom = 0;
				string responsabilite = "";
				int nbresponsabilite = 0;
				string postnom = "";
				int nbPostnom = 0;
				CaptureCollection mots = matchAuteur.Groups["mots"].Captures;
				string dernierMot = mots[mots.Count - 1].Value;
				int moinsUn = System.Convert.ToInt32(isInitiale(dernierMot) || _listPostnom.Contains(dernierMot.ToLower()));
				bool silent = false;
				if (this is LienWeb || parent.name == "ref")
					silent = true;

				for (int i = 0; i <= mots.Count - 2 + moinsUn; i++) {
					switch (evaluateMot(mots[i].Value, paramAuteur.value, silent)) {
						case _btnCancel:
							StopProcessing = true;
							paramAuteur.value = paramAuteur.value + "<!-- StopProcessing -->";
							return;
						case _btnInitiale:
						case _btnPrenom:
							prenom = prenom + mots[i].Value;
							nbPrenom += 1;
							break;
						//case _btnMiddle:                        {
						//        if (nbPrenom > 0)
						//        {
						//            prenom = prenom + mots[i].Value;
						//            nbPrenom += 1;
						//        }
						//        else
						//            break;
						//        break;
						//    }
						case _btnNom:
							if (true)
								break;
						case _btnPostnom:
							postnom = postnom + mots[i].Value;
							nbPostnom += 1;
							break;
						case _btnResponsabilité:
							if (nbPrenom == 0) {
								responsabilite = responsabilite + mots[i].Value;
								nbresponsabilite += 1;
							} else
								break;
							break;
						case _btnOrganisation:
							renommerAuteurInstitutionnel(paramAuteur, System.Convert.ToInt32(numAuteur));
							return;
						case _btnJeNeSaisPas:
							silent = true;
							if (i == 0) {
								prenom = prenom + mots[i].Value;
								nbPrenom += 1;
							} else
								break;
							break;
					}
				}

				if (nbPrenom == 0 && nbPostnom == 0) {
					if (mots.Count - 2 + moinsUn < 0 && isInitiale(mots[0].Value))
						return;
					if (mots.Count - 2 + moinsUn < 0 && _listPrenom.Contains(mots[0].Value.ToLower().Trim()) && paramAuteur.standardName.mainParam.name == "auteur1" && !tmplParams["auteur2"].hasValue)
						return;
					for (int i = mots.Count - 1; i >= 1 + nbresponsabilite; i += -1) {
						bool noResp = (nbresponsabilite == 0);
						switch (evaluateMot(mots[i].Value, paramAuteur.value, silent)) {
							case _btnCancel: {
									StopProcessing = true;
									paramAuteur.value = paramAuteur.value + "<!-- StopProcessing -->";
									return;
								}
							case _btnInitiale:
							case _btnPrenom:
							//case _btnMiddle:                            {
							//        prenom = mots[i].Value + prenom;
							//        nbPrenom = nbPrenom - 1;
							//        break;
							//    }
							case _btnPostnom: {
									postnom = mots[i].Value + postnom;
									nbPostnom += 1;
									break;
								}
							case _btnResponsabilité: {
									if (nbPrenom == 0 && noResp) {
										responsabilite = mots[i].Value + responsabilite;
										nbresponsabilite = nbresponsabilite + 1;
									} else
										break;
									break;
								}
							default: {
									if (true)
										break;
								}
						}
					}
				} else if (nbresponsabilite == 0) {
					for (int i = mots.Count - 1; i >= 1 + nbPrenom; i += -1) {
						if (_listResp.Contains(mots[i].Value)) {
							responsabilite = mots[i].Value + responsabilite;
							nbresponsabilite = nbresponsabilite + 1;
						} else if (_listPostnom.Contains(mots[i].Value.ToLower())) {
							postnom = mots[i].Value + postnom;
							nbPostnom += 1;
						} else
							break;
					}
				}

				// séparation responsabilité
				if (responsabilite != "") {
					parametre paramResp = new parametre(paramAuteur, "responsabilité" + numAuteur, responsabilite);
					if (paramAuteur.value.StartsWith(responsabilite))
						addParameterBefore(paramResp, paramAuteur);
					else
						addParameter(paramResp, paramAuteur);
					auteur = auteur.Replace(responsabilite, "").Trim();
					FlagSkip = false;
				}

				if (nbPrenom < 0) {
					auteur = prenom.Trim() + " " + Regex.Replace(auteur, Regex.Escape(prenom) + "$", "").Trim();
					nbPrenom = 0 - nbPrenom;
					FlagSkip = false;
				}

				// If Not lien And Not TypeOf Me Is LienWeb And parent.name <> "ref" And CInt(numAuteur) < 5 And _
				// (nbPrenom > 2 Or (nbPrenom = 2 And Not isInitiale(Regex.Replace(prenom, "^[^ .]+[ .]", "")) Or nbPostnom > 0)) Then
				// 'deux prénoms, séparation en deux paramètres, plus éventuellement le lien
				// If Not standardsParmetres("nom" & numAuteur).hasValue Then
				// If lien Then
				// If page = "" Then
				// page = auteur
				// End If
				// addParameter(New parametre(paramAuteur, "lien auteur" & numAuteur, page.Replace("|", "").Trim), after:=paramAuteur)
				// End If
				// If prenom <> "" Then
				// If auteur.StartsWith(prenom) Then
				// addParameter(New parametre(paramAuteur, "prénom" & numAuteur, prenom.Trim), before:=paramAuteur)
				// Else
				// addParameter(New parametre(paramAuteur, "prénom" & numAuteur, prenom.Trim), after:=paramAuteur)
				// End If
				// auteur = Regex.Replace(auteur, "\b" & Regex.Escape(prenom) & "\b", "")
				// End If
				// If postnom <> "" Then
				// If auteur.StartsWith(postnom) Then
				// addParameter(New parametre(paramAuteur, "postnom" & numAuteur, postnom.Trim), before:=paramAuteur)
				// Else
				// addParameter(New parametre(paramAuteur, "postnom" & numAuteur, postnom.Trim), after:=paramAuteur)
				// End If
				// auteur = auteur.Replace(postnom, "")
				// End If
				// renameParameter(paramAuteur, "nom" & numAuteur)
				// paramAuteur.value = auteur.Trim
				// FlagSkip = False
				// End If
				// ElseIf Not lien And Not TypeOf Me Is LienWeb And parent.name <> "ref" And CInt(numAuteur) < 5 And nbPrenom = 0 And Not standardsParmetres("nom" & numAuteur).hasValue Then
				// If responsabilite <> "" Then
				// paramAuteur.value = auteur
				// End If
				// If auteur.Trim.Contains(" ") Then
				// renameParameter(paramAuteur, "nom" & numAuteur)
				// FlagSkip = False
				// End If
				// Else
				// If lien Then
				// paramAuteur.value = "[[" & page & auteur & "]]"
				// Else
				// 'If standardsParmetres("lien auteur" & numAuteur).hasValue Then
				// '    Dim paramLien As parametre = standardsParmetres("lien auteur" & numAuteur).parametre
				// '    Dim paramLienValue As String = Regex.Replace(paramLien.value, "^\[\[([^|\[\]]+)\|?.*\]\]$", "$1")
				// '    If paramLienValue = auteur Then
				// '        paramAuteur.value = "[[" & auteur & "]]"
				// '    Else
				// '        paramAuteur.value = "[[" & paramLienValue & "|" & auteur & "]]"
				// '    End If
				// '    removeParameter(paramLien)
				// 'Else
				// paramAuteur.value = auteur
				// 'End If
				// End If
				// End If

				if (lien)
					paramAuteur.value = "[[" + page + auteur + "]]";
				else
					paramAuteur.value = auteur;
			}
			if (tmplParams["responsabilité" + numAuteur].hasValue) {
				parametre paramResp = tmplParams["responsabilité" + numAuteur].parametre;
				if (Regex.IsMatch(paramResp.value, @"(?i)^[ée]d(s?\.?|iteurs?|itors?)$"))
					paramResp.value = "éd.";
			}
		}

		private void renommerAuteurInstitutionnel(parametre paramAuteur, int numauteur) {
			if (this is LienWeb && paramAuteur.standardName.mainParam.name == "auteur1" || paramAuteur.value.ToLower() == "collectif")
				return;
			renameParameter(paramAuteur, "auteur institutionnel");
			if (tmplParams["lien auteur" + numauteur].hasValue) {
				string lienAuteurInst = tmplParams["lien auteur" + numauteur].value;
				lienAuteurInst = Regex.Replace(lienAuteurInst, @"^\[\[([^|\[\]]+)\|?.*\]\]$", "$1");
				if (paramAuteur.value == lienAuteurInst)
					paramAuteur.value = "[[" + paramAuteur.value + "]]";
				else
					paramAuteur.value = "[[" + lienAuteurInst + "|" + paramAuteur.value + "]]";
				removeParameter(tmplParams["lien auteur" + numauteur].parametre);
			} else {
				Match matchLienAuteur = Regex.Match(paramAuteur.value, @"(?x) ^ \[\[ ([^{}|\[\]]+) \|? ([\|^\[\]]+)? \]\] $ ");
				if (matchLienAuteur.Success && !isExistingPage(matchLienAuteur.Groups[1].Value)) {
					if (matchLienAuteur.Groups[2].Success)
						paramAuteur.value = matchLienAuteur.Groups[2].Value;
					else
						paramAuteur.value = matchLienAuteur.Groups[1].Value;
				}
			}
			int num = numauteur + 1;
			while (tmplParams["nom" + num].hasParametre || tmplParams["auteur" + num].hasParametre) {
				renameParameter(tmplParams["nom" + num].parametre, "nom" + (num - 1));
				renameParameter(tmplParams["prénom" + num].parametre, "prénom" + (num - 1));
				renameParameter(tmplParams["postnom" + num].parametre, "postnom" + (num - 1));
				renameParameter(tmplParams["auteur" + num].parametre, "auteur" + (num - 1));
				renameParameter(tmplParams["lien auteur" + num].parametre, "lien auteur" + (num - 1));
				renameParameter(tmplParams["responsabilité" + num].parametre, "responsabilité" + (num - 1));
				renameParameter(tmplParams["directeur" + num].parametre, "directeur" + (num - 1));
				num = num + 1;
			}
		}

		// date
		public string moisEnLettres(string mois) {
			switch (mois.ToLower()) {
				case "1":
				case "01":
				case "jan.":
				case "janv.":
				case "jan":
				case "janv":
				case "january":
				case "de enero de":
				case "de janeiro de": {
						mois = "janvier";
						break;
					}

				case "2":
				case "02":
				case "fevrier":
				case "fev.":
				case "fev":
				case "fév.":
				case "fév":
				case "february":
				case "de fevereiro de":
				case "de febrero de": {
						mois = "février";
						break;
					}

				case "3":
				case "03":
				case "mar.":
				case "mar":
				case "march":
				case "de marzo de":
				case "de março de": {
						mois = "mars";
						break;
					}

				case "4":
				case "04":
				case "avr.":
				case "avr":
				case "apr":
				case "april":
				case "de abril de": {
						mois = "avril";
						break;
					}

				case "5":
				case "05":
				case "may":
				case "de mayo de":
				case "de maio de": {
						mois = "mai";
						break;
					}

				case "6":
				case "06":
				case "jun":
				case "june":
				case "de junio de":
				case "de junho de": {
						mois = "juin";
						break;
					}

				case "7":
				case "07":
				case "juil.":
				case "juil":
				case "juill.":
				case "juill":
				case "jul":
				case "july":
				case "de julio de":
				case "de julho de": {
						mois = "juillet";
						break;
					}

				case "8":
				case "08":
				case "aou":
				case "aug":
				case "august":
				case "de agosto de": {
						mois = "aout";
						break;
					}

				case "9":
				case "09":
				case "sept.":
				case "sept":
				case "sep.":
				case "sep":
				case "september":
				case "de septiembre de":
				case "de setembro de": {
						mois = "septembre";
						break;
					}

				case "10":
				case "oct.":
				case "oct":
				case "october":
				case "de octubre de":
				case "de outubro de": {
						mois = "octobre";
						break;
					}

				case "11":
				case "nov.":
				case "nov":
				case "november":
				case "de noviembre de":
				case "de novembro de": {
						mois = "novembre";
						break;
					}

				case "12":
				case "decembre":
				case "déc.":
				case "dec.":
				case "dec":
				case "déc":
				case "december":
				case "de diciembre de":
				case "de dezembro de": {
						mois = "décembre";
						break;
					}

				case "spring":
				case "printemps": {
						mois = "printemps";
						break;
					}

				case "summer":
				case "été":
				case "eté": {
						mois = "été";
						break;
					}

				case "fall":
				case "automne": {
						mois = "automne";
						break;
					}

				case "winter":
				case "hiver": {
						mois = "hiver";
						break;
					}
			}
			return mois.ToLower();
		}

		private void inverserAnneeDate() {
			if (tmplParams["année"].hasValue) {
				parametre paramAnnee = tmplParams["année"].parametre;
				removeDateTemplate("année");
				paramAnnee.value = Regex.Replace(paramAnnee.value, " *[.,;]$", "");
				paramAnnee.value = Regex.Replace(paramAnnee.value, @"\[\[(\w+)\]\]", "$1");

				// détermine s'il y a une valeur pour la première édition dans ce paramètre
				Match premiereMatch = Regex.Match(paramAnnee.value, "(?i)[ (–-]+"
						+ "(?:première|1[eè]?re|1{{è?re}}|{{1[èe]?r?e}}|1st)"
						+ @" ?(?:[ée]d(?:\.|ition))(?: en)?"
						+ @" ?(\d{4})\)?");
				if (premiereMatch.Success) {
					if (tmplParams.ContainsKey("année première édition")) {
						addParameter(new parametre(paramAnnee, "année première édition", premiereMatch.Groups[1].Value), paramAnnee);
						paramAnnee.value = paramAnnee.value.Replace(premiereMatch.Value, "");
					}
				}

				// dètermine s'il y a information sur une réédition
				Match rééditMatch = Regex.Match(paramAnnee.value, "(?i)[ (–-]+"
						+ @"(?:réédité|réédition|rééd\.)(?: en| le)?"
						+ @" ?(\d{4})\)?");
				if (rééditMatch.Success) {
					if (tmplParams.ContainsKey("réimpression")) {
						addParameter(new parametre(paramAnnee, "réimpression", rééditMatch.Groups[1].Value), paramAnnee);
						paramAnnee.value = paramAnnee.value.Replace(rééditMatch.Value, "");
					}
				}

				if (!Regex.IsMatch(paramAnnee.value, @"^\d{4}([ -–]+\d{4})?[a-h]?( \(|$)"))
					renameParameter(paramAnnee, "date");
			}
			if (tmplParams["mois"].hasValue) {
				parametre paramMois = tmplParams["mois"].parametre;
				if (paramMois.value == "printemps" || paramMois.value == "été" || paramMois.value == "automne" || paramMois.value == "hiver")
					renameParameter(paramMois, "saison", true);
			}
		}

		private void fusionJourMoisAnnee() {
			if (tmplParams["mois"].hasValue && tmplParams["année"].hasValue) {
				parametre année = tmplParams["année"].parametre;
				string mois = tmplParams["mois"].value;
				string dateValue = "";
				if (tmplParams["jour"].hasValue)
					dateValue = Regex.Replace(tmplParams["jour"].value, "{{1er}}|1{{er}}", "1er");
				if (isNumber(mois))
					dateValue = dateValue + "-" + mois + "-" + année.value;
				else
					dateValue = dateValue + " " + mois + " " + année.value;
				année.value = Regex.Replace(dateValue.Trim(), "[. ,;]+$", "");
				renameParameter(année, "date");
			}
			removeParameter("mois");
			removeParameter("jour");
		}

		// titre
		public void retireItaliqueDuTitre(string titre) {
			if (tmplParams[titre].hasValue) {
				parametre paramTitre = tmplParams[titre].parametre;
				Regex regexGuillemet = new Regex(@"^([""\p{Pi}])(.+)(?(1)[""\p{Pf}])$");
				string titreSansItalique = regexGuillemet.Replace(paramTitre.value, "$2");
				titreSansItalique = Regex.Replace(titreSansItalique.Trim(), @"^''([’\u0000-\u024F-[|{}\[\]]]+)''$", "$1");
				titreSansItalique = regexGuillemet.Replace(titreSansItalique, "$2");
				if (titreSansItalique != paramTitre.value) {
					paramTitre.value = titreSansItalique.Trim();
					FlagSkip = false;
				}
			}
		}

		// format
		public void simplifierFormat() {
			Regex formatElectroniqueRegex = new Regex("(?ixn) ^"
				+ @"( {{ | \[\[(Portable\ Document\ Format\| | DOC\ (computing)\|)? | \[ )?"
				+ "(?<format> pdf | html | vid | aud | doc | rtf |ps )"
				+ @"( \] | \]\] | }} )? $");
			Regex formatLivreRegex = new Regex("(?ix)"
				+ @"(\bvol) | (tome) | (\bt\.) "
				+ @"| (illustré) | (ill.) | (relié) | (broché) | (poche) | (bande-dessinée) | (cartonné) | (\bCD\b)"
				+ "| (Hardcover) | (paperback)"
				+ @"| (\bin-) | ( [\d,]+\ ?(\wm\ ?)? x \ ?\d+ ) | ( \{\{\s*(D?unité(/2)?|nombre)\s*\|  )"
				+ @"| (^[\d,]+\ ?\wm$) | (\bA\d\b) ");

			if (tmplParams["format"].hasValue) {
				parametre param = tmplParams["format"].parametre;
				string value = wikitextToString(param.value);
				bool isOuvrage = (this) is Ouvrage;
				if (formatElectroniqueRegex.IsMatch(value))
					renameParameter(param, "format électronique", !isOuvrage);
				else if (formatLivreRegex.IsMatch(value))
					renameParameter(param, "format livre", isOuvrage);
			}
			if (tmplParams["format électronique"].hasParametre) {
				parametre param = tmplParams["format électronique"].parametre;
				param.value = formatElectroniqueRegex.Replace(wikitextToString(param.value), "${format}");
			}
		}

		// volume
		private void retireVolDeVolume() {
			if (tmplParams["volume"].hasValue) {
				parametre paramVolume = tmplParams["volume"].parametre;
				string volumeSansVol = Regex.Replace(paramVolume.value, @"(?i)^vol(\.|ume)?\s*", "");
				if (volumeSansVol != paramVolume.value) {
					paramVolume.value = volumeSansVol;
					FlagSkip = false;
				}
			}
		}

		// lieu
		public void traductionLieu() {
			if (tmplParams["lieu"].hasValue) {
				parametre lieu = tmplParams["lieu"].parametre;
				// traductions
				lieu.value = Regex.Replace(lieu.value, "Brussels", "Bruxelles");
				lieu.value = Regex.Replace(lieu.value, "California", "Californie");
				lieu.value = Regex.Replace(lieu.value, "Edinburgh", "Édimbourg");
				lieu.value = Regex.Replace(lieu.value, "Geneva", "Genève");
				lieu.value = Regex.Replace(lieu.value, "Ленинград", "Leningrad");
				lieu.value = Regex.Replace(lieu.value, @"М(\.|осква)", "Moscou");
				lieu.value = Regex.Replace(lieu.value, "Poland", "Pologne");
				lieu.value = Regex.Replace(lieu.value, "Philadelphia,?( PA| Pennsylvania)", "Philadelphie");
				lieu.value = Regex.Replace(lieu.value, "Roma", "Rome");
				lieu.value = Regex.Replace(lieu.value, "UK|United Kingdom|England", "Royaume-Uni");

				// simplifications
				lieu.value = Regex.Replace(lieu.value, @"(York St. #4, Covent Garden, )?Lond(on|res);?(,? \(?(UK|United Kingdom|Royaume-Uni|England)\)?)?", "Londres");
				lieu.value = Regex.Replace(lieu.value, @"Los Angeles,? \(?(USA|Californi[ea])\)?", "Los Angeles");
				lieu.value = Regex.Replace(lieu.value, @"New York,? \(?NY\)?", "New York");
				lieu.value = Regex.Replace(lieu.value, @"New York,? \(?(USA|New York)\)?", "New York");
				lieu.value = Regex.Replace(lieu.value, @"Oxford,? \(?Royaume-Uni\)?", "Oxford");
				lieu.value = Regex.Replace(lieu.value, @"Washington,? D\.?C\.?", "Washington");
				lieu.value = Regex.Replace(lieu.value, " (and|&) ", "/");
			}
		}

		// éditeur
		public void fusionlien(string nomParam, string nomLien) {
			parametre param = tmplParams[nomParam].mainParam.parametre;
			parametre paramLien = tmplParams[nomLien].mainParam.parametre;
			Regex regexTexteLienExterne = new Regex(@"^\[(?<url>(https?:)?//(www\d?.)?[^\[\]<> ]+) ?(?<value>[^\]]+)\]");
			if (param != null && paramLien != null && (!param.isEmpty && !paramLien.isEmpty)) {
				parametre paramTitre = tmplParams["titre"].parametre;
				if (paramLien.value.EndsWith(".pdf")) {
					renameParameter(paramLien, "lire en ligne");
					FlagSkip = false;
					param.value = lienInterne(param.value);
				} else if (paramLien.value.Contains("//") && (nomParam == "titre" || nomParam == "titre ouvrage" || paramLien.value.Length > 65 || paramTitre != null && Regex.Replace(paramLien.value.ToLower(), @"[\W_]", "").Contains(Regex.Replace(paramTitre.value.ToLower(), @"[\W_]", "")))
									  ) {
					renameParameter(paramLien, "présentation en ligne");
					FlagSkip = false;
					param.value = lienInterne(param.value);
				} else {
					if (paramLien.value.Contains("//")) {
						param.value = lienInterne(regexTexteLienExterne.Replace(param.value, "${value}"));
						FlagSkip = false;
					} else if (!Regex.IsMatch(param.value, @"^\[\[.+\]\]$") && isExistingPage(paramLien.value)) {
						string paramLienValue = Regex.Replace(paramLien.value, @"^\[\[([^|\[\]]+)\|?.*\]\]$", "$1");
						if (param.value == paramLienValue)
							param.value = "[[" + param.value + "]]";
						else if (lienInterne(param.value).StartsWith("[["))
							param.value = lienInterne(param.value);
						else
							param.value = "[[" + paramLienValue + "|" + param.value + "]]";
					}
					removeParameter(paramLien);
				}
			} else if (param != null && param.value.Contains("//")) {
				string value = regexTexteLienExterne.Replace(param.value, "${value}");
				if (value != param.value) {
					if (nomParam == "titre")
						addParameterBefore(new parametre(param, "présentation en ligne", regexTexteLienExterne.Match(param.value).Groups["url"].Value), param);
					param.value = lienInterne(value);
					FlagSkip = false;
				}
			}
		}

		public string lienInterne(string titre) {
			string testTitre = Regex.Replace(titre, @"^[ÉéEe]d(itions?|\.)?", "Éditions");
			switch (testTitre.ToLower()) {
				case "armand collin":
				case "armand colin": {
						return "[[Armand Colin]]";
					}

				case "éditions de l’aube":
				case "éditions de l'aube": {
						return "[[Éditions de l’Aube]]";
					}

				case "biro": {
						return "[[Biro & Cohen éditeurs|Biro]]";
					}

				case "le buteur":
				case "lebuteur":
				case "le-buteur": {
						return "[[Le Buteur]]";
					}

				case "chemins de fer régionaux et urbains": {
						return "[[Chemins de fer régionaux et urbains]]";
					}

				case "droz":
				case "librairie droz": {
						return "[[Librairie Droz]]";
					}

				case "catalogue/plongee/ editions gap": {
						return "[[Editions Gap]]";
					}

				case "grasset": {
						return "[[Éditions Grasset & Fasquelle|Grasset]]";
					}

				case "hachette": {
						return "[[Hachette Livre|Hachette]]";
					}

				case "lgdj":
				case "l.g.d.j.":
				case "librairie lgdj": {
						return "[[LGDJ]]";
					}

				case "le parisien, édition de la seine-saint-denis": {
						return "[[Le Parisien]], édition de la Seine-Saint-Denis";
					}

				case "plos genetics":
				case "plos genet":
				case "plos genet.": {
						return "[[PLOS Genetics]]";
					}

				case "les presses de l'université laval": {
						return "[[Presses de l'Université Laval]]";
					}

				case "rollingstone":
				case "rolling stone": {
						return "[[Rolling Stone]]";
					}

				case "sud-ouest":
				case "sud ouest": {
						return "[[Sud Ouest]]";
					}

				case "éditions techniques ingénieur": {
						return "[[Techniques de l'ingénieur]]";
					}

				case "éditions toucan":
				case "toucan":
				case "éditions du toucan": {
						return "[[Éditions du Toucan]]";
					}

				default: {
						switch (testTitre.Substring(0, 1).ToUpper() + testTitre.Substring(1)) {
							case "Perrin":
							case "Belin":
							case "Robert Laffont":
							case "Libre Expression":
							case "Racine":
							case "Larousse":
							case "Bréal":
							case "Denoël":
							case "Nouveau Monde": {
									return "[[Éditions " + testTitre + "|" + testTitre + "]]";
								}

							case "Le Monde":
							case "Lancet":
							case "Éditions Perrin":
							case "Éditions Belin":
							case "Monica Companys":
							case "Presses de l'Université Laval":
							case "Eyrolles":
							case "Dupuis":
							case "Les Éditions de Minuit":
							case "Éditions L'Harmattan":
							case "Éditions l'Harmattan":
							case "L'Harmattan":
							case "L’Harmattan":
							case "Cambridge University Press":
							case "Oxford University Press":
							case "Oup":
							case "CRC Press":
							case "Éditions Robert Laffont":
							case "Government Printing Office":
							case "De Boeck":
							case "Éditions techniques de l'ingénieur":
							case "Techniques de l'ingénieur":
							case "Éditions Libre Expression":
							case "Éditions Racine":
							case "Éditions Gap":
							case "Éditions JCL":
							case "Joca Seria":
							case "Éditions Joca Seria":
							case "University Press of Mississippi":
							case "Armand Colin":
							case "Éditions Larousse":
							case "Liana Levi":
							case "Éditions Liana Levi":
							case "marines éditions":
							case "librairie generale de droit et de jurisprudence":
							case "Osprey Publishing":
							case "sciences et avenir":
							case "du9":
							case "bbc news":
							case "Bloomberg Business Week":
							case "Business Week":
							case "Bloomberg Businessweek":
							case "Businessweek":
							case "La Dépêche du Midi":
							case "La dépêche du midi":
							case "Der Spiegel":
							case "Helsingin Sanomat":
							case "Les échos":
							case "Les Échos":
							case "El Heddaf":
							case "Alternatives Économiques":
							case "Alternatives économiques":
							case "Wall Street Journal":
							case "Librairie Plon":
							case "Plon":
							case "Bloud et Gay":
							case "Bourloton":
							case "Librairie Larousse":
							case "Jeanne Laffitte":
							case "Slatkine":
							case "Éditions Plon":
							case "Éditions de l’Aube":
							case "Plon Nourrit":
							case "Bulletin des lois de la République Française":
							case "FACS-UNECTO":
							case "Michel Lafon":
							case "Éditions Arthaud":
							case "Éditions Grasset":
							case "Éditions du Centre vendéen de recherches historiques":
							case "L’Expansion":
							case "Éditions Pierre Horay":
							case "Phaidon":
							case "Persee.fr":
							case "Éditions Gallimard":
							case "Gallimard":
							case "Time Magazine":
							case "Le Seuil": {
									return "[[" + testTitre + "]]";
								}

							case "le temps des cerises": {
									return "[[" + testTitre + " (éditions)|" + testTitre + "]]";
								}

							default: {
									return titre;
								}
						}
					}
			}
		}



		// numéro d'édition
		protected void numeroEdition() {
			parametre edition = findParam("édition", "edition");
			if (edition != null) {
				Regex regexNnum = new Regex("(?ni)^"
+ "(?<ordinalTpl>{{)?"
+ @"(?<num>\d+|première|deuxième|seconde|troisième|quatrième|cinquième|second|third)"
+ "(?(<ordinalTpl>)(e|re)}}|(e|{{e}}|éme|re|{{re}}|ére|st|nd|rd|th)?)"
+ @"\.?( ?[eé]d(ition|\.|))?"
+ @"([, ]*(mise|à|jour|et|rev|révisée|complétée|illustrée|and|updated|\.|,))*");
				Match numMatch = regexNnum.Match(wikitextToString(edition.value));
				if (numMatch.Success) {
					renameParameter(edition, "numéro d'édition");
					edition.value = numMatch.Groups["num"].Value;
					// edition.value = Regex.Replace(edition.value, "(?i)first", "1")  'First est un éditeur
					edition.value = Regex.Replace(edition.value, "(?i)second", "2");
					edition.value = Regex.Replace(edition.value, "(?i)third", "3");
				} else if (Regex.IsMatch(edition.value, "(?i)illustrated|illustrée|illustrate"))
					removeParameter(edition);
				if (tmplParams["éditeur"].hasValue && Regex.IsMatch(edition.value, "(?i)^first")) {
					renameParameter(edition, "numéro d'édition");
					edition.value = "1";
				}
			}
		}

		// isbn, issn
		public void isxn() {
			bool hasChanged = false;
			Regex regIsxn = new Regex(@"^(is[bs]n)( *)([\dX -]{8,})$");
			Match match;
			foreach (parametre param in invalidParameters) {
				if (param.isNamed && param.isEmpty) {
					match = regIsxn.Match(param.name);
					if (match.Success) {
						hasChanged = renameParameter(param, match.Groups[1].Value);
						// param.sep = match.Groups(2).Value
						param.value = match.Groups[3].Value;
						getSep(param);
					}
				} else if (!param.isNamed) {
					match = regIsxn.Match(param.value);
					if (match.Success) {
						hasChanged = renameParameter(param, match.Groups[1].Value);
						// param.sep = match.Groups(2).Value
						param.value = match.Groups[3].Value;
						getSep(param);
					}
				} else if (param.isNamed && Regex.IsMatch(param.name, @"^is[\w-[\d]]{1,3}\d?$")) {
					string stdBaseName = "isbn" + Regex.Match(param.name, @"\d").Value;
					if (param.value.Length < 10)
						stdBaseName = "issn" + Regex.Match(param.name, @"\d").Value;
					string stdName = stdBaseName;
					int i = 1;
					while (tmplParams[stdName].hasParametre) {
						i = i + 1;
						stdName = stdBaseName + i.ToString();
					}
					hasChanged = renameParameter(param, stdName);
				}
			}
			if (hasChanged) {
				FlagSkip = false;
			}
		}

		private void isbn10() {
			if (!tmplParams["isbn3"].mainParam.hasValue) {
				string Isbn1 = typeIsbn(tmplParams["isbn"].mainParam.parametre);
				string Isbn2 = typeIsbn(tmplParams["isbn2"].mainParam.parametre);
				bool pre2007 = tmplParams["année"].hasValue && isNumber(tmplParams["année"].value) && System.Convert.ToInt32(tmplParams["année"].value) < 2007
						|| tmplParams["date"].hasValue && Regex.IsMatch(tmplParams["date"].value, @"\d{4}") && System.Convert.ToInt32(Regex.Match(tmplParams["date"].value, @"\d{4}").Value) < 2007;
				if (Isbn1.Length == 10 && Isbn2.Length == 13 && Isbn1.Substring(0, 9) == Isbn2.Substring(3, 9)) {
					if (pre2007)
						renameParameter(tmplParams["isbn2"].mainParam.parametre, "ean");
					else {
						renameParameter(tmplParams["isbn"].mainParam.parametre, "isbn10");
						renameParameter(tmplParams["isbn2"].mainParam.parametre, "isbn");
					}
				} else if (Isbn1.Length == 13 && Isbn2.Length == 10 && Isbn1.Substring(3, 9) == Isbn2.Substring(0, 9)) {
					if (pre2007) {
						renameParameter(tmplParams["isbn"].mainParam.parametre, "ean");
						renameParameter(tmplParams["isbn2"].mainParam.parametre, "isbn");
					} else
						renameParameter(tmplParams["isbn2"].mainParam.parametre, "isbn10");
				}
			}
		}
		private string typeIsbn(parametre param) {
			if (param != null)
				return Regex.Replace(param.value, "[ -]", "");
			else
				return "";
		}

		// liens externes
		private void separerBnf() {
			if (tmplParams["commentaire"].hasValue) {
				parametre paramComm = tmplParams["commentaire"].parametre;
				Match bnfMatch = Regex.Match(wikitextToString(paramComm.value), @"(?i)^{{\s*(?:FR)?BNF\s*\|\s*(\w+)/?\s*}}$");
				if (bnfMatch.Success) {
					paramComm.value = bnfMatch.Groups[1].Value;
					renameParameter(paramComm, "bnf");
				}
			} else if (tmplParams["présentation en ligne"].hasValue) {
				parametre paramPresent = tmplParams["présentation en ligne"].parametre;
				Match bnfMatch = Regex.Match(paramPresent.value, @"^http://catalogue\.bnf\.fr/ark:/12148/(cb\d{8}\w)$");
				if (bnfMatch.Success) {
					paramPresent.value = bnfMatch.Groups[1].Value;
					renameParameter(paramPresent, "bnf");
				}
			}
			parametre paramIdentifiant = findParam("identifiant");
			if (paramIdentifiant != null) {
				Match bnfMatch = Regex.Match(paramIdentifiant.value, @"ark:/12148/(cb\d{8}\w)$");
				Match gallicaMatch = Regex.Match(paramIdentifiant.value, @"ark:/12148/(bpt\w{2}\d{6}\w.*)");
				if (bnfMatch.Success) {
					if (tmplParams["bnf"].hasValue)
						removeParameter(paramIdentifiant);
					else {
						paramIdentifiant.value = bnfMatch.Groups[1].Value;
						renameParameter(paramIdentifiant, "bnf");
					}
				} else if (gallicaMatch.Success) {
					if (tmplParams["lire en ligne"].hasValue)
						removeParameter(paramIdentifiant);
					else {
						paramIdentifiant.value = "http://gallica.bnf.fr/ark:/12148/" + gallicaMatch.Groups[1].Value;
						renameParameter(paramIdentifiant, "lire en ligne");
					}
				}
			}
		}

		private void supprimerGoogleLivreVide(string nomParam) {
			if (tmplParams[nomParam].hasValue) {
				parametre param = tmplParams[nomParam].parametre;
				if (Regex.IsMatch(wikitextToString(param.value), @"^{{\s*[Gg]oogle Livres\s*}}"))
					removeParameter(param);
			}
		}

		// page
		private void modeleP() {
			foreach (parametre param in invalidParameters) {
				if (!param.isNamed && (Regex.IsMatch(param.value, @"\s*(¤§t|{{)"))) {
					string value = wikitextToString(param.value);
					Match passageMatch = Regex.Match(value, @"{{p. *(?:\|\s*([\w\s-]+))?}}(.*)");
					if (passageMatch.Success) {
						if (renameParameter(param, "passage"))
							param.value = passageMatch.Groups[1].Value + passageMatch.Groups[2].Value;
						return;
					}
				}
			}
		}

		public void suprimmerPageDePassage(string nomParam) {
			if (tmplParams[nomParam].hasValue) {
				parametre param = tmplParams[nomParam].parametre;
				string page = Regex.Replace(param.value, @"(?i)^(?:p|pp|pages?)(?![\w-[\d]])\.?\s*", "");
				page = Regex.Replace(wikitextToString(page), @"(?i)^{{p\.\|?(}})?(.+)(?(1)|}})$", "$2");
				if (param.value != page) {
					param.value = page;
					FlagSkip = false;
				}
			}
		}

		public void suprimerPageDePagesTotales(string nomParam) {
			if (tmplParams[nomParam].hasValue) {
				parametre param = tmplParams[nomParam].parametre;
				string page = Regex.Replace(param.value, @"(?i) ?p(ages|p|)\.?$", "");
				page = Regex.Replace(wikitextToString(page), @"(?i)^ ?{{unité\|(.+)\|p(ages|p|)\.?}}$", "$1");
				if (param.value != page) {
					param.value = page;
					FlagSkip = false;
				}
			}
		}

		private void paragraphe() {
			parametre paragraphe = findParam("paragraphe");
			if (paragraphe != null) {
				if (this is Article && tmplParams["volume"].hasValue && !tmplParams["numéro"].hasValue)
					renameParameter(paragraphe, "numéro");
				else {
					paragraphe.value = "{{§|" + paragraphe.value + "}}";
					parametre passage = findParam("page", "passage", "p.", "pp.");
					if (passage == null && this is Article)
						passage = findParam("pages");
					if (passage == null)
						renameParameter(paragraphe, "passage");
					else {
						if (passage.value == "")
							passage.value = paragraphe.value;
						else
							passage.value = passage.value + ", " + paragraphe.value;
						removeParameter(paragraphe);
					}
				}
			}
		}
	}


	class Ouvrage : Biblio {
		public Ouvrage(string name, string contenuModele, WikitextClass parent, string[] ntemplateNames)
			: base(name, contenuModele, parent, ntemplateNames) {
		}
		public Ouvrage(TemplateClass template, string[] ntemplateNames)
			: base(template, ntemplateNames) {
		}
		public Ouvrage(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Ouvrage" }) {
		}
		public Ouvrage(TemplateClass template)
			: base(template, new string[] { "Ouvrage" }) {
		}

		protected override void addStandardParametres() {
			base.addStandardParametres();

			tmplParams.add(new parametreStandard("lire en ligne", "u"), new parametreStandard("url texte"), new parametreStandard("url"));
			tmplParams.add("nature ouvrage");
			tmplParams.add("collection", new parametreStandard("series", "o"));
			tmplParams.add("numéro dans la collection", "numéro dans collection");
			tmplParams.add("numéro d'édition");
			tmplParams.add("année première édition", new parametreStandard("origyear", "d"), new parametreStandard("année première impression", "o"), new parametreStandard("année d'origine", "o"), new parametreStandard("première édition", "o"));
			tmplParams.add("réimpression", new parametreStandard("publi", "d"));
			tmplParams.add("pages totales");
			if (this.templateName == "Chapitre")
				tmplParams.add("pages");
			else
				tmplParams.addAlias(tmplParams["pages totales"], new parametreStandard("pages", "o"));

			tmplParams.add("wikisource");
			tmplParams.add(new parametreStandard("écouter en ligne", "u"));
			tmplParams.add("partie");
			tmplParams.add("numéro chapitre", new parametreStandard("numéro", "d"));
			tmplParams.add("titre chapitre", new parametreStandard("chap", "d"), new parametreStandard("chapter", "o"), new parametreStandard("chapitre", "o"));
			tmplParams.add("passage", new parametreStandard("page", "o"), new parametreStandard("p.", "o"));
			tmplParams.add("référence");
			tmplParams.add("référence simplifiée", new parametreStandard("ref", "d"));

			tmplParams.parametreToDeleteIfEmpty("lire en ligne", "nature ouvrage", "collection", "numéro dans la collection", "numéro d'édition", "année première édition", "réimpression", "pages totales", "pages", "wikisource", "écouter en ligne", "partie", "numéro chapitre", "titre chapitre", "référence", "référence simplifiée");
		}

		public override void fix() {
			parametre param;
			parametre paramP = findParam("p", "p.");
			if (parent.name != "ref" && !tmplParams["pages totales"].hasParametre && paramP != null) {
				if (paramP.isEmpty)
					removeParameter(paramP);
				else
					renameParameter(paramP, "pages totales");
			}

			if (editors())
				return;
			if (periodique())
				return;

			if (name.Trim().ToLower() == "cite book") {
				parametre paramRef = findParam("ref");
				if (paramRef != null) {
					if (paramRef.value.StartsWith("¤§t"))
						removeParameter(paramRef);
					else
						renameParameter(paramRef, "id");
				}
				if (!tmplParams["langue"].hasValue)
					addParameter(new parametre(Parametres[1], "langue", "en"), 0);
			}

			numeroChapitre();
			numeroEdition();
			volumes();

			renameParameter("origdate", "année première édition");
			renameParameter("année originale", "année première édition");
			renameParameter("année origine", "année première édition");
			renameParameter("année première publication", "année première édition");
			renameParameter("première année édition", "année première édition");
			renameParameter("première édition", "année première édition");
			renameParameter("première edition", "année première édition");
			renameParameter("origannée", "année première édition");
			renameParameter("coll", "collection");
			renameParameter("titre collection", "collection");
			renameParameter("numéro de chapitre", "numéro chapitre");
			renameParameter("numéro collection", "numéro dans la collection");
			renameParameter("numéro édition", "numéro d'édition");
			renameParameter("ed", "numéro d'édition");
			renameParameter("Seiten", "pages totales");
			renameParameter("réédition", "réimpression");
			renameParameter("rééditions", "réimpression");
			renameParameter("référence simplifié", "ref");
			renameParameter("contribution", "titre chapitre");
			renameParameter("titre du chapitre", "titre chapitre");
			renameParameter("titre chapter", "titre chapitre");
			renameParameter("capítol", "titre chapitre");
			renameParameter("work", "titre chapitre");
			renameParameter("titre d'origine", "titre original");
			renameParameter("trans_title", "traduction titre");
			renameParameter("titre traduit", "traduction titre");
			renameParameter("trans_titre", "traduction titre");
			renameParameter("texte", "lire en ligne");
			renameParameter("url texte", "lire en ligne");
			renameParameter("résumé", "présentation en ligne");

			param = findParam("anné2005e");
			if (param != null) {
				param.value = "2005";
				renameParameter(param, "année");
			}

			removeParameter("nouvelle édition");

			base.fix();

			renameParameter("url texte", "lire en ligne", true);
			// renameParameter("url", "lire en ligne", minor:=True)
			renameParameter("site", "éditeur");
			renameParameter("imprimeur", "éditeur");
			supprimerLangDeTitre(tmplParams["titre chapitre"].parametre);
			supprimerLangDeTitre(tmplParams["titre volume"].parametre);
			supprimerLangDeTitre(tmplParams["titre tome"].parametre);
		}

		public override void otherFix() {
			parametre param;

			numeroChapitre();

			param = tmplParams["pages"].parametre;
			if (param != null && this.templateName == "Ouvrage") {
				if (Regex.IsMatch(param.value, @"\d+([-–età, ]+\d+)+") || Regex.IsMatch(param.value, "^p(p|.|age| )"))
					renameParameter(param, "passage");
			}

			param = tmplParams["page"].parametre;
			if (param == null)
				param = tmplParams["p."].parametre;
			if (param != null && Regex.IsMatch(param.value, @"\d+ p(\.|ages)"))
				renameParameter(param, "pages totales");

			param = tmplParams["pages"].parametre;
			if (param != null && !tmplParams["passage"].hasValue && (Regex.IsMatch(param.value, @"p(\.|ages?)?") || parent.name == "ref" && Regex.IsMatch(param.value, @"\d+( ?[–—-] ?| et | à |, )\d+")))
				renameParameter(param, "passage");


			if (tmplParams["série"].hasValue && !tmplParams["collection"].hasValue)
				renameParameter(tmplParams["série"].parametre, "collection");

			suprimmerPageDePassage("passage");
			suprimerPageDePagesTotales("pages totales");

			fusionlien("titre", "lien titre");

			retireItaliqueDuTitre("titre");
			retireItaliqueDuTitre("titre chapitre");
			retireItaliqueDuTitre("titre volume");
			retireItaliqueDuTitre("titre tome");
			supprimerLangDeTitre(tmplParams["titre chapitre"].parametre);
			supprimerLangDeTitre(tmplParams["titre volume"].parametre);
			supprimerLangDeTitre(tmplParams["titre tome"].parametre);

			base.otherFix();
		}

		private bool editors() {
			parametre paramEditor = findParam("editor", "editors", "editor-last", "editor1-last", "editor-nom", "editor1-nom");
			if (paramEditor != null) {
				if (paramEditor.isEmpty)
					removeParameter(paramEditor);
				else
					// s'il n'y a pas d'auteurs editors => auteurs, sinon test si cela doit être un modèle Chapitre
					if (!tmplParams["nom1"].mainParam.hasValue && !tmplParams["auteur"].mainParam.hasValue && !tmplParams["auteurs"].mainParam.hasValue) {
						renameParameter("editors", "auteurs");
						renameParameter("editor", "auteur");
						renameParameter("editor-first", "prénom1");
						renameParameter("editor1-first", "prénom1");
						renameParameter("editor2-first", "prénom2");
						renameParameter("editor3-first", "prénom3");
						renameParameter("editor4-first", "prénom4");
						renameParameter("editor-prénom", "prénom1");
						renameParameter("editor1-prénom", "prénom1");
						renameParameter("editor2-prénom", "prénom2");
						renameParameter("editor3-prénom", "prénom3");
						renameParameter("editor4-prénom", "prénom4");
						renameParameter("editor-last", "nom1");
						renameParameter("editor1-last", "nom1");
						renameParameter("editor2-last", "nom2");
						renameParameter("editor3-last", "nom3");
						renameParameter("editor4-last", "nom4");
						renameParameter("editor-nom", "nom1");
						renameParameter("editor1-nom", "nom1");
						renameParameter("editor2-nom", "nom2");
						renameParameter("editor3-nom", "nom3");
						renameParameter("editor4-nom", "nom4");
						renameParameter("editor-link", "authorlink1");
						renameParameter("editor1-link", "authorlink1");
						renameParameter("editor2-link", "authorlink2");
						renameParameter("editor3-link", "authorlink3");
					} else if (this.GetType().Equals(typeof(Ouvrage))) {
						bool isChapitre = false;
						foreach (parametre param in Parametres) {
							if (!param.isEmpty && param.name == "titre chapitre" || param.name == "chap" || param.name == "chapitre" || param.name == "chapter" || param.name == "contribution" || param.name == "encyclopedia") {
								isChapitre = true;
								break;
							}
						}
						if (isChapitre) {
							changeClassTo(typeof(Chapitre));
							return true;
						}
					}
			}
			return false;
		}

		private bool periodique() {
			parametre paramPeriodique = findParam("périodique", "journal", "revue", "newspaper", "periodical");
			if (paramPeriodique != null && paramPeriodique.value != "") {
				removeParameter("pages totales");
				removeParameter("numéro d'édition");
				changeClassTo(typeof(Article));
				return true;
			}
			return false;
		}

		private void numeroChapitre() {
			parametre chapitre = findParam("chapitre", "chap", "chapter", "titre chapitre");
			if (chapitre != null) {
				// test si le paramètre chapitre contient un numéro et/ou un nom de chapitre
				string numPattern = "(?xni) ^"
					+ @"( (chapitre|chap|chapter|capitulo) [\.:\ ]* )?"
					+ @"(?<num> \b [\dIVXLCM\.–-]+ \b )"
					+ "[,.]?"
					+ @"([ :–-]+ (?<titre> [\ \w',-[\d]]+ \b ) )? $";
				Match numMatch = Regex.Match(chapitre.value, numPattern);
				if (numMatch.Success) {
					// si oui, on ajoute le paramètre numéro chapitre, ou on renomme le paramètre existant.
					if (numMatch.Groups["titre"].Success) {
						parametre numeroChapitre = new parametre(chapitre, "numéro chapitre", numMatch.Groups["num"].Value);
						addParameterBefore(numeroChapitre, chapitre);
						chapitre.value = numMatch.Groups["titre"].Value;
					} else if (chapitre.name != "titre chapitre" && !tmplParams["numéro chapitre"].hasValue) {
						renameParameter(chapitre, "numéro chapitre");
						chapitre.value = numMatch.Groups["num"].Value;
					}
				}
			}
		}

		private void volumes() {
			parametre param = findParam("volumes", "tomes");
			if (param != null) {
				simplifierFormat();
				parametre formatLivre = tmplParams["format livre"].parametre;
				if (formatLivre == null) {
					if (isNumber(param.value))
						param.value = param.value + " " + param.name;
					renameParameter(param, "format livre");
				} else {
					if (formatLivre.value == "")
						formatLivre.value = param.value + param.name;
					else
						formatLivre.value = formatLivre.value + ", " + param.value + " " + param.name;
					removeParameter(param);
				}
			}
		}
	}

	class Chapitre : Ouvrage {
		public Chapitre(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Chapitre" }) {
		}
		public Chapitre(TemplateClass template)
			: base(template, new string[] { "Chapitre" }) {
		}

		protected override void addStandardParametres() {
			base.addStandardParametres();
			tmplParams.add("auteurs ouvrage", "auteur ouvrage");
			tmplParams.add("titre ouvrage", new parametreStandard("ouvrage", "d"));
			tmplParams.add("sous-titre ouvrage");
			tmplParams.add("lien titre ouvrage");
			tmplParams.add("sous-titre chapitre");
			tmplParams.add("page début chapitre");
			tmplParams.parametreToDeleteIfEmpty("sous-titre ouvrage", "lien titre ouvrage", "sous-titre chapitre", "page début chapitre");
		}

		public override void fix() {
			editors();
			renameParameter("encyclopedia", "titre ouvrage");
			renameParameter("aurteur ouvrage", "auteurs ouvrage");
			renameParameter("auteur de l'ouvrage", "auteurs ouvrage");
			renameParameter("auteur ouvrager", "auteurs ouvrage");
			renameParameter("auteur volume", "auteurs ouvrage");
			renameParameter("lien ouvrage", "lien titre ouvrage");
			renameParameter("lien titre", "lien titre ouvrage");

			base.fix();

			titre();
			retireItaliqueDuTitre("titre ouvrage");
			supprimerLangDeTitre(tmplParams["titre ouvrage"].parametre);
		}

		public override void otherFix() {
			fusionlien("titre ouvrage", "lien titre ouvrage");
			base.otherFix();
		}

		private void editors() {
			renameParameter("editors", "auteurs ouvrage");
			renameParameter("editor", "auteurs ouvrage");
			parametre auteursOuvrage = findParam("auteurs ouvrage");
			parametre prenom = findParam("editor-first", "editor-first1", "editor1-first");
			parametre nom = findParam("editor-last", "editor-last1", "editor1-last");
			if (nom != null) {
				if (auteursOuvrage == null) {
					renameParameter(nom, "auteurs ouvrage");
					auteursOuvrage = nom;
					if (prenom != null) {
						auteursOuvrage.value = prenom.value + " " + nom.value;
						removeParameter(prenom);
					}
				} else if (prenom == null) {
					auteursOuvrage.value = auteursOuvrage.value + ", " + nom.value;
					removeParameter(nom);
				} else {
					auteursOuvrage.value = auteursOuvrage.value + ", " + prenom.value + " " + nom.value;
					removeParameter(prenom);
					removeParameter(nom);
				}
				int i = 1;
				do {
					i = i + 1;
					prenom = findParam("editor-first" + i.ToString(), "editor" + i.ToString() + "-first");
					nom = findParam("editor-last" + i.ToString(), "editor" + i.ToString() + "-last");
					if (nom == null)
						break;
					else if (prenom == null) {
						auteursOuvrage.value = auteursOuvrage.value + ", " + nom.value;
						removeParameter(nom);
					} else {
						auteursOuvrage.value = auteursOuvrage.value + ", " + prenom.value + " " + nom.value;
						removeParameter(prenom);
						removeParameter(nom);
					}
				}
				while (true);
			}
		}

		private void titre() {
			parametre titre = findParam("titre");
			if (titre != null) {
				if (findParam("titre chapitre", "chap") == null)
					renameParameter(titre, "titre chapitre");
				else
					renameParameter(titre, "titre ouvrage");
			}

			parametre sousTitre = findParam("sous-titre");
			if (sousTitre != null) {
				if (findParam("sous-titre chapitre") == null)
					renameParameter(sousTitre, "sous-titre chapitre");
				else
					renameParameter(sousTitre, "sous-titre ouvrage");
			}
		}
	}

	class Article : Biblio {
		public Article(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Article", "Périodique" }) {
		}
		public Article(TemplateClass template)
			: base(template, new string[] { "Article", "Périodique" }) {
		}

		protected override void addStandardParametres() {
			base.addStandardParametres();

			tmplParams.add(new parametreStandard("lire en ligne", "u"), new parametreStandard("url texte"), new parametreStandard("url"), new parametreStandard("texte", "d"));
			tmplParams.add("périodique", new parametreStandard("journal"), new parametreStandard("revue", "d"));
			tmplParams.add("langue périodique");
			tmplParams.add("lien périodique", new parametreStandard("lien journal", "o"));

			tmplParams.add("numéro", new parametreStandard("issue", "o"), new parametreStandard("n°", "o"), new parametreStandard("number", "o"));
			tmplParams.add("titre numéro");
			tmplParams.add("pages", new parametreStandard("page"), new parametreStandard("passage"), new parametreStandard("p.", "d"), new parametreStandard("pp.", "d"));

			tmplParams.parametreToDeleteIfEmpty("langue périodique", "lien périodique", "numéro", "titre numéro");
		}

		public override void fix() {
			renameParameter("lien pérdioque", "lien périodique");
			renameParameter("lien périodi", "lien périodique");
			renameParameter("lien périoque", "lien périodique");
			renameParameter("lien pérodiue", "lien périodique");
			renameParameter("newspaper", "périodique");
			renameParameter("periodical", "périodique");
			renameParameter("numéro dans volume", "numéro");
			renameParameter("numéro de page", "pages");
			renameParameter("pagine", "pages");
			renameParameter("pages totales", "pages");

			numeroEdition();

			base.fix();

			if (tmplParams["périodique"].hasValue) {
				parametre periodique = tmplParams["périodique"].parametre;
				// suprime l'italique et les guillements de périodique
				periodique.value = Regex.Replace(periodique.value, @"^(['«""]+)(.*)(\1|»)$", "$2");

				suppimerLangDePériodique(periodique);
				fusionlien("périodique", "lien périodique");

				periodique.value = periodique.value.Replace("[[L'Acadie nouvelle|L'Acadie Nouvelle]]", "[[L'Acadie Nouvelle]]");
			} else if (tmplParams["éditeur"].hasValue)
				renameParameter(tmplParams["éditeur"].parametre, "périodique");
			else
				renameParameter("agency", "périodique");
			renameParameter("agency", "auteur institutionnel");

			suprimmerPageDePassage("pages");
			if (!article.jeuxVideo)
				retireItaliqueDuTitre("titre");
		}

		private void suppimerLangDePériodique(parametre paramPeriodique) {
			string idModele = Regex.Match(paramPeriodique.value, @"^¤§t(\d+)§¤$").Groups[1].Value;
			if (idModele != "" && article.templateList[System.Convert.ToInt32(idModele)] is Langue) {
				parametre paramLangP = article.templateList[System.Convert.ToInt32(idModele)]
.tmplParams["1"].mainParam.parametre;
				parametre paramTexte = article.templateList[System.Convert.ToInt32(idModele)]
												.tmplParams["texte"].mainParam.parametre;
				if (paramLangP != null && paramTexte != null) {
					parametre paramLangA = tmplParams["langue"].parametre;
					if (paramLangA == null) {
						addParameter(new parametre(paramPeriodique, "langue", "fr"), 0);
						addParameter(new parametre(paramPeriodique, "langue périodique", paramLangP.value), 1);
					} else if (paramLangA.value != paramLangP.value)
						addParameter(new parametre(paramPeriodique, "langue périodique", paramLangP.value), paramLangA);
					paramPeriodique.value = Regex.Replace(paramTexte.value, @"^(['«""]+)(.*)(\1|»)$", "$2");
				}
			}
		}
	}

	class LienWeb : Biblio {
		private bool _siteAdded = false;
		public LienWeb(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Lien web", "Lien brisé" }) {
		}
		public LienWeb(TemplateClass template)
			: base(template, new string[] { "Lien web" }) {
		}

		protected override void addStandardParametres() {
			base.addStandardParametres();
			tmplParams.add(new parametreStandard("url", "u"), new parametreStandard("url texte", "ud"), new parametreStandard("lire en ligne"), new parametreStandard("texte", "o"), new parametreStandard("lien", "d"));
			tmplParams.add("description");
			tmplParams.add("site", new parametreStandard("website", "d"));
			tmplParams.add("périodique", new parametreStandard("journal", "o"), new parametreStandard("revue", "o"));
			tmplParams.addAlias(tmplParams["série"], new parametreStandard("work"));
			tmplParams.add("pages", new parametreStandard("page"), new parametreStandard("passage"), new parametreStandard("p.", "o"));
			tmplParams.add("dead-url", "deadurl", "brisé le");

			tmplParams.add("en ligne le", "en ligne", "mis en ligne le");

			tmplParams.parametreToDeleteIfEmpty("périodique", "série", "pages", "année");
		}

		public override void fix() {
			renameParameter("site web", "site");
			base.fix();

			if (tmplParams["en ligne le"].hasParametre) {
				renameParameter(tmplParams["en ligne le"].parametre, "date");
				removeDateTemplate("date");
			}
		}

		public override void otherFix() {
			suppressionAuteur();
			déplacerTitreVersDescription();

			base.otherFix();

			site();
			renameParameter("agency", "auteur institutionnel");
			suppressionAuteur();
			removeDateTemplate("consulté le");
		}

		private void site() {
			renameParameter("obra", "site");
			if (!tmplParams["site"].hasValue) {
				if (!tmplParams["périodique"].hasValue && !tmplParams["éditeur"].hasValue && !tmplParams["série"].hasValue && tmplParams["lire en ligne"].hasValue) {
					parametre url = tmplParams["lire en ligne"].parametre;
					string siteValue = Regex.Match(url.value, @"(?<=^(https?:)?//(www\d?\.)?)(?!www\d?\.)([^/]+)").Value;
					if (siteValue.Length > 0) {
						parametre site = new parametre(url, "site", siteValue);
						addParameter(site, url);
						_siteAdded = true;
					}
				}
			} else {
				parametre paramSite = tmplParams["site"].parametre;
				// paramSite.value = Regex.Replace(paramSite.value, "^\[(https?:)?(//)?(www\d?.)?[^\[\] ]+ (?<site>[^\[\]]+)\]", "${site}")
				// paramSite.value = Regex.Replace(paramSite.value, "^(https?:)?(//)?(www\d?.)?(?<site>[^\[\] ]+)(?<!/)/?", "${site}")
				if (paramSite.value == "Site francegenweb")
					paramSite.value = "francegenweb.org";
			}
		}

		private void suppressionAuteur() {
			if (tmplParams["auteur1"].hasValue && !tmplParams["auteur2"].hasValue || tmplParams["auteur institutionnel"].hasValue) {
				parametre paramAuteur = tmplParams["auteur institutionnel"].parametre;
				if (paramAuteur == null)
					paramAuteur = tmplParams["auteur1"].parametre;
				string auteur = Regex.Replace(paramAuteur.value.ToLower(), @"\[\[([^|\[\]]+\|)?([^\[\]]+)\]\]", "$2");
				parametre paramSite = tmplParams["site"].parametre;
				if (Regex.IsMatch(auteur, @"^(la )?rédac(\.|tion)?(?= |$)") || (tmplParams["éditeur"].hasValue && (tmplParams["éditeur"].value.ToLower() == auteur || tmplParams["éditeur"].value.ToLower() == auteur.Replace(" ", ""))) || (tmplParams["périodique"].hasValue && (tmplParams["périodique"].value.ToLower() == auteur || tmplParams["périodique"].value.ToLower() == auteur.Replace(" ", "")))) {
					removeParameter(paramAuteur);
					supprAuteur = true;
				} else if (tmplParams["site"].hasValue && (paramSite.value.ToLower().Contains(auteur) || paramSite.value.ToLower().Contains(auteur.Replace(" ", "")))) {
					if (_siteAdded)
						removeParameter(paramSite);
					else {
					}
				}
			}
		}

		public void déplacerTitreVersDescription() {
			if (tmplParams["titre"].hasValue && !tmplParams["description"].hasValue && tmplParams["langue"].hasValue && !Regex.IsMatch(tmplParams["langue"].value, "(?i)^fr")) {
				string titre = tmplParams["titre"].value.ToLower();
				string Langue = tmplParams["langue"].value.ToLower();
				if (Regex.IsMatch(titre, @"\b(à|site officiel|histoire d[eu'])\b") || Regex.IsMatch(titre, @"\b(le|la|les)\b") && !(Langue == "es" || Langue == "it"))
					renameParameter("titre", "description");
			}
		}
	}



	class Ancre : idTemplate {
		// ne prend en compte que les deux premières ancres.
		public override string id {
			get {
				return tmplParams["1"].value;
			}
		}
		public override string id2 {
			get {
				if (tmplParams["2"].hasValue)
					return tmplParams["2"].value;
				else
					return id;
			}
		}

		public Ancre(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Ancre" }) {
			tmplParams.add("1");
			tmplParams.add("2");
			tmplParams.add("3");
			tmplParams.add("4");
			tmplParams.add("5");
			tmplParams.add("6");
		}

		public override void changeId(string newId) {
			if (unnamedIndex < 9) {
				for (int i = unnamedIndex; i >= 1; i += -1) {
					parametre param = tmplParams[i.ToString()].parametre;
					renameParameter(param, (i + 1).ToString());
				}
				addParameter(new parametre(this, 1, newId, "|", ""), 0);
				unnamedIndex = unnamedIndex + 1;
			} else
				tmplParams["1"].value = newId;
		}

		public override void fix() {
			name = Regex.Replace(name, "Anchor", "Ancre");
			base.fix();
		}
	}

	class Citation : TemplateClass {
		public Citation(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Citation", "Citation étrangère" }) {
			tmplParams.add("1");
			if (name.ToLower() == "citation étrangère")
				tmplParams.add("langue", "lang");
		}

		public override void fix() {
			parametre param1 = tmplParams["1"].parametre;

			// essaye de trouver le contenu si le param 1 est vide
			if (param1 != null && !Parametres[0].isValid && !Parametres[0].isEmpty) {
				if (Parametres[1].name != "title")
					Parametres[1].value = Parametres[1].name + "=" + Parametres[1].value;
				Parametres[1].changeName("1");
			}

			if (param1 == null)
				return;

			// supprime le modèle langue en renommant en citation étrangère
			if (Regex.IsMatch(param1.value, @"^('')?¤§t\d+§¤\.?('')?\.?$")) {
				Langue langTempl = article.templateList[System.Convert.ToInt32(Regex.Match(param1.value, @"¤§t(\d+)§¤").Groups[1].Value)] as Langue;
				if (langTempl != null) {
					string point = "";
					if (Regex.IsMatch(param1.value, @"\.('')?$"))
						point = ".";
					param1.value = Regex.Replace(langTempl.tmplParams["texte"].value, "^''(.+)''$", "$1") + point;
					if (Regex.Matches(param1.value, "''").Count == 1)
						param1.value = param1.value.Replace("''", "");
					addParameter(new parametre(param1, "langue", langTempl.tmplParams["1"].value), 0);
					if (name.Length == 8)
						name = name + " étrangère";
					else
						name = "citation étrangère";
					FlagSkip = false;
				}
			}

			// intégre l'italique
			if (Regex.IsMatch(param1.value, "(?<!')''(?!')") && Regex.IsMatch(parent.parsedString, @"((?<!')|(?<='''))''\(?" + strip)) {
				param1.value = "''" + param1.value + "''";
				parent.parsedString = Regex.Replace(parent.parsedString, @"''\(?" + strip + "[ .,)]*)('')?", "$1");
			}

			// transforme en citation bloc si la citation contient un chagnement de paragraphe ou une liste
			if (Regex.IsMatch(param1.value, @"\n *[\n*#:;] *\S")) {
				if (Regex.IsMatch(parent.parsedString, strip + @" *¤§h\d+§¤")) {
					HtmlElement @ref = article.htmlElementList[System.Convert.ToInt32(Regex.Match(parent.parsedString, strip + @" *¤§h(\d+)§¤").Groups[1].Value)];
					if (@ref.name == "ref") {
						Parametres.Add(new parametre(this, "référence", @ref.strip, "|", "=", ""));
						parent.parsedString = parent.parsedString.Replace(@ref.strip, "");
					}
				}
				changeClassTo(typeof(CitationBloc));
			}

			base.fix();
		}
	}

	class CitationBloc : TemplateClass {
		public CitationBloc(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Citation bloc", "Citation étrangère bloc" }) {
			addStandardParametres();
		}
		public CitationBloc(TemplateClass template)
			: base(template, new string[] { "Citation bloc", "Citation étrangère bloc" }) {
			addStandardParametres();
		}

		public void addStandardParametres() {
			tmplParams.add("1");
			tmplParams.add("2");
			tmplParams.add("3");
			tmplParams.add("style");
			tmplParams.add("sans italique");
			if (name == "Citation bloc étrangère")
				name = "Citation étrangère bloc";
			else if (name == "citation bloc étrangère")
				name = "citation étrangère bloc";
			if (name.ToLower() == "citation étrangère bloc")
				tmplParams.add("langue", "lang");
		}

		public override void fix() {
			renameParameter("auteur", "2");
			renameParameter("Auteur", "2");
			renameParameter("author", "2");


			// supprime le modèle langue en renommant en citation étrangère
			parametre param1 = tmplParams["1"].parametre;
			if (Regex.IsMatch(param1.value, @"^('')?¤§t\d+§¤\.?('')?\.?$")) {
				Langue langTempl = article.templateList[System.Convert.ToInt32(Regex.Match(param1.value, @"¤§t(\d+)§¤").Groups[1].Value)] as Langue;
				if (langTempl != null) {
					string point = "";
					if (Regex.IsMatch(param1.value, @"\.('')?$"))
						point = ".";
					param1.value = Regex.Replace(langTempl.tmplParams["texte"].value, "^''(.+)''$", "$1") + point;
					if (Regex.Matches(param1.value, "''").Count == 1)
						param1.value = param1.value.Replace("''", "");

					addParameter(new parametre(param1, "langue", langTempl.tmplParams["1"].value), 0);
					if (name.Length == 13)
						name = name.Replace("bloc", "étrangère bloc");
					else
						name = "Citation étrangère bloc";
					FlagSkip = false;
				}
			}

			// si le modèle commence dans une liste, ajoute le paramètre ad'hoc
			if (Regex.IsMatch(parent.parsedString, "(?m)^[*#:;].*?" + strip))
				Parametres.Add(new parametre(this, "dans une liste", "oui", "|", "=", ""));
			base.fix();
		}
	}

	class Citeref : idTemplate {
		public override string id {
			get {
				return generateHref();
			}
		}
		public override string id2 {
			get {
				return id;
			}
		}

		public Citeref(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Citeref" }) {
			tmplParams.add("1");
			tmplParams.add("2");
			tmplParams.add("3");
			tmplParams.add("4");
			tmplParams.add("5");
			tmplParams.add("6");
		}

		public string generateHref() {
			string id = "";
			for (int i = 1; i <= 5; i++) {
				if (tmplParams[(i + 1).ToString()].hasParametre)
					id = id + tmplParams[i.ToString()].value;
				else
					break;
			}
			return id;
		}

		public override void changeId(string newId) {
			tmplParams["1"].value = newId;
			while (tmplParams["3"].hasParametre)
				removeParameter(tmplParams["2"].parametre);
		}
	}

	class CommuneLimitrophe : TemplateClass {
		public CommuneLimitrophe(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Communes limitrophes" }) {
			tmplParams.add("commune", new parametreStandard("ville", "o"), new parametreStandard("village", "o"));
			tmplParams.add("nord");
			tmplParams.add("ouest");
			tmplParams.add("sud");
			tmplParams.add("est");
			tmplParams.add("nord-est");
			tmplParams.add("sud-ouest");
			tmplParams.add("sud-est");
			tmplParams.add("nord-ouest");
			tmplParams.add("align");
			tmplParams.add("élision");
			tmplParams.add("width");
			tmplParams.add("enclave ");
		}
	}

	class ModeleDate : TemplateClass {
		public ModeleDate(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Date", "Date-" }) {
			tmplParams.add("1", "jour");
			tmplParams.add("2", "mois");
			tmplParams.add("3", "annee");
			tmplParams.add("4", "qualificatif");

			tmplParams.add("âge", new parametreStandard("age", "d"));
			tmplParams.add("julien");
			tmplParams.add("avJC");
		}

		public override void otherFix() {
			switch (unnamedIndex) {
				case 7: {
						parametre param7 = findParam("7");
						tmplParams["4"].value = param7.value;
						removeParameter(param7);
						break;
					}

				case 6: {
						parametre a = findParam("6");
						if (Regex.IsMatch(a.value, @"^\d{3,4}$"))
							this.name = "Date de décès";
						else if (!a.isEmpty) {
							removeParameter(a);
							removeParameter(findParam("5"));
							removeParameter(findParam("4"));
						}

						break;
					}

				case 5: {
						parametre param5 = findParam("5");
						if (tmplParams["4"].value == "") {
							tmplParams["4"].value = param5.value;
							removeParameter(param5);
						} else if (param5.value == "1" || param5.value == tmplParams["4"].value || param5.value == tmplParams["3"].value || param5.value == tmplParams["1"].value + " " + tmplParams["2"].value
												  )
							removeParameter(param5);
						break;
					}
			}

			parametre param = findParam(unnamedIndex.ToString());
			while (param != null && param.isEmpty) {
				removeParameter(param);
				param = findParam(unnamedIndex.ToString());
			}

			moisAnnee();

			base.otherFix();
		}

		public string getDate() {
			if (tmplParams["1"].parametre == null)
				return "";
			else if (!tmplParams["3"].hasValue && tmplParams["1"].hasValue && Regex.IsMatch(tmplParams["1"].value, @"\d{4}"))
				return tmplParams["1"].value;
			else {
				string param2 = "";
				if (tmplParams["2"].hasValue)
					param2 = tmplParams["2"].value;
				string param3 = "";
				if (tmplParams["3"].hasValue)
					param3 = tmplParams["3"].value;
				if (isNumber(param2) && isNumber(param3))
					return tmplParams["1"].value + "-" + param2 + "-" + param3;
				return (tmplParams["1"].value + " " + param2 + " " + param3).Trim();
			}
		}

		public void moisAnnee() {
			if (tmplParams["1"].hasValue && tmplParams["2"].hasValue) {
				parametre param1 = tmplParams["1"].parametre;
				parametre param2 = tmplParams["2"].parametre;
				if (Regex.IsMatch(param1.value, @"^\p{L}+$") && Regex.IsMatch(param2.value, @"\d{4}")) {
					param1.value = param1.value + " " + param2.value;
					removeParameter(param2);
					FlagSkip = false;
				}
			}
		}
	}

	class Elu : TemplateClass {
		public Elu(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Élu" }) {
			tmplParams.add("Début");
			tmplParams.add("Fin");
			tmplParams.add("Identité");
			tmplParams.add("Parti", new parametreStandard("Etiquette", "o"));
			tmplParams.add("Qualité");
		}

		public override void fix() {
			removeParameter("align");
			removeParameter("Quantité");
			removeParameter("Commentaire");
			removeParameter("Ordre");

			renameParameter("Nom", "Identité");
			renameParameter("Qualité Maire", "Qualité");

			base.fix();
		}
	}

	class GoogleLivres : TemplateClass {
		public GoogleLivres(string name, string contenuModele, WikitextClass cm)
			: base(name, contenuModele, cm, new string[] { "Google Livres", "Google books", "Google Books", "Google livres" }) {
			tmplParams.add("id", "1", new parametreStandard("BuchID", "o"));
			tmplParams.add("titre", "2");
			tmplParams.add("page", new parametreStandard("Seite", "o"));
			tmplParams.add("couv");
			tmplParams.add("romain");
			tmplParams.add("surligne");
		}
	}

	class Harvard : TemplateClass {
		public string Href;

		public Harvard(string name, string contenuModele, WikitextClass parent)
			: base(name, contenuModele, parent, new string[] { "Harvsp", "Harv", "Sfn", "Référence Harvard sans parenthèses", "Référence Harvard", "Harvnb", "Harvard" }) {
			// flagRemoveEmptyKnownParam = True

			tmplParams.add("1");
			tmplParams.add("2");
			tmplParams.add("3");
			tmplParams.add("4");
			tmplParams.add("5");
			tmplParams.add("p", "pp", new parametreStandard("passage"), new parametreStandard("page"), new parametreStandard("pages"), new parametreStandard("p.", "o"));
			tmplParams.add("loc");
			tmplParams.add("texte");
			tmplParams.add("id", new parametreStandard("réf", "d"));
		}

		public override void fix() {
			Href = generateHref();

			base.fix();

			// suprimmeParamEnTrop()
			// integreAnnee()
			integreLoc("vol.", "vol", "volume");
			integreLoc("chap.", "chap", "chapitre", "ch");
			integreLoc("tome");
			integreTitre();

			parametre annee = findParam("année");
		}

		public string generateHref() {
			string id = "";
			parametre paramId = tmplParams["id"].parametre;
			if (paramId != null && paramId.value != "")
				return paramId.value;
			else
				for (int i = 1; i <= 5; i++) {
					if (tmplParams[i.ToString()].hasParametre)
						id = id + tmplParams[i.ToString()].value;
					else
						break;
				}
			return id;
		}

		public void suprimmeParamEnTrop() {
			if (unnamedIndex > 5) {
				parametre annee = findParam(unnamedIndex.ToString());
				if (annee != null) {
					if (isNumber(annee.value)) {
						findParam("5").value = annee.value;
						do
							removeParameter("6");
						while (findParam("6") != null);
					}
					if (!tmplParams["id"].hasValue)
						Href = generateHref();
				}
			}
		}

		public void integreAnnee() {
			if (unnamedIndex > 5)
				return;
			parametre paramAnnee = findParam("année", "annee");
			if (paramAnnee != null) {
				if (unnamedIndex > 0) {
					parametre lastUnnamed = tmplParams[unnamedIndex.ToString()].parametre;
					if (lastUnnamed.value == paramAnnee.value)
						removeParameter(paramAnnee);
					else if (!isNumber(lastUnnamed.value)) {
						if (Parametres.IndexOf(paramAnnee) < Parametres.IndexOf(lastUnnamed)) {
							removeParameter(paramAnnee);
							addParameter(paramAnnee, lastUnnamed);
						}
						renameParameter(paramAnnee, (unnamedIndex + 1).ToString());
						paramAnnee.sep = "";
						unnamedIndex = unnamedIndex + 1;
						Href = generateHref();
					}
				} else if (unnamedIndex == 0 && tmplParams["texte"].hasValue) {
					parametre texte = tmplParams["texte"].parametre;
					if (!texte.value.Contains(paramAnnee.value))
						texte.value = Regex.Replace(texte.value, "^(.+?)([,.]?)$", "$1, " + paramAnnee.value + "$2");
					removeParameter(paramAnnee);
				}
			}
		}

		public void integreLoc(string part, params string[] partAlias) {
			if (!tmplParams["loc"].hasValue) {
				parametre partParam = findParam(part, partAlias);
				if (partParam != null) {
					renameParameter(partParam, "loc");
					if (!partParam.value.StartsWith(part.Replace(".", "")))
						partParam.value = part + " " + partParam.value;
					FlagSkip = false;
				}
			}
		}

		public void integreTitre() {
			if (!tmplParams["loc"].hasValue) {
				parametre paramTitre = findParam("titre");
				if (paramTitre != null) {
					renameParameter(paramTitre, "loc");
					if (!paramTitre.value.StartsWith("''"))
						paramTitre.value = "''" + paramTitre.value + "''";
					FlagSkip = false;
				}
			}
		}
	}

	class InfoboxAvionMilitaire : TemplateClass {
		public InfoboxAvionMilitaire(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Infobox Avion militaire" }) {
			isLowerOnly = true;
			flagOnlyNamedParam = true;

			tmplParams.add("nom");
			tmplParams.add("image");
			tmplParams.add("légende");
			tmplParams.add("type");
			tmplParams.add("statut");
			tmplParams.add("constructeur");
			tmplParams.add("investissement");
			tmplParams.add("équipage");
			tmplParams.add("moteur");
			tmplParams.add("envergure", true);
			tmplParams.add("longueur", true);
			tmplParams.add("hauteur", true);
			tmplParams.add("carburant", true);
			tmplParams.add("altitude vitesse maxi", true);
			tmplParams.add("plafond", true);
			tmplParams.add("endurance");
			tmplParams.add("avionique");
			tmplParams.add("pays constructeur", new parametreStandard("paysconstructeur", "o"));
			tmplParams.add("premier vol", new parametreStandard("premiervol", "o"));
			tmplParams.add("mise en service", new parametreStandard("miseenservice", "o"));
			tmplParams.add("date retrait", new parametreStandard("dateretrait", "o"));
			tmplParams.add("coût unitaire", new parametreStandard("coûtunitaire", "o"));
			tmplParams.add("nombre construits", new parametreStandard("nombreconstruit", "o"));
			tmplParams.add("nombre de moteurs", new parametreStandard("nombredemoteur", "o"));
			tmplParams.add("type de moteur", new parametreStandard("typedemoteur", "o"));
			tmplParams.add("puissance unitaire", new parametreStandard("puissanceunitaire", "o"));
			tmplParams.add("poussée unitaire", new parametreStandard("pousséeunitaire", "o"));
			tmplParams.add("plan 3 vues", new parametreStandard("plan3vues", "o"));
			tmplParams.add("taille plan 3 vues", new parametreStandard("plan3vues-taille", "o"));
			tmplParams.add("surface aile", true, new parametreStandard("surfaceaile", "o"));
			tmplParams.add("masse à vide", true, new parametreStandard("masseàvide", "o"));
			tmplParams.add("masse avec arme", true, new parametreStandard("masseavecarme", "o"));
			tmplParams.add("masse maxi", true, new parametreStandard("massemaxi", "o"));
			tmplParams.add("vitesse de croisière", true, new parametreStandard("vitessecruising", "o"));
			tmplParams.add("vitesse maxi", true, new parametreStandard("vitessemaxi", "o"));
			tmplParams.add("vitesse maxi mach", true, new parametreStandard("vitessemaximach", "o"));
			tmplParams.add("vitesse mini", true, new parametreStandard("vitessemini", "o"));
			tmplParams.add("vitesse ascensionnelle", true, new parametreStandard("vitesseascens", "o"));
			tmplParams.add("facteur de charge", new parametreStandard("facteurdecharge", "o"));
			tmplParams.add("rayon d'action", true, new parametreStandard("rayonaction", "o"));
			tmplParams.add("charge alaire", true, new parametreStandard("chargealaire", "o"));
			tmplParams.add("poids poussée", true, new parametreStandard("poidspoussée", "o"));
			tmplParams.add("poids puissance", true, new parametreStandard("poidspuissance", "o"));
			tmplParams.add("arme principale", new parametreStandard("armeprincipale", "o"));
			tmplParams.add("arme auxiliaire", new parametreStandard("armeaux", "o"));
		}

		public override void fix() {
			renameParameter("vitessedecroisière", "vitesse de croisière");
			renameParameter("vitesse cruising", "vitesse de croisière");
			renameParameter("puissanceunitaire_ch", "puissance unitaire");
			renameParameter("vitesse ascension", "vitesse ascensionnelle");
			renameParameter("kérosène", "carburant");
			renameParameter("poids maxi", "masse maxi");
			renameParameter("poids à vide", "masse à vide");
			renameParameter("production", "nombre construits");
			renameParameter("armes principales", "arme principale");
			renameParameter("vitessecroisière", "vitesse de croisière");
			base.fix();

			fixVitesseMaxi();

			if (tmplParams["vitesse maxi mach"].hasValue)
				tmplParams["vitesse maxi mach"].value = tmplParams["vitesse maxi mach"].value.Replace(".", ",");
			if (tmplParams["poids poussée"].hasValue)
				tmplParams["poids poussée"].value = tmplParams["poids poussée"].value.Replace(".", ",");

			removeDateTemplate("premier vol");
			removeDateTemplate("mise en service");
			removeDateTemplate("date retrait");
		}

		public void fixVitesseMaxi() {
			parametre vitesseMaxi = findParam("vitessemaxi", "vitesse maxi");
			if (vitesseMaxi == null)
				return;
			if (vitesseMaxi.isEmpty)
				return;
			if (vitesseMaxi.value.Contains(@"¤§t\d+§¤")) {
				vitesseMaxi.value = Regex.Replace(wikitextToString(vitesseMaxi.value), "{{formatnum:([^}]+)}}", "$1");
				vitesseMaxi.value = Regex.Replace(wikitextToString(vitesseMaxi.value), @"{{unité\|([^}]+)}}", "$1");
			}
			if (vitesseMaxi != null && !isNumber(vitesseMaxi.value)) {
				parametre newPos = findParam("vitesse maxi mach");
				if (newPos == null)
					newPos = vitesseMaxi;
				Match m = Regex.Match(vitesseMaxi.value, @"\(?[àAÀ]? ?(\d+) ?m\b ?:?\)?");
				if (m.Success) {
					addParameter(new parametre(vitesseMaxi, "altitude vitesse maxi", m.Groups[1].Value), newPos);
					vitesseMaxi.value = vitesseMaxi.value.Replace(m.Value, "").Trim();
				}
				m = Regex.Match(vitesseMaxi.value, @"\(?([aA]?u niveau (du sol|de la mer)) ?:?\)?");
				if (m.Success) {
					addParameter(new parametre(vitesseMaxi, "altitude vitesse maxi", m.Groups[1].Value), newPos);
					vitesseMaxi.value = vitesseMaxi.value.Replace(m.Value, "").Trim();
				}
				m = Regex.Match(vitesseMaxi.value, @"\(([^)]+)\)");
				if (m.Success) {
					addParameter(new parametre(vitesseMaxi, "altitude vitesse maxi", m.Groups[1].Value), newPos);
					vitesseMaxi.value = vitesseMaxi.value.Replace(m.Value, "").Trim();
				}
				vitesseMaxi.value = vitesseMaxi.value.Replace("<small> *</small>", "").Trim();
				m = Regex.Match(vitesseMaxi.value, @"^\s*(~ ?)?(\d+|\d+ ?/ ?\d+|\d+<ref>.+</ref>|\d+<ref .+/>)\s*$");
				if (!m.Success)
					vitesseMaxi.value = vitesseMaxi.value + " <!-- à vérifier -->";
			}
		}
	}

	class InfoboxÉlection : TemplateClass {
		public InfoboxÉlection(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Infobox Élection" }) {
			isLowerOnly = true;
			flagOnlyNamedParam = true;

			tmplParams.add("entete");
			tmplParams.add("pays");
			tmplParams.add("variante");
			tmplParams.add("blason");
			tmplParams.add("nom élection", new parametreStandard("nom_élection", "o"));
			tmplParams.add("date élection", new parametreStandard("date_élection", "o"));
			tmplParams.add("endispute");
			tmplParams.add("endispute2");
			tmplParams.add("élection précédente", new parametreStandard("élection_précédente", "o"));
			tmplParams.add("date précédente", new parametreStandard("date_précédente", "o"));
			tmplParams.add("élection suivante", new parametreStandard("élection_suivante", "o"));
			tmplParams.add("date suivante", new parametreStandard("date_suivante", "o"));
			tmplParams.add("type");
			tmplParams.add("habitants");
			tmplParams.add("enregistrés");
			tmplParams.add("votants");
			tmplParams.add("votants2");
			tmplParams.add("participation");
			tmplParams.add("participation ref", new parametreStandard("participation_ref", "o"));
			tmplParams.add("participation pré", new parametreStandard("participation_pré", "o"));
			tmplParams.add("participation2");
			tmplParams.add("participation2 ref", new parametreStandard("participation2_ref", "o"));
			tmplParams.add("valables");
			tmplParams.add("valables2");
			tmplParams.add("blancs");
			tmplParams.add("blancs2");
			tmplParams.add("nuls");
			tmplParams.add("nuls2");
			tmplParams.add("campagne");
			tmplParams.add("débat");
			tmplParams.add("nom élus");
			tmplParams.add("nom élus2");
			tmplParams.add("carte titre", new parametreStandard("carte_titre", "o"));
			tmplParams.add("carte");
			tmplParams.add("taille carte", new parametreStandard("taille_carte", "o"));
			tmplParams.add("légende carte1", new parametreStandard("légende_carte1", "o"));
			tmplParams.add("légende carte2", new parametreStandard("légende_carte2", "o"));
			tmplParams.add("carte sous-titre", new parametreStandard("carte_sous-titre", "o"));
			tmplParams.add("diagramme titre", new parametreStandard("diagramme_titre", "o"));
			tmplParams.add("diagramme");
			tmplParams.add("taille diagramme", new parametreStandard("taille_diagramme", "o"));
			tmplParams.add("légende1");
			tmplParams.add("légende2");
			tmplParams.add("diagramme2 titre", new parametreStandard("diagramme2_titre", "o"));
			tmplParams.add("diagramme2");
			tmplParams.add("taille diagramme2", new parametreStandard("taille_diagramme2", "o"));
			tmplParams.add("légende1 2", new parametreStandard("légende1_2", "o"));
			tmplParams.add("légende2 2", new parametreStandard("légende2_2", "o"));
			tmplParams.add("titre barres", new parametreStandard("titre_barres", "o"));
			tmplParams.add("sous-titre barres", new parametreStandard("sous-titre_barres", "o"));
			tmplParams.add("poste");
			tmplParams.add("prédécesseur");
			tmplParams.add("titre prédécesseur", new parametreStandard("titre_prédécesseur", "o"));
			tmplParams.add("parti prédécesseur", new parametreStandard("parti_prédécesseur", "o"));
			tmplParams.add("sexe prédécesseur", new parametreStandard("sexe_prédécesseur", "o"));
			tmplParams.add("successeur");
			tmplParams.add("titre successeur", new parametreStandard("titre_successeur", "o"));
			tmplParams.add("parti successeur", new parametreStandard("parti_successeur", "o"));
			tmplParams.add("sexe successeur", new parametreStandard("sexe_successeur", "o"));
			tmplParams.add("sup entête", new parametreStandard("sup_entête", "o"));
			tmplParams.add("sup entête2", new parametreStandard("sup_entête2", "o"));
			tmplParams.add("sup contenu", new parametreStandard("sup_contenu", "o"));
			tmplParams.add("sup contenu2", new parametreStandard("sup_contenu2", "o"));
			tmplParams.add("site web", new parametreStandard("site_web", "o"));
			for (int i = 1; i <= 14; i++) {
				tmplParams.add("image" + i);
				tmplParams.add("couleur" + i);
				tmplParams.add("candidat" + i);
				tmplParams.add("colistier" + i);
				tmplParams.add("sexe" + i);
				tmplParams.add("parti" + i);
				tmplParams.add("coalition" + i);
				tmplParams.add("chef" + i);
				tmplParams.add("vote électoral" + i, new parametreStandard("vote_électoral" + i, "o"));
				tmplParams.add("vote électoral" + i + " pré", new parametreStandard("vote_électoral" + i + "_pré", "o"));
				tmplParams.add("votes" + i);
				tmplParams.add("votes" + i + " pré", new parametreStandard("votes" + i + "_pré", "o"));
				tmplParams.add("votes2v" + i);
				tmplParams.add("pourcentage" + i);
				tmplParams.add("pourcentage" + i + " pré", new parametreStandard("pourcentage" + i + "_pré", "o"));
				tmplParams.add("pourcentage2v" + i);
				tmplParams.add("sièges" + i);
				tmplParams.add("sièges" + i + " pré", new parametreStandard("sièges" + i + "_pré", "o"));
				tmplParams.add("sénateurs" + i);
				tmplParams.add("sénateurs" + i + " pré", new parametreStandard("sénateurs" + i + "_pré", "o"));
				tmplParams.add("représentants" + i);
				tmplParams.add("représentants" + i + " pré", new parametreStandard("représentants" + i + "_pré", "o"));
				tmplParams.add("députés" + i);
				tmplParams.add("députés" + i + " pré", new parametreStandard("députés" + i + "_pré", "o"));
				tmplParams.add("conseillers" + i);
				tmplParams.add("maires" + i);
				tmplParams.add("présidences" + i);
				tmplParams.add("présidences" + i + " pré", new parametreStandard("présidences" + i + "_pré", "o"));
				for (int j = 1; j <= 9; j++)
					tmplParams.add("parti" + j + " coalition" + i, new parametreStandard("parti" + j + "_coalition" + i, "o"));
			}
			for (int i = 1; i <= 5; i++) {
				tmplParams.add("barres" + i);
				tmplParams.add("couleur barres" + i, new parametreStandard("couleur_barres" + i, "o"));
				tmplParams.add("pourcentage barres" + i, new parametreStandard("pourcentage_barres" + i, "o"));
			}
			for (int j = 2; j <= 4; j++) {
				tmplParams.add("titre barresQ" + j, new parametreStandard("titre_barresQ" + j, "o"));
				for (int i = 1; i <= 5; i++) {
					tmplParams.add("barres" + i + "Q" + j);
					tmplParams.add("couleur barres" + i + "Q" + j, new parametreStandard("couleur_barres" + i + "Q" + j, "o"));
					tmplParams.add("pourcentage barres" + i + "Q" + j, new parametreStandard("pourcentage_barres" + i + "Q" + j, "o"));
				}
			}
		}

		public override void fix() {
			suppressionX();
			suppressionFormatnum();
			// nomélus()

			renameParameter("participation_réf", "participation ref ");
			renameParameter("inscrits", "enregistrés");

			// encours, abstention, fond
			removeParameter("encours");
			removeParameter("abstentions");
			removeParameter("fond");

			// carte taille
			renameParameter("carte_taille", "taille carte");

			// symbole
			for (int i = 1; i <= 4; i++)
				removeParameter("symbole" + i);

			// titre barre seul
			if (tmplParams["titre barres"].hasParametre) {
				if (tmplParams["titre barres"].parametre.isEmpty && !tmplParams["barres1"].hasParametre) {
					removeParameter(tmplParams["titre barres"].parametre);
					removeParameter(tmplParams["sous-titre barres"].parametre);
				}
			}

			// votes v2
			for (int i = 1; i <= 13; i++) {
				if (i == 2)
					continue;
				parametre vote = findParam("votes" + i + "v2");
				if (i != 2 && vote != null && !tmplParams["votes2v" + i].hasValue)
					renameParameter("votes" + i + "v2", "votes2v" + i);
				parametre pourcent = findParam("pourcentage" + i + "v2");
				if (pourcent != null && !tmplParams["pourcentage2v" + i].hasValue)
					renameParameter("pourcentage" + i + "v2", "pourcentage2v" + i);
			}

			base.fix();

			votepré();
		}

		public void nomélus() {
			// Ajout de nom élus
			parametre premierParamCandidats = findParam("image1", "couleur1", "candidat1", "parti1", "coalition1", "vote électoral1", "votes1", "pourcentage1");
			parametre paramélus = findParam("sénateurs1", "maires1", "présidences1", "députés1", "représentants1", "conseillers1");
			if (paramélus != null) {
				string nom = paramélus.name.TrimEnd('1');
				if (paramélus.isEmpty) {
					parametre élux;
					for (int i = 1; i <= 14; i++) {
						élux = tmplParams[nom + i].parametre;
						if (élux != null) {
							if (élux.isEmpty) {
								removeParameter(élux);
								removeParameter(nom + i + " pré");
								removeParameter(nom + i + "_pré");
							} else
								élux.value = élux.value + "<!-- pas de valeur pour 1 ? -->";
						}
					}
					nomélus();
				} else {
					int position;
					if (premierParamCandidats == null) {
						parametre nomélu = new parametre(paramélus, "nom élus", nom);
						position = Parametres.IndexOf(paramélus);
						addParameter(nomélu, position);
					} else {
						parametre nomélu = new parametre(premierParamCandidats, "nom élus", nom);
						position = Parametres.IndexOf(premierParamCandidats);
						addParameter(nomélu, position);
					}

					parametre paramélus2 = findParam("députés1", "représentants1", "conseillers1");
					if (paramélus2 != null && paramélus2 != paramélus && !paramélus2.isEmpty) {
						nom = paramélus2.name.TrimEnd('1');
						parametre nomélu = new parametre(paramélus2, "nom élus2", nom);
						addParameter(nomélu, position + 1);
					}
					FlagSkip = false;
				}
			}
		}

		private void suppressionX() {
			// suppression des paramètres <X> ou <Y>
			List<parametre> paramToBeRemoved = new List<parametre>();
			foreach (parametre param in Parametres) {
				if (param.name.Contains("<") || Regex.IsMatch(param.name, @"\w+[XY](_|\b)"))
					paramToBeRemoved.Add(param);
			}
			foreach (parametre param in paramToBeRemoved)
				removeParameter(param);
		}

		private void suppressionFormatnum() {
			foreach (parametre param in Parametres) {
				if (Regex.IsMatch(param.value, @"^¤§t\d+§¤$")) {
					TemplateClass tmpl = article.templateList[System.Convert.ToInt32(Regex.Match(param.value, @"\d+").Value)];
					if (Regex.IsMatch(tmpl.name, "formatnum:"))
						param.value = Regex.Match(tmpl.name, @"formatnum:\s*([\d,.  ]+)").Groups[1].Value;
				}
				if (Regex.IsMatch(param.value, @"^\d([\d  ]|&nbsp;)*((?<=\d)[,.]\d([\d  ]|&nbsp;)*)?$")) {
					param.value = Regex.Replace(param.value, "[  ]|&nbsp;", "");
					param.value = Regex.Replace(param.value, ",", ".");
				}
				if (Regex.IsMatch(param.name, @"^votes(2v)?\d+$") || Regex.IsMatch(param.name, @"^vote électoral(2v)?\d+$") || Regex.IsMatch(param.name, "^enregistrés$") || Regex.IsMatch(param.name, "^votants2?$") || Regex.IsMatch(param.name, "^valables2?$") || Regex.IsMatch(param.name, "^blancs2?$") || Regex.IsMatch(param.name, "^nuls2?$") || Regex.IsMatch(param.name, "^blancs2?$") || Regex.IsMatch(param.name, "^habitants$"))
					param.value = Regex.Replace(param.value, @"(?<=\d)[,.](?=\d)", "");
			}
		}

		public void votepré() {
			// remplacemet de vote pré par pourcentage pré
			for (int i = 1; i <= 14; i++) {
				if (tmplParams["votes" + i + " pré"].hasParametre) {
					// supprime le paramètre s'il est vide, ajoute un commentaire s'il y a une valeur
					parametre param = tmplParams["votes" + i + " pré"].parametre;
					if (param.isEmpty)
						removeParameter(param);
					else if (!tmplParams["pourcentage" + i + " pré"].hasValue)
						param.value = param.value + "<!-- à remplacer par le paramètre pourcentage" + i + " pré -->";
					// ajoute un paramètre pourcentage pré
					parametre pourcent = tmplParams["pourcentage" + i].parametre;
					if (pourcent != null && !tmplParams["pourcentage" + i + " pré"].hasParametre)
						addParameter(new parametre(pourcent, "pourcentage" + i + " pré", ""), pourcent);
				}

				// idem avec vote électoral pré

				if (tmplParams["vote électoral" + i + " pré"].hasParametre) {
					// supprime le paramètre s'il est vide, ajoute un commentaire s'il y a une valeur
					parametre param = tmplParams["vote électoral" + i + " pré"].parametre;
					if (param.isEmpty)
						removeParameter(param);
					else
						param.value = param.value + "<!-- à remplacer par le paramètre pourcentage" + i + " pré -->";
					// ajoute un paramètre pourcentage pré
					parametre pourcent = tmplParams["pourcentage" + i].parametre;
					if (pourcent != null && !tmplParams["pourcentage" + i + " pré"].hasParametre)
						addParameter(new parametre(pourcent, "pourcentage" + i + " pré", ""), pourcent);
				}
			}
		}
	}

	class InfoboxGare : TemplateClass {
		public InfoboxGare(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Infobox Gare" }) {
			falgRemoveDoubloonsEvenIfDifferent = true;

			tmplParams.add("nom");
			tmplParams.add("image");
			tmplParams.add("légende");
			tmplParams.add("titre pays");
			tmplParams.add("pays");
			tmplParams.add("titre ville");
			tmplParams.add("ville");
			tmplParams.add("titre quartier");
			tmplParams.add("quartier");
			tmplParams.add("adresse");
			tmplParams.add("latitude");
			tmplParams.add("longitude");
			tmplParams.add("géolocalisation");
			tmplParams.add("propriétaire");
			tmplParams.add("exploitant");
			tmplParams.add("titre services");
			tmplParams.add("services");
			tmplParams.add("lignes");
			tmplParams.add("voies");
			tmplParams.add("quais");
			tmplParams.add("hauteur");
			tmplParams.add("transit");
			tmplParams.add("zone");
			tmplParams.add("altitude");
			tmplParams.add("mise en service");
			tmplParams.add("ouverture");
			tmplParams.add("fermeture");
			tmplParams.add("architecte");
			tmplParams.add("protection");
			tmplParams.add("titre corresp 1");
			tmplParams.add("titre corresp 2");
			tmplParams.add("titre corresp 3");
			tmplParams.add("titre corresp 4");
			tmplParams.add("corresp 1");
			tmplParams.add("corresp 2");
			tmplParams.add("corresp 3");
			tmplParams.add("corresp 4");
			tmplParams.add("schéma");
		}
	}

	class InfoboxMusiqueOeuvre : TemplateClass {
		public InfoboxMusiqueOeuvre(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Infobox Musique (œuvre)" }) {
			flagOnlyNamedParam = true;
			falgRemoveDoubloonsEvenIfDifferent = true;

			tmplParams.add("album");
			tmplParams.add("album précédent");
			tmplParams.add("album suivant");
			tmplParams.add("artiste");
			tmplParams.add("artiste principal");
			tmplParams.add("auteur");
			tmplParams.add("auteur-compositeur");
			tmplParams.add("charte");
			tmplParams.add("classement");
			tmplParams.add("compositeur");
			tmplParams.add("critique");
			tmplParams.add("date album préc");
			tmplParams.add("date album suiv");
			tmplParams.add("date single préc");
			tmplParams.add("date single suiv");
			tmplParams.add("divers");
			tmplParams.add("durée");
			tmplParams.add("enregistré");
			tmplParams.add("enregistré lieu");
			tmplParams.add("face A");
			tmplParams.add("face B");
			tmplParams.add("featuring");
			tmplParams.add("film");
			tmplParams.add("format");
			tmplParams.add("genre");
			tmplParams.add("historique");
			tmplParams.add("image");
			tmplParams.add("label");
			tmplParams.add("langue");
			tmplParams.add("langue du titre");
			tmplParams.add("légende");
			tmplParams.add("numéro piste préc");
			tmplParams.add("numéro piste suiv");
			tmplParams.add("piste précédente");
			tmplParams.add("piste suivante");
			tmplParams.add("producteur");
			tmplParams.add("réalisateur");
			tmplParams.add("single");
			tmplParams.add("single précédent");
			tmplParams.add("single suivant");
			tmplParams.add("sorti");
			tmplParams.add("titre");
			tmplParams.add("titre article en italique");
			tmplParams.add("upright");
			tmplParams.add("édition");
		}

		public override void fix() {
			renameParameter("nom", "titre");
			renameParameter("Next album", "album suivant");
			renameParameter("Last album", "album précédent");
			renameParameter("Artist", "artiste");
			renameParameter("groupe", "artiste");
			renameParameter("Groupe", "artiste");
			renameParameter("parolier", "auteur");
			renameParameter("type", "charte");
			renameParameter("Type", "charte");
			renameParameter("Musique", "compositeur");
			renameParameter("musique", "compositeur");
			renameParameter("date album précédent", "date album préc");
			renameParameter("date single précédent", "date single préc");
			renameParameter("date album suivant", "date album suiv");
			renameParameter("année album suivant", "date album suiv");
			renameParameter("date piste préc", "date single préc");
			renameParameter("date single suivant", "date single suiv");
			renameParameter("date piste suiv", "date single suiv");
			renameParameter("temps", "durée");
			renameParameter("enregistrement", "enregistré");
			renameParameter("enregistrement lieu", "enregistré lieu");
			renameParameter("lieu d'enregistrement", "enregistré lieu");
			renameParameter("lieu", "enregistré lieu");
			renameParameter("lieu enregistré", "enregistré lieu");
			renameParameter("face a", "face A");
			renameParameter("A-side", "face A");
			renameParameter("face b", "face B");
			renameParameter("B-side", "face B");
			renameParameter("face-b", "face B");
			renameParameter("style", "genre");
			renameParameter("discographie", "label");
			renameParameter("distributeur", "label");
			renameParameter("production", "producteur");
			renameParameter("[[Réalisateur artistique]]", "réalisateur");
			renameParameter("réalisateur artistique", "réalisateur");

			removeParameter("pays");

			SelectImage("Cover");
			SelectImage("cover");
			SelectImage("couverture");
			SelectImage("Couverture");
			SelectImage("logo");
			SelectImage("photo");
			SelectImage("Pochette");

			pécédentSuivant();
			chansonPrecSuiv();
			séparerDatePrecSuiv();

			listeSingle();

			base.fix();
		}

		public void pécédentSuivant() {
			string charte = "";
			if (tmplParams["charte"].hasValue)
				charte = tmplParams["charte"].value;
			switch (charte.ToLower()) {
				case "single":
				case "simple": {
						renameParameter("suivant", "single suivant");
						renameParameter("précédent", "single précédent");
						renameParameter("date préc", "date single préc");
						renameParameter("date suiv", "date single suiv");
						break;
					}

				case "chanson":
				case "single promotionnel":
				case "démo":
				case "instrumental": {
						renameParameter("suivant", "piste suivante");
						renameParameter("précédent", "piste précédente");
						break;
					}

				default: {
						renameParameter("suivant", "album suivant");
						renameParameter("précédent", "album précédent");
						renameParameter("date préc", "date album préc");
						renameParameter("date suiv", "date album suiv");
						break;
					}
			}
		}

		public void chansonPrecSuiv() {
			if (findParam("chanson précédente", "chanson suivante") != null) {
				string charte = "";
				if (tmplParams["charte"].hasValue)
					charte = tmplParams["charte"].value;
				switch (charte.ToLower()) {
					case "single":
					case "simple": {
							renameParameter("chanson précédente", "single précédent");
							renameParameter("chanson suivante", "single suivant");
							break;
						}

					case "chanson":
					case "single promotionnel":
					case "démo":
					case "instrumental": {
							renameParameter("chanson précédente", "piste précédente");
							renameParameter("chanson suivante", "piste suivante");
							break;
						}

					default: {
							renameParameter("chanson précédente", "album précédent");
							renameParameter("chanson suivante", "album suivant");
							break;
						}
				}
			}
		}

		public void SelectImage(string paramName) {
			if (tmplParams["image"].hasValue)
				removeParameter(paramName);
			else {
				parametre param = findParam(paramName);
				if (param != null) {
					if (!param.isEmpty && param.value == wikitextToString(param.value) && isExistingFile(param.value))
						renameParameter(param, "image");
					else
						removeParameter(param);
				}
			}
		}

		public void séparerDatePrecSuiv() {
			parametre prec = findParam("album précédent", "piste précédente");
			parametre suiv = findParam("album suivant", "piste suivante");
			parametre datePrec = findParam("date album préc", "date single préc");
			parametre dateSuiv = findParam("date album suiv", "date single suiv");

			séparationDate("album précédent", "date album préc");
			séparationDate("album suivant", "date album suiv");

			séparationDate("single précédent", "date single préc");
			séparationDate("single suivant", "date single suiv");

			séparationPiste("piste précédente", "numéro piste préc");
			séparationPiste("piste suivante", "numéro piste suiv");

			séparationDate("piste précédente", "date piste préc");
			séparationDate("piste suivante", "date piste suiv");
		}

		public void séparationDate(string paramName, string targetName) {
			parametre param = findParam(paramName);

			if (param != null) {
				string initialValue = param.value;
				if (Regex.IsMatch(param.value, @"^¤§[th]\d+§¤$")) {
					string val = wikitextToString(param.value);
					val = Regex.Replace(val, "</?center>", "").Trim();
					val = Regex.Replace(val, @"^{{centrer\|(.+)}}$", "$1");
					if (val != wikitextToString(param.value))
						param.value = val;
				}
				if ((findParam(targetName) == null || findParam(targetName).isEmpty) && Regex.IsMatch(param.value, @"\b\d{4}(\]{2})?\)?('')?$")) {
					string dateParam = Regex.Match(param.value, @"((\[\[(\d{4} en musique\|)?)?\b\d{4}(?(2)\]{2}))\)?('')?$").Groups[1].Value;
					if (dateParam == param.value)
						return;

					param.value = Regex.Replace(param.value, @"('')?(\()?" + Regex.Escape(dateParam) + @"(?(2)\))('')?$", "");

					if (!targetName.StartsWith("date piste"))
						addParameter(new parametre(param, targetName, dateParam), param);
				}
				param.value = Regex.Replace(param.value, @"(<br[ /]*>(Album)?)?\s*$", "").Trim();
				param.value = Regex.Replace(param.value, "[', ]*$", "");
				param.value = Regex.Replace(param.value, "^''(?!')(.+?)$", "$1").Trim();
				param.value = Regex.Replace(param.value, "'''(?!')(.+)(?<!')$", "'''$1'''").Trim();
				FlagSkip = param.value == initialValue;
			}
		}

		public void séparationPiste(string paramName, string targetName) {
			parametre param = findParam(paramName);
			if (param != null) {
				string initialValue = param.value;
				if (Regex.IsMatch(param.value, @"^¤§[th]\d+§¤$")) {
					string val = wikitextToString(param.value);
					val = Regex.Replace(val, "</?center>", "").Trim();
					val = Regex.Replace(val, @"^{{centrer\|(.+)}}$", "$1");
					if (val != wikitextToString(param.value)) {
						param.value = val;
						FlagSkip = false;
					}
				}
				if ((findParam(targetName) == null || findParam(targetName).isEmpty)) {
					if (Regex.IsMatch(param.value, @"^\d+(\.|\.</?br ?/?>| ?: )")) {
						addParameter(new parametre(param, targetName, Regex.Match(param.value, @"^\d+").Value), param);
						param.value = Regex.Replace(param.value, @"^\d+(\.(</?br ?/?>)?| ?:)", "").Trim();
						FlagSkip = false;
					} else if (Regex.IsMatch(param.value, @"\b1?\d\)?$")) {
						addParameter(new parametre(param, targetName, Regex.Match(param.value, @"(\d+)\)?$").Groups[1].Value), param);
						param.value = Regex.Replace(param.value, @"('' *)?(</?br ?/?>)?\(?\d+\)?", "").Trim();
						FlagSkip = false;
					}
				}
				FlagSkip = param.value == initialValue;
			}
		}

		public void listeSingle() {
			parametre single1 = findParam("single 1");
			parametre singleParam = tmplParams["single"].parametre;
			if (single1 != null) {
				if (single1.isEmpty) {
					removeParameter(single1);
					removeParameter("date 1");
					removeParameter("single 2");
					removeParameter("date 2");
					removeParameter("single 3");
					removeParameter("date 3");
					removeParameter("single 4");
					removeParameter("date 4");
					removeParameter("single 5");
					removeParameter("date 5");
					removeParameter("single 6");
					removeParameter("date 6");
				} else if (singleParam != null && singleParam.isEmpty && Parametres.IndexOf(single1) == 1 + Parametres.IndexOf(singleParam)) {
					singleParam.value = "{{Singles";
					parametre lastsingle = findParam("date 6", "single 6", "date 5", "single 5", "date 4", "single 4", "date 3", "single 3", "date 2", "single 2", "date 1", "single 1");
					lastsingle.value = lastsingle.value + "\n   }}";
				}
			}
		}
	}

	class Langue : TemplateClass {
		public Langue(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Langue", "Lang" }) {
			tmplParams.add("1");
			tmplParams.add("texte", "2");
			tmplParams.add("3");
			tmplParams.add("dir");
			tmplParams.add("trans");
			tmplParams.parametreToDeleteIfEmpty("dir", "trans");
		}

		public override void fix() {
			deplacerRtlDansDir();
			supprimerItalicTrans();
			italicInterne();

			base.fix();

			absenceDeLangueOuTexte();
			supprimerDir();
			ajouteLatn();
		}

		private void deplacerRtlDansDir() {
			parametre param = tmplParams["1"].parametre;
			if (param != null) {
				if (param.value.ToLower() == "rtl" || param.value.ToLower() == "ltr") {
					if (tmplParams["dir"].hasParametre)
						tmplParams["dir"].value = param.value.ToLower();
					else {
						parametre dir = new parametre(this, "dir", param.value.ToLower(), "|", "=", "");
						addParameter(dir);
					}
					removeParameter(param);
				}
			}
		}

		public static string checkRtlScript(string codeLangue, string texte, WikitextClass callingObj) {
			switch (codeLangue) {
				case "ar":
				case "fa":
				case "aeb":
				case "arq":
				case "ary":
				case "arz": {
						// teste le premier caractére pour vérifié que ce n'est pas un caractère latin
						if (!Regex.IsMatch(callingObj.wikitextToString(texte), @"\p{IsArabic}"))
							return codeLangue + "-Latn";
						break;
					}

				case "he": {
						// teste le premier caractére pour vérifié que ce n'est pas un caractère latin
						if (!Regex.IsMatch(callingObj.wikitextToString(texte), @"\p{IsHebrew}"))
							return codeLangue + "-Latn";
						break;
					}
			}
			return codeLangue;
		}

		public void absenceDeLangueOuTexte() {
			if (!tmplParams["texte"].hasValue && unnamedIndex < 2) {
				if (tmplParams["1"].hasValue) {
					parametre lang = tmplParams["1"].parametre;
					if (lang.value.Length > 3) {
						Match match = Regex.Match(lang.value, "^(en|fr|de|it|es|la)(.+)$");
						if (match.Success) {
							lang.value = match.Groups[1].Value;
							parametre texte = new parametre(lang, "2", match.Groups[2].Value);
							addParameter(texte, lang);
							unnamedIndex = unnamedIndex + 1;
							FlagSkip = false;
						} else {
							parametre texte = lang;
							lang = new parametre(texte, "1", "en");
							addParameter(lang, 0);
							renameParameter(texte, "2");
							FlagSkip = false;
						}
					} else if (invalidParameters.Count == 1) {
						parametre texte = invalidParameters[0];
						texte.value = texte.name + texte.sep + texte.value;
						renameParameter(texte, "texte");
						FlagSkip = false;
					}
				} else {
				}
			}
		}

		public void supprimerItalicTrans() {
			if (tmplParams["trans"].hasValue) {
				parametre paramTrans = tmplParams["trans"].parametre;
				Regex regexGuillemet = new Regex(@"^([""\p{Pi}])(.+)(?(1)[""\p{Pf}])$");
				string transSansItalique = regexGuillemet.Replace(paramTrans.value, "$2");
				transSansItalique = Regex.Replace(transSansItalique.Trim(), @"^''([’\u0000-\u024F-[|{}\[\]]]+)''$", "$1");
				transSansItalique = regexGuillemet.Replace(transSansItalique, "$2");
				if (transSansItalique != paramTrans.value) {
					paramTrans.value = transSansItalique.Trim();
					FlagSkip = false;
				}
			}
		}

		public void italicInterne() {
			parametre texteP = tmplParams["texte"].parametre;
			if (texteP != null && !texteP.isEmpty && texteP.value.Contains("''")) {
				// gras
				if (Regex.IsMatch(parent.parsedString, "'''" + strip + "(?!''')") && Regex.IsMatch(texteP.value, "^([^']|'(?!''))+'''$")) {
					parent.parsedString = parent.parsedString.Replace(strip, strip + "'''");
					texteP.value = texteP.value.Replace("'''", "");
					FlagSkip = false;
				} else if (Regex.IsMatch(parent.parsedString, "'''" + strip + "(?!''')") && Regex.IsMatch(texteP.value, "^([^']|'(?!'))+'''")) {
					parent.parsedString = parent.parsedString.Replace("'''" + strip, strip);
					texteP.value = "'''" + texteP.value;
					FlagSkip = false;
				}

				// italique
				bool skip = false;
				if (Regex.IsMatch(parent.parsedString, "((?<!')|(?<='''))''" + strip + "''") && Regex.IsMatch(texteP.value, "(?<!')''(?!')")) {
					if (Regex.IsMatch(texteP.value, "^''(?!').+(?<!')''$"))
						texteP.value = Regex.Replace(texteP.value, "^''(?!')(.+)(?<!')''$", "$1");
					else if (Regex.IsMatch(texteP.value, "^''(?!')([^']|'(?!'))+$")
													| Regex.IsMatch(texteP.value, "^([^']|'(?!'))+''$"))
						texteP.value = texteP.value.Replace("''", "");
					else {
						parent.parsedString = parent.parsedString.Replace("''" + strip + "''", strip);
						texteP.value = "''" + texteP.value + "''";
					}
				} else if (Regex.IsMatch(parent.parsedString, "((?<!')|(?<='''))''" + strip + "(?!')") && Regex.IsMatch(texteP.value, "^([^']|'(?!'))+''$")) {
					parent.parsedString = parent.parsedString.Replace(strip, strip + "''");
					texteP.value = texteP.value.Replace("''", "");
				} else if (Regex.IsMatch(parent.parsedString, "(?<!')" + strip + "(?='')") && Regex.IsMatch(texteP.value, "^''([^']|(?<!')')+$")) {
					parent.parsedString = parent.parsedString.Replace(strip, "''" + strip);
					texteP.value = texteP.value.Replace("''", "");
				} else if (Regex.IsMatch(parent.parsedString, "((?<!')|(?<='''))''" + strip + "(?!')") && Regex.IsMatch(texteP.value, "[^']''[^']")) {
					parent.parsedString = parent.parsedString.Replace("''" + strip, strip);
					texteP.value = "''" + texteP.value;
				} else if (Regex.IsMatch(parent.parsedString, "(?<!')" + strip + "(?='')") && Regex.IsMatch(texteP.value, "[^']''[^']")) {
					parent.parsedString = parent.parsedString.Replace(strip + "''", strip);
					texteP.value = texteP.value + "''";
				} else if (Regex.IsMatch(parent.parsedString, "(?<!')'" + strip + "(?!')") && Regex.IsMatch(texteP.value, "^'(?!')(?>[^{}']+|'(?!'))*''(?>[^{}']+|'(?!'))*")) {
					parent.parsedString = parent.parsedString.Replace("'" + strip, strip);
					texteP.value = "'" + texteP.value;
				} else if (Regex.IsMatch(parent.parsedString, "(?<!')" + strip + "'(?!')") && Regex.IsMatch(texteP.value, "(?>[^{}']+|'(?!'))*''(?>[^{}']+|'(?!'))*(?<!')'$")) {
					parent.parsedString = parent.parsedString.Replace(strip + "'", strip);
					texteP.value = texteP.value + "'";
				} else
					skip = true;
				if (Regex.IsMatch(texteP.value, "(?<!')'{4}(?!')"))
					texteP.value = texteP.value.Replace("''''", "");
				FlagSkip = skip;
			}
		}

		public void supprimerDir() {
			if (tmplParams["dir"].hasValue) {
				parametre dir = tmplParams["dir"].parametre;
				if (dir != null && tmplParams["1"].hasParametre) {
					string lang = tmplParams["1"].value;
					if (dir.value.ToLower() == "rtl") {
						switch (lang) {
							case "ar":
							case "fa":
							case "aeb":
							case "arq":
							case "ary":
							case "arz": {
									removeParameter(dir);
									// teste le premier caractére pour vérifié que ce n'est pas un caractère latin
									if (tmplParams["texte"].hasValue) {
										if (!Regex.IsMatch(wikitextToString(tmplParams["texte"].value), @"[\p{IsArabic}\p{IsArabicPresentationForms-A}\p{IsArabicPresentationForms-B}]"))
											tmplParams["1"].value = lang + "-Latn";
									}

									break;
								}

							case "he": {
									removeParameter(dir);
									// teste le premier caractére pour vérifié que ce n'est pas un caractère latin
									if (tmplParams["texte"].hasValue) {
										if (!Regex.IsMatch(wikitextToString(tmplParams["texte"].value), @"\p{IsHebrew}"))
											tmplParams["1"].value = lang + "-Latn";
									}

									break;
								}
						}
					}
				}
			}
		}

		public void ajouteLatn() {
			if (tmplParams["1"].hasValue)
				tmplParams["1"].value = checkRtlScript(tmplParams["1"].value, tmplParams["texte"].value, this);
		}
	}

	class References : TemplateClass {
		public References(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Références", "Références nombreuses" }) {
			tmplParams.add("colonnes");
			tmplParams.add("groupe", new parametreStandard("group"));
			tmplParams.add("références", new parametreStandard("refs", "d"));
			tmplParams.add("taille");
			tmplParams.add("pasdecol");

			tmplParams.parametreToDeleteIfEmpty("colonnes", "groupe", "références", "taille", "pasdecol");
		}

		public override void fix() {
			renameParameter("Colonne", "colonnes");
			renameParameter("collones", "colonnes");
			renameParameter("columns", "colonnes");
			renameParameter("nombre", "colonnes");
			renameParameter("largeur", "taille");

			Regex reflist4 = new Regex(@"<div style=""height:[^"">]+reflist4;?""\s*>\s*" + strip + @"\s*</div>");
			Match match = reflist4.Match(parent.parsedString);
			if (match.Success) {
				parent.parsedString = parent.parsedString.Replace(match.Value, strip);
				name = "Références nombreuses";
			}

			if (tmplParams["colonnes"].hasValue && tmplParams["colonnes"].value == "2")
				removeParameter("colonnes");
			if (tmplParams["taille"].hasValue && isNumber(tmplParams["taille"].value)) {
				int taille = System.Convert.ToInt32(tmplParams["taille"].value);
				if (taille < 36 && taille > 29)
					removeParameter("taille");
			}

			base.fix();
		}
	}

	class Traduction : TemplateClass {
		public Traduction(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Traduction" }) {
			tmplParams.add("langue");
			tmplParams.add("1");
			tmplParams.add("2");
			tmplParams.add("3");
		}

		public override void fix() {
			renameParameter("lang", "langue");
			renameParameter("Lang", "langue");

			base.fix();

			supprimerItalique();
		}

		public void supprimerItalique() {
			parametre param = tmplParams["1"].parametre;
			if (param != null) {
				if (Regex.IsMatch(param.value, "^''(?!').+''$")) {
					param.value = Regex.Replace(param.value, "^''(.+)''$", "$1");
					FlagSkip = false;
				}
			}
		}
	}

	class tri : TemplateClass {
		public tri(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Tri" }) {
			tmplParams.add("1");
			tmplParams.add("2");
			tmplParams.add("t");
		}

		public override void fix() {
			if (tmplParams["t"].hasParametre)
				removeParameter(tmplParams["t"].parametre);

			if (!tmplParams["2"].hasValue) {
				string parentText = parent.parsedString;
				Regex findText = new Regex("(?x) (" + strip + @") ( ( \[\[ [^\[\]]*? \]\] | [^|\n] )+ (?<!\s) )");
				Match match = findText.Match(parentText);
				if (match.Success) {
					string value = match.Groups[2].Value;
					if (value.Contains("="))
						addParameter(new parametre(this, "2", value, "|", "=", ""));
					else
						addParameter(new parametre(this, 2, value, "|", ""));
					parent.parsedString = findText.Replace(parentText, "$1");
				}
			}

			base.fix();
		}
	}

	class Unité : TemplateClass {
		public Unité(string nname, string contenuModele, WikitextClass nparent)
			: base(nname, contenuModele, nparent, new string[] { "Unité", "Nombre" }) {
			tmplParams.add("1");
			tmplParams.add("2");
			tmplParams.add("3");
			tmplParams.add("4");
			tmplParams.add("5");
			tmplParams.add("6");
			tmplParams.add("7");
			tmplParams.add("8");
			tmplParams.add("9");
			tmplParams.add("e");
			tmplParams.add("et");
			tmplParams.add("à");
			tmplParams.add("±", "+/-");
			tmplParams.add("+");
			tmplParams.add("-");
			tmplParams.add("–");
			tmplParams.add("/");
			tmplParams.add("×");
			tmplParams.add("××");
			tmplParams.add("fraction");
			tmplParams.add("arrondi");
			tmplParams.add("décimales");
		}
	}