Technically, and this is highly discouraged do to it's unfriendly and unpredictable nature, you can do this:
<?php _e( $data['hair']['type'] ); ?>
However, you'll need to have all the possible values for that ("brown hair", "blond hair", etc.), listed in a .POT file (a translation template) so that translators know what to translate. You'll need to either have control over whatever creates the JSON data, or intimate knowledge of what it can output, you won't be able to get far.
__() and similar functions take a string, look it up in the translation file they're provided (you usually pass a "textdomain" name so it knows which one to use), and returns the matching translated string it finds, otherwise returning the original string.
When people say that those functions "make the contents translatable" they mean it identifies the text as being translatable for a human or bot that goes through the code to find and assemble the .POT file, which is then given to translators to make completed versions in their languages.
Addendum (hit submit too soon):
This is why you don't pass a variable to those functions; the person or bot searching your code won't know what the actual text is that needs to be translated.
You're options are to either write those entries in the POT file yourself, like this:
msgid "Brown hair"
msgid "Blond hair"
Or otherwise have the values defined in your code somewhere like so:
$hair_types = array(
'Brown hair' => __( 'Brown hair' ),
'Blond hair' => __( 'Blond hair' ),
And then reference it somewhat like this:
<?php echo $hair_types[ $data['hair']['type'] ]; ?>
And hope the JSON you get doesn't add curve balls like "Brownish-reddish-goldish hair" and other values you weren't aware were even possible.
Basically, if you don't have control over what's generating the text, you won't be able to (nor expected to) localize it.