I’m going to go and test this somewhere so I may need to make edits, but I think this should do it for you… as Michael mentioned in the comments, you have to wrap the output in the foreach. The method below will place a comma after each one, so I wrap the separator commas in their own span and then use CSS to hide the last one.
$sources = get_the_terms( $post->ID, 'source' );
if( !empty( $sources ) && !is_wp_error( $sources ) ) {
echo '<span class="source-meta">';
foreach( $sources as $source ) {
$source_link = sprintf(
'<a href="https://wordpress.stackexchange.com/questions/364187/%1$s">%2$s</a>%3$s',
esc_url( get_term_link( $source ) ),
esc_html( $source->name ),
'<span class="sep">, </span>'
);
echo sprintf( esc_html__( '%s', 'textdomain' ), $source_link );
}
echo '</span>';
}
In your stylesheet (style.css) you then want the following:
.source-meta .sep:last-of-type{
display:none;
}