Repository: urlaubsverwaltung/urlaubsverwaltung (java)

Issue:
Fehlerhafte Darstellung individuelle Abwesenheitsarten bei Auswahl von Locale de_DE
Wenn im Browser die Locale `de_DE` statt `de` ausgewählt ist, dann wird folgender Platzhalter angezeigt, statt der deutschen Übersetzung.
![grafik](https://github.com/urlaubsverwaltung/urlaubsverwaltung/assets/142107/80d1296a-8506-4b9d-a4ba-3c0ee6d03ce9)


Pull request description:
fix Abwesenheitsart Label Fallback für das Land (Locale)
closes #4660

<!--

Thanks for contributing to the Urlaubsverwaltung.
Please review the following notes before submitting you pull request.

Please look for other issues or pull requests which already work on this topic. Is somebody already on it? Do you need to synchronize?

# Security Vulnerabilities

🛑 STOP! 🛑 If your contribution fixes a security vulnerability, please do not submit it.
Instead, please open a report of a security vulnerability via
[Report a security vulnerability](https://github.com/urlaubsverwaltung/urlaubsverwaltung/security/advisories/new)


# Describing Your Changes

If, having reviewed the notes above, you're ready to submit your pull request, please
provide a brief description of the proposed changes.

If they:
 🐞 fix a bug, please describe the broken behaviour and how the changes fix it.
    Please label with 'type: bug' and 'status: new'
    
 🎁 make an enhancement, please describe the new functionality and why you believe it's useful.
    Please label with 'type: enhancement' and 'status: new'
 
If your pull request relates to any existing issues,
please reference them by using the issue number prefixed with #.

-->


Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/application/vacationtype/CustomVacationType.java b/src/main/java/org/synyx/urlaubsverwaltung/application/vacationtype/CustomVacationType.java
index 089b29891..2ae8a14a9 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/application/vacationtype/CustomVacationType.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/application/vacationtype/CustomVacationType.java
@@ -47,10 +47,16 @@ public final class CustomVacationType extends VacationType<CustomVacationType> {
     public static Builder builder(MessageSource messageSource) {
 
         final VacationTypeLabelResolver<CustomVacationType> labelResolver =
-            (vacationType, locale) -> Optional.ofNullable(vacationType.labelsByLocale().get(locale))
-                .map(VacationTypeLabel::label)
-                .filter(StringUtils::hasText)
-                .orElseGet(() -> messageSource.getMessage("vacationtype.label.fallback", new Object[]{}, locale));
+            (vacationType, locale) -> {
+                final Map<Locale, VacationTypeLabel> labelByLocale = vacationType.labelsByLocale();
+
+                return Optional.ofNullable(labelByLocale.get(locale))
+                    .map(VacationTypeLabel::label)
+                    .filter(StringUtils::hasText)
+                    // fallback to base locale (e.g. "de" for incoming "de_DE")
+                    .or(() -> Optional.of(labelByLocale.get(Locale.forLanguageTag(locale.getLanguage()))).map(VacationTypeLabel::label).filter(StringUtils::hasText))
+                    .orElseGet(() -> messageSource.getMessage("vacationtype.label.fallback", new Object[]{}, locale));
+            };
 
         return new Builder(labelResolver);
     }


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/application/vacationtype/CustomVacationTypeTest.java b/src/test/java/org/synyx/urlaubsverwaltung/application/vacationtype/CustomVacationTypeTest.java
new file mode 100644
index 000000000..3d4a057df
--- /dev/null
+++ b/src/test/java/org/synyx/urlaubsverwaltung/application/vacationtype/CustomVacationTypeTest.java
@@ -0,0 +1,47 @@
+package org.synyx.urlaubsverwaltung.application.vacationtype;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.springframework.context.support.StaticMessageSource;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CustomVacationTypeTest {
+
+    static Stream<Arguments> localeTuples() {
+        return Stream.of(
+            Arguments.of("de", "de-de"),
+            Arguments.of("en", "en-au")
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("localeTuples")
+    void ensureLabelFallbackToBaseLocale(String language, String languageAndCountry) {
+
+        final CustomVacationType vacationType = CustomVacationType.builder(new StaticMessageSource())
+            .labels(List.of(new VacationTypeLabel(Locale.forLanguageTag(language), "label")))
+            .build();
+
+        assertThat(vacationType.getLabel(Locale.forLanguageTag(languageAndCountry))).isEqualTo("label");
+    }
+
+    @Test
+    void ensureLabelFallbackToText() {
+
+        final StaticMessageSource messageSource = new StaticMessageSource();
+        messageSource.addMessage("vacationtype.label.fallback", Locale.GERMAN, "fallback");
+
+        final CustomVacationType vacationType = CustomVacationType.builder(messageSource)
+            .labels(List.of(new VacationTypeLabel(Locale.GERMAN, "")))
+            .build();
+
+        assertThat(vacationType.getLabel(Locale.GERMAN)).isEqualTo("fallback");
+    }
+}