printer_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package locale // import "miniflux.app/v2/internal/locale"
  4. import "testing"
  5. func TestPrintfWithMissingLanguage(t *testing.T) {
  6. defaultCatalog = catalog{}
  7. translation := NewPrinter("invalid").Printf("missing.key")
  8. if translation != "missing.key" {
  9. t.Errorf(`Wrong translation, got %q`, translation)
  10. }
  11. }
  12. func TestPrintfWithMissingKey(t *testing.T) {
  13. defaultCatalog = catalog{
  14. "en_US": translationDict{
  15. "k": "v",
  16. },
  17. }
  18. translation := NewPrinter("en_US").Printf("missing.key")
  19. if translation != "missing.key" {
  20. t.Errorf(`Wrong translation, got %q`, translation)
  21. }
  22. }
  23. func TestPrintfWithExistingKey(t *testing.T) {
  24. defaultCatalog = catalog{
  25. "en_US": translationDict{
  26. "auth.username": "Login",
  27. },
  28. }
  29. translation := NewPrinter("en_US").Printf("auth.username")
  30. if translation != "Login" {
  31. t.Errorf(`Wrong translation, got %q`, translation)
  32. }
  33. }
  34. func TestPrintfWithExistingKeyAndPlaceholder(t *testing.T) {
  35. defaultCatalog = catalog{
  36. "en_US": translationDict{
  37. "key": "Test: %s",
  38. },
  39. "fr_FR": translationDict{
  40. "key": "Test : %s",
  41. },
  42. }
  43. translation := NewPrinter("fr_FR").Printf("key", "ok")
  44. if translation != "Test : ok" {
  45. t.Errorf(`Wrong translation, got %q`, translation)
  46. }
  47. }
  48. func TestPrintfWithMissingKeyAndPlaceholder(t *testing.T) {
  49. defaultCatalog = catalog{
  50. "en_US": translationDict{
  51. "auth.username": "Login",
  52. },
  53. "fr_FR": translationDict{
  54. "auth.username": "Identifiant",
  55. },
  56. }
  57. translation := NewPrinter("fr_FR").Printf("Status: %s", "ok")
  58. if translation != "Status: ok" {
  59. t.Errorf(`Wrong translation, got %q`, translation)
  60. }
  61. }
  62. func TestPrintfWithInvalidValue(t *testing.T) {
  63. defaultCatalog = catalog{
  64. "en_US": translationDict{
  65. "auth.username": "Login",
  66. },
  67. "fr_FR": translationDict{
  68. "auth.username": true,
  69. },
  70. }
  71. translation := NewPrinter("fr_FR").Printf("auth.username")
  72. if translation != "auth.username" {
  73. t.Errorf(`Wrong translation, got %q`, translation)
  74. }
  75. }
  76. func TestPrintWithMissingLanguage(t *testing.T) {
  77. defaultCatalog = catalog{}
  78. translation := NewPrinter("invalid").Print("missing.key")
  79. if translation != "missing.key" {
  80. t.Errorf(`Wrong translation, got %q`, translation)
  81. }
  82. }
  83. func TestPrintWithMissingKey(t *testing.T) {
  84. defaultCatalog = catalog{
  85. "en_US": translationDict{
  86. "existing.key": "value",
  87. },
  88. }
  89. translation := NewPrinter("en_US").Print("missing.key")
  90. if translation != "missing.key" {
  91. t.Errorf(`Wrong translation, got %q`, translation)
  92. }
  93. }
  94. func TestPrintWithExistingKey(t *testing.T) {
  95. defaultCatalog = catalog{
  96. "en_US": translationDict{
  97. "auth.username": "Login",
  98. },
  99. }
  100. translation := NewPrinter("en_US").Print("auth.username")
  101. if translation != "Login" {
  102. t.Errorf(`Wrong translation, got %q`, translation)
  103. }
  104. }
  105. func TestPrintWithDifferentLanguages(t *testing.T) {
  106. defaultCatalog = catalog{
  107. "en_US": translationDict{
  108. "greeting": "Hello",
  109. },
  110. "fr_FR": translationDict{
  111. "greeting": "Bonjour",
  112. },
  113. "es_ES": translationDict{
  114. "greeting": "Hola",
  115. },
  116. }
  117. tests := []struct {
  118. language string
  119. expected string
  120. }{
  121. {"en_US", "Hello"},
  122. {"fr_FR", "Bonjour"},
  123. {"es_ES", "Hola"},
  124. }
  125. for _, test := range tests {
  126. translation := NewPrinter(test.language).Print("greeting")
  127. if translation != test.expected {
  128. t.Errorf(`Wrong translation for %s, got %q instead of %q`, test.language, translation, test.expected)
  129. }
  130. }
  131. }
  132. func TestPrintWithInvalidTranslationType(t *testing.T) {
  133. defaultCatalog = catalog{
  134. "en_US": translationDict{
  135. "valid.key": "valid string",
  136. "invalid.key": 12345, // not a string
  137. },
  138. }
  139. printer := NewPrinter("en_US")
  140. // Valid string should work
  141. translation := printer.Print("valid.key")
  142. if translation != "valid string" {
  143. t.Errorf(`Wrong translation for valid key, got %q`, translation)
  144. }
  145. // Invalid type should return the key itself
  146. translation = printer.Print("invalid.key")
  147. if translation != "invalid.key" {
  148. t.Errorf(`Wrong translation for invalid key, got %q`, translation)
  149. }
  150. }
  151. func TestPrintWithNilTranslation(t *testing.T) {
  152. defaultCatalog = catalog{
  153. "en_US": translationDict{
  154. "nil.key": nil,
  155. },
  156. }
  157. translation := NewPrinter("en_US").Print("nil.key")
  158. if translation != "nil.key" {
  159. t.Errorf(`Wrong translation for nil value, got %q`, translation)
  160. }
  161. }
  162. func TestPrintWithEmptyKey(t *testing.T) {
  163. defaultCatalog = catalog{
  164. "en_US": translationDict{
  165. "": "empty key translation",
  166. },
  167. }
  168. translation := NewPrinter("en_US").Print("")
  169. if translation != "empty key translation" {
  170. t.Errorf(`Wrong translation for empty key, got %q`, translation)
  171. }
  172. }
  173. func TestPrintWithEmptyTranslation(t *testing.T) {
  174. defaultCatalog = catalog{
  175. "en_US": translationDict{
  176. "empty.value": "",
  177. },
  178. }
  179. translation := NewPrinter("en_US").Print("empty.value")
  180. if translation != "" {
  181. t.Errorf(`Wrong translation for empty value, got %q`, translation)
  182. }
  183. }
  184. func TestPluralWithDefaultRule(t *testing.T) {
  185. defaultCatalog = catalog{
  186. "en_US": translationDict{
  187. "number_of_users": []string{"%d user (%s)", "%d users (%s)"},
  188. },
  189. "fr_FR": translationDict{
  190. "number_of_users": []string{"%d utilisateur (%s)", "%d utilisateurs (%s)"},
  191. },
  192. }
  193. printer := NewPrinter("fr_FR")
  194. translation := printer.Plural("number_of_users", 1, 1, "some text")
  195. expected := "1 utilisateur (some text)"
  196. if translation != expected {
  197. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  198. }
  199. translation = printer.Plural("number_of_users", 2, 2, "some text")
  200. expected = "2 utilisateurs (some text)"
  201. if translation != expected {
  202. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  203. }
  204. }
  205. func TestPluralWithRussianRule(t *testing.T) {
  206. defaultCatalog = catalog{
  207. "en_US": translationDict{
  208. "time_elapsed.years": []string{"%d year", "%d years"},
  209. },
  210. "ru_RU": translationDict{
  211. "time_elapsed.years": []string{"%d год назад", "%d года назад", "%d лет назад"},
  212. },
  213. }
  214. printer := NewPrinter("ru_RU")
  215. translation := printer.Plural("time_elapsed.years", 1, 1)
  216. expected := "1 год назад"
  217. if translation != expected {
  218. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  219. }
  220. translation = printer.Plural("time_elapsed.years", 2, 2)
  221. expected = "2 года назад"
  222. if translation != expected {
  223. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  224. }
  225. translation = printer.Plural("time_elapsed.years", 5, 5)
  226. expected = "5 лет назад"
  227. if translation != expected {
  228. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  229. }
  230. }
  231. func TestPluralWithMissingTranslation(t *testing.T) {
  232. defaultCatalog = catalog{
  233. "en_US": translationDict{
  234. "number_of_users": []string{"%d user (%s)", "%d users (%s)"},
  235. },
  236. "fr_FR": translationDict{},
  237. }
  238. translation := NewPrinter("fr_FR").Plural("number_of_users", 2)
  239. expected := "number_of_users"
  240. if translation != expected {
  241. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  242. }
  243. }
  244. func TestPluralWithInvalidValues(t *testing.T) {
  245. defaultCatalog = catalog{
  246. "en_US": translationDict{
  247. "number_of_users": []string{"%d user (%s)", "%d users (%s)"},
  248. },
  249. "fr_FR": translationDict{
  250. "number_of_users": "must be a slice",
  251. },
  252. }
  253. translation := NewPrinter("fr_FR").Plural("number_of_users", 2)
  254. expected := "number_of_users"
  255. if translation != expected {
  256. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  257. }
  258. }
  259. func TestPluralWithMissingLanguage(t *testing.T) {
  260. defaultCatalog = catalog{}
  261. translation := NewPrinter("invalid_language").Plural("test.key", 2)
  262. expected := "test.key"
  263. if translation != expected {
  264. t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
  265. }
  266. }
  267. func TestPluralWithAnySliceType(t *testing.T) {
  268. defaultCatalog = catalog{
  269. "en_US": translationDict{
  270. "test.key": []any{"%d item", "%d items"},
  271. },
  272. }
  273. printer := NewPrinter("en_US")
  274. translation := printer.Plural("test.key", 1, 1)
  275. expected := "1 item"
  276. if translation != expected {
  277. t.Errorf(`Wrong translation for singular, got %q instead of %q`, translation, expected)
  278. }
  279. translation = printer.Plural("test.key", 2, 2)
  280. expected = "2 items"
  281. if translation != expected {
  282. t.Errorf(`Wrong translation for plural, got %q instead of %q`, translation, expected)
  283. }
  284. }
  285. func TestPluralWithMixedAnySliceTypes(t *testing.T) {
  286. defaultCatalog = catalog{
  287. "en_US": translationDict{
  288. "mixed.key": []any{"single: %s", "multiple: %s", "many: %s"},
  289. },
  290. }
  291. printer := NewPrinter("en_US")
  292. // Test first element (should convert first any element to string)
  293. translation := printer.Plural("mixed.key", 0, "test") // n=0 uses index 0
  294. expected := "single: test"
  295. if translation != expected {
  296. t.Errorf(`Wrong translation for index 0, got %q instead of %q`, translation, expected)
  297. }
  298. // Test second element (should use plural form)
  299. translation = printer.Plural("mixed.key", 2, "items") // plural form for default language
  300. expected = "multiple: items"
  301. if translation != expected {
  302. t.Errorf(`Wrong translation for index 1, got %q instead of %q`, translation, expected)
  303. }
  304. }
  305. func TestPluralWithIndexOutOfBounds(t *testing.T) {
  306. defaultCatalog = catalog{
  307. "test_lang": translationDict{
  308. "limited.key": []string{"only one form"},
  309. },
  310. }
  311. // Force a scenario where getPluralForm might return an index >= len(plurals)
  312. // We'll create a scenario with Czech language rules
  313. defaultCatalog["cs_CZ"] = translationDict{
  314. "limited.key": []string{"one form only"}, // Only one form, but Czech has 3 plural forms
  315. }
  316. printer := NewPrinter("cs_CZ")
  317. // n=5 should return index 2 for Czech, but we only have 1 form (index 0)
  318. translation := printer.Plural("limited.key", 5)
  319. expected := "limited.key"
  320. if translation != expected {
  321. t.Errorf(`Wrong translation for out of bounds index, got %q instead of %q`, translation, expected)
  322. }
  323. }
  324. func TestPluralWithVariousLanguageRules(t *testing.T) {
  325. defaultCatalog = catalog{
  326. "ar_AR": translationDict{
  327. "items": []string{"no items", "one item", "two items", "few items", "many items", "other items"},
  328. },
  329. "pl_PL": translationDict{
  330. "files": []string{"one file", "few files", "many files"},
  331. },
  332. "ja_JP": translationDict{
  333. "photos": []string{"photos"},
  334. },
  335. }
  336. tests := []struct {
  337. language string
  338. key string
  339. n int
  340. expected string
  341. }{
  342. // Arabic tests
  343. {"ar_AR", "items", 0, "no items"},
  344. {"ar_AR", "items", 1, "one item"},
  345. {"ar_AR", "items", 2, "two items"},
  346. {"ar_AR", "items", 5, "few items"}, // n%100 >= 3 && n%100 <= 10
  347. {"ar_AR", "items", 15, "many items"}, // n%100 >= 11
  348. // Polish tests
  349. {"pl_PL", "files", 1, "one file"},
  350. {"pl_PL", "files", 3, "few files"}, // n%10 >= 2 && n%10 <= 4
  351. {"pl_PL", "files", 5, "many files"}, // default case
  352. // Japanese tests (always uses same form)
  353. {"ja_JP", "photos", 1, "photos"},
  354. {"ja_JP", "photos", 10, "photos"},
  355. }
  356. for _, test := range tests {
  357. printer := NewPrinter(test.language)
  358. translation := printer.Plural(test.key, test.n)
  359. if translation != test.expected {
  360. t.Errorf(`Wrong translation for %s with n=%d, got %q instead of %q`,
  361. test.language, test.n, translation, test.expected)
  362. }
  363. }
  364. }