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. Callers then can decide if they want to throw an exception in case of errors, or if they just want to log the results, or take other actions.
When it turns out this approach is not efficient enough for certain scenarios, (in reality, justifiedjustified 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.