My advice: **do both**. :)

The API client can decide to use the messages from your API or read each error code and create his own messages. Like:

        "errorCode": "4005",
        "message": "Person already registered"

But if you need to choose one, I would choose the error code solution. 

The reason: if your API client needs to show another message instead of the API message, the client will not able to have any unique identification of this API error message to map his own message. He will need to make some risk implementation, like mapping the entire API message with his client message, but this approach will break when the API decide to do any correction on the message. 

> I am hesitant on using method 1 as I have multiple clients for the API and the codes should be handled at both the places and would result in duplication of messages

This "duplication" is not really true, because I assume that each client has his own particularities and his way to show the best messages for his user. This "user view" problem is more a client problem, not an API problem. Your API needs to be more independent of what the clients are doing when consuming the API and concentrate on communicate well what kind of error is happening. When you have some doubt about your API responsibility, make your decision thinking that your API will be public and opened for anyone on the world to use :)