I'm almost embarassed to post this as it's the ugly result of "trying to learn scheme and lilypond internals all at the same time" - but at least it seems to work. I've tried to achieve exactly what you described: _If_ the first word is a dynamic _expression_, then Shevek's adjustment is applied.
%%% SNIPPET BEGINS
\version "2.19.44"
#(use-modules (ice-9 regex))
dynamic =
#(define-event-function (text) (markup?)
(if (string? text)
(let* ((underscores-replaced
(string-map
(lambda (x) (if (eq? x #\_) #\space x))
text)
)
(split-text (string-split underscores-replaced #\space))
(first-is-dynamic (string-match "^[mrzfps]*$" (car split-text)))
(formatted (map
(lambda (word)
(if (string-match "^[mrzfps]*$" word)
(markup #:dynamic word #:hspace 0.25)
(markup #:normal-text #:fontsize 0.625 #:italic word)))
split-text))
(offset (lambda (grob)
(if first-is-dynamic
(let* (
(first-as-dynamic
(list (markup #:dynamic (car split-text)))
)
(layout (ly:grob-layout grob))
(props (ly:grob-alist-chain grob
(ly:output-def-lookup layout 'text-font-defaults)))
(dyn-X-extent
(ly:stencil-extent
(ly:text-interface::interpret-markup layout props (make-line-markup first-as-dynamic))
X))
(width (abs
(- (cdr dyn-X-extent) (car dyn-X-extent))))
)
(- 1 (/ width 2))
)
0)
)))
#{
-\tweak X-offset #offset
#(make-dynamic-script (make-line-markup formatted))
#})
;; user provided a full-blown markup, so we don't mess with it:
#{
#(make-dynamic-script (markup #:normal-text #:fontsize 0.625 text))
#}))
<<
\new Staff { c''\dynamic "fffff dramatically" }
\new Staff { c''\dynamic "slightly more pp" }
>>
%%% SNIPPET ENDS