├── .discourse-compatibility
├── .github
└── workflows
│ └── discourse-plugin.yml
├── .gitignore
├── .npmrc
├── .prettierrc.cjs
├── .rubocop.yml
├── .streerc
├── .template-lintrc.cjs
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── about.json
├── app
├── controllers
│ └── discourse_solved
│ │ └── answer_controller.rb
├── models
│ └── discourse_solved
│ │ └── solved_topic.rb
└── serializers
│ └── concerns
│ └── discourse_solved
│ └── topic_answer_mixin.rb
├── assets
├── javascripts
│ └── discourse
│ │ ├── components
│ │ ├── solved-accept-answer-button.gjs
│ │ ├── solved-accepted-answer.gjs
│ │ └── solved-unaccept-answer-button.gjs
│ │ ├── connectors
│ │ ├── after-topic-status
│ │ │ └── solved-status.gjs
│ │ ├── bread-crumbs-right
│ │ │ └── solved-status-filter.gjs
│ │ ├── category-custom-settings
│ │ │ └── solved-settings.gjs
│ │ ├── topic-navigation
│ │ │ └── no-answer.gjs
│ │ ├── user-activity-bottom
│ │ │ └── solved-list.gjs
│ │ ├── user-card-metadata
│ │ │ └── accepted-answers.gjs
│ │ └── user-summary-stat
│ │ │ └── solved-count.gjs
│ │ ├── initializers
│ │ ├── add-topic-list-class.js
│ │ └── extend-for-solved-button.gjs
│ │ ├── pre-initializers
│ │ └── extend-category-for-solved.js
│ │ ├── routes
│ │ └── user-activity-solved.js
│ │ └── solved-route-map.js
└── stylesheets
│ ├── mobile
│ └── solutions.scss
│ └── solutions.scss
├── config
├── locales
│ ├── client.ar.yml
│ ├── client.be.yml
│ ├── client.bg.yml
│ ├── client.bs_BA.yml
│ ├── client.ca.yml
│ ├── client.cs.yml
│ ├── client.da.yml
│ ├── client.de.yml
│ ├── client.el.yml
│ ├── client.en.yml
│ ├── client.en_GB.yml
│ ├── client.es.yml
│ ├── client.et.yml
│ ├── client.fa_IR.yml
│ ├── client.fi.yml
│ ├── client.fr.yml
│ ├── client.gl.yml
│ ├── client.he.yml
│ ├── client.hr.yml
│ ├── client.hu.yml
│ ├── client.hy.yml
│ ├── client.id.yml
│ ├── client.it.yml
│ ├── client.ja.yml
│ ├── client.ko.yml
│ ├── client.lt.yml
│ ├── client.lv.yml
│ ├── client.nb_NO.yml
│ ├── client.nl.yml
│ ├── client.pl_PL.yml
│ ├── client.pt.yml
│ ├── client.pt_BR.yml
│ ├── client.ro.yml
│ ├── client.ru.yml
│ ├── client.sk.yml
│ ├── client.sl.yml
│ ├── client.sq.yml
│ ├── client.sr.yml
│ ├── client.sv.yml
│ ├── client.sw.yml
│ ├── client.te.yml
│ ├── client.th.yml
│ ├── client.tr_TR.yml
│ ├── client.ug.yml
│ ├── client.uk.yml
│ ├── client.ur.yml
│ ├── client.vi.yml
│ ├── client.zh_CN.yml
│ ├── client.zh_TW.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
├── routes.rb
└── settings.yml
├── db
├── fixtures
│ └── 001_badges.rb
└── migrate
│ ├── 20191209095548_ensures_unique_accepted_answer_post_id.rb
│ ├── 20210218022053_solved_fix_high_auto_close_topic_hours.rb
│ ├── 20210429154322_remove_nil_custom_fields_from_solved.rb
│ ├── 20210618142654_recreate_solutions_column.rb
│ ├── 20221121223417_rename_badges.rb
│ ├── 20240116100023_fill_accept_all_solutions_allowed_groups_based_on_deprecated_setting.rb
│ ├── 20250318024824_create_discourse_solved_solved_topics.rb
│ ├── 20250318024953_copy_solved_topic_custom_field_to_discourse_solved_solved_topics.rb
│ ├── 20250318024954_remove_duplicates_from_discourse_solved_solved_topics.rb
│ ├── 20250318025147_add_index_for_discourse_solved_topics.rb
│ └── 20250325074111_copy_remaining_solved_topic_custom_field_to_discourse_solved_solved_topics.rb
├── eslint.config.mjs
├── lib
├── discourse_assign
│ └── entry_point.rb
├── discourse_automation
│ └── entry_point.rb
├── discourse_dev
│ └── discourse_solved.rb
└── discourse_solved
│ ├── accepted_answer_cache.rb
│ ├── before_head_close.rb
│ ├── category_extension.rb
│ ├── engine.rb
│ ├── first_accepted_post_solution_validator.rb
│ ├── guardian_extensions.rb
│ ├── post_serializer_extension.rb
│ ├── register_filters.rb
│ ├── topic_extension.rb
│ ├── topic_posters_summary_extension.rb
│ ├── topic_view_serializer_extension.rb
│ ├── user_summary_extension.rb
│ └── web_hook_extension.rb
├── package.json
├── plugin.rb
├── pnpm-lock.yaml
├── spec
├── components
│ ├── composer_messages_finder_spec.rb
│ └── post_revisor_spec.rb
├── fabricators
│ ├── extend_topic_fabricator.rb
│ ├── solved_hook_fabricator.rb
│ └── solved_topic_fabricator.rb
├── integration
│ └── solved_spec.rb
├── lib
│ ├── first_accepted_post_solution_validator_spec.rb
│ └── guardian_extensions_spec.rb
├── models
│ ├── copy_solved_topic_custom_field_spec.rb
│ ├── directory_item_spec.rb
│ ├── remove_duplicates_from_discourse_solved_solved_topics_spec.rb
│ ├── site_spec.rb
│ └── user_summary_spec.rb
├── requests
│ ├── answer_controller_spec.rb
│ ├── list_controller_spec.rb
│ └── topics_controller_spec.rb
├── serializers
│ ├── topic_answer_mixin_spec.rb
│ ├── topic_view_serializer_spec.rb
│ └── user_card_serializer_spec.rb
└── system
│ ├── core_features_spec.rb
│ └── solved_spec.rb
├── stylelint.config.mjs
├── test
└── javascripts
│ ├── acceptance
│ ├── discourse-solved-post-menu-test.js
│ ├── discourse-solved-test.js
│ └── user-activity-solved-test.js
│ └── helpers
│ └── discourse-solved-helpers.js
└── translator.yml
/.discourse-compatibility:
--------------------------------------------------------------------------------
1 | < 3.5.0.beta5-dev: a8c534f11832d6bb8590ce5001119654fe6f335f
2 | < 3.5.0.beta3-dev: 4f0234f5be3aaa77db277e0f224cd9750d2713cd
3 | < 3.5.0.beta2-dev: e82c6ae1ca38ccebb34669148f8de93a3028906e
4 | < 3.5.0.beta1-dev: 5450a5ef4e2ae35185320fc6af9678621026e148
5 | < 3.4.0.beta4-dev: 3f724bf3114cc7877fa757bc8035f13a7390c739
6 | < 3.4.0.beta2-dev: 1bbdfd8f5681171dc3f0e9ea93cd56997dc7938a
7 | < 3.4.0.beta1-dev: dc1ef92be23332a54854751a23b9029463584845
8 | < 3.3.0.beta2-dev: a18ce6d712fafed286bcc99543dd173110c6dfb8
9 | < 3.3.0.beta1-dev: 526a44644a7b3f0c2a3ba4fc16e72f364e9fce6d
10 | < 3.2.0.beta2-dev: 9fbf43e2f077e86f0a1ff769af6036d4e78bfff1
11 | 3.1.999: b5d487d6a5bfe2571d936eec5911d02a5f3fcc32
12 | 3.1.0.beta1: 62fe282c756ac7de43a22a09b0d675882a507743
13 | 2.9.0.beta11: 882dd61e11f9bab8e99510296938b0cdbc3269c4
14 | 2.9.0.beta1: e6cce5486df906ede74aa1b17ab308a145a99b88
15 | 2.8.0.beta9: 4cf4b41c4e2c85b07da5d29353371a7593cac735
16 | 2.8.0.beta3: 13168027eb6f4cb2f9532fe9820b3f1284f37fcf
17 | 2.7.7: 13168027eb6f4cb2f9532fe9820b3f1284f37fcf
18 | 2.7.0.beta2: 30248619e28d5f173c4dfd149d3d8ba39e3b8796
19 | 2.6.0.beta0: 613267c3a2993bb8c3af7d61b539ba4a070a6d48
20 |
--------------------------------------------------------------------------------
/.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 | yarn-error.log
3 | .rubocop-https---raw-githubusercontent-com-discourse-*
4 |
--------------------------------------------------------------------------------
/.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.3.0)
19 | benchmark (0.4.1)
20 | bigdecimal (3.2.0)
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.8)
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 Solved
2 |
3 | Provides a solved button on designated categories
4 |
5 | ## How to Install this Plugin
6 |
7 | To install Discourse Plugin - https://meta.discourse.org/t/install-a-plugin/19157
8 |
9 | ## How to Check if Plugin is installed
10 |
11 | Go to Admin > Plugins
12 |
13 | You should now see:
14 |
15 | 
16 |
17 | ## What to expect if Plugin is installed
18 |
19 | Inside the Plugins, you will have the following options:
20 |
21 | 
22 |
23 | ## How to enable it in your posts
24 |
25 | New Categories - Check :white_check_mark: Allow topic owner and staff to mark a reply as the solution
26 |
27 | 
28 |
29 | Old Categories - Go to that Category > Edit > Settings > Check :white_check_mark: Allow topic owner and staff to mark a reply as the solution
30 |
31 | ## BONUS: How to add badges to those who answered correctly
32 |
33 | https://meta.discourse.org/t/discourse-solved-accepted-answer-plugin/30155
34 |
35 | ## License
36 |
37 | MIT
38 |
--------------------------------------------------------------------------------
/about.json:
--------------------------------------------------------------------------------
1 | {
2 | "tests": {
3 | "requiredPlugins": [
4 | "https://github.com/discourse/discourse-assign"
5 | ]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/controllers/discourse_solved/answer_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class DiscourseSolved::AnswerController < ::ApplicationController
4 | requires_plugin DiscourseSolved::PLUGIN_NAME
5 |
6 | def accept
7 | limit_accepts
8 |
9 | post = Post.find(params[:id].to_i)
10 |
11 | topic = post.topic
12 | topic ||= Topic.with_deleted.find(post.topic_id) if guardian.is_staff?
13 |
14 | guardian.ensure_can_accept_answer!(topic, post)
15 |
16 | DiscourseSolved.accept_answer!(post, current_user, topic: topic)
17 |
18 | render json: success_json
19 | end
20 |
21 | def unaccept
22 | limit_accepts
23 |
24 | post = Post.find(params[:id].to_i)
25 |
26 | topic = post.topic
27 | topic ||= Topic.with_deleted.find(post.topic_id) if guardian.is_staff?
28 |
29 | guardian.ensure_can_accept_answer!(topic, post)
30 |
31 | DiscourseSolved.unaccept_answer!(post, topic: topic)
32 |
33 | render json: success_json
34 | end
35 |
36 | def limit_accepts
37 | return if current_user.staff?
38 | run_rate_limiter =
39 | DiscoursePluginRegistry.apply_modifier(
40 | :solved_answers_controller_run_rate_limiter,
41 | true,
42 | current_user,
43 | )
44 | return if !run_rate_limiter
45 | RateLimiter.new(nil, "accept-hr-#{current_user.id}", 20, 1.hour).performed!
46 | RateLimiter.new(nil, "accept-min-#{current_user.id}", 4, 30.seconds).performed!
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/app/models/discourse_solved/solved_topic.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module DiscourseSolved
4 | class SolvedTopic < ActiveRecord::Base
5 | self.table_name = "discourse_solved_solved_topics"
6 |
7 | belongs_to :topic, class_name: "Topic"
8 | belongs_to :answer_post, class_name: "Post", foreign_key: "answer_post_id"
9 | belongs_to :accepter, class_name: "User", foreign_key: "accepter_user_id"
10 | belongs_to :topic_timer, dependent: :destroy
11 |
12 | validates :topic_id, presence: true
13 | validates :answer_post_id, presence: true
14 | validates :accepter_user_id, presence: true
15 | end
16 | end
17 |
18 | # == Schema Information
19 | #
20 | # Table name: discourse_solved_solved_topics
21 | #
22 | # id :bigint not null, primary key
23 | # topic_id :integer not null
24 | # answer_post_id :integer not null
25 | # accepter_user_id :integer not null
26 | # topic_timer_id :integer
27 | # created_at :datetime not null
28 | # updated_at :datetime not null
29 | #
30 | # Indexes
31 | #
32 | # index_discourse_solved_solved_topics_on_answer_post_id (answer_post_id) UNIQUE
33 | # index_discourse_solved_solved_topics_on_topic_id (topic_id) UNIQUE
34 | #
35 |
--------------------------------------------------------------------------------
/app/serializers/concerns/discourse_solved/topic_answer_mixin.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module DiscourseSolved
4 | module TopicAnswerMixin
5 | def self.included(klass)
6 | klass.attributes :has_accepted_answer, :can_have_answer
7 | end
8 |
9 | def has_accepted_answer
10 | object&.solved.present?
11 | end
12 |
13 | def include_has_accepted_answer?
14 | SiteSetting.solved_enabled
15 | end
16 |
17 | def can_have_answer
18 | return true if SiteSetting.allow_solved_on_all_topics
19 | return false if object.closed || object.archived
20 | scope.allow_accepted_answers?(object.category_id, object.tags.map(&:name))
21 | end
22 |
23 | def include_can_have_answer?
24 | SiteSetting.solved_enabled && SiteSetting.empty_box_on_unsolved
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/components/solved-accept-answer-button.gjs:
--------------------------------------------------------------------------------
1 | import Component from "@glimmer/component";
2 | import { action } from "@ember/object";
3 | import { service } from "@ember/service";
4 | import DButton from "discourse/components/d-button";
5 | import { ajax } from "discourse/lib/ajax";
6 | import { popupAjaxError } from "discourse/lib/ajax-error";
7 |
8 | export default class SolvedAcceptAnswerButton extends Component {
9 | static hidden(args) {
10 | return args.post.topic_accepted_answer;
11 | }
12 |
13 | @service appEvents;
14 | @service currentUser;
15 |
16 | get showLabel() {
17 | return this.currentUser?.id === this.args.post.topicCreatedById;
18 | }
19 |
20 | @action
21 | acceptAnswer() {
22 | const post = this.args.post;
23 |
24 | acceptPost(post, this.currentUser);
25 |
26 | this.appEvents.trigger("discourse-solved:solution-toggled", post);
27 |
28 | post.get("topic.postStream.posts").forEach((p) => {
29 | p.set("topic_accepted_answer", true);
30 | this.appEvents.trigger("post-stream:refresh", { id: p.id });
31 | });
32 | }
33 |
34 |
35 | {{i18n "solved.no_answer.description"}}{{i18n "solved.title"}}
20 |
21 | {{#unless this.siteSettings.allow_solved_on_all_topics}}
22 | {{i18n "solved.no_answer.title"}}
59 |
%{username} %{description}
" 38 | topic_status_filter: 39 | all: "الكل" 40 | solved: "محلول" 41 | unsolved: "غير محلول" 42 | no_solved_topics_title: "لم تحل أي موضوعات بعد" 43 | no_solved_topics_title_others: "لم يحل %{username} أي موضوعات حتى الآن" 44 | no_solved_topics_body: "عندما تقدِّم ردًا مفيدًا على أحد الموضوعات، فقد يحدِّد مالك الموضوع أو فريق العمل ردك على أنه الحل." 45 | no_answer: 46 | title: هل تمت الإجابة على سؤالك؟ 47 | description: "ميِّز الإجابة وساعد الآخرين عن طريق استخدام زر الحل أسفل الرد الصحيح." 48 | notification: 49 | title: "تم وضع علامة \"حل\" على منشورك" 50 | topic_statuses: 51 | solved: 52 | help: "هناك حل لهذا الموضوع" 53 | search: 54 | advanced: 55 | statuses: 56 | solved: "محلولة" 57 | unsolved: "غير محلولة" 58 | admin: 59 | web_hooks: 60 | solved_event: 61 | group_name: "حدث تم حله" 62 | accepted_solution: "عندما يضع مستخدم علامة على أحد المنشورات على أنه الإجابة المقبولة" 63 | unaccepted_solution: "عندما يضع مستخدم علامة على أحد المنشورات على أنه الإجابة غير المقبولة" 64 | api: 65 | scopes: 66 | descriptions: 67 | solved: 68 | answer: قبول/عدم قبول حل. 69 | discourse_automation: 70 | triggerables: 71 | first_accepted_solution: 72 | max_trust_level: 73 | tl1: < مستوى الثقة 1 74 | tl2: < مستوى الثقة 2 75 | tl3: < مستوى الثقة 3 76 | tl4: < مستوى الثقة 4 77 | any: أي 78 | fields: 79 | maximum_trust_level: 80 | label: مستوى الثقة 81 | description: سيقوم المستخدمون تحت مستوى الثقة هذا بتشغيل هذه الأتمتة 82 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | solved: 10 | topic_status_filter: 11 | all: "усе" 12 | discourse_automation: 13 | triggerables: 14 | first_accepted_solution: 15 | fields: 16 | maximum_trust_level: 17 | label: узровень даверу 18 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "прието" 13 | solved: 14 | accepted_answer: "Решение" 15 | solution: "Решение" 16 | topic_status_filter: 17 | all: "всички подкатегории" 18 | discourse_automation: 19 | triggerables: 20 | first_accepted_solution: 21 | fields: 22 | maximum_trust_level: 23 | label: Ниво на Доверие 24 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "prihvaćeno" 13 | solved: 14 | title: "Riješeno" 15 | allow_accepted_answers: "Dopustite vlasniku teme i osoblju da označe odgovor kao rješenje" 16 | accept_answer: "Odaberite ako ovaj odgovor rješava problem" 17 | accepted_description: "Ovo je prihvaćeno rješenje za ovu temu" 18 | has_no_accepted_answer: "Ova tema nema rešenje" 19 | unaccept_answer: "Poništite odabir ako ovaj odgovor više ne rješava problem" 20 | accepted_answer: "Rešenje" 21 | solution: "Rešenje" 22 | solution_summary: 23 | one: "riješenje" 24 | few: "riješenja" 25 | other: "riješenja" 26 | topic_status_filter: 27 | all: "sve" 28 | solved: "riješeno" 29 | unsolved: "nije riješeno" 30 | topic_statuses: 31 | solved: 32 | help: "Ova tema ima riješenje" 33 | discourse_automation: 34 | triggerables: 35 | first_accepted_solution: 36 | fields: 37 | maximum_trust_level: 38 | label: Trust Level 39 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "acceptat" 13 | solved: 14 | title: "Solucionat" 15 | allow_accepted_answers: "Permet que el propietari del tema i l'equip responsable marquin una resposta com a solució" 16 | accept_answer: "Seleccioneu aquesta resposta si resol el problema" 17 | accepted_description: "Aquesta és la solució acceptada per a aquest tema." 18 | has_no_accepted_answer: "Aquest tema no té cap solució" 19 | unaccept_answer: "Desseleccioneu aquesta resposta si ja no resol el problema" 20 | accepted_answer: "Solució" 21 | solution: "Solució" 22 | solution_summary: 23 | one: "solució" 24 | other: "solucions" 25 | accepted_html: "%{icon} Resolt per %{username} en la publicació núm. %{post_number}" 26 | accepted_notification: "%{username} %{description}
" 27 | topic_status_filter: 28 | all: "tots" 29 | solved: "resolt" 30 | unsolved: "no resolt" 31 | topic_statuses: 32 | solved: 33 | help: "Aquest tema té una solució" 34 | discourse_automation: 35 | triggerables: 36 | first_accepted_solution: 37 | fields: 38 | maximum_trust_level: 39 | label: Nivell de confiança 40 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discourse Solved" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "přijato" 18 | solutions: "Řešení" 19 | solved: 20 | title: "Vyřešeno" 21 | allow_accepted_answers: "Povolit vlastníkovi tématu a správcům označit odpověď jako řešení" 22 | solved_topics_auto_close_hours: "Automaticky uzavřít téma po (n) hodinách od poslední odpovědi, jakmile téma bylo označeno za vyřešené." 23 | accept_answer: "Vyberte, pokud tato odpověď řeší váš problém" 24 | accepted_description: "Toto je přijaté řešení tohoto tématu" 25 | has_no_accepted_answer: "Toto téma nemá žádné řešení" 26 | unaccept_answer: "Tato odpověď již neřeší tento problém" 27 | accepted_answer: "Řešení" 28 | solution: "Řešení" 29 | solution_summary: 30 | one: "řešení" 31 | few: "řešení" 32 | many: "řešení" 33 | other: "řešení" 34 | accepted_html: "%{icon} Vyřešil/a %{username} v příspěvku #%{post_number}" 35 | accepted_notification: "%{username} %{description}
" 36 | topic_status_filter: 37 | all: "vše" 38 | solved: "vyřešeno" 39 | unsolved: "nevyřešeno" 40 | no_solved_topics_title: "Zatím jste nevyřešili žádná témata" 41 | no_solved_topics_title_others: "%{username} zatím nevyřešil/a žádná témata" 42 | no_solved_topics_body: "Pokud na téma poskytnete užitečnou odpověď, vaše odpověď může být zvolena jako řešení vlastníkem tématu nebo redakcí." 43 | no_answer: 44 | title: Byla vaše otázka zodpovězena? 45 | description: "Pomozte ostatním tím, že zvýrazníte odpověď použitím tlačítka Řešení pod správnou odpovědí." 46 | notification: 47 | title: "váš příspěvek byl označen jako řešení" 48 | topic_statuses: 49 | solved: 50 | help: "Toto téma má řešení" 51 | search: 52 | advanced: 53 | statuses: 54 | solved: "jsou vyřešeny" 55 | unsolved: "nejsou vyřešeny" 56 | admin: 57 | web_hooks: 58 | solved_event: 59 | group_name: "Vyřešená událost" 60 | accepted_solution: "Když uživatel/ka označí příspěvek jako přijatou odpověď" 61 | unaccepted_solution: "Když uživatel/ka označí příspěvek jako nepřijatou odpověď" 62 | api: 63 | scopes: 64 | descriptions: 65 | solved: 66 | answer: Přijmout/nepřijmout řešení. 67 | discourse_automation: 68 | triggerables: 69 | first_accepted_solution: 70 | max_trust_level: 71 | tl1: < TL1 72 | tl2: < TL2 73 | tl3: < TL3 74 | tl4: < TL4 75 | any: Jakákoliv 76 | fields: 77 | maximum_trust_level: 78 | label: Věrohodnost 79 | description: Tuto automatizaci spustí uživatelé pod touto úrovní důvěryhodnosti 80 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "accepteret" 13 | solved: 14 | allow_accepted_answers: "Tillad ejer af emne og stab at markere svar som en løsning" 15 | accepted_answer: "Løsning" 16 | solution: "Løsning" 17 | topic_status_filter: 18 | all: "alle" 19 | topic_statuses: 20 | solved: 21 | help: "Dette svar har en løsning" 22 | discourse_automation: 23 | triggerables: 24 | first_accepted_solution: 25 | fields: 26 | maximum_trust_level: 27 | label: Tillidsniveau 28 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "αποδεκτό" 13 | solved: 14 | title: "Λύθηκε" 15 | allow_accepted_answers: "Επιτρέψτε στον κάτοχο και το προσωπικό να επισημάνει μια απάντηση ως λύση" 16 | solved_topics_auto_close_hours: "Αυτόματο κλείσιμο θέματος (n) ώρες μετά την τελευταία απάντηση μόλις το θέμα επισημανθεί ως επιλυμένο." 17 | accept_answer: "Επιλέξτε αν αυτή η απάντηση λύνει το πρόβλημα" 18 | accepted_description: "Αυτή είναι η αποδεκτή λύση σε αυτό το θέμα" 19 | has_no_accepted_answer: "Αυτό το θέμα δεν έχει καμία λύση" 20 | unaccept_answer: "Απενεργοποιήστε την επιλογή αν αυτή η απάντηση δεν λύνει πλέον το πρόβλημα" 21 | accepted_answer: "Λύση" 22 | solution: "Λύση" 23 | solution_summary: 24 | one: "λύση" 25 | other: "λύση" 26 | accepted_html: "%{icon} Λύθηκε από %{username} σε θέση #%{post_number}" 27 | accepted_notification: "%{username} %{description}
" 28 | topic_status_filter: 29 | all: "όλα" 30 | solved: "λύθηκε" 31 | unsolved: "άλυτο" 32 | topic_statuses: 33 | solved: 34 | help: "Αυτό το θέμα έχει μια λύση" 35 | search: 36 | advanced: 37 | statuses: 38 | solved: "έχουν επιλυθεί" 39 | admin: 40 | web_hooks: 41 | solved_event: 42 | group_name: "Επιλύθηκε γεγονός" 43 | discourse_automation: 44 | triggerables: 45 | first_accepted_solution: 46 | fields: 47 | maximum_trust_level: 48 | label: Επίπεδο Εμπιστοσύνης 49 | -------------------------------------------------------------------------------- /config/locales/client.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | admin_js: 3 | admin: 4 | site_settings: 5 | categories: 6 | discourse_solved: "Discourse Solved" 7 | js: 8 | notifications: 9 | alt: 10 | solved: 11 | accepted_notification: "accepted" 12 | solutions: "Solutions" 13 | 14 | solved: 15 | title: "Solved" 16 | allow_accepted_answers: "Allow topic owner and staff to mark a reply as the solution" 17 | solved_topics_auto_close_hours: "Auto close topic (n) hours after the last reply once the topic has been marked as solved." 18 | accept_answer: "Select if this reply solves the problem" 19 | accepted_description: "This is the accepted solution to this topic" 20 | has_no_accepted_answer: "This topic has no solution" 21 | unaccept_answer: "Unselect if this reply no longer solves the problem" 22 | accepted_answer: "Solution" 23 | solution: "Solution" 24 | solution_summary: 25 | one: "solution" 26 | other: "solutions" 27 | accepted_html: "%{icon} Solved by %{username} in post #%{post_number}" 28 | accepted_notification: "%{username} %{description}
" 29 | topic_status_filter: 30 | all: "all" 31 | solved: "solved" 32 | unsolved: "unsolved" 33 | no_solved_topics_title: "You haven’t solved any topics yet" 34 | no_solved_topics_title_others: "%{username} has not solved any topics yet" 35 | no_solved_topics_body: "When you provide a helpful reply to a topic, your reply might be selected as the solution by the topic owner or staff." 36 | marked_solved_by: "Marked as solved by %{username}" 37 | 38 | no_answer: 39 | title: Has your question been answered? 40 | description: "Highlight the answer and help others by using the solution button below the correct reply." 41 | notification: 42 | title: "your post was marked as solution" 43 | 44 | topic_statuses: 45 | solved: 46 | help: "This topic has a solution" 47 | 48 | search: 49 | advanced: 50 | statuses: 51 | solved: "are solved" 52 | unsolved: "are unsolved" 53 | 54 | admin: 55 | web_hooks: 56 | solved_event: 57 | group_name: "Solved Event" 58 | accepted_solution: "When an user marks a post as the accepted answer" 59 | unaccepted_solution: "When an user marks a post as the unaccepted answer" 60 | api: 61 | scopes: 62 | descriptions: 63 | solved: 64 | answer: Accept/Unaccept a solution. 65 | 66 | discourse_automation: 67 | triggerables: 68 | first_accepted_solution: 69 | max_trust_level: 70 | tl1: < TL1 71 | tl2: < TL2 72 | tl3: < TL3 73 | tl4: < TL4 74 | any: Any 75 | fields: 76 | maximum_trust_level: 77 | label: Trust Level 78 | description: Users under this Trust Level will trigger this automation 79 | -------------------------------------------------------------------------------- /config/locales/client.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/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "aktsepteeritud" 13 | solved: 14 | title: "Lahendatud" 15 | has_no_accepted_answer: "Sellel teemal pole lahendust" 16 | accepted_answer: "Lahendus" 17 | solution: "Lahendus" 18 | solution_summary: 19 | one: "lahendus" 20 | other: "lahendused" 21 | topic_status_filter: 22 | all: "kõik" 23 | solved: "lahendatud" 24 | topic_statuses: 25 | solved: 26 | help: "ellel teemal on lahendus" 27 | discourse_automation: 28 | triggerables: 29 | first_accepted_solution: 30 | fields: 31 | maximum_trust_level: 32 | label: Usaldustase 33 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "تایید شده" 13 | solutions: "راهحلها" 14 | solved: 15 | title: "حل شده" 16 | allow_accepted_answers: "به صاحب عنوان و مدیران اجازه انتخاب یک پاسخ را به عنوان راه حل بده." 17 | accept_answer: "اگر این پاسخ مشکل را حل میکند، آن را انتخاب کنید" 18 | has_no_accepted_answer: "این موضوع هیچ راه حلی ندارد" 19 | accepted_answer: "راه حل مورد قبول" 20 | solution: "راه حل" 21 | solution_summary: 22 | one: "راه حل" 23 | other: "راه حل ها" 24 | accepted_notification: "%{username} %{description}
" 25 | topic_status_filter: 26 | all: "همه" 27 | solved: "حل شده" 28 | unsolved: "حل نشده" 29 | no_solved_topics_title_others: "%{username} هنوز هیچ موضوعی را حل نکرده است" 30 | notification: 31 | title: "نوشته شما به عنوان راه حل مشخص شد" 32 | topic_statuses: 33 | solved: 34 | help: "این موضوع راه حلی دارد" 35 | search: 36 | advanced: 37 | statuses: 38 | solved: "حل شدهاند" 39 | admin: 40 | web_hooks: 41 | solved_event: 42 | group_name: "رویداد حل شده" 43 | api: 44 | scopes: 45 | descriptions: 46 | solved: 47 | answer: تایید/رد کردن یک راهحل 48 | discourse_automation: 49 | triggerables: 50 | first_accepted_solution: 51 | fields: 52 | maximum_trust_level: 53 | label: سطحاعتماد 54 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discoursen ratkaistu" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "hyväksytty" 18 | solutions: "Ratkaisut" 19 | solved: 20 | title: "Ratkaistu" 21 | allow_accepted_answers: "Salli ketjun omistajan ja henkilökunnan merkitä viesti ratkaisuksi" 22 | solved_topics_auto_close_hours: "Sulje ketju automaattisesti (n) tunnin kuluttua viimeisestä vastauksesta, kun ketjun on ratkaistu." 23 | accept_answer: "Valitse, jos tämä vastaus ratkaisee ongelman" 24 | accepted_description: "Tämä on ketjun hyväksytty ratkaisu" 25 | has_no_accepted_answer: "Tässä ketjussa ei ole ratkaisua" 26 | unaccept_answer: "Kumoa valinta, jos tämä vastaus ei enää ratkaise ongelmaa" 27 | accepted_answer: "Ratkaisu" 28 | solution: "Ratkaisu" 29 | solution_summary: 30 | one: "ratkaisu" 31 | other: "ratkaisua" 32 | accepted_html: "%{icon} %{username} ratkaisi viestissä %{post_number}" 33 | accepted_notification: "%{username} %{description}
" 34 | topic_status_filter: 35 | all: "kaikki" 36 | solved: "ratkaistu" 37 | unsolved: "ratkaisematon" 38 | no_solved_topics_title: "Et ole vielä ratkaissut ketjuja" 39 | no_solved_topics_title_others: "%{username} ei ole vielä ratkaissut ketjuja" 40 | no_solved_topics_body: "Kun annat hyödyllisen vastauksen ketjuun, aiheen omistaja tai henkilökunta saattaa valita vastauksesi ratkaisuksi." 41 | no_answer: 42 | title: Onko kysymykseesi vastattu? 43 | description: "Korosta vastaus ja auta muita käyttämällä oikean vastauksen alla olevaa ratkaisupainiketta." 44 | notification: 45 | title: "viestisi merkittiin ratkaisuksi" 46 | topic_statuses: 47 | solved: 48 | help: "Tämä ketju on ratkaistu" 49 | search: 50 | advanced: 51 | statuses: 52 | solved: "ovat ratkaistu" 53 | unsolved: "ovat ratkaisemattomia" 54 | admin: 55 | web_hooks: 56 | solved_event: 57 | group_name: "Ratkaistu-tapahtuma" 58 | accepted_solution: "Kun käyttäjä merkitsee viestin hyväksytyksi vastaukseksi" 59 | unaccepted_solution: "Kun käyttäjä merkitsee viestin hyväksymättömäksi vastaukseksi" 60 | api: 61 | scopes: 62 | descriptions: 63 | solved: 64 | answer: Hyväksy tai hylkää ratkaisu. 65 | discourse_automation: 66 | triggerables: 67 | first_accepted_solution: 68 | max_trust_level: 69 | tl1: < LT1 70 | tl2: < LT2 71 | tl3: < LT3 72 | tl4: < LT4 73 | any: Mikä tahansa 74 | fields: 75 | maximum_trust_level: 76 | label: Luottamustaso 77 | description: Tämän luottamustason alittavat käyttäjät laukaisevat tämän automaation 78 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "aceptado" 13 | solved: 14 | topic_status_filter: 15 | all: "todo" 16 | discourse_automation: 17 | triggerables: 18 | first_accepted_solution: 19 | fields: 20 | maximum_trust_level: 21 | label: Nivel de confianza 22 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discourse נפתר" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "התקבל" 18 | solutions: "פתרונות" 19 | solved: 20 | title: "נפתר" 21 | allow_accepted_answers: "לאפשר למשתמשים לאשר תשובות" 22 | solved_topics_auto_close_hours: "לסגור נושא אוטומטית (n) שעות לאחר התגובה האחרונה כאשר הנושא סומן כפתור." 23 | accept_answer: "יש לבחור בזה אם התגובה הזאת פותרת את הבעיה" 24 | accepted_description: "זה הפתרון המקובל לנושא זה" 25 | has_no_accepted_answer: "לנושא הזה אין פתרון" 26 | unaccept_answer: "יש לבטל את הסימון אם התגובה הזו אינה פותרת עוד את הבעיה." 27 | accepted_answer: "פתרון" 28 | solution: "פתרון" 29 | solution_summary: 30 | one: "פתרון" 31 | two: "פתרונות" 32 | many: "פתרונות" 33 | other: "פתרונות" 34 | accepted_html: "%{icon} נפתר על ידי %{username} בפוסט מס׳ %{post_number}" 35 | accepted_notification: "%{username} %{description}
" 36 | topic_status_filter: 37 | all: "הכול" 38 | solved: "נפתר" 39 | unsolved: "בלתי פתור" 40 | no_solved_topics_title: "עדיין לא פתרת אף נושא" 41 | no_solved_topics_title_others: "אין נושאים שנפתרו על ידי %{username} עדיין" 42 | no_solved_topics_body: "לאחר שכתבת תגובה מועילה לנושא, התגובה שלך עשויה להיבחר כפתרון על ידי בעלי הנושא או הסגל." 43 | marked_solved_by: "סומן כפתור על ידי %{username}" 44 | no_answer: 45 | title: התשובה שלך נענתה? 46 | description: "ניתן להדגיש את התשובה ולסייע לאחרים על ידי לחיצה על כפתור הפתרון שלהלן מתחת לתגובה הנכונה." 47 | notification: 48 | title: "הפוסט שלך סומן כפתרון" 49 | topic_statuses: 50 | solved: 51 | help: "נושא זה נפתר" 52 | search: 53 | advanced: 54 | statuses: 55 | solved: "נפתרו" 56 | unsolved: "לא נפתרו" 57 | admin: 58 | web_hooks: 59 | solved_event: 60 | group_name: "אירוע שנפתר" 61 | accepted_solution: "כשמשתמש מסמן פוסט כתשובה מקובלת" 62 | unaccepted_solution: "כשמשתמש מסמן פוסט כתשובה בלתי מקובלת" 63 | api: 64 | scopes: 65 | descriptions: 66 | solved: 67 | answer: קבלת/דחיית פתרון. 68 | discourse_automation: 69 | triggerables: 70 | first_accepted_solution: 71 | max_trust_level: 72 | tl1: < דרגת אמון 1 73 | tl2: < דרגת אמון 2 74 | tl3: < דרגת אמון 3 75 | tl4: < דרגת אמון 4 76 | any: כלשהו 77 | fields: 78 | maximum_trust_level: 79 | label: דרגת אמון 80 | description: משתמשים מתחת לדרגת אמון זו יקפיצו אוטומציה זו 81 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "prihvaćen" 13 | solved: 14 | title: "Riješeno" 15 | allow_accepted_answers: "Dopusti vlasniku teme i osoblju da označi odgovor kao rješenje" 16 | accept_answer: "Odaberite ¸ako ovaj odgovor rješava problem" 17 | accepted_description: "Ovo je prihvaćeno rješenje za ovu temu" 18 | has_no_accepted_answer: "Ova tema nema rješenje" 19 | unaccept_answer: "Poništite odabir ako ovaj odgovor više ne rješava problem" 20 | accepted_answer: "Riješenje" 21 | solution: "Riješenje" 22 | topic_status_filter: 23 | all: "sve" 24 | solved: "riješeno" 25 | discourse_automation: 26 | triggerables: 27 | first_accepted_solution: 28 | fields: 29 | maximum_trust_level: 30 | label: Razina povjerenja 31 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discourse Megoldott" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "elfogadva" 18 | solutions: "Megoldások" 19 | solved: 20 | title: "Megoldott" 21 | allow_accepted_answers: "Engedélyezi a téma tulajdonosának és a stábnak, hogy megoldásként jelöljenek meg egy bejegyzést" 22 | solved_topics_auto_close_hours: "Automatikusan zárja le a témát (n) órával az utolsó válasz után, miután a témát megoldották." 23 | accept_answer: "Válassza ezt, ha ez a válasz megoldja a problémát" 24 | accepted_description: "Ez a téma elfogadott megoldása" 25 | has_no_accepted_answer: "Ez a téma nem tartalmaz megoldást" 26 | unaccept_answer: "Válaszd ki ha ez a válasz már nem oldja meg a problémát" 27 | accepted_answer: "Megoldás" 28 | solution: "Megoldás" 29 | solution_summary: 30 | one: "megoldás" 31 | other: "megoldás" 32 | accepted_html: "%{icon} %{username} megoldotta a(z) %{post_number}. bejegyzéssel" 33 | accepted_notification: "%{username} %{description}
" 34 | topic_status_filter: 35 | all: "Összes" 36 | solved: "megoldott" 37 | unsolved: "megoldatlan" 38 | no_solved_topics_title: "Még egy témát sem oldott meg" 39 | no_solved_topics_title_others: "%{username} még nem oldott meg témát" 40 | no_solved_topics_body: "Ha hasznos választ ad egy témára, előfordulhat, hogy a téma tulajdonosa vagy a stáb az Ön válaszát választják megoldásként." 41 | no_answer: 42 | title: Megválaszolták a kérdését? 43 | description: "Emelje ki a választ, és segítsen másoknak is a helyes válasz alatti megoldás gomb segítségével." 44 | notification: 45 | title: "hozzászólásod megoldásként lett megjelölve" 46 | topic_statuses: 47 | solved: 48 | help: "Ez a téma tartalmaz egy megoldást" 49 | search: 50 | advanced: 51 | statuses: 52 | solved: "megoldott" 53 | unsolved: "megoldatlan" 54 | admin: 55 | web_hooks: 56 | solved_event: 57 | group_name: "Megoldott esemény" 58 | accepted_solution: "Amikor egy felhasználó egy hozzászólást elfogadott válaszként jelöl meg" 59 | unaccepted_solution: "Amikor egy felhasználó egy hozzászólást nem elfogadott válaszként jelöl meg" 60 | api: 61 | scopes: 62 | descriptions: 63 | solved: 64 | answer: Megoldás elfogadása/elutasítása. 65 | discourse_automation: 66 | triggerables: 67 | first_accepted_solution: 68 | max_trust_level: 69 | tl1: < BSZ1 70 | tl2: < BSZ2 71 | tl3: < BSZ3 72 | tl4: < BSZ4 73 | any: Bármely 74 | fields: 75 | maximum_trust_level: 76 | label: Bizalmi szint 77 | description: Az ezen a bizalmi szint alatti felhasználók aktiválják ezt az automatizálást 78 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "ընդունված" 13 | solved: 14 | title: "Լուծված է" 15 | allow_accepted_answers: "Թույլատրել թեմայի սեփականատիրոջը և անձնակազմին՝ նշել պատասխանը որպես լուծում" 16 | accept_answer: "Ընտրեք, թե արդյոք այս պատասխանը լուծում է խնդիրը" 17 | has_no_accepted_answer: "Այս թեման լուծում չունի" 18 | unaccept_answer: "Ընտրությունից հանել, եթե այս պատասխանը այլևս չի լուծում խնդիրը" 19 | accepted_answer: "Լուծում" 20 | solution: "Լուծում" 21 | solution_summary: 22 | one: "լուծում" 23 | other: "լուծում" 24 | topic_status_filter: 25 | all: "բոլոր" 26 | solved: "լուծված" 27 | unsolved: "չլուծված" 28 | topic_statuses: 29 | solved: 30 | help: "Այս թեման ունի լուծում:" 31 | discourse_automation: 32 | triggerables: 33 | first_accepted_solution: 34 | fields: 35 | maximum_trust_level: 36 | label: Վստահության Մակարդակ 37 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "Diterima" 13 | solved: 14 | allow_accepted_answers: "Izinkan pemilik topik dan staf untuk menandai balasan sebagai solusi" 15 | accept_answer: "Pilih jika balasan ini menyelesaikan masalah" 16 | has_no_accepted_answer: "Topik ini tidak memiliki solusi" 17 | accepted_answer: "Solusi" 18 | solution: "Solusi" 19 | solution_summary: 20 | other: "Solusi" 21 | topic_status_filter: 22 | all: "Semua" 23 | topic_statuses: 24 | solved: 25 | help: "Topik ini memiliki solusi" 26 | discourse_automation: 27 | triggerables: 28 | first_accepted_solution: 29 | fields: 30 | maximum_trust_level: 31 | label: Level Kepercayaan 32 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discourse 解決済み" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "受け入れられました" 18 | solutions: "解決策" 19 | solved: 20 | title: "解決済み" 21 | allow_accepted_answers: "トピックオーナーとスタッフが返信を解決策としてマークすることを許可する" 22 | solved_topics_auto_close_hours: "トピックが解決済みとマークされたら、最後の返信から (n) 時間後にトピックを自動クローズします。" 23 | accept_answer: "この返信が問題を解決したか選択してください" 24 | accepted_description: "これは、このトピックについて受け入れられた解決策です。" 25 | has_no_accepted_answer: "このトピックには解決策がありません" 26 | unaccept_answer: "この返信が問題を解決できなくなった場合は選択を解除してください" 27 | accepted_answer: "解決策" 28 | solution: "解決策" 29 | solution_summary: 30 | other: "解決策" 31 | accepted_html: "%{icon} 投稿 #%{post_number} で %{username} が解決" 32 | accepted_notification: "%{username} %{description}
" 33 | topic_status_filter: 34 | all: "すべて" 35 | solved: "解決済み" 36 | unsolved: "未解決" 37 | no_solved_topics_title: "まだトピックを解決していません。" 38 | no_solved_topics_title_others: "%{username} はまだトピックを解決していません" 39 | no_solved_topics_body: "トピックに有益な返信を提供すると、その返信がトピックオーナーまたはスタッフによって解決策として選択される場合があります。" 40 | no_answer: 41 | title: 質問の回答は得られましたか? 42 | description: "適切な返信の下にある解決策ボタンを使って回答をハイライトし、他のユーザーを助けましょう。" 43 | notification: 44 | title: "あなたの投稿が解決策としてマークされました" 45 | topic_statuses: 46 | solved: 47 | help: "このトピックには解決策があります" 48 | search: 49 | advanced: 50 | statuses: 51 | solved: "解決済み" 52 | unsolved: "未解決" 53 | admin: 54 | web_hooks: 55 | solved_event: 56 | group_name: "解決済みイベント" 57 | accepted_solution: "ユーザーが投稿を受け入れられる回答としてマークしたとき" 58 | unaccepted_solution: "ユーザーが投稿を受け入れられない回答としてマークしたとき" 59 | api: 60 | scopes: 61 | descriptions: 62 | solved: 63 | answer: 解決策に同意/非同意します。 64 | discourse_automation: 65 | triggerables: 66 | first_accepted_solution: 67 | max_trust_level: 68 | tl1: < TL1 69 | tl2: < TL2 70 | tl3: < TL3 71 | tl4: < TL4 72 | any: すべて 73 | fields: 74 | maximum_trust_level: 75 | label: 信頼レベル 76 | description: この信頼レベルより低いユーザーは、この自動化をトリガーする 77 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "승인됨" 13 | solved: 14 | title: "해결됨" 15 | allow_accepted_answers: "토픽 소유자 및 스태프가 해결책으로 답글을 표시하도록 허용" 16 | accept_answer: "이 답변이 문제를 해결할 경우 선택하십시오" 17 | accepted_description: "이것이이 주제에 대한 해결책입니다." 18 | has_no_accepted_answer: "이 토픽에는 해결책이 없습니다" 19 | unaccept_answer: "이 답변으로 더 이상 문제가 해결되지 않으면 선택 취소" 20 | accepted_answer: "해결책" 21 | solution: "해결책" 22 | solution_summary: 23 | other: "해결책" 24 | topic_status_filter: 25 | all: "모두" 26 | solved: "해결됨" 27 | unsolved: "미해결" 28 | topic_statuses: 29 | solved: 30 | help: "이 토픽에는 해결책이 있습니다" 31 | search: 32 | advanced: 33 | statuses: 34 | solved: "해결되다" 35 | admin: 36 | web_hooks: 37 | solved_event: 38 | group_name: "해결 된 이벤트" 39 | discourse_automation: 40 | triggerables: 41 | first_accepted_solution: 42 | fields: 43 | maximum_trust_level: 44 | label: 회원 레벨 45 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "patvirtinta" 13 | solved: 14 | title: "Išspresta" 15 | topic_status_filter: 16 | all: "visi" 17 | solved: "išspresta" 18 | discourse_automation: 19 | triggerables: 20 | first_accepted_solution: 21 | fields: 22 | maximum_trust_level: 23 | label: Patikimumo lygis 24 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "apstiprināts" 13 | solved: 14 | topic_status_filter: 15 | all: "Viss" 16 | discourse_automation: 17 | triggerables: 18 | first_accepted_solution: 19 | fields: 20 | maximum_trust_level: 21 | label: Uzticības līmenis 22 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "godtatt" 13 | solved: 14 | title: "Løst" 15 | allow_accepted_answers: "Tillat enmeeier og personale å markere et svar som løsning" 16 | solved_topics_auto_close_hours: "Lukk emnet automatisk (n) timer etter siste svar når det har blitt merket som løst." 17 | accept_answer: "Velg hvis dette svaret løser problemet" 18 | has_no_accepted_answer: "Dette emnet har ingen løsning" 19 | unaccept_answer: "Fravelg dette hvis svaret ikke lenger løser problemet" 20 | accepted_answer: "Løsning" 21 | solution: "Løsning" 22 | topic_status_filter: 23 | all: "alle" 24 | solved: "løst" 25 | topic_statuses: 26 | solved: 27 | help: "Dette emnet har en løsning" 28 | discourse_automation: 29 | triggerables: 30 | first_accepted_solution: 31 | fields: 32 | maximum_trust_level: 33 | label: Tillitsnivå 34 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discourse opgelost" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "geaccepteerd" 18 | solutions: "Oplossingen" 19 | solved: 20 | title: "Opgelost" 21 | allow_accepted_answers: "Topiceigenaar en medewerkers toestaan een antwoord als oplossing te markeren" 22 | solved_topics_auto_close_hours: "Topic (n) uur na het laatste antwoord automatisch sluiten zodra het topic als opgelost is gemarkeerd." 23 | accept_answer: "Selecteer als dit antwoord het probleem oplost" 24 | accepted_description: "Dit is de geaccepteerde oplossing voor dit topic" 25 | has_no_accepted_answer: "Dit topic heeft geen oplossing" 26 | unaccept_answer: "Deselecteer als dit antwoord het probleem niet meer oplost" 27 | accepted_answer: "Oplossing" 28 | solution: "Oplossing" 29 | solution_summary: 30 | one: "oplossing" 31 | other: "oplossingen" 32 | accepted_html: "%{icon} Opgelost door %{username} in bericht %{post_number}" 33 | accepted_notification: "%{username} %{description}
" 34 | topic_status_filter: 35 | all: "alle" 36 | solved: "opgelost" 37 | unsolved: "onopgelost" 38 | no_solved_topics_title: "Je hebt nog geen topics opgelost" 39 | no_solved_topics_title_others: "%{username} heeft nog geen topics opgelost" 40 | no_solved_topics_body: "Als je een nuttig antwoord geeft op een topic, kan je antwoord door de eigenaar van het topic of door een medewerker als oplossing worden gekozen." 41 | no_answer: 42 | title: Is je vraag beantwoord? 43 | description: "Markeer het antwoord en help anderen door de oplossingsknop onder het juiste antwoord te gebruiken." 44 | notification: 45 | title: "Je bericht is gemarkeerd als oplossing" 46 | topic_statuses: 47 | solved: 48 | help: "Dit topic heeft een oplossing" 49 | search: 50 | advanced: 51 | statuses: 52 | solved: "zijn opgelost" 53 | unsolved: "zijn onopgelost" 54 | admin: 55 | web_hooks: 56 | solved_event: 57 | group_name: "Oplossingsgebeurtenis" 58 | accepted_solution: "Wanneer een gebruiker een bericht als geaccepteerd antwoord markeert" 59 | unaccepted_solution: "Wanneer een gebruiker een bericht als niet-geaccepteerd antwoord markeert" 60 | api: 61 | scopes: 62 | descriptions: 63 | solved: 64 | answer: Accepteer/deaccepteer een oplossing. 65 | discourse_automation: 66 | triggerables: 67 | first_accepted_solution: 68 | max_trust_level: 69 | tl1: < TL1 70 | tl2: < TL2 71 | tl3: < TL3 72 | tl4: < TL4 73 | any: Alle 74 | fields: 75 | maximum_trust_level: 76 | label: Vertrouwensniveau 77 | description: Gebruikers onder dit vertrouwensniveau activeren deze automatisering 78 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "przyjęty" 13 | solutions: "Rozwiązania" 14 | solved: 15 | title: "Rozwiązany" 16 | allow_accepted_answers: "Pozwól użytkownikom na akceptowanie rozwiązań" 17 | solved_topics_auto_close_hours: "Zamknij temat automatycznie po (n) godzinach od ostatniej odpowiedzi, gdy temat został oznaczony jako rozwiązany." 18 | accept_answer: "Zaznacz, jeśli ta odpowiedź rozwiązuje Twój problem" 19 | accepted_description: "To zaakceptowane rozwiązanie tego tematu." 20 | has_no_accepted_answer: "Ten temat nie ma rozwiązania" 21 | unaccept_answer: "Odznacz jeśli ta odpowiedź nie rozwiązuje już problemu" 22 | accepted_answer: "Zaakceptowana odpowiedź" 23 | solution: "Rozwiązanie" 24 | solution_summary: 25 | one: "rozwiązanie" 26 | few: "rozwiązania" 27 | many: "rozwiązania" 28 | other: "rozwiązania" 29 | accepted_html: "%{icon} Rozwiązany przez %{username} w poście #%{post_number}" 30 | accepted_notification: "%{username} %{description}
" 31 | topic_status_filter: 32 | all: "wszystkie" 33 | solved: "rozwiązano" 34 | unsolved: "nierozwiązano" 35 | no_solved_topics_title: "Nie rozwiązałeś jeszcze żadnych tematów" 36 | no_solved_topics_title_others: "%{username} nie rozwiązał jeszcze żadnych tematów" 37 | no_solved_topics_body: "Gdy udzielisz pomocnej odpowiedzi na dany temat, Twoja odpowiedź może zostać wybrana jako rozwiązanie przez właściciela tematu lub personel." 38 | no_answer: 39 | title: Czy odpowiedź na twoje pytanie została udzielona? 40 | description: "Podświetl odpowiedź i pomóż innym, korzystając z przycisku rozwiązania pod prawidłową odpowiedzią." 41 | notification: 42 | title: "twój post został oznaczony jako rozwiązanie" 43 | topic_statuses: 44 | solved: 45 | help: "Ten temat ma zaakceptowane rozwiązanie" 46 | search: 47 | advanced: 48 | statuses: 49 | solved: "są rozwiązane" 50 | unsolved: "są nierozwiązane" 51 | admin: 52 | web_hooks: 53 | solved_event: 54 | group_name: "Zdarzenie rozwiązania" 55 | api: 56 | scopes: 57 | descriptions: 58 | solved: 59 | answer: Zaakceptuj/Odrzuć rozwiązanie. 60 | discourse_automation: 61 | triggerables: 62 | first_accepted_solution: 63 | max_trust_level: 64 | tl1: < TL1 65 | tl2: < TL2 66 | tl3: < TL3 67 | tl4: < TL4 68 | any: Dowolny 69 | fields: 70 | maximum_trust_level: 71 | label: Poziom zaufania 72 | description: Użytkownicy poniżej tego poziomu zaufania uruchomią tę automatyzację 73 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "aceite" 13 | solved: 14 | title: "Resolvido" 15 | allow_accepted_answers: "Permitir que o autor do tópico e a equipa marquem uma resposta como solução" 16 | solved_topics_auto_close_hours: "Fechar automaticamente o tópico (n) horas depois do tópico ser marcado como solução." 17 | accept_answer: "Selecionar se esta resposta resolver o problema" 18 | has_no_accepted_answer: "Este tópico não tem solução" 19 | unaccept_answer: "Desselecionar se esta resposta já não resolver o problema" 20 | accepted_answer: "Solução" 21 | solution: "Solução" 22 | solution_summary: 23 | one: "solução" 24 | other: "soluções" 25 | accepted_html: "%{icon} Resolvido por %{username} na mensagem #%{post_number}" 26 | accepted_notification: "%{username} %{description}
" 27 | topic_status_filter: 28 | all: "todos" 29 | solved: "resolvido" 30 | topic_statuses: 31 | solved: 32 | help: "Este tópico tem uma solução" 33 | discourse_automation: 34 | triggerables: 35 | first_accepted_solution: 36 | fields: 37 | maximum_trust_level: 38 | label: Nível de Confiança 39 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Solucionado no Discourse" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "aceito" 18 | solutions: "Soluções" 19 | solved: 20 | title: "Resolvido" 21 | allow_accepted_answers: "Permitir que o(a) proprietário(a) do tópico e a equipe marquem uma resposta como a solução" 22 | solved_topics_auto_close_hours: "Fechar tópico automaticamente (n) horas após a última resposta quando o tópico tiver sido marcado como resolvido." 23 | accept_answer: "Selecione se esta resposta resolve o problema" 24 | accepted_description: "Esta é a solução aceita para este tópico" 25 | has_no_accepted_answer: "Este tópico não tem solução" 26 | unaccept_answer: "Desmarque se esta resposta não resolve mais o problema" 27 | accepted_answer: "Solução" 28 | solution: "Solução" 29 | solution_summary: 30 | one: "solução" 31 | other: "soluções" 32 | accepted_html: "%{icon} resolvido por %{username} em postagem #%{post_number}" 33 | accepted_notification: "%{username} %{description}
" 34 | topic_status_filter: 35 | all: "tudo" 36 | solved: "solucionados" 37 | unsolved: "não solucionados" 38 | no_solved_topics_title: "Você ainda não solucionou nenhum tópico" 39 | no_solved_topics_title_others: "%{username} ainda não respondeu a nenhum tópico" 40 | no_solved_topics_body: "Quando você fornecer uma resposta útil a um tópico, ela poderá ser selecionada como uma solução pelo(a) proprietário(a) ou equipe do tópico." 41 | no_answer: 42 | title: Sua pergunta foi respondida? 43 | description: "Destaque a resposta e ajude as outras pessoas ao usar o botão de solução abaixo para responder corretamente." 44 | notification: 45 | title: "sua postagem foi marcada como uma solução" 46 | topic_statuses: 47 | solved: 48 | help: "Este tópico tem uma solução" 49 | search: 50 | advanced: 51 | statuses: 52 | solved: "foram solucionados" 53 | unsolved: "não foram solucionados" 54 | admin: 55 | web_hooks: 56 | solved_event: 57 | group_name: "Evento solucionado" 58 | accepted_solution: "Quando um(a) usuário(a) marca uma postagem como resposta aceita" 59 | unaccepted_solution: "Quando um(a) usuário(a) marca uma postagem como resposta não aceita" 60 | api: 61 | scopes: 62 | descriptions: 63 | solved: 64 | answer: Aceitar/não aceitar uma solução. 65 | discourse_automation: 66 | triggerables: 67 | first_accepted_solution: 68 | max_trust_level: 69 | tl1: < TL1 70 | tl2: < TL2 71 | tl3: < TL3 72 | tl4: < TL4 73 | any: Qualquer 74 | fields: 75 | maximum_trust_level: 76 | label: Nível de confiança 77 | description: Os(as) usuários(as) com este nível de confiança ativarão esta automação 78 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "acceptat" 13 | solved: 14 | title: "Rezolvat" 15 | allow_accepted_answers: "Permite inițiatorului subiectului și personalului să marcheze un răspuns ca soluție" 16 | solved_topics_auto_close_hours: "Închide subiectul automat la (n) ore după ultimul răspuns după ce subiectul a fost marcat ca rezolvat." 17 | accept_answer: "Selectează acest mesaj dacă rezolvă problema" 18 | accepted_description: "Aceasta este soluţia acceptată pentru acest subiect" 19 | has_no_accepted_answer: "Acest subiect nu are nicio soluție" 20 | unaccept_answer: "Deselectați dacă acest răspuns nu mai rezolvă problema" 21 | accepted_answer: "Soluție" 22 | solution: "Soluție" 23 | solution_summary: 24 | one: "soluţie" 25 | few: "soluţii" 26 | other: "soluţii" 27 | accepted_html: "%{icon} Rezolvat de %{username} în post #%{post_number}" 28 | accepted_notification: "%{username} %{description}
" 29 | topic_status_filter: 30 | all: "tot" 31 | solved: "rezolvat" 32 | unsolved: "nerezolvat" 33 | topic_statuses: 34 | solved: 35 | help: "Acest subiect are o soluție" 36 | search: 37 | advanced: 38 | statuses: 39 | solved: "sunt rezolvate" 40 | unsolved: "sunt nerezolvate" 41 | admin: 42 | web_hooks: 43 | solved_event: 44 | group_name: "Eveniment rezolvat" 45 | discourse_automation: 46 | triggerables: 47 | first_accepted_solution: 48 | fields: 49 | maximum_trust_level: 50 | label: Nivel de încredere 51 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Плагин «Решённые» для Discourse" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "принято" 18 | solutions: "Решения" 19 | solved: 20 | title: "Решённые" 21 | allow_accepted_answers: "Разрешать автору темы и модераторам помечать ответ статусом 'Вопрос решён'" 22 | solved_topics_auto_close_hours: "Автоматически закрывать тему через указанное здесь количество часов после того, как тема была отмечена как решенная." 23 | accept_answer: "Выберите, если этот ответ решает проблему" 24 | accepted_description: "Этот ответ решает вопрос, обсуждаемый в этой теме" 25 | has_no_accepted_answer: "Тема не содержит решения вопроса" 26 | unaccept_answer: "Отмените выбор, если этот ответ не решает проблему" 27 | accepted_answer: "Решение вопроса" 28 | solution: "Вопрос решён" 29 | solution_summary: 30 | one: "вопрос решён" 31 | few: "вопроса решены" 32 | many: "вопросов решены" 33 | other: "вопросов решены" 34 | accepted_html: "%{icon} Вопрос решён пользователем %{username} в сообщении #%{post_number}" 35 | accepted_notification: "%{username} %{description}
" 36 | topic_status_filter: 37 | all: "все" 38 | solved: "Решённые" 39 | unsolved: "Нерешённые" 40 | no_solved_topics_title: "У вас пока нет решённых тем" 41 | no_solved_topics_title_others: "У пользователя %{username} ещё нет тем, содержащих решённые вопросы" 42 | no_solved_topics_body: "Когда вы даете в теме полезный ответ, он может быть отмечен в качестве решения владельцем темы или сотрудниками." 43 | no_answer: 44 | title: Вопрос был решён? 45 | description: "Выделите ответ и помогите другим пользователям, нажав на кнопку 'Вопрос решён' под правильным ответом." 46 | notification: 47 | title: "ваша запись отмечена как решение" 48 | topic_statuses: 49 | solved: 50 | help: "Тема содержит решение вопроса" 51 | search: 52 | advanced: 53 | statuses: 54 | solved: "Решённые" 55 | unsolved: "Нерешённые" 56 | admin: 57 | web_hooks: 58 | solved_event: 59 | group_name: "Событие решения вопроса" 60 | accepted_solution: "Когда пользователь отмечает публикацию как принятый ответ" 61 | unaccepted_solution: "Когда пользователь отмечает публикацию как непринятый ответ" 62 | api: 63 | scopes: 64 | descriptions: 65 | solved: 66 | answer: Принять решение (отменить принятие). 67 | discourse_automation: 68 | triggerables: 69 | first_accepted_solution: 70 | max_trust_level: 71 | tl1: < ур. 1 72 | tl2: < ур. 2 73 | tl3: < ур. 3 74 | tl4: < ур. 4 75 | any: Любой 76 | fields: 77 | maximum_trust_level: 78 | label: Уровень доверия 79 | description: Пользователи с этим уровнем доверия будут запускать этот скрипт 80 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "prijať" 13 | solved: 14 | topic_status_filter: 15 | all: "všetko" 16 | discourse_automation: 17 | triggerables: 18 | first_accepted_solution: 19 | fields: 20 | maximum_trust_level: 21 | label: Stupeň dôvery 22 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "sprejeto" 13 | solutions: "Rešitve" 14 | solved: 15 | title: "Rešeno" 16 | allow_accepted_answers: "Lastniku teme in osebju dovolite, da odgovor označijo kot rešitev" 17 | solved_topics_auto_close_hours: "Samodejno zapiranje teme (n) ur po zadnjem odgovoru, ko je tema označena kot rešena." 18 | accept_answer: "Izberite, če ta odgovor reši problem" 19 | accepted_description: "To je sprejeta rešitev te teme" 20 | has_no_accepted_answer: "Ta tema nima rešitve" 21 | unaccept_answer: "Odstrani izbor, če ta odgovor več ne reši problema" 22 | accepted_answer: "Rešitev" 23 | solution: "Rešitev" 24 | solution_summary: 25 | one: "rešitev" 26 | two: "rešitvi" 27 | few: "rešitve" 28 | other: "rešitev" 29 | accepted_html: "%{icon} Rešil %{username} v objavi #%{post_number}" 30 | accepted_notification: "%{username} %{description}
" 31 | topic_status_filter: 32 | all: "vse" 33 | solved: "rešeno" 34 | unsolved: "nerešeno" 35 | no_solved_topics_title: "Rešili niste še nobene teme" 36 | no_solved_topics_title_others: "%{username} še ni rešil nobene teme" 37 | no_solved_topics_body: "Ko podate koristen odgovor na temo, lahko lastnik teme ali osebje izbere vaš odgovor kot rešitev." 38 | no_answer: 39 | title: Ste dobili odgovor na vaše vprašanje? 40 | description: "Z izbiro gumba za potrditev pod ustrezno objavo izpostavite odgovor in s tem pomagajte drugim." 41 | notification: 42 | title: "vaša objava je bila označena kot rešitev" 43 | topic_statuses: 44 | solved: 45 | help: "Ta tema ima rešitev" 46 | search: 47 | advanced: 48 | statuses: 49 | solved: "so rešene" 50 | unsolved: "so nerešene" 51 | admin: 52 | web_hooks: 53 | solved_event: 54 | group_name: "Rešen dogodek" 55 | api: 56 | scopes: 57 | descriptions: 58 | solved: 59 | answer: Sprejmi/prekliči rešitev. 60 | discourse_automation: 61 | triggerables: 62 | first_accepted_solution: 63 | max_trust_level: 64 | tl1: < TL1 65 | tl2: < TL2 66 | tl3: < TL3 67 | tl4: < TL4 68 | any: Katerikoli 69 | fields: 70 | maximum_trust_level: 71 | label: Nivo zaupanja 72 | description: Uporabniki pod to ravnjo zaupanja bodo sprožili to avtomatizacijo. 73 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | solved: 10 | topic_status_filter: 11 | all: "të gjitha" 12 | discourse_automation: 13 | triggerables: 14 | first_accepted_solution: 15 | fields: 16 | maximum_trust_level: 17 | label: Niveli i besimit 18 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "prihvaćen" 13 | solved: 14 | topic_status_filter: 15 | all: "sve" 16 | discourse_automation: 17 | triggerables: 18 | first_accepted_solution: 19 | fields: 20 | maximum_trust_level: 21 | label: Nivo poverenja 22 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "accepterad" 13 | solutions: "Lösningar" 14 | solved: 15 | title: "Löst" 16 | allow_accepted_answers: "Låt ämnesägare och personal markera ett svar som lösningen" 17 | solved_topics_auto_close_hours: "Stäng automatiskt ämnet (n) timmar efter det senaste svaret när ämnet har markerats som löst." 18 | accept_answer: "Välj om det här svaret löser problemet" 19 | accepted_description: "Detta är den accepterade lösningen för detta ämne" 20 | has_no_accepted_answer: "Detta ämne har ingen lösning" 21 | unaccept_answer: "Avmarkera om detta svar inte längre löser problemet" 22 | accepted_answer: "Lösning" 23 | solution: "Lösning" 24 | solution_summary: 25 | one: "lösning" 26 | other: "lösningar" 27 | accepted_html: "%{icon} Löst av %{username} i inlägg #%{post_number}" 28 | accepted_notification: "%{username}%{description}
" 29 | topic_status_filter: 30 | all: "alla" 31 | solved: "lösta" 32 | unsolved: "olösta" 33 | no_solved_topics_title: "Du har inte löst några ämnen än" 34 | no_solved_topics_title_others: "%{username} har inte löst några ämnen ännu" 35 | no_solved_topics_body: "När du ger ett användbart svar på ett ämne kan ditt svar väljas som lösningen av ämnesägaren eller personalen." 36 | no_answer: 37 | title: Har din fråga besvarats? 38 | description: "Markera svaret och hjälp andra genom att använda lösningsknappen under det korrekta svaret." 39 | topic_statuses: 40 | solved: 41 | help: "Detta ämne har en lösning" 42 | search: 43 | advanced: 44 | statuses: 45 | solved: "är lösta" 46 | unsolved: "är olösta" 47 | admin: 48 | web_hooks: 49 | solved_event: 50 | group_name: "Löst händelse" 51 | discourse_automation: 52 | triggerables: 53 | first_accepted_solution: 54 | max_trust_level: 55 | tl1: < TL1 56 | tl2: < TL2 57 | tl3: < TL3 58 | tl4: < TL4 59 | any: Valfri 60 | fields: 61 | maximum_trust_level: 62 | label: Förtroendenivå 63 | description: Användare under denna förtroendenivå kommer att utlösa denna automatisering 64 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "imeruhusiwa" 13 | solved: 14 | title: "Imetatuliwa" 15 | allow_accepted_answers: "Ruhusu Muanzilishi wa Mada na Wasaidizi kuchagua jibu kama Suluhisho" 16 | accept_answer: "Chagua kama jibu limetatua tatizo" 17 | has_no_accepted_answer: "Hii mada haina suluhisho" 18 | unaccept_answer: "Ondoa chaguo kama jibu halijatatua tatizo" 19 | accepted_answer: "Jibu" 20 | solution: "Suluhisho" 21 | topic_status_filter: 22 | all: "Zote" 23 | solved: "imetatuliwa" 24 | topic_statuses: 25 | solved: 26 | help: "Hii mada ina suluhisho" 27 | discourse_automation: 28 | triggerables: 29 | first_accepted_solution: 30 | fields: 31 | maximum_trust_level: 32 | label: Kiwango cha Uaminifu 33 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "ఆమోదించబడిన" 13 | solved: 14 | topic_status_filter: 15 | all: "అన్నీ" 16 | discourse_automation: 17 | triggerables: 18 | first_accepted_solution: 19 | fields: 20 | maximum_trust_level: 21 | label: నమ్మకం స్థాయి 22 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "ยอมรับแล้ว" 13 | solved: 14 | title: "แก้ไขแล้ว" 15 | allow_accepted_answers: "อนุญาตให้เจ้าของกระทู้และทีมงานทำเครื่องหมายการตอบกลับว่าเป็นวิธีแก้ปัญหา" 16 | accept_answer: "เลือกถ้าการตอบกลับนี้ช่วยแก้ปัญหาได้" 17 | accepted_description: "นี่เป็นวิธีแก้ปัญหาที่ได้รับการยอมรับสำหรับหัวข้อนี้" 18 | has_no_accepted_answer: "หัวข้อนี้ไม่มีวิธีแก้ปัญหา" 19 | unaccept_answer: "ยกเลิกการเลือกหากการตอบกลับนี้ไม่สามารถแก้ปัญหาได้อีกต่อไป" 20 | accepted_answer: "วิธีแก้ปัญหา" 21 | solution: "วิธีแก้ปัญหา" 22 | solution_summary: 23 | other: "การแก้ปัญหา" 24 | topic_status_filter: 25 | all: "ทั้งหมด" 26 | solved: "แก้ไขแล้ว" 27 | discourse_automation: 28 | triggerables: 29 | first_accepted_solution: 30 | fields: 31 | maximum_trust_level: 32 | label: ระดับความไว้ใจ 33 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discourse Çözüldü" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "kabul edildi" 18 | solutions: "Çözümler" 19 | solved: 20 | title: "Çözüldü" 21 | allow_accepted_answers: "Konu sahibinin ve personelin bir yanıtı çözüm olarak işaretlemesine izin verin" 22 | solved_topics_auto_close_hours: "Konu çözüldü olarak işaretlendiğinde, son yanıttan (n) saat sonra otomatik olarak kapatılsın." 23 | accept_answer: "Bu yanıtın sorunu çözüp çözmediğini seçin" 24 | accepted_description: "Bu, bu konu için kabul edilen çözümdür" 25 | has_no_accepted_answer: "Bu konunun çözümü yok" 26 | unaccept_answer: "Bu yanıt artık sorunu çözmüyorsa seçimi kaldırın" 27 | accepted_answer: "Çözüm" 28 | solution: "Çözüm" 29 | solution_summary: 30 | one: "çözüm" 31 | other: "çözüm" 32 | accepted_html: "%{icon} Çözüldü; %{username} tarafından, %{post_number}. gönderide" 33 | accepted_notification: "%{username} %{description}
" 34 | topic_status_filter: 35 | all: "tümü" 36 | solved: "çözüldü" 37 | unsolved: "çözülmedi" 38 | no_solved_topics_title: "Henüz hiçbir konuyu çözmediniz" 39 | no_solved_topics_title_others: "%{username} henüz hiçbir konuyu çözmedi" 40 | no_solved_topics_body: "Bir konuya yararlı bir yanıt verdiğinizde, yanıtınız konu sahibi veya personel tarafından çözüm olarak seçilebilir." 41 | no_answer: 42 | title: Sorunuz yanıtlandı mı? 43 | description: "Yanıtı vurgulayın ve doğru cevabın altındaki çözüm düğmesini kullanarak başkalarına yardımcı olun." 44 | notification: 45 | title: "gönderiniz çözüm olarak işaretlendi" 46 | topic_statuses: 47 | solved: 48 | help: "Bu konunun bir çözümü var" 49 | search: 50 | advanced: 51 | statuses: 52 | solved: "çözüldü" 53 | unsolved: "çözülmedi" 54 | admin: 55 | web_hooks: 56 | solved_event: 57 | group_name: "Çözülmüş Olay" 58 | accepted_solution: "Bir kullanıcı bir gönderiyi kabul edilen yanıt olarak işaretlediğinde" 59 | unaccepted_solution: "Bir kullanıcı bir gönderiyi kabul edilmeyen yanıt olarak işaretlediğinde" 60 | api: 61 | scopes: 62 | descriptions: 63 | solved: 64 | answer: Bir çözümü kabul edin/kabulünü kaldırın. 65 | discourse_automation: 66 | triggerables: 67 | first_accepted_solution: 68 | max_trust_level: 69 | tl1: < GS1 70 | tl2: < GS2 71 | tl3: < GS3 72 | tl4: < GS4 73 | any: Herhangi biri 74 | fields: 75 | maximum_trust_level: 76 | label: Güven Seviyesi 77 | description: Bu Güven Seviyesi altındaki kullanıcılar bu otomasyonu tetikler 78 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "قوشۇلدى" 13 | solved: 14 | topic_status_filter: 15 | all: "ھەممىسى" 16 | discourse_automation: 17 | triggerables: 18 | first_accepted_solution: 19 | fields: 20 | maximum_trust_level: 21 | label: ئىشەنچ دەرىجىسى 22 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "схвалено" 13 | solved: 14 | title: "Вирішено" 15 | allow_accepted_answers: "Дозвольте власнику теми та персоналу позначити відповідь як рішення" 16 | solved_topics_auto_close_hours: "Автоматично закрити тему (n) годин після останньої відповіді, як тільки тема позначена як вирішена." 17 | accept_answer: "Виберіть, чи вирішує ця відповідь проблему" 18 | accepted_description: "Це прийняте рішення цієї теми" 19 | has_no_accepted_answer: "Ця тема не має рішення" 20 | unaccept_answer: "Зніміть вибір, якщо ця відповідь більше не вирішує проблему" 21 | accepted_answer: "Рішення" 22 | solution: "Рішення" 23 | solution_summary: 24 | one: "рішення" 25 | few: "рішення" 26 | many: "рішення" 27 | other: "рішення" 28 | topic_status_filter: 29 | all: "всі" 30 | solved: "вирішено" 31 | unsolved: "невирішено" 32 | topic_statuses: 33 | solved: 34 | help: "Ця тема має рішення" 35 | discourse_automation: 36 | triggerables: 37 | first_accepted_solution: 38 | fields: 39 | maximum_trust_level: 40 | label: Рівень довіри 41 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "منظور" 13 | solved: 14 | title: "حل شدہ" 15 | allow_accepted_answers: "ٹاپک کے مالک اور اسٹاف کو حل کے طور پر ایک جواب کو نشان زد کرنے کی اجازت دیں" 16 | solved_topics_auto_close_hours: "ایک بار ٹاپک کو حل شدہ کے طور پر نشان زد کر دیا جائے تو آخری جواب کے (ن) گھنٹوں بعد خود کار انداز سے ٹاپک بند کر دیا جائے." 17 | accept_answer: "منتخب کریں اگر یہ جواب مسئلہ کو حل کردیتا ہے" 18 | accepted_description: "یہ اِس ٹاپک کا قبول شدہ حل ہے" 19 | has_no_accepted_answer: "اِس ٹاپک کا کوئی حل نہیں ہے" 20 | unaccept_answer: "غیر منتخب کریں اگر یہ جواب اب مسئلہ کو حل نہیں کرتا" 21 | accepted_answer: "حل" 22 | solution: "حل" 23 | solution_summary: 24 | one: "حل" 25 | other: "حل" 26 | topic_status_filter: 27 | all: "تمام" 28 | solved: "حل شدہ" 29 | unsolved: "غیر حل شدہ" 30 | topic_statuses: 31 | solved: 32 | help: "اِس ٹاپک کا حل ہے" 33 | discourse_automation: 34 | triggerables: 35 | first_accepted_solution: 36 | fields: 37 | maximum_trust_level: 38 | label: ٹرسٹ لَیول 39 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "đã chấp nhận" 13 | solved: 14 | title: "Đã xử lý" 15 | topic_status_filter: 16 | all: "Tất cả" 17 | solved: "đã xử lý" 18 | discourse_automation: 19 | triggerables: 20 | first_accepted_solution: 21 | fields: 22 | maximum_trust_level: 23 | label: Bậc tin tưởng 24 | -------------------------------------------------------------------------------- /config/locales/client.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 | admin_js: 9 | admin: 10 | site_settings: 11 | categories: 12 | discourse_solved: "Discourse Solved" 13 | js: 14 | notifications: 15 | alt: 16 | solved: 17 | accepted_notification: "已被接受" 18 | solutions: "解决方案" 19 | solved: 20 | title: "已解决" 21 | allow_accepted_answers: "允许话题所有者和管理人员将回复标记为解决方案" 22 | solved_topics_auto_close_hours: "当话题被标记为已解决后,在最后回复 (n) 小时后自动关闭话题。" 23 | accept_answer: "如果此回复解决了问题,请选择" 24 | accepted_description: "这是此话题被接受的解决方案" 25 | has_no_accepted_answer: "此话题尚无解决方案" 26 | unaccept_answer: "如果此回复不再解决问题,请取消选择" 27 | accepted_answer: "解决方案" 28 | solution: "解决方案" 29 | solution_summary: 30 | other: "解决方案" 31 | accepted_html: "已由 %{username} 在帖子 #%{post_number} 中解决 %{icon}" 32 | accepted_notification: "%{username} %{description}
" 33 | topic_status_filter: 34 | all: "所有" 35 | solved: "已解决" 36 | unsolved: "未解决" 37 | no_solved_topics_title: "您尚未解决任何话题" 38 | no_solved_topics_title_others: "%{username} 还没有解决任何话题" 39 | no_solved_topics_body: "当您对某个话题提供有用的回复时,您的回复可能会被话题所有者或管理人员选为解决方案。" 40 | no_answer: 41 | title: 您的问题是否已被解答? 42 | description: "在正确回复下方使用解决方案按钮突出显示回答并帮助他人。" 43 | notification: 44 | title: "您的帖子已被标记为解决方案" 45 | topic_statuses: 46 | solved: 47 | help: "此话题已有解决方案" 48 | search: 49 | advanced: 50 | statuses: 51 | solved: "已被解决" 52 | unsolved: "尚未解决" 53 | admin: 54 | web_hooks: 55 | solved_event: 56 | group_name: "已解决的事件" 57 | accepted_solution: "当用户将帖子标记为被接受的回答时" 58 | unaccepted_solution: "当用户将帖子标记为遭拒的回答时" 59 | api: 60 | scopes: 61 | descriptions: 62 | solved: 63 | answer: 接受/不接受解决方案。 64 | discourse_automation: 65 | triggerables: 66 | first_accepted_solution: 67 | max_trust_level: 68 | tl1: 信任级别 < 1 69 | tl2: 信任级别 < 2 70 | tl3: 信任级别 < 3 71 | tl4: 信任级别 < 4 72 | any: 任何 73 | fields: 74 | maximum_trust_level: 75 | label: 信任级别 76 | description: 此信任级别下的用户将触发此自动化 77 | -------------------------------------------------------------------------------- /config/locales/client.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 | js: 9 | notifications: 10 | alt: 11 | solved: 12 | accepted_notification: "已接受" 13 | solutions: "解決方案" 14 | solved: 15 | title: "已解決" 16 | allow_accepted_answers: "允許主題擁有者與管理員標示回覆為解決方式" 17 | accept_answer: "選擇此回覆是否解決了問題" 18 | accepted_description: "這是本主題被接受的答案" 19 | has_no_accepted_answer: "這個主題沒有解決方式" 20 | unaccept_answer: "如果此回覆已不能解決問題,請取消選取" 21 | accepted_answer: "解決方式" 22 | solution: "解決方式" 23 | solution_summary: 24 | other: "解決方案" 25 | accepted_html: "%{icon} 解決了 通過 %{username} 在 貼文 #%{post_number}" 26 | accepted_notification: "%{username} %{description}
" 27 | topic_status_filter: 28 | all: "全部" 29 | solved: "已解決" 30 | unsolved: "未解答" 31 | no_solved_topics_title: "您還沒有解決過任何主題" 32 | no_solved_topics_title_others: "%{username} 尚未解決任何主題" 33 | no_solved_topics_body: "當您對某個主題提供有用的回覆時,主題擁有者或工作人員可能會選取您的回覆作為解決方案。" 34 | no_answer: 35 | title: 您的問題已得到解答嗎? 36 | description: "在正確的回覆下方按下解答按鈕以突顯解決方案來幫助他人。" 37 | topic_statuses: 38 | solved: 39 | help: "這個主題有解決方式" 40 | search: 41 | advanced: 42 | statuses: 43 | solved: "解決了" 44 | unsolved: "未解決的" 45 | admin: 46 | web_hooks: 47 | solved_event: 48 | group_name: "已解決的事件" 49 | discourse_automation: 50 | triggerables: 51 | first_accepted_solution: 52 | max_trust_level: 53 | tl1: < TL1 54 | tl2: < TL2 55 | tl3: < TL3 56 | tl4: < TL4 57 | any: 任何 58 | fields: 59 | maximum_trust_level: 60 | label: 信任等級 61 | description: 此信任等級下的使用者將觸發此自動化操作 62 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "дзень" 11 | yaxis: "агульны" 12 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Ден" 11 | yaxis: "Общо" 12 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Day" 11 | yaxis: "Suma" 12 | badges: 13 | solved_1: 14 | name: "Riješeno!" 15 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Activa el connector de resolts. Permet als usuaris seleccionar solucions per als temes." 10 | accept_all_solutions_trust_level: "Nivell mínim de confiança necessari per a acceptar solucions sobre qualsevol tema (encara que no sigui OP)" 11 | empty_box_on_unsolved: "Mostra un quadre buit al costat dels temes no resolts" 12 | solved_quote_length: "Nombre de caràcters que se citen quan es mostri la solució sota la primera publicació" 13 | show_filter_by_solved_status: "Mostra un menú desplegable per a filtrar una llista de temes per estat de resolució." 14 | reports: 15 | accepted_solutions: 16 | title: "Solucions acceptades" 17 | xaxis: "Dia" 18 | yaxis: "Total" 19 | solved: 20 | no_solutions: 21 | self: "Encara no teniu solucions acceptades." 22 | others: "No hi ha solucions acceptades." 23 | badges: 24 | solved_1: 25 | name: "Solucionat!" 26 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Dag" 11 | yaxis: "Total" 12 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Ενεργοποιήστε το πρόσθετο solved, επιτρέψτε στους χρήστες να επιλέγουν λύσεις για θέματα" 10 | accept_all_solutions_trust_level: "Απαιτείται ελάχιστο επίπεδο εμπιστοσύνης για αποδοχή λύσεων σε οποιοδήποτε θέμα (ακόμα και όταν δεν είναι OP)" 11 | empty_box_on_unsolved: "Εμφάνιση ενός κενού πλαισίου δίπλα στα άλυτα θέματα" 12 | solved_quote_length: "Αριθμός χαρακτήρων προς παράθεση κατά την εμφάνιση της λύσης στην πρώτη ανάρτηση" 13 | show_filter_by_solved_status: "Εμφάνιση μιας αναπτυσσόμενης λίστας για να φιλτράρετε μια λίστα θεμάτων κατά κατάσταση επίλυσης." 14 | reports: 15 | accepted_solutions: 16 | title: "Αποδεκτές λύσεις" 17 | xaxis: "Ημέρα" 18 | yaxis: "Σύνολο" 19 | solved: 20 | no_solutions: 21 | self: "Δεν έχετε αποδεχτεί ακόμη λύσεις." 22 | others: "Καμία αποδεκτή λύση." 23 | badges: 24 | solved_1: 25 | name: "Λύθηκε!" 26 | -------------------------------------------------------------------------------- /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.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 | reports: 9 | accepted_solutions: 10 | title: "Aktsepteeritud lahendused" 11 | xaxis: "Päev" 12 | yaxis: "Kokku" 13 | solved: 14 | no_solutions: 15 | others: "Aktsepteeritud lahendusi pole." 16 | badges: 17 | solved_1: 18 | name: "Lahendatud!" 19 | -------------------------------------------------------------------------------- /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 | accepted_answer: "پاسخ پذیرفته شد" 9 | site_settings: 10 | prioritize_solved_topics_in_search: "اولویتبندی موضوعات حل شده در نتایج جستجو" 11 | reports: 12 | accepted_solutions: 13 | title: "راه حل مورد قبول" 14 | xaxis: "روز" 15 | yaxis: "مجموع" 16 | solved: 17 | no_solutions: 18 | others: "هیچ راه حل پذیرفته ای وجود ندارد" 19 | badges: 20 | solved_1: 21 | name: "حل شده!" 22 | description: "یک پاسخ به عنوان راه حل علامت گذاری شده است" 23 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Día" 11 | yaxis: "Total" 12 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Dan" 11 | yaxis: "Ukupno" 12 | badges: 13 | solved_1: 14 | name: "Riješeno!" 15 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Միացնել լուծված պլագինը, թույլատրել օգտատերերին ընտրել լուծումներ թեմաների համար" 10 | accept_all_solutions_trust_level: "Վստահության նվազագույն մակարդակը, որը պահանջվում է ցանկացած թեմայի լուծումը ընդունելու համար (նույնիսկ եթե OP չէ)" 11 | empty_box_on_unsolved: "Ցուցադրել դատարկ արկղ չլուծված թեմաների կողքին" 12 | solved_quote_length: "Մեջբերվող սիմվոլների քանակը՝ առաջին գրառմնա տակ լուծումը ցուցադրելիս" 13 | show_filter_by_solved_status: "Ցուցադրել բացվող ցուցակ՝ թեմաների ցանկը ըստ լուծման կարգավիճակի ֆիլտրելու համար:" 14 | reports: 15 | accepted_solutions: 16 | title: "Ընդունված լուծումներ" 17 | xaxis: "Օր" 18 | yaxis: "Ամբողջ" 19 | solved: 20 | no_solutions: 21 | self: "Դուք դեռևս չունեք ընդունված լուծումներ:" 22 | others: "Ընդունված լուծումներ չկան" 23 | badges: 24 | solved_1: 25 | name: "Լուծված է!" 26 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Aktifkan plugin yang terpecahkan, inzinkan pengguna memilih solusi untuk topik" 10 | notify_on_staff_accept_solved: "Kirim notifikasi ke pembuat topik saat postingan ditandai sebagai solusi oleh staf." 11 | reports: 12 | accepted_solutions: 13 | title: "Solusi yang diterima" 14 | xaxis: "Hari" 15 | yaxis: "Jumlah" 16 | solved: 17 | no_solutions: 18 | self: "Anda belum memiliki solusi yang diterima." 19 | others: "Tidak ada solusi yang diterima." 20 | -------------------------------------------------------------------------------- /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 | accepted_answer: "受け入れられた回答" 9 | site_settings: 10 | solved_enabled: "解決済みプラグインを有効にし、ユーザーがトピックの解決策を選択できるようにする" 11 | allow_solved_on_all_topics: "すべてのトピックでユーザーが解決策を選択することを許可する(オフの場合、解決策はカテゴリごとまたはタグごとに有効にできます)" 12 | accept_all_solutions_trust_level: "すべてのトピックで解決策を受け入れるために必要な最低信頼レベル(OP でない場合でも)" 13 | accept_all_solutions_allowed_groups: "(OP でない場合でも) すべてのトピックで解決策を受け入れることが許可されているグループ。管理者とモデレーターは常に許可されます。" 14 | empty_box_on_unsolved: "未解決のトピックの横に空のボックスを表示する" 15 | solved_quote_length: "最初の投稿の下に解決策を表示する場合に引用する文字数" 16 | solved_topics_auto_close_hours: "トピックが解決済みとマークされたら、最後の返信から(n)時間後にトピックを自動クローズします。自動クローズを無効にする場合は 0 に設定します。" 17 | show_filter_by_solved_status: "ドロップダウンを表示し、解決済みのステータスでトピックリストをフィルターします。" 18 | notify_on_staff_accept_solved: "スタッフによって投稿が解決策としてマークされたら、トピック作成者に通知を送信します。" 19 | ignore_solved_topics_in_assigned_reminder: "割り当てのリマインダーに解決済みのトピックが含まれないようにします。discourse-assign プラグインを使用している場合にのみ関連します。" 20 | assignment_status_on_solve: "トピックが解決したら、すべての割り当てをこのステータスに更新する" 21 | assignment_status_on_unsolve: "トピックが未解決の場合、すべての割り当てを子のステータスに更新する" 22 | disable_solved_education_message: "解決済みトピックの教育メッセージを無効にします。" 23 | accept_solutions_topic_author: "トピック作成者が解決策を受け入れることを許可します。" 24 | solved_add_schema_markup: "QAPage スキーママークアップを HTML に追加します。" 25 | enable_solved_tags: "ユーザーが解決策を選択できるようにするタグ。" 26 | prioritize_solved_topics_in_search: "検索結果では解決済みのトピックが優先されます。" 27 | keywords: 28 | accept_all_solutions_allowed_groups: "accept_all_solutions_trust_level" 29 | reports: 30 | accepted_solutions: 31 | title: "受け入れられた解決策" 32 | xaxis: "日" 33 | yaxis: "合計" 34 | solved: 35 | no_solutions: 36 | self: "受け入れられた解決策はありません。" 37 | others: "受け入れられた解決策はありません。" 38 | badges: 39 | solved_1: 40 | name: "解決済み!" 41 | description: "1 件の返信が解決策としてマークされた" 42 | long_description: "このバッジは返信がトピックの解決策としてマークされた場合に付与されます。:white_check_mark: よくできました。:+1:" 43 | solved_2: 44 | name: "ガイダンスカウンセラー" 45 | description: "10 件の返信が解決策としてマークされた" 46 | long_description: "このバッジは、10 件の返信がトピックの解決策としてマークされた場合に付与されます。:white_check_mark: あなたは他のコミュニティーメンバーにとって貴重なアセットです。" 47 | solved_3: 48 | name: "なんでも知ってる" 49 | description: "50 件の返信が解決策としてマークされた" 50 | long_description: "このバッジは、50 件の返信がトピックの解決策としてマークされた場合に付与されます。:white_check_mark: あなたは本当に物知りです。:clap:" 51 | solved_4: 52 | name: "解決策の宝庫" 53 | description: "150 件の返信が解決策としてマークされた" 54 | long_description: "このバッジは、150 件の返信がトピックの解決策としてマークされた場合に付与されます。:white_check_mark: 優秀です。:slightly_smiling_face: あなたは誰もが認める解決策の宝庫です。:brain:" 55 | discourse_automation: 56 | triggerables: 57 | first_accepted_solution: 58 | title: 最初に受け入れられた解決策 59 | doc: ユーザーの解決策が初めて受け入れられたときにトリガーされます。 60 | education: 61 | topic_is_solved: | 62 | ### このトピックは解決しました 63 | 64 | 以下に該当する場合にのみここに返信してください: 65 | 66 | - 追加の詳細情報がある場合 67 | 68 | - 解決策が機能しなかった場合 69 | 70 | 無関係の問題については、[新しいトピックを開始](%{base_url}/new-topic)してください。 71 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "해결 된 플러그인 사용, 사용자가 주제에 대한 솔루션을 선택할 수 있도록 허용" 10 | accept_all_solutions_trust_level: "모든 주제에 대한 솔루션을 수락하는 데 필요한 최소 신뢰 수준 (OP가 아닌 경우에도)" 11 | empty_box_on_unsolved: "해결되지 않은 주제 옆에 빈 상자 표시" 12 | solved_quote_length: "첫 번째 게시물 아래에 솔루션을 표시 할 때 인용 할 문자 수" 13 | show_filter_by_solved_status: "해결 된 상태별로 주제 목록을 필터링하는 드롭 다운을 표시하십시오." 14 | reports: 15 | accepted_solutions: 16 | title: "허용되는 솔루션" 17 | xaxis: "일" 18 | yaxis: "총" 19 | solved: 20 | no_solutions: 21 | self: "아직 수락 된 솔루션이 없습니다." 22 | others: "허용되는 솔루션이 없습니다." 23 | badges: 24 | solved_1: 25 | name: "해결됨!" 26 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Diena" 11 | yaxis: "Viso" 12 | badges: 13 | solved_1: 14 | name: "Išspresta!" 15 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Diena" 11 | yaxis: "Kopā" 12 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Skru på løsnings-programtillegget, tillat brukere å velge løsninger på emner" 10 | accept_all_solutions_trust_level: "Laveste tilltisnivå som kreves for å godta løsninger for ethvert emne (selv når du ikke er OP)" 11 | empty_box_on_unsolved: "Vis en tom boks ved siden av uløste emner" 12 | solved_quote_length: "Antall tegn som skal siteres når løsning vises under første innlegg" 13 | reports: 14 | accepted_solutions: 15 | title: "Godtatte løsninger" 16 | xaxis: "Dag" 17 | yaxis: "Totalt" 18 | solved: 19 | no_solutions: 20 | self: "Du har ingen godtatte løsninger enda." 21 | others: "Ingen godtatte løsninger." 22 | badges: 23 | solved_1: 24 | name: "Løst!" 25 | -------------------------------------------------------------------------------- /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 | solved_enabled: "Activar o plugin \"Solução\", permitindo aos utilizadores seleccionar soluções para tópicos" 10 | accept_all_solutions_trust_level: "Nível de confiança mínimo necessário para aceitar soluções em qualquer tópico (mesmo quando não é o autor do tópico)" 11 | empty_box_on_unsolved: "Mostrar uma caixa vazia junto a tópicos não soluccionados" 12 | solved_quote_length: "O número de carateres a serem citados quando exibir a solução debaixo da primeira mensagem" 13 | reports: 14 | accepted_solutions: 15 | title: "Soluções aceites" 16 | xaxis: "Dia" 17 | yaxis: "Total" 18 | solved: 19 | no_solutions: 20 | self: "Ainda não tem soluções aceites." 21 | others: "Sem soluções aceites." 22 | badges: 23 | solved_1: 24 | name: "Resolvido!" 25 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Activează extensia \"Rezolvat\", permite utilizatorilor să selecteze soluții pentru subiecte" 10 | accept_all_solutions_trust_level: "Nivelul minim de încredere necesar pentru a accepta soluții la fiecare subiect (chiar dacă nu este OP)." 11 | empty_box_on_unsolved: "Afișați o casetă goală lângă subiectele nerezolvate" 12 | solved_quote_length: "Numărul de caractere de citat când se afișează soluția sub prima postare" 13 | show_filter_by_solved_status: "Afișați o listă verticală pentru a filtra o listă de subiecte după criteriul rezolvat/nerezolvat." 14 | reports: 15 | accepted_solutions: 16 | title: "Soluții acceptate" 17 | xaxis: "Zi" 18 | yaxis: "Total" 19 | solved: 20 | no_solutions: 21 | self: "Încă nu aveți soluții acceptate" 22 | others: "Nu există soluții acceptate" 23 | badges: 24 | solved_1: 25 | name: "Rezolvat!" 26 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | title: "Prijaté riešenia" 11 | xaxis: "Deň" 12 | yaxis: "Celkom" 13 | solved: 14 | no_solutions: 15 | self: "Nemáte zatiaľ prijaté žiadne riešenie." 16 | others: "Žiadne prijaté riešenia." 17 | -------------------------------------------------------------------------------- /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 | accepted_answer: "Sprejet odgovor" 9 | site_settings: 10 | solved_enabled: "Omogočite vtičnik za rešitve, omogočite uporabnikom, da izberejo rešitve za teme" 11 | allow_solved_on_all_topics: "Uporabnikom omogočite izbiro rešitev za vse teme (če ta možnost ni označena, je mogoče omogočiti rešitve za posamezno kategorijo ali oznako)." 12 | accept_all_solutions_trust_level: "Minimalna raven zaupanja, potrebna za sprejemanje rešitev za katero koli temo (tudi če ni avtor teme)" 13 | empty_box_on_unsolved: "Prikaz praznega polja ob nerešenih temah" 14 | solved_quote_length: "Število citiranih znakov rešitve pod prvo objavo" 15 | solved_topics_auto_close_hours: "Samodejno zapiranje teme (n) ur po zadnjem odgovoru, ko je tema označena kot rešena. Vnesi 0, da preprečiš samodejno zapiranje." 16 | show_filter_by_solved_status: "Prikažite spustni seznam, če želite filtrirati seznam tem po rešenem stanju." 17 | notify_on_staff_accept_solved: "Pošlji obvestilo ustvarjalcu teme, ko osebje objavo označi kot rešitev." 18 | disable_solved_education_message: "Onemogočite izobraževalno sporočilo za rešene teme." 19 | accept_solutions_topic_author: "Avtorju teme dovolite, da sprejme rešitev." 20 | solved_add_schema_markup: "Dodajanje oznak sheme QAPage v HTML." 21 | enable_solved_tags: "Oznake, ki bodo uporabnikom omogočile izbiro rešitev." 22 | reports: 23 | accepted_solutions: 24 | title: "Sprejete rešitve" 25 | xaxis: "Dan" 26 | yaxis: "Skupaj" 27 | solved: 28 | no_solutions: 29 | self: "Zaenkrat nimate sprejetih rešitev." 30 | others: "Ni sprejetih rešitev." 31 | badges: 32 | solved_1: 33 | name: "Rešeno!" 34 | description: "Odgovor je označen kot rešitev" 35 | long_description: "Ta značka se podeli, če je odgovor označen kot rešitev za temo. :white_check_mark: Vse pohvale. :+1:" 36 | solved_2: 37 | name: "Strokovni svetovalec" 38 | description: "Imate 10 odgovorov, označenih kot Rešitve" 39 | long_description: "Ta značka se podeli, če je 10 vaših odgovorov označenih kot rešitev teme. :white_check_mark: Ste pravi zaklad za člane vaše skupnosti." 40 | solved_3: 41 | name: "Vseved" 42 | description: "Imate 50 odgovorov, označenih kot Rešitve" 43 | long_description: "Ta značka se podeli, če je 50 vaših odgovorov označenih kot rešitev teme. :white_check_mark: Ste resnični poznavalec. :clap:" 44 | solved_4: 45 | name: "Tovarna rešitev" 46 | description: "Imate 150 odgovorov, označenih kot Rešitve" 47 | long_description: "Ta značka je podeljena za 150 odgovorov, označenih kot Rešitve za teme. :white_check_mark: Odlično opravljeno. :slightly_smiling_face: Ste prava tovarna rešitev. :brain:" 48 | discourse_automation: 49 | triggerables: 50 | first_accepted_solution: 51 | title: Prva sprejeta rešitev 52 | doc: Sproži se prvič, ko je uporabnikov objava sprejeta kot rešitev. 53 | education: 54 | topic_is_solved: | 55 | ### Ta tema je bila rešena 56 | 57 | Tukaj odgovorite le, če: 58 | 59 | - imate dodatne podrobnosti 60 | 61 | - rešitev za vas ne deluje. 62 | 63 | Če imate nepovezano težavo, raje [odprite novo temo](%{base_url}/new-topic). 64 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Day" 11 | yaxis: "Total" 12 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | yaxis: "Ukupno" 11 | -------------------------------------------------------------------------------- /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 | accepted_answer: "Godkänt svar" 9 | site_settings: 10 | solved_enabled: "Aktivera tillägg för lösningar, låt användare välja lösningar för ämnen" 11 | allow_solved_on_all_topics: "Tillåt användare att välja lösningar för alla ämnen (om detta avmarkeras kan lösningar aktiveras per kategori eller tagg)" 12 | accept_all_solutions_trust_level: "Lägsta förtroendenivå som krävs för att godkänna lösningar för endera ämne (ursprunglig skribent eller ej)" 13 | empty_box_on_unsolved: "Visa en tom ruta bredvid olösta ämnen" 14 | solved_quote_length: "Antal tecken att citera när lösningen visas under det första inlägget" 15 | solved_topics_auto_close_hours: "Stäng automatiskt ämnet (n) timmar efter det senaste svaret när ämnet har markerats som löst. Ange 0 för att inaktivera automatisk stängning." 16 | show_filter_by_solved_status: "Visa en rullgardinsmeny för att filtrera en ämneslista efter löst status." 17 | notify_on_staff_accept_solved: "Meddela ämnesskaparen när personal markerar ett inlägg som en lösning." 18 | disable_solved_education_message: "Inaktivera utbildningsmeddelande för lösta ämnen." 19 | accept_solutions_topic_author: "Låt ämnets skapare godkänna en lösning." 20 | solved_add_schema_markup: "Lägg till QAPage-schemakod i HTML." 21 | enable_solved_tags: "Taggar som gör det möjligt för användare att välja lösningar." 22 | reports: 23 | accepted_solutions: 24 | title: "Godkända lösningar" 25 | xaxis: "Dag" 26 | yaxis: "Totalt" 27 | solved: 28 | no_solutions: 29 | self: "Du har inga godkända lösningar än." 30 | others: "Inga godkända lösningar." 31 | badges: 32 | solved_1: 33 | name: "Löst!" 34 | discourse_automation: 35 | triggerables: 36 | first_accepted_solution: 37 | title: Första accepterade lösningen 38 | doc: Utlöses när en användare fick en lösning accepterad för första gången. 39 | education: 40 | topic_is_solved: | 41 | ### Detta ämne har lösts 42 | 43 | Svara bara här om: 44 | 45 | - Du har ytterligare information 46 | 47 | - Lösningen inte fungerar för dig 48 | 49 | Om du har ett orelaterat problem ber vi dig [starta ett nytt ämne](%{base_url}/new-topic) istället. 50 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Ruhusu plugin ya suluhisho, waruhusu watumiaji wachague suluhisho ya mada" 10 | accept_all_solutions_trust_level: "Kiwango cha chini cha uaminifu kinachohitajika kukubali suluhisho kwenye mada zozote (hata kama sio OP)" 11 | empty_box_on_unsolved: "Onyesha boxi lililowazi pembeni ya mada ambazo hazijatatuliwa" 12 | solved_quote_length: "Namba za herufi kunuku wakati wa kuonyesha suluhisho chini ya taarifa za kwanza." 13 | reports: 14 | accepted_solutions: 15 | title: "Majibu yaliyokubaliwa" 16 | xaxis: "Siku" 17 | yaxis: "Jumla" 18 | solved: 19 | no_solutions: 20 | self: "Bado hauna majibu yaliyokubaliwa." 21 | others: "Hakuna majibu yaliyokubaliwa." 22 | badges: 23 | solved_1: 24 | name: "Imetatuliwa!" 25 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "రోజు" 11 | yaxis: "మొత్తం" 12 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | yaxis: "ทั้งหมด" 11 | badges: 12 | solved_1: 13 | name: "แก้ไขแล้ว!" 14 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "كۈن" 11 | yaxis: "جەمئىي" 12 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "Увімкнути плагін рішення, дозволити користувачам обирати рішення для тем" 10 | accept_all_solutions_trust_level: "Мінімальний рівень довіри, необхідний для прийняття рішень в будь-якій темі (навіть коли це не ОП)" 11 | empty_box_on_unsolved: "Показуйте порожнє поле поруч із невирішеними темами" 12 | solved_quote_length: "Кількість символів, які потрібно цитувати під час відображення рішення під першим дописом" 13 | show_filter_by_solved_status: "Показати випадаюче меню для фільтрації списку тем зі статусом вирішених." 14 | reports: 15 | accepted_solutions: 16 | title: "Прийняті рішення" 17 | xaxis: "День" 18 | yaxis: "Всього" 19 | solved: 20 | no_solutions: 21 | self: "У вас ще немає прийнятих рішень." 22 | others: "Немає прийнятих рішень." 23 | badges: 24 | solved_1: 25 | name: "Вирішено!" 26 | -------------------------------------------------------------------------------- /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 | site_settings: 9 | solved_enabled: "سَولوڈ پلگ اِن فعال کریں، صارفین کو ٹاپک کے حل منتخب کرنے کی اجازت دیں" 10 | accept_all_solutions_trust_level: "کسی بھی ٹاپک پر سے حل کا انتخاب کرنے کے لئے کم از کم ٹرسٹ لَیول (یہاں تک کہ جب اصل پوسٹر بھی نہ ہوں)" 11 | empty_box_on_unsolved: "غیر حل شدہ ٹاپکس کے ساتھ خالی باکس دکھائیں" 12 | solved_quote_length: "پہلی پوسٹ کے نیچے حل کو ظاہر کرنے کیلئے حروف کی تعداد" 13 | show_filter_by_solved_status: "حل شدہ ہونے کی بنیاد پر ٹاپک فہرست کو فلٹر کرنے کیلئے ڈراپ ڈاؤن دکھائیں۔" 14 | reports: 15 | accepted_solutions: 16 | title: "قبول شدہ حل" 17 | xaxis: "دن" 18 | yaxis: "کُل" 19 | solved: 20 | no_solutions: 21 | self: "ابھی تک آپ کے کوئیقبول شدہ حل نہیں ہیں۔" 22 | others: "کوئی قبول شدہ حل نہیں" 23 | badges: 24 | solved_1: 25 | name: "حل شدہ!" 26 | -------------------------------------------------------------------------------- /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 | reports: 9 | accepted_solutions: 10 | xaxis: "Ngày" 11 | yaxis: "Tổng số" 12 | badges: 13 | solved_1: 14 | name: "Đã xử lý!" 15 | -------------------------------------------------------------------------------- /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 | accepted_answer: "已被接受的回答" 9 | site_settings: 10 | solved_enabled: "启用“已解决”插件,允许用户为话题选择解决方案" 11 | allow_solved_on_all_topics: "允许用户选择所有话题的解决方案(未选中时,可以按类别或标签启用解决方案)" 12 | accept_all_solutions_trust_level: "在任何话题中接受解决方案所需的最小信任级别(即使不是 OP)" 13 | accept_all_solutions_allowed_groups: "能够接受任何话题解决方案的群组(即使不是 OP 时)。管理员和版主始终可以。" 14 | empty_box_on_unsolved: "在未解决的话题旁显示一个空框" 15 | solved_quote_length: "在第一个帖子下显示解决方案时引用的字符数量" 16 | solved_topics_auto_close_hours: "当话题被标记为已解决后,在最后一条回复 (n) 小时后自动关闭话题。设置为 0 将禁用自动关闭。" 17 | show_filter_by_solved_status: "显示下拉列表以按已解决状态筛选话题列表。" 18 | notify_on_staff_accept_solved: "当帖子被管理人员标记为解决方案时,向话题创建者发送通知" 19 | ignore_solved_topics_in_assigned_reminder: "防止指定的提醒中包含已解决的话题。仅在使用 discourse-assign 插件时相关。" 20 | assignment_status_on_solve: "当一个话题被解决时,将所有指定更新为此状态" 21 | assignment_status_on_unsolve: "当一个话题被取消解决时,将所有指定更新为此状态" 22 | disable_solved_education_message: "禁用已解决话题的教育消息。" 23 | accept_solutions_topic_author: "允许话题作者接受解决方案。" 24 | solved_add_schema_markup: "将 QAPage 架构标记添加到 HTML。" 25 | enable_solved_tags: "允许用户选择解决方案的标签。" 26 | prioritize_solved_topics_in_search: "在搜索结果中优先显示已解决的话题。" 27 | keywords: 28 | accept_all_solutions_allowed_groups: "accept_all_solutions_trust_level" 29 | reports: 30 | accepted_solutions: 31 | title: "已被接受的解决方案" 32 | xaxis: "天" 33 | yaxis: "总计" 34 | solved: 35 | no_solutions: 36 | self: "您还没有被接受的解决方案。" 37 | others: "没有被接受的解决方案。" 38 | badges: 39 | solved_1: 40 | name: "已解决!" 41 | description: "有 1 条回复被标记为解决方案" 42 | long_description: "此徽章是由于 1 条回复被标记为话题的解决方案而获得的。:white_check_mark: 干得好。:+1:" 43 | solved_2: 44 | name: "指导顾问" 45 | description: "有 10 条回复被标记为解决方案" 46 | long_description: "此徽章是由于 10 条回复被标记为话题的解决方案而获得的。:white_check_mark: 您对社区成员来说是真正的财富。" 47 | solved_3: 48 | name: "无所不知" 49 | description: "有 50 条回复被标记为解决方案" 50 | long_description: "此徽章是由于 50 条回复被标记为话题的解决方案而获得的。:white_check_mark: 您真的很懂行。:clap:" 51 | solved_4: 52 | name: "解决方案机构" 53 | description: "有 150 条回复被标记为解决方案" 54 | long_description: "此徽章是由于 150 条回复被标记为话题的解决方案而获得的。:white_check_mark: 太棒了。:slightly_smiling_face: 您正式成为一个解决方案机构。:brain:" 55 | discourse_automation: 56 | triggerables: 57 | first_accepted_solution: 58 | title: 第一个被接受的解决方案 59 | doc: 当用户的解决方案首次被接受时触发。 60 | education: 61 | topic_is_solved: | 62 | ### 此话题已被解决 63 | 64 | 仅在以下情况下回复: 65 | 66 | - 您有其他详细信息 67 | 68 | - 该解决方案对您不起作用 69 | 70 | 如果您有不相关的问题,请[新建话题](%{base_url}/new-topic)。 71 | -------------------------------------------------------------------------------- /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 | accepted_answer: "接受的答案" 9 | site_settings: 10 | solved_enabled: "啟用已解決的插件,允許用戶為主題選擇解決方案" 11 | reports: 12 | accepted_solutions: 13 | title: "接受的解決方案" 14 | xaxis: "天" 15 | yaxis: "總計" 16 | badges: 17 | solved_1: 18 | name: "已解決!" 19 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | DiscourseSolved::Engine.routes.draw do 4 | post "/accept" => "answer#accept" 5 | post "/unaccept" => "answer#unaccept" 6 | end 7 | 8 | Discourse::Application.routes.draw { mount ::DiscourseSolved::Engine, at: "solution" } 9 | -------------------------------------------------------------------------------- /config/settings.yml: -------------------------------------------------------------------------------- 1 | discourse_solved: 2 | solved_enabled: 3 | default: true 4 | client: true 5 | show_who_marked_solved: 6 | default: false 7 | client: true 8 | allow_solved_on_all_topics: 9 | default: false 10 | client: true 11 | accept_all_solutions_trust_level: 12 | default: 4 13 | client: true 14 | enum: "TrustLevelSetting" 15 | hidden: true 16 | accept_all_solutions_allowed_groups: 17 | default: "1|2|14" # auto group admin, moderators and trust_level_4 18 | mandatory_values: "1|2" # auto group admins, moderators 19 | type: group_list 20 | client: false 21 | allow_any: false 22 | refresh: true 23 | validator: "AtLeastOneGroupValidator" 24 | empty_box_on_unsolved: 25 | default: false 26 | client: true 27 | solved_quote_length: 28 | default: 300 29 | client: false 30 | solved_topics_auto_close_hours: 31 | default: 0 32 | min: 0 33 | max: 175200 # 20 years 34 | show_filter_by_solved_status: 35 | default: false 36 | client: true 37 | notify_on_staff_accept_solved: 38 | default: false 39 | ignore_solved_topics_in_assigned_reminder: 40 | default: false 41 | assignment_status_on_solve: 42 | type: string 43 | default: "" 44 | assignment_status_on_unsolve: 45 | type: string 46 | default: "" 47 | disable_solved_education_message: 48 | default: false 49 | accept_solutions_topic_author: 50 | default: true 51 | solved_add_schema_markup: 52 | type: enum 53 | default: "always" 54 | choices: 55 | - "never" 56 | - "always" 57 | - "answered only" 58 | prioritize_solved_topics_in_search: false 59 | enable_solved_tags: 60 | type: tag_list 61 | default: "" 62 | 63 | -------------------------------------------------------------------------------- /db/fixtures/001_badges.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | first_solution_query = <<~SQL 4 | SELECT post_id, user_id, created_at AS granted_at 5 | FROM ( 6 | SELECT p.id AS post_id, p.user_id, dsst.created_at, 7 | ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY dsst.created_at) AS row_number 8 | FROM discourse_solved_solved_topics dsst 9 | JOIN badge_posts p ON dsst.answer_post_id = p.id 10 | JOIN topics t ON p.topic_id = t.id 11 | WHERE p.user_id <> t.user_id -- ignore topics solved by OP 12 | AND (:backfill OR p.id IN (:post_ids)) 13 | ) x 14 | WHERE row_number = 1 15 | SQL 16 | 17 | Badge.seed(:name) do |badge| 18 | badge.name = "Solved 1" 19 | badge.default_icon = "square-check" 20 | badge.badge_type_id = BadgeType::Bronze 21 | badge.default_badge_grouping_id = BadgeGrouping::Community 22 | badge.query = first_solution_query 23 | badge.listable = true 24 | badge.target_posts = true 25 | badge.default_enabled = false 26 | badge.trigger = Badge::Trigger::PostRevision 27 | badge.auto_revoke = true 28 | badge.show_posts = true 29 | badge.system = true 30 | end 31 | 32 | def solved_query_with_count(min_count) 33 | <<~SQL 34 | SELECT p.user_id, MAX(dsst.created_at) AS granted_at 35 | FROM discourse_solved_solved_topics dsst 36 | JOIN badge_posts p ON dsst.answer_post_id = p.id 37 | JOIN topics t ON p.topic_id = t.id 38 | WHERE p.user_id <> t.user_id -- ignore topics solved by OP 39 | AND (:backfill OR p.id IN (:post_ids)) 40 | GROUP BY p.user_id 41 | HAVING COUNT(*) >= #{min_count} 42 | SQL 43 | end 44 | 45 | [ 46 | ["Solved 2", BadgeType::Silver, 10], 47 | ["Solved 3", BadgeType::Gold, 50], 48 | ["Solved 4", BadgeType::Gold, 150], 49 | ].each do |name, level, count| 50 | Badge.seed(:name) do |badge| 51 | badge.name = name 52 | badge.default_icon = "square-check" 53 | badge.badge_type_id = level 54 | badge.default_badge_grouping_id = BadgeGrouping::Community 55 | badge.query = solved_query_with_count(count) 56 | badge.listable = true 57 | badge.default_allow_title = true 58 | badge.target_posts = false 59 | badge.default_enabled = false 60 | badge.trigger = Badge::Trigger::PostRevision 61 | badge.auto_revoke = true 62 | badge.show_posts = false 63 | badge.system = true 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /db/migrate/20191209095548_ensures_unique_accepted_answer_post_id.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EnsuresUniqueAcceptedAnswerPostId < ActiveRecord::Migration[5.2] 4 | def change 5 | execute <<~SQL 6 | DELETE FROM topic_custom_fields AS tcf1 7 | USING topic_custom_fields AS tcf2 8 | WHERE tcf1.id > tcf2.id AND 9 | tcf1.topic_id = tcf2.topic_id AND 10 | tcf1.name = tcf2.name AND 11 | tcf1.name = 'accepted_answer_post_id' 12 | SQL 13 | 14 | add_index :topic_custom_fields, 15 | :topic_id, 16 | name: :idx_topic_custom_fields_accepted_answer, 17 | unique: true, 18 | where: "name = 'accepted_answer_post_id'" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20210218022053_solved_fix_high_auto_close_topic_hours.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class SolvedFixHighAutoCloseTopicHours < ActiveRecord::Migration[6.0] 3 | def up 4 | execute <<-SQL 5 | UPDATE site_settings 6 | SET value = '175000' 7 | WHERE name = 'solved_topics_auto_close_hours' 8 | AND CAST(value AS INT) > 175000 9 | SQL 10 | end 11 | 12 | def down 13 | raise ActiveRecord::IrreversibleMigration 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20210429154322_remove_nil_custom_fields_from_solved.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RemoveNilCustomFieldsFromSolved < ActiveRecord::Migration[6.0] 4 | def up 5 | execute <<~SQL 6 | DELETE FROM post_custom_fields 7 | WHERE name = 'is_accepted_answer' AND value IS NULL 8 | SQL 9 | 10 | execute <<~SQL 11 | DELETE FROM topic_custom_fields 12 | WHERE name = 'accepted_answer_post_id' AND value IS NULL 13 | SQL 14 | 15 | execute <<~SQL 16 | DELETE FROM topic_custom_fields 17 | WHERE name = 'solved_auto_close_topic_timer_id' AND value IS NULL 18 | SQL 19 | end 20 | 21 | def down 22 | raise ActiveRecord::IrreversibleMigration 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /db/migrate/20210618142654_recreate_solutions_column.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RecreateSolutionsColumn < ActiveRecord::Migration[6.1] 4 | def up 5 | if !ActiveRecord::Base.connection.column_exists?(:directory_items, :solutions) 6 | # A reverted commit had added this column previously so some sites have this 7 | # column, and some so not. Only add if the DB doesn't already have it. 8 | add_column :directory_items, :solutions, :integer, default: 0 9 | end 10 | end 11 | 12 | def down 13 | remove_column :directory_items, :solutions 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20221121223417_rename_badges.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RenameBadges < ActiveRecord::Migration[6.1] 4 | HELPDESK_TRANSLATIONS = { 5 | "ar" => "مكتب المساعدة", 6 | "el" => "Γραφείο βοήθειας", 7 | "es" => "Servicio de ayuda", 8 | "fi" => "Neuvonta", 9 | "fr" => "Service d'assistance", 10 | "he" => "דלפק עזרה", 11 | "hu" => "Ügyfélszolgálat", 12 | "ja" => "ヘルプデスク", 13 | "ko" => "안내 데스크", 14 | "pl_PL" => "Dział pomocy technicznej", 15 | "ro" => "Ajutor", 16 | "ru" => "Служба поддержки", 17 | "sl" => "Služba za pomoč", 18 | "sv" => "Kundtjänst", 19 | "tr_TR" => "Yardım masası", 20 | "zh_CN" => "帮助台", 21 | "zh_TW" => "服務台", 22 | } 23 | 24 | TECH_SUPPORT_TRANSLATIONS = { 25 | "ar" => "الدعم الفني", 26 | "de" => "Technischer Support", 27 | "el" => "Τεχνική υποστήριξη", 28 | "es" => "Asistencia técnica", 29 | "fi" => "Tukipalvelu", 30 | "fr" => "Assistance technique", 31 | "he" => "תמיכה טכנית", 32 | "hu" => "Műszaki támogatás", 33 | "id" => "Dukungan Teknis", 34 | "it" => "Supporto Tecnico", 35 | "ja" => "技術サポート", 36 | "ko" => "기술 지원", 37 | "nl" => "Technische ondersteuning", 38 | "pl_PL" => "Wsparcie techniczne", 39 | "pt_BR" => "Suporte Técnico", 40 | "ro" => "Asistenţă tehnică", 41 | "ru" => "Техническая поддержка", 42 | "sl" => "Tehnična podpora", 43 | "sv" => "Teknisk support", 44 | "tr_TR" => "Teknik Destek", 45 | "zh_CN" => "技术支持", 46 | "zh_TW" => "技術支援", 47 | } 48 | 49 | def up 50 | default_locale = 51 | DB.query_single("SELECT value FROM site_settings WHERE name = 'default_locale'").first || "en" 52 | 53 | sql = <<~SQL 54 | UPDATE badges 55 | SET name = :new_name, 56 | description = NULL, 57 | long_description = NULL 58 | WHERE name = :old_name 59 | SQL 60 | 61 | badge_name = HELPDESK_TRANSLATIONS.fetch(default_locale, "Helpdesk") 62 | DB.exec(sql, old_name: badge_name, new_name: "Solved 1") 63 | 64 | badge_name = TECH_SUPPORT_TRANSLATIONS.fetch(default_locale, "Tech Support") 65 | DB.exec(sql, old_name: badge_name, new_name: "Solved 2") 66 | end 67 | 68 | def down 69 | raise ActiveRecord::IrreversibleMigration 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /db/migrate/20240116100023_fill_accept_all_solutions_allowed_groups_based_on_deprecated_setting.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class FillAcceptAllSolutionsAllowedGroupsBasedOnDeprecatedSetting < ActiveRecord::Migration[7.0] 4 | def up 5 | old_setting_trust_level = 6 | DB.query_single( 7 | "SELECT value FROM site_settings WHERE name = 'accept_all_solutions_trust_level' LIMIT 1", 8 | ).first 9 | 10 | if old_setting_trust_level.present? 11 | allowed_groups = "1#{old_setting_trust_level}" 12 | 13 | DB.exec( 14 | "INSERT INTO site_settings(name, value, data_type, created_at, updated_at) 15 | VALUES('accept_all_solutions_allowed_groups', :setting, '20', NOW(), NOW())", 16 | setting: allowed_groups, 17 | ) 18 | end 19 | end 20 | 21 | def down 22 | raise ActiveRecord::IrreversibleMigration 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /db/migrate/20250318024824_create_discourse_solved_solved_topics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # 3 | class CreateDiscourseSolvedSolvedTopics < ActiveRecord::Migration[7.2] 4 | def change 5 | create_table :discourse_solved_solved_topics do |t| 6 | t.integer :topic_id, null: false 7 | t.integer :answer_post_id, null: false 8 | t.integer :accepter_user_id, null: false 9 | t.integer :topic_timer_id 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20250318024953_copy_solved_topic_custom_field_to_discourse_solved_solved_topics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # 3 | class CopySolvedTopicCustomFieldToDiscourseSolvedSolvedTopics < ActiveRecord::Migration[7.2] 4 | disable_ddl_transaction! 5 | 6 | BATCH_SIZE = 10_000 7 | 8 | def up 9 | max_id = 10 | DB.query_single( 11 | "SELECT MAX(id) FROM topic_custom_fields WHERE topic_custom_fields.name = 'accepted_answer_post_id'", 12 | ).first 13 | return unless max_id 14 | 15 | last_id = 0 16 | while last_id < max_id 17 | DB.exec(<<~SQL, last_id: last_id, batch_size: BATCH_SIZE) 18 | INSERT INTO discourse_solved_solved_topics ( 19 | topic_id, 20 | answer_post_id, 21 | topic_timer_id, 22 | accepter_user_id, 23 | created_at, 24 | updated_at 25 | ) 26 | SELECT 27 | tc.topic_id, 28 | tc.answer_post_id, 29 | tc.topic_timer_id, 30 | tc.accepter_user_id, 31 | tc.created_at, 32 | tc.updated_at 33 | FROM ( 34 | SELECT 35 | tc.topic_id, 36 | CAST(tc.value AS INTEGER) AS answer_post_id, 37 | CAST(tc2.value AS INTEGER) AS topic_timer_id, 38 | COALESCE(ua.acting_user_id, -1) AS accepter_user_id, 39 | tc.created_at, 40 | tc.updated_at, 41 | ROW_NUMBER() OVER (PARTITION BY tc.topic_id ORDER BY tc.created_at ASC) AS rn_topic, 42 | ROW_NUMBER() OVER (PARTITION BY CAST(tc.value AS INTEGER) ORDER BY tc.created_at ASC) AS rn_answer 43 | FROM topic_custom_fields tc 44 | LEFT JOIN topic_custom_fields tc2 ON tc2.topic_id = tc.topic_id AND tc2.name = 'solved_auto_close_topic_timer_id' 45 | LEFT JOIN user_actions ua ON ua.target_topic_id = tc.topic_id AND ua.action_type = 15 46 | WHERE tc.name = 'accepted_answer_post_id' 47 | AND tc.id > :last_id 48 | AND tc.id <= :last_id + :batch_size 49 | ) tc 50 | WHERE tc.rn_topic = 1 AND tc.rn_answer = 1 51 | ON CONFLICT DO NOTHING 52 | SQL 53 | 54 | last_id += BATCH_SIZE 55 | end 56 | end 57 | 58 | def down 59 | raise ActiveRecord::IrreversibleMigration 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /db/migrate/20250318024954_remove_duplicates_from_discourse_solved_solved_topics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RemoveDuplicatesFromDiscourseSolvedSolvedTopics < ActiveRecord::Migration[7.2] 4 | def up 5 | # remove duplicates on answer_post_id based on earliest created_at 6 | DB.exec(<<~SQL) 7 | DELETE FROM discourse_solved_solved_topics 8 | WHERE id NOT IN ( 9 | SELECT id FROM ( 10 | SELECT id, ROW_NUMBER() OVER (PARTITION BY answer_post_id ORDER BY created_at) as row_num 11 | FROM discourse_solved_solved_topics 12 | ) t WHERE row_num = 1 13 | ) 14 | SQL 15 | 16 | # remove duplicates on topic_id based on earliest created_at 17 | DB.exec(<<~SQL) 18 | DELETE FROM discourse_solved_solved_topics 19 | WHERE id NOT IN ( 20 | SELECT id FROM ( 21 | SELECT id, ROW_NUMBER() OVER (PARTITION BY topic_id ORDER BY created_at) as row_num 22 | FROM discourse_solved_solved_topics 23 | ) t WHERE row_num = 1 24 | ) 25 | SQL 26 | end 27 | 28 | def down 29 | raise ActiveRecord::IrreversibleMigration 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /db/migrate/20250318025147_add_index_for_discourse_solved_topics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # 3 | class AddIndexForDiscourseSolvedTopics < ActiveRecord::Migration[7.2] 4 | disable_ddl_transaction! 5 | 6 | def change 7 | remove_index :discourse_solved_solved_topics, 8 | :topic_id, 9 | algorithm: :concurrently, 10 | unique: true, 11 | if_exists: true 12 | remove_index :discourse_solved_solved_topics, 13 | :answer_post_id, 14 | algorithm: :concurrently, 15 | unique: true, 16 | if_exists: true 17 | 18 | add_index :discourse_solved_solved_topics, :topic_id, unique: true, algorithm: :concurrently 19 | add_index :discourse_solved_solved_topics, 20 | :answer_post_id, 21 | unique: true, 22 | algorithm: :concurrently 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /db/migrate/20250325074111_copy_remaining_solved_topic_custom_field_to_discourse_solved_solved_topics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # 3 | class CopyRemainingSolvedTopicCustomFieldToDiscourseSolvedSolvedTopics < ActiveRecord::Migration[ 4 | 7.2 5 | ] 6 | disable_ddl_transaction! 7 | 8 | BATCH_SIZE = 5000 9 | 10 | def up 11 | max_id = 12 | DB.query_single( 13 | "SELECT MAX(id) FROM topic_custom_fields WHERE topic_custom_fields.name = 'accepted_answer_post_id'", 14 | ).first 15 | return unless max_id 16 | 17 | last_id = 0 18 | while last_id < max_id 19 | DB.exec(<<~SQL, last_id: last_id, batch_size: BATCH_SIZE) 20 | INSERT INTO discourse_solved_solved_topics ( 21 | topic_id, 22 | answer_post_id, 23 | topic_timer_id, 24 | accepter_user_id, 25 | created_at, 26 | updated_at 27 | ) 28 | SELECT 29 | tc.topic_id, 30 | tc.answer_post_id, 31 | tc.topic_timer_id, 32 | tc.accepter_user_id, 33 | tc.created_at, 34 | tc.updated_at 35 | FROM ( 36 | SELECT 37 | tc.topic_id, 38 | CAST(tc.value AS INTEGER) AS answer_post_id, 39 | CAST(tc2.value AS INTEGER) AS topic_timer_id, 40 | COALESCE(ua.acting_user_id, -1) AS accepter_user_id, 41 | tc.created_at, 42 | tc.updated_at, 43 | ROW_NUMBER() OVER (PARTITION BY tc.topic_id ORDER BY tc.created_at ASC) AS rn_topic, 44 | ROW_NUMBER() OVER (PARTITION BY CAST(tc.value AS INTEGER) ORDER BY tc.created_at ASC) AS rn_answer 45 | FROM topic_custom_fields tc 46 | LEFT JOIN topic_custom_fields tc2 ON tc2.topic_id = tc.topic_id AND tc2.name = 'solved_auto_close_topic_timer_id' 47 | LEFT JOIN user_actions ua ON ua.target_topic_id = tc.topic_id AND ua.action_type = 15 48 | WHERE tc.name = 'accepted_answer_post_id' 49 | AND tc.id > :last_id 50 | AND tc.id <= :last_id + :batch_size 51 | ) tc 52 | WHERE tc.rn_topic = 1 AND tc.rn_answer = 1 53 | ON CONFLICT DO NOTHING 54 | SQL 55 | 56 | last_id += BATCH_SIZE 57 | end 58 | end 59 | 60 | def down 61 | raise ActiveRecord::IrreversibleMigration 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import DiscourseRecommended from "@discourse/lint-configs/eslint"; 2 | 3 | export default [...DiscourseRecommended]; 4 | -------------------------------------------------------------------------------- /lib/discourse_assign/entry_point.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseAssign 4 | class EntryPoint 5 | # TODO: These four plugin api usages should ideally be in the assign plugin, not the solved plugin. 6 | # They have been moved here from plugin.rb as part of the custom fields migration. 7 | 8 | def self.inject(plugin) 9 | plugin.register_modifier(:assigns_reminder_assigned_topics_query) do |query| 10 | next query if !SiteSetting.ignore_solved_topics_in_assigned_reminder 11 | query.where.not(id: DiscourseSolved::SolvedTopic.select(:topic_id)) 12 | end 13 | 14 | plugin.register_modifier(:assigned_count_for_user_query) do |query, user| 15 | next query if !SiteSetting.ignore_solved_topics_in_assigned_reminder 16 | next query if SiteSetting.assignment_status_on_solve.blank? 17 | query.where.not(status: SiteSetting.assignment_status_on_solve) 18 | end 19 | 20 | plugin.on(:accepted_solution) do |post| 21 | next if SiteSetting.assignment_status_on_solve.blank? 22 | assignments = Assignment.includes(:target).where(topic: post.topic) 23 | assignments.each do |assignment| 24 | assigned_user = User.find_by(id: assignment.assigned_to_id) 25 | Assigner.new(assignment.target, assigned_user).assign( 26 | assigned_user, 27 | status: SiteSetting.assignment_status_on_solve, 28 | ) 29 | end 30 | end 31 | 32 | plugin.on(:unaccepted_solution) do |post| 33 | next if SiteSetting.assignment_status_on_unsolve.blank? 34 | assignments = Assignment.includes(:target).where(topic: post.topic) 35 | assignments.each do |assignment| 36 | assigned_user = User.find_by(id: assignment.assigned_to_id) 37 | Assigner.new(assignment.target, assigned_user).assign( 38 | assigned_user, 39 | status: SiteSetting.assignment_status_on_unsolve, 40 | ) 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/discourse_automation/entry_point.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseAutomation 4 | class EntryPoint 5 | def self.inject(plugin) 6 | plugin.on(:accepted_solution) do |post| 7 | # testing directly automation is prone to issues 8 | # we prefer to abstract logic in service object and test this 9 | next if Rails.env.test? 10 | 11 | name = "first_accepted_solution" 12 | DiscourseAutomation::Automation 13 | .where(trigger: name, enabled: true) 14 | .find_each do |automation| 15 | maximum_trust_level = automation.trigger_field("maximum_trust_level")&.dig("value") 16 | if DiscourseSolved::FirstAcceptedPostSolutionValidator.check( 17 | post, 18 | trust_level: maximum_trust_level, 19 | ) 20 | automation.trigger!( 21 | "kind" => name, 22 | "accepted_post_id" => post.id, 23 | "usernames" => [post.user.username], 24 | "placeholders" => { 25 | "post_url" => Discourse.base_url + post.url, 26 | }, 27 | ) 28 | end 29 | end 30 | end 31 | 32 | plugin.add_triggerable_to_scriptable(:first_accepted_solution, :send_pms) 33 | 34 | DiscourseAutomation::Triggerable.add(:first_accepted_solution) do 35 | placeholder :post_url 36 | 37 | field :maximum_trust_level, 38 | component: :choices, 39 | extra: { 40 | content: [ 41 | { 42 | id: 1, 43 | name: 44 | "discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl1", 45 | }, 46 | { 47 | id: 2, 48 | name: 49 | "discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl2", 50 | }, 51 | { 52 | id: 3, 53 | name: 54 | "discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl3", 55 | }, 56 | { 57 | id: 4, 58 | name: 59 | "discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl4", 60 | }, 61 | { 62 | id: "any", 63 | name: 64 | "discourse_automation.triggerables.first_accepted_solution.max_trust_level.any", 65 | }, 66 | ], 67 | }, 68 | required: true 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/discourse_dev/discourse_solved.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseDev 4 | class DiscourseSolved 5 | def self.populate(plugin) 6 | plugin.on(:after_populate_dev_records) do |records, type| 7 | next unless SiteSetting.solved_enabled 8 | 9 | if type == :category 10 | next if SiteSetting.allow_solved_on_all_topics 11 | 12 | solved_category = 13 | DiscourseDev::Record.random( 14 | ::Category.where( 15 | read_restricted: false, 16 | id: records.pluck(:id), 17 | parent_category_id: nil, 18 | ), 19 | ) 20 | ::CategoryCustomField.create!( 21 | category_id: solved_category.id, 22 | name: ::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD, 23 | value: "true", 24 | ) 25 | puts "discourse-solved enabled on category '#{solved_category.name}' (#{solved_category.id})." 26 | elsif type == :topic 27 | topics = ::Topic.where(id: records.pluck(:id)) 28 | 29 | unless SiteSetting.allow_solved_on_all_topics 30 | solved_category_id = 31 | ::CategoryCustomField 32 | .where(name: ::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD, value: "true") 33 | .first 34 | .category_id 35 | 36 | unless topics.exists?(category_id: solved_category_id) 37 | topics.last.update(category_id: solved_category_id) 38 | end 39 | 40 | topics = topics.where(category_id: solved_category_id) 41 | end 42 | 43 | solved_topic = DiscourseDev::Record.random(topics) 44 | post = nil 45 | 46 | if solved_topic.posts_count > 1 47 | post = DiscourseDev::Record.random(solved_topic.posts.where.not(post_number: 1)) 48 | else 49 | post = DiscourseDev::Post.new(solved_topic, 1).create! 50 | end 51 | 52 | ::DiscourseSolved.accept_answer!(post, post.topic.user, topic: post.topic) 53 | end 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/discourse_solved/accepted_answer_cache.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved 4 | class AcceptedAnswerCache 5 | @@allowed_accepted_cache = DistributedCache.new("allowed_accepted") 6 | 7 | def self.reset_accepted_answer_cache 8 | @@allowed_accepted_cache["allowed"] = begin 9 | Set.new( 10 | CategoryCustomField.where( 11 | name: ::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD, 12 | value: "true", 13 | ).pluck(:category_id), 14 | ) 15 | end 16 | end 17 | 18 | def self.allowed 19 | @@allowed_accepted_cache["allowed"] 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/discourse_solved/before_head_close.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DiscourseSolved::BeforeHeadClose 4 | attr_reader :controller 5 | 6 | def initialize(controller) 7 | @controller = controller 8 | end 9 | 10 | def html 11 | return "" if !controller.instance_of? TopicsController 12 | 13 | topic_view = controller.instance_variable_get(:@topic_view) 14 | topic = topic_view&.topic 15 | return "" if !topic 16 | # note, we have canonicals so we only do this for page 1 at the moment 17 | # it can get confusing to have this on every page and it should make page 1 18 | # a bit more prominent + cut down on pointless work 19 | 20 | return "" if SiteSetting.solved_add_schema_markup == "never" 21 | 22 | allowed = 23 | controller.guardian.allow_accepted_answers?(topic.category_id, topic.tags.pluck(:name)) 24 | return "" if !allowed 25 | 26 | first_post = topic_view.posts&.first 27 | return "" if first_post&.post_number != 1 28 | 29 | question_json = { 30 | "@type" => "Question", 31 | "name" => topic.title, 32 | "text" => get_schema_text(first_post), 33 | "upvoteCount" => first_post.like_count, 34 | "answerCount" => 0, 35 | "datePublished" => topic.created_at, 36 | "author" => { 37 | "@type" => "Person", 38 | "name" => topic.user&.username, 39 | "url" => topic.user&.full_url, 40 | }, 41 | } 42 | 43 | if accepted_answer = topic.solved&.answer_post 44 | question_json["answerCount"] = 1 45 | question_json[:acceptedAnswer] = { 46 | "@type" => "Answer", 47 | "text" => get_schema_text(accepted_answer), 48 | "upvoteCount" => accepted_answer.like_count, 49 | "datePublished" => accepted_answer.created_at, 50 | "url" => accepted_answer.full_url, 51 | "author" => { 52 | "@type" => "Person", 53 | "name" => accepted_answer.user&.username, 54 | "url" => accepted_answer.user&.full_url, 55 | }, 56 | } 57 | else 58 | return "" if SiteSetting.solved_add_schema_markup == "answered only" 59 | end 60 | 61 | [ 62 | '", 73 | ].join("") 74 | end 75 | 76 | private 77 | 78 | def get_schema_text(post) 79 | post.excerpt(nil, keep_onebox_body: true).presence || 80 | post.excerpt(nil, keep_onebox_body: true, keep_quotes: true) 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/discourse_solved/category_extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved::CategoryExtension 4 | extend ActiveSupport::Concern 5 | 6 | prepended { after_save :reset_accepted_cache, if: -> { SiteSetting.solved_enabled? } } 7 | 8 | private 9 | 10 | def reset_accepted_cache 11 | ::DiscourseSolved::AcceptedAnswerCache.reset_accepted_answer_cache 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/discourse_solved/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ::DiscourseSolved 4 | class Engine < ::Rails::Engine 5 | engine_name PLUGIN_NAME 6 | isolate_namespace DiscourseSolved 7 | config.autoload_paths << File.join(config.root, "lib") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/discourse_solved/first_accepted_post_solution_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved 4 | class FirstAcceptedPostSolutionValidator 5 | def self.check(post, trust_level:) 6 | return false if post.archetype != Archetype.default 7 | return false if !post&.user&.human? 8 | return true if trust_level == "any" 9 | 10 | return false if TrustLevel.compare(post&.user&.trust_level, trust_level.to_i) 11 | 12 | if !UserAction.where(user_id: post&.user_id, action_type: UserAction::SOLVED).exists? 13 | return true 14 | end 15 | 16 | false 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/discourse_solved/guardian_extensions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved 4 | module GuardianExtensions 5 | def allow_accepted_answers?(category_id, tag_names = []) 6 | return true if SiteSetting.allow_solved_on_all_topics 7 | 8 | if SiteSetting.enable_solved_tags.present? && tag_names.present? 9 | allowed_tags = SiteSetting.enable_solved_tags.split("|") 10 | is_allowed = (tag_names & allowed_tags).present? 11 | 12 | return true if is_allowed 13 | end 14 | 15 | return false if category_id.blank? 16 | if !::DiscourseSolved::AcceptedAnswerCache.allowed 17 | ::DiscourseSolved::AcceptedAnswerCache.reset_accepted_answer_cache 18 | end 19 | ::DiscourseSolved::AcceptedAnswerCache.allowed.include?(category_id) 20 | end 21 | 22 | def can_accept_answer?(topic, post) 23 | return false if !authenticated? 24 | return false if !topic || topic.private_message? || !post || post.whisper? 25 | return false if !allow_accepted_answers?(topic.category_id, topic.tags.map(&:name)) 26 | 27 | return true if is_staff? 28 | if current_user.in_any_groups?(SiteSetting.accept_all_solutions_allowed_groups_map) 29 | return true 30 | end 31 | return true if is_category_group_moderator?(topic.category) 32 | 33 | topic.user_id == current_user.id && !topic.closed && SiteSetting.accept_solutions_topic_author 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/discourse_solved/post_serializer_extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved::PostSerializerExtension 4 | extend ActiveSupport::Concern 5 | 6 | private 7 | 8 | def topic 9 | topic_view&.topic || object.topic 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/discourse_solved/register_filters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved 4 | class RegisterFilters 5 | def self.register(plugin) 6 | solved_callback = ->(scope) do 7 | scope.joins( 8 | "INNER JOIN discourse_solved_solved_topics ON discourse_solved_solved_topics.topic_id = topics.id", 9 | ).where("topics.archetype <> ?", Archetype.private_message) 10 | end 11 | 12 | unsolved_callback = ->(scope) do 13 | scope = scope.where(<<~SQL) 14 | topics.id NOT IN ( 15 | SELECT topic_id 16 | FROM discourse_solved_solved_topics 17 | ) 18 | SQL 19 | 20 | if !SiteSetting.allow_solved_on_all_topics 21 | tag_ids = Tag.where(name: SiteSetting.enable_solved_tags.split("|")).pluck(:id) 22 | 23 | scope = scope.where <<~SQL, tag_ids 24 | topics.id IN ( 25 | SELECT t.id 26 | FROM topics t 27 | JOIN category_custom_fields cc 28 | ON t.category_id = cc.category_id 29 | AND cc.name = '#{::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD}' 30 | AND cc.value = 'true' 31 | ) 32 | OR 33 | topics.id IN ( 34 | SELECT topic_id 35 | FROM topic_tags 36 | WHERE tag_id IN (?) 37 | ) 38 | SQL 39 | end 40 | 41 | scope.where("topics.archetype <> ?", Archetype.private_message) 42 | end 43 | 44 | plugin.register_custom_filter_by_status("solved", &solved_callback) 45 | plugin.register_custom_filter_by_status("unsolved", &unsolved_callback) 46 | 47 | plugin.register_search_advanced_filter(/status:solved/, &solved_callback) 48 | plugin.register_search_advanced_filter(/status:unsolved/, &unsolved_callback) 49 | 50 | TopicQuery.add_custom_filter(:solved) do |results, topic_query| 51 | if topic_query.options[:solved] == "yes" 52 | solved_callback.call(results) 53 | elsif topic_query.options[:solved] == "no" 54 | unsolved_callback.call(results) 55 | else 56 | results 57 | end 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/discourse_solved/topic_extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved::TopicExtension 4 | extend ActiveSupport::Concern 5 | 6 | prepended { has_one :solved, class_name: "DiscourseSolved::SolvedTopic", dependent: :destroy } 7 | end 8 | -------------------------------------------------------------------------------- /lib/discourse_solved/topic_posters_summary_extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved::TopicPostersSummaryExtension 4 | extend ActiveSupport::Concern 5 | 6 | def descriptions_by_id 7 | if !defined?(@descriptions_by_id) 8 | super(ids: old_user_ids) 9 | 10 | if id = topic.accepted_answer_user_id 11 | @descriptions_by_id[id] ||= [] 12 | @descriptions_by_id[id] << I18n.t(:accepted_answer) 13 | end 14 | end 15 | 16 | super 17 | end 18 | 19 | def last_poster_is_topic_creator? 20 | super || topic.accepted_answer_user_id == topic.last_post_user_id 21 | end 22 | 23 | def user_ids 24 | if id = topic.accepted_answer_user_id 25 | super.insert(1, id) 26 | else 27 | super 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/discourse_solved/topic_view_serializer_extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved::TopicViewSerializerExtension 4 | extend ActiveSupport::Concern 5 | 6 | prepended { attributes :accepted_answer } 7 | 8 | def include_accepted_answer? 9 | SiteSetting.solved_enabled? && object.topic.solved.present? && 10 | object.topic.solved.answer_post.present? 11 | end 12 | 13 | def accepted_answer 14 | accepted_answer_post_info 15 | end 16 | 17 | private 18 | 19 | def accepted_answer_post_info 20 | solved = object.topic.solved 21 | answer_post = solved.answer_post 22 | answer_post_user = answer_post.user 23 | accepter = solved.accepter 24 | 25 | excerpt = 26 | if SiteSetting.solved_quote_length > 0 27 | PrettyText.excerpt( 28 | answer_post.cooked, 29 | SiteSetting.solved_quote_length, 30 | keep_emoji_images: true, 31 | ) 32 | else 33 | nil 34 | end 35 | 36 | accepted_answer = { 37 | post_number: answer_post.post_number, 38 | username: answer_post_user.username, 39 | name: answer_post_user.name, 40 | excerpt:, 41 | } 42 | 43 | if SiteSetting.show_who_marked_solved 44 | accepted_answer[:accepter_name] = accepter.name 45 | accepted_answer[:accepter_username] = accepter.username 46 | end 47 | 48 | if !SiteSetting.enable_names || !SiteSetting.display_name_on_posts 49 | accepted_answer[:name] = nil 50 | accepted_answer[:accepter_name] = nil 51 | end 52 | 53 | accepted_answer 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/discourse_solved/user_summary_extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved::UserSummaryExtension 4 | extend ActiveSupport::Concern 5 | 6 | def solved_count 7 | DiscourseSolved::SolvedTopic 8 | .joins(answer_post: :user, topic: {}) 9 | .where(posts: { user_id: @user.id, deleted_at: nil }) 10 | .where(topics: { archetype: Archetype.default, deleted_at: nil }) 11 | .count 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/discourse_solved/web_hook_extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiscourseSolved::WebHookExtension 4 | extend ActiveSupport::Concern 5 | 6 | class_methods do 7 | def enqueue_solved_hooks(event, post, payload = nil) 8 | if active_web_hooks(event).exists? && post.present? 9 | payload ||= WebHook.generate_payload(:post, post) 10 | 11 | WebHook.enqueue_hooks( 12 | :solved, 13 | event, 14 | id: post.id, 15 | category_id: post.topic&.category_id, 16 | tag_ids: post.topic&.tags&.pluck(:id), 17 | payload: payload, 18 | ) 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "@discourse/lint-configs": "2.23.0", 5 | "ember-template-lint": "7.7.0", 6 | "eslint": "9.28.0", 7 | "prettier": "3.5.3", 8 | "stylelint": "16.20.0" 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 | -------------------------------------------------------------------------------- /spec/components/composer_messages_finder_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # frozen_string_literal: true 3 | 4 | require "rails_helper" 5 | require "composer_messages_finder" 6 | 7 | describe ComposerMessagesFinder do 8 | describe ".check_topic_is_solved" do 9 | fab!(:user) 10 | fab!(:topic) 11 | fab!(:post) { Fabricate(:post, topic: topic, user: Fabricate(:user)) } 12 | 13 | before { SiteSetting.disable_solved_education_message = false } 14 | 15 | it "does not show message without a topic id" do 16 | expect( 17 | described_class.new(user, composer_action: "createTopic").check_topic_is_solved, 18 | ).to be_blank 19 | expect(described_class.new(user, composer_action: "reply").check_topic_is_solved).to be_blank 20 | end 21 | 22 | describe "a reply" do 23 | it "does not show message if topic is not solved" do 24 | expect( 25 | described_class.new( 26 | user, 27 | composer_action: "reply", 28 | topic_id: topic.id, 29 | ).check_topic_is_solved, 30 | ).to be_blank 31 | end 32 | 33 | it "does not show message if disable_solved_education_message is true" do 34 | SiteSetting.disable_solved_education_message = true 35 | DiscourseSolved.accept_answer!(post, Discourse.system_user) 36 | expect( 37 | described_class.new( 38 | user, 39 | composer_action: "reply", 40 | topic_id: topic.id, 41 | ).check_topic_is_solved, 42 | ).to be_blank 43 | end 44 | 45 | it "shows message if the topic is solved" do 46 | DiscourseSolved.accept_answer!(post, Discourse.system_user) 47 | message = 48 | described_class.new( 49 | user, 50 | composer_action: "reply", 51 | topic_id: topic.id, 52 | ).check_topic_is_solved 53 | expect(message).not_to be_blank 54 | expect(message[:body]).to include("This topic has been solved") 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/components/post_revisor_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | require "post_revisor" 5 | 6 | describe PostRevisor do 7 | fab!(:category) { Fabricate(:category_with_definition) } 8 | fab!(:admin) { Fabricate(:admin, refresh_auto_groups: true) } 9 | 10 | fab!(:category_solved) do 11 | category = Fabricate(:category_with_definition) 12 | category.upsert_custom_fields("enable_accepted_answers" => "true") 13 | category 14 | end 15 | 16 | it "refreshes post stream when topic category changes to a solved category" do 17 | topic = Fabricate(:topic, category: Fabricate(:category_with_definition)) 18 | post = Fabricate(:post, topic: topic) 19 | 20 | messages = 21 | MessageBus.track_publish("/topic/#{topic.id}") do 22 | described_class.new(post).revise!(admin, { category_id: category.id }) 23 | end 24 | 25 | expect(messages.first.data[:refresh_stream]).to eq(nil) 26 | 27 | messages = 28 | MessageBus.track_publish("/topic/#{topic.id}") do 29 | described_class.new(post).revise!(admin, { category_id: category_solved.id }) 30 | end 31 | 32 | expect(messages.first.data[:refresh_stream]).to eq(true) 33 | end 34 | 35 | describe "Allowing solved via tags" do 36 | before do 37 | SiteSetting.solved_enabled = true 38 | SiteSetting.tagging_enabled = true 39 | end 40 | 41 | fab!(:tag1) { Fabricate(:tag) } 42 | fab!(:tag2) { Fabricate(:tag) } 43 | 44 | fab!(:topic) 45 | let(:post) { Fabricate(:post, topic: topic) } 46 | 47 | it "sets the refresh option after adding an allowed tag" do 48 | SiteSetting.enable_solved_tags = tag1.name 49 | 50 | messages = 51 | MessageBus.track_publish("/topic/#{topic.id}") do 52 | described_class.new(post).revise!(admin, tags: [tag1.name]) 53 | end 54 | 55 | expect(messages.first.data[:refresh_stream]).to eq(true) 56 | end 57 | 58 | it "sets the refresh option if the added tag matches any of the allowed tags" do 59 | SiteSetting.enable_solved_tags = [tag1, tag2].map(&:name).join("|") 60 | 61 | messages = 62 | MessageBus.track_publish("/topic/#{topic.id}") do 63 | described_class.new(post).revise!(admin, tags: [tag2.name]) 64 | end 65 | 66 | expect(messages.first.data[:refresh_stream]).to eq(true) 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/fabricators/extend_topic_fabricator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Fabricator(:custom_topic, from: :topic) do 3 | transient :custom_topic_name 4 | transient :value 5 | after_create do |top, transients| 6 | custom_topic = 7 | TopicCustomField.new( 8 | topic_id: top.id, 9 | name: transients[:custom_topic_name], 10 | value: transients[:value], 11 | ) 12 | custom_topic.save 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/fabricators/solved_hook_fabricator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Fabricator(:solved_web_hook, from: :web_hook) do 4 | after_build do |web_hook| 5 | web_hook.web_hook_event_types = 6 | WebHookEventType.where(name: %w[accepted_solution unaccepted_solution]) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/fabricators/solved_topic_fabricator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Fabricator(:solved_topic, from: DiscourseSolved::SolvedTopic) do 3 | topic 4 | answer_post { Fabricate(:post) } 5 | accepter { Fabricate(:user) } 6 | end 7 | -------------------------------------------------------------------------------- /spec/lib/first_accepted_post_solution_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe DiscourseSolved::FirstAcceptedPostSolutionValidator do 6 | fab!(:user_tl1) { Fabricate(:user, trust_level: TrustLevel[1], refresh_auto_groups: true) } 7 | 8 | context "when user is under max trust level" do 9 | context "with no post accepted yet" do 10 | it "validates the post" do 11 | post_1 = create_post(user: user_tl1) 12 | expect(described_class.check(post_1, trust_level: TrustLevel[2])).to eq(true) 13 | end 14 | end 15 | 16 | context "with already had accepted posts" do 17 | before do 18 | accepted_post = create_post(user: user_tl1) 19 | DiscourseSolved.accept_answer!(accepted_post, Discourse.system_user) 20 | end 21 | 22 | it "doesn’t validate the post" do 23 | post_1 = create_post(user: user_tl1) 24 | expect(described_class.check(post_1, trust_level: TrustLevel[2])).to eq(false) 25 | end 26 | end 27 | end 28 | 29 | context "when a user is above or equal max trust level" do 30 | context "with no post accepted yet" do 31 | it "doesn’t validate the post" do 32 | post_1 = create_post(user: user_tl1) 33 | expect(described_class.check(post_1, trust_level: TrustLevel[1])).to eq(false) 34 | end 35 | end 36 | 37 | context "when a post is already accepted" do 38 | before do 39 | accepted_post = create_post(user: user_tl1) 40 | DiscourseSolved.accept_answer!(accepted_post, Discourse.system_user) 41 | end 42 | 43 | it "doesn’t validate the post" do 44 | post_1 = create_post(user: user_tl1) 45 | expect(described_class.check(post_1, trust_level: TrustLevel[1])).to eq(false) 46 | end 47 | end 48 | end 49 | 50 | context "when using any trust level" do 51 | it "validates the post" do 52 | post_1 = create_post(user: user_tl1) 53 | expect(described_class.check(post_1, trust_level: "any")).to eq(true) 54 | end 55 | end 56 | 57 | context "when user is system" do 58 | it "doesn’t validate the post" do 59 | post_1 = create_post(user: Discourse.system_user) 60 | expect(described_class.check(post_1, trust_level: "any")).to eq(false) 61 | end 62 | end 63 | 64 | context "when post is a PM" do 65 | it "doesn’t validate the post" do 66 | Group.refresh_automatic_groups! 67 | post_1 = 68 | create_post( 69 | user: user_tl1, 70 | target_usernames: [user_tl1.username], 71 | archetype: Archetype.private_message, 72 | ) 73 | expect(described_class.check(post_1, trust_level: "any")).to eq(false) 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /spec/lib/guardian_extensions_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe DiscourseSolved::GuardianExtensions do 6 | fab!(:user) { Fabricate(:user, refresh_auto_groups: true) } 7 | fab!(:other_user) { Fabricate(:user, refresh_auto_groups: true) } 8 | fab!(:topic) 9 | fab!(:post) { Fabricate(:post, topic: topic, user: other_user) } 10 | 11 | let(:guardian) { user.guardian } 12 | 13 | before { SiteSetting.allow_solved_on_all_topics = true } 14 | 15 | describe ".can_accept_answer?" do 16 | it "returns false for anon users" do 17 | expect(Guardian.new.can_accept_answer?(topic, post)).to eq(false) 18 | end 19 | 20 | it "returns false if the topic is nil, the post is nil, or for whispers" do 21 | expect(guardian.can_accept_answer?(nil, post)).to eq(false) 22 | expect(guardian.can_accept_answer?(topic, nil)).to eq(false) 23 | 24 | post.update!(post_type: Post.types[:whisper]) 25 | expect(guardian.can_accept_answer?(topic, post)).to eq(false) 26 | end 27 | 28 | it "returns false for private messages" do 29 | topic.update!(user:, category_id: nil, archetype: Archetype.private_message) 30 | expect(guardian.can_accept_answer?(topic, post)).to eq(false) 31 | end 32 | 33 | it "returns false if accepted answers are not allowed" do 34 | SiteSetting.allow_solved_on_all_topics = false 35 | expect(guardian.can_accept_answer?(topic, post)).to eq(false) 36 | end 37 | 38 | it "returns true for admins" do 39 | expect( 40 | Guardian.new(Fabricate(:admin, refresh_auto_groups: true)).can_accept_answer?(topic, post), 41 | ).to eq(true) 42 | end 43 | 44 | it "returns true if the user is in a group allowed to accept solutions" do 45 | SiteSetting.accept_all_solutions_allowed_groups = Group::AUTO_GROUPS[:trust_level_0] 46 | expect(guardian.can_accept_answer?(topic, post)).to eq(true) 47 | SiteSetting.accept_all_solutions_allowed_groups = Group::AUTO_GROUPS[:trust_level_4] 48 | expect(guardian.can_accept_answer?(topic, post)).to eq(false) 49 | end 50 | 51 | it "returns true if the user is a category group moderator for the topic" do 52 | group = Fabricate(:group) 53 | group.add(user) 54 | category = Fabricate(:category) 55 | Fabricate(:category_moderation_group, category:, group:) 56 | topic.update!(category: category) 57 | SiteSetting.enable_category_group_moderation = true 58 | expect(guardian.can_accept_answer?(topic, post)).to eq(true) 59 | end 60 | 61 | it "returns true if the user is the topic author for an open topic" do 62 | SiteSetting.accept_solutions_topic_author = true 63 | topic.update!(user: user) 64 | expect(guardian.can_accept_answer?(topic, post)).to eq(true) 65 | end 66 | 67 | it "returns false if the user is trust level 4 but the trust level 4 group is not allowd to accept solutions" do 68 | SiteSetting.accept_all_solutions_allowed_groups = Fabricate(:group).id 69 | user.update!(trust_level: TrustLevel[4]) 70 | expect(guardian.can_accept_answer?(topic, post)).to eq(false) 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/models/copy_solved_topic_custom_field_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "../../db/migrate/20250318024953_copy_solved_topic_custom_field_to_discourse_solved_solved_topics" 4 | 5 | RSpec.describe CopySolvedTopicCustomFieldToDiscourseSolvedSolvedTopics, type: :migration do 6 | let(:migration) { described_class.new } 7 | 8 | describe "handling duplicates" do 9 | it "ensures only unique topic_id and answer_post_id are inserted" do 10 | topic = Fabricate(:topic) 11 | topic1 = Fabricate(:topic) 12 | post1 = Fabricate(:post, topic: topic) 13 | TopicCustomField.create!( 14 | topic_id: topic.id, 15 | name: "accepted_answer_post_id", 16 | value: post1.id.to_s, 17 | ) 18 | # explicit duplicate 19 | TopicCustomField.create!( 20 | topic_id: topic1.id, 21 | name: "accepted_answer_post_id", 22 | value: post1.id.to_s, 23 | ) 24 | 25 | second_topic = Fabricate(:topic) 26 | post2 = Fabricate(:post, topic: second_topic) 27 | TopicCustomField.create!( 28 | topic_id: second_topic.id, 29 | name: "accepted_answer_post_id", 30 | value: post2.id.to_s, 31 | ) 32 | 33 | migration.up 34 | expected_count = DiscourseSolved::SolvedTopic.count 35 | 36 | expect(expected_count).to eq(2) 37 | 38 | expect(DiscourseSolved::SolvedTopic.where(topic_id: topic.id).count).to eq(1) 39 | expect(DiscourseSolved::SolvedTopic.where(answer_post_id: post1.id).count).to eq(1) 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/models/remove_duplicates_from_discourse_solved_solved_topics_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "../../db/migrate/20250318024954_remove_duplicates_from_discourse_solved_solved_topics" 4 | 5 | RSpec.describe RemoveDuplicatesFromDiscourseSolvedSolvedTopics, type: :migration do 6 | let(:migration) { described_class.new } 7 | 8 | before do 9 | # temp drop unique constraints to allow testing duplicate entries 10 | ActiveRecord::Base.connection.execute( 11 | "DROP INDEX IF EXISTS index_discourse_solved_solved_topics_on_topic_id;", 12 | ) 13 | ActiveRecord::Base.connection.execute( 14 | "DROP INDEX IF EXISTS index_discourse_solved_solved_topics_on_answer_post_id;", 15 | ) 16 | end 17 | 18 | after do 19 | DiscourseSolved::SolvedTopic.delete_all 20 | 21 | # reapply unique indexes 22 | ActiveRecord::Base.connection.execute( 23 | "CREATE UNIQUE INDEX index_discourse_solved_solved_topics_on_topic_id ON discourse_solved_solved_topics (topic_id);", 24 | ) 25 | ActiveRecord::Base.connection.execute( 26 | "CREATE UNIQUE INDEX index_discourse_solved_solved_topics_on_answer_post_id ON discourse_solved_solved_topics (answer_post_id);", 27 | ) 28 | end 29 | 30 | describe "removal of duplicate answer_post_ids" do 31 | it "keeps only the earliest record for each answer_post_id" do 32 | topic1 = Fabricate(:topic) 33 | post1 = Fabricate(:post, topic: topic1) 34 | topic2 = Fabricate(:topic) 35 | post2 = Fabricate(:post, topic: topic2) 36 | 37 | earlier = Fabricate(:solved_topic, topic: topic1, answer_post: post1, created_at: 2.days.ago) 38 | Fabricate(:solved_topic, topic: topic1, answer_post: post1, created_at: 1.day.ago) 39 | Fabricate(:solved_topic, topic: topic1, answer_post: post1, created_at: Date.today) 40 | another = Fabricate(:solved_topic, topic: topic2, answer_post: post2, created_at: Date.today) 41 | 42 | expect(DiscourseSolved::SolvedTopic.count).to eq(4) 43 | migration.up 44 | 45 | expect(DiscourseSolved::SolvedTopic.count).to eq(2) 46 | expect(DiscourseSolved::SolvedTopic.pluck(:id, :answer_post_id)).to contain_exactly( 47 | [earlier.id, post1.id], 48 | [another.id, post2.id], 49 | ) 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/models/site_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | require_dependency "site" 5 | 6 | describe Site do 7 | let(:category) { Fabricate(:category) } 8 | let(:guardian) { Guardian.new } 9 | 10 | before { SiteSetting.show_filter_by_solved_status = true } 11 | 12 | it "includes `enable_accepted_answers` custom field for categories" do 13 | category.custom_fields["enable_accepted_answers"] = true 14 | category.save_custom_fields 15 | 16 | json = Site.json_for(guardian) 17 | 18 | expect(json).to include("enable_accepted_answers") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/models/user_summary_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | describe UserSummary do 4 | fab!(:admin) 5 | 6 | describe "solved_count" do 7 | it "indicates the number of times a user's post is a topic's solution" do 8 | topic = Fabricate(:topic) 9 | Fabricate(:post, topic:) 10 | user = Fabricate(:user) 11 | post = Fabricate(:post, topic:, user:) 12 | 13 | user_summary = UserSummary.new(user, Guardian.new) 14 | admin_summary = UserSummary.new(admin, Guardian.new) 15 | 16 | expect(user_summary.solved_count).to eq(0) 17 | expect(admin_summary.solved_count).to eq(0) 18 | 19 | DiscourseSolved.accept_answer!(post, admin) 20 | 21 | expect(user_summary.solved_count).to eq(1) 22 | expect(admin_summary.solved_count).to eq(0) 23 | end 24 | 25 | it "excludes deleted topics" do 26 | topic = Fabricate(:topic) 27 | Fabricate(:post, topic:) 28 | user = Fabricate(:user) 29 | post = Fabricate(:post, topic:, user:) 30 | 31 | user_summary = UserSummary.new(user, Guardian.new) 32 | DiscourseSolved.accept_answer!(post, admin) 33 | 34 | topic.update!(deleted_at: Time.zone.now) 35 | 36 | expect(user_summary.solved_count).to eq(0) 37 | end 38 | 39 | it "excludes deleted posts" do 40 | topic = Fabricate(:topic) 41 | Fabricate(:post, topic:) 42 | user = Fabricate(:user) 43 | post = Fabricate(:post, topic:, user:) 44 | 45 | user_summary = UserSummary.new(user, Guardian.new) 46 | DiscourseSolved.accept_answer!(post, admin) 47 | 48 | post.update!(deleted_at: Time.zone.now) 49 | 50 | expect(user_summary.solved_count).to eq(0) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/requests/answer_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe DiscourseSolved::AnswerController do 6 | fab!(:user) 7 | fab!(:staff_user) { Fabricate(:admin) } 8 | fab!(:category) 9 | fab!(:topic) { Fabricate(:topic, category: category) } 10 | fab!(:p) { Fabricate(:post, topic: topic) } 11 | fab!(:solution_post) { Fabricate(:post, topic: topic) } 12 | 13 | before do 14 | SiteSetting.solved_enabled = true 15 | SiteSetting.allow_solved_on_all_topics = true 16 | category.custom_fields[DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD] = "true" 17 | category.save_custom_fields 18 | 19 | # Give permission to accept solutions 20 | user.update!(trust_level: 1) 21 | 22 | # Make user the topic creator so they can accept answers 23 | topic.update!(user_id: user.id) 24 | end 25 | 26 | describe "#accept" do 27 | context "with default rate limiting" do 28 | it "applies rate limits to regular users" do 29 | sign_in(user) 30 | 31 | # Should be rate limited 32 | RateLimiter.any_instance.expects(:performed!).raises(RateLimiter::LimitExceeded.new(60)) 33 | post "/solution/accept.json", params: { id: solution_post.id } 34 | expect(response.status).to eq(429) 35 | end 36 | 37 | it "does not apply rate limits to staff" do 38 | sign_in(staff_user) 39 | 40 | post "/solution/accept.json", params: { id: solution_post.id } 41 | expect(response.status).to eq(200) 42 | end 43 | end 44 | 45 | context "with plugin modifier" do 46 | it "allows plugins to bypass rate limiting" do 47 | sign_in(user) 48 | # Create a plugin instance and register a modifier 49 | plugin_instance = Plugin::Instance.new 50 | modifier_block = Proc.new { |_, _| false } 51 | plugin_instance.register_modifier( 52 | :solved_answers_controller_run_rate_limiter, 53 | &modifier_block 54 | ) 55 | 56 | post "/solution/accept.json", params: { id: solution_post.id } 57 | expect(response.status).to eq(200) 58 | post "/solution/accept.json", params: { id: solution_post.id } 59 | expect(response.status).to eq(200) 60 | 61 | # Unregister the modifier using DiscoursePluginRegistry 62 | DiscoursePluginRegistry.unregister_modifier( 63 | plugin_instance, 64 | :solved_answers_controller_run_rate_limiter, 65 | &modifier_block 66 | ) 67 | end 68 | end 69 | end 70 | describe "#unaccept" do 71 | before do 72 | # Setup an accepted solution 73 | sign_in(user) 74 | post "/solution/accept.json", params: { id: solution_post.id } 75 | expect(response.status).to eq(200) 76 | sign_out 77 | end 78 | 79 | it "applies rate limits to regular users" do 80 | sign_in(user) 81 | 82 | # Should be rate limited 83 | RateLimiter.any_instance.expects(:performed!).raises(RateLimiter::LimitExceeded.new(60)) 84 | post "/solution/unaccept.json", params: { id: solution_post.id } 85 | expect(response.status).to eq(429) 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /spec/requests/list_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | RSpec.describe ListController do 6 | fab!(:p1) { Fabricate(:post) } 7 | fab!(:p2) { Fabricate(:post, topic: p1.topic) } 8 | fab!(:p3) { Fabricate(:post, topic: p1.topic) } 9 | 10 | before { SiteSetting.allow_solved_on_all_topics = true } 11 | 12 | it "shows the user who posted the accepted answer second" do 13 | TopicFeaturedUsers.ensure_consistency! 14 | DiscourseSolved.accept_answer!(p3, p1.user, topic: p1.topic) 15 | 16 | get "/latest.json" 17 | posters = response.parsed_body["topic_list"]["topics"].first["posters"] 18 | expect(posters[0]["user_id"]).to eq(p1.user_id) 19 | expect(posters[1]["user_id"]).to eq(p3.user_id) 20 | expect(posters[1]["description"]).to include("Accepted Answer") 21 | expect(posters[2]["user_id"]).to eq(p2.user_id) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/serializers/topic_answer_mixin_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_helper" 4 | 5 | describe DiscourseSolved::TopicAnswerMixin do 6 | let(:topic) { Fabricate(:topic) } 7 | let(:post) { Fabricate(:post, topic: topic) } 8 | let(:guardian) { Guardian.new } 9 | 10 | before { Fabricate(:solved_topic, topic: topic, answer_post: post) } 11 | 12 | it "should have true for `has_accepted_answer` field in each serializer" do 13 | [ 14 | TopicListItemSerializer, 15 | SearchTopicListItemSerializer, 16 | SuggestedTopicSerializer, 17 | UserSummarySerializer::TopicSerializer, 18 | ].each do |serializer| 19 | json = serializer.new(topic, scope: guardian, root: false).as_json 20 | expect(json[:has_accepted_answer]).to be_truthy 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/serializers/topic_view_serializer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | describe TopicViewSerializer do 4 | fab!(:topic) 5 | fab!(:post1) { Fabricate(:post, topic:) } 6 | fab!(:post2) { Fabricate(:post, topic:) } 7 | fab!(:user) 8 | 9 | before { SiteSetting.solved_enabled = true } 10 | 11 | describe "#accepted_answer" do 12 | it "returns the accepted answer post when the topic has an accepted answer" do 13 | Fabricate(:solved_topic, topic: topic, answer_post: post2) 14 | serializer = TopicViewSerializer.new(TopicView.new(topic), scope: Guardian.new(user)) 15 | serialized = serializer.as_json 16 | expect(serialized[:topic_view][:accepted_answer][:post_number]).to eq(post2.post_number) 17 | end 18 | 19 | it "returns nil when the topic does not have an accepted answer" do 20 | unsolved_topic = Fabricate(:topic) 21 | serializer = TopicViewSerializer.new(TopicView.new(unsolved_topic), scope: Guardian.new(user)) 22 | serialized = serializer.as_json 23 | expect(serialized[:accepted_answer]).to be_nil 24 | end 25 | 26 | it "returns nil when the accepted answer post does not exist" do 27 | weird_topic = Fabricate(:solved_topic) 28 | weird_topic.update!(answer_post_id: 19_238_319) 29 | serializer = 30 | TopicViewSerializer.new(TopicView.new(weird_topic.topic), scope: Guardian.new(user)) 31 | serialized = serializer.as_json 32 | expect(serialized[:accepted_answer]).to be_nil 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/serializers/user_card_serializer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | describe UserCardSerializer do 4 | let(:user) { Fabricate(:user) } 5 | let(:serializer) { described_class.new(user, scope: Guardian.new, root: false) } 6 | let(:json) { serializer.as_json } 7 | 8 | it "accepted_answers serializes number of accepted answers" do 9 | expect(serializer.as_json[:accepted_answers]).to eq(0) 10 | 11 | post1 = Fabricate(:post, user: user) 12 | DiscourseSolved.accept_answer!(post1, Discourse.system_user) 13 | post1.topic.reload 14 | expect(serializer.as_json[:accepted_answers]).to eq(1) 15 | 16 | post2 = Fabricate(:post, user: user) 17 | DiscourseSolved.accept_answer!(post2, Discourse.system_user) 18 | expect(serializer.as_json[:accepted_answers]).to eq(2) 19 | 20 | post3 = Fabricate(:post, user: user) 21 | DiscourseSolved.accept_answer!(post3, Discourse.system_user) 22 | expect(serializer.as_json[:accepted_answers]).to eq(3) 23 | 24 | DiscourseSolved.unaccept_answer!(post1) 25 | expect(serializer.as_json[:accepted_answers]).to eq(2) 26 | 27 | post2.topic.trash!(Discourse.system_user) 28 | expect(serializer.as_json[:accepted_answers]).to eq(1) 29 | 30 | post3.topic.convert_to_private_message(Discourse.system_user) 31 | expect(serializer.as_json[:accepted_answers]).to eq(0) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/system/solved_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | describe "About page", type: :system do 4 | fab!(:admin) 5 | fab!(:solver) { Fabricate(:user) } 6 | fab!(:accepter) { Fabricate(:user) } 7 | fab!(:topic) { Fabricate(:post, user: admin).topic } 8 | fab!(:post1) { Fabricate(:post, topic:, user: solver, cooked: "The answer is 42") } 9 | let(:topic_page) { PageObjects::Pages::Topic.new } 10 | 11 | before do 12 | SiteSetting.solved_enabled = true 13 | SiteSetting.allow_solved_on_all_topics = true 14 | SiteSetting.accept_all_solutions_allowed_groups = Group::AUTO_GROUPS[:everyone] 15 | SiteSetting.show_who_marked_solved = true 16 | end 17 | 18 | %w[enabled disabled].each do |value| 19 | before { SiteSetting.glimmer_post_stream_mode = value } 20 | 21 | context "when glimmer_post_stream_mode=#{value}" do 22 | it "accepts post as solution and shows in OP" do 23 | sign_in(accepter) 24 | 25 | topic_page.visit_topic(topic, post_number: 2) 26 | 27 | expect(topic_page).to have_css(".post-action-menu__solved-unaccepted") 28 | 29 | find(".post-action-menu__solved-unaccepted").click 30 | 31 | expect(topic_page).to have_css(".post-action-menu__solved-accepted") 32 | expect(topic_page.find(".title .accepted-answer--solver")).to have_content( 33 | "Solved by #{solver.username}", 34 | ) 35 | expect(topic_page.find(".title .accepted-answer--accepter")).to have_content( 36 | "Marked as solved by #{accepter.username}", 37 | ) 38 | expect(topic_page.find("blockquote")).to have_content("The answer is 42") 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /stylelint.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | extends: ["@discourse/lint-configs/stylelint"], 3 | }; 4 | -------------------------------------------------------------------------------- /test/javascripts/acceptance/discourse-solved-post-menu-test.js: -------------------------------------------------------------------------------- 1 | import { click, visit } from "@ember/test-helpers"; 2 | import { test } from "qunit"; 3 | import { acceptance } from "discourse/tests/helpers/qunit-helpers"; 4 | import { i18n } from "discourse-i18n"; 5 | import { postStreamWithAcceptedAnswerExcerpt } from "../helpers/discourse-solved-helpers"; 6 | 7 | acceptance( 8 | "Discourse Solved | Post Menu | Accept and Unaccept", 9 | function (needs) { 10 | needs.user({ admin: true }); 11 | 12 | needs.settings({ 13 | solved_enabled: true, 14 | allow_solved_on_all_topics: true, 15 | }); 16 | 17 | needs.pretender((server, helper) => { 18 | server.post("/solution/accept", () => helper.response({ success: "OK" })); 19 | server.post("/solution/unaccept", () => 20 | helper.response({ success: "OK" }) 21 | ); 22 | 23 | server.get("/t/12.json", () => 24 | helper.response(postStreamWithAcceptedAnswerExcerpt(null)) 25 | ); 26 | }); 27 | 28 | test("accepting and unaccepting a post works", async function (assert) { 29 | await visit("/t/without-excerpt/12"); 30 | 31 | assert 32 | .dom("#post_2 .post-action-menu__solved-accepted") 33 | .exists("Unaccept button is visible") 34 | .hasText(i18n("solved.solution"), "Unaccept button has correct text"); 35 | 36 | await click("#post_2 .post-action-menu__solved-accepted"); 37 | 38 | assert 39 | .dom("#post_2 .post-action-menu__solved-unaccepted") 40 | .exists("Accept button is visible"); 41 | 42 | await click("#post_2 .post-action-menu__solved-unaccepted"); 43 | 44 | assert 45 | .dom("#post_2 .post-action-menu__solved-accepted") 46 | .exists("Unaccept button is visible again"); 47 | }); 48 | } 49 | ); 50 | -------------------------------------------------------------------------------- /test/javascripts/acceptance/discourse-solved-test.js: -------------------------------------------------------------------------------- 1 | import { click, fillIn, visit } from "@ember/test-helpers"; 2 | import { test } from "qunit"; 3 | import { cloneJSON } from "discourse/lib/object"; 4 | import pretender, { 5 | fixturesByUrl, 6 | response, 7 | } from "discourse/tests/helpers/create-pretender"; 8 | import { acceptance } from "discourse/tests/helpers/qunit-helpers"; 9 | import { postStreamWithAcceptedAnswerExcerpt } from "../helpers/discourse-solved-helpers"; 10 | 11 | ["enabled", "disabled"].forEach((postStreamMode) => { 12 | acceptance( 13 | `Discourse Solved Plugin (glimmer_post_stream_mode = ${postStreamMode})`, 14 | function (needs) { 15 | needs.settings({ 16 | glimmer_post_stream_mode: postStreamMode, 17 | }); 18 | needs.user(); 19 | 20 | test("A topic with an accepted answer shows an excerpt of the answer, if provided", async function (assert) { 21 | pretender.get("/t/11.json", () => 22 | response(postStreamWithAcceptedAnswerExcerpt("this is an excerpt")) 23 | ); 24 | 25 | pretender.get("/t/12.json", () => 26 | response(postStreamWithAcceptedAnswerExcerpt(null)) 27 | ); 28 | 29 | await visit("/t/with-excerpt/11"); 30 | assert.dom(".quote blockquote").hasText("this is an excerpt"); 31 | 32 | await visit("/t/without-excerpt/12"); 33 | assert.dom(".quote blockquote").doesNotExist(); 34 | assert.dom(".quote .title.title-only").exists(); 35 | }); 36 | 37 | test("Full page search displays solved status", async function (assert) { 38 | pretender.get("/search", () => { 39 | const fixtures = cloneJSON(fixturesByUrl["/search.json"]); 40 | fixtures.topics[0].has_accepted_answer = true; 41 | return response(fixtures); 42 | }); 43 | 44 | await visit("/search"); 45 | await fillIn(".search-query", "discourse"); 46 | await click(".search-cta"); 47 | 48 | assert.dom(".fps-topic").exists({ count: 1 }, "has one post"); 49 | assert.dom(".topic-statuses .solved").exists("shows the right icon"); 50 | }); 51 | } 52 | ); 53 | }); 54 | -------------------------------------------------------------------------------- /test/javascripts/acceptance/user-activity-solved-test.js: -------------------------------------------------------------------------------- 1 | import { visit } from "@ember/test-helpers"; 2 | import { test } from "qunit"; 3 | import { acceptance } from "discourse/tests/helpers/qunit-helpers"; 4 | import { i18n } from "discourse-i18n"; 5 | 6 | acceptance( 7 | "Discourse Solved Plugin | activity/solved | empty state", 8 | function (needs) { 9 | needs.user(); 10 | 11 | needs.pretender((server, helper) => { 12 | server.get("/user_actions.json", () => 13 | helper.response({ user_actions: [] }) 14 | ); 15 | }); 16 | 17 | test("When looking at own activity", async function (assert) { 18 | await visit(`/u/eviltrout/activity/solved`); 19 | 20 | assert 21 | .dom("div.empty-state span.empty-state-title") 22 | .hasText(i18n("solved.no_solved_topics_title")); 23 | assert 24 | .dom("div.empty-state div.empty-state-body") 25 | .hasText(i18n("solved.no_solved_topics_body")); 26 | }); 27 | 28 | test("When looking at another user's activity", async function (assert) { 29 | await visit(`/u/charlie/activity/solved`); 30 | 31 | assert.dom("div.empty-state span.empty-state-title").hasText( 32 | i18n("solved.no_solved_topics_title_others", { 33 | username: "charlie", 34 | }) 35 | ); 36 | assert.dom("div.empty-state div.empty-state-body").hasNoText(); 37 | }); 38 | } 39 | ); 40 | -------------------------------------------------------------------------------- /translator.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for discourse-translator-bot 2 | 3 | files: 4 | - source_path: config/locales/client.en.yml 5 | destination_path: client.yml 6 | - source_path: config/locales/server.en.yml 7 | destination_path: server.yml 8 | --------------------------------------------------------------------------------