Jelajahi Sumber

finish tests + sure up error types.

Dean Karn 9 tahun lalu
induk
melakukan
30ed40a283
4 mengubah file dengan 270 tambahan dan 28 penghapusan
  1. 85 0
      errors.go
  2. 14 18
      translator.go
  3. 169 8
      translator_test.go
  4. 2 2
      universal-translator.go

+ 85 - 0
errors.go

@@ -0,0 +1,85 @@
+package ut
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/go-playground/locales"
+)
+
+var (
+	// ErrUnknowTranslation indicates the translation could not be found
+	ErrUnknowTranslation = errors.New("Unknown Translation")
+)
+
+var _ error = new(ErrConflictingTranslation)
+var _ error = new(ErrRangeTranslation)
+var _ error = new(ErrOrdinalTranslation)
+var _ error = new(ErrCardinalTranslation)
+var _ error = new(ErrLocaleNotFound)
+var _ error = new(ErrMissingPluralTranslation)
+
+// ErrConflictingTranslation is the error representing a conflicting translation
+type ErrConflictingTranslation struct {
+	key  interface{}
+	rule locales.PluralRule
+	text string
+}
+
+// Error returns ErrConflictingTranslation's internal error text
+func (e *ErrConflictingTranslation) Error() string {
+	return fmt.Sprintf("warning: conflicting key '%#v' rule '%d' with text '%s', value being ignored", e.key, e.rule, e.text)
+}
+
+// ErrRangeTranslation is the error representing a range translation error
+type ErrRangeTranslation struct {
+	text string
+}
+
+// Error returns ErrRangeTranslation's internal error text
+func (e *ErrRangeTranslation) Error() string {
+	return e.text
+}
+
+// ErrOrdinalTranslation is the error representing an ordinal translation error
+type ErrOrdinalTranslation struct {
+	text string
+}
+
+// Error returns ErrOrdinalTranslation's internal error text
+func (e *ErrOrdinalTranslation) Error() string {
+	return e.text
+}
+
+// ErrCardinalTranslation is the error representing a cardinal translation error
+type ErrCardinalTranslation struct {
+	text string
+}
+
+// Error returns ErrCardinalTranslation's internal error text
+func (e *ErrCardinalTranslation) Error() string {
+	return e.text
+}
+
+// ErrLocaleNotFound is the error signifying a locale which could not be found
+type ErrLocaleNotFound struct {
+	text string
+}
+
+// Error returns ErrLocaleNotFound's internal error text
+func (e *ErrLocaleNotFound) Error() string {
+	return e.text
+}
+
+// ErrMissingPluralTranslation is the error signifying a missing translation given
+// the locales plural rules.
+type ErrMissingPluralTranslation struct {
+	key             interface{}
+	rule            locales.PluralRule
+	translationType string
+}
+
+// Error returns ErrMissingPluralTranslation's internal error text
+func (e *ErrMissingPluralTranslation) Error() string {
+	return fmt.Sprintf("error: missing %s plural rule '%s' for translation with key '%#v", e.translationType, e.rule, e.key)
+}

+ 14 - 18
translator.go

@@ -1,7 +1,6 @@
 package ut
 
 import (
-	"errors"
 	"fmt"
 	"strconv"
 	"strings"
@@ -15,11 +14,6 @@ const (
 	unknownTranslation = ""
 )
 
-var (
-	// ErrUnknowTranslation indicates the translation could not be found
-	ErrUnknowTranslation = errors.New("Unknown Translation")
-)
-
 // Translator is universal translators
 // translator instance which is a thin wrapper
 // around locales.Translator instance providing
@@ -66,6 +60,10 @@ type Translator interface {
 	//  creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and
 	//  'digit2' arguments and 'param1' and 'param2' passed in
 	R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error)
+
+	// VerifyTranslations checks to ensures that no plural rules have been
+	// missed within the translations.
+	VerifyTranslations() error
 }
 
 var _ Translator = new(translator)
@@ -135,7 +133,7 @@ func (t *translator) AddCardinal(key interface{}, text string, rule locales.Plur
 	if ok {
 		// verify not adding a conflicting record
 		if len(tarr) > 0 && tarr[rule] != nil {
-			return fmt.Errorf("warning: conflicting key '%#v' rule '%d' with text '%s', value being ignored", key, rule, text)
+			return &ErrConflictingTranslation{key: key, rule: rule, text: text}
 		}
 
 	} else {
@@ -154,7 +152,7 @@ func (t *translator) AddCardinal(key interface{}, text string, rule locales.Plur
 	idx := strings.Index(text, paramZero)
 	if idx == -1 {
 		tarr[rule] = nil
-		return fmt.Errorf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'", paramZero)
+		return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'", paramZero)}
 	}
 
 	trans.indexes[0] = idx
@@ -174,7 +172,7 @@ func (t *translator) AddOrdinal(key interface{}, text string, rule locales.Plura
 	if ok {
 		// verify not adding a conflicting record
 		if len(tarr) > 0 && tarr[rule] != nil {
-			return fmt.Errorf("warning: conflicting key '%#v' rule '%d' with text '%s', value being ignored", key, rule, text)
+			return &ErrConflictingTranslation{key: key, rule: rule, text: text}
 		}
 
 	} else {
@@ -192,7 +190,7 @@ func (t *translator) AddOrdinal(key interface{}, text string, rule locales.Plura
 	idx := strings.Index(text, paramZero)
 	if idx == -1 {
 		tarr[rule] = nil
-		return fmt.Errorf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'", paramZero)
+		return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'", paramZero)}
 	}
 
 	trans.indexes[0] = idx
@@ -210,7 +208,7 @@ func (t *translator) AddRange(key interface{}, text string, rule locales.PluralR
 	if ok {
 		// verify not adding a conflicting record
 		if len(tarr) > 0 && tarr[rule] != nil {
-			return fmt.Errorf("warning: conflicting key '%#v' rule '%s' with text '%s', value being ignored", key, rule, text)
+			return &ErrConflictingTranslation{key: key, rule: rule, text: text}
 		}
 
 	} else {
@@ -228,7 +226,7 @@ func (t *translator) AddRange(key interface{}, text string, rule locales.PluralR
 	idx := strings.Index(text, paramZero)
 	if idx == -1 {
 		tarr[rule] = nil
-		return fmt.Errorf("error: parameter '%s' not found, are you sure you're adding a Range Translation?", paramZero)
+		return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation?", paramZero)}
 	}
 
 	trans.indexes[0] = idx
@@ -237,7 +235,7 @@ func (t *translator) AddRange(key interface{}, text string, rule locales.PluralR
 	idx = strings.Index(text, paramOne)
 	if idx == -1 {
 		tarr[rule] = nil
-		return fmt.Errorf("error: parameter '%s' not found, a Range Translation requires two parameters", paramOne)
+		return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters", paramOne)}
 	}
 
 	trans.indexes[2] = idx
@@ -339,8 +337,6 @@ func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float
 	return string(b), nil
 }
 
-// TODO: Add VerifyTranslations function to check that all plurals rules are accounted for after all have been added.
-
 // VerifyTranslations checks to ensures that no plural rules have been
 // missed within the translations.
 func (t *translator) VerifyTranslations() error {
@@ -350,7 +346,7 @@ func (t *translator) VerifyTranslations() error {
 		for _, rule := range t.PluralsCardinal() {
 
 			if v[rule] == nil {
-				return fmt.Errorf("error: missing cardinal plural rule '%s' for translation with key '%#v", rule, k)
+				return &ErrMissingPluralTranslation{translationType: "plural", rule: rule, key: k}
 			}
 		}
 	}
@@ -360,7 +356,7 @@ func (t *translator) VerifyTranslations() error {
 		for _, rule := range t.PluralsOrdinal() {
 
 			if v[rule] == nil {
-				return fmt.Errorf("error: missing ordinal plural rule '%s' for translation with key '%#v", rule, k)
+				return &ErrMissingPluralTranslation{translationType: "ordinal", rule: rule, key: k}
 			}
 		}
 	}
@@ -370,7 +366,7 @@ func (t *translator) VerifyTranslations() error {
 		for _, rule := range t.PluralsRange() {
 
 			if v[rule] == nil {
-				return fmt.Errorf("error: missing range plural rule '%s' for translation with key '%#v", rule, k)
+				return &ErrMissingPluralTranslation{translationType: "range", rule: rule, key: k}
 			}
 		}
 	}

+ 169 - 8
translator_test.go

@@ -97,7 +97,7 @@ func TestCardinalTranslation(t *testing.T) {
 			key:           "cardinal_test",
 			trans:         "You have a day left.",
 			rule:          locales.PluralRuleOne,
-			expected:      fmt.Errorf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'", paramZero),
+			expected:      &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'", paramZero)},
 			expectedError: true,
 		},
 		{
@@ -116,7 +116,7 @@ func TestCardinalTranslation(t *testing.T) {
 			key:           "cardinal_test",
 			trans:         "You have {0} days left.",
 			rule:          locales.PluralRuleOther,
-			expected:      fmt.Errorf("warning: conflicting key '%#v' rule '%d' with text '%s', value being ignored", "cardinal_test", locales.PluralRuleOther, "You have {0} days left."),
+			expected:      &ErrConflictingTranslation{key: "cardinal_test", rule: locales.PluralRuleOther, text: "You have {0} days left."},
 			expectedError: true,
 		},
 	}
@@ -144,7 +144,7 @@ func TestCardinalTranslation(t *testing.T) {
 			num:      1,
 			digits:   0,
 			param:    string(en.FmtNumber(1, 0)),
-			expected: "You have " + string(en.FmtNumber(1, 0)) + " day left.",
+			expected: "You have 1 day left.",
 		},
 		// bad translation key
 		{
@@ -193,7 +193,7 @@ func TestOrdinalTranslation(t *testing.T) {
 			key:           "day",
 			trans:         "st",
 			rule:          locales.PluralRuleOne,
-			expected:      fmt.Errorf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'", paramZero),
+			expected:      &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'", paramZero)},
 			expectedError: true,
 		},
 		{
@@ -225,7 +225,7 @@ func TestOrdinalTranslation(t *testing.T) {
 			key:           "day",
 			trans:         "{0}th",
 			rule:          locales.PluralRuleOther,
-			expected:      fmt.Errorf("warning: conflicting key '%#v' rule '%d' with text '%s', value being ignored", "day", locales.PluralRuleOther, "{0}th"),
+			expected:      &ErrConflictingTranslation{key: "day", rule: locales.PluralRuleOther, text: "{0}th"},
 			expectedError: true,
 		},
 	}
@@ -331,7 +331,7 @@ func TestRangeTranslation(t *testing.T) {
 			key:           "day",
 			trans:         "er -{1} dag vertrokken",
 			rule:          locales.PluralRuleOne,
-			expected:      fmt.Errorf("error: parameter '%s' not found, are you sure you're adding a Range Translation?", paramZero),
+			expected:      &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation?", paramZero)},
 			expectedError: true,
 		},
 		// bad translation
@@ -339,7 +339,7 @@ func TestRangeTranslation(t *testing.T) {
 			key:           "day",
 			trans:         "er {0}- dag vertrokken",
 			rule:          locales.PluralRuleOne,
-			expected:      fmt.Errorf("error: parameter '%s' not found, a Range Translation requires two parameters", paramOne),
+			expected:      &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters", paramOne)},
 			expectedError: true,
 		},
 		{
@@ -359,7 +359,7 @@ func TestRangeTranslation(t *testing.T) {
 			key:           "day",
 			trans:         "er zijn {0}-{1} dagen over",
 			rule:          locales.PluralRuleOther,
-			expected:      fmt.Errorf("warning: conflicting key '%#v' rule '%s' with text '%s', value being ignored", "day", locales.PluralRuleOther, "er zijn {0}-{1} dagen over"),
+			expected:      &ErrConflictingTranslation{key: "day", rule: locales.PluralRuleOther, text: "er zijn {0}-{1} dagen over"},
 			expectedError: true,
 		},
 	}
@@ -443,3 +443,164 @@ func TestRangeTranslation(t *testing.T) {
 		}
 	}
 }
+
+func TestFallbackTranslator(t *testing.T) {
+
+	uni, err := New("en", "en")
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	en := uni.GetTranslator("en")
+	if en.Locale() != "en" {
+		t.Errorf("Expected '%s' Got '%s'", "en", en.Locale())
+	}
+
+	fallback := uni.GetTranslator("nl")
+	if fallback.Locale() != "en" {
+		t.Errorf("Expected '%s' Got '%s'", "en", fallback.Locale())
+	}
+
+	en = uni.FindTranslator("nl", "en")
+	if en.Locale() != "en" {
+		t.Errorf("Expected '%s' Got '%s'", "en", en.Locale())
+	}
+
+	fallback = uni.FindTranslator("nl")
+	if fallback.Locale() != "en" {
+		t.Errorf("Expected '%s' Got '%s'", "en", fallback.Locale())
+	}
+}
+
+func TestNew(t *testing.T) {
+
+	_, err := New("en", "en")
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	expected := &ErrLocaleNotFound{text: fmt.Sprintf("locale '%s' could not be found, perhaps it is not supported.", "definitly doesn't exist!")}
+
+	_, err = New("en", "definitly doesn't exist!")
+	if err == nil || err.Error() != expected.Error() {
+		t.Fatalf("Expected '%s' Got '%s'", expected, err)
+	}
+
+	expected = &ErrLocaleNotFound{text: fmt.Sprintf("fallback locale '%s' could not be found", "fallback definitly doesn't exist!")}
+
+	_, err = New("fallback definitly doesn't exist!", "en")
+	if err == nil || err.Error() != expected.Error() {
+		t.Fatalf("Expected '%s' Got '%s'", expected, err)
+	}
+}
+
+func TestVerifyTranslations(t *testing.T) {
+
+	// dutch
+	uni, err := New("nl", "nl")
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	loc := uni.GetTranslator("nl")
+	if loc.Locale() != "nl" {
+		t.Errorf("Expected '%s' Got '%s'", "nl", loc.Locale())
+	}
+
+	// cardinal checks
+
+	err = loc.AddCardinal("day", "je {0} dag hebben verlaten", locales.PluralRuleOne)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	// fail cardinal rules
+	expected := &ErrMissingPluralTranslation{translationType: "plural", rule: locales.PluralRuleOther, key: "day"}
+	err = loc.VerifyTranslations()
+	if err == nil || err.Error() != expected.Error() {
+		t.Errorf("Expected '%s' Got '%s'", expected, err)
+	}
+
+	// success cardinal
+	err = loc.AddCardinal("day", "je {0} dagen hebben verlaten", locales.PluralRuleOther)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	err = loc.VerifyTranslations()
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	// range checks
+	err = loc.AddRange("day", "je {0}-{1} dagen hebben verlaten", locales.PluralRuleOther)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	// fail range rules
+	expected = &ErrMissingPluralTranslation{translationType: "range", rule: locales.PluralRuleOne, key: "day"}
+	err = loc.VerifyTranslations()
+	if err == nil || err.Error() != expected.Error() {
+		t.Errorf("Expected '%s' Got '%s'", expected, err)
+	}
+
+	// success range
+	err = loc.AddRange("day", "je {0}-{1} dag hebben verlaten", locales.PluralRuleOne)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	err = loc.VerifyTranslations()
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	// ok so 'nl' aka dutch, ony has one plural rule for ordinals, so going to switch to english from here which has 4
+
+	uni, err = New("en", "en")
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	loc = uni.GetTranslator("en")
+	if loc.Locale() != "en" {
+		t.Errorf("Expected '%s' Got '%s'", "en", loc.Locale())
+	}
+
+	// ordinal checks
+
+	err = loc.AddOrdinal("day", "{0}st", locales.PluralRuleOne)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	err = loc.AddOrdinal("day", "{0}rd", locales.PluralRuleFew)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	err = loc.AddOrdinal("day", "{0}th", locales.PluralRuleOther)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	// fail ordinal rules
+	expected = &ErrMissingPluralTranslation{translationType: "ordinal", rule: locales.PluralRuleTwo, key: "day"}
+	err = loc.VerifyTranslations()
+	if err == nil || err.Error() != expected.Error() {
+		t.Errorf("Expected '%s' Got '%s'", expected, err)
+	}
+
+	// success ordinal
+
+	err = loc.AddOrdinal("day", "{0}nd", locales.PluralRuleTwo)
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+
+	err = loc.VerifyTranslations()
+	if err != nil {
+		t.Fatalf("Expected '<nil>' Got '%s'", err)
+	}
+}

+ 2 - 2
universal-translator.go

@@ -38,7 +38,7 @@ func New(fallback string, supportedLocales ...string) (*UniversalTranslator, err
 
 		tf, ok = lcMapping[lc]
 		if !ok {
-			return nil, fmt.Errorf("locale '%s' could not be found", loc)
+			return nil, &ErrLocaleNotFound{text: fmt.Sprintf("locale '%s' could not be found, perhaps it is not supported.", loc)}
 		}
 
 		if fallbackLower == lc {
@@ -51,7 +51,7 @@ func New(fallback string, supportedLocales ...string) (*UniversalTranslator, err
 	}
 
 	if t.fallback == nil {
-		return nil, fmt.Errorf("fallback locale '%s' could not be found", fallback)
+		return nil, &ErrLocaleNotFound{text: fmt.Sprintf("fallback locale '%s' could not be found", fallback)}
 	}
 
 	return t, nil