get_posts is WP_Query with four different defaults

The two are usually described as interchangeable and they are not: get_posts sets four arguments differently, and three of them change behaviour.

// get_posts() applies, whether you asked or not:
//   'suppress_filters' => true      ← pre_get_posts does NOT run
//   'no_found_rows'    => true      ← pagination is unavailable
//   'ignore_sticky_posts' => true
//   'post_status'      => 'publish'

// which is why a plugin filtering queries has no effect here
$posts = get_posts( array( 'suppress_filters' => false ) );

suppress_filters is the one that produces the confusing bug: a multilingual plugin or a visibility filter hooked to pre_get_posts is bypassed entirely, so get_posts returns content the site is supposed to be hiding. That is a correctness problem rather than a performance one. The defaults are otherwise sensible for a simple fetch, which is what it exists for — anything with pagination or filtering wants WP_Query.