瀏覽代碼

finalizing function names and params

- updated to accept translator interface instead of string, this allows for easier overriding
- decreases compile time as not referencing ever locale is references anymore, but provided.
- remove Overwrite functions and add param to override or not.
- add AddTranslator function.
Dean Karn 9 年之前
父節點
當前提交
94bc451b3b
共有 6 個文件被更改,包括 190 次插入221 次删除
  1. 1 1
      README.md
  2. 29 9
      benchmarks_test.go
  3. 12 12
      errors.go
  4. 16 64
      translator.go
  5. 92 105
      translator_test.go
  6. 40 30
      universal-translator.go

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 ## universal-translator
 <img align="right" src="https://raw.githubusercontent.com/go-playground/universal-translator/master/logo.png">
-![Project status](https://img.shields.io/badge/version-0.10.0-green.svg)
+![Project status](https://img.shields.io/badge/version-0.11.0-green.svg)
 [![Build Status](https://semaphoreci.com/api/v1/joeybloggs/universal-translator/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/universal-translator)
 [![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator)
 [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator)

+ 29 - 9
benchmarks_test.go

@@ -1,16 +1,22 @@
 package ut
 
-import "testing"
+import (
+	"testing"
+
+	"github.com/go-playground/locales/en"
+)
 
 func BenchmarkBasicTranslation(b *testing.B) {
 
-	ut, _ := New("en", "en")
+	en := en.New()
+	ut := New(en, en)
 	loc := ut.FindTranslator("en")
 
 	translations := []struct {
 		key      interface{}
 		trans    string
 		expected error
+		override bool
 	}{
 		{
 			key:      "welcome",
@@ -30,16 +36,20 @@ func BenchmarkBasicTranslation(b *testing.B) {
 	}
 
 	for _, tt := range translations {
-		if err := loc.Add(tt.key, tt.trans); err != nil {
+		if err := loc.Add(tt.key, tt.trans, tt.override); err != nil {
 			b.Fatalf("adding translation '%s' failed with key '%s'", tt.trans, tt.key)
 		}
 	}
 
+	var err error
+
 	b.ResetTimer()
 
 	b.Run("", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			loc.T("welcome")
+			if _, err = loc.T("welcome"); err != nil {
+				b.Error(err)
+			}
 		}
 	})
 
@@ -48,14 +58,18 @@ func BenchmarkBasicTranslation(b *testing.B) {
 		b.RunParallel(func(pb *testing.PB) {
 
 			for pb.Next() {
-				loc.T("welcome")
+				if _, err = loc.T("welcome"); err != nil {
+					b.Error(err)
+				}
 			}
 		})
 	})
 
 	b.Run("With1Param", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			loc.T("welcome-user", "Joeybloggs")
+			if _, err = loc.T("welcome-user", "Joeybloggs"); err != nil {
+				b.Error(err)
+			}
 		}
 	})
 
@@ -64,14 +78,18 @@ func BenchmarkBasicTranslation(b *testing.B) {
 		b.RunParallel(func(pb *testing.PB) {
 
 			for pb.Next() {
-				loc.T("welcome-user", "Joeybloggs")
+				if _, err = loc.T("welcome-user", "Joeybloggs"); err != nil {
+					b.Error(err)
+				}
 			}
 		})
 	})
 
 	b.Run("With2Param", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			loc.T("welcome-user2", "Joeybloggs", "/dev/tty0")
+			if _, err = loc.T("welcome-user2", "Joeybloggs", "/dev/tty0"); err != nil {
+				b.Error(err)
+			}
 		}
 	})
 
@@ -80,7 +98,9 @@ func BenchmarkBasicTranslation(b *testing.B) {
 		b.RunParallel(func(pb *testing.PB) {
 
 			for pb.Next() {
-				loc.T("welcome-user2", "Joeybloggs", "/dev/tty0")
+				if _, err = loc.T("welcome-user2", "Joeybloggs", "/dev/tty0"); err != nil {
+					b.Error(err)
+				}
 			}
 		})
 	})

+ 12 - 12
errors.go

@@ -16,8 +16,18 @@ var _ error = new(ErrConflictingTranslation)
 var _ error = new(ErrRangeTranslation)
 var _ error = new(ErrOrdinalTranslation)
 var _ error = new(ErrCardinalTranslation)
-var _ error = new(ErrLocaleNotFound)
 var _ error = new(ErrMissingPluralTranslation)
+var _ error = new(ErrExistingTranslator)
+
+// ErrExistingTranslator is the error representing a conflicting translator
+type ErrExistingTranslator struct {
+	locale string
+}
+
+// Error returns ErrExistingTranslator's internal error text
+func (e *ErrExistingTranslator) Error() string {
+	return fmt.Sprintf("error: conflicting translator key '%s'", e.locale)
+}
 
 // ErrConflictingTranslation is the error representing a conflicting translation
 type ErrConflictingTranslation struct {
@@ -28,7 +38,7 @@ type ErrConflictingTranslation struct {
 
 // 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)
+	return fmt.Sprintf("error: 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
@@ -61,16 +71,6 @@ 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 {

+ 16 - 64
translator.go

@@ -24,20 +24,14 @@ type Translator interface {
 	// adds a normal translation for a particular language/locale
 	// {#} is the only replacement type accepted and are add infintium
 	// eg. one: '{0} day left' other: '{0} days left'
-	Add(key interface{}, text string) error
-
-	// is the same as Add only it allows existing translations to be overridden
-	Overwrite(key interface{}, text string) error
+	Add(key interface{}, text string, override bool) error
 
 	// adds a cardinal plural translation for a particular language/locale
 	// {0} is the only replacement type accepted and only one variable is accepted as
 	// multiple cannot be used for a plural rule determination, unless it is a range;
 	// see AddRange below.
 	// eg. in locale 'en' one: '{0} day left' other: '{0} days left'
-	AddCardinal(key interface{}, text string, rule locales.PluralRule) error
-
-	// is the same as AddCardinal only it allows existing translations to be overridden
-	OverwriteCardinal(key interface{}, text string, rule locales.PluralRule) error
+	AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error
 
 	// adds an ordinal plural translation for a particular language/locale
 	// {0} is the only replacement type accepted and only one variable is accepted as
@@ -45,21 +39,15 @@ type Translator interface {
 	// see AddRange below.
 	// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring'
 	// - 1st, 2nd, 3rd...
-	AddOrdinal(key interface{}, text string, rule locales.PluralRule) error
-
-	// is the same as AddOrdinal only it allows for existing translations to be overridden
-	OverwriteOrdinal(key interface{}, text string, rule locales.PluralRule) error
+	AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error
 
 	// adds a range plural translation for a particular language/locale
 	// {0} and {1} are the only replacement types accepted and only these are accepted.
 	// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
-	AddRange(key interface{}, text string, rule locales.PluralRule) error
-
-	// is the same as AddRange only allows an existing translation to be overridden
-	OverwriteRange(key interface{}, text string, rule locales.PluralRule) error
+	AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error
 
 	// creates the translation for the locale given the 'key' and params passed in
-	T(key interface{}, params ...string) string
+	T(key interface{}, params ...string) (string, error)
 
 	// creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments
 	//  and param passed in
@@ -107,18 +95,9 @@ func newTranslator(trans locales.Translator) Translator {
 // Add adds a normal translation for a particular language/locale
 // {#} is the only replacement type accepted and are add infintium
 // eg. one: '{0} day left' other: '{0} days left'
-func (t *translator) Add(key interface{}, text string) error {
-	return t.add(key, text, false)
-}
+func (t *translator) Add(key interface{}, text string, override bool) error {
 
-// Overwrite is the same as Add only it allows existing translations to be overridden
-func (t *translator) Overwrite(key interface{}, text string) error {
-	return t.add(key, text, true)
-}
-
-func (t *translator) add(key interface{}, text string, overwrite bool) error {
-
-	if _, ok := t.translations[key]; ok && !overwrite {
+	if _, ok := t.translations[key]; ok && !override {
 		return &ErrConflictingTranslation{key: key, text: text}
 	}
 
@@ -154,21 +133,12 @@ func (t *translator) add(key interface{}, text string, overwrite bool) error {
 // multiple cannot be used for a plural rule determination, unless it is a range;
 // see AddRange below.
 // eg. in locale 'en' one: '{0} day left' other: '{0} days left'
-func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule) error {
-	return t.addCardinal(key, text, rule, false)
-}
-
-// OverwriteCardinal is the same as AddCardinal only it allows existing translations to be overridden
-func (t *translator) OverwriteCardinal(key interface{}, text string, rule locales.PluralRule) error {
-	return t.addCardinal(key, text, rule, true)
-}
-
-func (t *translator) addCardinal(key interface{}, text string, rule locales.PluralRule, overwrite bool) error {
+func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
 
 	tarr, ok := t.cardinalTanslations[key]
 	if ok {
 		// verify not adding a conflicting record
-		if len(tarr) > 0 && tarr[rule] != nil && !overwrite {
+		if len(tarr) > 0 && tarr[rule] != nil && !override {
 			return &ErrConflictingTranslation{key: key, rule: rule, text: text}
 		}
 
@@ -201,21 +171,12 @@ func (t *translator) addCardinal(key interface{}, text string, rule locales.Plur
 // multiple cannot be used for a plural rule determination, unless it is a range;
 // see AddRange below.
 // eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - 1st, 2nd, 3rd...
-func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule) error {
-	return t.addOrdinal(key, text, rule, false)
-}
-
-// OverwriteOrdinal is the same as AddOrdinal only it allows for existing translations to be overridden
-func (t *translator) OverwriteOrdinal(key interface{}, text string, rule locales.PluralRule) error {
-	return t.addOrdinal(key, text, rule, true)
-}
-
-func (t *translator) addOrdinal(key interface{}, text string, rule locales.PluralRule, overwrite bool) error {
+func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
 
 	tarr, ok := t.ordinalTanslations[key]
 	if ok {
 		// verify not adding a conflicting record
-		if len(tarr) > 0 && tarr[rule] != nil && !overwrite {
+		if len(tarr) > 0 && tarr[rule] != nil && !override {
 			return &ErrConflictingTranslation{key: key, rule: rule, text: text}
 		}
 
@@ -246,21 +207,12 @@ func (t *translator) addOrdinal(key interface{}, text string, rule locales.Plura
 // AddRange adds a range plural translation for a particular language/locale
 // {0} and {1} are the only replacement types accepted and only these are accepted.
 // eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
-func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule) error {
-	return t.addRange(key, text, rule, false)
-}
-
-// OverwriteRange is the same as AddRange only allows an existing translation to be overridden
-func (t *translator) OverwriteRange(key interface{}, text string, rule locales.PluralRule) error {
-	return t.addRange(key, text, rule, true)
-}
-
-func (t *translator) addRange(key interface{}, text string, rule locales.PluralRule, overwrite bool) error {
+func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error {
 
 	tarr, ok := t.rangeTanslations[key]
 	if ok {
 		// verify not adding a conflicting record
-		if len(tarr) > 0 && tarr[rule] != nil && !overwrite {
+		if len(tarr) > 0 && tarr[rule] != nil && !override {
 			return &ErrConflictingTranslation{key: key, rule: rule, text: text}
 		}
 
@@ -298,11 +250,11 @@ func (t *translator) addRange(key interface{}, text string, rule locales.PluralR
 }
 
 // T creates the translation for the locale given the 'key' and params passed in
-func (t *translator) T(key interface{}, params ...string) string {
+func (t *translator) T(key interface{}, params ...string) (string, error) {
 
 	trans, ok := t.translations[key]
 	if !ok {
-		return unknownTranslation
+		return unknownTranslation, ErrUnknowTranslation
 	}
 
 	b := make([]byte, 0, 64)
@@ -320,7 +272,7 @@ func (t *translator) T(key interface{}, params ...string) string {
 
 	b = append(b, trans.text[start:]...)
 
-	return string(b)
+	return string(b), nil
 }
 
 // C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in

+ 92 - 105
translator_test.go

@@ -5,6 +5,9 @@ import (
 	"testing"
 
 	"github.com/go-playground/locales"
+	"github.com/go-playground/locales/en"
+	"github.com/go-playground/locales/en_CA"
+	"github.com/go-playground/locales/nl"
 )
 
 // NOTES:
@@ -20,11 +23,8 @@ import (
 
 func TestBasicTranslation(t *testing.T) {
 
-	uni, err := New("en", "en")
-	if err != nil {
-		t.Fatalf("Expected '<nil>' Got '%s'", err)
-	}
-
+	e := en.New()
+	uni := New(e, e)
 	en := uni.GetTranslator("en") // or fallback if fails to find 'en'
 
 	translations := []struct {
@@ -32,7 +32,7 @@ func TestBasicTranslation(t *testing.T) {
 		trans         string
 		expected      error
 		expectedError bool
-		overwrite     bool
+		override      bool
 	}{
 		{
 			key:      "test_trans",
@@ -61,23 +61,16 @@ func TestBasicTranslation(t *testing.T) {
 			expectedError: true,
 		},
 		{
-			key:       "test_trans",
-			trans:     "Welcome {0} to the {1}.",
-			expected:  nil,
-			overwrite: true,
+			key:      "test_trans",
+			trans:    "Welcome {0} to the {1}.",
+			expected: nil,
+			override: true,
 		},
 	}
 
 	for _, tt := range translations {
 
-		var err error
-
-		if tt.overwrite {
-			err = en.Overwrite(tt.key, tt.trans)
-		} else {
-			err = en.Add(tt.key, tt.trans)
-		}
-
+		err := en.Add(tt.key, tt.trans, tt.override)
 		if err != tt.expected {
 			if !tt.expectedError && err.Error() != tt.expected.Error() {
 				t.Errorf("Expected '%s' Got '%s'", tt.expected, err)
@@ -121,7 +114,7 @@ func TestBasicTranslation(t *testing.T) {
 	}
 
 	for _, tt := range tests {
-		s := en.T(tt.key, tt.params...)
+		s, err := en.T(tt.key, tt.params...)
 		if s != tt.expected {
 			if !tt.expectedError && err != ErrUnknowTranslation {
 				t.Errorf("Expected '%s' Got '%s'", tt.expected, s)
@@ -132,11 +125,8 @@ func TestBasicTranslation(t *testing.T) {
 
 func TestCardinalTranslation(t *testing.T) {
 
-	uni, err := New("en", "en")
-	if err != nil {
-		t.Fatalf("Expected '<nil>' Got '%s'", err)
-	}
-
+	e := en.New()
+	uni := New(e, e)
 	en := uni.GetTranslator("en")
 
 	translations := []struct {
@@ -145,7 +135,7 @@ func TestCardinalTranslation(t *testing.T) {
 		rule          locales.PluralRule
 		expected      error
 		expectedError bool
-		overwrite     bool
+		override      bool
 	}{
 		// bad translation
 		{
@@ -175,24 +165,17 @@ func TestCardinalTranslation(t *testing.T) {
 			expectedError: true,
 		},
 		{
-			key:       "cardinal_test",
-			trans:     "You have {0} day left.",
-			rule:      locales.PluralRuleOne,
-			expected:  nil,
-			overwrite: true,
+			key:      "cardinal_test",
+			trans:    "You have {0} day left.",
+			rule:     locales.PluralRuleOne,
+			expected: nil,
+			override: true,
 		},
 	}
 
 	for _, tt := range translations {
 
-		var err error
-
-		if tt.overwrite {
-			err = en.OverwriteCardinal(tt.key, tt.trans, tt.rule)
-		} else {
-			err = en.AddCardinal(tt.key, tt.trans, tt.rule)
-		}
-
+		err := en.AddCardinal(tt.key, tt.trans, tt.rule, tt.override)
 		if err != tt.expected {
 			if !tt.expectedError || err.Error() != tt.expected.Error() {
 				t.Errorf("Expected '<nil>' Got '%s'", err)
@@ -243,11 +226,8 @@ func TestCardinalTranslation(t *testing.T) {
 
 func TestOrdinalTranslation(t *testing.T) {
 
-	uni, err := New("en", "en")
-	if err != nil {
-		t.Fatalf("Expected '<nil>' Got '%s'", err)
-	}
-
+	e := en.New()
+	uni := New(e, e)
 	en := uni.GetTranslator("en")
 
 	translations := []struct {
@@ -256,7 +236,7 @@ func TestOrdinalTranslation(t *testing.T) {
 		rule          locales.PluralRule
 		expected      error
 		expectedError bool
-		overwrite     bool
+		override      bool
 	}{
 		// bad translation
 		{
@@ -299,24 +279,17 @@ func TestOrdinalTranslation(t *testing.T) {
 			expectedError: true,
 		},
 		{
-			key:       "day",
-			trans:     "{0}st",
-			rule:      locales.PluralRuleOne,
-			expected:  nil,
-			overwrite: true,
+			key:      "day",
+			trans:    "{0}st",
+			rule:     locales.PluralRuleOne,
+			expected: nil,
+			override: true,
 		},
 	}
 
 	for _, tt := range translations {
 
-		var err error
-
-		if tt.overwrite {
-			err = en.OverwriteOrdinal(tt.key, tt.trans, tt.rule)
-		} else {
-			err = en.AddOrdinal(tt.key, tt.trans, tt.rule)
-		}
-
+		err := en.AddOrdinal(tt.key, tt.trans, tt.rule, tt.override)
 		if err != tt.expected {
 			if !tt.expectedError || err.Error() != tt.expected.Error() {
 				t.Errorf("Expected '<nil>' Got '%s'", err)
@@ -395,10 +368,8 @@ func TestOrdinalTranslation(t *testing.T) {
 
 func TestRangeTranslation(t *testing.T) {
 
-	uni, err := New("nl", "nl")
-	if err != nil {
-		t.Fatalf("Expected '<nil>' Got '%s'", err)
-	}
+	n := nl.New()
+	uni := New(n, n)
 
 	// dutch
 	nl := uni.GetTranslator("nl")
@@ -409,7 +380,7 @@ func TestRangeTranslation(t *testing.T) {
 		rule          locales.PluralRule
 		expected      error
 		expectedError bool
-		overwrite     bool
+		override      bool
 	}{
 		// bad translation
 		{
@@ -448,22 +419,17 @@ func TestRangeTranslation(t *testing.T) {
 			expectedError: true,
 		},
 		{
-			key:       "day",
-			trans:     "er {0}-{1} dag vertrokken",
-			rule:      locales.PluralRuleOne,
-			expected:  nil,
-			overwrite: true,
+			key:      "day",
+			trans:    "er {0}-{1} dag vertrokken",
+			rule:     locales.PluralRuleOne,
+			expected: nil,
+			override: true,
 		},
 	}
 
 	for _, tt := range translations {
 
-		if tt.overwrite {
-			err = nl.OverwriteRange(tt.key, tt.trans, tt.rule)
-		} else {
-			err = nl.AddRange(tt.key, tt.trans, tt.rule)
-		}
-
+		err := nl.AddRange(tt.key, tt.trans, tt.rule, tt.override)
 		if err != tt.expected {
 			if !tt.expectedError || err.Error() != tt.expected.Error() {
 				t.Errorf("Expected '%#v' Got '%s'", tt.expected, err)
@@ -543,12 +509,10 @@ 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)
-	}
-
+	e := en.New()
+	uni := New(e, e)
 	en := uni.GetTranslator("en")
+
 	if en.Locale() != "en" {
 		t.Errorf("Expected '%s' Got '%s'", "en", en.Locale())
 	}
@@ -569,35 +533,58 @@ func TestFallbackTranslator(t *testing.T) {
 	}
 }
 
-func TestNew(t *testing.T) {
-
-	_, err := New("en", "en")
-	if err != nil {
-		t.Fatalf("Expected '<nil>' Got '%s'", err)
-	}
+func TestAddTranslator(t *testing.T) {
 
-	expected := &ErrLocaleNotFound{text: fmt.Sprintf("locale '%s' could not be found, perhaps it is not supported.", "definitly doesn't exist!")}
+	e := en.New()
+	n := nl.New()
+	uni := New(e, n)
 
-	_, err = New("en", "definitly doesn't exist!")
-	if err == nil || err.Error() != expected.Error() {
-		t.Fatalf("Expected '%s' Got '%s'", expected, err)
+	tests := []struct {
+		trans         locales.Translator
+		expected      error
+		expectedError bool
+		override      bool
+	}{
+		{
+			trans:    en_CA.New(),
+			expected: nil,
+			override: false,
+		},
+		{
+			trans:         n,
+			expected:      &ErrExistingTranslator{locale: n.Locale()},
+			expectedError: true,
+			override:      false,
+		},
+		{
+			trans:         e,
+			expected:      &ErrExistingTranslator{locale: e.Locale()},
+			expectedError: true,
+			override:      false,
+		},
+		{
+			trans:    e,
+			expected: nil,
+			override: true,
+		},
 	}
 
-	expected = &ErrLocaleNotFound{text: fmt.Sprintf("fallback locale '%s' could not be found", "fallback definitly doesn't exist!")}
+	for _, tt := range tests {
 
-	_, err = New("fallback definitly doesn't exist!", "en")
-	if err == nil || err.Error() != expected.Error() {
-		t.Fatalf("Expected '%s' Got '%s'", expected, err)
+		err := uni.AddTranslator(tt.trans, tt.override)
+		if err != tt.expected {
+			if !tt.expectedError || err.Error() != tt.expected.Error() {
+				t.Errorf("Expected '%s' Got '%s'", tt.expected, err)
+			}
+		}
 	}
 }
 
 func TestVerifyTranslations(t *testing.T) {
 
+	n := nl.New()
 	// dutch
-	uni, err := New("nl", "nl")
-	if err != nil {
-		t.Fatalf("Expected '<nil>' Got '%s'", err)
-	}
+	uni := New(n, n)
 
 	loc := uni.GetTranslator("nl")
 	if loc.Locale() != "nl" {
@@ -606,7 +593,7 @@ func TestVerifyTranslations(t *testing.T) {
 
 	// cardinal checks
 
-	err = loc.AddCardinal("day", "je {0} dag hebben verlaten", locales.PluralRuleOne)
+	err := loc.AddCardinal("day", "je {0} dag hebben verlaten", locales.PluralRuleOne, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
@@ -619,7 +606,7 @@ func TestVerifyTranslations(t *testing.T) {
 	}
 
 	// success cardinal
-	err = loc.AddCardinal("day", "je {0} dagen hebben verlaten", locales.PluralRuleOther)
+	err = loc.AddCardinal("day", "je {0} dagen hebben verlaten", locales.PluralRuleOther, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
@@ -630,7 +617,7 @@ func TestVerifyTranslations(t *testing.T) {
 	}
 
 	// range checks
-	err = loc.AddRange("day", "je {0}-{1} dagen hebben verlaten", locales.PluralRuleOther)
+	err = loc.AddRange("day", "je {0}-{1} dagen hebben verlaten", locales.PluralRuleOther, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
@@ -643,7 +630,7 @@ func TestVerifyTranslations(t *testing.T) {
 	}
 
 	// success range
-	err = loc.AddRange("day", "je {0}-{1} dag hebben verlaten", locales.PluralRuleOne)
+	err = loc.AddRange("day", "je {0}-{1} dag hebben verlaten", locales.PluralRuleOne, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
@@ -655,7 +642,7 @@ func TestVerifyTranslations(t *testing.T) {
 
 	// 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")
+	err = uni.AddTranslator(en.New(), false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
@@ -667,17 +654,17 @@ func TestVerifyTranslations(t *testing.T) {
 
 	// ordinal checks
 
-	err = loc.AddOrdinal("day", "{0}st", locales.PluralRuleOne)
+	err = loc.AddOrdinal("day", "{0}st", locales.PluralRuleOne, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
 
-	err = loc.AddOrdinal("day", "{0}rd", locales.PluralRuleFew)
+	err = loc.AddOrdinal("day", "{0}rd", locales.PluralRuleFew, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
 
-	err = loc.AddOrdinal("day", "{0}th", locales.PluralRuleOther)
+	err = loc.AddOrdinal("day", "{0}th", locales.PluralRuleOther, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}
@@ -691,7 +678,7 @@ func TestVerifyTranslations(t *testing.T) {
 
 	// success ordinal
 
-	err = loc.AddOrdinal("day", "{0}nd", locales.PluralRuleTwo)
+	err = loc.AddOrdinal("day", "{0}nd", locales.PluralRuleTwo, false)
 	if err != nil {
 		t.Fatalf("Expected '<nil>' Got '%s'", err)
 	}

+ 40 - 30
universal-translator.go

@@ -1,10 +1,9 @@
 package ut
 
 import (
-	"fmt"
 	"strings"
 
-	"github.com/go-playground/locales/locales-list"
+	"github.com/go-playground/locales"
 )
 
 // UniversalTranslator holds all locale & translation data
@@ -15,46 +14,27 @@ type UniversalTranslator struct {
 
 // New returns a new UniversalTranslator instance set with
 // the fallback locale and locales it should support
-func New(fallback string, supportedLocales ...string) (*UniversalTranslator, error) {
+func New(fallback locales.Translator, supportedLocales ...locales.Translator) *UniversalTranslator {
 
 	t := &UniversalTranslator{
 		translators: make(map[string]Translator),
 	}
 
-	lcMapping := make(localeslist.LocaleMap)
+	for _, v := range supportedLocales {
 
-	for loc, tf := range localeslist.Map() {
-		lcMapping[strings.ToLower(loc)] = tf
-	}
-
-	var ok bool
-	var lc string
-	var tf localeslist.LocaleFunc
-	fallbackLower := strings.ToLower(fallback)
-
-	for _, loc := range supportedLocales {
-
-		lc = strings.ToLower(loc)
+		trans := newTranslator(v)
+		t.translators[strings.ToLower(trans.Locale())] = newTranslator(trans)
 
-		tf, ok = lcMapping[lc]
-		if !ok {
-			return nil, &ErrLocaleNotFound{text: fmt.Sprintf("locale '%s' could not be found, perhaps it is not supported.", loc)}
+		if fallback.Locale() == v.Locale() {
+			t.fallback = trans
 		}
-
-		if fallbackLower == lc {
-			t.fallback = newTranslator(tf())
-			t.translators[lc] = t.fallback
-			continue
-		}
-
-		t.translators[lc] = newTranslator(tf())
 	}
 
-	if t.fallback == nil {
-		return nil, &ErrLocaleNotFound{text: fmt.Sprintf("fallback locale '%s' could not be found", fallback)}
+	if t.fallback == nil && fallback != nil {
+		t.fallback = newTranslator(fallback)
 	}
 
-	return t, nil
+	return t
 }
 
 // FindTranslator trys to find a Translator based on an array of locales
@@ -84,3 +64,33 @@ func (t *UniversalTranslator) GetTranslator(locale string) Translator {
 
 	return t.fallback
 }
+
+// AddTranslator adds the supplied translator, if it already exists the override param
+// will be checked and if false an error will be returned, otherwise the translator will be
+// overridden; if the fallback matches the supplied translator it will be overridden as well
+// NOTE: this is normally only used when translator is embedded within a library
+func (t *UniversalTranslator) AddTranslator(translator locales.Translator, override bool) error {
+
+	lc := strings.ToLower(translator.Locale())
+	_, ok := t.translators[lc]
+	if ok && !override {
+		return &ErrExistingTranslator{locale: translator.Locale()}
+	}
+
+	trans := newTranslator(translator)
+
+	if t.fallback.Locale() == translator.Locale() {
+
+		// because it's optional to have a fallback, I don't impose that limitation
+		// don't know why you wouldn't but...
+		if !override {
+			return &ErrExistingTranslator{locale: translator.Locale()}
+		}
+
+		t.fallback = trans
+	}
+
+	t.translators[lc] = trans
+
+	return nil
+}