PR #511: Improve validation messages (German and default) (#536)

* PR #511: Improve validation messages (German and default)

* fixed NoClassDefFoundError for ValidatorTypeCode due to incomplete ResourceBundle
diff --git a/pom.xml b/pom.xml
index c0c1386..b0df773 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.networknt</groupId>
     <artifactId>json-schema-validator</artifactId>
-    <version>1.0.67</version>
+    <version>1.0.68-SNAPSHOT</version>
     <packaging>bundle</packaging>
     <description>A json schema validator that supports draft v4, v6, v7 and v2019-09</description>
     <url>https://github.com/networknt/json-schema-validator</url>
diff --git a/src/main/java/com/networknt/schema/I18nSupport.java b/src/main/java/com/networknt/schema/I18nSupport.java
index a6cb162..70c78f4 100644
--- a/src/main/java/com/networknt/schema/I18nSupport.java
+++ b/src/main/java/com/networknt/schema/I18nSupport.java
@@ -1,5 +1,6 @@
 package com.networknt.schema;
 
+import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
 /**
@@ -8,10 +9,36 @@
  * @author leaves chen leaves615@gmail.com
  */
 public class I18nSupport {
+
     private static final String BASE_NAME = "jsv-messages";
-    private static ResourceBundle bundle = ResourceBundle.getBundle(BASE_NAME);
+    private static final ResourceBundle bundle;
+
+    static {
+        ResourceBundle tmpBundle = null;
+        try {
+            tmpBundle = ResourceBundle.getBundle(BASE_NAME);
+        } catch (MissingResourceException mre) {
+            // Need to avoid by all means that we fail loading ValidatorTypeCode with a
+            // "java.lang.NoClassDefFoundError: Could not initialize class com.networknt.schema.ValidatorTypeCode"
+            // due to the fact that a ResourceBundle is incomplete
+            mre.printStackTrace();
+            System.exit(1);
+        }
+        bundle = tmpBundle;
+    }
 
     public static String getString(String key) {
-        return bundle.getString(key);
+        String retval = null;
+        try {
+            retval = bundle.getString(key);
+        } catch (MissingResourceException mre) {
+            // Need to avoid by all means that we fail loading ValidatorTypeCode with a
+            // "java.lang.NoClassDefFoundError: Could not initialize class com.networknt.schema.ValidatorTypeCode"
+            // due to the fact that a ResourceBundle is incomplete
+            mre.printStackTrace();
+            System.exit(2);
+        }
+        return retval;
     }
+    
 }
diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java
index 1df3369..b95bae6 100644
--- a/src/main/java/com/networknt/schema/OneOfValidator.java
+++ b/src/main/java/com/networknt/schema/OneOfValidator.java
@@ -308,8 +308,7 @@
             msg = msg.concat(schemaValue);

         }

 

-        return ValidationMessage.of(getValidatorType().getValue(), ValidatorTypeCode.ONE_OF ,

-                at, String.format("but more than one schemas {%s} are valid ",msg));

+        return ValidationMessage.of(getValidatorType().getValue(), ValidatorTypeCode.ONE_OF, at, msg);

     }

 

     @Override

diff --git a/src/main/java/com/networknt/schema/ValidatorTypeCode.java b/src/main/java/com/networknt/schema/ValidatorTypeCode.java
index 4fc4f68..e84c059 100644
--- a/src/main/java/com/networknt/schema/ValidatorTypeCode.java
+++ b/src/main/java/com/networknt/schema/ValidatorTypeCode.java
@@ -62,7 +62,7 @@
     TYPE("type", "1029", new MessageFormat(I18nSupport.getString("type")), TypeValidator.class, 15),
     UNION_TYPE("unionType", "1030", new MessageFormat(I18nSupport.getString("unionType")), UnionTypeValidator.class, 15),
     UNIQUE_ITEMS("uniqueItems", "1031", new MessageFormat(I18nSupport.getString("uniqueItems")), UniqueItemsValidator.class, 15),
-    DATETIME("date-time", "1034", new MessageFormat("{0}: {1} is an invalid {2}"), null, 15),
+    DATETIME("dateTime", "1034", new MessageFormat(I18nSupport.getString("dateTime")), null, 15),
     UUID("uuid", "1035", new MessageFormat(I18nSupport.getString("uuid")), null, 15),
     ID("id", "1036", new MessageFormat(I18nSupport.getString("id")), null, 15),
     IF_THEN_ELSE("if", "1037", null, IfValidator.class, 12),  // V7|V201909
@@ -91,11 +91,11 @@
     private final MessageFormat messageFormat;
     private String customMessage;
     private final String errorCodeKey;
-    private final Class validator;
+    private final Class<?> validator;
     private final long versionCode;
 
 
-    private ValidatorTypeCode(String value, String errorCode, MessageFormat messageFormat, Class validator, long versionCode) {
+    private ValidatorTypeCode(String value, String errorCode, MessageFormat messageFormat, Class<?> validator, long versionCode) {
         this.value = value;
         this.errorCode = errorCode;
         this.messageFormat = messageFormat;
diff --git a/src/main/resources/jsv-messages.properties b/src/main/resources/jsv-messages.properties
index 313f692..6c7be2f 100644
--- a/src/main/resources/jsv-messages.properties
+++ b/src/main/resources/jsv-messages.properties
@@ -1,41 +1,42 @@
+$ref = {0}: has an error with 'refs'
 additionalProperties = {0}.{1}: is not defined in the schema and the schema does not allow additional properties
 allOf = {0}: should be valid to all the schemas {1}
 anyOf = {0}: should be valid to any of the schemas {1}
+const = {0}: must be a constant value {1}
+contains = {0}: does not contain an element that passes these validations: {1}
 crossEdits = {0}: has an error with 'cross edits'
+dateTime = {0}: {1} is an invalid {2}
 dependencies = {0}: has an error with dependencies {1}
 dependentRequired = {0}: has a missing property which is dependent required {1}
 dependentSchemas = {0}: has an error with dependentSchemas {1}
 edits = {0}: has an error with 'edits'
 enum = {0}: does not have a value in the enumeration {1}
-format = {0}: does not match the {1} pattern {2}
-items = {0}[{1}]: no validator found at this index
-maximum = {0}: must have a maximum value of {1}
-maxItems = {0}: there must be a maximum of {1} items in the array
-maxLength = {0}: may only be {1} characters long
-maxProperties = {0}: may only have a maximum of {1} properties
-minimum = {0}: must have a minimum value of {1}
-minItems = {0}: there must be a minimum of {1} items in the array
-minLength = {0}: must be at least {1} characters long
-minProperties = {0}: should have a minimum of {1} properties
-multipleOf = {0}: must be multiple of {1}
-notAllowed = {0}.{1}: is not allowed but it is in the data
-not = {0}: should not be valid to the schema {1}
-oneOf = {0}: should be valid to one and only one of the schemas {1}
-patternProperties = {0}: has some error with 'pattern properties'
-pattern = {0}: does not match the regex pattern {1}
-properties = {0}: has an error with 'properties'
-readOnly = {0}: is a readonly field, it cannot be changed
-$ref = {0}: has an error with 'refs'
-required = {0}.{1}: is missing but it is required
-type = {0}: {1} found, {2} expected
-unionType = {0}: {1} found, but {2} is required
-uniqueItems = {0}: the items in the array must be unique
-uuid = {0}: {1} is an invalid {2}
-id = {0}: {1} is an invalid segment for URI {2}
 exclusiveMaximum = {0}: must have an exclusive maximum value of {1}
 exclusiveMinimum = {0}: must have an exclusive minimum value of {1}
 false = Boolean schema false is not valid
-const = {0}: must be a constant value {1}
-contains = {0}: does not contain an element that passes these validations: {1}
+format = {0}: does not match the {1} pattern {2}
+id = {0}: {1} is an invalid segment for URI {2}
+items = {0}[{1}]: no validator found at this index
+maxItems = {0}: there must be a maximum of {1} items in the array
+maxLength = {0}: may only be {1} characters long
+maxProperties = {0}: may only have a maximum of {1} properties
+maximum = {0}: must have a maximum value of {1}
+minItems = {0}: there must be a minimum of {1} items in the array
+minLength = {0}: must be at least {1} characters long
+minProperties = {0}: should have a minimum of {1} properties
+minimum = {0}: must have a minimum value of {1}
+multipleOf = {0}: must be multiple of {1}
+not = {0}: should not be valid to the schema {1}
+notAllowed = {0}.{1}: is not allowed but it is in the data
+oneOf = {0}: should be valid to one and only one of schema, but more than one are valid: {1}
+pattern = {0}: does not match the regex pattern {1}
+patternProperties = {0}: has some error with 'pattern properties'
+properties = {0}: has an error with 'properties'
 propertyNames = Property name {0} is not valid for validation: {1}
+readOnly = {0}: is a readonly field, it cannot be changed
+required = {0}.{1}: is missing but it is required
+type = {0}: {1} found, {2} expected
 unevaluatedProperties = There are unevaluated properties at following paths {0}
+unionType = {0}: {1} found, but {2} is required
+uniqueItems = {0}: the items in the array must be unique
+uuid = {0}: {1} is an invalid {2}
diff --git a/src/main/resources/jsv-messages_de.properties b/src/main/resources/jsv-messages_de.properties
index ecaaed7..fd4cdef 100644
--- a/src/main/resources/jsv-messages_de.properties
+++ b/src/main/resources/jsv-messages_de.properties
@@ -1,40 +1,42 @@
+$ref = {0}: Ein Fehler mit 'refs' ist aufgetreten
 additionalProperties = {0}.{1} ist nicht im Schema definiert und das Schema verbietet 'additionalProperties'
 allOf = {0} muss gültig für alle Schemata {1} sein
 anyOf = {0} muss gültig für mindestens ein Schema {1} sein
+const = {0} muss den konstanten Wert {1} annehmen
+contains = {0} beinhaltet kein Element, das diese Validierung besteht: {1}
 crossEdits = {0}: Ein Fehler mit 'cross edits' ist aufgetreten
+dateTime = {0}: {1} ist ein ungültiges {2}
 dependencies = {0} hat einen Fehler mit Abhängigkeiten {1}
 dependentRequired = {0} fehlt eine Eigenschaft, welche 'dependentRequired' {1} ist
 dependentSchemas = {0}: Ein Fehler mit 'dependentSchemas' {1} ist aufgetreten
 edits = {0}: Ein Fehler mit 'edits' ist aufgetreten
 enum = {0}: Ein Wert in der Aufzählung {1} fehlt
+exclusiveMaximum = {0} muss größer sein als {1}
+exclusiveMinimum = {0} muss kleiner sein als {1}
+false = Das boolesche Schema 'false' ist ungültig
 format = {0} muss dem Format {1} entsprechen {2}
+id = {0}: {1} ist ein ungültiges Segment für die URI {2}
 items = {0}[{1}]: Kein Validator an diesem Index gefunden
-maximum = {0} darf den Wert {1} nicht überschreiten
 maxItems = {0} darf höchstens {1} Element(e) beinhalten
 maxLength = {0} darf höchstens {1} Zeichen lang sein
 maxProperties = {0} darf höchstens {1} Eigenschaft(en) haben
-minimum = {0} muss mindestens den Wert {1} haben
+maximum = {0} darf den Wert {1} nicht überschreiten
 minItems = {0} muss mindestens {1} Element(e) beinhalten
 minLength = {0} muss mindestens {1} Zeichen lang sein
 minProperties = {0} muss mindestens {1} Eigenschaft(en) haben
+minimum = {0} muss mindestens den Wert {1} haben
 multipleOf = {0} muss ein Vielfaches von {1} sein
+not = {0} darf nicht gültig sein für das Schema {1}
 notAllowed = {0}.{1} ist nicht erlaubt und darf folglich nicht auftreten
-not = {0} soll nicht gültig sein für das Schema {1}
-oneOf = {0} darf nur für ein einziges Schema {1} gültig sein
-patternProperties = {0} stimmt nicht überein mit dem Format definiert in 'pattern properties'
+oneOf = {0} darf nur für ein einziges Schema gültig sein, aber mehr als ein Schema ist gültig: {1}
 pattern = {0} stimmt nicht mit dem regulären Ausdruck {1} überein
+patternProperties = {0} stimmt nicht überein mit dem Format definiert in 'pattern properties'
 properties = {0}: Ein Fehler mit 'properties' ist aufgetreten
-readOnly = {0} ist ein schreibgeschütztes Feld und kann nicht verändert werden
-$ref = {0}: Ein Fehler mit 'refs' ist aufgetreten
-required = {0}.{1} ist ein Pflichtfeld aber fehlt
-type = {0}: {1} wurde gefunden aber {2} erwartet
-unionType = {0}: {1} wurde gefunden aber {2} wird verlangt
-uniqueItems = {0}: Die Element(e) des Arrays müssen einmalig sein
-uuid = {0}: {1} ist ein ungültiges {2}
-id = {0}: {1} ist ein ungültiges Segment für die URI {2}
-exclusiveMaximum = {0} muss größer sein als {1}
-exclusiveMinimum = {0} muss kleiner sein als {1}
-false = Das boolsche Schema 'false' ist ungültig
-const = {0} muss den konstanten Wert {1} annehmen
-contains = {0} beinhaltet kein Element, das diese Validierung besteht: {1}
 propertyNames = Eigenschaftsname {0} ist ungültig für die Validierung: {1}
+readOnly = {0} ist ein schreibgeschütztes Feld und kann nicht verändert werden
+required = {0}.{1} ist ein Pflichtfeld aber fehlt
+type = {0}: {1} wurde gefunden, aber {2} erwartet
+unevaluatedProperties = Eigenschaften in folgenden Pfaden wurden nicht evaluiert: {0}
+unionType = {0}: {1} wurde gefunden aber {2} wird verlangt
+uniqueItems = {0}: Die Element(e) des Arrays dürfen nur einmal auftreten
+uuid = {0}: {1} ist ein ungültiges {2}
diff --git a/src/main/resources/jsv-messages_zh_CN.properties b/src/main/resources/jsv-messages_zh_CN.properties
index 7b5e636..0ee21b2 100644
--- a/src/main/resources/jsv-messages_zh_CN.properties
+++ b/src/main/resources/jsv-messages_zh_CN.properties
@@ -1,40 +1,45 @@
+$ref = {0}\uFF1A'refs' \u6709\u9519\u8BEF
 additionalProperties = {0}.{1}\uFF1A\u672A\u5728\u67B6\u6784\u4E2D\u5B9A\u4E49\u4E14\u67B6\u6784\u4E0D\u5141\u8BB8\u9644\u52A0\u5C5E\u6027
 allOf = {0}\uFF1A\u5E94\u8BE5\u5BF9\u6240\u6709\u6A21\u5F0F {1} \u90FD\u6709\u6548
 anyOf = {0}\uFF1A\u5E94\u8BE5\u5BF9\u4EFB\u4F55\u67B6\u6784 {1} \u90FD\u6709\u6548
+const = {0}\uFF1A\u5FC5\u987B\u662F\u5E38\u91CF\u503C {1}
+contains = {0}\uFF1A\u4E0D\u5305\u542B\u901A\u8FC7\u8FD9\u4E9B\u9A8C\u8BC1\u7684\u5143\u7D20\uFF1A{1}
 crossEdits = {0}\uFF1A\u201C\u4EA4\u53C9\u7F16\u8F91\u201D\u6709\u9519\u8BEF
+# "dateTime" translation to be reviewed by a native speaker
+dateTime = {0}\uFF1A{1} \u662F\u65E0\u6548\u7684 {2}
 dependencies = {0}\uFF1A\u4F9D\u8D56\u9879 {1} \u6709\u9519\u8BEF
 dependentRequired = {0}\u7f3a\u5c11\u4f9d\u8d56\u9879\u6240\u9700\u7684\u5c5e\u6027 {1}
 dependentSchemas = {0}\u4f9d\u8d56\u6a21\u5f0f {1} \u6709\u9519\u8BEF
 edits = {0}\uFF1A\u201C\u7F16\u8F91\u201D\u6709\u9519\u8BEF
 enum = {0}\uFF1A\u679A\u4E3E {1} \u4E2D\u6CA1\u6709\u503C
-format = {0}\uFF1A\u4E0E {1} \u6A21\u5F0F {2} \u4E0D\u5339\u914D
-items = {0}[{1}]\uFF1A\u5728\u6B64\u7D22\u5F15\u4E2D\u627E\u4E0D\u5230\u9A8C\u8BC1\u5668
-maximum = {0}\uFF1A\u6700\u5927\u503C\u5FC5\u987B\u4E3A {1}
-maxItems = {0}\uFF1A\u6570\u7EC4\u4E2D\u6700\u591A\u5FC5\u987B\u6709 {1} \u4E2A\u9879\u76EE
-maxLength = {0}\uFF1A\u53EF\u80FD\u53EA\u6709 {1} \u4E2A\u5B57\u7B26\u957F
-maxProperties = {0}\uFF1A\u6700\u591A\u53EA\u80FD\u6709 {1} \u4E2A\u5C5E\u6027
-minimum = {0}\uFF1A\u6700\u5C0F\u503C\u5FC5\u987B\u4E3A {1}
-minItems = {0}\uFF1A\u6570\u7EC4\u4E2D\u5FC5\u987B\u81F3\u5C11\u6709 {1} \u4E2A\u9879\u76EE
-minLength = {0}\uFF1A\u5FC5\u987B\u81F3\u5C11\u4E3A {1} \u4E2A\u5B57\u7B26\u957F
-minProperties = {0}\uFF1A\u5E94\u8BE5\u81F3\u5C11\u6709 {1} \u4E2A\u5C5E\u6027
-multipleOf = {0}\uFF1A\u5FC5\u987B\u662F {1} \u7684\u500D\u6570
-notAllowed = {0}.{1}\uFF1A\u4E0D\u88AB\u5141\u8BB8\u4F46\u5728\u6570\u636E\u4E2D
-not = {0}\uFF1A\u4E0D\u5E94\u5BF9\u67B6\u6784 {1} \u6709\u6548
-oneOf = {0}\uFF1A\u5E94\u8BE5\u5BF9\u4E00\u79CD\u4E14\u4EC5\u4E00\u79CD\u6A21\u5F0F\u6709\u6548 {1}
-patternProperties = {0}\uFF1A\u201C\u6A21\u5F0F\u5C5E\u6027\u201D\u6709\u4E00\u4E9B\u9519\u8BEF
-pattern = {0}\uFF1A\u4E0E\u6B63\u5219\u8868\u8FBE\u5F0F\u6A21\u5F0F {1} \u4E0D\u5339\u914D
-properties = {0}\uFF1A\u201C\u5C5E\u6027\u201D\u6709\u9519\u8BEF
-readOnly = {0}\uFF1A\u662F\u53EA\u8BFB\u5B57\u6BB5\uFF0C\u4E0D\u80FD\u66F4\u6539
-$ref = {0}\uFF1A'refs' \u6709\u9519\u8BEF
-required = {0}.{1}\uFF1A\u7F3A\u5C11\u4F46\u5B83\u662F\u5FC5\u9700\u7684
-type = {0}\uFF1A\u627E\u5230 {1}\uFF0C\u9884\u671F\u4E3A {2}
-unionType = {0}\uFF1A\u627E\u5230 {1}\uFF0C\u4F46\u9700\u8981 {2}
-uniqueItems = {0}\uFF1A\u6570\u7EC4\u4E2D\u7684\u9879\u76EE\u5FC5\u987B\u662F\u552F\u4E00\u7684
-uuid = {0}\uFF1A{1} \u662F\u65E0\u6548\u7684 {2}
-id = {0}\uFF1A{1} \u662F URI {2} \u7684\u65E0\u6548\u6BB5
 exclusiveMaximum = {0}\uFF1A\u5FC5\u987B\u5177\u6709 {1} \u7684\u72EC\u5360\u6700\u5927\u503C
 exclusiveMinimum = {0}\uFF1A\u5FC5\u987B\u5177\u6709 {1} \u7684\u552F\u4E00\u6700\u5C0F\u503C
 false = \u5E03\u5C14\u67B6\u6784 false \u65E0\u6548
-const = {0}\uFF1A\u5FC5\u987B\u662F\u5E38\u91CF\u503C {1}
-contains = {0}\uFF1A\u4E0D\u5305\u542B\u901A\u8FC7\u8FD9\u4E9B\u9A8C\u8BC1\u7684\u5143\u7D20\uFF1A{1}
+format = {0}\uFF1A\u4E0E {1} \u6A21\u5F0F {2} \u4E0D\u5339\u914D
+id = {0}\uFF1A{1} \u662F URI {2} \u7684\u65E0\u6548\u6BB5
+items = {0}[{1}]\uFF1A\u5728\u6B64\u7D22\u5F15\u4E2D\u627E\u4E0D\u5230\u9A8C\u8BC1\u5668
+maxItems = {0}\uFF1A\u6570\u7EC4\u4E2D\u6700\u591A\u5FC5\u987B\u6709 {1} \u4E2A\u9879\u76EE
+maxLength = {0}\uFF1A\u53EF\u80FD\u53EA\u6709 {1} \u4E2A\u5B57\u7B26\u957F
+maxProperties = {0}\uFF1A\u6700\u591A\u53EA\u80FD\u6709 {1} \u4E2A\u5C5E\u6027
+maximum = {0}\uFF1A\u6700\u5927\u503C\u5FC5\u987B\u4E3A {1}
+minItems = {0}\uFF1A\u6570\u7EC4\u4E2D\u5FC5\u987B\u81F3\u5C11\u6709 {1} \u4E2A\u9879\u76EE
+minLength = {0}\uFF1A\u5FC5\u987B\u81F3\u5C11\u4E3A {1} \u4E2A\u5B57\u7B26\u957F
+minProperties = {0}\uFF1A\u5E94\u8BE5\u81F3\u5C11\u6709 {1} \u4E2A\u5C5E\u6027
+minimum = {0}\uFF1A\u6700\u5C0F\u503C\u5FC5\u987B\u4E3A {1}
+multipleOf = {0}\uFF1A\u5FC5\u987B\u662F {1} \u7684\u500D\u6570
+not = {0}\uFF1A\u4E0D\u5E94\u5BF9\u67B6\u6784 {1} \u6709\u6548
+notAllowed = {0}.{1}\uFF1A\u4E0D\u88AB\u5141\u8BB8\u4F46\u5728\u6570\u636E\u4E2D
+# needs to be re-worked by a native speaker
+#oneOf = {0}\uFF1A\u5E94\u8BE5\u5BF9\u4E00\u79CD\u4E14\u4EC5\u4E00\u79CD\u6A21\u5F0F\u6709\u6548 {1}
+pattern = {0}\uFF1A\u4E0E\u6B63\u5219\u8868\u8FBE\u5F0F\u6A21\u5F0F {1} \u4E0D\u5339\u914D
+patternProperties = {0}\uFF1A\u201C\u6A21\u5F0F\u5C5E\u6027\u201D\u6709\u4E00\u4E9B\u9519\u8BEF
+properties = {0}\uFF1A\u201C\u5C5E\u6027\u201D\u6709\u9519\u8BEF
 propertyNames = \u5C5E\u6027\u540D\u79F0 {0} \u5BF9\u9A8C\u8BC1\u65E0\u6548\uFF1A{1}
+readOnly = {0}\uFF1A\u662F\u53EA\u8BFB\u5B57\u6BB5\uFF0C\u4E0D\u80FD\u66F4\u6539
+required = {0}.{1}\uFF1A\u7F3A\u5C11\u4F46\u5B83\u662F\u5FC5\u9700\u7684
+type = {0}\uFF1A\u627E\u5230 {1}\uFF0C\u9884\u671F\u4E3A {2}
+# "unevaluatedProperties" translation to be added by a native speaker
+# unevaluatedProperties = 
+unionType = {0}\uFF1A\u627E\u5230 {1}\uFF0C\u4F46\u9700\u8981 {2}
+uniqueItems = {0}\uFF1A\u6570\u7EC4\u4E2D\u7684\u9879\u76EE\u5FC5\u987B\u662F\u552F\u4E00\u7684
+uuid = {0}\uFF1A{1} \u662F\u65E0\u6548\u7684 {2}