I would typically start with the most simple and DRY solution first you can come up with. I guess this would be a reusable component which produces everything (error objects and value objects) and let callers decide what they need, or if they want to throw an exception, or log the results.

When it turns out this approach is not efficient enough for certain scenarios, (in reality, justified by real observations and measurements, not just hypothetically), then it is time to optimize, not beforehand. Optimization could include, for example, parameters for the component to control which values to return, like

- localized error messages 

- value objects (or not)

or if certain validation checks shall be applied or not.

The decision if validation stops at the first error, or after the first 10 errors, or after collecting all errors can be solved most cleanly by using an iterator (a.k.a. generator function) approach, if your environment supports this (so the caller can simply stop iterating over the resultset, when they like to). But you could also introduce a parameter like "maximum number of errors to return".

IMHO the most important thing, however, is, that you don't try to make all these decisions upfront. Instead, start simple and small, and optimize by doing some refactoring cycles. No stranger from the internet can do this for you, hence we cannot tell you which of these alternatives will suit your case best.