Here is a general solution (taken from regexpatch.sty):
\documentclass{article}
\usepackage[french]{babel}
\usepackage{expl3}
\ExplSyntaxOn
\seq_new:N \g_ddef_commands_seq
\tl_new:N \l_ddef_prefix_tl
\tl_new:N \l_ddef_arg_tl
\tl_new:N \l_ddef_replacement_tl
\cs_new_protected:Npn \delimited #1 \def #2
{
\seq_gput_right:Nn \g_ddef_commands_seq { #2 }
#1 \tex_def:D #2
}
\AtBeginDocument{
\seq_map_inline:Nn \g_ddef_commands_seq { \ddef_normalize:N #1 }
}
\cs_new_protected:Npn \ddef_normalize:N #1
{
\tl_set:Nf \l_ddef_prefix_tl { \token_get_prefix_spec:N #1 }
\tl_set_rescan:Nnx \l_ddef_prefix_tl { } \l_ddef_prefix_tl
\tl_set:Nf \l_ddef_arg_tl { \token_get_arg_spec:N #1 }
\tl_set_rescan:Nnx \l_ddef_arg_tl { } \l_ddef_arg_tl
\tl_set:Nf \l_ddef_replacement_tl { \token_get_replacement_spec:N #1 }
\tl_set_rescan:Nnx \l_ddef_replacement_tl { } \l_ddef_replacement_tl
\use:x
{
\exp_not:V \l_ddef_prefix_tl
\tex_def:D
\exp_not:N #1
\exp_not:V \l_ddef_arg_tl
{ \exp_not:V \l_ddef_replacement_tl }
}
}
\ExplSyntaxOff
\delimited\def\foo!#1!{Whatever with #1}
\delimited\long\def\bar+#1+{Another with #1}
\begin{document}
\foo!x!
\number\catcode`!
\bar+y+
\number\catcode`+
\show\bar
\end{document}
This will print
Whatever with x
13
Another with y
12
Basically we remember all commands that are defined with \delimited\def; prefixes such as \long or \protected must go between those two tokens, but using \protected can open a can of worms if the command is used in a moving argument. Then at begin document, so after babel has possibly activated some characters, the commands are rebuilt under the current category codes.
Notice that also the tokens in the replacement text will possibly change category code, but this shouldn't be a problem.
By loading regexpatch the code is simpler:
\usepackage{regexpatch}
\ExplSyntaxOn
\seq_new:N \g_ddef_commands_seq
\cs_new_protected:Npn \delimited #1 \def #2
{
\seq_gput_right:Nn \g_ddef_commands_seq { #2 }
#1 \tex_def:D #2
}
\AtBeginDocument{
\seq_map_inline:Nn \g_ddef_commands_seq { \ddef_normalize:N #1 }
}
\cs_new_protected:Npn \ddef_normalize:N #1
{
\xpatch_get_all:N #1
\xpatch_rebuild:N #1
}
\ExplSyntaxOff
Note
If we want to be sure that the code is performed after babel has activated the characters (but babel should be among the package loaded very early) one can say
\usepackage{etoolbox}
and use \AfterEndPreamble instead of \AtBeginDocument.
\AtBeginDocument{\ifnum\catcode`\!=\active\let\foo\foo@active\else\let\foo\foo@nonactive\fi}as babel activates the characters at begin document. – egreg May 01 '12 at 17:51\AtBeginDocumenttrick must be written after\usepackage[french]{babel}if I understand correctly, as\AtBeginDocumentbuilds a kind of FIFO stack. – cjorssen May 01 '12 at 21:45\AfterEndPreambleofetoolboxto ensure that the code is executed afterbabelactivations. – egreg May 01 '12 at 21:58