plural_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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 TestPluralRules(t *testing.T) {
  6. scenarios := map[string]map[int]int{
  7. // Default rule (covers fr_FR, pt_BR, tr_TR, and other unlisted languages)
  8. "default": {
  9. 0: 0, // n <= 1
  10. 1: 0, // n <= 1
  11. 2: 1, // n > 1
  12. 5: 1, // n > 1
  13. },
  14. // Arabic (ar_AR) - 6 forms
  15. "ar_AR": {
  16. 0: 0, // n == 0
  17. 1: 1, // n == 1
  18. 2: 2, // n == 2
  19. 3: 3, // n%100 >= 3 && n%100 <= 10
  20. 5: 3, // n%100 >= 3 && n%100 <= 10
  21. 10: 3, // n%100 >= 3 && n%100 <= 10
  22. 11: 4, // n%100 >= 11
  23. 15: 4, // n%100 >= 11
  24. 99: 4, // n%100 >= 11
  25. 100: 5, // default case (n%100 == 0, doesn't match any condition)
  26. 101: 5, // default case (n%100 == 1, but n != 1)
  27. 200: 5, // default case
  28. },
  29. // Czech (cs_CZ) - 3 forms
  30. "cs_CZ": {
  31. 1: 0, // n == 1
  32. 2: 1, // n >= 2 && n <= 4
  33. 3: 1, // n >= 2 && n <= 4
  34. 4: 1, // n >= 2 && n <= 4
  35. 5: 2, // default case
  36. },
  37. // French (fr_FR) - uses default rule
  38. "fr_FR": {
  39. 0: 0, // n <= 1
  40. 1: 0, // n <= 1
  41. 2: 1, // n > 1
  42. 5: 1, // n > 1
  43. },
  44. // Indonesian (id_ID) - always form 0
  45. "id_ID": {
  46. 0: 0,
  47. 1: 0,
  48. 5: 0,
  49. 100: 0,
  50. },
  51. // Japanese (ja_JP) - always form 0
  52. "ja_JP": {
  53. 0: 0,
  54. 1: 0,
  55. 2: 0,
  56. 5: 0,
  57. 100: 0,
  58. },
  59. // Polish (pl_PL) - 3 forms
  60. "pl_PL": {
  61. 1: 0, // n == 1
  62. 2: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  63. 3: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  64. 4: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  65. 5: 2, // default case
  66. 10: 2, // default case (n%100 < 10, but n%10 not in 2-4)
  67. 11: 2, // default case (n%100 >= 10 and < 20)
  68. 12: 2, // default case (n%100 >= 10 and < 20)
  69. 22: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 >= 20)
  70. 24: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 >= 20)
  71. },
  72. // Portuguese Brazilian (pt_BR) - uses default rule
  73. "pt_BR": {
  74. 0: 0, // n <= 1
  75. 1: 0, // n <= 1
  76. 2: 1, // n > 1
  77. 5: 1, // n > 1
  78. },
  79. // Romanian (ro_RO) - 3 forms
  80. "ro_RO": {
  81. 0: 1, // n == 0 || (n%100 > 0 && n%100 < 20)
  82. 1: 0, // n == 1
  83. 2: 1, // n == 0 || (n%100 > 0 && n%100 < 20)
  84. 5: 1, // n == 0 || (n%100 > 0 && n%100 < 20)
  85. 19: 1, // n == 0 || (n%100 > 0 && n%100 < 20)
  86. 20: 2, // default case
  87. 21: 2, // default case
  88. 100: 2, // default case (n%100 == 0, so condition fails)
  89. 101: 1, // n%100 == 1, so n%100 > 0 && n%100 < 20
  90. },
  91. // Russian (ru_RU) - 3 forms
  92. "ru_RU": {
  93. 1: 0, // n%10 == 1 && n%100 != 11
  94. 2: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  95. 3: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  96. 4: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  97. 5: 2, // default case
  98. 11: 2, // n%10 == 1 but n%100 == 11, so default case
  99. 12: 2, // default case
  100. 21: 0, // n%10 == 1 && n%100 != 11
  101. 22: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 >= 20)
  102. },
  103. // Serbian (sr_RS) - same as Russian
  104. "sr_RS": {
  105. 1: 0, // n%10 == 1 && n%100 != 11
  106. 2: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  107. 5: 2, // default case
  108. 11: 2, // n%10 == 1 but n%100 == 11, so default case
  109. 21: 0, // n%10 == 1 && n%100 != 11
  110. },
  111. // Turkish (tr_TR) - uses default rule
  112. "tr_TR": {
  113. 0: 0, // n <= 1
  114. 1: 0, // n <= 1
  115. 2: 1, // n > 1
  116. 5: 1, // n > 1
  117. },
  118. // Ukrainian (uk_UA) - same as Russian
  119. "uk_UA": {
  120. 1: 0, // n%10 == 1 && n%100 != 11
  121. 2: 1, // n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)
  122. 5: 2, // default case
  123. 11: 2, // n%10 == 1 but n%100 == 11, so default case
  124. 21: 0, // n%10 == 1 && n%100 != 11
  125. },
  126. // Chinese Simplified (zh_CN) - always form 0
  127. "zh_CN": {
  128. 0: 0,
  129. 1: 0,
  130. 5: 0,
  131. 100: 0,
  132. },
  133. // Chinese Traditional (zh_TW) - always form 0
  134. "zh_TW": {
  135. 0: 0,
  136. 1: 0,
  137. 5: 0,
  138. 100: 0,
  139. },
  140. // Min Nan (nan_Latn_pehoeji) - always form 0
  141. "nan_Latn_pehoeji": {
  142. 0: 0,
  143. 1: 0,
  144. 5: 0,
  145. 100: 0,
  146. },
  147. // Additional languages from AvailableLanguages that use default rule
  148. "de_DE": {
  149. 0: 0, // n <= 1
  150. 1: 0, // n <= 1
  151. 2: 1, // n > 1
  152. },
  153. "el_EL": {
  154. 0: 0, // n <= 1
  155. 1: 0, // n <= 1
  156. 2: 1, // n > 1
  157. },
  158. "en_US": {
  159. 0: 0, // n <= 1
  160. 1: 0, // n <= 1
  161. 2: 1, // n > 1
  162. },
  163. "es_ES": {
  164. 0: 0, // n <= 1
  165. 1: 0, // n <= 1
  166. 2: 1, // n > 1
  167. },
  168. "fi_FI": {
  169. 0: 0, // n <= 1
  170. 1: 0, // n <= 1
  171. 2: 1, // n > 1
  172. },
  173. "hi_IN": {
  174. 0: 0, // n <= 1
  175. 1: 0, // n <= 1
  176. 2: 1, // n > 1
  177. },
  178. "it_IT": {
  179. 0: 0, // n <= 1
  180. 1: 0, // n <= 1
  181. 2: 1, // n > 1
  182. },
  183. "nl_NL": {
  184. 0: 0, // n <= 1
  185. 1: 0, // n <= 1
  186. 2: 1, // n > 1
  187. },
  188. // Test a language not in the switch (should use default rule)
  189. "unknown_language": {
  190. 0: 0, // n <= 1
  191. 1: 0, // n <= 1
  192. 2: 1, // n > 1
  193. },
  194. }
  195. for rule, values := range scenarios {
  196. for input, expected := range values {
  197. result := getPluralForm(rule, input)
  198. if result != expected {
  199. t.Errorf(`Unexpected result for %q rule, got %d instead of %d for %d as input`, rule, result, expected, input)
  200. }
  201. }
  202. }
  203. }