<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://ethans-workspace.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3ADetect_singular</id>
	<title>Module:Detect singular - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://ethans-workspace.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3ADetect_singular"/>
	<link rel="alternate" type="text/html" href="http://ethans-workspace.com/w/index.php?title=Module:Detect_singular&amp;action=history"/>
	<updated>2026-05-25T09:13:07Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>http://ethans-workspace.com/w/index.php?title=Module:Detect_singular&amp;diff=3211&amp;oldid=prev</id>
		<title>Ethan at 19:33, 26 October 2025</title>
		<link rel="alternate" type="text/html" href="http://ethans-workspace.com/w/index.php?title=Module:Detect_singular&amp;diff=3211&amp;oldid=prev"/>
		<updated>2025-10-26T19:33:36Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 14:33, 26 October 2025&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l2&quot;&gt;Line 2:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 2:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local yesNo = require(&amp;#039;Module:Yesno&amp;#039;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local yesNo = require(&amp;#039;Module:Yesno&amp;#039;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;local getPlain = require(&#039;Module:Text&#039;).Text().getPlain&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;-- function to determine whether &amp;quot;sub&amp;quot; occurs in &amp;quot;s&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;-- function to determine whether &amp;quot;sub&amp;quot; occurs in &amp;quot;s&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l25&quot;&gt;Line 25:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 24:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--   origArgs.no_comma:  if false, use commas to detect plural (default false)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--   origArgs.no_comma:  if false, use commas to detect plural (default false)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--   origArgs.parse_links: if false, treat wikilinks as opaque singular objects (default false)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--   origArgs.parse_links: if false, treat wikilinks as opaque singular objects (default false)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;--   origArgs.any_comma: if true, allow any comma to cause likely plural, including ones between digits (default false)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;--   origArgs.no_and: if false, use existence of &quot;and&quot; to detect plural (default false)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;--   origArgs.parse_number: if true, parse number in argument: if 1, singular; if &amp;gt;1, plural (default false)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;-- Returns:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;-- Returns:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--   singular, likelyPlural, or plural (see constants above), or nil for completely unknown&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--   singular, likelyPlural, or plural (see constants above), or nil for completely unknown&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l84&quot;&gt;Line 84:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 86:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasBreak = mw.ustring.find(s,&amp;#039;&amp;lt;%s*br&amp;#039;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasBreak = mw.ustring.find(s,&amp;#039;&amp;lt;%s*br&amp;#039;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	-- For the last 4, evaluate on string stripped of wikimarkup&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	-- For the last 4, evaluate on string stripped of wikimarkup&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;    local getPlain = require(&#039;Module:Text&#039;).Text().getPlain&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	s = getPlain(s)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	s = getPlain(s)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasBullets = countMatches(s,&amp;#039;%*+&amp;#039;) &amp;gt; 1&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasBullets = countMatches(s,&amp;#039;%*+&amp;#039;) &amp;gt; 1&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l92&quot;&gt;Line 92:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 95:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local commaPattern = anyComma and &amp;#039;[,;]&amp;#039; or &amp;#039;%D[,;]%D&amp;#039;  -- semi-colon similar to comma&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local commaPattern = anyComma and &amp;#039;[,;]&amp;#039; or &amp;#039;%D[,;]%D&amp;#039;  -- semi-colon similar to comma&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasComma = checkComma and mw.ustring.find(s, commaPattern)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasComma = checkComma and mw.ustring.find(s, commaPattern)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasAnd = checkAnd and mw.ustring.find(s,&#039;[,%s]and%s&#039;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local hasAnd = checkAnd and &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;(&lt;/ins&gt;mw.ustring.find(s,&#039;[,%s]and%s&#039;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;) or mw.ustring.find(s,&#039;&amp;amp;&#039;,1,true)&lt;/ins&gt;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	if hasBreak or hasComma or hasAnd then&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	if hasBreak or hasComma or hasAnd then&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		return likelyPlural&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		return likelyPlural&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key ethan_wiki:diff:1.41:old-3147:rev-3211:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Ethan</name></author>
	</entry>
	<entry>
		<id>http://ethans-workspace.com/w/index.php?title=Module:Detect_singular&amp;diff=3147&amp;oldid=prev</id>
		<title>Ethan: Created page with &quot;local p = {} local getArgs = require(&#039;Module:Arguments&#039;).getArgs local yesNo = require(&#039;Module:Yesno&#039;) local getPlain = require(&#039;Module:Text&#039;).Text().getPlain  -- function to determine whether &quot;sub&quot; occurs in &quot;s&quot; local function plainFind(s, sub) 	return mw.ustring.find(s, sub, 1, true) end  -- function to count the number of times &quot;pattern&quot; (a regex) occurs in &quot;s&quot; local function countMatches(s, pattern) 	local _, count = mw.ustring.gsub(s, pattern, &#039;&#039;) 	return count end...&quot;</title>
		<link rel="alternate" type="text/html" href="http://ethans-workspace.com/w/index.php?title=Module:Detect_singular&amp;diff=3147&amp;oldid=prev"/>
		<updated>2025-08-11T17:43:21Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;local p = {} local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs local yesNo = require(&amp;#039;Module:Yesno&amp;#039;) local getPlain = require(&amp;#039;Module:Text&amp;#039;).Text().getPlain  -- function to determine whether &amp;quot;sub&amp;quot; occurs in &amp;quot;s&amp;quot; local function plainFind(s, sub) 	return mw.ustring.find(s, sub, 1, true) end  -- function to count the number of times &amp;quot;pattern&amp;quot; (a regex) occurs in &amp;quot;s&amp;quot; local function countMatches(s, pattern) 	local _, count = mw.ustring.gsub(s, pattern, &amp;#039;&amp;#039;) 	return count end...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local p = {}&lt;br /&gt;
local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;br /&gt;
local yesNo = require(&amp;#039;Module:Yesno&amp;#039;)&lt;br /&gt;
local getPlain = require(&amp;#039;Module:Text&amp;#039;).Text().getPlain&lt;br /&gt;
&lt;br /&gt;
-- function to determine whether &amp;quot;sub&amp;quot; occurs in &amp;quot;s&amp;quot;&lt;br /&gt;
local function plainFind(s, sub)&lt;br /&gt;
	return mw.ustring.find(s, sub, 1, true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- function to count the number of times &amp;quot;pattern&amp;quot; (a regex) occurs in &amp;quot;s&amp;quot;&lt;br /&gt;
local function countMatches(s, pattern)&lt;br /&gt;
	local _, count = mw.ustring.gsub(s, pattern, &amp;#039;&amp;#039;)&lt;br /&gt;
	return count&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local singular = 1&lt;br /&gt;
local likelyPlural = 2&lt;br /&gt;
local plural = 3&lt;br /&gt;
&lt;br /&gt;
-- Determine whether a string is singular or plural (i.e., it represents one&lt;br /&gt;
-- item or many)&lt;br /&gt;
-- Arguments:&lt;br /&gt;
--   origArgs[1]: string to process&lt;br /&gt;
--   origArgs.no_comma:  if false, use commas to detect plural (default false)&lt;br /&gt;
--   origArgs.parse_links: if false, treat wikilinks as opaque singular objects (default false)&lt;br /&gt;
-- Returns:&lt;br /&gt;
--   singular, likelyPlural, or plural (see constants above), or nil for completely unknown&lt;br /&gt;
function p._main(origArgs)&lt;br /&gt;
	origArgs = type(origArgs) == &amp;#039;table&amp;#039; and origArgs or {}&lt;br /&gt;
	local args = {}&lt;br /&gt;
	-- canonicalize boolean arguments&lt;br /&gt;
	for key, default in pairs({no_comma=false,parse_links=false,any_comma=false,&lt;br /&gt;
                               no_and=false,parse_number=false}) do&lt;br /&gt;
		if origArgs[key] == nil then&lt;br /&gt;
			args[key] = default&lt;br /&gt;
		else&lt;br /&gt;
			args[key] = yesNo(origArgs[key],default)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local checkComma = not args.no_comma&lt;br /&gt;
	local checkAnd = not args.no_and&lt;br /&gt;
	local rewriteLinks = not args.parse_links&lt;br /&gt;
	local anyComma = args.any_comma&lt;br /&gt;
    local parseNumber = args.parse_number&lt;br /&gt;
	local s = origArgs[1]  -- the input string&lt;br /&gt;
	if not s then&lt;br /&gt;
		return nil -- empty input returns nil&lt;br /&gt;
	end&lt;br /&gt;
	s = tostring(s)&lt;br /&gt;
	s = mw.text.decode(s,true)  --- replace HTML entities (to avoid spurious semicolons)&lt;br /&gt;
	if plainFind(s,&amp;#039;data-plural=&amp;quot;0&amp;quot;&amp;#039;) then -- magic data string to return true&lt;br /&gt;
		return singular&lt;br /&gt;
	end&lt;br /&gt;
	if plainFind(s,&amp;#039;data-plural=&amp;quot;1&amp;quot;&amp;#039;) then -- magic data string to return false&lt;br /&gt;
		return plural&lt;br /&gt;
	end&lt;br /&gt;
	-- count number of list items&lt;br /&gt;
	local numListItems = countMatches(s,&amp;#039;&amp;lt;%s*li&amp;#039;)&lt;br /&gt;
	-- if exactly one, then singular, if more than one, then plural&lt;br /&gt;
	if numListItems == 1 then&lt;br /&gt;
		return singular&lt;br /&gt;
	end&lt;br /&gt;
	if numListItems &amp;gt; 1 then&lt;br /&gt;
		return plural&lt;br /&gt;
	end&lt;br /&gt;
	-- if &amp;quot;list of&amp;quot; occurs inside of wlink, then it&amp;#039;s plural&lt;br /&gt;
	if mw.ustring.find(s:lower(), &amp;#039;%[%[[^%]]*list of[^%]]+%]%]&amp;#039;) then&lt;br /&gt;
		return plural&lt;br /&gt;
	end&lt;br /&gt;
    if parseNumber then&lt;br /&gt;
        local m = tonumber(mw.ustring.match(s,&amp;quot;^%s*(%d+)&amp;quot;))&lt;br /&gt;
        if m then&lt;br /&gt;
            return m &amp;gt; 1 and plural or singular&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
	-- fix for trailing br tags passed through [[template:marriage]]&lt;br /&gt;
	s = mw.ustring.gsub(s, &amp;#039;&amp;lt;%s*br[^&amp;gt;]*&amp;gt;%s*(&amp;lt;/div&amp;gt;)&amp;#039;, &amp;#039;%1&amp;#039;)&lt;br /&gt;
	-- replace all wikilinks with fixed string&lt;br /&gt;
	if rewriteLinks then&lt;br /&gt;
		s = mw.ustring.gsub(s,&amp;#039;%b[]&amp;#039;,&amp;#039;WIKILINK&amp;#039;) &lt;br /&gt;
	end&lt;br /&gt;
	-- Five conditions: any one of them can make the string a likely plural or plural&lt;br /&gt;
	local hasBreak = mw.ustring.find(s,&amp;#039;&amp;lt;%s*br&amp;#039;)&lt;br /&gt;
	-- For the last 4, evaluate on string stripped of wikimarkup&lt;br /&gt;
	s = getPlain(s)&lt;br /&gt;
	local hasBullets = countMatches(s,&amp;#039;%*+&amp;#039;) &amp;gt; 1&lt;br /&gt;
	local multipleQids = mw.ustring.find(s,&amp;#039;Q%d+[%p%s]+Q%d+&amp;#039;) -- has multiple QIDs in a row&lt;br /&gt;
	if hasBullets or multipleQids then&lt;br /&gt;
		return plural&lt;br /&gt;
	end&lt;br /&gt;
	local commaPattern = anyComma and &amp;#039;[,;]&amp;#039; or &amp;#039;%D[,;]%D&amp;#039;  -- semi-colon similar to comma&lt;br /&gt;
	local hasComma = checkComma and mw.ustring.find(s, commaPattern)&lt;br /&gt;
	local hasAnd = checkAnd and mw.ustring.find(s,&amp;#039;[,%s]and%s&amp;#039;)&lt;br /&gt;
	if hasBreak or hasComma or hasAnd then&lt;br /&gt;
		return likelyPlural&lt;br /&gt;
	end&lt;br /&gt;
	return singular&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._pluralize(args)&lt;br /&gt;
	args = type(args) == &amp;#039;table&amp;#039; and args or {}&lt;br /&gt;
	local singularForm = args[3] or args.singular or &amp;quot;&amp;quot;&lt;br /&gt;
	local pluralForm = args[4] or args.plural or &amp;quot;&amp;quot;&lt;br /&gt;
	local likelyForm = args.likely or pluralForm&lt;br /&gt;
	local link = args[5] or args.link&lt;br /&gt;
	if link then&lt;br /&gt;
		link = tostring(link)&lt;br /&gt;
		singularForm = &amp;#039;[[&amp;#039;..link..&amp;#039;|&amp;#039;..singularForm..&amp;#039;]]&amp;#039;&lt;br /&gt;
		pluralForm = &amp;#039;[[&amp;#039;..link..&amp;#039;|&amp;#039;..pluralForm..&amp;#039;]]&amp;#039;&lt;br /&gt;
		likelyForm = &amp;#039;[[&amp;#039;..link..&amp;#039;|&amp;#039;..likelyForm..&amp;#039;]]&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	if args[2] then&lt;br /&gt;
		return pluralForm&lt;br /&gt;
	end&lt;br /&gt;
	local detect = p._main(args)&lt;br /&gt;
	if detect == nil then&lt;br /&gt;
		return &amp;quot;&amp;quot;   -- return blank on complete failure&lt;br /&gt;
	end&lt;br /&gt;
	if detect == singular then&lt;br /&gt;
		return singularForm&lt;br /&gt;
	elseif detect == likelyPlural then&lt;br /&gt;
		return likelyForm&lt;br /&gt;
	else&lt;br /&gt;
		return pluralForm&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	-- For template, return 1 if singular, blank if plural or empty&lt;br /&gt;
	local result = p._main(args)&lt;br /&gt;
	if result == nil then&lt;br /&gt;
		return 1&lt;br /&gt;
	end&lt;br /&gt;
	return result == singular and 1 or &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.pluralize(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	return p._pluralize(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ethan</name></author>
	</entry>
</feed>