Runtime verified Test scripts included

Syntax

ProperCase(sourceString)  →  string
1 argument — exactly

Parameters

Name Type Required Description
sourceString string | number | date Yes Value to convert

Example

%%[
  VAR @name
  SET @name = ProperCase("BARB BROWN")
]%%
%%=v(@name)=%%

Renders Barb Brown.

The safe use is tidying a field that arrives in all caps, where there is no casing to lose:

%%[
  VAR @city
  SET @city = ProperCase(Trim(AttributeValue("City")))
]%%

Running it over mixed-case text is a different matter — the conversion destroys capitals inside a word, see below.

Return value

string — the value with the first letter of each word capitalized and the rest lower-cased.

The result is arbitrary cased text, so there is no closed set of sentinel values to test for.

Behaviour

Whitespace is preserved exactly. ProperCase(" spaced out ") gives ` Spaced Out ` — the padding and the inner run of spaces both survive, so this is not a substitute for Trim.

An empty string returns an empty string, and a single-letter word is capitalized — ProperCase("x") gives X.

Accented letters are cased like any other. A leading accented lowercase vowel came back as its accented capital, and the German sharp s is returned unchanged rather than expanding.

Numbers and dates are accepted and handled as their string form. ProperCase(12345) gives 12345, and ProperCase(Now()) returned the formatted date with the PM meridiem re-cased as Pm — proof that the value was stringified and genuinely processed.

Booleans are swallowed silently. ProperCase(true) returns HTTP 200 with an empty result, so this is a rejection rather than boolean support and the parameter type stays string | number | date. The same happens across the String family; see Differs from official docs.

The conversion is destructive, not additive

This is the load-bearing finding. Raising the first letter of a word is only half of what happens — every remaining letter in that word is forced down.

Call Renders
ProperCase("iPhone") Iphone
ProperCase("McDONALD") Mcdonald
ProperCase("HTML and CSS") Html And Css
ProperCase("o'neill mcdonald-smith") O'neill Mcdonald-smith
ProperCase("a1b c2d") A1b C2d

The last two rows show that word boundaries are narrower than they look: a letter directly after an apostrophe, hyphen, full stop or comma is not raised, so this cannot format names like O'Neill or McDonald-Smith correctly. A digit does not block the word either — ProperCase("3rd street") gives 3rd Street.

So the function is safe on all-caps input and lossy on anything else. If the source may already carry meaningful capitals, leave it alone or case it yourself.

The behaviour is catalogued on Differs from official docs. The official reference is silent about the lower-casing rather than wrong about it, so the entry is not flagged as contradicting the docs.

Show test script
%%[
  VAR @b, @acc
  SET @b = RequestParameter("b")
  SET @acc = Concat(Char(233), Char(224), Char(252))

  /* documented form, plus the lower-casing of everything after the first letter */
  IF @b == "safe" THEN
    OutputLine(Concat("P1=[", ProperCase("BARB BROWN"), "]"))
    OutputLine(Concat("P2=[", ProperCase("hello world"), "]"))
    OutputLine(Concat("P3=[", ProperCase("McDONALD"), "]"))
    OutputLine(Concat("PEMPTY=[", ProperCase(""), "]"))
  ENDIF

  /* what counts as a word boundary, and what happens to whitespace */
  IF @b == "pword" THEN
    OutputLine(Concat("PW1=[", ProperCase("o'neill mcdonald-smith"), "]"))
    OutputLine(Concat("PW2=[", ProperCase("abc3def"), "]"))
    OutputLine(Concat("PW3=[", ProperCase("HTML and CSS"), "]"))
    OutputLine(Concat("PW4=[", ProperCase("  spaced   out  "), "]"))
    OutputLine(Concat("PW5=[", ProperCase("a.b,c"), "]"))
    OutputLine(Concat("PW6=[", ProperCase("x"), "]"))
    OutputLine(Concat("PM1=[", ProperCase("iPhone"), "]"))
    OutputLine(Concat("PM2=[", ProperCase("3rd street"), "]"))
    OutputLine(Concat("PM3=[", ProperCase("a1b c2d"), "]"))
  ENDIF

  /* non-ASCII: read these as codepoints, not on a console */
  IF @b == "nau" THEN
    OutputLine(Concat("ECHO=[", @acc, "]"))
    OutputLine(Concat("PNAU=[", ProperCase(Concat(@acc, " ", @acc)), "]"))
    OutputLine(Concat("PSS=[", ProperCase(Concat(Char(223), "trasse")), "]"))
    OutputLine(Concat("LENPSS=[", Length(ProperCase(Concat(Char(223), "trasse"))), "]"))
  ENDIF

  /* numbers and dates are accepted and handled as their string form */
  IF @b == "pnum" THEN
    OutputLine(Concat("PNUM=[", ProperCase(12345), "]"))
    OutputLine(Concat("PNUMSTR=[", ProperCase("abc 42def"), "]"))
  ENDIF

  IF @b == "pdate" THEN
    OutputLine(Concat("PDATE=[", ProperCase(Now()), "]"))
  ENDIF

  /* a boolean returns HTTP 200 but renders nothing */
  IF @b == "pbool" THEN
    OutputLine(Concat("PBOOL=[", ProperCase(true), "]"))
  ENDIF

  /* both branches below abort the page: the start marker never renders */
  IF @b == "p0" THEN
    OutputLine(Concat("--- p0 start ---"))
    OutputLine(Concat("P0=[", ProperCase(), "]"))
  ENDIF

  IF @b == "p2" THEN
    OutputLine(Concat("--- p2 start ---"))
    OutputLine(Concat("P2A=[", ProperCase("a b", "c d"), "]"))
  ENDIF
]%%

Availability

Platform Available
Marketing Cloud Engagement Yes
Marketing Cloud Next Yes, from API 67.0

See also