├── .discourse-compatibility ├── .github └── workflows │ └── discourse-plugin.yml ├── .gitignore ├── .npmrc ├── .prettierrc.cjs ├── .rubocop.yml ├── .streerc ├── .template-lintrc.cjs ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app ├── helpers │ └── yearly_review_helper.rb ├── jobs │ └── yearly_review.rb └── views │ └── yearly-review │ ├── _yearly_review.html.erb │ └── _yearly_review_category.html.erb ├── assets ├── javascripts │ └── discourse │ │ ├── components │ │ └── yearly-review-admin-notice.gjs │ │ └── initializers │ │ └── yearly-review-admin-notice.js └── stylesheets │ └── yearly_review.scss ├── config ├── locales │ ├── client.en.yml │ ├── server.ar.yml │ ├── server.be.yml │ ├── server.bg.yml │ ├── server.bs_BA.yml │ ├── server.ca.yml │ ├── server.cs.yml │ ├── server.da.yml │ ├── server.de.yml │ ├── server.el.yml │ ├── server.en.yml │ ├── server.en_GB.yml │ ├── server.es.yml │ ├── server.et.yml │ ├── server.fa_IR.yml │ ├── server.fi.yml │ ├── server.fr.yml │ ├── server.gl.yml │ ├── server.he.yml │ ├── server.hr.yml │ ├── server.hu.yml │ ├── server.hy.yml │ ├── server.id.yml │ ├── server.it.yml │ ├── server.ja.yml │ ├── server.ko.yml │ ├── server.lt.yml │ ├── server.lv.yml │ ├── server.nb_NO.yml │ ├── server.nl.yml │ ├── server.pl_PL.yml │ ├── server.pt.yml │ ├── server.pt_BR.yml │ ├── server.ro.yml │ ├── server.ru.yml │ ├── server.sk.yml │ ├── server.sl.yml │ ├── server.sq.yml │ ├── server.sr.yml │ ├── server.sv.yml │ ├── server.sw.yml │ ├── server.te.yml │ ├── server.th.yml │ ├── server.tr_TR.yml │ ├── server.ug.yml │ ├── server.uk.yml │ ├── server.ur.yml │ ├── server.vi.yml │ ├── server.zh_CN.yml │ └── server.zh_TW.yml └── settings.yml ├── db └── migrate │ └── 20230208123631_backfill_yearly_review_custom_fields.rb ├── eslint.config.mjs ├── package.json ├── plugin.rb ├── pnpm-lock.yaml ├── spec ├── jobs │ └── yearly_review_spec.rb └── system │ └── core_features_spec.rb ├── stylelint.config.mjs └── translator.yml /.discourse-compatibility: -------------------------------------------------------------------------------- 1 | < 3.5.0.beta1-dev: 439e0d78f1d8a0f387c0ec26f233a090ce82ef72 2 | < 3.4.0.beta1-dev: bb124c211c37873c1d54e96e7063f1868c65d0ad 3 | < 3.3.0.beta1-dev: 59b98bab5ee370da4774f60ea7b5122dddcbd83a 4 | 3.1.999: 3246c6b378f9e69e664c575efc63c2ad83bcac2f 5 | 2.9.0.beta3: 3377b0d9f168eef839cb5fd0f4dcabcb3e313b4b 6 | -------------------------------------------------------------------------------- /.github/workflows/discourse-plugin.yml: -------------------------------------------------------------------------------- 1 | name: Discourse Plugin 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | ci: 11 | uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict = true 2 | auto-install-peers = false 3 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require("@discourse/lint-configs/prettier"); 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-discourse: stree-compat.yml 3 | -------------------------------------------------------------------------------- /.streerc: -------------------------------------------------------------------------------- 1 | --print-width=100 2 | --plugins=plugin/trailing_comma 3 | -------------------------------------------------------------------------------- /.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require("@discourse/lint-configs/template-lint"); 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | group :development do 6 | gem "rubocop-discourse" 7 | gem "syntax_tree" 8 | end 9 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (8.0.2) 5 | base64 6 | benchmark (>= 0.3) 7 | bigdecimal 8 | concurrent-ruby (~> 1.0, >= 1.3.1) 9 | connection_pool (>= 2.2.5) 10 | drb 11 | i18n (>= 1.6, < 2) 12 | logger (>= 1.4.2) 13 | minitest (>= 5.1) 14 | securerandom (>= 0.3) 15 | tzinfo (~> 2.0, >= 2.0.5) 16 | uri (>= 0.13.1) 17 | ast (2.4.3) 18 | base64 (0.2.0) 19 | benchmark (0.4.0) 20 | bigdecimal (3.1.9) 21 | concurrent-ruby (1.3.5) 22 | connection_pool (2.5.3) 23 | drb (2.2.3) 24 | i18n (1.14.7) 25 | concurrent-ruby (~> 1.0) 26 | json (2.12.2) 27 | language_server-protocol (3.17.0.5) 28 | lint_roller (1.1.0) 29 | logger (1.7.0) 30 | minitest (5.25.5) 31 | parallel (1.27.0) 32 | parser (3.3.8.0) 33 | ast (~> 2.4.1) 34 | racc 35 | prettier_print (1.2.1) 36 | prism (1.4.0) 37 | racc (1.8.1) 38 | rack (3.1.15) 39 | rainbow (3.1.1) 40 | regexp_parser (2.10.0) 41 | rubocop (1.75.7) 42 | json (~> 2.3) 43 | language_server-protocol (~> 3.17.0.2) 44 | lint_roller (~> 1.1.0) 45 | parallel (~> 1.10) 46 | parser (>= 3.3.0.2) 47 | rainbow (>= 2.2.2, < 4.0) 48 | regexp_parser (>= 2.9.3, < 3.0) 49 | rubocop-ast (>= 1.44.0, < 2.0) 50 | ruby-progressbar (~> 1.7) 51 | unicode-display_width (>= 2.4.0, < 4.0) 52 | rubocop-ast (1.44.1) 53 | parser (>= 3.3.7.2) 54 | prism (~> 1.4) 55 | rubocop-capybara (2.22.1) 56 | lint_roller (~> 1.1) 57 | rubocop (~> 1.72, >= 1.72.1) 58 | rubocop-discourse (3.12.1) 59 | activesupport (>= 6.1) 60 | lint_roller (>= 1.1.0) 61 | rubocop (>= 1.73.2) 62 | rubocop-capybara (>= 2.22.0) 63 | rubocop-factory_bot (>= 2.27.0) 64 | rubocop-rails (>= 2.30.3) 65 | rubocop-rspec (>= 3.0.1) 66 | rubocop-rspec_rails (>= 2.31.0) 67 | rubocop-factory_bot (2.27.1) 68 | lint_roller (~> 1.1) 69 | rubocop (~> 1.72, >= 1.72.1) 70 | rubocop-rails (2.32.0) 71 | activesupport (>= 4.2.0) 72 | lint_roller (~> 1.1) 73 | rack (>= 1.1) 74 | rubocop (>= 1.75.0, < 2.0) 75 | rubocop-ast (>= 1.44.0, < 2.0) 76 | rubocop-rspec (3.6.0) 77 | lint_roller (~> 1.1) 78 | rubocop (~> 1.72, >= 1.72.1) 79 | rubocop-rspec_rails (2.31.0) 80 | lint_roller (~> 1.1) 81 | rubocop (~> 1.72, >= 1.72.1) 82 | rubocop-rspec (~> 3.5) 83 | ruby-progressbar (1.13.0) 84 | securerandom (0.4.1) 85 | syntax_tree (6.2.0) 86 | prettier_print (>= 1.2.0) 87 | tzinfo (2.0.6) 88 | concurrent-ruby (~> 1.0) 89 | unicode-display_width (3.1.4) 90 | unicode-emoji (~> 4.0, >= 4.0.4) 91 | unicode-emoji (4.0.4) 92 | uri (1.0.3) 93 | 94 | PLATFORMS 95 | ruby 96 | 97 | DEPENDENCIES 98 | rubocop-discourse 99 | syntax_tree 100 | 101 | BUNDLED WITH 102 | 2.6.9 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Civilized Discourse Construction Kit, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Discourse Yearly Review 2 | 3 | https://meta.discourse.org/t/discourse-yearly-review-plugin/105713/ 4 | 5 | Creates a Yearly Review topic that summarizes user and topic stats for the previous year. 6 | Stats are only selected from a site's public categories. 7 | 8 | The review categories can be configured with the `yearly_review_categories` Site Setting. The review is published to the category set in 9 | the `yearly_review_publish_category` Site Setting. If the `yearly_review_featured_badge` setting is configured, users who 10 | have been granted that badge will be displayed. 11 | 12 | User stats: 13 | 14 | - most topics 15 | - most replies 16 | - most likes given 17 | - most likes received 18 | - most visits 19 | - most replied to 20 | - users who have been granted the featured badge 21 | 22 | Topic stats: 23 | 24 | - most popular topics 25 | - most liked topics (OP like count) 26 | - most replied to topics 27 | - most bookmarked topics 28 | -------------------------------------------------------------------------------- /app/helpers/yearly_review_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module YearlyReviewHelper 4 | include ActionView::Helpers::NumberHelper 5 | 6 | AVATAR_SIZE = "50" 7 | 8 | def avatar_image(username, uploaded_avatar_id) 9 | template = User.avatar_template(username, uploaded_avatar_id).gsub(/{size}/, AVATAR_SIZE) 10 | "![avatar\\|25x25](#{template})" 11 | end 12 | 13 | def user_link(username) 14 | "@#{username}" 15 | end 16 | 17 | def topic_link(title, slug, topic_id) 18 | link = "#{Discourse.base_url}/t/#{slug}/#{topic_id}" 19 | "[#{title}](#{link})" 20 | end 21 | 22 | def class_from_key(key) 23 | key.gsub("_", "-") 24 | end 25 | 26 | def slug_from_name(name) 27 | name.downcase.gsub(" ", "-") 28 | end 29 | 30 | def badge_link(name, id, more) 31 | url = "#{Discourse.base_url}/badges/#{id}/#{slug_from_name(name)}" 32 | "[#{t("yearly_review.more_badge_users", more: more)}](#{url})" 33 | end 34 | 35 | def category_link(name) 36 | category = Category.find_by(name: name) 37 | if category.parent_category_id 38 | parent_category = Category.find(category.parent_category_id) 39 | url = "#{Discourse.base_url}/c/#{parent_category.slug}/#{category.slug}/l/top" 40 | else 41 | url = "#{Discourse.base_url}/c/#{category.slug}/l/top" 42 | end 43 | link_text = t("yearly_review.category_topics_title", category: name) 44 | "[#{link_text}](#{url})" 45 | end 46 | 47 | def user_visits_link 48 | if SiteSetting.enable_user_directory && !SiteSetting.hide_user_profiles_from_public 49 | url = "#{Discourse.base_url}/u?order=days_visited&period=yearly" 50 | "[#{t("yearly_review.all_yearly_visits")}](#{url})" 51 | end 52 | end 53 | 54 | def format_number(number) 55 | number_to_human(number, units: { thousand: "k" }, format: "%n%u") 56 | end 57 | 58 | def table_row(*values) 59 | "|#{values.join("|")}|" 60 | end 61 | 62 | def table_header(*labels) 63 | headings = labels.map { |label| I18n.t("yearly_review.#{label}") } 64 | "|#{headings.join("|")}|" 65 | end 66 | 67 | def compiled_method_container 68 | self.class 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /app/jobs/yearly_review.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "../../app/helpers/yearly_review_helper" 4 | 5 | module ::Jobs 6 | class YearlyReview < ::Jobs::Scheduled 7 | MAX_USERS = 10 8 | MAX_BADGE_USERS = 15 9 | 10 | every 1.day 11 | 12 | def execute(args) 13 | now = Time.now 14 | review_year = args[:review_year] ? args[:review_year] : ::YearlyReview.last_year 15 | review_start = Time.new(review_year, 1, 1) 16 | review_end = review_start.end_of_year 17 | title = I18n.t("yearly_review.topic_title", year: review_year) 18 | 19 | if !args[:force] 20 | return if !SiteSetting.yearly_review_enabled 21 | return unless now.month == 1 && now.day <= 31 22 | return if review_topic_exists?(review_year) 23 | end 24 | 25 | view = ActionView::Base.with_view_paths(ActionController::Base.view_paths) 26 | view.singleton_class.include(YearlyReviewHelper) 27 | 28 | raw_topic_html = render_raw_topic_view(view, review_year, review_start, review_end) 29 | if raw_topic_html.present? 30 | topic_opts = { 31 | title: title, 32 | raw: raw_topic_html, 33 | category: SiteSetting.yearly_review_publish_category, 34 | skip_validations: true, 35 | custom_fields: { 36 | ::YearlyReview::POST_CUSTOM_FIELD => review_year, 37 | }, 38 | } 39 | 40 | post = PostCreator.create!(Discourse.system_user, topic_opts) 41 | 42 | if post.respond_to? :topic_id 43 | create_category_posts view, review_start, review_end, post.topic_id 44 | end 45 | end 46 | end 47 | 48 | def render_raw_topic_view(view, review_year, review_start, review_end) 49 | review_featured_badge = SiteSetting.yearly_review_featured_badge 50 | include_user_stats = SiteSetting.yearly_review_include_user_stats 51 | 52 | user_stats = include_user_stats ? user_stats(review_start, review_end) : [] 53 | featured_badge_users = 54 | ( 55 | if review_featured_badge.blank? 56 | [] 57 | else 58 | featured_badge_users(review_featured_badge, review_start, review_end) 59 | end 60 | ) 61 | daily_visits = daily_visits review_start, review_end 62 | view.assign( 63 | review_year: review_year, 64 | user_stats: user_stats, 65 | daily_visits: daily_visits, 66 | featured_badge_users: featured_badge_users, 67 | ) 68 | 69 | view.render partial: "yearly_review", layout: false 70 | end 71 | 72 | def create_category_posts(view, review_start, review_end, topic_id) 73 | review_categories = review_categories_from_settings 74 | 75 | review_categories.each do |category_id| 76 | category_post_topics = category_post_topics category_id, review_start, review_end 77 | if category_post_topics[:topics] 78 | view.assign(category_topics: category_post_topics) 79 | raw_html = view.render partial: "yearly_review_category", layout: false 80 | if raw_html.present? 81 | post_opts = { 82 | topic_id: topic_id, 83 | raw: raw_html, 84 | skip_validations: true, 85 | custom_fields: { 86 | ::YearlyReview::POST_CUSTOM_FIELD => review_start.year, 87 | }, 88 | } 89 | 90 | PostCreator.create!(Discourse.system_user, post_opts) 91 | end 92 | end 93 | end 94 | end 95 | 96 | def category_post_topics(cat_id, start_date, end_date) 97 | data = {} 98 | 99 | most_read = ranked_topics(cat_id, start_date, end_date, most_read_topic_sql) 100 | most_liked = ranked_topics(cat_id, start_date, end_date, most_liked_topic_sql) 101 | most_replied_to = ranked_topics(cat_id, start_date, end_date, most_replied_to_topic_sql) 102 | most_popular = ranked_topics(cat_id, start_date, end_date, most_popular_topic_sql) 103 | most_bookmarked = ranked_topics(cat_id, start_date, end_date, most_bookmarked_topic_sql) 104 | 105 | category_topics = {} 106 | category_topics[:most_read] = most_read if most_read.any? 107 | category_topics[:most_liked] = most_liked if most_liked.any? 108 | category_topics[:most_replied_to] = most_replied_to if most_replied_to.any? 109 | category_topics[:most_popular] = most_popular if most_popular.any? 110 | category_topics[:most_bookmarked] = most_bookmarked if most_bookmarked.any? 111 | 112 | if category_topics.any? 113 | category_name = Category.find(cat_id).name 114 | data[:category_name] = category_name 115 | data[:topics] = category_topics 116 | end 117 | 118 | data 119 | end 120 | 121 | def review_categories_from_settings 122 | read_restricted = SiteSetting.yearly_review_include_private_categories 123 | 124 | if SiteSetting.yearly_review_categories.blank? 125 | Category.where(read_restricted: false).order("topics_year DESC")[0, 5].pluck(:id) 126 | else 127 | opts = {} 128 | opts[:read_restricted] = false if read_restricted == false 129 | opts[:id] = SiteSetting.yearly_review_categories.split("|") 130 | Category.where(opts).order("topics_year DESC").pluck(:id) 131 | end 132 | end 133 | 134 | def user_stats(review_start, review_end) 135 | exclude_staff = SiteSetting.yearly_review_exclude_staff 136 | read_restricted = SiteSetting.yearly_review_include_private_categories 137 | user_stats = [] 138 | most_time_read = most_time_read review_start, review_end, exclude_staff, read_restricted 139 | most_topics = most_topics review_start, review_end, exclude_staff, read_restricted 140 | most_replies = most_replies review_start, review_end, exclude_staff, read_restricted 141 | most_likes = most_likes_given review_start, review_end, exclude_staff, read_restricted 142 | most_likes_received = 143 | most_likes_received review_start, review_end, exclude_staff, read_restricted 144 | most_visits = most_visits review_start, review_end, exclude_staff 145 | most_replied_to = most_replied_to review_start, review_end, exclude_staff, read_restricted 146 | user_stats << { key: "time_read", users: most_time_read } if most_time_read.any? 147 | user_stats << { key: "topics_created", users: most_topics } if most_topics.any? 148 | user_stats << { key: "replies_created", users: most_replies } if most_replies.any? 149 | user_stats << { key: "most_replied_to", users: most_replied_to } if most_replied_to.any? 150 | user_stats << { key: "likes_given", users: most_likes } if most_likes.any? 151 | if most_likes_received.any? 152 | user_stats << { key: "likes_received", users: most_likes_received } 153 | end 154 | user_stats << { key: "visits", users: most_visits } if most_visits.any? 155 | user_stats 156 | end 157 | 158 | def ranked_topics(cat_id, start_date, end_date, sql) 159 | data = [] 160 | exclude_staff = SiteSetting.yearly_review_exclude_staff 161 | DB 162 | .query( 163 | sql, 164 | start_date: start_date, 165 | end_date: end_date, 166 | cat_id: cat_id, 167 | exclude_staff: exclude_staff, 168 | limit: 5, 169 | ) 170 | .each do |row| 171 | if row 172 | action = row.action 173 | case action 174 | when "likes" 175 | next if row.action_count < 10 176 | when "replies" 177 | next if row.action_count < 10 178 | when "bookmarks" 179 | next if row.action_count < 5 180 | when "score" 181 | next if row.action_count < 10 182 | when "read_time" 183 | next if row.action_count < 5 184 | end 185 | data << row 186 | end 187 | end 188 | data 189 | end 190 | 191 | def most_topics(start_date, end_date, exclude_staff, read_restricted) 192 | sql = <<~SQL 193 | SELECT 194 | t.user_id, 195 | COUNT(t.user_id) AS action_count, 196 | u.username, 197 | u.uploaded_avatar_id 198 | FROM topics t 199 | JOIN users u 200 | ON u.id = t.user_id 201 | JOIN categories c 202 | ON c.id = t.category_id 203 | WHERE t.archetype = 'regular' 204 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 205 | #{"AND c.read_restricted = false" unless read_restricted} 206 | AND t.user_id > 0 207 | AND t.created_at >= :start_date 208 | AND t.created_at <= :end_date 209 | AND t.deleted_at IS NULL 210 | GROUP BY t.user_id, u.username, u.uploaded_avatar_id 211 | ORDER BY action_count DESC 212 | LIMIT #{MAX_USERS} 213 | SQL 214 | 215 | DB.query(sql, exclude_staff:, start_date:, end_date:) 216 | end 217 | 218 | def most_replies(start_date, end_date, exclude_staff, read_restricted) 219 | sql = <<~SQL 220 | SELECT 221 | p.user_id, 222 | u.username, 223 | u.uploaded_avatar_id, 224 | COUNT(p.user_id) AS action_count 225 | FROM posts p 226 | JOIN users u 227 | ON u.id = p.user_id 228 | JOIN topics t 229 | ON t.id = p.topic_id 230 | JOIN categories c 231 | ON c.id = t.category_id 232 | WHERE t.archetype = 'regular' 233 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 234 | #{"AND c.read_restricted = false" unless read_restricted} 235 | AND p.user_id > 0 236 | AND p.post_number > 1 237 | AND p.post_type = 1 238 | AND p.created_at >= :start_date 239 | AND p.created_at <= :end_date 240 | AND t.deleted_at IS NULL 241 | AND p.deleted_at IS NULL 242 | GROUP BY p.user_id, u.username, u.uploaded_avatar_id 243 | ORDER BY action_count DESC 244 | LIMIT #{MAX_USERS} 245 | SQL 246 | 247 | DB.query(sql, exclude_staff:, start_date:, end_date:) 248 | end 249 | 250 | def daily_visits(start_date, end_date) 251 | sql = <<~SQL 252 | WITH visits AS ( 253 | SELECT 254 | user_id, 255 | COUNT(user_id) AS days_visited_count 256 | FROM user_visits uv 257 | WHERE uv.visited_at >= :start_date 258 | AND uv.visited_at <= :end_date 259 | GROUP BY user_id 260 | ) 261 | SELECT 262 | COUNT(user_id) AS users, 263 | days_visited_count AS days 264 | FROM visits 265 | GROUP BY days_visited_count 266 | ORDER BY days_visited_count DESC 267 | LIMIT 10 268 | SQL 269 | 270 | DB.query(sql, start_date:, end_date:) 271 | end 272 | 273 | def most_visits(start_date, end_date, exclude_staff) 274 | sql = <<~SQL 275 | SELECT 276 | uv.user_id, 277 | u.username, 278 | u.uploaded_avatar_id, 279 | COUNT(uv.user_id) AS action_count 280 | FROM user_visits uv 281 | JOIN users u 282 | ON u.id = uv.user_id 283 | WHERE u.id > 0 284 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 285 | AND uv.visited_at >= :start_date 286 | AND uv.visited_at <= :end_date 287 | GROUP BY uv.user_id, u.username, u.uploaded_avatar_id 288 | ORDER BY action_count DESC 289 | LIMIT #{MAX_USERS} 290 | SQL 291 | 292 | DB.query(sql, exclude_staff:, start_date:, end_date:) 293 | end 294 | 295 | def most_time_read(start_date, end_date, exclude_staff, read_restricted) 296 | sql = <<~SQL 297 | SELECT 298 | u.id, 299 | u.username, 300 | u.uploaded_avatar_id, 301 | ROUND(SUM(tu.total_msecs_viewed::numeric) / (1000 * 60 * 60)::numeric, 2) AS action_count 302 | FROM users u 303 | JOIN topic_users tu 304 | ON tu.user_id = u.id 305 | JOIN topics t 306 | ON t.id = tu.topic_id 307 | JOIN categories c 308 | ON c.id = t.category_id 309 | WHERE t.archetype = 'regular' 310 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 311 | #{"AND c.read_restricted = false" unless read_restricted} 312 | AND u.id > 0 313 | AND t.created_at >= :start_date 314 | AND t.created_at <= :end_date 315 | AND t.deleted_at IS NULL 316 | AND tu.total_msecs_viewed > (1000 * 60) 317 | GROUP BY u.id, username, uploaded_avatar_id 318 | ORDER BY action_count DESC 319 | LIMIT #{MAX_USERS} 320 | SQL 321 | 322 | DB.query(sql, exclude_staff:, start_date:, end_date:) 323 | end 324 | 325 | def most_likes_given(start_date, end_date, exclude_staff, read_restricted) 326 | sql = <<~SQL 327 | SELECT 328 | ua.acting_user_id, 329 | u.username, 330 | u.uploaded_avatar_id, 331 | COUNT(ua.user_id) AS action_count 332 | FROM user_actions ua 333 | JOIN topics t 334 | ON t.id = ua.target_topic_id 335 | JOIN users u 336 | ON u.id = ua.acting_user_id 337 | JOIN categories c 338 | ON c.id = t.category_id 339 | WHERE t.archetype = 'regular' 340 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 341 | #{"AND c.read_restricted = false" unless read_restricted} 342 | AND u.id > 0 343 | AND ua.created_at >= :start_date 344 | AND ua.created_at <= :end_date 345 | AND ua.action_type = 2 346 | AND t.deleted_at IS NULL 347 | GROUP BY ua.acting_user_id, u.username, u.uploaded_avatar_id 348 | ORDER BY action_count DESC 349 | LIMIT #{MAX_USERS} 350 | SQL 351 | 352 | DB.query(sql, exclude_staff:, start_date:, end_date:) 353 | end 354 | 355 | def most_likes_received(start_date, end_date, exclude_staff, read_restricted) 356 | sql = <<~SQL 357 | SELECT 358 | u.username, 359 | u.uploaded_avatar_id, 360 | COUNT(ua.user_id) AS action_count 361 | FROM user_actions ua 362 | JOIN topics t 363 | ON t.id = ua.target_topic_id 364 | JOIN users u 365 | ON u.id = ua.user_id 366 | JOIN categories c 367 | ON c.id = t.category_id 368 | WHERE t.archetype = 'regular' 369 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 370 | #{"AND c.read_restricted = false" unless read_restricted} 371 | AND u.id > 0 372 | AND ua.created_at >= :start_date 373 | AND ua.created_at <= :end_date 374 | AND ua.action_type = 2 375 | AND t.deleted_at IS NULL 376 | GROUP BY u.username, u.uploaded_avatar_id 377 | ORDER BY action_count DESC 378 | LIMIT #{MAX_USERS} 379 | SQL 380 | 381 | DB.query(sql, exclude_staff:, start_date:, end_date:) 382 | end 383 | 384 | def most_replied_to(start_date, end_date, exclude_staff, read_restricted) 385 | sql = <<~SQL 386 | SELECT 387 | u.username, 388 | u.uploaded_avatar_id, 389 | SUM(p.reply_count) AS action_count 390 | FROM posts p 391 | JOIN topics t 392 | ON t.id = p.topic_id 393 | JOIN users u 394 | ON u.id = p.user_id 395 | JOIN categories c 396 | ON c.id = t.category_id 397 | WHERE t.archetype = 'regular' 398 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 399 | #{"AND c.read_restricted = false" unless read_restricted} 400 | AND p.created_at >= :start_date 401 | AND p.created_at <= :end_date 402 | AND p.reply_count > 0 403 | AND t.deleted_at IS NULL 404 | AND p.deleted_at IS NULL 405 | AND p.post_type = 1 406 | AND p.user_id > 0 407 | GROUP BY u.username, u.uploaded_avatar_id 408 | ORDER BY action_count DESC 409 | LIMIT #{MAX_USERS} 410 | SQL 411 | 412 | DB.query(sql, exclude_staff:, start_date:, end_date:) 413 | end 414 | 415 | def featured_badge_users(badge_name, start_date, end_date) 416 | sql = <<~SQL 417 | SELECT DISTINCT ON(u.id) 418 | u.id AS user_id, 419 | username, 420 | uploaded_avatar_id, 421 | b.name, 422 | b.id, 423 | ((COUNT(*) OVER()) - #{MAX_BADGE_USERS}) AS more_users 424 | FROM badges b 425 | JOIN user_badges ub 426 | ON ub.badge_id = b.id 427 | JOIN users u 428 | ON u.id = ub.user_id 429 | WHERE b.name = :badge_name 430 | AND ((NOT :exclude_staff) OR (u.admin = false AND u.moderator = false)) 431 | AND ub.granted_at BETWEEN :start_date AND :end_date 432 | AND u.id > 0 433 | ORDER BY u.id 434 | LIMIT #{MAX_BADGE_USERS} 435 | SQL 436 | 437 | DB.query( 438 | sql, 439 | exclude_staff: SiteSetting.yearly_review_exclude_staff, 440 | badge_name:, 441 | start_date:, 442 | end_date:, 443 | ) 444 | end 445 | 446 | def most_read_topic_sql 447 | <<~SQL 448 | SELECT 449 | username, 450 | uploaded_avatar_id, 451 | t.id, 452 | t.slug AS topic_slug, 453 | t.title, 454 | t.created_at, 455 | c.slug AS category_slug, 456 | c.name AS category_name, 457 | c.id AS category_id, 458 | ROUND(SUM(tu.total_msecs_viewed::numeric) / (1000 * 60 * 60)::numeric, 2) AS action_count, 459 | 'read_time' AS action 460 | FROM users u 461 | JOIN topics t 462 | ON t.user_id = u.id 463 | JOIN topic_users tu 464 | ON tu.topic_id = t.id 465 | JOIN categories c 466 | ON c.id = t.category_id 467 | WHERE t.deleted_at IS NULL 468 | AND ((:exclude_staff = false) OR (u.admin = false AND u.moderator = false)) 469 | AND t.created_at BETWEEN :start_date AND :end_date 470 | AND t.visible = true 471 | AND c.id = :cat_id 472 | AND u.id > 0 473 | GROUP BY t.id, username, uploaded_avatar_id, c.id 474 | ORDER BY action_count DESC 475 | LIMIT :limit 476 | SQL 477 | end 478 | 479 | def most_popular_topic_sql 480 | <<~SQL 481 | SELECT 482 | username, 483 | uploaded_avatar_id, 484 | t.id, 485 | t.slug AS topic_slug, 486 | t.title, 487 | t.created_at, 488 | c.slug AS category_slug, 489 | c.name AS category_name, 490 | c.id AS category_id, 491 | ROUND(tt.yearly_score::numeric, 2) AS action_count, 492 | 'score' AS action 493 | FROM top_topics tt 494 | JOIN topics t 495 | ON t.id = tt.topic_id 496 | JOIN categories c 497 | ON c.id = t.category_id 498 | JOIN users u 499 | ON u.id = t.user_id 500 | WHERE t.deleted_at IS NULL 501 | AND ((:exclude_staff = false) OR (u.admin = false AND u.moderator = false)) 502 | AND t.created_at BETWEEN :start_date AND :end_date 503 | AND t.visible = true 504 | AND c.id = :cat_id 505 | AND u.id > 0 506 | ORDER BY tt.yearly_score DESC 507 | LIMIT :limit 508 | SQL 509 | end 510 | 511 | def most_liked_topic_sql 512 | <<~SQL 513 | SELECT 514 | username, 515 | uploaded_avatar_id, 516 | t.id, 517 | t.slug AS topic_slug, 518 | t.title, 519 | t.created_at, 520 | c.slug AS category_slug, 521 | c.name AS category_name, 522 | c.id AS category_id, 523 | COUNT(*) AS action_count, 524 | 'likes' AS action 525 | FROM post_actions pa 526 | JOIN posts p 527 | ON p.id = pa.post_id 528 | JOIN topics t 529 | ON t.id = p.topic_id 530 | JOIN categories c 531 | ON c.id = t.category_id 532 | JOIN users u 533 | ON u.id = t.user_id 534 | WHERE pa.created_at BETWEEN :start_date AND :end_date 535 | AND ((:exclude_staff = false) OR (u.admin = false AND u.moderator = false)) 536 | AND pa.post_action_type_id = 2 537 | AND c.id = :cat_id 538 | AND p.post_number = 1 539 | AND p.deleted_at IS NULL 540 | AND t.deleted_at IS NULL 541 | AND t.visible = true 542 | AND u.id > 0 543 | GROUP BY p.id, t.id, topic_slug, category_slug, category_name, c.id, username, uploaded_avatar_id 544 | ORDER BY action_count DESC 545 | LIMIT :limit 546 | SQL 547 | end 548 | 549 | def most_replied_to_topic_sql 550 | <<~SQL 551 | SELECT 552 | username, 553 | uploaded_avatar_id, 554 | t.id, 555 | t.slug AS topic_slug, 556 | t.title, 557 | t.created_at, 558 | c.slug AS category_slug, 559 | c.name AS category_name, 560 | c.id AS category_id, 561 | COUNT(*) AS action_count, 562 | 'replies' AS action 563 | FROM posts p 564 | JOIN topics t 565 | ON t.id = p.topic_id 566 | JOIN categories c 567 | ON c.id = t.category_id 568 | JOIN users u 569 | ON u.id = t.user_id 570 | WHERE p.created_at BETWEEN :start_date AND :end_date 571 | AND ((:exclude_staff = false) OR (u.admin = false AND u.moderator = false)) 572 | AND c.id = :cat_id 573 | AND t.deleted_at IS NULL 574 | AND t.visible = true 575 | AND p.deleted_at IS NULL 576 | AND p.post_type = 1 577 | AND p.post_number > 1 578 | AND t.posts_count > 1 579 | AND u.id > 0 580 | GROUP BY t.id, topic_slug, category_slug, category_name, c.id, username, uploaded_avatar_id 581 | ORDER BY action_count DESC 582 | LIMIT :limit 583 | SQL 584 | end 585 | 586 | def most_bookmarked_topic_sql 587 | post_bookmark_join_sql = 588 | if SiteSetting.use_polymorphic_bookmarks 589 | "ON p.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Post'" 590 | else 591 | "ON p.id = bookmarks.post_id" 592 | end 593 | <<~SQL 594 | SELECT 595 | username, 596 | uploaded_avatar_id, 597 | t.id, 598 | t.slug AS topic_slug, 599 | t.title, 600 | t.created_at, 601 | c.slug AS category_slug, 602 | c.name AS category_name, 603 | c.id AS category_id, 604 | COUNT(*) AS action_count, 605 | 'bookmarks' AS action 606 | FROM bookmarks 607 | JOIN posts p 608 | #{post_bookmark_join_sql} 609 | JOIN topics t 610 | ON t.id = p.topic_id 611 | JOIN categories c 612 | ON c.id = t.category_id 613 | JOIN users u 614 | ON u.id = t.user_id 615 | WHERE bookmarks.created_at BETWEEN :start_date AND :end_date 616 | AND ((:exclude_staff = false) OR (u.admin = false AND u.moderator = false)) 617 | AND c.id = :cat_id 618 | AND t.deleted_at IS NULL 619 | AND t.visible = true 620 | AND p.deleted_at IS NULL 621 | GROUP BY t.id, category_slug, category_name, c.id, username, uploaded_avatar_id 622 | ORDER BY action_count DESC 623 | LIMIT :limit 624 | SQL 625 | end 626 | 627 | def review_topic_exists?(review_year) 628 | TopicCustomField.joins(:topic).exists?( 629 | name: ::YearlyReview::POST_CUSTOM_FIELD, 630 | value: review_year.to_s, 631 | topic: { 632 | deleted_at: nil, 633 | }, 634 | ) 635 | end 636 | end 637 | end 638 | -------------------------------------------------------------------------------- /app/views/yearly-review/_yearly_review.html.erb: -------------------------------------------------------------------------------- 1 | <% if @user_stats.any? %> 2 |
3 | 4 | ## <%= t('yearly_review.title.users_section', year: @review_year) %> 5 | <% @user_stats.each do |obj| %> 6 | <% if obj[:users] %> 7 | ## <%= t("yearly_review.title.#{obj[:key]}") %> 8 | 9 |
10 | 11 | <%= table_header('user', "action.#{obj[:key]}") %> 12 | |---|---| 13 | <% obj[:users].each_with_index do |user, i| %> 14 | <%= table_row("#{avatar_image(user.username, user.uploaded_avatar_id)} @#{user.username}", format_number(user.action_count < 1 ? 1 : user.action_count.round)) %> 15 | <% end %> 16 | 17 |
18 | 19 | <% if obj[:key] == 'visits' %> 20 | <%= raw(user_visits_link) %> 21 | <% end %> 22 | <% end %> 23 | <% end %> 24 |
25 | <% end %> 26 | 27 | <% if @daily_visits.any? %> 28 |
29 | 30 | ## <%= t("yearly_review.title.daily_visits") %> 31 | 32 | <%= table_header("days_visited", "users") %> 33 | |---|---| 34 | <% @daily_visits.each do |visit| %> 35 | <%= table_row(format_number(visit.days), format_number(visit.users)) %> 36 | <% end %> 37 | 38 |
39 | <% end %> 40 | 41 | <% if @featured_badge_users.any? %> 42 |
43 | 44 | ## <%= t("yearly_review.title.featured_badge", badge_name: SiteSetting.yearly_review_featured_badge) %> 45 | 46 | <% @featured_badge_users.each do |user| %> 47 | @<%= user.username %> 48 | <% end %> 49 | <% if @featured_badge_users[0].more_users > 0 %> 50 | <% @badge = @featured_badge_users[0] %> 51 | <%= raw(badge_link(@badge.name, @badge.id, format_number(@badge.more_users))) %> 52 | <% end %> 53 | 54 |
55 | <% end %> 56 | -------------------------------------------------------------------------------- /app/views/yearly-review/_yearly_review_category.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | ## <%= raw(category_link(@category_topics[:category_name])) %> 4 | <% @category_topics[:topics].each do |key, val| %> 5 | ### <%= t("yearly_review.rank_type.title.#{key}") %> 6 | 7 | <%= table_header('user', 'topic', "rank_type.action_types.#{key}") %> 8 | |---|---|---| 9 | <% val.each do |topic| %> 10 | <%= table_row(avatar_image(topic.username, topic.uploaded_avatar_id), topic_link(topic.title, topic.topic_slug, topic.id), format_number(topic.action_count.round)) %> 11 | <% end %> 12 | 13 | <% end %> 14 |
15 | -------------------------------------------------------------------------------- /assets/javascripts/discourse/components/yearly-review-admin-notice.gjs: -------------------------------------------------------------------------------- 1 | import Component from "@glimmer/component"; 2 | import { htmlSafe } from "@ember/template"; 3 | import replaceEmoji from "discourse/helpers/replace-emoji"; 4 | import getURL from "discourse/lib/get-url"; 5 | import { i18n } from "discourse-i18n"; 6 | 7 | export function janNextYear() { 8 | return new Date(new Date().getFullYear() + 1, 0, 1); 9 | } 10 | 11 | export default class YearlyReviewAdminNotice extends Component { 12 | get toBeCreatedDate() { 13 | return moment(janNextYear()).format(i18n("dates.full_with_year_no_time")); 14 | } 15 | 16 | get settingsUrl() { 17 | return getURL( 18 | "/admin/site_settings/category/plugins?filter=plugin%3Adiscourse-yearly-review" 19 | ); 20 | } 21 | 22 | 35 | } 36 | -------------------------------------------------------------------------------- /assets/javascripts/discourse/initializers/yearly-review-admin-notice.js: -------------------------------------------------------------------------------- 1 | import { withPluginApi } from "discourse/lib/plugin-api"; 2 | import YearlyReviewAdminNotice from "discourse/plugins/discourse-yearly-review/discourse/components/yearly-review-admin-notice"; 3 | 4 | export default { 5 | name: "yearly-review-admin-notice", 6 | initialize(container) { 7 | withPluginApi("1.18.0", (api) => { 8 | const siteSettings = container.lookup("service:site-settings"); 9 | 10 | if (!siteSettings.yearly_review_enabled) { 11 | return; 12 | } 13 | 14 | // Only show this in December of the current year (getMonth is 0-based). 15 | const now = new Date(); 16 | if (now.getMonth() === 11) { 17 | api.renderInOutlet("admin-dashboard-top", YearlyReviewAdminNotice); 18 | } 19 | }); 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /assets/stylesheets/yearly_review.scss: -------------------------------------------------------------------------------- 1 | [data-review-topic-users="true"] table, 2 | [data-review-featured-topics="true"] table { 3 | width: 100%; 4 | } 5 | 6 | [data-review-topic-users="true"] table th, 7 | [data-review-featured-topics="true"] table th { 8 | text-align: left; 9 | } 10 | 11 | [data-review-topic-users="true"] table { 12 | th { 13 | width: 50%; 14 | } 15 | 16 | td:first-child img { 17 | border-radius: 50%; 18 | } 19 | } 20 | 21 | [data-review-topic-users="true"] table table tr { 22 | border-top: none; 23 | border-bottom: none; 24 | } 25 | 26 | [data-review-topic-users="true"] table table tbody { 27 | border-top: none; 28 | } 29 | 30 | [data-review-topic-users="true"] table table td { 31 | text-align: left; 32 | padding-left: 0; 33 | } 34 | 35 | [data-review-topic-users="true"] table table td:first-child { 36 | width: 25px; 37 | } 38 | 39 | [data-review-users="true"] span { 40 | white-space: pre; 41 | display: inline-block; 42 | margin-bottom: 4px; 43 | } 44 | 45 | [data-review-featured-topics="true"] table th:first-child { 46 | width: 10%; 47 | } 48 | 49 | [data-review-featured-topics="true"] table th:nth-child(2) { 50 | width: 60%; 51 | } 52 | 53 | [data-review-featured-topics="true"] table th:last-child { 54 | width: 30%; 55 | } 56 | 57 | [data-review-featured-topics="true"] table td:first-child img { 58 | border-radius: 50%; 59 | } 60 | -------------------------------------------------------------------------------- /config/locales/client.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | js: 3 | yearly_review: 4 | admin_notice: "The Yearly Review topic will be created soon on %{to_be_created_date}. Please review the settings now and be sure to set the yearly review publish category to the staff or other private category so you can view it before making it public, and maybe add a celebratory note! :partying_face: More details…" 5 | 6 | -------------------------------------------------------------------------------- /config/locales/server.ar.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ar: 8 | site_settings: 9 | yearly_review_enabled: "تفعيل المراجعة السنوية. سيتم إنشاء موضوع تلقائيًا في الأول من يناير لتلخيص نشاط المنتدى في العام السابق." 10 | yearly_review_categories: "الفئات العامة التي سيتم تلخيصها. سيتم إنشاء منشور واحد يتضمن الإحصاءات لكل فئة ضمن أبرز 5 فئات في هذه القائمة. اتركها فارغة لاستخدام أبرز 5 فئات عامة." 11 | yearly_review_exclude_staff: "استثناء فريق العمل من إحصاءات الأعضاء." 12 | yearly_review_include_user_stats: "إضافة إحصاءات تحدد هوية الأعضاء." 13 | yearly_review_include_private_categories: "تضمين نشاط الأعضاء من الفئات الخاصة أو المقيَّدة قراءتها في المراجعة." 14 | yearly_review_publish_category: "الفئة التي سيتم نشر المراجعة فيها. يوصى بشدة بتحديد فئة فريق العمل أو الفئة الخاصة الأخرى؛ حتى تتمكن من عرض الموضوع قبل نشره للعامة." 15 | yearly_review_featured_badge: "أدخل اسم الشارة بالكامل. يمكن تركه فارغًا." 16 | yearly_review: 17 | topic_title: "%{year}: العام قيد المراجعة" 18 | category_topics_title: "أبرز #%{category} من الموضوعات" 19 | title: 20 | users_section: "أبرز المستخدمين في %{year}" 21 | topics_created: "الأكثر في عدد الموضوعات" 22 | replies_created: "الأكثر في عدد الردود" 23 | likes_given: "الأكثر في عدد الإعجابات الممنوحة" 24 | likes_received: "الأكثر في عدد الإعجابات المتلقاة" 25 | visits: "الأكثر في عدد الزيارات" 26 | daily_visits: "الزيارات اليومية" 27 | time_read: "الأكثر في وقت القراءة" 28 | most_replied_to: "الأكثر ردًا عليه" 29 | featured_badge: "المستخدمون الذين تم منحهم شارة %{badge_name}" 30 | action: 31 | topics_created: "الموضوعات" 32 | most_replied_to: "الردود" 33 | replies_created: "الردود" 34 | likes_given: "الإعجابات" 35 | likes_received: "الإعجابات" 36 | visits: "أيام الزيارة" 37 | time_read: "ساعات القراءة" 38 | rank_type: 39 | title: 40 | most_liked: "الأكثر تلقيًا للإعجاب" 41 | most_replied_to: "الأكثر في عدد الردود" 42 | most_popular: "الأكثر شعبية" 43 | most_bookmarked: "الأكثر تسجيلًا للإشارات المرجعية" 44 | most_read: "الأكثر قراءة" 45 | action_types: 46 | most_popular: "النقاط" 47 | most_replied_to: "الردود" 48 | most_liked: "الإعجابات" 49 | most_bookmarked: "الإشارات المرجعية" 50 | most_read: "ساعات القراءة" 51 | user: "المستخدم" 52 | users: "المستخدمون" 53 | days_visited: "أيام الزيارة" 54 | all_yearly_visits: "جميع الزيارات السنوية" 55 | topics: "الموضوعات" 56 | topic: "الموضوع" 57 | likes: "الإعجابات" 58 | bookmarks: "الإشارات المرجعية" 59 | score: "النقاط" 60 | replies: "الردود" 61 | visits: "الزيارات" 62 | more_badge_users: "و%{more} أخرى..." 63 | -------------------------------------------------------------------------------- /config/locales/server.be.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | be: 8 | yearly_review: 9 | action: 10 | topics_created: "тэмы" 11 | most_replied_to: "Адказы" 12 | replies_created: "Адказы" 13 | likes_given: "сімпатыі" 14 | likes_received: "сімпатыі" 15 | visits: "дзён наведанае" 16 | rank_type: 17 | action_types: 18 | most_popular: "кошт" 19 | most_replied_to: "Адказы" 20 | most_liked: "сімпатыі" 21 | most_bookmarked: "закладкі" 22 | user: "карыстальнік" 23 | users: "карыстальнікаў" 24 | days_visited: "дзён наведанае" 25 | topics: "тэмы" 26 | topic: "тэма" 27 | likes: "сімпатыі" 28 | bookmarks: "закладкі" 29 | score: "кошт" 30 | replies: "Адказы" 31 | visits: "наведванне" 32 | -------------------------------------------------------------------------------- /config/locales/server.bg.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | bg: 8 | yearly_review: 9 | action: 10 | topics_created: "Теми" 11 | most_replied_to: "Отговори" 12 | replies_created: "Отговори" 13 | likes_given: "Дадени харесвания" 14 | likes_received: "Дадени харесвания" 15 | visits: "Дни Посетена" 16 | rank_type: 17 | title: 18 | most_liked: "Най-харесвани" 19 | action_types: 20 | most_popular: "Точки" 21 | most_replied_to: "Отговори" 22 | most_liked: "Дадени харесвания" 23 | most_bookmarked: "Отметки" 24 | user: "Потребител " 25 | users: "Потребители" 26 | days_visited: "Дни Посетена" 27 | topics: "Теми" 28 | topic: "Тема" 29 | likes: "Дадени харесвания" 30 | bookmarks: "Отметки" 31 | score: "Точки" 32 | replies: "Отговори" 33 | visits: "Посещения " 34 | -------------------------------------------------------------------------------- /config/locales/server.bs_BA.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | bs_BA: 8 | yearly_review: 9 | action: 10 | topics_created: "Topics" 11 | most_replied_to: "Odgovori" 12 | replies_created: "Odgovori" 13 | likes_given: "Lajkovi" 14 | likes_received: "Lajkovi" 15 | visits: "Dani posjete" 16 | rank_type: 17 | title: 18 | most_liked: "Najviše sviđanja" 19 | action_types: 20 | most_popular: "bodovi" 21 | most_replied_to: "Odgovori" 22 | most_liked: "Lajkovi" 23 | most_bookmarked: "Zabilješke" 24 | user: "Korisnik" 25 | users: "Users" 26 | days_visited: "Dani posjete" 27 | topics: "Topics" 28 | topic: "Topic" 29 | likes: "Lajkovi" 30 | bookmarks: "Zabilješke" 31 | score: "bodovi" 32 | replies: "Odgovori" 33 | visits: "Visits" 34 | more_badge_users: "I %{more} vIše..." 35 | -------------------------------------------------------------------------------- /config/locales/server.ca.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ca: 8 | yearly_review: 9 | action: 10 | topics_created: "Temes" 11 | most_replied_to: "Respostes" 12 | replies_created: "Respostes" 13 | likes_given: "'M'agrada'" 14 | likes_received: "'M'agrada'" 15 | visits: "Dies visitats" 16 | rank_type: 17 | title: 18 | most_liked: "Ha tingut més 'm'agrada'" 19 | action_types: 20 | most_popular: "Puntuació" 21 | most_replied_to: "Respostes" 22 | most_liked: "'M'agrada'" 23 | most_bookmarked: "Preferits" 24 | user: "Usuari" 25 | users: "Usuaris" 26 | days_visited: "Dies visitats" 27 | topics: "Temes" 28 | topic: "Tema" 29 | likes: "'M'agrada'" 30 | bookmarks: "Preferits" 31 | score: "Puntuació" 32 | replies: "Respostes" 33 | visits: "Visites" 34 | more_badge_users: "I %{more} més..." 35 | -------------------------------------------------------------------------------- /config/locales/server.cs.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | cs: 8 | yearly_review: 9 | action: 10 | topics_created: "Témata" 11 | most_replied_to: "Odpovědi" 12 | replies_created: "Odpovědi" 13 | likes_given: "Nová 'líbí se'" 14 | likes_received: "Nová 'líbí se'" 15 | visits: "Přítomen dnů" 16 | rank_type: 17 | title: 18 | most_liked: "Nejoblíbenější" 19 | action_types: 20 | most_popular: "Skóre" 21 | most_replied_to: "Odpovědi" 22 | most_liked: "Nová 'líbí se'" 23 | most_bookmarked: "Záložky" 24 | user: "Uživatel" 25 | users: "Účastníci" 26 | days_visited: "Přítomen dnů" 27 | topics: "Témata" 28 | topic: "Témata" 29 | likes: "Nová 'líbí se'" 30 | bookmarks: "Záložky" 31 | score: "Skóre" 32 | replies: "Odpovědi" 33 | visits: "Návštěv" 34 | -------------------------------------------------------------------------------- /config/locales/server.da.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | da: 8 | yearly_review: 9 | action: 10 | topics_created: "Emner" 11 | most_replied_to: "Svar" 12 | replies_created: "Svar" 13 | likes_given: "Syntes godt om" 14 | likes_received: "Syntes godt om" 15 | visits: "Besøgsdage" 16 | rank_type: 17 | title: 18 | most_liked: "Flest 'Synes godt om' andre" 19 | action_types: 20 | most_popular: "Score" 21 | most_replied_to: "Svar" 22 | most_liked: "Syntes godt om" 23 | most_bookmarked: "Bogmærker" 24 | user: "bruger" 25 | users: "Deltagere" 26 | days_visited: "Besøgsdage" 27 | topics: "Emner" 28 | topic: "Emne" 29 | likes: "Syntes godt om" 30 | bookmarks: "Bogmærker" 31 | score: "Score" 32 | replies: "Svar" 33 | visits: "Besøg" 34 | more_badge_users: "Og %{more} mere..." 35 | -------------------------------------------------------------------------------- /config/locales/server.de.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | de: 8 | site_settings: 9 | yearly_review_enabled: "Aktiviere den Jahresrückblick. Am 1. Januar wird automatisch ein Thema erstellt, in dem die Forumsaktivitäten des vergangenen Jahres zusammengefasst werden." 10 | yearly_review_categories: "Öffentliche Kategorien zum Zusammenfassen. Für jede der Top-5-Kategorien in dieser Liste wird ein Beitrag mit Statistiken erstellt. Lass das Feld leer, um die Top 5 der öffentlichen Kategorien zu verwenden." 11 | yearly_review_exclude_staff: "Schließe das Team aus der Mitgliederstatistik aus." 12 | yearly_review_include_user_stats: "Füge Statistiken zur Identifizierung der Mitglieder hinzu." 13 | yearly_review_include_private_categories: "Beziehe Mitgliederaktivitäten aus privaten oder lesebeschränkten Kategorien in den Rückblick ein." 14 | yearly_review_publish_category: "Kategorie, in welcher der Rückblick veröffentlicht werden soll. Es wird dringend empfohlen, die Kategorie Team oder eine andere private Kategorie anzugeben, damit du das Thema vor der Veröffentlichung sehen kannst." 15 | yearly_review_featured_badge: "Gib den vollständigen Namen des Abzeichens ein. Kann leer gelassen werden." 16 | yearly_review: 17 | topic_title: "%{year}: das Jahr im Rückblick" 18 | category_topics_title: "Top #%{category} Themen" 19 | title: 20 | users_section: "Top-Benutzer %{year}" 21 | topics_created: "Meiste Themen" 22 | replies_created: "Meiste Antworten" 23 | likes_given: "Meiste vergebene „Gefällt mir“" 24 | likes_received: "Meiste erhaltene „Gefällt mir“" 25 | visits: "Meiste Besuche" 26 | daily_visits: "Tägliche Besuche" 27 | time_read: "Meiste Lesezeit" 28 | most_replied_to: "Häufigste Antworten an" 29 | featured_badge: "Benutzer, denen das Abzeichen „%{badge_name}“ verliehen wurde" 30 | action: 31 | topics_created: "Themen" 32 | most_replied_to: "Antworten" 33 | replies_created: "Antworten" 34 | likes_given: "„Gefällt mir“" 35 | likes_received: "„Gefällt mir“" 36 | visits: "Besuchstage" 37 | time_read: "Stunden gelesen" 38 | rank_type: 39 | title: 40 | most_liked: "Am meisten „Gefällt mir“" 41 | most_replied_to: "Meiste Antworten" 42 | most_popular: "Am beliebtesten" 43 | most_bookmarked: "Am häufigsten mit Lesezeichen versehen" 44 | most_read: "Meist gelesen" 45 | action_types: 46 | most_popular: "Score" 47 | most_replied_to: "Antworten" 48 | most_liked: "„Gefällt mir“" 49 | most_bookmarked: "Lesezeichen" 50 | most_read: "Stunden gelesen" 51 | user: "Benutzer" 52 | users: "Benutzer" 53 | days_visited: "Besuchstage" 54 | all_yearly_visits: "Alle jährlichen Besuche" 55 | topics: "Themen" 56 | topic: "Thema" 57 | likes: "„Gefällt mir“" 58 | bookmarks: "Lesezeichen" 59 | score: "Score" 60 | replies: "Antworten" 61 | visits: "Besuche" 62 | more_badge_users: "Und %{more} mehr …" 63 | -------------------------------------------------------------------------------- /config/locales/server.el.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | el: 8 | yearly_review: 9 | action: 10 | topics_created: "Θέματα" 11 | most_replied_to: "Απαντήσεις" 12 | replies_created: "Απαντήσεις" 13 | likes_given: "Μου αρέσει" 14 | likes_received: "Μου αρέσει" 15 | visits: "Μέρες Επίσκεψης" 16 | rank_type: 17 | title: 18 | most_liked: "Περισσότερα \"Μου Αρέσει\"" 19 | action_types: 20 | most_popular: "Βαθμολογία" 21 | most_replied_to: "Απαντήσεις" 22 | most_liked: "Μου αρέσει" 23 | most_bookmarked: "Σελιδοδείκτες" 24 | user: "Όνομα Χρήστη" 25 | users: "Χρήστες" 26 | days_visited: "Μέρες Επίσκεψης" 27 | topics: "Θέματα" 28 | topic: "Νήμα" 29 | likes: "Μου αρέσει" 30 | bookmarks: "Σελιδοδείκτες" 31 | score: "Βαθμολογία" 32 | replies: "Απαντήσεις" 33 | visits: "Επισκέψεις" 34 | more_badge_users: "Και %{more} ακόμη..." 35 | -------------------------------------------------------------------------------- /config/locales/server.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | site_settings: 3 | yearly_review_enabled: "Enable the yearly review. On January 1st, a topic will be automatically created summarizing the previous year's forum activity." 4 | yearly_review_categories: "Public categories to summarize. One post with stats will be created for each of the top 5 categories in this list. Leave blank to use the top 5 public categories." 5 | yearly_review_exclude_staff: "Exclude Staff from member stats." 6 | yearly_review_include_user_stats: "Add member-identifying stats." 7 | yearly_review_include_private_categories: "Include member activity from private or read-restricted categories in the review." 8 | yearly_review_publish_category: "Category the review will be published in. It is highly recommended to specify the staff or other private category so you can view the topic before making it public." 9 | yearly_review_featured_badge: "Enter the full badge name. Can be left blank." 10 | yearly_review: 11 | topic_title: "%{year}: The Year in Review" 12 | category_topics_title: "Top #%{category} Topics" 13 | title: 14 | users_section: "%{year}'s Top Users" 15 | topics_created: "Most Topics" 16 | replies_created: "Most Replies" 17 | likes_given: "Most Likes Given" 18 | likes_received: "Most Likes Received" 19 | visits: "Most Visits" 20 | daily_visits: "Daily Visits" 21 | time_read: "Most Time Reading" 22 | most_replied_to: "Most Replied to" 23 | featured_badge: "Users Granted the %{badge_name} Badge" 24 | action: 25 | topics_created: "Topics" 26 | most_replied_to: "Replies" 27 | replies_created: "Replies" 28 | likes_given: "Likes" 29 | likes_received: "Likes" 30 | visits: "Days Visited" 31 | time_read: "Hours Read" 32 | rank_type: 33 | title: 34 | most_liked: "Most Liked" 35 | most_replied_to: "Most Replies" 36 | most_popular: "Most Popular" 37 | most_bookmarked: "Most Bookmarked" 38 | most_read: "Most Read" 39 | action_types: 40 | most_popular: "Score" 41 | most_replied_to: "Replies" 42 | most_liked: "Likes" 43 | most_bookmarked: "Bookmarks" 44 | most_read: "Hours Read" 45 | user: "User" 46 | users: "Users" 47 | days_visited: "Days Visited" 48 | all_yearly_visits: "All Yearly Visits" 49 | topics: "Topics" 50 | topic: "Topic" 51 | likes: "Likes" 52 | bookmarks: "Bookmarks" 53 | score: "Score" 54 | replies: "Replies" 55 | visits: "Visits" 56 | more_badge_users: "And %{more} more..." 57 | -------------------------------------------------------------------------------- /config/locales/server.en_GB.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | en_GB: 8 | -------------------------------------------------------------------------------- /config/locales/server.es.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | es: 8 | site_settings: 9 | yearly_review_enabled: "Activa la revisión anual. El 1 de enero se creará automáticamente un tema que resumirá la actividad del foro del año anterior." 10 | yearly_review_categories: "Categorías públicas para resumir. Se creará una entrada con estadísticas para cada una de las 5 categorías principales de esta lista. Déjalo en blanco para utilizar las 5 categorías públicas principales." 11 | yearly_review_exclude_staff: "Excluir al personal de las estadísticas de los miembros." 12 | yearly_review_include_user_stats: "Añadir estadísticas identificativas de los miembros." 13 | yearly_review_include_private_categories: "Incluir en la revisión la actividad de los miembros de las categorías privadas o de lectura restringida." 14 | yearly_review_publish_category: "Categoría en la que se publicará la revisión. Es muy recomendable especificar la categoría personal u otra categoría privada para que puedas ver el tema antes de hacerlo público." 15 | yearly_review_featured_badge: "Introduce el nombre completo de la insignia. Se puede dejar en blanco." 16 | yearly_review: 17 | topic_title: "%{year}: Resumen del año" 18 | category_topics_title: "Principales #%{category} temas" 19 | title: 20 | users_section: "Usuarios principales de %{year}" 21 | topics_created: "Más temas" 22 | replies_created: "Más respuestas" 23 | likes_given: "Mayor número de «Me gusta» dados" 24 | likes_received: "Mayor número de «Me gusta» recibidos" 25 | visits: "Más visitas" 26 | daily_visits: "Visitas diarias" 27 | time_read: "Más tiempo de lectura" 28 | most_replied_to: "Más respondido a" 29 | featured_badge: "Usuarios con la insignia %{badge_name}" 30 | action: 31 | topics_created: "Temas" 32 | most_replied_to: "Respuestas" 33 | replies_created: "Respuestas" 34 | likes_given: "Me gusta dados" 35 | likes_received: "Me gusta recibidos" 36 | visits: "Días visitados" 37 | time_read: "Horas de lectura" 38 | rank_type: 39 | title: 40 | most_liked: "Más me gusta recibidos" 41 | most_replied_to: "Más respuestas" 42 | most_popular: "Más popular" 43 | most_bookmarked: "Más marcados" 44 | most_read: "Más leídos" 45 | action_types: 46 | most_popular: "Puntuación" 47 | most_replied_to: "Respuestas" 48 | most_liked: "Me gusta" 49 | most_bookmarked: "Marcadores" 50 | most_read: "Horas de lectura" 51 | user: "Usuario" 52 | users: "Usuarios" 53 | days_visited: "Días visitados" 54 | all_yearly_visits: "Todas las visitas anuales" 55 | topics: "Temas" 56 | topic: "Tema" 57 | likes: "Me gusta" 58 | bookmarks: "Marcadores" 59 | score: "Puntuación" 60 | replies: "Respuestas" 61 | visits: "Visitas" 62 | more_badge_users: "Y %{more} más..." 63 | -------------------------------------------------------------------------------- /config/locales/server.et.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | et: 8 | yearly_review: 9 | action: 10 | topics_created: "Teemasid" 11 | most_replied_to: "Vastused" 12 | replies_created: "Vastused" 13 | likes_given: "Meeldimisi antud" 14 | likes_received: "Meeldimisi antud" 15 | visits: "Päevi külastatud" 16 | rank_type: 17 | title: 18 | most_liked: "Enim meeldinud" 19 | action_types: 20 | most_popular: "Skoor" 21 | most_replied_to: "Vastused" 22 | most_liked: "Meeldimisi antud" 23 | most_bookmarked: "Järjehoidjat" 24 | user: "Kasutaja" 25 | users: "Kasutajad" 26 | days_visited: "Päevi külastatud" 27 | topics: "Teemasid" 28 | topic: "Teema" 29 | likes: "Meeldimisi antud" 30 | bookmarks: "Järjehoidjat" 31 | score: "Skoor" 32 | replies: "Vastused" 33 | visits: "Külastusi" 34 | -------------------------------------------------------------------------------- /config/locales/server.fa_IR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fa_IR: 8 | yearly_review: 9 | action: 10 | topics_created: "موضوعات" 11 | most_replied_to: "پاسخ‌ها" 12 | replies_created: "پاسخ‌ها" 13 | likes_given: "پسندیده‌ها" 14 | likes_received: "پسندیده‌ها" 15 | visits: "روز های بازدید شده" 16 | rank_type: 17 | title: 18 | most_liked: "بیشترین پسندیده شده" 19 | action_types: 20 | most_popular: "امتیاز" 21 | most_replied_to: "پاسخ‌ها" 22 | most_liked: "پسندیده‌ها" 23 | most_bookmarked: "نشانک‌ها" 24 | user: "کاربر" 25 | users: "کاربران" 26 | days_visited: "روز های بازدید شده" 27 | topics: "موضوعات" 28 | topic: "موضوعات" 29 | likes: "پسندیده‌ها" 30 | bookmarks: "نشانک‌ها" 31 | score: "امتیاز" 32 | replies: "پاسخ‌ها" 33 | visits: "بازدیدها" 34 | -------------------------------------------------------------------------------- /config/locales/server.fi.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fi: 8 | site_settings: 9 | yearly_review_enabled: "Ota vuosikatsaus käyttöön. Tammikuun 1. päivänä luodaan automaattisesti ketju, joka sisältää yhteenvedon edellisen vuoden foorumitoiminnasta." 10 | yearly_review_categories: "Yleiset alueet, joista yhteenveto tehdään. Yksi tilastot sisältävä viesti luodaan jokaiselle tämän luettelon viidelle suosituimmalle alueelle. Jätä tyhjäksi käyttääksesi viittä suosituinta julkista aluetta." 11 | yearly_review_exclude_staff: "Jätä henkilökunta pois jäsentilastoista." 12 | yearly_review_include_user_stats: "Lisää tilastoja, joista jäsenet ovat tunnistettavissa." 13 | yearly_review_include_private_categories: "Sisällytä katsaukseen jäsenten toiminta yksityisillä tai lukurajoitetuilla alueilla." 14 | yearly_review_publish_category: "Alue, jolla katsausjulkaistaan. On erittäin suositeltavaa määrittää henkilökunnan alue tai muu yksityinen alue, jotta voit tarkastella aihetta ennen sen julkistamista." 15 | yearly_review_featured_badge: "Anna kunniamerkin koko nimi. Voidaan jättää tyhjäksi." 16 | yearly_review: 17 | topic_title: "%{year}: vuosikatsaus" 18 | category_topics_title: "Alueen %{category} suosituimmat ketjut" 19 | title: 20 | users_section: "Vuoden %{year} huippukäyttäjät" 21 | topics_created: "Eniten ketjuja" 22 | replies_created: "Eniten vastauksia" 23 | likes_given: "Eniten annettuja tykkäyksiä" 24 | likes_received: "Eniten saatuja tykkäyksiä" 25 | visits: "Eniten vierailuja" 26 | daily_visits: "Päivittäisiä vierailuja" 27 | time_read: "Eniten lukuaikaa" 28 | most_replied_to: "Useimmin vastatut" 29 | featured_badge: "Käyttäjät, joille on annettu kunniamerkki %{badge_name}" 30 | action: 31 | topics_created: "Ketjuja" 32 | most_replied_to: "Vastauksia" 33 | replies_created: "Vastauksia" 34 | likes_given: "Tykkäyksiä" 35 | likes_received: "Tykkäyksiä" 36 | visits: "Vierailupäiviä" 37 | time_read: "Lukutunteja" 38 | rank_type: 39 | title: 40 | most_liked: "Tykätyimmät" 41 | most_replied_to: "Eniten vastauksia" 42 | most_popular: "Suosituimmat" 43 | most_bookmarked: "Kirjanmerkityimmät" 44 | most_read: "Luetuimmat" 45 | action_types: 46 | most_popular: "Pistemäärä" 47 | most_replied_to: "Vastauksia" 48 | most_liked: "Tykkäyksiä" 49 | most_bookmarked: "Kirjanmerkkejä" 50 | most_read: "Lukutunteja" 51 | user: "Käyttäjä" 52 | users: "Käyttäjiä" 53 | days_visited: "Vierailupäiviä" 54 | all_yearly_visits: "Kaikki vuosittaiset vierailut" 55 | topics: "Ketjuja" 56 | topic: "Ketju" 57 | likes: "Tykkäyksiä" 58 | bookmarks: "Kirjanmerkkejä" 59 | score: "Pistemäärä" 60 | replies: "Vastauksia" 61 | visits: "Vierailuja" 62 | more_badge_users: "Ja %{more} muuta..." 63 | -------------------------------------------------------------------------------- /config/locales/server.fr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | fr: 8 | site_settings: 9 | yearly_review_enabled: "Activez la révision annuelle. Le 1er janvier, un sujet sera automatiquement créé pour résumer l'activité du forum de l'année précédente." 10 | yearly_review_categories: "Catégories publiques à résumer. Une publication avec des statistiques sera créée pour chacune des 5 premières catégories de cette liste. Laissez-les vide pour utiliser les 5 principales catégories publiques." 11 | yearly_review_exclude_staff: "Exclure le personnel des statistiques des membres." 12 | yearly_review_include_user_stats: "Ajouter les statistiques d'identification des membres." 13 | yearly_review_include_private_categories: "Inclure l'activité du membre à partir de catégories privées ou en lecture restreintes dans la révision." 14 | yearly_review_publish_category: "Catégorie dans laquelle l'avis sera publié. Il est fortement recommandé de spécifier la catégorie du personnel ou une autre catégorie privée afin de pouvoir consulter le sujet avant de le rendre public." 15 | yearly_review_featured_badge: "Saisissez le nom complet du badge. Cet espace peut être laissé vide." 16 | yearly_review: 17 | topic_title: "%{year} : bilan de l'année" 18 | category_topics_title: "%{category} principaux sujets" 19 | title: 20 | users_section: "Meilleurs utilisateurs de %{year}" 21 | topics_created: "Le plus de sujets" 22 | replies_created: "Le plus de réponses" 23 | likes_given: "Le plus de « J'aime » donnés" 24 | likes_received: "Le plus de « J'aime » reçus" 25 | visits: "Le plus de visites" 26 | daily_visits: "Visites quotidiennes" 27 | time_read: "Le plus de temps de lecture" 28 | most_replied_to: "Le plus de réponses reçues" 29 | featured_badge: "Utilisateurs ayant obtenu le badge %{badge_name}" 30 | action: 31 | topics_created: "Sujets" 32 | most_replied_to: "Réponses" 33 | replies_created: "Réponses" 34 | likes_given: "« J'aime » attribués" 35 | likes_received: "« J'aime » attribués" 36 | visits: "Jours de visite" 37 | time_read: "Heures de lecture" 38 | rank_type: 39 | title: 40 | most_liked: "Le plus aimé" 41 | most_replied_to: "Le plus de réponses" 42 | most_popular: "Le plus populaire" 43 | most_bookmarked: "Le plus marqué" 44 | most_read: "Le plus lu" 45 | action_types: 46 | most_popular: "Score" 47 | most_replied_to: "Réponses" 48 | most_liked: "« J'aime » attribués" 49 | most_bookmarked: "Signets" 50 | most_read: "Heures de lecture" 51 | user: "Utilisateur" 52 | users: "Utilisateurs" 53 | days_visited: "Jours de visite" 54 | all_yearly_visits: "Toutes les visites de l'année" 55 | topics: "Sujets" 56 | topic: "Sujet" 57 | likes: "« J'aime » attribués" 58 | bookmarks: "Signets" 59 | score: "Score" 60 | replies: "Réponses" 61 | visits: "Visites" 62 | more_badge_users: "Et %{more} de plus…" 63 | -------------------------------------------------------------------------------- /config/locales/server.gl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | gl: 8 | yearly_review: 9 | action: 10 | topics_created: "Temas" 11 | most_replied_to: "Réplicas" 12 | replies_created: "Réplicas" 13 | likes_given: "Gústame" 14 | likes_received: "Gústame" 15 | visits: "Días desde visita" 16 | rank_type: 17 | title: 18 | most_liked: "Con máis gústames" 19 | action_types: 20 | most_popular: "Puntuación" 21 | most_replied_to: "Réplicas" 22 | most_liked: "Gústame" 23 | most_bookmarked: "Marcadores" 24 | user: "Usuario" 25 | users: "Usuarios" 26 | days_visited: "Días desde visita" 27 | topics: "Temas" 28 | topic: "Tema" 29 | likes: "Gústame" 30 | bookmarks: "Marcadores" 31 | score: "Puntuación" 32 | replies: "Réplicas" 33 | visits: "Visitas" 34 | -------------------------------------------------------------------------------- /config/locales/server.he.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | he: 8 | site_settings: 9 | yearly_review_enabled: "הפעלת הסקירה השנתית. ב־1 בינואר, ייווצר נושא אוטומטית שמסכם את פעילות הפורום בשנה החולפת." 10 | yearly_review_categories: "קטגוריות ציבוריות לסקירה. ייווצר פוסט אחד עם סטטיסטיקה לכל אחת מחמש הקטגוריות המובילות ברשימה הזאת. יש להשאיר ריק כדי להשתמש בחמש הקטגוריות הציבוריות המובילות." 11 | yearly_review_exclude_staff: "להחריג את הסגל מסטטיסטיקת החברים." 12 | yearly_review_include_user_stats: "הוספת נתונים גלויים על משתמשים." 13 | yearly_review_include_private_categories: "לכלול פעילות חברים מקטגוריות פרטיות או מוגבלות לקריאה בסקירה." 14 | yearly_review_publish_category: "הקטגוריה בה תתפרסם הסקירה. מומלץ מאוד לציין את הסגל או קטגוריה פרטית אחרת כדי לאפשר לך לצפות בנושא לפני הצגתו לציבור." 15 | yearly_review_featured_badge: "נא למלא את שם העיטור המלא. יכול להישאר ריק." 16 | yearly_review: 17 | topic_title: "%{year}: סקירת השנה" 18 | category_topics_title: "%{category} הנושאים המובילים" 19 | title: 20 | users_section: "המשתמשים המובילים לשנת %{year}" 21 | topics_created: "הכי הרבה נושאים" 22 | replies_created: "הכי הרבה תגובות" 23 | likes_given: "הכי הרבה לייקים ניתנו" 24 | likes_received: "הכי הרבה לייקים התקבלו" 25 | visits: "הכי הרבה ביקורים" 26 | daily_visits: "ביקורים יומיים" 27 | time_read: "זמן הקריאה המרבי" 28 | most_replied_to: "התקבלו הכי הרבה תגובות" 29 | featured_badge: "משתמשים שהוענק להם העיטור %{badge_name}" 30 | action: 31 | topics_created: "נושאים" 32 | most_replied_to: "תגובות" 33 | replies_created: "תגובות" 34 | likes_given: "לייקים" 35 | likes_received: "לייקים" 36 | visits: "ביקורים יומיים" 37 | time_read: "שעות קריאה" 38 | rank_type: 39 | title: 40 | most_liked: "הכי הרבה לייקים" 41 | most_replied_to: "הכי הרבה תגובות" 42 | most_popular: "הכי נפוץ" 43 | most_bookmarked: "הכי נוסף לסימניות" 44 | most_read: "הכי נקרא" 45 | action_types: 46 | most_popular: "ניקוד" 47 | most_replied_to: "תגובות" 48 | most_liked: "לייקים" 49 | most_bookmarked: "סימניות" 50 | most_read: "שעות קריאה" 51 | user: "משתמש" 52 | users: "משתמשים" 53 | days_visited: "ביקורים יומיים" 54 | all_yearly_visits: "כל הביקורים השנתיים" 55 | topics: "נושאים" 56 | topic: "נושא" 57 | likes: "לייקים" 58 | bookmarks: "סימניות" 59 | score: "ניקוד" 60 | replies: "תגובות" 61 | visits: "ביקורים" 62 | more_badge_users: "ועוד %{more}…" 63 | -------------------------------------------------------------------------------- /config/locales/server.hr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hr: 8 | yearly_review: 9 | action: 10 | topics_created: "Tema" 11 | most_replied_to: "Odgovori" 12 | replies_created: "Odgovori" 13 | likes_given: "Like-ova" 14 | likes_received: "Like-ova" 15 | visits: "Dana posjećeno" 16 | rank_type: 17 | title: 18 | most_liked: "Najviše se sviđa" 19 | action_types: 20 | most_popular: "Ocjena" 21 | most_replied_to: "Odgovori" 22 | most_liked: "Like-ova" 23 | most_bookmarked: "Zabilješke" 24 | user: "¸Korisnik" 25 | users: "Korisnika" 26 | days_visited: "Dana posjećeno" 27 | topics: "Tema" 28 | topic: "Teme" 29 | likes: "Like-ova" 30 | bookmarks: "Zabilješke" 31 | score: "Ocjena" 32 | replies: "Odgovori" 33 | visits: "Posjeta" 34 | more_badge_users: "I %{more} vIše..." 35 | -------------------------------------------------------------------------------- /config/locales/server.hu.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hu: 8 | yearly_review: 9 | action: 10 | topics_created: "Témák" 11 | most_replied_to: "Válaszok" 12 | replies_created: "Válaszok" 13 | likes_given: "Kapott kedvelések" 14 | likes_received: "Kapott kedvelések" 15 | visits: "Látogatott nap" 16 | rank_type: 17 | title: 18 | most_liked: "Legtöbbet kedvelt" 19 | action_types: 20 | most_popular: "Pontszám" 21 | most_replied_to: "Válaszok" 22 | most_liked: "Kapott kedvelések" 23 | most_bookmarked: "Könyvjelzők" 24 | user: "Felhasználó" 25 | users: "Felhasználók" 26 | days_visited: "Látogatott nap" 27 | topics: "Témák" 28 | topic: "Témák" 29 | likes: "Kapott kedvelések" 30 | bookmarks: "Könyvjelzők" 31 | score: "Pontszám" 32 | replies: "Válaszok" 33 | visits: "Látogatás" 34 | more_badge_users: "És még további %{more}..." 35 | -------------------------------------------------------------------------------- /config/locales/server.hy.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | hy: 8 | yearly_review: 9 | action: 10 | topics_created: "Թեմա" 11 | most_replied_to: "Պատասխաններ" 12 | replies_created: "Պատասխաններ" 13 | likes_given: "Հավանում" 14 | likes_received: "Հավանում" 15 | visits: "Այցելության Օր" 16 | rank_type: 17 | title: 18 | most_liked: "Ամենաշատ Հավանած" 19 | action_types: 20 | most_popular: "Քանակ" 21 | most_replied_to: "Պատասխաններ" 22 | most_liked: "Հավանում" 23 | most_bookmarked: "Էջանշաններ" 24 | user: "Օգտատեր" 25 | users: "Օգտատեր" 26 | days_visited: "Այցելության Օր" 27 | topics: "Թեմա" 28 | topic: "Թեմա" 29 | likes: "Հավանում" 30 | bookmarks: "Էջանշաններ" 31 | score: "Քանակ" 32 | replies: "Պատասխաններ" 33 | visits: "Այցելություններ" 34 | -------------------------------------------------------------------------------- /config/locales/server.id.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | id: 8 | yearly_review: 9 | action: 10 | topics_created: "Topik-topik" 11 | most_replied_to: "balasan" 12 | replies_created: "balasan" 13 | likes_given: "Likes Diberikan" 14 | likes_received: "Likes Diberikan" 15 | visits: "Hari Berkunjung" 16 | rank_type: 17 | title: 18 | most_liked: "Paling Disukai" 19 | action_types: 20 | most_popular: "Skor" 21 | most_replied_to: "balasan" 22 | most_liked: "Likes Diberikan" 23 | most_bookmarked: "Markah" 24 | user: "Pengguna" 25 | users: "Pengguna" 26 | days_visited: "Hari Berkunjung" 27 | topics: "Topik-topik" 28 | topic: "Topik" 29 | likes: "Likes Diberikan" 30 | bookmarks: "Markah" 31 | score: "Skor" 32 | replies: "balasan" 33 | visits: "mengunjungi" 34 | -------------------------------------------------------------------------------- /config/locales/server.it.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | it: 8 | site_settings: 9 | yearly_review_enabled: "Abilita la revisione annuale. Il 1° gennaio verrà creato automaticamente un argomento che riassume l'attività del forum dell'anno precedente." 10 | yearly_review_categories: "Categorie pubbliche da riassumere. Verrà creato un post con statistiche per ciascuna delle prime 5 categorie in questo elenco. Lascia vuoto per utilizzare le prime 5 categorie pubbliche." 11 | yearly_review_exclude_staff: "Escludi lo staff dalle statistiche dei membri." 12 | yearly_review_include_user_stats: "Aggiungi statistiche identificative dei membri." 13 | yearly_review_include_private_categories: "Includi nella revisione l'attività dei membri di categorie private o con limitazioni di lettura." 14 | yearly_review_publish_category: "Categoria in cui verrà pubblicata la revisione. Si consiglia vivamente di specificare la categoria personale o altra categoria privata in modo da poter visualizzare l'argomento prima di renderlo pubblico." 15 | yearly_review_featured_badge: "Inserisci il nome completo del distintivo. Può essere lasciato vuoto." 16 | yearly_review: 17 | topic_title: "%{year}: L'anno della revisione" 18 | category_topics_title: "I primi %{category} argomenti" 19 | title: 20 | users_section: "I principali utenti dell'anno %{year}" 21 | topics_created: "Con più argomenti" 22 | replies_created: "Con più risposte" 23 | likes_given: "Più \"Mi piace\" dati" 24 | likes_received: "Più \"Mi piace\" ricevuti" 25 | visits: "Con più visite" 26 | daily_visits: "Visite giornaliere" 27 | time_read: "Maggiori tempi di lettura" 28 | most_replied_to: "Più risposte ricevute" 29 | featured_badge: "Utenti che hanno ottenuto il distintivo %{badge_name}" 30 | action: 31 | topics_created: "Argomenti" 32 | most_replied_to: "Risposte" 33 | replies_created: "Risposte" 34 | likes_given: "Mi piace" 35 | likes_received: "Mi piace" 36 | visits: "Giorni di frequenza" 37 | time_read: "Ore di lettura" 38 | rank_type: 39 | title: 40 | most_liked: "Con più \"Mi Piace\"" 41 | most_replied_to: "Più risposte" 42 | most_popular: "Più popolari" 43 | most_bookmarked: "Più aggiunti ai segnalibri" 44 | most_read: "Più letti" 45 | action_types: 46 | most_popular: "Punteggio" 47 | most_replied_to: "Risposte" 48 | most_liked: "Mi piace" 49 | most_bookmarked: "Segnalibri" 50 | most_read: "Ore di lettura" 51 | user: "Utente" 52 | users: "Utenti" 53 | days_visited: "Giorni di frequenza" 54 | all_yearly_visits: "Tutte le visite annuali" 55 | topics: "Argomenti" 56 | topic: "Argomento" 57 | likes: "Mi piace" 58 | bookmarks: "Segnalibri" 59 | score: "Punteggio" 60 | replies: "Risposte" 61 | visits: "Visite" 62 | more_badge_users: "E altri %{more}..." 63 | -------------------------------------------------------------------------------- /config/locales/server.ja.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ja: 8 | site_settings: 9 | yearly_review_enabled: "年次レビューを有効化します。1 月 1 日に、前年のフォーラムのアクティビティを要約したトピックが自動的に作成されます。" 10 | yearly_review_categories: "要約する公開カテゴリです。このリストの上位 5 つのカテゴリごとに、統計付きの投稿が 1 件作成されます。上位 5 件の公開カテゴリを使用する場合は、空白のままにします。" 11 | yearly_review_exclude_staff: "メンバー統計からスタッフを除外します。" 12 | yearly_review_include_user_stats: "メンバーを特定する統計を追加します。" 13 | yearly_review_include_private_categories: "レビューに非公開のカテゴリまたは読み取り制限のあるカテゴリのメンバーアクティビティを含めます。" 14 | yearly_review_publish_category: "レビューが公開されるカテゴリです。公開前にトピックを確認できるように、スタッフまたはその他の非公開カテゴリを指定することを強くお勧めします。" 15 | yearly_review_featured_badge: "バッジのフルネームを入力します。空白にすることができます。" 16 | yearly_review: 17 | topic_title: "%{year} 年を振り返る" 18 | category_topics_title: "上位 %{category} 件のトピック" 19 | title: 20 | users_section: "%{year} 年の上位ユーザー" 21 | topics_created: "最も多かったトピック" 22 | replies_created: "最も多かった返信" 23 | likes_given: "最も多く与えた「いいね!」" 24 | likes_received: "最も多く受けた「いいね!」" 25 | visits: "最も多かったアクセス" 26 | daily_visits: "1 日当たりのアクセス" 27 | time_read: "最も長く閲覧された項目" 28 | most_replied_to: "最も返信の多かった項目" 29 | featured_badge: "%{badge_name} バッジを付与されたユーザー" 30 | action: 31 | topics_created: "トピック" 32 | most_replied_to: "返信先" 33 | replies_created: "返信" 34 | likes_given: "「いいね!」" 35 | likes_received: "「いいね!」" 36 | visits: "アクセス日数" 37 | time_read: "閲覧時間" 38 | rank_type: 39 | title: 40 | most_liked: "最も「いいね!」の多い項目" 41 | most_replied_to: "最も返信の多い項目" 42 | most_popular: "最も人気の高い項目" 43 | most_bookmarked: "最もブックマークされた項目" 44 | most_read: "最も閲覧された項目" 45 | action_types: 46 | most_popular: "スコア" 47 | most_replied_to: "返信" 48 | most_liked: "「いいね!」" 49 | most_bookmarked: "ブックマーク" 50 | most_read: "閲覧時間" 51 | user: "ユーザー" 52 | users: "ユーザー" 53 | days_visited: "アクセス日数" 54 | all_yearly_visits: "年間アクセス数" 55 | topics: "トピック" 56 | topic: "トピック" 57 | likes: "「いいね!」" 58 | bookmarks: "ブックマーク" 59 | score: "スコア" 60 | replies: "返信" 61 | visits: "アクセス" 62 | more_badge_users: "および他 %{more} 人..." 63 | -------------------------------------------------------------------------------- /config/locales/server.ko.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ko: 8 | yearly_review: 9 | action: 10 | topics_created: "글" 11 | most_replied_to: "댓글" 12 | replies_created: "댓글" 13 | likes_given: "좋아요 수" 14 | likes_received: "좋아요 수" 15 | visits: "방문 일수" 16 | rank_type: 17 | title: 18 | most_liked: "가장 좋아함" 19 | action_types: 20 | most_popular: "점수" 21 | most_replied_to: "댓글" 22 | most_liked: "좋아요 수" 23 | most_bookmarked: "북마크" 24 | user: "사용자" 25 | users: "사용자들" 26 | days_visited: "방문 일수" 27 | topics: "글" 28 | topic: "토픽" 29 | likes: "좋아요 수" 30 | bookmarks: "북마크" 31 | score: "점수" 32 | replies: "댓글" 33 | visits: "방문" 34 | more_badge_users: "그리고 %{more} 더..." 35 | -------------------------------------------------------------------------------- /config/locales/server.lt.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | lt: 8 | yearly_review: 9 | action: 10 | topics_created: "Temos" 11 | most_replied_to: "Atsakymai" 12 | replies_created: "Atsakymai" 13 | likes_given: "Patikimai" 14 | likes_received: "Patikimai" 15 | visits: "Apsilankymo dienos" 16 | rank_type: 17 | title: 18 | most_liked: "Labiausiai Mėgstamos" 19 | action_types: 20 | most_popular: "Rezultatas" 21 | most_replied_to: "Atsakymai" 22 | most_liked: "Patikimai" 23 | most_bookmarked: "Žymės" 24 | user: "Vartotojas" 25 | users: "Vartotojai" 26 | days_visited: "Apsilankymo dienos" 27 | topics: "Temos" 28 | topic: "Tema" 29 | likes: "Patikimai" 30 | bookmarks: "Žymės" 31 | score: "Rezultatas" 32 | replies: "Atsakymai" 33 | visits: "Apsilankymai" 34 | more_badge_users: "Ir %{more} daugiau..." 35 | -------------------------------------------------------------------------------- /config/locales/server.lv.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | lv: 8 | yearly_review: 9 | action: 10 | topics_created: "Tēmas" 11 | most_replied_to: "Atbildes" 12 | replies_created: "Atbildes" 13 | likes_given: "Patīk" 14 | likes_received: "Patīk" 15 | visits: "Dienas, kurās apmeklēja" 16 | rank_type: 17 | title: 18 | most_liked: "Visvairāk atzinību" 19 | action_types: 20 | most_popular: "Rezultāts" 21 | most_replied_to: "Atbildes" 22 | most_liked: "Patīk" 23 | most_bookmarked: "Grāmatzīmes" 24 | user: "Lietotājs" 25 | users: "Lietotāji" 26 | days_visited: "Dienas, kurās apmeklēja" 27 | topics: "Tēmas" 28 | topic: "Tēmas" 29 | likes: "Patīk" 30 | bookmarks: "Grāmatzīmes" 31 | score: "Rezultāts" 32 | replies: "Atbildes" 33 | visits: "Apmeklējumi" 34 | -------------------------------------------------------------------------------- /config/locales/server.nb_NO.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | nb_NO: 8 | yearly_review: 9 | action: 10 | topics_created: "Emner" 11 | most_replied_to: "Svar" 12 | replies_created: "Svar" 13 | likes_given: "Likes tildelt" 14 | likes_received: "Likes tildelt" 15 | visits: "Dager besøkt" 16 | rank_type: 17 | title: 18 | most_liked: "Mest Likt" 19 | action_types: 20 | most_popular: "Poeng" 21 | most_replied_to: "Svar" 22 | most_liked: "Likes tildelt" 23 | most_bookmarked: "Bokmerker" 24 | user: "Bruker" 25 | users: "Deltakere" 26 | days_visited: "Dager besøkt" 27 | topics: "Emner" 28 | topic: "Emne" 29 | likes: "Likes tildelt" 30 | bookmarks: "Bokmerker" 31 | score: "Poeng" 32 | replies: "Svar" 33 | visits: "Besøk" 34 | -------------------------------------------------------------------------------- /config/locales/server.nl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | nl: 8 | site_settings: 9 | yearly_review_enabled: "Schakel de jaarlijkse beoordeling in. Op 1 januari wordt automatisch een topic gemaakt met een samenvatting van de forumactiviteit van het afgelopen jaar." 10 | yearly_review_categories: "Openbare categorieën om samen te vatten. Er wordt één bericht met statistieken gemaakt voor elk van de top 5 categorieën in deze lijst. Laat dit veld leeg om de top 5 openbare categorieën te gebruiken." 11 | yearly_review_exclude_staff: "Sluit medewerkers uit van ledenstatistieken." 12 | yearly_review_include_user_stats: "Voeg ledenidentificerende statistieken toe." 13 | yearly_review_include_private_categories: "Neem ledenactiviteit van privécategorieën of categorieën met leesbeperkingen op in de beoordeling." 14 | yearly_review_publish_category: "Categorie waarin de beoordeling wordt gepubliceerd. Het wordt sterk aanbevolen om de medewerkerscategorie of een andere privécategorie op te geven, zodat je het topic kunt bekijken voordat je het openbaar maakt." 15 | yearly_review_featured_badge: "Voer de volledige badgenaam in. Mag leeg worden gelaten." 16 | yearly_review: 17 | topic_title: "%{year}: het jaaroverzicht" 18 | category_topics_title: "Top %{category} topics" 19 | title: 20 | users_section: "Topgebruikers van %{year}" 21 | topics_created: "Meeste topics" 22 | replies_created: "Meeste antwoorden" 23 | likes_given: "Meeste likes gegeven" 24 | likes_received: "Meeste likes ontvangen" 25 | visits: "Meeste bezoeken" 26 | daily_visits: "Dagelijkse bezoeken" 27 | time_read: "Meeste leestijd" 28 | most_replied_to: "Meest op geantwoord" 29 | featured_badge: "Gebruikers met de badge %{badge_name}" 30 | action: 31 | topics_created: "Topics" 32 | most_replied_to: "Antwoorden" 33 | replies_created: "Antwoorden" 34 | likes_given: "Likes" 35 | likes_received: "Likes" 36 | visits: "Dagen bezocht" 37 | time_read: "Uren gelezen" 38 | rank_type: 39 | title: 40 | most_liked: "Meest geliket" 41 | most_replied_to: "Meeste antwoorden" 42 | most_popular: "Populairst" 43 | most_bookmarked: "Meest gebladwijzerd" 44 | most_read: "Meest gelezen" 45 | action_types: 46 | most_popular: "Score" 47 | most_replied_to: "Antwoorden" 48 | most_liked: "Likes" 49 | most_bookmarked: "Bladwijzers" 50 | most_read: "Uren gelezen" 51 | user: "Gebruiker" 52 | users: "Gebruikers" 53 | days_visited: "Dagen bezocht" 54 | all_yearly_visits: "Alle jaarlijkse bezoeken" 55 | topics: "Topics" 56 | topic: "Topic" 57 | likes: "Likes" 58 | bookmarks: "Bladwijzers" 59 | score: "Score" 60 | replies: "Antwoorden" 61 | visits: "Bezoeken" 62 | more_badge_users: "En nog %{more}..." 63 | -------------------------------------------------------------------------------- /config/locales/server.pl_PL.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pl_PL: 8 | site_settings: 9 | yearly_review_enabled: "Włącz przegląd roczny. 1 stycznia zostanie automatycznie utworzony temat podsumowujący aktywność na forum w poprzednim roku." 10 | yearly_review_categories: "Publiczne kategorie do podsumowania. Jeden post ze statystykami zostanie utworzony dla każdej z 5 najlepszych kategorii z tej listy. Pozostaw puste, aby użyć 5 najlepszych kategorii publicznych." 11 | yearly_review_exclude_staff: "Wyklucz personel ze statystyk członków." 12 | yearly_review_include_user_stats: "Dodaj statystyki identyfikujące członków." 13 | yearly_review_include_private_categories: "Uwzględnij w przeglądzie aktywność użytkowników z kategorii prywatnych lub z ograniczonym dostępem." 14 | yearly_review_publish_category: "Kategoria, w której recenzja zostanie opublikowana. Zaleca się określenie kategorii personelu lub innej kategorii prywatnej, abyś mógł wyświetlić temat przed jego upublicznieniem." 15 | yearly_review_featured_badge: "Wprowadź pełną nazwę odznaki. Można pozostawić puste." 16 | yearly_review: 17 | topic_title: "%{year}: Podsumowanie roku" 18 | category_topics_title: "Top #%{category} tematów" 19 | title: 20 | users_section: "Najlepsi użytkownicy %{year}" 21 | topics_created: "Najwięcej utworzonych tematów" 22 | replies_created: "Najwięcej odpowiedzi" 23 | likes_given: "Najwięcej danych polubień" 24 | likes_received: "Najwięcej danych polubień" 25 | visits: "Najwięcej odwiedzin" 26 | daily_visits: "Dzienne odwiedziny" 27 | time_read: "Najwięcej czasu czytania" 28 | most_replied_to: "Najwięcej odpowiedzi do" 29 | featured_badge: "Użytkownicy, którym przyznano odznakę %{badge_name}" 30 | action: 31 | topics_created: "Tematy" 32 | most_replied_to: "Odpowiedzi" 33 | replies_created: "Odpowiedzi" 34 | likes_given: "Polubienia" 35 | likes_received: "Polubienia" 36 | visits: "Dni odwiedzin" 37 | time_read: "Godzin czytania" 38 | rank_type: 39 | title: 40 | most_liked: "Najbardziej lubiane" 41 | most_replied_to: "Najwięcej odpowiedzi" 42 | most_popular: "Najbardziej popularne" 43 | most_bookmarked: "Najczęściej dodawane do zakładek" 44 | most_read: "Najczęściej czytane" 45 | action_types: 46 | most_popular: "Wynik" 47 | most_replied_to: "Odpowiedzi" 48 | most_liked: "Polubień" 49 | most_bookmarked: "Zakładek" 50 | most_read: "Godzin czytania" 51 | user: "Użytkownik" 52 | users: "Użytkownicy" 53 | days_visited: "Dni odwiedzin" 54 | all_yearly_visits: "Wszystkie roczne wizyty" 55 | topics: "Tematy" 56 | topic: "Temat" 57 | likes: "Polubienia" 58 | bookmarks: "Zakładek" 59 | score: "Wynik" 60 | replies: "Odpowiedzi" 61 | visits: "Odwiedzin" 62 | more_badge_users: "I %{more} wIęcej..." 63 | -------------------------------------------------------------------------------- /config/locales/server.pt.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pt: 8 | site_settings: 9 | yearly_review_featured_badge: "Introduza o nome completo do badge. Pode ficar em branco." 10 | yearly_review: 11 | topic_title: "%{year}: O Ano em Revista" 12 | category_topics_title: "Melhores tópicos da categoria #%{category}" 13 | title: 14 | users_section: "Principais utilizadores do ano %{year}" 15 | topics_created: "Com Mais Tópicos" 16 | replies_created: "Com Mais Respostas" 17 | likes_given: "Com Mais Gostos Dados" 18 | likes_received: "Com Mais Gostos Recebidos" 19 | visits: "Com Mais Visitas" 20 | daily_visits: "Com Mais Dias Visitados" 21 | time_read: "Com Mais Tempo de Leitura" 22 | most_replied_to: "Com Mais Respostas Recebidas" 23 | featured_badge: "Utilizadores aos quais foi atribuido a etiqueta %{badge_name}" 24 | action: 25 | topics_created: "Tópicos" 26 | most_replied_to: "Respostas" 27 | replies_created: "Respostas" 28 | likes_given: "Gostos" 29 | likes_received: "Gostos" 30 | visits: "Dias Visitados" 31 | time_read: "Horas Leitura" 32 | rank_type: 33 | title: 34 | most_liked: "Os Mais Gostados" 35 | most_replied_to: "Os Mais Respondidos" 36 | most_popular: "Os Mais Populares" 37 | most_bookmarked: "Os Mais Marcados" 38 | most_read: "Os Mais Lidos" 39 | action_types: 40 | most_popular: "Total" 41 | most_replied_to: "Respostas" 42 | most_liked: "Gostos" 43 | most_bookmarked: "Marcadores" 44 | most_read: "Horas Leitura" 45 | user: "Utilizador" 46 | users: "Utilizadores" 47 | days_visited: "Dias Visitados" 48 | all_yearly_visits: "Todas as Visitas Anuais" 49 | topics: "Tópicos" 50 | topic: "Tópico" 51 | likes: "Gostos" 52 | bookmarks: "Marcadores" 53 | score: "Total" 54 | replies: "Respostas" 55 | visits: "Visitas" 56 | more_badge_users: "E mais %{more} utilizadores..." 57 | -------------------------------------------------------------------------------- /config/locales/server.pt_BR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | pt_BR: 8 | site_settings: 9 | yearly_review_enabled: "Ative a revisão anual. No dia 1º de janeiro, será criado automaticamente um tópico para resumir a atividade do fórum no ano anterior." 10 | yearly_review_categories: "Categorias públicas para resumir. Será criada uma postagem com estatísticas para cada uma das cinco melhores categorias desta lista. Deixe vazio para usar as cinco melhores categorias públicas." 11 | yearly_review_exclude_staff: "Excluir Equipe das estatísticas dos membros." 12 | yearly_review_include_user_stats: "Adicionar estatísticas para identificar os membros." 13 | yearly_review_include_private_categories: "Inclua atividade de membro da categoria privada ou restrita à leitura na revisão." 14 | yearly_review_publish_category: "A categoria na qual a revisão será publicada. É altamente recomendável especificar a equipe ou outra categoria privada para poder revisar o tópico antes de torná-lo público." 15 | yearly_review_featured_badge: "Insira o nome completo do emblema. Não pode ser deixado em branco." 16 | yearly_review: 17 | topic_title: "%{year}: ano da revisão" 18 | category_topics_title: "Melhores #%{category} tópicos" 19 | title: 20 | users_section: "Melhores usuários(as) do ano %{year}" 21 | topics_created: "Mais tópicos" 22 | replies_created: "Mais respostas" 23 | likes_given: "Mais curtidas dadas" 24 | likes_received: "Mais curtidas recebidas" 25 | visits: "Mais visitas" 26 | daily_visits: "Visitas diárias" 27 | time_read: "Mais tempo de leitura" 28 | most_replied_to: "Mais respondidos(as)" 29 | featured_badge: "Os(as) usuários(as) concederam o emblema %{badge_name}" 30 | action: 31 | topics_created: "Tópicos" 32 | most_replied_to: "Respostas" 33 | replies_created: "Respostas" 34 | likes_given: "Curtidas" 35 | likes_received: "Curtidas" 36 | visits: "Dias visitados" 37 | time_read: "Horas lidas" 38 | rank_type: 39 | title: 40 | most_liked: "Mais curtido(a)" 41 | most_replied_to: "Mais respostas" 42 | most_popular: "Mais popular" 43 | most_bookmarked: "Mais favoritado" 44 | most_read: "Mais lidos" 45 | action_types: 46 | most_popular: "Pontuação" 47 | most_replied_to: "Respostas" 48 | most_liked: "Curtidas" 49 | most_bookmarked: "Favoritos" 50 | most_read: "Horas lidas" 51 | user: "Usuário(a)" 52 | users: "Usuários(as)" 53 | days_visited: "Dias visitados" 54 | all_yearly_visits: "Todas as visitas anuais" 55 | topics: "Tópicos" 56 | topic: "Tópico" 57 | likes: "Curtidas" 58 | bookmarks: "Favoritos" 59 | score: "Pontuação" 60 | replies: "Respostas" 61 | visits: "Acessos" 62 | more_badge_users: "E mais %{more}..." 63 | -------------------------------------------------------------------------------- /config/locales/server.ro.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ro: 8 | yearly_review: 9 | action: 10 | topics_created: "Discuții" 11 | most_replied_to: "Răspunsuri" 12 | replies_created: "Răspunsuri" 13 | likes_given: "Aprecieri date" 14 | likes_received: "Aprecieri date" 15 | visits: "Zile de vizită" 16 | rank_type: 17 | title: 18 | most_liked: "Cele mai apreciate" 19 | action_types: 20 | most_popular: "Scor" 21 | most_replied_to: "Răspunsuri" 22 | most_liked: "Aprecieri date" 23 | most_bookmarked: "Semne de carte" 24 | user: "Utilizator" 25 | users: "Utilizatori" 26 | days_visited: "Zile de vizită" 27 | topics: "Discuții" 28 | topic: "Discuție" 29 | likes: "Aprecieri date" 30 | bookmarks: "Semne de carte" 31 | score: "Scor" 32 | replies: "Răspunsuri" 33 | visits: "Vizite" 34 | -------------------------------------------------------------------------------- /config/locales/server.ru.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ru: 8 | site_settings: 9 | yearly_review_enabled: "Включить ежегодный обзор. Тогда 1 января будет автоматически создана тема с подведением итогов работы форума за предыдущий год." 10 | yearly_review_categories: "Публичные категории для подведения итогов. Для каждой из 5 лучших категорий в этом списке будет создана публикация со статистикой. Оставьте поле пустым, чтобы использовать 5 самых популярных публичных категорий." 11 | yearly_review_exclude_staff: "Исключить персонал из статистики участников." 12 | yearly_review_include_user_stats: "Добавить статистику, идентифицирующую участников." 13 | yearly_review_include_private_categories: "Включить в обзор действия участников из закрытых или ограниченных для чтения категорий." 14 | yearly_review_publish_category: "Категория, в которой будет опубликован обзор. Настоятельно рекомендуем указать категорию персонала или другую закрытую категорию, чтобы вы могли ознакомиться с темой до публикации в общем доступе." 15 | yearly_review_featured_badge: "Введите полное имя награды. Можно оставить пустым." 16 | yearly_review: 17 | topic_title: "%{year}: годовой обзор" 18 | category_topics_title: "Лучшие темы #%{category}" 19 | title: 20 | users_section: "Лучшие пользователи %{year}" 21 | topics_created: "По количеству тем" 22 | replies_created: "По количеству ответов" 23 | likes_given: "По количеству поставленных лайков" 24 | likes_received: "По количеству полученных лайков" 25 | visits: "По количеству посещений" 26 | daily_visits: "По количеству ежедневных посещений" 27 | time_read: "По времени чтения" 28 | most_replied_to: "По количеству ответов на тему" 29 | featured_badge: "Пользователи получили награду: %{badge_name}" 30 | action: 31 | topics_created: "Тем" 32 | most_replied_to: "Ответов" 33 | replies_created: "Ответов" 34 | likes_given: "Лайков" 35 | likes_received: "Лайков" 36 | visits: "Дней посещения" 37 | time_read: "Часов чтения" 38 | rank_type: 39 | title: 40 | most_liked: "Фаворит" 41 | most_replied_to: "Большинство ответов" 42 | most_popular: "Самые популярные темы" 43 | most_bookmarked: "Больше всего закладок" 44 | most_read: "Тексточитатель" 45 | action_types: 46 | most_popular: "Оценка" 47 | most_replied_to: "Ответов" 48 | most_liked: "Лайков" 49 | most_bookmarked: "Закладок" 50 | most_read: "Часов чтения" 51 | user: "Пользователь" 52 | users: "Пользователей" 53 | days_visited: "Дней посещения" 54 | all_yearly_visits: "Все посещения за год" 55 | topics: "Темы" 56 | topic: "Тема" 57 | likes: "Лайки" 58 | bookmarks: "Закладки" 59 | score: "Оценка" 60 | replies: "Ответов" 61 | visits: "Посещений" 62 | more_badge_users: "И ещё %{more}..." 63 | -------------------------------------------------------------------------------- /config/locales/server.sk.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sk: 8 | yearly_review: 9 | action: 10 | topics_created: "Témy" 11 | most_replied_to: "Odpovede" 12 | replies_created: "Odpovede" 13 | likes_given: "Rozdaných 'páči sa mi'" 14 | likes_received: "Rozdaných 'páči sa mi'" 15 | visits: "Dní na stránke" 16 | rank_type: 17 | title: 18 | most_liked: "Najviac sa páčia" 19 | action_types: 20 | most_popular: "Skóre" 21 | most_replied_to: "Odpovede" 22 | most_liked: "Rozdaných 'páči sa mi'" 23 | most_bookmarked: "Záložky" 24 | user: "Používateľ" 25 | users: "Používatelia" 26 | days_visited: "Dní na stránke" 27 | topics: "Témy" 28 | topic: "Témy" 29 | likes: "Rozdaných 'páči sa mi'" 30 | bookmarks: "Záložky" 31 | score: "Skóre" 32 | replies: "Odpovede" 33 | visits: "Návštev" 34 | more_badge_users: "A %{more} ďAlšie..." 35 | -------------------------------------------------------------------------------- /config/locales/server.sl.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sl: 8 | site_settings: 9 | yearly_review_featured_badge: "Vnesite polno ime značke. Lahko ostane prazno." 10 | yearly_review: 11 | topic_title: "%{year}: Letni pregled" 12 | category_topics_title: "Najboljše teme v #%{category}" 13 | title: 14 | users_section: "Najboljši uporabniki leta %{year}" 15 | topics_created: "Največ ustvarjenih tem" 16 | replies_created: "Največ odgovorov" 17 | likes_given: "Največ danih všečkov" 18 | likes_received: "Največ prejetih všečkov" 19 | visits: "Največ obiskov" 20 | daily_visits: "Dnevnih obiskov" 21 | time_read: "Najzvestejši bralci" 22 | most_replied_to: "Prejemniki največ odgovorov" 23 | featured_badge: "Uporabniki, ki so prejeli značko %{badge_name}" 24 | action: 25 | topics_created: "Teme" 26 | most_replied_to: "Odgovori" 27 | replies_created: "Odgovori" 28 | likes_given: "Všečki" 29 | likes_received: "Všečki" 30 | visits: "Dni prisotnosti" 31 | time_read: "Ur branja" 32 | rank_type: 33 | title: 34 | most_liked: "Najbolj všečkani" 35 | most_replied_to: "Največ odgovorov" 36 | most_popular: "Najbolj priljubljeni" 37 | most_bookmarked: "Največkrat zaznamovano" 38 | most_read: "Najbolj brano" 39 | action_types: 40 | most_popular: "Ocena" 41 | most_replied_to: "Odgovori" 42 | most_liked: "Všečki" 43 | most_bookmarked: "Zaznamki" 44 | most_read: "Ur branja" 45 | user: "Uporabnik" 46 | users: "Uporabniki" 47 | days_visited: "Dni prisotnosti" 48 | all_yearly_visits: "Vsi letni obiski" 49 | topics: "Teme" 50 | topic: "Tema" 51 | likes: "Všečki" 52 | bookmarks: "Zaznamki" 53 | score: "Ocena" 54 | replies: "Odgovori" 55 | visits: "Obiski" 56 | more_badge_users: "In še %{more}..." 57 | -------------------------------------------------------------------------------- /config/locales/server.sq.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sq: 8 | yearly_review: 9 | action: 10 | topics_created: "Topics" 11 | most_replied_to: "Përgjigjet" 12 | replies_created: "Përgjigjet" 13 | likes_given: "Pëlqimet" 14 | likes_received: "Pëlqimet" 15 | visits: "Ditë vizituar" 16 | rank_type: 17 | title: 18 | most_liked: "Më të pëlqyer" 19 | action_types: 20 | most_replied_to: "Përgjigjet" 21 | most_liked: "Pëlqimet" 22 | most_bookmarked: "Të preferuarat" 23 | user: "Përdorues" 24 | users: "Përdoruesit" 25 | days_visited: "Ditë vizituar" 26 | topics: "Topics" 27 | topic: "Temë" 28 | likes: "Pëlqimet" 29 | bookmarks: "Të preferuarat" 30 | replies: "Përgjigjet" 31 | visits: "Vizita" 32 | -------------------------------------------------------------------------------- /config/locales/server.sr.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sr: 8 | yearly_review: 9 | action: 10 | topics_created: "Teme" 11 | most_replied_to: "Odgovori" 12 | replies_created: "Odgovori" 13 | likes_given: "Lajkova" 14 | likes_received: "Lajkova" 15 | visits: "Dana posećeno" 16 | rank_type: 17 | title: 18 | most_liked: "Najviše sviđanja" 19 | action_types: 20 | most_replied_to: "Odgovori" 21 | most_liked: "Lajkova" 22 | most_bookmarked: "Markiraj" 23 | user: "Korisnik" 24 | users: "Korisnici" 25 | days_visited: "Dana posećeno" 26 | topics: "Teme" 27 | topic: "Tema" 28 | likes: "Lajkova" 29 | bookmarks: "Markiraj" 30 | replies: "Odgovori" 31 | visits: "Poseta" 32 | -------------------------------------------------------------------------------- /config/locales/server.sv.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sv: 8 | site_settings: 9 | yearly_review_featured_badge: "Ange utmärkelsens hela namn. Kan lämnas tomt." 10 | yearly_review: 11 | topic_title: "%{year}: En granskning av året" 12 | category_topics_title: "Toppämnen i #%{category}" 13 | title: 14 | users_section: "Toppanvändarna %{year}" 15 | topics_created: "Flest ämnen" 16 | replies_created: "Flest svar" 17 | likes_given: "Flest givna gillningar" 18 | likes_received: "Flest mottagna gillningar" 19 | visits: "Flest besök" 20 | daily_visits: "Dagliga besök" 21 | time_read: "Mest tid läst" 22 | most_replied_to: "Mest besvarade" 23 | featured_badge: "Användare beviljade utmärkelsen %{badge_name}" 24 | action: 25 | topics_created: "Ämnen" 26 | most_replied_to: "Svar" 27 | replies_created: "Svar" 28 | likes_given: "Gillningar" 29 | likes_received: "Gillningar" 30 | visits: "Besökta dagar" 31 | time_read: "Lästa timmar" 32 | rank_type: 33 | title: 34 | most_liked: "Mest gillade" 35 | most_replied_to: "Flest svar" 36 | most_popular: "Mest populära" 37 | most_bookmarked: "Mest bokmärkta" 38 | most_read: "Mest lästa" 39 | action_types: 40 | most_popular: "Poäng" 41 | most_replied_to: "Svar" 42 | most_liked: "Gillningar" 43 | most_bookmarked: "Bokmärken" 44 | most_read: "Lästa timmar" 45 | user: "Användare" 46 | users: "Användare" 47 | days_visited: "Besökta dagar" 48 | all_yearly_visits: "Årets alla besök" 49 | topics: "Ämnen" 50 | topic: "Ämne" 51 | likes: "Gillningar" 52 | bookmarks: "Bokmärken" 53 | score: "Poäng" 54 | replies: "Svar" 55 | visits: "Besök" 56 | more_badge_users: "Och ytterligare %{more}..." 57 | -------------------------------------------------------------------------------- /config/locales/server.sw.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | sw: 8 | yearly_review: 9 | action: 10 | topics_created: "Mada" 11 | most_replied_to: "Majibu" 12 | replies_created: "Majibu" 13 | likes_given: "Upendo Uliotolewa" 14 | likes_received: "Upendo Uliotolewa" 15 | visits: "Siku Iliyotembelewa" 16 | rank_type: 17 | title: 18 | most_liked: "Iliyopendwa Zaidi" 19 | action_types: 20 | most_replied_to: "Majibu" 21 | most_liked: "Upendo Uliotolewa" 22 | most_bookmarked: "Mialamisho" 23 | user: "Mtumiaji" 24 | users: "Watumiaji" 25 | days_visited: "Siku Iliyotembelewa" 26 | topics: "Mada" 27 | topic: "Mada" 28 | likes: "Upendo Uliotolewa" 29 | bookmarks: "Mialamisho" 30 | replies: "Majibu" 31 | visits: "Waliotembelea" 32 | -------------------------------------------------------------------------------- /config/locales/server.te.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | te: 8 | yearly_review: 9 | action: 10 | topics_created: "విషయాలు" 11 | most_replied_to: "జవాబులు" 12 | replies_created: "జవాబులు" 13 | likes_given: "ఇచ్చిన ఇష్టాలు " 14 | likes_received: "ఇచ్చిన ఇష్టాలు " 15 | visits: "దర్శించిన రోజులు" 16 | rank_type: 17 | title: 18 | most_liked: "ఎక్కువగా ఇష్టపడింది" 19 | action_types: 20 | most_popular: "స్కోర్" 21 | most_replied_to: "జవాబులు" 22 | most_liked: "ఇచ్చిన ఇష్టాలు " 23 | most_bookmarked: "పేజీకలు" 24 | user: "వాడుకరి" 25 | users: "సభ్యులు" 26 | days_visited: "దర్శించిన రోజులు" 27 | topics: "విషయాలు" 28 | topic: "విషయం" 29 | likes: "ఇచ్చిన ఇష్టాలు " 30 | bookmarks: "పేజీకలు" 31 | score: "స్కోర్" 32 | replies: "జవాబులు" 33 | visits: "సందర్శనాలు" 34 | -------------------------------------------------------------------------------- /config/locales/server.th.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | th: 8 | yearly_review: 9 | action: 10 | topics_created: "หัวข้อ" 11 | most_replied_to: "ตอบ" 12 | replies_created: "ตอบ" 13 | likes_given: "ถูกใจ" 14 | likes_received: "ถูกใจ" 15 | visits: "วันที่เข้าชม" 16 | rank_type: 17 | title: 18 | most_liked: "ถูกใจมากที่สุด" 19 | action_types: 20 | most_popular: "คะแนน" 21 | most_replied_to: "ตอบ" 22 | most_liked: "ถูกใจ" 23 | most_bookmarked: "บุ๊คมาร์ค" 24 | user: "ชื่อผู้ใช้" 25 | users: "ผู้ใช้" 26 | days_visited: "วันที่เข้าชม" 27 | topics: "หัวข้อ" 28 | topic: "กระทู้" 29 | likes: "ถูกใจ" 30 | bookmarks: "บุ๊คมาร์ค" 31 | score: "คะแนน" 32 | replies: "ตอบ" 33 | visits: "เยี่ยมชม" 34 | -------------------------------------------------------------------------------- /config/locales/server.tr_TR.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | tr_TR: 8 | site_settings: 9 | yearly_review_enabled: "Yıllık incelemeyi etkinleştirin. 1 Ocak'ta, bir önceki yılın forum aktivitesini özetleyen bir konu otomatik olarak oluşturulacaktır." 10 | yearly_review_categories: "Özetlenecek herkese açık kategoriler. Bu listedeki ilk 5 kategorinin her biri için istatistikleri içeren bir gönderi oluşturulacaktır. En iyi 5 herkese açık kategoriyi kullanmak için boş bırakın." 11 | yearly_review_exclude_staff: "Personeli üye istatistiklerinden hariç tutun." 12 | yearly_review_include_user_stats: "Üye tanımlayıcı istatistikler ekleyin." 13 | yearly_review_include_private_categories: "Özel veya okuma kısıtlamalı kategorilerdeki üye aktivitesini incelemeye dahil edin." 14 | yearly_review_publish_category: "İncelemenin yayınlanacağı kategori. Herkese açık hâle getirmeden önce konuyu görüntüleyebilmeniz için personel veya diğer özel kategoriyi belirtmeniz şiddetle tavsiye edilir." 15 | yearly_review_featured_badge: "Tam rozet adını girin. Boş bırakılabilir." 16 | yearly_review: 17 | topic_title: "%{year}: Yılın Değerlendirmesi" 18 | category_topics_title: "En İyi #%{category} Konuları" 19 | title: 20 | users_section: "%{year} Yılının En İyi Kullanıcıları" 21 | topics_created: "En Çok Konu" 22 | replies_created: "En Çok Yanıt" 23 | likes_given: "En Çok Beğeni Veren" 24 | likes_received: "En Çok Beğeni Alan" 25 | visits: "En Çok Ziyaret" 26 | daily_visits: "Günlük Ziyaretler" 27 | time_read: "En Çok Okuma Süresi" 28 | most_replied_to: "En Çok Yanıtlanan" 29 | featured_badge: "%{badge_name} Rozeti Verilen Kullanıcılar" 30 | action: 31 | topics_created: "Konular" 32 | most_replied_to: "Yanıtlar" 33 | replies_created: "Yanıtlar" 34 | likes_given: "Beğeniler" 35 | likes_received: "Beğeniler" 36 | visits: "Ziyaret Edilen Gün" 37 | time_read: "Okumakla Geçen Saat" 38 | rank_type: 39 | title: 40 | most_liked: "En Çok Beğenilen" 41 | most_replied_to: "En Çok Yanıt" 42 | most_popular: "En Popüler" 43 | most_bookmarked: "En Çok Yer İmi Eklenen" 44 | most_read: "En Çok Okunan" 45 | action_types: 46 | most_popular: "Puan" 47 | most_replied_to: "Yanıtlar" 48 | most_liked: "Beğeniler" 49 | most_bookmarked: "Yer imleri" 50 | most_read: "Okumakla Geçen Saat" 51 | user: "Kullanıcı" 52 | users: "Kullanıcı" 53 | days_visited: "Ziyaret Edilen Gün" 54 | all_yearly_visits: "Tüm Yıllık Ziyaretler" 55 | topics: "Konular" 56 | topic: "Konu" 57 | likes: "Beğeniler" 58 | bookmarks: "Yer imleri" 59 | score: "Puan" 60 | replies: "Yanıtlar" 61 | visits: "Ziyaretler" 62 | more_badge_users: "Ve %{more} tane daha..." 63 | -------------------------------------------------------------------------------- /config/locales/server.ug.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ug: 8 | yearly_review: 9 | action: 10 | topics_created: "تېما" 11 | most_replied_to: "جاۋاب" 12 | replies_created: "جاۋاب" 13 | likes_given: "ياقتۇرۇش" 14 | likes_received: "ياقتۇرۇش" 15 | visits: "زىيارەت كۈن سانى" 16 | rank_type: 17 | title: 18 | most_liked: "كۆپ ياقتۇرغان" 19 | action_types: 20 | most_popular: "نومۇر" 21 | most_replied_to: "جاۋاب" 22 | most_liked: "ياقتۇرۇش" 23 | most_bookmarked: "خەتكۈچ" 24 | user: "ئىشلەتكۈچى" 25 | users: "ئىشلەتكۈچى" 26 | days_visited: "زىيارەت كۈن سانى" 27 | topics: "تېما" 28 | topic: "تېما" 29 | likes: "ياقتۇرۇش" 30 | bookmarks: "خەتكۈچ" 31 | score: "نومۇر" 32 | replies: "جاۋاب" 33 | visits: "زىيارەت" 34 | more_badge_users: "ۋە باشقا %{more}..." 35 | -------------------------------------------------------------------------------- /config/locales/server.uk.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | uk: 8 | yearly_review: 9 | action: 10 | topics_created: "Теми" 11 | most_replied_to: "Відповіді" 12 | replies_created: "Відповіді" 13 | likes_given: "Вподобані" 14 | likes_received: "Вподобані" 15 | visits: "Днів відвідувань" 16 | rank_type: 17 | title: 18 | most_liked: "Найбільше подобається" 19 | action_types: 20 | most_popular: "Бал" 21 | most_replied_to: "Відповіді" 22 | most_liked: "Вподобані" 23 | most_bookmarked: "Закладки" 24 | user: "Користувач" 25 | users: "Користувачі" 26 | days_visited: "Днів відвідувань" 27 | topics: "Теми" 28 | topic: "Тема" 29 | likes: "Вподобані" 30 | bookmarks: "Закладки" 31 | score: "Бал" 32 | replies: "Відповіді" 33 | visits: "Відвідини" 34 | more_badge_users: "І бІльше %{more}..." 35 | -------------------------------------------------------------------------------- /config/locales/server.ur.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | ur: 8 | yearly_review: 9 | action: 10 | topics_created: "ٹاپکس" 11 | most_replied_to: "جوابات" 12 | replies_created: "جوابات" 13 | likes_given: "لائیکس دیے گئے" 14 | likes_received: "لائیکس دیے گئے" 15 | visits: "دورہ کیے گئے دن" 16 | rank_type: 17 | title: 18 | most_liked: "سب سے زیادہ لائک کیا گیا" 19 | action_types: 20 | most_popular: "اسکور" 21 | most_replied_to: "جوابات" 22 | most_liked: "لائیکس دیے گئے" 23 | most_bookmarked: "بُک مارکس" 24 | user: "صارف" 25 | users: "صارفین" 26 | days_visited: "دورہ کیے گئے دن" 27 | topics: "ٹاپکس" 28 | topic: "ٹاپک" 29 | likes: "لائیکس دیے گئے" 30 | bookmarks: "بُک مارکس" 31 | score: "اسکور" 32 | replies: "جوابات" 33 | visits: "زائرین کی تعداد" 34 | -------------------------------------------------------------------------------- /config/locales/server.vi.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | vi: 8 | yearly_review: 9 | action: 10 | topics_created: "Chủ đề" 11 | most_replied_to: "Trả lời" 12 | replies_created: "Trả lời" 13 | likes_given: "Lượt Thích" 14 | likes_received: "Lượt Thích" 15 | visits: "Số ngày đã thăm" 16 | rank_type: 17 | title: 18 | most_liked: "Thích nhiều nhất" 19 | action_types: 20 | most_popular: "Điểm số" 21 | most_replied_to: "Trả lời" 22 | most_liked: "Lượt Thích" 23 | most_bookmarked: "Các đánh dấu" 24 | user: "Thành viên" 25 | users: "Tài khoản" 26 | days_visited: "Số ngày đã thăm" 27 | topics: "Chủ đề" 28 | topic: "Chủ đề" 29 | likes: "Lượt Thích" 30 | bookmarks: "Các đánh dấu" 31 | score: "Điểm số" 32 | replies: "Trả lời" 33 | visits: "Lượt xem" 34 | more_badge_users: "Và %{more} nữa..." 35 | -------------------------------------------------------------------------------- /config/locales/server.zh_CN.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | zh_CN: 8 | site_settings: 9 | yearly_review_enabled: "启用年度回顾。1 月 1 日,将自动创建一个话题,总结去年的论坛活动。" 10 | yearly_review_categories: "要总结的公开类别。将为此列表中前 5 个类别中的每个类别创建一篇包含统计信息的帖子。留空则会使用前 5 个公开类别。" 11 | yearly_review_exclude_staff: "从成员统计中排除管理人员。" 12 | yearly_review_include_user_stats: "添加成员识别统计信息。" 13 | yearly_review_include_private_categories: "在回顾中包括不公开或阅读限制类别的成员活动。" 14 | yearly_review_publish_category: "回顾将被发布到的类别。强烈建议指定管理人员或其他不公开类别,以便您可以在公开之前查看话题。" 15 | yearly_review_featured_badge: "输入完整的徽章名称。可以留空。" 16 | yearly_review: 17 | topic_title: "%{year} 年度回顾" 18 | category_topics_title: "#%{category} 的最佳话题" 19 | title: 20 | users_section: "%{year} 的热门用户" 21 | topics_created: "最多话题" 22 | replies_created: "回复最多" 23 | likes_given: "给出最多赞" 24 | likes_received: "收到最多赞" 25 | visits: "最常访问" 26 | daily_visits: "每日访问" 27 | time_read: "最长阅读时间" 28 | most_replied_to: "收到回复最多" 29 | featured_badge: "被授予%{badge_name}徽章的用户" 30 | action: 31 | topics_created: "话题" 32 | most_replied_to: "回复" 33 | replies_created: "回复" 34 | likes_given: "赞" 35 | likes_received: "赞" 36 | visits: "访问天数" 37 | time_read: "阅读小时数" 38 | rank_type: 39 | title: 40 | most_liked: "最多赞" 41 | most_replied_to: "最多回复" 42 | most_popular: "最受欢迎" 43 | most_bookmarked: "添加为书签最多" 44 | most_read: "最多阅读" 45 | action_types: 46 | most_popular: "分数" 47 | most_replied_to: "回复" 48 | most_liked: "赞" 49 | most_bookmarked: "书签" 50 | most_read: "阅读小时数" 51 | user: "用户" 52 | users: "用户" 53 | days_visited: "访问天数" 54 | all_yearly_visits: "所有年度访问" 55 | topics: "话题" 56 | topic: "话题" 57 | likes: "赞" 58 | bookmarks: "书签" 59 | score: "分数" 60 | replies: "回复" 61 | visits: "访问次数" 62 | more_badge_users: "和其他 %{more} 个…" 63 | -------------------------------------------------------------------------------- /config/locales/server.zh_TW.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Never edit this file. 2 | # It will be overwritten when translations are pulled from Crowdin. 3 | # 4 | # To work with us on translations, join this project: 5 | # https://translate.discourse.org/ 6 | 7 | zh_TW: 8 | yearly_review: 9 | action: 10 | topics_created: "討論話題" 11 | most_replied_to: "回覆" 12 | replies_created: "回覆" 13 | likes_given: "新的讚" 14 | likes_received: "新的讚" 15 | visits: "到訪天數" 16 | rank_type: 17 | title: 18 | most_liked: "最多讚" 19 | action_types: 20 | most_popular: "分數" 21 | most_replied_to: "回覆" 22 | most_liked: "新的讚" 23 | most_bookmarked: "書籤" 24 | user: "使用者" 25 | users: "使用者數量" 26 | days_visited: "到訪天數" 27 | topics: "討論話題" 28 | topic: "話題" 29 | likes: "新的讚" 30 | bookmarks: "書籤" 31 | score: "分數" 32 | replies: "回覆" 33 | visits: "造訪" 34 | -------------------------------------------------------------------------------- /config/settings.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | yearly_review_enabled: 3 | default: false 4 | client: true 5 | yearly_review_categories: 6 | type: category_list 7 | default: "" 8 | yearly_review_exclude_staff: 9 | default: true 10 | yearly_review_include_user_stats: 11 | default: true 12 | yearly_review_include_private_categories: 13 | default: false 14 | yearly_review_publish_category: 15 | type: category 16 | default: "" 17 | yearly_review_featured_badge: 18 | default: "" 19 | -------------------------------------------------------------------------------- /db/migrate/20230208123631_backfill_yearly_review_custom_fields.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class BackfillYearlyReviewCustomFields < ActiveRecord::Migration[6.1] 4 | def change 5 | 2017.upto(2022) do |year| 6 | topic_title = I18n.t("yearly_review.topic_title", year: year) 7 | DB.exec( 8 | <<~SQL, 9 | INSERT INTO post_custom_fields (post_id, name, value, created_at, updated_at) 10 | SELECT posts.id, :name, :value, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP 11 | FROM posts 12 | INNER JOIN topics ON topics.id = posts.topic_id 13 | WHERE posts.user_id = :user_id AND topics.title = :title 14 | ON CONFLICT DO NOTHING 15 | SQL 16 | title: topic_title, 17 | user_id: Discourse::SYSTEM_USER_ID, 18 | name: YearlyReview::POST_CUSTOM_FIELD, 19 | value: year.to_s, 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import DiscourseRecommended from "@discourse/lint-configs/eslint"; 2 | 3 | export default [...DiscourseRecommended]; 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "@discourse/lint-configs": "2.20.0", 5 | "ember-template-lint": "7.7.0", 6 | "eslint": "9.27.0", 7 | "prettier": "3.5.3", 8 | "stylelint": "16.19.1" 9 | }, 10 | "engines": { 11 | "node": ">= 22", 12 | "npm": "please-use-pnpm", 13 | "yarn": "please-use-pnpm", 14 | "pnpm": "9.x" 15 | }, 16 | "packageManager": "pnpm@9.15.5" 17 | } 18 | -------------------------------------------------------------------------------- /plugin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # name: discourse-yearly-review 4 | # about: Creates a topic that summarizes the previous year’s forum activity. 5 | # meta_topic_id: 105713 6 | # version: 0.2 7 | # author: Simon Cossar 8 | # url: https://github.com/discourse/discourse-yearly-review 9 | 10 | enabled_site_setting :yearly_review_enabled 11 | register_asset "stylesheets/yearly_review.scss" 12 | 13 | after_initialize do 14 | module ::YearlyReview 15 | PLUGIN_NAME = "yearly-review" 16 | POST_CUSTOM_FIELD = "yearly_review" 17 | 18 | def self.current_year 19 | Time.now.year 20 | end 21 | 22 | def self.last_year 23 | current_year - 1 24 | end 25 | end 26 | 27 | ::ActionController::Base.prepend_view_path File.expand_path( 28 | "../app/views/yearly-review", 29 | __FILE__, 30 | ) 31 | 32 | require_relative "app/jobs/yearly_review" 33 | 34 | require_dependency "email/styles" 35 | Email::Styles.register_plugin_style do |doc| 36 | doc.css("[data-review-topic-users] table").each { |element| element["width"] = "100%" } 37 | doc.css("[data-review-featured-topics] table").each { |element| element["width"] = "100%" } 38 | 39 | doc 40 | .css("[data-review-topic-users] th") 41 | .each do |element| 42 | element["style"] = "text-align: left;padding-bottom: 12px;" 43 | element["width"] = "50%" 44 | end 45 | doc 46 | .css("[data-review-featured-topics] th") 47 | .each { |element| element["style"] = "text-align: left;padding-bottom: 12px;" } 48 | doc 49 | .css("[data-review-featured-topics] th:first-child") 50 | .each { |element| element["width"] = "15%" } 51 | doc 52 | .css("[data-review-featured-topics] th:nth-child(2)") 53 | .each { |element| element["width"] = "60%" } 54 | doc 55 | .css("[data-review-featured-topics] th:last-child") 56 | .each { |element| element["width"] = "25%" } 57 | 58 | doc 59 | .css("[data-review-topic-users] td") 60 | .each do |element| 61 | element["style"] = "padding-bottom: 6px;" 62 | element["valign"] = "top" 63 | end 64 | doc 65 | .css("[data-review-featured-topics] td") 66 | .each do |element| 67 | element["style"] = "padding-bottom: 6px;" 68 | element["valign"] = "top" 69 | end 70 | 71 | doc 72 | .css("[data-review-topic-users] td table td:first-child") 73 | .each do |element| 74 | element["style"] = "padding-bottom: 6px;" 75 | element["width"] = "25" 76 | end 77 | doc 78 | .css("[data-review-topic-users] td table td:nth-child(2)") 79 | .each { |element| element["style"] = "padding-left: 4px;padding-bottom: 6px;" } 80 | end 81 | 82 | on(:username_changed) do |old_username, new_username| 83 | Post 84 | .joins(:_custom_fields) 85 | .where( 86 | "post_custom_fields.name = ? AND posts.raw LIKE ?", 87 | YearlyReview::POST_CUSTOM_FIELD, 88 | "%/#{old_username}/%", 89 | ) 90 | .update_all( 91 | "raw = REPLACE(raw, '/#{old_username}/', '/#{new_username}/'), baked_version = NULL", 92 | ) 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /spec/jobs/yearly_review_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe Jobs::YearlyReview do 6 | class Helper 7 | extend YearlyReviewHelper 8 | end 9 | 10 | before { SiteSetting.yearly_review_enabled = true } 11 | 12 | let(:category) { Fabricate(:category) } 13 | let(:top_review_user) { Fabricate(:user, username: "top_review_user") } 14 | let(:reviewed_user) { Fabricate(:user, username: "reviewed_user") } 15 | 16 | describe "publishing the topic" do 17 | describe "on January 1st" do 18 | before do 19 | SiteSetting.yearly_review_publish_category = category.id 20 | freeze_time DateTime.parse("#{::YearlyReview.current_year}-01-01") 21 | Fabricate(:topic, created_at: 1.month.ago) 22 | end 23 | 24 | it "publishes a review topic" do 25 | Jobs::YearlyReview.new.execute({}) 26 | topic = Topic.last 27 | expect(topic.title).to eq( 28 | I18n.t("yearly_review.topic_title", year: ::YearlyReview.last_year), 29 | ) 30 | expect(topic.first_post.custom_fields).to eq( 31 | YearlyReview::POST_CUSTOM_FIELD => ::YearlyReview.last_year.to_s, 32 | ) 33 | expect(topic.custom_fields).to eq( 34 | YearlyReview::POST_CUSTOM_FIELD => ::YearlyReview.last_year.to_s, 35 | ) 36 | end 37 | end 38 | 39 | describe "on February 1st" do 40 | before do 41 | freeze_time DateTime.parse("#{::YearlyReview.current_year}-02-01") 42 | Fabricate( 43 | :topic, 44 | created_at: 2.months.ago, 45 | title: "A topic from #{::YearlyReview.last_year}", 46 | ) 47 | end 48 | 49 | it "doesn't publish a review topic" do 50 | expect { Jobs::YearlyReview.new.execute({}) }.not_to change { Topic.count } 51 | end 52 | end 53 | 54 | describe "after the review has been published" do 55 | before do 56 | SiteSetting.yearly_review_publish_category = category.id 57 | freeze_time DateTime.parse("#{::YearlyReview.current_year}-01-01") 58 | Fabricate(:topic, created_at: 1.month.ago) 59 | Jobs::YearlyReview.new.execute({}) 60 | @yearly_review_topic = Topic.last 61 | end 62 | 63 | it "doesn't publish the review topic again if it already exists with the custom field and a different title" do 64 | @yearly_review_topic.update!(title: "This is a test for yearly review") 65 | expect { Jobs::YearlyReview.new.execute({}) }.not_to change { Topic.count } 66 | end 67 | 68 | it "doesn't publish the review topic again if it already exists and a previous version was deleted" do 69 | @yearly_review_topic.trash! 70 | expect { Jobs::YearlyReview.new.execute({}) }.to change { Topic.count } 71 | expect { Jobs::YearlyReview.new.execute({}) }.not_to change { Topic.count } 72 | end 73 | 74 | it "doesn't publish the review topic again if it already exists and has been moved to another category" do 75 | @yearly_review_topic.update!(category: Fabricate(:category)) 76 | expect { Jobs::YearlyReview.new.execute({}) }.not_to change { Topic.count } 77 | end 78 | end 79 | end 80 | 81 | describe "user stats" do 82 | before do 83 | freeze_time DateTime.parse("#{::YearlyReview.current_year}-01-01") 84 | SiteSetting.yearly_review_publish_category = category.id 85 | end 86 | 87 | describe "most topics" do 88 | before do 89 | 5.times { Fabricate(:topic, user: top_review_user, created_at: 1.month.ago) } 90 | Fabricate(:topic, user: reviewed_user, created_at: 1.month.ago) 91 | end 92 | 93 | it "ranks topics created by users correctly" do 94 | Jobs::YearlyReview.new.execute({}) 95 | raw = Topic.last.first_post.raw 96 | expect(raw).to have_tag("div.topics-created") { with_text(/\@top_review_user\|5/) } 97 | expect(raw).to have_tag("div.topics-created") { with_text(/\@reviewed_user\|1/) } 98 | end 99 | 100 | it "updates username correctly after anonymizing the user" do 101 | Jobs.run_immediately! 102 | UserActionManager.enable 103 | stub_image_size 104 | 105 | upload = Fabricate(:upload, user: top_review_user) 106 | top_review_user.user_avatar = 107 | UserAvatar.new(user_id: top_review_user.id, custom_upload_id: upload.id) 108 | top_review_user.uploaded_avatar_id = upload.id 109 | top_review_user.save! 110 | 111 | Jobs::YearlyReview.new.execute({}) 112 | post = Topic.last.first_post 113 | raw = post.raw 114 | expect(raw).to have_tag("div.topics-created") { with_text(/\@top_review_user\|5/) } 115 | expect(raw).to have_tag("div.topics-created") { with_text(%r{/top_review_user/50/}) } 116 | 117 | user = UserAnonymizer.new(top_review_user, Discourse.system_user, {}).make_anonymous 118 | raw = post.reload.raw 119 | expect(raw).to have_tag("div.topics-created") { with_text(/\@#{user.username}\|5/) } 120 | expect(raw).to have_tag("div.topics-created") { with_text(%r{/#{user.username}/50/}) } 121 | expect(post.baked_version).to be_nil 122 | end 123 | end 124 | 125 | describe "most replies" do 126 | before do 127 | SiteSetting.max_consecutive_replies = 5 128 | SiteSetting.yearly_review_publish_category = category.id 129 | topic_user = Fabricate(:user) 130 | reviewed_topic = Fabricate(:topic, user: topic_user, created_at: 1.year.ago) 131 | Fabricate(:post, topic: reviewed_topic, user: topic_user) 132 | 5.times do 133 | Fabricate(:post, topic: reviewed_topic, user: top_review_user, created_at: 1.month.ago) 134 | end 135 | Fabricate(:post, topic: reviewed_topic, user: reviewed_user, created_at: 1.month.ago) 136 | end 137 | 138 | it "ranks replies created by users correctly" do 139 | Jobs::YearlyReview.new.execute({}) 140 | raw = Topic.last.first_post.raw 141 | expect(raw).to have_tag("div.replies-created") { with_text(/\@top_review_user\|5/) } 142 | expect(raw).to have_tag("div.replies-created") { with_text(/\@reviewed_user\|1/) } 143 | end 144 | end 145 | 146 | describe "most bookmarks" do 147 | let(:topic_user) { Fabricate(:user) } 148 | let(:reviewed_topic) { Fabricate(:topic, user: topic_user, created_at: 1.year.ago) } 149 | 150 | before do 151 | SiteSetting.yearly_review_publish_category = category.id 152 | 10.times do 153 | Fabricate(:post, topic: reviewed_topic, created_at: 1.month.ago, user: top_review_user) 154 | end 155 | reviewed_topic.reload 156 | Fabricate( 157 | :bookmark, 158 | bookmarkable: reviewed_topic.posts[1], 159 | user: topic_user, 160 | created_at: 1.month.ago, 161 | ) 162 | Fabricate( 163 | :bookmark, 164 | bookmarkable: reviewed_topic.posts[2], 165 | user: topic_user, 166 | created_at: 1.month.ago, 167 | ) 168 | Fabricate( 169 | :bookmark, 170 | bookmarkable: reviewed_topic.posts[3], 171 | user: topic_user, 172 | created_at: 1.month.ago, 173 | ) 174 | Fabricate( 175 | :bookmark, 176 | bookmarkable: reviewed_topic.posts[4], 177 | user: topic_user, 178 | created_at: 1.month.ago, 179 | ) 180 | Fabricate( 181 | :bookmark, 182 | bookmarkable: reviewed_topic.posts[5], 183 | user: topic_user, 184 | created_at: 1.month.ago, 185 | ) 186 | end 187 | 188 | it "ranks bookmarks created by users correctly" do 189 | Jobs::YearlyReview.new.execute({}) 190 | topic = Topic.last 191 | raw = Post.where(topic_id: topic.id).second.raw 192 | expect(raw).to include( 193 | Helper.table_header("user", "topic", "rank_type.action_types.most_bookmarked"), 194 | ) 195 | expect(raw).to include( 196 | Helper.table_row( 197 | Helper.avatar_image( 198 | reviewed_topic.user.username, 199 | reviewed_topic.user.uploaded_avatar_id, 200 | ), 201 | Helper.topic_link(reviewed_topic.title, reviewed_topic.slug, reviewed_topic.id), 202 | 5, 203 | ), 204 | ) 205 | end 206 | end 207 | 208 | describe "likes given and received" do 209 | SiteSetting.max_consecutive_replies = 20 210 | let(:reviewed_topic) { Fabricate(:topic, created_at: 1.year.ago) } 211 | before do 212 | 11.times do 213 | post = 214 | Fabricate(:post, topic: reviewed_topic, user: reviewed_user, created_at: 1.month.ago) 215 | UserAction.create!( 216 | action_type: PostActionType.types[:like], 217 | user_id: reviewed_user.id, 218 | acting_user_id: top_review_user.id, 219 | target_post_id: post.id, 220 | target_topic_id: reviewed_topic.id, 221 | created_at: 1.month.ago, 222 | ) 223 | end 224 | 10.times do 225 | post = 226 | Fabricate(:post, topic: reviewed_topic, user: top_review_user, created_at: 1.month.ago) 227 | UserAction.create!( 228 | action_type: PostActionType.types[:like], 229 | user_id: top_review_user.id, 230 | acting_user_id: reviewed_user.id, 231 | target_post_id: post.id, 232 | target_topic_id: reviewed_topic.id, 233 | created_at: 1.month.ago, 234 | ) 235 | end 236 | end 237 | 238 | it "should rank likes given and received correctly" do 239 | Jobs::YearlyReview.new.execute({}) 240 | raw = Topic.last.first_post.raw 241 | expect(raw).to have_tag("div.likes-given") { with_text(/\@top_review_user\|11/) } 242 | expect(raw).to have_tag("div.likes-given") { with_text(/\@reviewed_user\|10/) } 243 | 244 | expect(raw).to have_tag("div.likes-received") { with_text(/\@reviewed_user\|11/) } 245 | expect(raw).to have_tag("div.likes-received") { with_text(/\@top_review_user\|10/) } 246 | end 247 | end 248 | end 249 | 250 | describe "featured badge" do 251 | let(:admin) { Fabricate(:user, admin: true) } 252 | let(:badge) { Fabricate(:badge) } 253 | 254 | before do 255 | SiteSetting.yearly_review_featured_badge = badge.name 256 | SiteSetting.yearly_review_publish_category = category.id 257 | freeze_time DateTime.parse("#{::YearlyReview.current_year}-01-01") 258 | 16.times do 259 | user = Fabricate(:user) 260 | UserBadge.create!( 261 | badge_id: badge.id, 262 | user_id: user.id, 263 | granted_at: 1.month.ago, 264 | granted_by_id: admin.id, 265 | ) 266 | end 267 | end 268 | 269 | it "it should only display the first 100 users" do 270 | Jobs::YearlyReview.new.execute({}) 271 | raw = Topic.last.first_post.raw 272 | expect(raw).to include("[And 1 more...](") 273 | end 274 | end 275 | end 276 | -------------------------------------------------------------------------------- /spec/system/core_features_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe "Core features", type: :system do 4 | before { enable_current_plugin } 5 | 6 | it_behaves_like "having working core features" 7 | end 8 | -------------------------------------------------------------------------------- /stylelint.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | extends: ["@discourse/lint-configs/stylelint"], 3 | }; 4 | -------------------------------------------------------------------------------- /translator.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for discourse-translator-bot 2 | 3 | files: 4 | - source_path: config/locales/server.en.yml 5 | destination_path: server.yml 6 | --------------------------------------------------------------------------------