This is because of the way salesforce loads custom labels. we had the same problem since we had a survey framework where questions were loaded from metadata and so we did the concatenation on the client side.
Salesforce loads all the labels as part of markup or controller when it loads the component to UI, in this case since the label is coming dynamically it makes a async call to get the label and thats why you dont see the dynamic label in UI.
Any time you put in the markup you will see its value
Check out this section of documentation: getting labels in javascript
Read the static and dynamic label access in javascript part:
Dynamic Labels
$A.get(labelReference) must be able to resolve the label reference at compile time, so that the label values can be sent to the client along with the component definition.
If you must defer label resolution until runtime, you can dynamically create labels in JavaScript code. This technique can be useful when you need to use a label, but which specific label isn’t known until runtime.
If the label is already known on the client, $A.get() displays the label. If the value is not known, an empty string is displayed in production mode, or a placeholder value showing the label key is displayed in debug mode.
Using $A.get()with a label that can't be determined at runtime means that dynamicLabel is an empty string, and won’t be updated to the retrieved value. Since the label, "$Label.c." + day, is dynamically generated, the framework can’t parse it or send it to the client when the component is requested.
There are a few alternative approaches to using $A.get() so that you can work with dynamically generated labels.
If your component uses a known set of dynamically constructed labels, you can avoid a server roundtrip for the labels by adding a reference to the labels in a JavaScript resource. The framework sends these labels to the client when the component is requested. For example, if your component dynamically generates $Label.c.task_mode_today and $Label.c.task_mode_tomorrow label keys, you can add references to the labels in a comment in a JavaScript resource, such as a client-side controller or helper
// hints to ensure labels are preloaded
// $Label.c.task_mode_today
// $Label.c.task_mode_tomorrow
If your code dynamically generates many labels, this approach doesn’t scale well.
If you don’t want to add comment hints for all the potential labels, the alternative is to use $A.getReference(). This approach comes with the added cost of a server trip to retrieve the label value.
Performance Spoiler: If you use hints approach and if you have more than 100 dynamic labels the component load times goes up. The approach that we took was to load all labels from server side in a map to client and then access it in javascript