translator_test.go 14 KB

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