Hello. For this, you would have to recreate the default heading show rule from scratch and change its behavior:
#import "@preview/text-dirr:1.0.0": text-dir
#set heading(numbering: "1.")
#show heading.where(level: 2): it => {
let spacing = h(0.3em, weak: true)
let numbering = counter(heading).display(it.numbering)
let realized = it.body + spacing + numbering
let indent = if it.hanging-indent == auto { 0pt } else { it.hanging-indent }
if it.hanging-indent == auto { indent = measure(numbering).width + 0.3em }
let start = if text-dir() == ltr { "left" } else { "right" }
block(inset: ((start): indent), h(-indent) + realized)
}
= Section
== Subsection
#heading(depth: 2, hanging-indent: 5em, numbering: "I.")[#lorem(15)]

Now that the text-dirr package is out, so you can fully copy the default behavior that uses “start” alignment or side, which depends on text.dir.
For full heading implementation, see Add default typst implementation code where possible · Issue #5095 · typst/typst · GitHub. Since the numbering is now not at the start, I don’t know if you need auto hanging indent or hanging indent at all. If not, then you can simply do this:
#show heading.where(level: 2): it => block(
it.body + h(0.3em, weak: true) + counter(heading).display(it.numbering),
)