translator_test.go 16 KB

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