Change Text Case

Sorry, I’m being a bit slow too!

It may be worth noting that, on long strings, Qwerty’s rewrite will break due to a stack overflow error. OMM, it tripped over a string (based largely on those tested earlier) of 57,586 characters. (As some of you will know, the overflow threshold can vary considerably, depending on string content - and possibly on OS, too. In the most extreme cases, it might even be as low as 4000 - 5000 characters.) In addition, a few sentence case issues still remain (to which I may now have a solution, of sorts). These include white space at the beginning of a string and multiple white spaces following a full point.

However, I’m slightly concerned that this thread is kinda ‘growing like topsy’ for a Code Exchange item. Since I feel somewhat responsible for much of the noise, now might be an appropriate time for me to bow out (to attend, anyway, to a rather pressing local issue) - and to leave the conclusion of this lively discussion to you good guys. I look forward to seeing the results with great interest. :slight_smile:

:lol: That’s just one of the burdens of a perpetual insomniac, Nigel. Can play havoc with one’s social life, too! :wink:

Nigel, thanks for your rewrite of mine!
Unfortunately, it didn’t work with periods that didn’t have a space after them, like a decimal number. Hopefully, this one should. I have kept the alphabets split to make it easy to compare if you did want to add diacritical characters.

property lower_alphabet : "abcdefghijklmnopqrstuvwxyz"
property upper_alphabet : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
property white_space : {space, tab, return, ASCII character 10, ASCII character 13}
property sentence_terminators : ".!?"

set this_string to "this TEXT will be RETURNED with CHARACTERS CAPITALISED as SPECIFIED. any OTHER CHARACTERS will be LOWER CASE. Text containing punctuation: x-ray, don't. This sentence contains a real number 5.3 with text following."

get change_case(this_string, "Sentence")

on change_case(this_text, this_case)
	if this_case is not in {"UPPER", "lower", "Title", "Sentence"} then
		error "Error: Case must be UPPER, lower, Title or Sentence"
	end if
	set new_text to {}
	set use_capital to this_case is not "lower"
	if class of this_text is Unicode text then
		set case_alphabet to lower_alphabet & upper_alphabet as Unicode text
	else if class of this_text is string then
		set case_alphabet to lower_alphabet & upper_alphabet as string
	else
		display dialog "OH NO! WE'RE ALL GOING TO DIE!" buttons {"AAAGGHHH!"} default button 1 with icon caution
		error number -128
	end if
	repeat with i from 1 to count of this_text
		set this_char to character i of this_text
		considering case -- for speed and to customise 'offset' in Tiger
			set this_offset to offset of this_char in case_alphabet
		end considering
		if this_offset is not 0 then
			if use_capital then
				set end of new_text to character ((this_offset - 1) mod 26 + 27) of case_alphabet
				set use_capital to this_case is "UPPER"
			else
				set end of new_text to character ((this_offset - 1) mod 26 + 1) of case_alphabet
			end if
		else
			if (this_case is "Title" and this_char is in white_space) or (this_case is "Sentence" and this_char is in sentence_terminators and ¬
				i is not (count of this_text) and character (i + 1) of this_text is in white_space) then
				set use_capital to true
			end if
			set end of new_text to this_char
		end if
	end repeat
	
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ""
	tell new_text to set new_text to its beginning & its rest
	set AppleScript's text item delimiters to astid
	return new_text
end change_case

Unfortunately kai, this script does handle spaces at the beginning and multiples after periods. If this script was written in say, C or Obejctive-C, wouldn’t it be as fast as yours?

Using my rewrite of kai’s, I can use this code to generate the input string and it still does not crash for me (using his reply to me as the source text ;)). I don’t know what the problem is?

set this_string to ""
repeat until (count of this_string) > 500000
	set this_string to this_string & "It may be worth noting that, on long strings, Qwerty's rewrite will break due to a stack overflow error. OMM, it tripped over a string (based largely on those tested earlier) of 57,586 characters. (As some of you will know, the overflow threshold can vary considerably, depending on string content - and possibly on OS, too. In the most extreme cases, it might even be as low as 4000 - 5000 characters.) In addition, a few sentence case issues still remain (to which I may now have a solution, of sorts). These include white space at the beginning of a string and multiple white spaces following a full point.

However, I'm slightly concerned that this thread is kinda 'growing like topsy' for a Code Exchange item. Since I feel somewhat responsible for much of the noise, now might be an appropriate time for me to bow out (to attend, anyway, to a rather pressing local issue) - and to leave the conclusion of this lively discussion to you good guys. I look forward to seeing the results with great interest. "
end repeat

Hi, Qwerty. Well spotted about the decimal point bug in my rewrite!

I haven’t had time this weekend to study your version of kai’s script, but I can confirm that both it and your latest version of your own seem to work well in both Jaguar and Tiger.

I see that in your rewrite of my rewrite, you’ve abandoned the “pre-tested this_case” optimisation. It might not make any noticeable difference here - unless this_string is very long - but as a general rule, it’s best not to test things inside a repeat when this can be done beforehand and the results aren’t going to change during the repeat.

For instance, the test ‘this_case is “Title”’ compares the individual characters of this_case with those of “Title”. We need a case-insensitive comparison here, which takes longer because allowances are made for the fact that individual characters might not be exactly the same. The comparison produces a ‘true’ or ‘false’ result, which becomes the parameter for the ‘if’ statement.

Inside the repeat, this test process (and possibly the one for “Sentence”), or else the one for “UPPER”, is performed with every character of this_string.

My version of the script tests each possible value of this_case just once, before the repeat, and simply feeds in the appropriate ‘trues’ and ‘falses’ during the repeat. This has an additional advantage in that it’s easier to arrange for punctuation and white space to be tested in the faster ‘considering case’ mode.

You could do a similar, once-only thing with ‘count of this_text’.

Apologies if you were already familiar with these concepts. :slight_smile:

No, I’m not really familiar with this. It seems so logical! Anyway, my rewrite of kai’s cannot handle Unicode text, and so is inferior. We also need to watch out for things like ‘I’, in sentence case it is not capitalized.

True the thread is growing like crazy, but think of the really valuable education embedded in this thread for a new scripter. It really does flesh out all the issues involved in what, at first anyway, seems like a straight-forward process. Perhaps when the experts agree that they’ve reached a “golden master” version, then that one would be flagged as “THE” version and the rest would be preserved as discussion.

Apologies for the delayed response, NovaScotian. I believe I wrote the comments that prompted your reply - but I take your point about the possible usefulness of such discussions. (Indeed, I’ve found the discussion of differences in script behaviour between OS versions quite illuminating.) :slight_smile:

I’ve therefore added a further version (below) that sacrifices some speed & brevity to address several of the issues discussed previously. In exploring these, it’s clear that a comprehensive solution might be possible only with access to a substantial dictionary (proper nouns, acronyms, etc.) - which is perhaps beyond the scope of a relatively simple script such as this. (The script introduces various properties, partly to accelerate runtime execution, but also to accommodate any further adjustments that might be considered necessary.)

I might be able to clear up the confusion there, Nigel. At the time, I was working in Jaguar (not Panther) - which would explain the differences in behaviour. (However, for the record, the script below was written and tested in Tiger).

The problem to which I was referring is a stack overflow error (errOSAStackOverflow: -2706), rather than a crash. In older versions of the Mac OS (including some versions of Mac OS X), the error can occur when the resulting number of string elements (text items, characters [items], words or paragraphs) exceeds about 4,060 (the precise figure can vary).

So, apart from getting a list of characters, the problem has very little to do with actual string length - since it depends primarily on the number of resulting string elements.

While such considerations may be of little concern to those using later versions of the Mac OS, they may still be worth noting for anyone interested in portability (for example, if a script is to be distributed generally - such as in a forum like this).

In later versions, the limit appears to have been removed. However, the algorithm to achieve this seems somewhat buggy - and, where there may be several thousand string elements involved, the dreaded, ever-spinning beach ball can appear. (The point at which this occurs appears to vary quite considerably, so it’s difficult to pin it down with any precision. OMM, it’s very likely to occur above 300,000 or 400,000 items - but has sometimes struck at around 60,000 items.)

Silent hanging aside, there also appear to be efficiency issues when getting particularly long lists of string elements - which may therefore take a disproportionate time to evaluate.

I’ve now modified my original ˜textItems’ handler (see script below) in an effort to side-step all three issues. So far, it seems to have worked quite effectively - as demonstrated by the following results (all the usual caveats for interpreting execution times apply):

			[u]execution time (secs)[/u]

number of with without
text items handler handler

1000	    0.01		  0.01

10000 0.2 0.5
20000 0.4 3.8
30000 0.7 7.8
40000 1.1 11.7
50000 1.4 25.1
60000 1.9 40.8
70000 2.0 42.7
80000 2.7 68.5
90000 3.6 127.8
100000 4.7 138.2

The following version introduces an additional option for case type: “mixed” - a variation of title-case that renders definite and indefinite articles, conjunctions and prepositions as lowercase (except where they start a sentence):

-- syntax : changeCase of someText to caseType
-- someText (string) : plain or encoded text
-- caseType (string) : the type of case required ("upper", "lower", "sentence", "title" or "mixed")

-- "upper" : all uppercase text (no exceptions)
-- "lower" : all lowercase text (no exceptions)
-- "sentence" : uppercase character at start of each sentence, other characters lowercase (apart from words in sentenceModList)
-- "title" : uppercase character at start of each word, other characters lowercase (no exceptions)
-- "mixed" : similar to title, except for definite and indefinite articles, conjunctions and prepositions (see mixedModList) that don't start a sentence

property lowerStr : "abcdefghijklmnopqrstuvwxyzáà âäãåæçéèêëíìîïñóòôöõøœúùûüÿ"
property upperStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZÁÀÂÄÃÅÆÇÉÈÊËÍÌÎÏÑÓÒÔÖÕØŒÚÙÛÜŸ"
property alphaList : lowerStr's characters & reverse of upperStr's characters
property sentenceBreak : {".", "!", "?"}
property wordBreak : {space, ASCII character 202, tab}
property everyBreak : wordBreak & sentenceBreak
property whiteSpace : wordBreak & {return, ASCII character 10}
property currList : missing value
property sentenceModList : {"i", "i'm", "i'm", "i've", "i've", "I've", "I've", "I'm", "I'm", "I"} (* could be extended to include certain proper nouns, acronyms, etc. *)
property mixedModList : {"By Means Of", "In Front Of", "In Order That", "On Account Of", "Whether Or Not", "According To", "As To", "Aside From", "Because Of", "Even If", "Even Though", "In Case", "Inside Of", "Now That", "Only If", "Out Of", "Owing To", "Prior To", "Subsequent To", "A", "About", "Above", "Across", "After", "Against", "Along", "Although", "Among", "An", "And", "Around", "As", "At", "Because", "Before", "Behind", "Below", "Beneath", "Beside", "Between", "Beyond", "But", "By", "De", "Down", "During", "Except", "For", "From", "If", "In", "Inside", "Into", "Like", "Near", "Of", "Off", "On", "Onto", "Or", "Out", "Outside", "Over", "Past", "Since", "So", "The", "Though", "Through", "Throughout", "To", "Under", "Unless", "Until", "Up", "Upon", "When", "Whereas", "While", "With", "Within", "Without", "Ye", "ye", "without", "within", "with", "while", "whereas", "when", "upon", "up", "until", "unless", "under", "to", "throughout", "through", "though", "the", "so", "since", "past", "over", "outside", "out", "or", "onto", "on", "off", "of", "near", "like", "into", "inside", "in", "if", "from", "for", "except", "during", "down", "de", "by", "but", "beyond", "between", "beside", "beneath", "below", "behind", "before", "because", "at", "as", "around", "and", "an", "among", "although", "along", "against", "after", "across", "above", "about", "a", "subsequent to", "prior to", "owing to", "out of", "only if", "now that", "inside of", "in case", "even though", "even if", "because of", "aside from", "as to", "according to", "whether or not", "on account of", "in order that", "in front of", "by means of"}

on textItems from currTxt
	tell (count currTxt's text items) to if it > 4000 then tell it div 2 to return ¬
		my (textItems from (currTxt's text 1 thru text item it)) & ¬
		my (textItems from (currTxt's text from text item (it + 1) to -1))
	currTxt's text items
end textItems

on initialCap(currTxt)
	tell currTxt to if (count words) > 0 then tell word 1's character 1 to if it is in lowerStr then
		set AppleScript's text item delimiters to it
		tell my (textItems from currTxt) to return beginning & upperStr's character ((count lowerStr's text item 1) + 1) & rest
	end if
	currTxt
end initialCap

to capItems from currTxt against breakList
	repeat with currBreak in breakList
		set text item delimiters to currBreak
		if (count currTxt's text items) > 1 then
			set currList to my (textItems from currTxt)
			repeat with n from 2 to count currList
				set my currList's item n to initialCap(my currList's item n)
			end repeat
			set text item delimiters to currBreak's contents
			tell my currList to set currTxt to beginning & ({""} & rest)
		end if
	end repeat
	currTxt
end capItems

on modItems from currTxt against modList
	set currList to modList
	set currCount to (count modList) div 2
	repeat with currBreak in everyBreak
		set text item delimiters to currBreak
		if (count currTxt's text items) > 1 then repeat with n from 1 to currCount
			set text item delimiters to my currList's item n & currBreak
			if (count currTxt's text items) > 1 then
				set currTxt to textItems from currTxt
				set text item delimiters to my currList's item -n & currBreak
				tell currTxt to set currTxt to beginning & ({""} & rest)
			end if
		end repeat
	end repeat
	currTxt
end modItems

to changeCase of currTxt to caseType
	if (count currTxt's words) is 0 then return currTxt
	
	ignoring case
		tell caseType to set {upper_Case, lower_Case, sentence_Case, title_Case, mixed_Case} to {it is "upper", it is "lower", it is "sentence", it is "title", it is "mixed"}
	end ignoring
	
	if not (upper_Case or lower_Case or title_Case or sentence_Case or mixed_Case) then
		error "The term \"" & caseType & "\" is not a valid case type option. Please use \"upper\", \"lower\", \"sentence\", \"title\" or \"mixed\"."
	else if upper_Case then
		set n to 1
	else
		set n to -1
	end if
	
	considering case
		set tid to text item delimiters
		
		repeat with n from n to n * (count lowerStr) by n
			set text item delimiters to my alphaList's item n
			set currTxt to textItems from currTxt
			set text item delimiters to my alphaList's item -n
			tell currTxt to set currTxt to beginning & ({""} & rest)
		end repeat
		
		if sentence_Case then
			set currTxt to initialCap(modItems from (capItems from currTxt against sentenceBreak) against sentenceModList)
		else if title_Case or mixed_Case then
			set currTxt to initialCap(capItems from currTxt against whiteSpace)
			if mixed_Case then set currTxt to initialCap(capItems from (modItems from currTxt against mixedModList) against sentenceBreak)
		end if
		
		set text item delimiters to tid
	end considering
	currTxt
end changeCase

set someText to "How far you go in life depends on your being TENDER with the YOUNG, COMPASSIONATE with the AGED, SYMPATHETIC with the STRIVING and TOLERANT of the WEAK and STRONG. Because SOMEDAY in your life you will have been ALL of these." (* George Washington Carver. *)

changeCase of someText to "upper" (* "upper", "lower", "sentence", "title" or "mixed" *)

Script subsequently edited to insert underscore characters in certain variable labels (see discussion below)

Results:

Apologies for the length of all this… :slight_smile:

Hi, Kai.

Thanks for your latest contribution to this thread. The script’s pretty remarkable as its “sentence” mode even capitalises correctly after brackets and quotes, which don’t (at first sight) seem to have been explicitly catered for! Maybe I’ll see how it works when I’ve had more time to study it in detail. :slight_smile:

After you signed off last time, I worked out a script ” less thorough than yours ” that explicitly treated quotes and brackets as “whitish” space, but I didn’t post it because I began to feel that “sentence” mode itself was a mistake ” at least in the context of discussing techniques on this forum.

“Lower”, “upper”, and “title” modes are easy to implement and have already been adequately covered. Philosophically, they do explicit and grammatically irrelevant things to the text.

The only real use for “sentence” mode, though, is as a tidier-upper of bad typing. As you’ve already noted, it’s far more complex to implement and involves context. The script needs to be versed in the proper nouns and acronyms of the language of the text. It also needs a thorough knowledge of that language’s other grammatical features, many of which can be very difficult to handle. For instance, quoted speech in English:

Or mixed contexts:

Unless “sentence” mode is given some specific, narrowly-defined purpose, it might be best to leave it to a fully-fledged application or to a ‘has’-sized library.

By the way, there’s a potential problem with your ‘uppercase’ and ‘lowercase’ variables. These words are commands in the Satimage OSAX. I wouldn’t care to tell you what they do… :wink:

As you’ve no doubt seen by now, Nigel, it simply capitalises the first character of the first word following a ‘sentenceBreak’ - and so effectively ignores any intervening characters. AppleScript’s magic - not mine, I’m afraid. :wink:

Agreed. I’d been veering towards this conclusion for a while, but my last attempt really clinched it for me. (I’m sure it’s no coincidence that AppleScript itself readily defines words and paragraphs - but steers well clear of the muddy water of sentences!)

I’d be happier to leave things as a short, quick fix - rather than attempting to go down the tortuous path of refining any further. Nevertheless, the discussion’s been a very interesting one. :slight_smile:

Good point. Thanks for the reminder - I had a feeling they looked familiar! I’ve since edited the script in situ to insert underscores in the variable labels, just for safety’s sake.

Nothing quite like reinventing the wheel to while away a few spare moments, right? :lol:

The above conversations are very interesting to me as a very novice scripter–I have learned a lot. I am looking for a way to carry out text modifications of the kind described above (uppercase, lowercase, title, etc) on a list rather than on a string. I think there must be a simple modification or addition to some of the scripts you’ve shared, but the solution has eluded me so far. Any help would be appreciated. Thanks!

I need this myself. I would try something like this:

on changeCase of subject to |case|
	set returnList to true
	
	if |case| is not in {"lower", "upper", "title", "capitalize"} then ¬
		error "Invalid case for changeCase."
	
	-- Make one-item list if needed
	if class of subject is not list then ¬
		set {subject, returnList} to {{subject}, false}
	
	repeat with i from 1 to (count subject)
		do shell script "/usr/bin/python -c \"import sys; " & ¬
			"print unicode(sys.argv[1], 'utf8')." & ¬
			|case| & "().encode('utf8')\" " & ¬
			quoted form of (item i of subject)
		set item i of subject to result
	end repeat
	
	if not returnList then return first item of subject -- Unicode text
	return subject -- list of Unicode text
end changeCase

changeCase of "hELLO, wORlD!" to "capitalize"

-- Python should handle accented characters as well. Try out this line:
-- changeCase of "hELLo, wORlD! éèê äöü ñ" to "upper"

Thanks to Has for mentioning python before: Capitalize String

Thanks Bruce–works like a charm.

My next task should be to check against a list of exceptions for title case, so that conjunctions, most prepositions, articles etc. are not capitalized in my list. Like Kai’s version of changeCase that uses the mixedModList. Any hints on how to modify Kai’s script to accept lists? I get a bit lost in the code, especially with all the switching of text delimiters.

I’d suggest to build a case changer using perl and regular expressions. Perl supports those special characters.

Just to give you an example:
Make lowercase:

echo 'Têst' | perl -pe 'use encoding utf8;s/(\w)/\L$1/gi'

Make uppercase:

echo 'Têst' | perl -pe 'use encoding utf8;s/(\w)/\U$1/gi'

If you’d rather use perl instead, then you could just modify the python one I posted above:

on changeCase of subject to someCase
	set returnList to true
	
	if someCase is not in {"L", "U"} then error "Invalid case for changeCase()" number 1
	
	-- Make one-item list if needed
	if subject's class is not list then set {subject, returnList} to {{subject}, false}
	
	count subject
	
	repeat with i from 1 to result
		do shell script "echo " & quoted form of (subject's item i) & " | /usr/bin/perl -pe 'use encoding utf8; s/(\\w)/\\" & someCase & "$1/gi'"
		set subject's item i to result
	end repeat
	
	if not returnList then set subject to subject's first item
	return subject
end changeCase

changeCase of "hELLo, wORlD! éèê äöü ñ" to "U"

Qwerty Denzel,
Great script, works very well for me.

I know this thread is old but it solved my problem perfectly and I wanted to thank kai for his “mixed” case solution. [Edit: I reposted a new version of the code I think is more useful; just copy the text you want to convert, use this script, and then paste. The newly pasted text will have been converted while on the clipboard.] I post it here in the hopes that it may save someone else some effort:


-- syntax : changeCase of someText to caseType
-- someText (string) : plain or encoded text
-- caseType (string) : the type of case required ("upper", "lower", "sentence", "title" or "mixed")

-- "upper" : all uppercase text (no exceptions)
-- "lower" : all lowercase text (no exceptions)
-- "sentence" : uppercase character at start of each sentence, other characters lowercase (apart from words in sentenceModList)
-- "title" : uppercase character at start of each word, other characters lowercase (no exceptions)
-- "mixed" : similar to title, except for definite and indefinite articles, conjunctions and prepositions (see mixedModList) that don't start a sentence

property lowerStr : "abcdefghijklmnopqrstuvwxyzáà âäãåæçéèêëíìîïñóòôöõøœúùûüÿ"
property upperStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZÁÀÂÄÃÅÆÇÉÈÊËÍÌÎÏÑÓÒÔÖÕØŒÚÙÛÜŸ"
property alphaList : lowerStr's characters & reverse of upperStr's characters
property sentenceBreak : {".", "!", "?", ":"}
property wordBreak : {space, ASCII character 202, tab}
property everyBreak : wordBreak & sentenceBreak
property whiteSpace : wordBreak & {return, ASCII character 10}
property currList : missing value
property sentenceModList : {"i", "i'm", "i'm", "i've", "i've", "I've", "I've", "I'm", "I'm", "I"} (* could be extended to include certain proper nouns, acronyms, etc. *)
property mixedModList : {"Be", "By Means Of", "In Front Of", "In Order That", "On Account Of", "Whether Or Not", "According To", "As To", "Aside From", "Because Of", "Even If", "Even Though", "In Case", "Inside Of", "Now That", "Only If", "Out Of", "Owing To", "Prior To", "Subsequent To", "A", "About", "Above", "Across", "After", "Against", "Along", "Although", "Among", "An", "And", "Around", "As", "At", "Because", "Before", "Behind", "Below", "Beneath", "Beside", "Between", "Beyond", "But", "By", "De", "Down", "During", "Except", "For", "From", "If", "In", "Inside", "Into", "Like", "Near", "Of", "Off", "On", "Onto", "Or", "Out", "Outside", "Over", "Past", "Since", "So", "The", "Though", "Through", "Throughout", "To", "Under", "Unless", "Until", "Upon", "When", "Whereas", "While", "With", "Within", "Without", "Ye", "ye", "without", "within", "with", "while", "whereas", "when", "upon", "until", "unless", "under", "to", "throughout", "through", "though", "the", "so", "since", "past", "over", "outside", "out", "or", "onto", "on", "off", "of", "near", "like", "into", "inside", "in", "if", "from", "for", "except", "during", "down", "de", "by", "but", "beyond", "between", "beside", "beneath", "below", "behind", "before", "because", "at", "as", "around", "and", "an", "among", "although", "along", "against", "after", "across", "above", "about", "a", "subsequent to", "prior to", "owing to", "out of", "only if", "now that", "inside of", "in case", "even though", "even if", "because of", "aside from", "as to", "according to", "whether or not", "on account of", "in order that", "in front of", "by means of", "be"}

on textItems from currTxt
	tell (count currTxt's text items) to if it > 4000 then tell it div 2 to return my (textItems from (currTxt's text 1 thru text item it)) & my (textItems from (currTxt's text from text item (it + 1) to -1))
	currTxt's text items
end textItems

on initialCap(currTxt)
	tell currTxt to if (count words) > 0 then tell word 1's character 1 to if it is in lowerStr then
		set AppleScript's text item delimiters to it
		tell my (textItems from currTxt) to return beginning & upperStr's character ((count lowerStr's text item 1) + 1) & rest
	end if
	currTxt
end initialCap

to capItems from currTxt against breakList
	repeat with currBreak in breakList
		set text item delimiters to currBreak
		if (count currTxt's text items) > 1 then
			set currList to my (textItems from currTxt)
			repeat with n from 2 to count currList
				set my currList's item n to initialCap(my currList's item n)
			end repeat
			set text item delimiters to currBreak's contents
			tell my currList to set currTxt to beginning & ({""} & rest)
		end if
	end repeat
	currTxt
end capItems

on modItems from currTxt against modList
	set currList to modList
	set currCount to (count modList) div 2
	repeat with currBreak in everyBreak
		set text item delimiters to currBreak
		if (count currTxt's text items) > 1 then repeat with n from 1 to currCount
			set text item delimiters to my currList's item n & currBreak
			if (count currTxt's text items) > 1 then
				set currTxt to textItems from currTxt
				set text item delimiters to my currList's item -n & currBreak
				tell currTxt to set currTxt to beginning & ({""} & rest)
			end if
		end repeat
	end repeat
	currTxt
end modItems

to changeCase of currTxt to caseType
	if (count currTxt's words) is 0 then return currTxt
	
	ignoring case
		tell caseType to set {upper_Case, lower_Case, sentence_Case, title_Case, mixed_Case} to {it is "upper", it is "lower", it is "sentence", it is "title", it is "mixed"}
	end ignoring
	
	if not (upper_Case or lower_Case or title_Case or sentence_Case or mixed_Case) then
		error "The term \"" & caseType & "\" is not a valid case type option. Please use \"upper\", \"lower\", \"sentence\", \"title\" or \"mixed\"."
	else if upper_Case then
		set n to 1
	else
		set n to -1
	end if
	
	considering case
		set tid to text item delimiters
		
		repeat with n from n to n * (count lowerStr) by n
			set text item delimiters to my alphaList's item n
			set currTxt to textItems from currTxt
			set text item delimiters to my alphaList's item -n
			tell currTxt to set currTxt to beginning & ({""} & rest)
		end repeat
		
		if sentence_Case then
			set currTxt to initialCap(modItems from (capItems from currTxt against sentenceBreak) against sentenceModList)
		else if title_Case or mixed_Case then
			set currTxt to initialCap(capItems from currTxt against whiteSpace)
			if mixed_Case then set currTxt to initialCap(capItems from (modItems from currTxt against mixedModList) against sentenceBreak)
		end if
		
		set text item delimiters to tid
	end considering
	currTxt
end changeCase

tell application "Finder"
	copy (the clipboard as list) to {text_returned}
end tell
set someText to text_returned
set cnvrtdText to (changeCase of someText to "mixed") (* "upper", "lower", "sentence", "title" or "mixed" *)
set the clipboard to cnvrtdText

The basic conversions can be done a little more simply than a reader of this (occasionally rather baroque) old thread might have guessed :wink:

on ucase(str)
	do shell script "echo " & quoted form of str & " | tr \"[:lower:]\" \"[:upper:]\""
end ucase

on lcase(str)
	do shell script "echo " & quoted form of str & " | tr \"[:upper:]\" \"[:lower:]\""
end lcase

And there will be subtle cases, but I find that even the initial letters of words and sentences can often be shifted quite simply:

-- First letter of each word
on TitleCase(str)
	do shell script "echo " & quoted form of str & " | perl -ple 's/(\\w+)/\\u$1/g'"
end TitleCase

-- First letter of each sentence
on SentenceCase(str)
	set {strDelim, my text item delimiters} to {my text item delimiters, ". "}
	set lst to text items of str
	repeat with i from 1 to length of lst
		set item i of lst to do shell script ("echo " & quoted form of (item i of lst) & " | perl -nle 'print ucfirst lc'")
	end repeat
	set {strSentences, my text item delimiters} to {lst as text, strDelim}
	strSentences
end SentenceCase