@jjlmoya/utils-converters 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (393) hide show
  1. package/package.json +4 -2
  2. package/src/category/i18n/de.ts +1 -1
  3. package/src/category/i18n/fr.ts +9 -9
  4. package/src/category/i18n/ru.ts +1 -1
  5. package/src/category/i18n/sv.ts +1 -1
  6. package/src/data.ts +0 -21
  7. package/src/layouts/PreviewLayout.astro +2 -1
  8. package/src/pages/[locale]/[slug].astro +28 -12
  9. package/src/tests/diacritics_density.test.ts +118 -0
  10. package/src/tests/inverted_punctuation.test.ts +84 -0
  11. package/src/tests/no_en_dash.test.ts +70 -0
  12. package/src/tests/pagespeed_best_practices.test.ts +198 -0
  13. package/src/tests/script_density.test.ts +94 -0
  14. package/src/tests/shared-test-helpers.ts +56 -0
  15. package/src/tests/tool_exports.test.ts +34 -0
  16. package/src/tool/avifAJpg/bibliography.ts +12 -0
  17. package/src/tool/avifAJpg/entry.ts +2 -0
  18. package/src/tool/avifAJpg/i18n/de.ts +2 -12
  19. package/src/tool/avifAJpg/i18n/en.ts +2 -12
  20. package/src/tool/avifAJpg/i18n/es.ts +2 -12
  21. package/src/tool/avifAJpg/i18n/fr.ts +11 -21
  22. package/src/tool/avifAJpg/i18n/id.ts +2 -12
  23. package/src/tool/avifAJpg/i18n/it.ts +2 -12
  24. package/src/tool/avifAJpg/i18n/ja.ts +1 -11
  25. package/src/tool/avifAJpg/i18n/ko.ts +1 -11
  26. package/src/tool/avifAJpg/i18n/nl.ts +2 -12
  27. package/src/tool/avifAJpg/i18n/pl.ts +2 -12
  28. package/src/tool/avifAJpg/i18n/pt.ts +2 -12
  29. package/src/tool/avifAJpg/i18n/ru.ts +7 -17
  30. package/src/tool/avifAJpg/i18n/sv.ts +2 -12
  31. package/src/tool/avifAJpg/i18n/tr.ts +1 -11
  32. package/src/tool/avifAJpg/i18n/zh.ts +4 -14
  33. package/src/tool/avifAJpg/seo.astro +2 -1
  34. package/src/tool/avifAPng/bibliography.ts +12 -0
  35. package/src/tool/avifAPng/entry.ts +2 -0
  36. package/src/tool/avifAPng/i18n/de.ts +4 -14
  37. package/src/tool/avifAPng/i18n/en.ts +4 -14
  38. package/src/tool/avifAPng/i18n/es.ts +4 -14
  39. package/src/tool/avifAPng/i18n/fr.ts +10 -20
  40. package/src/tool/avifAPng/i18n/id.ts +4 -14
  41. package/src/tool/avifAPng/i18n/it.ts +1 -11
  42. package/src/tool/avifAPng/i18n/ja.ts +1 -11
  43. package/src/tool/avifAPng/i18n/ko.ts +1 -11
  44. package/src/tool/avifAPng/i18n/nl.ts +4 -14
  45. package/src/tool/avifAPng/i18n/pl.ts +4 -14
  46. package/src/tool/avifAPng/i18n/pt.ts +4 -14
  47. package/src/tool/avifAPng/i18n/ru.ts +5 -15
  48. package/src/tool/avifAPng/i18n/sv.ts +4 -14
  49. package/src/tool/avifAPng/i18n/tr.ts +1 -11
  50. package/src/tool/avifAPng/i18n/zh.ts +3 -13
  51. package/src/tool/avifAPng/seo.astro +2 -1
  52. package/src/tool/avifAWebp/bibliography.ts +12 -0
  53. package/src/tool/avifAWebp/entry.ts +2 -0
  54. package/src/tool/avifAWebp/i18n/de.ts +6 -16
  55. package/src/tool/avifAWebp/i18n/en.ts +6 -16
  56. package/src/tool/avifAWebp/i18n/es.ts +6 -16
  57. package/src/tool/avifAWebp/i18n/fr.ts +13 -23
  58. package/src/tool/avifAWebp/i18n/id.ts +6 -16
  59. package/src/tool/avifAWebp/i18n/it.ts +1 -11
  60. package/src/tool/avifAWebp/i18n/ja.ts +1 -11
  61. package/src/tool/avifAWebp/i18n/ko.ts +2 -12
  62. package/src/tool/avifAWebp/i18n/nl.ts +6 -16
  63. package/src/tool/avifAWebp/i18n/pl.ts +6 -16
  64. package/src/tool/avifAWebp/i18n/pt.ts +6 -16
  65. package/src/tool/avifAWebp/i18n/ru.ts +7 -17
  66. package/src/tool/avifAWebp/i18n/sv.ts +6 -16
  67. package/src/tool/avifAWebp/i18n/tr.ts +2 -12
  68. package/src/tool/avifAWebp/i18n/zh.ts +6 -16
  69. package/src/tool/avifAWebp/seo.astro +2 -1
  70. package/src/tool/bmpAJpg/bibliography.ts +12 -0
  71. package/src/tool/bmpAJpg/entry.ts +2 -0
  72. package/src/tool/bmpAJpg/i18n/de.ts +1 -11
  73. package/src/tool/bmpAJpg/i18n/en.ts +1 -11
  74. package/src/tool/bmpAJpg/i18n/es.ts +1 -11
  75. package/src/tool/bmpAJpg/i18n/fr.ts +9 -19
  76. package/src/tool/bmpAJpg/i18n/id.ts +1 -11
  77. package/src/tool/bmpAJpg/i18n/it.ts +1 -11
  78. package/src/tool/bmpAJpg/i18n/ja.ts +1 -11
  79. package/src/tool/bmpAJpg/i18n/ko.ts +1 -11
  80. package/src/tool/bmpAJpg/i18n/nl.ts +1 -11
  81. package/src/tool/bmpAJpg/i18n/pl.ts +1 -11
  82. package/src/tool/bmpAJpg/i18n/pt.ts +1 -11
  83. package/src/tool/bmpAJpg/i18n/ru.ts +5 -15
  84. package/src/tool/bmpAJpg/i18n/sv.ts +1 -11
  85. package/src/tool/bmpAJpg/i18n/tr.ts +1 -11
  86. package/src/tool/bmpAJpg/i18n/zh.ts +1 -11
  87. package/src/tool/bmpAJpg/seo.astro +2 -1
  88. package/src/tool/bmpAPng/bibliography.ts +12 -0
  89. package/src/tool/bmpAPng/entry.ts +2 -0
  90. package/src/tool/bmpAPng/i18n/de.ts +2 -12
  91. package/src/tool/bmpAPng/i18n/en.ts +3 -13
  92. package/src/tool/bmpAPng/i18n/es.ts +2 -12
  93. package/src/tool/bmpAPng/i18n/fr.ts +12 -22
  94. package/src/tool/bmpAPng/i18n/id.ts +2 -12
  95. package/src/tool/bmpAPng/i18n/it.ts +2 -12
  96. package/src/tool/bmpAPng/i18n/ja.ts +1 -11
  97. package/src/tool/bmpAPng/i18n/ko.ts +1 -11
  98. package/src/tool/bmpAPng/i18n/nl.ts +2 -12
  99. package/src/tool/bmpAPng/i18n/pl.ts +2 -12
  100. package/src/tool/bmpAPng/i18n/pt.ts +2 -12
  101. package/src/tool/bmpAPng/i18n/ru.ts +9 -19
  102. package/src/tool/bmpAPng/i18n/sv.ts +2 -12
  103. package/src/tool/bmpAPng/i18n/tr.ts +1 -11
  104. package/src/tool/bmpAPng/i18n/zh.ts +1 -11
  105. package/src/tool/bmpAPng/seo.astro +2 -1
  106. package/src/tool/bmpAWebp/bibliography.ts +12 -0
  107. package/src/tool/bmpAWebp/entry.ts +2 -0
  108. package/src/tool/bmpAWebp/i18n/de.ts +8 -18
  109. package/src/tool/bmpAWebp/i18n/en.ts +8 -18
  110. package/src/tool/bmpAWebp/i18n/es.ts +8 -18
  111. package/src/tool/bmpAWebp/i18n/fr.ts +13 -23
  112. package/src/tool/bmpAWebp/i18n/id.ts +8 -18
  113. package/src/tool/bmpAWebp/i18n/it.ts +8 -18
  114. package/src/tool/bmpAWebp/i18n/ja.ts +7 -17
  115. package/src/tool/bmpAWebp/i18n/ko.ts +7 -17
  116. package/src/tool/bmpAWebp/i18n/nl.ts +8 -18
  117. package/src/tool/bmpAWebp/i18n/pl.ts +8 -18
  118. package/src/tool/bmpAWebp/i18n/pt.ts +8 -18
  119. package/src/tool/bmpAWebp/i18n/ru.ts +10 -20
  120. package/src/tool/bmpAWebp/i18n/sv.ts +8 -18
  121. package/src/tool/bmpAWebp/i18n/tr.ts +8 -18
  122. package/src/tool/bmpAWebp/i18n/zh.ts +8 -18
  123. package/src/tool/bmpAWebp/seo.astro +2 -1
  124. package/src/tool/gifAJpg/bibliography.ts +12 -0
  125. package/src/tool/gifAJpg/entry.ts +2 -0
  126. package/src/tool/gifAJpg/i18n/de.ts +1 -11
  127. package/src/tool/gifAJpg/i18n/en.ts +1 -11
  128. package/src/tool/gifAJpg/i18n/es.ts +1 -11
  129. package/src/tool/gifAJpg/i18n/fr.ts +9 -19
  130. package/src/tool/gifAJpg/i18n/id.ts +1 -11
  131. package/src/tool/gifAJpg/i18n/it.ts +1 -11
  132. package/src/tool/gifAJpg/i18n/ja.ts +1 -11
  133. package/src/tool/gifAJpg/i18n/ko.ts +1 -11
  134. package/src/tool/gifAJpg/i18n/nl.ts +1 -11
  135. package/src/tool/gifAJpg/i18n/pl.ts +1 -11
  136. package/src/tool/gifAJpg/i18n/pt.ts +1 -11
  137. package/src/tool/gifAJpg/i18n/ru.ts +2 -12
  138. package/src/tool/gifAJpg/i18n/sv.ts +1 -11
  139. package/src/tool/gifAJpg/i18n/tr.ts +1 -11
  140. package/src/tool/gifAJpg/i18n/zh.ts +1 -11
  141. package/src/tool/gifAJpg/seo.astro +2 -1
  142. package/src/tool/gifAPng/bibliography.ts +12 -0
  143. package/src/tool/gifAPng/entry.ts +2 -0
  144. package/src/tool/gifAPng/i18n/de.ts +2 -12
  145. package/src/tool/gifAPng/i18n/en.ts +3 -13
  146. package/src/tool/gifAPng/i18n/es.ts +2 -12
  147. package/src/tool/gifAPng/i18n/fr.ts +11 -21
  148. package/src/tool/gifAPng/i18n/id.ts +2 -12
  149. package/src/tool/gifAPng/i18n/it.ts +2 -12
  150. package/src/tool/gifAPng/i18n/ja.ts +1 -11
  151. package/src/tool/gifAPng/i18n/ko.ts +1 -11
  152. package/src/tool/gifAPng/i18n/nl.ts +2 -12
  153. package/src/tool/gifAPng/i18n/pl.ts +3 -13
  154. package/src/tool/gifAPng/i18n/pt.ts +2 -12
  155. package/src/tool/gifAPng/i18n/ru.ts +3 -13
  156. package/src/tool/gifAPng/i18n/sv.ts +3 -13
  157. package/src/tool/gifAPng/i18n/tr.ts +1 -11
  158. package/src/tool/gifAPng/i18n/zh.ts +2 -12
  159. package/src/tool/gifAPng/seo.astro +2 -1
  160. package/src/tool/gifAWebp/bibliography.ts +12 -0
  161. package/src/tool/gifAWebp/entry.ts +2 -0
  162. package/src/tool/gifAWebp/i18n/de.ts +3 -13
  163. package/src/tool/gifAWebp/i18n/en.ts +3 -13
  164. package/src/tool/gifAWebp/i18n/es.ts +1 -11
  165. package/src/tool/gifAWebp/i18n/fr.ts +11 -21
  166. package/src/tool/gifAWebp/i18n/id.ts +3 -13
  167. package/src/tool/gifAWebp/i18n/it.ts +1 -11
  168. package/src/tool/gifAWebp/i18n/ja.ts +1 -11
  169. package/src/tool/gifAWebp/i18n/ko.ts +1 -11
  170. package/src/tool/gifAWebp/i18n/nl.ts +3 -13
  171. package/src/tool/gifAWebp/i18n/pl.ts +3 -13
  172. package/src/tool/gifAWebp/i18n/pt.ts +3 -13
  173. package/src/tool/gifAWebp/i18n/ru.ts +7 -17
  174. package/src/tool/gifAWebp/i18n/sv.ts +3 -13
  175. package/src/tool/gifAWebp/i18n/tr.ts +1 -11
  176. package/src/tool/gifAWebp/i18n/zh.ts +4 -14
  177. package/src/tool/gifAWebp/seo.astro +2 -1
  178. package/src/tool/imagenBase64/bibliography.ts +12 -0
  179. package/src/tool/imagenBase64/entry.ts +2 -0
  180. package/src/tool/imagenBase64/i18n/de.ts +7 -17
  181. package/src/tool/imagenBase64/i18n/en.ts +7 -17
  182. package/src/tool/imagenBase64/i18n/es.ts +7 -17
  183. package/src/tool/imagenBase64/i18n/fr.ts +17 -27
  184. package/src/tool/imagenBase64/i18n/id.ts +7 -17
  185. package/src/tool/imagenBase64/i18n/it.ts +6 -16
  186. package/src/tool/imagenBase64/i18n/ja.ts +1 -11
  187. package/src/tool/imagenBase64/i18n/ko.ts +1 -11
  188. package/src/tool/imagenBase64/i18n/nl.ts +7 -17
  189. package/src/tool/imagenBase64/i18n/pl.ts +7 -17
  190. package/src/tool/imagenBase64/i18n/pt.ts +7 -17
  191. package/src/tool/imagenBase64/i18n/ru.ts +9 -19
  192. package/src/tool/imagenBase64/i18n/sv.ts +7 -17
  193. package/src/tool/imagenBase64/i18n/tr.ts +2 -12
  194. package/src/tool/imagenBase64/i18n/zh.ts +7 -17
  195. package/src/tool/imagenBase64/seo.astro +2 -1
  196. package/src/tool/jpgAIco/bibliography.ts +12 -0
  197. package/src/tool/jpgAIco/entry.ts +2 -0
  198. package/src/tool/jpgAIco/i18n/de.ts +9 -19
  199. package/src/tool/jpgAIco/i18n/en.ts +9 -19
  200. package/src/tool/jpgAIco/i18n/es.ts +7 -17
  201. package/src/tool/jpgAIco/i18n/fr.ts +13 -23
  202. package/src/tool/jpgAIco/i18n/id.ts +9 -19
  203. package/src/tool/jpgAIco/i18n/it.ts +3 -13
  204. package/src/tool/jpgAIco/i18n/ja.ts +2 -12
  205. package/src/tool/jpgAIco/i18n/ko.ts +2 -12
  206. package/src/tool/jpgAIco/i18n/nl.ts +9 -19
  207. package/src/tool/jpgAIco/i18n/pl.ts +9 -19
  208. package/src/tool/jpgAIco/i18n/pt.ts +9 -19
  209. package/src/tool/jpgAIco/i18n/ru.ts +11 -21
  210. package/src/tool/jpgAIco/i18n/sv.ts +9 -19
  211. package/src/tool/jpgAIco/i18n/tr.ts +3 -13
  212. package/src/tool/jpgAIco/i18n/zh.ts +9 -19
  213. package/src/tool/jpgAIco/seo.astro +2 -1
  214. package/src/tool/jpgAPng/bibliography.ts +12 -0
  215. package/src/tool/jpgAPng/entry.ts +2 -0
  216. package/src/tool/jpgAPng/i18n/de.ts +7 -17
  217. package/src/tool/jpgAPng/i18n/en.ts +9 -19
  218. package/src/tool/jpgAPng/i18n/es.ts +7 -17
  219. package/src/tool/jpgAPng/i18n/fr.ts +13 -23
  220. package/src/tool/jpgAPng/i18n/id.ts +7 -17
  221. package/src/tool/jpgAPng/i18n/it.ts +5 -15
  222. package/src/tool/jpgAPng/i18n/ja.ts +3 -13
  223. package/src/tool/jpgAPng/i18n/ko.ts +3 -13
  224. package/src/tool/jpgAPng/i18n/nl.ts +7 -17
  225. package/src/tool/jpgAPng/i18n/pl.ts +7 -17
  226. package/src/tool/jpgAPng/i18n/pt.ts +7 -17
  227. package/src/tool/jpgAPng/i18n/ru.ts +10 -20
  228. package/src/tool/jpgAPng/i18n/sv.ts +7 -17
  229. package/src/tool/jpgAPng/i18n/tr.ts +6 -16
  230. package/src/tool/jpgAPng/i18n/zh.ts +8 -18
  231. package/src/tool/jpgAPng/seo.astro +2 -1
  232. package/src/tool/jpgAWebp/bibliography.ts +12 -0
  233. package/src/tool/jpgAWebp/entry.ts +2 -0
  234. package/src/tool/jpgAWebp/i18n/de.ts +10 -20
  235. package/src/tool/jpgAWebp/i18n/en.ts +10 -20
  236. package/src/tool/jpgAWebp/i18n/es.ts +4 -14
  237. package/src/tool/jpgAWebp/i18n/fr.ts +16 -26
  238. package/src/tool/jpgAWebp/i18n/id.ts +10 -20
  239. package/src/tool/jpgAWebp/i18n/it.ts +6 -16
  240. package/src/tool/jpgAWebp/i18n/ja.ts +2 -12
  241. package/src/tool/jpgAWebp/i18n/ko.ts +3 -13
  242. package/src/tool/jpgAWebp/i18n/nl.ts +10 -20
  243. package/src/tool/jpgAWebp/i18n/pl.ts +10 -20
  244. package/src/tool/jpgAWebp/i18n/pt.ts +10 -20
  245. package/src/tool/jpgAWebp/i18n/ru.ts +10 -20
  246. package/src/tool/jpgAWebp/i18n/sv.ts +10 -20
  247. package/src/tool/jpgAWebp/i18n/tr.ts +5 -15
  248. package/src/tool/jpgAWebp/i18n/zh.ts +9 -19
  249. package/src/tool/jpgAWebp/seo.astro +2 -1
  250. package/src/tool/pngAIco/bibliography.ts +12 -0
  251. package/src/tool/pngAIco/entry.ts +2 -0
  252. package/src/tool/pngAIco/i18n/de.ts +8 -18
  253. package/src/tool/pngAIco/i18n/en.ts +8 -18
  254. package/src/tool/pngAIco/i18n/es.ts +8 -18
  255. package/src/tool/pngAIco/i18n/fr.ts +14 -24
  256. package/src/tool/pngAIco/i18n/id.ts +8 -18
  257. package/src/tool/pngAIco/i18n/it.ts +4 -14
  258. package/src/tool/pngAIco/i18n/ja.ts +2 -12
  259. package/src/tool/pngAIco/i18n/ko.ts +2 -12
  260. package/src/tool/pngAIco/i18n/nl.ts +8 -18
  261. package/src/tool/pngAIco/i18n/pl.ts +8 -18
  262. package/src/tool/pngAIco/i18n/pt.ts +8 -18
  263. package/src/tool/pngAIco/i18n/ru.ts +11 -21
  264. package/src/tool/pngAIco/i18n/sv.ts +8 -18
  265. package/src/tool/pngAIco/i18n/tr.ts +7 -17
  266. package/src/tool/pngAIco/i18n/zh.ts +8 -18
  267. package/src/tool/pngAIco/seo.astro +2 -1
  268. package/src/tool/pngAJpg/bibliography.ts +12 -0
  269. package/src/tool/pngAJpg/entry.ts +2 -0
  270. package/src/tool/pngAJpg/i18n/de.ts +1 -11
  271. package/src/tool/pngAJpg/i18n/en.ts +1 -11
  272. package/src/tool/pngAJpg/i18n/es.ts +1 -11
  273. package/src/tool/pngAJpg/i18n/fr.ts +3 -13
  274. package/src/tool/pngAJpg/i18n/id.ts +1 -11
  275. package/src/tool/pngAJpg/i18n/it.ts +1 -11
  276. package/src/tool/pngAJpg/i18n/ja.ts +1 -11
  277. package/src/tool/pngAJpg/i18n/ko.ts +1 -11
  278. package/src/tool/pngAJpg/i18n/nl.ts +1 -11
  279. package/src/tool/pngAJpg/i18n/pl.ts +7 -17
  280. package/src/tool/pngAJpg/i18n/pt.ts +1 -11
  281. package/src/tool/pngAJpg/i18n/ru.ts +9 -19
  282. package/src/tool/pngAJpg/i18n/sv.ts +1 -11
  283. package/src/tool/pngAJpg/i18n/tr.ts +1 -11
  284. package/src/tool/pngAJpg/i18n/zh.ts +6 -16
  285. package/src/tool/pngAJpg/seo.astro +2 -1
  286. package/src/tool/pngAWebp/bibliography.ts +16 -0
  287. package/src/tool/pngAWebp/entry.ts +2 -0
  288. package/src/tool/pngAWebp/i18n/de.ts +7 -21
  289. package/src/tool/pngAWebp/i18n/en.ts +7 -21
  290. package/src/tool/pngAWebp/i18n/es.ts +6 -20
  291. package/src/tool/pngAWebp/i18n/fr.ts +13 -27
  292. package/src/tool/pngAWebp/i18n/id.ts +7 -21
  293. package/src/tool/pngAWebp/i18n/it.ts +3 -17
  294. package/src/tool/pngAWebp/i18n/ja.ts +2 -16
  295. package/src/tool/pngAWebp/i18n/ko.ts +2 -16
  296. package/src/tool/pngAWebp/i18n/nl.ts +7 -21
  297. package/src/tool/pngAWebp/i18n/pl.ts +7 -21
  298. package/src/tool/pngAWebp/i18n/pt.ts +6 -20
  299. package/src/tool/pngAWebp/i18n/ru.ts +8 -22
  300. package/src/tool/pngAWebp/i18n/sv.ts +7 -21
  301. package/src/tool/pngAWebp/i18n/tr.ts +6 -20
  302. package/src/tool/pngAWebp/i18n/zh.ts +6 -20
  303. package/src/tool/pngAWebp/seo.astro +2 -1
  304. package/src/tool/svgAJpg/bibliography.ts +12 -0
  305. package/src/tool/svgAJpg/entry.ts +2 -0
  306. package/src/tool/svgAJpg/i18n/de.ts +2 -12
  307. package/src/tool/svgAJpg/i18n/en.ts +1 -11
  308. package/src/tool/svgAJpg/i18n/es.ts +1 -11
  309. package/src/tool/svgAJpg/i18n/fr.ts +6 -16
  310. package/src/tool/svgAJpg/i18n/id.ts +1 -11
  311. package/src/tool/svgAJpg/i18n/it.ts +1 -11
  312. package/src/tool/svgAJpg/i18n/ja.ts +1 -11
  313. package/src/tool/svgAJpg/i18n/ko.ts +1 -11
  314. package/src/tool/svgAJpg/i18n/nl.ts +1 -11
  315. package/src/tool/svgAJpg/i18n/pl.ts +1 -11
  316. package/src/tool/svgAJpg/i18n/pt.ts +1 -11
  317. package/src/tool/svgAJpg/i18n/ru.ts +3 -13
  318. package/src/tool/svgAJpg/i18n/sv.ts +1 -11
  319. package/src/tool/svgAJpg/i18n/tr.ts +1 -11
  320. package/src/tool/svgAJpg/i18n/zh.ts +1 -11
  321. package/src/tool/svgAJpg/seo.astro +2 -1
  322. package/src/tool/svgAPng/bibliography.ts +12 -0
  323. package/src/tool/svgAPng/entry.ts +2 -0
  324. package/src/tool/svgAPng/i18n/de.ts +3 -13
  325. package/src/tool/svgAPng/i18n/en.ts +3 -13
  326. package/src/tool/svgAPng/i18n/es.ts +1 -11
  327. package/src/tool/svgAPng/i18n/fr.ts +9 -19
  328. package/src/tool/svgAPng/i18n/id.ts +3 -13
  329. package/src/tool/svgAPng/i18n/it.ts +3 -13
  330. package/src/tool/svgAPng/i18n/ja.ts +1 -11
  331. package/src/tool/svgAPng/i18n/ko.ts +1 -11
  332. package/src/tool/svgAPng/i18n/nl.ts +3 -13
  333. package/src/tool/svgAPng/i18n/pl.ts +3 -13
  334. package/src/tool/svgAPng/i18n/pt.ts +3 -13
  335. package/src/tool/svgAPng/i18n/ru.ts +5 -15
  336. package/src/tool/svgAPng/i18n/sv.ts +3 -13
  337. package/src/tool/svgAPng/i18n/tr.ts +3 -13
  338. package/src/tool/svgAPng/i18n/zh.ts +3 -13
  339. package/src/tool/svgAPng/seo.astro +2 -1
  340. package/src/tool/webpAIco/bibliography.ts +12 -0
  341. package/src/tool/webpAIco/entry.ts +2 -0
  342. package/src/tool/webpAIco/i18n/de.ts +1 -11
  343. package/src/tool/webpAIco/i18n/en.ts +1 -11
  344. package/src/tool/webpAIco/i18n/es.ts +1 -11
  345. package/src/tool/webpAIco/i18n/fr.ts +5 -15
  346. package/src/tool/webpAIco/i18n/id.ts +1 -11
  347. package/src/tool/webpAIco/i18n/it.ts +1 -11
  348. package/src/tool/webpAIco/i18n/ja.ts +1 -11
  349. package/src/tool/webpAIco/i18n/ko.ts +1 -11
  350. package/src/tool/webpAIco/i18n/nl.ts +1 -11
  351. package/src/tool/webpAIco/i18n/pl.ts +1 -11
  352. package/src/tool/webpAIco/i18n/pt.ts +1 -11
  353. package/src/tool/webpAIco/i18n/ru.ts +3 -13
  354. package/src/tool/webpAIco/i18n/sv.ts +1 -11
  355. package/src/tool/webpAIco/i18n/tr.ts +1 -11
  356. package/src/tool/webpAIco/i18n/zh.ts +1 -12
  357. package/src/tool/webpAIco/seo.astro +2 -1
  358. package/src/tool/webpAJpg/bibliography.ts +16 -0
  359. package/src/tool/webpAJpg/entry.ts +2 -0
  360. package/src/tool/webpAJpg/i18n/de.ts +3 -17
  361. package/src/tool/webpAJpg/i18n/en.ts +3 -17
  362. package/src/tool/webpAJpg/i18n/es.ts +1 -15
  363. package/src/tool/webpAJpg/i18n/fr.ts +10 -24
  364. package/src/tool/webpAJpg/i18n/id.ts +3 -17
  365. package/src/tool/webpAJpg/i18n/it.ts +2 -16
  366. package/src/tool/webpAJpg/i18n/ja.ts +1 -15
  367. package/src/tool/webpAJpg/i18n/ko.ts +1 -15
  368. package/src/tool/webpAJpg/i18n/nl.ts +2 -16
  369. package/src/tool/webpAJpg/i18n/pl.ts +3 -17
  370. package/src/tool/webpAJpg/i18n/pt.ts +3 -17
  371. package/src/tool/webpAJpg/i18n/ru.ts +7 -21
  372. package/src/tool/webpAJpg/i18n/sv.ts +3 -17
  373. package/src/tool/webpAJpg/i18n/tr.ts +2 -16
  374. package/src/tool/webpAJpg/i18n/zh.ts +2 -16
  375. package/src/tool/webpAJpg/seo.astro +2 -1
  376. package/src/tool/webpAPng/bibliography.ts +16 -0
  377. package/src/tool/webpAPng/entry.ts +2 -0
  378. package/src/tool/webpAPng/i18n/de.ts +3 -17
  379. package/src/tool/webpAPng/i18n/en.ts +3 -17
  380. package/src/tool/webpAPng/i18n/es.ts +1 -15
  381. package/src/tool/webpAPng/i18n/fr.ts +9 -23
  382. package/src/tool/webpAPng/i18n/id.ts +3 -17
  383. package/src/tool/webpAPng/i18n/it.ts +3 -17
  384. package/src/tool/webpAPng/i18n/ja.ts +1 -15
  385. package/src/tool/webpAPng/i18n/ko.ts +1 -15
  386. package/src/tool/webpAPng/i18n/nl.ts +3 -17
  387. package/src/tool/webpAPng/i18n/pl.ts +3 -17
  388. package/src/tool/webpAPng/i18n/pt.ts +3 -17
  389. package/src/tool/webpAPng/i18n/ru.ts +4 -18
  390. package/src/tool/webpAPng/i18n/sv.ts +3 -17
  391. package/src/tool/webpAPng/i18n/tr.ts +2 -17
  392. package/src/tool/webpAPng/i18n/zh.ts +3 -17
  393. package/src/tool/webpAPng/seo.astro +2 -1
@@ -0,0 +1,198 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { readdirSync, readFileSync } from 'fs';
3
+ import { join, relative } from 'path';
4
+ import { ALL_TOOLS } from '../tools';
5
+ import type { SEOSection, ToolLocaleContent } from '../types';
6
+
7
+ const srcDir = join(process.cwd(), 'src');
8
+ const toolDir = join(srcDir, 'tool');
9
+ const geometryReads = [
10
+ 'offsetWidth',
11
+ 'offsetHeight',
12
+ 'offsetTop',
13
+ 'offsetLeft',
14
+ 'clientWidth',
15
+ 'clientHeight',
16
+ 'clientTop',
17
+ 'clientLeft',
18
+ 'scrollWidth',
19
+ 'scrollHeight',
20
+ 'scrollTop',
21
+ 'scrollLeft',
22
+ 'getBoundingClientRect',
23
+ 'getClientRects',
24
+ 'computedStyle',
25
+ 'getComputedStyle',
26
+ ];
27
+ const domWrites = [
28
+ '.style.',
29
+ '.classList.add',
30
+ '.classList.remove',
31
+ '.classList.toggle',
32
+ '.appendChild',
33
+ '.insertBefore',
34
+ '.prepend',
35
+ '.append',
36
+ '.remove',
37
+ '.innerHTML',
38
+ '.textContent',
39
+ '.setAttribute',
40
+ ];
41
+
42
+ function findFiles(dir: string, extensions: string[]): string[] {
43
+ const files: string[] = [];
44
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
45
+ const fullPath = join(dir, entry.name);
46
+ if (entry.isDirectory()) files.push(...findFiles(fullPath, extensions));
47
+ else if (extensions.some((extension) => entry.name.endsWith(extension))) files.push(fullPath);
48
+ }
49
+ return files;
50
+ }
51
+
52
+ function relativePath(file: string): string {
53
+ return relative(process.cwd(), file).replace(/\\/g, '/');
54
+ }
55
+
56
+ function findFormControls(content: string, tagName: 'input' | 'select'): RegExpMatchArray[] {
57
+ return Array.from(content.matchAll(new RegExp(`<${tagName}\\b[^>]*>`, 'gi')));
58
+ }
59
+
60
+ function attrValue(tag: string, attr: string): string | null {
61
+ const match = tag.match(new RegExp(`\\b${attr}\\s*=\\s*(?:"([^"]+)"|'([^']+)'|\\{([^}]+)\\})`, 'i'));
62
+ return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
63
+ }
64
+
65
+ function booleanAttr(tag: string, attr: string): boolean {
66
+ return new RegExp(`\\b${attr}\\b`, 'i').test(tag);
67
+ }
68
+
69
+ function controlStartIndex(content: string, tag: RegExpMatchArray): number {
70
+ return tag.index ?? content.indexOf(tag[0]);
71
+ }
72
+
73
+ function hasWrappingLabel(content: string, tag: RegExpMatchArray): boolean {
74
+ const index = controlStartIndex(content, tag);
75
+ const before = content.slice(0, index);
76
+ const labelOpen = before.lastIndexOf('<label');
77
+ const labelClose = before.lastIndexOf('</label>');
78
+ const nextLabelClose = content.indexOf('</label>', index + tag[0].length);
79
+ return labelOpen > labelClose && nextLabelClose !== -1;
80
+ }
81
+
82
+ function hasAccessibleName(content: string, tag: RegExpMatchArray): boolean {
83
+ const source = tag[0];
84
+ if (attrValue(source, 'aria-label')) return true;
85
+ if (attrValue(source, 'aria-labelledby')) return true;
86
+ const id = attrValue(source, 'id');
87
+ if (id && hasExplicitLabel(content, id)) return true;
88
+ return hasWrappingLabel(content, tag);
89
+ }
90
+
91
+ function isVisuallyHiddenFileInput(tag: string): boolean {
92
+ const type = attrValue(tag, 'type')?.toLowerCase() ?? 'text';
93
+ const attributes = `${attrValue(tag, 'style') ?? ''} ${attrValue(tag, 'class') ?? ''}`.toLowerCase();
94
+ const hiddenPatterns = ['display:none', 'display: none', 'file-input'];
95
+ return type === 'file' && hiddenPatterns.some((pattern) => attributes.includes(pattern));
96
+ }
97
+
98
+ function isIgnoredInput(tag: string): boolean {
99
+ const type = attrValue(tag, 'type')?.toLowerCase() ?? 'text';
100
+ return ['hidden', 'button', 'submit', 'reset'].includes(type) || booleanAttr(tag, 'aria-hidden') || isVisuallyHiddenFileInput(tag);
101
+ }
102
+
103
+ function controlFailures(content: string, tagName: 'input' | 'select'): string[] {
104
+ return findFormControls(content, tagName)
105
+ .filter((tag) => tagName !== 'input' || !isIgnoredInput(tag[0]))
106
+ .filter((tag) => !hasAccessibleName(content, tag))
107
+ .map((tag) => tag[0]);
108
+ }
109
+
110
+ function explicitLabelMessage(tagName: string, path: string, failures: string[]): string {
111
+ return `${tagName} controls without label, wrapping label, aria-label or aria-labelledby in ${path}:\n${failures.join('\n')}`;
112
+ }
113
+
114
+ function hasExplicitLabel(content: string, id: string): boolean {
115
+ const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
116
+ return (
117
+ new RegExp(`<label\\b[^>]*\\bfor\\s*=\\s*["']${escapedId}["'][^>]*>`, 'i').test(content)
118
+ || new RegExp(`<label\\b[^>]*\\bfor\\s*=\\s*\\{${escapedId}\\}[^>]*>`, 'i').test(content)
119
+ );
120
+ }
121
+
122
+ function headingLevels(sections: SEOSection[]): number[] {
123
+ return sections
124
+ .filter((section) => section.type === 'title')
125
+ .map((section) => Number('level' in section ? section.level : 0))
126
+ .filter((level) => Number.isInteger(level) && level > 0);
127
+ }
128
+
129
+ function findHeadingLevelJumps(levels: number[]): string[] {
130
+ const failures: string[] = [];
131
+ levels.forEach((level, index) => {
132
+ const previous = index === 0 ? 1 : levels[index - 1];
133
+ if (previous && level > previous + 1) {
134
+ failures.push(`h${previous} -> h${level}`);
135
+ }
136
+ });
137
+ return failures;
138
+ }
139
+
140
+ function hasDomWriteBeforeGeometryRead(content: string): boolean {
141
+ const normalized = content.replace(/\s+/g, ' ');
142
+ return domWrites.some((write) => {
143
+ const writeIndex = normalized.indexOf(write);
144
+ if (writeIndex === -1) return false;
145
+ return geometryReads.some((read) => normalized.indexOf(read, writeIndex + write.length) !== -1);
146
+ });
147
+ }
148
+
149
+ describe('PageSpeed best-practice guards', () => {
150
+ const astroToolFiles = findFiles(toolDir, ['.astro']);
151
+ const scriptFiles = findFiles(toolDir, ['.astro', '.ts', '.js']);
152
+
153
+ astroToolFiles.forEach((file) => {
154
+ const displayPath = relativePath(file);
155
+
156
+ it(`${displayPath} labels every input with an explicit label`, () => {
157
+ const content = readFileSync(file, 'utf-8');
158
+ const failures = controlFailures(content, 'input');
159
+
160
+ expect(failures, explicitLabelMessage('Input', displayPath, failures)).toEqual([]);
161
+ });
162
+
163
+ it(`${displayPath} labels every select with an explicit label`, () => {
164
+ const content = readFileSync(file, 'utf-8');
165
+ const failures = controlFailures(content, 'select');
166
+
167
+ expect(failures, explicitLabelMessage('Select', displayPath, failures)).toEqual([]);
168
+ });
169
+ });
170
+
171
+ ALL_TOOLS.forEach((tool) => {
172
+ Object.entries(tool.entry.i18n).forEach(([locale, loader]) => {
173
+ it(`${tool.entry.id}/${locale} keeps SEO headings sequential`, async () => {
174
+ if (!loader) return;
175
+ const content = (await loader()) as ToolLocaleContent;
176
+ const levels = headingLevels(content.seo);
177
+ const failures = findHeadingLevelJumps(levels);
178
+
179
+ expect(
180
+ failures,
181
+ `SEO headings in ${tool.entry.id}/${locale} skip levels: ${failures.join(', ')}`,
182
+ ).toEqual([]);
183
+ });
184
+ });
185
+ });
186
+
187
+ scriptFiles.forEach((file) => {
188
+ const displayPath = relativePath(file);
189
+
190
+ it(`${displayPath} avoids static forced-reflow patterns`, () => {
191
+ const content = readFileSync(file, 'utf-8');
192
+ expect(
193
+ hasDomWriteBeforeGeometryRead(content),
194
+ `${displayPath} appears to read layout geometry after DOM/style mutations. Split writes and reads across frames or measure before mutating.`,
195
+ ).toBe(false);
196
+ });
197
+ });
198
+ });
@@ -0,0 +1,94 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+
4
+ type ScriptLocale = keyof typeof SCRIPT_RULES;
5
+
6
+ const SCRIPT_RULES = {
7
+ ja: {
8
+ language: 'Japanese',
9
+ scriptName: 'kana/kanji',
10
+ scriptCharacters: /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}]/gu,
11
+ minScriptRatio: 0.45,
12
+ },
13
+ ko: {
14
+ language: 'Korean',
15
+ scriptName: 'hangul',
16
+ scriptCharacters: /\p{Script=Hangul}/gu,
17
+ minScriptRatio: 0.55,
18
+ },
19
+ ru: {
20
+ language: 'Russian',
21
+ scriptName: 'cyrillic',
22
+ scriptCharacters: /\p{Script=Cyrillic}/gu,
23
+ minScriptRatio: 0.65,
24
+ },
25
+ zh: {
26
+ language: 'Chinese',
27
+ scriptName: 'han',
28
+ scriptCharacters: /\p{Script=Han}/gu,
29
+ minScriptRatio: 0.45,
30
+ },
31
+ } as const;
32
+
33
+ const LETTERS = /\p{L}/gu;
34
+ const TRANSLATABLE_KEYS = ['title', 'description', 'ui', 'seo', 'faq', 'howTo'] as const;
35
+
36
+ function collectStrings(value: unknown): string[] {
37
+ if (typeof value === 'string') return [value];
38
+ if (!value || typeof value !== 'object') return [];
39
+ if (Array.isArray(value)) return value.flatMap(collectStrings);
40
+ return Object.values(value).flatMap(collectStrings);
41
+ }
42
+
43
+ function normalizeText(value: unknown): string {
44
+ return collectStrings(value).join(' ').normalize('NFC');
45
+ }
46
+
47
+ function translatableContent(content: Record<string, unknown>) {
48
+ return TRANSLATABLE_KEYS.map((key) => content[key]);
49
+ }
50
+
51
+ function letterCount(text: string): number {
52
+ return text.match(LETTERS)?.length ?? 0;
53
+ }
54
+
55
+ function scriptCount(text: string, locale: ScriptLocale): number {
56
+ return text.match(SCRIPT_RULES[locale].scriptCharacters)?.length ?? 0;
57
+ }
58
+
59
+ function scriptRatio(text: string, locale: ScriptLocale): number {
60
+ const letters = letterCount(text);
61
+ if (letters === 0) return 0;
62
+ return scriptCount(text, locale) / letters;
63
+ }
64
+
65
+ describe('Native script density validation', () => {
66
+ ALL_TOOLS.forEach((tool) => {
67
+ describe(`Tool: ${tool.entry.id}`, () => {
68
+ Object.keys(SCRIPT_RULES).forEach((locale) => {
69
+ it(`${locale} keeps most translated text in its native script`, async () => {
70
+ const typedLocale = locale as ScriptLocale;
71
+ const loader = tool.entry.i18n[typedLocale];
72
+ if (!loader) return;
73
+
74
+ const content = await loader();
75
+ const rule = SCRIPT_RULES[typedLocale];
76
+ const text = normalizeText(translatableContent(content as Record<string, unknown>));
77
+ const letters = letterCount(text);
78
+ const matches = scriptCount(text, typedLocale);
79
+ const ratio = scriptRatio(text, typedLocale);
80
+
81
+ expect(
82
+ ratio,
83
+ [
84
+ `Possible broken translation detected in ${tool.entry.id}/${typedLocale} (${rule.language}).`,
85
+ `The text has ${matches} ${rule.scriptName} characters out of ${letters} analyzed letters (${(ratio * 100).toFixed(1)}%).`,
86
+ `Most translatable content should be written in ${rule.scriptName} script.`,
87
+ 'Non-translatable fields such as slug, bibliography, and schemas are ignored to avoid false positives.',
88
+ ].join(' '),
89
+ ).toBeGreaterThanOrEqual(rule.minScriptRatio);
90
+ });
91
+ });
92
+ });
93
+ });
94
+ });
@@ -0,0 +1,56 @@
1
+ import type { ToolDefinition } from '../types';
2
+
3
+ export interface ToolExportValidationResult {
4
+ passed: boolean;
5
+ failures: string[];
6
+ }
7
+
8
+ function validateComponentType(
9
+ toolId: string,
10
+ componentName: string,
11
+ component: unknown,
12
+ failures: string[],
13
+ ): void {
14
+ if (typeof component !== 'function') {
15
+ failures.push(`${toolId}: ${componentName} is not a function (${typeof component})`);
16
+ }
17
+ }
18
+
19
+ async function validateComponentExecution(
20
+ toolId: string,
21
+ componentName: string,
22
+ fn: () => Promise<unknown>,
23
+ failures: string[],
24
+ ): Promise<void> {
25
+ try {
26
+ const result = await fn();
27
+ if (!result || typeof result !== 'object') {
28
+ failures.push(`${toolId}: ${componentName} import returned invalid result`);
29
+ }
30
+ } catch (error) {
31
+ failures.push(`${toolId}: ${componentName} execution error - ${error instanceof Error ? error.message : 'unknown'}`);
32
+ }
33
+ }
34
+
35
+ export async function validateToolExports(tools: ToolDefinition[]): Promise<ToolExportValidationResult> {
36
+ const failures: string[] = [];
37
+
38
+ for (const tool of tools) {
39
+ validateComponentType(tool.entry.id, 'Component', tool.Component, failures);
40
+ validateComponentType(tool.entry.id, 'SEOComponent', tool.SEOComponent, failures);
41
+ validateComponentType(tool.entry.id, 'BibliographyComponent', tool.BibliographyComponent, failures);
42
+
43
+ const componentFn = tool.Component as () => Promise<unknown>;
44
+ const seoFn = tool.SEOComponent as () => Promise<unknown>;
45
+ const bibFn = tool.BibliographyComponent as () => Promise<unknown>;
46
+
47
+ await validateComponentExecution(tool.entry.id, 'Component', componentFn, failures);
48
+ await validateComponentExecution(tool.entry.id, 'SEOComponent', seoFn, failures);
49
+ await validateComponentExecution(tool.entry.id, 'BibliographyComponent', bibFn, failures);
50
+ }
51
+
52
+ return {
53
+ passed: failures.length === 0,
54
+ failures,
55
+ };
56
+ }
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import { validateToolExports } from './shared-test-helpers';
4
+
5
+ describe('Tool Exports Pattern Validation', () => {
6
+ describe('Component Exports Format', () => {
7
+ ALL_TOOLS.forEach((tool) => {
8
+ it(`${tool.entry.id}: Component should be a lazy-loaded function`, () => {
9
+ expect(typeof tool.Component).toBe('function');
10
+ expect(tool.Component).toBeInstanceOf(Function);
11
+ });
12
+
13
+ it(`${tool.entry.id}: SEOComponent should be a lazy-loaded function`, () => {
14
+ expect(typeof tool.SEOComponent).toBe('function');
15
+ expect(tool.SEOComponent).toBeInstanceOf(Function);
16
+ });
17
+
18
+ it(`${tool.entry.id}: BibliographyComponent should be a lazy-loaded function`, () => {
19
+ expect(typeof tool.BibliographyComponent).toBe('function');
20
+ expect(tool.BibliographyComponent).toBeInstanceOf(Function);
21
+ });
22
+ });
23
+ });
24
+
25
+ describe('Dynamic Import Validation', () => {
26
+ it('all tools must have functional dynamic imports', async () => {
27
+ const result = await validateToolExports(ALL_TOOLS);
28
+ if (!result.passed) {
29
+ throw new Error(`Tool export validation failed:\n${result.failures.join('\n')}`);
30
+ }
31
+ expect(result.passed).toBe(true);
32
+ });
33
+ });
34
+ });
@@ -0,0 +1,12 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliography: BibliographyEntry[] = [
4
+ {
5
+ name: 'AV1 Image File Format (AVIF)',
6
+ url: 'https://aomediacodec.github.io/av1-avif/',
7
+ },
8
+ {
9
+ name: 'Allgemeine Informationen zu JPG',
10
+ url: 'https://en.wikipedia.org/wiki/JPEG',
11
+ },
12
+ ];
@@ -27,3 +27,5 @@ export const avifAJpg: ConvertersToolEntry<ImageConverterUI> = {
27
27
  zh: () => import('./i18n/zh').then((m) => m.content),
28
28
  },
29
29
  };
30
+
31
+ export { bibliography } from './bibliography';
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ImageConverterUI } from '../../../shared/ImageConverter.astro';
2
3
  import { generateSchemas } from '../../../shared/logic/schemas';
3
4
  import type { AvifAJpgLocaleContent } from '../index';
@@ -51,17 +52,6 @@ const howTo: AvifAJpgLocaleContent['howTo'] = [
51
52
  },
52
53
  ];
53
54
 
54
- const bibliography: AvifAJpgLocaleContent['bibliography'] = [
55
- {
56
- name: 'AV1 Image File Format (AVIF)',
57
- url: 'https://aomediacodec.github.io/av1-avif/',
58
- },
59
- {
60
- name: 'Allgemeine Informationen zu JPG',
61
- url: 'https://en.wikipedia.org/wiki/JPEG',
62
- },
63
- ];
64
-
65
55
  const seo: AvifAJpgLocaleContent['seo'] = [
66
56
  {
67
57
  type: 'title',
@@ -135,7 +125,7 @@ const seo: AvifAJpgLocaleContent['seo'] = [
135
125
  {
136
126
  type: 'tip',
137
127
  title: 'Das perfekte Gleichgewicht zwischen Kompression und Kompatibilität',
138
- html: 'AVIF ist bei gleicher Qualität um 50 % kleiner als JPG aber JPG bietet 100 % Kompatibilität mit jeder Software. Wenn Sie AVIF-Bilder vom Server erhalten und diese bearbeiten, per E-Mail versenden oder in Desktop-Tools öffnen müssen, konvertieren Sie sie zuerst in JPG.',
128
+ html: 'AVIF ist bei gleicher Qualität um 50 % kleiner als JPG - aber JPG bietet 100 % Kompatibilität mit jeder Software. Wenn Sie AVIF-Bilder vom Server erhalten und diese bearbeiten, per E-Mail versenden oder in Desktop-Tools öffnen müssen, konvertieren Sie sie zuerst in JPG.',
139
129
  },
140
130
  {
141
131
  type: 'title',
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ImageConverterUI } from '../../../shared/ImageConverter.astro';
2
3
  import { generateSchemas } from '../../../shared/logic/schemas';
3
4
  import type { AvifAJpgLocaleContent } from '../index';
@@ -51,17 +52,6 @@ const howTo: AvifAJpgLocaleContent['howTo'] = [
51
52
  },
52
53
  ];
53
54
 
54
- const bibliography: AvifAJpgLocaleContent['bibliography'] = [
55
- {
56
- name: 'AV1 Image File Format (AVIF)',
57
- url: 'https://aomediacodec.github.io/av1-avif/',
58
- },
59
- {
60
- name: 'JPEG Overview',
61
- url: 'https://en.wikipedia.org/wiki/JPEG',
62
- },
63
- ];
64
-
65
55
  const seo: AvifAJpgLocaleContent['seo'] = [
66
56
  {
67
57
  type: 'title',
@@ -135,7 +125,7 @@ const seo: AvifAJpgLocaleContent['seo'] = [
135
125
  {
136
126
  type: 'tip',
137
127
  title: 'The perfect balance between compression and compatibility',
138
- html: 'AVIF is 50% smaller than JPG at equal quality but JPG has 100% compatibility with any software. If you receive AVIF images from the server and need to edit them, send them by email or open them in desktop tools, convert them to JPG first.',
128
+ html: 'AVIF is 50% smaller than JPG at equal quality - but JPG has 100% compatibility with any software. If you receive AVIF images from the server and need to edit them, send them by email or open them in desktop tools, convert them to JPG first.',
139
129
  },
140
130
  {
141
131
  type: 'title',
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ImageConverterUI } from '../../../shared/ImageConverter.astro';
2
3
  import { generateSchemas } from '../../../shared/logic/schemas';
3
4
  import type { AvifAJpgLocaleContent } from '../index';
@@ -51,17 +52,6 @@ const howTo: AvifAJpgLocaleContent['howTo'] = [
51
52
  },
52
53
  ];
53
54
 
54
- const bibliography: AvifAJpgLocaleContent['bibliography'] = [
55
- {
56
- name: 'AV1 Image File Format (AVIF)',
57
- url: 'https://aomediacodec.github.io/av1-avif/',
58
- },
59
- {
60
- name: 'Información General del JPG',
61
- url: 'https://en.wikipedia.org/wiki/JPEG',
62
- },
63
- ];
64
-
65
55
  const seo: AvifAJpgLocaleContent['seo'] = [
66
56
  {
67
57
  type: 'title',
@@ -135,7 +125,7 @@ const seo: AvifAJpgLocaleContent['seo'] = [
135
125
  {
136
126
  type: 'tip',
137
127
  title: 'El equilibrio perfecto entre compresión y compatibilidad',
138
- html: 'AVIF es un 50% más pequeño que JPG a igual calidad pero JPG tiene compatibilidad del 100% con cualquier software. Si recibes imágenes AVIF del servidor y necesitas editarlas, enviarlas por email o abrirlas en herramientas de escritorio, conviértelas a JPG primero.',
128
+ html: 'AVIF es un 50% más pequeño que JPG a igual calidad - pero JPG tiene compatibilidad del 100% con cualquier software. Si recibes imágenes AVIF del servidor y necesitas editarlas, enviarlas por email o abrirlas en herramientas de escritorio, conviértelas a JPG primero.',
139
129
  },
140
130
  {
141
131
  type: 'title',
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ImageConverterUI } from '../../../shared/ImageConverter.astro';
2
3
  import { generateSchemas } from '../../../shared/logic/schemas';
3
4
  import type { AvifAJpgLocaleContent } from '../index';
@@ -51,35 +52,24 @@ const howTo: AvifAJpgLocaleContent['howTo'] = [
51
52
  },
52
53
  ];
53
54
 
54
- const bibliography: AvifAJpgLocaleContent['bibliography'] = [
55
- {
56
- name: 'Format de fichier image AV1 (AVIF)',
57
- url: 'https://aomediacodec.github.io/av1-avif/',
58
- },
59
- {
60
- name: 'Aperçu JPEG',
61
- url: 'https://en.wikipedia.org/wiki/JPEG',
62
- },
63
- ];
64
-
65
55
  const seo: AvifAJpgLocaleContent['seo'] = [
66
56
  {
67
57
  type: 'title',
68
- text: 'Convertisseur AVIF vers JPG : Compatibilité Universelle pour Vos Images Modernes',
58
+ text: 'Convertisseur AVIF vers JPG: Compatibilité Universelle pour Vos Images Modernes',
69
59
  level: 2,
70
60
  },
71
61
  {
72
62
  type: 'paragraph',
73
- html: 'AVIF (AV1 Image File Format) est le format d\'image le plus efficace disponible aujourd\'hui. Développé par l\'Alliance for Open Media et basé sur le codec vidéo AV1, il offre une compression 50% meilleure que le JPG à la même qualité visuelle. Cependant, cet avantage technique a un prix : AVIF n\'est pas pris en charge par Photoshop, Lightroom, la visionneuse de photos Windows, ni par la plupart des outils d\'édition et de visualisation traditionnels. Convertir en JPG résout ce problème à la racine.',
63
+ html: 'AVIF (AV1 Image File Format) est le format d\'image le plus efficace disponible aujourd\'hui. Développé par l\'Alliance for Open Media et basé sur le codec vidéo AV1, il offre une compression 50% meilleure que le JPG à la même qualité visuelle. Cependant, cet avantage technique a un prix: AVIF n\'est pas pris en charge par Photoshop, Lightroom, la visionneuse de photos Windows, ni par la plupart des outils d\'édition et de visualisation traditionnels. Convertir en JPG résout ce problème à la racine.',
74
64
  },
75
65
  {
76
66
  type: 'title',
77
- text: 'AVIF vs JPG : Efficacité versus Compatibilité Universelle',
67
+ text: 'AVIF vs JPG: Efficacité versus Compatibilité Universelle',
78
68
  level: 3,
79
69
  },
80
70
  {
81
71
  type: 'paragraph',
82
- html: 'AVIF compresse de manière extraordinaire : une photo haute résolution qui pèse 4 Mo en JPG peut peser à peine 2 Mo en AVIF à la même qualité perçue. Cela le rend parfait pour la livraison web où les performances de chargement sont critiques. Cependant, en dehors du navigateur, AVIF est pratiquement invisible : Windows Explorer ne le prévisualise pas, les appareils photo ne l\'exportent pas, et les designers ne l\'accueillent pas favorablement dans leurs flux de travail.',
72
+ html: 'AVIF compresse de manière extraordinaire: une photo haute résolution qui pèse 4 Mo en JPG peut peser à peine 2 Mo en AVIF à la même qualité perçue. Cela le rend parfait pour la livraison web où les performances de chargement sont critiques. Cependant, en dehors du navigateur, AVIF est pratiquement invisible: Windows Explorer ne le prévisualise pas, les appareils photo ne l\'exportent pas, et les designers ne l\'accueillent pas favorablement dans leurs flux de travail.',
83
73
  },
84
74
  {
85
75
  type: 'paragraph',
@@ -87,7 +77,7 @@ const seo: AvifAJpgLocaleContent['seo'] = [
87
77
  },
88
78
  {
89
79
  type: 'title',
90
- text: 'Comparatif : Convertisseurs Cloud vs Notre Architecture Locale',
80
+ text: 'Comparatif: Convertisseurs Cloud vs Notre Architecture Locale',
91
81
  level: 3,
92
82
  },
93
83
  {
@@ -135,11 +125,11 @@ const seo: AvifAJpgLocaleContent['seo'] = [
135
125
  {
136
126
  type: 'tip',
137
127
  title: 'L\'équilibre parfait entre compression et compatibilité',
138
- html: 'AVIF est 50% plus petit que JPG à qualité égale mais JPG a une compatibilité de 100% avec n\'importe quel logiciel. Si vous recevez des images AVIF du serveur et devez les éditer, les envoyer par email ou les ouvrir dans des outils de bureau, convertissez-les d\'abord en JPG.',
128
+ html: 'AVIF est 50% plus petit que JPG à qualité égale - mais JPG a une compatibilité de 100% avec n\'importe quel logiciel. Si vous recevez des images AVIF du serveur et devez les éditer, les envoyer par email ou les ouvrir dans des outils de bureau, convertissez-les d\'abord en JPG.',
139
129
  },
140
130
  {
141
131
  type: 'title',
142
- text: 'Cas d\'utilisation : quand convertir AVIF en JPG',
132
+ text: 'Cas d\'utilisation: quand convertir AVIF en JPG',
143
133
  level: 3,
144
134
  },
145
135
  {
@@ -150,17 +140,17 @@ const seo: AvifAJpgLocaleContent['seo'] = [
150
140
  'Partage d\'images avec des utilisateurs sur des appareils ou systèmes d\'exploitation anciens.',
151
141
  'Pièces jointes d\'emails pour garantir la visualisation dans n\'importe quel client de messagerie.',
152
142
  'Publication sur des plateformes qui n\'acceptent pas l\'AVIF comme format de téléversement.',
153
- 'Impression : les services d\'impression professionnels travaillent nativement avec le JPG.',
143
+ 'Impression: les services d\'impression professionnels travaillent nativement avec le JPG.',
154
144
  ],
155
145
  },
156
146
  {
157
147
  type: 'title',
158
- text: 'Conclusion : La compatibilité universelle en une seule étape',
148
+ text: 'Conclusion: La compatibilité universelle en une seule étape',
159
149
  level: 3,
160
150
  },
161
151
  {
162
152
  type: 'paragraph',
163
- html: 'AVIF est l\'avenir de la compression d\'images, mais le présent exige de la compatibilité. Notre convertisseur vous permet d\'obtenir le meilleur des deux mondes : recevez et stockez des images en AVIF pour une efficacité maximale, et convertissez-les instantanément en JPG quand vous avez besoin qu\'elles fonctionnent dans n\'importe quel contexte. Le tout en toute confidentialité, gratuitement et sans limites.',
153
+ html: 'AVIF est l\'avenir de la compression d\'images, mais le présent exige de la compatibilité. Notre convertisseur vous permet d\'obtenir le meilleur des deux mondes: recevez et stockez des images en AVIF pour une efficacité maximale, et convertissez-les instantanément en JPG quand vous avez besoin qu\'elles fonctionnent dans n\'importe quel contexte. Le tout en toute confidentialité, gratuitement et sans limites.',
164
154
  },
165
155
  ];
166
156
 
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ImageConverterUI } from '../../../shared/ImageConverter.astro';
2
3
  import { generateSchemas } from '../../../shared/logic/schemas';
3
4
  import type { AvifAJpgLocaleContent } from '../index';
@@ -51,17 +52,6 @@ const howTo: AvifAJpgLocaleContent['howTo'] = [
51
52
  },
52
53
  ];
53
54
 
54
- const bibliography: AvifAJpgLocaleContent['bibliography'] = [
55
- {
56
- name: 'AV1 Image File Format (AVIF)',
57
- url: 'https://aomediacodec.github.io/av1-avif/',
58
- },
59
- {
60
- name: 'Informasi Umum JPG',
61
- url: 'https://en.wikipedia.org/wiki/JPEG',
62
- },
63
- ];
64
-
65
55
  const seo: AvifAJpgLocaleContent['seo'] = [
66
56
  {
67
57
  type: 'title',
@@ -135,7 +125,7 @@ const seo: AvifAJpgLocaleContent['seo'] = [
135
125
  {
136
126
  type: 'tip',
137
127
  title: 'Keseimbangan sempurna antara kompresi dan kompatibilitas',
138
- html: 'AVIF 50% lebih kecil dari JPG untuk kualitas yang sama tetapi JPG memiliki kompatibilitas 100% dengan perangkat lunak apa pun. Jika Anda menerima gambar AVIF dari server dan perlu mengeditnya, mengirimnya melalui email, atau membukanya di alat desktop, ubah ke JPG terlebih dahulu.',
128
+ html: 'AVIF 50% lebih kecil dari JPG untuk kualitas yang sama - tetapi JPG memiliki kompatibilitas 100% dengan perangkat lunak apa pun. Jika Anda menerima gambar AVIF dari server dan perlu mengeditnya, mengirimnya melalui email, atau membukanya di alat desktop, ubah ke JPG terlebih dahulu.',
139
129
  },
140
130
  {
141
131
  type: 'title',
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ImageConverterUI } from '../../../shared/ImageConverter.astro';
2
3
  import { generateSchemas } from '../../../shared/logic/schemas';
3
4
  import type { AvifAJpgLocaleContent } from '../index';
@@ -51,17 +52,6 @@ const howTo: AvifAJpgLocaleContent['howTo'] = [
51
52
  },
52
53
  ];
53
54
 
54
- const bibliography: AvifAJpgLocaleContent['bibliography'] = [
55
- {
56
- name: 'AV1 Image File Format (AVIF)',
57
- url: 'https://aomediacodec.github.io/av1-avif/',
58
- },
59
- {
60
- name: 'Informazioni Generali sul JPG',
61
- url: 'https://en.wikipedia.org/wiki/JPEG',
62
- },
63
- ];
64
-
65
55
  const seo: AvifAJpgLocaleContent['seo'] = [
66
56
  {
67
57
  type: 'title',
@@ -135,7 +125,7 @@ const seo: AvifAJpgLocaleContent['seo'] = [
135
125
  {
136
126
  type: 'tip',
137
127
  title: 'L\'equilibrio perfetto tra compressione e compatibilità',
138
- html: 'L\'AVIF è più piccolo del 50% rispetto al JPG a parità di qualità ma il JPG ha una compatibilità del 100% con qualsiasi software. Se ricevi immagini AVIF dal server e hai bisogno di modificarle, inviarle via email o aprirle in strumenti desktop, convertile prima in JPG.',
128
+ html: 'L\'AVIF è più piccolo del 50% rispetto al JPG a parità di qualità - ma il JPG ha una compatibilità del 100% con qualsiasi software. Se ricevi immagini AVIF dal server e hai bisogno di modificarle, inviarle via email o aprirle in strumenti desktop, convertile prima in JPG.',
139
129
  },
140
130
  {
141
131
  type: 'title',