<NME> LocalizationConsistencyTest.java
<BEF> package org.jabref.logic.l10n;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.testfx.framework.junit5.ApplicationExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

// Need to run on JavaFX thread since we are parsing FXML files
@ExtendWith(ApplicationExtension.class)
class LocalizationConsistencyTest {

    @Test
    void allFilesMustBeInLanguages() throws IOException {
        String bundle = "JabRef";
        // e.g., "<bundle>_en.properties", where <bundle> is [JabRef, Menu]
        Pattern propertiesFile = Pattern.compile(String.format("%s_.{2,}.properties", bundle));
        Set<String> localizationFiles = new HashSet<>();
        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Path.of("src/main/resources/l10n"))) {
            for (Path fullPath : directoryStream) {
                String fileName = fullPath.getFileName().toString();
                if (propertiesFile.matcher(fileName).matches()) {
                    localizationFiles.add(fileName.substring(bundle.length() + 1, fileName.length() - ".properties".length()));
                }
            }
        }

        Set<String> knownLanguages = Stream.of(Language.values())
                                           .map(Language::getId)
                                           .collect(Collectors.toSet());
        assertEquals(knownLanguages, localizationFiles, "There are some localization files that are not present in org.jabref.logic.l10n.Language or vice versa!");
    }

    @Test
    void ensureNoDuplicates() {
        String bundle = "JabRef";
        for (Language lang : Language.values()) {
            String propertyFilePath = String.format("/l10n/%s_%s.properties", bundle, lang.getId());

            // read in
            DuplicationDetectionProperties properties = new DuplicationDetectionProperties();
            try (InputStream is = LocalizationConsistencyTest.class.getResourceAsStream(propertyFilePath);
                 InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
                properties.load(reader);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            List<String> duplicates = properties.getDuplicates();

            assertEquals(Collections.emptyList(), duplicates, "Duplicate keys inside bundle " + bundle + "_" + lang.getId());
        }
    }

    @Test
    void keyValueShouldBeEqualForEnglishPropertiesMessages() {
        Properties englishKeys = LocalizationParser.getProperties(String.format("/l10n/%s_%s.properties", "JabRef", "en"));
        for (Map.Entry<Object, Object> entry : englishKeys.entrySet()) {
            String expectedKeyEqualsKey = String.format("%s=%s", entry.getKey(), entry.getKey().toString().replace("\n", "\\n"));
            String actualKeyEqualsValue = String.format("%s=%s", entry.getKey(), entry.getValue().toString().replace("\n", "\\n"));
            assertEquals(expectedKeyEqualsKey, actualKeyEqualsValue);
        }
    }

    @Test
    void languageKeysShouldNotContainUnderscoresForSpaces() throws IOException {
        final List<LocalizationEntry> quotedEntries = LocalizationParser
                .findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.LANG)
                .stream()
                .filter(key -> key.getKey().contains("\\_"))
                .collect(Collectors.toList());
        assertEquals(Collections.emptyList(), quotedEntries,
                "Language keys must not use underscores for spaces! Use \"This is a message\" instead of \"This_is_a_message\".\n" +
                        "Please correct the following entries:\n" +
                        quotedEntries
                                .stream()
                                .map(key -> String.format("\n%s (%s)\n", key.getKey(), key.getPath()))
                                .collect(Collectors.toList()));
    }

    @Test
    void languageKeysShouldNotContainHtmlBrAndHtmlP() throws IOException {
        final List<LocalizationEntry> entriesWithHtml = LocalizationParser
                .findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.LANG)
                .stream()
                .filter(key -> key.getKey().contains("<br>") || key.getKey().contains("<p>"))
                .collect(Collectors.toList());
        assertEquals(Collections.emptyList(), entriesWithHtml,
                "Language keys must not contain HTML <br> or <p>. Use \\n for a line break.\n" +
                        "Please correct the following entries:\n" +
                        entriesWithHtml
                                .stream()
                                .map(key -> String.format("\n%s (%s)\n", key.getKey(), key.getPath()))
                                .collect(Collectors.toList()));
    }

    @Test
    void findMissingLocalizationKeys() throws IOException {
        List<LocalizationEntry> missingKeys = LocalizationParser.findMissingKeys(LocalizationBundleForTest.LANG)
                                                                .stream()
                                                                .collect(Collectors.toList());
        assertEquals(Collections.emptyList(), missingKeys,
                missingKeys.stream()
                           .map(key -> LocalizationKey.fromKey(key.getKey()))
                           .map(key -> String.format("%s=%s",
                                   key.getEscapedPropertiesKey(),
                                   key.getValueForEnglishPropertiesFile()))
                           .collect(Collectors.joining("\n",
                                   "\n\nDETECTED LANGUAGE KEYS WHICH ARE NOT IN THE ENGLISH LANGUAGE FILE\n" +
                                           "PASTE THESE INTO THE ENGLISH LANGUAGE FILE\n\n",
                                   "\n\n")));
    }

    @Test
    void findObsoleteLocalizationKeys() throws IOException {
        Set<String> obsoleteKeys = LocalizationParser.findObsolete(LocalizationBundleForTest.LANG);
        assertEquals(Collections.emptySet(), obsoleteKeys,
                obsoleteKeys.stream().collect(Collectors.joining("\n",
                        "Obsolete keys found in language properties file: \n\n",
                        "\n\n1. CHECK IF THE KEY IS REALLY NOT USED ANYMORE\n" +
                                "2. REMOVE THESE FROM THE ENGLISH LANGUAGE FILE\n"))
        );
    }

    @Test
    void localizationParameterMustIncludeAString() throws IOException {
        // Must start or end with "
        // Localization.lang("test"), Localization.lang("test" + var), Localization.lang(var + "test")
        // TODO: Localization.lang(var1 + "test" + var2) not covered
        // Localization.lang("Problem downloading from %1", address)
        Set<LocalizationEntry> keys = LocalizationParser.findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.LANG);
        for (LocalizationEntry e : keys) {
            assertTrue(e.getKey().startsWith("\"") || e.getKey().endsWith("\""), "Illegal localization parameter found. Must include a String with potential concatenation or replacement parameters. Illegal parameter: Localization.lang(" + e.getKey());
        }

        keys = LocalizationParser.findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.MENU);
        for (LocalizationEntry e : keys) {
            assertTrue(e.getKey().startsWith("\"") || e.getKey().endsWith("\""), "Illegal localization parameter found. Must include a String with potential concatenation or replacement parameters. Illegal parameter: Localization.lang(" + e.getKey());
        }
    }

    private static Language[] installedLanguages() {
        return Language.values();
    }

    @ParameterizedTest
    @MethodSource("installedLanguages")
    void resourceBundleExists(Language language) {
        Path messagesPropertyFile = Path.of("src/main/resources").resolve(Localization.RESOURCE_PREFIX + "_" + language.getId() + ".properties");
        assertTrue(Files.exists(messagesPropertyFile));
    }

    @ParameterizedTest
    @MethodSource("installedLanguages")
    void languageCanBeLoaded(Language language) {
        Locale oldLocale = Locale.getDefault();
        try {
            Locale locale = Language.convertToSupportedLocale(language).get();
        }
    }

    private static class DuplicationDetectionProperties extends Properties {

        private static final long serialVersionUID = 1L;
            return super.put(key, value);
        }

        List<String> getDuplicates() {
            return duplicates;
        }
    }
}

<MSG> adding of the new method localisationTestForInvalidStrings not complete for instance

<DFF> @@ -183,6 +183,28 @@ public class LocalizationConsistencyTest {
         }
     }
 
+    @Test
+    public void localisationTestForInvalidStrings() throws IOException {
+        for (String bundle : Arrays.asList("JabRef", "Menu")) {
+            for (String lang : Languages.LANGUAGES.values()) {
+                String propertyFilePath = String.format("/l10n/%s_%s.properties", bundle, lang);
+
+                // read in
+                DuplicationDetectionProperties properties = new DuplicationDetectionProperties();
+                try (InputStream is = LocalizationConsistencyTest.class.getResourceAsStream(propertyFilePath);
+                        InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
+                    properties.load(reader);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+
+                //parse object "properties" to find any spaces
+                //if there is any space replace it with an underscore
+
+            }
+        }
+    }
+
     private static class DuplicationDetectionProperties extends Properties {
 
         private static final long serialVersionUID = 1L;

{'commit': 'diff --git a/src/test/java/org/jabref/logic/l10n/LocalizationConsistencyTest.java b/src/test/java/org/jabref/logic/l10n/LocalizationConsistencyTest.java\nindex 2da5cc5b96..ef573f5f90 100644\n--- a/src/test/java/org/jabref/logic/l10n/LocalizationConsistencyTest.java\n+++ b/src/test/java/org/jabref/logic/l10n/LocalizationConsistencyTest.java\n@@ -183,6 +183,28 @@ public class LocalizationConsistencyTest {\n         }\n     }\n \n+    @Test\n+    public void localisationTestForInvalidStrings() throws IOException {\n+        for (String bundle : Arrays.asList("JabRef", "Menu")) {\n+            for (String lang : Languages.LANGUAGES.values()) {\n+                String propertyFilePath = String.format("/l10n/%s_%s.properties", bundle, lang);\n+\n+                // read in\n+                DuplicationDetectionProperties properties = new DuplicationDetectionProperties();\n+                try (InputStream is = LocalizationConsistencyTest.class.getResourceAsStream(propertyFilePath);\n+                        InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {\n+                    properties.load(reader);\n+                } catch (IOException e) {\n+                    throw new RuntimeException(e);\n+                }\n+\n+                //parse object "properties" to find any spaces\n+                //if there is any space replace it with an underscore\n+\n+            }\n+        }\n+    }\n+\n     private static class DuplicationDetectionProperties extends Properties {\n \n         private static final long serialVersionUID = 1L;\n', 'commit_subject': 'adding of the new method localisationTestForInvalidStrings not complete for instance', 'repo_name': 'JabRef/jabref', 'committer': {'name': 'steph37tours', 'email': '1bf45ac5e949ca126ec52a02276eb1d165c5aa71@orange.fr', 'time_sec': '1504618647', 'tz_offset': '120', 'date': {'seconds': '1504618647'}}, 'commit_tree': 'f7b20621e2494946b474b4938deb4f39270061fb', 'commit_message': 'adding of the new method localisationTestForInvalidStrings not complete for instance\n', 'language_name': 'Ruby', 'license': 'mit', 'hunks': '@@ -183,6 +183,28 @@ public class LocalizationConsistencyTest {\n         }\n     }\n \n+    @Test\n+    public void localisationTestForInvalidStrings() throws IOException {\n+        for (String bundle : Arrays.asList("JabRef", "Menu")) {\n+            for (String lang : Languages.LANGUAGES.values()) {\n+                String propertyFilePath = String.format("/l10n/%s_%s.properties", bundle, lang);\n+\n+                // read in\n+                DuplicationDetectionProperties properties = new DuplicationDetectionProperties();\n+                try (InputStream is = LocalizationConsistencyTest.class.getResourceAsStream(propertyFilePath);\n+                        InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {\n+                    properties.load(reader);\n+                } catch (IOException e) {\n+                    throw new RuntimeException(e);\n+                }\n+\n+                //parse object "properties" to find any spaces\n+                //if there is any space replace it with an underscore\n+\n+            }\n+        }\n+    }\n+\n     private static class DuplicationDetectionProperties extends Properties {\n \n         private static final long serialVersionUID = 1L;\n', 'addition_count': 22, 'deletion_count': 0, 'src_file': 'a/src/test/java/org/jabref/logic/l10n/LocalizationConsistencyTest.java', 'tgt_file': 'b/src/test/java/org/jabref/logic/l10n/LocalizationConsistencyTest.java', 'file_extension': '.java', 'before_file': 'package org.jabref.logic.l10n;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.DirectoryStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.testfx.framework.junit5.ApplicationExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n// Need to run on JavaFX thread since we are parsing FXML files\n@ExtendWith(ApplicationExtension.class)\nclass LocalizationConsistencyTest {\n\n    @Test\n    void allFilesMustBeInLanguages() throws IOException {\n        String bundle = "JabRef";\n        // e.g., "<bundle>_en.properties", where <bundle> is [JabRef, Menu]\n        Pattern propertiesFile = Pattern.compile(String.format("%s_.{2,}.properties", bundle));\n        Set<String> localizationFiles = new HashSet<>();\n        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Path.of("src/main/resources/l10n"))) {\n            for (Path fullPath : directoryStream) {\n                String fileName = fullPath.getFileName().toString();\n                if (propertiesFile.matcher(fileName).matches()) {\n                    localizationFiles.add(fileName.substring(bundle.length() + 1, fileName.length() - ".properties".length()));\n                }\n            }\n        }\n\n        Set<String> knownLanguages = Stream.of(Language.values())\n                                           .map(Language::getId)\n                                           .collect(Collectors.toSet());\n        assertEquals(knownLanguages, localizationFiles, "There are some localization files that are not present in org.jabref.logic.l10n.Language or vice versa!");\n    }\n\n    @Test\n    void ensureNoDuplicates() {\n        String bundle = "JabRef";\n        for (Language lang : Language.values()) {\n            String propertyFilePath = String.format("/l10n/%s_%s.properties", bundle, lang.getId());\n\n            // read in\n            DuplicationDetectionProperties properties = new DuplicationDetectionProperties();\n            try (InputStream is = LocalizationConsistencyTest.class.getResourceAsStream(propertyFilePath);\n                 InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {\n                properties.load(reader);\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n\n            List<String> duplicates = properties.getDuplicates();\n\n            assertEquals(Collections.emptyList(), duplicates, "Duplicate keys inside bundle " + bundle + "_" + lang.getId());\n        }\n    }\n\n    @Test\n    void keyValueShouldBeEqualForEnglishPropertiesMessages() {\n        Properties englishKeys = LocalizationParser.getProperties(String.format("/l10n/%s_%s.properties", "JabRef", "en"));\n        for (Map.Entry<Object, Object> entry : englishKeys.entrySet()) {\n            String expectedKeyEqualsKey = String.format("%s=%s", entry.getKey(), entry.getKey().toString().replace("\\n", "\\\\n"));\n            String actualKeyEqualsValue = String.format("%s=%s", entry.getKey(), entry.getValue().toString().replace("\\n", "\\\\n"));\n            assertEquals(expectedKeyEqualsKey, actualKeyEqualsValue);\n        }\n    }\n\n    @Test\n    void languageKeysShouldNotContainUnderscoresForSpaces() throws IOException {\n        final List<LocalizationEntry> quotedEntries = LocalizationParser\n                .findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.LANG)\n                .stream()\n                .filter(key -> key.getKey().contains("\\\\_"))\n                .collect(Collectors.toList());\n        assertEquals(Collections.emptyList(), quotedEntries,\n                "Language keys must not use underscores for spaces! Use \\"This is a message\\" instead of \\"This_is_a_message\\".\\n" +\n                        "Please correct the following entries:\\n" +\n                        quotedEntries\n                                .stream()\n                                .map(key -> String.format("\\n%s (%s)\\n", key.getKey(), key.getPath()))\n                                .collect(Collectors.toList()));\n    }\n\n    @Test\n    void languageKeysShouldNotContainHtmlBrAndHtmlP() throws IOException {\n        final List<LocalizationEntry> entriesWithHtml = LocalizationParser\n                .findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.LANG)\n                .stream()\n                .filter(key -> key.getKey().contains("<br>") || key.getKey().contains("<p>"))\n                .collect(Collectors.toList());\n        assertEquals(Collections.emptyList(), entriesWithHtml,\n                "Language keys must not contain HTML <br> or <p>. Use \\\\n for a line break.\\n" +\n                        "Please correct the following entries:\\n" +\n                        entriesWithHtml\n                                .stream()\n                                .map(key -> String.format("\\n%s (%s)\\n", key.getKey(), key.getPath()))\n                                .collect(Collectors.toList()));\n    }\n\n    @Test\n    void findMissingLocalizationKeys() throws IOException {\n        List<LocalizationEntry> missingKeys = LocalizationParser.findMissingKeys(LocalizationBundleForTest.LANG)\n                                                                .stream()\n                                                                .collect(Collectors.toList());\n        assertEquals(Collections.emptyList(), missingKeys,\n                missingKeys.stream()\n                           .map(key -> LocalizationKey.fromKey(key.getKey()))\n                           .map(key -> String.format("%s=%s",\n                                   key.getEscapedPropertiesKey(),\n                                   key.getValueForEnglishPropertiesFile()))\n                           .collect(Collectors.joining("\\n",\n                                   "\\n\\nDETECTED LANGUAGE KEYS WHICH ARE NOT IN THE ENGLISH LANGUAGE FILE\\n" +\n                                           "PASTE THESE INTO THE ENGLISH LANGUAGE FILE\\n\\n",\n                                   "\\n\\n")));\n    }\n\n    @Test\n    void findObsoleteLocalizationKeys() throws IOException {\n        Set<String> obsoleteKeys = LocalizationParser.findObsolete(LocalizationBundleForTest.LANG);\n        assertEquals(Collections.emptySet(), obsoleteKeys,\n                obsoleteKeys.stream().collect(Collectors.joining("\\n",\n                        "Obsolete keys found in language properties file: \\n\\n",\n                        "\\n\\n1. CHECK IF THE KEY IS REALLY NOT USED ANYMORE\\n" +\n                                "2. REMOVE THESE FROM THE ENGLISH LANGUAGE FILE\\n"))\n        );\n    }\n\n    @Test\n    void localizationParameterMustIncludeAString() throws IOException {\n        // Must start or end with "\n        // Localization.lang("test"), Localization.lang("test" + var), Localization.lang(var + "test")\n        // TODO: Localization.lang(var1 + "test" + var2) not covered\n        // Localization.lang("Problem downloading from %1", address)\n        Set<LocalizationEntry> keys = LocalizationParser.findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.LANG);\n        for (LocalizationEntry e : keys) {\n            assertTrue(e.getKey().startsWith("\\"") || e.getKey().endsWith("\\""), "Illegal localization parameter found. Must include a String with potential concatenation or replacement parameters. Illegal parameter: Localization.lang(" + e.getKey());\n        }\n\n        keys = LocalizationParser.findLocalizationParametersStringsInJavaFiles(LocalizationBundleForTest.MENU);\n        for (LocalizationEntry e : keys) {\n            assertTrue(e.getKey().startsWith("\\"") || e.getKey().endsWith("\\""), "Illegal localization parameter found. Must include a String with potential concatenation or replacement parameters. Illegal parameter: Localization.lang(" + e.getKey());\n        }\n    }\n\n    private static Language[] installedLanguages() {\n        return Language.values();\n    }\n\n    @ParameterizedTest\n    @MethodSource("installedLanguages")\n    void resourceBundleExists(Language language) {\n        Path messagesPropertyFile = Path.of("src/main/resources").resolve(Localization.RESOURCE_PREFIX + "_" + language.getId() + ".properties");\n        assertTrue(Files.exists(messagesPropertyFile));\n    }\n\n    @ParameterizedTest\n    @MethodSource("installedLanguages")\n    void languageCanBeLoaded(Language language) {\n        Locale oldLocale = Locale.getDefault();\n        try {\n            Locale locale = Language.convertToSupportedLocale(language).get();\n        }\n    }\n\n    private static class DuplicationDetectionProperties extends Properties {\n\n        private static final long serialVersionUID = 1L;\n            return super.put(key, value);\n        }\n\n        List<String> getDuplicates() {\n            return duplicates;\n        }\n    }\n}\n', 'message': 'adding of the new method localisationTestForInvalidStrings not complete for instance\n', 'author_name': 'steph37tours', 'lang': 'java'}