translator_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. package ut
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/go-playground/locales"
  6. "github.com/go-playground/locales/en"
  7. "github.com/go-playground/locales/en_CA"
  8. "github.com/go-playground/locales/nl"
  9. )
  10. // NOTES:
  11. // - Run "go test" to run tests
  12. // - Run "gocov test | gocov report" to report on test converage by file
  13. // - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
  14. //
  15. // or
  16. //
  17. // -- may be a good idea to change to output path to somewherelike /tmp
  18. // go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
  19. //
  20. func TestBasicTranslation(t *testing.T) {
  21. e := en.New()
  22. uni := New(e, e)
  23. en := uni.GetTranslator("en") // or fallback if fails to find 'en'
  24. translations := []struct {
  25. key interface{}
  26. trans string
  27. expected error
  28. expectedError bool
  29. override bool
  30. }{
  31. {
  32. key: "test_trans",
  33. trans: "Welcome {0}",
  34. expected: nil,
  35. },
  36. {
  37. key: "test_trans2",
  38. trans: "{0} to the {1}.",
  39. expected: nil,
  40. },
  41. {
  42. key: "test_trans3",
  43. trans: "Welcome {0} to the {1}",
  44. expected: nil,
  45. },
  46. {
  47. key: "test_trans4",
  48. trans: "{0}{1}",
  49. expected: nil,
  50. },
  51. {
  52. key: "test_trans",
  53. trans: "{0}{1}",
  54. expected: &ErrConflictingTranslation{key: "bad_trans", text: "{0}{1}"},
  55. expectedError: true,
  56. },
  57. {
  58. key: "test_trans",
  59. trans: "Welcome {0} to the {1}.",
  60. expected: nil,
  61. override: true,
  62. },
  63. }
  64. for _, tt := range translations {
  65. err := en.Add(tt.key, tt.trans, tt.override)
  66. if err != tt.expected {
  67. if !tt.expectedError && err.Error() != tt.expected.Error() {
  68. t.Errorf("Expected '%s' Got '%s'", tt.expected, err)
  69. }
  70. }
  71. }
  72. tests := []struct {
  73. key interface{}
  74. params []string
  75. expected string
  76. expectedError bool
  77. }{
  78. {
  79. key: "test_trans",
  80. params: []string{"Joeybloggs", "The Test"},
  81. expected: "Welcome Joeybloggs to the The Test.",
  82. },
  83. {
  84. key: "test_trans2",
  85. params: []string{"Joeybloggs", "The Test"},
  86. expected: "Joeybloggs to the The Test.",
  87. },
  88. {
  89. key: "test_trans3",
  90. params: []string{"Joeybloggs", "The Test"},
  91. expected: "Welcome Joeybloggs to the The Test",
  92. },
  93. {
  94. key: "test_trans4",
  95. params: []string{"Joeybloggs", "The Test"},
  96. expected: "JoeybloggsThe Test",
  97. },
  98. // bad translation
  99. {
  100. key: "non-existant-key",
  101. params: []string{"Joeybloggs", "The Test"},
  102. expected: "",
  103. expectedError: true,
  104. },
  105. }
  106. for _, tt := range tests {
  107. s, err := en.T(tt.key, tt.params...)
  108. if s != tt.expected {
  109. if !tt.expectedError && err != ErrUnknowTranslation {
  110. t.Errorf("Expected '%s' Got '%s'", tt.expected, s)
  111. }
  112. }
  113. }
  114. }
  115. func TestCardinalTranslation(t *testing.T) {
  116. e := en.New()
  117. uni := New(e, e)
  118. en := uni.GetTranslator("en")
  119. translations := []struct {
  120. key interface{}
  121. trans string
  122. rule locales.PluralRule
  123. expected error
  124. expectedError bool
  125. override bool
  126. }{
  127. // bad translation
  128. {
  129. key: "cardinal_test",
  130. trans: "You have a day left.",
  131. rule: locales.PluralRuleOne,
  132. expected: &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'", paramZero)},
  133. expectedError: true,
  134. },
  135. {
  136. key: "cardinal_test",
  137. trans: "You have {0} day",
  138. rule: locales.PluralRuleOne,
  139. expected: nil,
  140. },
  141. {
  142. key: "cardinal_test",
  143. trans: "You have {0} days left.",
  144. rule: locales.PluralRuleOther,
  145. expected: nil,
  146. },
  147. {
  148. key: "cardinal_test",
  149. trans: "You have {0} days left.",
  150. rule: locales.PluralRuleOther,
  151. expected: &ErrConflictingTranslation{key: "cardinal_test", rule: locales.PluralRuleOther, text: "You have {0} days left."},
  152. expectedError: true,
  153. },
  154. {
  155. key: "cardinal_test",
  156. trans: "You have {0} day left.",
  157. rule: locales.PluralRuleOne,
  158. expected: nil,
  159. override: true,
  160. },
  161. }
  162. for _, tt := range translations {
  163. err := en.AddCardinal(tt.key, tt.trans, tt.rule, tt.override)
  164. if err != tt.expected {
  165. if !tt.expectedError || err.Error() != tt.expected.Error() {
  166. t.Errorf("Expected '<nil>' Got '%s'", err)
  167. }
  168. }
  169. }
  170. tests := []struct {
  171. key interface{}
  172. num float64
  173. digits uint64
  174. param string
  175. expected string
  176. expectedError bool
  177. }{
  178. {
  179. key: "cardinal_test",
  180. num: 1,
  181. digits: 0,
  182. param: string(en.FmtNumber(1, 0)),
  183. expected: "You have 1 day left.",
  184. },
  185. // bad translation key
  186. {
  187. key: "non-existant",
  188. num: 1,
  189. digits: 0,
  190. param: string(en.FmtNumber(1, 0)),
  191. expected: "",
  192. expectedError: true,
  193. },
  194. }
  195. for _, tt := range tests {
  196. s, err := en.C(tt.key, tt.num, tt.digits, tt.param)
  197. if err != nil {
  198. if !tt.expectedError && err != ErrUnknowTranslation {
  199. t.Errorf("Expected '<nil>' Got '%s'", err)
  200. }
  201. }
  202. if s != tt.expected {
  203. t.Errorf("Expected '%s' Got '%s'", tt.expected, s)
  204. }
  205. }
  206. }
  207. func TestOrdinalTranslation(t *testing.T) {
  208. e := en.New()
  209. uni := New(e, e)
  210. en := uni.GetTranslator("en")
  211. translations := []struct {
  212. key interface{}
  213. trans string
  214. rule locales.PluralRule
  215. expected error
  216. expectedError bool
  217. override bool
  218. }{
  219. // bad translation
  220. {
  221. key: "day",
  222. trans: "st",
  223. rule: locales.PluralRuleOne,
  224. expected: &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'", paramZero)},
  225. expectedError: true,
  226. },
  227. {
  228. key: "day",
  229. trans: "{0}sfefewt",
  230. rule: locales.PluralRuleOne,
  231. expected: nil,
  232. },
  233. {
  234. key: "day",
  235. trans: "{0}nd",
  236. rule: locales.PluralRuleTwo,
  237. expected: nil,
  238. },
  239. {
  240. key: "day",
  241. trans: "{0}rd",
  242. rule: locales.PluralRuleFew,
  243. expected: nil,
  244. },
  245. {
  246. key: "day",
  247. trans: "{0}th",
  248. rule: locales.PluralRuleOther,
  249. expected: nil,
  250. },
  251. // bad translation
  252. {
  253. key: "day",
  254. trans: "{0}th",
  255. rule: locales.PluralRuleOther,
  256. expected: &ErrConflictingTranslation{key: "day", rule: locales.PluralRuleOther, text: "{0}th"},
  257. expectedError: true,
  258. },
  259. {
  260. key: "day",
  261. trans: "{0}st",
  262. rule: locales.PluralRuleOne,
  263. expected: nil,
  264. override: true,
  265. },
  266. }
  267. for _, tt := range translations {
  268. err := en.AddOrdinal(tt.key, tt.trans, tt.rule, tt.override)
  269. if err != tt.expected {
  270. if !tt.expectedError || err.Error() != tt.expected.Error() {
  271. t.Errorf("Expected '<nil>' Got '%s'", err)
  272. }
  273. }
  274. }
  275. tests := []struct {
  276. key interface{}
  277. num float64
  278. digits uint64
  279. param string
  280. expected string
  281. expectedError bool
  282. }{
  283. {
  284. key: "day",
  285. num: 1,
  286. digits: 0,
  287. param: string(en.FmtNumber(1, 0)),
  288. expected: "1st",
  289. },
  290. {
  291. key: "day",
  292. num: 2,
  293. digits: 0,
  294. param: string(en.FmtNumber(2, 0)),
  295. expected: "2nd",
  296. },
  297. {
  298. key: "day",
  299. num: 3,
  300. digits: 0,
  301. param: string(en.FmtNumber(3, 0)),
  302. expected: "3rd",
  303. },
  304. {
  305. key: "day",
  306. num: 4,
  307. digits: 0,
  308. param: string(en.FmtNumber(4, 0)),
  309. expected: "4th",
  310. },
  311. {
  312. key: "day",
  313. num: 10258.43,
  314. digits: 0,
  315. param: string(en.FmtNumber(10258.43, 0)),
  316. expected: "10,258th",
  317. },
  318. // bad translation
  319. {
  320. key: "d-day",
  321. num: 10258.43,
  322. digits: 0,
  323. param: string(en.FmtNumber(10258.43, 0)),
  324. expected: "",
  325. expectedError: true,
  326. },
  327. }
  328. for _, tt := range tests {
  329. s, err := en.O(tt.key, tt.num, tt.digits, tt.param)
  330. if err != nil {
  331. if !tt.expectedError && err != ErrUnknowTranslation {
  332. t.Errorf("Expected '<nil>' Got '%s'", err)
  333. }
  334. }
  335. if s != tt.expected {
  336. t.Errorf("Expected '%s' Got '%s'", tt.expected, s)
  337. }
  338. }
  339. }
  340. func TestRangeTranslation(t *testing.T) {
  341. n := nl.New()
  342. uni := New(n, n)
  343. // dutch
  344. nl := uni.GetTranslator("nl")
  345. translations := []struct {
  346. key interface{}
  347. trans string
  348. rule locales.PluralRule
  349. expected error
  350. expectedError bool
  351. override bool
  352. }{
  353. // bad translation
  354. {
  355. key: "day",
  356. trans: "er -{1} dag vertrokken",
  357. rule: locales.PluralRuleOne,
  358. expected: &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation?", paramZero)},
  359. expectedError: true,
  360. },
  361. // bad translation
  362. {
  363. key: "day",
  364. trans: "er {0}- dag vertrokken",
  365. rule: locales.PluralRuleOne,
  366. expected: &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters", paramOne)},
  367. expectedError: true,
  368. },
  369. {
  370. key: "day",
  371. trans: "er {0}-{1} dag",
  372. rule: locales.PluralRuleOne,
  373. expected: nil,
  374. },
  375. {
  376. key: "day",
  377. trans: "er zijn {0}-{1} dagen over",
  378. rule: locales.PluralRuleOther,
  379. expected: nil,
  380. },
  381. // bad translation
  382. {
  383. key: "day",
  384. trans: "er zijn {0}-{1} dagen over",
  385. rule: locales.PluralRuleOther,
  386. expected: &ErrConflictingTranslation{key: "day", rule: locales.PluralRuleOther, text: "er zijn {0}-{1} dagen over"},
  387. expectedError: true,
  388. },
  389. {
  390. key: "day",
  391. trans: "er {0}-{1} dag vertrokken",
  392. rule: locales.PluralRuleOne,
  393. expected: nil,
  394. override: true,
  395. },
  396. }
  397. for _, tt := range translations {
  398. err := nl.AddRange(tt.key, tt.trans, tt.rule, tt.override)
  399. if err != tt.expected {
  400. if !tt.expectedError || err.Error() != tt.expected.Error() {
  401. t.Errorf("Expected '%#v' Got '%s'", tt.expected, err)
  402. }
  403. }
  404. }
  405. tests := []struct {
  406. key interface{}
  407. num1 float64
  408. digits1 uint64
  409. num2 float64
  410. digits2 uint64
  411. param1 string
  412. param2 string
  413. expected string
  414. expectedError bool
  415. }{
  416. {
  417. key: "day",
  418. num1: 1,
  419. digits1: 0,
  420. num2: 2,
  421. digits2: 0,
  422. param1: string(nl.FmtNumber(1, 0)),
  423. param2: string(nl.FmtNumber(2, 0)),
  424. expected: "er zijn 1-2 dagen over",
  425. },
  426. {
  427. key: "day",
  428. num1: 0,
  429. digits1: 0,
  430. num2: 1,
  431. digits2: 0,
  432. param1: string(nl.FmtNumber(0, 0)),
  433. param2: string(nl.FmtNumber(1, 0)),
  434. expected: "er 0-1 dag vertrokken",
  435. },
  436. {
  437. key: "day",
  438. num1: 0,
  439. digits1: 0,
  440. num2: 2,
  441. digits2: 0,
  442. param1: string(nl.FmtNumber(0, 0)),
  443. param2: string(nl.FmtNumber(2, 0)),
  444. expected: "er zijn 0-2 dagen over",
  445. },
  446. // bad translations from here
  447. {
  448. key: "d-day",
  449. num1: 0,
  450. digits1: 0,
  451. num2: 2,
  452. digits2: 0,
  453. param1: string(nl.FmtNumber(0, 0)),
  454. param2: string(nl.FmtNumber(2, 0)),
  455. expected: "",
  456. expectedError: true,
  457. },
  458. }
  459. for _, tt := range tests {
  460. s, err := nl.R(tt.key, tt.num1, tt.digits1, tt.num2, tt.digits2, tt.param1, tt.param2)
  461. if err != nil {
  462. if !tt.expectedError && err != ErrUnknowTranslation {
  463. t.Errorf("Expected '<nil>' Got '%s'", err)
  464. }
  465. }
  466. if s != tt.expected {
  467. t.Errorf("Expected '%s' Got '%s'", tt.expected, s)
  468. }
  469. }
  470. }
  471. func TestFallbackTranslator(t *testing.T) {
  472. e := en.New()
  473. uni := New(e, e)
  474. en := uni.GetTranslator("en")
  475. if en.Locale() != "en" {
  476. t.Errorf("Expected '%s' Got '%s'", "en", en.Locale())
  477. }
  478. fallback := uni.GetTranslator("nl")
  479. if fallback.Locale() != "en" {
  480. t.Errorf("Expected '%s' Got '%s'", "en", fallback.Locale())
  481. }
  482. en = uni.FindTranslator("nl", "en")
  483. if en.Locale() != "en" {
  484. t.Errorf("Expected '%s' Got '%s'", "en", en.Locale())
  485. }
  486. fallback = uni.FindTranslator("nl")
  487. if fallback.Locale() != "en" {
  488. t.Errorf("Expected '%s' Got '%s'", "en", fallback.Locale())
  489. }
  490. }
  491. func TestAddTranslator(t *testing.T) {
  492. e := en.New()
  493. n := nl.New()
  494. uni := New(e, n)
  495. tests := []struct {
  496. trans locales.Translator
  497. expected error
  498. expectedError bool
  499. override bool
  500. }{
  501. {
  502. trans: en_CA.New(),
  503. expected: nil,
  504. override: false,
  505. },
  506. {
  507. trans: n,
  508. expected: &ErrExistingTranslator{locale: n.Locale()},
  509. expectedError: true,
  510. override: false,
  511. },
  512. {
  513. trans: e,
  514. expected: &ErrExistingTranslator{locale: e.Locale()},
  515. expectedError: true,
  516. override: false,
  517. },
  518. {
  519. trans: e,
  520. expected: nil,
  521. override: true,
  522. },
  523. }
  524. for _, tt := range tests {
  525. err := uni.AddTranslator(tt.trans, tt.override)
  526. if err != tt.expected {
  527. if !tt.expectedError || err.Error() != tt.expected.Error() {
  528. t.Errorf("Expected '%s' Got '%s'", tt.expected, err)
  529. }
  530. }
  531. }
  532. }
  533. func TestVerifyTranslations(t *testing.T) {
  534. n := nl.New()
  535. // dutch
  536. uni := New(n, n)
  537. loc := uni.GetTranslator("nl")
  538. if loc.Locale() != "nl" {
  539. t.Errorf("Expected '%s' Got '%s'", "nl", loc.Locale())
  540. }
  541. // cardinal checks
  542. err := loc.AddCardinal("day", "je {0} dag hebben verlaten", locales.PluralRuleOne, false)
  543. if err != nil {
  544. t.Fatalf("Expected '<nil>' Got '%s'", err)
  545. }
  546. // fail cardinal rules
  547. expected := &ErrMissingPluralTranslation{translationType: "plural", rule: locales.PluralRuleOther, key: "day"}
  548. err = loc.VerifyTranslations()
  549. if err == nil || err.Error() != expected.Error() {
  550. t.Errorf("Expected '%s' Got '%s'", expected, err)
  551. }
  552. // success cardinal
  553. err = loc.AddCardinal("day", "je {0} dagen hebben verlaten", locales.PluralRuleOther, false)
  554. if err != nil {
  555. t.Fatalf("Expected '<nil>' Got '%s'", err)
  556. }
  557. err = loc.VerifyTranslations()
  558. if err != nil {
  559. t.Fatalf("Expected '<nil>' Got '%s'", err)
  560. }
  561. // range checks
  562. err = loc.AddRange("day", "je {0}-{1} dagen hebben verlaten", locales.PluralRuleOther, false)
  563. if err != nil {
  564. t.Fatalf("Expected '<nil>' Got '%s'", err)
  565. }
  566. // fail range rules
  567. expected = &ErrMissingPluralTranslation{translationType: "range", rule: locales.PluralRuleOne, key: "day"}
  568. err = loc.VerifyTranslations()
  569. if err == nil || err.Error() != expected.Error() {
  570. t.Errorf("Expected '%s' Got '%s'", expected, err)
  571. }
  572. // success range
  573. err = loc.AddRange("day", "je {0}-{1} dag hebben verlaten", locales.PluralRuleOne, false)
  574. if err != nil {
  575. t.Fatalf("Expected '<nil>' Got '%s'", err)
  576. }
  577. err = loc.VerifyTranslations()
  578. if err != nil {
  579. t.Fatalf("Expected '<nil>' Got '%s'", err)
  580. }
  581. // ok so 'nl' aka dutch, ony has one plural rule for ordinals, so going to switch to english from here which has 4
  582. err = uni.AddTranslator(en.New(), false)
  583. if err != nil {
  584. t.Fatalf("Expected '<nil>' Got '%s'", err)
  585. }
  586. loc = uni.GetTranslator("en")
  587. if loc.Locale() != "en" {
  588. t.Errorf("Expected '%s' Got '%s'", "en", loc.Locale())
  589. }
  590. // ordinal checks
  591. err = loc.AddOrdinal("day", "{0}st", locales.PluralRuleOne, false)
  592. if err != nil {
  593. t.Fatalf("Expected '<nil>' Got '%s'", err)
  594. }
  595. err = loc.AddOrdinal("day", "{0}rd", locales.PluralRuleFew, false)
  596. if err != nil {
  597. t.Fatalf("Expected '<nil>' Got '%s'", err)
  598. }
  599. err = loc.AddOrdinal("day", "{0}th", locales.PluralRuleOther, false)
  600. if err != nil {
  601. t.Fatalf("Expected '<nil>' Got '%s'", err)
  602. }
  603. // fail ordinal rules
  604. expected = &ErrMissingPluralTranslation{translationType: "ordinal", rule: locales.PluralRuleTwo, key: "day"}
  605. err = loc.VerifyTranslations()
  606. if err == nil || err.Error() != expected.Error() {
  607. t.Errorf("Expected '%s' Got '%s'", expected, err)
  608. }
  609. // success ordinal
  610. err = loc.AddOrdinal("day", "{0}nd", locales.PluralRuleTwo, false)
  611. if err != nil {
  612. t.Fatalf("Expected '<nil>' Got '%s'", err)
  613. }
  614. err = loc.VerifyTranslations()
  615. if err != nil {
  616. t.Fatalf("Expected '<nil>' Got '%s'", err)
  617. }
  618. }