Ok I gave a VBA solution but here's a pure Excel version using the relatively new LAMBDA functions, no VBA.
Note I've posted an alternate version with comma separated args PRINTF(mask, arg1, arg2, ...)
over on Code Review, however I'll try to keep this one canonical and up to date as new better options become available
To achieve this I have created a named function with signature printf(mask, tokensArray)
where:
mask
is a string you want to format, containing positional {}
or indexed {i}
interpolation locations.
tokensArray
is the set of values to be substituted in, supplied as either a 1D Range (row or column), an array (hardcoded or returned from a function) or a single value.
... and which returns a formatted string. Called from a cell like:


=printf("Some text '{1}', more text: '{2}'", A1:A2) //continuous 1D row/col
=printf("Some text '{1}', more text: '{2}'", {"foo","bar"}) //hardcoded array
=printf("Single Value {1}", "foo")
or with positional args (left to right)
=printf("Some text '{}', more text: '{}'", A1:A2)
Define these two functions by entering them in the name manager (see LAMBDA function MSDN docs for detailed instructions, though this link will die I'm sure...) :
Param |
Value |
Name |
ReplaceRecursive |
Scope |
Workbook |
Comment |
Recursively substitutes {} or {i} with tokens from the tokens list, which it escapes one by one leaving } in the result string |
Refers To |
=LAMBDA(mask,tokens,i,tokenCount, IF(i >tokenCount, mask, LET(token, INDEX(tokens,i),escapedToken,SUBSTITUTE(token,"}", "\}"),inIndexedMode,ISERROR(FIND("{}",mask,1)),substituted, IF(inIndexedMode, SUBSTITUTE(mask,"{"&i&"}", escapedToken),SUBSTITUTE(mask, "{}", escapedToken,1) ),ReplaceRecursive(substituted,tokens,i+1,tokenCount)))) |
=LAMBDA(
mask,
tokens,
i,
tokenCount,
IF(
i > tokenCount,
mask,
LET(
token,
INDEX(
tokens,
i
),
escapedToken,
SUBSTITUTE(
token,
"}",
"\}"
),
inIndexedMode,
ISERROR(
FIND(
"{}",
mask,
1
)
),
substituted,
IF(
inIndexedMode,
SUBSTITUTE(
mask,
"{" & i & "}",
escapedToken
),
SUBSTITUTE(
mask,
"{}",
escapedToken,
1
)
),
ReplaceRecursive(
substituted,
tokens,
i + 1,
tokenCount
)
)
)
)
Param |
Value |
Name |
printf |
Scope |
Workbook |
Comment |
printf(mask: str, tokensArray: {array,} | range | str ) -> str | mask: string to substitute tokens into e.g. "Hello {}, {}" or "Hello {2}, {1}" (1-indexed) | tokensArray: 1D range or array of tokens, e.g. "world" or {"foo","bar"} or A1:A5 |
Refers To |
=LAMBDA(mask,tokensArray,LET(r,ROWS(tokensArray), c, COLUMNS(tokensArray), length, MAX(r,c), IF(AND(r>1, c>1), "tokensArray must be 1 dimensional", SUBSTITUTE(ReplaceRecursive(mask, tokensArray, 1, length), "\}","}")))) |
=LAMBDA(
mask,
tokensArray,
LET(
r,
ROWS(
tokensArray
),
c,
COLUMNS(
tokensArray
),
length,
MAX(
r,
c
),
IF(
AND(
r > 1,
c > 1
),
"tokensArray must be 1 dimensional",
SUBSTITUTE(
ReplaceRecursive(
mask,
tokensArray,
1,
length
),
"\}",
"}"
)
)
)
)
There will be improvements to Excel's LAMBDA functions that make this easier to write I'm sure, but this recursive approach is good for now I think.