Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Improve the quality of your English messages


  • Please log in to reply
8 replies to this topic
PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
By message, I mean an information for the user. It can be displayed in a message box, a line in a GUI or written to a log, etc.

One difficulty with such messages (setting aside the localization problems which I won't treat here) is to express a quantity.

Way too often, I see messages like:
The script processed 1 lines.
Obviously, this isn't correct English.
So the lazy programmer corrects the message:
The script processed 1 line(s).
This is uglier!
So he puts a test, but since it is repetitive, it puts it in a function:
The script processed 1 line.
The script processed 10 lines.

That's better. Then he uses his function elsewhere:
10 appendixs were created.
Ouch!

At my job, I found a Java function trying to address this problem, but only treated the nouns finishing by 'y': days but tries, guys but spies, etc.
I saw some limitations, and after a quick search on the Internet, I saw many more exceptions!
So I rewrote the function and improved a nearby function addressing the problem of ordinal numbers (1st, 10th...).

I thought it would be nice to provide these functions to AutoHotkey users, so here there are:
/*
EnglishMessages.ahk

Functions to format properly messages in English.

// by Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr
// File/Project history:
 1.00.000 -- 2006/03/23 (PL) -- Creation.
 */

/*
// Get the numerical ordinal number string:
// 1 => "1st"
// 2 => "2nd"
// 3 => "3rd"
// 4 => "4th"
// ...
// http://home.comcast.net/~igpl/NWA.html
*/
GetNumericalOrdinalNumber@English(_number)
{
	local ret, suffix

	ret := _number

	If (_number > 0)
	{
		If (Mod(_number, 100) > 3 && Mod(_number, 100) < 20)
			suffix := "th"
		Else If (Mod(_number, 10) = 1)
			suffix := "st"
		Else If (Mod(_number, 10) = 2)
			suffix := "nd"
		Else If (Mod(_number, 10) = 3)
			suffix := "rd"
		Else
			suffix := "th"
		ret = %ret%%suffix%
	}
	Return ret
}

/*
// Computes the plural form of a noun.
// http://www2.gsu.edu/~wwwesl/egw/pluralsn.htm
// I skipped some rare cases...
// Also there are some exceptions (zoo/zoos, corpus/corpora)
// and some really irregular names:
// mouse -> mice, child -> children, die -> dice, etc.
// taken in account by an optional parameter.
*/
GetPluralName@English(_count, _name, _pluralName="")
{
	local len, lastChar, previousChar

	_count := _count . " "

	If (_count = 1 || _count < 0)
	{
		; Singular
		; In English, we write: 0 foos, 1 foo, 2 foos, etc.
		Return _count . _name
	}
	if (_pluralName != "")
	{
		Return _count . _pluralName
	}
	len := StrLen(_name)
	StringRight lastChar, _name, 1
	previousChar = ?
	If (len > 1)
	{
		StringMid previousChar, _name, len - 1, 1
	}
	If (lastChar = "y")
	{
		If (previousChar != "a" && previousChar != "e"
				&& previousChar != "o" && previousChar != "u")
		{
			; Not a voyel
			; baby/babies, spy/spies, try/tries
			StringLeft _name, _name, len - 1
			Return _count . _name . "ies"
		}
		; days, preys, boys, guys: fall through
	}
	Else If ((previousChar = "e" || previousChar = "i")
			&& lastChar = "x")
	{
		; appendix/appendices, index/indices
		StringLeft _name, _name, len - 2
		Return _count . _name . "ices"
	}
	Else If (previousChar = "i" && lastChar = "s")
	{
		; analysis/analyses, basis/bases, parenthesis/parentheses
		StringLeft _name, _name, len - 2
		Return _count . _name . "es"
	}
	Else If (previousChar = "f" && lastChar = "e")
	{
		; knife/knives, wife/wives
		StringLeft _name, _name, len - 2
		Return _count . _name . "ves"
	}
	Else If (previousChar = "o" && lastChar = "n"
			|| previousChar = "u" && lastChar = "m")
	{
		; criterion/criteria, phenomenon/phenomena
		; datum/data, curriculum/curricula
		StringLeft _name, _name, len - 2
		Return _count . _name . "a"
	}
	Else If (previousChar = "u" && lastChar = "s")
	{
		; focus/foci, stimulus/stimuli
		; but also
		; corpus/corpora, genus/genera, left here...
		StringLeft _name, _name, len - 2
		Return _count . _name . "i"
	}
	Else If (lastChar = "f")
	{
		; shelf/shelves, wolf/wolves, half/halves
		StringLeft _name, _name, len - 1
		Return _count . _name . "ves"
	}
	Else If (lastChar = "a")
	{
		; formula/formulae, antenna/antennae
		Return _count . _name . "e"
	}
	Else If (lastChar = "s" || lastChar = "z"
			|| lastChar = "x" || lastChar = "o"
			|| (previousChar = "s" || previousChar = "c") && lastChar = "h")
	{
		; glass/glasses, buzz/buzzes, box/boxes, bush/bushes, switch/switches
		; potato/potatoes, echo/echoes, hero/heroes
		; Exception: studio/studios, piano/pianos,
		; kangaroo/kangaroos, zoo/zoos, 
		; to be treated with the optional parameter
		Return _count . _name . "es"
	}
	; Last resort, the most common one...
	Return _count . _name . "s"
}


; Some test script, run only if the file is ran standalone
If (A_ScriptName = "EnglishMessages.ahk")
{

appName = English Messages

testNames =
( Join|
baby|spy|try|day|prey|boy|guy
glass|buzz|box|bush|switch|beach
potato|echo|hero
appendix|index|shelf|wolf|knife|wife|half
analysis|basis|parenthesis
formula|antenna|criterion|phenomenon|datum|curriculum
focus|stimulus
)
res =
Loop Parse, testNames, |
{
	res := res . GetPluralName@English(1, A_LoopField) . " / "
	res := res . GetPluralName@English(2, A_LoopField) . "`n"
}
MsgBox %res%

res =
Loop 35
{
	res := res . GetNumericalOrdinalNumber@English(A_Index) . ", "
}
res := res . "`n"
Loop 35
{
	res := res . GetNumericalOrdinalNumber@English(99 + A_Index) . ", "
}
MsgBox %res%

Exit

}
I don't know if somebody will ever use them, but at least it was fun and instructive to write them!

I didn't dare to write them for French...
Another challenge I can address in both languages is how to write a number in letters.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Nice! It could be used in language classes. Especially, if you add a small, editable dictionary of the exceptions (mice, children, dice, corpora...). It is museums, not musea, papas, not papae, etc. Maybe, the list of some types of irregular plurals is not that large. One could handle groups of cases by just listing them in the dictionary.

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I don't see what the problem is with 'The script processed 1 line(s).'. Whenever that is used, the digit 1 is (usually) computer generated so the '(s)' is permissable.

Good work anyway :)

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Like in Terminator 2: "Human casualties = 0.0". It was the style of computer output 30 years ago. Since fractional casualties make no sense, it just looks stupid. Like "1 line(s)". It is only slightly better to output "Number of processed lines = 1", which is the style of fill-up forms. It just appear more professional to show "1 line has been processed" instead of "1 line(s) has/have been processed".

  • Guests
  • Last active:
  • Joined: --
PhiLho, great job!

From my programmer´s point of view, it´s overkill for such a stupid little typo like "1 line(s)".

But from me being highly interested in good user interfaces (the computer has to become more oriented to humans than vice versa), it´s a pretty like gem.
Thanks for sharing this with us!

AHKnow
  • Members
  • 121 posts
  • Last active: May 17 2009 09:11 PM
  • Joined: 03 Jul 2004
Nice work and very interesting.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

Nice! It could be used in language classes. Especially, if you add a small, editable dictionary of the exceptions (mice, children, dice, corpora...). It is museums, not musea, papas, not papae, etc. Maybe, the list of some types of irregular plurals is not that large. One could handle groups of cases by just listing them in the dictionary.

Actually, I just removed the few exceptions I treated. Either I try to be exhaustive, or I left them altogether, counting on the caller to provide the exceptions with the optional parameter.

From my programmer´s point of view, it´s overkill for such a stupid little typo like "1 line(s)"

Not if you are a purist. :-)
It is an overkill to write such a function for just one call, indeed, and in this case, it can be treated locally. C/Java ternary expression that Chris seems to be willing to implement (count = 1 ? "" : "s") can help here.
But if you have lot of such forms, it can be useful. And now, you don't even need to write it! You can even easily trim it down to most common cases if you want a shorter function.

Thank you all for the nice appreciations.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I faced a similar situation in one of my scripts today which made me remember this thread. Perhaps you can upgrade it using regex, here's an example:

pl(word, qty = 2) {
	If qty = 1
		Return, word
	s = ~
	v = aeiou
	rules = (?<=ea|o)u%s%ux,(?<=(.)\1|x|ch|(?<![%v%n])o)%s%es
		,y%s%ies,fe%s%ves,x%s%ces,is%s%es,on%s%a,us%s%i,a%s%ae
	Loop, Parse, rules, `,
	{
		StringSplit, r, A_LoopField, %s%
		If RegExMatch(word, r1 .= "$")
			Return, RegExReplace(word, r1, r2)
	}
	Return, word . "s"
}

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


ScottMattes
  • Members
  • 195 posts
  • Last active: Feb 16 2015 01:43 AM
  • Joined: 21 May 2007
Titan,
Thank you for the code.

For my uses I changed

If qty = 1
to

If ( qty = 1 or qty = "one" )