Allow external use of AddError
diff --git a/.gitignore b/.gitignore
index c1e0636..4c60981 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 *.sw[nop]
+*.iml
diff --git a/errors.go b/errors.go
index 58cb01f..072ce25 100644
--- a/errors.go
+++ b/errors.go
@@ -254,11 +254,15 @@
 	err.SetDetails(details)
 	details["field"] = err.Field()
 
+	if err.DescriptionFormat() == "" && d != "" {
+		err.SetDescriptionFormat(d)
+	}
+
 	if _, exists := details["context"]; !exists && context != nil {
 		details["context"] = context.String()
 	}
 
-	err.SetDescription(formatErrorDescription(d, details))
+	err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
 }
 
 // formatErrorDescription takes a string in the default text/template
diff --git a/jsonContext.go b/jsonContext.go
index fcc8d9d..1c58656 100644
--- a/jsonContext.go
+++ b/jsonContext.go
@@ -32,7 +32,7 @@
 	tail *jsonContext
 }
 
-func newJsonContext(head string, tail *jsonContext) *jsonContext {
+func NewJsonContext(head string, tail *jsonContext) *jsonContext {
 	return &jsonContext{head, tail}
 }
 
diff --git a/result.go b/result.go
index 6ad56ae..d21e53f 100644
--- a/result.go
+++ b/result.go
@@ -44,6 +44,8 @@
 		Context() *jsonContext
 		SetDescription(string)
 		Description() string
+		SetDescriptionFormat(string)
+		DescriptionFormat() string
 		SetValue(interface{})
 		Value() interface{}
 		SetDetails(ErrorDetails)
@@ -55,11 +57,12 @@
 	// ResultErrorFields implements the ResultError interface, so custom errors
 	// can be defined by just embedding this type
 	ResultErrorFields struct {
-		errorType   string       // A string with the type of error (i.e. invalid_type)
-		context     *jsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
-		description string       // A human readable error message
-		value       interface{}  // Value given by the JSON file that is the source of the error
-		details     ErrorDetails
+		errorType         string       // A string with the type of error (i.e. invalid_type)
+		context           *jsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
+		description       string       // A human readable error message
+		descriptionFormat string       // A format for human readable error message
+		value             interface{}  // Value given by the JSON file that is the source of the error
+		details           ErrorDetails
 	}
 
 	Result struct {
@@ -106,6 +109,14 @@
 	return v.description
 }
 
+func (v *ResultErrorFields) SetDescriptionFormat(descriptionFormat string) {
+	v.descriptionFormat = descriptionFormat
+}
+
+func (v *ResultErrorFields) DescriptionFormat() string {
+	return v.descriptionFormat
+}
+
 func (v *ResultErrorFields) SetValue(value interface{}) {
 	v.value = value
 }
@@ -155,7 +166,7 @@
 	return v.errors
 }
 
-func (v *Result) addError(err ResultError, context *jsonContext, value interface{}, details ErrorDetails) {
+func (v *Result) addInternalError(err ResultError, context *jsonContext, value interface{}, details ErrorDetails) {
 	newError(err, context, value, Locale, details)
 	v.errors = append(v.errors, err)
 	v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function
diff --git a/validation.go b/validation.go
index 664da21..3a1ae26 100644
--- a/validation.go
+++ b/validation.go
@@ -64,7 +64,7 @@
 	// begin validation
 
 	result := &Result{}
-	context := newJsonContext(STRING_CONTEXT_ROOT, nil)
+	context := NewJsonContext(STRING_CONTEXT_ROOT, nil)
 	v.rootSchema.validateRecursive(v.rootSchema, root, result, context)
 
 	return result, nil
@@ -94,7 +94,7 @@
 	// Check for null value
 	if currentNode == nil {
 		if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_NULL) {
-			result.addError(
+			result.addInternalError(
 				new(InvalidTypeError),
 				context,
 				currentNode,
@@ -126,7 +126,7 @@
 					givenType = TYPE_NUMBER
 				}
 
-				result.addError(
+				result.addInternalError(
 					new(InvalidTypeError),
 					context,
 					currentNode,
@@ -155,7 +155,7 @@
 			case reflect.Slice:
 
 				if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_ARRAY) {
-					result.addError(
+					result.addInternalError(
 						new(InvalidTypeError),
 						context,
 						currentNode,
@@ -178,7 +178,7 @@
 
 			case reflect.Map:
 				if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_OBJECT) {
-					result.addError(
+					result.addInternalError(
 						new(InvalidTypeError),
 						context,
 						currentNode,
@@ -203,7 +203,7 @@
 				for _, pSchema := range currentSubSchema.propertiesChildren {
 					nextNode, ok := castCurrentNode[pSchema.property]
 					if ok {
-						subContext := newJsonContext(pSchema.property, context)
+						subContext := NewJsonContext(pSchema.property, context)
 						v.validateRecursive(pSchema, nextNode, result, subContext)
 					}
 				}
@@ -213,7 +213,7 @@
 			case reflect.Bool:
 
 				if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_BOOLEAN) {
-					result.addError(
+					result.addInternalError(
 						new(InvalidTypeError),
 						context,
 						currentNode,
@@ -235,7 +235,7 @@
 			case reflect.String:
 
 				if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_STRING) {
-					result.addError(
+					result.addInternalError(
 						new(InvalidTypeError),
 						context,
 						currentNode,
@@ -288,7 +288,7 @@
 		}
 		if !validatedAnyOf {
 
-			result.addError(new(NumberAnyOfError), context, currentNode, ErrorDetails{})
+			result.addInternalError(new(NumberAnyOfError), context, currentNode, ErrorDetails{})
 
 			if bestValidationResult != nil {
 				// add error messages of closest matching subSchema as
@@ -314,7 +314,7 @@
 
 		if nbValidated != 1 {
 
-			result.addError(new(NumberOneOfError), context, currentNode, ErrorDetails{})
+			result.addInternalError(new(NumberOneOfError), context, currentNode, ErrorDetails{})
 
 			if nbValidated == 0 {
 				// add error messages of closest matching subSchema as
@@ -337,14 +337,14 @@
 		}
 
 		if nbValidated != len(currentSubSchema.allOf) {
-			result.addError(new(NumberAllOfError), context, currentNode, ErrorDetails{})
+			result.addInternalError(new(NumberAllOfError), context, currentNode, ErrorDetails{})
 		}
 	}
 
 	if currentSubSchema.not != nil {
 		validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context)
 		if validationResult.Valid() {
-			result.addError(new(NumberNotError), context, currentNode, ErrorDetails{})
+			result.addInternalError(new(NumberNotError), context, currentNode, ErrorDetails{})
 		}
 	}
 
@@ -357,7 +357,7 @@
 					case []string:
 						for _, dependOnKey := range dependency {
 							if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved {
-								result.addError(
+								result.addInternalError(
 									new(MissingDependencyError),
 									context,
 									currentNode,
@@ -380,14 +380,14 @@
 		if currentSubSchema._then != nil && validationResultIf.Valid() {
 			validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context)
 			if !validationResultThen.Valid() {
-				result.addError(new(ConditionThenError), context, currentNode, ErrorDetails{})
+				result.addInternalError(new(ConditionThenError), context, currentNode, ErrorDetails{})
 				result.mergeErrors(validationResultThen)
 			}
 		}
 		if currentSubSchema._else != nil && !validationResultIf.Valid() {
 			validationResultElse := currentSubSchema._else.subValidateWithContext(currentNode, context)
 			if !validationResultElse.Valid() {
-				result.addError(new(ConditionElseError), context, currentNode, ErrorDetails{})
+				result.addInternalError(new(ConditionElseError), context, currentNode, ErrorDetails{})
 				result.mergeErrors(validationResultElse)
 			}
 		}
@@ -407,10 +407,10 @@
 	if len(currentSubSchema.enum) > 0 {
 		has, err := currentSubSchema.ContainsEnum(value)
 		if err != nil {
-			result.addError(new(InternalError), context, value, ErrorDetails{"error": err})
+			result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err})
 		}
 		if !has {
-			result.addError(
+			result.addInternalError(
 				new(EnumError),
 				context,
 				value,
@@ -436,7 +436,7 @@
 	// TODO explain
 	if currentSubSchema.itemsChildrenIsSingleSchema {
 		for i := range value {
-			subContext := newJsonContext(strconv.Itoa(i), context)
+			subContext := NewJsonContext(strconv.Itoa(i), context)
 			validationResult := currentSubSchema.itemsChildren[0].subValidateWithContext(value[i], subContext)
 			result.mergeErrors(validationResult)
 		}
@@ -447,7 +447,7 @@
 
 			// while we have both schemas and values, check them against each other
 			for i := 0; i != nbItems && i != nbValues; i++ {
-				subContext := newJsonContext(strconv.Itoa(i), context)
+				subContext := NewJsonContext(strconv.Itoa(i), context)
 				validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext)
 				result.mergeErrors(validationResult)
 			}
@@ -459,12 +459,12 @@
 				switch currentSubSchema.additionalItems.(type) {
 				case bool:
 					if !currentSubSchema.additionalItems.(bool) {
-						result.addError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{})
+						result.addInternalError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{})
 					}
 				case *subSchema:
 					additionalItemSchema := currentSubSchema.additionalItems.(*subSchema)
 					for i := nbItems; i != nbValues; i++ {
-						subContext := newJsonContext(strconv.Itoa(i), context)
+						subContext := NewJsonContext(strconv.Itoa(i), context)
 						validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext)
 						result.mergeErrors(validationResult)
 					}
@@ -476,7 +476,7 @@
 	// minItems & maxItems
 	if currentSubSchema.minItems != nil {
 		if nbValues < int(*currentSubSchema.minItems) {
-			result.addError(
+			result.addInternalError(
 				new(ArrayMinItemsError),
 				context,
 				value,
@@ -486,7 +486,7 @@
 	}
 	if currentSubSchema.maxItems != nil {
 		if nbValues > int(*currentSubSchema.maxItems) {
-			result.addError(
+			result.addInternalError(
 				new(ArrayMaxItemsError),
 				context,
 				value,
@@ -501,10 +501,10 @@
 		for _, v := range value {
 			vString, err := marshalToJsonString(v)
 			if err != nil {
-				result.addError(new(InternalError), context, value, ErrorDetails{"err": err})
+				result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err})
 			}
 			if isStringInSlice(stringifiedItems, *vString) {
-				result.addError(
+				result.addInternalError(
 					new(ItemsMustBeUniqueError),
 					context,
 					value,
@@ -528,7 +528,7 @@
 	// minProperties & maxProperties:
 	if currentSubSchema.minProperties != nil {
 		if len(value) < int(*currentSubSchema.minProperties) {
-			result.addError(
+			result.addInternalError(
 				new(ArrayMinPropertiesError),
 				context,
 				value,
@@ -538,7 +538,7 @@
 	}
 	if currentSubSchema.maxProperties != nil {
 		if len(value) > int(*currentSubSchema.maxProperties) {
-			result.addError(
+			result.addInternalError(
 				new(ArrayMaxPropertiesError),
 				context,
 				value,
@@ -553,7 +553,7 @@
 		if ok {
 			result.incrementScore()
 		} else {
-			result.addError(
+			result.addInternalError(
 				new(RequiredError),
 				context,
 				value,
@@ -584,7 +584,7 @@
 					if found {
 
 						if pp_has && !pp_match {
-							result.addError(
+							result.addInternalError(
 								new(AdditionalPropertyNotAllowedError),
 								context,
 								value[pk],
@@ -595,7 +595,7 @@
 					} else {
 
 						if !pp_has || !pp_match {
-							result.addError(
+							result.addInternalError(
 								new(AdditionalPropertyNotAllowedError),
 								context,
 								value[pk],
@@ -647,7 +647,7 @@
 
 			if pp_has && !pp_match {
 
-				result.addError(
+				result.addInternalError(
 					new(InvalidPropertyPatternError),
 					context,
 					value[pk],
@@ -678,7 +678,7 @@
 	for pk, pv := range currentSubSchema.patternProperties {
 		if matches, _ := regexp.MatchString(pk, key); matches {
 			has = true
-			subContext := newJsonContext(key, context)
+			subContext := NewJsonContext(key, context)
 			validationResult := pv.subValidateWithContext(value, subContext)
 			result.mergeErrors(validationResult)
 			if validationResult.Valid() {
@@ -718,7 +718,7 @@
 	// minLength & maxLength:
 	if currentSubSchema.minLength != nil {
 		if utf8.RuneCount([]byte(stringValue)) < int(*currentSubSchema.minLength) {
-			result.addError(
+			result.addInternalError(
 				new(StringLengthGTEError),
 				context,
 				value,
@@ -728,7 +728,7 @@
 	}
 	if currentSubSchema.maxLength != nil {
 		if utf8.RuneCount([]byte(stringValue)) > int(*currentSubSchema.maxLength) {
-			result.addError(
+			result.addInternalError(
 				new(StringLengthLTEError),
 				context,
 				value,
@@ -740,7 +740,7 @@
 	// pattern:
 	if currentSubSchema.pattern != nil {
 		if !currentSubSchema.pattern.MatchString(stringValue) {
-			result.addError(
+			result.addInternalError(
 				new(DoesNotMatchPatternError),
 				context,
 				value,
@@ -753,7 +753,7 @@
 	// format
 	if currentSubSchema.format != "" {
 		if !FormatCheckers.IsFormat(currentSubSchema.format, stringValue) {
-			result.addError(
+			result.addInternalError(
 				new(DoesNotMatchFormatError),
 				context,
 				value,
@@ -784,7 +784,7 @@
 	if currentSubSchema.multipleOf != nil {
 
 		if q := new(big.Float).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() {
-			result.addError(
+			result.addInternalError(
 				new(MultipleOfError),
 				context,
 				resultErrorFormatJsonNumber(number),
@@ -797,7 +797,7 @@
 	if currentSubSchema.maximum != nil {
 		if currentSubSchema.exclusiveMaximum {
 			if float64Value.Cmp(currentSubSchema.maximum) >= 0 {
-				result.addError(
+				result.addInternalError(
 					new(NumberLTError),
 					context,
 					resultErrorFormatJsonNumber(number),
@@ -808,7 +808,7 @@
 			}
 		} else {
 			if float64Value.Cmp(currentSubSchema.maximum) == 1 {
-				result.addError(
+				result.addInternalError(
 					new(NumberLTEError),
 					context,
 					resultErrorFormatJsonNumber(number),
@@ -825,7 +825,7 @@
 		if currentSubSchema.exclusiveMinimum {
 			if float64Value.Cmp(currentSubSchema.minimum) <= 0 {
 				// if float64Value <= *currentSubSchema.minimum {
-				result.addError(
+				result.addInternalError(
 					new(NumberGTError),
 					context,
 					resultErrorFormatJsonNumber(number),
@@ -836,7 +836,7 @@
 			}
 		} else {
 			if float64Value.Cmp(currentSubSchema.minimum) == -1 {
-				result.addError(
+				result.addInternalError(
 					new(NumberGTEError),
 					context,
 					resultErrorFormatJsonNumber(number),
@@ -851,7 +851,7 @@
 	// format
 	if currentSubSchema.format != "" {
 		if !FormatCheckers.IsFormat(currentSubSchema.format, float64Value) {
-			result.addError(
+			result.addInternalError(
 				new(DoesNotMatchFormatError),
 				context,
 				value,