Quellcode durchsuchen

Add current English text to DIRTY translation comments (#9525)

* Add English references to DIRTY translation comments

Translators currently have to find the English source separately for each `// DIRTY` entry. The translation formatter now appends it on the same line, including English plural fallback for languages with additional plural forms. It keeps hints on one safe PHP comment line and refreshes them when English changes.

Only comments change in the locale files; translation values and plural rules are unchanged. Single-form translations show both English forms as `Singular: … | Plural: …`. Generated README contribution links match bare and annotated TODO/DIRTY markers and retain the end-of-line anchor.

Closes #9463.

* Show both English forms for single-form translations
Jgolding1998 vor 9 Stunden
Ursprung
Commit
d03296f074
72 geänderte Dateien mit 399 neuen und 264 gelöschten Zeilen
  1. 1 0
      CREDITS.md
  2. 31 31
      README.fr.md
  3. 31 31
      README.md
  4. 1 1
      app/i18n/ar/admin.php
  5. 1 1
      app/i18n/az/admin.php
  6. 2 2
      app/i18n/cs/admin.php
  7. 5 5
      app/i18n/cs/conf.php
  8. 3 3
      app/i18n/cs/feedback.php
  9. 7 7
      app/i18n/cs/gen.php
  10. 3 3
      app/i18n/cs/sub.php
  11. 1 1
      app/i18n/el/admin.php
  12. 2 2
      app/i18n/el/conf.php
  13. 5 5
      app/i18n/el/gen.php
  14. 1 1
      app/i18n/es/admin.php
  15. 2 2
      app/i18n/fa/admin.php
  16. 2 2
      app/i18n/fa/conf.php
  17. 5 5
      app/i18n/fa/gen.php
  18. 1 1
      app/i18n/fi/admin.php
  19. 2 2
      app/i18n/fi/conf.php
  20. 5 5
      app/i18n/fi/gen.php
  21. 2 2
      app/i18n/he/admin.php
  22. 5 5
      app/i18n/he/conf.php
  23. 4 4
      app/i18n/he/feedback.php
  24. 3 3
      app/i18n/he/gen.php
  25. 1 1
      app/i18n/he/sub.php
  26. 2 2
      app/i18n/hu/conf.php
  27. 5 5
      app/i18n/hu/gen.php
  28. 1 1
      app/i18n/id/admin.php
  29. 2 2
      app/i18n/ja/admin.php
  30. 1 1
      app/i18n/ja/gen.php
  31. 4 4
      app/i18n/ko/admin.php
  32. 5 5
      app/i18n/ko/conf.php
  33. 3 3
      app/i18n/ko/gen.php
  34. 2 2
      app/i18n/ko/sub.php
  35. 1 1
      app/i18n/lt/admin.php
  36. 3 3
      app/i18n/lv/admin.php
  37. 5 5
      app/i18n/lv/conf.php
  38. 3 3
      app/i18n/lv/feedback.php
  39. 7 7
      app/i18n/lv/gen.php
  40. 2 2
      app/i18n/lv/sub.php
  41. 1 1
      app/i18n/oc/admin.php
  42. 5 5
      app/i18n/oc/conf.php
  43. 3 3
      app/i18n/oc/feedback.php
  44. 3 3
      app/i18n/oc/gen.php
  45. 4 4
      app/i18n/oc/sub.php
  46. 1 1
      app/i18n/pt-BR/admin.php
  47. 1 1
      app/i18n/ru/admin.php
  48. 2 2
      app/i18n/ru/conf.php
  49. 7 7
      app/i18n/ru/gen.php
  50. 2 2
      app/i18n/sk/admin.php
  51. 5 5
      app/i18n/sk/conf.php
  52. 7 7
      app/i18n/sk/gen.php
  53. 8 8
      app/i18n/sk/sub.php
  54. 1 1
      app/i18n/tr/admin.php
  55. 2 2
      app/i18n/tr/conf.php
  56. 5 5
      app/i18n/tr/gen.php
  57. 1 1
      app/i18n/uk/admin.php
  58. 2 2
      app/i18n/uk/conf.php
  59. 7 7
      app/i18n/uk/gen.php
  60. 3 3
      app/i18n/zh-CN/admin.php
  61. 2 2
      app/i18n/zh-CN/api.php
  62. 1 1
      app/i18n/zh-CN/conf.php
  63. 1 1
      app/i18n/zh-CN/feedback.php
  64. 5 5
      app/i18n/zh-CN/gen.php
  65. 6 6
      app/i18n/zh-CN/sub.php
  66. 2 2
      app/i18n/zh-TW/admin.php
  67. 1 1
      app/i18n/zh-TW/gen.php
  68. 1 1
      cli/check.translation.php
  69. 29 4
      cli/i18n/I18nFile.php
  70. 4 0
      cli/i18n/I18nValue.php
  71. 4 0
      docs/en/internationalization.md
  72. 101 0
      tests/cli/i18n/I18nFileTest.php

+ 1 - 0
CREDITS.md

@@ -180,6 +180,7 @@ People are sorted by name so please keep this order.
 * [Julien Reichardt](https://github.com/j8r): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:j8r)
 * [Julien Reichardt](https://github.com/j8r): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:j8r)
 * [Julien-Pierre Avérous](https://github.com/javerous): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:javerous), [Web](https://www.sourcemac.fr/)
 * [Julien-Pierre Avérous](https://github.com/javerous): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:javerous), [Web](https://www.sourcemac.fr/)
 * [junkfood](https://github.com/JunkFood02): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:JunkFood02)
 * [junkfood](https://github.com/JunkFood02): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:JunkFood02)
+* [Justin Golding](https://github.com/Jgolding1998): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Jgolding1998)
 * [Justin Tracey](https://github.com/jtracey): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:jtracey), [Web](https://unsuspicious.click)
 * [Justin Tracey](https://github.com/jtracey): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:jtracey), [Web](https://unsuspicious.click)
 * [Kachelkaiser](https://github.com/Kachelkaiser): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Kachelkaiser)
 * [Kachelkaiser](https://github.com/Kachelkaiser): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Kachelkaiser)
 * [Kaibin Yang](https://github.com/ghost): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:SkyYkb), [Web](https://kaibinyang.com/)
 * [Kaibin Yang](https://github.com/ghost): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:SkyYkb), [Web](https://kaibinyang.com/)

+ 31 - 31
README.fr.md

@@ -236,37 +236,37 @@ Voir le [dépôt dédié à ces extensions](https://github.com/FreshRSS/Extensio
 
 
 | Langage | Progression | |
 | Langage | Progression | |
 | - | - | - |
 | - | - | - |
-| العربية (ar) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Far+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Azərbaycanca (az) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Faz+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Беларуская (be) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fbe+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Čeština (cs) | ■■■■■■■■・・ 81% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fcs+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Deutsch (de) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fde+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Ελληνικά (el) | ■■■■■■■・・・ 75% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fel+%2F%28TODO%7CDIRTY%29%24%2F) |
-| English (en) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen+%2F%28TODO%7CDIRTY%29%24%2F) |
-| English (United States) (en-US) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen-US+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Español (es) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
-| فارسی (fa) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Suomi (fi) | ■■■■■■■■■・ 91% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Français (fr) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
-| עברית (he) | ■■■■・・・・・・ 41% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Magyar (hu) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Bahasa Indonesia (id) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Italiano (it) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 日本語 (ja) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fja+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 한국어 (ko) | ■■■■■■■■・・ 80% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fko+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Lietuvių (lt) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Latviešu (lv) | ■■■■■■■■・・ 81% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flv+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Nederlands (nl) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fnl+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Occitan (oc) | ■■■■■■■・・・ 73% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Polski (pl) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpl+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Português (Brasil) (pt-BR) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-BR+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Português (Portugal) (pt-PT) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-PT+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Русский (ru) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fru+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Slovenčina (sk) | ■■■■■■■■・・ 80% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fsk+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Türkçe (tr) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ftr+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Українська (uk) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fuk+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 简体中文 (zh-CN) | ■■■■■■■■■・ 95% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-CN+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 正體中文 (zh-TW) | ■■■■■■■■■・ 97% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-TW+%2F%28TODO%7CDIRTY%29%24%2F) |
+| العربية (ar) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Far+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Azərbaycanca (az) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Faz+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Беларуская (be) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fbe+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Čeština (cs) | ■■■■■■■■・・ 81% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fcs+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Deutsch (de) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fde+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Ελληνικά (el) | ■■■■■■■・・・ 75% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fel+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| English (en) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| English (United States) (en-US) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen-US+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Español (es) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| فارسی (fa) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Suomi (fi) | ■■■■■■■■■・ 91% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Français (fr) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| עברית (he) | ■■■■・・・・・・ 41% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Magyar (hu) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Bahasa Indonesia (id) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Italiano (it) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 日本語 (ja) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fja+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 한국어 (ko) | ■■■■■■■■・・ 80% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fko+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Lietuvių (lt) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Latviešu (lv) | ■■■■■■■■・・ 81% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flv+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Nederlands (nl) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fnl+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Occitan (oc) | ■■■■■■■・・・ 73% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Polski (pl) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpl+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Português (Brasil) (pt-BR) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-BR+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Português (Portugal) (pt-PT) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-PT+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Русский (ru) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fru+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Slovenčina (sk) | ■■■■■■■■・・ 80% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fsk+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Türkçe (tr) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ftr+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Українська (uk) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fuk+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 简体中文 (zh-CN) | ■■■■■■■■■・ 95% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-CN+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 正體中文 (zh-TW) | ■■■■■■■■■・ 97% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-TW+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
 
 
 </translations>
 </translations>
 
 

+ 31 - 31
README.md

@@ -132,37 +132,37 @@ See the [repository dedicated to those extensions](https://github.com/FreshRSS/E
 
 
 | Language | Progress | |
 | Language | Progress | |
 | - | - | - |
 | - | - | - |
-| العربية (ar) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Far+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Azərbaycanca (az) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Faz+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Беларуская (be) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fbe+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Čeština (cs) | ■■■■■■■■・・ 81% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fcs+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Deutsch (de) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fde+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Ελληνικά (el) | ■■■■■■■・・・ 75% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fel+%2F%28TODO%7CDIRTY%29%24%2F) |
-| English (en) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen+%2F%28TODO%7CDIRTY%29%24%2F) |
-| English (United States) (en-US) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen-US+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Español (es) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
-| فارسی (fa) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Suomi (fi) | ■■■■■■■■■・ 91% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Français (fr) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
-| עברית (he) | ■■■■・・・・・・ 41% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Magyar (hu) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Bahasa Indonesia (id) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Italiano (it) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 日本語 (ja) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fja+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 한국어 (ko) | ■■■■■■■■・・ 80% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fko+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Lietuvių (lt) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Latviešu (lv) | ■■■■■■■■・・ 81% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flv+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Nederlands (nl) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fnl+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Occitan (oc) | ■■■■■■■・・・ 73% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Polski (pl) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpl+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Português (Brasil) (pt-BR) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-BR+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Português (Portugal) (pt-PT) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-PT+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Русский (ru) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fru+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Slovenčina (sk) | ■■■■■■■■・・ 80% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fsk+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Türkçe (tr) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ftr+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Українська (uk) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fuk+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 简体中文 (zh-CN) | ■■■■■■■■■・ 95% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-CN+%2F%28TODO%7CDIRTY%29%24%2F) |
-| 正體中文 (zh-TW) | ■■■■■■■■■・ 97% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-TW+%2F%28TODO%7CDIRTY%29%24%2F) |
+| العربية (ar) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Far+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Azərbaycanca (az) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Faz+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Беларуская (be) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fbe+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Čeština (cs) | ■■■■■■■■・・ 81% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fcs+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Deutsch (de) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fde+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Ελληνικά (el) | ■■■■■■■・・・ 75% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fel+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| English (en) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| English (United States) (en-US) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen-US+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Español (es) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| فارسی (fa) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Suomi (fi) | ■■■■■■■■■・ 91% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Français (fr) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| עברית (he) | ■■■■・・・・・・ 41% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Magyar (hu) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Bahasa Indonesia (id) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Italiano (it) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 日本語 (ja) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fja+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 한국어 (ko) | ■■■■■■■■・・ 80% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fko+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Lietuvių (lt) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Latviešu (lv) | ■■■■■■■■・・ 81% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flv+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Nederlands (nl) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fnl+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Occitan (oc) | ■■■■■■■・・・ 73% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Polski (pl) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpl+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Português (Brasil) (pt-BR) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-BR+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Português (Portugal) (pt-PT) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-PT+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Русский (ru) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fru+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Slovenčina (sk) | ■■■■■■■■・・ 80% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fsk+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Türkçe (tr) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ftr+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| Українська (uk) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fuk+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 简体中文 (zh-CN) | ■■■■■■■■■・ 95% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-CN+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
+| 正體中文 (zh-TW) | ■■■■■■■■■・ 97% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fzh-TW+%2F%28TODO%7CDIRTY%29%28+-+.%2A%29%3F%24%2F) |
 
 
 </translations>
 </translations>
 
 

+ 1 - 1
app/i18n/ar/admin.php

@@ -15,7 +15,7 @@ return array(
 		'allow_anonymous' => 'السماح بالقراءة المجهولة لمقالات المستخدم الافتراضي (%s)',
 		'allow_anonymous' => 'السماح بالقراءة المجهولة لمقالات المستخدم الافتراضي (%s)',
 		'allow_anonymous_refresh' => 'السماح بالتحديث المجهول للمقالات',
 		'allow_anonymous_refresh' => 'السماح بالتحديث المجهول للمقالات',
 		'api_enabled' => 'السماح بالوصول إلى <abbr>API</abbr> <small>(مطلوب لتطبيقات الجوال ومشاركة استعلامات المستخدم)</small>',
 		'api_enabled' => 'السماح بالوصول إلى <abbr>API</abbr> <small>(مطلوب لتطبيقات الجوال ومشاركة استعلامات المستخدم)</small>',
-		'form' => 'نموذج الويب (تقليدي، يتطلب JavaScript)',	// DIRTY
+		'form' => 'نموذج الويب (تقليدي، يتطلب JavaScript)',	// DIRTY - Web form (traditional)
 		'http' => 'HTTP (متقدم: يديره خادم الويب، OIDC، SSO…)',
 		'http' => 'HTTP (متقدم: يديره خادم الويب، OIDC، SSO…)',
 		'none' => 'لا شيء (خطير)',
 		'none' => 'لا شيء (خطير)',
 		'title' => 'المصادقة',
 		'title' => 'المصادقة',

+ 1 - 1
app/i18n/az/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Buraxılış kanalı',
 			'_' => 'Buraxılış kanalı',
 			'edge' => 'Davamlı buraxılış (“edge”)',
 			'edge' => 'Davamlı buraxılış (“edge”)',
-			'latest' => 'Stabil buraxılış (“latest”)',	// DIRTY
+			'latest' => 'Stabil buraxılış (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'FreshRSS-i yenilə',
 		'title' => 'FreshRSS-i yenilə',
 		'viaGit' => 'git və GitHub.com vasitəsilə yeniləmə başladı',
 		'viaGit' => 'git və GitHub.com vasitəsilə yeniləmə başladı',

+ 2 - 2
app/i18n/cs/admin.php

@@ -14,7 +14,7 @@ return array(
 	'auth' => array(
 	'auth' => array(
 		'allow_anonymous' => 'Povolit anonymní čtení článků výchozího uživatele (%s)',
 		'allow_anonymous' => 'Povolit anonymní čtení článků výchozího uživatele (%s)',
 		'allow_anonymous_refresh' => 'Povolit anonymní obnovení článků',
 		'allow_anonymous_refresh' => 'Povolit anonymní obnovení článků',
-		'api_enabled' => 'Povolit přístup k <abbr>API</abbr> <small>(vyžadováno pro mobilní aplikace and sharing user queries)</small>',	// DIRTY
+		'api_enabled' => 'Povolit přístup k <abbr>API</abbr> <small>(vyžadováno pro mobilní aplikace and sharing user queries)</small>',	// DIRTY - Allow <abbr>API</abbr> access <small>(required for mobile apps and sharing user queries)</small>
 		'form' => 'Webový formulář (tradiční)',
 		'form' => 'Webový formulář (tradiční)',
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'none' => 'Žádný (nebezpečné)',
 		'none' => 'Žádný (nebezpečné)',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Kanál pro vydání',
 			'_' => 'Kanál pro vydání',
 			'edge' => 'Vydání "Rolling" / Nepřetržitě aktualizované vydání (“edge”)',
 			'edge' => 'Vydání "Rolling" / Nepřetržitě aktualizované vydání (“edge”)',
-			'latest' => 'Stabilní vydání (“latest”)',	// DIRTY
+			'latest' => 'Stabilní vydání (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Aktualizovat systém',
 		'title' => 'Aktualizovat systém',
 		'viaGit' => 'Aktualizace přes git a GitHub.com začala',
 		'viaGit' => 'Aktualizace přes git a GitHub.com začala',

+ 5 - 5
app/i18n/cs/conf.php

@@ -144,7 +144,7 @@ return array(
 		),
 		),
 	),
 	),
 	'mark_read_button' => array(
 	'mark_read_button' => array(
-		'_' => '„označit vše jako přečtené“ button',	// DIRTY
+		'_' => '„označit vše jako přečtené“ button',	// DIRTY - “Mark all as read” button
 		'big' => 'Big',	// TODO
 		'big' => 'Big',	// TODO
 		'none' => 'None',	// TODO
 		'none' => 'None',	// TODO
 		'small' => 'Small',	// TODO
 		'small' => 'Small',	// TODO
@@ -283,13 +283,13 @@ return array(
 			'article_viewed' => 'když je článek zobrazen',
 			'article_viewed' => 'když je článek zobrazen',
 			'focus' => 'při zaměření (kromě důležitých kanálů)',
 			'focus' => 'při zaměření (kromě důležitých kanálů)',
 			'keep_max_n_unread' => 'Maximální počet článků, které ponechat jako nepřečtené',
 			'keep_max_n_unread' => 'Maximální počet článků, které ponechat jako nepřečtené',
-			'scroll' => 'během posouvání (kromě důležitých kanálů)',	// DIRTY
+			'scroll' => 'během posouvání (kromě důležitých kanálů)',	// DIRTY - while scrolling (except for important feeds)
 			'upon_gone' => 'když se již nenachází v upstreamu zpráv.',
 			'upon_gone' => 'když se již nenachází v upstreamu zpráv.',
 			'upon_reception' => 'po obdržení článku',
 			'upon_reception' => 'po obdržení článku',
 			'when' => 'Označit článek jako přečtený…',
 			'when' => 'Označit článek jako přečtený…',
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
-			'when_same_title_in_feed' => 'když shodný název již existuje v top <i>n</i> nejnovějších článcích (of the feed)',	// DIRTY
+			'when_same_title_in_feed' => 'když shodný název již existuje v top <i>n</i> nejnovějších článcích (of the feed)',	// DIRTY - if an identical title already exists in the top <i>n</i> newest articles of the feed
 		),
 		),
 		'show' => array(
 		'show' => array(
 			'_' => 'Počet zobrazených článků',
 			'_' => 'Počet zobrazených článků',
@@ -308,8 +308,8 @@ return array(
 			'when' => 'Mark an article as favourite…',	// TODO
 			'when' => 'Mark an article as favourite…',	// TODO
 		),
 		),
 		'sticky_post' => 'Při otevření připnout článek na začátek',
 		'sticky_post' => 'Při otevření připnout článek na začátek',
-		'sticky_sort' => 'Zachovat ruční řazení při navigaci',	// DIRTY
-		'sticky_sort_help' => 'Určuje, zda se zachová poslední ruční řazení, nebo zda každá kategorie či kanál vždy použije vlastní výchozí nebo globální nastavení.',	// DIRTY
+		'sticky_sort' => 'Zachovat ruční řazení při navigaci',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Určuje, zda se zachová poslední ruční řazení, nebo zda každá kategorie či kanál vždy použije vlastní výchozí nebo globální nastavení.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Čtení',
 		'title' => 'Čtení',
 		'view' => array(
 		'view' => array(
 			'default' => 'Výchozí zobrazení',
 			'default' => 'Výchozí zobrazení',

+ 3 - 3
app/i18n/cs/feedback.php

@@ -58,11 +58,11 @@ return array(
 	),
 	),
 	'import_export' => array(
 	'import_export' => array(
 		'export_no_zip_extension' => 'Na serveru není přítomno rozšíření ZIP. Zkuste exportovat soubory jeden po druhém.',
 		'export_no_zip_extension' => 'Na serveru není přítomno rozšíření ZIP. Zkuste exportovat soubory jeden po druhém.',
-		'feeds_imported' => 'Vaše kanály byly naimportovány a budou nyní aktualizovány / Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
-		'feeds_imported_with_errors' => 'Vaše kanály byly naimportovány, došlo ale k nějakým chybám / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
+		'feeds_imported' => 'Vaše kanály byly naimportovány a budou nyní aktualizovány / Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.
+		'feeds_imported_with_errors' => 'Vaše kanály byly naimportovány, došlo ale k nějakým chybám / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.
 		'file_cannot_be_uploaded' => 'Soubor nelze nahrát!',
 		'file_cannot_be_uploaded' => 'Soubor nelze nahrát!',
 		'no_zip_extension' => 'Na serveru není přítomno rozšíření ZIP.',
 		'no_zip_extension' => 'Na serveru není přítomno rozšíření ZIP.',
-		'zip_error' => 'Během importu ZIP došlo k chybě.',	// DIRTY
+		'zip_error' => 'Během importu ZIP došlo k chybě.',	// DIRTY - An error occurred during ZIP processing.
 	),
 	),
 	'profile' => array(
 	'profile' => array(
 		'error' => 'Váš profil nelze změnit',
 		'error' => 'Váš profil nelze změnit',

+ 7 - 7
app/i18n/cs/gen.php

@@ -180,14 +180,14 @@ return array(
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'Ve FreshRSS je %d nových článků k přečtení.',	// DIRTY
-				1 => 'Ve FreshRSS je %d nových článků k přečtení.',	// DIRTY
-				2 => 'Ve FreshRSS je %d nových článků k přečtení.',	// DIRTY
+				0 => 'Ve FreshRSS je %d nových článků k přečtení.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'Ve FreshRSS je %d nových článků k přečtení.',	// DIRTY - There are %d new articles to read on FreshRSS.
+				2 => 'Ve FreshRSS je %d nových článků k přečtení.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(nepřečtené: %d)',	// DIRTY
-				1 => '(nepřečtené: %d)',	// DIRTY
-				2 => '(nepřečtené: %d)',	// DIRTY
+				0 => '(nepřečtené: %d)',	// DIRTY - (unread: %d)
+				1 => '(nepřečtené: %d)',	// DIRTY - (unread: %d)
+				2 => '(nepřečtené: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Požadavek selhal, to může být způsobeno problémy s připojení k internetu.',
 			'request_failed' => 'Požadavek selhal, to může být způsobeno problémy s připojení k internetu.',
 			'title_new_articles' => 'FreshRSS: nové články!',
 			'title_new_articles' => 'FreshRSS: nové články!',
@@ -315,7 +315,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Záložky',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Záložky',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 3 - 3
app/i18n/cs/sub.php

@@ -70,10 +70,10 @@ return array(
 		),
 		),
 		'description' => 'Popis',
 		'description' => 'Popis',
 		'empty' => 'Tento kanál je prázdný. Ověřte prosím, zda je stále udržován.',
 		'empty' => 'Tento kanál je prázdný. Ověřte prosím, zda je stále udržován.',
-		'error' => 'Vyskytl se problém s kanálem. Ověřte prosím, že je vždy dostupný.',	// DIRTY
+		'error' => 'Vyskytl se problém s kanálem. Ověřte prosím, že je vždy dostupný.',	// DIRTY - This feed has encountered a problem. If this situation persists, please verify that it is still reachable.
 		'export-as-opml' => array(
 		'export-as-opml' => array(
 			'download' => 'Stáhnout',
 			'download' => 'Stáhnout',
-			'help' => 'Soubor XML (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// DIRTY
+			'help' => 'Soubor XML (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// DIRTY - XML file (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)
 			'label' => 'Exportovat jako OPML',
 			'label' => 'Exportovat jako OPML',
 		),
 		),
 		'ext_favicon' => 'Set automatically',	// TODO
 		'ext_favicon' => 'Set automatically',	// TODO
@@ -155,7 +155,7 @@ return array(
 				'help' => 'JSON s tečkovým zápisem používá tečky mezi objekty a závorky pro pole. (e.g. <code>data.items[0].title</code>)',
 				'help' => 'JSON s tečkovým zápisem používá tečky mezi objekty a závorky pro pole. (e.g. <code>data.items[0].title</code>)',
 				'item' => array(
 				'item' => array(
 					'_' => 'vyhledávání nových <strong>položek</strong><br /><small>(nejdůležitější)</small>',
 					'_' => 'vyhledávání nových <strong>položek</strong><br /><small>(nejdůležitější)</small>',
-					'help' => 'JSON cesta k poli obsahujícímu položky, např.: <code>$</code> or <code>newsItems</code>',	// DIRTY
+					'help' => 'JSON cesta k poli obsahujícímu položky, např.: <code>$</code> or <code>newsItems</code>',	// DIRTY - JSON path to the array containing the items, e.g. <code>$</code> or <code>newsItems</code>
 				),
 				),
 				'item_author' => 'autor položky',
 				'item_author' => 'autor položky',
 				'item_categories' => 'štítky položky',
 				'item_categories' => 'štítky položky',

+ 1 - 1
app/i18n/el/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Κανάλι κυκλοφορίας',
 			'_' => 'Κανάλι κυκλοφορίας',
 			'edge' => 'Rolling release («edge»)',
 			'edge' => 'Rolling release («edge»)',
-			'latest' => 'Σταθερή κυκλοφορία («latest»)',	// DIRTY
+			'latest' => 'Σταθερή κυκλοφορία («latest»)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Ενημέρωση συστήματος',
 		'title' => 'Ενημέρωση συστήματος',
 		'viaGit' => 'Ξεκίνησε η ενημέρωση μέσω git και GitHub.com',
 		'viaGit' => 'Ξεκίνησε η ενημέρωση μέσω git και GitHub.com',

+ 2 - 2
app/i18n/el/conf.php

@@ -308,8 +308,8 @@ return array(
 			'when' => 'Σήμανση άρθρου ως αγαπημένου…',
 			'when' => 'Σήμανση άρθρου ως αγαπημένου…',
 		),
 		),
 		'sticky_post' => 'Καρφίτσωμα του άρθρου στην κορυφή όταν ανοίγεται',
 		'sticky_post' => 'Καρφίτσωμα του άρθρου στην κορυφή όταν ανοίγεται',
-		'sticky_sort' => 'Διατήρηση της χειροκίνητης ταξινόμησης κατά την πλοήγηση',	// DIRTY
-		'sticky_sort_help' => 'Καθορίζει αν διατηρείται η τελευταία χειροκίνητη ταξινόμηση ή αν κάθε κατηγορία ή ροή χρησιμοποιεί πάντα τη δική της προεπιλεγμένη ή καθολική ρύθμιση.',	// DIRTY
+		'sticky_sort' => 'Διατήρηση της χειροκίνητης ταξινόμησης κατά την πλοήγηση',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Καθορίζει αν διατηρείται η τελευταία χειροκίνητη ταξινόμηση ή αν κάθε κατηγορία ή ροή χρησιμοποιεί πάντα τη δική της προεπιλεγμένη ή καθολική ρύθμιση.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Ανάγνωση',
 		'title' => 'Ανάγνωση',
 		'view' => array(
 		'view' => array(
 			'default' => 'Προεπιλεγμένη προβολή',
 			'default' => 'Προεπιλεγμένη προβολή',

+ 5 - 5
app/i18n/el/gen.php

@@ -174,12 +174,12 @@ return array(
 		'confirm_exit_slider' => 'Είστε σίγουροι οτι δεν θέλετε να αποθηκεύσετε τις τρέχουσες αλλαγές σας;',
 		'confirm_exit_slider' => 'Είστε σίγουροι οτι δεν θέλετε να αποθηκεύσετε τις τρέχουσες αλλαγές σας;',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'Υπάρχουν %d νέα άρθρα να διαβάσετε στο FreshRSS.',	// DIRTY
-				1 => 'Υπάρχουν %d νέα άρθρα να διαβάσετε στο FreshRSS.',	// DIRTY
+				0 => 'Υπάρχουν %d νέα άρθρα να διαβάσετε στο FreshRSS.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'Υπάρχουν %d νέα άρθρα να διαβάσετε στο FreshRSS.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(αδιάβαστα: %d)',	// DIRTY
-				1 => '(αδιάβαστα: %d)',	// DIRTY
+				0 => '(αδιάβαστα: %d)',	// DIRTY - (unread: %d)
+				1 => '(αδιάβαστα: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Η αναζήτηση απέτυχε, ελέγξτε για πιθανά προβλήματα με την σύνδεση σας.',
 			'request_failed' => 'Η αναζήτηση απέτυχε, ελέγξτε για πιθανά προβλήματα με την σύνδεση σας.',
 			'title_new_articles' => 'FreshRSS: Νέα άρθρα!',
 			'title_new_articles' => 'FreshRSS: Νέα άρθρα!',
@@ -307,7 +307,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Σελιδοδείκτες',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Σελιδοδείκτες',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 1 - 1
app/i18n/es/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Canal de publicación',
 			'_' => 'Canal de publicación',
 			'edge' => 'Versión de pruebas (“edge”)',
 			'edge' => 'Versión de pruebas (“edge”)',
-			'latest' => 'Versión estable (“latest”)',	// DIRTY
+			'latest' => 'Versión estable (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Actualizar sistema',
 		'title' => 'Actualizar sistema',
 		'viaGit' => 'Actualización comenzada vía git y GitHub',
 		'viaGit' => 'Actualización comenzada vía git y GitHub',

+ 2 - 2
app/i18n/fa/admin.php

@@ -15,7 +15,7 @@ return array(
 		'allow_anonymous' => ' اجازه خواندن ناشناس مقالات کاربر پیش‌فرض (%s)',
 		'allow_anonymous' => ' اجازه خواندن ناشناس مقالات کاربر پیش‌فرض (%s)',
 		'allow_anonymous_refresh' => ' اجازه بازخوانی ناشناس مقالات را بدهید',
 		'allow_anonymous_refresh' => ' اجازه بازخوانی ناشناس مقالات را بدهید',
 		'api_enabled' => 'اجازه دسترسی به <abbr>API</abbr> <small>(برای برنامه‌های موبایل و اشتراک‌گذاری پرس‌وجوهای کاربر لازم است)</small>',
 		'api_enabled' => 'اجازه دسترسی به <abbr>API</abbr> <small>(برای برنامه‌های موبایل و اشتراک‌گذاری پرس‌وجوهای کاربر لازم است)</small>',
-		'form' => 'فرم وب (سنتی، نیازمند JavaScript)',	// DIRTY
+		'form' => 'فرم وب (سنتی، نیازمند JavaScript)',	// DIRTY - Web form (traditional)
 		'http' => 'HTTP (پیشرفته: مدیریت‌شده توسط وب‌سرور، OIDC، SSO و...)',
 		'http' => 'HTTP (پیشرفته: مدیریت‌شده توسط وب‌سرور، OIDC، SSO و...)',
 		'none' => ' هیچ (خطرناک)',
 		'none' => ' هیچ (خطرناک)',
 		'title' => ' احراز هویت',
 		'title' => ' احراز هویت',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => ' کانال انتشار',
 			'_' => ' کانال انتشار',
 			'edge' => 'انتشار پیوسته («edge»)',
 			'edge' => 'انتشار پیوسته («edge»)',
-			'latest' => 'انتشار پایدار («latest»)',	// DIRTY
+			'latest' => 'انتشار پایدار («latest»)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'به‌روزرسانی FreshRSS',
 		'title' => 'به‌روزرسانی FreshRSS',
 		'viaGit' => 'به‌روزرسانی از طریق git و GitHub.com شروع شد',
 		'viaGit' => 'به‌روزرسانی از طریق git و GitHub.com شروع شد',

+ 2 - 2
app/i18n/fa/conf.php

@@ -308,8 +308,8 @@ return array(
 			'when' => 'یک مطلب را به عنوان مورد علاقه علامت‌گذاری کن...',
 			'when' => 'یک مطلب را به عنوان مورد علاقه علامت‌گذاری کن...',
 		),
 		),
 		'sticky_post' => ' وقتی باز شد مقاله را به بالا بچسبانید',
 		'sticky_post' => ' وقتی باز شد مقاله را به بالا بچسبانید',
-		'sticky_sort' => 'حفظ ترتیب مرتب‌سازی دستی هنگام پیمایش',	// DIRTY
-		'sticky_sort_help' => 'تعیین می‌کند که آیا آخرین ترتیب مرتب‌سازی دستی فعال بماند یا هر دسته یا خوراک همیشه از تنظیم پیش‌فرض یا سراسری خود استفاده کند.',	// DIRTY
+		'sticky_sort' => 'حفظ ترتیب مرتب‌سازی دستی هنگام پیمایش',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'تعیین می‌کند که آیا آخرین ترتیب مرتب‌سازی دستی فعال بماند یا هر دسته یا خوراک همیشه از تنظیم پیش‌فرض یا سراسری خود استفاده کند.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => ' خواندن',
 		'title' => ' خواندن',
 		'view' => array(
 		'view' => array(
 			'default' => ' نمای پیش فرض',
 			'default' => ' نمای پیش فرض',

+ 5 - 5
app/i18n/fa/gen.php

@@ -174,12 +174,12 @@ return array(
 		'confirm_exit_slider' => 'مطمئنید می‌خواهید تنظیمات ذخیره‌نشده را نادیده بگیرید؟',
 		'confirm_exit_slider' => 'مطمئنید می‌خواهید تنظیمات ذخیره‌نشده را نادیده بگیرید؟',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => ' %d مقاله جدید برای خواندن در FreshRSS وجود دارد.',	// DIRTY
-				1 => ' %d مقاله جدید برای خواندن در FreshRSS وجود دارد.',	// DIRTY
+				0 => ' %d مقاله جدید برای خواندن در FreshRSS وجود دارد.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => ' %d مقاله جدید برای خواندن در FreshRSS وجود دارد.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => ' (خوانده نشده: %d)',	// DIRTY
-				1 => ' (خوانده نشده: %d)',	// DIRTY
+				0 => ' (خوانده نشده: %d)',	// DIRTY - (unread: %d)
+				1 => ' (خوانده نشده: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => ' یک درخواست شکست خورده است',
 			'request_failed' => ' یک درخواست شکست خورده است',
 			'title_new_articles' => ' FreshRSS: مقالات جدید!',
 			'title_new_articles' => ' FreshRSS: مقالات جدید!',
@@ -307,7 +307,7 @@ return array(
 		'linkedin' => ' لینکدین',
 		'linkedin' => ' لینکدین',
 		'mastodon' => ' ماستودون',
 		'mastodon' => ' ماستودون',
 		'movim' => ' Movim',
 		'movim' => ' Movim',
-		'nextcloud-bookmarks' => 'Nextcloud نشانک‌ها',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud نشانک‌ها',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => ' همه چیزخوار',
 		'omnivore' => ' همه چیزخوار',
 		'pinboard' => ' پینبرد',
 		'pinboard' => ' پینبرد',
 		'pinterest' => ' پینترست',
 		'pinterest' => ' پینترست',

+ 1 - 1
app/i18n/fi/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Julkaisukanava',
 			'_' => 'Julkaisukanava',
 			'edge' => 'Uusin versio (“edge”)',
 			'edge' => 'Uusin versio (“edge”)',
-			'latest' => 'Vakaa versio (“latest”)',	// DIRTY
+			'latest' => 'Vakaa versio (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Päivitä FreshRSS',
 		'title' => 'Päivitä FreshRSS',
 		'viaGit' => 'Päivitys gitin ja GitHub.comin avulla on aloitettu',
 		'viaGit' => 'Päivitys gitin ja GitHub.comin avulla on aloitettu',

+ 2 - 2
app/i18n/fi/conf.php

@@ -308,8 +308,8 @@ return array(
 			'when' => 'Merkitse artikkeli suosikiksi…',
 			'when' => 'Merkitse artikkeli suosikiksi…',
 		),
 		),
 		'sticky_post' => 'Kiinnitä artikkeli ylimmäksi avattaessa',
 		'sticky_post' => 'Kiinnitä artikkeli ylimmäksi avattaessa',
-		'sticky_sort' => 'Säilytä manuaalinen lajittelujärjestys navigoinnin aikana',	// DIRTY
-		'sticky_sort_help' => 'Määrittää, pysyykö viimeisin manuaalinen lajittelu käytössä vai käyttääkö jokainen luokka tai syöte aina omaa oletus- tai yleisasetustaan.',	// DIRTY
+		'sticky_sort' => 'Säilytä manuaalinen lajittelujärjestys navigoinnin aikana',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Määrittää, pysyykö viimeisin manuaalinen lajittelu käytössä vai käyttääkö jokainen luokka tai syöte aina omaa oletus- tai yleisasetustaan.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Lukeminen',
 		'title' => 'Lukeminen',
 		'view' => array(
 		'view' => array(
 			'default' => 'Oletusnäkymä',
 			'default' => 'Oletusnäkymä',

+ 5 - 5
app/i18n/fi/gen.php

@@ -174,12 +174,12 @@ return array(
 		'confirm_exit_slider' => 'Haluatko varmasti hylätä muutetut asetukset?',
 		'confirm_exit_slider' => 'Haluatko varmasti hylätä muutetut asetukset?',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'FreshRSS-sovelluksessa on %d uusi artikkeli luettavana.',	// DIRTY
-				1 => 'FreshRSS-sovelluksessa on %d uutta artikkelia luettavana.',	// DIRTY
+				0 => 'FreshRSS-sovelluksessa on %d uusi artikkeli luettavana.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'FreshRSS-sovelluksessa on %d uutta artikkelia luettavana.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(lukematta: %d)',	// DIRTY
-				1 => '(lukematta: %d)',	// DIRTY
+				0 => '(lukematta: %d)',	// DIRTY - (unread: %d)
+				1 => '(lukematta: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Pyyntö epäonnistui. Internetyhteydessä on ehkä ollut ongelmia.',
 			'request_failed' => 'Pyyntö epäonnistui. Internetyhteydessä on ehkä ollut ongelmia.',
 			'title_new_articles' => 'FreshRSS: uusia artikkeleita!',
 			'title_new_articles' => 'FreshRSS: uusia artikkeleita!',
@@ -307,7 +307,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Kirjanmerkit',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Kirjanmerkit',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 2 - 2
app/i18n/he/admin.php

@@ -14,7 +14,7 @@ return array(
 	'auth' => array(
 	'auth' => array(
 		'allow_anonymous' => 'הרשאה לאנונימיים לקרוא את מאמרי משתמש ברירת המחדל (%s)',
 		'allow_anonymous' => 'הרשאה לאנונימיים לקרוא את מאמרי משתמש ברירת המחדל (%s)',
 		'allow_anonymous_refresh' => 'הרשאה לאנונימיים לרענן את רשימת המאמרים',
 		'allow_anonymous_refresh' => 'הרשאה לאנונימיים לרענן את רשימת המאמרים',
-		'api_enabled' => 'הרשאת גישה ל <abbr>API</abbr> <small>(נדרש ליישומים סלולריים and sharing user queries)</small>',	// DIRTY
+		'api_enabled' => 'הרשאת גישה ל <abbr>API</abbr> <small>(נדרש ליישומים סלולריים and sharing user queries)</small>',	// DIRTY - Allow <abbr>API</abbr> access <small>(required for mobile apps and sharing user queries)</small>
 		'form' => 'טופס אינטרנטי (מסורתי)',
 		'form' => 'טופס אינטרנטי (מסורתי)',
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'none' => 'ללא (מסוכן)',
 		'none' => 'ללא (מסוכן)',
@@ -67,7 +67,7 @@ return array(
 		'number_entries' => '%d מאמרים',
 		'number_entries' => '%d מאמרים',
 		'overview' => 'Overview',	// TODO
 		'overview' => 'Overview',	// TODO
 		'percent_of_total' => '% מסך הכל',
 		'percent_of_total' => '% מסך הכל',
-		'repartition' => 'חלוקת המאמרים: %s',	// DIRTY
+		'repartition' => 'חלוקת המאמרים: %s',	// DIRTY - Articles repartition: %s
 		'status_favorites' => 'מועדפים',
 		'status_favorites' => 'מועדפים',
 		'status_read' => 'נקרא',
 		'status_read' => 'נקרא',
 		'status_total' => 'סך הכל',
 		'status_total' => 'סך הכל',

+ 5 - 5
app/i18n/he/conf.php

@@ -144,7 +144,7 @@ return array(
 		),
 		),
 	),
 	),
 	'mark_read_button' => array(
 	'mark_read_button' => array(
-		'_' => '“סימון הכל כנקרא” button',	// DIRTY
+		'_' => '“סימון הכל כנקרא” button',	// DIRTY - “Mark all as read” button
 		'big' => 'Big',	// TODO
 		'big' => 'Big',	// TODO
 		'none' => 'None',	// TODO
 		'none' => 'None',	// TODO
 		'small' => 'Small',	// TODO
 		'small' => 'Small',	// TODO
@@ -170,7 +170,7 @@ return array(
 	'profile' => array(
 	'profile' => array(
 		'_' => 'Profile management',	// TODO
 		'_' => 'Profile management',	// TODO
 		'api' => array(
 		'api' => array(
-			'_' => 'API management',	// DIRTY
+			'_' => 'API management',	// DIRTY - External access via API
 			'api_not_set' => 'API password not set',	// TODO
 			'api_not_set' => 'API password not set',	// TODO
 			'api_set' => 'API password set',	// TODO
 			'api_set' => 'API password set',	// TODO
 			'check_link' => 'Check API status via: <kbd><a href="../api/" target="_blank">%s</a></kbd>',	// TODO
 			'check_link' => 'Check API status via: <kbd><a href="../api/" target="_blank">%s</a></kbd>',	// TODO
@@ -283,7 +283,7 @@ return array(
 			'article_viewed' => 'כאשר מאמר נצפה',
 			'article_viewed' => 'כאשר מאמר נצפה',
 			'focus' => 'when focused (except for important feeds)',	// TODO
 			'focus' => 'when focused (except for important feeds)',	// TODO
 			'keep_max_n_unread' => 'Max number of articles to keep unread',	// TODO
 			'keep_max_n_unread' => 'Max number of articles to keep unread',	// TODO
-			'scroll' => '(except for important feeds) כאשר גוללים',	// DIRTY
+			'scroll' => '(except for important feeds) כאשר גוללים',	// DIRTY - while scrolling (except for important feeds)
 			'upon_gone' => 'when it is no longer in the upstream news feed',	// TODO
 			'upon_gone' => 'when it is no longer in the upstream news feed',	// TODO
 			'upon_reception' => 'כאשר המאמר מתקבל',
 			'upon_reception' => 'כאשר המאמר מתקבל',
 			'when' => 'סימון מאמרים כנקראו…',
 			'when' => 'סימון מאמרים כנקראו…',
@@ -308,8 +308,8 @@ return array(
 			'when' => 'Mark an article as favourite…',	// TODO
 			'when' => 'Mark an article as favourite…',	// TODO
 		),
 		),
 		'sticky_post' => 'הצמדת המאמר לחלק העליון כאשר הוא פתוח',
 		'sticky_post' => 'הצמדת המאמר לחלק העליון כאשר הוא פתוח',
-		'sticky_sort' => 'שמירת סדר המיון הידני בעת ניווט',	// DIRTY
-		'sticky_sort_help' => 'קובע אם סדר המיון הידני האחרון נשאר פעיל או שכל קטגוריה או הזנה משתמשת תמיד בהגדרת ברירת המחדל או הכללית שלה.',	// DIRTY
+		'sticky_sort' => 'שמירת סדר המיון הידני בעת ניווט',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'קובע אם סדר המיון הידני האחרון נשאר פעיל או שכל קטגוריה או הזנה משתמשת תמיד בהגדרת ברירת המחדל או הכללית שלה.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'קריאה',
 		'title' => 'קריאה',
 		'view' => array(
 		'view' => array(
 			'default' => 'תצוגת ברירת המחדל',
 			'default' => 'תצוגת ברירת המחדל',

+ 4 - 4
app/i18n/he/feedback.php

@@ -58,11 +58,11 @@ return array(
 	),
 	),
 	'import_export' => array(
 	'import_export' => array(
 		'export_no_zip_extension' => 'הרחבת ZIP אינה מותקנת על השרת.',
 		'export_no_zip_extension' => 'הרחבת ZIP אינה מותקנת על השרת.',
-		'feeds_imported' => 'ההזנות שלך יובאו וכעת יעודכנו / Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
-		'feeds_imported_with_errors' => 'ההזנות שלך יובאו אך אירעו מספר שגיאות / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
+		'feeds_imported' => 'ההזנות שלך יובאו וכעת יעודכנו / Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.
+		'feeds_imported_with_errors' => 'ההזנות שלך יובאו אך אירעו מספר שגיאות / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.
 		'file_cannot_be_uploaded' => 'אין אפשרות להעלות את הקובץ!',
 		'file_cannot_be_uploaded' => 'אין אפשרות להעלות את הקובץ!',
 		'no_zip_extension' => 'הרחבת ZIP אינה מותקנת על השרת.',
 		'no_zip_extension' => 'הרחבת ZIP אינה מותקנת על השרת.',
-		'zip_error' => 'אירעה שגיאה במהלך ייבוא קובץ הZIP.',	// DIRTY
+		'zip_error' => 'אירעה שגיאה במהלך ייבוא קובץ הZIP.',	// DIRTY - An error occurred during ZIP processing.
 	),
 	),
 	'profile' => array(
 	'profile' => array(
 		'error' => 'Your profile cannot be modified',	// TODO
 		'error' => 'Your profile cannot be modified',	// TODO
@@ -100,7 +100,7 @@ return array(
 				'too_large' => 'Uploaded icon is too large. The maximum file size is <em>%s</em>.',	// TODO
 				'too_large' => 'Uploaded icon is too large. The maximum file size is <em>%s</em>.',	// TODO
 				'unsupported_format' => 'Unsupported image file format!',	// TODO
 				'unsupported_format' => 'Unsupported image file format!',	// TODO
 			),
 			),
-			'internal_problem' => 'אין אפשרות להוסיף את ההזנה. <a href="%s">בדקו את הלוגים</a> לפרטים. You can try force adding by appending <code>#force_feed</code> to the URL.',	// DIRTY
+			'internal_problem' => 'אין אפשרות להוסיף את ההזנה. <a href="%s">בדקו את הלוגים</a> לפרטים. You can try force adding by appending <code>#force_feed</code> to the URL.',	// DIRTY - The newsfeed could not be added. <a href="%s">Check FreshRSS logs</a> for details. You can try force adding by appending <code>#force_feed</code> to the URL.
 			'invalid_url' => 'URL <em>%s</em> אינו תקין',
 			'invalid_url' => 'URL <em>%s</em> אינו תקין',
 			'n_actualized' => '%d הזנות עודכנו',
 			'n_actualized' => '%d הזנות עודכנו',
 			'n_entries_deleted' => '%d המאמרים נמחקו',
 			'n_entries_deleted' => '%d המאמרים נמחקו',

+ 3 - 3
app/i18n/he/gen.php

@@ -174,8 +174,8 @@ return array(
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'ישנם %d מאמרים חדשים לקרוא ב FreshRSS.',	// DIRTY
-				1 => 'ישנם %d מאמרים חדשים לקרוא ב FreshRSS.',	// DIRTY
+				0 => 'ישנם %d מאמרים חדשים לקרוא ב FreshRSS.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'ישנם %d מאמרים חדשים לקרוא ב FreshRSS.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
 				0 => '(unread: %d)',	// TODO
 				0 => '(unread: %d)',	// TODO
@@ -307,7 +307,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud סימניות',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud סימניות',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 1 - 1
app/i18n/he/sub.php

@@ -70,7 +70,7 @@ return array(
 		),
 		),
 		'description' => 'תיאור',
 		'description' => 'תיאור',
 		'empty' => 'הזנה זו ריקה. אנא ודאו שהיא עדיין מתוחזקת.',
 		'empty' => 'הזנה זו ריקה. אנא ודאו שהיא עדיין מתוחזקת.',
-		'error' => 'הזנה זו נתקלה בשגיאה, אנא ודאו שהיא תקינה ואז נסו שנית.',	// DIRTY
+		'error' => 'הזנה זו נתקלה בשגיאה, אנא ודאו שהיא תקינה ואז נסו שנית.',	// DIRTY - This feed has encountered a problem. If this situation persists, please verify that it is still reachable.
 		'export-as-opml' => array(
 		'export-as-opml' => array(
 			'download' => 'Download',	// TODO
 			'download' => 'Download',	// TODO
 			'help' => 'XML file (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// TODO
 			'help' => 'XML file (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// TODO

+ 2 - 2
app/i18n/hu/conf.php

@@ -308,8 +308,8 @@ return array(
 			'when' => 'Cikk megjelölése kedvencnek…',
 			'when' => 'Cikk megjelölése kedvencnek…',
 		),
 		),
 		'sticky_post' => 'A cikk gördüljön felülre, amikor megnyitásra kerül',
 		'sticky_post' => 'A cikk gördüljön felülre, amikor megnyitásra kerül',
-		'sticky_sort' => 'Kézi rendezés megtartása navigáció közben',	// DIRTY
-		'sticky_sort_help' => 'Meghatározza, hogy az utolsó kézi rendezés aktív marad-e, vagy minden kategória, illetve hírcsatorna mindig a saját alapértelmezett vagy globális beállítását használja.',	// DIRTY
+		'sticky_sort' => 'Kézi rendezés megtartása navigáció közben',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Meghatározza, hogy az utolsó kézi rendezés aktív marad-e, vagy minden kategória, illetve hírcsatorna mindig a saját alapértelmezett vagy globális beállítását használja.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Olvasás',
 		'title' => 'Olvasás',
 		'view' => array(
 		'view' => array(
 			'default' => 'Alapértelmezett nézet',
 			'default' => 'Alapértelmezett nézet',

+ 5 - 5
app/i18n/hu/gen.php

@@ -174,12 +174,12 @@ return array(
 		'confirm_exit_slider' => 'Biztosan elveti a nem mentett beállításokat?',
 		'confirm_exit_slider' => 'Biztosan elveti a nem mentett beállításokat?',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => '%d db új cikk olvasható a FreshRSS-ben.',	// DIRTY
-				1 => '%d db új cikk olvasható a FreshRSS-ben.',	// DIRTY
+				0 => '%d db új cikk olvasható a FreshRSS-ben.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => '%d db új cikk olvasható a FreshRSS-ben.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(olvasatlan: %d)',	// DIRTY
-				1 => '(olvasatlan: %d)',	// DIRTY
+				0 => '(olvasatlan: %d)',	// DIRTY - (unread: %d)
+				1 => '(olvasatlan: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Egy művelet nem sikerült, lehetséges hogy az internet kapcsolattal vannak problémák.',
 			'request_failed' => 'Egy művelet nem sikerült, lehetséges hogy az internet kapcsolattal vannak problémák.',
 			'title_new_articles' => 'FreshRSS: új cikkek!',
 			'title_new_articles' => 'FreshRSS: új cikkek!',
@@ -307,7 +307,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Könyvjelzők',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Könyvjelzők',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 1 - 1
app/i18n/id/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Kanal rilis',
 			'_' => 'Kanal rilis',
 			'edge' => 'Rilis Baru (“edge”)',
 			'edge' => 'Rilis Baru (“edge”)',
-			'latest' => 'Rilis Stabil (“latest”)',	// DIRTY
+			'latest' => 'Rilis Stabil (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Pembaruan FreshRSS',
 		'title' => 'Pembaruan FreshRSS',
 		'viaGit' => 'Pembaruan lewat git dan Github.com dimulai',
 		'viaGit' => 'Pembaruan lewat git dan Github.com dimulai',

+ 2 - 2
app/i18n/ja/admin.php

@@ -15,7 +15,7 @@ return array(
 		'allow_anonymous' => 'ログインしていないユーザーにもデフォルトユーザー(%s)の記事の閲覧を許可する',
 		'allow_anonymous' => 'ログインしていないユーザーにもデフォルトユーザー(%s)の記事の閲覧を許可する',
 		'allow_anonymous_refresh' => 'ログインしていないユーザーにも記事の更新を許可する',
 		'allow_anonymous_refresh' => 'ログインしていないユーザーにも記事の更新を許可する',
 		'api_enabled' => '<abbr>API</abbr>アクセスを許可する<small>(モバイルアプリやユーザークエリの共有に必要)</small>',
 		'api_enabled' => '<abbr>API</abbr>アクセスを許可する<small>(モバイルアプリやユーザークエリの共有に必要)</small>',
-		'form' => 'ウェブフォーム(JavaScriptが必要です)',	// DIRTY
+		'form' => 'ウェブフォーム(JavaScriptが必要です)',	// DIRTY - Web form (traditional)
 		'http' => 'HTTP(上級者向け:Webサーバー、OIDC、SSOなどで管理)',
 		'http' => 'HTTP(上級者向け:Webサーバー、OIDC、SSOなどで管理)',
 		'none' => 'なし(危険)',
 		'none' => 'なし(危険)',
 		'title' => '認証',
 		'title' => '認証',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'リリースチャンネル',
 			'_' => 'リリースチャンネル',
 			'edge' => 'ローリングリリース(edge)',
 			'edge' => 'ローリングリリース(edge)',
-			'latest' => '安定版リリース(latest)',	// DIRTY
+			'latest' => '安定版リリース(latest)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'アップデートシステム',
 		'title' => 'アップデートシステム',
 		'viaGit' => 'gitおよびGitHub.com経由でアップデートを開始しました。',
 		'viaGit' => 'gitおよびGitHub.com経由でアップデートを開始しました。',

+ 1 - 1
app/i18n/ja/gen.php

@@ -299,7 +299,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud ブックマーク',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud ブックマーク',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 4 - 4
app/i18n/ko/admin.php

@@ -14,7 +14,7 @@ return array(
 	'auth' => array(
 	'auth' => array(
 		'allow_anonymous' => '누구나 기본 사용자의 글을 읽을 수 있도록 합니다(%s)',
 		'allow_anonymous' => '누구나 기본 사용자의 글을 읽을 수 있도록 합니다(%s)',
 		'allow_anonymous_refresh' => '누구나 피드를 갱신할 수 있도록 합니다',
 		'allow_anonymous_refresh' => '누구나 피드를 갱신할 수 있도록 합니다',
-		'api_enabled' => '<abbr>API</abbr> 사용을 허가합니다<small>(모바일 애플리케이션을 사용할 때 필요합니다	and sharing user queries)</small>',	// DIRTY
+		'api_enabled' => '<abbr>API</abbr> 사용을 허가합니다<small>(모바일 애플리케이션을 사용할 때 필요합니다	and sharing user queries)</small>',	// DIRTY - Allow <abbr>API</abbr> access <small>(required for mobile apps and sharing user queries)</small>
 		'form' => '웹폼 (전통적인 방식)',
 		'form' => '웹폼 (전통적인 방식)',
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'none' => '사용하지 않음 (위험)',
 		'none' => '사용하지 않음 (위험)',
@@ -67,7 +67,7 @@ return array(
 		'number_entries' => '%d 개의 글',
 		'number_entries' => '%d 개의 글',
 		'overview' => 'Overview',	// TODO
 		'overview' => 'Overview',	// TODO
 		'percent_of_total' => '전체에서의 비율 (%)',
 		'percent_of_total' => '전체에서의 비율 (%)',
-		'repartition' => '글 분류: %s',	// DIRTY
+		'repartition' => '글 분류: %s',	// DIRTY - Articles repartition: %s
 		'status_favorites' => '즐겨찾기',
 		'status_favorites' => '즐겨찾기',
 		'status_read' => '읽음',
 		'status_read' => '읽음',
 		'status_total' => '전체',
 		'status_total' => '전체',
@@ -114,7 +114,7 @@ return array(
 			),
 			),
 			'title' => '사용자 회원가입 양식',
 			'title' => '사용자 회원가입 양식',
 		),
 		),
-		'sensitive-parameter' => 'Sensitive parameter. <kbd>./data/config.php</kbd>에서 직접 수정',	// DIRTY
+		'sensitive-parameter' => 'Sensitive parameter. <kbd>./data/config.php</kbd>에서 직접 수정',	// DIRTY - Sensitive parameter. Edit manually in <kbd>./data/config.php</kbd>
 		'tos' => array(
 		'tos' => array(
 			'disabled' => '주어지지 않음',
 			'disabled' => '주어지지 않음',
 			'enabled' => '<a href="./?a=tos">활성화됨</a>',
 			'enabled' => '<a href="./?a=tos">활성화됨</a>',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => '릴리즈 채널',
 			'_' => '릴리즈 채널',
 			'edge' => '롤링 릴리즈 (“edge”)',
 			'edge' => '롤링 릴리즈 (“edge”)',
-			'latest' => '안정 릴리즈 (“latest”)',	// DIRTY
+			'latest' => '안정 릴리즈 (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => '업데이트',
 		'title' => '업데이트',
 		'viaGit' => 'Git 및 GitHub.com을 통한 업데이트 시작 됨',
 		'viaGit' => 'Git 및 GitHub.com을 통한 업데이트 시작 됨',

+ 5 - 5
app/i18n/ko/conf.php

@@ -144,7 +144,7 @@ return array(
 		),
 		),
 	),
 	),
 	'mark_read_button' => array(
 	'mark_read_button' => array(
-		'_' => '“모두 읽음으로 표시” button',	// DIRTY
+		'_' => '“모두 읽음으로 표시” button',	// DIRTY - “Mark all as read” button
 		'big' => 'Big',	// TODO
 		'big' => 'Big',	// TODO
 		'none' => 'None',	// TODO
 		'none' => 'None',	// TODO
 		'small' => 'Small',	// TODO
 		'small' => 'Small',	// TODO
@@ -283,13 +283,13 @@ return array(
 			'article_viewed' => '글을 읽었을 때',
 			'article_viewed' => '글을 읽었을 때',
 			'focus' => '포커스 됐을 때 (중요 피드 제외)',
 			'focus' => '포커스 됐을 때 (중요 피드 제외)',
 			'keep_max_n_unread' => '읽지 않은 상태로 유지할 최대 글 개수',
 			'keep_max_n_unread' => '읽지 않은 상태로 유지할 최대 글 개수',
-			'scroll' => '스크롤을 하며 지나갈 때 (except for important feeds)',	// DIRTY
+			'scroll' => '스크롤을 하며 지나갈 때 (except for important feeds)',	// DIRTY - while scrolling (except for important feeds)
 			'upon_gone' => '원본 뉴스 피드에서 글 삭제 되었을 때',
 			'upon_gone' => '원본 뉴스 피드에서 글 삭제 되었을 때',
 			'upon_reception' => '글을 가져오자마자',
 			'upon_reception' => '글을 가져오자마자',
 			'when' => '읽음으로 표시…',
 			'when' => '읽음으로 표시…',
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
-			'when_same_title_in_feed' => '상위 <i>n</i>개의 최신 글에 동일한 제목이 이미 있는 경우 (of the feed)',	// DIRTY
+			'when_same_title_in_feed' => '상위 <i>n</i>개의 최신 글에 동일한 제목이 이미 있는 경우 (of the feed)',	// DIRTY - if an identical title already exists in the top <i>n</i> newest articles of the feed
 		),
 		),
 		'show' => array(
 		'show' => array(
 			'_' => '글 표시 방식',
 			'_' => '글 표시 방식',
@@ -308,8 +308,8 @@ return array(
 			'when' => 'Mark an article as favourite…',	// TODO
 			'when' => 'Mark an article as favourite…',	// TODO
 		),
 		),
 		'sticky_post' => '글이 펼쳐진 경우 최상단에 고정하기',
 		'sticky_post' => '글이 펼쳐진 경우 최상단에 고정하기',
-		'sticky_sort' => '탐색 중에도 수동 정렬 순서 유지',	// DIRTY
-		'sticky_sort_help' => '마지막 수동 정렬 순서를 계속 사용할지, 아니면 각 카테고리나 피드가 항상 자체 기본값 또는 전역 설정을 사용할지 결정합니다.',	// DIRTY
+		'sticky_sort' => '탐색 중에도 수동 정렬 순서 유지',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => '마지막 수동 정렬 순서를 계속 사용할지, 아니면 각 카테고리나 피드가 항상 자체 기본값 또는 전역 설정을 사용할지 결정합니다.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => '읽기',
 		'title' => '읽기',
 		'view' => array(
 		'view' => array(
 			'default' => '기본 보기 모드',
 			'default' => '기본 보기 모드',

+ 3 - 3
app/i18n/ko/gen.php

@@ -168,10 +168,10 @@ return array(
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => '%d 개의 새 글이 FreshRSS에 있습니다.',	// DIRTY
+				0 => '%d 개의 새 글이 FreshRSS에 있습니다.',	// DIRTY - Singular: There is %d new article to read on FreshRSS. | Plural: There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(%d 개 읽지 않음)',	// DIRTY
+				0 => '(%d 개 읽지 않음)',	// DIRTY - Singular: (unread: %d) | Plural: (unread: %d)
 			),
 			),
 			'request_failed' => '요청한 작업을 수행할 수 없습니다. 인터넷 연결에 문제가 발생한 것 같습니다.',
 			'request_failed' => '요청한 작업을 수행할 수 없습니다. 인터넷 연결에 문제가 발생한 것 같습니다.',
 			'title_new_articles' => 'FreshRSS: 새 글이 있습니다!',
 			'title_new_articles' => 'FreshRSS: 새 글이 있습니다!',
@@ -299,7 +299,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud 북마크',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud 북마크',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 2 - 2
app/i18n/ko/sub.php

@@ -70,10 +70,10 @@ return array(
 		),
 		),
 		'description' => '설명',
 		'description' => '설명',
 		'empty' => '이 피드는 비어있습니다. 피드가 계속 운영되고 있는지 확인하세요.',
 		'empty' => '이 피드는 비어있습니다. 피드가 계속 운영되고 있는지 확인하세요.',
-		'error' => '이 피드에 문제가 발생했습니다. 이 피드에 접근 권한이 있는지 확인하세요.',	// DIRTY
+		'error' => '이 피드에 문제가 발생했습니다. 이 피드에 접근 권한이 있는지 확인하세요.',	// DIRTY - This feed has encountered a problem. If this situation persists, please verify that it is still reachable.
 		'export-as-opml' => array(
 		'export-as-opml' => array(
 			'download' => '다운로드',
 			'download' => '다운로드',
-			'help' => 'XML 파일 (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// DIRTY
+			'help' => 'XML 파일 (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// DIRTY - XML file (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)
 			'label' => 'OPML로 내보내기',
 			'label' => 'OPML로 내보내기',
 		),
 		),
 		'ext_favicon' => 'Set automatically',	// TODO
 		'ext_favicon' => 'Set automatically',	// TODO

+ 1 - 1
app/i18n/lt/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Leidimų kanalas',
 			'_' => 'Leidimų kanalas',
 			'edge' => 'Nuolatinis leidimas („edge“)',
 			'edge' => 'Nuolatinis leidimas („edge“)',
-			'latest' => 'Stabilus leidimas („latest“)',	// DIRTY
+			'latest' => 'Stabilus leidimas („latest“)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Atnaujinti FreshRSS',
 		'title' => 'Atnaujinti FreshRSS',
 		'viaGit' => 'Pradėtas atnaujinimas per „git“ ir „GitHub.com“',
 		'viaGit' => 'Pradėtas atnaujinimas per „git“ ir „GitHub.com“',

+ 3 - 3
app/i18n/lv/admin.php

@@ -14,7 +14,7 @@ return array(
 	'auth' => array(
 	'auth' => array(
 		'allow_anonymous' => 'Ļaut anonīmi lasīt noklusējuma lietotāja rakstus (%s)',
 		'allow_anonymous' => 'Ļaut anonīmi lasīt noklusējuma lietotāja rakstus (%s)',
 		'allow_anonymous_refresh' => 'Atļaut anonīmu rakstu atsvaidzināšanu',
 		'allow_anonymous_refresh' => 'Atļaut anonīmu rakstu atsvaidzināšanu',
-		'api_enabled' => 'Atļaut <abbr>API</abbr> piekļuvi <small>(nepieciešams mobilajām lietotnēm and sharing user queries)</small>',	// DIRTY
+		'api_enabled' => 'Atļaut <abbr>API</abbr> piekļuvi <small>(nepieciešams mobilajām lietotnēm and sharing user queries)</small>',	// DIRTY - Allow <abbr>API</abbr> access <small>(required for mobile apps and sharing user queries)</small>
 		'form' => 'Tīmekļa veidlapa (tradicionālā)',
 		'form' => 'Tīmekļa veidlapa (tradicionālā)',
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'none' => 'Nav (bīstami)',
 		'none' => 'Nav (bīstami)',
@@ -67,7 +67,7 @@ return array(
 		'number_entries' => '%d raksti',
 		'number_entries' => '%d raksti',
 		'overview' => 'Overview',	// TODO
 		'overview' => 'Overview',	// TODO
 		'percent_of_total' => '% no kopsummas',
 		'percent_of_total' => '% no kopsummas',
-		'repartition' => 'Rakstu pārdalīšana: %s',	// DIRTY
+		'repartition' => 'Rakstu pārdalīšana: %s',	// DIRTY - Articles repartition: %s
 		'status_favorites' => 'Mīļākie',
 		'status_favorites' => 'Mīļākie',
 		'status_read' => 'Izlasīti',
 		'status_read' => 'Izlasīti',
 		'status_total' => 'Kopā',
 		'status_total' => 'Kopā',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Izlaiduma kanāls',
 			'_' => 'Izlaiduma kanāls',
 			'edge' => 'Ritošā atbrīvošana (“edge”)',
 			'edge' => 'Ritošā atbrīvošana (“edge”)',
-			'latest' => 'Stabila atbrīvošana (“latest”)',	// DIRTY
+			'latest' => 'Stabila atbrīvošana (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Atjaunināt sistēmu',
 		'title' => 'Atjaunināt sistēmu',
 		'viaGit' => 'Atjaunināt, izmantojot git un GitHub.com sākts',
 		'viaGit' => 'Atjaunināt, izmantojot git un GitHub.com sākts',

+ 5 - 5
app/i18n/lv/conf.php

@@ -17,7 +17,7 @@ return array(
 		'help' => 'Vairākas opcijas ir pieejamas atsevišķas barotnes iestatījumos.',
 		'help' => 'Vairākas opcijas ir pieejamas atsevišķas barotnes iestatījumos.',
 		'keep_favourites' => 'Nekad neizdzēst mīļākos',
 		'keep_favourites' => 'Nekad neizdzēst mīļākos',
 		'keep_labels' => 'Nekad neizdzēst birkas',
 		'keep_labels' => 'Nekad neizdzēst birkas',
-		'keep_max' => 'Maksimālais saglabājamo rakstu skaits',	// DIRTY
+		'keep_max' => 'Maksimālais saglabājamo rakstu skaits',	// DIRTY - Maximum number of articles to keep per feed
 		'keep_min_by_feed' => 'Minimālais rakstu skaits, kas jāsaglabā vienā barotnē',
 		'keep_min_by_feed' => 'Minimālais rakstu skaits, kas jāsaglabā vienā barotnē',
 		'keep_period' => 'Maksimālais saglabājamo rakstu vecums',
 		'keep_period' => 'Maksimālais saglabājamo rakstu vecums',
 		'keep_unreads' => 'Nekad neizdzēst nelasītos rakstus',
 		'keep_unreads' => 'Nekad neizdzēst nelasītos rakstus',
@@ -144,7 +144,7 @@ return array(
 		),
 		),
 	),
 	),
 	'mark_read_button' => array(
 	'mark_read_button' => array(
-		'_' => '"atzīmēt visus kā izlasītus" button',	// DIRTY
+		'_' => '"atzīmēt visus kā izlasītus" button',	// DIRTY - “Mark all as read” button
 		'big' => 'Big',	// TODO
 		'big' => 'Big',	// TODO
 		'none' => 'None',	// TODO
 		'none' => 'None',	// TODO
 		'small' => 'Small',	// TODO
 		'small' => 'Small',	// TODO
@@ -289,7 +289,7 @@ return array(
 			'when' => 'Atzīmēt rakstu kā izlasītu…',
 			'when' => 'Atzīmēt rakstu kā izlasītu…',
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
-			'when_same_title_in_feed' => 'ja identisks virsraksts jau ir jaunākajos <i>n</i> rakstos (of the feed)',	// DIRTY
+			'when_same_title_in_feed' => 'ja identisks virsraksts jau ir jaunākajos <i>n</i> rakstos (of the feed)',	// DIRTY - if an identical title already exists in the top <i>n</i> newest articles of the feed
 		),
 		),
 		'show' => array(
 		'show' => array(
 			'_' => 'Rādāmie raksti',
 			'_' => 'Rādāmie raksti',
@@ -308,8 +308,8 @@ return array(
 			'when' => 'Mark an article as favourite…',	// TODO
 			'when' => 'Mark an article as favourite…',	// TODO
 		),
 		),
 		'sticky_post' => 'Uzlīmēt rakstu augšā, kad atvērts',
 		'sticky_post' => 'Uzlīmēt rakstu augšā, kad atvērts',
-		'sticky_sort' => 'Saglabāt manuālo kārtošanas secību navigācijas laikā',	// DIRTY
-		'sticky_sort_help' => 'Nosaka, vai tiek saglabāta pēdējā manuālā kārtošanas secība, vai arī katra kategorija vai plūsma vienmēr izmanto savu noklusējuma vai globālo iestatījumu.',	// DIRTY
+		'sticky_sort' => 'Saglabāt manuālo kārtošanas secību navigācijas laikā',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Nosaka, vai tiek saglabāta pēdējā manuālā kārtošanas secība, vai arī katra kategorija vai plūsma vienmēr izmanto savu noklusējuma vai globālo iestatījumu.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Lasīšana',
 		'title' => 'Lasīšana',
 		'view' => array(
 		'view' => array(
 			'default' => 'Noklusējuma skats',
 			'default' => 'Noklusējuma skats',

+ 3 - 3
app/i18n/lv/feedback.php

@@ -58,11 +58,11 @@ return array(
 	),
 	),
 	'import_export' => array(
 	'import_export' => array(
 		'export_no_zip_extension' => 'Jūsu serverī nav ZIP paplašinājuma. Lūdzu, mēģiniet eksportēt failus pa vienam.',
 		'export_no_zip_extension' => 'Jūsu serverī nav ZIP paplašinājuma. Lūdzu, mēģiniet eksportēt failus pa vienam.',
-		'feeds_imported' => 'Jūsu barotnes tika importētas un tagad tiks atjauninātas.	/ Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
-		'feeds_imported_with_errors' => 'Jūsu barotnes tika importētas, bet ir radušās dažas kļūdas / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
+		'feeds_imported' => 'Jūsu barotnes tika importētas un tagad tiks atjauninātas.	/ Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.
+		'feeds_imported_with_errors' => 'Jūsu barotnes tika importētas, bet ir radušās dažas kļūdas / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.
 		'file_cannot_be_uploaded' => 'Failu nevar augšupielādēt!',
 		'file_cannot_be_uploaded' => 'Failu nevar augšupielādēt!',
 		'no_zip_extension' => 'Jūsu serverī nav ZIP paplašinājuma.',
 		'no_zip_extension' => 'Jūsu serverī nav ZIP paplašinājuma.',
-		'zip_error' => 'ZIP importa laikā notika kļūda.',	// DIRTY
+		'zip_error' => 'ZIP importa laikā notika kļūda.',	// DIRTY - An error occurred during ZIP processing.
 	),
 	),
 	'profile' => array(
 	'profile' => array(
 		'error' => 'Jūsu profilu nevar mainīt',
 		'error' => 'Jūsu profilu nevar mainīt',

+ 7 - 7
app/i18n/lv/gen.php

@@ -180,14 +180,14 @@ return array(
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'FreshRSS ir %d jauni raksti lasīšanai.',	// DIRTY
-				1 => 'FreshRSS ir %d jauni raksti lasīšanai.',	// DIRTY
-				2 => 'FreshRSS ir %d jauni raksti lasīšanai.',	// DIRTY
+				0 => 'FreshRSS ir %d jauni raksti lasīšanai.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'FreshRSS ir %d jauni raksti lasīšanai.',	// DIRTY - There are %d new articles to read on FreshRSS.
+				2 => 'FreshRSS ir %d jauni raksti lasīšanai.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(neizlasīti: %d)',	// DIRTY
-				1 => '(neizlasīti: %d)',	// DIRTY
-				2 => '(neizlasīti: %d)',	// DIRTY
+				0 => '(neizlasīti: %d)',	// DIRTY - (unread: %d)
+				1 => '(neizlasīti: %d)',	// DIRTY - (unread: %d)
+				2 => '(neizlasīti: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Pieprasījums nav izdevies, iespējams, to izraisījušas interneta savienojuma problēmas.',
 			'request_failed' => 'Pieprasījums nav izdevies, iespējams, to izraisījušas interneta savienojuma problēmas.',
 			'title_new_articles' => 'FreshRSS: jauni raksti!',
 			'title_new_articles' => 'FreshRSS: jauni raksti!',
@@ -315,7 +315,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Grāmatzīmes',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Grāmatzīmes',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 2 - 2
app/i18n/lv/sub.php

@@ -70,7 +70,7 @@ return array(
 		),
 		),
 		'description' => 'Apraksts',
 		'description' => 'Apraksts',
 		'empty' => 'Šī barotne ir tukša. Lūdzu, pārbaudiet, vai tā joprojām tiek uzturēta.',
 		'empty' => 'Šī barotne ir tukša. Lūdzu, pārbaudiet, vai tā joprojām tiek uzturēta.',
-		'error' => 'Šajā barotnē ir radusies problēma. Lūdzu, pārbaudiet, vai tā vienmēr ir sasniedzama.',	// DIRTY
+		'error' => 'Šajā barotnē ir radusies problēma. Lūdzu, pārbaudiet, vai tā vienmēr ir sasniedzama.',	// DIRTY - This feed has encountered a problem. If this situation persists, please verify that it is still reachable.
 		'export-as-opml' => array(
 		'export-as-opml' => array(
 			'download' => 'Lejupielādēt',
 			'download' => 'Lejupielādēt',
 			'help' => 'XML fails (datu apakškopa. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">Skatīt dokumentāciju</a>)',
 			'help' => 'XML fails (datu apakškopa. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">Skatīt dokumentāciju</a>)',
@@ -80,7 +80,7 @@ return array(
 		'favicon_changed_by_ext' => 'The icon has been set by the <b>%s</b> extension.',	// TODO
 		'favicon_changed_by_ext' => 'The icon has been set by the <b>%s</b> extension.',	// TODO
 		'filteractions' => array(
 		'filteractions' => array(
 			'_' => 'Filtra darbības',
 			'_' => 'Filtra darbības',
-			'help' => 'Uzrakstiet vienu meklēšanas filtru katrā rindā. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.',	// DIRTY
+			'help' => 'Uzrakstiet vienu meklēšanas filtru katrā rindā. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.',	// DIRTY - Write one search filter per line. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.
 			'view_filter' => 'Preview filters on existing articles (new window)',	// TODO
 			'view_filter' => 'Preview filters on existing articles (new window)',	// TODO
 		),
 		),
 		'global_hint' => 'Use <a href="%s">the global view</a> to see how many articles in each feed are matching a state or a search expression',	// TODO
 		'global_hint' => 'Use <a href="%s">the global view</a> to see how many articles in each feed are matching a state or a search expression',	// TODO

+ 1 - 1
app/i18n/oc/admin.php

@@ -14,7 +14,7 @@ return array(
 	'auth' => array(
 	'auth' => array(
 		'allow_anonymous' => 'Autorizar la lectura anonima dels articles de l’utilizaire per defaut (%s)',
 		'allow_anonymous' => 'Autorizar la lectura anonima dels articles de l’utilizaire per defaut (%s)',
 		'allow_anonymous_refresh' => 'Autorizar l’actualizacion anonime dels fluxes',
 		'allow_anonymous_refresh' => 'Autorizar l’actualizacion anonime dels fluxes',
-		'api_enabled' => 'Autorizar l’accès per <abbr>API</abbr><small>(necessari per las aplicacions mobil and sharing user queries)</small>',	// DIRTY
+		'api_enabled' => 'Autorizar l’accès per <abbr>API</abbr><small>(necessari per las aplicacions mobil and sharing user queries)</small>',	// DIRTY - Allow <abbr>API</abbr> access <small>(required for mobile apps and sharing user queries)</small>
 		'form' => 'Formulari (tradicional)',
 		'form' => 'Formulari (tradicional)',
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'none' => 'Cap (perilhós)',
 		'none' => 'Cap (perilhós)',

+ 5 - 5
app/i18n/oc/conf.php

@@ -144,7 +144,7 @@ return array(
 		),
 		),
 	),
 	),
 	'mark_read_button' => array(
 	'mark_read_button' => array(
-		'_' => '« o marcar tot coma legit » button',	// DIRTY
+		'_' => '« o marcar tot coma legit » button',	// DIRTY - “Mark all as read” button
 		'big' => 'Big',	// TODO
 		'big' => 'Big',	// TODO
 		'none' => 'None',	// TODO
 		'none' => 'None',	// TODO
 		'small' => 'Small',	// TODO
 		'small' => 'Small',	// TODO
@@ -283,13 +283,13 @@ return array(
 			'article_viewed' => 'quand l’article es mostrat',
 			'article_viewed' => 'quand l’article es mostrat',
 			'focus' => 'when focused (except for important feeds)',	// TODO
 			'focus' => 'when focused (except for important feeds)',	// TODO
 			'keep_max_n_unread' => 'Nombre max d’articles a gardar pas legits',
 			'keep_max_n_unread' => 'Nombre max d’articles a gardar pas legits',
-			'scroll' => 'en davalant la pagina (except for important feeds)',	// DIRTY
+			'scroll' => 'en davalant la pagina (except for important feeds)',	// DIRTY - while scrolling (except for important feeds)
 			'upon_gone' => 'quand es pas mai dins lo flux de novèla font',
 			'upon_gone' => 'quand es pas mai dins lo flux de novèla font',
 			'upon_reception' => 'en recebre un article novèl',
 			'upon_reception' => 'en recebre un article novèl',
 			'when' => 'Marcar un article coma legit…',
 			'when' => 'Marcar un article coma legit…',
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
-			'when_same_title_in_feed' => 'se un títol identic existís ja demest lo <i>n</i> articles mai recents (of the feed)',	// DIRTY
+			'when_same_title_in_feed' => 'se un títol identic existís ja demest lo <i>n</i> articles mai recents (of the feed)',	// DIRTY - if an identical title already exists in the top <i>n</i> newest articles of the feed
 		),
 		),
 		'show' => array(
 		'show' => array(
 			'_' => 'Articles de mostrar',
 			'_' => 'Articles de mostrar',
@@ -308,8 +308,8 @@ return array(
 			'when' => 'Mark an article as favourite…',	// TODO
 			'when' => 'Mark an article as favourite…',	// TODO
 		),
 		),
 		'sticky_post' => 'Gardar l’article amont quand es dobèrt',
 		'sticky_post' => 'Gardar l’article amont quand es dobèrt',
-		'sticky_sort' => 'Servar l’òrdre de triada manual pendent la navigacion',	// DIRTY
-		'sticky_sort_help' => 'Determina se lo darrièr òrdre de triada manual es conservat o se cada categoria o flux utiliza totjorn son pròpri paramètre per defaut o global.',	// DIRTY
+		'sticky_sort' => 'Servar l’òrdre de triada manual pendent la navigacion',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Determina se lo darrièr òrdre de triada manual es conservat o se cada categoria o flux utiliza totjorn son pròpri paramètre per defaut o global.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Lectura',
 		'title' => 'Lectura',
 		'view' => array(
 		'view' => array(
 			'default' => 'Vista per defaut',
 			'default' => 'Vista per defaut',

+ 3 - 3
app/i18n/oc/feedback.php

@@ -58,11 +58,11 @@ return array(
 	),
 	),
 	'import_export' => array(
 	'import_export' => array(
 		'export_no_zip_extension' => 'L’extension ZIP es pas presenta sul servidor. Volgatz ensajar d’exportar los fichièrs un per un.',
 		'export_no_zip_extension' => 'L’extension ZIP es pas presenta sul servidor. Volgatz ensajar d’exportar los fichièrs un per un.',
-		'feeds_imported' => 'Vòstres fluxes son estats importats seràn actualizats en seguida / Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
-		'feeds_imported_with_errors' => 'Vòstres fluxes son estats importats mas i a agut d’errors / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY
+		'feeds_imported' => 'Vòstres fluxes son estats importats seràn actualizats en seguida / Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported. If you are done importing, you can now click the <i>Update feeds</i> button.
+		'feeds_imported_with_errors' => 'Vòstres fluxes son estats importats mas i a agut d’errors / Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.',	// DIRTY - Your feeds have been imported, but some errors occurred. If you are done importing, you can now click the <i>Update feeds</i> button.
 		'file_cannot_be_uploaded' => 'Telecargament del fichièr impossible',
 		'file_cannot_be_uploaded' => 'Telecargament del fichièr impossible',
 		'no_zip_extension' => 'L’extension es pas presenta sul servidor.',
 		'no_zip_extension' => 'L’extension es pas presenta sul servidor.',
-		'zip_error' => 'Una error s’es producha pendent l’importacion del fichièr ZIP.',	// DIRTY
+		'zip_error' => 'Una error s’es producha pendent l’importacion del fichièr ZIP.',	// DIRTY - An error occurred during ZIP processing.
 	),
 	),
 	'profile' => array(
 	'profile' => array(
 		'error' => 'Impossible d’actualizar vòstre perfil',
 		'error' => 'Impossible d’actualizar vòstre perfil',

+ 3 - 3
app/i18n/oc/gen.php

@@ -174,8 +174,8 @@ return array(
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'I a %d nòus articles per legir sus FreshRSS.',	// DIRTY
-				1 => 'I a %d nòus articles per legir sus FreshRSS.',	// DIRTY
+				0 => 'I a %d nòus articles per legir sus FreshRSS.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'I a %d nòus articles per legir sus FreshRSS.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
 				0 => '(unread: %d)',	// TODO
 				0 => '(unread: %d)',	// TODO
@@ -307,7 +307,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Marcapaginas',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Marcapaginas',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 4 - 4
app/i18n/oc/sub.php

@@ -70,7 +70,7 @@ return array(
 		),
 		),
 		'description' => 'Descripcion',	// IGNORE
 		'description' => 'Descripcion',	// IGNORE
 		'empty' => 'Aqueste flux es void. Assegurats-vos qu’es totjorn mantengut.',
 		'empty' => 'Aqueste flux es void. Assegurats-vos qu’es totjorn mantengut.',
-		'error' => 'Aqueste flux a rescontrat un problèma. Volgatz verificar que siá totjorn accessible.',	// DIRTY
+		'error' => 'Aqueste flux a rescontrat un problèma. Volgatz verificar que siá totjorn accessible.',	// DIRTY - This feed has encountered a problem. If this situation persists, please verify that it is still reachable.
 		'export-as-opml' => array(
 		'export-as-opml' => array(
 			'download' => 'Download',	// TODO
 			'download' => 'Download',	// TODO
 			'help' => 'XML file (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// TODO
 			'help' => 'XML file (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// TODO
@@ -80,7 +80,7 @@ return array(
 		'favicon_changed_by_ext' => 'The icon has been set by the <b>%s</b> extension.',	// TODO
 		'favicon_changed_by_ext' => 'The icon has been set by the <b>%s</b> extension.',	// TODO
 		'filteractions' => array(
 		'filteractions' => array(
 			'_' => 'Filtre d’accion',
 			'_' => 'Filtre d’accion',
-			'help' => 'Escrivètz una recèrca per linha. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.',	// DIRTY
+			'help' => 'Escrivètz una recèrca per linha. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.',	// DIRTY - Write one search filter per line. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.
 			'view_filter' => 'Preview filters on existing articles (new window)',	// TODO
 			'view_filter' => 'Preview filters on existing articles (new window)',	// TODO
 		),
 		),
 		'global_hint' => 'Use <a href="%s">the global view</a> to see how many articles in each feed are matching a state or a search expression',	// TODO
 		'global_hint' => 'Use <a href="%s">the global view</a> to see how many articles in each feed are matching a state or a search expression',	// TODO
@@ -196,7 +196,7 @@ return array(
 			'clear_cache' => 'Escafar lo cache',
 			'clear_cache' => 'Escafar lo cache',
 			'clear_cache_help' => 'Escafar lo cache d’aqueste flux sul disc',
 			'clear_cache_help' => 'Escafar lo cache d’aqueste flux sul disc',
 			'reload_articles' => 'Recargar los articles',
 			'reload_articles' => 'Recargar los articles',
-			'reload_articles_help' => 'Recargar los articles e recuperar lo contengut complet',	// DIRTY
+			'reload_articles_help' => 'Recargar los articles e recuperar lo contengut complet',	// DIRTY - Reload that many articles and fetch complete content if a selector is defined.
 			'title' => 'Mantenença',
 			'title' => 'Mantenença',
 		),
 		),
 		'max_http_redir' => 'Max HTTP redireccions',
 		'max_http_redir' => 'Max HTTP redireccions',
@@ -224,7 +224,7 @@ return array(
 			'main_stream' => 'Mostar al flux màger',
 			'main_stream' => 'Mostar al flux màger',
 		),
 		),
 		'proxy' => 'Definir un servidor proxy per trapar aqueste flux',
 		'proxy' => 'Definir un servidor proxy per trapar aqueste flux',
-		'proxy_help' => 'Seleccionatz un protocòl (ex : SOCKS5) e picatz l’adreça del proxy (ex : <kbd>127.0.0.1:1080</kbd> or <kbd>username:password@127.0.0.1:1080</kbd>)',	// DIRTY
+		'proxy_help' => 'Seleccionatz un protocòl (ex : SOCKS5) e picatz l’adreça del proxy (ex : <kbd>127.0.0.1:1080</kbd> or <kbd>username:password@127.0.0.1:1080</kbd>)',	// DIRTY - Select a protocol (e.g: SOCKS5) and enter the proxy address (e.g: <kbd>127.0.0.1:1080</kbd> or <kbd>username:password@127.0.0.1:1080</kbd>)
 		'reset_favicon' => 'Reset to default',	// TODO
 		'reset_favicon' => 'Reset to default',	// TODO
 		'selector_preview' => array(
 		'selector_preview' => array(
 			'show_raw' => 'Veire lo còdi font',
 			'show_raw' => 'Veire lo còdi font',

+ 1 - 1
app/i18n/pt-BR/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Canal de Release',
 			'_' => 'Canal de Release',
 			'edge' => 'Release contínua (“edge”)',
 			'edge' => 'Release contínua (“edge”)',
-			'latest' => 'Release estável (“latest”)',	// DIRTY
+			'latest' => 'Release estável (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Sistema de atualização',
 		'title' => 'Sistema de atualização',
 		'viaGit' => 'Atualização via git e GitHub.com iniciada',
 		'viaGit' => 'Atualização via git e GitHub.com iniciada',

+ 1 - 1
app/i18n/ru/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Релизный канал',
 			'_' => 'Релизный канал',
 			'edge' => 'Плавающий релиз (“edge”)',
 			'edge' => 'Плавающий релиз (“edge”)',
-			'latest' => 'Стабильный релиз (“latest”)',	// DIRTY
+			'latest' => 'Стабильный релиз (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Обновить систему',
 		'title' => 'Обновить систему',
 		'viaGit' => 'Обновление с помощью git и GitHub.com запущено',
 		'viaGit' => 'Обновление с помощью git и GitHub.com запущено',

+ 2 - 2
app/i18n/ru/conf.php

@@ -308,8 +308,8 @@ return array(
 			'when' => 'Отмечать статью избранной…',
 			'when' => 'Отмечать статью избранной…',
 		),
 		),
 		'sticky_post' => 'Прикрепить статью к верху при открытии',
 		'sticky_post' => 'Прикрепить статью к верху при открытии',
-		'sticky_sort' => 'Сохранять ручной порядок сортировки при навигации',	// DIRTY
-		'sticky_sort_help' => 'Определяет, сохраняется ли последний ручной порядок сортировки или каждая категория либо лента всегда использует собственную настройку по умолчанию или глобальную.',	// DIRTY
+		'sticky_sort' => 'Сохранять ручной порядок сортировки при навигации',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Определяет, сохраняется ли последний ручной порядок сортировки или каждая категория либо лента всегда использует собственную настройку по умолчанию или глобальную.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Чтение',
 		'title' => 'Чтение',
 		'view' => array(
 		'view' => array(
 			'default' => 'Вид по умолчанию',
 			'default' => 'Вид по умолчанию',

+ 7 - 7
app/i18n/ru/gen.php

@@ -180,14 +180,14 @@ return array(
 		'confirm_exit_slider' => 'Вы уверены, что хотите отменить несохраненные настройки?',
 		'confirm_exit_slider' => 'Вы уверены, что хотите отменить несохраненные настройки?',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => '%d новых статей в FreshRSS.',	// DIRTY
-				1 => '%d новых статей в FreshRSS.',	// DIRTY
-				2 => '%d новых статей в FreshRSS.',	// DIRTY
+				0 => '%d новых статей в FreshRSS.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => '%d новых статей в FreshRSS.',	// DIRTY - There are %d new articles to read on FreshRSS.
+				2 => '%d новых статей в FreshRSS.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(Непрочитанные: %d)',	// DIRTY
-				1 => '(Непрочитанные: %d)',	// DIRTY
-				2 => '(Непрочитанные: %d)',	// DIRTY
+				0 => '(Непрочитанные: %d)',	// DIRTY - (unread: %d)
+				1 => '(Непрочитанные: %d)',	// DIRTY - (unread: %d)
+				2 => '(Непрочитанные: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Запрос не удался. Возможно, это вызвано проблемами с подключением к Интернет.',
 			'request_failed' => 'Запрос не удался. Возможно, это вызвано проблемами с подключением к Интернет.',
 			'title_new_articles' => 'FreshRSS: новые статьи!',
 			'title_new_articles' => 'FreshRSS: новые статьи!',
@@ -315,7 +315,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Закладки',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Закладки',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 2 - 2
app/i18n/sk/admin.php

@@ -14,7 +14,7 @@ return array(
 	'auth' => array(
 	'auth' => array(
 		'allow_anonymous' => 'Povoliť čítanie článkov prednastaveného používateľa (%s) bez prihlásenia.',
 		'allow_anonymous' => 'Povoliť čítanie článkov prednastaveného používateľa (%s) bez prihlásenia.',
 		'allow_anonymous_refresh' => 'Povoliť obnovenie článkov bez prihlásenia',
 		'allow_anonymous_refresh' => 'Povoliť obnovenie článkov bez prihlásenia',
-		'api_enabled' => 'Povoliť prístup cez <abbr>API</abbr> <small>(vyžadujú mobilné aplikácie and sharing user queries)</small>',	// DIRTY
+		'api_enabled' => 'Povoliť prístup cez <abbr>API</abbr> <small>(vyžadujú mobilné aplikácie and sharing user queries)</small>',	// DIRTY - Allow <abbr>API</abbr> access <small>(required for mobile apps and sharing user queries)</small>
 		'form' => 'Webový formulár (traditičný)',
 		'form' => 'Webový formulár (traditičný)',
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'http' => 'HTTP (advanced: managed by Web server, OIDC, SSO…)',	// TODO
 		'none' => 'Žiadny (nebezpečné)',
 		'none' => 'Žiadny (nebezpečné)',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Kanál verzií',
 			'_' => 'Kanál verzií',
 			'edge' => 'Vývojárska verzia (“edge”)',
 			'edge' => 'Vývojárska verzia (“edge”)',
-			'latest' => 'Stabilná verzia (“latest”)',	// DIRTY
+			'latest' => 'Stabilná verzia (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Aktualizácia systému',
 		'title' => 'Aktualizácia systému',
 		'viaGit' => 'Začala sa aktualizácia prostredníctvom git a GitHub.com',
 		'viaGit' => 'Začala sa aktualizácia prostredníctvom git a GitHub.com',

+ 5 - 5
app/i18n/sk/conf.php

@@ -17,7 +17,7 @@ return array(
 		'help' => 'Viac možností nájdete v nastaveniach kanála',
 		'help' => 'Viac možností nájdete v nastaveniach kanála',
 		'keep_favourites' => 'Nikdy nemazať obľúbené',
 		'keep_favourites' => 'Nikdy nemazať obľúbené',
 		'keep_labels' => 'Nikdy nemazať štítky',
 		'keep_labels' => 'Nikdy nemazať štítky',
-		'keep_max' => 'Maximálny počet článkov na zachovanie',	// DIRTY
+		'keep_max' => 'Maximálny počet článkov na zachovanie',	// DIRTY - Maximum number of articles to keep per feed
 		'keep_min_by_feed' => 'Minimálny počet článkov kanála na zachovanie',
 		'keep_min_by_feed' => 'Minimálny počet článkov kanála na zachovanie',
 		'keep_period' => 'Maximálny vek článkov na zachovanie',
 		'keep_period' => 'Maximálny vek článkov na zachovanie',
 		'keep_unreads' => 'Nikdy nemazať neprečítané články',
 		'keep_unreads' => 'Nikdy nemazať neprečítané články',
@@ -144,7 +144,7 @@ return array(
 		),
 		),
 	),
 	),
 	'mark_read_button' => array(
 	'mark_read_button' => array(
-		'_' => '“Označiť všetko ako prečítané” button',	// DIRTY
+		'_' => '“Označiť všetko ako prečítané” button',	// DIRTY - “Mark all as read” button
 		'big' => 'Big',	// TODO
 		'big' => 'Big',	// TODO
 		'none' => 'None',	// TODO
 		'none' => 'None',	// TODO
 		'small' => 'Small',	// TODO
 		'small' => 'Small',	// TODO
@@ -289,7 +289,7 @@ return array(
 			'when' => 'Označiť článok ako prečítaný…',
 			'when' => 'Označiť článok ako prečítaný…',
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_guid_in_category' => 'if an identical GUID already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
 			'when_same_title_in_category' => 'if an identical title already exists in the top <i>n</i> newest articles of the category',	// TODO
-			'when_same_title_in_feed' => 'ak rovnaký nadpis už existuje v TOP <i>n</i> najnovších článkoch (of the feed)',	// DIRTY
+			'when_same_title_in_feed' => 'ak rovnaký nadpis už existuje v TOP <i>n</i> najnovších článkoch (of the feed)',	// DIRTY - if an identical title already exists in the top <i>n</i> newest articles of the feed
 		),
 		),
 		'show' => array(
 		'show' => array(
 			'_' => 'Článkov na zobrazenie',
 			'_' => 'Článkov na zobrazenie',
@@ -308,8 +308,8 @@ return array(
 			'when' => 'Mark an article as favourite…',	// TODO
 			'when' => 'Mark an article as favourite…',	// TODO
 		),
 		),
 		'sticky_post' => 'Po otvorení posunúť článok hore',
 		'sticky_post' => 'Po otvorení posunúť článok hore',
-		'sticky_sort' => 'Zachovať ručné zoradenie počas navigácie',	// DIRTY
-		'sticky_sort_help' => 'Určuje, či zostane zachované posledné ručné zoradenie, alebo či každá kategória či kanál vždy použije vlastné predvolené alebo globálne nastavenie.',	// DIRTY
+		'sticky_sort' => 'Zachovať ručné zoradenie počas navigácie',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Určuje, či zostane zachované posledné ručné zoradenie, alebo či každá kategória či kanál vždy použije vlastné predvolené alebo globálne nastavenie.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Čítanie',
 		'title' => 'Čítanie',
 		'view' => array(
 		'view' => array(
 			'default' => 'Prednastavené zobrazenie',
 			'default' => 'Prednastavené zobrazenie',

+ 7 - 7
app/i18n/sk/gen.php

@@ -180,14 +180,14 @@ return array(
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'confirm_exit_slider' => 'Are you sure you want to discard unsaved settings?',	// TODO
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'Počet nových článkov v čítačke FreshRSS: %d',	// DIRTY
-				1 => 'Počet nových článkov v čítačke FreshRSS: %d',	// DIRTY
-				2 => 'Počet nových článkov v čítačke FreshRSS: %d',	// DIRTY
+				0 => 'Počet nových článkov v čítačke FreshRSS: %d',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'Počet nových článkov v čítačke FreshRSS: %d',	// DIRTY - There are %d new articles to read on FreshRSS.
+				2 => 'Počet nových článkov v čítačke FreshRSS: %d',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(neprečítané: %d)',	// DIRTY
-				1 => '(neprečítané: %d)',	// DIRTY
-				2 => '(neprečítané: %d)',	// DIRTY
+				0 => '(neprečítané: %d)',	// DIRTY - (unread: %d)
+				1 => '(neprečítané: %d)',	// DIRTY - (unread: %d)
+				2 => '(neprečítané: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Nepodarilo sa spracovať váš dopyt, pravdepodobne kvôli problému s pripojením do internetu.',
 			'request_failed' => 'Nepodarilo sa spracovať váš dopyt, pravdepodobne kvôli problému s pripojením do internetu.',
 			'title_new_articles' => 'FreshRSS: nové články!',
 			'title_new_articles' => 'FreshRSS: nové články!',
@@ -315,7 +315,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Záložky',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Záložky',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 8 - 8
app/i18n/sk/sub.php

@@ -70,17 +70,17 @@ return array(
 		),
 		),
 		'description' => 'Popis',
 		'description' => 'Popis',
 		'empty' => 'Tento kanál je prázdny. Overte, prosím, či je ešte spravovaný autorom.',
 		'empty' => 'Tento kanál je prázdny. Overte, prosím, či je ešte spravovaný autorom.',
-		'error' => 'Vyskytol sa problém s týmto kanálom. Overte, prosím, či kanál stále existuje.',	// DIRTY
+		'error' => 'Vyskytol sa problém s týmto kanálom. Overte, prosím, či kanál stále existuje.',	// DIRTY - This feed has encountered a problem. If this situation persists, please verify that it is still reachable.
 		'export-as-opml' => array(
 		'export-as-opml' => array(
 			'download' => 'Stiahnuť',
 			'download' => 'Stiahnuť',
-			'help' => 'XML súbor (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// DIRTY
+			'help' => 'XML súbor (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)',	// DIRTY - XML file (data subset. <a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">See documentation</a>)
 			'label' => 'Exportovať ako OPML',
 			'label' => 'Exportovať ako OPML',
 		),
 		),
 		'ext_favicon' => 'Set automatically',	// TODO
 		'ext_favicon' => 'Set automatically',	// TODO
 		'favicon_changed_by_ext' => 'The icon has been set by the <b>%s</b> extension.',	// TODO
 		'favicon_changed_by_ext' => 'The icon has been set by the <b>%s</b> extension.',	// TODO
 		'filteractions' => array(
 		'filteractions' => array(
 			'_' => 'Filtrovať akcie',
 			'_' => 'Filtrovať akcie',
-			'help' => 'Napíšte jeden výraz hľadania na riadok. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.',	// DIRTY
+			'help' => 'Napíšte jeden výraz hľadania na riadok. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.',	// DIRTY - Write one search filter per line. Operators <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">see documentation</a>.
 			'view_filter' => 'Preview filters on existing articles (new window)',	// TODO
 			'view_filter' => 'Preview filters on existing articles (new window)',	// TODO
 		),
 		),
 		'global_hint' => 'Use <a href="%s">the global view</a> to see how many articles in each feed are matching a state or a search expression',	// TODO
 		'global_hint' => 'Use <a href="%s">the global view</a> to see how many articles in each feed are matching a state or a search expression',	// TODO
@@ -129,11 +129,11 @@ return array(
 				),
 				),
 				'item_timestamp' => array(
 				'item_timestamp' => array(
 					'_' => 'položka dátum',
 					'_' => 'položka dátum',
-					'help' => 'Výsledok spracuje <a href="https://www.php.net/strtotime" target="_blank"><code>strtotime()</code></a>',	// DIRTY
+					'help' => 'Výsledok spracuje <a href="https://www.php.net/strtotime" target="_blank"><code>strtotime()</code></a>',	// DIRTY - The result will be parsed by <a href="https://www.php.net/strtotime" target="_blank"><code>strtotime()</code></a>
 				),
 				),
 				'item_title' => array(
 				'item_title' => array(
 					'_' => 'položka nadpis',
 					'_' => 'položka nadpis',
-					'help' => 'Použite hlavne <a href="https://developer.mozilla.org/docs/Web/XML/XPath/Reference/Axes" target="_blank">XPath axis</a> <code>descendant::</code> like <code>descendant::h2</code>',	// DIRTY
+					'help' => 'Použite hlavne <a href="https://developer.mozilla.org/docs/Web/XML/XPath/Reference/Axes" target="_blank">XPath axis</a> <code>descendant::</code> like <code>descendant::h2</code>',	// DIRTY - Use in particular the <a href="https://developer.mozilla.org/docs/Web/XML/XPath/Reference/Axes" target="_blank">XPath axis</a> <code>descendant::</code> like <code>descendant::h2</code>
 				),
 				),
 				'item_uid' => array(
 				'item_uid' => array(
 					'_' => 'položka unikátny identifikátor',
 					'_' => 'položka unikátny identifikátor',
@@ -155,7 +155,7 @@ return array(
 				'help' => 'JSON so zápisom s bodkou používa bodky na oddelenie objekov a zložené zátvorky pre polia (príklad: <code>data.items[0].title</code>)',
 				'help' => 'JSON so zápisom s bodkou používa bodky na oddelenie objekov a zložené zátvorky pre polia (príklad: <code>data.items[0].title</code>)',
 				'item' => array(
 				'item' => array(
 					'_' => 'hľadajú sa <strong>položky</strong> noviniek<br /><small>(najdôležitejšie)</small>',
 					'_' => 'hľadajú sa <strong>položky</strong> noviniek<br /><small>(najdôležitejšie)</small>',
-					'help' => 'JSON cesta k polu obsahujúce položky, príklad: <code>$</code> or <code>newsItems</code>',	// DIRTY
+					'help' => 'JSON cesta k polu obsahujúce položky, príklad: <code>$</code> or <code>newsItems</code>',	// DIRTY - JSON path to the array containing the items, e.g. <code>$</code> or <code>newsItems</code>
 				),
 				),
 				'item_author' => 'autor položky',
 				'item_author' => 'autor položky',
 				'item_categories' => 'značky položky',
 				'item_categories' => 'značky položky',
@@ -196,7 +196,7 @@ return array(
 			'clear_cache' => 'Vymazať vyrovnáciu pamäť',
 			'clear_cache' => 'Vymazať vyrovnáciu pamäť',
 			'clear_cache_help' => 'Vymazať vyrovnáciu pamäť pre tento kanál.',
 			'clear_cache_help' => 'Vymazať vyrovnáciu pamäť pre tento kanál.',
 			'reload_articles' => 'Obnoviť články',
 			'reload_articles' => 'Obnoviť články',
-			'reload_articles_help' => 'Obnoviť články a stiahnuť kompletný obsah, ak je definovaný selektor.',	// DIRTY
+			'reload_articles_help' => 'Obnoviť články a stiahnuť kompletný obsah, ak je definovaný selektor.',	// DIRTY - Reload that many articles and fetch complete content if a selector is defined.
 			'title' => 'Údržba',
 			'title' => 'Údržba',
 		),
 		),
 		'max_http_redir' => 'Max HTTP presmerovaní',
 		'max_http_redir' => 'Max HTTP presmerovaní',
@@ -224,7 +224,7 @@ return array(
 			'main_stream' => 'Zobraziť v prehľade kanálov',
 			'main_stream' => 'Zobraziť v prehľade kanálov',
 		),
 		),
 		'proxy' => 'Na sťahovanie tohto kanálu nastaviť proxy',
 		'proxy' => 'Na sťahovanie tohto kanálu nastaviť proxy',
-		'proxy_help' => 'Vyberte protokol (napr.: SOCKS5) a zadajte adresu proxy servera (napr.: <kbd>127.0.0.1:1080</kbd> or <kbd>username:password@127.0.0.1:1080</kbd>)',	// DIRTY
+		'proxy_help' => 'Vyberte protokol (napr.: SOCKS5) a zadajte adresu proxy servera (napr.: <kbd>127.0.0.1:1080</kbd> or <kbd>username:password@127.0.0.1:1080</kbd>)',	// DIRTY - Select a protocol (e.g: SOCKS5) and enter the proxy address (e.g: <kbd>127.0.0.1:1080</kbd> or <kbd>username:password@127.0.0.1:1080</kbd>)
 		'reset_favicon' => 'Reset to default',	// TODO
 		'reset_favicon' => 'Reset to default',	// TODO
 		'selector_preview' => array(
 		'selector_preview' => array(
 			'show_raw' => 'Zobraziť zdrojový kód',
 			'show_raw' => 'Zobraziť zdrojový kód',

+ 1 - 1
app/i18n/tr/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Yayın kanalı',
 			'_' => 'Yayın kanalı',
 			'edge' => 'Sürekli yayın (“edge”)',
 			'edge' => 'Sürekli yayın (“edge”)',
-			'latest' => 'Kararlı yayın (“latest”)',	// DIRTY
+			'latest' => 'Kararlı yayın (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'FreshRSS’yi Güncelle',
 		'title' => 'FreshRSS’yi Güncelle',
 		'viaGit' => 'Git ve GitHub.com üzerinden güncelleme başlatıldı',
 		'viaGit' => 'Git ve GitHub.com üzerinden güncelleme başlatıldı',

+ 2 - 2
app/i18n/tr/conf.php

@@ -308,8 +308,8 @@ return array(
 			'when' => 'Bir makaleyi favori olarak işaretle…',
 			'when' => 'Bir makaleyi favori olarak işaretle…',
 		),
 		),
 		'sticky_post' => 'Makale açıldığında üstte sabitle',
 		'sticky_post' => 'Makale açıldığında üstte sabitle',
-		'sticky_sort' => 'Gezinme sırasında el ile belirlenen sıralama düzenini koru',	// DIRTY
-		'sticky_sort_help' => 'Son el ile belirlenen sıralama düzeninin etkin kalıp kalmayacağını ya da her kategori veya beslemenin her zaman kendi varsayılan veya genel ayarını kullanacağını belirler.',	// DIRTY
+		'sticky_sort' => 'Gezinme sırasında el ile belirlenen sıralama düzenini koru',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Son el ile belirlenen sıralama düzeninin etkin kalıp kalmayacağını ya da her kategori veya beslemenin her zaman kendi varsayılan veya genel ayarını kullanacağını belirler.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Okuma',
 		'title' => 'Okuma',
 		'view' => array(
 		'view' => array(
 			'default' => 'Varsayılan görünüm',
 			'default' => 'Varsayılan görünüm',

+ 5 - 5
app/i18n/tr/gen.php

@@ -174,12 +174,12 @@ return array(
 		'confirm_exit_slider' => 'Kaydedilmemiş ayarları atmak istediğinizden emin misiniz?',
 		'confirm_exit_slider' => 'Kaydedilmemiş ayarları atmak istediğinizden emin misiniz?',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'FreshRSS’de okunacak %d yeni makale var.',	// DIRTY
-				1 => 'FreshRSS’de okunacak %d yeni makale var.',	// DIRTY
+				0 => 'FreshRSS’de okunacak %d yeni makale var.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'FreshRSS’de okunacak %d yeni makale var.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(okunmamış: %d)',	// DIRTY
-				1 => '(okunmamış: %d)',	// DIRTY
+				0 => '(okunmamış: %d)',	// DIRTY - (unread: %d)
+				1 => '(okunmamış: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Bir istek başarısız oldu, bu internet bağlantı sorunlarından kaynaklanmış olabilir.',
 			'request_failed' => 'Bir istek başarısız oldu, bu internet bağlantı sorunlarından kaynaklanmış olabilir.',
 			'title_new_articles' => 'FreshRSS: yeni makaleler!',
 			'title_new_articles' => 'FreshRSS: yeni makaleler!',
@@ -307,7 +307,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Yer imleri',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Yer imleri',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 1 - 1
app/i18n/uk/admin.php

@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => 'Канал випусків',
 			'_' => 'Канал випусків',
 			'edge' => 'Крайній випуск («edge»)',
 			'edge' => 'Крайній випуск («edge»)',
-			'latest' => 'Стабільний випуск («latest»)',	// DIRTY
+			'latest' => 'Стабільний випуск («latest»)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => 'Оновити FreshRSS',
 		'title' => 'Оновити FreshRSS',
 		'viaGit' => 'Розпочато оновлення за допомогою git і GitHub.com',
 		'viaGit' => 'Розпочато оновлення за допомогою git і GitHub.com',

+ 2 - 2
app/i18n/uk/conf.php

@@ -308,8 +308,8 @@ return array(
 			'when' => 'Вподобати статтю…',
 			'when' => 'Вподобати статтю…',
 		),
 		),
 		'sticky_post' => 'Закріплювати статтю вгорі при відкритті',
 		'sticky_post' => 'Закріплювати статтю вгорі при відкритті',
-		'sticky_sort' => 'Зберігати ручний порядок сортування під час навігації',	// DIRTY
-		'sticky_sort_help' => 'Визначає, чи зберігається останній ручний порядок сортування, чи кожна категорія або стрічка завжди використовує власне типове або глобальне налаштування.',	// DIRTY
+		'sticky_sort' => 'Зберігати ручний порядок сортування під час навігації',	// DIRTY - Keep custom sort order during navigation
+		'sticky_sort_help' => 'Визначає, чи зберігається останній ручний порядок сортування, чи кожна категорія або стрічка завжди використовує власне типове або глобальне налаштування.',	// DIRTY - Determines whether the last custom sort order is kept active or whether each category or feed always uses its own default or global setting.
 		'title' => 'Читання',
 		'title' => 'Читання',
 		'view' => array(
 		'view' => array(
 			'default' => 'Типовий показ',
 			'default' => 'Типовий показ',

+ 7 - 7
app/i18n/uk/gen.php

@@ -180,14 +180,14 @@ return array(
 		'confirm_exit_slider' => 'Точно відкинути незбережені параметри?',
 		'confirm_exit_slider' => 'Точно відкинути незбережені параметри?',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'Наявні нові статті (%d) у FreshRSS.',	// DIRTY
-				1 => 'Наявні нові статті (%d) у FreshRSS.',	// DIRTY
-				2 => 'Наявні нові статті (%d) у FreshRSS.',	// DIRTY
+				0 => 'Наявні нові статті (%d) у FreshRSS.',	// DIRTY - There is %d new article to read on FreshRSS.
+				1 => 'Наявні нові статті (%d) у FreshRSS.',	// DIRTY - There are %d new articles to read on FreshRSS.
+				2 => 'Наявні нові статті (%d) у FreshRSS.',	// DIRTY - There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(непрочитано: %d)',	// DIRTY
-				1 => '(непрочитано: %d)',	// DIRTY
-				2 => '(непрочитано: %d)',	// DIRTY
+				0 => '(непрочитано: %d)',	// DIRTY - (unread: %d)
+				1 => '(непрочитано: %d)',	// DIRTY - (unread: %d)
+				2 => '(непрочитано: %d)',	// DIRTY - (unread: %d)
 			),
 			),
 			'request_failed' => 'Не вдалося виконати запит. Можливо, інтернет-зʼєднання нестабільне.',
 			'request_failed' => 'Не вдалося виконати запит. Можливо, інтернет-зʼєднання нестабільне.',
 			'title_new_articles' => 'FreshRSS: нові статті!',
 			'title_new_articles' => 'FreshRSS: нові статті!',
@@ -315,7 +315,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud Закладки',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud Закладки',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 3 - 3
app/i18n/zh-CN/admin.php

@@ -31,7 +31,7 @@ return array(
 		'empty_list' => '没有已安装的扩展',
 		'empty_list' => '没有已安装的扩展',
 		'empty_list_help' => '检查日志以确定扩展列表为空的原因。',
 		'empty_list_help' => '检查日志以确定扩展列表为空的原因。',
 		'enabled' => '已启用',
 		'enabled' => '已启用',
-		'is_compatible' => '兼容',	// DIRTY
+		'is_compatible' => '兼容',	// DIRTY - Is compatible
 		'latest' => '已安装',
 		'latest' => '已安装',
 		'name' => '名称',
 		'name' => '名称',
 		'no_configure_view' => '此扩展无法配置。',
 		'no_configure_view' => '此扩展无法配置。',
@@ -67,7 +67,7 @@ return array(
 		'number_entries' => '%d 篇文章',
 		'number_entries' => '%d 篇文章',
 		'overview' => '概览',
 		'overview' => '概览',
 		'percent_of_total' => '%',
 		'percent_of_total' => '%',
-		'repartition' => '文章分布: %s',	// DIRTY
+		'repartition' => '文章分布: %s',	// DIRTY - Articles repartition: %s
 		'status_favorites' => '收藏',
 		'status_favorites' => '收藏',
 		'status_read' => '已读',
 		'status_read' => '已读',
 		'status_total' => '总计',
 		'status_total' => '总计',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => '发布通道',
 			'_' => '发布通道',
 			'edge' => '滚动发布 (“edge”)',
 			'edge' => '滚动发布 (“edge”)',
-			'latest' => '稳定版本 (“latest”)',	// DIRTY
+			'latest' => '稳定版本 (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => '更新系统',
 		'title' => '更新系统',
 		'viaGit' => '开始通过 git 和 GitHub.com 更新',
 		'viaGit' => '开始通过 git 和 GitHub.com 更新',

+ 2 - 2
app/i18n/zh-CN/api.php

@@ -15,7 +15,7 @@ return array(
 		'address' => '你的 API 地址:',
 		'address' => '你的 API 地址:',
 		'output' => array(
 		'output' => array(
 			'encoding-support' => '⚠️ 警告:<code>%2F</code> 支持缺失,部分客户端可能无法正常工作!',
 			'encoding-support' => '⚠️ 警告:<code>%2F</code> 支持缺失,部分客户端可能无法正常工作!',
-			'invalid-configuration' => '⚠️ 警告:./data/config.php 中可能存在无效的 base URL',	// DIRTY
+			'invalid-configuration' => '⚠️ 警告:./data/config.php 中可能存在无效的 base URL',	// DIRTY - ⚠️ WARN: Probable invalid base URL in ./data/config.php
 			'pass' => '✔️ 通过',
 			'pass' => '✔️ 通过',
 			'unknown-error' => '❌ ',	// IGNORE
 			'unknown-error' => '❌ ',	// IGNORE
 		),
 		),
@@ -24,7 +24,7 @@ return array(
 			'greader' => 'Google Reader API 配置测试',
 			'greader' => 'Google Reader API 配置测试',
 		),
 		),
 		'title' => array(
 		'title' => array(
-			'_' => 'FreshRSS API 端点',	// DIRTY
+			'_' => 'FreshRSS API 端点',	// DIRTY - FreshRSS API endpoints
 			'extension' => '供拓展使用的 API',
 			'extension' => '供拓展使用的 API',
 			'fever' => 'Fever 兼容的 API',
 			'fever' => 'Fever 兼容的 API',
 			'greader' => 'Google Reader 兼容的 API',
 			'greader' => 'Google Reader 兼容的 API',

+ 1 - 1
app/i18n/zh-CN/conf.php

@@ -238,7 +238,7 @@ return array(
 				'_' => '已禁用',
 				'_' => '已禁用',
 				'title' => '分享',
 				'title' => '分享',
 			),
 			),
-			'greader' => 'GReader JSON 的分享链接',	// DIRTY
+			'greader' => 'GReader JSON 的分享链接',	// DIRTY - Shareable link to the GReader JSON
 			'help' => '获取此自定义查询的分享链接',
 			'help' => '获取此自定义查询的分享链接',
 			'html' => 'HTML 页面的分享链接',
 			'html' => 'HTML 页面的分享链接',
 			'opml' => '订阅源 OPML 的分享链接',
 			'opml' => '订阅源 OPML 的分享链接',

+ 1 - 1
app/i18n/zh-CN/feedback.php

@@ -62,7 +62,7 @@ return array(
 		'feeds_imported_with_errors' => '你的订阅源已导入,但发生错误。你可以点击 <i>更新订阅</i> 按钮以完成导入。',
 		'feeds_imported_with_errors' => '你的订阅源已导入,但发生错误。你可以点击 <i>更新订阅</i> 按钮以完成导入。',
 		'file_cannot_be_uploaded' => '文件未能上传!',
 		'file_cannot_be_uploaded' => '文件未能上传!',
 		'no_zip_extension' => '服务器未启用 ZIP 扩展。',
 		'no_zip_extension' => '服务器未启用 ZIP 扩展。',
-		'zip_error' => '导入 ZIP 文件时出错',	// DIRTY
+		'zip_error' => '导入 ZIP 文件时出错',	// DIRTY - An error occurred during ZIP processing.
 	),
 	),
 	'profile' => array(
 	'profile' => array(
 		'error' => '你的帐户无法修改',
 		'error' => '你的帐户无法修改',

+ 5 - 5
app/i18n/zh-CN/gen.php

@@ -168,10 +168,10 @@ return array(
 		'confirm_exit_slider' => '您确定要放弃未保存的设置吗?',
 		'confirm_exit_slider' => '您确定要放弃未保存的设置吗?',
 		'feedback' => array(
 		'feedback' => array(
 			'body_new_articles' => array(
 			'body_new_articles' => array(
-				0 => 'FreshRSS 中有 %d 篇文章等待阅读。',	// DIRTY
+				0 => 'FreshRSS 中有 %d 篇文章等待阅读。',	// DIRTY - Singular: There is %d new article to read on FreshRSS. | Plural: There are %d new articles to read on FreshRSS.
 			),
 			),
 			'body_unread_articles' => array(
 			'body_unread_articles' => array(
-				0 => '(未读: %d)',	// DIRTY
+				0 => '(未读: %d)',	// DIRTY - Singular: (unread: %d) | Plural: (unread: %d)
 			),
 			),
 			'request_failed' => '请求失败,这可能是因为网络连接问题。',
 			'request_failed' => '请求失败,这可能是因为网络连接问题。',
 			'title_new_articles' => 'FreshRSS: 新文章!',
 			'title_new_articles' => 'FreshRSS: 新文章!',
@@ -258,14 +258,14 @@ return array(
 		'content' => '内容',
 		'content' => '内容',
 		'date_from' => '从',
 		'date_from' => '从',
 		'date_modified' => 'Server Modification Date',	// TODO
 		'date_modified' => 'Server Modification Date',	// TODO
-		'date_past' => '过去',	// DIRTY
+		'date_past' => '过去',	// DIRTY - In the past
 		'date_published' => '发布日期',
 		'date_published' => '发布日期',
 		'date_range' => '日期范围',
 		'date_range' => '日期范围',
 		'date_received' => '接收日期',
 		'date_received' => '接收日期',
 		'date_to' => '到',
 		'date_to' => '到',
 		'date_user' => '用户修改日期',
 		'date_user' => '用户修改日期',
 		'feeds' => '订阅源',
 		'feeds' => '订阅源',
-		'free_text' => '自由文本',	// DIRTY
+		'free_text' => '自由文本',	// DIRTY - Free Text
 		'free_text_help' => '在标题和内容中搜索',
 		'free_text_help' => '在标题和内容中搜索',
 		'full_documentation' => '查看 <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">完整搜索文档</a>',
 		'full_documentation' => '查看 <a href="https://freshrss.github.io/FreshRSS/en/users/10_filter.html#with-the-search-field" target="_blank">完整搜索文档</a>',
 		'labels' => '我的标签',
 		'labels' => '我的标签',
@@ -299,7 +299,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud 书签',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud 书签',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 6 - 6
app/i18n/zh-CN/sub.php

@@ -70,7 +70,7 @@ return array(
 		),
 		),
 		'description' => '描述',
 		'description' => '描述',
 		'empty' => '此源为空。请确认它是否正常更新。',
 		'empty' => '此源为空。请确认它是否正常更新。',
-		'error' => '此源遇到一些问题。请在确认是否能正常访问后重试。',	// DIRTY
+		'error' => '此源遇到一些问题。请在确认是否能正常访问后重试。',	// DIRTY - This feed has encountered a problem. If this situation persists, please verify that it is still reachable.
 		'export-as-opml' => array(
 		'export-as-opml' => array(
 			'download' => '下载',
 			'download' => '下载',
 			'help' => 'XML 文件(数据子集。<a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">查看文档</a>)',
 			'help' => 'XML 文件(数据子集。<a href="https://freshrss.github.io/FreshRSS/en/developers/OPML.html" target="_blank">查看文档</a>)',
@@ -93,9 +93,9 @@ return array(
 		'kind' => array(
 		'kind' => array(
 			'_' => '订阅源类型',
 			'_' => '订阅源类型',
 			'html_json' => array(
 			'html_json' => array(
-				'_' => 'HTML + XPath + JSON 点表示法(HTML 中的 JSON)',	// DIRTY
+				'_' => 'HTML + XPath + JSON 点表示法(HTML 中的 JSON)',	// DIRTY - HTML + XPath + JSON dot notation (JSON in HTML)
 				'xpath' => array(
 				'xpath' => array(
-					'_' => '用于 HTML 中 JSON 的 XPath',	// DIRTY
+					'_' => '用于 HTML 中 JSON 的 XPath',	// DIRTY - XPath for JSON in HTML
 					'help' => '示例:<code>normalize-space(//script[@type="application/json"])</code>(单个 JSON)<br />或:<code>//script[@type="application/ld+json"]</code>(每篇文章一个 JSON 对象)',
 					'help' => '示例:<code>normalize-space(//script[@type="application/json"])</code>(单个 JSON)<br />或:<code>//script[@type="application/ld+json"]</code>(每篇文章一个 JSON 对象)',
 				),
 				),
 			),
 			),
@@ -155,7 +155,7 @@ return array(
 				'help' => 'JSON 点表达式(JSON 路径)在对象之间使用点,在数组中使用中括号 (例如 <code>data.items[0].title</code>)',
 				'help' => 'JSON 点表达式(JSON 路径)在对象之间使用点,在数组中使用中括号 (例如 <code>data.items[0].title</code>)',
 				'item' => array(
 				'item' => array(
 					'_' => '寻找新的 <strong>文章</strong><br /><small>(最重要的参数)</small>',
 					'_' => '寻找新的 <strong>文章</strong><br /><small>(最重要的参数)</small>',
-					'help' => '包含文章数组的 JSON 路径,例如 <code>$</code> 或 <code>newsItems</code>',	// DIRTY
+					'help' => '包含文章数组的 JSON 路径,例如 <code>$</code> 或 <code>newsItems</code>',	// DIRTY - JSON path to the array containing the items, e.g. <code>$</code> or <code>newsItems</code>
 				),
 				),
 				'item_author' => '文章作者',
 				'item_author' => '文章作者',
 				'item_categories' => '文章标签',
 				'item_categories' => '文章标签',
@@ -209,7 +209,7 @@ return array(
 		'moved_category_deleted' => '删除分类时,其中的订阅源会自动归类到 <em>%s</em>',
 		'moved_category_deleted' => '删除分类时,其中的订阅源会自动归类到 <em>%s</em>',
 		'mute' => array(
 		'mute' => array(
 			'_' => '暂停',
 			'_' => '暂停',
-			'state_is_muted' => '此订阅源已静音',	// DIRTY
+			'state_is_muted' => '此订阅源已静音',	// DIRTY - This feed is muted
 		),
 		),
 		'no_selected' => '未选择订阅源',
 		'no_selected' => '未选择订阅源',
 		'number_entries' => '%d 篇文章',
 		'number_entries' => '%d 篇文章',
@@ -218,7 +218,7 @@ return array(
 		'priority' => array(
 		'priority' => array(
 			'_' => '可见性',
 			'_' => '可见性',
 			'category' => '在分类中显示',
 			'category' => '在分类中显示',
-			'feed' => '在其订阅源中显示',	// DIRTY
+			'feed' => '在其订阅源中显示',	// DIRTY - Show in its feed
 			'hidden' => '不显示',
 			'hidden' => '不显示',
 			'important' => '在“重要的订阅”中显示',
 			'important' => '在“重要的订阅”中显示',
 			'main_stream' => '在首页中显示',
 			'main_stream' => '在首页中显示',

+ 2 - 2
app/i18n/zh-TW/admin.php

@@ -15,7 +15,7 @@ return array(
 		'allow_anonymous' => '允許匿名閱讀預設使用者的文章 (%s)',
 		'allow_anonymous' => '允許匿名閱讀預設使用者的文章 (%s)',
 		'allow_anonymous_refresh' => '允許匿名刷新文章',
 		'allow_anonymous_refresh' => '允許匿名刷新文章',
 		'api_enabled' => '允許 <abbr>API</abbr> 存取 <small>(行動應用程式與分享使用者查詢需要)</small>',
 		'api_enabled' => '允許 <abbr>API</abbr> 存取 <small>(行動應用程式與分享使用者查詢需要)</small>',
-		'form' => 'Web 表單 (傳統方式,需要 JavaScript)',	// DIRTY
+		'form' => 'Web 表單 (傳統方式,需要 JavaScript)',	// DIRTY - Web form (traditional)
 		'http' => 'HTTP (進階: 由 Web 伺服器、OIDC、SSO 等管理)',
 		'http' => 'HTTP (進階: 由 Web 伺服器、OIDC、SSO 等管理)',
 		'none' => '無 (危險)',
 		'none' => '無 (危險)',
 		'title' => '身份驗證',
 		'title' => '身份驗證',
@@ -137,7 +137,7 @@ return array(
 		'releaseChannel' => array(
 		'releaseChannel' => array(
 			'_' => '發行方式',
 			'_' => '發行方式',
 			'edge' => '滾動發行 (“edge”)',
 			'edge' => '滾動發行 (“edge”)',
-			'latest' => '穩定發行 (“latest”)',	// DIRTY
+			'latest' => '穩定發行 (“latest”)',	// DIRTY - Versioned release (“latest”)
 		),
 		),
 		'title' => '更新 FreshRSS',
 		'title' => '更新 FreshRSS',
 		'viaGit' => '透過 git 和 GitHub.com 開始更新',
 		'viaGit' => '透過 git 和 GitHub.com 開始更新',

+ 1 - 1
app/i18n/zh-TW/gen.php

@@ -301,7 +301,7 @@ return array(
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'linkedin' => 'LinkedIn',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'mastodon' => 'Mastodon',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
 		'movim' => 'Movim',	// IGNORE
-		'nextcloud-bookmarks' => 'Nextcloud 書籤',	// DIRTY
+		'nextcloud-bookmarks' => 'Nextcloud 書籤',	// DIRTY - Nextcloud Bookmarks
 		'omnivore' => 'Omnivore',	// IGNORE
 		'omnivore' => 'Omnivore',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinboard' => 'Pinboard',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE
 		'pinterest' => 'Pinterest',	// IGNORE

+ 1 - 1
cli/check.translation.php

@@ -141,7 +141,7 @@ if ($cliOptions->generateReadme) {
 		$uncompleted = intval(ceil((100 - $percentageInt) / 10));
 		$uncompleted = intval(ceil((100 - $percentageInt) / 10));
 		$progressBar = str_repeat('■', $completed) . str_repeat('・', $uncompleted);
 		$progressBar = str_repeat('■', $completed) . str_repeat('・', $uncompleted);
 
 
-		$ghSearchUrl = 'https://github.com/search?q=' . urlencode("repo:FreshRSS/FreshRSS path:app/i18n/$lang /(TODO|DIRTY)$/");
+		$ghSearchUrl = 'https://github.com/search?q=' . urlencode("repo:FreshRSS/FreshRSS path:app/i18n/$lang /(TODO|DIRTY)( - .*)?$/");
 
 
 		$markdownTable .= '| ' . implode(' | ', [
 		$markdownTable .= '| ' . implode(' | ', [
 			_t('gen.lang.' . $lang) . " ($lang)",
 			_t('gen.lang.' . $lang) . " ($lang)",

+ 29 - 4
cli/i18n/I18nFile.php

@@ -55,7 +55,8 @@ class I18nFile {
 			}
 			}
 			foreach ($file as $name => $content) {
 			foreach ($file as $name => $content) {
 				$filename = $dir . DIRECTORY_SEPARATOR . $name;
 				$filename = $dir . DIRECTORY_SEPARATOR . $name;
-				file_put_contents($filename, $this->format($content));
+				$reference = $language === 'en' ? [] : ($i18n['en'][$name] ?? []);
+				file_put_contents($filename, $this->format($content, $reference));
 			}
 			}
 		}
 		}
 	}
 	}
@@ -145,6 +146,10 @@ class I18nFile {
 				$normalisedKey = ctype_digit($key) ? (int)$key : $key;
 				$normalisedKey = ctype_digit($key) ? (int)$key : $key;
 				if ($index === $lastIndex) {
 				if ($index === $lastIndex) {
 					$current[$normalisedKey] = $value->__toString();
 					$current[$normalisedKey] = $value->__toString();
+					if ($value->isDirty()) {
+						// Carry the key through var_export, including when several keys share the same translation.
+						$current[$normalisedKey] .= ':' . base64_encode($compoundKey);
+					}
 					continue;
 					continue;
 				}
 				}
 				if (!isset($current[$normalisedKey]) || !is_array($current[$normalisedKey])) {
 				if (!isset($current[$normalisedKey]) || !is_array($current[$normalisedKey])) {
@@ -166,12 +171,13 @@ class I18nFile {
 	 * formatting regexes are applied to match the original content.
 	 * formatting regexes are applied to match the original content.
 	 *
 	 *
 	 * @param array<string,I18nValue> $translation
 	 * @param array<string,I18nValue> $translation
+	 * @param array<string,I18nValue> $reference
 	 */
 	 */
-	private function format(array $translation): string {
+	private function format(array $translation, array $reference = []): string {
+		$translationKeys = array_fill_keys(array_keys($translation), true);
 		$translation = var_export($this->unflatten($translation), true);
 		$translation = var_export($this->unflatten($translation), true);
 		$patterns = [
 		$patterns = [
 			'/ -> todo\',/',
 			'/ -> todo\',/',
-			'/ -> dirty\',/',
 			'/ -> ignore\',/',
 			'/ -> ignore\',/',
 			'/array \(/',
 			'/array \(/',
 			'/=>\s*array/',
 			'/=>\s*array/',
@@ -180,7 +186,6 @@ class I18nFile {
 		];
 		];
 		$replacements = [
 		$replacements = [
 			"',\t// TODO", // Double quoting is mandatory to have a tab instead of the \t string
 			"',\t// TODO", // Double quoting is mandatory to have a tab instead of the \t string
-			"',\t// DIRTY", // Double quoting is mandatory to have a tab instead of the \t string
 			"',\t// IGNORE", // Double quoting is mandatory to have a tab instead of the \t string
 			"',\t// IGNORE", // Double quoting is mandatory to have a tab instead of the \t string
 			'array(',
 			'array(',
 			'=> array',
 			'=> array',
@@ -188,6 +193,26 @@ class I18nFile {
 			"\t", // Double quoting is mandatory to have a tab instead of the \t string
 			"\t", // Double quoting is mandatory to have a tab instead of the \t string
 		];
 		];
 		$translation = preg_replace($patterns, $replacements, $translation);
 		$translation = preg_replace($patterns, $replacements, $translation);
+		$translation = preg_replace_callback('/ -> dirty:([A-Za-z0-9+\/=]+)\',/', static function (array $matches) use ($reference, $translationKeys): string {
+			$key = base64_decode($matches[1], true);
+			$original = $key === false ? null : ($reference[$key] ?? null);
+			if ($original === null && $key !== false && preg_match('/^(.+)\.\d+$/', $key, $parts) === 1) {
+				$original = $reference[$parts[1] . '.1'] ?? $reference[$parts[1] . '.0'] ?? null;
+			}
+			if ($original === null) {
+				return "',\t// DIRTY";
+			}
+
+			$comment = $original->getValue();
+			if ($key !== false && preg_match('/^(.+)\.0$/', $key, $parts) === 1
+				&& !isset($translationKeys[$parts[1] . '.1']) && isset($reference[$key], $reference[$parts[1] . '.1'])) {
+				$comment = 'Singular: ' . $comment . ' | Plural: ' . $reference[$parts[1] . '.1']->getValue();
+			}
+			// Keep the reference inside its line comment, including when it contains a PHP closing tag.
+			$comment = trim(preg_replace('/\s+/', ' ', $comment) ?? '');
+			$comment = str_replace('?>', '? >', $comment);
+			return "',\t// DIRTY" . ($comment === '' ? '' : ' - ' . $comment);
+		}, $translation ?? '');
 
 
 		return <<<PHP
 		return <<<PHP
 <?php
 <?php

+ 4 - 0
cli/i18n/I18nValue.php

@@ -44,6 +44,10 @@ class I18nValue implements \Stringable {
 		return $this->state === self::STATE_IGNORE;
 		return $this->state === self::STATE_IGNORE;
 	}
 	}
 
 
+	public function isDirty(): bool {
+		return $this->state === self::STATE_DIRTY;
+	}
+
 	public function isTodo(): bool {
 	public function isTodo(): bool {
 		return $this->state === self::STATE_TODO;
 		return $this->state === self::STATE_TODO;
 	}
 	}

+ 4 - 0
docs/en/internationalization.md

@@ -69,6 +69,10 @@ make i18n-format
 
 
 It will remove the comments on the lines that you’ve changed, and will reformat the file correctly. If you’ve made any mistakes, it will fix them automatically or it will tell you it can’t (well… the command will dramatically fail without any damage, don’t worry).
 It will remove the comments on the lines that you’ve changed, and will reformat the file correctly. If you’ve made any mistakes, it will fix them automatically or it will tell you it can’t (well… the command will dramatically fail without any damage, don’t worry).
 
 
+Entries marked `// DIRTY` need review. Formatting adds the current English text after the marker, for example `// DIRTY - Save your changes`, so you can compare it with the translation on the same line. Remove the marker once you have reviewed the translation.
+
+For translations with a single plural form, the comment includes both English forms, labelled `Singular:` and `Plural:`.
+
 The strings to translate can be easily found in the translations files thanks to the tag we spoke about at the end of the previous section. Indeed, it indicates to our tools that the strings are not translated yet. This means you can find them with Git. For instance for the Greek language:
 The strings to translate can be easily found in the translations files thanks to the tag we spoke about at the end of the previous section. Indeed, it indicates to our tools that the strings are not translated yet. This means you can find them with Git. For instance for the Greek language:
 
 
 ```sh
 ```sh

+ 101 - 0
tests/cli/i18n/I18nFileTest.php

@@ -3,6 +3,107 @@ declare(strict_types=1);
 require_once dirname(__DIR__, 3) . '/cli/i18n/I18nFile.php';
 require_once dirname(__DIR__, 3) . '/cli/i18n/I18nFile.php';
 
 
 final class I18nFileTest extends \PHPUnit\Framework\TestCase {
 final class I18nFileTest extends \PHPUnit\Framework\TestCase {
+	public function testDirtyCommentsContainTheMatchingEnglishText(): void {
+		$file = new I18nFile();
+		$format = new ReflectionMethod(I18nFile::class, 'format');
+		$translation = [
+			'example.second' => new I18nValue('Identique -> dirty'),
+			'example.first' => new I18nValue('Identique -> dirty'),
+			'example.missing' => new I18nValue('Absent -> dirty'),
+			'example.empty' => new I18nValue('Vide -> dirty'),
+			'example.todo' => new I18nValue('Not translated -> todo'),
+			'example.ignore' => new I18nValue('Ignored -> ignore'),
+			'example.reviewed' => new I18nValue('Reviewed'),
+		];
+		$reference = [
+			'example.first' => new I18nValue('First English string'),
+			'example.second' => new I18nValue('Second English string'),
+			'example.empty' => new I18nValue(" \t\r\n"),
+			'example.todo' => new I18nValue('Not translated'),
+			'example.ignore' => new I18nValue('Ignored'),
+			'example.reviewed' => new I18nValue('Reviewed English string'),
+		];
+		$output = $format->invoke($file, $translation, $reference);
+		self::assertIsString($output);
+		self::assertStringContainsString("'first' => 'Identique',\t// DIRTY - First English string", $output);
+		self::assertStringContainsString("'second' => 'Identique',\t// DIRTY - Second English string", $output);
+		self::assertStringContainsString("'missing' => 'Absent',\t// DIRTY\n", $output);
+		self::assertStringContainsString("'empty' => 'Vide',\t// DIRTY\n", $output);
+		self::assertStringContainsString("'todo' => 'Not translated',\t// TODO\n", $output);
+		self::assertStringContainsString("'ignore' => 'Ignored',\t// IGNORE\n", $output);
+		self::assertStringContainsString("'reviewed' => 'Reviewed',\n", $output);
+	}
+
+	public function testDirtyCommentsAreSafeAndIdempotent(): void {
+		$file = new I18nFile();
+		$format = new ReflectionMethod(I18nFile::class, 'format');
+		$process = new ReflectionMethod(I18nFile::class, 'process');
+		$flatten = new ReflectionMethod(I18nFile::class, 'flatten');
+		$translation = ['example.key' => new I18nValue("L’apostrophe ' et \\ -> dirty")];
+		$reference = ['example.key' => new I18nValue("English ' and \\ %s <b>HTML</b>\r\nnext\tline ?> trailing  ")];
+		$output = $format->invoke($file, $translation, $reference);
+		self::assertIsString($output);
+		self::assertStringContainsString("// DIRTY - English ' and \\ %s <b>HTML</b> next line ? > trailing\n", $output);
+		self::assertStringNotContainsString('?>', $output);
+
+		$filename = tempnam(sys_get_temp_dir(), 'freshrss-i18n-');
+		self::assertIsString($filename);
+		try {
+			file_put_contents($filename, $output);
+			$values = include $filename;
+			self::assertSame(['key' => "L’apostrophe ' et \\"], $values);
+			$loaded = $process->invoke($file, $filename);
+			self::assertIsArray($loaded);
+			$roundTrip = $flatten->invoke($file, $loaded, 'example');
+			self::assertSame($output, $format->invoke($file, $roundTrip, $reference));
+		} finally {
+			unlink($filename);
+		}
+
+		$reference['example.key'] = new I18nValue('Updated English');
+		$updated = $format->invoke($file, $translation, $reference);
+		self::assertIsString($updated);
+		self::assertStringContainsString('// DIRTY - Updated English', $updated);
+		self::assertStringNotContainsString('trailing', $updated);
+	}
+
+	public function testDirtyCommentsUseEnglishFallbackForAdditionalPluralForms(): void {
+		$format = new ReflectionMethod(I18nFile::class, 'format');
+		$output = $format->invoke(new I18nFile(), [
+			'example.count.0' => new I18nValue('Un article -> dirty'),
+			'example.count.1' => new I18nValue('Des articles -> dirty'),
+			'example.count.2' => new I18nValue('Plusieurs articles -> dirty'),
+			'example.only.2' => new I18nValue('Autre forme -> dirty'),
+		], [
+			'example.count.0' => new I18nValue('One article'),
+			'example.count.1' => new I18nValue('Many articles'),
+			'example.only.0' => new I18nValue('One form'),
+		]);
+		self::assertIsString($output);
+		self::assertStringContainsString("0 => 'Un article',\t// DIRTY - One article", $output);
+		self::assertStringContainsString("1 => 'Des articles',\t// DIRTY - Many articles", $output);
+		self::assertStringContainsString("2 => 'Plusieurs articles',\t// DIRTY - Many articles", $output);
+		self::assertStringContainsString("2 => 'Autre forme',\t// DIRTY - One form", $output);
+	}
+
+	public function testDirtyCommentsShowBothEnglishFormsForSingleFormTranslations(): void {
+		$format = new ReflectionMethod(I18nFile::class, 'format');
+		$output = $format->invoke(new I18nFile(), [
+			'example.count.0' => new I18nValue('記事が %d 件あります -> dirty'),
+			'example.only.0' => new I18nValue('一つの形 -> dirty'),
+			'example.fallback.0' => new I18nValue('別の形 -> dirty'),
+		], [
+			'example.count.0' => new I18nValue("One article\n?>"),
+			'example.count.1' => new I18nValue("Many articles\t?>"),
+			'example.only.0' => new I18nValue('One form'),
+			'example.fallback.1' => new I18nValue('Plural only'),
+		]);
+		self::assertIsString($output);
+		self::assertStringContainsString("0 => '記事が %d 件あります',\t// DIRTY - Singular: One article ? > | Plural: Many articles ? >\n", $output);
+		self::assertStringContainsString("0 => '一つの形',\t// DIRTY - One form\n", $output);
+		self::assertStringContainsString("0 => '別の形',\t// DIRTY - Plural only\n", $output);
+	}
+
 	public function test(): void {
 	public function test(): void {
 		$before = $this->computeFilesHash();
 		$before = $this->computeFilesHash();