diff --git a/.eslintignore b/.eslintignore
index 6758d7f68c..4447ad6726 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,10 +1,8 @@
-app/assets/javascripts/browser-update.js
app/assets/javascripts/locales/i18n.js
app/assets/javascripts/ember-addons/
-app/assets/javascripts/discourse/lib/autosize.js
lib/javascripts/locale/
lib/javascripts/messageformat.js
-lib/highlight_js/
+lib/javascripts/messageformat-lookup.js
lib/pretty_text/
plugins/**/lib/javascripts/locale
public/
diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml
index 03e9ad63c7..c86a7474e4 100644
--- a/.github/workflows/licenses.yml
+++ b/.github/workflows/licenses.yml
@@ -52,7 +52,7 @@ jobs:
- name: Get pnpm cache directory
id: node-cache-dir
- run: echo "::set-output name=dir::$(pnpm store path --silent)"
+ run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Node cache
uses: actions/cache@v3
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index 8c31b023d7..144e6e4538 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -49,7 +49,7 @@ jobs:
- name: Get pnpm cache directory
id: node-cache-dir
- run: echo "::set-output name=dir::$(pnpm store path --silent)"
+ run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Node cache
uses: actions/cache@v3
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index f806754ce6..cbb0289db4 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -41,8 +41,6 @@ jobs:
target: plugins
- build_type: frontend
target: core # Handled by core_frontend_tests job (below)
- - build_type: system
- target: plugins # Enable once at least 1 plugin has system tests
steps:
- uses: actions/checkout@v3
@@ -83,7 +81,7 @@ jobs:
- name: Get pnpm cache directory
id: node-cache-dir
- run: echo "::set-output name=dir::$(pnpm store path --silent)"
+ run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Node cache
uses: actions/cache@v3
@@ -178,7 +176,7 @@ jobs:
- name: Plugin System Tests
if: matrix.build_type == 'system' && matrix.target == 'plugins'
- run: bin/system_rspec plugins/*/spec/system
+ run: LOAD_PLUGINS=1 bin/system_rspec plugins/*/spec/system
- name: Upload failed system test screenshots
uses: actions/upload-artifact@v3
@@ -223,7 +221,7 @@ jobs:
TESTEM_FIREFOX_PATH: ${{ (matrix.browser == 'Firefox Evergreen') && '/opt/firefox-evergreen/firefox' }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 1
@@ -234,7 +232,7 @@ jobs:
- name: Get pnpm cache directory
id: node-cache-dir
- run: echo "::set-output name=dir::$(pnpm store path --silent)"
+ run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Node cache
uses: actions/cache@v3
diff --git a/.streerc b/.streerc
new file mode 100644
index 0000000000..0bc4379d46
--- /dev/null
+++ b/.streerc
@@ -0,0 +1,2 @@
+--print-width=100
+--plugins=plugin/trailing_comma
diff --git a/Gemfile.lock b/Gemfile.lock
index e1cf749b27..733c3bb450 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -89,7 +89,7 @@ GEM
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (11.1.3)
- capybara (3.37.1)
+ capybara (3.38.0)
addressable
matrix
mini_mime (>= 0.1.3)
@@ -145,17 +145,17 @@ GEM
sprockets (>= 3.3, < 4.1)
ember-source (2.18.2)
erubi (1.11.0)
- excon (0.93.1)
+ excon (0.94.0)
execjs (2.8.1)
exifr (1.3.10)
fabrication (2.30.0)
faker (2.23.0)
i18n (>= 1.8.11, < 2)
fakeweb (1.3.0)
- faraday (2.6.0)
+ faraday (2.7.0)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
- faraday-net_http (3.0.1)
+ faraday-net_http (3.0.2)
faraday-retry (2.0.0)
faraday (~> 2.0)
fast_blank (1.0.1)
@@ -185,7 +185,7 @@ GEM
image_size (3.2.0)
in_threads (1.6.0)
jmespath (1.6.1)
- jquery-rails (4.5.0)
+ jquery-rails (4.5.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
@@ -239,7 +239,7 @@ GEM
mini_suffix (0.3.3)
ffi (~> 1.9)
minitest (5.16.3)
- mocha (2.0.1)
+ mocha (2.0.2)
ruby2_keywords (>= 0.0.5)
msgpack (1.6.0)
multi_json (1.15.0)
@@ -307,7 +307,7 @@ GEM
openssl (> 2.0, < 3.1)
optimist (3.0.1)
parallel (1.22.1)
- parallel_tests (3.13.0)
+ parallel_tests (4.0.0)
parallel
parser (3.1.2.1)
ast (~> 2.4.1)
@@ -329,7 +329,7 @@ GEM
rack (2.2.4)
rack-mini-profiler (3.0.0)
rack (>= 1.2.0)
- rack-protection (3.0.2)
+ rack-protection (3.0.3)
rack
rack-test (2.0.2)
rack (>= 1.3)
@@ -371,7 +371,7 @@ GEM
rack (>= 1.4)
rexml (3.2.5)
rinku (2.0.6)
- rotp (6.2.0)
+ rotp (6.2.1)
rqrcode (2.1.2)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
@@ -407,7 +407,7 @@ GEM
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
- rubocop (1.38.0)
+ rubocop (1.39.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.1.2.1)
@@ -443,7 +443,7 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
- selenium-webdriver (4.5.0)
+ selenium-webdriver (4.6.1)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
@@ -507,7 +507,7 @@ GEM
xpath (3.2.0)
nokogiri (~> 1.8)
yaml-lint (0.0.10)
- zeitwerk (2.6.4)
+ zeitwerk (2.6.6)
PLATFORMS
aarch64-linux
diff --git a/app/assets/javascripts/admin/addon/components/watched-word-form.js b/app/assets/javascripts/admin/addon/components/watched-word-form.js
index 1d6ba8ccba..f1b3138a78 100644
--- a/app/assets/javascripts/admin/addon/components/watched-word-form.js
+++ b/app/assets/javascripts/admin/addon/components/watched-word-form.js
@@ -2,7 +2,7 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators";
import Component from "@ember/component";
import I18n from "I18n";
import WatchedWord from "admin/models/watched-word";
-import { equal } from "@ember/object/computed";
+import { equal, not } from "@ember/object/computed";
import { isEmpty } from "@ember/utils";
import { schedule } from "@ember/runloop";
import { inject as service } from "@ember/service";
@@ -16,7 +16,7 @@ export default Component.extend({
showMessage: false,
selectedTags: null,
isCaseSensitive: false,
-
+ submitDisabled: not("word"),
canReplace: equal("actionKey", "replace"),
canTag: equal("actionKey", "tag"),
canLink: equal("actionKey", "link"),
diff --git a/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js b/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js
index 07372d2f10..37f5126068 100644
--- a/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js
+++ b/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js
@@ -6,11 +6,13 @@ import discourseDebounce from "discourse-common/lib/debounce";
import { observes } from "discourse-common/utils/decorators";
import { clipboardCopy } from "discourse/lib/utilities";
import { inject as service } from "@ember/service";
+import { or } from "@ember/object/computed";
export default Controller.extend({
dialog: service(),
loading: false,
filter: null,
+ showSearch: or("model.length", "filter"),
_debouncedShow() {
Permalink.findAll(this.filter).then((result) => {
diff --git a/app/assets/javascripts/admin/addon/controllers/admin-plugins.js b/app/assets/javascripts/admin/addon/controllers/admin-plugins.js
index be663cadb1..34ffe45783 100644
--- a/app/assets/javascripts/admin/addon/controllers/admin-plugins.js
+++ b/app/assets/javascripts/admin/addon/controllers/admin-plugins.js
@@ -1,17 +1,27 @@
import { action } from "@ember/object";
import Controller from "@ember/controller";
-import discourseComputed from "discourse-common/utils/decorators";
+import { inject as service } from "@ember/service";
export default Controller.extend({
- @discourseComputed
- adminRoutes() {
+ router: service(),
+
+ get adminRoutes() {
+ return this.allAdminRoutes.filter((r) => this.routeExists(r.full_location));
+ },
+
+ get brokenAdminRoutes() {
+ return this.allAdminRoutes.filter(
+ (r) => !this.routeExists(r.full_location)
+ );
+ },
+
+ get allAdminRoutes() {
return this.model
+ .filter((p) => p?.enabled)
.map((p) => {
- if (p.get("enabled")) {
- return p.admin_route;
- }
+ return p.admin_route;
})
- .compact();
+ .filter(Boolean);
},
@action
@@ -21,4 +31,13 @@ export default Controller.extend({
adminDetail.classList.toggle(state);
});
},
+
+ routeExists(routeName) {
+ try {
+ this.router.urlFor(routeName);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ },
});
diff --git a/app/assets/javascripts/admin/addon/controllers/modals/admin-start-backup.js b/app/assets/javascripts/admin/addon/controllers/modals/admin-start-backup.js
index 2934f251ee..f60908b0e2 100644
--- a/app/assets/javascripts/admin/addon/controllers/modals/admin-start-backup.js
+++ b/app/assets/javascripts/admin/addon/controllers/modals/admin-start-backup.js
@@ -1,9 +1,22 @@
import Controller, { inject as controller } from "@ember/controller";
+import discourseComputed from "discourse-common/utils/decorators";
import ModalFunctionality from "discourse/mixins/modal-functionality";
export default Controller.extend(ModalFunctionality, {
adminBackupsLogs: controller(),
+ @discourseComputed
+ warningMessage() {
+ // this is never shown here, but we may want to show different
+ // messages in plugins
+ return "";
+ },
+
+ @discourseComputed
+ yesLabel() {
+ return "yes_value";
+ },
+
actions: {
startBackupWithUploads() {
this.send("closeModal");
diff --git a/app/assets/javascripts/admin/addon/mixins/setting-component.js b/app/assets/javascripts/admin/addon/mixins/setting-component.js
index d651d1690c..6b9bfbda10 100644
--- a/app/assets/javascripts/admin/addon/mixins/setting-component.js
+++ b/app/assets/javascripts/admin/addon/mixins/setting-component.js
@@ -43,6 +43,7 @@ export default Mixin.create({
validationMessage: null,
isSecret: oneWay("setting.secret"),
setting: null,
+ attributeBindings: ["setting.setting:data-setting"],
@discourseComputed("buffered.value", "setting.value")
dirty(bufferVal, settingVal) {
@@ -136,6 +137,7 @@ export default Mixin.create({
"default_email_mailing_list_mode_frequency",
"default_email_previous_replies",
"default_email_in_reply_to",
+ "default_hide_profile_and_presence",
"default_other_new_topic_duration_minutes",
"default_other_auto_track_topics_after_msecs",
"default_other_notification_level_when_replying",
diff --git a/app/assets/javascripts/admin/addon/templates/components/permalink-form.hbs b/app/assets/javascripts/admin/addon/templates/components/permalink-form.hbs
index 705ac6894d..2e30cd353d 100644
--- a/app/assets/javascripts/admin/addon/templates/components/permalink-form.hbs
+++ b/app/assets/javascripts/admin/addon/templates/components/permalink-form.hbs
@@ -6,8 +6,8 @@
{{i18n "search.no_results"}} {{i18n "admin.permalink.no_permalinks"}}
-
-
+ {{else}}
+ {{#if this.filter}}
+ {{i18n "admin.permalink.url"}}
- {{i18n "admin.permalink.destination"}}
-
-
-
- {{#each this.model as |pl|}}
-
-
+ {{/each}}
+
+
-
-
- {{#if pl.topic_id}}
- {{pl.topic_title}}
- {{/if}}
- {{#if pl.post_id}}
- {{pl.post_topic_title}} #{{pl.post_number}}
- {{/if}}
- {{#if pl.category_id}}
- {{category-link pl.category}}
- {{/if}}
- {{#if pl.tag_id}}
- {{pl.tag_name}}
- {{/if}}
- {{#if pl.external_url}}
- {{#if pl.linkIsExternal}}
- {{d-icon "external-link-alt"}}
+
+
+
+
- {{else}}
- {{i18n "search.no_results"}}
- {{/if}}
+ {{#if pl.post_id}}
+ {{pl.post_topic_title}} #{{pl.post_number}}
+ {{/if}}
+ {{#if pl.category_id}}
+ {{category-link pl.category}}
+ {{/if}}
+ {{#if pl.tag_id}}
+ {{pl.tag_name}}
+ {{/if}}
+ {{#if pl.external_url}}
+ {{#if pl.linkIsExternal}}
+ {{d-icon "external-link-alt"}}
+ {{/if}}
+ {{pl.external_url}}
+ {{/if}}
+ {{i18n "admin.permalink.url"}}
+ {{i18n "admin.permalink.destination"}}
+
+
+
+ {{#each this.model as |pl|}}
+
+
- {{/each}}
-
-
+
+
+ {{#if pl.topic_id}}
+ {{pl.topic_title}}
{{/if}}
- {{pl.external_url}}
- {{/if}}
-
-
-
-
+
+
this is an emoji ![]()
this is an emoji ![]()
this is an emoji ![]()
this is an emoji ![]()
`,
+ `emoticons
`,
"emoticons are still supported"
);
testUnescape(
"With emoji :O: :frog: :smile:",
- `With emoji
`,
+ `With emoji
`,
"title with emoji"
);
testUnescape(
@@ -47,27 +47,27 @@ discourseModule("Unit | Utility | emoji", function () {
);
testUnescape(
"(:frog:) :)",
- `(
)
`,
+ `(
)
`,
"non-word characters allowed next to emoji"
);
testUnescape(
":smile: hi",
- `
hi`,
+ `
hi`,
"start of line"
);
testUnescape(
"hi :smile:",
- `hi
`,
+ `hi
`,
"end of line"
);
testUnescape(
"hi :blonde_woman:t4:",
- `hi
`,
+ `hi
`,
"support for skin tones"
);
testUnescape(
"hi :blonde_woman:t4: :blonde_man:t6:",
- `hi
`,
+ `hi
`,
"support for multiple skin tones"
);
testUnescape(
@@ -95,7 +95,7 @@ discourseModule("Unit | Utility | emoji", function () {
);
testUnescape(
"Hello 😊 World",
- `Hello
World`,
+ `Hello
World`,
"emoji from Unicode emoji"
);
testUnescape(
@@ -108,7 +108,7 @@ discourseModule("Unit | Utility | emoji", function () {
);
testUnescape(
"Hello😊World",
- `Hello
World`,
+ `Hello
World`,
"emoji from Unicode emoji when inline translation enabled",
{
enable_inline_emoji_translation: true,
@@ -124,13 +124,13 @@ discourseModule("Unit | Utility | emoji", function () {
);
testUnescape(
"hi:smile:",
- `hi
`,
+ `hi
`,
"emoji when inline translation enabled",
{ enable_inline_emoji_translation: true }
);
assert.strictEqual(
emojiUnescape(":smile:", { tabIndex: "0" }),
- `
`,
+ `
`,
"emoji when tabindex is enabled"
);
});
diff --git a/app/assets/javascripts/discourse/tests/unit/lib/formatter-test.js b/app/assets/javascripts/discourse/tests/unit/lib/formatter-test.js
index 311fc2d763..415f5dade8 100644
--- a/app/assets/javascripts/discourse/tests/unit/lib/formatter-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/lib/formatter-test.js
@@ -150,6 +150,7 @@ discourseModule("Unit | Utility | formatter", function (hooks) {
test("formatting tiny dates", function (assert) {
let shortDateYear = shortDateTester("MMM 'YY");
+ this.siteSettings.relative_date_duration = 14;
assert.strictEqual(formatMins(0), "1m");
assert.strictEqual(formatMins(1), "1m");
diff --git a/app/assets/javascripts/discourse/tests/unit/lib/link-lookup-test.js b/app/assets/javascripts/discourse/tests/unit/lib/link-lookup-test.js
index 9947393575..e1454fd4ae 100644
--- a/app/assets/javascripts/discourse/tests/unit/lib/link-lookup-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/lib/link-lookup-test.js
@@ -1,10 +1,11 @@
import LinkLookup from "discourse/lib/link-lookup";
import { module, test } from "qunit";
-import Post from "discourse/models/post";
+import { getOwner } from "discourse-common/lib/get-owner";
module("Unit | Utility | link-lookup", function (hooks) {
hooks.beforeEach(function () {
- this.post = Post.create();
+ const store = getOwner(this).lookup("service:store");
+ this.post = store.createRecord("post");
this.linkLookup = new LinkLookup({
"en.wikipedia.org/wiki/handheld_game_console": {
post_number: 1,
diff --git a/app/assets/javascripts/discourse/tests/unit/lib/pretty-text-test.js b/app/assets/javascripts/discourse/tests/unit/lib/pretty-text-test.js
index b0c2ead7d9..4e7e824722 100644
--- a/app/assets/javascripts/discourse/tests/unit/lib/pretty-text-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/lib/pretty-text-test.js
@@ -4,19 +4,19 @@ import {
deleteCachedInlineOnebox,
} from "pretty-text/inline-oneboxer";
import QUnit, { module, test } from "qunit";
-import Post from "discourse/models/post";
import { buildQuote } from "discourse/lib/quote";
import { deepMerge } from "discourse-common/lib/object";
import { extractDataAttribute } from "pretty-text/engines/discourse-markdown-it";
import { registerEmoji } from "pretty-text/emoji";
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
+import { getOwner } from "discourse-common/lib/get-owner";
const rawOpts = {
siteSettings: {
enable_emoji: true,
enable_emoji_shortcuts: true,
enable_mentions: true,
- emoji_set: "google_classic",
+ emoji_set: "twitter",
external_emoji_url: "",
highlighted_languages: "json|ruby|javascript",
default_code_lang: "auto",
@@ -1274,7 +1274,8 @@ eviltrout
});
test("quotes", function (assert) {
- const post = Post.create({
+ const store = getOwner(this).lookup("service:store");
+ const post = store.createRecord("post", {
cooked: "lorem ipsum
", username: "eviltrout", post_number: 1, @@ -1334,7 +1335,8 @@ eviltrout }); test("quoting a quote", function (assert) { - const post = Post.create({ + const store = getOwner(this).lookup("service:store"); + const post = store.createRecord("post", { cooked: new PrettyText(defaultOpts).cook( '[quote="sam, post:1, topic:1, full:true"]\nhello\n[/quote]\n*Test*' ), @@ -1524,7 +1526,7 @@ var bar = 'bar'; assert.cookedOptions( ":grin: @sam", { featuresOverride: ["emoji"] }, - `
@sam
@sam
![]()
![]()
![]()
![]()
![]()
![]()
test
test
test
test
<script>alert('hi')</script>"
+ );
+ });
});
diff --git a/app/assets/javascripts/discourse/tests/unit/models/composer-test.js b/app/assets/javascripts/discourse/tests/unit/models/composer-test.js
index eb3f513dd2..216d007080 100644
--- a/app/assets/javascripts/discourse/tests/unit/models/composer-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/models/composer-test.js
@@ -10,9 +10,9 @@ import {
} from "discourse/tests/helpers/qunit-helpers";
import AppEvents from "discourse/services/app-events";
import EmberObject from "@ember/object";
-import Post from "discourse/models/post";
import createStore from "discourse/tests/helpers/create-store";
import { test } from "qunit";
+import { getOwner } from "discourse-common/lib/get-owner";
function createComposer(opts) {
opts = opts || {};
@@ -276,7 +276,8 @@ discourseModule("Unit | Model | composer", function () {
const composer = createComposer();
assert.ok(!composer.get("editingFirstPost"), "it's false by default");
- const post = Post.create({ id: 123, post_number: 2 });
+ const store = getOwner(this).lookup("service:store");
+ const post = store.createRecord("post", { id: 123, post_number: 2 });
composer.setProperties({ post, action: EDIT });
assert.ok(
!composer.get("editingFirstPost"),
@@ -291,10 +292,11 @@ discourseModule("Unit | Model | composer", function () {
});
test("clearState", function (assert) {
+ const store = getOwner(this).lookup("service:store");
const composer = createComposer({
originalText: "asdf",
reply: "asdf2",
- post: Post.create({ id: 1 }),
+ post: store.createRecord("post", { id: 1 }),
title: "wat",
});
@@ -358,7 +360,8 @@ discourseModule("Unit | Model | composer", function () {
this.siteSettings.max_topic_title_length = 10;
const composer = createComposer();
- const post = Post.create({
+ const store = getOwner(this).lookup("service:store");
+ const post = store.createRecord("post", {
id: 123,
post_number: 2,
static_doc: true,
@@ -382,6 +385,7 @@ discourseModule("Unit | Model | composer", function () {
});
test("title placeholder depends on what you're doing", function (assert) {
+ this.siteSettings.topic_featured_link_enabled = false;
let composer = createComposer({ action: CREATE_TOPIC });
assert.strictEqual(
composer.get("titlePlaceholder"),
diff --git a/app/assets/javascripts/discourse/tests/unit/models/pending-post-test.js b/app/assets/javascripts/discourse/tests/unit/models/pending-post-test.js
index d42f3b6082..4849fdd3e3 100644
--- a/app/assets/javascripts/discourse/tests/unit/models/pending-post-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/models/pending-post-test.js
@@ -1,18 +1,22 @@
import { module, test } from "qunit";
-import PendingPost from "discourse/models/pending-post";
-import createStore from "discourse/tests/helpers/create-store";
+import { setupTest } from "ember-qunit";
+import { getOwner } from "discourse-common/lib/get-owner";
import { settled } from "@ember/test-helpers";
-module("Unit | Model | pending-post", function () {
+module("Unit | Model | pending-post", function (hooks) {
+ setupTest(hooks);
+
test("Properties", async function (assert) {
- const store = createStore();
+ const store = getOwner(this).lookup("service:store");
const category = store.createRecord("category", { id: 2 });
- const post = PendingPost.create({
+ const post = store.createRecord("pending-post", {
id: 1,
topic_url: "topic-url",
username: "USERNAME",
category_id: 2,
});
+
+ // pending-post initializer performs async operations
await settled();
assert.equal(post.postUrl, "topic-url", "topic_url is aliased to postUrl");
@@ -30,7 +34,12 @@ module("Unit | Model | pending-post", function () {
});
test("it cooks raw_text", async function (assert) {
- const post = PendingPost.create({ raw_text: "**bold text**" });
+ const store = getOwner(this).lookup("service:store");
+ const post = store.createRecord("pending-post", {
+ raw_text: "**bold text**",
+ });
+
+ // pending-post initializer performs async operations
await settled();
assert.equal(
diff --git a/app/assets/javascripts/discourse/tests/unit/models/post-test.js b/app/assets/javascripts/discourse/tests/unit/models/post-test.js
index 6938b2b476..c702a5e6d5 100644
--- a/app/assets/javascripts/discourse/tests/unit/models/post-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/models/post-test.js
@@ -1,30 +1,20 @@
import { module, test } from "qunit";
-import Post from "discourse/models/post";
import User from "discourse/models/user";
-import { deepMerge } from "discourse-common/lib/object";
+import { getOwner } from "discourse-common/lib/get-owner";
-function buildPost(args) {
- return Post.create(
- deepMerge(
- {
- id: 1,
- can_delete: true,
- version: 1,
- },
- args || {}
- )
- );
-}
+module("Unit | Model | post", function (hooks) {
+ hooks.beforeEach(function () {
+ this.store = getOwner(this).lookup("service:store");
+ });
-module("Unit | Model | post", function () {
test("defaults", function (assert) {
- let post = Post.create({ id: 1 });
+ const post = this.store.createRecord("post", { id: 1 });
assert.blank(post.get("deleted_at"), "it has no deleted_at by default");
assert.blank(post.get("deleted_by"), "there is no deleted_by by default");
});
test("new_user", function (assert) {
- let post = Post.create({ trust_level: 0 });
+ const post = this.store.createRecord("post", { trust_level: 0 });
assert.ok(post.get("new_user"), "post is from a new user");
post.set("trust_level", 1);
@@ -32,7 +22,7 @@ module("Unit | Model | post", function () {
});
test("firstPost", function (assert) {
- let post = Post.create({ post_number: 1 });
+ const post = this.store.createRecord("post", { post_number: 1 });
assert.ok(post.get("firstPost"), "it's the first post");
post.set("post_number", 10);
@@ -40,17 +30,14 @@ module("Unit | Model | post", function () {
});
test("updateFromPost", function (assert) {
- let post = Post.create({
+ const post = this.store.createRecord("post", {
post_number: 1,
raw: "hello world",
});
post.updateFromPost(
- Post.create({
+ this.store.createRecord("post", {
raw: "different raw",
- wat: function () {
- return 123;
- },
})
);
@@ -58,8 +45,13 @@ module("Unit | Model | post", function () {
});
test("destroy by staff", async function (assert) {
- let user = User.create({ username: "staff", moderator: true });
- let post = buildPost({ user });
+ const user = User.create({ username: "staff", moderator: true });
+ const post = this.store.createRecord("post", {
+ id: 1,
+ can_delete: true,
+ version: 1,
+ user,
+ });
await post.destroy(user);
@@ -85,7 +77,13 @@ module("Unit | Model | post", function () {
test("destroy by non-staff", async function (assert) {
const originalCooked = "this is the original cooked value";
const user = User.create({ username: "evil trout" });
- const post = buildPost({ user, cooked: originalCooked });
+ const post = this.store.createRecord("post", {
+ id: 1,
+ can_delete: true,
+ version: 1,
+ user,
+ cooked: originalCooked,
+ });
await post.destroy(user);
diff --git a/app/assets/javascripts/discourse/tests/unit/models/topic-test.js b/app/assets/javascripts/discourse/tests/unit/models/topic-test.js
index 7483d83325..c4dc505d92 100644
--- a/app/assets/javascripts/discourse/tests/unit/models/topic-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/models/topic-test.js
@@ -204,7 +204,7 @@ discourseModule("Unit | Model | topic", function (hooks) {
assert.strictEqual(
topic.get("fancyTitle"),
- `
with all
the emojis 
`,
+ `
with all
the emojis 
`,
"supports emojis"
);
});
@@ -238,7 +238,7 @@ discourseModule("Unit | Model | topic", function (hooks) {
assert.strictEqual(
topic.get("escapedExcerpt"),
- `This is a test topic
`,
+ `This is a test topic
`,
"supports emojis"
);
});
diff --git a/app/assets/javascripts/discourse/tests/unit/models/topic-tracking-state-test.js b/app/assets/javascripts/discourse/tests/unit/models/topic-tracking-state-test.js
index 434972596a..482ea1a0d8 100644
--- a/app/assets/javascripts/discourse/tests/unit/models/topic-tracking-state-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/models/topic-tracking-state-test.js
@@ -104,6 +104,16 @@ discourseModule("Unit | Model | topic-tracking-state", function (hooks) {
0,
"pending tag new counts"
);
+
+ // Ensure it is not throwing an error when filterTag is set and message payload is missing tags
+ trackingState.trackIncoming("tag/test/l/latest");
+ trackingState.notifyIncoming({
+ message_type: "new_topic",
+ topic_id: 4,
+ payload: { category_id: 2 },
+ });
+ const testTagCount = trackingState.countTags(["test"]);
+ assert.strictEqual(testTagCount["test"].unreadCount, 0);
});
test("tag counts - with total", function (assert) {
diff --git a/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js b/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js
index dc7598055e..1c07f7dcf5 100644
--- a/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js
@@ -57,12 +57,18 @@ class NativeComponent extends Component {
const TestStub = EmberObject.extend({
counter: 0,
otherCounter: 0,
+ state: null,
@debounce(50)
increment(value) {
this.counter += value;
},
+ @debounce(50, true)
+ setState(state) {
+ this.state = state;
+ },
+
// Note: it only works in this particular order:
// `@observes()` first, then `@debounce()`
@observes("prop")
@@ -149,6 +155,16 @@ module("Unit | Utils | decorators", function (hooks) {
assert.strictEqual(stub.counter, 6);
});
+ test("immediate debounce", async function (assert) {
+ const stub = TestStub.create();
+
+ stub.setState("foo");
+ stub.setState("bar");
+ await settled();
+
+ assert.strictEqual(stub.state, "foo");
+ });
+
test("debounce works with @observe", async function (assert) {
const stub = TestStub.create();
diff --git a/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js b/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js
index 8ce6442575..e0813c2310 100644
--- a/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js
+++ b/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js
@@ -385,6 +385,12 @@ function setupMarkdownEngine(opts, featureConfig) {
opts.pluginCallbacks.forEach(([feature, callback]) => {
if (featureConfig[feature]) {
+ if (callback === null || callback === undefined) {
+ // eslint-disable-next-line no-console
+ console.log("BAD MARKDOWN CALLBACK FOUND");
+ // eslint-disable-next-line no-console
+ console.log(`FEATURE IS: ${feature}`);
+ }
opts.engine.use(callback);
}
});
diff --git a/app/assets/javascripts/pretty-text/jsconfig.json b/app/assets/javascripts/pretty-text/jsconfig.json
index db42aeaaa5..63aedc6928 100644
--- a/app/assets/javascripts/pretty-text/jsconfig.json
+++ b/app/assets/javascripts/pretty-text/jsconfig.json
@@ -5,6 +5,7 @@
"paths": {
"pretty-text/*": ["./addon/*"],
"discourse/*": ["../discourse/app/*"],
+ "discourse/tests/*": ["../discourse/tests/*"],
"discourse-common/*": ["../discourse-common/addon/*"]
}
},
diff --git a/app/assets/javascripts/pretty-text/package.json b/app/assets/javascripts/pretty-text/package.json
index 1a9b9556f3..ab73854a87 100644
--- a/app/assets/javascripts/pretty-text/package.json
+++ b/app/assets/javascripts/pretty-text/package.json
@@ -18,7 +18,7 @@
"ember-auto-import": "^2.4.3",
"ember-cli-babel": "^7.26.10",
"ember-cli-htmlbars": "^6.1.1",
- "webpack": "^5.74.0",
+ "webpack": "^5.75.0",
"xss": "^1.0.14"
},
"devDependencies": {
diff --git a/app/assets/javascripts/select-kit/addon/components/category-chooser.js b/app/assets/javascripts/select-kit/addon/components/category-chooser.js
index 7193d6457f..a6964c0b5c 100644
--- a/app/assets/javascripts/select-kit/addon/components/category-chooser.js
+++ b/app/assets/javascripts/select-kit/addon/components/category-chooser.js
@@ -44,8 +44,11 @@ export default ComboBoxComponent.extend({
) {
return Category.findUncategorized();
} else {
- const generalCategoryId = this.siteSettings.general_category_id;
- if (!generalCategoryId || generalCategoryId < 0) {
+ const defaultCategoryId = parseInt(
+ this.siteSettings.default_composer_category,
+ 10
+ );
+ if (!defaultCategoryId || defaultCategoryId < 0) {
return this.defaultItem(null, htmlSafe(I18n.t("category.choose")));
}
}
diff --git a/app/assets/javascripts/select-kit/addon/components/select-kit.js b/app/assets/javascripts/select-kit/addon/components/select-kit.js
index 11348538cd..316e80749c 100644
--- a/app/assets/javascripts/select-kit/addon/components/select-kit.js
+++ b/app/assets/javascripts/select-kit/addon/components/select-kit.js
@@ -283,6 +283,7 @@ export default Component.extend(
closeOnChange: true,
limitMatches: null,
placement: isDocumentRTL() ? "bottom-end" : "bottom-start",
+ verticalOffset: 3,
filterComponent: "select-kit/select-kit-filter",
selectedNameComponent: "selected-name",
selectedChoiceComponent: "selected-choice",
@@ -898,7 +899,7 @@ export default Component.extend(
{
name: "offset",
options: {
- offset: [0, 3],
+ offset: [0, this.selectKit.options.verticalOffset],
},
},
{
diff --git a/app/assets/javascripts/select-kit/jsconfig.json b/app/assets/javascripts/select-kit/jsconfig.json
index b21f46e2cb..93aa23e58d 100644
--- a/app/assets/javascripts/select-kit/jsconfig.json
+++ b/app/assets/javascripts/select-kit/jsconfig.json
@@ -5,6 +5,7 @@
"paths": {
"select-kit/*": ["./addon/*"],
"discourse/*": ["../discourse/app/*"],
+ "discourse/tests/*": ["../discourse/tests/*"],
"discourse-common/*": ["../discourse-common/addon/*"],
}
},
diff --git a/app/assets/javascripts/select-kit/package.json b/app/assets/javascripts/select-kit/package.json
index e6b266d771..bd0781fc37 100644
--- a/app/assets/javascripts/select-kit/package.json
+++ b/app/assets/javascripts/select-kit/package.json
@@ -18,7 +18,7 @@
"ember-auto-import": "^2.4.3",
"ember-cli-babel": "^7.26.10",
"ember-cli-htmlbars": "^6.1.1",
- "webpack": "^5.74.0"
+ "webpack": "^5.75.0"
},
"devDependencies": {
"@babel/core": "^7.19.6",
diff --git a/app/assets/javascripts/service-worker.js.erb b/app/assets/javascripts/service-worker.js.erb
index 8580e6e6fc..e54831fef0 100644
--- a/app/assets/javascripts/service-worker.js.erb
+++ b/app/assets/javascripts/service-worker.js.erb
@@ -8,6 +8,8 @@ workbox.setConfig({
});
var authUrls = ["auth", "session/sso_login", "session/sso"].map(path => `<%= Discourse.base_path %>/${path}`);
+var chatRegex = /\/chat\/channel\/(\d+)\//;
+var inlineReplyIcon = "<%= UrlHelper.absolute("/images/push-notifications/inline_reply.png") %>";
var cacheVersion = "1";
var discourseCacheName = "discourse-" + cacheVersion;
@@ -134,6 +136,16 @@ function showNotification(title, body, icon, badge, tag, baseUrl, url) {
tag: tag
}
+ if (chatRegex.test(url)) {
+ notificationOptions['actions'] = [{
+ action: "reply",
+ title: "Reply",
+ placeholder: "reply",
+ type: "text",
+ icon: inlineReplyIcon
+ }];
+ }
+
return self.registration.showNotification(title, notificationOptions);
}
@@ -163,18 +175,51 @@ self.addEventListener('notificationclick', function(event) {
var url = event.notification.data.url;
var baseUrl = event.notification.data.baseUrl;
- // This looks to see if the current window is already open and
- // focuses if it is
- event.waitUntil(
- clients.matchAll({ type: "window" })
- .then(function(clientList) {
- var reusedClientWindow = clientList.some(function(client) {
- if (client.url === baseUrl + url && 'focus' in client) {
+ if (event.action === "reply") {
+ let csrf;
+ fetch("/session/csrf", {
+ credentials: "include",
+ headers: {
+ Accept: "application/json",
+ },
+ })
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error("Network response was not OK");
+ }
+ return response.json();
+ })
+ .then((data) => {
+ csrf = data.csrf;
+
+ let chatTest = url.match(chatRegex);
+ if (chatTest.length > 0) {
+ let chatChannel = chatTest[1];
+
+ fetch(`${baseUrl}/chat/${chatChannel}.json`, {
+ credentials: "include",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
+ "X-CSRF-Token": csrf,
+ },
+ body: `message=${event.reply}`,
+ method: "POST",
+ mode: "cors",
+ });
+ }
+ });
+ } else {
+ // This looks to see if the current window is already open and
+ // focuses if it is
+ event.waitUntil(
+ clients.matchAll({ type: "window" }).then(function (clientList) {
+ var reusedClientWindow = clientList.some(function (client) {
+ if (client.url === baseUrl + url && "focus" in client) {
client.focus();
return true;
}
- if ('postMessage' in client && 'focus' in client) {
+ if ("postMessage" in client && "focus" in client) {
client.focus();
client.postMessage({ url: url });
return true;
@@ -182,9 +227,11 @@ self.addEventListener('notificationclick', function(event) {
return false;
});
- if (!reusedClientWindow && clients.openWindow) return clients.openWindow(baseUrl + url);
+ if (!reusedClientWindow && clients.openWindow)
+ return clients.openWindow(baseUrl + url);
})
- );
+ );
+ }
});
self.addEventListener('message', function(event) {
diff --git a/app/assets/javascripts/truth-helpers/package.json b/app/assets/javascripts/truth-helpers/package.json
index 52aefc35d2..4e4673b468 100644
--- a/app/assets/javascripts/truth-helpers/package.json
+++ b/app/assets/javascripts/truth-helpers/package.json
@@ -18,7 +18,7 @@
"ember-auto-import": "^2.4.3",
"ember-cli-babel": "^7.26.10",
"ember-cli-htmlbars": "^6.1.1",
- "webpack": "^5.74.0"
+ "webpack": "^5.75.0"
},
"devDependencies": {
"@babel/core": "^7.19.6",
diff --git a/app/assets/javascripts/wizard/jsconfig.json b/app/assets/javascripts/wizard/jsconfig.json
index 14f0542ff7..bc4cb2b2f7 100644
--- a/app/assets/javascripts/wizard/jsconfig.json
+++ b/app/assets/javascripts/wizard/jsconfig.json
@@ -5,6 +5,7 @@
"paths": {
"wizard/*": ["./addon/*"],
"discourse/*": ["../discourse/app/*"],
+ "discourse/tests/*": ["../discourse/tests/*"],
"discourse-common/*": ["../discourse-common/addon/*"],
}
},
diff --git a/app/assets/javascripts/wizard/package.json b/app/assets/javascripts/wizard/package.json
index c177b4974a..8843ae606b 100644
--- a/app/assets/javascripts/wizard/package.json
+++ b/app/assets/javascripts/wizard/package.json
@@ -18,7 +18,7 @@
"ember-auto-import": "^2.4.3",
"ember-cli-babel": "^7.26.10",
"ember-cli-htmlbars": "^6.1.1",
- "webpack": "^5.74.0",
+ "webpack": "^5.75.0",
"xss": "^1.0.14"
},
"devDependencies": {
diff --git a/app/assets/stylesheets/common/base/_index.scss b/app/assets/stylesheets/common/base/_index.scss
index 163ec74b3c..eb810f8194 100644
--- a/app/assets/stylesheets/common/base/_index.scss
+++ b/app/assets/stylesheets/common/base/_index.scss
@@ -12,7 +12,6 @@
@import "crawler_layout";
@import "d-icon";
@import "d-popover";
-@import "d-onboarding";
@import "dialog";
@import "directory";
@import "discourse";
@@ -59,4 +58,5 @@
@import "topic";
@import "upload";
@import "user-badges";
+@import "user-tips";
@import "user";
diff --git a/app/assets/stylesheets/common/base/d-onboarding.scss b/app/assets/stylesheets/common/base/d-onboarding.scss
deleted file mode 100644
index f7f795d9d7..0000000000
--- a/app/assets/stylesheets/common/base/d-onboarding.scss
+++ /dev/null
@@ -1,37 +0,0 @@
-.onboarding-popup-container {
- min-width: 300px;
- padding: 0.5em;
- text-align: left;
-
- .onboarding-popup-title {
- font-size: $font-up-2;
- font-weight: bold;
- }
-
- .onboarding-popup-content {
- margin-top: 0.25em;
- }
-
- .onboarding-popup-buttons {
- margin-top: 1em;
- }
-}
-
-.tippy-box[data-theme~="d-onboarding"][data-placement^="left"]
- > .tippy-svg-arrow
- > svg {
- left: 11px;
-}
-
-.tippy-box[data-theme~="d-onboarding"][data-placement^="bottom"]
- > .tippy-svg-arrow
- > svg {
- top: -13px;
- left: -1px;
-}
-
-.tippy-box[data-theme~="d-onboarding"] > .tippy-svg-arrow:after,
-.tippy-box[data-theme~="d-onboarding"] > .tippy-svg-arrow > svg {
- width: 18px;
- height: 18px;
-}
diff --git a/app/assets/stylesheets/common/base/new-user.scss b/app/assets/stylesheets/common/base/new-user.scss
index 85772e3fe3..49a72dea9e 100644
--- a/app/assets/stylesheets/common/base/new-user.scss
+++ b/app/assets/stylesheets/common/base/new-user.scss
@@ -89,10 +89,11 @@
.user-navigation-secondary {
--user-navigation__border-width: 2px;
+ --navigation-secondary-padding-top: 0.5em;
position: relative;
display: flex;
min-width: 0;
- margin: 0.5em 0;
+ margin: 0;
gap: 0 0.5em;
border-bottom: 1px solid var(--primary-low);
@@ -121,6 +122,7 @@
flex: 1 1 auto;
font-size: var(--font-down-1);
justify-content: flex-start;
+ padding-top: var(--navigation-secondary-padding-top);
li {
flex: 1 0 auto;
@@ -135,6 +137,10 @@
}
}
}
+
+ [class*="horizontal-overflow-nav__scroll"] {
+ padding-top: var(--navigation-secondary-padding-top);
+ }
}
.user-nav-dropdown-button {
diff --git a/app/assets/stylesheets/common/base/sidebar-section-link.scss b/app/assets/stylesheets/common/base/sidebar-section-link.scss
index 67bddf6cba..79a5043c71 100644
--- a/app/assets/stylesheets/common/base/sidebar-section-link.scss
+++ b/app/assets/stylesheets/common/base/sidebar-section-link.scss
@@ -25,10 +25,6 @@
color: var(--primary);
background: var(--d-sidebar-highlight-color);
- .sidebar-section-link-content-badge {
- color: var(--primary-high);
- }
-
.sidebar-section-link-prefix {
&.icon {
color: var(--primary-high);
@@ -40,7 +36,7 @@
@include ellipsis;
padding-left: 0.5em;
text-align: right;
- color: var(--primary-medium);
+ color: var(--primary-high);
font-size: var(--font-down-1);
font-weight: normal;
margin-left: auto;
diff --git a/app/assets/stylesheets/common/base/user-tips.scss b/app/assets/stylesheets/common/base/user-tips.scss
new file mode 100644
index 0000000000..ae1f511825
--- /dev/null
+++ b/app/assets/stylesheets/common/base/user-tips.scss
@@ -0,0 +1,43 @@
+.user-tip-container {
+ min-width: 300px;
+ padding: 0.5em;
+ text-align: left;
+
+ .user-tip-title {
+ font-size: $font-up-2;
+ font-weight: bold;
+ }
+
+ .user-tip-content {
+ margin-top: 0.25em;
+ }
+
+ .user-tip-buttons {
+ margin-top: 1em;
+ }
+}
+
+.tippy-box[data-theme~="user-tips"][data-placement^="left"]
+ > .tippy-svg-arrow
+ > svg {
+ left: 11px;
+}
+
+.tippy-box[data-theme~="user-tips"][data-placement^="top"]
+ > .tippy-svg-arrow
+ > svg {
+ top: 11px;
+}
+
+.tippy-box[data-theme~="user-tips"][data-placement^="bottom"]
+ > .tippy-svg-arrow
+ > svg {
+ top: -13px;
+ left: -1px;
+}
+
+.tippy-box[data-theme~="user-tips"] > .tippy-svg-arrow:after,
+.tippy-box[data-theme~="user-tips"] > .tippy-svg-arrow > svg {
+ width: 18px;
+ height: 18px;
+}
diff --git a/app/assets/stylesheets/mobile/menu-panel.scss b/app/assets/stylesheets/mobile/menu-panel.scss
index 785e5778c3..b73a77b61e 100644
--- a/app/assets/stylesheets/mobile/menu-panel.scss
+++ b/app/assets/stylesheets/mobile/menu-panel.scss
@@ -6,6 +6,7 @@
}
.panel-body-contents {
max-height: unset;
+ min-height: 100%;
}
}
.header-cloak {
@@ -89,6 +90,7 @@
display: flex;
flex-direction: column;
padding-top: 0.5em;
+ flex: 1 1 auto;
}
}
@@ -125,6 +127,7 @@
display: flex;
flex-direction: column;
box-sizing: border-box;
+ flex: 1 1 auto;
}
.sidebar-section-content {
diff --git a/app/assets/stylesheets/testem.scss b/app/assets/stylesheets/testem.scss
index cb117a8825..a1e4c5dad3 100644
--- a/app/assets/stylesheets/testem.scss
+++ b/app/assets/stylesheets/testem.scss
@@ -19,6 +19,7 @@ $love: #fa6c8d !default;
@import "common/foundation/mixins";
@import "desktop";
@import "color_definitions";
+@import "admin";
#ember-testing-container {
box-sizing: border-box;
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 377ceccaac..788f5e6006 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -111,7 +111,7 @@ class Admin::GroupsController < Admin::StaffController
raise Discourse::NotFound unless group
users = User.where(username: group_params[:usernames].split(","))
- users.each { |user| guardian.ensure_can_change_primary_group!(user) }
+ users.each { |user| guardian.ensure_can_change_primary_group!(user, group) }
users.update_all(primary_group_id: params[:primary] == "true" ? group.id : nil)
render json: success_json
diff --git a/app/controllers/admin/site_settings_controller.rb b/app/controllers/admin/site_settings_controller.rb
index a0a6ad9b86..4db8b72e43 100644
--- a/app/controllers/admin/site_settings_controller.rb
+++ b/app/controllers/admin/site_settings_controller.rb
@@ -197,7 +197,8 @@ class Admin::SiteSettingsController < Admin::AdminController
default_email_digest_frequency: "digest_after_minutes",
default_include_tl0_in_digests: "include_tl0_in_digests",
default_text_size: "text_size_key",
- default_title_count_mode: "title_count_mode_key"
+ default_title_count_mode: "title_count_mode_key",
+ default_hide_profile_and_presence: "hide_profile_and_presence"
}
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 86a9ad4df1..6d2cf1d3fd 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -241,11 +241,11 @@ class Admin::UsersController < Admin::StaffController
end
def primary_group
- guardian.ensure_can_change_primary_group!(@user)
-
if params[:primary_group_id].present?
primary_group_id = params[:primary_group_id].to_i
if group = Group.find(primary_group_id)
+ guardian.ensure_can_change_primary_group!(@user, group)
+
if group.user_ids.include?(@user.id)
@user.primary_group_id = primary_group_id
end
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
index 33325cf61c..d6e1a6e2de 100644
--- a/app/controllers/categories_controller.rb
+++ b/app/controllers/categories_controller.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class CategoriesController < ApplicationController
+ include TopicQueryParams
requires_login except: [:index, :categories_and_latest, :categories_and_top, :show, :redirect, :find_by_slug, :visible_groups]
@@ -291,6 +292,8 @@ class CategoriesController < ApplicationController
per_page: CategoriesController.topics_per_page,
no_definitions: true,
}
+
+ topic_options.merge!(build_topic_list_options)
style = SiteSetting.desktop_category_page_style
topic_options[:order] = 'created' if style == "categories_and_latest_topics_created_date"
diff --git a/app/controllers/new_topic_controller.rb b/app/controllers/new_topic_controller.rb
new file mode 100644
index 0000000000..f6b9017666
--- /dev/null
+++ b/app/controllers/new_topic_controller.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class NewTopicController < ApplicationController
+ def index; end
+end
diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb
index d05ba4351f..490f4db0ed 100644
--- a/app/controllers/session_controller.rb
+++ b/app/controllers/session_controller.rb
@@ -760,7 +760,7 @@ class SessionController < ApplicationController
end
if invite.redeemable?
- if !invite.is_invite_link? && sso.email != invite.email
+ if invite.is_email_invite? && sso.email != invite.email
raise Invite::ValidationFailed.new(I18n.t("invite.not_matching_email"))
end
elsif invite.expired?
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index d2078d15eb..96d3c11a65 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -626,13 +626,16 @@ module ApplicationHelper
end
def discourse_theme_color_meta_tags
- result = +<<~HTML
-
- HTML
+ result = +""
if dark_scheme_id != -1
result << <<~HTML
+
HTML
+ else
+ result << <<~HTML
+
+ HTML
end
result.html_safe
end
diff --git a/app/helpers/email_helper.rb b/app/helpers/email_helper.rb
index 952fb40cfe..ba14776aff 100644
--- a/app/helpers/email_helper.rb
+++ b/app/helpers/email_helper.rb
@@ -60,7 +60,7 @@ module EmailHelper
p,
span,
td {
- color: #dddddd !important;
+ color: inherit !important;
}
[data-stripped-secure-media] {
diff --git a/app/jobs/regular/export_user_archive.rb b/app/jobs/regular/export_user_archive.rb
index 256b8a3d4d..657cc4aafe 100644
--- a/app/jobs/regular/export_user_archive.rb
+++ b/app/jobs/regular/export_user_archive.rb
@@ -26,7 +26,7 @@ module Jobs
)
HEADER_ATTRS_FOR ||= HashWithIndifferentAccess.new(
- user_archive: ['topic_title', 'categories', 'is_pm', 'post', 'like_count', 'reply_count', 'url', 'created_at'],
+ user_archive: ['topic_title', 'categories', 'is_pm', 'post_raw', 'post_cooked', 'like_count', 'reply_count', 'url', 'created_at'],
user_archive_profile: ['location', 'website', 'bio', 'views'],
auth_tokens: ['id', 'auth_token_hash', 'prev_auth_token_hash', 'auth_token_seen', 'client_ip', 'user_agent', 'seen_at', 'rotated_at', 'created_at', 'updated_at'],
auth_token_logs: ['id', 'action', 'user_auth_token_id', 'client_ip', 'auth_token_hash', 'created_at', 'path', 'user_agent'],
@@ -134,7 +134,7 @@ module Jobs
Post.includes(topic: :category)
.where(user_id: @current_user.id)
- .select(:topic_id, :post_number, :raw, :like_count, :reply_count, :created_at)
+ .select(:topic_id, :post_number, :raw, :cooked, :like_count, :reply_count, :created_at)
.order(:created_at)
.with_deleted
.each do |user_archive|
@@ -441,7 +441,15 @@ module Jobs
is_pm = topic_data.archetype == "private_message" ? I18n.t("csv_export.boolean_yes") : I18n.t("csv_export.boolean_no")
url = "#{Discourse.base_url}/t/#{topic_data.slug}/#{topic_data.id}/#{user_archive['post_number']}"
- topic_hash = { "post" => user_archive['raw'], "topic_title" => topic_data.title, "categories" => categories, "is_pm" => is_pm, "url" => url }
+ topic_hash = {
+ "post_raw" => user_archive['raw'],
+ "post_cooked" => user_archive["cooked"],
+ "topic_title" => topic_data.title,
+ "categories" => categories,
+ "is_pm" => is_pm,
+ "url" => url
+ }
+
user_archive.merge!(topic_hash)
HEADER_ATTRS_FOR['user_archive'].each do |attr|
diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb
index 2bd607d1e6..c6947cf078 100644
--- a/app/models/bookmark.rb
+++ b/app/models/bookmark.rb
@@ -204,10 +204,8 @@ end
#
# Indexes
#
-# idx_bookmarks_user_polymorphic_unique (user_id,bookmarkable_type,bookmarkable_id) UNIQUE
-# index_bookmarks_on_post_id (post_id)
-# index_bookmarks_on_reminder_at (reminder_at)
-# index_bookmarks_on_reminder_set_at (reminder_set_at)
-# index_bookmarks_on_user_id (user_id)
-# index_bookmarks_on_user_id_and_post_id_and_for_topic (user_id,post_id,for_topic) UNIQUE
+# idx_bookmarks_user_polymorphic_unique (user_id,bookmarkable_type,bookmarkable_id) UNIQUE
+# index_bookmarks_on_reminder_at (reminder_at)
+# index_bookmarks_on_reminder_set_at (reminder_set_at)
+# index_bookmarks_on_user_id (user_id)
#
diff --git a/app/models/category.rb b/app/models/category.rb
index 0efbb5f3ed..7e7bd947b0 100644
--- a/app/models/category.rb
+++ b/app/models/category.rb
@@ -374,7 +374,7 @@ class Category < ActiveRecord::Base
elsif SiteSetting.slug_generation_method == 'ascii' && !CGI.unescape(self.slug).ascii_only?
errors.add(:slug, I18n.t("category.errors.slug_contains_non_ascii_chars"))
elsif duplicate_slug?
- errors.add(:slug, 'is already in use')
+ errors.add(:slug, I18n.t("category.errors.is_already_in_use"))
end
else
# auto slug
diff --git a/app/models/concerns/has_custom_fields.rb b/app/models/concerns/has_custom_fields.rb
index 16d9872344..07ebc880ce 100644
--- a/app/models/concerns/has_custom_fields.rb
+++ b/app/models/concerns/has_custom_fields.rb
@@ -64,9 +64,7 @@ module HasCustomFields
has_many :_custom_fields, dependent: :destroy, class_name: "#{name}CustomField"
after_save :save_custom_fields
- # TODO (martin) Post 2.8 release, change to attr_reader because this is
- # set by set_preloaded_custom_fields
- attr_accessor :preloaded_custom_fields
+ attr_reader :preloaded_custom_fields
def custom_fields_fk
@custom_fields_fk ||= "#{_custom_fields.reflect_on_all_associations(:belongs_to)[0].name}_id"
diff --git a/app/models/concerns/second_factor_manager.rb b/app/models/concerns/second_factor_manager.rb
index e21957e75f..4e212306af 100644
--- a/app/models/concerns/second_factor_manager.rb
+++ b/app/models/concerns/second_factor_manager.rb
@@ -79,7 +79,7 @@ module SecondFactorManager
end
def has_any_second_factor_methods_enabled?
- totp_enabled? || security_keys_enabled?
+ totp_enabled? || security_keys_enabled? || backup_codes_enabled?
end
def has_multiple_second_factor_methods?
diff --git a/app/models/emoji.rb b/app/models/emoji.rb
index 2525b30948..750be81283 100644
--- a/app/models/emoji.rb
+++ b/app/models/emoji.rb
@@ -29,11 +29,11 @@ class Emoji
end
def self.aliases
- Discourse.cache.fetch(cache_key("aliases_emojis")) { db['aliases'] }
+ db['aliases']
end
def self.search_aliases
- Discourse.cache.fetch(cache_key("search_aliases_emojis")) { db['searchAliases'] }
+ db['searchAliases']
end
def self.translations
@@ -45,7 +45,7 @@ class Emoji
end
def self.tonable_emojis
- Discourse.cache.fetch(cache_key("tonable_emojis")) { db['tonableEmojis'] }
+ db['tonableEmojis']
end
def self.custom?(name)
@@ -118,7 +118,7 @@ class Emoji
end
def self.clear_cache
- %w{custom standard aliases search_aliases translations all tonable}.each do |key|
+ %w{custom standard translations all}.each do |key|
Discourse.cache.delete(cache_key("#{key}_emojis"))
end
global_emoji_cache.clear
diff --git a/app/models/invite.rb b/app/models/invite.rb
index 917b486f29..1910700185 100644
--- a/app/models/invite.rb
+++ b/app/models/invite.rb
@@ -74,8 +74,16 @@ class Invite < ActiveRecord::Base
end
end
+ # Even if a domain is specified on the invite, it still counts as
+ # an invite link.
def is_invite_link?
- email.blank?
+ self.email.blank?
+ end
+
+ # Email invites have specific behaviour and it's easier to visually
+ # parse is_email_invite? than !is_invite_link?
+ def is_email_invite?
+ self.email.present?
end
def redeemable?
@@ -201,8 +209,6 @@ class Invite < ActiveRecord::Base
)
return if !redeemable?
- email = self.email if email.blank? && !is_invite_link?
-
InviteRedeemer.new(
invite: self,
email: email,
diff --git a/app/models/invite_redeemer.rb b/app/models/invite_redeemer.rb
index 93496140fa..6a7ae4b22a 100644
--- a/app/models/invite_redeemer.rb
+++ b/app/models/invite_redeemer.rb
@@ -1,5 +1,18 @@
# frozen_string_literal: true
+# NOTE: There are a _lot_ of complicated rules and conditions for our
+# invite system, and the code is spread out through a lot of places.
+# Tread lightly and read carefully when modifying this code. You may
+# also want to look at:
+#
+# * InvitesController
+# * SessionController
+# * Invite model
+# * User model
+#
+# Invites that are scoped to a specific email (email IS NOT NULL on the Invite
+# model) have different rules to invites that are considered an "invite link",
+# (email IS NULL) on the Invite model.
class InviteRedeemer
attr_reader :invite,
:email,
@@ -13,7 +26,7 @@ class InviteRedeemer
:redeeming_user
def initialize(
- invite: nil,
+ invite:,
email: nil,
username: nil,
name: nil,
@@ -23,9 +36,7 @@ class InviteRedeemer
session: nil,
email_token: nil,
redeeming_user: nil)
-
@invite = invite
- @email = email
@username = username
@name = name
@password = password
@@ -34,6 +45,8 @@ class InviteRedeemer
@session = session
@email_token = email_token
@redeeming_user = redeeming_user
+
+ ensure_email_is_present!(email)
end
def redeem
@@ -45,7 +58,29 @@ class InviteRedeemer
end
end
- # extracted from User cause it is very specific to invites
+ # The email must be present in some form since many of the methods
+ # for processing + redemption rely on it. If it's still nil after
+ # these checks then we have hit an edge case and should not proceed!
+ def ensure_email_is_present!(email)
+ if email.blank?
+ Rails.logger.warn(
+ "email param was blank in InviteRedeemer for invite ID #{@invite.id}. The `redeeming_user` was #{@redeeming_user.present? ? "(ID: #{@redeeming_user.id})" : "not"} present.",
+ )
+ end
+
+ if email.blank? && @invite.is_email_invite?
+ @email = @invite.email
+ elsif @redeeming_user.present?
+ @email = @redeeming_user.email
+ else
+ @email = email
+ end
+
+ raise Discourse::InvalidParameters if @email.blank?
+ end
+
+ # This will _never_ be called if there is a redeeming_user being passed
+ # in to InviteRedeemer -- see invited_user below.
def self.create_user_from_invite(email:, invite:, username: nil, name: nil, password: nil, user_custom_fields: nil, ip_address: nil, session: nil, email_token: nil)
if username && UsernameValidator.new(username).valid_format? && User.username_available?(username, email)
available_username = username
@@ -107,7 +142,10 @@ class InviteRedeemer
user.save!
authenticator.finish
- if invite.emailed_status != Invite.emailed_status_types[:not_required] && email == invite.email && invite.email_token.present? && email_token == invite.email_token
+ if invite.emailed_status != Invite.emailed_status_types[:not_required] &&
+ email == invite.email &&
+ invite.email_token.present? &&
+ email_token == invite.email_token
user.activate
end
@@ -118,24 +156,26 @@ class InviteRedeemer
def can_redeem_invite?
return false if !invite.redeemable?
+ return false if email.blank?
- # Invite has already been redeemed by anyone.
- if !invite.is_invite_link? && InvitedUser.exists?(invite_id: invite.id)
+ # Invite scoped to email has already been redeemed by anyone.
+ if invite.is_email_invite? && InvitedUser.exists?(invite_id: invite.id)
return false
end
- # Email will not be present if we are claiming an invite link, which
- # does not have an email or domain scope on the invitation.
- if email.present? || redeeming_user.present?
- email_to_check = redeeming_user&.email || email
+ # The email will be present for either an invite link (where the user provides
+ # us the email manually) or for an invite scoped to an email, where we
+ # prefill the email and do not let the user modify it.
+ #
+ # Note that an invite link can also have a domain scope which must be checked.
+ email_to_check = redeeming_user&.email || email
- if invite.email.present? && !invite.email_matches?(email_to_check)
- raise ActiveRecord::RecordNotSaved.new(I18n.t('invite.not_matching_email'))
- end
+ if invite.email.present? && !invite.email_matches?(email_to_check)
+ raise ActiveRecord::RecordNotSaved.new(I18n.t('invite.not_matching_email'))
+ end
- if invite.domain.present? && !invite.domain_matches?(email_to_check)
- raise ActiveRecord::RecordNotSaved.new(I18n.t('invite.domain_not_allowed'))
- end
+ if invite.domain.present? && !invite.domain_matches?(email_to_check)
+ raise ActiveRecord::RecordNotSaved.new(I18n.t('invite.domain_not_allowed'))
end
# Anon user is trying to redeem an invitation, if an existing user already
@@ -148,6 +188,10 @@ class InviteRedeemer
true
end
+ # Note that the invited_user is returned by #redeemed, so other places
+ # (e.g. the InvitesController) can perform further actions on it, this
+ # is why things like send_welcome_message are set without being saved
+ # on the model.
def invited_user
return @invited_user if defined?(@invited_user)
@@ -196,9 +240,18 @@ class InviteRedeemer
end
def add_to_private_topics_if_invited
- topic_ids = Topic.where(archetype: Archetype::private_message).includes(:invites).where(invites: { email: email }).pluck(:id)
+ # Should not happen because of ensure_email_is_present!, but better to cover bases.
+ return if email.blank?
+
+ topic_ids = TopicInvite.joins(:invite)
+ .joins(:topic)
+ .where("topics.archetype = ?", Archetype::private_message)
+ .where("invites.email = ?", email)
+ .pluck(:topic_id)
topic_ids.each do |id|
- TopicAllowedUser.create!(user_id: invited_user.id, topic_id: id) unless TopicAllowedUser.exists?(user_id: invited_user.id, topic_id: id)
+ if !TopicAllowedUser.exists?(user_id: invited_user.id, topic_id: id)
+ TopicAllowedUser.create!(user_id: invited_user.id, topic_id: id)
+ end
end
end
@@ -221,15 +274,17 @@ class InviteRedeemer
end
def notify_invitee
- if inviter = invite.invited_by
- inviter.notifications.create!(
- notification_type: Notification.types[:invitee_accepted],
- data: { display_username: invited_user.username }.to_json
- )
- end
+ return if invite.invited_by.blank?
+ invite.invited_by.notifications.create!(
+ notification_type: Notification.types[:invitee_accepted],
+ data: { display_username: invited_user.username }.to_json
+ )
end
def delete_duplicate_invites
+ # Should not happen because of ensure_email_is_present!, but better to cover bases.
+ return if email.blank?
+
Invite
.where('invites.max_redemptions_allowed = 1')
.joins("LEFT JOIN invited_users ON invites.id = invited_users.invite_id")
diff --git a/app/models/onboarding_popup.rb b/app/models/onboarding_popup.rb
deleted file mode 100644
index f672f6eea4..0000000000
--- a/app/models/onboarding_popup.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-class OnboardingPopup
- def self.types
- @types ||= Enum.new(
- first_notification: 1,
- topic_timeline: 2,
- )
- end
-end
diff --git a/app/models/post.rb b/app/models/post.rb
index 5f493c4585..362ce9bf8b 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -989,10 +989,6 @@ class Post < ActiveRecord::Base
end
end
- def downloaded_images
- self.custom_fields[Post::DOWNLOADED_IMAGES] || {}
- end
-
def each_upload_url(fragments: nil, include_local_upload: true)
current_db = RailsMultisite::ConnectionManagement.current_db
upload_patterns = [
diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb
index fc73aa76dc..7ec4016dc1 100644
--- a/app/models/site_setting.rb
+++ b/app/models/site_setting.rb
@@ -106,14 +106,6 @@ class SiteSetting < ActiveRecord::Base
SiteSetting.manual_polling_enabled? || SiteSetting.pop3_polling_enabled?
end
- WATCHED_SETTINGS ||= [
- :default_locale,
- :blocked_attachment_content_types,
- :blocked_attachment_filenames,
- :allowed_unicode_username_characters,
- :markdown_typographer_quotation_marks
- ]
-
def self.blocked_attachment_content_types_regex
current_db = RailsMultisite::ConnectionManagement.current_db
@@ -193,8 +185,7 @@ class SiteSetting < ActiveRecord::Base
def self.whispers_allowed_group_ids
if SiteSetting.enable_whispers && SiteSetting.whispers_allowed_groups.present?
- # TODO (martin) Change to whispers_allowed_groups_map
- SiteSetting.whispers_allowed_groups.split("|").map(&:to_i)
+ SiteSetting.whispers_allowed_groups_map
else
[]
end
diff --git a/app/models/upload.rb b/app/models/upload.rb
index d11e79d073..ad004f6e66 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -34,6 +34,7 @@ class Upload < ActiveRecord::Base
attr_accessor :for_export
attr_accessor :for_site_setting
attr_accessor :for_gravatar
+ attr_accessor :validate_file_size
validates_presence_of :filesize
validates_presence_of :original_filename
@@ -92,6 +93,11 @@ class Upload < ActiveRecord::Base
.where("ur.upload_id IS NULL")
end
+ def initialize(*args)
+ super
+ self.validate_file_size = true
+ end
+
def to_s
self.url
end
diff --git a/app/models/user.rb b/app/models/user.rb
index d92780a13b..4045830007 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -138,7 +138,7 @@ class User < ActiveRecord::Base
after_create :set_default_sidebar_section_links
after_update :set_default_sidebar_section_links, if: Proc.new {
- self.saved_change_to_staged?
+ self.saved_change_to_staged? || self.saved_change_to_admin?
}
after_update :trigger_user_updated_event, if: Proc.new {
@@ -284,6 +284,16 @@ class User < ActiveRecord::Base
MAX_STAFF_DELETE_POST_COUNT ||= 5
+ def self.user_tips
+ @user_tips ||= Enum.new(
+ first_notification: 1,
+ topic_timeline: 2,
+ post_menu: 3,
+ topic_notification_levels: 4,
+ suggested_topics: 5,
+ )
+ end
+
def visible_sidebar_tags(user_guardian = nil)
user_guardian ||= guardian
DiscourseTagging.filter_visible(custom_sidebar_tags, user_guardian)
@@ -1944,7 +1954,7 @@ class User < ActiveRecord::Base
end
end
- SidebarSectionLink.insert_all!(records) if records.present?
+ SidebarSectionLink.insert_all(records) if records.present?
end
def stat
diff --git a/app/models/user_option.rb b/app/models/user_option.rb
index b65d28df8a..1bf0062d20 100644
--- a/app/models/user_option.rb
+++ b/app/models/user_option.rb
@@ -88,6 +88,8 @@ class UserOption < ActiveRecord::Base
self.title_count_mode = SiteSetting.default_title_count_mode
+ self.hide_profile_and_presence = SiteSetting.default_hide_profile_and_presence
+
true
end
diff --git a/app/models/username_validator.rb b/app/models/username_validator.rb
index 1bb51491e6..747eb2f777 100644
--- a/app/models/username_validator.rb
+++ b/app/models/username_validator.rb
@@ -138,6 +138,6 @@ class UsernameValidator
end
def self.allowed_char?(c)
- c.match?(/[\w.-]/) || c.match?(SiteSetting.allowed_unicode_username_characters)
+ c.match?(/[\w.-]/) || c.match?(SiteSetting.allowed_unicode_username_characters_regex)
end
end
diff --git a/app/serializers/admin_detailed_user_serializer.rb b/app/serializers/admin_detailed_user_serializer.rb
index aab939247c..454fcf6cbd 100644
--- a/app/serializers/admin_detailed_user_serializer.rb
+++ b/app/serializers/admin_detailed_user_serializer.rb
@@ -45,7 +45,7 @@ class AdminDetailedUserSerializer < AdminUserSerializer
has_many :groups, embed: :object, serializer: BasicGroupSerializer
def second_factor_enabled
- object.totp_enabled? || object.security_keys_enabled?
+ object.totp_enabled? || object.security_keys_enabled? || object.backup_codes_enabled?
end
def can_disable_second_factor
diff --git a/app/serializers/current_user_serializer.rb b/app/serializers/current_user_serializer.rb
index 0a607648e7..70920d01a5 100644
--- a/app/serializers/current_user_serializer.rb
+++ b/app/serializers/current_user_serializer.rb
@@ -170,7 +170,7 @@ class CurrentUserSerializer < BasicUserSerializer
end
def can_send_private_messages
- scope.can_send_private_message?(Discourse.system_user)
+ scope.can_send_private_messages?
end
def can_edit
@@ -293,7 +293,7 @@ class CurrentUserSerializer < BasicUserSerializer
end
def include_seen_popups?
- SiteSetting.enable_onboarding_popups
+ SiteSetting.enable_user_tips
end
def include_primary_group_id?
@@ -323,7 +323,7 @@ class CurrentUserSerializer < BasicUserSerializer
end
def second_factor_enabled
- object.totp_enabled? || object.security_keys_enabled?
+ object.totp_enabled? || object.security_keys_enabled? || object.backup_codes_enabled?
end
def featured_topic
diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb
index de82619c9f..30636fe61f 100644
--- a/app/serializers/site_serializer.rb
+++ b/app/serializers/site_serializer.rb
@@ -6,7 +6,7 @@ class SiteSerializer < ApplicationSerializer
:default_archetype,
:notification_types,
:post_types,
- :onboarding_popup_types,
+ :user_tips,
:trust_levels,
:groups,
:filters,
@@ -104,12 +104,12 @@ class SiteSerializer < ApplicationSerializer
Post.types
end
- def onboarding_popup_types
- OnboardingPopup.types
+ def user_tips
+ User.user_tips
end
- def include_onboarding_popup_types?
- SiteSetting.enable_onboarding_popups
+ def include_user_tips?
+ SiteSetting.enable_user_tips
end
def filters
diff --git a/app/serializers/user_card_serializer.rb b/app/serializers/user_card_serializer.rb
index a109e9ebbf..5e208169c7 100644
--- a/app/serializers/user_card_serializer.rb
+++ b/app/serializers/user_card_serializer.rb
@@ -141,7 +141,7 @@ class UserCardSerializer < BasicUserSerializer
# Needed because 'send_private_message_to_user' will always return false
# when the current user is being serialized
def can_send_private_messages
- scope.can_send_private_message?(Discourse.system_user)
+ scope.can_send_private_messages?
end
def can_send_private_message_to_user
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 0e9f975032..eb2db4e56e 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -105,7 +105,7 @@ class UserSerializer < UserCardSerializer
end
def second_factor_enabled
- object.totp_enabled? || object.security_keys_enabled?
+ object.totp_enabled? || object.security_keys_enabled? || object.backup_codes_enabled?
end
def include_second_factor_backup_enabled?
diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb
index ad7271c067..0083ff6955 100644
--- a/app/services/user_updater.rb
+++ b/app/services/user_updater.rb
@@ -181,11 +181,7 @@ class UserUpdater
end
if attributes.key?(:skip_new_user_tips)
- user.user_option.seen_popups = if user.user_option.skip_new_user_tips
- OnboardingPopup.types.values
- else
- nil
- end
+ user.user_option.seen_popups = user.user_option.skip_new_user_tips ? [-1] : nil
end
# automatically disable digests when mailing_list_mode is enabled
diff --git a/app/views/qunit/theme.html.erb b/app/views/qunit/theme.html.erb
index ec07b37e7b..35f98e8949 100644
--- a/app/views/qunit/theme.html.erb
+++ b/app/views/qunit/theme.html.erb
@@ -13,6 +13,7 @@
<%- end %>
<%= preload_script "test-support" %>
<%= preload_script "test-helpers" %>
+ <%= preload_script "test-site-settings" %>
<%= theme_translations_lookup %>
<%= theme_js_lookup %>
<%= theme_lookup("head_tag") %>
diff --git a/app/views/users/password_reset.html.erb b/app/views/users/password_reset.html.erb
index 9b9a5268f6..2cb06b1add 100644
--- a/app/views/users/password_reset.html.erb
+++ b/app/views/users/password_reset.html.erb
@@ -1,16 +1,20 @@
Nodig een lijst met gebruikers uit om uw community snel op gang te helpen. Bereid een CSV-bestand voor met ten minste één rij per e-mailadres van gebruikers die u wilt uitnodigen. De volgende door komma's gescheiden informatie kan worden verstrekt als u mensen aan groepen wilt toevoegen of ze naar een specifiek topic wilt sturen de eerste keer dat ze zich aanmelden.
-john@smith.com,eerste_groepsnaam;tweede_groepsnaam,topic_id-
Elk e-mailadres in uw geüploade CSV-bestand zal een uitnodiging opgestuurd krijgen en u zult deze later kunnen beheren.
+Nodig een lijst van gebruikers uit om je community snel op gang te helpen. Stel een CSV-bestand op met minimaal één rij per e-mailadres van gebruikers die je wilt uitnodigen. De volgende door komma's gescheiden gegevens kunnen worden verstrekt als je mensen aan groepen wilt toevoegen of ze naar een specifiek topic wilt sturen de eerste keer dat ze zich aanmelden.
+jan@pietersen.com,eerste_groepsnaam;tweede_groepsnaam,topic_id+
Elk e-mailadres in je geüploade CSV-bestand ontvangt een uitnodiging die je later kunt beheren.
progress: "%{progress}% geüpload..." - success: "Bestand succesvol geüpload. U ontvangt een melding zodra het proces is voltooid." - error: "Sorry, bestand dient de CSV-indeling te hebben." + success: "Bestand geüpload. Je ontvangt een melding zodra het proces is voltooid." + error: "Sorry, het bestand moet de CSV-indeling hebben." password: title: "Wachtwoord" too_short: "Uw wachtwoord is te kort." - common: "Dat wachtwoord wordt al te vaak gebruikt." - same_as_username: "Uw wachtwoord is hetzelfde als uw gebruikersnaam." - same_as_email: "Uw wachtwoord is hetzelfde als uw e-mailadres." + common: "Dat wachtwoord wordt te vaak gebruikt." + same_as_username: "Je wachtwoord is hetzelfde als je gebruikersnaam." + same_as_email: "Je wachtwoord is hetzelfde als je e-mailadres." ok: "Uw wachtwoord ziet er goed uit." - instructions: "minstens %{count} tekens" + instructions: "minimaal %{count} tekens" required: "Voer een wachtwoord in" summary: title: "Samenvatting" @@ -1512,7 +1511,7 @@ nl: one: "bericht gelezen" other: "berichten gelezen" bookmark_count: - one: "favoriet" + one: "bladwijzer" other: "bladwijzers" top_replies: "Topantwoorden" no_replies: "Nog geen antwoorden." @@ -1523,11 +1522,11 @@ nl: top_badges: "Topbadges" no_badges: "Nog geen badges." more_badges: "Meer badges" - top_links: "Topkoppelingen" - no_links: "Nog geen koppelingen." + top_links: "Toplinks" + no_links: "Nog geen links." most_liked_by: "Meest geliket door" most_liked_users: "Meest geliket" - most_replied_to_users: "Meest geantwoord op" + most_replied_to_users: "Meest op geantwoord" no_likes: "Nog geen likes." top_categories: "Topcategorieën" topics: "Topics" @@ -1544,7 +1543,7 @@ nl: title: title: "Titel" none: "(geen)" - instructions: "komt achter je gebruikersnaam" + instructions: "wordt weergegeven na je gebruikersnaam" flair: none: "(geen)" primary_group: @@ -1569,10 +1568,10 @@ nl: unknown: "Fout" not_found: "Pagina niet gevonden" desc: - network: "Controleer uw verbinding." + network: "Controleer je verbinding." network_fixed: "De verbinding lijkt te zijn hersteld." server: "Foutcode: %{status}" - forbidden: "U mag dat niet bekijken." + forbidden: "Je mag dat niet bekijken." not_found: "Oeps, de toepassing heeft geprobeerd een URL te laden die niet bestaat." unknown: "Er is iets misgegaan." buttons: @@ -1583,16 +1582,16 @@ nl: close: "sluiten" dismiss_error: "Fout negeren" close: "Sluiten" - assets_changed_confirm: "Deze website heeft zojuist een software-upgrade ontvangen. Nu de laatste versie downloaden?" - logout: "U bent afgemeld." + assets_changed_confirm: "Deze website heeft zojuist een software-upgrade gekregen. Nieuwste versie downloaden?" + logout: "Je bent afgemeld." refresh: "Vernieuwen" home: "Hoofdpagina" read_only_mode: - enabled: "Deze website bevindt zich in alleen-lezenmodus. U kunt doorgaan met browsen, maar berichten beantwoorden, likes geven en andere acties zijn momenteel uitgeschakeld." - login_disabled: "Aanmelden is uitgeschakeld zolang de website zich in alleen-lezenmodus bevindt." - logout_disabled: "Afmelden is uitgeschakeld zolang de website zich in alleen-lezenmodus bevindt." + enabled: "Deze website bevindt zich in de alleen-lezenmodus. Je kunt doorgaan met browsen, maar berichten beantwoorden, likes geven en andere acties zijn momenteel uitgeschakeld." + login_disabled: "Aanmelden is uitgeschakeld zolang de website zich in de alleen-lezenmodus bevindt." + logout_disabled: "Afmelden is uitgeschakeld zolang de website zich in de alleen-lezenmodus bevindt." too_few_topics_and_posts_notice_MF: >- - Laten we de discussie starten! Er {currentTopics, plural, one {is # topic} other {zijn # topics}} en {currentPosts, plural, one {# bericht} other {# berichten}}. Bezoekers hebben er meer nodig om te lezen en op te antwoorden – minstens {requiredTopics, plural, one {# topic} other {# topics}} en {requiredPosts, plural, one {# bericht} other {# berichten}} wordt aanbevolen. Alleen stafleden kunnen dit bericht zien. + Laten we de discussie starten! Er {currentTopics, plural, one {is # topic} other {zijn # topics}} en {currentPosts, plural, one {# bericht} other {# berichten}}. Bezoekers hebben er meer nodig om te lezen en op te antwoorden – minimaal {requiredTopics, plural, one {# topic} other {# topics}} en {requiredPosts, plural, one {# bericht} other {# berichten}} wordt aanbevolen. Alleen stafleden kunnen dit bericht zien. too_few_topics_notice_MF: >- Laten we de discussie starten! Er {currentTopics, plural, one {is # topic} other {zijn # topics}}. Bezoekers hebben er meer nodig om te lezen en op te antwoorden – minstens {requiredTopics, plural, one {# topic} other {# topics}} wordt aanbevolen. Alleen stafleden kunnen dit bericht zien. too_few_posts_notice_MF: >- @@ -1619,10 +1618,10 @@ nl: signup_cta: sign_up: "Registreren" hide_forever: "nee, bedankt" - hidden_for_session: "OK, we vragen het u morgen. U kunt ook altijd 'Inloggen' gebruiken om een account aan te maken." - intro: "Hallo! Zo te zien beleeft u plezier aan de discussie, maar hebt u zich nog niet voor een account geregistreerd." + hidden_for_session: "OK, we vragen het je morgen. Je kunt ook altijd 'Aanmelden' gebruiken om een account te maken." + intro: "Hallo! Zo te zien heb je plezier aan de discussie, maar heb je je nog niet geregistreerd voor een account." summary: - enabled_description: "U bekijkt een samenvatting van dit topic: de interessantste berichten volgens de gemeenschap." + enabled_description: "Je bekijkt een samenvatting van dit topic: de interessantste berichten volgens de community." description: one: "Er is %{count} antwoord." other: "Er zijn %{count} antwoorden." @@ -1630,21 +1629,21 @@ nl: enable: "Dit topic samenvatten" disable: "Alle berichten tonen" short_label: "Samenvatten" - short_title: "Bekijk een samenvatting van dit topic: de interessantste berichten volgens de gemeenschap" + short_title: "Bekijk een samenvatting van dit topic: de interessantste berichten volgens de community" deleted_filter: enabled_description: "Dit topic bevat verwijderde berichten, die zijn verborgen." - disabled_description: "Verwijderde berichten in het topic worden getoond." + disabled_description: "Verwijderde berichten in het topic worden weergegeven." enable: "Verwijderde berichten verbergen" - disable: "Verwijderde berichten tonen" + disable: "Verwijderde berichten weergeven" private_message_info: title: "Bericht" invite: "Anderen uitnodigen..." edit: "Toevoegen of verwijderen..." remove: "Verwijderen..." add: "Toevoegen..." - leave_message: "Weet u zeker dat u dit bericht wilt verlaten?" - remove_allowed_user: "Wilt u %{name} echt uit dit bericht verwijderen?" - remove_allowed_group: "Wilt u %{name} echt uit dit bericht verwijderen?" + leave_message: "Weet je zeker dat je dit bericht wilt verlaten?" + remove_allowed_user: "Weet je zeker dat je %{name} wilt verwijderen uit dit bericht?" + remove_allowed_group: "Weet je zeker dat je %{name} wilt verwijderen uit dit bericht?" leave: "Verlaten" remove_user: "Gebruiker verwijderen" email: "E-mailadres" @@ -1657,32 +1656,32 @@ nl: create_account: header_title: "Welkom!" subheader_title: "Laten we uw account maken" - disclaimer: "Door te registreren gaat u akkoord met het privacybeleid en de servicevoorwaarden." + disclaimer: "Door je te registreren, ga je akkoord met het privacybeleid en de gebruiksvoorwaarden." title: "Nieuw account maken" - failed: "Er is iets misgegaan; mogelijk is het e-mailadres al geregistreerd. Probeer de koppeling 'Wachtwoord vergeten'." + failed: "Er is iets misgegaan, mogelijk is het e-mailadres al geregistreerd. Probeer de link 'Wachtwoord vergeten'." forgot_password: - title: "Wachtwoord herinitialiseren" + title: "Wachtwoord herstellen" action: "Ik ben mijn wachtwoord vergeten" - invite: "Voer uw gebruikersnaam of e-mailadres in, en we sturen u een e-mail om uw wachtwoord opnieuw in te stellen." - reset: "Wachtwoord herinitialiseren" - complete_username: "Als een account overeenkomt met de gebruikersnaam %{username}, zou u spoedig een e-mail moeten ontvangen met instructies om uw wachtwoord opnieuw in te stellen." - complete_email: "Als een account overeenkomt met %{email}, zou u spoedig een e-mail moeten ontvangen met instructies om uw wachtwoord opnieuw in te stellen." - complete_username_found: "We hebben een account gevonden die met de gebruikersnaam %{username} overeenkomt. U zou spoedig een e-mail moeten ontvangen met instructies om uw wachtwoord opnieuw in te stellen." - complete_email_found: "We hebben een account gevonden die met het e-mailadres %{email} overeenkomt. U zou spoedig een e-mail moeten ontvangen met instructies om uw wachtwoord opnieuw in te stellen." + invite: "Voer je gebruikersnaam of e-mailadres in, dan sturen we je een e-mail om je wachtwoord te herstellen." + reset: "Wachtwoord herstellen" + complete_username: "Als een account overeenkomt met de gebruikersnaam %{username}, zou je binnen enkele ogenblikken een e-mail moeten ontvangen met instructies om je wachtwoord te herstellen." + complete_email: "Als een account overeenkomt met %{email}, zou je binnen enkele ogenblikken een e-mail moeten ontvangen met instructies om je wachtwoord te herstellen." + complete_username_found: "We hebben een account gevonden dat overeenkomt met de gebruikersnaam %{username}. Je zou binnen enkele ogenblikken een e-mail moeten ontvangen met instructies om je wachtwoord te herstellen." + complete_email_found: "We hebben een account gevonden dat overeenkomt met %{email}. Je zou binnen enkele ogenblikken een e-mail moeten ontvangen met instructies om je wachtwoord te herstellen." complete_username_not_found: "Geen account met de gebruikersnaam %{username} gevonden" complete_email_not_found: "Geen account met het e-mailadres %{email} gevonden" - help: "Komt de e-mail niet aan? Controleer eerst uw spammap.Weet u niet zeker welk e-mailadres u hebt gebruikt? Voer een e-mailadres in en we laten u weten of het hier bestaat.
Als u geen toegang meer hebt tot het e-mailadres van uw account, neem dan contact op met onze behulpzame staf.
" + help: "Geen e-mail? Controleer eerst je spammap.Weet je niet zeker welk e-mailadres je hebt gebruikt? Voer een e-mailadres in, dan laten we je weten of het bestaat hier.
Als je geen toegang meer hebt tot het e-mailadres van je account, neem dan contact op met onze behulpzame staf.
" button_ok: "OK" button_help: "Help" email_login: - link_label: "Een koppeling voor aanmelding e-mailen" - button_label: "met e-mail" - login_link: "Sla het wachtwoord over; mail me een inloglink" - emoji: "slot-emoji" - complete_username: "Als een account overeenkomt met de gebruikersnaam %{username}, zou u spoedig een e-mail met een koppeling voor aanmelding moeten ontvangen." - complete_email: "Als een account overeenkomt met %{email}, zou u spoedig een e-mail met een koppeling voor aanmelding moeten ontvangen." - complete_username_found: "We hebben een account gevonden die overeenkomt met de gebruikersnaam %{username}. U zou spoedig een e-mail met een koppeling voor aanmelding moeten ontvangen." - complete_email_found: "We hebben een account gevonden die overeenkomt met %{email}. U zou spoedig een e-mail met een koppeling voor aanmelding moeten ontvangen." + link_label: "E-mail mij een aanmeldlink" + button_label: "met e-mailadres" + login_link: "Wachtwoord overslaan, e-mail me een aanmeldlink" + emoji: "slotemoji" + complete_username: "Als een account overeenkomt met %{username}, zou je binnen enkele ogenblikken een e-mail met een aanmeldlink moeten ontvangen." + complete_email: "Als een account overeenkomt met %{email}, zou je binnen enkele ogenblikken een e-mail met een aanmeldlink moeten ontvangen." + complete_username_found: "We hebben een account gevonden dat overeenkomt met de gebruikersnaam %{username}. Je zou binnen enkele ogenblikken een e-mail met een aanmeldlink moeten ontvangen." + complete_email_found: "We hebben een account gevonden dat overeenkomt met %{email}. Je zou binnen enkele ogenblikken een e-mail met een aanmeldlink moeten ontvangen." complete_username_not_found: "Er komt geen account overeen met de gebruikersnaam %{username}" complete_email_not_found: "Er komt geen account overeen met %{email}" confirm_title: Doorgaan naar %{site_name} @@ -1697,42 +1696,42 @@ nl: second_factor_title: "Tweefactorauthenticatie" second_factor_description: "Voer de authenticatiecode van uw app in:" second_factor_backup: "Aanmelden met een back-upcode" - second_factor_backup_title: "Tweefactor-back-up" - second_factor_backup_description: "Voer één van uw back-upcodes in:" + second_factor_backup_title: "Back-up voor tweeledige verificatie" + second_factor_backup_description: "Voer één van je back-upcodes in:" second_factor: "Aanmelden met authenticator-app" security_key_alternative: "Andere manier proberen" - security_key_authenticate: "Authenticeren met beveiligingssleutel" - security_key_not_allowed_error: "Het authenticatieproces van de beveiligingssleutel had een time-out of is geannuleerd." - security_key_no_matching_credential_error: "Geen referenties gevonden in de opgegeven beveiligingssleutel." - security_key_support_missing_error: "Uw huidige apparaat of browser ondersteunt geen gebruik van beveiligingssleutels. Gebruik een andere methode." - email_placeholder: "E-mailadres / Gebruikersnaam" - caps_lock_warning: "Caps Lock staat aan" + security_key_authenticate: "Verifiëren met beveiligingssleutel" + security_key_not_allowed_error: "Het verificatieproces met beveiligingssleutel had een time-out of is geannuleerd." + security_key_no_matching_credential_error: "Geen aanmeldingsgegevens gevonden in de opgegeven beveiligingssleutel." + security_key_support_missing_error: "Uw huidige apparaat of browser ondersteunt het gebruik van beveiligingssleutels niet. Gebruik een andere methode." + email_placeholder: "E-mailadres / gebruikersnaam" + caps_lock_warning: "Caps Lock is ingeschakeld" error: "Onbekende fout" - cookies_error: "Het lijkt erop dat uw browser geen cookies toestaat. Als u ze niet eerst toestaat, kunt u zich misschien niet aanmelden." - rate_limit: "Wacht even voordat u zich opnieuw probeert aan te melden." - blank_username: "Voer uw e-mailadres of gebruikersnaam in." - blank_username_or_password: "Voer uw e-mailadres of gebruikersnaam en wachtwoord in." - reset_password: "Wachtwoord herinitialiseren" + cookies_error: "Het lijkt erop dat je browser geen cookies toestaat. Als je ze niet toestaat, kun je je mogelijk niet aanmelden." + rate_limit: "Wacht voordat je je opnieuw probeert aan te melden." + blank_username: "Voer je e-mailadres of gebruikersnaam in." + blank_username_or_password: "Voer je e-mailadres of gebruikersnaam en je wachtwoord in." + reset_password: "Wachtwoord herstellen" logging_in: "Aanmelden..." or: "Of" - authenticating: "Authenticeren..." - awaiting_activation: "Uw account wacht op activering; gebruik de koppeling 'Wachtwoord vergeten' om een nieuwe activeringsmail te ontvangen." - awaiting_approval: "Uw account is nog niet door een staflid goedgekeurd. U ontvangt een e-mail zodra dat is gebeurd." - requires_invite: "Sorry, toegang tot dit forum werkt alleen via uitnodiging." - not_activated: "U kunt zich nog niet aanmelden. We hebben een activeringsmail naar %{sentTo} gestuurd. Volg de instructies in dat e-mailbericht om uw account te activeren." - not_allowed_from_ip_address: "U kunt zich niet aanmelden vanaf dat IP-adres." - admin_not_allowed_from_ip_address: "U kunt zich niet aanmelden als beheerder vanaf dat IP-adres." - resend_activation_email: "Klik hier om de activeringsmail opnieuw te versturen." - omniauth_disallow_totp: "Uw account heeft tweefactorauthenticatie ingeschakeld. Meld u aan met uw wachtwoord." - resend_title: "Activeringsmail opnieuw versturen" + authenticating: "Verifiëren..." + awaiting_activation: "Uw account wacht op activering. Gebruik de link 'Wachtwoord vergeten' om een nieuwe activerings-e-mail te ontvangen." + awaiting_approval: "Je account is nog niet goedgekeurd door een staflid. Je ontvangt een e-mail zodra dat is gebeurd." + requires_invite: "Sorry, dit forum is alleen toegankelijk op uitnodiging." + not_activated: "Je kunt je nog niet aanmelden. We hebben een activerings-e-mail naar %{sentTo} gestuurd. Volg de instructies in de e-mail om je account te activeren." + not_allowed_from_ip_address: "Je kunt je niet aanmelden vanaf dat IP-adres." + admin_not_allowed_from_ip_address: "Je kunt je niet aanmelden als beheerder vanaf dat IP-adres." + resend_activation_email: "Klik hier om de activerings-e-mail opnieuw te sturen." + omniauth_disallow_totp: "Tweeledige verificatie is ingeschakeld voor je account. Meld je aan met je wachtwoord." + resend_title: "Activerings-e-mail opnieuw sturen" change_email: "E-mailadres wijzigen" - provide_new_email: "Geef een nieuw adres op en we sturen uw bevestigingsmail opnieuw." + provide_new_email: "Geef een nieuw adres op, dan sturen we je bevestigings-e-mail opnieuw." submit_new_email: "E-mailadres bijwerken" - sent_activation_email_again: "We hebben een nieuwe activeringsmail naar %{currentEmail} gestuurd. Het kan een aantal minuten duren voor deze aankomt; controleer ook uw spammap." - sent_activation_email_again_generic: "We hebben nog een activeringsmail gestuurd. Het kan een aantal minuten duren voordat deze aankomt; controleer ook uw spammap." - to_continue: "Meld u aan" - preferences: "U dient aangemeld te zijn om uw gebruikersvoorkeuren te wijzigen." - not_approved: "Uw account is nog niet goedgekeurd. U ontvangt een melding via e-mail zodra u zich kunt aanmelden." + sent_activation_email_again: "We hebben een nieuwe activerings-e-mail naar %{currentEmail} gestuurd. Het kan enkele minuten duren voordat deze aankomt. Controleer ook de spammap." + sent_activation_email_again_generic: "We hebben een nieuwe activerings-e-mail gestuurd. Het kan enkele minuten duren voordat deze aankomt. Controleer ook de spammap." + to_continue: "Meld je aan" + preferences: "Je moet aangemeld zijn om je gebruikersvoorkeuren te wijzigen." + not_approved: "Je account is nog niet goedgekeurd. Je ontvangt een melding via e-mail zodra je je kunt aanmelden." google_oauth2: name: "Google" twitter: @@ -1746,35 +1745,35 @@ nl: discord: name: "Discord" second_factor_toggle: - totp: "Een authenticator-app gebruiken" - backup_code: "Een back-upcode gebruiken" + totp: "Authenticator-app gebruiken" + backup_code: "Back-upcode gebruiken" invites: accept_title: "Uitnodiging" - emoji: "envelop-emoji" + emoji: "envelopemoji" welcome_to: "Welkom bij %{site_name}!" - invited_by: "U bent uitgenodigd door:" - social_login_available: "U kunt zich ook aanmelden met een willekeurige sociale aanmelding die dat e-mailadres gebruikt." - your_email: "Het e-mailadres van uw account is %{email}." + invited_by: "Je bent uitgenodigd door:" + social_login_available: "Je kunt je ook aanmelden met een sociale-media-account dat dat e-mailadres gebruikt." + your_email: "Het e-mailadres van je account is %{email}." accept_invite: "Uitnodiging accepteren" - success: "Uw account is gemaakt en u bent nu aangemeld." + success: "Je account is gemaakt en je bent aangemeld." name_label: "Naam" password_label: "Wachtwoord" password_reset: continue: "Doorgaan naar %{site_name}" emoji_set: - apple_international: "Apple/International" + apple_international: "Apple/internationaal" google: "Google" twitter: "Twitter" - win10: "Win10" + win10: "WIndows 10" google_classic: "Google Klassiek" facebook_messenger: "Facebook Messenger" category_page_style: categories_only: "Alleen categorieën" - categories_with_featured_topics: "Categorieën met aanbevolen topics" + categories_with_featured_topics: "Categorieën met uitgelichte topics" categories_and_latest_topics: "Categorieën en nieuwste topics" categories_and_top_topics: "Categorieën en toptopics" categories_boxes: "Vakken met subcategorieën" - categories_boxes_with_topics: "Vakken met aanbevolen topics" + categories_boxes_with_topics: "Vakken met uitgelichte topics" shortcut_modifier_key: shift: "Shift" ctrl: "Ctrl" @@ -1795,21 +1794,21 @@ nl: select_kit: delete_item: "%{name} verwijderen" filter_by: "Filteren op: %{name}" - select_to_filter: "Een waarde selecteren om te filteren" + select_to_filter: "Selecteer een waarde om te filteren" default_header_text: Selecteren... no_content: Geen overeenkomsten gevonden results_count: one: "%{count} resultaat" other: "%{count} resultaten" filter_placeholder: Zoeken... - filter_placeholder_with_any: Zoeken of aanmaken... - create: "Aanmaken: '%{content}'" + filter_placeholder_with_any: Zoeken of maken... + create: "Maken: '%{content}'" max_content_reached: - one: "U kunt maar %{count} item selecteren." - other: "U kunt maar %{count} items selecteren." + one: "Je kunt slechts %{count} item selecteren." + other: "Je kunt slechts %{count} items selecteren." min_content_not_reached: - one: "Selecteer minstens %{count} item." - other: "Selecteer minstens %{count} items." + one: "Selecteer minimaal %{count} item." + other: "Selecteer minimaal %{count} items." components: tag_drop: filter_for_more: Filteren voor meer... @@ -1819,30 +1818,30 @@ nl: from: Van to: Aan emoji_picker: - filter_placeholder: Zoeken naar emoji - smileys_&_emotion: Smileys en Emotie - people_&_body: Mensen en Lichaam - animals_&_nature: Dieren en Natuur - food_&_drink: Eten en Drinken - travel_&_places: Reizen en Plaatsen + filter_placeholder: Emoji zoeken + smileys_&_emotion: Smileys en emoji's + people_&_body: Mensen en lichaam + animals_&_nature: Dieren en natuur + food_&_drink: Eten en drinken + travel_&_places: Reizen en plaatsen activities: Activiteiten - objects: Objecten + objects: Voorwerpen symbols: Symbolen flags: Vlaggen - recent: Onlangs gebruikt + recent: Recent gebruikt default_tone: Geen huidskleur light_tone: Lichte huidskleur - medium_light_tone: Licht gemiddelde huidskleur + medium_light_tone: Gemiddeld lichte huidskleur medium_tone: Gemiddelde huidskleur - medium_dark_tone: Donker gemiddelde huidskleur + medium_dark_tone: Gemiddelde donkere huidskleur dark_tone: Donkere huidskleur - default: Eigen emoji + default: Eigen emoji's shared_drafts: title: "Gedeelde concepten" notice: "Dit topic is alleen zichtbaar voor degenen die gedeelde concepten kunnen publiceren." destination_category: "Bestemmingscategorie" publish: "Gedeeld concept publiceren" - confirm_publish: "Weet u zeker dat u dit concept wilt publiceren?" + confirm_publish: "Weet je zeker dat je dit concept wilt publiceren?" publishing: "Topic publiceren..." composer: emoji: "Emoji :)" @@ -1851,39 +1850,39 @@ nl: whisper: "fluisteren" unlist: "onzichtbaar" add_warning: "Dit is een officiële waarschuwing." - toggle_whisper: "Fluistermodus in-/uitschakelen" + toggle_whisper: "Fluisteren in-/uitschakelen" toggle_unlisted: "Onzichtbaar in-/uitschakelen" - posting_not_on_topic: "Op welk topic wilt u antwoorden?" + posting_not_on_topic: "Op welk topic wil je antwoorden?" saved_local_draft_tip: "lokaal opgeslagen" similar_topics: "Uw topic lijkt op..." drafts_offline: "concepten offline" edit_conflict: "bewerkingsconflict" group_mentioned_limit: - one: "Waarschuwing! U hebt %{group} genoemd, maar deze groep heeft meer leden dan de door een beheerder geconfigureerde limiet van %{count} gebruiker voor noemen. Niemand krijgt een melding." - other: "Waarschuwing! U hebt %{group} genoemd, maar deze groep heeft meer leden dan de door een beheerder geconfigureerde limiet van %{count} gebruikers voor noemen. Niemand krijgt een melding." + one: "Waarschuwing! Je hebt %{group} genoemd, maar deze groep heeft meer leden dan de door een beheerder geconfigureerde vermeldingslimiet van %{count} gebruiker. Niemand krijgt een melding." + other: "Waarschuwing! Je hebt %{group} genoemd, maar deze groep heeft meer leden dan de door een beheerder geconfigureerde vermeldingslimiet van %{count} gebruikers. Niemand krijgt een melding." group_mentioned: - one: "Door %{group} te vermelden, gaat u %{count} persoon op de hoogte brengen – weet u dit zeker?" - other: "Door %{group} te vermelden, gaat u %{count} personen op de hoogte brengen – weet u dit zeker?" - duplicate_link: "Het lijkt erop dat uw koppeling naar %{domain} al in het topic is geplaatst door @%{username} in een antwoord op %{ago} – weet u zeker dat u deze opnieuw wilt plaatsen?" + one: "Door %{group} te vermelden, krijgt %{count} persoon een melding. Weet je het zeker?" + other: "Door %{group} te vermelden, krijgen %{count} personen een melding. Weet je het zeker?" + duplicate_link: "Het lijkt erop dat je link naar %{domain} al in het topic is geplaatst door @%{username} in een antwoord op %{ago} – weet je zeker dat je deze opnieuw wilt plaatsen?" reference_topic_title: "RE: %{title}" error: title_missing: "Titel is vereist" title_too_short: - one: "Titel moet minstens %{count} teken bevatten." - other: "Titel moet minstens %{count} tekens bevatten." + one: "Titel moet minimaal %{count} teken lang zijn." + other: "Titel moet minimaal %{count} tekens lang zijn." title_too_long: - one: "Titel kan niet langer zijn dan %{count} teken" - other: "Titel kan niet langer zijn dan %{count} tekens" - post_missing: "Bericht kan niet leeg zijn" + one: "Titel mag niet langer zijn dan %{count} teken" + other: "Titel mag niet langer zijn dan %{count} tekens" + post_missing: "Bericht mag niet leeg zijn" post_length: - one: "Bericht moet minstens %{count} teken bevatten." - other: "Bericht moet minstens %{count} tekens bevatten." - try_like: "Hebt u de knop %{heart} geprobeerd?" - category_missing: "U moet een categorie kiezen" + one: "Bericht moet minimaal %{count} teken lang zijn." + other: "Bericht moet minimaal %{count} tekens lang zijn." + try_like: "Heb je de knop %{heart} geprobeerd?" + category_missing: "Je moet een categorie kiezen" tags_missing: - one: "U moet minstens %{count} tag kiezen" - other: "U moet minstens %{count} tags kiezen" - topic_template_not_modified: "Voeg details en specifieke kenmerken toe aan uw topic door de topicsjabloon te bewerken." + one: "Je moet minimaal %{count} tag kiezen" + other: "Je moet minimaal %{count} tags kiezen" + topic_template_not_modified: "Voeg details en specifieke informatie toe aan uw topic door de topicsjabloon te bewerken." save_edit: "Bewerking opslaan" overwrite_edit: "Bewerking overschrijven" reply_original: "Antwoorden op oorspronkelijke topic" @@ -1897,29 +1896,29 @@ nl: edit_shared_draft: "Gedeeld concept bewerken" title: "Of druk op %{modifier}Enter" title_placeholder: "Waar gaat deze discussie over in één korte zin?" - title_or_link_placeholder: "Typ de titel, of plak hier een koppeling" - edit_reason_placeholder: "vanwaar deze bewerking?" - topic_featured_link_placeholder: "Voer koppeling in die met titel wordt getoond." - remove_featured_link: "Koppeling uit topic verwijderen." + title_or_link_placeholder: "Typ de titel of plak hier een link" + edit_reason_placeholder: "waarom bewerk je?" + topic_featured_link_placeholder: "Voer de weergegeven link in met titel." + remove_featured_link: "Link verwijderen uit topic." reply_placeholder: "Typ hier. Gebruik Markdown, BBCode of HTML voor opmaak. Sleep of plak afbeeldingen." reply_placeholder_no_images: "Typ hier. Gebruik Markdown, BBCode of HTML voor opmaak." - reply_placeholder_choose_category: "Selecteer een categorie voordat u hier typt." + reply_placeholder_choose_category: "Selecteer een categorie voordat je hier typt." view_new_post: "Uw nieuwe bericht bekijken" saving: "Opslaan" saved: "Opgeslagen!" saved_draft: "Berichtconcept wordt uitgevoerd. Tik om te hervatten." uploading: "Uploaden..." - show_preview: "toon voorbeeld" - hide_preview: "verberg voorbeeld" + show_preview: "voorbeeld weergeven" + hide_preview: "voorbeeld verbergen" quote_post_title: "Hele bericht citeren" bold_label: "B" bold_title: "Vet" - bold_text: "Vetgedrukte tekst" + bold_text: "vetgedrukte tekst" italic_label: "I" italic_title: "Cursief" italic_text: "Cursieve tekst" link_title: "Hyperlink" - link_description: "voer hier een omschrijving in" + link_description: "voer hier een linkbeschrijving in" link_dialog_title: "Hyperlink invoegen" link_optional_text: "optionele titel" link_url_placeholder: "Plak een URL of typ om topics te zoeken" @@ -1944,12 +1943,12 @@ nl: hide_toolbar: "editorwerkbalk verbergen" modal_ok: "OK" modal_cancel: "Annuleren" - cant_send_pm: "Sorry, u kunt geen bericht naar %{username} sturen." + cant_send_pm: "Sorry, je kunt geen bericht naar %{username} sturen." yourself_confirm: - title: "Bent u ontvangers vergeten toe te voegen?" - body: "Het bericht wordt nu alleen naar uzelf verstuurd!" + title: "Ben je vergeten ontvangers toe te voegen?" + body: "Het bericht wordt nu alleen naar jezelf gestuurd!" slow_mode: - error: "Dit topic bevindt zich in de langzame modus. U hebt onlangs al gepost; U kunt opnieuw posten in %{timeLeft}." + error: "Dit topic bevindt zich in de langzame modus. Je hebt onlangs al geplaatst; je kunt opnieuw plaatsen over %{timeLeft}." admin_options_title: "Optionele stafinstellingen voor dit topic" composer_actions: reply: Antwoorden @@ -1961,27 +1960,27 @@ nl: reply_as_new_topic: label: Antwoorden als gekoppeld topic desc: Een nieuw topic maken dat aan dit topic is gekoppeld - confirm: U hebt een nieuw-topicconcept opgeslagen, dat wordt overschreven als u een gekoppeld topic maakt. + confirm: Je hebt een nieuw topicconcept opgeslagen, dat wordt overschreven als je een gekoppeld topic maakt. reply_as_new_group_message: label: Antwoorden als nieuw groepsbericht reply_to_topic: label: Antwoorden op topic - desc: Antwoorden op het topic, niet een bepaald bericht + desc: Antwoord op het topic, niet een bepaald bericht toggle_whisper: - label: Fluistermodus in-/uitschakelen + label: Fluisteren in-/uitschakelen desc: Fluisterberichten zijn alleen zichtbaar voor stafleden create_topic: label: "Nieuw topic" shared_draft: label: "Gedeeld concept" - desc: "Een concepttopic maken dat alleen zichtbaar is voor toegestane gebruikers" + desc: "Maak een topicconcept dat alleen zichtbaar is voor toegestane gebruikers" toggle_topic_bump: - label: "Topicbump in-/uitschakelen" + label: "Omhoog plaatsen van topic in-/uitschakelen" desc: "Antwoorden zonder datum van laatste antwoord te wijzigen" reload: "Opnieuw laden" ignore: "Negeren" image_alt_text: - aria_label: Alt-tekst voor afbeelding + aria_label: Alternatieve tekst voor afbeelding notifications: tooltip: regular: @@ -1993,11 +1992,11 @@ nl: high_priority: one: "%{count} ongelezen melding met hoge prioriteit" other: "%{count} ongelezen meldingen met hoge prioriteit" - title: "meldingen van @naam-vermeldingen, antwoorden op uw berichten en topics, berichten, etc." + title: "meldingen van @naamvermeldingen, antwoorden op je berichten en topics, berichten, enz." none: "Meldingen kunnen momenteel niet worden geladen." empty: "Geen meldingen gevonden." post_approved: "Uw bericht is goedgekeurd" - reviewable_items: "items die beoordeling nodig hebben" + reviewable_items: "items die moeten worden beoordeeld" watching_first_post_label: "Nieuw topic" mentioned: "%{username} %{description}" group_mentioned: "%{username} %{description}" @@ -2013,46 +2012,46 @@ nl: other: "%{username}, %{username2} en %{count} anderen %{description}" liked_by_2_users: "%{username}, %{username2}" liked_by_multiple_users: - one: "%{username}, %{username2} en %{count} anderen" + one: "%{username}, %{username2} en %{count} ander" other: "%{username}, %{username2} en %{count} anderen" liked_consolidated_description: - one: "heeft %{count} van uw berichten geliket" - other: "heeft %{count} van uw berichten geliket" + one: "heeft %{count} van je berichten geliket" + other: "heeft %{count} van je berichten geliket" liked_consolidated: "%{username} %{description}" private_message: "%{username} %{description}" invited_to_private_message: "%{username} %{description}" invited_to_topic: "%{username} %{description}" - invitee_accepted: "%{username} heeft uw uitnodiging geaccepteerd" + invitee_accepted: "%{username} heeft je uitnodiging geaccepteerd" moved_post: "%{username} heeft %{description} verplaatst" linked: "%{username} %{description}" - granted_badge: "'%{description}' ontvangen" + granted_badge: "'%{description}' verdiend" topic_reminder: "%{username} %{description}" - watching_first_post: "Nieuw Topic %{description}" - membership_request_accepted: "Lidmaatschap geaccepteerd in '%{group_name}'" + watching_first_post: "Nieuw topic %{description}" + membership_request_accepted: "Lidmaatschap geaccepteerd voor '%{group_name}'" membership_request_consolidated: - one: "%{count} open lidmaatschapsaanvraag voor '%{group_name}'" - other: "%{count} open lidmaatschapsaanvragen voor '%{group_name}'" + one: "%{count} openstaand lidmaatschapsverzoek voor '%{group_name}'" + other: "%{count} openstaande lidmaatschapsverzoeken voor '%{group_name}'" reaction: "%{username} %{description}" reaction_2: "%{username}, %{username2} %{description}" votes_released: "%{description} - voltooid" dismiss_confirmation: body: default: - one: "Weet u het zeker? U hebt %{count} belangrijke melding." - other: "Weet u het zeker? U hebt %{count} belangrijke meldingen." + one: "Weet je het zeker? Je hebt %{count} belangrijke melding." + other: "Weet je het zeker? Je hebt %{count} belangrijke meldingen." dismiss: "Negeren" cancel: "Annuleren" group_message_summary: - one: "%{count} bericht in uw Postvak IN voor %{group_name}" - other: "%{count} berichten in uw Postvak IN voor %{group_name}" + one: "%{count} bericht in je inbox voor %{group_name}" + other: "%{count} berichten in je inbox voor %{group_name}" popup: - mentioned: '%{username} heeft u genoemd in ''%{topic}'' - %{site_title}' - group_mentioned: '%{username} heeft u genoemd in ''%{topic}'' - %{site_title}' - quoted: '%{username} heeft u geciteerd in ''%{topic}'' - %{site_title}' - replied: '%{username} heeft op u geantwoord in ''%{topic}'' - %{site_title}' + mentioned: '%{username} heeft je genoemd in ''%{topic}'' - %{site_title}' + group_mentioned: '%{username} heeft je genoemd in ''%{topic}'' - %{site_title}' + quoted: '%{username} heeft je geciteerd in ''%{topic}'' - %{site_title}' + replied: '%{username} heeft op je geantwoord in ''%{topic}'' - %{site_title}' posted: '%{username} heeft een bericht geplaatst in ''%{topic}'' - %{site_title}' - private_message: '%{username} heeft u een persoonlijk bericht gestuurd in ''%{topic}'' - %{site_title}' - linked: '%{username} heeft een koppeling naar uw bericht geplaatst vanaf ''%{topic}'' - %{site_title}' + private_message: '%{username} heeft je een persoonlijk bericht gestuurd in ''%{topic}'' - %{site_title}' + linked: '%{username} heeft een link geplaatst naar je bericht in ''%{topic}'' - %{site_title}' watching_first_post: '%{username} heeft een nieuw topic gemaakt: ''%{topic}'' - %{site_title}' confirm_title: "Meldingen ingeschakeld - %{site_title}" confirm_body: "Gelukt! Meldingen zijn ingeschakeld." @@ -2068,7 +2067,7 @@ nl: invitee_accepted: "uitnodiging geaccepteerd" posted: "nieuw bericht" moved_post: "bericht verplaatst" - linked: "gekoppeld" + linked: "gelinkt" bookmark_reminder: "bladwijzerherinnering" bookmark_reminder_with_name: "bladwijzerherinnering - %{name}" granted_badge: "badge toegekend" @@ -2076,15 +2075,15 @@ nl: group_mentioned: "groep genoemd" group_message_summary: "nieuwe groepsberichten" watching_first_post: "nieuw topic" - topic_reminder: "topic-herinnering" + topic_reminder: "topicherinnering" liked_consolidated: "nieuwe likes" post_approved: "bericht goedgekeurd" - membership_request_consolidated: "nieuwe lidmaatschapsaanvragen" + membership_request_consolidated: "nieuwe lidmaatschapsverzoeken" reaction: "nieuwe reactie" votes_released: "Stem is vrijgegeven" upload_selector: uploading: "Uploaden" - select_file: "Bestand selecteren" + select_file: "Selecteer bestand" default_image_alt_text: afbeelding search: sort_by: "Sorteren op" @@ -2098,39 +2097,39 @@ nl: too_short: "Uw zoekterm is te kort." clear_search: "Zoekopdracht wissen" result_count: - one: "%{count} resultaat voor%{term}" - other: "%{count}%{plus} resultaten voor%{term}" + one: "%{count} resultaat voor %{term}" + other: "%{count}%{plus} resultaten voor %{term}" title: "Zoeken" full_page_title: "Zoeken" no_results: "Geen resultaten gevonden." - no_more_results: "Geen resultaten meer gevonden." - post_format: "#%{post_number} door %{username}" + no_more_results: "Geen verdere resultaten gevonden." + post_format: "#%{post_number} van %{username}" results_page: "Zoekresultaten voor '%{term}'" - more_results: "Er zijn meer resultaten. Verfijn uw zoekcriteria." - cant_find: "Kunt u niet vinden wat u zoekt?" + more_results: "Er zijn meer resultaten. Verfijn je zoekcriteria." + cant_find: "Kun je niet vinden wat je zoekt?" start_new_topic: "Misschien een nieuw topic starten?" - or_search_google: "Of probeer in plaats hiervan te zoeken met Google:" - search_google: "Probeer in plaats hiervan te zoeken met Google:" + or_search_google: "Of probeer in plaats daarvan te zoeken met Google:" + search_google: "Probeer in plaats daarvan te zoeken met Google:" search_google_button: "Google" search_button: "Zoeken" categories: "Categorieën" tags: "Tags" in: "in" - in_this_topic: "in dit onderwerp" + in_this_topic: "in dit topic" in_topics_posts: "in alle topics en berichten" enter_hint: "of druk op Enter" in_posts_by: "In berichten van @%{username}" type: - default: "Onderwerpen/berichten" + default: "Topics/berichten" users: "Gebruikers" categories: "Categorieën" categories_and_tags: "Categorieën/tags" context: - user: "Berichten van @%{username} doorzoeken" - category: "De categorie #%{category} doorzoeken" - tag: "De tag #%{tag} doorzoeken" - topic: "Dit topic doorzoeken" - private_messages: "Berichten doorzoeken" + user: "Zoeken in berichten van @%{username}" + category: "Zoeken in de categorie #%{category}" + tag: "Zoeken in de tag #%{tag}" + topic: "Zoeken in dit topic" + private_messages: "Zoeken in berichten" tips: category_tag: "filtert op categorie of tag" full_search: "start zoeken op volledige pagina" @@ -2146,14 +2145,14 @@ nl: label: Met badge with_tags: label: Getagd - aria_label: Filter met tags + aria_label: Filteren met tags filters: label: Alleen topics/berichten weergeven... - title: Die alleen met de titel overeenkomen + title: Alleen matchen in titel likes: die ik heb geliket posted: waarin ik iets heb geplaatst - created: die ik heb aangemaakt - watching: die ik in de gaten houd + created: die ik heb gemaakt + watching: die ik observeer tracking: die ik volg private: In mijn berichten bookmarks: Ik heb een bladwijzer gemaakt @@ -2192,13 +2191,13 @@ nl: placeholder: maximaal aria_label: filteren op maximale weergaven additional_options: - label: "Filteren op aantal berichten en onderwerpweergaven" + label: "Filteren op aantal berichten en topicweergaven" hamburger_menu: "menu" new_item: "nieuw" go_back: "terug" not_logged_in_user: "gebruikerspagina met samenvatting van huidige activiteit en voorkeuren" current_user: "naar uw gebruikerspagina" - view_all: "Alle %{tab} bekijken" + view_all: "alle %{tab} weergeven" user_menu: tabs: replies: "Antwoorden" @@ -2218,85 +2217,85 @@ nl: dismiss: "Negeren" dismiss_read: "Alle ongelezen negeren" dismiss_read_with_selected: - one: "%{count} ongelezen onderwerp negeren" - other: "%{count} ongelezen onderwerpen negeren" + one: "%{count} ongelezen topic negeren" + other: "%{count} ongelezen topics negeren" dismiss_button: "Negeren..." dismiss_button_with_selected: one: "Negeren (%{count})…" other: "Negeren (%{count})…" - dismiss_tooltip: "Alleen nieuwe berichten negeren of het volgen van topics stoppen" - also_dismiss_topics: "Het volgen van deze topics stoppen, zodat ze nooit meer als ongelezen verschijnen." - dismiss_new: "Nieuwe berichten negeren" + dismiss_tooltip: "Negeer alleen nieuwe berichten of stop het volgen van topics" + also_dismiss_topics: "Volgen van deze topics stoppen, zodat ze nooit meer als ongelezen verschijnen voor mij" + dismiss_new: "Nieuwe negeren" dismiss_new_with_selected: - one: "Negeer nieuw topic (%{count})" - other: "Negeer nieuwe topics (%{count})" + one: "Nieuwe negeren (%{count})" + other: "Nieuwe negeren (%{count})" toggle: "bulkselectie van topics in-/uitschakelen" actions: "Bulkacties" close_topics: "Topics sluiten" archive_topics: "Topics archiveren" - move_messages_to_inbox: "Naar Postvak IN verplaatsen" + move_messages_to_inbox: "Verplaatsen naar inbox" notification_level: "Meldingen..." - change_notification_level: "Wijzig meldingsniveau" + change_notification_level: "Meldingsniveau wijzigen" choose_new_category: "Kies de nieuwe categorie voor de topics:" selected: - one: "U hebt %{count} topic geselecteerd." - other: "U hebt %{count} topics geselecteerd." + one: "Je hebt %{count} topic geselecteerd." + other: "Je hebt %{count} topics geselecteerd." change_tags: "Tags vervangen" append_tags: "Tags toevoegen" choose_new_tags: "Kies nieuwe tags voor deze topics:" - choose_append_tags: "Kies nieuwe tags om voor deze topics toe te voegen:" + choose_append_tags: "Kies nieuwe tags om toe te voegen voor deze topics:" changed_tags: "De tags van deze topics zijn gewijzigd." - remove_tags: "Verwijder alle tags" + remove_tags: "Alle tags verwijderen" confirm_remove_tags: - one: "Alle tags zullen verwijderd worden uit dit topic. Weet u het zeker?" - other: "Alle tags zullen verwijderd worden uit %{count} topics. Weet u het zeker?" + one: "Alle tags worden verwijderd van dit topic. Weet je het zeker?" + other: "Alle tags worden verwijderd van %{count} topics. Weet je het zeker?" progress: one: "Voortgang: %{count} topic" other: "Voortgang: %{count} topics" none: - unread: "U hebt geen ongelezen topics." - unseen: "U hebt geen ongelezen topics." - new: "U hebt geen nieuwe topics." - read: "U hebt nog geen topics gelezen." - posted: "U hebt nog niet in een topic gereageerd." - latest: "U bent helemaal bij!" - bookmarks: "U hebt nog geen bladwijzers voor topics gemaakt." + unread: "Je hebt geen ongelezen topics." + unseen: "Je hebt geen ongeziene topics." + new: "Je hebt geen nieuwe topics." + read: "Je hebt nog geen topics gelezen." + posted: "Je hebt nog geen berichten geplaatst in een topic." + latest: "Je bent helemaal bij!" + bookmarks: "Je hebt nog geen bladwijzers voor topics gemaakt." category: "Er zijn geen topics in %{category}." top: "Er zijn geen toptopics." educate: - new: '
Hier verschijnen uw nieuwe topics. Standaard worden topics als nieuw beschouwd en verschijnt de indicator als deze in de afgelopen 2 dagen zijn aangemaakt.
Bezoek uw voorkeuren om dit te wijzigen.
' - unread: "Uw ongelezen topics verschijnen hier.
Standaard worden topics als ongelezen beschouwd en zullen ze het aantal ongelezen berichten laten zien 1 als u:
Of als u het topic expliciet heeft ingesteld op \"Gevolgd\" of \"In de gaten gehouden\" via de \U0001F514 in elk topic.
Bezoek uw voorkeuren om dit te wijzigen.
" + new: 'Hier worden je nieuwe topics weergegeven. Standaard worden topics als nieuw beschouwd en wordt de indicator weergegeven als ze de afgelopen 2 dagen zijn gemaakt.
Ga naar je voorkeuren om dit te wijzigen.
' + unread: "Je ongelezen topics worden hier weergegeven.
Standaard worden topics als ongelezen beschouwd en wordt het aantal ongelezen berichten aangegeven 1 als je:
Of als je het topic expliciet hebt ingesteld op \"Gevolgd\" of \"Geobserveerd\" via de \U0001F514 in elk topic.
Ga naar je voorkeuren om dit te wijzigen.
" bottom: - latest: "Er zijn geen nieuwste topics meer." - posted: "Er zijn geen topics meer geplaatst." - read: "Er zijn geen gelezen topics meer." - new: "Er zijn geen nieuwe topics meer." - unread: "Er zijn geen ongelezen topics meer." - unseen: "Er zijn geen ongelezen topics meer." - category: "Er zijn geen topics meer in %{category}." - tag: "Er zijn geen topics meer in %{tag}." - top: "Er zijn geen toptopics meer." - bookmarks: "Er zijn geen topics met bladwijzers meer." + latest: "Er zijn verder geen nieuwste topics." + posted: "Er zijn verder geen geplaatste topics." + read: "Er zijn verder geen gelezen topics." + new: "Er zijn verder geen nieuwe topics." + unread: "Er zijn verder geen ongelezen topics." + unseen: "Er zijn verder geen ongeziene topics." + category: "Er zijn verder geen topics in %{category}." + tag: "Er zijn verder geen topics over %{tag}." + top: "Er zijn verder geen toptopics." + bookmarks: "Er zijn verder geen topics met bladwijzers." topic: filter_to: one: "%{count} bericht in topic" other: "%{count} berichten in topic" create: "Nieuw topic" - create_long: "Een nieuw topic maken" + create_long: "Nieuw topic maken" open_draft: "Concept openen" - private_message: "Een bericht sturen" + private_message: "Bericht sturen" archive_message: - help: "Bericht naar uw archief verplaatsen" + help: "Bericht verplaatsen naar je archief" title: "Archiveren" move_to_inbox: - title: "Verplaatsen naar Postvak IN" - help: "Bericht weer naar Postvak IN verplaatsen" + title: "Verplaatsen naar inbox" + help: "Bericht terug naar inbox verplaatsen" edit_message: - help: "Het eerste bericht van het bericht bewerken" + help: "Eerste bericht bewerken" title: "Bewerken" defer: help: "Markeren als ongelezen" - title: "Negeren" + title: "Uitstellen" list: "Topics" new: "nieuw topic" unread: "ongelezen" @@ -2309,8 +2308,8 @@ nl: title: "Topic" invalid_access: title: "Topic is privé" - description: "Sorry, u hebt geen toegang tot dat topic!" - login_required: "U dient zich aan te melden om dat topic te zien." + description: "Sorry, je hebt geen toegang tot dat topic!" + login_required: "Je moet je aanmelden om dat topic te zien." server_error: title: "Laden van topic is mislukt" description: "Sorry, we konden dit topic niet laden, mogelijk door een verbindingsprobleem. Probeer het opnieuw. Als het probleem zich blijft voordoen, laat het ons dan weten." @@ -2318,22 +2317,22 @@ nl: title: "Topic niet gevonden" description: "Sorry, we konden het opgevraagde topic niet vinden. Misschien is het verwijderd door een moderator?" unread_posts: - one: "u hebt %{count} ongelezen bericht in dit topic" - other: "u hebt %{count} ongelezen berichten in dit topic" + one: "Je hebt %{count} ongelezen bericht in dit topic" + other: "Je hebt %{count} ongelezen berichten in dit topic" likes: one: "er is %{count} like in dit topic" other: "er zijn %{count} likes in dit topic" back_to_list: "Terug naar topiclijst" options: "Topic-opties" - show_links: "koppelingen binnen dit topic tonen" - read_more_in_category: "Wilt u meer lezen? U kunt door andere topics in %{catLink} bladeren, of de %{latestLink}." - read_more: "Wilt u meer lezen? %{catLink} of %{latestLink}." + show_links: "links binnen dit topic weergeven" + read_more_in_category: "Wil je meer lezen? Je kunt bladeren door andere topics in %{catLink} of %{latestLink}." + read_more: "WIl je meer lezen? %{catLink} of %{latestLink}." unread_indicator: "Nog geen enkel lid heeft het laatste bericht van dit topic gelezen." read_more_MF: "Er { UNREAD, plural, =0 {} one { is # ongelezen } other { zijn # ongelezen } } { NEW, plural, =0 {} one { {BOTH, select, true{en } false {is } other{}} # nieuw topic} other { {BOTH, select, true{en } false {zijn } other{}} # nieuwe topics} } over, of {CATEGORY, select, true {bekijk andere topics in {catLink}} false {{latestLink}} other {}}" bumped_at_title_MF: "{FIRST_POST}: {CREATED_AT}\n{LAST_POST}: {BUMPED_AT}" browse_all_categories: Alle categorieën bekijken browse_all_tags: Alle tags bekijken - view_latest_topics: nieuwste topics bekijken + view_latest_topics: nieuwste topics weergeven jump_reply_up: naar eerder antwoord springen jump_reply_down: naar later antwoord springen deleted: "Het topic is verwijderd" @@ -2371,9 +2370,9 @@ nl: publish_to: "Publiceren naar:" when: "Wanneer:" time_frame_required: "Selecteer een tijdsbestek" - min_duration: "Tijdsduur moet langer zijn dan 0" - max_duration: "Tijdsduur moet minder dan 20 jaar zijn" - duration: "Tijdsduur" + min_duration: "Duur moet langer zijn dan 0" + max_duration: "Duur moet minder dan 20 jaar zijn" + duration: "Duur" publish_to_category: title: "Publicatie plannen" temp_open: @@ -2384,26 +2383,26 @@ nl: title: "Topic automatisch sluiten" label: "Sluit topic automatisch na:" error: "Voer een geldige waarde in." - based_on_last_post: "Pas sluiten als het laatste bericht in het topic minstens zo oud is" + based_on_last_post: "Pas sluiten als het laatste bericht in het topic minimaal zo oud is" auto_close_after_last_post: title: "Topic automatisch sluiten na laatste bericht" auto_delete: title: "Topic automatisch verwijderen" auto_bump: - title: "Topic automatisch bumpen" + title: "Topic automatisch omhoog plaatsen" reminder: title: "Mij herinneren" auto_delete_replies: title: "Antwoorden automatisch verwijderen" status_update_notice: - auto_open: "Dit topic wordt %{timeLeft} automatisch geopend." - auto_close: "Dit topic wordt %{timeLeft} automatisch gesloten." - auto_publish_to_category: "Dit topic wordt %{timeLeft} naar #%{categoryName} gepubliceerd." + auto_open: "Dit topic wordt automatisch geopend over %{timeLeft}." + auto_close: "Dit topic wordt automatisch gesloten over %{timeLeft}." + auto_publish_to_category: "Dit topic wordt over %{timeLeft} gepubliceerd in #%{categoryName}." auto_close_after_last_post: "Dit topic wordt %{duration} na het laatste antwoord gesloten." - auto_delete: "Dit topic wordt %{timeLeft} automatisch verwijderd." - auto_bump: "Dit topic wordt %{timeLeft} automatisch gebumpt." - auto_reminder: "U wordt %{timeLeft} aan dit topic herinnerd." - auto_delete_replies: "Antwoorden op dit topic worden na %{duration} automatisch verwijderd." + auto_delete: "Dit topic wordt automatisch verwijderd over %{timeLeft} ." + auto_bump: "Dit topic wordt automatisch omhoog geplaatst over %{timeLeft}." + auto_reminder: "Je wordt herinnerd aan dit topic herinnerd over %{timeLeft}." + auto_delete_replies: "Antwoorden op dit topic worden automatisch verwijderd na %{duration}." auto_close_title: "Instellingen voor automatisch sluiten" auto_close_immediate: one: "Het laatste bericht in dit topic is al %{count} uur oud, dus het topic wordt meteen gesloten." @@ -2413,7 +2412,7 @@ nl: other: "Het laatste bericht in dit topic is al %{count} uur oud, dus het topic wordt gesloten." timeline: back: "Terug" - back_description: "Terug naar uw laatste ongelezen bericht" + back_description: "Ga terug naar je laatste ongelezen bericht" replies_short: "%{current} / %{total}" progress: title: topicvoortgang @@ -2422,51 +2421,51 @@ nl: one: "van %{count} bericht" other: "van %{count} berichten" jump_prompt_long: "Springen naar..." - jump_prompt_to_date: "tot datum" + jump_prompt_to_date: "naar datum" jump_prompt_or: "of" notifications: - title: wijzigen hoe vaak u meldingen over dit topic ontvangt + title: wijzig hoe vaak je meldingen over dit topic ontvangt reasons: - mailing_list_mode: "U hebt de mailinglijstmodus ingeschakeld, dus u ontvangt meldingen over antwoorden op dit topic via e-mail." - "3_10": "U ontvangt meldingen, omdat u een tag in dit topic in de gaten houdt." - "3_6": "U ontvangt meldingen, omdat u deze categorie in de gaten houdt." - "3_5": "U ontvangt meldingen, omdat u dit topic automatisch in de gaten houdt." - "3_2": "U ontvangt meldingen, omdat u dit topic in de gaten houdt." - "3_1": "U ontvangt meldingen, omdat u dit topic hebt aangemaakt." - "3": "U ontvangt meldingen, omdat u dit topic in de gaten houdt." - "2_8": "U ziet het aantal nieuwe antwoorden, omdat u deze categorie volgt." - "2_4": "U ziet het aantal nieuwe antwoorden, omdat u een antwoord in dit topic hebt geplaatst." - "2_2": "U ziet het aantal nieuwe antwoorden, omdat u dit topic volgt." - "2": 'U ziet een aantal nieuwe antwoorden, omdat u dit topic hebt gelezen.' - "1_2": "U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." - "1": "U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." - "0_7": "U negeert alle meldingen in deze categorie." - "0_2": "U negeert alle meldingen in dit topic." - "0": "U negeert alle meldingen in dit topic." + mailing_list_mode: "Je hebt de mailinglijstmodus ingeschakeld, dus je ontvangt meldingen over antwoorden op dit topic via e-mail." + "3_10": "Je ontvangt meldingen omdat je een tag in dit topic observeert." + "3_6": "Je ontvangt meldingen omdat je deze categorie observeert." + "3_5": "Je ontvangt meldingen omdat je dit topic automatisch observeert." + "3_2": "Je ontvangt meldingen omdat je dit topic observeert." + "3_1": "Je ontvangt meldingen omdat je dit topic hebt gemaakt." + "3": "Je ontvangt meldingen omdat je dit topic observeert." + "2_8": "Je ziet het aantal nieuwe antwoorden omdat je deze categorie volgt." + "2_4": "Je ziet het aantal nieuwe antwoorden omdat je een antwoord in dit topic hebt geplaatst." + "2_2": "Je ziet het aantal nieuwe antwoorden omdat je dit topic volgt." + "2": 'Je ziet een aantal nieuwe antwoorden omdat je dit topic hebt gelezen.' + "1_2": "Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." + "1": "Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." + "0_7": "Je negeert alle meldingen in deze categorie." + "0_2": "Je negeert alle meldingen in dit topic." + "0": "Je negeert alle meldingen in dit topic." watching_pm: - title: "In de gaten houden" - description: "U ontvangt een melding voor elk nieuw antwoord op dit bericht, en het aantal nieuwe antwoorden wordt weergegeven." + title: "Geobserveerd" + description: "Je ontvangt een melding voor elk nieuw antwoord op dit bericht en het aantal nieuwe antwoorden wordt weergegeven." watching: - title: "In de gaten houden" - description: "U ontvangt een melding voor elk nieuw antwoord in dit topic, en het aantal nieuwe antwoorden wordt weergegeven." + title: "Geobserveerd" + description: "Je ontvangt een melding voor elk nieuw antwoord in dit topic en het aantal nieuwe antwoorden wordt weergegeven." tracking_pm: title: "Volgen" - description: "Het aantal nieuwe antwoorden op dit bericht wordt weergegeven. U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." + description: "Het aantal nieuwe antwoorden op dit bericht wordt weergegeven. Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." tracking: title: "Volgen" - description: "Het aantal nieuwe antwoorden op dit topic wordt weergegeven. U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." + description: "Het aantal nieuwe antwoorden op dit topic wordt weergegeven. Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." regular: title: "Normaal" - description: "U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." + description: "Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." regular_pm: title: "Normaal" - description: "U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." + description: "Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." muted_pm: title: "Genegeerd" - description: "U ontvangt geen enkele melding over dit bericht." + description: "Je ontvangt geen meldingen over dit bericht." muted: title: "Genegeerd" - description: "U ontvangt geen enkele melding over dit topic, en het verschijnt niet in Nieuwste." + description: "Je ontvangt geen meldingen over dit topic en het wordt niet weergegeven in Nieuwste." actions: title: "Acties" recover: "Topic verwijderen ongedaan maken" @@ -2491,9 +2490,9 @@ nl: title: "Antwoorden" help: "beginnen met opstellen van een antwoord op dit topic" share: - extended_title: "Een koppeling delen" - help: "een koppeling naar dit topic delen" - instructions: "Een koppeling naar dit topic delen:" + extended_title: "Deel een link" + help: "deel een link naar dit topic" + instructions: "Deel een link naar dit topic:" copied: "Topiclink gekopieerd." invite_users: "Uitnodigen" print: @@ -2502,13 +2501,13 @@ nl: flag_topic: title: "Markeren" help: "een privémarkering aan dit topic geven of er een privébericht over sturen" - success_message: "U hebt dit topic succesvol gemarkeerd." + success_message: "Je hebt dit topic gemarkeerd." make_public: title: "Converteren naar openbaar topic" choose_category: "Kies een categorie voor het openbare topic:" feature_topic: title: "Dit topic aanbevelen" - pin: "Dit topic boven in de categorie %{categoryLink} laten verschijnen tot" + pin: "Laat dit topic bovenaan de categorie %{categoryLink} weergeven tot" unpin: "Verwijder dit topic uit de bovenkant van de categorie %{categoryLink}." unpin_until: "Verwijder dit topic uit de bovenkant van de categorie %{categoryLink} of wacht tot %{until}." pin_note: "Gebruikers kunnen het topic individueel voor zichzelf losmaken." @@ -2517,10 +2516,10 @@ nl: already_pinned: one: "Momenteel vastgemaakte topics in %{categoryLink}: %{count}" other: "Momenteel vastgemaakte topics in %{categoryLink}: %{count}." - pin_globally: "Dit topic bovenaan in alle topiclijsten laten verschijnen tot" + pin_globally: "Laat dit topic bovenaan alle topiclijsten weergeven tot" confirm_pin_globally: - one: "U hebt al %{count} globaal vastgemaakt topic. Te veel vastgemaakte topics kunnen storend zijn voor nieuwe en anonieme gebruikers. Weet u zeker dat u nog een topic globaal wilt vastmaken?" - other: "U hebt al %{count} globaal vastgemaakte topics. Te veel vastgemaakte topics kunnen storend zijn voor nieuwe en anonieme gebruikers. Weet u zeker dat u nog een topic globaal wilt vastmaken?" + one: "Je hebt al %{count} globaal vastgemaakt topic. Te veel vastgemaakte topics kunnen storend zijn voor nieuwe en anonieme gebruikers. Weet je zeker dat je nog een topic globaal wilt vastmaken?" + other: "Je hebt al %{count} globaal vastgemaakte topics. Te veel vastgemaakte topics kunnen storend zijn voor nieuwe en anonieme gebruikers. Weet je zeker dat je nog een topic globaal wilt vastmaken?" unpin_globally: "Verwijder dit topic uit de bovenkant van alle topiclijsten." unpin_globally_until: "Verwijder dit topic uit de bovenkant van alle topiclijsten of wacht tot %{until}." global_pin_note: "Gebruikers kunnen het topic individueel voor zichzelf losmaken." @@ -2528,8 +2527,8 @@ nl: already_pinned_globally: one: "Momenteel globaal vastgemaakte topics: %{count}" other: "Momenteel globaal vastgemaakte topics: %{count}." - make_banner: "Dit topic omzetten naar een banner die bovenaan alle pagina's verschijnt" - remove_banner: "De banner die bovenaan alle pagina's verschijnt verwijderen" + make_banner: "Zet dit topic om in een banner die bovenaan alle pagina's wordt weergegeven." + remove_banner: "Verwijder de banner die bovenaan alle pagina's wordt weergegeven." banner_note: "Gebruikers kunnen de banner negeren door deze te sluiten. Er kan maar één bannertopic tegelijk bestaan." no_banner_exists: "Er is geen bannertopic." banner_exists: "Er is momenteel een bannertopic." @@ -2551,14 +2550,14 @@ nl: username_placeholder: "gebruikersnaam" action: "Uitnodiging sturen" help: "anderen uitnodigen voor dit topic via e-mail of meldingen" - to_forum: "We sturen een kort mailtje waarmee uw vriend(in) direct kan deelnemen door op een koppeling te klikken." + to_forum: "We sturen een korte e-mail waarmee uw vriend(in) direct kan deelnemen door op een link te klikken." discourse_connect_enabled: "Voer de gebruikersnaam in van de persoon die u voor dit topic wilt uitnodigen." to_topic_blank: "Voer de gebruikersnaam of het e-mailadres in van de persoon die u voor dit topic wilt uitnodigen." - to_topic_email: "U hebt een e-mailadres ingevoerd. We sturen een uitnodiging per e-mail waarmee uw vriend(in) direct op dit topic kan antwoorden." - to_topic_username: "U hebt een gebruikersnaam ingevoerd. We sturen een melding met een uitnodigingskoppeling om aan dit topic deel te nemen." - to_username: "Voer de gebruikersnaam in van de persoon die u wilt uitnodigen. We sturen een melding met een uitnodigingskoppeling om aan dit topic deel te nemen." + to_topic_email: "Je hebt een e-mailadres ingevoerd. We sturen een uitnodiging per e-mail waarmee uw vriend(in) direct op dit topic kan antwoorden." + to_topic_username: "Je hebt een gebruikersnaam ingevoerd. We sturen een melding met een uitnodigingslink om aan dit topic deel te nemen." + to_username: "Voer de gebruikersnaam in van de persoon die u wilt uitnodigen. We sturen een melding met een uitnodigingslink om aan dit topic deel te nemen." email_placeholder: "name@example.com" - success_email: "We hebben een uitnodiging verstuurd naar %{invitee}. We brengen u op de hoogte wanneer de uitnodiging is afgegeven. Controleer het tabblad Uitnodigingen op uw gebruikerspagina om uw uitnodigingen bij te houden." + success_email: "We hebben een uitnodiging gestuurd naar %{invitee}. We laten het je weten wanneer de uitnodiging wordt ingewisseld. Controleer het tabblad Uitnodigingen op je gebruikerspagina om je uitnodigingen bij te houden." success_username: "We hebben die gebruiker uitgenodigd om aan dit topic deel te nemen." error: "Sorry, we konden deze persoon niet uitnodigen. Misschien is deze al uitgenodigd? (Uitnodigingen zijn in aantal beperkt)" success_existing_email: "Er bestaat al een gebruiker met het e-mailadres %{emailOrUsername}. We hebben deze gebruiker uitgenodigd om aan dit topic deel te nemen." @@ -2579,8 +2578,8 @@ nl: radio_label: "Nieuw topic" error: "Er is een fout opgetreden bij het verplaatsen van berichten naar het nieuwe topic." instructions: - one: "U staat op het punt een nieuw topic te maken en het in te vullen met het bericht dat u hebt geselecteerd." - other: "U staat op het punt een nieuw topic te maken en het in te vullen met de %{count} berichten die u hebt geselecteerd." + one: "Je staat op het punt een nieuw topic te maken en het in te vullen met het bericht dat je hebt geselecteerd." + other: "Je staat op het punt een nieuw topic te maken en het in te vullen met de %{count} berichten die je hebt geselecteerd." merge_topic: title: "Verplaatsen naar bestaand topic" action: "verplaatsen naar bestaand topic" @@ -2596,8 +2595,8 @@ nl: radio_label: "Nieuw bericht" participants: "Deelnemers" instructions: - one: "U staat op het punt een nieuw bericht te maken en het in te vullen met het bericht dat u hebt geselecteerd." - other: "U staat op het punt een nieuw bericht te maken en het in te vullen met de %{count} berichten die u hebt geselecteerd." + one: "Je staat op het punt een nieuw bericht te maken en het in te vullen met het bericht dat je hebt geselecteerd." + other: "Je staat op het punt een nieuw bericht te maken en het in te vullen met de %{count} berichten die je hebt geselecteerd." move_to_existing_message: title: "Verplaatsen naar bestaand bericht" action: "verplaatsen naar bestaand bericht" @@ -2620,7 +2619,7 @@ nl: publish_url: "Uw pagina is gepubliceerd op:" topic_published: "Uw topic is gepubliceerd op:" preview_url: "Uw pagina wordt gepubliceerd op:" - invalid_slug: "Sorry, u kunt deze pagina niet publiceren." + invalid_slug: "Sorry, je kunt deze pagina niet publiceren." unpublish: "Publicatie ongedaan maken" unpublished: "Uw pagina is niet meer gepubliceerd en niet meer toegankelijk." publishing_settings: "Publicatie-instellingen" @@ -2661,8 +2660,8 @@ nl: select_all: alles selecteren deselect_all: alles deselecteren description: - one: U hebt %{count} bericht geselecteerd. - other: "U hebt %{count} berichten geselecteerd." + one: Je hebt %{count} bericht geselecteerd. + other: "Je hebt %{count} berichten geselecteerd." post: quote_reply: "Citeren" quote_reply_shortcut: "Of druk op q" @@ -2687,7 +2686,7 @@ nl: one: "%{count} verborgen antwoord weergeven" other: "%{count} verborgen antwoorden weergeven" notice: - new_user: "Dit is de eerste keer dat %{user} iets heeft geplaatst – we heten hem/haar welkom in onze gemeenschap!" + new_user: "Dit is de eerste keer dat %{user} iets heeft geplaatst – we heten hem/haar welkom in onze community!" returning_user: "Het is al even geleden dat we %{user} hebben gezien – hun laatste bericht dateert van %{time}." unread: "Bericht is ongelezen" has_replies: @@ -2698,33 +2697,33 @@ nl: has_likes_title: one: "%{count} persoon heeft dit bericht geliket" other: "%{count} personen hebben dit bericht geliket" - has_likes_title_only_you: "u hebt dit bericht geliket" + has_likes_title_only_you: "je hebt dit bericht geliket" has_likes_title_you: - one: "u en %{count} andere persoon hebben dit bericht geliket" - other: "u en %{count} andere personen hebben dit bericht geliket" + one: "jij en %{count} ander hebben dit bericht geliket" + other: "jij en %{count} anderen hebben dit bericht geliket" filtered_replies_hint: - one: "Dit bericht en zijn antwoord bekijken" - other: "Dit bericht en zijn %{count} antwoorden bekijken" + one: "Dit bericht en 1 antwoord weergeven" + other: "Dit bericht en %{count} antwoorden weergeven" filtered_replies_viewing: - one: "%{count} antwoord aan het bekijken op" - other: "%{count} antwoorden aan het bekijken op" + one: "%{count} antwoord weergegeven op" + other: "%{count} antwoorden weergegeven op" in_reply_to: "Bovenliggend bericht laden" - view_all_posts: "Alle berichten bekijken" + view_all_posts: "Alle berichten weergeven" errors: create: "Sorry, er is een fout opgetreden bij het plaatsen van uw bericht. Probeer het opnieuw." edit: "Sorry, er is een fout opgetreden bij het bewerken van uw bericht. Probeer het opnieuw." upload: "Sorry, er is een fout opgetreden bij het uploaden van dat bestand. Probeer het opnieuw." - file_too_large: "Sorry, dat bestand is te groot (maximale grootte is %{max_size_kb}kb). Misschien kunt u het uploaden naar een cloudopslagdienst, en dan de koppeling plakken?" - too_many_uploads: "Sorry, u kunt maar één bestand tegelijk uploaden." + file_too_large: "Sorry, dat bestand is te groot (maximale grootte is %{max_size_kb}kb). Misschien kunt u het uploaden naar een cloudopslagdienst, en dan de link plakken?" + too_many_uploads: "Sorry, je kunt slechts één bestand tegelijk uploaden." too_many_dragged_and_dropped_files: - one: "Sorry, u kunt maar %{count} bestand tegelijk uploaden." - other: "Sorry, u kunt maar %{count} bestanden tegelijk uploaden." - upload_not_authorized: "Sorry, het bestand dat u probeert te uploaden is niet geautoriseerd (geautoriseerde extensies: %{authorized_extensions})." + one: "Sorry, je kunt slechts %{count} bestand tegelijk uploaden." + other: "Sorry, u kunt slechts %{count} bestanden tegelijk uploaden." + upload_not_authorized: "Sorry, het bestand dat je probeert te uploaden is niet toegestaan (toegestane extensies: %{authorized_extensions})." image_upload_not_allowed_for_new_user: "Sorry, nieuwe gebruikers kunnen geen afbeeldingen uploaden." attachment_upload_not_allowed_for_new_user: "Sorry, nieuwe gebruikers kunnen geen bijlagen uploaden." - attachment_download_requires_login: "Sorry, u dient aangemeld te zijn om bijlagen te downloaden." + attachment_download_requires_login: "Sorry, je moet aangemeld zijn om bijlagen te downloaden." cancel_composer: - confirm: "Wat wilt u met uw bericht doen?" + confirm: "Wat wil je doen met je bericht?" discard: "Negeren" save_draft: "Concept opslaan voor later" keep_editing: "Blijf bewerken" @@ -2733,7 +2732,7 @@ nl: whisper: "dit bericht is alleen toegankelijk voor moderators" wiki: about: "dit bericht is een wiki" - few_likes_left: "Bedankt voor uw steun! U kunt vandaag nog een paar likes uitdelen." + few_likes_left: "Bedankt voor je steun! Je kunt vandaag nog maar een paar likes uitdelen." controls: reply: "beginnen met opstellen van een antwoord op dit bericht" like: "dit bericht liken" @@ -2742,14 +2741,14 @@ nl: undo_like: "like ongedaan maken" edit: "dit bericht bewerken" edit_action: "Bewerken" - edit_anonymous: "Sorry, maar u dient aangemeld te zijn om dit bericht te bewerken." + edit_anonymous: "Sorry, je moet aangemeld zijn om dit bericht te bewerken." flag: "een privémarkering aan dit topic geven of er een privébericht over sturen" delete: "dit bericht verwijderen" undelete: "dit bericht herstellen" - share: "een koppeling naar dit bericht delen" + share: "deel een link naar dit bericht" more: "Meer" delete_replies: - confirm: "Wilt u ook de antwoorden op dit bericht verwijderen?" + confirm: "Wil je ook de antwoorden op dit bericht verwijderen?" direct_replies: one: "Ja, en %{count} direct antwoord" other: "Ja, en %{count} directe antwoorden" @@ -2770,11 +2769,11 @@ nl: lock_post_description: "voorkomen dat de schrijver dit bericht bewerkt" unlock_post: "Bericht ontgrendelen" unlock_post_description: "toestaan dat de schrijver dit bericht bewerkt" - delete_topic_disallowed_modal: "U hebt geen toestemming om dit topic te verwijderen. Als u echt wilt dat het wordt verwijderd, plaats dan een markering voor aandacht van een moderator, en voeg de reden bij." - delete_topic_disallowed: "u hebt geen toestemming om dit topic te verwijderen" + delete_topic_disallowed_modal: "Je hebt geen toestemming om dit topic te verwijderen. Als je echt wilt dat het wordt verwijderd, plaats dan een markering om een moderator erop te wijzigen en voeg de reden bij." + delete_topic_disallowed: "je hebt geen toestemming om dit topic te verwijderen" delete_topic_confirm_modal: - one: "Dit topic heeft momenteel meer dan %{count} weergave en kan een populaire zoekbestemming zijn. Weet u zeker dat u dit topic volledig wilt verwijderen, in plaats van het te bewerken voor verbetering?" - other: "Dit topic heeft momenteel meer dan %{count} weergaven en kan een populaire zoekbestemming zijn. Weet u zeker dat u dit topic volledig wilt verwijderen, in plaats van het te bewerken voor verbetering?" + one: "Dit topic heeft momenteel meer dan %{count} weergave en kan een populaire zoekbestemming zijn. Weet je zeker dat je dit topic volledig wilt verwijderen, in plaats van het te bewerken om het te verbeteren?" + other: "Dit topic heeft momenteel meer dan %{count} weergaven en kan een populaire zoekbestemming zijn. Weet je zeker dat je dit topic volledig wilt verwijderen, in plaats van het te bewerken om het te verbeteren?" delete_topic_confirm_modal_yes: "Ja, dit topic verwijderen" delete_topic_confirm_modal_no: "Nee, dit topic behouden" delete_topic_error: "Er is een fout opgetreden bij het verwijderen van dit topic" @@ -2797,15 +2796,15 @@ nl: one: "en %{count} ander heeft dit gelezen" other: "en %{count} anderen hebben dit gelezen" by_you: - off_topic: "U hebt dit als off-topic gemarkeerd" - spam: "U hebt dit als spam gemarkeerd" - inappropriate: "U hebt dit als ongepast gemarkeerd" - notify_moderators: "U hebt dit voor moderatie gemarkeerd" - notify_user: "U hebt een bericht naar deze gebruiker gestuurd" + off_topic: "Je hebt dit als off-topic gemarkeerd" + spam: "Je hebt dit als spam gemarkeerd" + inappropriate: "Je hebt dit als ongepast gemarkeerd" + notify_moderators: "Je hebt dit voor moderatie gemarkeerd" + notify_user: "Je hebt een bericht naar deze gebruiker gestuurd" delete: confirm: - one: "Weet u zeker dat u dat bericht wilt verwijderen?" - other: "Weet u zeker dat u die %{count} berichten wilt verwijderen?" + one: "Weet je zeker dat je dat bericht wilt verwijderen?" + other: "Weet je zeker dat je die %{count} berichten wilt verwijderen?" revisions: controls: first: "Eerste revisie" @@ -2842,7 +2841,7 @@ nl: bookmarks: create: "Bladwijzer maken" edit: "Bladwijzer bewerken" - edit_for_topic: "Bewerk bladwijzer voor onderwerp" + edit_for_topic: "Bladwijzer voor topic bewerken" created: "Gemaakt" updated: "Bijgewerkt" name: "Naam" @@ -2863,9 +2862,9 @@ nl: name: "Bladwijzer losmaken" description: "Maak de bladwijzer los. Het wordt niet langer boven aan uw bladwijzerlijst weergegeven." filtered_replies: - viewing_posts_by: "%{post_count} berichten aan het bekijken door" + viewing_posts_by: "%{post_count} berichten weergegeven van" viewing_subset: "Sommige antwoorden zijn samengevouwen" - viewing_summary: "Samenvatting van dit topic aan het bekijken" + viewing_summary: "Samenvatting van dit topic weergegeven" post_number: "%{username}, bericht #%{post_number}" show_all: "Alles tonen" category: @@ -2889,7 +2888,7 @@ nl: allow_global_tags_label: "Ook andere tags toestaan" required_tag_group: delete: "Verwijderen" - topic_featured_link_allowed: "Aanbevolen koppelingen in deze categorie toestaan" + topic_featured_link_allowed: "Uitgelichte links toestaan in deze categorie" delete: "Categorie verwijderen" create: "Nieuwe categorie" create_long: "Een nieuwe categorie maken" @@ -2907,7 +2906,7 @@ nl: foreground_color: "Voorgrondkleur" name_placeholder: "Maximaal een of twee woorden" color_placeholder: "Willekeurige webkleur" - delete_confirm: "Weet u zeker dat u deze categorie wilt verwijderen?" + delete_confirm: "Weet je zeker dat je deze categorie wilt verwijderen?" delete_error: "Er is een fout opgetreden bij het verwijderen van de categorie." list: "Lijst van categorieën" no_description: "Voeg een omschrijving toe voor deze categorie." @@ -2917,7 +2916,7 @@ nl: security_add_group: "Een groep toevoegen" permissions: group: "Groep" - see: "Bekijken" + see: "Weergeven" reply: "Antwoorden" create: "Aanmaken" no_groups_selected: "Er is geen toegang tot groepen verleend; deze categorie is alleen zichtbaar voor stafleden." @@ -2925,10 +2924,10 @@ nl: toggle_reply: "Toestemming Antwoorden in-/uitschakelen" toggle_full: "Toestemming Aanmaken in-/uitschakelen" inherited: 'Deze toestemming is overgenomen van ''iedereen''' - special_warning: "Waarschuwing: deze categorie is een vooraf geseede categorie, en de beveiligingsinstellingen kunnen niet worden bewerkt. Als u deze categorie niet wenst te gebruiken, verwijder deze dan in plaats van het doel ervan te wijzigen." + special_warning: "Waarschuwing: deze categorie is een vooraf geseede categorie, de beveiligingsinstellingen kunnen niet worden bewerkt. Als je deze categorie niet wilt gebruiken, verwijder deze dan in plaats van het doel ervan te wijzigen." uncategorized_security_warning: "Deze categorie is bijzonder. Hij is bedoeld als wachtruimte voor topics die geen categorie hebben, en kan geen beveiligingsinstellingen bevatten." - uncategorized_general_warning: 'Deze categorie is bijzonder. Hij is bedoeld als de standaardcategorie voor nieuwe topics die geen selecteerde categorie hebben. Als u dit gedrag wilt voorkomen en categorieselectie wilt afdwingen, schakel de instelling dan hier uit. Als u de naam of omschrijving wilt wijzigen, ga dan naar Aanpassen / Tekstinhoud.' - pending_permission_change_alert: "U hebt %{group} niet aan deze categorie toegevoegd; klik op deze knop om de groep toe te voegen." + uncategorized_general_warning: 'Deze categorie is bijzonder. Hij wordt gebruikt als de standaardcategorie voor nieuwe topics waarvoor geen categorie is selecteerd. Als je dit gedrag wilt voorkomen en categorieselectie wilt afdwingen, schakel de instelling dan hier uit. Als je de naam of beschrijving wilt wijzigen, ga dan naar Aanpassen / Tekstinhoud.' + pending_permission_change_alert: "Je hebt %{group} niet toegevoegd aan deze categorie. Klik op deze knop om de groep toe te voegen." images: "Afbeeldingen" email_in: "Aangepast adres voor inkomende e-mail:" email_in_allow_strangers: "E-mails van anonieme gebruikers zonder account accepteren" @@ -2949,8 +2948,8 @@ nl: allow_badges_label: "Badges laten toekennen in deze categorie" edit_permissions: "Toestemmingen bewerken" review_group_name: "groepsnaam" - require_topic_approval: "Goedkeuring van moderator voor alle nieuwe topics vereisen" - require_reply_approval: "Goedkeuring van moderator voor alle nieuwe antwoorden vereisen" + require_topic_approval: "Goedkeuring van moderator vereisen voor alle nieuwe topics" + require_reply_approval: "Goedkeuring van moderator vereisen voor alle nieuwe antwoorden" this_year: "dit jaar" position: "Positie op de categoriepagina:" default_position: "Standaardpositie" @@ -2962,20 +2961,20 @@ nl: navigate_to_first_post_after_read: "Naar eerste bericht nadat topics zijn gelezen" notifications: watching: - title: "In de gaten houden" - description: "U houdt automatisch alle nieuwe topics in deze categorieën in de gaten. U ontvangt meldingen bij elk nieuw bericht in elk topic, en het aantal nieuwe antwoorden verschijnt naast het topic." + title: "Geobserveerd" + description: "Je observeert automatisch alle nieuwe topics in deze categorieën. Je ontvangt meldingen bij elk nieuw bericht in elk topic en het aantal nieuwe antwoorden wordt weergegeven." watching_first_post: - title: "Eerste bericht in de gaten houden" - description: "U ontvangt meldingen over nieuwe topics in deze categorie, maar niet over antwoorden op de topics." + title: "Eerste bericht geobserveerd" + description: "Je ontvangt meldingen over nieuwe topics in deze categorie, maar niet over antwoorden op de topics." tracking: title: "Volgen" - description: "U volgt automatisch alle nieuwe topics in deze categorie. U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt, en het aantal nieuwe antwoorden wordt weergeven." + description: "Je volgt automatisch alle nieuwe topics in deze categorie. Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt en het aantal nieuwe antwoorden wordt weergeven." regular: title: "Normaal" - description: "U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." + description: "Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." muted: title: "Genegeerd" - description: "U ontvangt geen enkele melding over nieuwe topics in deze categorie, en ze verschijnen niet in Nieuwste." + description: "Je ontvangt geen meldingen over nieuwe topics in deze categorie en ze worden niet weergegeven in Nieuwste." search_priority: label: "Zoekprioriteit" options: @@ -3010,15 +3009,15 @@ nl: list_filters: all: "alle topics" none: "geen subcategorieën" - colors_disabled: "U kunt geen kleuren selecteren omdat u een categoriestijl van geen hebt." + colors_disabled: "Je kunt geen kleuren selecteren omdat je categoriestijl 'none' is." flagging: - title: "Bedankt voor het beleefd houden van onze gemeenschap!" + title: "Bedankt voor het beleefd houden van onze community!" action: "Bericht markeren" take_action: "Actie ondernemen..." take_action_options: default: title: "Actie ondernemen" - details: "De markeerdrempel direct bereiken, in plaats van op meer markeringen door de gemeenschap te wachten" + details: "De markeerdrempel direct bereiken, in plaats van op meer markeringen door de community te wachten" suspend: title: "Gebruiker schorsen" details: "De markeerdrempel bereiken, en de gebruiker schorsen" @@ -3029,20 +3028,20 @@ nl: official_warning: "Officiële waarschuwing" delete_spammer: "Spammer verwijderen" flag_for_review: "Voor beoordeling in wachtrij zetten" - delete_confirm_MF: "U staat op het punt {POSTS, plural, one {# bericht} other {# berichten}} en {TOPICS, plural, one {# topic} other {# topics}} van deze gebruiker te verwijderen, hun account te verwijderen, registraties vanaf hun IP-adres {ip_address} te blokkeren, en hun e-mailadres {email} toe te voegen aan een permanente blokkeerlijst. Weet u het zeker?" + delete_confirm_MF: "Je staat op het punt {POSTS, plural, one {# bericht} other {# berichten}} en {TOPICS, plural, one {# topic} other {# topics}} van deze gebruiker te verwijderen, diens account te verwijderen, registraties vanaf diens IP-adres {ip_address} te blokkeren en diens e-mailadres {email} toe te voegen aan een permanente blokkeerlijst. Weet je het zeker?" yes_delete_spammer: "Ja, spammer verwijderen" ip_address_missing: "(N.v.t.)" hidden_email_address: "(verborgen)" submit_tooltip: "De privémarkering versturen" - take_action_tooltip: "De markeerdrempel direct bereiken, in plaats van op meer markeringen door de gemeenschap te wachten" - cant: "Sorry, u kunt dit bericht momenteel niet markeren." + take_action_tooltip: "De markeerdrempel direct bereiken, in plaats van op meer markeringen door de community te wachten" + cant: "Sorry, je kunt dit bericht momenteel niet markeren." notify_staff: "Beheerders een privémelding sturen" formatted_name: off_topic: "Het is off-topic" inappropriate: "Het is ongepast" spam: "Het is spam" custom_placeholder_notify_user: "Wees specifiek, opbouwend en blijf altijd beleefd." - custom_placeholder_notify_moderators: "Laat ons met name weten waar u zich zorgen om maakt, en stuur relevante koppelingen en voorbeelden mee waar mogelijk." + custom_placeholder_notify_moderators: "Laat ons met name weten waar je je zorgen om maakt en geef relevante links en voorbeelden waar mogelijk." custom_message: at_least: one: "voer minstens %{count} teken in" @@ -3054,19 +3053,19 @@ nl: one: "%{count} resterend" other: "%{count} resterend" flagging_topic: - title: "Bedankt voor het beleefd houden van onze gemeenschap!" + title: "Bedankt voor het beleefd houden van onze community!" action: "Topic markeren" notify_action: "Bericht" topic_map: title: "Topicsamenvatting" participants_title: "Frequente schrijvers" - links_title: "Populaire koppelingen" - links_shown: "meer koppelingen tonen..." + links_title: "Populaire links" + links_shown: "meer links weergeven..." clicks: one: "%{count} klik" other: "%{count} klikken" post_links: - about: "meer koppelingen voor dit bericht uitvouwen" + about: "meer links voor dit bericht uitvouwen" title: one: "nog %{count}" other: "nog %{count}" @@ -3074,7 +3073,7 @@ nl: warning: help: "Dit is een officiële waarschuwing." bookmarked: - help: "U hebt een bladwijzer voor dit topic gemaakt" + help: "Je hebt een bladwijzer voor dit topic gemaakt" locked: help: "Dit topic is gesloten; nieuwe antwoorden zijn niet meer mogelijk" archived: @@ -3091,7 +3090,7 @@ nl: title: "Vastgemaakt" help: "Dit topic is voor u vastgemaakt; het wordt boven in de categorie ervan weergegeven" unlisted: - help: "Dit topic is niet zichtbaar; het verschijnt niet in topiclijsten en kan alleen via een rechtstreekse koppeling worden benaderd" + help: "Dit topic is niet zichtbaar; het wordt niet weergegeven niet in topiclijsten en is alleen toegankelijk via een rechtstreekse link" personal_message: title: "Dit topic is een persoonlijk bericht" help: "Dit topic is een persoonlijk bericht" @@ -3142,7 +3141,7 @@ nl: help: "topics met recente berichten" read: title: "Gelezen" - help: "topics die u hebt gelezen, in de volgorde waarin u ze voor het laatst hebt gelezen" + help: "topics die je hebt gelezen, in de volgorde waarin je ze het laatst hebt gelezen" categories: title: "Categorieën" title_in: "Categorie - %{categoryName}" @@ -3152,7 +3151,7 @@ nl: title_with_count: one: "Ongelezen (%{count})" other: "Ongelezen (%{count})" - help: "topics die u momenteel in de gaten houdt of volgt met ongelezen berichten" + help: "topics die je momenteel observeert of volgt met ongelezen berichten" lower_title_with_count: one: "%{count} ongelezen" other: "%{count} ongelezen" @@ -3171,10 +3170,10 @@ nl: help: "in de afgelopen paar dagen aangemaakte topics" posted: title: "Mijn berichten" - help: "topics waarin u een bericht hebt geplaatst" + help: "topics waarin je een bericht hebt geplaatst" bookmarks: title: "Bladwijzers" - help: "topics waarvoor u een bladwijzer hebt" + help: "topics waarvoor je een bladwijzer hebt" category: title: "%{categoryName}" title_with_count: @@ -3202,11 +3201,11 @@ nl: this_month: "Maand" this_week: "Week" today: "Vandaag" - other_periods: "eerste bekijken:" + other_periods: "top weergeven:" permission_types: - full: "Maken / Antwoorden / Bekijken" - create_post: "Antwoorden / Bekijken" - readonly: "Bekijken" + full: "Maken / Antwoorden / Weergeven" + create_post: "Antwoorden / Weergeven" + readonly: "Weergeven" preloader_text: "Laden" lightbox: download: "downloaden" @@ -3255,7 +3254,7 @@ nl: show_incoming_updated_topics: "%{shortcut} Bijgewerkte topics tonen" search: "%{shortcut} Zoeken" help: "%{shortcut} Hulp voor sneltoetsen openen" - dismiss_new: "%{shortcut} Nieuwe topics negeren" + dismiss_new: "%{shortcut} Nieuwe negeren" dismiss_topics: "%{shortcut} Topics negeren" log_out: "%{shortcut} Afmelden" composing: @@ -3292,12 +3291,12 @@ nl: delete: "%{shortcut} Bericht verwijderen" mark_muted: "%{shortcut} Topic negeren" mark_tracking: "%{shortcut} Topic volgen" - mark_watching: "%{shortcut} Topic in de gaten houden" + mark_watching: "%{shortcut} Topic observeren" print: "%{shortcut} Topic afdrukken" defer: "%{shortcut} Topic negeren" topic_admin_actions: "%{shortcut} Beheeracties voor topic openen" search_menu: - title: "Menu Zoeken" + title: "Zoekmenu" prev_next: "%{shortcut} Selectie omhoog en omlaag verplaatsen" insert_url: "%{shortcut} Selectie in open editorvenster invoegen" full_page_search: "%{shortcut} Start zoeken op volledige pagina" @@ -3308,8 +3307,8 @@ nl: granted_on: "Toegekend op %{date}" others_count: "Anderen met deze badge (%{count})" title: Badges - allow_title: "U kunt deze badge als een titel gebruiken" - multiple_grant: "U kunt dit meerdere keren verdienen" + allow_title: "Je kunt deze badge als een titel gebruiken" + multiple_grant: "Je kunt dit meerdere keren verdienen" badge_count: one: "%{count} badge" other: "%{count} badges" @@ -3321,12 +3320,12 @@ nl: other: "%{count} toegekend" select_badge_for_title: Kies een badge om als uw titel te gebruiken none: "(geen)" - successfully_granted: "%{badge} is succesvol toegekend aan %{username}" + successfully_granted: "%{badge} toegekend aan %{username}" badge_grouping: getting_started: name: Aan de slag community: - name: Gemeenschap + name: Community trust_level: name: Vertrouwensniveau other: @@ -3348,7 +3347,7 @@ nl: info: "Info" default_info: "Deze tag is niet beperkt tot categorieën, en heeft geen synoniemen." staff_info: "Om beperkingen toe te voegen, plaats deze tag in een tag-groep." - category_restricted: "Deze tag is beperkt tot categorieën waartoe u geen toegang hebt." + category_restricted: "Deze tag is beperkt tot categorieën waartoe je geen toegang hebt." synonyms: "Synoniemen" synonyms_description: "Wanneer de volgende tags worden gebruikt, worden deze vervangen door %{base_tag_name}." save: "Sla de naam en omschrijving van de tag op" @@ -3362,16 +3361,16 @@ nl: add_synonyms_label: "Synoniemen toevoegen:" add_synonyms: "Toevoegen" add_synonyms_explanation: - one: "Overal waar deze tag momenteel wordt gebruikt, zal dit worden gewijzigd naar het gebruik van %{tag_name}. Weet u zeker dat u deze wijziging wilt aanbrengen?" - other: "Overal waar deze tags momenteel worden gebruikt, zal dit worden gewijzigd naar het gebruik van %{tag_name}. Weet u zeker dat u deze wijziging wilt aanbrengen?" + one: "Overal waar deze tag momenteel wordt gebruikt, wordt dit gewijzigd naar %{tag_name}. Weet je zeker dat je deze wijziging wilt aanbrengen?" + other: "Overal waar deze tags momenteel worden gebruikt, wordt dit gewijzigd naar %{tag_name}. Weet je zeker dat je deze wijziging wilt aanbrengen?" add_synonyms_failed: "De volgende tags konden niet worden toegevoegd als synoniemen: %{tag_names}. Zorg dat ze geen synoniemen hebben en geen synoniem van een andere tag zijn." remove_synonym: "Synoniem verwijderen" - delete_synonym_confirm: 'Weet u zeker dat u het synoniem ''%{tag_name}'' wilt verwijderen?' + delete_synonym_confirm: 'Weet je zeker dat je het synoniem ''%{tag_name}'' wilt verwijderen?' delete_tag: "Tag verwijderen" delete_confirm: - one: "Weet u zeker dat u deze tag wilt verwijderen en loskoppelen van %{count} topic waaraan deze is toegewezen?" - other: "Weet u zeker dat u deze tag wilt verwijderen en loskoppelen van %{count} topics waaraan deze is toegewezen?" - delete_confirm_no_topics: "Weet u zeker dat u deze tag wilt verwijderen?" + one: "Weet je zeker dat je deze tag wilt verwijderen en loskoppelen van %{count} topic waaraan deze is toegewezen?" + other: "Weet je zeker dat je deze tag wilt verwijderen en loskoppelen van %{count} topics waaraan deze is toegewezen?" + delete_confirm_no_topics: "Weet je zeker dat je deze tag wilt verwijderen?" delete_confirm_synonyms: one: "Het synoniem ervan wordt ook verwijderd." other: "De %{count} synoniemen ervan worden ook verwijderd." @@ -3385,7 +3384,7 @@ nl: upload: "Tags uploaden" upload_description: "Een CSV-bestand uploaden om bulksgewijs tags te maken" upload_instructions: "Eén per regel, optioneel met een tag-groep in de notatie 'tag_name,tag_group'." - upload_successful: "Tags succesvol geüpload" + upload_successful: "Tags geüpload" delete_unused_confirmation: one: "%{count} tag wordt verwijderd: %{tags}" other: "%{count} tags worden verwijderd: %{tags}" @@ -3403,25 +3402,25 @@ nl: untagged_with_category: "%{filter} ongetagde topics in %{category}" notifications: watching: - title: "In de gaten houden" - description: "U houdt automatisch alle nieuwe topics met deze tag in de gaten. U ontvangt meldingen bij alle nieuwe berichten en topics, en het aantal ongelezen en nieuwe berichten verschijnt ook naast het topic." + title: "Geobserveerd" + description: "Je observeert automatisch alle nieuwe topics met deze tag. Je ontvangt meldingen bij alle nieuwe berichten en topics en het aantal ongelezen en nieuwe berichten wordt weergegeven naast het topic." watching_first_post: - title: "Eerste bericht in de gaten houden." - description: "U ontvangt meldingen over nieuwe topics in deze tag, maar niet over antwoorden op de topics." + title: "Eerste bericht geobserveerd" + description: "Je ontvangt meldingen over nieuwe topics in deze tag, maar niet over antwoorden op de topics." tracking: title: "Volgen" - description: "U volgt automatisch alle topics met deze tag. Het aantal ongelezen en nieuwe berichten verschijnt naast het topic." + description: "Je volgt automatisch alle topics met deze tag. Het aantal ongelezen en nieuwe berichten wordt weergegeven naast het topic." regular: title: "Normaal" - description: "U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt." + description: "Je ontvangt een melding als iemand je @naam noemt of een bericht van je beantwoordt." muted: title: "Genegeerd" - description: "U ontvangt geen enkele melding over nieuwe topics met deze tag, en ze verschijnen niet op uw tabblad Ongelezen." + description: "Je ontvangt geen meldingen over nieuwe topics met deze tag en ze worden niet weergegeven op je tabblad Ongelezen." groups: title: "Tag-groepen" about_heading: "Selecteer een tag-groep of maak een nieuwe aan" about_heading_empty: "Om te beginnen maak een tag-groep aan" - about_description: "Met tag-groepen kunt u toestemmingen voor meerdere tags op één plek beheren." + about_description: "Met taggroepen kun je toestemmingen voor meerdere tags op één plek beheren." new: "Nieuwe groep" new_title: "Maak nieuwe groep" edit_title: "Bewerk tag-groep" @@ -3433,7 +3432,7 @@ nl: name_placeholder: "Naam" save: "Opslaan" delete: "Verwijderen" - confirm_delete: "Weet u zeker dat u deze tag-groep wilt verwijderen?" + confirm_delete: "Weet je zeker dat je deze taggroep wilt verwijderen?" everyone_can_use: "Tags kunnen door iedereen worden gebruikt" usable_only_by_groups: "Tags zijn voor iedereen zichtbaar, maar alleen de volgende groepen kunnen ze gebruiken" visible_only_to_groups: "Tags zijn alleen zichtbaar voor de volgende groepen" @@ -3444,27 +3443,27 @@ nl: disabled: "Taggen is uitgeschakeld. " topics: none: - unread: "U hebt geen ongelezen topics." - unseen: "U hebt geen ongelezen topics." - new: "U hebt geen nieuwe topics." - read: "U hebt nog geen topics gelezen." - posted: "U hebt nog geen berichten in topics geplaatst." + unread: "Je hebt geen ongelezen topics." + unseen: "Je hebt geen ongelezen topics." + new: "Je hebt geen nieuwe topics." + read: "Je hebt nog geen topics gelezen." + posted: "Je hebt nog geen berichten in topics geplaatst." latest: "Er zijn geen nieuwste topics." - bookmarks: "U hebt nog geen topics met bladwijzers." + bookmarks: "Je hebt nog geen topics met bladwijzers." top: "Er zijn geen toptopics." invite: custom_message: "Maak uw uitnodiging iets persoonlijker door een eigen bericht te schrijven." custom_message_placeholder: "Voer uw eigen bericht in" approval_not_required: "De gebruiker wordt automatisch goedgekeurd zodra hij of zij deze uitnodiging accepteert." - custom_message_template_forum: "Hee, u zou aan dit forum moeten deelnemen!" - custom_message_template_topic: "Hee, dit topic lijkt me wel iets voor u!" + custom_message_template_forum: "Hé, je zou moeten deelnemen aan dit forum!" + custom_message_template_topic: "Hé, dit topic lijkt me wat voor jou!" forced_anonymous: "Vanwege overbelasting wordt dit tijdelijk voor iedereen weergegeven zoals niet-aangemelde gebruikers het zien." forced_anonymous_login_required: "De website staat onder extreme belasting en kan op dit moment niet worden geladen, probeer het over een paar minuten opnieuw." footer_nav: back: "Vorige" forward: "Volgende" share: "Delen" - dismiss: "Verwijderen" + dismiss: "Sluiten" safe_mode: enabled: "Veilige modus is ingeschakeld; sluit dit browservenster om de veilige modus te verlaten" image_removed: "(afbeelding verwijderd)" @@ -3488,7 +3487,7 @@ nl: leader: "leider" user_activity: no_activity_title: "Nog geen activiteit" - no_read_topics_body: "Zodra u discussies gaat lezen, ziet u hier een lijst. Om te beginnen met lezen, zoek naar interessante topics onder Top of Categorieën of zoek op trefwoord %{searchIcon}" + no_read_topics_body: "Wanneer je discussies gaat lezen, zie je hier een lijst. Om te beginnen met lezen, kun je zoeken naar interessante topics onder Top of Categorieën of zoeken op trefwoorden %{searchIcon}" sidebar: unread_count: one: "%{count} ongelezen" @@ -3516,7 +3515,7 @@ nl: categories: header_link_text: "Categorieën" community: - header_link_text: "Gemeenschap" + header_link_text: "Community" links: about: content: "Over" @@ -3555,13 +3554,13 @@ nl: last_updated: "Dashboard bijgewerkt:" discourse_last_updated: "Discourse bijgewerkt:" version: "Versie" - up_to_date: "U bent up-to-date!" + up_to_date: "Je bent helemaal bij!" critical_available: "Er is een kritieke update beschikbaar." updates_available: "Er zijn updates beschikbaar." please_upgrade: "Voer een upgrade uit!" no_check_performed: "Er is nog niet op updates gecontroleerd. Zorg dat sidekiq actief is." stale_data: "Er is de laatste tijd niet op updates gecontroleerd. Zorg dat sidekiq actief is." - version_check_pending: "U hebt de software onlangs bijgewerkt. Prachtig!" + version_check_pending: "Je hebt de software onlangs bijgewerkt. Fantastisch!" installed_version: "Geïnstalleerd" latest_version: "Nieuwste" problems_found: "Advies op basis van uw huidige website-instellingen" @@ -3592,7 +3591,7 @@ nl: page_views: "Paginaweergaven" page_views_short: "Paginaweergaven" show_traffic_report: "Gedetailleerd verkeersrapport tonen" - community_health: Status van gemeenschap + community_health: Status van community moderators_activity: Moderator-activiteit whats_new_in_discourse: Wat is er nieuw in Discourse? activity_metrics: Activiteitsgegevens @@ -3605,7 +3604,7 @@ nl: disabled: Uitgeschakeld timeout_error: Sorry, de query duurt te lang. Kies een korter interval. exception_error: Sorry, er is een fout opgetreden bij het uitvoeren van de query. - too_many_requests: U hebt deze actie te vaak uitgevoerd. Wacht even voordat u het opnieuw probeert. + too_many_requests: Je hebt deze actie te vaak uitgevoerd. Wacht voordat je het opnieuw probeert. not_found_error: Dit rapport bestaat niet filter_reports: Rapporten filteren reports: @@ -3632,7 +3631,7 @@ nl: total: "Totaal sinds begin" no_data: "Geen gegevens om weer te geven." trending_search: - more: 'Zoeklogboeken' + more: 'Zoeken in logs' disabled: 'Rapport voor populaire zoekopdrachten is uitgeschakeld. Schakel zoekopdrachten registreren in om gegevens te verzamelen.' average_chart_label: Gemiddeld filters: @@ -3693,8 +3692,8 @@ nl: delete: "Verwijderen" delete_confirm: "Deze groepen verwijderen?" delete_with_messages_confirm: - one: "Als u deze groep verwijdert, wordt %{count} bericht verweesd, groepsleden hebben er geen toegang meer toe.Kies een nieuwe eigenaar voor de inhoud van @%{username}.
-Alle topics, berichten en andere inhoud die door @%{username} is gemaakt, wordt overgedragen.
+Alle topics, berichten en andere inhoud gemaakt door @%{username} worden overgedragen.
target_username_placeholder: "Gebruikersnaam van nieuwe eigenaar" - transfer_and_delete: "Overdragen & @%{username} verwijderen" + transfer_and_delete: "Overdragen en @%{username}en verwijderen" cancel: "Annuleren" progress: title: "Voortgang met samenvoegen" @@ -4656,8 +4655,8 @@ nl: delete_forbidden_because_staff: "Beheerders en moderators kunnen niet worden verwijderd." delete_posts_forbidden_because_staff: "Kan niet alle berichten van beheerders en moderators verwijderen." delete_forbidden: - one: "Gebruikers kunnen niet worden verwijderd als ze berichten hebben geplaatst. Verwijder alle berichten voordat u een gebruiker probeert te verwijderen. (Berichten ouder dan %{count} dag kunnen niet worden verwijderd.)" - other: "Gebruikers kunnen niet worden verwijderd als ze berichten hebben geplaatst. Verwijder alle berichten voordat u een gebruiker probeert te verwijderen. (Berichten ouder dan %{count} dagen kunnen niet worden verwijderd.)" + one: "Gebruikers kunnen niet worden verwijderd als ze berichten hebben geplaatst. Verwijder alle berichten voordat je een gebruiker probeert te verwijderen. (Berichten ouder dan %{count} dag kunnen niet worden verwijderd.)" + other: "Gebruikers kunnen niet worden verwijderd als ze berichten hebben geplaatst. Verwijder alle berichten voordat je een gebruiker probeert te verwijderen. (Berichten ouder dan %{count} dagen kunnen niet worden verwijderd.)" cant_delete_all_posts: one: "Kan niet alle berichten verwijderen. Sommige berichten zijn ouder dan %{count} dag. (De instelling delete_user_max_post_age.)" other: "Kan niet alle berichten verwijderen. Sommige berichten zijn ouder dan %{count} dagen. (De instelling delete_user_max_post_age.)" @@ -4668,9 +4667,9 @@ nl: delete_dont_block: "Alleen verwijderen" deleting_user: "Gebruiker verwijderen..." deleted: "De gebruiker is verwijderd." - delete_failed: "Er is een fout opgetreden bij het verwijderen van die gebruiker. Zorg ervoor dat alle berichten zijn verwijderd voordat u de gebruiker probeert te verwijderen." + delete_failed: "Er is een fout opgetreden bij het verwijderen van de gebruiker. Zorg dat alle berichten zijn verwijderd voordat je de gebruiker probeert te verwijderen." send_activation_email: "Activeringsmail versturen" - activation_email_sent: "Er is een activeringsmail verstuurd." + activation_email_sent: "Er is een activerings-e-mail gestuurd." send_activation_email_failed: "Er is een probleem opgetreden bij het versturen van de activeringsmail. %{error}" activate: "Account activeren" activate_failed: "Er is een probleem opgetreden bij het activeren van de gebruiker." @@ -4678,7 +4677,7 @@ nl: deactivate_failed: "Er is een probleem opgetreden bij het deactiveren van de gebruiker." unsilence_failed: "Er is een probleem opgetreden bij het opheffen van het dempen van de gebruiker." silence_failed: "Er is een probleem opgetreden bij het dempen van de gebruiker." - silence_confirm: "Weet u zeker dat u deze gebruiker wilt dempen? De gebruiker zal dan geen nieuwe topics of berichten kunnen plaatsen." + silence_confirm: "Weet je zeker dat je deze gebruiker wilt dempen? De gebruiker kan dan geen nieuwe topics of berichten plaatsen." silence_accept: "Ja, deze gebruiker dempen" bounce_score: "Bouncescore" reset_bounce_score: @@ -4695,10 +4694,10 @@ nl: threshold_reached: "Er zijn te veel bounceberichten van dat e-mailadres ontvangen." trust_level_change_failed: "Er is een probleem opgetreden bij het wijzigen van het vertrouwensniveau van de gebruiker." suspend_modal_title: "Gebruiker schorsen" - confirm_cancel_penalty: "Weet u zeker dat u het minpunt wilt negeren?" + confirm_cancel_penalty: "Weet je zeker dat je de straf wilt wissen?" trust_level_2_users: "Gebruikers met vertrouwensniveau 2" trust_level_3_requirements: "Vereisten voor vertrouwensniveau 3" - trust_level_locked_tip: "vertrouwensniveau is vergrendeld; het systeem zal geen gebruikers promoveren of degraderen" + trust_level_locked_tip: "vertrouwensniveau is vergrendeld; het systeem promoveert en degradeert geen gebruikers" lock_trust_level: "Vertrouwensniveau vergrendelen" unlock_trust_level: "Vertrouwensniveau ontgrendelen" silenced_count: "Gedempt" @@ -4707,12 +4706,12 @@ nl: title: "Vereisten voor vertrouwensniveau 3" table_title: one: "De afgelopen dag:" - other: "In de afgelopen %{count} dagen:" + other: "De afgelopen %{count} dagen:" value_heading: "Waarde" requirement_heading: "Vereiste" visits: "Bezoeken" days: "dagen" - topics_replied_to: "Topics waarin is geantwoord" + topics_replied_to: "Beantwoorde topics" topics_viewed: "Bekeken topics" topics_viewed_all_time: "Bekeken topics (sinds begin)" posts_read: "Gelezen berichten" @@ -4727,13 +4726,13 @@ nl: silenced: "Gedempt (afgelopen 6 maanden)" qualifies: "Komt in aanmerking voor vertrouwensniveau 3." does_not_qualify: "Komt niet in aanmerking voor vertrouwensniveau 3." - will_be_promoted: "Zal binnenkort worden gepromoveerd." - will_be_demoted: "Zal binnenkort worden gedegradeerd." - on_grace_period: "Op dit moment in promotiewachtperiode, zal niet worden gedegradeerd." - locked_will_not_be_promoted: "Vertrouwensniveau vergrendeld. Zal nooit worden gepromoveerd." - locked_will_not_be_demoted: "Vertrouwensniveau vergrendeld. Zal nooit worden gedegradeerd." + will_be_promoted: "Wordt binnenkort gepromoveerd." + will_be_demoted: "Wordt binnenkort gedegradeerd." + on_grace_period: "Momenteel in promotiewachtperiode, wordt niet gedegradeerd." + locked_will_not_be_promoted: "Vertrouwensniveau vergrendeld. Wordt nooit gepromoveerd." + locked_will_not_be_demoted: "Vertrouwensniveau vergrendeld. Wordt nooit gedegradeerd." discourse_connect: - title: "DiscourseConnect Single Sign On" + title: "DiscourseConnect eenmalige aanmelding" external_id: "Externe ID" external_username: "Gebruikersnaam" external_name: "Naam" @@ -4741,39 +4740,34 @@ nl: external_avatar_url: "URL van profielafbeelding" last_payload: "Laatste payload" delete_sso_record: "SSO-record verwijderen" - confirm_delete: "Weet u zeker dat u dit DiscourseConnect-record wilt verwijderen?" + confirm_delete: "Weet je zeker dat je dit DiscourseConnect-record wilt verwijderen?" user_fields: title: "Gebruikersvelden" - help: "Voeg velden toe die uw gebruikers kunnen invullen." + help: "Voeg velden toe die je gebruikers kunnen invullen." create: "Gebruikersveld maken" untitled: "Geen titel" name: "Veldnaam" type: "Veldtype" - description: "Veldomschrijving" + description: "Veldbeschrijving" save: "Opslaan" edit: "Bewerken" delete: "Verwijderen" cancel: "Annuleren" - delete_confirm: "Weet u zeker dat u dat gebruikersveld wilt verwijderen?" + delete_confirm: "Weet je zeker dat je dat gebruikersveld wilt verwijderen?" options: "Opties" required: - title: "Vereist bij registratie?" enabled: "vereist" disabled: "niet vereist" editable: - title: "Bewerkbaar na registratie?" enabled: "bewerkbaar" disabled: "niet bewerkbaar" show_on_profile: - title: "Tonen in openbaar profiel?" - enabled: "getoond in profiel" - disabled: "niet getoond in profiel" + enabled: "weergegeven in profiel" + disabled: "niet weergegeven in profiel" show_on_user_card: - title: "Tonen op gebruikerskaart?" enabled: "getoond op gebruikerskaart" disabled: "niet getoond op gebruikerskaart" searchable: - title: "Doorzoekbaar?" enabled: "doorzoekbaar" disabled: "niet doorzoekbaar" field_types: @@ -4781,12 +4775,12 @@ nl: confirm: "Bevestiging" dropdown: "Vervolgkeuzelijst" site_text: - description: "U kunt alle tekst op uw forum aanpassen. Begin door hieronder te zoeken:" - search: "Zoek de tekst die u wilt bewerken" + description: "Je kunt alle tekst op je forum aanpassen. Begin door hieronder te zoeken:" + search: "Zoek de tekst die je wilt bewerken" title: "Tekst" edit: "bewerken" revert: "Wijzigingen ongedaan maken" - revert_confirm: "Weet u zeker dat u uw wijzigingen ongedaan wilt maken?" + revert_confirm: "Weet je zeker dat je je wijzigingen ongedaan wilt maken?" go_back: "Terug naar Zoeken" recommended: "We raden aan de volgende tekst naar wens aan te passen:" show_overriden: "Alleen aangepaste tonen" @@ -4794,7 +4788,7 @@ nl: more_than_50_results: "Er zijn meer dan 50 resultaten. Verfijn uw zoekopdracht." settings: show_overriden: "Alleen aangepaste tonen" - history: "Wijzigingsoverzicht bekijken" + history: "Wijzigingsgeschiedenis weergeven" reset: "terugzetten" none: "geen" site_settings: @@ -4833,11 +4827,11 @@ nl: spam: "Spam" rate_limits: "Frequentielimieten" developer: "Ontwikkelaar" - embedding: "Inbedding" + embedding: "Insluiting" legal: "Juridisch" api: "API" user_api: "Gebruikers-API" - uncategorized: "Overige" + uncategorized: "Overig" backups: "Back-ups" login: "Aanmelden" plugins: "Plug-ins" @@ -4849,7 +4843,7 @@ nl: secret_list: invalid_input: "Invoervelden mogen niet leeg zijn of verticale-streeptekens bevatten." default_categories: - modal_description: "Wilt u deze wijziging op het verleden toepassen? Hierdoor worden voor %{count} bestaande gebruikers voorkeuren gewijzigd." + modal_description: "Wil je deze wijziging op het verleden toepassen? Hierdoor worden voorkeuren gewijzigd voor %{count} bestaande gebruikers." modal_yes: "Ja" modal_no: "Nee, wijziging alleen vanaf nu toepassen" simple_list: @@ -4864,22 +4858,22 @@ nl: name: Naam badge: Badge display_name: Weergavenaam - description: Omschrijving - long_description: Lange omschrijving + description: Beschrijving + long_description: Lange beschrijving badge_type: Badgetype badge_grouping: Groep badge_groupings: modal_title: Badgegroeperingen granted_by: Toegekend door granted_at: Toegekend op - reason_help: (Een koppeling naar een bericht of topic) + reason_help: (Een link naar een bericht of topic) save: Opslaan delete: Verwijderen - delete_confirm: Weet u zeker dat u deze badge wilt verwijderen? + delete_confirm: Weet je zeker dat je deze badge wilt verwijderen? revoke: Intrekken reason: Reden expand: Uitvouwen … - revoke_confirm: Weet u zeker dat u deze badge wilt intrekken? + revoke_confirm: Weet je zeker dat je deze badge wilt intrekken? edit_badges: Badges bewerken grant_badge: Badge toekennen granted_badges: Toegekende badges @@ -4889,7 +4883,7 @@ nl: none_selected: "Selecteer een badge om te beginnen" allow_title: Badge mag als titel worden gebruikt multiple_grant: Kan meerdere malen worden toegekend - listable: Badge op de openbare badgespagina tonen + listable: Badge weergeven op de openbare badgespagina enabled: Badge inschakelen icon: Pictogram image: Afbeelding @@ -4915,7 +4909,7 @@ nl: plan_text: "Voorbeeld met queryplan" modal_title: "Voorbeeld van badgequery" sql_error_header: "Er is een fout opgetreden bij de query." - error_help: "Bekijk de volgende koppelingen voor hulp bij badgequery's." + error_help: "Zie de volgende links voor hulp bij vragen over badges." bad_count_warning: header: "WAARSCHUWING!" text: "Er ontbreken toekenningsvoorbeelden. Dit gebeurt als de badgequery gebruikers- of bericht-ID's retourneert die niet bestaan. Dit kan later tot onverwachte resultaten leiden - controleer uw query." @@ -4950,33 +4944,33 @@ nl: name: "Naam" group: "Groep" image: "Afbeelding" - alt: "voorbeeld van eigen emoji" - delete_confirm: "Weet u zeker dat u de emoji :%{name}: wilt verwijderen?" + alt: "voorbeeld van aangepaste emoji" + delete_confirm: "Weet je zeker dat je de emoji :%{name}: wilt verwijderen?" embedding: - get_started: "Als u Discourse in een andere website wilt inbedden, begin dan door de host ervan toe te voegen." - confirm_delete: "Weet u zeker dat u die host wilt verwijderen?" - title: "Inbedding" + get_started: "Als je Discourse wilt insluiren op een andere website, begin dan door de host ervan toe te voegen." + confirm_delete: "Weet je zeker dat je die host wilt verwijderen?" + title: "Insluiting" host: "Toegestane hosts" class_name: "Klassenaam" allowed_paths: "Pad-acceptatielijst" edit: "bewerken" category: "Bericht naar categorie" add_host: "Host toevoegen" - settings: "Inbeddingsinstellingen" + settings: "Insluitingsinstellingen" crawling_settings: "Crawlerinstellingen" - crawling_description: "Als Discourse topics voor uw berichten aanmaakt en er geen RSS/ATOM-feed aanwezig is, wordt geprobeerd uw inhoud vanuit HTML te parsen. Omdat het soms een uitdaging kan zijn om inhoud te extraheren, bieden we de mogelijkheid voor het opgeven van CSS-regels om de extractie makkelijker te maken." + crawling_description: "Als Discourse topics maakt voor je berichten en er geen RSS/ATOM-feed aanwezig is, wordt geprobeerd je content te parsen vanuit HTML. Omdat het soms een uitdaging kan zijn om content te extraheren, bieden we de mogelijkheid voor het opgeven van CSS-regels om de extractie makkelijker te maken." embed_by_username: "Gebruikersnaam voor het maken van topics" - embed_post_limit: "Maximale aantal berichten om in te bedden" - embed_title_scrubber: "Reguliere expressie voor het afleiden van de titels van berichten" - embed_truncate: "Ingebedde berichten inkorten" - embed_unlisted: "Geïmporteerde topics worden onzichtbaar totdat er antwoord is." - allowed_embed_selectors: "CSS-selector voor elementen die bij inbedding worden toegestaan" - blocked_embed_selectors: "CSS-selector voor elementen die bij inbedding worden verwijderd" - allowed_embed_classnames: "Toegestane CSS-klassenamen" - save: "Inbeddingsinstellingen opslaan" + embed_post_limit: "Maximaal aantal in te sluiten berichten" + embed_title_scrubber: "Reguliere expressie voor het afleiden van de titel van berichten" + embed_truncate: "Ingesloten berichten afkappen" + embed_unlisted: "Geïmporteerde topics worden onzichtbaar totdat er een antwoord is." + allowed_embed_selectors: "CSS-kiezer voor elementen die zijn toegestaan in insluitingen" + blocked_embed_selectors: "CSS-kiezer voor elementen die worden verwijderd uit insluitingen" + allowed_embed_classnames: "Namen toegestane CSS-klassen" + save: "Insluitingsinstellingen opslaan" permalink: title: "Permalinks" - description: "Toe te passen omleiding voor URL's die voor het forum niet bekend zijn." + description: "Toe te passen omleiding voor URL's die onbekend zijn voor het forum." url: "URL" topic_id: "Topic-ID" topic_title: "Topic" @@ -4987,8 +4981,8 @@ nl: tag_name: "Tagnaam" external_url: "Externe of relatieve URL" destination: "Bestemming" - copy_to_clipboard: "Permalink naar klembord kopiëren" - delete_confirm: Weet u zeker dat u deze permalink wilt verwijderen? + copy_to_clipboard: "Permalink kopiëren naar klembord" + delete_confirm: Weet je zeker dat je deze permalink wilt verwijderen? form: label: "Nieuw:" add: "Toevoegen" @@ -5005,7 +4999,7 @@ nl: replace: "Vervangen" wizard_js: wizard: - back: "Vorige" + back: "Terug" next: "Volgende" step-text: "Stap" step: "%{current} van %{total}" @@ -5013,11 +5007,11 @@ nl: uploading: "Uploaden..." upload_error: "Er is een fout opgetreden bij het uploaden van dat bestand. Probeer het opnieuw." staff_count: - one: "Uw gemeenschap heeft %{count} staflid (u)." - other: "Uw gemeenschap heeft %{count} stafleden, waaronder u." + one: "Uw community heeft %{count} staflid (u)." + other: "Uw community heeft %{count} stafleden, waaronder u." invites: add_user: "toevoegen" - none_added: "U hebt geen stafleden uitgenodigd. Weet u zeker dat u wilt verdergaan?" + none_added: "Je hebt geen stafleden uitgenodigd. Weet je zeker dat je door wilt gaan?" roles: admin: "Beheerder" moderator: "Moderator" diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index 8391d60cf1..a8a18ab753 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -290,6 +290,7 @@ pl_PL: delete: "Usuń" generic_error: "Przepraszamy, wystąpił błąd." generic_error_with_reason: "Wystąpił błąd: %{error}" + multiple_errors: "Wystąpiło wiele błędów: %{errors}" sign_up: "Rejestracja" log_in: "Logowanie" age: "Wiek" @@ -1086,6 +1087,7 @@ pl_PL: user_fields: none: "(wybierz opcję)" required: 'Wprowadź wartość dla "%{name}”' + same_as_password: "Twoje hasło nie powinno się powtarzać w innych polach." user: said: "%{username}:" profile: "Profil" @@ -1182,6 +1184,8 @@ pl_PL: no_notifications_title: "Nie masz jeszcze żadnych powiadomień" no_notifications_body: > W tym panelu zostaniesz powiadomiony o aktywności bezpośrednio związanej z Tobą, w tym o odpowiedziach na Twoje tematy i posty, gdy ktoś @oznaczy lub zacytuje Cię i odpowie na obserwowane przez Ciebie tematy. Powiadomienia będą również wysyłane na Twój adres e-mail, gdy nie jesteś zalogowany przez jakiś czas.
@@ -534,7 +534,7 @@ nl:
admin_quick_start_title: "LEES MIJ EERST: Snelstartgids voor beheerders"
category:
topic_prefix: "Over de categorie %{category}"
- replace_paragraph: "(Vervang deze eerste alinea door een korte beschrijving van uw nieuwe categorie. Deze leidraad verschijnt in het categorieselectiegebied, dus probeer deze onder de 200 tekens te houden.)"
+ replace_paragraph: "(Vervang deze eerste alinea door een korte beschrijving van de nieuwe categorie. Deze leidraad wordt weergegeven in het categorieselectiegebied, dus probeer deze onder de 200 tekens te houden.)"
post_template: "%{replace_paragraph}\n\nGebruik de volgende alinea's voor een langere beschrijving, of om richtlijnen of regels voor de categorie op te stellen:\n\n- Waarom zouden mensen deze categorie gebruiken? Waar dient deze voor?\n\n- Op welke punten onderscheidt deze categorie zich van andere categorieën die we al hebben?\n\n- Wat dienen topics in deze categorie over het algemeen te bevatten?\n\n- Hebben we deze categorie nodig? Kunnen we deze samenvoegen met een andere categorie of subcategorie?\n"
errors:
not_found: "Categorie niet gevonden!"
@@ -656,11 +656,11 @@ nl:
choose: "Een wachtwoord kiezen"
update: "Wachtwoord bijwerken"
save: "Wachtwoord instellen"
- title: "Wachtwoord herinitialiseren"
- success: "U hebt uw wachtwoord succesvol gewijzigd en bent nu aangemeld."
- success_unapproved: "U hebt uw wachtwoord succesvol gewijzigd."
+ title: "Wachtwoord herstellen"
+ success: "U hebt uw wachtwoord gewijzigd en bent nu aangemeld."
+ success_unapproved: "U hebt uw wachtwoord gewijzigd."
email_login:
- invalid_token: "Sorry, die koppeling voor het aanmelden via e-mail is te oud. Selecteer de knop Aanmelden en gebruik 'Ik ben mijn wachtwoord vergeten' om een nieuwe koppeling te ontvangen."
+ invalid_token: "Sorry, die e-mailaanmeldlink is te oud. Selecteer de knop Aanmelden en gebruik 'Ik ben mijn wachtwoord vergeten' om een nieuwe link te ontvangen."
title: "E-mailaanmelding"
user_auth_tokens:
browser:
@@ -698,7 +698,7 @@ nl:
error: "Er is een fout opgetreden bij het wijzigen van uw e-mailadres. Misschien is het adres al in gebruik?"
doesnt_exist: "Dat e-mailadres is niet aan uw account gekoppeld."
error_staged: "Er is een fout opgetreden bij het wijzigen van uw e-mailadres. Het adres is al in gebruik door een staged gebruiker."
- already_done: "Sorry, deze bevestigingskoppeling is niet meer geldig. Misschien is uw e-mailadres al gewijzigd?"
+ already_done: "Sorry, deze bevestigingslink is niet meer geldig. Misschien is uw e-mailadres al gewijzigd?"
confirm: "Bevestigen"
authorizing_new:
title: "Bevestig uw nieuwe e-mailadres"
@@ -708,13 +708,13 @@ nl:
old_email: "Oude e-mailadres: %{email}"
new_email: "Nieuwe e-mailadres: %{email}"
almost_done_title: "Nieuwe e-mailadres bevestigen"
- almost_done_description: "Er is een e-mail naar uw nieuwe e-mailadres verstuurd om de wijziging te bevestigen!"
+ almost_done_description: "Er is een e-mail naar uw nieuwe e-mailadres gestuurd om de wijziging te bevestigen!"
associated_accounts:
revoke_failed: "Intrekken van uw account bij %{provider_name} is mislukt."
connected: "(verbonden)"
activation:
action: "Klik hier om uw account te activeren"
- already_done: "Sorry, deze koppeling voor het bevestigen van uw account is niet meer geldig. Misschien is uw account al actief?"
+ already_done: "Sorry, deze link voor het bevestigen van uw account is niet meer geldig. Misschien is uw account al actief?"
please_continue: "Uw account is bevestigd; u wordt nu doorgestuurd naar de startpagina."
continue_button: "Doorgaan naar %{site_name}"
welcome_to: "Welkom bij %{site_name}!"
@@ -737,14 +737,14 @@ nl:
short_description: "Niet relevant voor de discussie"
spam:
title: "Spam"
- description: "Dit bericht is een advertentie of vandalisme. Het is niet relevant voor het huidige onderwerp."
+ description: "Dit bericht is een advertentie of vandalisme. Het is niet relevant voor het huidige topic."
short_description: "Dit is een advertentie of vandalisme"
email_title: '''%{title}'' is als spam gemarkeerd'
email_body: "%{link}\n\n%{message}"
inappropriate:
title: "Ongepast"
- description: 'Dit bericht bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze gemeenschapsrichtlijnen zou beschouwen.'
- short_description: 'Een overtreding van onze gemeenschapsrichtlijnen'
+ description: 'Dit bericht bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze communityrichtlijnen zou beschouwen.'
+ short_description: 'Een overtreding van onze communityrichtlijnen'
notify_user:
title: "@%{username} een bericht sturen"
description: "I wil rechtstreeks en persoonlijk met deze persoon over zijn of haar bericht praten."
@@ -779,7 +779,7 @@ nl:
invalid_origin_error: "De oorsprong van de authenticatieaanvraag komt niet overeen met de serveroorsprong."
malformed_attestation_error: "Er is een fout opgetreden bij het decoderen van de attestgegevens."
invalid_relying_party_id_error: "De Relying Party-ID van de authenticatieaanvraag komt niet overeen met de Relying Party-ID van de server."
- user_verification_error: "Er is gebruikersverificatie vereist."
+ user_verification_error: "Gebruikersverificatie is vereist."
unsupported_public_key_algorithm_error: "Het opgegeven algoritme van de openbare sleutel wordt niet ondersteund door de server."
unsupported_attestation_format_error: "De attestation-indeling wordt niet ondersteund door de server."
credential_id_in_use_error: "De opgegeven referentie-ID is al in gebruik."
@@ -795,9 +795,9 @@ nl:
short_description: "Dit is een advertentie"
inappropriate:
title: "Ongepast"
- description: 'Dit topic bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze gemeenschapsrichtlijnen zou beschouwen.'
+ description: 'Dit topic bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze communityrichtlijnen zou beschouwen.'
long_form: "heeft dit als ongepast gemarkeerd"
- short_description: 'Een overtreding van onze gemeenschapsrichtlijnen'
+ short_description: 'Een overtreding van onze communityrichtlijnen'
notify_moderators:
title: "Iets anders"
description: 'Dit topic vereist algemene aandacht van een staflid op basis van de richtlijnen, TOS, of om een andere reden dan hierboven vermeld.'
@@ -806,8 +806,8 @@ nl:
email_title: 'Het topic ''%{title}'' vereist aandacht van een moderator'
email_body: "%{link}\n\n%{message}"
flagging:
- you_must_edit: 'Uw bericht is gemarkeerd door de gemeenschap. Bekijk uw berichten.
' - user_must_edit: "Uw bericht is gemarkeerd door de gemeenschap en is tijdelijk verborgen.
" + you_must_edit: 'Uw bericht is gemarkeerd door de community. Bekijk uw berichten.
' + user_must_edit: "Uw bericht is gemarkeerd door de community en is tijdelijk verborgen.
" ignored: hidden_content: "Verborgen inhoud
" archetypes: @@ -816,8 +816,8 @@ nl: banner: title: "Bannertopic" message: - make: "Dit topic is nu een banner. De banner verschijnt bovenaan elke pagina, totdat de gebruiker deze verbergt." - remove: "Dit topic is geen banner meer. Het zal niet meer bovenaan elke pagina verschijnen." + make: "Dit topic is nu een banner. De banner wordt weergegeven bovenaan elke pagina, totdat de gebruiker deze sluit." + remove: "Dit topic is geen banner meer. Het wordt niet meer bovenaan elke pagina weergegeven." unsubscribed: title: "E-mailvoorkeuren bijgewerkt!" topic_description: "Gebruik de meldingsinstellingen onder of rechts van het topic om u opnieuw voor %{link} in te schrijven." @@ -849,7 +849,7 @@ nl: read: "lezen" read_write: "lezen/schrijven" description: '''%{application_name}'' vraagt de volgende toegang tot uw account:' - instructions: 'We hebben zojuist een nieuwe API-gebruikerssleutel voor u gegenereerd voor gebruik met ''%{application_name}''. Plak de volgende koppeling in uw toepassing:' + instructions: 'We hebben zojuist een nieuwe API-gebruikerssleutel voor u gegenereerd voor gebruik met ''%{application_name}''. Plak de volgende link in uw toepassing:' otp_description: 'Wilt u ''%{application_name}'' toegang tot deze website geven?' otp_confirmation: confirm_title: Doorgaan naar %{site_name} @@ -962,7 +962,7 @@ nl: title: "DAU/MAU" xaxis: "Day" yaxis: "DAU/MAU" - description: "Aantal leden dat de afgelopen dag was aangemeld, gedeeld door het aantal leden dat de afgelopen maand was aangemeld – geeft een % terug dat 'plakkerigheid' in de gemeenschap aangeeft. Het streven is >30%." + description: "Aantal leden dat de afgelopen dag was aangemeld, gedeeld door het aantal leden dat de afgelopen maand was aangemeld – geeft een % terug dat 'plakkerigheid' in de community aangeeft. Het streven is >30%." daily_engaged_users: title: "Dagelijkse actieve gebruikers" xaxis: "Dag" @@ -1044,12 +1044,12 @@ nl: title: "Systeem" xaxis: "Dag" yaxis: "Aantal berichten" - description: "Aantal persoonlijke berichten dat automatisch door het systeem is verstuurd." + description: "Aantal persoonlijke berichten dat automatisch door het systeem is verzonden." moderator_warning_private_messages: title: "Moderatorwaarschuwing" xaxis: "Dag" yaxis: "Aantal berichten" - description: "Aantal waarschuwingen dat door persoonlijke berichten van moderators is verstuurd." + description: "Aantal waarschuwingen dat door persoonlijke berichten van moderators is verzonden." notify_moderators_private_messages: title: "Moderators inlichten" xaxis: "Dag" @@ -1069,7 +1069,7 @@ nl: user: "Gebruiker" num_clicks: "Klikken" num_topics: "Topics" - description: "Gebruikers vermeld op aantallen klikken op koppelingen die ze hebben gedeeld." + description: "Gebruikers vermeld op volgorde van het aantal klikken op links die ze hebben gedeeld." top_traffic_sources: title: "Meeste verkeersbronnen" xaxis: "Domein" @@ -1223,13 +1223,13 @@ nl: failing_emails_warning: 'Er zijn %{num_failed_jobs} mislukte e-mailtaken. Controleer uw bestand app.yml en zorg ervoor dat de mailserverinstellingen juist zijn. Bekijk de mislukte taken in Sidekiq.' subfolder_ends_in_slash: "Uw submapconfiguratie is onjuist; de DISCOURSE_RELATIVE_URL_ROOT eindigt met een schuine streep." email_polling_errored_recently: - one: "E-mailpolling heeft de afgelopen 24 uur een fout gegenereerd. Bekijk de logboeken voor meer details." - other: "E-mailpolling heeft de afgelopen 24 uur %{count} fouten gegenereerd. Bekijk de logboeken voor meer details." + one: "E-mailpolling heeft de afgelopen 24 uur een fout gegenereerd. Zie de logs voor meer informatie." + other: "E-mailpolling heeft de afgelopen 24 uur %{count} fouten gegenereerd. Zie de logs voor meer informatie." missing_mailgun_api_key: "De server is geconfigureerd om e-mails via Mailgun te verzenden, maar u hebt geen API-sleutel opgegeven voor verificatie van de webhookberichten." bad_favicon_url: "De favicon wordt niet geladen. Controleer uw favicon-instelling in de Website-instellingen." poll_pop3_timeout: "Time-out voor verbinding met de POP3-server. Inkomende e-mail kon niet worden opgehaald. Controleer uw POP3-instellingen en serviceprovider." poll_pop3_auth_error: "Verbinding met de POP3-server is mislukt met een authenticatiefout. Controleer uw POP3-instellingen." - force_https_warning: "Uw website gebruikt SSL, maar `force_https` is nog niet ingeschakeld in uw website-instellingen." + force_https_warning: "Uw website gebruikt SSL, maar 'force_https' is nog niet ingeschakeld in uw website-instellingen." out_of_date_themes: "Er zijn updates voor de volgende thema's beschikbaar:" unreachable_themes: "We konden niet op updates controleren voor de volgende thema's:" site_settings: @@ -1245,9 +1245,9 @@ nl: min_first_post_length: "Minimaal toegestane lengte van eerste bericht (topictekst) in tekens" min_personal_message_post_length: "Minimaal toegestane berichtlengte in tekens voor berichten" max_post_length: "Maximaal toegestane lengte van een bericht in tekens" - topic_featured_link_enabled: "Plaatsing van een koppeling met topics toestaan" - show_topic_featured_link_in_digest: "Koppeling naar aanbevolen topics in de samenvattingsmail tonen" - min_topic_views_for_delete_confirm: "Minimale aantal weergaven dat een topic moet hebben om een bevestigingspop-up te laten verschijnen wanneer het wordt verwijderd" + topic_featured_link_enabled: "Sta het plaatsen van een link bij topics toe." + show_topic_featured_link_in_digest: "Link naar aanbevolen topic weergeven in de digest-e-mail." + min_topic_views_for_delete_confirm: "Minimale aantal weergaven dat een topic moet hebben voordat een bevestigingspop-up wordt weergegeven wanneer het wordt verwijderd" min_topic_title_length: "Minimaal toegestane lengte van een topictitel in tekens" max_topic_title_length: "Maximaal toegestane lengte van een topictitel in tekens" min_personal_message_title_length: "Minimaal toegestane titellengte voor een bericht in tekens" @@ -1283,9 +1283,9 @@ nl: max_image_height: "Maximale miniatuurhoogte van afbeeldingen in een bericht" responsive_post_image_sizes: "Grootte van Lightbox-voorbeeldafbeeldingen mag op hoge-DPI-schermen met de volgende pixelverhoudingen worden aangepast. Verwijder alle waarden om responsieve afbeeldingen uit te schakelen." fixed_category_positions: "Wanneer aangevinkt, kunt u categorieën in een vaste volgorde schikken. Wanneer niet aangevinkt, worden categorieën op activiteit vermeld." - fixed_category_positions_on_create: "Wanneer aangevinkt, wordt de categorievolgorde bij het maken van een topic aangehouden (vereist fixed_category_positions)." - add_rel_nofollow_to_user_content: '''rel nofollow'' toevoegen aan alle ingediende gebruikersinhoud, behalve voor interne koppelingen (inclusief bovenliggende domeinen). Als u dit wijzigt, moet u alle berichten opnieuw opbouwen met ''rake posts:rebake''' - exclude_rel_nofollow_domains: "Een lijst van domeinen waarvoor 'nofollow' niet aan koppelingen mag worden toegevoegd. example.com staat ook automatisch sub.example.com toe. Voeg minimaal het domein van deze website toe om webcrawlers alle inhoud te helpen vinden. Als andere onderdelen van uw website zich op andere domeinen bevinden, voeg die dan ook toe." + fixed_category_positions_on_create: "Wanneer aangevinkt, wordt de categorievolgorde aangehouden bij het maken van een topic (vereist fixed_category_positions)." + add_rel_nofollow_to_user_content: 'Voeg ''rel nofollow'' toe aan alle ingediende gebruikerscontent, behalve voor interne links (inclusief bovenliggende domeinen). Als u dit wijzigt, moet u alle berichten opnieuw opbouwen met ''rake posts:rebake''' + exclude_rel_nofollow_domains: "Een lijst van domeinen waarvoor 'nofollow' niet aan links mag worden toegevoegd. example.com staat ook automatisch sub.example.com toe. Voeg minimaal het domein van deze website toe om webcrawlers te helpen alle inhoud te vinden. Als onderdelen van uw website zich op andere domeinen bevinden, voeg die dan ook toe." post_excerpt_maxlength: "Maximale lengte van een fragment / samenvatting van een bericht." topic_excerpt_maxlength: "Maximale lengte van een fragment / samenvatting van een topic, gegenereerd vanuit het eerste bericht in een topic." show_pinned_excerpt_mobile: "Fragment tonen bij vastgemaakte topics in mobiele weergave." @@ -1298,8 +1298,8 @@ nl: facebook_app_access_token: "Een token, gegenereerd op basis van uw Facebook-app-ID en -geheim. Gebruikt om Instagram-oneboxes te genereren." logo: "De logoafbeelding links bovenaan op uw website. Gebruik een brede rechthoekige afbeelding met een hoogte van 120 en een hoogte-breedteverhouding groter dan 3:1. Bij leeg laten wordt de titeltekst van de website getoond." logo_small: "De kleine logoafbeelding links bovenaan op uw website, zichtbaar bij omlaag scrollen. Gebruik een vierkante afbeelding van 120 × 120. Bij leeg laten wordt een startpaginasymbool getoond." - digest_logo: "De alternatieve logoafbeelding bovenaan de e-mailsamenvatting van uw website. Gebruik een brede rechthoekige afbeelding. Gebruik geen SVG-afbeelding. Bij leeg laten wordt de afbeelding van de instelling `logo` gebruikt." - mobile_logo: "Het logo dat op de mobiele versie van uw website wordt gebruikt. Gebruik een brede rechthoekige afbeelding met een hoogte van 120 en een hoogte-breedteverhouding groter dan 3:1. Bij leeg laten wordt de afbeelding van de instelling `logo` gebruikt." + digest_logo: "De alternatieve logoafbeelding bovenaan de e-mailsamenvatting van uw website. Gebruik een brede rechthoekige afbeelding. Gebruik geen SVG-afbeelding. Bij leeg laten wordt de afbeelding van de instelling 'logo' gebruikt." + mobile_logo: "Het logo dat op de mobiele versie van uw website wordt gebruikt. Gebruik een brede rechthoekige afbeelding met een hoogte van 120 en een hoogte-breedteverhouding groter dan 3:1. Bij leeg laten wordt de afbeelding van de instelling 'logo' gebruikt." large_icon: "Afbeelding die als basis voor andere metagegevenspictogrammen wordt gebruikt. Moet idealiter groter zijn dan 512 x 512. Bij leeg laten wordt logo_small gebruikt." manifest_icon: "Afbeelding die als logo/splashafbeelding op Android wordt gebruikt. Wordt automatisch verkleind naar 512 x 512. Bij leeg laten wordt large_icon gebruikt." favicon: "Een favicon voor uw website, zie https://nl.wikipedia.org/wiki/Favicon. Om goed via een CDN te werken, moet dit een png zijn. Wordt verkleind naar 32x32. Bij leeg laten wordt large_icon gebruikt." @@ -1324,7 +1324,7 @@ nl: hide_post_sensitivity: "De waarschijnlijkheid dat een gemarkeerd bericht wordt verborgen" silence_new_user_sensitivity: "De waarschijnlijkheid dat een nieuwe gebruiker wordt gedempt op basis van spammarkeringen" auto_close_topic_sensitivity: "De waarschijnlijkheid dat een gemarkeerd topic automatisch wordt gesloten" - cooldown_minutes_after_hiding_posts: "Het aantal minuten dat een gebruiker moet wachten voordat deze een via de gemeenschap gemarkeerd bericht kan bewerken" + cooldown_minutes_after_hiding_posts: "Het aantal minuten dat een gebruiker moet wachten voordat deze een via de community gemarkeerd bericht kan bewerken" max_topics_in_first_day: "Het maximale aantal topics dat een gebruiker in de 24 uursperiode na het schrijven van een eerste bericht mag aanmaken" max_replies_in_first_day: "Het maximale aantal antwoorden dat een gebruiker in de 24 uursperiode na het schrijven van een eerste bericht mag aanmaken" tl2_additional_likes_per_day_multiplier: "Limiet van likes per dag voor TL2 (lid) verhogen door met dit getal te vermenigvuldigen" @@ -1337,8 +1337,8 @@ nl: flag_sockpuppets: "Als een nieuwe gebruiker op een topic antwoordt vanaf hetzelfde IP-adres als de gebruiker die het topic heeft gestart, beide berichten ervan markeren als potentiële spam." traditional_markdown_linebreaks: "Traditionele regeleinden gebruiken in Markdown, waarin 2 spaties aan het einde van een regel nodig zijn voor een regeleinde." enable_markdown_typographer: "Typografieregels gebruiken om leesbaarheid van tekst te verbeteren: rechte aanhalingstekens ' vervangen door gekrulde aanhalingstekens ’, (c) (tm) door symbolen, -- door emdash –, etc." - enable_markdown_linkify: "Tekst die eruitziet als een koppeling automatisch als een koppeling behandelen: www.example.com en https://example.com worden automatisch gekoppeld" - markdown_linkify_tlds: "Lijst van topleveldomeinen die automatisch als koppeling worden behandeld" + enable_markdown_linkify: "Behandel tekst die eruitziet automatisch als een link: www.example.com en https://example.com worden automatisch gelinkt" + markdown_linkify_tlds: "Lijst van topleveldomeinen die automatisch als link worden behandeld" markdown_typographer_quotation_marks: "Lijst van vervangingsparen voor dubbele en enkele aanhalingstekens" post_undo_action_window_mins: "Het aantal minuten dat gebruikers recente acties op een bericht ongedaan mogen maken (liken, markeren, etc)." must_approve_users: "Stafleden moeten alle nieuwe gebruikersaccounts goedkeuren voordat ze de website mogen bezoeken." @@ -1348,7 +1348,7 @@ nl: gtm_container_id: "Google Tag Manager-container-ID, bv. GTM-ABCDEF.U bent er bijna! We hebben een activeringsmail naar %{email} gestuurd. Volg de instructies in het e-mailbericht om uw account te activeren.
Als dit niet aankomt, contoleer dan uw spammap.
" - not_activated: "U kunt zich nog niet aanmelden. We hebben u een activeringsmail gestuurd. Volg de instructies in het e-mailbericht om uw account te activeren." - not_allowed_from_ip_address: "U kunt zich niet als %{username} aanmelden vanaf dat IP-adres." - admin_not_allowed_from_ip_address: "U kunt zich niet als beheerder aanmelden vanaf dat IP-adres." - reset_not_allowed_from_ip_address: "U kunt geen wachtwoordherinitialisatie aanvragen vanaf dat IP-adres." - suspended: "U kunt zich tot %{date} niet aanmelden." + not_activated: "Je kunt je nog niet aanmelden. We hebben je een activerings-e-mail gestuurd. Volg de instructies in de e-mail om je account te activeren." + not_allowed_from_ip_address: "Je kunt zich niet als %{username} aanmelden vanaf dat IP-adres." + admin_not_allowed_from_ip_address: "Je kunt zich niet als beheerder aanmelden vanaf dat IP-adres." + reset_not_allowed_from_ip_address: "Je kunt geen wachtwoordherstel aanvragen vanaf dat IP-adres." + suspended: "Je kunt zich tot %{date} niet aanmelden." suspended_with_reason: "Account geschorst tot %{date}: %{reason}" errors: "%{errors}" not_available: "Niet beschikbaar. %{suggestion} proberen?" - something_already_taken: "Er is iets misgegaan; misschien is de gebruikersnaam of het e-mailadres al geregistreerd. Probeer de koppeling 'Wachtwoord vergeten'." + something_already_taken: "Er is iets misgegaan; mogelijk is de gebruikersnaam of het e-mailadres al geregistreerd. Probeer de link 'Wachtwoord vergeten'." omniauth_error: generic: "Sorry, er is een fout opgetreden bij het autoriseren van uw account. Probeer het opnieuw." - csrf_detected: "Time-out bij autorisatie, of u hebt van browser gewisseld. Probeer het opnieuw." + csrf_detected: "Time-out bij autorisatie, of je bent van browser gewisseld. Probeer het opnieuw." request_error: "Er is een fout opgetreden bij het begin van de autorisatie. Probeer het opnieuw." invalid_iat: "Kan autorisatietoken niet verifiëren vanwege verschillen met de serverklok. Probeer het opnieuw." omniauth_error_unknown: "Er is iets misgegaan bij het verwerken van uw aanmelding. Probeer het opnieuw." omniauth_confirm_title: "Aanmelden via %{provider}" omniauth_confirm_button: "Doorgaan" - authenticator_error_no_valid_email: "E-mailadressen die met %{account} zijn gekoppeld, zijn niet toegestaan. Mogelijk dient u uw account met een ander e-mailadres te configureren." + authenticator_error_no_valid_email: "E-mailadressen gekoppeld aan %{account} zijn niet toegestaan. Mogelijk moet je je account configureren met een ander e-mailadres." new_registrations_disabled: "Nieuwe accountregistraties zijn op dit moment niet toegestaan." password_too_long: "Wachtwoorden mogen maximaal 200 tekens lang zijn." email_too_long: "Het opgegeven e-mailadres is te lang. Postvaknamen mogen niet langer zijn dan 254 tekens, en domeinnamen niet langer dan 253 tekens." - wrong_invite_code: "De door u ingevoerde code is ongeldig." + wrong_invite_code: "De ingevoerde code is ongeldig." reserved_username: "Die gebruikersnaam is niet toegestaan." - missing_user_field: "U hebt niet alle gebruikersvelden ingevuld" + missing_user_field: "Je hebt niet alle gebruikersvelden ingevuld" auth_complete: "Authenticatie is voltooid." click_to_continue: "Klik hier om door te gaan." second_factor_title: "Tweefactorauthenticatie" @@ -2078,19 +2078,19 @@ nl: already_suspended: "Gebruiker is %{time_ago} al geschorst door %{staff}." unsubscribe_mailer: title: "Mailer Uitschrijven" - subject_template: "Bevestig dat u geen e-mailupdates van %{site_title} meer wilt ontvangen" + subject_template: "Bevestig dat je geen e-mailupdates van %{site_title} meer wilt ontvangen" text_body_template: | - Iemand (mogelijk u?) heeft gevraagd geen e-mailupdates van %{site_domain_name} meer naar dit adres te sturen. - Als u dit wilt bevestigen, klikt u op deze koppeling: + Iemand (mogelijk jij?) heeft gevraagd geen e-mailupdates van %{site_domain_name} meer naar dit adres te sturen. + Klik op deze koppeling om dat te bevestigen: %{confirm_unsubscribe_link} - Als u e-mailupdates wilt blijven ontvangen, kunt u dit e-mailbericht negeren. + Als je e-mailupdates wilt blijven ontvangen, kun je dit e-mailbericht negeren. invite_mailer: title: "Mailer Uitnodiging" - subject_template: "%{inviter_name} heeft u uitgenodigd in '%{topic_title}' op %{site_domain_name}" + subject_template: "%{inviter_name} heeft je uitgenodigd voor '%{topic_title}' op %{site_domain_name}" text_body_template: | - %{inviter_name} heeft u uitgenodigd voor een discussie + %{inviter_name} heeft je uitgenodigd voor een discussie > **%{topic_title}** > @@ -2100,12 +2100,12 @@ nl: > %{site_title} -- %{site_description} - Als u geïnteresseerd bent, klik dan op de onderstaande koppeling: + Als je geïnteresseerd bent, klik dan op de onderstaande link: %{invite_link} custom_invite_mailer: title: "Mailer Aangepaste uitnodiging" - subject_template: "%{inviter_name} heeft u uitgenodigd in '%{topic_title}' op %{site_domain_name}" + subject_template: "%{inviter_name} heeft je uitgenodigd voor '%{topic_title}' op %{site_domain_name}" invite_forum_mailer: title: "Mailer Uitnodiging voor forum" custom_invite_forum_mailer: @@ -2116,7 +2116,7 @@ nl: download_backup_mailer: title: "Mailer Back-up downloaden" no_token: | - Sorry, deze back-up-downloadkoppeling is al gebruikt of is verlopen. + Sorry, deze back-updownloadlink is al gebruikt of is verlopen. admin_confirmation_mailer: title: "Mailer Beheerdersbevestiging" subject_template: "[%{email_prefix}] Bevestig nieuwe beheerdersaccount" @@ -2129,10 +2129,10 @@ nl: new_version_mailer: title: "Mailer Nieuwe versie" flag_reasons: - off_topic: "Uw bericht is gemarkeerd als **off-topic**: de gemeenschap vindt dat het niet goed bij het topic past, zoals momenteel bepaald door de titel en het eerste bericht." - inappropriate: "Uw bericht is gemarkeerd als **ongepast**: de gemeenschap vindt het bericht beledigend, grof, of een schending van [onze gemeenschapsrichtlijnen](%{base_path}/guidelines)." - spam: "Uw bericht is gemarkeerd als **spam**: de gemeenschap vindt dat het bericht een advertentie is, dat wil zeggen overdreven promotioneel van aard in plaats van nuttig of relevant voor het topic, zoals verwacht." - notify_moderators: "Uw bericht is gemarkeerd voor **aandacht van een moderator**: de gemeenschap vindt dat iets in het bericht handmatige interventie door een staflid vereist." + off_topic: "Uw bericht is gemarkeerd als **off-topic**: de community vindt dat het niet goed bij het topic past, zoals momenteel bepaald door de titel en het eerste bericht." + inappropriate: "Uw bericht is gemarkeerd als **ongepast**: de community vindt het bericht beledigend, grof, of een schending van [onze communityrichtlijnen](%{base_path}/guidelines)." + spam: "Uw bericht is gemarkeerd als **spam**: de community vindt dat het bericht een advertentie is, dat wil zeggen overdreven promotioneel van aard in plaats van nuttig of relevant voor het topic, zoals verwacht." + notify_moderators: "Uw bericht is gemarkeerd voor **aandacht van een moderator**: de community vindt dat iets in het bericht handmatige interventie door een staflid vereist." flags_dispositions: agreed: "Bedankt voor het informeren. We zijn het ermee eens dat er een probleem is en gaan ernaar kijken." agreed_and_deleted: "Bedankt voor het informeren. We zijn het ermee eens dat er een probleem is en hebben het bericht verwijderd." @@ -2143,7 +2143,7 @@ nl: private_topic_title: "Topic #%{id}" post_hidden: title: "Bericht verborgen" - subject_template: "Bericht verborgen door gemeenschapsmarkeringen" + subject_template: "Bericht verborgen door communitymarkeringen" post_hidden_again: title: "Bericht opnieuw verborgen" queued_by_staff: @@ -2162,7 +2162,7 @@ nl: %{new_user_tips} - We geloven altijd in [beschaafd gemeenschappelijk gedrag](%{base_url}/guidelines). + We geloven altijd in [beschaafd communitygedrag](%{base_url}/guidelines). Geniet van uw verblijf! welcome_tl1_user: @@ -2178,13 +2178,13 @@ nl: subject_template: "Gefeliciteerd met de promotie van uw vertrouwensniveau!" backup_succeeded: title: "Back-up geslaagd" - subject_template: "Back-up succesvol voltooid" + subject_template: "Back-up voltooid" backup_failed: title: "Back-up mislukt" subject_template: "Back-up mislukt" restore_succeeded: title: "Back-up teruggezet" - subject_template: "Back-up succesvol voltooid" + subject_template: "Back-up voltooid" restore_failed: title: "Terugzetten mislukt" subject_template: "Terugzetten mislukt" @@ -2216,11 +2216,11 @@ nl: Uw met dit e-mailadres gekoppelde account is gedempt. email_reject_empty: text_body_template: | - Het spijt ons, maar het plaatsen van uw e-mailbericht naar %{destination} (getiteld %{former_title}) is niet gelukt. + Sorry, je e-mailbericht aan %{destination} (getiteld %{former_title}) is niet gelukt. - We konden geen antwoordtekst in uw e-mail vinden. + We konden geen antwoordtekst vinden in je e-mail. - Als u deze melding ziet en _wel_ een antwoord had bijgevoegd, kunt u het opnieuw proberen met een eenvoudigere opmaak. + Als je deze melding ziet en _wel_ een antwoord had bijgevoegd, kun je het opnieuw proberen met een eenvoudigere opmaak. email_reject_parsing: text_body_template: | Het spijt ons, maar het plaatsen van uw e-mailbericht naar %{destination} (met de titel %{former_title}) is niet gelukt. @@ -2234,13 +2234,13 @@ nl: Enkele mogelijke oorzaken zijn: complexe opmaak, bericht te groot, bericht te klein. Probeer het opnieuw, of plaats uw bericht via de website als dit aanhoudt. email_reject_invalid_post_specified: text_body_template: | - Het spijt ons, maar het plaatsen van uw e-mailbericht naar %{destination} (getiteld %{former_title}) is niet gelukt. + Sorry, je e-mailbericht aan %{destination} (getiteld %{former_title}) is niet gelukt. Reden: %{post_error} - Als u het probleem kunt oplossen, probeer het dan opnieuw. + Als je het probleem kunt oplossen, probeer het dan opnieuw. email_reject_post_too_short: title: "E-mail Weigering - Bericht te kort" email_reject_invalid_post_action: @@ -2263,7 +2263,7 @@ nl: subject_template: "Account tijdelijk geblokkeerd" user_automatically_silenced: title: "Gebruiker automatisch gedempt" - subject_template: "Nieuwe gebruiker %{username} gedempt wegens markeringen door gemeenschap" + subject_template: "Nieuwe gebruiker %{username} gedempt wegens markeringen door community" text_body_template: | Dit is een automatisch bericht. @@ -2271,18 +2271,18 @@ nl: [Beoordeel de markeringen](%{base_url}/admin/flags). Als %{username} ten onrechte is gedempt, klik dan op de knop voor opheffen van het dempen op [de beheerpagina van deze gebruiker](%{user_url}). - Deze drempel kan worden gewijzigd via de `silence_new_user`-website-instellingen. + Deze drempel kan worden gewijzigd via de 'silence_new_user'-website-instellingen. spam_post_blocked: title: "Spambericht geblokkeerd" - subject_template: "Berichten van nieuwe gebruiker %{username} geblokkeerd vanwege herhaalde koppelingen" + subject_template: "Berichten van nieuwe gebruiker %{username} geblokkeerd vanwege herhaalde links" unsilenced: subject_template: "Account niet meer geblokkeerd" text_body_template: | Hallo, - Dit is een automatisch bericht van %{site_name} om u te laten weten dat uw account na beoordeling door een staflid niet meer is geblokkeerd. + Dit is een automatisch bericht van %{site_name} om u je laten weten dat je account na beoordeling door een staflid niet langer is geblokkeerd. - U kunt nu weer nieuwe antwoorden en topics aanmaken. Bedankt voor uw geduld. + Je kunt nu weer nieuwe antwoorden plaatsen en topics maken. Bedankt voor je geduld. pending_users_reminder: subject_template: one: "%{count} gebruiker wacht op goedkeuring" @@ -2298,8 +2298,8 @@ nl: subject_pm: "[PM]" email_from: "%{user_name} via %{site_name}" user_notifications: - previous_discussion: "Vorige reacties" - in_reply_to: "in reactie op" + previous_discussion: "Eerdere antwoorden" + in_reply_to: "In antwoord op" unsubscribe: title: "Uitschrijven" description: "Niet geïnteresseerd in deze e-mails? Geen probleem! Klik hieronder om direct uitgeschreven te worden:" @@ -2468,7 +2468,7 @@ nl: popular_posts: "Populaire berichten" more_new: "Nieuw voor u" subject_template: "[%{email_prefix}] Samenvatting" - unsubscribe: "Deze samenvatting wordt vanaf %{site_link} als we u een tijd niet hebben gezien. Wijzig %{email_preferences_link}, of %{unsubscribe_link} om u uit te schrijven." + unsubscribe: "Deze samenvatting wordt verzonden van %{site_link} als we je een tijdje niet hebben gezien. Wijzig %{email_preferences_link}, of %{unsubscribe_link} om je af te melden." your_email_settings: "uw e-mailinstellingen" click_here: "klik hier" from: "%{site_name}" @@ -2477,21 +2477,21 @@ nl: title: "Wachtwoord vergeten" subject_template: "[%{email_prefix}] Wachtwoord opnieuw instellen" text_body_template: | - Iemand heeft gevraagd uw wachtwoord op [%{site_name}](%{base_url}) opnieuw in te stellen. + Iemand heeft gevraagd je wachtwoord op [%{site_name}](%{base_url}) te herstellen. - Als u dit niet was, kunt u deze e-mail gewoon negeren. + Als jij dat niet was, kun je deze e-mail negeren. - Klik op de volgende koppeling om een nieuw wachtwoord te kiezen: + Klik op de volgende link om een nieuw wachtwoord te kiezen: %{base_url}/u/password-reset/%{email_token} email_login: - title: "Aanmelden via koppeling" - subject_template: "[%{email_prefix}] Aanmelden via koppeling" + title: "Aanmelden via link" + subject_template: "[%{email_prefix}] Aanmelden via link" text_body_template: | - Dit is uw koppeling om u bij [%{site_name}](%{base_url}) aan te melden. + Dit is je link om je aan te melden bij [%{site_name}](%{base_url}). - Als u niet om deze koppeling hebt gevraagd, kunt u deze e-mail gewoon negeren. + Als je niet om deze link hebt gevraagd, kun je deze e-mail negeren. - Klik op de volgende koppeling om u aan te melden: + Klik op de volgende link om je aan te melden: %{base_url}/session/email-login/%{email_token} set_password: title: "Wachtwoord instellen" @@ -2500,11 +2500,11 @@ nl: title: "Beheerdersaanmelding" subject_template: "[%{email_prefix}] Aanmelding" text_body_template: | - Iemand heeft om aanmelding gevraagd bij uw account op [%{site_name}](%{base_url}). + Iemand heeft gevraagd om aanmelding op je account op [%{site_name}](%{base_url}). - Als u deze aanvraag niet hebt gedaan, kunt u deze e-mail gewoon negeren. + Als je hier niet om hebt gevraagd, kun je deze e-mail negeren. - Klik op de volgende koppeling om u aan te melden: + Klik op de volgende link om je aan te melden: %{base_url}/session/email-login/%{email_token} account_created: title: "Account gemaakt" @@ -2518,11 +2518,9 @@ nl: confirm_old_email: subject_template: "[%{email_prefix}] Bevestig uw huidige e-mailadres" text_body_template: | - Voordat we uw e-mailadres kunnen wijzigen, dient u te bevestigen dat u de huidige - e-mailaccount beheert. Nadat u deze stap hebt voltooid, laten we u het nieuwe - e-mailadres bevestigen. + Voordat we je e-mailadres kunnen wijzigen, moet je bevestigen dat je de controle hebt over het huidige e-mailaccount. Nadat je deze stap hebt voltooid, moet je het nieuwe e-mailadres bevestigen. - Bevestig uw huidige e-mailadres voor %{site_name} door op de volgende koppeling te klikken: + Bevestig je huidige e-mailadres voor %{site_name} door op de volgende link te klikken: %{base_url}/u/confirm-old-email/%{email_token} confirm_old_email_add: @@ -2557,16 +2555,16 @@ nl: image: "afbeelding" upload: edit_reason: "lokale kopieën van afbeeldingen gedownload" - unauthorized: "Sorry, het bestand dat u probeert te uploaden is niet toegestaan (toegestane extensies: %{authorized_extensions})." + unauthorized: "Sorry, het bestand dat je probeert te uploaden is niet toegestaan (toegestane extensies: %{authorized_extensions})." pasted_image_filename: "Geplakte afbeelding" store_failure: "Het opslaan van upload #%{upload_id} voor gebruiker #%{user_id} is mislukt." - file_missing: "Sorry, u moet een bestand opgeven om te uploaden." - empty: "Sorry, maar het bestand dat u hebt opgegeven is leeg." + file_missing: "Sorry, je moet een bestand opgeven om te uploaden." + empty: "Sorry, het verstrekte bestand is leeg." png_to_jpg_conversion_failure_message: "Er is een fout opgetreden bij het converteren van PNG naar JPG." attachments: - too_large: "Sorry, het bestand dat u probeert te uploaden is te groot (maximumgrootte is %{max_size_kb}%KB)." + too_large: "Sorry, het bestand dat je probeert te uploaden is te groot (maximale grootte is %{max_size_kb} KB)." images: - too_large: "Sorry, de afbeelding die u probeert te uploaden is te groot (maximumgrootte is %{max_size_kb}%KB). Verklein de afbeelding en probeer het opnieuw." + too_large: "Sorry, de afbeelding die je probeert te uploaden is te groot (maximale grootte is %{max_size_kb} KB). Verklein de afbeelding en probeer het opnieuw." size_not_found: "Het is niet gelukt de afmetingen van de afbeelding te bepalen. Misschien is het bestand corrupt?" avatar: missing: "Sorry, we kunnen geen avatar vinden die met dat e-mailadres is gekoppeld. Kunt u proberen deze opnieuw te uploaden?" @@ -2642,7 +2640,7 @@ nl: Deze badge wordt toegekend als u voor het eerst een wiki-bericht bewerkt. basic_user: name: Basis - description: Alle essentiële gemeenschapsfuncties toegekend + description: Alle essentiële communityfuncties toegekend member: name: Lid description: Uitnodigingen, groepsberichten, meer likes toegekend @@ -2675,7 +2673,7 @@ nl: name: Goed topic description: Heeft 25 likes op een topic ontvangen long_description: | - Deze badge wordt toegekend wanneer je topic 25 likes krijgt. Je startte een levendig gesprek waar de gemeenschap zich omheen verzamelde. + Deze badge wordt toegekend wanneer je topic 25 likes krijgt. Je startte een levendig gesprek waar de community zich omheen verzamelde. great_topic: name: Geweldig topic description: Heeft 50 likes op een topic ontvangen @@ -2701,21 +2699,21 @@ nl: name: Campaigner description: 3 basisgebruikers uitgenodigd long_description: | - Deze badge wordt toegekend wanneer u 3 mensen hebt uitgenodigd die aansluitend genoeg tijd op deze website hebben doorgebracht om basisgebruiker te worden. Een levendige gemeenschap heeft een regelmatige inbreng van nieuwkomers nodig die regelmatig deelnemen en nieuwe geluiden aan de conversaties toevoegen. + Deze badge wordt toegekend wanneer u 3 mensen hebt uitgenodigd die aansluitend genoeg tijd op deze website hebben doorgebracht om basisgebruiker te worden. Een levendige community heeft een regelmatige inbreng van nieuwkomers nodig die regelmatig deelnemen en nieuwe geluiden aan de conversaties toevoegen. champion: name: Kampioen first_share: name: Eerste deelactie description: Heeft een bericht gedeeld first_link: - name: Eerste koppeling - description: Heeft een koppeling naar een ander topic toegevoegd + name: Eerste link + description: Heeft een link naar een ander topic toegevoegd first_quote: name: Eerste citaat description: Heeft een bericht geciteerd read_guidelines: name: Richtlijnen gelezen - description: Heeft de gemeenschapsrichtlijnen gelezen + description: Heeft de communityrichtlijnen gelezen long_description: | Deze badge wordt toegekend als u de richtlijnen hebt gelezen. Het opvolgen en delen van deze eenvoudige richtlijnen draagt bij aan een veilige, leuke en duurzame community voor iedereen. Bedenk altijd dat er een mens, net zoals u, aan de andere kant van het scherm zit. Wees aardig! reader: @@ -2724,14 +2722,14 @@ nl: long_description: | Deze badge wordt toegekend wanneer u voor het eerst een topic met meer dan 100 antwoorden hebt gelezen. Als u een gesprek zorgvuldig volgt, kan u de discussie volgen en de verschillende standpunten begrijpen; dit leidt tot interessantere gesprekken. Hoe meer u leest, hoe beter het gesprek wordt. Zoals we zeggen, lezen is fundamenteel! :slight_smile: popular_link: - name: Populaire koppeling - description: Heeft een externe koppeling met 50 klikken geplaatst + name: Populaire link + description: Heeft een externe klink met 50 klikken geplaatst hot_link: name: Zeer populaire koppeling - description: Heeft een externe koppeling met 300 klikken geplaatst + description: Heeft een externe klink met 300 klikken geplaatst famous_link: - name: Uiterst populaire koppeling - description: Heeft een externe koppeling met 1000 klikken geplaatst + name: Beroemde link + description: Heeft een externe klink met 1000 klikken geplaatst appreciated: name: Gewaardeerd description: Heeft 1 like op 20 berichten ontvangen @@ -2744,7 +2742,7 @@ nl: out_of_love: description: '%{max_likes_per_day} likes op een dag gebruikt' long_description: | - Deze badge wordt toegekend wanneer u alle %{max_likes_per_day} van uw dagelijkse likes gebruikt. Door eraan te denken even de tijd te nemen en de berichten waar u plezier aan beleeft en die u waardeert te liken, worden uw medegemeenschapsleden aangemoedigd in de toekomst zelfs nog geweldigere discussies aan te maken. + Deze badge wordt toegekend wanneer u alle %{max_likes_per_day} van uw dagelijkse likes gebruikt. Door eraan te denken even de tijd te nemen en de berichten waar u plezier aan beleeft en die u waardeert te liken, worden uw medecommunityleden aangemoedigd in de toekomst zelfs nog geweldigere discussies aan te maken. higher_love: description: 5 keer %{max_likes_per_day} likes op een dag gebruikt long_description: | @@ -2752,7 +2750,7 @@ nl: crazy_in_love: description: 20 keer %{max_likes_per_day} likes op een dag gebruikt long_description: | - Deze badge wordt toegekend wanneer u 20 dagen lang alle %{max_likes_per_day} van uw dagelijkse likes gebruikt. Wow! U bent een rolmodel voor het aanmoedigen van uw medegemeenschapsleden! + Deze badge wordt toegekend wanneer u 20 dagen lang alle %{max_likes_per_day} van uw dagelijkse likes gebruikt. Wow! U bent een rolmodel voor het aanmoedigen van uw medecommunityleden! thank_you: name: Bedankt description: Heeft 20 geliket en 10 likes gegeven @@ -2789,7 +2787,7 @@ nl: Deze badge wordt toegekend voor het bezoeken op 365 opeenvolgende dagen. Wauw, een heel jaar! badge_title_metadata: "%{display_name}-badge op %{site_title}" admin_login: - success: "E-mail verstuurd" + success: "E-mail verzonden" errors: unknown_email_address: "Onbekend e-mailadres." invalid_token: "Ongeldig token." @@ -2800,7 +2798,7 @@ nl: initial_topic_title: Performancerapportages van de website tags: title: "Tags" - restricted_tag_disallowed: 'U kunt de tag "%{tag}” niet toepassen.' + restricted_tag_disallowed: 'Je kunt de tag "%{tag}” niet toepassen.' minimum_required_tags: one: "U moet minstens %{count} tag selecteren." other: "U moet minstens %{count} tags selecteren." @@ -2822,7 +2820,7 @@ nl: title: "Bevestig uw e-mailadres" resend_email: title: "Activeringsmail opnieuw versturen" - message: "We hebben de activeringsmail opnieuw naar %{email} verstuurd" + message: "
We hebben de activerings-e-mail opnieuw naar %{email} gestuurd"
safe_mode:
title: "Veilige modus starten"
no_unofficial_plugins: "Niet-officiële plug-ins uitschakelen"
@@ -2841,7 +2839,7 @@ nl:
fields:
login_required:
placeholder: "Privé"
- extra_description: "Alleen aangemelde gebruikers hebben toegang tot deze gemeenschap"
+ extra_description: "Alleen aangemelde gebruikers hebben toegang tot deze community"
must_approve_users:
placeholder: "Heeft goedkeuring nodig"
ready:
@@ -2894,7 +2892,7 @@ nl:
label: "Geautomatiseerde berichten"
invites:
title: "Staf uitnodigen"
- description: "U bent bijna klaar! Nodig wat mensen uit om te helpen met het seeden van uw discussies met interessante topics en berichten om uw gemeenschap op te zetten."
+ description: "U bent bijna klaar! Nodig wat mensen uit om te helpen met het seeden van uw discussies met interessante topics en berichten om uw community op te zetten."
disabled: "Omdat sociale aanmeldingen zijn uitgeschakeld, is het niet mogelijk om uitnodigingen naar iedereen te versturen. Ga verder met de volgende stap."
finished:
title: "Uw Discourse is gereed!"
@@ -2909,7 +2907,7 @@ nl:
replied: '%{username} heeft op u geantwoord in ''%{topic}'' - %{site_title}'
posted: '%{username} heeft een bericht geplaatst in ''%{topic}'' - %{site_title}'
private_message: '%{username} heeft u een privébericht gestuurd in ''%{topic}'' - %{site_title}'
- linked: '%{username} heeft een koppeling naar uw bericht geplaatst vanaf ''%{topic}'' - %{site_title}'
+ linked: '%{username} heeft een link naar je bericht geplaatst vanaf ''%{topic}'' - %{site_title}'
watching_first_post: '%{username} heeft een nieuw topic gemaakt: ''%{topic}'' - %{site_title}'
confirm_title: "Meldingen ingeschakeld - %{site_title}"
confirm_body: "Gelukt! Meldingen zijn ingeschakeld."
@@ -2935,8 +2933,8 @@ nl:
low: "Laag"
medium: "Gemiddeld"
high: "Hoog"
- must_claim: "U moet items opeisen voordat u er handelingen op kunt uitvoeren."
- user_claimed: "Dit item is door een andere gebruiker opgeëist."
+ must_claim: "U moet items claimen voordat u er acties op kunt uitvoeren."
+ user_claimed: "Dit item is door een andere gebruiker geclaimd."
missing_version: "U moet een versieparameter opgeven"
conflict: "Een updateconflict heeft ervoor gezorgd dat u dit niet kon doen."
reasons:
@@ -2972,14 +2970,14 @@ nl:
delete_and_ignore_replies:
title: "Bericht + antwoorden verwijderen en negeren"
description: "Bericht en alle antwoorden erop verwijderen; als dit het eerste bericht is, ook het topic verwijderen"
- confirm: "Weet u zeker dat u de antwoorden op het bericht ook wilt verwijderen?"
+ confirm: "Weet je zeker dat je de antwoorden op het bericht ook wilt verwijderen?"
delete_and_agree:
title: "Bericht verwijderen en akkoord"
description: "Bericht verwijderen; als dit het eerste bericht is, ook het topic verwijderen"
delete_and_agree_replies:
title: "Bericht + antwoorden verwijderen en akkoord"
description: "Bericht en alle antwoorden erop verwijderen; als dit het eerste bericht is, ook het topic verwijderen"
- confirm: "Weet u zeker dat u de antwoorden op het bericht ook wilt verwijderen?"
+ confirm: "Weet je zeker dat je de antwoorden op het bericht ook wilt verwijderen?"
disagree_and_restore:
title: "Niet akkoord en bericht terugzetten"
description: "Het bericht terugzetten, zodat alle gebruikers het kunnen zien."
diff --git a/config/locales/server.pl_PL.yml b/config/locales/server.pl_PL.yml
index 53b55716be..60019ff0fe 100644
--- a/config/locales/server.pl_PL.yml
+++ b/config/locales/server.pl_PL.yml
@@ -260,6 +260,7 @@ pl_PL:
invalid_access: "Nie masz uprawnień do przeglądania żądanego zasobu."
requires_groups: "Zaproszenie nie zostało zapisane, ponieważ podany temat jest niedostępny. Dodaj jedną z następujących grup: %{groups}."
domain_not_allowed: "Twój adres e-mail nie może zostać użyty do zrealizowania tego zaproszenia."
+ existing_user_success: "Zaproszenie zostało pomyślnie zrealizowane"
bulk_invite:
file_should_be_csv: "Wgrywany plik powinien być formatu csv."
max_rows: "Wysłano %{max_bulk_invites} pierwszych zaproszeń. Sprobuj podzielić plik na mniejsze części."
@@ -538,6 +539,7 @@ pl_PL:
Możesz edytować swoje wcześniejsze odpowiedzi, aby dodać cytat, poprzez podświetlenie tekstu i wybranie przycisku cytuj odpowiedź który się pojawi.
Każdemu łatwiej będzie przeczytać temat, który ma kilka pogłębionych odpowiedzi, zamiast wielu niewielkich, indywidualnych.
+ dominating_topic: Zamieściłeś tutaj ponad %{percent}% odpowiedzi; czy możemy zasugerować, żebyś dał innym ludziom możliwość wypowiedzenia się?
get_a_room: Odpowiedziałeś na @%{reply_username} %{count} razy. Czy wiesz, że możesz zamiast tego wysłać wiadomość osobistą?
too_many_replies: |
### Osiągnąłeś/łaś limit odpowiedzi w tym temacie
@@ -642,6 +644,7 @@ pl_PL:
<<: *errors
uncategorized_category_name: "Bez kategorii"
general_category_name: "Ogólne"
+ general_category_description: "Utwórz tutaj tematy, które nie pasują do żadnej innej istniejącej kategorii."
meta_category_name: "Dyskusje o serwisie"
meta_category_description: "Dyskusje o tej stronie, jej organizacji, tym jak działa i jak możemy ją usprawnić."
staff_category_name: "Zespół"
@@ -1635,6 +1638,7 @@ pl_PL:
post_menu: "Określ które elementy menu wpisu powinny być widoczne i w jakiej kolejności. Przykład like|edit|flag|delete|share|bookmark|reply"
post_menu_hidden_items: "Elementy w menu, które w menu wpisu będą ukryte, chyba że kliknięty zostanie przycisk rozwijania."
share_links: "Określ które elementy menu udostępniania powinny być widoczne i w jakiej kolejności. "
+ allow_username_in_share_links: "Zezwalaj na umieszczanie nazw użytkowników w linkach do udostępniania. Jest to przydatne, aby nagradzać odznaki na podstawie unikalnych odwiedzających."
site_contact_username: "Nazwa użytkownika, spod której wysyłane będą automatyczne wiadomości. Jeśli pusta, użyte będzie domyślne konto System."
site_contact_group_name: "Prawidłowa nazwa grupy, do której zostaną zaproszone wszystkie automatyczne wiadomości."
send_welcome_message: "Wyślij wszystkim nowym użytkownikom powitalną wiadomość z krótkim przewodnikiem."
@@ -1657,6 +1661,7 @@ pl_PL:
enable_badges: "Włącz system odznak"
max_favorite_badges: "Maksymalna liczba odznak, które użytkownik może wybrać"
enable_whispers: "Pozwalaj administracji na prywatną rozmowę w tematach."
+ whispers_allowed_groups: "Zezwalaj na prywatną komunikację w ramach tematów członkom określonych grup."
allow_index_in_robots_txt: "W pliku robots.txt określ, że ta witryna może być indeksowana przez wyszukiwarki internetowe. W wyjątkowych przypadkach możesz trwale zastąpić plik robots.txt."
blocked_email_domains: "Lista domen poczty e-mail rozdzielonych pionową kreską, z których użytkownicy nie mogą rejestrować kont. Przykład: mailinator.com|trashmail.net"
allowed_email_domains: "Lista rozdzielonych pionową kreską domen e-mail, z których użytkownicy MUSZĄ się rejestrować. UWAGA: Użytkownicy z domenami e-mail innymi niż wypisane tutaj nie będą mogli się zarejestrować!"
@@ -1707,6 +1712,7 @@ pl_PL:
google_oauth2_client_secret: "Client Secret twojej aplikacji w Google"
google_oauth2_prompt: "Opcjonalna lista wartości łańcuchowych rozdzielanych spacjami, która określa, czy serwer autoryzacji monituje użytkownika o ponowne uwierzytelnienie i zgodę. Możliwe wartości można znaleźć na https://developers.google.com/identity/protocols/OpenIDConnect#prompt ."
google_oauth2_hd: "Opcjonalna domena Google Apps Hosted, do której logowanie będzie ograniczone. Więcej informacji można znaleźć na https://developers.google.com/identity/protocols/OpenIDConnect#hd-param"
+ google_oauth2_hd_groups: "(eksperymentalne) Pobierz grupy dyskusyjne Google użytkowników w domenie hostowanej po uwierzytelnieniu. Pobrane grupy dyskusyjne Google mogą być używane do przyznawania automatycznego członkostwa w grupach Discourse (zobacz ustawienia grupy). Więcej informacji znajdziesz na https://meta.discourse.org/t/226850"
enable_twitter_logins: "Włącz uwierzytelnianie przez Twittera, wymaga twitter_consumer_key i twitter_consumer_secret. Zobacz Konfigurowanie logowania przez Twittera (i bogatych embedów) dla Discourse."
twitter_consumer_key: "Klucz klienta do uwierzytelnienia na Twitterze, zarejestrowany na https://developer.twitter.com/apps"
twitter_consumer_secret: "Sekret klienta dotyczący uwierzytelniania na Twitterze, zarejestrowany na stronie https://developer.twitter.com/apps"
diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml
index c1e90c26b5..b01b503e08 100644
--- a/config/locales/server.ru.yml
+++ b/config/locales/server.ru.yml
@@ -5,6 +5,267 @@
# https://translate.discourse.org/
ru:
+ dates:
+ short_date_no_year: "D MMM"
+ short_date: "D MMM YYYY"
+ long_date: "D MMMM YYYY, HH:mm"
+ datetime_formats: &datetime_formats
+ formats:
+ short: "%d.%m.%Y"
+ short_no_year: "%d %B"
+ date_only: "%d.%m.%Y"
+ long: "%d.%B.%Y %H:%M:%S"
+ no_day: "%B %Y"
+ calendar_ics: "%d%m%Y %H%M%S (UTC)"
+ date:
+ month_names:
+ - null
+ - Январь
+ - Февраль
+ - Март
+ - Апрель
+ - Май
+ - Июнь
+ - Июль
+ - Август
+ - Сентябрь
+ - Октябрь
+ - Ноябрь
+ - Декабрь
+ <<: *datetime_formats
+ time:
+ am: "до полудня"
+ pm: "после полудня"
+ <<: *datetime_formats
+ title: "Discourse"
+ topics: "Темы"
+ posts: "Сообщения"
+ views: "Просмотры"
+ loading: "Загрузка..."
+ powered_by_html: 'При поддержке Discourse, лучше всего использовать с включённым JavaScript'
+ sign_up: "Зарегистрироваться"
+ log_in: "Войти"
+ submit: "Подтвердить"
+ purge_reason: "Учётная запись удалена автоматически как заброшенная, выключенная."
+ disable_remote_images_download_reason: "Загрузка картинок была отключена из-за недостатка места на диске."
+ anonymous: "Анонимный"
+ remove_posts_deleted_by_author: "Удалено автором"
+ redirect_warning: "Нам не удалось убедиться, что выбранная вами ссылка действительно была размещена на форуме. Если вы хотите продолжить, выберите ссылку ниже."
+ on_another_topic: "По другой теме"
+ inline_oneboxer:
+ topic_page_title_post_number: "#%{post_number}"
+ topic_page_title_post_number_by_user: "#%{post_number} от пользователя %{username}"
+ themes:
+ bad_color_scheme: "Не удаётся обновить тему, недопустимая цветовая палитра"
+ other_error: "При обновлении темы оформления что-то пошло не так."
+ ember_selector_error: "К сожалению, использование CSS-селекторов #ember или .ember-view запрещено, поскольку эти имена создаются динамически во время выполнения кода и со временем будут меняться. Попробуйте другой селектор."
+ import_error:
+ generic: При импорте темы произошла ошибка
+ upload: "Ошибка при создании ресурса загрузки: %{name}. %{errors}"
+ about_json: "Ошибка импорта: файл about.json не существует или имеет неверный формат. Вы действительно импортируете тему Discourse?"
+ about_json_values: "Файл about.json содержит недопустимые значения:%{errors}"
+ modifier_values: "модификаторы about.json содержат недопустимые значения: %{errors}"
+ git: "Ошибка клонирования git-репозитория: доступ запрещён или репозиторий не найден"
+ git_ref_not_found: "Невозможно получить объект по ссылке git: %{ref}"
+ unpack_failed: "Не удалось распаковать файл"
+ file_too_big: "Несжатый файл слишком велик."
+ unknown_file_type: "Загруженный файл не является допустимой темой Discourse."
+ not_allowed_theme: "Репозиторий `%{repo}` отсутствует в списке разрешённых тем (см. параметр `allowed_theme_repos`)."
+ ssh_key_gone: "Вы слишком долго ждали установки темы и срок действия ключа SSH истёк. Пожалуйста, попробуйте ещё раз."
+ errors:
+ component_no_user_selectable: "Компоненты темы не могут быть выбраны пользователем"
+ component_no_default: "Компоненты темы не могут быть темой по умолчанию"
+ component_no_color_scheme: "Компоненты темы не могут иметь цветовые палитры"
+ no_multilevels_components: "Темы с дочерними темами не могут сами быть дочерними темами"
+ optimized_link: Оптимизированные ссылки на изображения являются эфемерными и не должны включаться в исходный код темы.
+ settings_errors:
+ invalid_yaml: "Неверный формат YAML-файла."
+ data_type_not_a_number: "Тип `%{name}` не поддерживается. Поддерживаются следующие типы: `integer`, `bool`, `list`, `enum` и `upload`"
+ name_too_long: "Есть настройка со слишком длинным названием. Максимальная длина - 255 символов."
+ default_value_missing: "Настройка `%{name}` не имеет значения по умолчанию"
+ default_not_match_type: "Стандартные значения настройки `%{name}` не соответствуют допустимым типам."
+ default_out_range: "Стандартное значения настройки `%{name}` не находится в указанном диапазоне."
+ enum_value_not_valid: "Выбранное значение не является одним из вариантов перечисления."
+ number_value_not_valid: "Новое значение находится за пределами допустимого диапазона."
+ number_value_not_valid_min_max: "Значение должно быть от %{min} до %{max}."
+ number_value_not_valid_min: "Значение должно быть равным или больше чем %{min}"
+ number_value_not_valid_max: "Значение должно быть равным или меньше чем %{max}"
+ string_value_not_valid: "Длина нового значения находится за пределами допустимого диапазона."
+ string_value_not_valid_min_max: "Допустимый размер - от %{min} до %{max} символов."
+ string_value_not_valid_min: "Значение должно быть не менее %{min} символов."
+ string_value_not_valid_max: "Значение должно быть не более %{max} символов."
+ locale_errors:
+ top_level_locale: "Ключ верхнего уровня в файле локали должен соответствовать имени локали"
+ invalid_yaml: "Неверный перевод YAML-файла"
+ emails:
+ incoming:
+ default_subject: "Тема входящего письма по умолчанию."
+ show_trimmed_content: "Показать обрезанный текст"
+ maximum_staged_user_per_email_reached: "Достигнуто максимальное количество сымитированных пользователей, созданных по электронной почте."
+ no_subject: "(нет темы)"
+ no_body: "(нет содержимого)"
+ missing_attachment: "(Вложение %{filename} отсутствует)"
+ continuing_old_discussion:
+ one: "Продолжение обсуждение темы [%{title}](%{url}), поскольку указанная тема была создана более %{count} дня назад."
+ few: "Продолжение обсуждение темы [%{title}](%{url}), поскольку указанная тема была создана более %{count} дней назад."
+ many: "Продолжение обсуждение темы [%{title}](%{url}), поскольку указанная тема была создана более %{count} дней назад."
+ other: "Продолжение обсуждение темы [%{title}](%{url}), поскольку указанная тема была создана более %{count} дней назад."
+ errors:
+ empty_email_error: "Происходит, когда мы получаем пустое письмо."
+ no_message_id_error: "Происходит, когда письмо не имеет заголовка 'Message-Id'."
+ auto_generated_email_error: "Происходит, когда заголовок \"приоритет\" установлен в: 'bulk', 'list', 'auto_reply', 'junk', или когда другой заголовок содержит флаги 'auto-submitted', 'auto-replied' или 'auto-generated'."
+ no_body_detected_error: "Происходит, когда мы не смогли извлечь текст письма и не было никаких вложений."
+ no_sender_detected_error: "Происходит, когда мы не можем найти действительный Email-адрес в заголовке From."
+ from_reply_by_address_error: "Происходит, когда заголовок From совпадает с ответом по адресу электронной почты."
+ inactive_user_error: "Происходит, когда отправитель неактивен."
+ silenced_user_error: "Происходит, когда отправитель заблокирован."
+ bad_destination_address: "Происходит, когда ни один из адресов электронной почты в полях «Кому / Копия» не соответствует настроенному адресу входящей электронной почты."
+ strangers_not_allowed_error: "Происходит, когда пользователь пытается создать новую тему в разделе, участником которого он не является."
+ insufficient_trust_level_error: "Происходит, когда пользователь пытается создать новую тему в разделе, недоступному ему по уровню доступа."
+ reply_user_not_matching_error: "Происходит, когда ответ приходит с адреса, отличного от того, на который было направлено уведомление."
+ topic_not_found_error: "Происходит, когда ответ пришёл, но связанная тема уже удалена."
+ topic_closed_error: "Происходит когда ответ пришёл, но связанная тема уже закрыта."
+ bounced_email_error: "Возврат недоставленного сообщения электронной почты."
+ screened_email_error: "Происходит, когда адрес отправителя электронной почты уже верифицирован (проверен)."
+ unsubscribe_not_allowed: "Происходит, когда отписка по электронной почте не разрешена для этого пользователя."
+ email_not_allowed: "Происходит, когда адрес электронной почты отсутствует в белом или чёрном списке."
+ unrecognized_error: "Неопознанная ошибка"
+ secure_uploads_placeholder: "На сайте включено ограничение доступа к загружаемому контенту. Для просмотра вложений откройте эту тему на сайте или нажмите на 'Просмотр вложений'."
+ view_redacted_media: "Просмотр вложений"
+ errors: &errors
+ format: ! "%{attribute} %{message}"
+ format_with_full_message: "%{attribute}: %{message}"
+ messages:
+ too_long_validation: "не может быть длиннее %{max} символов, а введено %{length}."
+ invalid_boolean: "Неправильное булевое значение."
+ taken: "уже занято"
+ accepted: должно быть принято
+ blank: не может быть пустым
+ present: должно быть пустым
+ confirmation: ! "не совпадает с %{attribute}"
+ empty: не может быть пустым
+ equal_to: должно быть равно %{count}
+ even: должно быть чётным
+ exclusion: зарезервировано
+ greater_than: должно быть больше чем %{count}
+ greater_than_or_equal_to: должно быть больше или равно %{count}
+ has_already_been_used: "уже было использовано"
+ inclusion: не включено в список
+ invalid: неверное
+ is_invalid: "не совсем ясен, это предложение закончено?"
+ is_invalid_meaningful: "выглядит странно, большинство слов содержат одни и те же повторяющиеся буквы."
+ is_invalid_unpretentious: "выглядит странно, одно или несколько слов очень длинные."
+ is_invalid_quiet: "выглядит странно, вы действительно хотели ввести весь текст ЗАГЛАВНЫМИ БУКВАМИ?"
+ invalid_timezone: "'%{tz}' не является допустимым часовым поясом"
+ contains_censored_words: "содержит следующие запрещённые слова %{censored_words}"
+ less_than: должен быть меньше %{count}
+ less_than_or_equal_to: должен быть меньше или равен %{count}
+ not_a_number: не число
+ not_an_integer: должно быть целым
+ odd: должно быть нечётным
+ record_invalid: ! "Ошибка валидации: %{errors}"
+ max_emojis: "не может содержать более %{max_emojis_count} эмодзи"
+ emojis_disabled: "не может содержать эмодзи"
+ ip_address_already_screened: "уже включён в существующее правило"
+ restrict_dependent_destroy:
+ one: "Невозможно удалить запись, поскольку существует зависимая запись: %{record}"
+ few: "Невозможно удалить запись, поскольку существуют зависимые записи: %{record}"
+ many: "Невозможно удалить запись, поскольку существуют зависимые записи: %{record}"
+ other: "Невозможно удалить запись, поскольку существуют зависимые записи: %{record}"
+ too_long:
+ one: слишком длинное (максимум %{count} символ)
+ few: слишком длинное (максимум %{count} символа)
+ many: слишком длинное (максимум %{count} символов)
+ other: слишком длинное (максимум %{count} символов)
+ too_short:
+ one: слишком короткое (минимум %{count} символ)
+ few: слишком короткое (минимум %{count} символа)
+ many: слишком короткое (минимум %{count} символов)
+ other: слишком короткое (минимум %{count} символов)
+ wrong_length:
+ one: неправильная длина (должен быть %{count} символ)
+ few: неправильная длина (должно быть %{count} символа)
+ many: неправильная длина (должно быть %{count} символов)
+ other: неправильная длина (должно быть %{count} символов)
+ other_than: "не должно быть равно %{count}"
+ auth_overrides_username: "Имя пользователя должно быть обновлено на стороне поставщика аутентификации, так как включена настройка `sso_overrides_username`."
+ template:
+ body: ! "Обнаружены ошибки в следующих полях:"
+ header:
+ one: "%{count} ошибка препятствует сохранению %{model}"
+ few: ! "%{count} ошибки препятствуют сохранению %{model}"
+ many: ! "%{count} ошибок препятствуют сохранению %{model}"
+ other: ! "%{count} ошибок препятствуют сохранению %{model}"
+ embed:
+ load_from_remote: "Произошла ошибка при загрузке сообщения."
+ site_settings:
+ invalid_category_id: "Вы указали несуществующий раздел"
+ invalid_choice:
+ one: "Вы указали неверный выбор %{name}"
+ few: "Вы указали неверные варианты %{name}"
+ many: "Вы указали неверные варианты %{name}"
+ other: "Вы указали неверные варианты %{name}"
+ default_categories_already_selected: "Нельзя выбрать раздел, используемый в другом списке."
+ default_tags_already_selected: "Вы не можете выбрать тег, используемый в другом списке."
+ s3_upload_bucket_is_required: "Нельзя включить загрузки на S3, пока не задана настройка 's3_upload_bucket'."
+ enable_s3_uploads_is_required: "Вы не можете включить инвентаризацию для S3, если вы не включили загрузки S3."
+ page_publishing_requirements: "Публикация страниц не может быть включена при включённом ограничении доступа к мультимедийному контенту."
+ s3_backup_requires_s3_settings: "Нельзя включить резервные копии на S3, пока не задана настройка '%{setting_name}'"
+ s3_bucket_reused: "Нельзя использовать одну и ту же корзину для 's3_upload_bucket' и 's3_backup_bucket'. Выберите другую корзину или используйте различные пути для каждой корзины."
+ secure_uploads_requirements: "Перед тем как ограничить доступ к загружаемому контенту, необходимо включить загрузку в S3"
+ share_quote_facebook_requirements: "Вы должны установить идентификатор приложения, чтобы включить обмен цитатами для Фейсбука."
+ second_factor_cannot_enforce_with_socials: "Вы не можете применить двухфакторную аутентификацию при включённом входе в социальные сети. Сначала вы должны отключить вход через: %{auth_provider_names}"
+ second_factor_cannot_be_enforced_with_disabled_local_login: "Вы не можете применить двухфакторную аутентификацию, если локальный вход в систему отключён."
+ second_factor_cannot_be_enforced_with_discourse_connect_enabled: "Вы не можете применить двухфакторную аутентификацию, если используется технология единого входа DiscourseConnect."
+ local_login_cannot_be_disabled_if_second_factor_enforced: "Вы не можете отключить локальный вход в систему, если применяется двухфакторная аутентификация. Отключите её перед отключением возможности локального входа."
+ cannot_enable_s3_uploads_when_s3_enabled_globally: "Вы не можете включить загрузку S3, потому что загрузка S3 уже включена глобально, и включение этого уровня сайта может вызвать критические проблемы с загрузками"
+ cors_origins_should_not_have_trailing_slash: "Не следует добавлять завершающую косую черту (/) к источникам кросс-доменных запросов (CORS)."
+ slow_down_crawler_user_agent_must_be_at_least_3_characters: "Значение «User agent» должно содержать не менее 3 символов для корректного ограничения скорости сканирования."
+ slow_down_crawler_user_agent_cannot_be_popular_browsers: "Не допускается использование следующих значений: %{values}."
+ strip_image_metadata_cannot_be_disabled_if_composer_media_optimization_image_enabled: "В не можете отключить удаление метаданных из изображения, пока включена оптимизация загружаемых файлов изображений на стороне клиента. Отключите параметр 'composer media optimization image enabled' перед отключением параметра 'strip image metadata'."
+ twitter_summary_large_image_no_svg: "Изображения сводной карточки Twitter, используемые для метаданных twitter:image, не могут быть в формате 'svg'."
+ conflicting_google_user_id: 'Идентификатор учётной записи Google для этой учётной записи изменился; по соображениям безопасности требуется вмешательство персонала. Пожалуйста, свяжитесь с персоналом и укажите
https://meta.discourse.org/t/76575'
+ onebox:
+ invalid_address: "К сожалению, нам не удалось создать предварительный просмотр этой веб-страницы, потому что сервер \"%{hostname}\" не может быть найден. Вместо предварительного просмотра в вашем сообщении будет отображаться только ссылка. :cry:"
+ error_response: "К сожалению, нам не удалось создать предварительный просмотр этой веб-страницы, потому что веб-сервер вернул код ошибки %{status_code}. Вместо предварительного просмотра в вашем сообщении будет отображаться только ссылка. :cry:"
+ missing_data:
+ one: "К сожалению, нам не удалось создать предварительный просмотр этой веб-страницы, поскольку не удалось найти следующий тег oEmbed / OpenGraph: %{missing_attributes}"
+ few: "К сожалению, нам не удалось создать предварительный просмотр этой веб-страницы, поскольку не удалось найти следующие теги oEmbed / OpenGraph: %{missing_attributes}"
+ many: "К сожалению, нам не удалось создать предварительный просмотр этой веб-страницы, поскольку не удалось найти следующие теги oEmbed / OpenGraph: %{missing_attributes}"
+ other: "К сожалению, нам не удалось создать предварительный просмотр этой веб-страницы, поскольку не удалось найти следующие теги oEmbed / OpenGraph: %{missing_attributes}"
+ word_connector:
+ comma: ", "
+ invite:
+ expired: "Срок действия токена приглашения истек. Пожалуйста, свяжитесь с персоналом."
+ not_found: "Неверный код приглашения. Пожалуйста, свяжитесь с персоналом."
+ not_found_json: "Неверный код приглашения. Пожалуйста, свяжитесь с персоналом."
+ not_matching_email: "Ваш адрес электронной почты и адрес электронной почты, связанный с токеном приглашения, не совпадают. Пожалуйста, свяжитесь с сотрудником."
+ not_found_template: |
+
Ваше приглашение на сайт %{site_name} уже использовано.
+ +Если вы помните ваш пароль, вы можете Войти в учётную запись.
+ +В противном случае восстановите пароль.
+ not_found_template_link: | +Это приглашение на сайт %{site_name} более недействительно. Попросите пригласившего вас прислать вам новое приглашение.
+ user_exists: "Нет необходимости приглашать пользователя с адресом %{email}, у него уже есть аккаунт!" + invite_exists: "Вы уже пригласили пользователя с адресом %{email}" + invalid_email: "Адрес '%{email}' не является корректным адресом электронной почты." + rate_limit: + one: "Вы уже отправили %{count} приглашение за последний день. Подождите %{time_left} перед повторной попыткой." + few: "Вы уже отправили %{count} приглашения за последний день. Подождите %{time_left} перед повторной попыткой." + many: "Вы уже отправили %{count} приглашений за последний день. Подождите %{time_left} перед повторной попыткой." + other: "Вы уже отправили %{count} приглашений за последний день. Подождите %{time_left} прежде чем повторить попытку." + confirm_email: "Вы почти закончили! Мы отправили письмо активации на ваш адрес электронной почты. Пожалуйста, следуйте инструкциям в письме для активации учётной записи.
Если письмо не приходит, проверьте папку со спамом.
" + cant_invite_to_group: "Вы не можете приглашать пользователей в указанные группы. Убедитесь, что вы являетесь владельцем группы (групп), в которую вы пытаетесь пригласить." + disabled_errors: + discourse_connect_enabled: "Возможность использования приглашений отключена, поскольку включена технология единого входа DiscourseConnect." + invalid_access: "У вас нет прав для просмотра запрашиваемого ресурса." + requires_groups: "Приглашение не сохранено, потому что указанная тема недоступна. Добавьте одну из следующих групп: %{groups}." + domain_not_allowed: "Ваш адрес электронной почты не может быть использован для активации этого приглашения." + max_redemptions_allowed_one: "Для приглашений по электронной почте значение должно быть равно '1'." + redemption_count_less_than_max: "Значение должно быть меньше %{max_redemptions_allowed}" + email_xor_domain: "Одновременное указание домена и адреса электронной почты не допускается" bulk_invite: file_should_be_csv: "Загружаемый файл должен быть в csv-формате." max_rows: "Первые %{max_bulk_invites} приглашения были отправлены. Попробуйте разделить файл на более мелкие части." @@ -386,6 +647,7 @@ ru: attributes: linkable_type: invalid: "Недопустимый тип" + <<: *errors uncategorized_category_name: "РАЗНОЕ" general_category_name: "Основной" general_category_description: "Создавайте здесь темы, не соответствующие ни одному другому разделу." @@ -4550,3 +4812,6 @@ ru: user_status: errors: ends_at_should_be_greater_than_set_at: "Значение переменной end_at должно быть больше, чем set_at" + activemodel: + errors: + <<: *errors diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index 66512b62a8..05bf6a7acc 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -5,7 +5,250 @@ # https://translate.discourse.org/ sv: + dates: + short_date_no_year: "D MMM" + short_date: "D MMM, YYYY" + long_date: "D MMMM YYYY LT" + datetime_formats: &datetime_formats + formats: + short: "%d-%m-%Y" + short_no_year: "%-d %B" + date_only: "%-d %B %Y" + long: "%-d %B %Y %H:%M" + no_day: "%B %Y" + calendar_ics: "%Y%m%dT%H%M%SZ" + date: + month_names: + - null + - Januari + - Februari + - Mars + - April + - Maj + - Juni + - Juli + - Augusti + - September + - Oktober + - November + - December + <<: *datetime_formats + time: + am: "förmiddag" + pm: "eftermiddag" + <<: *datetime_formats + title: "Discourse" + topics: "Ämnen" + posts: "inlägg" + views: "visningar" + loading: "Laddar" + powered_by_html: 'Drivs av Discourse, visas bäst med JavaScript aktiverat' + sign_up: "Registrera dig" + log_in: "Logga in" + submit: "Skicka" + purge_reason: "Automatiskt borttaget som övergivet, inaktiverat konto" + disable_remote_images_download_reason: "Nedladdning av externa bilder har inaktiverats eftersom det inte fanns tillräckligt mycket lagringsutrymme tillgängligt." + anonymous: "Anonym" + remove_posts_deleted_by_author: "Borttaget av författaren" + redirect_warning: "Vi kunde inte verifiera att länken som du valt faktiskt har lagts upp i forumet. Välj länken nedan om du vill fortsätta ändå." + on_another_topic: "I ett annat ämne" + inline_oneboxer: + topic_page_title_post_number: "Nr %{post_number}" + topic_page_title_post_number_by_user: "Nr %{post_number} av %{username}" + themes: + bad_color_scheme: "Kan inte uppdatera tema, ogiltig färgpalett" + other_error: "Något blev fel när temat uppdaterades" + ember_selector_error: "Tyvärr — det är inte tillåtet att använda CSS-selektorerna #ember eller .ember-view, eftersom dessa namn genereras dynamiskt under körning och kommer att ändras över tid, vilket resulterar i trasig CSS. Prova en annan selektor." + import_error: + generic: Det uppstod ett fel vid import av det temat + upload: "Det uppstod ett fel under skapandet av uppladdningstillgång: %{name}. %{errors}" + about_json: "Importfel: about.json finns inte eller är ogiltig. Är du säker på att detta är ett Discourse-tema?" + about_json_values: "about.json innehåller ogiltiga värden: %{errors}" + modifier_values: "about.json-modifierare innehåller ogiltiga värden: %{errors}" + git: "Fel vid kloning av git-lagringsplats, åtkomst nekas eller lagringsplatsen finns inte" + git_ref_not_found: "Det gick inte att ta ut git-referens: %{ref}" + git_unsupported_scheme: "Det går inte att klona git repo: schemat stöds inte" + unpack_failed: "Det gick inte att packa upp filen" + file_too_big: "Den okomprimerade filen är för stor." + unknown_file_type: "Filen som du laddade upp verkar inte vara ett giltigt Discourse-tema." + not_allowed_theme: "`%{repo}` finns inte i listan över tillåtna teman (bocka för den globala inställningen `allowed_theme_repos`)." + ssh_key_gone: "Du väntade för länge med att installera temat så SSH-nyckeln har löpt ut. Vi ber dig försöka igen." + errors: + component_no_user_selectable: "Temakomponenter kan inte väljas av användaren" + component_no_default: "Temakomponenter kan inte vara standardtema" + component_no_color_scheme: "Temakomponenter kan inte ha färgpaletter" + no_multilevels_components: "Teman med underordnade teman kan inte själva vara underordnade teman" + optimized_link: Optimerade bildlänkar är flyktiga och bör inte inkluderas i temakällkoden. + settings_errors: + invalid_yaml: "Angivet YAML är ogiltigt." + data_type_not_a_number: "Det går inte att ställa in typen `%{name}`. Typer som stöds är `integer`, `bool`, `list` , `enum` och `upload`" + name_too_long: "En inställning har ett namn som är för långt. Maximal längd är 255" + default_value_missing: "Inställningen `%{name}` har inget standardvärde" + default_not_match_type: "Standardvärdestypen i inställningen `%{name}` överensstämmer inte med inställningstypen." + default_out_range: "Standardvärdet för inställningen `%{name}` ligger inte inom angivet intervall." + enum_value_not_valid: "Valt värde är inte ett av enum-valen." + number_value_not_valid: "Nytt värde ligger inte inom tillåtet intervall." + number_value_not_valid_min_max: "Det måste vara mellan %{min} och %{max}." + number_value_not_valid_min: "Det måste vara större än eller lika med %{min}." + number_value_not_valid_max: "Det måste vara mindre än eller lika med %{max}." + string_value_not_valid: "Den nya värdelängden ligger inte inom tillåtet intervall." + string_value_not_valid_min_max: "Den måste vara mellan %{min} och %{max} tecken lång." + string_value_not_valid_min: "Den måste vara minst %{min} tecken lång." + string_value_not_valid_max: "Den måste vara högst %{max} tecken lång." + locale_errors: + top_level_locale: "Den övre nivån i en språkfil måste matcha språkets namn" + invalid_yaml: "Översättning av YAML är ogiltig" + emails: + incoming: + default_subject: "Detta ämne behöver en titel" + show_trimmed_content: "Visa trimmat innehåll" + maximum_staged_user_per_email_reached: "Uppnått maximalt antal arrangerade användare skapade per e-post." + no_subject: "(inget ämne)" + no_body: "(ingen meddelandetext)" + missing_attachment: "(Bilagan %{filename} saknas)" + continuing_old_discussion: + one: "Fortsätter diskussionen från [%{title}](%{url}), eftersom den skapades för mer än %{count} dag sedan." + other: "Fortsätter diskussionen från [%{title}](%{url}), eftersom den skapades för mer än %{count} dagar sedan." + errors: + empty_email_error: "Händer när rådata vi mottagit är tom." + no_message_id_error: "Händer när e-post saknar 'Message-Id'-rubrik." + auto_generated_email_error: "Händer när 'precedence'-rubriken är satt till: lista, skräp, mass eller auto-svar (list, junk, bulk eller auto_reply), eller när någon annan rubrik innehåller: auto-skickad, auto-svarad eller auto-genererad (auto-submitted, auto-replied eller auto-generated)." + no_body_detected_error: "Händer när vi inte kunde extrahera någon huvuddel och det inte fanns några bilagor." + no_sender_detected_error: "Det händer när vi inte kunde hitta en giltig e-postadress i Från-rubriken." + from_reply_by_address_error: "Det händer när Från-rubriken matchar Svara via e-post-adressen." + inactive_user_error: "Händer när sändaren inte är aktiv." + silenced_user_error: "Det händer när avsändaren har tystats." + bad_destination_address: "Det händer när ingen av e-postadresserna i Till/Kopia-fälten matchade en konfigurerad inkommande e-postadress." + strangers_not_allowed_error: "Händer när en användare försöker skapa ett nytt ämne i en kategori som användare inte är en medlem av." + insufficient_trust_level_error: "Händer när en användare försöker skapa ett nytt ämne i en kategori som användare saknar förtroendenivå för." + reply_user_not_matching_error: "Händer när ett svar kommer in från en annan e-postadress än den aviseringen skickades till." + topic_not_found_error: "Händer när ett svar kommer in, men ämnet har raderats." + topic_closed_error: "Händer när ett svar kommer in, men ämnet har stängts." + bounced_email_error: "E-post är en rapport om studsad e-post." + screened_email_error: "Händer när sändarens e-postadress redan har kontrollerats." + unsubscribe_not_allowed: "Det händer när avprenumeration via e-post inte tillåts för den här användaren." + email_not_allowed: "Händer när e-postadressen inte står på vitlistan eller om den står på svartlistan." + unrecognized_error: "Okänt fel" + secure_uploads_placeholder: "Redigerad: Denna webbplats har säkerhet för uppladdningar aktiverad, gå till ämnet eller klicka på \"Visa media\" för att se bifogade uppladdningar." + view_redacted_media: "Visa media" + errors: &errors + format: ! "%{attribute} %{message}" + format_with_full_message: "%{attribute}: %{message}" + messages: + too_long_validation: "är begränsad till %{max} tecken; du skrev in %{length}. " + invalid_boolean: "Ogiltig booleansk." + taken: "används redan" + accepted: måste godkännas + blank: kan inte vara tomt + present: måste vara tomt + confirmation: ! "matchar inte %{attribute}" + empty: kan inte vara tomt + equal_to: måste vara lika med %{count} + even: måste vara jämnt + exclusion: är reserverad + greater_than: måste vara större än %{count} + greater_than_or_equal_to: måste vara större eller lika med %{count} + has_already_been_used: "har redan använts" + inclusion: ingår inte i listan + invalid: är ogiltig + is_invalid: "Verkar oklart, är det en fullständig mening?" + is_invalid_meaningful: "verkar oklart, de flesta orden innehåller samma bokstäver om och om igen?" + is_invalid_unpretentious: "verkar oklart, ett eller flera ord är mycket långa?" + is_invalid_quiet: "verkar oklart, var det din avsikt att skriva med STORA BOKSTÄVER?" + invalid_timezone: "'%{tz}' är inte en giltig tidszon" + contains_censored_words: "innehåller följande censurerade ord: %{censored_words}" + less_than: måste vara mindre än %{count} + less_than_or_equal_to: måste vara mindre eller lika med %{count} + not_a_number: är inte ett nummer + not_an_integer: måste vara ett tal + odd: måste vara udda + record_invalid: ! "Validering misslyckades: %{errors}" + max_emojis: "kan inte ha mer än %{max_emojis_count} emojier" + emojis_disabled: "kan inte ha emoji(er)" + ip_address_already_screened: "ingår redan i en befintlig regel" + restrict_dependent_destroy: + one: "Kan inte radera post på grund av att det finns en beroende %{record}" + other: "Kan inte radera post på grund av att det finns beroende %{record}" + too_long: + one: är för långt (högsta tillåtna är %{count} tecken) + other: är för långt (högsta tillåtna är %{count} tecken) + too_short: + one: är för kort (minsta tillåtna är %{count} tecken) + other: är för kort (minsta tillåtna är %{count} tecken) + wrong_length: + one: har fel längd (borde vara %{count} tecken) + other: har fel längd (borde vara %{count} tecken) + other_than: "måste vara skilt från %{count}" + auth_overrides_username: "Användarnamnet måste uppdateras hos autentiseringsleverantören, eftersom inställningen `auth_overrides_username` är aktiverad." + template: + body: ! "Det uppstod problem med följande fält:" + header: + one: "%{count} fel hindrade %{model} från att sparas" + other: ! "%{count} fel hindrade %{model} från att sparas" + embed: + load_from_remote: "Det uppstod ett fel vid laddning av inlägget." + site_settings: + invalid_category_id: "Du angav en kategori som inte finns" + invalid_choice: + one: "Du angav det ogiltiga valet %{name}" + other: "Du angav de ogiltiga valen %{name}" + default_categories_already_selected: "Du kan inte välja en kategori som används i en annan lista." + default_tags_already_selected: "Du kan inte välja en etikett som används i en annan lista." + s3_upload_bucket_is_required: "Du kan inte aktivera uppladdning till S3 innan du har angivit 's3_upload_bucket'." + enable_s3_uploads_is_required: "Du kan inte aktivera lagret till S3 innan du har aktiverat s3-uppladdningarna." + page_publishing_requirements: "Sidpublicering kan inte aktiveras om säkra medier är aktiverade." + s3_backup_requires_s3_settings: "Du kan inte använda S3 som säkerhetskopieringsplats innan du har angivit '%{setting_name}'." + s3_bucket_reused: "Du kan inte använda samma behållare för 's3_upload_bucket' och 's3_backup_bucket'. Välj en annan behållare eller använd en annan sökväg för varje behållare." + secure_uploads_requirements: "S3-uppladdningar måste ha aktiverats innan du aktiverar säkra uppladdningar." + share_quote_facebook_requirements: "Du måste ange ett Facebook-app-id för att möjliggöra delning av citat på Facebook." + second_factor_cannot_enforce_with_socials: "Du kan inte tvinga 2FA med sociala inloggningar aktiverade. Du måste först inaktivera inloggning via: %{auth_provider_names}" + second_factor_cannot_be_enforced_with_disabled_local_login: "Du kan inte genomdriva 2FA om lokala inloggningar är inaktiverade." + second_factor_cannot_be_enforced_with_discourse_connect_enabled: "Du kan inte genomdriva 2FA om DiscourseConnect har aktiverats." + local_login_cannot_be_disabled_if_second_factor_enforced: "Du kan inte inaktivera lokal inloggning om 2FA har genomdrivits. Inaktivera genomdriven 2FA innan du inaktiverar lokala inloggningar." + cannot_enable_s3_uploads_when_s3_enabled_globally: "Du kan inte aktivera S3-uppladdningar eftersom S3-uppladdningar redan är aktiverade globalt, och aktivering av denna webbplatsnivå kan orsaka kritiska problem med uppladdningar" + cors_origins_should_not_have_trailing_slash: "Du bör inte lägga till det avslutande snedstrecket (/) till CORS-ursprung." + slow_down_crawler_user_agent_must_be_at_least_3_characters: "Användaragenter måste vara minst 3 tecken långt för att undvika felaktiga hastighetsbegränsning för mänskliga användare." + slow_down_crawler_user_agent_cannot_be_popular_browsers: "Du kan inte lägga till något av följande värden till inställningen: %{values}." + strip_image_metadata_cannot_be_disabled_if_composer_media_optimization_image_enabled: "Du kan inte inaktivera Radera metadata för bild om 'composer media optimization image enabled' är aktiverat. Inaktivera 'composer media optimization image enabled' innan du inaktiverar Radera metadata för bild." + twitter_summary_large_image_no_svg: "Twitter, sammanfattningsbilder som används för twitter: bildens metadata kan inte vara .svg" + conflicting_google_user_id: 'Google-konto-ID:et för det här kontot har ändrats och av säkerhetsskäl krävs personalens ingripande. Kontakta personalen och hänvisa dem tillDin inbjudan till %{site_name} har redan lösts in.
+ +Om du kommer ihåg ditt lösenord kan du logga in.
+ +Annars ber vi dig att återställa lösenordet.
+ not_found_template_link: | +Denna inbjudan till %{site_name} kan inte längre lösas in. Be personen som bjöd in dig att skicka en ny inbjudan till dig.
+ user_exists: "Det finns ingen anledning att bjuda in %{email}, de har redan ett konto!" + invite_exists: "Du har redan bjudit in %{email}." + invalid_email: "%{email} är inte en giltig e-postadress." + rate_limit: + one: "Du har redan skickat %{count} inbjudan den senaste dagen, vänta %{time_left} innan du försöker igen." + other: "Du har redan skickat %{count} inbjudningar den senaste dagen, vänta %{time_left} innan du försöker igen." + confirm_email: "Du är nästan klar! Vi skickade ett aktiveringsmeddelande till din e-postadress. Följ instruktionerna i e-postmeddelandet för att aktivera ditt konto.
Om du inte ser det kan du kolla i din skräppostmapp.
" + cant_invite_to_group: "Du är inte behörig att bjuda in användare till specificerade grupper. Kontrollera att du är ägare för de grupper som du försöker bjuda in till." + disabled_errors: + discourse_connect_enabled: "Inbjudningar är inaktiverade eftersom DiscourseConnect har aktiverats." + invalid_access: "Du har inte behörighet att visa den efterfrågade resursen." + requires_groups: "Inbjudan sparades inte eftersom det angivna ämnet är otillgängligt. Lägg till en av följande grupper: %{groups}." + domain_not_allowed: "Din e-post kan inte användas för att lösa in denna inbjudan." + max_redemptions_allowed_one: "för e-postinbjudningar ska vara 1." + redemption_count_less_than_max: "bör vara mindre än %{max_redemptions_allowed}." + email_xor_domain: "E-post- och domänfält är inte tillåtna samtidigt" existing_user_success: "Inbjudan har lösts in" bulk_invite: file_should_be_csv: "De uppladdade filerna skall vara i csv-format." @@ -356,6 +599,7 @@ sv: attributes: linkable_type: invalid: "är inte giltig" + <<: *errors uncategorized_category_name: "Ej kategoriserad" general_category_name: "Allmänt" general_category_description: "Skapa ämnen här som inte passar in i någon annan befintlig kategori." @@ -1807,6 +2051,7 @@ sv: disable_mailing_list_mode: "Tillåt inte att användare aktiverar utskicksläget för e-postlistor (förhindrar att meddelanden till e-postlistan skickas.)" default_email_previous_replies: "Som standard omfattas tidigare svar i e-postmeddelanden." default_email_in_reply_to: "Omfatta ett utdrag av svar på inlägg i e-post som standard." + default_hide_profile_and_presence: "Dölj användarens offentliga profil och närvarofunktioner som standard." default_other_new_topic_duration_minutes: "Generellt standardvillkor för när ett ämne anses nytt." default_other_auto_track_topics_after_msecs: "Generell standardtid innan ett ämne följs automatiskt." default_other_notification_level_when_replying: "Generell standardiserad bevakningssnivå när en användare svarar i ett ämne." @@ -4138,3 +4383,6 @@ sv: payload_url: blocked_or_internal: "Försändelse-URL:en kan inte användas eftersom den leder till en blockerad eller intern IP-adress." unsafe: "Försändelse-URL:en kan inte användas eftersom den är osäker" + activemodel: + errors: + <<: *errors diff --git a/config/nginx.sample.conf b/config/nginx.sample.conf index 99f05f075f..fdb264d872 100644 --- a/config/nginx.sample.conf +++ b/config/nginx.sample.conf @@ -16,8 +16,12 @@ proxy_cache_path /var/nginx/cache inactive=1440m levels=1:2 keys_zone=one:10m ma # Increased from the default value to acommodate large cookies during oAuth2 flows # like in https://meta.discourse.org/t/x/74060 and large CSP and Link (preload) headers -proxy_buffer_size 16k; -proxy_buffers 4 16k; +proxy_buffer_size 32k; +proxy_buffers 4 32k; + +# Increased from the default value to allow for a large volume of cookies in request headers +# Discourse itself tries to minimise cookie size, but we cannot control other cookies set by other tools on the same domain. +large_client_header_buffers 4 32k; # If you are going to use Puma, use these: # diff --git a/config/routes.rb b/config/routes.rb index 27d7f27b6e..22e7389507 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -838,8 +838,8 @@ Discourse::Application.routes.draw do get 'embed/count' => 'embed#count' get 'embed/info' => 'embed#info' - get "new-topic" => "list#latest" - get "new-message" => "list#latest" + get "new-topic" => "new_topic#index" + get "new-message" => "new_topic#index" # Topic routes get "t/id_for/:slug" => "topics#id_for_slug" diff --git a/config/site_settings.yml b/config/site_settings.yml index 2145dbddd8..bd52c57722 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -379,7 +379,7 @@ basic: enable_user_status: client: true default: false - enable_onboarding_popups: + enable_user_tips: client: true default: false @@ -2136,6 +2136,7 @@ backups: include_s3_uploads_in_backups: default: false hidden: true + client: true search: use_pg_headlines_for_excerpt: @@ -2338,6 +2339,11 @@ uncategorized: uncategorized_category_id: default: -1 hidden: true + default_composer_category: + client: true + type: category + default: "" + validator: "DefaultComposerCategoryValidator" notify_about_flags_after: type: float @@ -2532,6 +2538,8 @@ user_preferences: default: 2 default_email_in_reply_to: default: false + default_hide_profile_and_presence: + default: false default_other_new_topic_duration_minutes: enum: "NewTopicDurationSiteSetting" diff --git a/db/migrate/20221101140632_rename_onboarding_popups_site_setting.rb b/db/migrate/20221101140632_rename_onboarding_popups_site_setting.rb new file mode 100644 index 0000000000..da6fa2e5fc --- /dev/null +++ b/db/migrate/20221101140632_rename_onboarding_popups_site_setting.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class RenameOnboardingPopupsSiteSetting < ActiveRecord::Migration[7.0] + def up + execute "UPDATE site_settings SET name = 'enable_user_tips' WHERE name = 'enable_onboarding_popups'" + end + + def down + execute "UPDATE site_settings SET name = 'enable_onboarding_popups' WHERE name = 'enable_user_tips'" + end +end diff --git a/db/migrate/20221101181505_hide_all_user_tips_for_existent_users.rb b/db/migrate/20221101181505_hide_all_user_tips_for_existent_users.rb new file mode 100644 index 0000000000..4336add8e9 --- /dev/null +++ b/db/migrate/20221101181505_hide_all_user_tips_for_existent_users.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class HideAllUserTipsForExistentUsers < ActiveRecord::Migration[7.0] + def up + execute "UPDATE user_options SET seen_popups = '{1, 2}'" + end + + def down + execute "UPDATE user_options SET seen_popups = '{}'" + end +end diff --git a/db/migrate/20221103051248_remove_invalid_topic_allowed_users_from_invites.rb b/db/migrate/20221103051248_remove_invalid_topic_allowed_users_from_invites.rb new file mode 100644 index 0000000000..d161a9f16d --- /dev/null +++ b/db/migrate/20221103051248_remove_invalid_topic_allowed_users_from_invites.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +class RemoveInvalidTopicAllowedUsersFromInvites < ActiveRecord::Migration[7.0] + def up + # We are getting all the topic_allowed_users records that + # match an invited user, which is created as part of the invite + # redemption flow. The original invite would _not_ have had a topic_invite + # record, and the user should have been added to the topic in the brief + # period between creation of the invited_users record and the update of + # that record. + # + # Having > 2 topic allowed users disqualifies messages sent only + # by the system or an admin to the user. + subquery_sql = <<~SQL + SELECT DISTINCT id + FROM ( + SELECT tau.id, tau.user_id, COUNT(*) OVER (PARTITION BY tau.user_id) + FROM topic_allowed_users tau + JOIN invited_users iu ON iu.user_id = tau.user_id + LEFT JOIN topic_invites ti ON ti.invite_id = iu.invite_id AND tau.topic_id = ti.topic_id + WHERE ti.id IS NULL + AND tau.created_at BETWEEN iu.created_at AND iu.updated_at + AND iu.redeemed_at > '2022-10-27' + ) AS matching_topic_allowed_users + WHERE matching_topic_allowed_users.count > 2 + SQL + + # Back up the records we are going to change in case we are too + # brutal, and for further inspection. + # + # TODO DROP this table (topic_allowed_users_backup_nov_2022) in a later migration. + DB.exec(<<~SQL) + CREATE TABLE topic_allowed_users_backup_nov_2022 + ( + id INT NOT NULL, + user_id INT NOT NULL, + topic_id INT NOT NULL + ); + INSERT INTO topic_allowed_users_backup_nov_2022(id, user_id, topic_id) + SELECT id, user_id, topic_id + FROM topic_allowed_users + WHERE id IN ( + #{subquery_sql} + ) + SQL + + # Delete the invalid topic allowed users that should not be there. + DB.query(<<~SQL) + DELETE + FROM topic_allowed_users + WHERE id IN ( + #{subquery_sql} + ) + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20221110175456_populate_default_composer_category.rb b/db/migrate/20221110175456_populate_default_composer_category.rb new file mode 100644 index 0000000000..812e9ba0ae --- /dev/null +++ b/db/migrate/20221110175456_populate_default_composer_category.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +## If the general_category_id has a value, set the *new* +# default_composer_category site setting to match + +class PopulateDefaultComposerCategory < ActiveRecord::Migration[7.0] + def up + general_category_id = DB.query_single("SELECT value FROM site_settings WHERE name = 'general_category_id'") + return if general_category_id.blank? || general_category_id[0].to_i < 0 + default_composer_category = DB.query_single("SELECT value FROM site_settings where name = 'default_composer_category'") + return if !default_composer_category.blank? + DB.exec( + "INSERT INTO site_settings(name, value, data_type, created_at, updated_at) + VALUES('default_composer_category', :setting, '16', NOW(), NOW())", + setting: general_category_id[0].to_i + ) + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20221114215902_hide_user_tips_3_to_5_for_existing_users.rb b/db/migrate/20221114215902_hide_user_tips_3_to_5_for_existing_users.rb new file mode 100644 index 0000000000..b4aa179946 --- /dev/null +++ b/db/migrate/20221114215902_hide_user_tips_3_to_5_for_existing_users.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class HideUserTips3To5ForExistingUsers < ActiveRecord::Migration[7.0] + def up + execute "UPDATE user_options SET seen_popups = seen_popups || '{3, 4, 5}'" + end + + def down + execute "UPDATE user_options SET seen_popups = array_remove(array_remove(array_remove(seen_popups, 3), 4), 5)" + end +end diff --git a/db/post_migrate/20221108032233_drop_old_bookmark_columns_v2.rb b/db/post_migrate/20221108032233_drop_old_bookmark_columns_v2.rb new file mode 100644 index 0000000000..a0ad3fd3d1 --- /dev/null +++ b/db/post_migrate/20221108032233_drop_old_bookmark_columns_v2.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'migration/column_dropper' + +class DropOldBookmarkColumnsV2 < ActiveRecord::Migration[7.0] + DROPPED_COLUMNS ||= { + bookmarks: %i{ + post_id + for_topic + } + } + + def up + DROPPED_COLUMNS.each do |table, columns| + Migration::ColumnDropper.execute_drop(table, columns) + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/lefthook.yml b/lefthook.yml index 1c15df9e7f..0547a4063f 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -4,6 +4,9 @@ skip_output: pre-commit: parallel: true + skip: + - merge + - rebase commands: rubocop: glob: "*.rb" diff --git a/lib/guardian.rb b/lib/guardian.rb index 0dbdd1dec3..83387fc7ba 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -359,8 +359,8 @@ class Guardian flair_icon.present? || flair_upload_id.present? end - def can_change_primary_group?(user) - user && is_staff? + def can_change_primary_group?(user, group) + user && can_edit_group?(group) end def can_change_trust_level?(user) @@ -445,23 +445,42 @@ class Guardian can_send_private_message?(group) end - def can_send_private_message?(target, notify_moderators: false) - is_user = target.is_a?(User) - is_group = target.is_a?(Group) + ## + # This should be used as a general, but not definitive, check for whether + # the user can send private messages _generally_, which is mostly useful + # for changing the UI. + # + # Please otherwise use can_send_private_message?(target, notify_moderators) + # to check if a single target can be messaged. + def can_send_private_messages?(notify_moderators: false) from_system = @user.is_system_user? from_bot = @user.bot? - (is_group || is_user) && # User is authenticated authenticated? && + # User can send PMs, this can be covered by trust levels as well via AUTO_GROUPS + (is_staff? || from_bot || from_system || \ + (@user.in_any_groups?(SiteSetting.personal_message_enabled_groups_map)) || notify_moderators) + end + + ## + # This should be used as a final check for when a user is sending a message + # to a target user or group. + def can_send_private_message?(target, notify_moderators: false) + target_is_user = target.is_a?(User) + target_is_group = target.is_a?(Group) + from_system = @user.is_system_user? + + # Must be a valid target + (target_is_group || target_is_user) && + # User is authenticated and can send PMs, this can be covered by trust levels as well via AUTO_GROUPS + can_send_private_messages?(notify_moderators: notify_moderators) && # User disabled private message - (is_staff? || is_group || target.user_option.allow_private_messages) && - # User can send PMs, this can be covered by trust levels as well via AUTO_GROUPS - (is_staff? || from_bot || from_system || (@user.in_any_groups?(SiteSetting.personal_message_enabled_groups_map)) || notify_moderators) && + (is_staff? || target_is_group || target.user_option.allow_private_messages) && # Can't send PMs to suspended users - (is_staff? || is_group || !target.suspended?) && + (is_staff? || target_is_group || !target.suspended?) && # Check group messageable level - (from_system || is_user || Group.messageable(@user).where(id: target.id).exists? || notify_moderators) && + (from_system || target_is_user || Group.messageable(@user).where(id: target.id).exists? || notify_moderators) && # Silenced users can only send PM to staff (!is_silenced? || target.staff?) end diff --git a/lib/guardian/tag_guardian.rb b/lib/guardian/tag_guardian.rb index 8d445fca9c..5a4be92ab4 100644 --- a/lib/guardian/tag_guardian.rb +++ b/lib/guardian/tag_guardian.rb @@ -15,8 +15,7 @@ module TagGuardian return false if @user.blank? return true if @user == Discourse.system_user - # TODO (martin) Change to pm_tags_allowed_for_groups_map - group_ids = SiteSetting.pm_tags_allowed_for_groups.to_s.split("|").map(&:to_i) + group_ids = SiteSetting.pm_tags_allowed_for_groups_map group_ids.include?(Group::AUTO_GROUPS[:everyone]) || @user.group_users.exists?(group_id: group_ids) end diff --git a/lib/s3_helper.rb b/lib/s3_helper.rb index 859ca462ca..d1a08cef37 100644 --- a/lib/s3_helper.rb +++ b/lib/s3_helper.rb @@ -105,6 +105,15 @@ class S3Helper rescue Aws::S3::Errors::NoSuchKey, Aws::S3::Errors::NotFound end + def delete_objects(keys) + s3_bucket.delete_objects({ + delete: { + objects: keys.map { |k| { key: k } }, + quiet: true, + }, + }) + end + def copy(source, destination, options: {}) if options[:apply_metadata_to_destination] options = options.except(:apply_metadata_to_destination).merge(metadata_directive: "REPLACE") diff --git a/lib/seed_data/categories.rb b/lib/seed_data/categories.rb index acad6d9e57..0f7cef8425 100644 --- a/lib/seed_data/categories.rb +++ b/lib/seed_data/categories.rb @@ -87,7 +87,8 @@ module SeedData text_color: 'FFFFFF', permissions: { everyone: :full }, force_permissions: true, - sidebar: true + sidebar: true, + default_composer_category: true } ] @@ -99,7 +100,7 @@ module SeedData end def create_category(site_setting_name:, name:, description:, position:, color:, text_color:, - permissions:, force_permissions:, force_existence: false, sidebar: false) + permissions:, force_permissions:, force_existence: false, sidebar: false, default_composer_category: false) category_id = SiteSetting.get(site_setting_name) if should_create_category?(category_id, force_existence) @@ -123,6 +124,10 @@ module SeedData sidebar_categories << category.id SiteSetting.set('default_sidebar_categories', sidebar_categories.join('|')) end + + if default_composer_category + SiteSetting.set('default_composer_category', category.id) + end elsif category = Category.find_by(id: category_id) if description.present? && (category.topic_id.blank? || !Topic.exists?(category.topic_id)) category.description = description diff --git a/lib/shrink_uploaded_image.rb b/lib/shrink_uploaded_image.rb index 3123468ef1..f6ef2354d4 100644 --- a/lib/shrink_uploaded_image.rb +++ b/lib/shrink_uploaded_image.rb @@ -83,7 +83,7 @@ class ShrinkUploadedImage if post.raw_changed? log "Updating post" - elsif post.downloaded_images.has_value?(original_upload.id) + elsif post.post_hotlinked_media.exists?(upload_id: original_upload.id) log "A hotlinked, unreferenced image" elsif post.raw.include?(upload.short_url) log "Already processed" @@ -161,13 +161,10 @@ class ShrinkUploadedImage ) end - if existing_upload && post.downloaded_images.present? - downloaded_images = post.downloaded_images.transform_values do |upload_id| - upload_id == original_upload.id ? upload.id : upload_id - end - - post.custom_fields[Post::DOWNLOADED_IMAGES] = downloaded_images - post.save_custom_fields + if existing_upload + post.post_hotlinked_media + .where(upload_id: original_upload.id) + .update_all(upload_id: upload.id) end post.rebake! diff --git a/lib/stylesheet/manager.rb b/lib/stylesheet/manager.rb index 1ee4e8e557..97ac5d076c 100644 --- a/lib/stylesheet/manager.rb +++ b/lib/stylesheet/manager.rb @@ -6,10 +6,10 @@ require 'stylesheet/compiler' module Stylesheet; end class Stylesheet::Manager + BASE_COMPILER_VERSION = 1 CACHE_PATH ||= 'tmp/stylesheet-cache' MANIFEST_DIR ||= "#{Rails.root}/tmp/cache/assets/#{Rails.env}" - MANIFEST_FULL_PATH ||= "#{MANIFEST_DIR}/stylesheet-manifest" THEME_REGEX ||= /_theme$/ COLOR_SCHEME_STYLESHEET ||= "color_definitions" @@ -105,34 +105,65 @@ class Stylesheet::Manager nil end - def self.last_file_updated - if Rails.env.production? - @last_file_updated ||= if File.exist?(MANIFEST_FULL_PATH) - File.readlines(MANIFEST_FULL_PATH, 'r')[0] + def self.fs_asset_cachebuster + if use_file_hash_for_cachebuster? + @cachebuster ||= if File.exist?(manifest_full_path) + File.readlines(manifest_full_path, 'r')[0] else - mtime = max_file_mtime + cachebuster = "#{BASE_COMPILER_VERSION}:#{fs_assets_hash}" FileUtils.mkdir_p(MANIFEST_DIR) - File.open(MANIFEST_FULL_PATH, "w") { |f| f.print(mtime) } - mtime + File.open(manifest_full_path, "w") { |f| f.print(cachebuster) } + cachebuster end else - max_file_mtime + "#{BASE_COMPILER_VERSION}:#{max_file_mtime}" end end - def self.max_file_mtime - globs = ["#{Rails.root}/app/assets/stylesheets/**/*.*css", - "#{Rails.root}/app/assets/images/**/*.*"] + def self.recalculate_fs_asset_cachebuster! + File.delete(manifest_full_path) if File.exist?(manifest_full_path) + @cachebuster = nil + fs_asset_cachebuster + end - Discourse.plugins.map { |plugin| File.dirname(plugin.path) }.each do |path| + def self.manifest_full_path + path = "#{MANIFEST_DIR}/stylesheet-manifest" + return path if !Rails.env.test? + "#{path}-test_#{ENV['TEST_ENV_NUMBER'].presence || '0'}" + end + private_class_method :manifest_full_path + + def self.use_file_hash_for_cachebuster? + Rails.env.production? + end + private_class_method :use_file_hash_for_cachebuster? + + def self.list_files + globs = [ + "#{Rails.root}/app/assets/stylesheets/**/*.*css", + "#{Rails.root}/app/assets/images/**/*.*" + ] + + Discourse.plugins.each do |plugin| + path = File.dirname(plugin.path) globs << "#{path}/plugin.rb" globs << "#{path}/assets/stylesheets/**/*.*css" end - globs.map do |pattern| - Dir.glob(pattern).map { |x| File.mtime(x) }.max - end.compact.max.to_i + globs.flat_map { |g| Dir.glob(g) }.compact end + private_class_method :list_files + + def self.max_file_mtime + list_files.map { |x| File.mtime(x) }.compact.max.to_i + end + private_class_method :max_file_mtime + + def self.fs_assets_hash + hashes = list_files.sort.map { |x| Digest::SHA1.hexdigest("#{x}: #{File.read(x)}") } + Digest::SHA1.hexdigest(hashes.join("|")) + end + private_class_method :fs_assets_hash def self.cache_fullpath path = "#{Rails.root}/#{CACHE_PATH}" diff --git a/lib/stylesheet/manager/builder.rb b/lib/stylesheet/manager/builder.rb index f4da9d14dc..25b1e078a8 100644 --- a/lib/stylesheet/manager/builder.rb +++ b/lib/stylesheet/manager/builder.rb @@ -13,7 +13,7 @@ class Stylesheet::Manager::Builder def compile(opts = {}) if !opts[:force] if File.exist?(stylesheet_fullpath) - unless StylesheetCache.where(target: qualified_target, digest: digest).exists? + if !StylesheetCache.where(target: qualified_target, digest: digest).exists? begin source_map = begin File.read(source_map_fullpath) @@ -229,7 +229,7 @@ class Stylesheet::Manager::Builder end def default_digest - Digest::SHA1.hexdigest "default-#{Stylesheet::Manager.last_file_updated}-#{plugins_digest}-#{current_hostname}" + Digest::SHA1.hexdigest "default-#{Stylesheet::Manager.fs_asset_cachebuster}-#{plugins_digest}-#{current_hostname}" end def color_scheme_digest @@ -248,9 +248,9 @@ class Stylesheet::Manager::Builder digest_string = "#{current_hostname}-" if cs || categories_updated > 0 theme_color_defs = resolve_baked_field(:common, :color_definitions) - digest_string += "#{RailsMultisite::ConnectionManagement.current_db}-#{cs&.id}-#{cs&.version}-#{theme_color_defs}-#{Stylesheet::Manager.last_file_updated}-#{categories_updated}-#{fonts}" + digest_string += "#{RailsMultisite::ConnectionManagement.current_db}-#{cs&.id}-#{cs&.version}-#{theme_color_defs}-#{Stylesheet::Manager.fs_asset_cachebuster}-#{categories_updated}-#{fonts}" else - digest_string += "defaults-#{Stylesheet::Manager.last_file_updated}-#{fonts}" + digest_string += "defaults-#{Stylesheet::Manager.fs_asset_cachebuster}-#{fonts}" if cdn_url = GlobalSetting.cdn_url digest_string += "-#{cdn_url}" diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake index 4ce68c323a..a4d61e265c 100644 --- a/lib/tasks/assets.rake +++ b/lib/tasks/assets.rake @@ -9,14 +9,20 @@ task 'assets:precompile:before' do end if ENV["EMBER_CLI_COMPILE_DONE"] != "1" - compile_command = "NODE_OPTIONS='--max-old-space-size=2048' pnpm --dir app/assets/javascripts/discourse exec ember build -prod" + compile_command = "pnpm --dir app/assets/javascripts/discourse exec ember build -prod" + + if check_node_heap_size_limit < 1024 + STDERR.puts "Detected low Node.js heap_size_limit. Using --max-old-space-size=1024." + compile_command = "NODE_OPTIONS='--max-old-space-size=1024' #{compile_command}" + end + only_assets_precompile_remaining = (ARGV.last == "assets:precompile") if only_assets_precompile_remaining # Using exec to free up Rails app memory during ember build exec "#{compile_command} && EMBER_CLI_COMPILE_DONE=1 bin/rake assets:precompile" else - system compile_command + system compile_command, exception: true end end @@ -64,6 +70,7 @@ task 'assets:precompile:css' => 'environment' do STDERR.puts "-------------" STDERR.puts "Compiling CSS for #{db} #{Time.zone.now}" begin + Stylesheet::Manager.recalculate_fs_asset_cachebuster! Stylesheet::Manager.precompile_css if db == "default" Stylesheet::Manager.precompile_theme_css rescue PG::UndefinedColumn, ActiveModel::MissingAttributeError, NoMethodError => e @@ -90,6 +97,12 @@ task 'assets:flush_sw' => 'environment' do end end +def check_node_heap_size_limit + output, status = Open3.capture2("node", "-e", "console.log(v8.getHeapStatistics().heap_size_limit/1024/1024)") + raise "Failed to fetch node memory limit" if status != 0 + output.to_f +end + def assets_path "#{Rails.root}/public/assets" end diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index 861b6d998a..7b1b02eeef 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -160,7 +160,8 @@ def insert_user_options auto_track_topics_after_msecs, notification_level_when_replying, like_notification_frequency, - skip_new_user_tips + skip_new_user_tips, + hide_profile_and_presence ) SELECT u.id , #{SiteSetting.default_email_mailing_list_mode} @@ -181,6 +182,7 @@ def insert_user_options , #{SiteSetting.default_other_notification_level_when_replying} , #{SiteSetting.default_other_like_notification_frequency} , #{SiteSetting.default_other_skip_new_user_tips} + , #{SiteSetting.default_hide_profile_and_presence} FROM users u LEFT JOIN user_options uo ON uo.user_id = u.id WHERE uo.user_id IS NULL diff --git a/lib/tasks/plugin.rake b/lib/tasks/plugin.rake index e9b6218615..ddff652498 100644 --- a/lib/tasks/plugin.rake +++ b/lib/tasks/plugin.rake @@ -174,7 +174,9 @@ def spec(plugin, parallel: false) params << "--seed #{ENV['RSPEC_SEED']}" if Integer(ENV['RSPEC_SEED'], exception: false) ruby = `which ruby`.strip - files = Dir.glob("./plugins/#{plugin}/spec/**/*_spec.rb").sort + # reject system specs as they are slow and need dedicated setup + files = + Dir.glob("./plugins/#{plugin}/spec/**/*_spec.rb").reject { |f| f.include?("spec/system/") }.sort if files.length > 0 cmd = parallel ? "bin/turbo_rspec" : "bin/rspec" sh "LOAD_PLUGINS=1 #{cmd} #{files.join(' ')} #{params.join(' ')}" diff --git a/lib/tasks/s3.rake b/lib/tasks/s3.rake index 1a7ed0ad84..454630d72f 100644 --- a/lib/tasks/s3.rake +++ b/lib/tasks/s3.rake @@ -10,10 +10,18 @@ def gzip_s3_path(path) "#{path[0..-ext.length]}gz#{ext}" end +def existing_assets + @existing_assets ||= Set.new(helper.list("assets/").map(&:key)) +end + +def prefix_s3_path(path) + path = File.join(helper.s3_bucket_folder_path, path) if helper.s3_bucket_folder_path + path +end + def should_skip?(path) return false if ENV['FORCE_S3_UPLOADS'] - @existing_assets ||= Set.new(helper.list("assets/").map(&:key)) - @existing_assets.include?(path) + existing_assets.include?(prefix_s3_path(path)) end def upload(path, remote_path, content_type, content_encoding = nil) @@ -196,26 +204,37 @@ end task 's3:expire_missing_assets' => :environment do ensure_s3_configured! - count = 0 - keep = 0 + puts "Checking for stale S3 assets..." - in_manifest = asset_paths + assets_to_delete = existing_assets.dup - puts "Ensuring AWS assets are tagged correctly for removal" - helper.list('assets/').each do |f| - if !in_manifest.include?(f.key) - helper.tag_file(f.key, old: true) - count += 1 - else - # ensure we do not delete this by mistake - helper.tag_file(f.key, {}) - keep += 1 + # Check that all current assets are uploaded, and remove them from the to_delete list + asset_paths.each do |current_asset_path| + uploaded = assets_to_delete.delete?(prefix_s3_path(current_asset_path)) + if !uploaded + puts "A current asset does not exist on S3 (#{current_asset_path}). Aborting cleanup task." + exit 1 end end - puts "#{count} assets were flagged for removal in 10 days (#{keep} assets will be retained)" + if assets_to_delete.size > 0 + puts "Found #{assets_to_delete.size} assets to delete..." - puts "Ensuring AWS rule exists for purging old assets" - helper.update_lifecycle("delete_old_assets", 10, tag: { key: 'old', value: 'true' }) + assets_to_delete.each do |to_delete| + if !to_delete.start_with?(prefix_s3_path("assets/")) + # Sanity check, this should never happen + raise "Attempted to delete a non-/asset S3 path (#{to_delete}). Aborting" + end + end + assets_to_delete.each_slice(500) do |slice| + message = "Deleting #{slice.size} assets...\n" + message += slice.join("\n").indent(2) + puts message + helper.delete_objects(slice) + puts "... done" + end + else + puts "No stale assets found" + end end diff --git a/lib/tasks/themes.rake b/lib/tasks/themes.rake index b461e54b8b..289823643f 100644 --- a/lib/tasks/themes.rake +++ b/lib/tasks/themes.rake @@ -52,6 +52,12 @@ task "themes:install" => :environment do |task, args| end end +desc "Install themes & theme components from an archive" +task "themes:install:archive" => :environment do |task, args| + filename = ENV["THEME_ARCHIVE"] + RemoteTheme.update_zipped_theme(filename, File.basename(filename)) +end + def update_themes Theme.includes(:remote_theme).where(enabled: true, auto_update: true).find_each do |theme| begin diff --git a/lib/tasks/uploads.rake b/lib/tasks/uploads.rake index e113eedee7..702f684625 100644 --- a/lib/tasks/uploads.rake +++ b/lib/tasks/uploads.rake @@ -1076,3 +1076,154 @@ task "uploads:fix_missing_s3" => :environment do end end end + +# Supported ENV arguments: +# +# VERBOSE=1 +# Shows debug information. +# +# INTERACTIVE=1 +# Shows debug information and pauses for input on issues. +# +# WORKER_ID/WORKER_COUNT +# When running the script on a single forum in multiple terminals. +# For example, if you want 4 concurrent scripts use WORKER_COUNT=4 +# and WORKER_ID from 0 to 3. +# +# START_ID +# Skip uploads with id lower than START_ID. +task "uploads:downsize" => :environment do + min_image_pixels = 500_000 # 0.5 megapixels + default_image_pixels = 1_000_000 # 1 megapixel + + max_image_pixels = [ + ARGV[0]&.to_i || default_image_pixels, + min_image_pixels + ].max + + ENV["VERBOSE"] = "1" if ENV["INTERACTIVE"] + + def log(*args) + puts(*args) if ENV["VERBOSE"] + end + + puts "", "Downsizing images to no more than #{max_image_pixels} pixels" + + dimensions_count = 0 + downsized_count = 0 + + scope = Upload + .by_users + .with_no_non_post_relations + .where("LOWER(extension) IN ('jpg', 'jpeg', 'gif', 'png')") + + scope = scope.where(<<-SQL, max_image_pixels) + COALESCE(width, 0) = 0 OR + COALESCE(height, 0) = 0 OR + COALESCE(thumbnail_width, 0) = 0 OR + COALESCE(thumbnail_height, 0) = 0 OR + width * height > ? + SQL + + if ENV["WORKER_ID"] && ENV["WORKER_COUNT"] + scope = scope.where("uploads.id % ? = ?", ENV["WORKER_COUNT"], ENV["WORKER_ID"]) + end + + if ENV["START_ID"] + scope = scope.where("uploads.id >= ?", ENV["START_ID"]) + end + + skipped = 0 + total_count = scope.count + puts "Uploads to process: #{total_count}" + + scope.find_each.with_index do |upload, index| + progress = (index * 100.0 / total_count).round(1) + + log "\n" + print "\r#{progress}% Fixed dimensions: #{dimensions_count} Downsized: #{downsized_count} Skipped: #{skipped} (upload id: #{upload.id})" + log "\n" + + path = if upload.local? + Discourse.store.path_for(upload) + else + (Discourse.store.download(upload, max_file_size_kb: 100.megabytes) rescue nil)&.path + end + + unless path + log "No image path" + skipped += 1 + next + end + + begin + w, h = FastImage.size(path, raise_on_failure: true) + rescue FastImage::UnknownImageType + log "Unknown image type" + skipped += 1 + next + rescue FastImage::SizeNotFound + log "Size not found" + skipped += 1 + next + end + + if !w || !h + log "Invalid image dimensions" + skipped += 1 + next + end + + ww, hh = ImageSizer.resize(w, h) + + if w == 0 || h == 0 || ww == 0 || hh == 0 + log "Invalid image dimensions" + skipped += 1 + next + end + + upload.attributes = { + width: w, + height: h, + thumbnail_width: ww, + thumbnail_height: hh, + filesize: File.size(path) + } + + if upload.changed? + log "Correcting the upload dimensions" + log "Before: #{upload.width_was}x#{upload.height_was} #{upload.thumbnail_width_was}x#{upload.thumbnail_height_was} (#{upload.filesize_was})" + log "After: #{w}x#{h} #{ww}x#{hh} (#{upload.filesize})" + + dimensions_count += 1 + + # Don't validate the size - max image size setting might have + # changed since the file was uploaded, so this could fail + upload.validate_file_size = false + upload.save! + end + + if w * h < max_image_pixels + log "Image size within allowed range" + skipped += 1 + next + end + + result = ShrinkUploadedImage.new( + upload: upload, + path: path, + max_pixels: max_image_pixels, + verbose: ENV["VERBOSE"], + interactive: ENV["INTERACTIVE"] + ).perform + + if result + downsized_count += 1 + else + skipped += 1 + end + end + + STDIN.beep + puts "", "Done", Time.zone.now +end diff --git a/lib/theme_store/git_importer.rb b/lib/theme_store/git_importer.rb index 93878b4e21..63db9b4b82 100644 --- a/lib/theme_store/git_importer.rb +++ b/lib/theme_store/git_importer.rb @@ -108,7 +108,7 @@ class ThemeStore::GitImporter args << "clone" if @branch.present? - args.concat(["-b", @branch]) + args.concat(['--single-branch', "-b", @branch]) end args.concat([@url, @temp_folder]) diff --git a/lib/validators/default_composer_category_validator.rb b/lib/validators/default_composer_category_validator.rb new file mode 100644 index 0000000000..2ce38692ca --- /dev/null +++ b/lib/validators/default_composer_category_validator.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class DefaultComposerCategoryValidator + def initialize(opts = {}) + @opts = opts + end + + def valid_value?(val) + category_id = val.to_i + unless SiteSetting.allow_uncategorized_topics + return false if category_id == SiteSetting.uncategorized_category_id + end + true + end + + def error_message + I18n.t('site_settings.errors.invalid_uncategorized_category_setting') + end +end diff --git a/lib/validators/upload_validator.rb b/lib/validators/upload_validator.rb index 7dd4f75781..5fb084c835 100644 --- a/lib/validators/upload_validator.rb +++ b/lib/validators/upload_validator.rb @@ -2,10 +2,7 @@ require "file_helper" -module Validators; end - class UploadValidator < ActiveModel::Validator - def validate(upload) # staff can upload any file in PM if (upload.for_private_message && SiteSetting.allow_staff_to_upload_any_file_in_pm) @@ -141,6 +138,8 @@ class UploadValidator < ActiveModel::Validator end def maximum_file_size(upload, type) + return if !upload.validate_file_size + max_size_kb = if upload.for_export SiteSetting.max_export_file_size_kb else @@ -157,5 +156,4 @@ class UploadValidator < ActiveModel::Validator upload.errors.add(:filesize, message) end end - end diff --git a/lib/version.rb b/lib/version.rb index d3777983db..922ef2e01c 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -10,7 +10,7 @@ module Discourse MAJOR = 2 MINOR = 9 TINY = 0 - PRE = 'beta11' + PRE = 'beta12' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/package.json b/package.json index 81dbfec6f7..0f3d02e607 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "@highlightjs/cdn-assets": "^11.6.0", "@json-editor/json-editor": "^2.6.1", "ace-builds": "1.4.13", - "bootbox": "3.2.0", "chart.js": "3.5.1", "chartjs-plugin-datalabels": "^2.0.0", "diffhtml": "^1.0.0-beta.20", @@ -29,10 +28,10 @@ "workbox-sw": "^4.3.1" }, "devDependencies": { - "@arkweid/lefthook": "^0.7.7", "@mixer/parallel-prettier": "^2.0.1", "chrome-launcher": "^0.15.0", "chrome-remote-interface": "^0.31.2", + "lefthook": "^1.2.0", "puppeteer-core": "^13.7.0" }, "scripts": { diff --git a/plugins/chat/app/controllers/chat_controller.rb b/plugins/chat/app/controllers/chat_controller.rb index 287bcd8f35..3f78f15b8e 100644 --- a/plugins/chat/app/controllers/chat_controller.rb +++ b/plugins/chat/app/controllers/chat_controller.rb @@ -144,9 +144,9 @@ class Chat::ChatController < Chat::ChatBaseController end def edit_message - guardian.ensure_can_edit_chat!(@message) chat_message_updater = Chat::ChatMessageUpdater.update( + guardian: guardian, chat_message: @message, new_content: params[:new_message], upload_ids: params[:upload_ids] || [], @@ -352,6 +352,7 @@ class Chat::ChatController < Chat::ChatBaseController message: "chat.invitation_notification", chat_channel_id: @chat_channel.id, chat_channel_title: @chat_channel.title(user), + chat_channel_slug: @chat_channel.slug, invited_by_username: current_user.username, } data[:chat_message_id] = params[:chat_message_id] if params[:chat_message_id] diff --git a/plugins/chat/app/jobs/regular/chat_notify_mentioned.rb b/plugins/chat/app/jobs/regular/chat_notify_mentioned.rb index 523d3259f2..d6fa48e332 100644 --- a/plugins/chat/app/jobs/regular/chat_notify_mentioned.rb +++ b/plugins/chat/app/jobs/regular/chat_notify_mentioned.rb @@ -39,9 +39,10 @@ module Jobs is_direct_message_channel: @chat_channel.direct_message_channel?, } - data[:chat_channel_title] = @chat_channel.title( - membership.user, - ) unless @is_direct_message_channel + if !@is_direct_message_channel + data[:chat_channel_title] = @chat_channel.title(membership.user) + data[:chat_channel_slug] = @chat_channel.slug + end return data if identifier_type == :direct_mentions @@ -64,8 +65,7 @@ module Jobs username: @creator.username, tag: Chat::ChatNotifier.push_notification_tag(:mention, @chat_channel.id), excerpt: @chat_message.push_notification_excerpt, - post_url: - "/chat/channel/#{@chat_channel.id}/#{@chat_channel.title(membership.user)}?messageId=#{@chat_message.id}", + post_url: "#{@chat_channel.relative_url}?messageId=#{@chat_message.id}", } translation_prefix = diff --git a/plugins/chat/app/jobs/regular/chat_notify_watching.rb b/plugins/chat/app/jobs/regular/chat_notify_watching.rb index c2c2cd8681..e9b8805e88 100644 --- a/plugins/chat/app/jobs/regular/chat_notify_watching.rb +++ b/plugins/chat/app/jobs/regular/chat_notify_watching.rb @@ -62,7 +62,7 @@ module Jobs payload = { username: @creator.username, notification_type: Notification.types[:chat_message], - post_url: "/chat/channel/#{@chat_channel.id}/#{@chat_channel.title(user)}", + post_url: @chat_channel.relative_url, translated_title: I18n.t(translation_key, translation_args), tag: Chat::ChatNotifier.push_notification_tag(:message, @chat_channel.id), excerpt: @chat_message.push_notification_excerpt, diff --git a/plugins/chat/app/models/category_channel.rb b/plugins/chat/app/models/category_channel.rb index d9561d7f1f..e95c3d5cff 100644 --- a/plugins/chat/app/models/category_channel.rb +++ b/plugins/chat/app/models/category_channel.rb @@ -17,7 +17,27 @@ class CategoryChannel < ChatChannel category.secure_group_ids.to_a.concat(staff_groups) end - def title(_) + def title(_ = nil) name.presence || category.name end + + def generate_auto_slug + return if self.slug.present? + self.slug = Slug.for(self.title.strip, "") + self.slug = "" if duplicate_slug? + end + + def ensure_slug_ok + # if we don't unescape it first we strip the % from the encoded version + slug = SiteSetting.slug_generation_method == "encoded" ? CGI.unescape(self.slug) : self.slug + self.slug = Slug.for(slug, "", method: :encoded) + + if self.slug.blank? + errors.add(:slug, :invalid) + elsif SiteSetting.slug_generation_method == "ascii" && !CGI.unescape(self.slug).ascii_only? + errors.add(:slug, I18n.t("chat.category_channel.errors.slug_contains_non_ascii_chars")) + elsif duplicate_slug? + errors.add(:slug, I18n.t("chat.category_channel.errors.is_already_in_use")) + end + end end diff --git a/plugins/chat/app/models/chat_channel.rb b/plugins/chat/app/models/chat_channel.rb index f04094939a..c2cbca6fd3 100644 --- a/plugins/chat/app/models/chat_channel.rb +++ b/plugins/chat/app/models/chat_channel.rb @@ -21,6 +21,8 @@ class ChatChannel < ActiveRecord::Base }, presence: true, allow_nil: true + validate :ensure_slug_ok + before_validation :generate_auto_slug scope :public_channels, -> { @@ -31,6 +33,8 @@ class ChatChannel < ActiveRecord::Base ) } + delegate :empty?, to: :chat_messages, prefix: true + class << self def public_channel_chatable_types ["Category"] @@ -72,11 +76,11 @@ class ChatChannel < ActiveRecord::Base end def url - "#{Discourse.base_url}/chat/channel/#{self.id}/-" + "#{Discourse.base_url}#{relative_url}" end - def public_channel_title - chatable.name + def relative_url + "/chat/channel/#{self.id}/#{self.slug || "-"}" end private @@ -107,6 +111,10 @@ class ChatChannel < ActiveRecord::Base ChatPublisher.publish_channel_status(self) end + + def duplicate_slug? + ChatChannel.where(slug: self.slug).where.not(id: self.id).any? + end end # == Schema Information @@ -130,6 +138,7 @@ end # auto_join_users :boolean default(FALSE), not null # user_count_stale :boolean default(FALSE), not null # slug :string +# type :string # # Indexes # diff --git a/plugins/chat/app/models/chat_message.rb b/plugins/chat/app/models/chat_message.rb index b0784d4ccb..9b4159b3ef 100644 --- a/plugins/chat/app/models/chat_message.rb +++ b/plugins/chat/app/models/chat_message.rb @@ -9,6 +9,7 @@ class ChatMessage < ActiveRecord::Base belongs_to :chat_channel belongs_to :user belongs_to :in_reply_to, class_name: "ChatMessage" + belongs_to :last_editor, class_name: "User" has_many :replies, class_name: "ChatMessage", foreign_key: "in_reply_to_id", dependent: :nullify has_many :revisions, class_name: "ChatMessageRevision", dependent: :destroy has_many :reactions, class_name: "ChatMessageReaction", dependent: :destroy @@ -32,6 +33,8 @@ class ChatMessage < ActiveRecord::Base scope :created_before, ->(date) { where("chat_messages.created_at < ?", date) } + before_save { self.last_editor_id ||= self.user_id } + def validate_message(has_uploads:) WatchedWordsValidator.new(attributes: [:message]).validate(self) Chat::DuplicateMessageValidator.new(self).validate @@ -207,9 +210,11 @@ end # message :text # cooked :text # cooked_version :integer +# last_editor_id :integer not null # # Indexes # # idx_chat_messages_by_created_at_not_deleted (created_at) WHERE (deleted_at IS NULL) # index_chat_messages_on_chat_channel_id_and_created_at (chat_channel_id,created_at) +# index_chat_messages_on_last_editor_id (last_editor_id) # diff --git a/plugins/chat/app/models/chat_message_revision.rb b/plugins/chat/app/models/chat_message_revision.rb index 03f1916835..e13cf507e1 100644 --- a/plugins/chat/app/models/chat_message_revision.rb +++ b/plugins/chat/app/models/chat_message_revision.rb @@ -2,6 +2,7 @@ class ChatMessageRevision < ActiveRecord::Base belongs_to :chat_message + belongs_to :user end # == Schema Information @@ -14,8 +15,10 @@ end # new_message :text not null # created_at :datetime not null # updated_at :datetime not null +# user_id :integer not null # # Indexes # # index_chat_message_revisions_on_chat_message_id (chat_message_id) +# index_chat_message_revisions_on_user_id (user_id) # diff --git a/plugins/chat/app/models/direct_message_channel.rb b/plugins/chat/app/models/direct_message_channel.rb index 69be58827a..9d116643d7 100644 --- a/plugins/chat/app/models/direct_message_channel.rb +++ b/plugins/chat/app/models/direct_message_channel.rb @@ -18,4 +18,12 @@ class DirectMessageChannel < ChatChannel def title(user) direct_message.chat_channel_title_for_user(self, user) end + + def ensure_slug_ok + true + end + + def generate_auto_slug + self.slug = nil + end end diff --git a/plugins/chat/app/serializers/chat_channel_serializer.rb b/plugins/chat/app/serializers/chat_channel_serializer.rb index 8bb2457769..6a194047f9 100644 --- a/plugins/chat/app/serializers/chat_channel_serializer.rb +++ b/plugins/chat/app/serializers/chat_channel_serializer.rb @@ -9,6 +9,7 @@ class ChatChannelSerializer < ApplicationSerializer :chatable_url, :description, :title, + :slug, :last_message_sent_at, :status, :archive_failed, diff --git a/plugins/chat/assets/javascripts/discourse/components/channels-list.js b/plugins/chat/assets/javascripts/discourse/components/channels-list.js index bd1a8592b8..a1e42f0c1b 100644 --- a/plugins/chat/assets/javascripts/discourse/components/channels-list.js +++ b/plugins/chat/assets/javascripts/discourse/components/channels-list.js @@ -4,15 +4,14 @@ import { action, computed } from "@ember/object"; import { schedule } from "@ember/runloop"; import { inject as service } from "@ember/service"; import { and, empty, reads } from "@ember/object/computed"; -import { DRAFT_CHANNEL_VIEW } from "discourse/plugins/chat/discourse/services/chat"; export default class ChannelsList extends Component { @service chat; @service router; + @service chatStateManager; tagName = ""; inSidebar = false; toggleSection = null; - onSelect = null; @reads("chat.publicChannels.[]") publicChannels; @reads("chat.directMessageChannels.[]") directMessageChannels; @empty("publicChannels") publicChannelsEmpty; @@ -80,24 +79,6 @@ export default class ChannelsList extends Component { }`; } - @action - browseChannels() { - this.router.transitionTo("chat.browse"); - return false; - } - - @action - startCreatingDmChannel() { - if ( - this.site.mobileView || - this.router.currentRouteName.startsWith("chat.") - ) { - this.router.transitionTo("chat.draft-channel"); - } else { - this.appEvents.trigger("chat:open-view", DRAFT_CHANNEL_VIEW); - } - } - @action toggleChannelSection(section) { this.toggleSection(section); @@ -111,19 +92,16 @@ export default class ChannelsList extends Component { @action storeScrollPosition() { - const scroller = document.querySelector(".channels-list"); - if (scroller) { - const scrollTop = scroller.scrollTop || 0; - this.session.set("channels-list-position", scrollTop); - } + const scrollTop = document.querySelector(".channels-list")?.scrollTop || 0; + this.session.channelsListPosition = scrollTop; } @bind _applyScrollPosition() { - const data = this.session.get("channels-list-position"); - if (data) { - const scroller = document.querySelector(".channels-list"); - scroller.scrollTo(0, data); - } + const position = this.chatStateManager.isFullPage + ? this.session.channelsListPosition || 0 + : 0; + const scroller = document.querySelector(".channels-list"); + scroller.scrollTo(0, position); } } diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-browse-view.js b/plugins/chat/assets/javascripts/discourse/components/chat-browse-view.js index b270b19cd0..a453ad360f 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-browse-view.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-browse-view.js @@ -87,11 +87,6 @@ export default class ChatBrowseView extends Component { showModal("create-channel"); } - @action - returnToChannelsList() { - return this.router.transitionTo("chat.index"); - } - @bind filterChannels(filter) { this.canLoadMore = true; diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-channel-row.js b/plugins/chat/assets/javascripts/discourse/components/chat-channel-row.js index bc8b6ad421..ae3b281645 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-channel-row.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-channel-row.js @@ -1,8 +1,6 @@ import Component from "@ember/component"; import I18n from "I18n"; import discourseComputed from "discourse-common/utils/decorators"; -import getURL from "discourse-common/lib/get-url"; -import { action } from "@ember/object"; import { equal } from "@ember/object/computed"; import { inject as service } from "@ember/service"; import { CHATABLE_TYPES } from "discourse/plugins/chat/discourse/models/chat-channel"; @@ -12,7 +10,6 @@ export default Component.extend({ router: service(), chat: service(), channel: null, - switchChannel: null, isDirectMessageRow: equal( "channel.chatable_type", CHATABLE_TYPES.directMessageChannel @@ -75,52 +72,6 @@ export default Component.extend({ ); }, - @action - handleNewWindow(event) { - // Middle mouse click - if (event.which === 2) { - window - .open( - getURL(`/chat/channel/${this.channel.id}/${this.channel.title}`), - "_blank" - ) - .focus(); - } - }, - - @action - handleSwitchChannel(event) { - if (this.switchChannel) { - this.switchChannel(this.channel); - event.preventDefault(); - } - }, - - @action - handleClick(event) { - if (event.target.classList.contains("chat-channel-leave-btn")) { - return true; - } - - if ( - event.target.classList.contains("chat-channel-settings-btn") || - event.target.parentElement.classList.contains("select-kit-header-wrapper") - ) { - return; - } - - this.handleSwitchChannel(event); - }, - - @action - handleKeyUp(event) { - if (event.key !== "Enter") { - return; - } - - this.handleSwitchChannel(event); - }, - @discourseComputed("channel.chatable_type") leaveChatTitle() { if (this.channel.isDirectMessageChannel) { diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-composer-uploads.js b/plugins/chat/assets/javascripts/discourse/components/chat-composer-uploads.js index f4b6877bf5..dba574f963 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-composer-uploads.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-composer-uploads.js @@ -9,11 +9,11 @@ import UppyUploadMixin from "discourse/mixins/uppy-upload"; export default Component.extend(UppyUploadMixin, { classNames: ["chat-composer-uploads"], mediaOptimizationWorker: service(), + chatStateManager: service(), id: "chat-composer-uploader", type: "chat-composer", uploads: null, useMultipartUploadsIfAvailable: true, - fullPage: false, init() { this._super(...arguments); @@ -66,7 +66,7 @@ export default Component.extend(UppyUploadMixin, { _uploadDropTargetOptions() { let targetEl; - if (this.fullPage) { + if (this.chatStateManager.isFullPage) { targetEl = document.querySelector(".full-page-chat"); } else { targetEl = document.querySelector( diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-composer.js b/plugins/chat/assets/javascripts/discourse/components/chat-composer.js index ad4f55e8d1..bb9f68be19 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-composer.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-composer.js @@ -37,8 +37,8 @@ export default Component.extend(TextareaTextManipulation, { userSilenced: readOnly("details.user_silenced"), chatEmojiReactionStore: service("chat-emoji-reaction-store"), chatEmojiPickerManager: service("chat-emoji-picker-manager"), + chatStateManager: service("chat-state-manager"), editingMessage: null, - fullPage: false, onValueChange: null, timer: null, value: "", @@ -63,7 +63,7 @@ export default Component.extend(TextareaTextManipulation, { return picker.opened && picker.context === "chat-composer"; }, - @discourseComputed("fullPage") + @discourseComputed("chatStateManager.isFullPage") fileUploadElementId(fullPage) { return fullPage ? "chat-full-page-uploader" : "chat-widget-uploader"; }, diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-emoji-picker.js b/plugins/chat/assets/javascripts/discourse/components/chat-emoji-picker.js index 4395e95d44..57623143a3 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-emoji-picker.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-emoji-picker.js @@ -86,10 +86,14 @@ export default class ChatEmojiPicker extends Component { } @action - didPressEscape(event) { + trapKeyUpEvents(event) { if (event.key === "Escape") { this.chatEmojiPickerManager.close(); } + + if (event.key === "ArrowUp") { + event.stopPropagation(); + } } @action @@ -220,10 +224,6 @@ export default class ChatEmojiPicker extends Component { @action didNavigateSection(event) { - if (event.type !== "keyup") { - return; - } - const sectionEmojis = [ ...event.target .closest(".chat-emoji-picker__section") @@ -252,6 +252,7 @@ export default class ChatEmojiPicker extends Component { if (event.key === "ArrowDown") { event.preventDefault(); + event.stopPropagation(); sectionEmojis .filter((c) => c.offsetTop > event.target.offsetTop) @@ -261,6 +262,7 @@ export default class ChatEmojiPicker extends Component { if (event.key === "ArrowUp") { event.preventDefault(); + event.stopPropagation(); sectionEmojis .reverse() diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-live-pane.js b/plugins/chat/assets/javascripts/discourse/components/chat-live-pane.js index e4200f06c9..54fd768d2d 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-live-pane.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-live-pane.js @@ -20,7 +20,6 @@ import discourseLater from "discourse-common/lib/later"; import { inject as service } from "@ember/service"; import { Promise } from "rsvp"; import { resetIdle } from "discourse/lib/desktop-notifications"; -import { defaultHomepage } from "discourse/lib/utilities"; import { capitalize } from "@ember/string"; import { onPresenceChange, @@ -42,7 +41,6 @@ const FUTURE = "future"; export default Component.extend({ classNameBindings: [":chat-live-pane", "sendingLoading", "loading"], chatChannel: null, - fullPage: false, registeredChatChannelId: null, // ?Number loading: false, loadingMorePast: false, @@ -74,7 +72,7 @@ export default Component.extend({ router: service(), chatEmojiPickerManager: service(), chatComposerPresenceManager: service(), - fullPageChat: service(), + chatStateManager: service(), getCachedChannelDetails: null, clearCachedChannelDetails: null, @@ -248,7 +246,7 @@ export default Component.extend({ this.highlightOrFetchMessage(this.targetMessageId); } - this.focusComposer(); + this._focusComposer(); }) .catch(this._handleErrors) .finally(() => { @@ -1167,6 +1165,7 @@ export default Component.extend({ } if (lastUserMessage) { this.set("editingMessage", lastUserMessage); + this._focusComposer(); } }, @@ -1264,16 +1263,15 @@ export default Component.extend({ }, @action - onCloseFullScreen(channel) { - this.fullPageChat.isPreferred = false; - this.appEvents.trigger("chat:open-channel", channel); + onCloseFullScreen() { + this.chatStateManager.prefersDrawer(); - const previousRouteInfo = this.fullPageChat.exit(); - if (previousRouteInfo) { - this._transitionToRoute(previousRouteInfo); - } else { - this.router.transitionTo(`discovery.${defaultHomepage()}`); - } + this.router.transitionTo(this.chatStateManager.lastKnownAppURL).then(() => { + this.appEvents.trigger( + "chat:open-url", + this.chatStateManager.lastKnownChatURL + ); + }); }, @action @@ -1398,21 +1396,6 @@ export default Component.extend({ return this._fetchAndScrollToLatest(); }, - focusComposer() { - if ( - this._selfDeleted || - this.site.mobileView || - this.chatChannel?.isDraft - ) { - return; - } - - schedule("afterRender", () => { - document.querySelector(".chat-composer-input")?.focus(); - }); - }, - - @afterRender _focusComposer() { this.appEvents.trigger("chat:focus-composer"); }, @@ -1432,18 +1415,6 @@ export default Component.extend({ }); }, - _transitionToRoute(routeInfo) { - const routeName = routeInfo.name; - let params = []; - - do { - params = Object.values(routeInfo.params).concat(params); - routeInfo = routeInfo.parent; - } while (routeInfo); - - this.router.transitionTo(routeName, ...params); - }, - @bind _forceBodyScroll() { // when keyboard is visible this will ensure body diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-desktop.js b/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-desktop.js index d5f8765984..5a7e3e1434 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-desktop.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-desktop.js @@ -2,12 +2,16 @@ import Component from "@ember/component"; import { action } from "@ember/object"; import { createPopper } from "@popperjs/core"; import { schedule } from "@ember/runloop"; +import { inject as service } from "@ember/service"; -const MSG_ACTIONS_PADDING = 2; +const MSG_ACTIONS_HORIZONTAL_PADDING = 2; +const MSG_ACTIONS_VERTICAL_PADDING = -15; export default Component.extend({ tagName: "", + chatStateManager: service(), + messageActions: null, didReceiveAttrs() { @@ -21,7 +25,7 @@ export default Component.extend({ `.chat-message-container[data-id="${this.message.id}"]` ), document.querySelector( - `.chat-msgactions-hover[data-id="${this.message.id}"] .chat-msgactions` + `.chat-message-actions-container[data-id="${this.message.id}"] .chat-message-actions` ), { placement: "right-start", @@ -32,9 +36,9 @@ export default Component.extend({ options: { offset: ({ popper, placement }) => { return [ - MSG_ACTIONS_PADDING, + MSG_ACTIONS_VERTICAL_PADDING, -(placement.includes("left") || placement.includes("right") - ? popper.width + MSG_ACTIONS_PADDING + ? popper.width + MSG_ACTIONS_HORIZONTAL_PADDING : popper.height), ]; }, diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-mobile.js b/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-mobile.js index 5ffb58def3..1457f56e55 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-mobile.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-message-actions-mobile.js @@ -54,13 +54,13 @@ export default Component.extend({ _addFadeIn() { document - .querySelector(".chat-msgactions-backdrop") + .querySelector(".chat-message-actions-backdrop") ?.classList.add("fade-in"); }, _removeFadeIn() { document - .querySelector(".chat-msgactions-backdrop") + .querySelector(".chat-message-actions-backdrop") ?.classList?.remove("fade-in"); }, }); diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-message.js b/plugins/chat/assets/javascripts/discourse/components/chat-message.js index 139b8291ee..52908dd15d 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-message.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-message.js @@ -261,7 +261,7 @@ export default Component.extend({ restore: this.restore, rebakeMessage: this.rebakeMessage, toggleBookmark: this.toggleBookmark, - startReactionForMsgActions: this.startReactionForMsgActions, + startReactionForMessageActions: this.startReactionForMessageActions, }; }, @@ -502,7 +502,7 @@ export default Component.extend({ }, @action - startReactionForMsgActions() { + startReactionForMessageActions() { this.chatEmojiPickerManager.startFromMessageActions( this.message, this.site.desktopView, diff --git a/plugins/chat/assets/javascripts/discourse/components/direct-message-creator.js b/plugins/chat/assets/javascripts/discourse/components/direct-message-creator.js index 426029db34..66cd36bcee 100644 --- a/plugins/chat/assets/javascripts/discourse/components/direct-message-creator.js +++ b/plugins/chat/assets/javascripts/discourse/components/direct-message-creator.js @@ -21,6 +21,7 @@ export default Component.extend({ focusedUser: null, chat: service(), router: service(), + chatStateManager: service(), isLoading: false, onSwitchChannel: null, diff --git a/plugins/chat/assets/javascripts/discourse/components/topic-chat-float.js b/plugins/chat/assets/javascripts/discourse/components/topic-chat-float.js index 17ae2dac5f..7df6d32ec4 100644 --- a/plugins/chat/assets/javascripts/discourse/components/topic-chat-float.js +++ b/plugins/chat/assets/javascripts/discourse/components/topic-chat-float.js @@ -1,6 +1,5 @@ import Component from "@ember/component"; import discourseComputed, { observes } from "discourse-common/utils/decorators"; -import getURL from "discourse-common/lib/get-url"; import { action } from "@ember/object"; import { CHAT_VIEW, @@ -18,7 +17,7 @@ export default Component.extend({ classNameBindings: [":topic-chat-float-container", "hidden"], chat: service(), router: service(), - fullPageChat: service(), + chatStateManager: service(), hidden: true, loading: false, expanded: true, // TODO - false when not first-load topic @@ -35,15 +34,8 @@ export default Component.extend({ } this._checkSize(); - this.appEvents.on("chat:navigated-to-full-page", this, "close"); - this.appEvents.on("chat:open-view", this, "openView"); - this.appEvents.on("chat:toggle-open", this, "toggleChat"); + this.appEvents.on("chat:open-url", this, "openURL"); this.appEvents.on("chat:toggle-close", this, "close"); - this.appEvents.on( - "chat:open-channel-for-chatable", - this, - "openChannelForChatable" - ); this.appEvents.on("chat:open-channel", this, "switchChannel"); this.appEvents.on( "chat:open-channel-at-message", @@ -70,15 +62,8 @@ export default Component.extend({ } if (this.appEvents) { - this.appEvents.off("chat:open-view", this, "openView"); - this.appEvents.off("chat:navigated-to-full-page", this, "close"); - this.appEvents.off("chat:toggle-open", this, "toggleChat"); + this.appEvents.off("chat:open-url", this, "openURL"); this.appEvents.off("chat:toggle-close", this, "close"); - this.appEvents.off( - "chat:open-channel-for-chatable", - this, - "openChannelForChatable" - ); this.appEvents.off("chat:open-channel", this, "switchChannel"); this.appEvents.off( "chat:open-channel-at-message", @@ -116,14 +101,6 @@ export default Component.extend({ this.appEvents.trigger("chat:rerender-header"); }, - async openChannelForChatable(channel) { - if (!channel) { - return; - } - - this.switchChannel(channel); - }, - @discourseComputed("expanded") topLineClass(expanded) { const baseClass = "topic-chat-drawer-header__top-line"; @@ -225,38 +202,45 @@ export default Component.extend({ }, @action - openView(view) { - this.setProperties({ - hidden: false, - expanded: true, - view, - }); + openURL(URL = null) { + this.chat.setActiveChannel(null); + this.set("hidden", false); + this.set("expanded", true); - this.appEvents.trigger("chat:float-toggled", false); + this.chatStateManager.storeChatURL(URL); + + const route = this._buildRouteFromURL( + URL || this.chatStateManager.lastKnownChatURL + ); + + switch (route.name) { + case "chat": + this.set("view", LIST_VIEW); + this.appEvents.trigger("chat:float-toggled", false); + return; + case "chat.draft-channel": + this.set("view", DRAFT_CHANNEL_VIEW); + this.appEvents.trigger("chat:float-toggled", false); + return; + case "chat.channel": + return this.chat + .getChannelBy("id", route.params.channelId) + .then((channel) => { + this.chat.set("messageId", route.queryParams.messageId); + this.chat.setActiveChannel(channel); + this.set("view", CHAT_VIEW); + this.appEvents.trigger("chat:float-toggled", false); + }); + } }, @action - openInFullPage(e) { - const channel = this.chat.activeChannel; - - this.set("expanded", false); - this.set("hidden", true); + openInFullPage() { + this.chatStateManager.storeAppURL(); + this.chatStateManager.prefersFullPage(); this.chat.setActiveChannel(null); - this.fullPageChat.isPreferred = true; - if (!channel) { - return this.router.transitionTo("chat"); - } - - if (e.which === 2) { - // Middle mouse click - window - .open(getURL(`/chat/channel/${channel.id}/${channel.title}`), "_blank") - .focus(); - return false; - } - - this.chat.openChannel(channel); + return this.router.transitionTo(this.chatStateManager.lastKnownChatURL); }, @action @@ -267,42 +251,12 @@ export default Component.extend({ @action close() { - this.setProperties({ - hidden: true, - expanded: false, - }); + this.set("hidden", true); + this.set("expanded", false); this.chat.setActiveChannel(null); this.appEvents.trigger("chat:float-toggled", this.hidden); }, - @action - toggleChat() { - this.set("hidden", !this.hidden); - this.appEvents.trigger("chat:float-toggled", this.hidden); - if (this.hidden) { - return this.chat.setActiveChannel(null); - } else { - this.set("expanded", true); - this.appEvents.trigger("chat:toggle-expand", this.expanded); - if (this.chat.activeChannel) { - // Channel was previously open, so after expand we are done. - return this.chat.setActiveChannel(null); - } - } - - // Look for DM channel with unread, and fallback to public channel with unread - this.chat.getIdealFirstChannelId().then((channelId) => { - if (channelId) { - this.chat.getChannelBy("id", channelId).then((channel) => { - this.switchChannel(channel); - }); - } else { - // No channels with unread messages. Fetch channel index. - this.fetchChannels(); - } - }); - }, - @action refreshChannels() { if (this.view === LIST_VIEW) { @@ -341,11 +295,41 @@ export default Component.extend({ this.chat.setActiveChannel(channel); if (!channel) { - this.openView(LIST_VIEW); + const URL = this._buildURLFromState(LIST_VIEW); + this.openURL(URL); return; } - this.openView(CHAT_VIEW); + const URL = this._buildURLFromState(CHAT_VIEW, channel); + this.openURL(URL); }); }, + + _buildRouteFromURL(URL) { + let route = this.router.recognize(URL || "/"); + + // ember might recognize the index subroute + if (route.localName === "index") { + route = route.parent; + } + + return route; + }, + + _buildURLFromState(view, channel = null) { + switch (view) { + case LIST_VIEW: + return "/chat"; + case DRAFT_CHANNEL_VIEW: + return "/chat/draft-channel"; + case CHAT_VIEW: + if (channel) { + return `/chat/channel/${channel.id}/${channel.slug || "-"}`; + } else { + return "/chat"; + } + default: + return "/chat"; + } + }, }); diff --git a/plugins/chat/assets/javascripts/discourse/controllers/preferences-chat.js b/plugins/chat/assets/javascripts/discourse/controllers/preferences-chat.js index 392dc8f7f9..b89baf2ccd 100644 --- a/plugins/chat/assets/javascripts/discourse/controllers/preferences-chat.js +++ b/plugins/chat/assets/javascripts/discourse/controllers/preferences-chat.js @@ -4,7 +4,8 @@ import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; import { action } from "@ember/object"; import { popupAjaxError } from "discourse/lib/ajax-error"; -import { CHAT_SOUNDS } from "discourse/plugins/chat/discourse/initializers/chat-notification-sounds"; +import { CHAT_SOUNDS } from "discourse/plugins/chat/discourse/services/chat-audio-manager"; +import { inject as service } from "@ember/service"; const CHAT_ATTRS = [ "chat_enabled", @@ -20,6 +21,8 @@ const EMAIL_FREQUENCY_OPTIONS = [ ]; export default class PreferencesChatController extends Controller { + @service chatAudioManager; + emailFrequencyOptions = EMAIL_FREQUENCY_OPTIONS; @discourseComputed @@ -32,8 +35,7 @@ export default class PreferencesChatController extends Controller { @action onChangeChatSound(sound) { if (sound && !isTesting()) { - const audio = new Audio(CHAT_SOUNDS[sound]); - audio.play(); + this.chatAudioManager.playImmediately(sound); } this.model.set("user_option.chat_sound", sound); } diff --git a/plugins/chat/assets/javascripts/discourse/initializers/chat-audio.js b/plugins/chat/assets/javascripts/discourse/initializers/chat-audio.js new file mode 100644 index 0000000000..448d31d13d --- /dev/null +++ b/plugins/chat/assets/javascripts/discourse/initializers/chat-audio.js @@ -0,0 +1,29 @@ +import { withPluginApi } from "discourse/lib/plugin-api"; + +const MENTION = 29; +const MESSAGE = 30; +const CHAT_NOTIFICATION_TYPES = [MENTION, MESSAGE]; + +export default { + name: "chat-audio", + + initialize(container) { + const currentUser = container.lookup("service:current-user"); + const chatService = container.lookup("service:chat"); + + if (!chatService.userCanChat || !currentUser?.chat_sound) { + return; + } + + const chatAudioManager = container.lookup("service:chat-audio-manager"); + chatAudioManager.setup(); + + withPluginApi("0.12.1", (api) => { + api.registerDesktopNotificationHandler((data, siteSettings, user) => { + if (CHAT_NOTIFICATION_TYPES.includes(data.notification_type)) { + chatAudioManager.play(user.chat_sound); + } + }); + }); + }, +}; diff --git a/plugins/chat/assets/javascripts/discourse/initializers/chat-keyboard-shortcuts.js b/plugins/chat/assets/javascripts/discourse/initializers/chat-keyboard-shortcuts.js index d80c994889..6b993a70d4 100644 --- a/plugins/chat/assets/javascripts/discourse/initializers/chat-keyboard-shortcuts.js +++ b/plugins/chat/assets/javascripts/discourse/initializers/chat-keyboard-shortcuts.js @@ -14,7 +14,9 @@ export default { return; } + const router = container.lookup("service:router"); const appEvents = container.lookup("service:app-events"); + const chatStateManager = container.lookup("service:chat-state-manager"); const openChannelSelector = (e) => { e.preventDefault(); e.stopPropagation(); @@ -77,7 +79,9 @@ export default { } event.preventDefault(); event.stopPropagation(); - appEvents.trigger("chat:toggle-open", event); + + chatStateManager.prefersDrawer(); + router.transitionTo(chatStateManager.lastKnownChatURL || "chat"); }; const closeChatDrawer = (event) => { diff --git a/plugins/chat/assets/javascripts/discourse/initializers/chat-notification-sounds.js b/plugins/chat/assets/javascripts/discourse/initializers/chat-notification-sounds.js deleted file mode 100644 index 3a9548c739..0000000000 --- a/plugins/chat/assets/javascripts/discourse/initializers/chat-notification-sounds.js +++ /dev/null @@ -1,47 +0,0 @@ -import { withPluginApi } from "discourse/lib/plugin-api"; -import discourseDebounce from "discourse-common/lib/debounce"; - -export const CHAT_SOUNDS = { - bell: "/plugins/chat/audio/bell.mp3", - ding: "/plugins/chat/audio/ding.mp3", -}; - -const MENTION = 29; -const MESSAGE = 30; -const CHAT_NOTIFICATION_TYPES = [MENTION, MESSAGE]; - -const AUDIO_DEBOUNCE_TIMEOUT = 3000; - -export default { - name: "chat-notification-sounds", - initialize(container) { - const currentUser = container.lookup("service:current-user"); - const chatService = container.lookup("service:chat"); - - if (!chatService.userCanChat || !currentUser?.chat_sound) { - return; - } - - function playAudio(user) { - const audio = new Audio(CHAT_SOUNDS[user.chat_sound]); - audio.play().catch(() => { - // eslint-disable-next-line no-console - console.info( - "User needs to interact with DOM before we can play notification sounds" - ); - }); - } - - function playAudioWithDebounce(user) { - discourseDebounce(this, playAudio, user, AUDIO_DEBOUNCE_TIMEOUT, true); - } - - withPluginApi("0.12.1", (api) => { - api.registerDesktopNotificationHandler((data, siteSettings, user) => { - if (CHAT_NOTIFICATION_TYPES.includes(data.notification_type)) { - playAudioWithDebounce(user); - } - }); - }); - }, -}; diff --git a/plugins/chat/assets/javascripts/discourse/initializers/chat-sidebar.js b/plugins/chat/assets/javascripts/discourse/initializers/chat-sidebar.js index a523b2cea6..470e9212ea 100644 --- a/plugins/chat/assets/javascripts/discourse/initializers/chat-sidebar.js +++ b/plugins/chat/assets/javascripts/discourse/initializers/chat-sidebar.js @@ -4,13 +4,13 @@ import { withPluginApi } from "discourse/lib/plugin-api"; import I18n from "I18n"; import { bind } from "discourse-common/utils/decorators"; import { tracked } from "@glimmer/tracking"; -import { DRAFT_CHANNEL_VIEW } from "discourse/plugins/chat/discourse/services/chat"; import { avatarUrl, escapeExpression } from "discourse/lib/utilities"; import { dasherize } from "@ember/string"; import { emojiUnescape } from "discourse/lib/text"; import { decorateUsername } from "discourse/helpers/decorate-username-selector"; import { until } from "discourse/lib/formatter"; import { inject as service } from "@ember/service"; +import { computed } from "@ember/object"; export default { name: "chat-sidebar", @@ -34,16 +34,19 @@ export default { super(...arguments); this.channel = channel; this.chatService = chatService; + } - this.chatService.appEvents.on( + @bind + willDestroy() { + this.chatService.appEvents.off( "chat:user-tracking-state-changed", this._refreshTrackingState ); } @bind - willDestroy() { - this.chatService.appEvents.off( + didInsert() { + this.chatService.appEvents.on( "chat:user-tracking-state-changed", this._refreshTrackingState ); @@ -58,13 +61,22 @@ export default { } get name() { - return dasherize(slugifyChannel(this.title)); + return dasherize(slugifyChannel(this.channel)); } + @computed("chatService.activeChannel") get classNames() { - return this.channel.current_user_membership.muted - ? "sidebar-section-link--muted" - : ""; + const classes = []; + + if (this.channel.current_user_membership.muted) { + classes.push("sidebar-section-link--muted"); + } + + if (this.channel.id === this.chatService.activeChannel?.id) { + classes.push("sidebar-section-link--active"); + } + + return classes.join(" "); } get route() { @@ -72,7 +84,7 @@ export default { } get models() { - return [this.channel.id, slugifyChannel(this.title)]; + return [this.channel.id, slugifyChannel(this.channel)]; } get text() { @@ -136,19 +148,18 @@ export default { return; } this.chatService = container.lookup("service:chat"); - this.chatService.appEvents.on( - "chat:refresh-channels", - this._refreshChannels - ); + this.router = container.lookup("service:router"); + this.appEvents = container.lookup("service:app-events"); + this.appEvents.on("chat:refresh-channels", this._refreshChannels); this._refreshChannels(); } @bind willDestroy() { - if (!this.chatService) { + if (!this.appEvents) { return; } - this.chatService.appEvents.off( + this.appEvents.off( "chat:refresh-channels", this._refreshChannels ); @@ -187,9 +198,7 @@ export default { { id: "browseChannels", title: I18n.t("chat.channels_list_popup.browse"), - action: () => { - this.chatService.router.transitionTo("chat.browse"); - }, + action: () => this.router.transitionTo("chat.browse.open"), }, ]; } @@ -240,13 +249,22 @@ export default { } get name() { - return slugifyChannel(this.title); + return slugifyChannel(this.channel); } + @computed("chatService.activeChannel") get classNames() { - return this.channel.current_user_membership.muted - ? "sidebar-section-link--muted" - : ""; + const classes = []; + + if (this.channel.current_user_membership.muted) { + classes.push("sidebar-section-link--muted"); + } + + if (this.channel.id === this.chatService.activeChannel?.id) { + classes.push("sidebar-section-link--active"); + } + + return classes.join(" "); } get route() { @@ -254,7 +272,7 @@ export default { } get models() { - return [this.channel.id, slugifyChannel(this.title)]; + return [this.channel.id, slugifyChannel(this.channel)]; } get title() { @@ -336,7 +354,9 @@ export default { } get hoverAction() { - return () => { + return (event) => { + event.stopPropagation(); + event.preventDefault(); this.chatService.unfollowChannel(this.channel); }; } @@ -371,6 +391,7 @@ export default { const SidebarChatDirectMessagesSection = class extends BaseCustomSidebarSection { @service site; + @service router; @tracked sectionLinks = []; @tracked userCanDirectMessage = this.chatService.userCanDirectMessage; @@ -440,19 +461,7 @@ export default { id: "startDm", title: I18n.t("chat.direct_messages.new"), action: () => { - if ( - this.site.mobileView || - this.chatService.router.currentRouteName.startsWith("") - ) { - this.chatService.router.transitionTo( - "chat.draft-channel" - ); - } else { - this.appEvents.trigger( - "chat:open-view", - DRAFT_CHANNEL_VIEW - ); - } + this.router.transitionTo("chat.draft-channel"); }, }, ]; diff --git a/plugins/chat/assets/javascripts/discourse/initializers/chat-user-menu.js b/plugins/chat/assets/javascripts/discourse/initializers/chat-user-menu.js index 192203a506..62758b9b0c 100644 --- a/plugins/chat/assets/javascripts/discourse/initializers/chat-user-menu.js +++ b/plugins/chat/assets/javascripts/discourse/initializers/chat-user-menu.js @@ -20,11 +20,15 @@ export default { (NotificationItemBase) => { return class extends NotificationItemBase { get linkHref() { - const title = this.notification.data.chat_channel_title - ? slugifyChannel(this.notification.data.chat_channel_title) - : "-"; - - return `/chat/channel/${this.notification.data.chat_channel_id}/${title}?messageId=${this.notification.data.chat_message_id}`; + const slug = slugifyChannel({ + title: this.notification.data.chat_channel_title, + slug: this.notification.data.chat_channel_slug, + }); + return `/chat/channel/${ + this.notification.data.chat_channel_id + }/${slug || "-"}?messageId=${ + this.notification.data.chat_message_id + }`; } get linkTitle() { @@ -53,11 +57,15 @@ export default { (NotificationItemBase) => { return class extends NotificationItemBase { get linkHref() { - const title = this.notification.data.chat_channel_title - ? slugifyChannel(this.notification.data.chat_channel_title) - : "-"; - - return `/chat/channel/${this.notification.data.chat_channel_id}/${title}?messageId=${this.notification.data.chat_message_id}`; + const slug = slugifyChannel({ + title: this.notification.data.chat_channel_title, + slug: this.notification.data.chat_channel_slug, + }); + return `/chat/channel/${ + this.notification.data.chat_channel_id + }/${slug || "-"}?messageId=${ + this.notification.data.chat_message_id + }`; } get linkTitle() { diff --git a/plugins/chat/assets/javascripts/discourse/lib/slugify-channel.js b/plugins/chat/assets/javascripts/discourse/lib/slugify-channel.js index 885f9d11de..f7a2734879 100644 --- a/plugins/chat/assets/javascripts/discourse/lib/slugify-channel.js +++ b/plugins/chat/assets/javascripts/discourse/lib/slugify-channel.js @@ -1,8 +1,19 @@ import { slugify } from "discourse/lib/utilities"; -export default function slugifyChannel(title) { - const slug = slugify(title); - return ( - slug.length ? slug : title.trim().toLowerCase().replace(/\s|_+/g, "-") +export default function slugifyChannel(channel) { + if (channel.slug) { + return channel.slug; + } + const slug = slugify(channel.escapedTitle || channel.title); + const resolvedSlug = ( + slug.length + ? slug + : channel.title.trim().toLowerCase().replace(/\s|_+/g, "-") ).slice(0, 100); + + if (!resolvedSlug) { + return "-"; + } + + return resolvedSlug; } diff --git a/plugins/chat/assets/javascripts/discourse/routes/chat-channel.js b/plugins/chat/assets/javascripts/discourse/routes/chat-channel.js index 83f792c682..17e6a2f745 100644 --- a/plugins/chat/assets/javascripts/discourse/routes/chat-channel.js +++ b/plugins/chat/assets/javascripts/discourse/routes/chat-channel.js @@ -8,6 +8,7 @@ import slugifyChannel from "discourse/plugins/chat/discourse/lib/slugify-channel export default class ChatChannelRoute extends DiscourseRoute { @service chat; + @service router; async model(params) { let [chatChannel, channels] = await Promise.all([ @@ -36,13 +37,12 @@ export default class ChatChannelRoute extends DiscourseRoute { } afterModel(model) { - this.appEvents.trigger("chat:navigated-to-full-page"); this.chat.setActiveChannel(model?.chatChannel); const queryParams = this.paramsFor(this.routeName); - const slug = slugifyChannel(model.chatChannel.title); + const slug = slugifyChannel(model.chatChannel); if (queryParams?.channelTitle !== slug) { - this.replaceWith("chat.channel.index", model.chatChannel.id, slug); + this.router.replaceWith("chat.channel.index", model.chatChannel.id, slug); } } diff --git a/plugins/chat/assets/javascripts/discourse/routes/chat-index.js b/plugins/chat/assets/javascripts/discourse/routes/chat-index.js index aeb6402be6..fbf8cd0a3c 100644 --- a/plugins/chat/assets/javascripts/discourse/routes/chat-index.js +++ b/plugins/chat/assets/javascripts/discourse/routes/chat-index.js @@ -3,8 +3,9 @@ import { inject as service } from "@ember/service"; export default class ChatIndexRoute extends DiscourseRoute { @service chat; + @service router; - beforeModel() { + redirect() { if (this.site.mobileView) { return; // Always want the channel index on mobile. } @@ -14,11 +15,10 @@ export default class ChatIndexRoute extends DiscourseRoute { return this.chat.getIdealFirstChannelIdAndTitle().then((channelInfo) => { if (channelInfo) { return this.chat.getChannelBy("id", channelInfo.id).then((c) => { - this.chat.openChannel(c); - return; + return this.chat.openChannel(c); }); } else { - return this.transitionTo("chat.browse"); + return this.router.transitionTo("chat.browse"); } }); } diff --git a/plugins/chat/assets/javascripts/discourse/routes/chat.js b/plugins/chat/assets/javascripts/discourse/routes/chat.js index cfda809d28..04310b7a2c 100644 --- a/plugins/chat/assets/javascripts/discourse/routes/chat.js +++ b/plugins/chat/assets/javascripts/discourse/routes/chat.js @@ -4,11 +4,12 @@ import { defaultHomepage } from "discourse/lib/utilities"; import { inject as service } from "@ember/service"; import { scrollTop } from "discourse/mixins/scroll-top"; import { schedule } from "@ember/runloop"; +import { action } from "@ember/object"; export default class ChatRoute extends DiscourseRoute { @service chat; @service router; - @service fullPageChat; + @service chatStateManager; titleToken() { return I18n.t("chat.title_capitalized"); @@ -16,13 +17,46 @@ export default class ChatRoute extends DiscourseRoute { beforeModel(transition) { if (!this.chat.userCanChat) { - return this.transitionTo(`discovery.${defaultHomepage()}`); + return this.router.transitionTo(`discovery.${defaultHomepage()}`); } - this.fullPageChat.enter(transition?.from); + const INTERCEPTABLE_ROUTES = [ + "chat.channel.index", + "chat.channel", + "chat", + "chat.index", + "chat.draft-channel", + ]; + + if ( + transition.from && // don't intercept when directly loading chat + this.chatStateManager.isDrawerPreferred && + INTERCEPTABLE_ROUTES.includes(transition.targetName) + ) { + transition.abort(); + + let URL = transition.intent.url; + if ( + transition.targetName === "chat.channel.index" || + transition.targetName === "chat.channel" + ) { + URL ??= this.router.urlFor( + transition.targetName, + ...transition.intent.contexts + ); + } else { + URL ??= this.router.urlFor(transition.targetName); + } + + this.appEvents.trigger("chat:open-url", URL); + return; + } + + this.appEvents.trigger("chat:toggle-close"); } activate() { + this.chatStateManager.storeAppURL(); this.chat.updatePresence(); schedule("afterRender", () => { @@ -32,12 +66,19 @@ export default class ChatRoute extends DiscourseRoute { } deactivate() { - this.fullPageChat.exit(); this.chat.setActiveChannel(null); + schedule("afterRender", () => { document.body.classList.remove("has-full-page-chat"); document.documentElement.classList.remove("has-full-page-chat"); scrollTop(); }); } + + @action + willTransition(transition) { + if (!transition?.to?.name?.startsWith("chat.")) { + this.chatStateManager.storeChatURL(); + } + } } diff --git a/plugins/chat/assets/javascripts/discourse/services/chat-audio-manager.js b/plugins/chat/assets/javascripts/discourse/services/chat-audio-manager.js new file mode 100644 index 0000000000..8a2d339267 --- /dev/null +++ b/plugins/chat/assets/javascripts/discourse/services/chat-audio-manager.js @@ -0,0 +1,68 @@ +import Service from "@ember/service"; +import { debounce } from "discourse-common/utils/decorators"; + +const AUDIO_DEBOUNCE_DELAY = 3000; + +export const CHAT_SOUNDS = { + bell: [{ src: "/plugins/chat/audio/bell.mp3", type: "audio/mpeg" }], + ding: [{ src: "/plugins/chat/audio/ding.mp3", type: "audio/mpeg" }], +}; + +const DEFAULT_SOUND_NAME = "bell"; + +const createAudioCache = (sources) => { + const audio = new Audio(); + sources.forEach(({ type, src }) => { + const source = document.createElement("source"); + source.type = type; + source.src = src; + audio.appendChild(source); + }); + return audio; +}; + +export default class ChatAudioManager extends Service { + _audioCache = {}; + + setup() { + Object.keys(CHAT_SOUNDS).forEach((soundName) => { + this._audioCache[soundName] = createAudioCache(CHAT_SOUNDS[soundName]); + }); + } + + willDestroy() { + this._super(...arguments); + + this._audioCache = {}; + } + + playImmediately(soundName) { + return this._play(soundName); + } + + @debounce(AUDIO_DEBOUNCE_DELAY, true) + play(soundName) { + return this._play(soundName); + } + + _play(soundName) { + const audio = + this._audioCache[soundName] || this._audioCache[DEFAULT_SOUND_NAME]; + + if (!audio.paused) { + audio.pause(); + if (typeof audio.fastSeek === "function") { + audio.fastSeek(0); + } else { + audio.currentTime = 0; + } + } + + return audio.play().catch(() => { + // eslint-disable-next-line no-console + console.info( + "[chat] User needs to interact with DOM before we can play notification sounds." + ); + }); + } +} diff --git a/plugins/chat/assets/javascripts/discourse/services/chat-emoji-picker-manager.js b/plugins/chat/assets/javascripts/discourse/services/chat-emoji-picker-manager.js index 48e2b9ef51..b19d53b61a 100644 --- a/plugins/chat/assets/javascripts/discourse/services/chat-emoji-picker-manager.js +++ b/plugins/chat/assets/javascripts/discourse/services/chat-emoji-picker-manager.js @@ -77,7 +77,7 @@ export default class ChatEmojiPickerManager extends Service { startFromMessageActions(message, isDesktop, callback) { const trigger = document.querySelector( - `.chat-msgactions-hover[data-id="${message.id}"] .chat-msgactions` + `.chat-message-actions-container[data-id="${message.id}"] .chat-message-actions` ); this.startFromMessage(callback, isDesktop, trigger); } diff --git a/plugins/chat/assets/javascripts/discourse/services/chat-state-manager.js b/plugins/chat/assets/javascripts/discourse/services/chat-state-manager.js new file mode 100644 index 0000000000..38fe1af7e5 --- /dev/null +++ b/plugins/chat/assets/javascripts/discourse/services/chat-state-manager.js @@ -0,0 +1,73 @@ +import Service, { inject as service } from "@ember/service"; +import { defaultHomepage } from "discourse/lib/utilities"; +import { tracked } from "@glimmer/tracking"; +import KeyValueStore from "discourse/lib/key-value-store"; +import Site from "discourse/models/site"; + +const PREFERRED_MODE_KEY = "preferred_mode"; +const PREFERRED_MODE_STORE_NAMESPACE = "discourse_chat_"; +const FULL_PAGE_CHAT = "FULL_PAGE_CHAT"; +const DRAWER_CHAT = "DRAWER_CHAT"; + +export default class ChatStateManager extends Service { + @service router; + @tracked _chatURL = null; + @tracked _appURL = null; + + _store = new KeyValueStore(PREFERRED_MODE_STORE_NAMESPACE); + + reset() { + this._store.remove(PREFERRED_MODE_KEY); + this._chatURL = null; + this._appURL = null; + } + + prefersFullPage() { + this._store.setObject({ key: PREFERRED_MODE_KEY, value: FULL_PAGE_CHAT }); + } + + prefersDrawer() { + this._store.setObject({ key: PREFERRED_MODE_KEY, value: DRAWER_CHAT }); + } + + get isFullPagePreferred() { + return !!( + Site.currentProp("mobileView") || + this._store.getObject(PREFERRED_MODE_KEY) === FULL_PAGE_CHAT + ); + } + + get isDrawerPreferred() { + return !!( + !this.isFullPagePreferred || + (!Site.currentProp("mobileView") && + (!this._store.getObject(PREFERRED_MODE_KEY) || + this._store.getObject(PREFERRED_MODE_KEY) === DRAWER_CHAT)) + ); + } + + get isFullPage() { + return this.router.currentRouteName?.startsWith("chat"); + } + + storeAppURL(URL = null) { + this._appURL = URL || this.router.currentURL; + } + + storeChatURL(URL = null) { + this._chatURL = URL || this.router.currentURL; + } + + get lastKnownAppURL() { + let url = this._appURL; + if (!url || url === "/") { + url = this.router.urlFor(`discovery.${defaultHomepage()}`); + } + + return url; + } + + get lastKnownChatURL() { + return this._chatURL || "/chat"; + } +} diff --git a/plugins/chat/assets/javascripts/discourse/services/chat.js b/plugins/chat/assets/javascripts/discourse/services/chat.js index 86c98ec072..bf8a59b839 100644 --- a/plugins/chat/assets/javascripts/discourse/services/chat.js +++ b/plugins/chat/assets/javascripts/discourse/services/chat.js @@ -35,7 +35,7 @@ const READ_INTERVAL = 1000; export default class Chat extends Service { @service appEvents; @service chatNotificationManager; - @service fullPageChat; + @service chatStateManager; @service presence; @service router; @service site; @@ -176,7 +176,7 @@ export default class Chat extends Service { updatePresence() { next(() => { - if (this.fullPageChat.isActive || this.chatOpen) { + if (this.chatStateManager.isFullPage || this.chatOpen) { this.presenceChannel.enter({ activeOptions: CHAT_ONLINE_OPTIONS }); } else { this.presenceChannel.leave(); @@ -502,7 +502,7 @@ export default class Chat extends Service { return this.router.transitionTo( "chat.channel", response.id, - slugifyChannel(response.title), + slugifyChannel(response), { queryParams } ); }); @@ -525,16 +525,16 @@ export default class Chat extends Service { this.setActiveChannel(channel); if ( - this.fullPageChat.isActive || + this.chatStateManager.isFullPage || this.site.mobileView || - this.fullPageChat.isPreferred + this.chatStateManager.isFullPagePreferred ) { const queryParams = messageId ? { messageId } : {}; return this.router.transitionTo( "chat.channel", channel.id, - slugifyChannel(channel.title), + slugifyChannel(channel), { queryParams } ); } else { @@ -747,7 +747,7 @@ export default class Chat extends Service { this._unsubscribeFromChatChannel(channel); this.stopTrackingChannel(channel); - if (channel.isDirectMessageChannel) { + if (channel === this.activeChannel && channel.isDirectMessageChannel) { this.router.transitionTo("chat"); } }); diff --git a/plugins/chat/assets/javascripts/discourse/services/full-page-chat.js b/plugins/chat/assets/javascripts/discourse/services/full-page-chat.js deleted file mode 100644 index f50630d5f2..0000000000 --- a/plugins/chat/assets/javascripts/discourse/services/full-page-chat.js +++ /dev/null @@ -1,33 +0,0 @@ -import KeyValueStore from "discourse/lib/key-value-store"; -import Service from "@ember/service"; - -const FULL_PAGE = "fullPage"; -const STORE_NAMESPACE_CHAT_WINDOW = "discourse_chat_window_"; - -export default class FullPageChat extends Service { - store = new KeyValueStore(STORE_NAMESPACE_CHAT_WINDOW); - _previousRouteInfo = null; - _isActive = false; - - enter(previousRouteInfo) { - this._previousRouteInfo = previousRouteInfo; - this._isActive = true; - } - - exit() { - this._isActive = false; - return this._previousRouteInfo; - } - - get isActive() { - return this._isActive; - } - - get isPreferred() { - return !!this.store.getObject(FULL_PAGE); - } - - set isPreferred(value) { - this.store.setObject({ key: FULL_PAGE, value }); - } -} diff --git a/plugins/chat/assets/javascripts/discourse/templates/chat-channel-index.hbs b/plugins/chat/assets/javascripts/discourse/templates/chat-channel-index.hbs index 3aaeca4ccc..dc261f0041 100644 --- a/plugins/chat/assets/javascripts/discourse/templates/chat-channel-index.hbs +++ b/plugins/chat/assets/javascripts/discourse/templates/chat-channel-index.hbs @@ -1 +1 @@ -تؤدي أرشفة القناة إلى وضعها في وضع القراءة فقط ونقل جميع الرسائل من القناة إلى موضوع جديد أو موجود. لا يمكن إرسال رسائل جديدة، ولا يمكن تعديل أو حذف أي رسائل حالية.
هل أنت متأكد أنك تريد أرشفة قناة %{channelTitle}؟
" + process_started: "لقد بدأت عملية الأرشفة. سيتم إغلاق هذا النموذج بعد قليل، وستتلقى رسالة شخصية عند اكتمال عملية الأرشفة." + retry: "إعادة المحاولة" + channel_open: + title: "فتح القناة" + instructions: "يعيد فتح القناة، وسيتمكن جميع المستخدمين من إرسال الرسائل وتعديل رسائلهم الحالية." + channel_close: + title: "إغلاق القناة" + instructions: "يمنع إغلاق القناة المستخدمين من غير فريق العمل من إرسال رسائل جديدة أو تعديل الرسائل الحالية. هل تريد بالتأكيد إغلاق هذه القناة؟" + channel_delete: + title: "حذف القناة" + instructions: "يحذف قناة %{name} وسجل الدردشة. سيتم حذف جميع الرسائل والبيانات ذات الصلة، مثل التفاعلات والتحميلات نهائيًا. إذا كنت ترغب في الحفاظ على سجل القناة وإيقاف تشغيلها، فقد ترغب في أرشفة القناة بدلًا من ذلك.
هل تريد بالتأكيد المتابعة إلى الحذف النهائي للقناة؟ للتأكيد، اكتب اسم القناة في المربع أدناه.
" + confirm: "أتفهم العواقب، احذف القناة" + confirm_channel_name: "أدخل اسم القناة" + process_started: "لقد بدأت عملية حذف القناة. سيتم إغلاق هذا النموذج بعد قليل، ولن يصبح بإمكانك رؤية القناة المحذوفة في أي مكان." channels_list_popup: browse: "استعراض القنوات" + create: "قناة جديدة" click_to_join: "انقر هنا لعرض القنوات المتاحة" close: "إغلاق" collapse: "طي ساحب الدردشة" confirm_flag: "هل تريد بالتأكيد الإبلاغ عن رسالة %{username}؟" deleted: "تم حذف رسالة. [view]" + hidden: "تم حذف إحدى الرسائل. [view]" delete: "حذف" edited: "تم التعديل" + muted: "تم كتمه" + joined: "انضم" empty_state: + direct_message_cta: "بدء دردشة شخصية" direct_message: "يمكنك أيضًا بدء دردشة شخصية مع أحد المستخدمين أو أكثر." + title: "لم يتم العثور على أي قناة" email_frequency: + description: "سنراسلك عبر البريد الإلكتروني فقط إذا لم نرك في آخر 15 دقيقة." never: "أبدًا" + title: "إشعارات البريد الإلكتروني" + when_away: "عندما أكون غائبًا فقط" enable: "تفعيل الدردشة" flag: "الإبلاغ" + emoji: "إدراج رمز تعبيري" flagged: "لقد تم الإبلاغ عن الرسالة للمراجعة" invalid_access: "ليس لديك إذن الوصول لعرض قناة الدردشة هذه" + invitation_notification: "دعاك %{username} للانضمام إلى قناة دردشة" in_reply_to: "ردًا على" heading: "الدردشة" join: "الانضمام" new_messages: "رسائل جديدة" mention_warning: cannot_see: - zero: "لا يستطيع %{usernames} الوصول إلى هذه القناة ولم يتم إرسال إشعار إليه." + zero: "لا يستطيع %{usernames} الوصول إلى هذه القناة ولم يتم إرسال إشعار إليهم." one: "لا يستطيع %{usernames} الوصول إلى هذه القناة ولم يتم إرسال إشعار إليه." two: "لا يستطيع %{usernames} الوصول إلى هذه القناة ولم يتم إرسال إشعار إليهما." few: "لا يستطيع %{usernames} الوصول إلى هذه القناة ولم يتم إرسال إشعار إليهم." @@ -54,12 +114,12 @@ ar: other: "لا يستطيع %{usernames} الوصول إلى هذه القناة ولم يتم إرسال إشعار إليهم." dismiss: "تجاهل" invitations_sent: - zero: "تم إرسال دعوة" + zero: "تم إرسال دعوات" one: "تم إرسال دعوة" two: "تم إرسال دعوتين" few: "تم إرسال دعوات" - many: "تم إرسال دعوة" - other: "تم إرسال دعوة" + many: "تم إرسال دعوات" + other: "تم إرسال دعوات" invite: "دعوة إلى القناة" without_membership: zero: "لم ينضم %{usernames} إلى هذه القناة." @@ -68,25 +128,45 @@ ar: few: "لم ينضم %{usernames} إلى هذه القناة." many: "لم ينضم %{usernames} إلى هذه القناة." other: "لم ينضم %{usernames} إلى هذه القناة." + aria_roles: + header: "رأس الدردشة" + composer: "أداة إنشاء الدردشة" + channels_list: "قائمة قنوات الدردشة" no_public_channels: "لم تنضم إلى أي قنوات." only_chat_push_notifications: title: "إرسال الإشعارات الفورية للدردشة فقط" description: "حظر إرسال جميع الإشعارات الفورية غير المتعلقة بالدردشة" + ignore_channel_wide_mention: + title: "تجاهل الإشارات على مستوى القناة" + description: "عدم إرسال الإشعارات للإشارات على مستوى القناة (@here و@all)" open: "فتح الدردشة" open_full_page: "فتح الدردشة في شاشة كاملة" + close_full_page: "إغلاق الدردشة في وضع الشاشة الكاملة" open_message: "فتح رسالة في الدردشة" placeholder_self: "تدوين شيء ما" placeholder_others: "الدردشة مع %{messageRecipient}" + placeholder_new_message_disallowed: "القناة %{status}، لا يمكنك إرسال رسائل جديدة الآن." + placeholder_silenced: "لا يمكنك إرسال رسائل في الوقت الحالي." + placeholder_start_conversation: ابدأ محادثة مع %{usernames} remove_upload: "إزالة ملف" react: "التفاعل برمز تعبيري" reply: "رد" edit: "تعديل" copy_link: "نسخ الرابط" rebake_message: "إعادة بناء HTML" + retry_staged_message: + title: "خطأ في الشبكة" + action: "هل تريد الإرسال مجددًا؟" + unreliable_network: "الشبكة غير موثوقة، وقد لا تعمل ميزتا إرسال الرسائل وحفظ المسودات" + bookmark_message: "إشارة مرجعية" + bookmark_message_edit: "تعديل الإشارة المرجعية" restore: "استعادة الرسالة المحذوفة" save: "حفظ" select: "تحديد" + silence: "كتم المستخدم" + return_to_list: "العودة إلى قائمة القنوات" scroll_to_bottom: "التمرير إلى الأسفل" + scroll_to_new_messages: "عرض الرسائل الجديدة" sound: title: "صوت إشعارات دردشة سطح المكتب" sounds: @@ -97,31 +177,103 @@ ar: title_capitalized: "الدردشة" upload: "إرفاق ملف" uploaded_files: - zero: "%{count} ملف" + zero: "%{count} ملفًا" one: "ملف واحد (%{count})" two: "ملفان (%{count})" few: "%{count} ملفات" many: "%{count} ملفًا" - other: "%{count} ملف" + other: "%{count} ملفًا" you_flagged: "لقد أبلغت عن هذه الرسالة" exit: "عودة" + channel_status: + read_only_header: "القناة للقراءة فقط" + read_only: "للقراءة فقط" + archived_header: "القناة مؤرشفة" + archived: "مؤرشفة" + archive_failed: "فشلت أرشفة القناة. تمت أرشفة %{completed}/%{total} من الرسائل في الموضوع المستهدف. اضغط على إعادة المحاولة لمحاولة إكمال الأرشيف." + archive_completed: "راجع موضوع الأرشيف" + closed_header: "القناة مغلقة" + closed: "مغلقة" + open_header: "القناة مفتوحة" + open: "مفتوحة" browse: + back: "العودة" title: القنوات + filter_all: الكل + filter_open: مفتوحة + filter_closed: مغلقة + filter_archived: مؤرشفة + filter_input_placeholder: البحث عن القناة بالاسم + chat_message_separator: + today: اليوم + yesterday: الأمس + members_view: + filter_placeholder: العثور على أعضاء about_view: + associated_topic: الموضوع المرتبط + associated_category: الفئة المرتبطة + title: العنوان description: الوصف channel_info: + back_to_all_channels: "كل القنوات" back_to_channel: "العودة" + tabs: + about: نبذة + members: الأعضاء + settings: الإعدادات + channel_edit_title_modal: + title: تعديل العنوان + input_placeholder: إضافة عنوان + description: أعط عنوانًا وصفيًا قصيرًا لقناتك + channel_edit_description_modal: + title: تعديل الوصف + input_placeholder: أضِف وصفًا + description: أخبر الناس عن محتوى هذه القناة + direct_message_creator: + title: رسالة جديدة + prefix: "إلى:" + no_results: لا توجد نتائج + selected_user_title: "إلغاء تحديد %{username}" channel_selector: title: "الانتقال إلى القناة" no_channels: "لا توجد قنوات تطابق بحثك" + channel: + no_memberships: لا يوجد أعضاء في هذه القناة + no_memberships_found: لم يتم العثور على أعضاء + memberships_count: + zero: "%{count} عضوًا" + one: "عضو واحد (%{count})" + two: "عضوان (%{count})" + few: "%{count} أعضاء" + many: "%{count} عضوًا" + other: "%{count} عضوًا" create_channel: + auto_join_users: + public_category_warning: "%{category} هي فئة عامة. هل تريد إضافة كل المستخدمين النشطين مؤخرًا إلى هذه القناة؟" + warning_groups: + zero: هل تريد إضافة %{members_count} مستخدمًا نشطًا مؤخرًا من %{group} و%{group_2}؟ + one: هل تريد إضافة مستخدم واحد (%{members_count}) نشط مؤخرًا من %{group}؟ + two: هل تريد إضافة مستخدمين (%{members_count}) نشطين مؤخرًا من %{group} و%{group_2}؟ + few: هل تريد إضافة %{members_count} مستخدمين نشطين مؤخرًا من %{group} و%{group_2}؟ + many: هل تريد إضافة %{members_count} مستخدمًا نشطًا مؤخرًا من %{group} و%{group_2}؟ + other: هل تريد إضافة %{members_count} مستخدمًا نشطًا مؤخرًا من %{group} و%{group_2}؟ + warning_multiple_groups: هل تريد إضافة %{members_count} من المستخدمين من %{group_1} و%{count} أخرى؟ choose_category: label: "اختيار فئة" none: "اختر واحدة..." - default_hint: إدارة الوصول من خلال زيارة %{category} إعدادات الأمان + default_hint: إدارة الوصول بالانتقال إلى %{category} إعدادات الأمان + hint_groups: + zero: سيحظى المستخدمون في %{hint} و%{hint_2} بإمكانية الوصول إلى هذه القناة حسب إعدادات الأمان + one: سيحظى المستخدمون في %{hint} بإمكانية الوصول إلى هذه القناة حسب إعدادات الأمان + two: سيحظى المستخدمون في %{hint} و%{hint_2} بإمكانية الوصول إلى هذه القناة حسب إعدادات الأمان + few: سيحظى المستخدمون في %{hint} و%{hint_2} بإمكانية الوصول إلى هذه القناة حسب إعدادات الأمان + many: سيحظى المستخدمون في %{hint} و%{hint_2} بإمكانية الوصول إلى هذه القناة حسب إعدادات الأمان + other: سيحظى المستخدمون في %{hint} و%{hint_2} بإمكانية الوصول إلى هذه القناة حسب إعدادات الأمان + hint_multiple_groups: سيحظى المستخدمون في %{hint_1} و%{count} من المجموعات الأخرى بإمكانية الوصول إلى هذه القناة حسب إعدادات الأمان create: "إنشاء قناة" description: "الوصف (اختياري)" name: "اسم القناة" + title: "قناة جديدة" type: "النوع" types: category: "الفئة" @@ -136,13 +288,23 @@ ar: you_others_and_more: "لقد تفاعلت أنت و%{usernames} و%{more} من المستخدمين الآخرين باستخدام :%{emoji}:" composer: toggle_toolbar: "تشغيل شريط الأدوات" + italic_text: "نص بارز" + bold_text: "نص بارز" + code_text: "نص رمز برمجي" + quote: + original_channel: 'تم إرساله في الأساس في %{channel}' + copy_success: "تم نسخ اقتباس الدردشة إلى الحافظة" notification_levels: never: "أبدًا" mention: "للإشارات فقط" always: "للنشاط بأكمله" settings: + enable_auto_join_users: "إضافة كل المستخدمين النشطين مؤخرًا تلقائيًا" + disable_auto_join_users: "إيقاف إضافة المستخدمين تلقائيًا" + auto_join_users_warning: "سينضم كل المستخدمين الذين ليسوا أعضاءً في هذه القناة ولديهم وصول إلى فئة %{category}. هل أنت متأكد؟" desktop_notification_level: "إشعارات سطح المكتب" follow: "الانضمام" + followed: "انضم" mobile_notification_level: "الإشعارات الفورية للجوَّال" mute: "كتم القناة" muted_on: "تشغيل" @@ -159,6 +321,7 @@ ar: new: "دردشة شخصية جديدة" create: "إنشاء" leave: "مغادرة هذه الدردشة الشخصية" + cannot_create: "عذرًا، لا يمكنك إرسال الرسائل المباشرة." incoming_webhooks: back: "العودة" channel_placeholder: "اختيار قناة" @@ -181,44 +344,51 @@ ar: system: "النظام" title: "خطافات الويب الواردة" url: "عنوان URL" + url_instructions: "يحتوي عنوان URL هذا على قيمة سرية - احتفظ بها في مكانٍ آمن." username: "اسم المستخدم" username_instructions: "اسم المستخدم لبرنامج الروبوت الذي ينشر على القناة. يتم ضبطه افتراضيًا على \"النظام\" عند تركه فارغة." + instructions: "يمكن استخدام خطافات الويب الواردة بواسطة أنظمة خارجية لنشر الرسائل في قناة دردشة مخصَّصة كمستخدم برنامج روبوت عبر نقطة النهاية/hooks/:key. يتألف الحمل من معلمة نصية فردية، وهي مقيَّدة إلى 2000 حرف.النصية بتنسيق Slack، مع استخراج الروابط والإشارات بناءً على التنسيق في https://api.slack.com/reference/surfaces/formatting، لكن يجب استخدام نقطة النهاية /hooks/:key/slack من أجل ذلك."
selection:
cancel: "إلغاء"
+ quote_selection: "اقتباس في الموضوع"
+ copy: "نسخ"
+ move_selection_to_channel: "النقل إلى القناة"
error: "حدث خطأ في أثناء نقل رسائل الدردشة"
title: "نقل الدردشة إلى الموضوع"
new_topic:
title: "النقل إلى موضوع جديد"
instructions:
- zero: "أنت على وشك إنشاء موضوع جديد وتعبئته بعدد %{count} من رسائل الدردشة التي حدَّدتها."
+ zero: "أنت على وشك إنشاء موضوع جديد وتعبئته برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
one: "أنت على وشك إنشاء موضوع جديد وتعبئته برسالة الدردشة التي حدَّدتها."
two: "أنت على وشك إنشاء موضوع جديد وتعبئته برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
- few: "أنت على وشك إنشاء موضوع جديد وتعبئته بعدد %{count} من رسائل الدردشة التي حدَّدتها."
- many: "أنت على وشك إنشاء موضوع جديد وتعبئته بعدد %{count} من رسائل الدردشة التي حدَّدتها."
- other: "أنت على وشك إنشاء موضوع جديد وتعبئته بعدد %{count} من رسائل الدردشة التي حدَّدتها."
+ few: "أنت على وشك إنشاء موضوع جديد وتعبئته برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
+ many: "أنت على وشك إنشاء موضوع جديد وتعبئته برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
+ other: "أنت على وشك إنشاء موضوع جديد وتعبئته برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
+ instructions_channel_archive: "أنت على وشك إنشاء موضوع جديد وأٍِرشفة رسائل القناة إليه."
existing_topic:
title: "النقل إلى موضوع حالي"
instructions:
- zero: "يُرجى اختيار الموضوع الذي ترغب في نقل رسائل الدردشة البالغ عددها %{count} إليه."
+ zero: "يُرجى اختيار الموضوع الذي ترغب في نقل رسالتَي الدردشة (%{count}) إليه."
one: "يُرجى اختيار الموضوع الذي ترغب في نقل رسالة الدردشة إليه."
two: "يُرجى اختيار الموضوع الذي ترغب في نقل رسالتَي الدردشة (%{count}) إليه."
- few: "يُرجى اختيار الموضوع الذي ترغب في نقل رسائل الدردشة البالغ عددها %{count} إليه."
- many: "يُرجى اختيار الموضوع الذي ترغب في نقل رسائل الدردشة البالغ عددها %{count} إليه."
- other: "يُرجى اختيار الموضوع الذي ترغب في نقل رسائل الدردشة البالغ عددها %{count} إليه."
+ few: "يُرجى اختيار الموضوع الذي ترغب في نقل رسالتَي الدردشة (%{count}) إليه."
+ many: "يُرجى اختيار الموضوع الذي ترغب في نقل رسالتَي الدردشة (%{count}) إليه."
+ other: "يُرجى اختيار الموضوع الذي ترغب في نقل رسالتَي الدردشة (%{count}) إليه."
+ instructions_channel_archive: "يُرجى اختيار الموضوع الذي ترغب في أرشفة رسائل القناة إليه."
new_message:
title: "النقل إلى رسالة جديدة"
instructions:
- zero: "أنت على وشك إنشاء رسالة جديدة وتعبئتها بعدد %{count} من رسائل الدردشة التي حدَّدتها."
+ zero: "أنت على وشك إنشاء رسالة جديدة وتعبئتها برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
one: "أنت على وشك إنشاء رسالة جديدة وتعبئتها برسالة الدردشة التي حدَّدتها."
two: "أنت على وشك إنشاء رسالة جديدة وتعبئتها برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
- few: "أنت على وشك إنشاء رسالة جديدة وتعبئتها بعدد %{count} من رسائل الدردشة التي حدَّدتها."
- many: "أنت على وشك إنشاء رسالة جديدة وتعبئتها بعدد %{count} من رسائل الدردشة التي حدَّدتها."
- other: "أنت على وشك إنشاء رسالة جديدة وتعبئتها بعدد %{count} من رسائل الدردشة التي حدَّدتها."
+ few: "أنت على وشك إنشاء رسالة جديدة وتعبئتها برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
+ many: "أنت على وشك إنشاء رسالة جديدة وتعبئتها برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
+ other: "أنت على وشك إنشاء رسالة جديدة وتعبئتها برسالتَي الدردشة (%{count}) الذين حدَّدتهما."
replying_indicator:
single_user: "%{username} يكتب"
multiple_users: "%{commaSeparatedUsernames} و%{lastUsername} يكتبون"
many_users:
- zero: "%{commaSeparatedUsernames} و%{count} آخر يكتب"
+ zero: "%{commaSeparatedUsernames} و%{count} آخرون يكتبون"
one: "%{commaSeparatedUsernames} و%{count} آخر يكتبان"
two: "%{commaSeparatedUsernames} و%{count} آخران يكتبون"
few: "%{commaSeparatedUsernames} و%{count} آخرون يكتبون"
@@ -228,12 +398,51 @@ ar:
public: "يتم الاحتفاظ بسجل القناة لمدة %{days} من الأيام."
dm: "يتم الاحتفاظ بسجل الدردشة الشخصية لمدة %{days} من الأيام."
topic_button_title: "الدردشة"
+ flags:
+ off_topic: "هذه الرسالة ليست ذات صلة بالمناقشة الحالية كما هو محدَّد في عنوان القناة، وربما ينبغي نقلها إلى مكانٍ آخر."
+ inappropriate: "تحتوي هذه الرسالة على محتوى قد يعتبره الشخص العاقل مسيئًا أو مهينًا أو ينتهك إرشادات المجتمع لدينا."
+ spam: "هذه الرسالة إعلانية أو تخريبية. إنها ليست مفيدة أو ذات صلة بالقناة الحالية."
+ notify_user: "أريد التحدث إلى هذا الشخص مباشرةً وشخصيًا بشأن رسالته."
+ notify_moderators: "تتطلب هذه الرسالة انتباه فريق العمل لسبب آخر غير مُدرَج أعلاه."
+ flagging:
+ action: "الإبلاغ عن الرسالة"
+ emoji_picker:
+ favorites: "المستخدمة بشكلٍ متكرر"
+ smileys_&_emotion: "الرموز التعبيرية"
+ objects: "الأشياء"
+ people_&_body: "الأشخاص والجسم"
+ travel_&_places: "السفر والأماكن"
+ animals_&_nature: "الحيوانات والطبيعة"
+ food_&_drink: "الطعام والشراب"
+ activities: "الأنشطة"
+ flags: "البلاغات"
+ symbols: "الرموز"
+ search_placeholder: "البحث باسم الرمز التعبيري والاسم المستعار..."
+ no_results: "لا توجد نتائج"
+ draft_channel_screen:
+ header: "رسالة جديدة"
+ cancel: "إلغاء"
notifications:
+ chat_invitation: "دعاك للانضمام إلى قناة دردشة"
+ chat_invitation_html: "دعاك %{username} للانضمام إلى قناة دردشة"
+ chat_quoted: "%{username} %{description}"
popup:
+ chat_mention:
+ direct: 'أشار إليك في "%{channel}"'
+ direct_html: 'أشار %{username} إليك في "%{channel}"'
+ other_plain: 'أشار إلى %{identifier} في "%{channel}"'
+ other_html: 'أشار %{username} إلى %{identifier} في "%{channel}"'
+ direct_message_chat_mention:
+ direct: "أشار إليك في دردشة شخصية"
+ direct_html: "أشار %{username} إليك في دردشة شخصية"
+ other_plain: "أشار إلى %{identifier} في دردشة شخصية"
+ other_html: "أشار %{username} إلى %{identifier} في دردشة شخصية"
chat_message: "رسالة دردشة جديدة"
+ chat_quoted: "اقتبس %{username} رسالة الدردشة الخاصة بك"
titles:
chat_mention: "إشارة في الدردشة"
chat_invitation: "دعوة الدردشة"
+ chat_quoted: "تم اقتباس الدردشة"
action_codes:
chat:
enabled: 'فعَّل %{who} في %{when}'
@@ -251,6 +460,8 @@ ar:
label: المرسل
description: يتم ضبطه افتراضيًا على النظام
review:
+ transcript:
+ view: "عرض نص الرسائل السابقة"
types:
reviewable_chat_message:
title: "رسالة دردشة تم الإبلاغ عنها"
@@ -258,3 +469,34 @@ ar:
keyboard_shortcuts_help:
chat:
title: "الدردشة"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} تبديل القناة"
+ open_quick_channel_selector: "%{shortcut} فتح محدِّد قناة سريع"
+ open_insert_link_modal: "%{shortcut} إدراج رابط تشعبي (أداة الإنشاء فقط)"
+ composer_bold: "%{shortcut} غامق (أداة الإنشاء فقط)"
+ composer_italic: "%{shortcut} مائل (أداة الإنشاء فقط)"
+ composer_code: "%{shortcut} رمز برمجي (أداة الإنشاء فقط)"
+ drawer_open: "%{shortcut} فتح درج الدردشة"
+ drawer_close: "%{shortcut} إغلاق درج الدردشة"
+ topic_statuses:
+ chat:
+ help: "الدردشة مفعَّلة لهذا الموضوع"
+ user:
+ allow_private_messages: "السماح للمستخدمين الآخرين بإرسال رسائل شخصية إليَّ ورسائل مباشرة في الدردشة"
+ muted_users_instructions: "منع كل الإشعارات والرسائل الشخصية والرسائل المباشرة في الدردشة من هؤلاء المستخدمين."
+ allowed_pm_users_instructions: "اسمح فقط بالرسائل الشخصية أو الرسائل المباشرة في الدردشة من هؤلاء المستخدمين."
+ allow_private_messages_from_specific_users: "السماح للمستخدمين المحدَّدين فقط بإرسال رسائل شخصية إليَّ أو رسائل مباشرة في الدردشة"
+ ignored_users_instructions: "منع كل المنشورات والرسائل والإشعارات والرسائل الشخصية والرسائل المباشرة في الدردشة من هؤلاء المستخدمين."
+ user_menu:
+ no_chat_notifications_title: "ليس لديك أي إشعارات دردشة حتى الآن"
+ no_chat_notifications_body: >
+ سيتم إرسال إشعار إليك في هذه اللوحة عندما يراسلك أحدهم مباشرةً أو يشير إليك @mention في الدردشة. سيتم أيضًا إرسال الإشعارات إلى بريدك الإلكتروني في حال عدم تسجيلك الدخول لفترة من الوقت. Das Archivieren eines Kanals versetzt ihn in den schreibgeschützten Modus und verschiebt alle Nachrichten aus dem Kanal in ein neues oder vorhandenes Thema. Es können keine neuen Nachrichten gesendet und keine bestehenden Nachrichten bearbeitet oder gelöscht werden.
Möchtest du den Kanal %{channelTitle} wirklich archivieren?
" + process_started: "Der Archivierungsprozess hat begonnen. Dieser Modal-Dialog wird in Kürze geschlossen und du erhältst eine persönliche Nachricht, wenn der Archivierungsvorgang abgeschlossen ist." + retry: "Erneut versuchen" + channel_open: + title: "Kanal öffnen" + instructions: "Wenn du den Kanal wieder öffnest, können alle Benutzer Nachrichten senden und ihre bestehenden Nachrichten bearbeiten." + channel_close: + title: "Kanal schließen" + instructions: "Das Schließen des Kanals verhindert, dass Nicht-Teammitglieder neue Nachrichten senden oder bestehende Nachrichten bearbeiten können. Bist du sicher, dass du diesen Kanal schließen möchtest?" + channel_delete: + title: "Kanal löschen" + instructions: "Löscht den Kanal %{name} und den Chat-Verlauf. Alle Nachrichten und zugehörigen Daten wie Reaktionen und Uploads werden dauerhaft gelöscht. Wenn du den Kanalverlauf beibehalten und den Kanal nur außer Betrieb nehmen möchtest, kannst du ihn stattdessen archivieren.
Möchtest du den Kanal wirklich dauerhaft löschen? Gib zur Bestätigung den Namen des Kanals in das Feld unten ein.
" + confirm: "Ich verstehe die Konsequenzen und möchte den Kanal löschen" + confirm_channel_name: "Kanalnamen eingeben" + process_started: "Der Löschvorgang für den Kanal hat begonnen. Dieser Modal-Dialog wird in Kürze geschlossen und du wirst den gelöschten Kanal nirgendwo mehr sehen." channels_list_popup: browse: "Kanäle durchsuchen" + create: "Neuer Kanal" click_to_join: "Klicke hier, um die verfügbaren Kanäle zu sehen." close: "Schließen" collapse: "Chat-Bereich ausblenden" confirm_flag: "Bist du sicher, dass du die Nachricht von %{username} markieren möchtest?" deleted: "Eine Nachricht wurde gelöscht. [Anzeigen]" + hidden: "Eine Nachricht wurde ausgeblendet. [Anzeigen]" delete: "Löschen" edited: "bearbeitet" + muted: "stummgeschaltet" + joined: "beigetreten" empty_state: + direct_message_cta: "Persönlichen Chat beginnen" direct_message: "Du kannst auch einen persönlichen Chat mit einem oder mehreren Benutzern beginnen." + title: "Keine Kanäle gefunden" email_frequency: + description: "Wir schicken dir nur dann eine E-Mail, wenn wir dich in den letzten 15 Minuten nicht gesehen haben." never: "Niemals" + title: "E-Mail-Benachrichtigungen" + when_away: "Nur bei Abwesenheit" enable: "Chat aktivieren" flag: "Melden" + emoji: "Emoji einfügen" flagged: "Diese Nachricht wurde zur Überprüfung markiert" invalid_access: "Du bist nicht befugt, diesen Chat-Kanal anzuzeigen" + invitation_notification: "%{username} hat dich eingeladen, einem Chat-Kanal beizutreten" in_reply_to: "Als Antwort auf" heading: "Chat" join: "Beitreten" @@ -56,25 +112,45 @@ de: without_membership: one: "%{usernames} ist diesem Kanal nicht beigetreten." other: "%{usernames} sind diesem Kanal nicht beigetreten." + aria_roles: + header: "Chat-Kopfzeile" + composer: "Chat-Composer" + channels_list: "Liste der Chat-Kanäle" no_public_channels: "Du bist keinem Kanal beigetreten." only_chat_push_notifications: title: "Nur Chat-Push-Benachrichtigungen senden" description: "Alle Nicht-Chat-Push-Benachrichtigungen blockieren und nicht senden" + ignore_channel_wide_mention: + title: "Kanalweite Erwähnungen ignorieren" + description: "Keine Benachrichtigungen für kanalweite Erwähnungen senden (@here und @all)" open: "Chat öffnen" open_full_page: "Vollbild-Chat öffnen" + close_full_page: "Vollbild-Chat schließen" open_message: "Nachricht im Chat öffnen" placeholder_self: "Etwas notieren" placeholder_others: "Chat mit %{messageRecipient}" + placeholder_new_message_disallowed: "Der Kanal ist %{status}, du kannst im Moment keine neuen Nachrichten senden." + placeholder_silenced: "Du kannst derzeit keine Nachrichten senden." + placeholder_start_conversation: Beginne eine Unterhaltung mit %{usernames} remove_upload: "Datei löschen" react: "Mit Emoji reagieren" reply: "Antworten" edit: "Bearbeiten" copy_link: "Link kopieren" rebake_message: "HTML neu erstellen" + retry_staged_message: + title: "Netzwerkfehler" + action: "Erneut senden?" + unreliable_network: "Das Netzwerk ist unzuverlässig, das Senden von Nachrichten und das Speichern von Entwürfen funktioniert möglicherweise nicht" + bookmark_message: "Lesezeichen" + bookmark_message_edit: "Lesezeichen bearbeiten" restore: "Gelöschte Nachricht wiederherstellen" save: "Speichern" select: "Auswählen" + silence: "Benutzer stummschalten" + return_to_list: "Zurück zur Kanalliste" scroll_to_bottom: "Nach unten scrollen" + scroll_to_new_messages: "Neue Nachrichten anzeigen" sound: title: "Desktop-Chat-Benachrichtigungston" sounds: @@ -89,23 +165,82 @@ de: other: "%{count} Dateien" you_flagged: "Du hast diese Nachricht markiert" exit: "zurück" + channel_status: + read_only_header: "Kanal ist schreibgeschützt" + read_only: "Schreibgeschützt" + archived_header: "Kanal ist archiviert" + archived: "Archiviert" + archive_failed: "Archivieren des Kanals fehlgeschlagen. %{completed}/%{total} Nachrichten wurden im Zielthema archiviert. Drücke auf „Erneut versuchen“, um zu versuchen, die Archivierung abzuschließen." + archive_completed: "Archivthema anzeigen" + closed_header: "Kanal ist geschlossen" + closed: "Geschlossen" + open_header: "Kanal ist offen" + open: "Offen" browse: title: Kanäle + filter_all: Alle + filter_open: Offen + filter_closed: Geschlossen + filter_archived: Archiviert + filter_input_placeholder: Kanal nach Namen suchen + chat_message_separator: + today: Heute + yesterday: Gestern + members_view: + filter_placeholder: Mitglieder finden about_view: + associated_topic: Verknüpftes Thema + associated_category: Verknüpfte Kategorie + title: Titel description: Beschreibung channel_info: + back_to_all_channels: "Alle Kanäle" back_to_channel: "Zurück" + tabs: + about: Über + members: Mitglieder + settings: Einstellungen + channel_edit_title_modal: + title: Titel bearbeiten + input_placeholder: Titel hinzufügen + description: Gib deinem Kanal einen kurzen aussagekräftigen Titel + channel_edit_description_modal: + title: Beschreibung bearbeiten + input_placeholder: Beschreibung hinzufügen + description: Sag den Leuten, worum es in diesem Kanal geht + direct_message_creator: + title: Neue Nachricht + prefix: "An:" + no_results: Keine Ergebnisse + selected_user_title: "%{username} abwählen" channel_selector: title: "Zum Kanal springen" no_channels: "Keine Kanäle entsprechen deiner Suche" + channel: + no_memberships: Dieser Kanal hat keine Mitglieder + no_memberships_found: Keine Mitglieder gefunden + memberships_count: + one: "%{count} Mitglied" + other: "%{count} Mitglieder" create_channel: + auto_join_users: + public_category_warning: "%{category} ist eine öffentliche Kategorie. Alle kürzlich aktiven Benutzer automatisch zu diesem Kanal hinzufügen?" + warning_groups: + one: Automatisch %{members_count} Benutzer von %{group} hinzufügen? + other: Automatisch %{members_count} Benutzer von %{group} und %{group_2} hinzufügen? + warning_multiple_groups: Automatisch %{members_count} Benutzer von %{group_1} und %{count} anderen hinzufügen? choose_category: label: "Kategorie auswählen" none: "eine auswählen …" default_hint: Verwalte den Zugang, indem du die Sicherheitseinstellungen für %{category} besuchst + hint_groups: + one: Benutzer in %{hint} haben gemäß den Sicherheitseinstellungen Zugriff auf diesen Kanal + other: Benutzer in %{hint} und %{hint_2} haben gemäß den Sicherheitseinstellungen Zugriff auf diesen Kanal + hint_multiple_groups: Benutzer in %{hint_1} und %{count} anderen Gruppen haben gemäß den Sicherheitseinstellungen Zugriff auf diesen Kanal create: "Kanal erstellen" description: "Beschreibung (optional)" name: "Kanalname" + title: "Neuer Kanal" type: "Typ" types: category: "Kategorie" @@ -120,13 +255,23 @@ de: you_others_and_more: "Du, %{usernames} und %{more} andere haben reagiert mit :%{emoji}:" composer: toggle_toolbar: "Symbolleiste umschalten" + italic_text: "hervorgehobener Text" + bold_text: "fett gedruckter Text" + code_text: "Code-Text" + quote: + original_channel: 'Ursprünglich gesendet in %{channel}' + copy_success: "Chat-Zitat in die Zwischenablage kopiert" notification_levels: never: "Niemals" mention: "Nur für Erwähnungen" always: "Für alle Aktivitäten" settings: + enable_auto_join_users: "Automatisch alle kürzlich aktiven Benutzer hinzufügen" + disable_auto_join_users: "Benutzer nicht mehr automatisch hinzufügen" + auto_join_users_warning: "Jeder Benutzer, der kein Mitglied dieses Kanals ist und Zugriff auf die Kategorie %{category} hat, wird beitreten. Bist du dir sicher?" desktop_notification_level: "Desktop-Benachrichtigungen" follow: "Beitreten" + followed: "Beigetreten" mobile_notification_level: "Mobile Push-Benachrichtigungen" mute: "Kanal stummschalten" muted_on: "An" @@ -143,6 +288,7 @@ de: new: "Neuer persönlicher Chat" create: "Los" leave: "Diesen persönlichen Chat verlassen" + cannot_create: "Du kannst leider keine Direktnachrichten senden." incoming_webhooks: back: "Zurück" channel_placeholder: "Kanal auswählen" @@ -165,10 +311,15 @@ de: system: "System" title: "Eingehende Webhooks" url: "URL" + url_instructions: "Diese URL enthält einen geheimen Wert – bewahre ihn sicher auf." username: "Benutzername" username_instructions: "Benutzername des Bots, der etwas im Kanal veröffentlicht. Standardmäßig „System“, wenn das Feld leer gelassen wird." + instructions: "Eingehende Webhooks können von externen Systemen verwendet werden, um Nachrichten als Bot-Benutzer über den Endpunkt/hooks/:key in einem bestimmten Chat-Kanal zu posten. Die Payload besteht aus einem einzigen text-Parameter, der auf 2000 Zeichen begrenzt ist.text-Parameter und extrahieren Links und Erwähnungen basierend auf dem Format unter https://api.slack.com/reference/surfaces/formatting, aber dazu muss der Endpunkt /hooks/:key/slack verwendet werden."
selection:
cancel: "Abbrechen"
+ quote_selection: "Zitat im Thema"
+ copy: "Kopieren"
+ move_selection_to_channel: "In Kanal verschieben"
error: "Beim Verschieben der Chat-Nachrichten ist ein Fehler aufgetreten"
title: "Chat in Thema verschieben"
new_topic:
@@ -176,11 +327,13 @@ de:
instructions:
one: "Du bist dabei, ein neues Thema zu erstellen und es mit der ausgewählten Chat-Nachricht zu füllen."
other: "Du bist dabei, ein neues Thema zu erstellen und es mit den %{count} ausgewählten Chat-Nachrichten zu füllen."
+ instructions_channel_archive: "Du bist dabei, ein neues Thema zu erstellen und die Kanalnachrichten darin zu archivieren."
existing_topic:
title: "In bestehendes Thema verschieben"
instructions:
one: "Bitte wähle das Thema aus, in das du die Chat-Nachricht verschieben möchtest."
other: "Bitte wähle das Thema aus, in das du die %{count} Chat-Nachrichten verschieben möchtest."
+ instructions_channel_archive: "Bitte wähle das Thema aus, in dem du die Kanalnachrichten archivieren möchtest."
new_message:
title: "In neue Nachricht verschieben"
instructions:
@@ -196,12 +349,51 @@ de:
public: "Der Kanalverlauf wird für %{days} Tage gespeichert."
dm: "Der persönliche Chatverlauf wird für %{days} Tage gespeichert."
topic_button_title: "Chat"
+ flags:
+ off_topic: "Diese Nachricht ist für die aktuelle Diskussion im Sinne des Kanaltitels nicht relevant und sollte wahrscheinlich an eine andere Stelle verschoben werden."
+ inappropriate: "Diese Nachricht enthält Inhalte, die eine vernünftige Person als anstößig, beleidigend oder als Verstoß gegen unsere Community-Richtlinien ansehen würde."
+ spam: "Diese Nachricht ist Werbung oder Vandalismus. Sie ist nicht nützlich oder relevant für den aktuellen Kanal."
+ notify_user: "Ich möchte mit dieser Person direkt und persönlich über ihre Nachricht sprechen."
+ notify_moderators: "Diese Nachricht erfordert die Aufmerksamkeit des Teams aus einem anderen, oben nicht aufgeführten Grund."
+ flagging:
+ action: "Nachricht markieren"
+ emoji_picker:
+ favorites: "Häufig verwendet"
+ smileys_&_emotion: "Smileys und Emotionen"
+ objects: "Objekte"
+ people_&_body: "Mensch und Körper"
+ travel_&_places: "Reisen und Orte"
+ animals_&_nature: "Tiere und Natur"
+ food_&_drink: "Essen und Trinken"
+ activities: "Aktivitäten"
+ flags: "Flaggen"
+ symbols: "Symbole"
+ search_placeholder: "Nach Emoji-Namen und -Alias suchen …"
+ no_results: "Keine Ergebnisse"
+ draft_channel_screen:
+ header: "Neue Nachricht"
+ cancel: "Abbrechen"
notifications:
+ chat_invitation: "hat dich eingeladen, einem Chat-Kanal beizutreten"
+ chat_invitation_html: "%{username} hat dich eingeladen, einem Chat-Kanal beizutreten"
+ chat_quoted: "%{username} %{description}"
popup:
+ chat_mention:
+ direct: 'hat dich in „%{channel}“ erwähnt'
+ direct_html: '%{username} hat dich in „%{channel}“ erwähnt'
+ other_plain: 'hat %{identifier} in „%{channel}“ erwähnt'
+ other_html: '%{username} hat %{identifier} in „%{channel}“ erwähnt'
+ direct_message_chat_mention:
+ direct: "hat dich im persönlichen Chat erwähnt"
+ direct_html: "%{username} hat dich im persönlichen Chat erwähnt"
+ other_plain: "hat %{identifier} im persönlichen Chat erwähnt"
+ other_html: "%{username} hat %{identifier} im persönlichen Chat erwähnt"
chat_message: "Neue Chat-Nachricht"
+ chat_quoted: "%{username} hat deine Chat-Nachricht zitiert"
titles:
chat_mention: "Chat-Erwähnung"
chat_invitation: "Chat-Einladung"
+ chat_quoted: "Chat zitiert"
action_codes:
chat:
enabled: '%{who} hat aktiviert %{when}'
@@ -219,6 +411,8 @@ de:
label: Absender
description: Standardmäßig System
review:
+ transcript:
+ view: "Transkript früherer Nachrichten anzeigen"
types:
reviewable_chat_message:
title: "Chat-Nachricht markiert"
@@ -226,3 +420,30 @@ de:
keyboard_shortcuts_help:
chat:
title: "Chat"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} Kanal wechseln"
+ open_quick_channel_selector: "%{shortcut} Schnellauswahl für Kanäle öffnen"
+ open_insert_link_modal: "%{shortcut} Hyperlink einfügen (nur Composer)"
+ composer_bold: "%{shortcut} Fett (nur Composer)"
+ composer_italic: "%{shortcut} Kursiv (nur Composer)"
+ composer_code: "%{shortcut} Code (nur Composer)"
+ drawer_open: "%{shortcut} Chat-Bereich öffnen"
+ drawer_close: "%{shortcut} Chat-Bereich schließen"
+ topic_statuses:
+ chat:
+ help: "Der Chat ist für dieses Thema aktiviert"
+ user:
+ allow_private_messages: "Anderen Benutzern erlauben, mir persönliche Nachrichten und Chat-Direktnachrichten zu senden"
+ muted_users_instructions: "Alle Benachrichtigungen, persönlichen Nachrichten und Chat-Direktnachrichten von diesen Benutzern unterdrücken."
+ allowed_pm_users_instructions: "Nur persönliche Nachrichten oder Chat-Direktnachrichten von diesen Benutzern erlauben."
+ allow_private_messages_from_specific_users: "Nur bestimmten Benutzern erlauben, mir persönliche Nachrichten oder Chat-Direktnachrichten zu senden"
+ ignored_users_instructions: "Alle Beiträge, Nachrichten, Benachrichtigungen, persönlichen Nachrichten und Chat-Direktnachrichten von diesen Benutzern unterdrücken."
+ user_menu:
+ no_chat_notifications_title: "Du hast noch keine Chat-Benachrichtigungen"
+ no_chat_notifications_body: >
+ In diesem Bereich wirst du benachrichtigt, wenn dir jemand eine Direktnachricht sendet oder dich im Chat per @ erwähnt. Außerdem werden Benachrichtigungen an deine E-Mail-Adresse geschickt, wenn du dich eine Weile nicht angemeldet hast. Archiving a channel puts it into read-only mode and moves all messages from the channel into a new or existing topic. No new messages can be sent, and no existing messages can be edited or deleted.
Are you sure you want to archive the %{channelTitle} channel?
" @@ -371,8 +378,6 @@ en: public: "Channel history is retained for %{days} days." dm: "Personal chat history is retained for %{days} days." - topic_button_title: "Chat" - flags: off_topic: "This message is not relevant to the current discussion as defined by the channel title, and should probably be moved elsewhere." inappropriate: "This message contains content that a reasonable person would consider offensive, abusive, or a violation of our community guidelines." diff --git a/plugins/chat/config/locales/client.en_GB.yml b/plugins/chat/config/locales/client.en_GB.yml index 2d4fa180ec..4e05263133 100644 --- a/plugins/chat/config/locales/client.en_GB.yml +++ b/plugins/chat/config/locales/client.en_GB.yml @@ -5,3 +5,7 @@ # https://translate.discourse.org/ en_GB: + js: + chat: + composer: + italic_text: "emphasised text" diff --git a/plugins/chat/config/locales/client.es.yml b/plugins/chat/config/locales/client.es.yml index 8d10b37422..bd83e3fe72 100644 --- a/plugins/chat/config/locales/client.es.yml +++ b/plugins/chat/config/locales/client.es.yml @@ -6,6 +6,21 @@ es: js: + admin: + logs: + staff_actions: + actions: + chat_channel_status_change: "Se ha cambiado el estado del canal de chat" + chat_channel_delete: "Canal de chat eliminado" + api: + scopes: + descriptions: + chat: + create_message: "Crea un mensaje de chat en un canal especificado." + about: + chat_messages_count: "Mensajes de chat" + chat_channels_count: "Canales de chat" + chat_users_count: "Usuarios del chat" chat: dates: time_tiny: "h:mm" @@ -17,29 +32,70 @@ es: cancel: "Cancelar" cancel_reply: "Cancelar respuesta" chat_channels: "Canales" + browse_all_channels: "Buscar todos los canales" + move_to_channel: + title: "Mover los mensajes al canal" + instructions: + one: "Estás moviendo %{count} mensaje. Selecciona un canal de destino. Se creará un mensaje marcador de posición en el canal %{channelTitle} para indicar que se ha movido este mensaje." + other: "Estás moviendo %{count} mensajes. Selecciona un canal de destino. Se creará un mensaje marcador de posición en el canal %{channelTitle} para indicar que se han movido estos mensajes." + confirm_move: "Mover mensajes" channel_settings: + title: "Ajustes del canal" edit: "Editar" + add: "Añade" + close_channel: "Cerrar canal" + open_channel: "Abrir canal" + archive_channel: "Archivar canal" + delete_channel: "Eliminar canal" join_channel: "Unirse al canal" leave_channel: "Abandonar canal" join: "Unirse" leave: "Abandonar" + channel_archive: + title: "Archivar canal" + instructions: "Archivar un canal lo pone en modo de solo lectura y mueve todos los mensajes del canal a un tema nuevo o existente. No se pueden enviar mensajes nuevos y no se pueden editar ni eliminar mensajes existentes.
¿Seguro que desea archivar el canal %{channelTitle} ?
" + process_started: "El proceso de archivado ha comenzado. Este modal se cerrará en breve, y recibirás un mensaje personal cuando el proceso de archivo haya finalizado." + retry: "Reintentar" + channel_open: + title: "Abrir canal" + instructions: "Reabre el canal, todos los usuarios podrán enviar mensajes y editar los existentes." + channel_close: + title: "Cerrar canal" + instructions: "El cierre del canal impide que los usuarios que no son del personal envíen nuevos mensajes o editen los existentes. ¿Seguro que quieres cerrar este canal?" + channel_delete: + title: "Eliminar canal" + instructions: "Elimina el canal de %{name} y el historial de chat. Todos los mensajes y datos relacionados, como las reacciones y las subidas, se eliminarán permanentemente. Si quieres conservar el historial del canal y descomponerlo, quizá quieras archivar el canal en su lugar.
¿Seguro que quieres eliminar permanentemente el canal? Para confirmarlo, escribe el nombre del canal en la casilla de abajo.
" + confirm: "Comprendo las consecuencias, eliminar el canal" + confirm_channel_name: "Introduce el nombre del canal" + process_started: "Se ha iniciado el proceso de eliminación del canal. Este modal se cerrará en breve, ya no verás el canal eliminado en ninguna parte." channels_list_popup: browse: "Examinar canales" + create: "Nuevo canal" click_to_join: "Haz clic aquí para ver los canales disponibles." close: "Cerrar" collapse: "Contraer contenedor del chat" confirm_flag: "¿Seguro que quieres denunciar el mensaje de %{username}?" deleted: "Se eliminó un mensaje. [view]" + hidden: "Se ha ocultado un mensaje. [ver]" delete: "Eliminar" edited: "editado" + muted: "silenciado" + joined: "se unió" empty_state: + direct_message_cta: "Iniciar un chat personal" direct_message: "También puedes iniciar un chat personal con uno o más usuarios." + title: "No se han encontrado canales" email_frequency: + description: "Solo te enviaremos un correo electrónico si no te hemos visto en los últimos 15 minutos." never: "Nunca" + title: "Notificaciones por correo electrónico" + when_away: "Solo cuando estés ausente" enable: "Habilitar chat" flag: "Denunciar" + emoji: "Insertar emoji" flagged: "Este mensaje ha sido denunciado y se someterá a revisión" invalid_access: "No tienes acceso para ver este canal de chat" + invitation_notification: "%{username} te ha invitado a unirte a un canal de chat" in_reply_to: "En respuesta a" heading: "Chat" join: "Unirse" @@ -56,25 +112,45 @@ es: without_membership: one: "%{usernames} no se ha unido a este canal." other: "%{usernames} no se han unido a este canal." + aria_roles: + header: "Encabezado del chat" + composer: "Compositor del chat" + channels_list: "Lista de canales de chat" no_public_channels: "No te has unido a ningún canal." only_chat_push_notifications: title: "Enviar solo notificaciones de chat" description: "Bloquear el envío de todas las notificaciones que no sean de chat" + ignore_channel_wide_mention: + title: "Ignorar las menciones de todo el canal" + description: "No enviar notificaciones para las menciones de todo el canal (@aquí y @todos)" open: "Abrir chat" open_full_page: "Abrir chat en pantalla completa" + close_full_page: "Cerrar el chat a pantalla completa" open_message: "Abrir mensaje en el chat" placeholder_self: "Anota algo" placeholder_others: "Chatear con %{messageRecipient}" + placeholder_new_message_disallowed: "El canal es %{status}, no puedes enviar nuevos mensajes en este momento." + placeholder_silenced: "No puedes enviar mensajes en este momento." + placeholder_start_conversation: Inicia una conversación con %{usernames} remove_upload: "Eliminar archivo" react: "Reaccionar con emojis" reply: "Responder" edit: "Editar" copy_link: "Copiar enlace" rebake_message: "Reconstruir HTML" + retry_staged_message: + title: "Error de red" + action: "¿Enviar de nuevo?" + unreliable_network: "La red no es fiable, el envío de mensajes y el guardado de borradores pueden no funcionar" + bookmark_message: "Marcador" + bookmark_message_edit: "Editar marcador" restore: "Restaurar mensaje eliminado" save: "Guardar" select: "Seleccionar" + silence: "Silencio usuario" + return_to_list: "Volver a la lista de canales" scroll_to_bottom: "Desplazar hacia abajo" + scroll_to_new_messages: "Ver nuevos mensajes" sound: title: "Sonido de notificación de chat de escritorio" sounds: @@ -89,23 +165,82 @@ es: other: "%{count} archivos" you_flagged: "Has denunciado este mensaje" exit: "atrás" + channel_status: + read_only_header: "El canal es de solo lectura" + read_only: "Solo lectura" + archived_header: "El canal está archivado" + archived: "Archivado" + archive_failed: "El canal de archivo ha fallado. Se han archivado %{completed}/%{total} mensajes en el tema de destino. Pulsa reintentar para intentar completar el archivo." + archive_completed: "Ver el tema del archivo" + closed_header: "El canal está cerrado" + closed: "Cerrado" + open_header: "El canal está abierto" + open: "Abierto" browse: title: Canales + filter_all: Todos + filter_open: Abierto + filter_closed: Cerrado + filter_archived: Archivado + filter_input_placeholder: Busca el canal por su nombre + chat_message_separator: + today: Hoy + yesterday: Ayer + members_view: + filter_placeholder: Buscar miembros about_view: + associated_topic: Tema vinculado + associated_category: Categoría vinculada + title: Título description: Descripción channel_info: + back_to_all_channels: "Todos los canales" back_to_channel: "Atrás" + tabs: + about: Acerca de + members: Miembros + settings: Ajustes + channel_edit_title_modal: + title: Editar título + input_placeholder: Añade un título + description: Pon un título corto y descriptivo a tu canal + channel_edit_description_modal: + title: Editar descripción + input_placeholder: Añade una descripción + description: Cuéntale a la gente de qué se trata este canal + direct_message_creator: + title: Nuevo mensaje + prefix: "Para:" + no_results: No hay resultados + selected_user_title: "Deseleccionar %{username}" channel_selector: title: "Ir al canal" no_channels: "Ningún canal coincide con tu búsqueda" + channel: + no_memberships: Este canal no tiene miembros + no_memberships_found: No se ha encontrado ningún miembro + memberships_count: + one: "%{count} miembro" + other: "%{count} miembros" create_channel: + auto_join_users: + public_category_warning: "%{category} es una categoría pública. ¿Añadir automáticamente a este canal a todos los usuarios activos recientemente?" + warning_groups: + one: '¿Añadir automáticamente %{members_count} usuarios de %{group}?' + other: '¿Añadir automáticamente %{members_count} usuarios de %{group} y %{group_2}?' + warning_multiple_groups: '¿Añadir automáticamente %{members_count} usuarios de %{group_1} y %{count} otros?' choose_category: label: "Elige una categoría" none: "selecciona una..." default_hint: Administra el acceso visitando la %{category} configuración de seguridad + hint_groups: + one: Los usuarios de %{hint} tendrán acceso a este canal según la configuración de seguridad + other: Los usuarios de %{hint} y %{hint_2} tendrán acceso a este canal según la configuración de seguridad + hint_multiple_groups: Los usuarios de %{hint_1} y %{count} otros grupos tendrán acceso a este canal según la configuración de seguridad. create: "Crear canal" description: "Descripción (opcional)" name: "Nombre del canal" + title: "Nuevo canal" type: "Tipo" types: category: "Categoría" @@ -120,13 +255,23 @@ es: you_others_and_more: "Tú, %{usernames} y %{more} personas más reaccionasteis con :%{emoji}:" composer: toggle_toolbar: "Alternar barra de herramientas" + italic_text: "texto enfatizado" + bold_text: "texto fuerte" + code_text: "texto del código" + quote: + original_channel: 'Enviado originalmente en %{channel}' + copy_success: "Cita del chat copiada en el portapapeles" notification_levels: never: "Nunca" mention: "Solo para menciones" always: "Para toda actividad" settings: + enable_auto_join_users: "Añade automáticamente todos los usuarios activos recientemente" + disable_auto_join_users: "Dejar de añadir usuarios automáticamente" + auto_join_users_warning: "Todos los usuarios que no sean miembros de este canal y tengan acceso a la categoría %{category} se unirán. ¿Estás seguro/a?" desktop_notification_level: "Notificaciones de escritorio" follow: "Unirse" + followed: "Se unió" mobile_notification_level: "Notificaciones móviles" mute: "Silenciar canal" muted_on: "Activado" @@ -143,6 +288,7 @@ es: new: "Nuevo chat personal" create: "Ir" leave: "Abandonar este chat personal" + cannot_create: "Lo sentimos, no puedes enviar mensajes directos." incoming_webhooks: back: "Atrás" channel_placeholder: "Selecciona un canal" @@ -165,10 +311,15 @@ es: system: "sistema" title: "Webhooks entrantes" url: "URL" + url_instructions: "Esta URL contiene un valor secreto: mantenlo seguro." username: "Nombre de usuario" username_instructions: "Nombre de usuario del bot que publica en el canal. El valor predeterminado es «sistema» cuando se deja en blanco." + instructions: "Los webhooks entrantes pueden utilizarse por sistemas externos para publicar mensajes en un canal de chat designado como usuario del bot a través del punto final/hooks/:key. La carga útil consiste en un único parámetro texto, que está limitado a 2000 caracteres.texto limitados con formato Slack, extrayendo enlaces y menciones basados en el formato en https://api.slack.com/reference/surfaces/formatting, pero para ello debe utilizarse el punto final /hooks/:key/slack."
selection:
cancel: "Cancelar"
+ quote_selection: "Cita en el Tema"
+ copy: "Copia"
+ move_selection_to_channel: "Pasar al canal"
error: "Se ha producido un error al mover los mensajes de chat"
title: "Mover chat a tema"
new_topic:
@@ -176,11 +327,13 @@ es:
instructions:
one: "Estás a punto de crear un nuevo tema y rellenarlo con el mensaje de chat que has seleccionado."
other: "Estás a punto de crear un nuevo tema y rellenarlo con los %{count} mensajes de chat que has seleccionado."
+ instructions_channel_archive: "Vas a crear un nuevo tema y archivar en él los mensajes del canal."
existing_topic:
title: "Mover a un tema existente"
instructions:
one: "Elige el tema al que quieres mover ese mensaje de chat."
other: "Elige el tema al que quieres mover esos %{count} mensajes de chat."
+ instructions_channel_archive: "Elige el tema en el que quieres archivar los mensajes del canal."
new_message:
title: "Mover a mensaje nuevo"
instructions:
@@ -196,12 +349,51 @@ es:
public: "El historial del canal se conserva durante %{days} días."
dm: "El historial de chat personal se conserva durante %{days} días."
topic_button_title: "Chat"
+ flags:
+ off_topic: "Este mensaje no es relevante para la discusión actual, tal y como se define en el título del canal, y probablemente debería moverse a otro lugar."
+ inappropriate: "Este mensaje tiene un contenido que una persona razonable consideraría ofensivo, abusivo o que viola las directrices de nuestra comunidad."
+ spam: "Este mensaje es un anuncio o vandalismo. No es útil ni relevante para el canal actual."
+ notify_user: "Quiero hablar con esta persona directa y personalmente sobre su mensaje."
+ notify_moderators: "Este mensaje requiere la atención del personal por otra razón no mencionada anteriormente."
+ flagging:
+ action: "Denunciar mensaje"
+ emoji_picker:
+ favorites: "De uso frecuente"
+ smileys_&_emotion: "Sonrisas y emociones"
+ objects: "Objetos"
+ people_&_body: "Personas y cuerpo"
+ travel_&_places: "Viajes y lugares"
+ animals_&_nature: "Animales y naturaleza"
+ food_&_drink: "Comida y bebida"
+ activities: "Actividades"
+ flags: "Denuncias"
+ symbols: "Símbolos"
+ search_placeholder: "Busca por nombre de emoji y alias..."
+ no_results: "No hay resultados"
+ draft_channel_screen:
+ header: "Nuevo mensaje"
+ cancel: "Cancelar"
notifications:
+ chat_invitation: "te invitó a unirte a un canal de chat"
+ chat_invitation_html: "%{username} te ha invitado a unirte a un canal de chat"
+ chat_quoted: "%{username} %{description}"
popup:
+ chat_mention:
+ direct: 'te ha mencionado en «%{channel}»'
+ direct_html: '%{username} te mencionó en «%{channel}»'
+ other_plain: 'mencionó %{identifier} en «%{channel}»'
+ other_html: '%{username} mencionó %{identifier} en «%{channel}»'
+ direct_message_chat_mention:
+ direct: "te mencionó en el chat personal"
+ direct_html: "%{username} te mencionó en el chat personal"
+ other_plain: "mencionó %{identifier} en el chat personal"
+ other_html: "%{username} mencionó %{identifier} en el chat personal"
chat_message: "Nuevo mensaje de chat"
+ chat_quoted: "%{username} citó tu mensaje de chat"
titles:
chat_mention: "Mención de chat"
chat_invitation: "Invitación de chat"
+ chat_quoted: "Chat citado"
action_codes:
chat:
enabled: '%{who} habilitó el %{when}'
@@ -219,6 +411,8 @@ es:
label: Remitente
description: Valores predeterminados del sistema
review:
+ transcript:
+ view: "Ver la transcripción de los mensajes anteriores"
types:
reviewable_chat_message:
title: "Mensaje de chat denunciado"
@@ -226,3 +420,30 @@ es:
keyboard_shortcuts_help:
chat:
title: "Chat"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} Cambiar de canal"
+ open_quick_channel_selector: "%{shortcut} Abrir selector rápido de canales"
+ open_insert_link_modal: "%{shortcut} Insertar hipervínculo (solo compositor)"
+ composer_bold: "%{shortcut} Negrita (solo compositor)"
+ composer_italic: "%{shortcut} Cursiva (solo compositor)"
+ composer_code: "%{shortcut} Código (solo compositor)"
+ drawer_open: "%{shortcut} Abrir el cajón del chat"
+ drawer_close: "%{shortcut} Cerrar cajón del chat"
+ topic_statuses:
+ chat:
+ help: "El chat está activado para este tema"
+ user:
+ allow_private_messages: "Permitir que otros usuarios me envíen mensajes personales y mensajes directos del chat"
+ muted_users_instructions: "Suprime todas las notificaciones, mensajes personales y mensajes directos del chat de estos usuarios."
+ allowed_pm_users_instructions: "Solo permitir mensajes personales o mensajes directos de estos usuarios."
+ allow_private_messages_from_specific_users: "Permitir solo a determinados usuarios que me envíen mensajes personales o mensajes directos del chat"
+ ignored_users_instructions: "Suprime todas las publicaciones, mensajes, notificaciones, mensajes personales y mensajes directos del chat de estos usuarios."
+ user_menu:
+ no_chat_notifications_title: "Todavía no tienes ninguna notificación del chat"
+ no_chat_notifications_body: >
+ Se te notificará en este panel cuando alguien te envíe un mensaje directo o te @mencione en el chat. También se enviarán notificaciones a tu correo electrónico cuando no te hayas conectado durante un tiempo. Kanavan arkistointi asettaa sen vain luku -tilaan ja siirtää kaikki kanavan viestit uuteen tai olemassa olevaan ketjuun. Uusia viestejä ei voi lähettää, eikä olemassa olevia viestejä voi muokata tai poistaa.
Oletko varma, että haluat arkistoida kanavan %{channelTitle}?
" + process_started: "Arkistointiprosessi on alkanut. Tämä modaalinen ikkuna sulkeutuu pian, ja saat henkilökohtaisen viestin, kun arkistointiprosessi on valmis." + retry: "Yritä uudelleen" + channel_open: + title: "Avaa kanava" + instructions: "Avaa kanavan uudelleen; kaikki käyttäjät voivat lähettää viestejä ja muokata olemassa olevia viestejään." + channel_close: + title: "Sulje kanava" + instructions: "Kanavan sulkeminen estää muita kuin henkilökunnan käyttäjiä lähettämästä uusia viestejä tai muokkaamasta olemassa olevia viestejä. Haluatko varmasti sulkea tämän kanavan?" + channel_delete: + title: "Poista kanava" + instructions: "Poistaa kanavan %{name} ja chat-historian. Kaikki viestit ja niihin liittyvät tiedot, kuten reaktiot ja lataukset, poistetaan pysyvästi. Jos haluat säilyttää kanavan historian ja poistaa sen käytöstä, voit sen sijaan arkistoida kanavan.
Haluatko varmasti poistaa kanavan psyyvästi? Vahvista kirjoittamalla kanavan nimi alla olevaan ruutuun.
" + confirm: "Ymmärrän seuraukset, poista kanava" + confirm_channel_name: "Anna kanavan nimi" + process_started: "Kanavan poistoprosessi on alkanut. Tämä modaalinen ikkuna sulkeutuu pian, etkä näe enää poistettua kanavaa missään." channels_list_popup: browse: "Selaa kanavia" + create: "Uusi kanava" click_to_join: "Näytä saatavilla olevat kanavat napsauttamalla tätä." close: "Sulje" collapse: "Tiivistä chat-laatikko" confirm_flag: "Haluatko varmasti liputtaa käyttäjän %{username} viestin?" deleted: "Viesti poistettiin. [näytä]" + hidden: "Viesti piilotettiin. [näytä]" delete: "Poista" edited: "muokattu" + muted: "vaimennettu" + joined: "liittyi" empty_state: + direct_message_cta: "Aloita henkilökohtainen chat" direct_message: "Voit myös aloittaa henkilökohtaisen chatin yhden tai useamman käyttäjän kanssa." + title: "Kanavia ei löytynyt" email_frequency: + description: "Lähetämme sinulle sähköpostia vain, jos emme ole nähneet sinua viimeisen 15 minuutin aikana." never: "Ei koskaan" + title: "Sähköposti-ilmoitukset" + when_away: "Vain poissa ollessa" enable: "Ota chat käyttöön" flag: "Liputa" + emoji: "Lisää emoji" flagged: "Tämä viesti on liputettu käsiteltäväksi" invalid_access: "Sinulla ei ole tämän chat-kanavan katseluoikeutta" + invitation_notification: "%{username} kutsui sinut liittymään chat-kanavalle" in_reply_to: "Vastauksena:" heading: "Chat" join: "Liity" @@ -56,25 +112,45 @@ fi: without_membership: one: "%{usernames} ei ole liittynyt tälle kanavalle." other: "%{usernames} eivät ole liittyneet tälle kanavalle." + aria_roles: + header: "Chatin ylätunniste" + composer: "Chatin tekstieditori" + channels_list: "Chat-kanavien luettelo" no_public_channels: "Et ole liittynyt kanaville." only_chat_push_notifications: title: "Lähetä vain chatin push-ilmoituksia" description: "Estä kaikkien muiden kuin chatin push-ilmoitusten lähettäminen" + ignore_channel_wide_mention: + title: "Ohita kanavanlaajuiset maininnat" + description: "Älä lähetä ilmoituksia kanavanlaajuisista maininnoista (@here ja @all)" open: "Avaa chat" open_full_page: "Avaa koko näytön chat" + close_full_page: "Sulje koko näytön chat" open_message: "Avaa viesti chatissa" placeholder_self: "Kirjoita jotakin" placeholder_others: "Chat-keskustelu käyttäjän %{messageRecipient} kanssa" + placeholder_new_message_disallowed: "Kanava on %{status}, et voi lähettää uusia viestejä juuri nyt." + placeholder_silenced: "Et voi lähettää viestejä tällä hetkellä." + placeholder_start_conversation: Aloita keskustelu käyttäjän %{usernames} kanssa remove_upload: "Poista tiedosto" react: "Reagoi emojilla" reply: "Vastaa" edit: "Muokkaa" copy_link: "Kopioi linkki" rebake_message: "Kokoa HTML uudelleen" + retry_staged_message: + title: "Verkkovirhe" + action: "Lähetetäänkö uudelleen?" + unreliable_network: "Verkko on epäluotettava, viestien lähettäminen ja luonnoksen tallentaminen ei ehkä toimi" + bookmark_message: "Kirjanmerkki" + bookmark_message_edit: "Muokkaa kirjanmerkkiä" restore: "Palauta poistettu viesti" save: "Tallenna" select: "Valitse" + silence: "Hiljennä käyttäjä" + return_to_list: "Palaa kanavaluetteloon" scroll_to_bottom: "Vieritä alas" + scroll_to_new_messages: "Katso uudet viestit" sound: title: "Työpöytälaitteen chat-ilmoitusääni" sounds: @@ -89,23 +165,82 @@ fi: other: "%{count} tiedostoa" you_flagged: "Liputit tämän viestin" exit: "takaisin" + channel_status: + read_only_header: "Kanava on vain luettavissa" + read_only: "Vain luku" + archived_header: "Kanava on arkistoitu" + archived: "Arkistoitu" + archive_failed: "Kanavan arkistointi epäonnistui. %{completed}/%{total} viestiä on arkistoitu kohdeketjuun. Yritä arkistoinnin viimeistelyä uudelleen painamalla Yritä uudelleen -painiketta." + archive_completed: "Katso arkistoketju" + closed_header: "Kanava on suljettu" + closed: "Suljettu" + open_header: "Kanava on avoinna" + open: "Avoinna" browse: title: Kanavat + filter_all: Kaikki + filter_open: Avattu + filter_closed: Suljettu + filter_archived: Arkistoitu + filter_input_placeholder: Hae kanavaa nimellä + chat_message_separator: + today: Tänään + yesterday: Eilen + members_view: + filter_placeholder: Etsi jäseniä about_view: + associated_topic: Linkitetty ketju + associated_category: Linkitetty alue + title: Otsikko description: Kuvaus channel_info: + back_to_all_channels: "Kaikki kanavat" back_to_channel: "Takaisin" + tabs: + about: Tietoja + members: Jäsenet + settings: Asetukset + channel_edit_title_modal: + title: Muokkaa otsikkoa + input_placeholder: Lisää otsikko + description: Anna kanavallesi lyhyt kuvaava otsikko + channel_edit_description_modal: + title: Muokkaa kuvausta + input_placeholder: Lisää kuvaus + description: Kerro ihmisille, mistä tässä kanavassa on kyse + direct_message_creator: + title: Uusi viesti + prefix: "Vastaanottaja:" + no_results: Ei tuloksia + selected_user_title: "Poista käyttäjän %{username} valinta" channel_selector: title: "Siirry kanavalle" no_channels: "Hakuasi vastaavia kanavia ei ole" + channel: + no_memberships: Tällä kanavalla ei ole jäseniä + no_memberships_found: Jäseniä ei löytynyt + memberships_count: + one: "%{count} jäsen" + other: "%{count} jäsentä" create_channel: + auto_join_users: + public_category_warning: "%{category} on julkinen alue. Lisätäänkö kaikki hiljattain aktiiviset käyttäjät automaattisesti tälle kanavalle?" + warning_groups: + one: Lisätäänkö %{members_count} käyttäjää automaattisesti ryhmästä %{group}? + other: Lisätäänkö %{members_count} käyttäjää automaattisesti ryhmistä %{group} ja %{group_2}? + warning_multiple_groups: Lisätäänkö %{members_count} käyttäjää automaattisesti ryhmästä %{group_1} ja %{count} muusta? choose_category: label: "Valitse alue" none: "valitse yksi..." default_hint: Hallinnoi käyttöoikeutta alueen %{category} turvallisuusasetuksissa + hint_groups: + one: Ryhmän %{hint} käyttäjillä on automaattisesti pääsy tälle kanavalle turvallisuusasetusten mukaan + other: Ryhmien %{hint} ja %{hint_2} käyttäjillä on automaattisesti pääsy tälle kanavalle turvallisuusasetusten mukaan + hint_multiple_groups: Ryhmän %{hint_1} ja %{count} muun ryhmän käyttäjillä on automaattisesti pääsy tälle kanavalle turvallisuusasetusten mukaan create: "Luo kanava" description: "Kuvaus (valinnainen)" name: "Kanavan nimi" + title: "Uusi kanava" type: "Tyyppi" types: category: "Alue" @@ -120,15 +255,23 @@ fi: you_others_and_more: "Sinä, %{usernames} ja %{more} muuta reagoivat emojilla :%{emoji}:" composer: toggle_toolbar: "Vaihda työkalupalkki" + italic_text: "korostettu teksti" + bold_text: "lihavoitu teksti" + code_text: "kooditeksti" quote: + original_channel: 'Lähetetty alun perin kanavalla %{channel}' copy_success: "Chat-lainaus kopioitiin leikepöydälle" notification_levels: never: "Ei koskaan" mention: "Vain maininnoissa" always: "Kaikessa toiminnassa" settings: + enable_auto_join_users: "Lisää automaattisesti kaikki hiljattain aktiiviset käyttäjät" + disable_auto_join_users: "Lopeta käyttäjien automaattinen lisääminen" + auto_join_users_warning: "Jokainen käyttäjä, joka ei ole tämän kanavan jäsen ja jolla on pääsy alueelle %{category}, liittyy. Oletko varma?" desktop_notification_level: "Työpöytäilmoitukset" follow: "Liity" + followed: "Liittyi" mobile_notification_level: "Mobiili-push-ilmoitukset" mute: "Mykistä kanava" muted_on: "Käytössä" @@ -145,6 +288,7 @@ fi: new: "Uusi henkilökohtainen chat" create: "Siirry" leave: "Poistu tästä henkilökohtaisesta chatista" + cannot_create: "Valitettavasti et voi lähettää yksityisviestejä." incoming_webhooks: back: "Takaisin" channel_placeholder: "Valitse kanava" @@ -167,10 +311,15 @@ fi: system: "järjestelmä" title: "Saapuvat webhookit" url: "URL" + url_instructions: "Tämä URL-osoite sisältää salaisen arvon – pidä se turvassa." username: "Käyttäjätunnus" username_instructions: "Kanavalle lähettävän botin käyttäjätunnus. Oletus on \"järjestelmä\", kun tämä jätetään tyhjäksi." + instructions: "Ulkoiset järjestelmät voivat käyttää saapuvia webhookeja viestien lähettämiseen määrätylle chat-kanavalle bot-käyttäjänä/hooks/:key-päätepisteen kautta. Hyötykuorma koostuu yhdestä text-parametrista, joka on rajoitettu 2 000 merkkiin.text-parametreja, linkkien ja mainintojen poimimista osoitteessa https://api.slack.com/reference/surfaces/formatting kuvatun muodon perusteella, mutta tähän täytyy käyttää /hooks/:key/ Slack-päätepistettä."
selection:
cancel: "Peruuta"
+ quote_selection: "Lainaus ketjussa"
+ copy: "Kopioi"
+ move_selection_to_channel: "Siirrä kanavalle"
error: "Chat-viestien siirtämisessä tapahtui virhe"
title: "Siirrä chat ketjuun"
new_topic:
@@ -178,11 +327,13 @@ fi:
instructions:
one: "Olet luomassa uutta ketjua ja lisäämässä valitsemasi chat-viestin siihen."
other: "Olet luomassa uutta ketjua ja lisäämässä %{count} valitsemaasi chat-viestiä siihen."
+ instructions_channel_archive: "Olet luomassa uutta ketjua ja arkistoimassa kanavan viestit siihen."
existing_topic:
title: "Siirrä olemassa olevaan ketjuun"
instructions:
one: "Valitse ketju, johon haluat siirtää tämän chat-viestin."
other: "Valitse ketju, johon haluat siirtää nämä %{count} chat-viestiä."
+ instructions_channel_archive: "Valitse ketju, johon haluat arkistoida kanavan viestit."
new_message:
title: "Siirrä uuteen viestiin"
instructions:
@@ -198,12 +349,51 @@ fi:
public: "Kanavan historia säilytetään %{days} päivän ajan."
dm: "Henkilökohtainen keskusteluhistoria säilytetään %{days} päivän ajan."
topic_button_title: "Chat"
+ flags:
+ off_topic: "Tämä viesti ei liity nykyiseen keskusteluun kanavan otsikon mukaan, ja se pitäisi todennäköisesti siirtää muualle."
+ inappropriate: "Tämä viesti sisältää sisältöä, jota kohtuullinen henkilö pitäisi loukkaavana, herjaavana tai yhteisömme ohjeiden vastaisena."
+ spam: "Tämä viesti on mainos tai ilkivaltaa. Se ei ole hyödyllinen tai olennainen nykyiselle kanavalle."
+ notify_user: "Haluan keskustella tämän henkilön kanssa suoraan ja henkilökohtaisesti hänen viestistään."
+ notify_moderators: "Tämä viesti vaatii henkilökunnan huomion muusta syystä, jota ei ole mainittu edellä."
+ flagging:
+ action: "Liputa viesti"
+ emoji_picker:
+ favorites: "Usein käytetyt"
+ smileys_&_emotion: "Hymiöt ja tunteet"
+ objects: "Esineet"
+ people_&_body: "Ihmiset ja keho"
+ travel_&_places: "Matkailu ja paikat"
+ animals_&_nature: "Eläimet ja luonto"
+ food_&_drink: "Ruoka ja juoma"
+ activities: "Aktiviteetit"
+ flags: "Liput"
+ symbols: "Symbolit"
+ search_placeholder: "Hae emojin nimen ja aliaksen mukaan..."
+ no_results: "Ei tuloksia"
+ draft_channel_screen:
+ header: "Uusi viesti"
+ cancel: "Peruuta"
notifications:
+ chat_invitation: "kutsui sinut liittymään chat-kanavalle"
+ chat_invitation_html: "%{username} kutsui sinut liittymään chat-kanavalle"
+ chat_quoted: "%{username} %{description}"
popup:
+ chat_mention:
+ direct: 'mainitsi sinut kanavalla "%{channel}"'
+ direct_html: '%{username} mainitsi sinut kanavalla "%{channel}"'
+ other_plain: 'mainitsi kohteen %{identifier} kanavalla "%{channel}"'
+ other_html: '%{username} mainitsi kohteen %{identifier} kanavalla "%{channel}"'
+ direct_message_chat_mention:
+ direct: "mainitsi sinut henkilökohtaisessa chatissa"
+ direct_html: "%{username} mainitsi sinut henkilökohtaisessa chatissa"
+ other_plain: "mainitsi kohteen %{identifier} henkilökohtaisessa chatissa"
+ other_html: "%{username} mainitsi kohteen %{identifier} henkilökohtaisessa chatissa"
chat_message: "Uusi chat-viesti"
+ chat_quoted: "%{username} lainasi chat-viestiäsi"
titles:
chat_mention: "Chat-maininta"
chat_invitation: "Chat-kutsu"
+ chat_quoted: "Chat-keskustelua lainattu"
action_codes:
chat:
enabled: '%{who} otti käyttöön %{when}'
@@ -221,6 +411,8 @@ fi:
label: Lähettäjä
description: Oletuksena järjestelmä
review:
+ transcript:
+ view: "Näytä aiempien viestien transkriptio"
types:
reviewable_chat_message:
title: "Liputettu chat-viesti"
@@ -228,3 +420,30 @@ fi:
keyboard_shortcuts_help:
chat:
title: "Chat"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} Vaihda kanavaa"
+ open_quick_channel_selector: "%{shortcut} Avaa kanavan pikavalitsin"
+ open_insert_link_modal: "%{shortcut} Lisää hyperlinkki (vain tekstieditori)"
+ composer_bold: "%{shortcut} Lihavointi (vain tekstieditori)"
+ composer_italic: "%{shortcut} Kursiivi (vain tekstieditori)"
+ composer_code: "%{shortcut} Koodi (vain tekstieditori)"
+ drawer_open: "%{shortcut} Avaa chat-laatikko"
+ drawer_close: "%{shortcut} Sulje chat-laatikko"
+ topic_statuses:
+ chat:
+ help: "Chat on käytössä tässä ketjussa"
+ user:
+ allow_private_messages: "Salli muiden käyttäjien lähettää minulle yksityisviestejä ja chat-yksityisviestejä"
+ muted_users_instructions: "Estä kaikki ilmoitukset, yksityisviestit ja chat-yksityisviestit näiltä käyttäjiltä."
+ allowed_pm_users_instructions: "Salli vain näiden käyttäjien yksityisviestit tai chat-yksityisviestit."
+ allow_private_messages_from_specific_users: "Salli vain tiettyjen käyttäjien lähettää minulle yksityisviestejä tai chat-yksityisviestejä"
+ ignored_users_instructions: "Estä kaikki viestit, ilmoitukset, yksityisviestit ja chat-yksityisviestit näiltä käyttäjiltä."
+ user_menu:
+ no_chat_notifications_title: "Sinulla ei ole vielä chat-ilmoituksia"
+ no_chat_notifications_body: >
+ Saat ilmoituksen tässä paneelissa, kun joku lähettää sinulle viestin tai @mainitsee sinut chatissa. Ilmoitukset lähetetään myös sähköpostiisi, jos et ole kirjautunut sisään vähään aikaan. L'archivage d'un canal le place en mode lecture seule et déplace tous les messages du canal vers un sujet nouveau ou existant. Aucun nouveau message ne peut être envoyé et aucun message existant ne peut être modifié ou supprimé.
Voulez-vous vraiment archiver le canal %{channelTitle} ?
" + process_started: "Le processus d'archivage a commencé. Ce modal sera bientôt fermé et vous recevrez un message personnel lorsque le processus d'archivage sera terminé." + retry: "Réessayer" + channel_open: + title: "Ouvrir le canal" + instructions: "Rouvre le canal, tous les utilisateurs pourront envoyer des messages et modifier leurs messages existants." + channel_close: + title: "Fermer le canal" + instructions: "La fermeture du canal empêche les utilisateurs non responsables d'envoyer de nouveaux messages ou de modifier des messages existants. Voulez-vous vraiment fermer ce canal ?" + channel_delete: + title: "Supprimer le canal" + instructions: "Supprime le canal %{name} et l'historique des discussions. Tous les messages et données associées, telles que les réactions et les téléversements, seront définitivement supprimés. Si vous souhaitez conserver l'historique du canal et le désactiver, vous pouvez plutôt archiver le canal.
Voulez-vous vraiment supprimer définitivement le canal ? Pour confirmer, saisissez le nom du canal dans la case ci-dessous.
" + confirm: "Je comprends les conséquences. Supprimer le canal" + confirm_channel_name: "Saisissez le nom du canal" + process_started: "Le processus de suppression du canal a commencé. Ce modal sera bientôt fermé et vous ne verrez plus le canal supprimé nulle part." channels_list_popup: browse: "Parcourir les canaux" + create: "Nouveau canal" click_to_join: "Cliquez ici pour voir les canaux disponibles." close: "Fermer" - collapse: "Réduire le tiroir de chat" + collapse: "Réduire le tiroir de discussion" confirm_flag: "Voulez-vous vraiment signaler le message de %{username} ?" deleted: "Un message a été supprimé. [view]" + hidden: "Un message a été masqué. [view]" delete: "Supprimer" edited: "modifié" + muted: "en sourdine" + joined: "rejoint" empty_state: - direct_message: "Vous pouvez également démarrer une discussion personnelle avec un ou plusieurs utilisateurs." + direct_message_cta: "Commencer une discussion privée" + direct_message: "Vous pouvez également démarrer une discussion privée avec un ou plusieurs utilisateurs." + title: "Aucun canal trouvé" email_frequency: + description: "Nous ne vous enverrons un e-mail que si nous ne vous avons pas vu au cours des 15 dernières minutes." never: "Jamais" - enable: "Activer le chat" + title: "Notifications par e-mail" + when_away: "Seulement en cas d'absence" + enable: "Activer la discussion" flag: "Drapeau" + emoji: "Insérer un émoji" flagged: "Ce message a été signalé pour examen" invalid_access: "Vous n'avez pas accès à ce canal de discussion" + invitation_notification: "%{username} vous a invité(e) à rejoindre un canal de discussion" in_reply_to: "En réponse à" - heading: "Chat" + heading: "Discussion" join: "Rejoindre" new_messages: "nouveaux messages" mention_warning: @@ -56,62 +112,141 @@ fr: without_membership: one: "%{usernames} n'a pas rejoint ce canal." other: "%{usernames} n'ont pas rejoint ce canal." + aria_roles: + header: "En-tête de discussion" + composer: "Compositeur de discussion" + channels_list: "Liste des canaux de discussion" no_public_channels: "Vous n'avez rejoint aucun canal." only_chat_push_notifications: - title: "Envoyer uniquement des notifications push de chat" - description: "Bloquer l'envoi de toutes les notifications push non liées au chat" - open: "Ouvrir le chat" - open_full_page: "Ouvrir le chat en plein écran" - open_message: "Ouvrir le message dans le chat" + title: "Envoyer uniquement des notifications push de discussion" + description: "Bloquer l'envoi de toutes les notifications push non liées à la discussion" + ignore_channel_wide_mention: + title: "Ignorer les mentions à l'échelle du canal" + description: "Ne pas envoyer de notifications pour les mentions à l'échelle du canal (@here et @all)" + open: "Ouvrir la discussion" + open_full_page: "Ouvrir la discussion en plein écran" + close_full_page: "Fermer la discussion en plein écran" + open_message: "Ouvrir le message dans la discussion" placeholder_self: "Noter quelque chose" placeholder_others: "Discuter avec %{messageRecipient}" + placeholder_new_message_disallowed: "Le canal a le statut %{status}, vous ne pouvez pas envoyer de nouveaux messages pour le moment." + placeholder_silenced: "Vous ne pouvez pas envoyer de messages pour le moment." + placeholder_start_conversation: Démarrer une conversation avec %{usernames} remove_upload: "Supprimer le fichier" react: "Réagir avec un émoji" reply: "Répondre" edit: "Modifier" copy_link: "Copier le lien" rebake_message: "Reconstruire le HTML" + retry_staged_message: + title: "Erreur de réseau" + action: "Envoyer à nouveau ?" + unreliable_network: "Le réseau n'est pas fiable, l'envoi de messages et l'enregistrement de brouillon peuvent ne pas fonctionner" + bookmark_message: "Signet" + bookmark_message_edit: "Modifier le signet" restore: "Restaurer le message supprimé" save: "Enregistrer" select: "Sélectionner" + silence: "Désactiver l'utilisateur" + return_to_list: "Retour à la liste des canaux" scroll_to_bottom: "Défiler vers le bas" + scroll_to_new_messages: "Voir les nouveaux messages" sound: - title: "Son de notification du chat sur ordinateur" + title: "Son de notification de discussion sur ordinateur" sounds: none: "Aucun" bell: "Cloche" ding: "Ding" - title: "chat" - title_capitalized: "Chat" + title: "discussion" + title_capitalized: "Discussion" upload: "Joindre un fichier" uploaded_files: one: "%{count} fichier" other: "%{count} fichiers" you_flagged: "Vous avez signalé ce message" exit: "retour" + channel_status: + read_only_header: "Le canal est en lecture seule" + read_only: "Lecture seule" + archived_header: "Le canal est archivé" + archived: "Archivé" + archive_failed: "Échec de l'archivage du canal. %{completed}/%{total} messages ont été archivés dans le sujet de destination. Appuyez sur Réessayer pour tenter de terminer l'archivage." + archive_completed: "Voir le sujet de l'archive" + closed_header: "Le canal est fermé" + closed: "Fermé" + open_header: "Le canal est ouvert" + open: "Ouvert" browse: title: Canaux + filter_all: Tout + filter_open: Ouvert + filter_closed: Fermé + filter_archived: Archivé + filter_input_placeholder: Rechercher un canal par nom + chat_message_separator: + today: Aujourd'hui + yesterday: Hier + members_view: + filter_placeholder: Trouver des membres about_view: + associated_topic: Sujet lié + associated_category: Catégorie liée + title: Titre description: Description channel_info: + back_to_all_channels: "Tous les canaux" back_to_channel: "Retour" + tabs: + about: À propos + members: Membres + settings: Paramètres + channel_edit_title_modal: + title: Modifier le titre + input_placeholder: Ajouter un titre + description: Ajoutez un court titre descriptif à votre canal + channel_edit_description_modal: + title: Modifier la description + input_placeholder: Ajouter une description + description: Dites aux utilisateurs en quoi consiste ce canal + direct_message_creator: + title: Nouveau message + prefix: "À :" + no_results: Aucun résultat + selected_user_title: "Désélectionner %{username}" channel_selector: title: "Accéder au canal" no_channels: "Aucun canal ne correspond à votre recherche" + channel: + no_memberships: Ce canal ne comprend aucun membre + no_memberships_found: Aucun membre trouvé + memberships_count: + one: "%{count} membre" + other: "%{count} membres" create_channel: + auto_join_users: + public_category_warning: "%{category} est une catégorie publique. Ajouter automatiquement tous les utilisateurs récemment actifs à ce canal ?" + warning_groups: + one: Ajouter automatiquement %{members_count} utilisateurs de %{group} ? + other: Ajouter automatiquement %{members_count} utilisateurs de %{group} et %{group_2} ? + warning_multiple_groups: Ajouter automatiquement %{members_count} utilisateurs de %{group_1} et %{count} autres groupes ? choose_category: label: "Choisir une catégorie" none: "sélectionnez-en une…" default_hint: Gérer l'accès en visitant les paramètres de sécurité de %{category} + hint_groups: + one: Les utilisateurs de %{hint} auront accès à ce canal selon les paramètres de sécurité + other: Les utilisateurs de %{hint} et de %{hint_2} auront accès à ce canal selon les paramètres de sécurité + hint_multiple_groups: Les utilisateurs de %{hint_1} et de %{count} autres groupes auront accès à ce canal selon les paramètres de sécurité create: "Créer un canal" description: "Description (facultative)" name: "Nom du canal" + title: "Nouveau canal" type: "Type" types: category: "Catégorie" topic: "Sujet" reviewable: - type: "Message de chat" + type: "Message de discussion" reactions: only_you: "Vous avez réagi avec :%{emoji}:" and_others: "Vous, %{usernames} avez réagi avec :%{emoji}:" @@ -120,13 +255,23 @@ fr: you_others_and_more: "Vous, %{usernames} et %{more} autres utilisateurs avez réagi avec :%{emoji}:" composer: toggle_toolbar: "Basculer la barre d'outils" + italic_text: "texte souligné" + bold_text: "texte gras" + code_text: "texte codé" + quote: + original_channel: 'Envoyé à l''origine dans le canal %{channel}' + copy_success: "Citation de discussion copiée dans le presse-papiers" notification_levels: never: "Jamais" mention: "Seulement pour les mentions" always: "Pour toutes les activités" settings: + enable_auto_join_users: "Ajouter automatiquement tous les utilisateurs récemment actifs" + disable_auto_join_users: "Arrêter l'ajout automatique d'utilisateurs" + auto_join_users_warning: "Chaque utilisateur qui n'est pas membre de ce canal et qui a accès à la catégorie %{category} le rejoindra. Voulez-vous continuer ?" desktop_notification_level: "Notifications sur le bureau" follow: "Rejoindre" + followed: "Rejoint" mobile_notification_level: "Notifications push mobiles" mute: "Mettre le canal en sourdine" muted_on: "Activé" @@ -137,12 +282,13 @@ fr: saved: "Enregistré" unfollow: "Quitter" admin: - title: "Chat" + title: "Discussion" direct_messages: title: "Discussion privée" new: "Nouvelle discussion privée" create: "Valider" leave: "Quitter cette discussion privée" + cannot_create: "Nous sommes désolés, vous ne pouvez pas envoyer de messages privés." incoming_webhooks: back: "Retour" channel_placeholder: "Sélectionnez un canal" @@ -165,27 +311,34 @@ fr: system: "système" title: "Webhooks entrants" url: "URL" + url_instructions: "Cette adresse URL contient une valeur secrète - conservez-la précieusement." username: "Nom d'utilisateur" username_instructions: "Nom d'utilisateur du robot qui publie sur le canal. La valeur par défaut est « système » lorsqu'elle est laissée vide." + instructions: "Les webhooks entrants peuvent être utilisés par des systèmes externes pour publier des messages dans un canal de discussion désigné en tant qu'utilisateur robot via le point de terminaison/hooks/:key. La charge utile se compose d'un seul paramètre texte, qui est limité à 2000 caractères.texte au format Slack limités, en extrayant des liens et des mentions sur la base du format https://api.slack.com/reference/surfaces/formatting, mais le point de terminaison /hooks/:key/slack doit être utilisé pour cela."
selection:
cancel: "Annuler"
- error: "Une erreur s'est produite lors du déplacement des messages du chat"
- title: "Déplacer le chat vers le sujet"
+ quote_selection: "Citation dans le sujet"
+ copy: "Copie"
+ move_selection_to_channel: "Déplacer vers le canal"
+ error: "Une erreur s'est produite lors du déplacement des messages de la discussion"
+ title: "Déplacer la discussion vers le sujet"
new_topic:
title: "Déplacer vers un nouveau sujet"
instructions:
- one: "Vous êtes sur le point de créer un nouveau sujet et de le remplir avec le message de chat que vous avez sélectionné."
- other: "Vous êtes sur le point de créer un nouveau sujet et de le remplir avec les %{count} messages de chat que vous avez sélectionnés."
+ one: "Vous êtes sur le point de créer un nouveau sujet et de le remplir avec le message de discussion que vous avez sélectionné."
+ other: "Vous êtes sur le point de créer un nouveau sujet et de le remplir avec les %{count} messages de discussion que vous avez sélectionnés."
+ instructions_channel_archive: "Vous êtes sur le point de créer un nouveau sujet et d'y archiver les messages du canal."
existing_topic:
title: "Déplacer vers un sujet existant"
instructions:
- one: "Veuillez choisir le sujet vers lequel vous souhaitez déplacer ce message de chat."
- other: "Veuillez choisir le sujet vers lequel vous souhaitez déplacer ces %{count} messages de chat."
+ one: "Veuillez choisir le sujet vers lequel vous souhaitez déplacer ce message de discussion."
+ other: "Veuillez choisir le sujet vers lequel vous souhaitez déplacer ces %{count} messages de discussion."
+ instructions_channel_archive: "Veuillez choisir le sujet dans lequel vous souhaitez archiver les messages du canal."
new_message:
title: "Déplacer vers un nouveau message"
instructions:
- one: "Vous êtes sur le point de créer un nouveau message et de le remplir avec le message de chat que vous avez sélectionné."
- other: "Vous êtes sur le point de créer un nouveau message et de le remplir avec les %{count} messages de chat que vous avez sélectionnés."
+ one: "Vous êtes sur le point de créer un nouveau message et de le remplir avec le message de discussion que vous avez sélectionné."
+ other: "Vous êtes sur le point de créer un nouveau message et de le remplir avec les %{count} messages de discussion que vous avez sélectionnés."
replying_indicator:
single_user: "%{username} est en train d'écrire"
multiple_users: "%{commaSeparatedUsernames} et %{lastUsername} sont en train d'écrire"
@@ -194,22 +347,61 @@ fr:
other: "%{commaSeparatedUsernames} et %{count} autres utilisateurs sont en train d'écrire"
retention_reminders:
public: "L'historique du canal est conservé pendant %{days} jours."
- dm: "L'historique du chat personnel est conservé pendant %{days} jours."
- topic_button_title: "Chat"
+ dm: "L'historique des discussions privées est conservé pendant %{days} jours."
+ topic_button_title: "Discussion"
+ flags:
+ off_topic: "Ce message n'est pas pertinent pour la discussion en cours telle que définie par le titre du canal et devrait probablement être déplacé ailleurs."
+ inappropriate: "Ce message contient du contenu qu'une personne raisonnable considérerait comme offensant, abusif ou contraire à nos consignes communautaires."
+ spam: "Ce message est une publicité ou du vandalisme. Il n'est pas utile ou pertinent pour le canal actuel."
+ notify_user: "Je veux parler à cette personne directement et personnellement de son message."
+ notify_moderators: "Ce message requiert l'attention d'un responsable pour une autre raison non répertoriée ci-dessus."
+ flagging:
+ action: "Signaler un message"
+ emoji_picker:
+ favorites: "Fréquemment utilisé"
+ smileys_&_emotion: "Smileys et émotions"
+ objects: "Objets"
+ people_&_body: "Personnes et corps"
+ travel_&_places: "Voyages et lieux"
+ animals_&_nature: "Animaux et nature"
+ food_&_drink: "Nourriture et boissons"
+ activities: "Activités"
+ flags: "Drapeaux"
+ symbols: "Symboles"
+ search_placeholder: "Recherche par nom d'émoji et alias…"
+ no_results: "Aucun résultat"
+ draft_channel_screen:
+ header: "Nouveau message"
+ cancel: "Annuler"
notifications:
+ chat_invitation: "vous a invité(e) à rejoindre un canal de discussion"
+ chat_invitation_html: "%{username} vous a invité(e) à rejoindre un canal de discussion"
+ chat_quoted: "%{username} %{description}"
popup:
+ chat_mention:
+ direct: 'vous a mentionné(e) dans « %{channel} »'
+ direct_html: '%{username} vous a mentionné(e) dans « %{channel} »'
+ other_plain: 'a mentionné %{identifier} dans « %{channel} »'
+ other_html: '%{username} a mentionné %{identifier} dans « %{channel} »'
+ direct_message_chat_mention:
+ direct: "vous a mentionné(e) dans la discussion privée"
+ direct_html: "%{username} vous a mentionné(e) dans une discussion privée"
+ other_plain: "a mentionné %{identifier} dans une discussion privée"
+ other_html: "%{username} a mentionné %{identifier} dans une discussion privée"
chat_message: "Nouveau message de discussion"
+ chat_quoted: "%{username} a cité votre message de discussion"
titles:
- chat_mention: "Mention de chat"
- chat_invitation: "Invitation à rejoindre le chat"
+ chat_mention: "Mention de discussion"
+ chat_invitation: "Invitation à rejoindre la discussion"
+ chat_quoted: "Discussion citée"
action_codes:
chat:
- enabled: '%{who} a activé le %{when}'
- disabled: "%{who} a fermé le chat %{when}"
+ enabled: '%{who} a activé la %{when}'
+ disabled: "%{who} a fermé la discussion %{when}"
discourse_automation:
scriptables:
send_chat_message:
- title: Envoyer un message de chat
+ title: Envoyer un message de discussion
fields:
chat_channel_id:
label: ID du canal de discussion
@@ -219,10 +411,39 @@ fr:
label: Expéditeur
description: Paramètres par défaut
review:
+ transcript:
+ view: "Afficher la transcription des messages précédents"
types:
reviewable_chat_message:
- title: "Message de chat signalé"
+ title: "Message de discussion signalé"
flagged_by: "Signalé par"
keyboard_shortcuts_help:
chat:
- title: "Chat"
+ title: "Discussion"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} Changer de canal"
+ open_quick_channel_selector: "%{shortcut} Ouvrir le sélecteur de canal rapide"
+ open_insert_link_modal: "%{shortcut} Insérer un lien hypertexte (compositeur uniquement)"
+ composer_bold: "%{shortcut} Gras (compositeur uniquement)"
+ composer_italic: "%{shortcut} Italique (compositeur uniquement)"
+ composer_code: "%{shortcut} Code (compositeur uniquement)"
+ drawer_open: "%{shortcut} Ouvrir le tiroir de discussion"
+ drawer_close: "%{shortcut} Fermer le tiroir de discussion"
+ topic_statuses:
+ chat:
+ help: "La discussion est activée pour ce sujet"
+ user:
+ allow_private_messages: "Autoriser les autres utilisateurs à m'envoyer des messages privés et des messages directs de discussion"
+ muted_users_instructions: "Supprimer toutes les notifications, les messages privés et les messages directs de discussion de ces utilisateurs."
+ allowed_pm_users_instructions: "Autoriser uniquement les messages privés ou les messages directs de ces utilisateurs."
+ allow_private_messages_from_specific_users: "Autoriser uniquement des utilisateurs spécifiques à m'envoyer des messages privés ou des messages directs dans la discussion"
+ ignored_users_instructions: "Supprimer tous les messages, notifications, messages privés et messages directs de discussion de ces utilisateurs."
+ user_menu:
+ no_chat_notifications_title: "Vous n'avez pas encore reçu de notification de discussion"
+ no_chat_notifications_body: >
+ Vous serez averti(e) dans ce panneau lorsque quelqu'un vous enverra un message direct ou une @mention dans la discussion. Des notifications seront également envoyées à votre adresse e-mail si vous ne vous êtes pas connecté(e) pendant un certain temps. העברת ערוץ לארכיון מעבירה אותו למצב לקריאה בלבד ומעביר את כל ההודעות מהערוץ לנושא חדש או קיים. אי אפשר לשלוח הודעות חדשות ואי אפשר לערוך או למחוק הודעות קיימות.
להעביר את הערוץ %{channelTitle} לארכיון?
" @@ -81,8 +81,8 @@ he: hidden: "הודעה הוסתרה. [צפייה]" delete: "מחיקה" edited: "נערך" - muted: "מושתק" - joined: "הצטרפת" + muted: "בהשתקה" + joined: "הצטרפו" empty_state: direct_message_cta: "התחלת צ׳אט אישי" direct_message: "אפשר לפתוח בצ׳אט אישי עם משתמש אחד או יותר." @@ -93,14 +93,14 @@ he: title: "הודעות בדוא״ל" when_away: "רק כשלא במערכת" enable: "הפעלת צ׳אט" - flag: "סימון בדגל" + flag: "דיגול" emoji: "הוספת אמוג׳י" flagged: "הודעה זו סומנה לסקירה" invalid_access: "אין לך גישה לצפות בערוץ הצ׳אט הזה" invitation_notification: "הוזמנת להצטרף לערוץ צ׳אט על ידי %{username}" in_reply_to: "בתגובה אל" heading: "צ׳אט" - join: "הצטרפות" + join: "הצטרף" new_messages: "הודעות חדשות" mention_warning: cannot_see: @@ -142,15 +142,15 @@ he: placeholder_start_conversation: פתיחת דיון עם %{usernames} remove_upload: "הסרת קובץ" react: "להגיב עם אמוג׳י" - reply: "תגובה" + reply: "להגיב" edit: "עריכה" copy_link: "העתקת קישור" - rebake_message: "בניית HTML מחדש" + rebake_message: "בנייה מחודשת של HTML" retry_staged_message: title: "שגיאת רשת" action: "לשלוח שוב?" unreliable_network: "הרשת אינה אמינה, ייתכן ששליחת הודעות ושמירת טיוטה לא תפעלנה" - bookmark_message: "סימון" + bookmark_message: "סימנייה" bookmark_message_edit: "עריכת סימנייה" restore: "שחזור הודעה שנמחקה" save: "שמירה" @@ -162,7 +162,7 @@ he: sound: title: "צליל התראת צ׳אט שולחן עבודה" sounds: - none: "בלי" + none: "ללא" bell: "פעמון" ding: "דינג" title: "צ׳אט" @@ -183,14 +183,15 @@ he: archive_failed: "העברת הערוץ לארכיון נכשלה. %{completed}/%{total} הודעות הועברו לארכיון תחת נושא היעד. נא לנסות להשלים את ההעברה לארכיון פעם נוספת." archive_completed: "אפשר לעיין בארכיון הנושא" closed_header: "הערוץ סגור" - closed: "סגור" + closed: "סגורה" open_header: "הערוץ פתוח" - open: "פתוח" + open: "פתיחה" browse: + back: "חזרה" title: ערוצים filter_all: הכול filter_open: נפתחו - filter_closed: נסגרו + filter_closed: סגורה filter_archived: בארכיון filter_input_placeholder: חיפוש ערוץ לפי שם chat_message_separator: @@ -207,7 +208,7 @@ he: back_to_all_channels: "כל הערוצים" back_to_channel: "חזרה" tabs: - about: על אודות + about: אודות members: חברים settings: הגדרות channel_edit_title_modal: @@ -230,7 +231,7 @@ he: no_memberships: אין חברים בערוץ הזה no_memberships_found: לא נמצאו חברים memberships_count: - one: חבר אחד + one: "חבר %{count}" two: "%{count} חברים" many: "%{count} חברים" other: "%{count} חברים" @@ -238,20 +239,20 @@ he: auto_join_users: public_category_warning: "%{category} היא קטגוריה ציבורית. להוסיף את כל המשתמשים שהיו פעילים לאחרונה לערוץ הזה?" warning_groups: - one: להוסיף %{members_count} משתמשים מתוך %{group_1} אוטומטית? - two: להוסיף %{members_count} משתמשים מתוך %{group_1} ומתוך %{group_2} אוטומטית? - many: להוסיף %{members_count} משתמשים מתוך %{group_1} ומתוך %{group_2} אוטומטית? - other: להוסיף %{members_count} משתמשים מתוך %{group_1} ומתוך %{group_2} אוטומטית? + one: להוסיף %{members_count} משתמשים מתוך %{group} אוטומטית? + two: להוסיף %{members_count} משתמשים מתוך %{group} ומתוך %{group_2} אוטומטית? + many: להוסיף %{members_count} משתמשים מתוך %{group} ומתוך %{group_2} אוטומטית? + other: להוסיף %{members_count} משתמשים מתוך %{group} ומתוך %{group_2} אוטומטית? warning_multiple_groups: להוסיף %{members_count} משתמשים מתוך %{group_1} ועוד %{count} קבוצות אוטומטית? choose_category: label: "נא לבחור קטגוריה" none: "נא לבחור אחד…" default_hint: ניתן לנהל את הגישה באמצעות ביקור בהגדרות האבטחה של %{category} hint_groups: - one: למשתמשים ב־%{hint_1} תהיה גישה לערוץ בהתאם להגדרות האבטחה - two: למשתמשים ב־%{hint_1} וב־%{hint_2} תהיה גישה לערוץ בהתאם להגדרות האבטחה - many: למשתמשים ב־%{hint_1} וב־%{hint_2} תהיה גישה לערוץ בהתאם להגדרות האבטחה - other: למשתמשים ב־%{hint_1} וב־%{hint_2} תהיה גישה לערוץ בהתאם להגדרות האבטחה + one: למשתמשים ב־%{hint} תהיה גישה לערוץ בהתאם להגדרות האבטחה + two: למשתמשים ב־%{hint} וב־%{hint_2} תהיה גישה לערוץ בהתאם להגדרות האבטחה + many: למשתמשים ב־%{hint} וב־%{hint_2} תהיה גישה לערוץ בהתאם להגדרות האבטחה + other: למשתמשים ב־%{hint} וב־%{hint_2} תהיה גישה לערוץ בהתאם להגדרות האבטחה hint_multiple_groups: למשתמשים ב־%{hint_1} וב־%{count} קבוצות נוספות תהיה גישה לערוץ בהתאם להגדרות האבטחה create: "יצירת ערוץ" description: "תיאור (רשות)" @@ -271,8 +272,8 @@ he: you_others_and_more: "הגבת, יחד עם %{usernames} ו־%{more} נוספים באמוג׳י :%{emoji}:" composer: toggle_toolbar: "החלפת מצב סרגל כלים" - italic_text: "טקסט מודגש" - bold_text: "טקסט בולט" + italic_text: "טקסט נטוי" + bold_text: "טקסט מודגש" code_text: "טקסט קוד" quote: original_channel: 'נשלח במקור ב־%{channel}' @@ -286,8 +287,8 @@ he: disable_auto_join_users: "להפסיק להוסיף משתמשים אוטומטית" auto_join_users_warning: "כל משתמש שאינו חבר בערוץ הזה ויש לו גישה לקטגוריה %{category} יצטרף. זה בסדר?" desktop_notification_level: "התראות שולחן עבודה" - follow: "הצטרפות" - followed: "הצטרפות" + follow: "הצטרף" + followed: "הצטרפו" mobile_notification_level: "התראות בדחיפה לנייד" mute: "השתקת ערוץ" muted_on: "פעילה" @@ -295,8 +296,8 @@ he: notifications: "התראות" preview: "תצוגה מקדימה" save: "שמירה" - saved: "נשמרו" - unfollow: "לעזוב" + saved: "נשמר" + unfollow: "עזוב" admin: title: "צ׳אט" direct_messages: @@ -339,7 +340,7 @@ he: error: "אירעה שגיאה בהעברת הודעות הצ׳אט" title: "העברת צ׳אט לנושא" new_topic: - title: "העברה לנושא חדש" + title: "העבר לנושא חדש" instructions: one: "פעולה זו תיצור נושא חדש ותמלא בו את הודעת הצ׳אט שבחרת." two: "פעולה זו תיצור נושא חדש ותמלא בו את %{count} הודעות הצ׳אט שבחרת." @@ -347,7 +348,7 @@ he: other: "פעולה זו תיצור נושא חדש ותמלא בו את %{count} הודעות הצ׳אט שבחרת." instructions_channel_archive: "פעולה זו תיצור נושא חדש ותעביר אליו את הודעות הערוץ כארכיון." existing_topic: - title: "העברה לנושא קיים" + title: "העבר לנושא קיים" instructions: one: "נא לבחור את הנושא אליו ברצונך להעביר את הודעת הצ׳אט הזו." two: "נא לבחור את הנושא אליו ברצונך להעביר את %{count} הודעות הצ׳אט האלו." @@ -355,7 +356,7 @@ he: other: "נא לבחור את הנושא אליו ברצונך להעביר את %{count} הודעות הצ׳אט האלו." instructions_channel_archive: "נא לבחור את הנושא אליו ברצונך להעביר את הודעות הערוץ כארכיון." new_message: - title: "העברה להודעה חדשה" + title: "העבר להודעה חדשה" instructions: one: "פעולה זו תיצור הודעה חדשה ותמלא בה את הודעת הצ׳אט שבחרת." two: "פעולה זו תיצור הודעה חדשה ותמלא בה את %{count} הודעות הצ׳אט שבחרת." @@ -375,7 +376,7 @@ he: topic_button_title: "צ׳אט" flags: off_topic: "הודעה זו לא תואמת לדיון הנוכחי כפי שהוגדר בכותרת הערוץ וכנראה שצריך להעביר אותה." - inappropriate: "הודעה זו מכילה תוכן שאדם מן השורה עשוי להחשיב כפוגעני, נצלני או מפר את הכללים המנחים את הקהילה שלנו." + inappropriate: "הודעה זו מכילה תוכן שאדם מן השורה עשוי להחשיב כפוגעני, נצלני או מפר את הכללים המנחים את הקהילה שלנו." spam: "הודעה זו היא פרסומת או השחתה. היא אינה שימושית או רלוונטית לערוץ הנוכחי." notify_user: "אשמח לדבר עם אותו גורם ישירות ובאופן אישי על ההודעה שנשלחה על ידי הגורם." notify_moderators: "הודעה זו דורשת את התערבות הסגל מסיבות אחרות שאינן מופיעות לעיל." @@ -384,16 +385,19 @@ he: emoji_picker: favorites: "נפוצים" smileys_&_emotion: "חייכנים ורגשות" - objects: "חפצים" + objects: "עצמים" people_&_body: "אנשים וגוף" travel_&_places: "טיולים ומקומות" animals_&_nature: "חיות וטבע" - food_&_drink: "מזון ושתייה" + food_&_drink: "מזון ומשקאות" activities: "פעילויות" flags: "דגלים" symbols: "סמלים" search_placeholder: "חיפוש לפי שם וכינוי של האמוג׳י…" no_results: "אין תוצאות" + draft_channel_screen: + header: "הודעה חדשה" + cancel: "ביטול" notifications: chat_invitation: "נשלחה אליך הזמנה להצטרף לערוץ צ׳אט" chat_invitation_html: "הוזמנת להצטרף לערוץ צ׳אט על ידי %{username}" @@ -402,12 +406,12 @@ he: chat_mention: direct: 'אוזכרת בערוץ „%{channel}”' direct_html: 'אוזכרת בערוץ „%{channel}” על ידי %{username}' - other: 'נוסף אזכור של %{identifier} בערוץ „%{channel}”' + other_plain: 'נוסף אזכור של %{identifier} בערוץ „%{channel}”' other_html: 'נוסף אזכור של %{identifier} בערוץ „%{channel}” על ידי %{username}' direct_message_chat_mention: direct: "אוזכרת בצ׳אט אישי" direct_html: "אוזכרת בצ׳אט אישי על ידי %{username}" - other: "נוסף אזכור של %{identifier} בצ׳אט אישי" + other_plain: "נוסף אזכור של %{identifier} בצ׳אט אישי" other_html: "נוסף אזכור של %{identifier} בצ׳אט אישי על ידי %{username}" chat_message: "הודעת צ׳אט חדשה" chat_quoted: "הודעת הצ׳אט שלך צוטטה על ידי %{username}" @@ -437,7 +441,7 @@ he: types: reviewable_chat_message: title: "הודעת צ׳אט מסומנת" - flagged_by: "סומנה על ידי" + flagged_by: "דוגל על ידי" keyboard_shortcuts_help: chat: title: "צ׳אט" diff --git a/plugins/chat/config/locales/client.hr.yml b/plugins/chat/config/locales/client.hr.yml index 24df4b375b..245e81fc4e 100644 --- a/plugins/chat/config/locales/client.hr.yml +++ b/plugins/chat/config/locales/client.hr.yml @@ -24,8 +24,8 @@ hr: already_enabled: "Chat je već omogućen na ovu temu. Molimo osvježite." disabled_for_topic: "Chat je onemogućen na ovu temu." bot: "bot" - create: "Stvorite" - cancel: "Otkazati" + create: "Kreiraj" + cancel: "Odustani" cancel_reply: "Otkaži odgovor" chat_channels: "Kanali" move_to_channel: @@ -33,8 +33,11 @@ hr: confirm_move: "Premjesti poruke" channel_settings: title: "Postavke kanala" + edit: "Uredi" + add: "Dodaj" leave_channel: "Napusti kanal" join: "Pridružite se" + leave: "Napustiti" channel_archive: title: "Arhiviraj kanal" instructions: "Arhiviranje kanala stavlja ga u način rada samo za čitanje i premješta sve poruke s kanala u novu ili postojeću temu. Ne mogu se slati nove poruke, niti se postojeće poruke ne mogu uređivati ili brisati.
Jeste li sigurni da želite arhivirati %{channelTitle} kanal?
" @@ -55,16 +58,20 @@ hr: channels_list_popup: browse: "Pretražujte kanale" click_to_join: "Kliknite ovdje da biste vidjeli dostupne kanale." - close: "Zatvoriti" + close: "Zatvori" collapse: "Sažmi ladicu za chat" confirm_flag: "Jeste li sigurni da želite označiti poruku korisnika %{username}?" deleted: "Poruka je izbrisana. [pogledaj]" - delete: "Izbriši" + delete: "Pobriši" edited: "uredio" + muted: "utišano" + joined: "prijavljen" empty_state: direct_message: "Također možete započeti osobni razgovor s jednim ili više korisnika." + email_frequency: + never: "Nikad" enable: "Omogući chat" - flag: "Prijavi" + flag: "Označi zastavicom" flagged: "Ova poruka je označena za pregled" invalid_access: "Nemate pristup za pregled ovog kanala za chat" invitation_notification: "%{username} vas je pozvao da se pridružite chat kanalu" @@ -77,7 +84,7 @@ hr: one: "%{usernames} ne može pristupiti ovom kanalu i nije obaviješten." few: "%{usernames} ne mogu pristupiti ovom kanalu i nisu obaviješteni." other: "%{usernames} ne mogu pristupiti ovom kanalu i nisu obaviješteni." - dismiss: "odbaciti" + dismiss: "skloni" invitations_sent: one: "Poziv poslan" few: "Pozivnice poslane" @@ -90,7 +97,102 @@ hr: aria_roles: header: "Zaglavlje chata" composer: "Skladatelj chata" + reply: "Odgovor" + edit: "Uredi" + rebake_message: "Popravi HTML" + bookmark_message: "Zabilješka" + bookmark_message_edit: "Uredi oznaku" + save: "Spremi" + sounds: + none: "Ništa" + title: "čet" + title_capitalized: "Čet" + exit: "natrag" + channel_status: + closed: "Zatvoreno" + open: "Otvori" browse: + back: "Natrag" title: Kanali + filter_all: Sve + filter_closed: Zatvoreno + chat_message_separator: + today: Danas + yesterday: Jučer + about_view: + title: Naslov + description: Opis + channel_info: + back_to_channel: "Natrag" + tabs: + about: O nama + members: Članovi + settings: Postavke + direct_message_creator: + title: Nova poruka + prefix: "Za:" + create_channel: + type: "Tip" + types: + category: "Kategorija" + topic: "Tema" + composer: + italic_text: "naglašen tekst" + bold_text: "jaki text" + notification_levels: + never: "Nikad" + settings: + follow: "Pridružite se" + followed: "Prijavljen" + notifications: "Obavijest" + preview: "Pregled" + save: "Spremi" + saved: "Spremljeno" + unfollow: "Napustiti" + admin: + title: "Čet" + incoming_webhooks: + back: "Natrag" + description: "Opis" + delete: "Pobriši" + emoji: "Emoji" + name: "Ime" + save: "Spremi" + edit: "Uredi" + system: "sistem" + url: "URL" + username: "Korisničko ime" + selection: + cancel: "Odustani" + copy: "Kopija" + new_topic: + title: "Prebaci u novu temu" + existing_topic: + title: "Prebaci u postojeću temu" + new_message: + title: "Premjestite u novu poruku" + topic_button_title: "Čet" + emoji_picker: + objects: "Objekti" + activities: "Aktivnosti" + flags: "Oznake zastavicom" + symbols: "Simboli" + draft_channel_screen: + header: "Nova poruka" + cancel: "Odustani" notifications: chat_invitation_html: "%{username} vas je pozvao da se pridružite chat kanalu" + chat_quoted: "%{username} %{description}" + discourse_automation: + scriptables: + send_chat_message: + fields: + message: + label: Poruka + review: + types: + reviewable_chat_message: + flagged_by: "Označio" + keyboard_shortcuts_help: + chat: + title: "Čet" diff --git a/plugins/chat/config/locales/client.hu.yml b/plugins/chat/config/locales/client.hu.yml index 6c08e51cd8..bc4a5e13b9 100644 --- a/plugins/chat/config/locales/client.hu.yml +++ b/plugins/chat/config/locales/client.hu.yml @@ -35,21 +35,18 @@ hu: browse_all_channels: "Összes csatorna böngészése" move_to_channel: title: "Üzenetek áthelyezése a csatornába" - instructions: - one: "Ön 1 üzenetet helyet át. Válasszon ki egy célcsatornát. A(z) %{channelTitle} csatornán egy helykitöltő üzenet jön létre, amely jelzi, hogy ez az üzenet áthelyezésre került." - other: "Ön %{count} üzenetet helyet át. Válasszon ki egy célcsatornát. A(z) %{channelTitle} csatornán egy helykitöltő üzenet jön létre, amely jelzi, hogy ez az üzenet áthelyezésre került." confirm_move: "Üzenetek áthelyezése" channel_settings: title: "Csatornabeállítások" edit: "Szerkesztés" - add: "Hozzáadás" + add: "Új" close_channel: "Csatorna bezárása" open_channel: "Csatorna megnyitása" archive_channel: "Csatorna archiválása" delete_channel: "Csatorna törlése" join_channel: "Csatlakozás a csatornához" leave_channel: "Csatorna elhagyása" - join: "Csatlakozás" + join: "Belépés" leave: "Elhagyás" channel_archive: title: "Csatorna archiválása" @@ -71,14 +68,14 @@ hu: channels_list_popup: browse: "Csatornák böngészése" click_to_join: "Kattintson ide az elérhető csatornák megtekintéséhez." - close: "Bezárás" + close: "Lezárás" collapse: "Csevegőfiók összecsukása" confirm_flag: "Biztos, hogy jelenti %{username} üzenetét?" deleted: "Egy üzenet törölve lett. [megtekintés]" hidden: "Egy üzenet el lett rejtve. [megtekintés]" delete: "Törlés" edited: "szerkesztve" - muted: "némítva" + muted: "némítás" joined: "csatlakozott" empty_state: direct_message_cta: "Személyes csevegés indítása" @@ -90,19 +87,19 @@ hu: title: "E-mail értesítések" when_away: "Csak ha távol van" enable: "Csevegés engedélyezése" - flag: "Jelentés" + flag: "Jelölés" flagged: "Ezt az üzenetet felülvizsgálatra jelentették" invalid_access: "Nincs hozzáférése ennek a csevegőcsatornának a megtekintéséhez" invitation_notification: "%{username} meghvta Önt, hogy csatlakozzon egy csevegőcsatornához" in_reply_to: "Válaszul erre:" heading: "Csevegés" - join: "Csatlakozás" + join: "Belépés" new_messages: "új üzenetek" mention_warning: cannot_see: one: "%{usernames} nem fér hozzá ehhez a csatornához, és nem lett értesítve." other: "%{usernames} nem fér hozzá ehhez a csatornához, és nem lettek értesítve." - dismiss: "eltüntetés" + dismiss: "elvetés" invitations_sent: one: "Meghívó elküldve" other: "Meghívók elküldve" @@ -135,7 +132,7 @@ hu: reply: "Válasz" edit: "Szerkesztés" copy_link: "Hivatkozás másolása" - rebake_message: "HTML újraépítése" + rebake_message: "HTML újjáépítése" retry_staged_message: title: "Hálózati hiba" action: "Újraküldi?" @@ -152,7 +149,7 @@ hu: sound: title: "Asztali csevegőértesítés hangja" sounds: - none: "Nincs" + none: "Egyik sem" bell: "Harang" ding: "Csengettyű" title: "csevegés" @@ -171,14 +168,15 @@ hu: archive_failed: "A csatorna archiválása nem sikerült.%{completed}/%{total} üzenet archiválva lett a céltémában . Nyomja meg az Újra gombot az archiválás befejezéséhez." archive_completed: "Lásd az archív témát" closed_header: "A csatorna zárolt" - closed: "Zárolt" + closed: "Zárt" open_header: "A csatorna nyitott" - open: "Nyitott" + open: "Megnyitás" browse: + back: "Vissza" title: Csatornák filter_all: Összes filter_open: Nyitott - filter_closed: Zárolt + filter_closed: Zárt filter_archived: Archivált filter_input_placeholder: Csatorna keresése név szerint chat_message_separator: @@ -218,14 +216,14 @@ hu: no_memberships: Ennek a csatornának nincsenek tagjai no_memberships_found: Nem találhatók tagok memberships_count: - one: 1 tag + one: "%{count} tag" other: "%{count} tag" create_channel: auto_join_users: public_category_warning: "A(z) %{category} egy nyilvános kategória. Automatikusan hozzáadja az összes nemrég aktív felhasználót ehhez a csatornához?" warning_groups: - one: '%{members_count} felhasználó automatikus hozzáadása a(z) %{group_1} csoportból?' - other: '%{members_count} felhasználó automatikus hozzáadása a(z) %{group_1} és %{group_2} csoportokból?' + one: '%{members_count} felhasználó automatikus hozzáadása a(z) %{group} csoportból?' + other: '%{members_count} felhasználó automatikus hozzáadása a(z) %{group} és %{group_2} csoportokból?' warning_multiple_groups: Automatikusan hozzáadja a(z) %{group_1} csoport %{members_count} felhasználóját, és további %{count} felhasználót? choose_category: label: "Válasszon kategóriát" @@ -248,8 +246,8 @@ hu: you_others_and_more: "Ön, %{usernames} és még %{more} valaki ezzel reagált: :%{emoji}:" composer: toggle_toolbar: "Eszköztár be/ki" - italic_text: "kiemelt szöveg" - bold_text: "erős szöveg" + italic_text: "dőlt szöveg" + bold_text: "félkövér szöveg" code_text: "kódszöveg" quote: copy_success: "Csevegési idézet a vágólapra másolva" @@ -261,7 +259,7 @@ hu: enable_auto_join_users: "Az összes nemrég aktív felhasználó automatikus hozzáadása" disable_auto_join_users: "A felhasználók automatikus hozzáadásának leállítása" desktop_notification_level: "Asztali értesítések" - follow: "Csatlakozás" + follow: "Belépés" followed: "Csatlakozott" mobile_notification_level: "Mobilos leküldéses értesítések" mute: "Csatorna némítása" @@ -304,26 +302,26 @@ hu: username: "Felhasználónév" username_instructions: "A csatornán közzétevő bot felhasználóneve. Ha üresen marad, akkor alapértelmezés szerint „rendszer”." selection: - cancel: "Mégse" + cancel: "Visszavon" quote_selection: "Idézet a témában" copy: "Másolás" move_selection_to_channel: "Áthelyezés csatornába" error: "Hiba történt a csevegőüzenetek áthelyezésekor" title: "Csevegés áthelyezése a témához" new_topic: - title: "Áthelyezés új témához" + title: "Áthelyezés új témába" instructions: one: "Arra készül, hogy új témát hozzon létre, és feltöltse azt a kiválasztott csevegőüzenettel." other: "Arra készül, hogy új témát hozzon létre, és feltöltse azt %{count} kiválasztott csevegőüzenettel." instructions_channel_archive: "Egy új témát fog létrehozni, és archiválni fogja a csatornaüzeneteket." existing_topic: - title: "Áthelyezés meglévő témához" + title: "Áthelyezés egy meglévő témába" instructions: one: "Válassza ki azt a témát, amelyhez áthelyezi a csevegőüzenetet." other: "Válassza ki azt a témát, amelyhez áthelyez %{count} csevegőüzenetet." instructions_channel_archive: "Válassza ki azt a témát, amelybe a csatornaüzeneteket archiválná." new_message: - title: "Ugrás az új üzenethez" + title: "Áthelyezés az új üzenethez" instructions: one: "Arra készül, hogy új üzenet hozzon létre, és feltöltse azt a kiválasztott csevegőüzenettel." other: "Arra készül, hogy új üzenetet hozzon létre, és feltöltse azt %{count} kiválasztott csevegőüzenettel." @@ -338,21 +336,28 @@ hu: dm: "A személyes csevegési előzményei %{days} napig maradnak meg." topic_button_title: "Csevegés" emoji_picker: + objects: "Tárgyak" + activities: "Tevékenységek" + flags: "Zászlók" + symbols: "Szimbólumok" no_results: "Nincs találat" + draft_channel_screen: + header: "Új üzenet" + cancel: "Visszavon" notifications: chat_invitation: "meghívta Önt, hogy csatlakozzon egy csevegőcsatornához" chat_invitation_html: "%{username} meghvta Önt, hogy csatlakozzon egy csevegőcsatornához" - chat_quoted: "%{username} %{description}" + chat_quoted: "%{username}%{description}" popup: chat_mention: direct: 'megemlítette Önt a következő csatornán: „%{channel}”' direct_html: '%{username} megemlítette Önt a következő csatornán: „%{channel}”' - other: 'megemlítette %{identifier} felhasználót a következő csatornán: „%{channel}”' + other_plain: 'megemlítette %{identifier} felhasználót a következő csatornán: „%{channel}”' other_html: '%{username} megemlítette %{identifier} felhasználót a következő csatornán: „%{channel}”' direct_message_chat_mention: direct: "megemlítette Önt egy személyes csevegésben" direct_html: "%{username} megemlítette Önt egy személyes csevegésben" - other: "megemlítette %{identifier} felhasználót egy személyes csevegésben" + other_plain: "megemlítette %{identifier} felhasználót egy személyes csevegésben" other_html: "%{username} megemlítette %{identifier} felhasználót egy személyes csevegésben" chat_message: "Új csevegőüzenet" chat_quoted: "%{username} idézte a csevegési üzenetet" @@ -380,7 +385,7 @@ hu: types: reviewable_chat_message: title: "Jelentett csevegőüzenet" - flagged_by: "Jelentette:" + flagged_by: "Megjelölte" keyboard_shortcuts_help: chat: title: "Csevegés" diff --git a/plugins/chat/config/locales/client.hy.yml b/plugins/chat/config/locales/client.hy.yml index cb18f64d35..8931700910 100644 --- a/plugins/chat/config/locales/client.hy.yml +++ b/plugins/chat/config/locales/client.hy.yml @@ -5,3 +5,111 @@ # https://translate.discourse.org/ hy: + js: + chat: + create: "Ստեղծել" + cancel: "Չեղարկել" + channel_settings: + edit: "Խմբագրել" + add: "Ավելացնել" + join: "Միանալ" + leave: "Լքել" + close: "Փակել" + delete: "Ջնջել" + edited: "խմբագրվել է" + muted: "խլացված" + joined: "միացել է" + email_frequency: + never: "Երբեք" + flag: "Դրոշակավորել" + join: "Միանալ" + mention_warning: + dismiss: "չեղարկել" + reply: "Պատասխանել" + edit: "Խմբագրել" + rebake_message: "Վերակառուցել HTML-ը" + bookmark_message: "Էջանշել" + save: "Պահպանել" + sounds: + none: "Ոչ մի" + exit: "ետ" + channel_status: + closed: "Փակված" + open: "Բացել" + browse: + back: "Ետ" + filter_all: Բոլորը + filter_closed: Փակված + chat_message_separator: + today: Այսօրվա + yesterday: Երեկվա + about_view: + title: Վերնագիր + description: Նկարագրությունը + channel_info: + back_to_channel: "Ետ" + tabs: + about: Մասին + members: Անդամներ + settings: Կարգավորումներ + direct_message_creator: + title: Նոր Հաղորդագրություն + prefix: "Ում:" + create_channel: + type: "Տիպը" + types: + category: "Կատեգորիա" + topic: "Թեմա" + composer: + italic_text: "շեղ տեքստ" + bold_text: "թավ տեքստ" + notification_levels: + never: "Երբեք" + settings: + follow: "Միանալ" + followed: "Միացել է" + notifications: "Ծանուցումներ" + preview: "Նախադիտում" + save: "Պահպանել" + saved: "Պահված է" + unfollow: "Լքել" + incoming_webhooks: + back: "Ետ" + description: "Նկարագրությունը" + delete: "Ջնջել" + emoji: "Էմոջի" + name: "Անուն" + save: "Պահպանել" + edit: "Խմբագրել" + system: "համակարգային" + url: "URL" + username: "Օգտանուն" + selection: + cancel: "Չեղարկել" + copy: "Կրկնօրինակել" + new_topic: + title: "Տեղափոխվել դեպի Նոր Թեմա" + existing_topic: + title: "Տեղափոխել դեպի Գոյություն Ունեցող Թեմա" + new_message: + title: "Տեղափոխել դեպի Նոր Հաղորդագրություն" + emoji_picker: + objects: "Օբյեկտներ" + activities: "Ակտիվություն" + flags: "Դրոշակներ" + symbols: "Նշաններ" + draft_channel_screen: + header: "Նոր Հաղորդագրություն" + cancel: "Չեղարկել" + notifications: + chat_quoted: "%{username} %{description}" + discourse_automation: + scriptables: + send_chat_message: + fields: + message: + label: Նոր Հաղորդագրություն + review: + types: + reviewable_chat_message: + flagged_by: "Դրոշակավորել է" diff --git a/plugins/chat/config/locales/client.id.yml b/plugins/chat/config/locales/client.id.yml index 596e36b2e1..ce26a3808b 100644 --- a/plugins/chat/config/locales/client.id.yml +++ b/plugins/chat/config/locales/client.id.yml @@ -5,3 +5,84 @@ # https://translate.discourse.org/ id: + js: + chat: + cancel: "Batal" + channel_settings: + edit: "Ubah" + add: "Menambahkan" + join: "Gabung" + leave: "Keluar" + close: "Tutup" + delete: "Hapus" + muted: "diredam" + joined: "joined" + email_frequency: + never: "Tidak pernah" + join: "Gabung" + mention_warning: + dismiss: "bubar" + reply: "Balas" + edit: "Ubah" + bookmark_message: "Penandaan" + bookmark_message_edit: "Edit Bookmark" + save: "Simpan" + sounds: + none: "Tidak ada" + channel_status: + closed: "Tertutup" + open: "Buka" + browse: + filter_all: Semua + filter_closed: Tertutup + about_view: + title: Judul + channel_info: + tabs: + about: Tentang + members: Anggota + settings: Pengaturan + direct_message_creator: + title: Pesan Baru + prefix: "Kepada:" + create_channel: + type: "Tipe" + types: + category: "Kategori" + topic: "Topik" + notification_levels: + never: "Tidak pernah" + settings: + follow: "Gabung" + followed: "Joined" + notifications: "Pemberitahuan" + save: "Simpan" + saved: "Tersimpan" + unfollow: "Keluar" + incoming_webhooks: + delete: "Hapus" + name: "Name" + save: "Simpan" + edit: "Ubah" + system: "sistem" + username: "Nama Pengguna" + selection: + cancel: "Batal" + new_topic: + title: "Pindahkan sebagai Topik Baru" + emoji_picker: + objects: "Objek" + flags: "Flags" + draft_channel_screen: + header: "Pesan Baru" + cancel: "Batal" + discourse_automation: + scriptables: + send_chat_message: + fields: + message: + label: pesan + review: + types: + reviewable_chat_message: + flagged_by: "Dipanji Oleh" diff --git a/plugins/chat/config/locales/client.it.yml b/plugins/chat/config/locales/client.it.yml index b6de5b753c..d05a194349 100644 --- a/plugins/chat/config/locales/client.it.yml +++ b/plugins/chat/config/locales/client.it.yml @@ -6,6 +6,21 @@ it: js: + admin: + logs: + staff_actions: + actions: + chat_channel_status_change: "Lo stato del canale di chat è cambiato" + chat_channel_delete: "Canale di chat eliminato" + api: + scopes: + descriptions: + chat: + create_message: "Crea un messaggio di chat in un canale specifico." + about: + chat_messages_count: "Messaggi di chat" + chat_channels_count: "Canali di chat" + chat_users_count: "Utenti della chat" chat: dates: time_tiny: "h:mm" @@ -17,29 +32,70 @@ it: cancel: "Annulla" cancel_reply: "Annulla risposta" chat_channels: "Canali" + browse_all_channels: "Sfoglia tutti i canali" + move_to_channel: + title: "Sposta i messaggi sul canale" + instructions: + one: "Stai spostando %{count} messaggio. Seleziona un canale di destinazione. Verrà creato un messaggio segnaposto nel canale %{channelTitle} per indicare che questo messaggio è stato spostato." + other: "Stai spostando %{count} messaggi. Seleziona un canale di destinazione. Verrà creato un messaggio segnaposto nel canale %{channelTitle} per indicare che questi messaggi sono stati spostati." + confirm_move: "Sposta messaggi" channel_settings: + title: "Impostazioni del canale" edit: "Modifica" + add: "Aggiungi" + close_channel: "Chiudi canale" + open_channel: "Apri canale" + archive_channel: "Archivia canale" + delete_channel: "Elimina canale" join_channel: "Partecipa al canale" leave_channel: "Lascia il canale" join: "Partecipa" leave: "Esci" + channel_archive: + title: "Archivia canale" + instructions: "L'archiviazione di un canale lo mette in modalità di sola lettura e sposta tutti i messaggi dal canale a un argomento nuovo o esistente. Non è possibile inviare nuovi messaggi e non è possibile modificare o eliminare messaggi esistenti.
Sei sicuro di voler archiviare il canale %{channelTitle}?
" + process_started: "Il processo di archiviazione è iniziato. Questo avviso si chiuderà a breve e riceverai un messaggio personale al termine del processo di archiviazione." + retry: "Riprova" + channel_open: + title: "Apri canale" + instructions: "Riapre il canale, tutti gli utenti potranno inviare messaggi e modificare i propri messaggi esistenti." + channel_close: + title: "Chiudi canale" + instructions: "La chiusura del canale impedisce agli utenti non appartenenti allo staff di inviare nuovi messaggi o modificare quelli esistenti. Sei sicuro di voler chiudere questo canale?" + channel_delete: + title: "Elimina canale" + instructions: "Elimina il canale %{name} e la cronologia delle chat. Tutti i messaggi e i dati correlati, come reazioni e caricamenti, verranno eliminati in modo permanente. Se vuoi conservare la cronologia del canale e rimuoverla, puoi invece archiviare il canale.
Sei sicuro di voler eliminare definitivamente il canale? Per confermare, digita il nome del canale nella casella sottostante.
" + confirm: "Ho capito le conseguenze, procedi all'eliminazione del canale" + confirm_channel_name: "Inserisci il nome del canale" + process_started: "Il processo di eliminazione del canale è iniziato. Questo avviso si chiuderà a breve, non vedrai più il canale eliminato da nessuna parte." channels_list_popup: browse: "Sfoglia i canali" + create: "Nuovo canale" click_to_join: "Fai clic qui per visualizzare i canali disponibili." close: "Chiudi" collapse: "Comprimi il cassetto della chat" confirm_flag: "Vuoi segnalare il messaggio di %{username}?" deleted: "Un messaggio è stato eliminato. [visualizza]" + hidden: "Un messaggio è stato nascosto. [view]" delete: "Elimina" edited: "modificato" + muted: "silenziato" + joined: "partecipante" empty_state: + direct_message_cta: "Avvia una chat personale" direct_message: "Puoi anche avviare una chat personale con uno o più utenti." + title: "Nessun canale trovato" email_frequency: + description: "Ti invieremo un'e-mail solo se non ti sei fatto vedere negli ultimi 15 minuti." never: "Mai" + title: "Notifiche via e-mail" + when_away: "Solo quando non sono collegato" enable: "Abilita chat" flag: "Segnala" + emoji: "Inserisci emoji" flagged: "Questo messaggio è stato segnalato per la revisione" invalid_access: "Non hai accesso alla visualizzazione di questo canale chat" + invitation_notification: "%{username} ti ha inviato un invito a partecipare a un canale di chat" in_reply_to: "In risposta a" heading: "Chat" join: "Partecipa" @@ -56,25 +112,45 @@ it: without_membership: one: "%{usernames} non ha partecipato a questo canale." other: "%{usernames} non hanno partecipato a questo canale." + aria_roles: + header: "Intestazione della chat" + composer: "Compositore di chat" + channels_list: "Elenco dei canali di chat" no_public_channels: "Non hai partecipato a nessun canale." only_chat_push_notifications: title: "Invia solo le notifiche push della chat" description: "Blocca l'invio di tutte le notifiche push non relative alla chat" + ignore_channel_wide_mention: + title: "Ignora le menzioni a livello di canale" + description: "Non inviare notifiche per menzioni a livello di canale (@here e @all)" open: "Apri chat" open_full_page: "Apri chat a schermo intero" + close_full_page: "Chiudi la chat a schermo intero" open_message: "Apri messaggio in chat" placeholder_self: "Scrivi qualche annotazione" placeholder_others: "Chatta con %{messageRecipient}" + placeholder_new_message_disallowed: "Il canale è %{status}, non puoi inviare nuovi messaggi in questo momento." + placeholder_silenced: "In questo momento non puoi inviare messaggi." + placeholder_start_conversation: Inizia una conversazione con %{usernames} remove_upload: "Rimuovi file" react: "Reagisci con delle emoji" reply: "Rispondi" edit: "Modifica" copy_link: "Copia link" rebake_message: "Ricompila HTML" + retry_staged_message: + title: "Errore di rete" + action: "Inviare di nuovo?" + unreliable_network: "La rete non è affidabile, l'invio di messaggi e il salvataggio della bozza potrebbero non funzionare" + bookmark_message: "Aggiungi ai segnalibri" + bookmark_message_edit: "Modifica segnalibri" restore: "Ripristina messaggio eliminato" save: "Salva" select: "Seleziona" + silence: "Silenzia utente" + return_to_list: "Torna all'elenco dei canali" scroll_to_bottom: "Scorri fino in fondo" + scroll_to_new_messages: "Vedi nuovi messaggi" sound: title: "Suono di notifica della chat desktop" sounds: @@ -89,23 +165,82 @@ it: other: "%{count} file" you_flagged: "Hai segnalato questo messaggio" exit: "indietro" + channel_status: + read_only_header: "Il canale è di sola lettura" + read_only: "Sola lettura" + archived_header: "Il canale è archiviato" + archived: "Archiviati" + archive_failed: "Archiviazione del canale non riuscita. %{completed}/%{total} messaggi sono stati archiviati nell'argomento di destinazione. Premi Riprova per tentare di completare l'archiviazione." + archive_completed: "Vedi l'argomento di archiviazione" + closed_header: "Il canale è chiuso" + closed: "Chiusi" + open_header: "Il canale è aperto" + open: "Aperto" browse: title: Canali + filter_all: Tutti + filter_open: Aperti + filter_closed: Chiusi + filter_archived: Archiviati + filter_input_placeholder: Cerca canale per nome + chat_message_separator: + today: Oggi + yesterday: Ieri + members_view: + filter_placeholder: Trova membri about_view: + associated_topic: Argomento collegato + associated_category: Categoria collegata + title: Titolo description: Descrizione channel_info: + back_to_all_channels: "Tutti i canali" back_to_channel: "Indietro" + tabs: + about: Informazioni + members: Membri + settings: Impostazioni + channel_edit_title_modal: + title: Modifica titolo + input_placeholder: Aggiungi un titolo + description: Assegna un breve titolo descrittivo al tuo canale + channel_edit_description_modal: + title: Modifica descrizione + input_placeholder: Aggiungi una descrizione + description: Spiega agli altri di cosa si occupa questo canale + direct_message_creator: + title: Nuovo messaggio + prefix: "A:" + no_results: Nessun risultato + selected_user_title: "Deseleziona %{username}" channel_selector: title: "Vai al canale" no_channels: "Nessun canale corrisponde alla tua ricerca" + channel: + no_memberships: Questo canale non ha membri + no_memberships_found: Nessun membro trovato + memberships_count: + one: "%{count} membro" + other: "%{count} membri" create_channel: + auto_join_users: + public_category_warning: "%{category} è una categoria pubblica. Aggiungere automaticamente tutti gli utenti attivi di recente a questo canale?" + warning_groups: + one: Aggiungere automaticamente %{members_count} utenti da %{group}? + other: Aggiungere automaticamente %{members_count} utenti da %{group} e %{group_2}? + warning_multiple_groups: Aggiungere automaticamente %{members_count} utenti da %{group_1} e altri %{count}? choose_category: label: "Scegli una categoria" none: "selezionane una..." default_hint: Gestisci l'accesso visitando le impostazioni di sicurezza di %{category} + hint_groups: + one: Gli utenti in %{hint} avranno accesso a questo canale in base alle impostazioni di sicurezza + other: Gli utenti in %{hint} e %{hint_2} avranno accesso a questo canale in base alle impostazioni di sicurezza + hint_multiple_groups: Gli utenti in %{hint_1} e altri %{count} gruppi avranno accesso a questo canale in base alle impostazioni di sicurezza create: "Crea canale" description: "Descrizione (facoltativa)" name: "Nome del canale" + title: "Nuovo canale" type: "Tipo" types: category: "Categoria" @@ -120,13 +255,23 @@ it: you_others_and_more: "Tu, %{usernames} e altri %{more} avete reagito con :%{emoji}:" composer: toggle_toolbar: "Attiva barra degli strumenti" + italic_text: "testo in evidenza" + bold_text: "testo in grassetto" + code_text: "testo di codice" + quote: + original_channel: 'Inviato in origine in #%{channel}' + copy_success: "Citazione della chat copiata negli appunti" notification_levels: never: "Mai" mention: "Solo per le menzioni" always: "Per tutte le attività" settings: + enable_auto_join_users: "Aggiungi automaticamente tutti gli utenti attivi di recente" + disable_auto_join_users: "Smetti di aggiungere automaticamente gli utenti" + auto_join_users_warning: "Tutti gli utenti che non sono membri di questo canale e hanno accesso alla categoria %{category} parteciperanno. Vuoi procedere?" desktop_notification_level: "Notifiche sul desktop" follow: "Partecipa" + followed: "Partecipante" mobile_notification_level: "Notifiche push su dispositivi mobili" mute: "Silenzia canale" muted_on: "On" @@ -143,6 +288,7 @@ it: new: "Nuova chat personale" create: "Vai" leave: "Lascia questa chat personale" + cannot_create: "Spiacenti, non puoi inviare messaggi diretti." incoming_webhooks: back: "Indietro" channel_placeholder: "Seleziona un canale" @@ -165,10 +311,15 @@ it: system: "sistema" title: "Webhook in ingresso" url: "URL" + url_instructions: "Questo URL contiene un valore segreto: tienilo al sicuro." username: "Nome utente" username_instructions: "Nome utente del bot che pubblica sul canale. Il valore predefinito è 'system' se l'impostazione è lasciata vuota." + instructions: "I webhook in entrata possono essere utilizzati da sistemi esterni per pubblicare messaggi in un canale di chat designato come utente bot tramite l'endpoint/hooks/:key. Il carico utile è costituito da un solo parametro di testo, che è limitato a 2000 caratteri.testo con formattazione Slack limitata, l'estrazione di link e menzioni in base al formato su https://api. lack.com/reference/surfaces/formatting, ma l'endpoint /hooks/:key/slack deve essere utilizzato per questo."
selection:
cancel: "Annulla"
+ quote_selection: "Cita in argomento"
+ copy: "Copia"
+ move_selection_to_channel: "Sposta nel canale"
error: "Si è verificato un errore durante lo spostamento dei messaggi di chat"
title: "Sposta la chat nell'argomento"
new_topic:
@@ -176,11 +327,13 @@ it:
instructions:
one: "Stai per creare un nuovo argomento, inserendovi il messaggio di chat che hai selezionato."
other: "Stai per creare un nuovo argomento, inserendovi i %{count} messaggi di chat che hai selezionato."
+ instructions_channel_archive: "Stai per creare un nuovo argomento e archiviare in esso i messaggi del canale."
existing_topic:
title: "Sposta in argomento esistente"
instructions:
one: "Scegli l'argomento in cui intendi spostare il messaggio di chat."
other: "Scegli l'argomento in cui intendi spostare questi %{count} messaggi di chat."
+ instructions_channel_archive: "Scegli l'argomento in cui intendi archiviare i messaggi del canale."
new_message:
title: "Sposta in un nuovo messaggio"
instructions:
@@ -196,12 +349,51 @@ it:
public: "La cronologia del canale è conservata per %{days} giorni."
dm: "La cronologia della chat personale è conservata per %{days} giorni."
topic_button_title: "Chat"
+ flags:
+ off_topic: "Questo messaggio non è rilevante per la discussione in corso in base alla definizione del titolo del canale e probabilmente dovrebbe essere spostato altrove."
+ inappropriate: "Questo messaggio ha contenuti che chiunque considererebbe offensivi o ingiuriosi, oppure contiene violazioni delle nostre linee guida della community."
+ spam: "Questo messaggio è un annuncio pubblicitario o un atto vandalico. Non è utile o rilevante per il canale corrente."
+ notify_user: "Voglio parlare con questa persona direttamente e personalmente del suo messaggio."
+ notify_moderators: "Questo messaggio richiede l'attenzione dello staff per motivi diversi da quelli sopra indicati"
+ flagging:
+ action: "Segnala messaggio"
+ emoji_picker:
+ favorites: "Utilizzati di frequente"
+ smileys_&_emotion: "Faccine ed emoticon"
+ objects: "Oggetti"
+ people_&_body: "Persone e corpo"
+ travel_&_places: "Viaggi e luoghi"
+ animals_&_nature: "Animali e natura"
+ food_&_drink: "Cibo e bevande"
+ activities: "Attività"
+ flags: "Segnalazioni"
+ symbols: "Simboli"
+ search_placeholder: "Cerca per nome emoji e alias..."
+ no_results: "Nessun risultato"
+ draft_channel_screen:
+ header: "Nuovo messaggio"
+ cancel: "Annulla"
notifications:
+ chat_invitation: "ti ha inviato un invito a partecipare a un canale di chat"
+ chat_invitation_html: "%{username} ti ha inviato un invito a partecipare a un canale di chat"
+ chat_quoted: "%{username} %{description}"
popup:
+ chat_mention:
+ direct: 'ti ha menzionato in "%{channel}"'
+ direct_html: '%{username} ti ha menzionato in "%{channel}"'
+ other_plain: 'ha menzionato %{identifier} in "%{channel}"'
+ other_html: '%{username} ha menzionato %{identifier} in "%{channel}"'
+ direct_message_chat_mention:
+ direct: "ti ha menzionato nella chat personale"
+ direct_html: "%{username} ti ha menzionato nella chat personale"
+ other_plain: "ha menzionato %{identifier} nella chat personale"
+ other_html: "%{username} ha menzionato %{identifier} nella chat personale"
chat_message: "Nuovo messaggio di chat"
+ chat_quoted: "%{username} ha citato il tuo messaggio di chat"
titles:
chat_mention: "Menzione in chat"
chat_invitation: "Invito alla chat"
+ chat_quoted: "Chat citata"
action_codes:
chat:
enabled: '%{who} ha abilitato la %{when}'
@@ -219,6 +411,8 @@ it:
label: Mittente
description: Torna ai valori predefiniti di sistema
review:
+ transcript:
+ view: "Visualizza la trascrizione dei messaggi precedenti"
types:
reviewable_chat_message:
title: "Messaggio di chat segnalato"
@@ -226,3 +420,30 @@ it:
keyboard_shortcuts_help:
chat:
title: "Chat"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} Cambia canale"
+ open_quick_channel_selector: "%{shortcut} Apri il selettore rapido dei canali"
+ open_insert_link_modal: "%{shortcut} Inserisci collegamento ipertestuale (solo compositore)"
+ composer_bold: "%{shortcut} Grassetto (solo compositore)"
+ composer_italic: "%{shortcut} Corsivo (solo compositore)"
+ composer_code: "%{shortcut} Codice (solo compositore)"
+ drawer_open: "%{shortcut} Apri il cassetto della chat"
+ drawer_close: "%{shortcut} Chiudi il cassetto della chat"
+ topic_statuses:
+ chat:
+ help: "La chat è abilitata per questo argomento"
+ user:
+ allow_private_messages: "Consenti ad altri utenti di inviarmi messaggi personali e messaggi diretti in chat"
+ muted_users_instructions: "Elimina tutte le notifiche, i messaggi personali e i messaggi diretti della chat da questi utenti."
+ allowed_pm_users_instructions: "Consenti solo messaggi personali o messaggi diretti in chat da questi utenti."
+ allow_private_messages_from_specific_users: "Consenti solo a utenti specifici di inviarmi messaggi personali o messaggi diretti in chat"
+ ignored_users_instructions: "Elimina tutti i post, i messaggi, le notifiche, i messaggi personali e i messaggi diretti in chat di questi utenti."
+ user_menu:
+ no_chat_notifications_title: "Non hai ancora nessuna notifica di chat"
+ no_chat_notifications_body: >
+ Riceverai una notifica in questo pannello quando qualcuno ti invia un messaggio diretto o ti @menziona nella chat. Le notifiche verranno inviate anche alla tua e-mail quando non effettui l'accesso da un po' di tempo. チャンネルをアーカイブすると、チャンネルは読み取り専用モードになり、すべてのメッセージが新しいトピックまたは既存のトピックに移動されます。新しいメッセージを送信することはできません。また既存のメッセージの編集や削除も行えません。
%{channelTitle} チャンネルをアーカイブしてもよろしいですか?
" + process_started: "アーカイブ処理が開始されました。このモーダルは間もなく閉じられ、アーカイブ処理が完了すると個人メッセージが送信されます。" + retry: "再試行" + channel_open: + title: "チャンネルを開く" + instructions: "チャンネルを再開すると、すべてのユーザーがメッセージの送信と、既存のメッセージの編集を行えるようになります。" + channel_close: + title: "チャンネルを閉鎖" + instructions: "チャンネルを閉鎖すると、スタッフ以外のユーザーが新しいメッセージの送信や既存のメッセージの編集を行えなくなります。このチャンネルを閉鎖してもよろしいですか?" + channel_delete: + title: "チャンネルを削除" + instructions: "%{name} チャンネルとチャット履歴を削除します。すべてのメッセージと、リアクションやアップロードといった関連データは永久に削除されます。チャンネルの履歴を保持して閉鎖するには、チャンネルを削除ではなくアーカイブすることをお勧めします。
チャンネルを永久に削除してもよろしいですか?確定するには、チャンネルの名前を下のボックスに入力してください。
" + confirm: "結果を理解し、チャンネルを削除します" + confirm_channel_name: "チャンネル名を入力してください" + process_started: "チャンネルの削除処理が開始しました。このモーダルは間もなく閉じられ、削除されたチャンネルがどこにも表示されなくなります。" channels_list_popup: browse: "チャンネルを閲覧する" + create: "新しいチャンネル" click_to_join: "利用可能なチャンネルを表示するにはここをクリックします。" close: "閉じる" collapse: "チャットドロワーを折りたたむ" confirm_flag: "%{username} のメッセージを通報してよろしいですか?" deleted: "メッセージは削除されました。[view]" + hidden: "メッセージが非表示でした。[view]" delete: "削除" edited: "編集済み" + muted: "ミュート中" + joined: "参加中" empty_state: + direct_message_cta: "パーソナルチャットを開始" direct_message: "1 人または複数のユーザーとパーソナルチャットを開始することもできます。" + title: "チャンネルが見つかりません" email_frequency: + description: "15 分以上アクセスがない場合にのみメールで通知します。" never: "なし" + title: "メール通知" + when_away: "退席中の時のみ" enable: "チャットを有効にする" flag: "通報する" + emoji: "絵文字を挿入する" flagged: "このメッセージはレビュー目的で通報されました" invalid_access: "このチャットチャンネルを表示するためのアクセス権がありません" + invitation_notification: "%{username} があなたをチャットチャンネルに招待しました" in_reply_to: "返信先" heading: "チャット" join: "参加" @@ -53,25 +108,45 @@ ja: invite: "チャンネルに招待する" without_membership: other: "%{usernames} はこのチャンネルに参加していません。" + aria_roles: + header: "チャットヘッダー" + composer: "チャット作成ツール" + channels_list: "チャットのチャンネルリスト" no_public_channels: "どのチャンネルにも参加していません。" only_chat_push_notifications: title: "チャットのプッシュ通知のみ送信する" description: "チャット以外のすべてのプッシュ通知の送信をブロックします" + ignore_channel_wide_mention: + title: "チャンネル全体のメンションを無視する" + description: "チャット全体のメンション(@here および @all)の通知を送信しません" open: "チャットを開く" open_full_page: "全画面チャットを開く" + close_full_page: "全画面チャットを閉じる" open_message: "メッセージをチャットで開く" placeholder_self: "メモを書き留める" placeholder_others: "%{messageRecipient} とチャット" + placeholder_new_message_disallowed: "チャンネルは %{status} です。現在、新しいメッセージを送信できません。" + placeholder_silenced: "現在、メッセージを送信できません。" + placeholder_start_conversation: '%{usernames} と会話を始める' remove_upload: "ファイルを削除する" react: "絵文字でリアクション" reply: "返信" edit: "編集" copy_link: "リンクをコピーする" rebake_message: "HTML を再構築" + retry_staged_message: + title: "ネットワークエラー" + action: "もう一度送信しますか?" + unreliable_network: "ネットワークが不安定です。メッセージの送信と下書きの保存が機能しない可能性があります" + bookmark_message: "ブックマーク" + bookmark_message_edit: "ブックマークを編集" restore: "削除されたメッセージを復元する" save: "保存" select: "選択" + silence: "ユーザーを投稿禁止にする" + return_to_list: "チャンネルリストに戻る" scroll_to_bottom: "一番下にスクロール" + scroll_to_new_messages: "新しいメッセージを見る" sound: title: "デスクトップチャットの通知音" sounds: @@ -85,23 +160,79 @@ ja: other: "%{count} 個のファイル" you_flagged: "このメッセージを通報しました" exit: "戻る" + channel_status: + read_only_header: "チャンネルは読み取り専用です" + read_only: "読み取り専用" + archived_header: "チャンネルはアーカイブされています" + archived: "アーカイブ済み" + archive_failed: "チャンネルのアーカイブに失敗しました。%{completed} / 全 %{total} 件のメッセージがアーカイブ先のトピックにアーカイブされました。再試行を押して、アーカイブの完了を試してください。" + archive_completed: "アーカイブ済みのトピックを見る" + closed_header: "チャンネルは閉鎖されています" + closed: "閉鎖" + open_header: "チャンネルは開いています" + open: "オープン" browse: title: チャンネル + filter_all: すべて + filter_open: オープン + filter_closed: 閉鎖 + filter_archived: アーカイブ済み + filter_input_placeholder: チャンネル名で検索 + chat_message_separator: + today: 今日 + yesterday: 昨日 + members_view: + filter_placeholder: メンバーを検索 about_view: + associated_topic: リンクされたトピック + associated_category: リンクされたカテゴリ + title: タイトル description: 説明 channel_info: + back_to_all_channels: "すべてのチャンネル" back_to_channel: "戻る" + tabs: + about: 紹介 + members: メンバー + settings: 設定 + channel_edit_title_modal: + title: タイトルを編集する + input_placeholder: タイトルを追加する + description: チャンネルに説明的な短いタイトルを付けます + channel_edit_description_modal: + title: 説明を編集する + input_placeholder: 説明を追加する + description: このチャンネルの紹介を入力します + direct_message_creator: + title: 新しいメッセージ + prefix: "宛先:" + no_results: 結果がありません + selected_user_title: "%{username} を選択解除" channel_selector: title: "チャンネルにジャンプ" no_channels: "検索に一致するチャンネルはありません" + channel: + no_memberships: このチャンネルにはメンバーがいません + no_memberships_found: メンバーが見つかりません + memberships_count: + other: "%{count} 人のメンバー" create_channel: + auto_join_users: + public_category_warning: "%{category} は公開カテゴリです。最近アクティブなすべてのユーザーをこのチャンネルに自動的に追加しますか?" + warning_groups: + other: '%{group} と %{group_2} から %{members_count} 人のユーザーを自動的に追加しますか?' + warning_multiple_groups: '%{group_1} の %{members_count} 人のユーザーと他 %{count} 人を自動的に追加しますか?' choose_category: label: "カテゴリを選択する" none: "1 つ選択してください..." default_hint: %{category} のセキュリティ設定に移動し、アクセス権を管理します + hint_groups: + other: '%{hint} と %{hint_2} ユーザーは、セキュリティ設定に従って、このチャンネルにアクセスできます。' + hint_multiple_groups: '%{hint_1} のユーザー他 %{count} 個のグループのユーザーは、セキュリティ設定に従って、このチャンネルにアクセスできます。' create: "チャンネルを作成" description: "説明(オプション)" name: "チャンネル名" + title: "新しいチャンネル" type: "タイプ" types: category: "カテゴリ" @@ -116,13 +247,23 @@ ja: you_others_and_more: "あなた、%{usernames} と他 %{more} 人が :%{emoji}: でリアクションしました" composer: toggle_toolbar: "ツールバーの切り替え" + italic_text: "強調文字" + bold_text: "太字" + code_text: "コードテキスト" + quote: + original_channel: '最初に %{channel} で送信されました' + copy_success: "チャットの引用をクリップボードにコピーしました" notification_levels: never: "なし" mention: "メンションのみ" always: "すべてのアクティビティ" settings: + enable_auto_join_users: "最近アクティブなユーザーをすべて自動的に追加する" + disable_auto_join_users: "ユーザーの自動追加を停止する" + auto_join_users_warning: "このチャンネルのメンバーでなく、%{category} カテゴリにアクセスできるすべてのユーザーが参加します。よろしいですか?" desktop_notification_level: "デスクトップ通知" follow: "参加" + followed: "参加中" mobile_notification_level: "モバイルプッシュ通知" mute: "チャンネルをミュート" muted_on: "オン" @@ -139,6 +280,7 @@ ja: new: "新しいパーソナルチャット" create: "開始" leave: "このパーソナルチャットから退出する" + cannot_create: "ダイレクトメッセージを送信できません。" incoming_webhooks: back: "戻る" channel_placeholder: "チャンネルを選択する" @@ -161,20 +303,27 @@ ja: system: "システム" title: "着信 Webhook" url: "URL" + url_instructions: "この URL には秘密の値が含まれます。安全に保管してください。" username: "ユーザー名" username_instructions: "チャンネルに投稿するボットのユーザー名。空白のままにすると、デフォルトで「システム」になります。" + instructions: "受信 Webhook は、外部システムが/hooks/:key エンドポイントを介して指定されたチャットチャンネルにボットユーザーとしてメッセージを投稿する際に使用できます。ペイロードは、2000 文字に制限された単一の text パラメーターで構成されます。text パラメーターも制限付きでサポートしており、https://api.slack.com/reference/surfaces/formatting のフォーマットに基いてリンクとメンションが抽出されますが、これには、/hooks/:key/slack エンドポイントを使用する必要があります。"
selection:
cancel: "キャンセル"
+ quote_selection: "トピックで引用"
+ copy: "コピー"
+ move_selection_to_channel: "チャンネルに移動"
error: "チャットメッセージを移動中にエラーが発生しました"
title: "チャットをトピックに移動"
new_topic:
title: "新しいトピックに移動"
instructions:
other: "新しいトピックを作成し、選択した %{count} 件のチャットメッセージを挿入しようとしています。"
+ instructions_channel_archive: "新しいトピックを作成し、それにチャンネルメッセージをアーカイブしようとしています。"
existing_topic:
title: "既存のトピックに移動"
instructions:
other: "それらの %{count} 件のチャットメッセージを移動するトピックを選択してください。"
+ instructions_channel_archive: "チャンネルメッセージのアーカイブ先のトピックを選択してください。"
new_message:
title: "新しいメッセージに移動"
instructions:
@@ -188,12 +337,51 @@ ja:
public: "チャンネル履歴は %{days} 日間保持されます。"
dm: "パーソナルチャット履歴は %{days} 日間保持されます。"
topic_button_title: "チャット"
+ flags:
+ off_topic: "このメッセージは、チャンネルのタイトルで定義された現在のディスカッションとは関係がないため、おそらく別の場所に移動する必要があります。"
+ inappropriate: "このメッセージには、合理的な人が攻撃的、虐待的、またはコミュニティーガイドラインに違反すると見なすコンテンツが含まれます。"
+ spam: "このメッセージは広告または荒らし行為です。現在のチャンネルに有益な内容でなく、関連性もありません。"
+ notify_user: "この人のメッセージについて、この人に直接個人的に話を聞きたいと思います。"
+ notify_moderators: "このメッセージには、上記以外の理由でスタッフの注意が必要です。"
+ flagging:
+ action: "メッセージを通報する"
+ emoji_picker:
+ favorites: "よく使用される絵文字"
+ smileys_&_emotion: "顔文字と感情"
+ objects: "物"
+ people_&_body: "人と体"
+ travel_&_places: "旅行と場所"
+ animals_&_nature: "動物と自然"
+ food_&_drink: "食べ物と飲み物"
+ activities: "活動"
+ flags: "旗"
+ symbols: "記号"
+ search_placeholder: "絵文字名とエイリアスで検索..."
+ no_results: "結果がありません"
+ draft_channel_screen:
+ header: "新しいメッセージ"
+ cancel: "キャンセル"
notifications:
+ chat_invitation: "があなたをチャットチャンネルに招待しました"
+ chat_invitation_html: "%{username} があなたをチャットチャンネルに招待しました"
+ chat_quoted: "%{username} %{description}"
popup:
+ chat_mention:
+ direct: 'があなたを "%{channel}" でメンションしました'
+ direct_html: '%{username} があなたを "%{channel}" でメンションしました'
+ other_plain: 'が %{identifier} を "%{channel}" でメンションしました'
+ other_html: '%{username} が %{identifier} を "%{channel}" でメンションしました'
+ direct_message_chat_mention:
+ direct: "があなたをパーソナルチャットでメンションしました"
+ direct_html: "%{username} があなたをパーソナルチャットでメンションしました"
+ other_plain: "が %{identifier} をパーソナルチャットでメンションしました"
+ other_html: "%{username} が %{identifier} をパーソナルチャットでメンションしました"
chat_message: "新しいチャットメッセージ"
+ chat_quoted: "%{username} があなたのチャットメッセージを引用しました"
titles:
chat_mention: "チャットのメンション"
chat_invitation: "チャットの招待状"
+ chat_quoted: "チャットが引用されました"
action_codes:
chat:
enabled: '%{who} がを有効にしました: %{when}'
@@ -211,6 +399,8 @@ ja:
label: 送信者
description: デフォルトはシステムです
review:
+ transcript:
+ view: "前のメッセージのトランスクリプトを表示"
types:
reviewable_chat_message:
title: "通報されたチャットメッセージ"
@@ -218,3 +408,29 @@ ja:
keyboard_shortcuts_help:
chat:
title: "チャット"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} チャンネルを切り替える"
+ open_quick_channel_selector: "%{shortcut} クイックチャンネルセレクターを開く"
+ open_insert_link_modal: "%{shortcut} ハイパーリンクを挿入(作成ツールのみ)"
+ composer_bold: "%{shortcut} 太字(作成ツールのみ)"
+ composer_italic: "%{shortcut} 斜体(作成ツールのみ)"
+ composer_code: "%{shortcut} コード(作成ツールのみ)"
+ drawer_open: "%{shortcut} チャットドロワーを開く"
+ drawer_close: "%{shortcut} チャットドロワーを閉じる"
+ topic_statuses:
+ chat:
+ help: "このトピックのチャットは有効になっています"
+ user:
+ allow_private_messages: "他のユーザーが個人メッセージとチャットダイレクトメッセージを自分に送信することを許可する"
+ muted_users_instructions: "これらのユーザーからのすべての通知、個人メッセージ、およびチャットダイレクトメッセージを非表示にします。"
+ allowed_pm_users_instructions: "これらのユーザーからの個人メッセージまたはチャットダイレクトメッセージのみを許可します。"
+ allow_private_messages_from_specific_users: "特定のユーザーのみが個人メッセージまたはチャットダイレクトメッセージを自分に送信することを許可する"
+ ignored_users_instructions: "これらのユーザーからのすべての投稿、メッセージ、通知、個人メッセージ、およびチャットダイレクトメッセージを非表示にします。"
+ user_menu:
+ no_chat_notifications_title: "チャット通知はまだありません"
+ no_chat_notifications_body: >
+ 誰かがあなたにダイレクトメッセージを送信したり、チャットであなたを @メンションすると、このパネルに通知されます。あなたがしばらくログインしていない場合、通知はメールでも送信されます。O arquivamento de um canal o coloca em modo somente leitura e move todas as mensagens do canal para um tópico novo ou existente. Nenhuma mensagem nova pode ser enviada e nenhuma mensagem existente pode ser editada ou excluída.
Tem certeza de que deseja arquivar o canal %{channelTitle}?
" + process_started: "O processo de arquivamento foi iniciado. Este modal será encerrado em breve e você receberá uma mensagem pessoal quando o processo de arquivamento for concluído." + retry: "Tentar novamente" + channel_open: + title: "Abrir Canal" + instructions: "Reabre o canal, todos os usuários poderão enviar mensagens e editar suas mensagens existentes." + channel_close: + title: "Fechar Canal" + instructions: "Fechar o canal impede que usuários não funcionários enviem novas mensagens ou editem mensagens existentes. Tem certeza de que deseja fechar este canal?" + channel_delete: + title: "Excluir Canal" + instructions: "Exclui o canal %{name} e o histórico do chat. Todas as mensagens e dados relacionados, como reações e envios, serão excluídos permanentemente. Se você quiser preservar o histórico do canal e desativá-lo, talvez seja melhor arquivar o canal.
Tem certeza de que deseja excluir permanentemente o canal? Para confirmar, digite o nome do canal na caixa abaixo.
" + confirm: "Eu entendo as consequências, exclua o canal" + confirm_channel_name: "Digite o nome do canal" + process_started: "O processo para excluir o canal foi iniciado. Este modal será fechado em breve, você não verá mais o canal excluído em lugar algum." channels_list_popup: browse: "Navegar por canais" + create: "Novo canal" click_to_join: "Clique aqui para visualizar os canais disponíveis." close: "Fechar" collapse: "Recolher gaveta de chat" confirm_flag: "Tem certeza de que deseja sinalizar a mensagem de %{username}?" deleted: "Uma mensagem foi excluída. [view]" + hidden: "Uma mensagem foi ocultada. [view]" delete: "Excluir" edited: "editou" + muted: "silenciado" + joined: "entrou" empty_state: + direct_message_cta: "Inicie um chat pessoal" direct_message: "Você também pode iniciar um chat pessoal com um(as) ou mais usuários(as)." + title: "Nenhum canal encontrado" email_frequency: + description: "Só enviaremos um e-mail mediante ausência nos últimos 15 minutos." never: "Nunca" + title: "Notificações por e-mail" + when_away: "Só quando estiver ausente" enable: "Ativar chat" flag: "Sinalizar" + emoji: "Inserir emoji" flagged: "Esta mensagem foi sinalizada para revisão" invalid_access: "Você não tem acesso para ver este canal de chat" + invitation_notification: "%{username} convidou você para entrar em um canal de chat" in_reply_to: "Em resposta a" heading: "Chat" join: "Participar" @@ -63,15 +112,25 @@ pt_BR: without_membership: one: "%{usernames} não entrou neste canal." other: "%{usernames} não entraram neste canal." + aria_roles: + header: "Cabeçalho do chat" + composer: "Compositor de chat" + channels_list: "Lista de canais de chat" no_public_channels: "Você não entrou em nenhum canal." only_chat_push_notifications: title: "Enviar apenas notificações por push" description: "Bloquear envio de todas as notificações por push não relacionadas a chat" + ignore_channel_wide_mention: + title: "Ignorar menções em todo o canal" + description: "Não envie notificações para menções em todo o canal (@aqui e @todos)" open: "Abrir chat" open_full_page: "Abrir chat em tela cheia" + close_full_page: "Fechar chat em tela cheia" open_message: "Abrir mensagem no chat" placeholder_self: "Anotar algo" placeholder_others: "Conversar com %{messageRecipient}" + placeholder_new_message_disallowed: "O canal é %{status}, você não pode enviar novas mensagens agora." + placeholder_silenced: "Você não pode enviar mensagens neste momento." placeholder_start_conversation: Iniciar uma conversa com %{usernames} remove_upload: "Remover arquivo" react: "Reagir com emoji" @@ -79,10 +138,19 @@ pt_BR: edit: "Editar" copy_link: "Copiar link" rebake_message: "Reconstruir HTML" + retry_staged_message: + title: "Erro de rede" + action: "Enviar novamente?" + unreliable_network: "A rede não é confiável, enviar mensagens e salvar rascunho pode não funcionar" + bookmark_message: "Marcador" + bookmark_message_edit: "Editar marcador" restore: "Restaurar mensagem excluída" save: "Salvar" select: "Selecionar" + silence: "Silenciar usuário(a)" + return_to_list: "Retornar para lista de canais" scroll_to_bottom: "Rolar para a parte inferior" + scroll_to_new_messages: "Ver novas mensagens" sound: title: "Som de notificação do chat no desktop" sounds: @@ -98,10 +166,16 @@ pt_BR: you_flagged: "Você sinalizou esta mensagem" exit: "voltar" channel_status: + read_only_header: "O canal é somente para leitura" + read_only: "Somente leitura" archived_header: "O canal está arquivado" - archived: "Arquivado" + archived: "Arquivados" + archive_failed: "Falha no canal de arquivamento. %{completed}/%{total} mensagens foram arquivadas no tópico de destino. Pressione repetir para tentar concluir o arquivo." + archive_completed: "Veja o tópico de arquivo" closed_header: "Canal fechado" - closed: "Fechado" + closed: "Fechados" + open_header: "Canal aberto" + open: "Aberto" browse: title: Canais filter_all: Todos @@ -130,23 +204,43 @@ pt_BR: title: Editar título input_placeholder: Adicione um título description: Dê um breve título descritivo ao seu canal + channel_edit_description_modal: + title: Editar descrição + input_placeholder: Adicione uma descrição + description: Diga às pessoas do que se trata este canal direct_message_creator: + title: Novas mensagens prefix: "Para:" no_results: Nenhum resultado + selected_user_title: "Desmarcar %{username}" channel_selector: title: "Pular para o canal" no_channels: "Nenhum canal corresponde à sua pesquisa" channel: no_memberships: Este canal não tem membros no_memberships_found: Nenhum membro encontrado + memberships_count: + one: "%{count} membro" + other: "%{count} membros" create_channel: + auto_join_users: + public_category_warning: "%{category} é uma categoria pública. Deseja adicionar automaticamente todos os usuários recentemente ativos a este canal?" + warning_groups: + one: Adicionar automaticamente %{members_count} usuários de %{group}? + other: Adicionar automaticamente %{members_count} usuários de %{group} e %{group_2}? + warning_multiple_groups: Adicionar automaticamente %{members_count} usuários de %{group_1} e outros %{count}? choose_category: label: "Escolha uma categoria" none: "selecionar um..." default_hint: Gerencie o acesso ao acessar as %{category} configurações de segurança + hint_groups: + one: Os usuários em %{hint} terão acesso a este canal de acordo com as configurações de segurança + other: Os usuários em %{hint} e %{hint_2} terão acesso a este canal de acordo com as configurações de segurança + hint_multiple_groups: Os usuários em %{hint_1} e %{count} terão acesso a este canal de acordo com as configurações de segurança create: "Criar canal" description: "Descrição (opcional)" name: "Nome do canal" + title: "Novo canal" type: "Tipo" types: category: "Categoria" @@ -161,6 +255,12 @@ pt_BR: you_others_and_more: "Você, %{usernames} e mais %{more} reagiram com :%{emoji}:" composer: toggle_toolbar: "Ativar/desativar barra de ferramentas" + italic_text: "texto enfatizado" + bold_text: "texto forte" + code_text: "texto do código" + quote: + original_channel: 'Originalmente enviado em %{channel}' + copy_success: "Citação do chat copiada para área de transferência" notification_levels: never: "Nunca" mention: "Apenas para menções" @@ -168,8 +268,10 @@ pt_BR: settings: enable_auto_join_users: "Adicionar automaticamente todos os usuários ativos recentemente" disable_auto_join_users: "Parar de adicionar usuários automaticamente" + auto_join_users_warning: "Todos os usuários que não são membros deste canal e têm acesso à categoria %{category} participarão. Tem certeza?" desktop_notification_level: "Notificações do desktop" follow: "Participar" + followed: "Entrou" mobile_notification_level: "Notificações por push em dispositivos móveis" mute: "Silenciar canal" muted_on: "Ligado" @@ -186,6 +288,7 @@ pt_BR: new: "Novo chat pessoal" create: "Ir" leave: "Sair deste chat pessoal" + cannot_create: "Desculpe, você não pode enviar mensagens diretas." incoming_webhooks: back: "Voltar" channel_placeholder: "Selecione um canal" @@ -208,10 +311,15 @@ pt_BR: system: "sistema" title: "Webhooks recebidos" url: "URL" + url_instructions: "Esta URL contém um valor secreto - mantenha-o seguro." username: "Nome de usuário(a)" username_instructions: "Nome de usuário(a) de robô que posta no canal. Padrão para \"sistema\" quando deixado em branco." + instructions: "Os webhooks de entrada podem ser usados por sistemas externos para postar mensagens em um canal de chat designado como um usuário de bot por meio do endpoint/hooks/:key . A carga útil consiste em um único parâmetro text , limitado a 2.000 caracteres.text formatados pelo Slack, extraindo links e menções com base no formato em https://api.slack.com/reference/surfaces/formatting, mas /hooks/:key/ O endpoint do slack deve ser usado para isso."
selection:
cancel: "Cancelar"
+ quote_selection: "Citar no tópico"
+ copy: "Copiar"
+ move_selection_to_channel: "Mover para o canal"
error: "Houve um erro ao mover as mensagens de chat"
title: "Mover chat para tópico"
new_topic:
@@ -219,11 +327,13 @@ pt_BR:
instructions:
one: "Você está prestes a criar um novo tópico e preenchê-lo com a mensagem de chat selecionada."
other: "Você está prestes a criar um novo tópico e preenchê-lo com as %{count} mensagens de chat selecionadas."
+ instructions_channel_archive: "Você está prestes a criar um novo tópico e arquivar as mensagens do canal nele."
existing_topic:
title: "Mover para tópico existente"
instructions:
one: "Escolha o tópico para o qual você gostaria de mover a mensagem de chat."
other: "Escolha o tópico para o qual você gostaria de mover as %{count} mensagens de chat."
+ instructions_channel_archive: "Escolha o tópico para o qual você gostaria de arquivar as mensagens de canal."
new_message:
title: "Mover para nova mensagem"
instructions:
@@ -239,16 +349,51 @@ pt_BR:
public: "Histórico do canal é mantido por %{days} dias."
dm: "Histórico de conversas pessoais é mantido por %{days} dias."
topic_button_title: "Chat"
+ flags:
+ off_topic: "Esta mensagem não é relevante para a discussão atual, conforme definido pelo título do canal, e provavelmente deve ser movida para outro lugar."
+ inappropriate: "Esta mensagem contém conteúdo que uma pessoa razoável consideraria ofensivo, abusivo ou uma violação de nossas diretrizes da comunidade."
+ spam: "Esta mensagem é uma propaganda, ou vandalismo. Não é útil ou relevante para o canal atual."
+ notify_user: "Quero falar com essa pessoa diretamente sobre sua mensagem."
+ notify_moderators: "Esta mensagem requer atenção da equipe por outro motivo não listado acima."
+ flagging:
+ action: "Sinalizar mensagem"
emoji_picker:
+ favorites: "Usado frequentemente"
+ smileys_&_emotion: "Smileys e emoções"
+ objects: "Objetos"
+ people_&_body: "Pessoas e corpo"
+ travel_&_places: "Viagens e lugares"
+ animals_&_nature: "Animais e natureza"
+ food_&_drink: "Comida e bebida"
+ activities: "Atividades"
+ flags: "Sinalizações"
+ symbols: "Símbolos"
+ search_placeholder: "Pesquisar por nome de emoji e codinomes..."
no_results: "Nenhum resultado"
+ draft_channel_screen:
+ header: "Novas mensagens"
+ cancel: "Cancelar"
notifications:
+ chat_invitation: "convidou você para participar de um canal de chat"
+ chat_invitation_html: "%{username} convidou você para entrar em um canal de chat"
+ chat_quoted: "%{username} %{description}"
popup:
chat_mention:
direct: 'mencionou você em "%{channel}"'
+ direct_html: '%{username} convidou você em "%{channel}"'
+ other_plain: 'mencionou %{identifier} em "%{channel}"'
+ other_html: '%{username} mencionou %{identifier} em "%{channel}"'
+ direct_message_chat_mention:
+ direct: "mencionei você no chat pessoal"
+ direct_html: "%{username} mencionou você no chat pessoal"
+ other_plain: "mencionou %{identifier} no chat pessoal"
+ other_html: "%{username} mencionou %{identifier} no chat pessoal"
chat_message: "Nova mensagem de chat"
+ chat_quoted: "%{username} citou sua mensagem do chat"
titles:
chat_mention: "Menção no chat"
chat_invitation: "Convite para chat"
+ chat_quoted: "Chat citado"
action_codes:
chat:
enabled: '%{who} ativou o %{when}'
@@ -266,6 +411,8 @@ pt_BR:
label: Remetente
description: Padrões do sistema
review:
+ transcript:
+ view: "Ver transcrição de mensagens anteriores"
types:
reviewable_chat_message:
title: "Mensagem de chat sinalizada"
@@ -273,3 +420,30 @@ pt_BR:
keyboard_shortcuts_help:
chat:
title: "Chat"
+ keyboard_shortcuts:
+ switch_channel_arrows: "%{shortcut} Mudar de canal"
+ open_quick_channel_selector: "%{shortcut} Abrir o seletor rápido de canais"
+ open_insert_link_modal: "%{shortcut} Inserir hiperlink (somente compositor)"
+ composer_bold: "%{shortcut} Negrito (somente compositor)"
+ composer_italic: "%{shortcut} Itálico (somente compositor)"
+ composer_code: "%{shortcut} Código (somente compositor)"
+ drawer_open: "%{shortcut} Abrir a gaveta do chat"
+ drawer_close: "%{shortcut} Fechar a gaveta do chat"
+ topic_statuses:
+ chat:
+ help: "O chat está sativado para este tópico"
+ user:
+ allow_private_messages: "Permitir que outros usuários me enviem mensagens pessoais e mensagens diretas no chat"
+ muted_users_instructions: "Suprimir todas as notificações, mensagens pessoais e mensagens diretas no chat desses usuários."
+ allowed_pm_users_instructions: "Permitir apenas mensagens pessoais ou mensagens diretas no chat desses usuários."
+ allow_private_messages_from_specific_users: "Permitir apenas que usuários específicos me enviem mensagens pessoais ou mensagens diretas no chat"
+ ignored_users_instructions: "Suprimir todas as postagens, mensagens, notificações, mensagens pessoais e mensagens diretas no chat desses usuários."
+ user_menu:
+ no_chat_notifications_title: "Você ainda não tem notificações no chat"
+ no_chat_notifications_body: >
+ Você receberá uma notificação neste painel quando alguém enviar uma mensagem ou @mencionar você no chat. As notificações também serão enviadas para o seu e-mail quando você não fizer login por um tempo. /hooks/:key. Полезная нагрузка состоит из одного параметра text, который ограничен 2000 символами.текстовые параметры, извлекая ссылки и упоминания на основе формата https://api.slack.com/reference/surfaces/formatting, но для этого необходимо использовать конечную точку /hooks/:key/slack."
selection:
- cancel: "Отменить"
+ cancel: "Отмена"
quote_selection: "Цитировать в теме"
copy: "Копировать"
move_selection_to_channel: "Переместить в канал"
@@ -375,7 +375,7 @@ ru:
topic_button_title: "Чат"
flags:
off_topic: "Это сообщение не имеет отношения к текущему обсуждению, как указано в названии канала, и, вероятно, его следует переместить в другое место."
- inappropriate: "Это сообщение содержит контент, который разумный человек счел бы недопустимым, оскорбительным или нарушающим основные принципы нашего сообщества."
+ inappropriate: "Это сообщение содержит контент, который разумный человек счёл бы недопустимым, оскорбительным или нарушающим основные принципы нашего сообщества."
spam: "Это сообщение является рекламой. Оно не несёт полезной нагрузки или не имеет отношения к текущему каналу."
notify_user: "Я хочу поговорить с этим человеком напрямую и обсудить его сообщение."
notify_moderators: "Это сообщение требует внимания модератора по причине, не указанной выше."
@@ -388,12 +388,14 @@ ru:
people_&_body: "Люди и части тел"
travel_&_places: "Путешествия и места"
animals_&_nature: "Животные и природа"
- food_&_drink: "Еда и напитки"
activities: "Деятельность"
flags: "Флаги"
symbols: "Символы"
search_placeholder: "Поиск по названию эмодзи и псевдониму..."
no_results: "Нет результатов"
+ draft_channel_screen:
+ header: "Новое сообщение"
+ cancel: "Отмена"
notifications:
chat_invitation: "пригласил вас присоединиться к каналу чата"
chat_invitation_html: "Пользователь %{username} пригласил вас присоединиться к каналу"
@@ -402,12 +404,12 @@ ru:
chat_mention:
direct: 'упомянул вас в "%{channel}"'
direct_html: 'Пользователь %{username} упомянул вас на канале "%{channel}"'
- other: 'упомянул %{identifier} в "%{channel}"'
+ other_plain: 'упомянул %{identifier} в канале "%{channel}"'
other_html: 'Пользователь %{username} упомянул %{identifier} на канале "%{channel}"'
direct_message_chat_mention:
direct: "упомянул вас в личном чате"
direct_html: "Пользователь %{username} упомянул вас в личном чате"
- other: "упомянул %{identifier} в личном чате"
+ other_plain: "упомянул %{identifier} в личном чате"
other_html: "Пользователь %{username} упомянул @%{identifier} в личном чате"
chat_message: "Новое сообщение в чате"
chat_quoted: "Пользователь %{username} процитировал ваше сообщение в чате"
diff --git a/plugins/chat/config/locales/client.sk.yml b/plugins/chat/config/locales/client.sk.yml
index 6f81562408..8334d004da 100644
--- a/plugins/chat/config/locales/client.sk.yml
+++ b/plugins/chat/config/locales/client.sk.yml
@@ -5,3 +5,100 @@
# https://translate.discourse.org/
sk:
+ js:
+ chat:
+ create: "Vytvoriť"
+ cancel: "Zrušiť"
+ channel_settings:
+ edit: "Upraviť"
+ add: "Pridať"
+ join: "Pridať sa"
+ leave: "Opustiť"
+ close: "Zavrieť"
+ delete: "Odstrániť"
+ muted: "ignorovaní"
+ joined: "vytvorený"
+ email_frequency:
+ never: "Nikdy"
+ flag: "Označenie"
+ join: "Pridať sa"
+ mention_warning:
+ dismiss: "zahodiť"
+ reply: "Odpoveď"
+ edit: "Upraviť"
+ rebake_message: "Pregenerovať HTML"
+ bookmark_message: "Záložka"
+ save: "Uložiť"
+ sounds:
+ none: "Žiadny"
+ exit: "späť"
+ channel_status:
+ closed: "Zatvorené"
+ open: "Zahájiť"
+ browse:
+ back: "Späť"
+ filter_all: Všetky
+ filter_closed: Zatvorené
+ chat_message_separator:
+ today: Dnes
+ yesterday: Včera
+ about_view:
+ title: Názov
+ description: Popis
+ channel_info:
+ back_to_channel: "Späť"
+ tabs:
+ about: O stránke
+ members: Členovia
+ settings: Nastavenia
+ direct_message_creator:
+ title: Nová správa
+ prefix: "Komu:"
+ create_channel:
+ type: "Typ"
+ types:
+ category: "Kategória"
+ topic: "Témy"
+ composer:
+ italic_text: "zdôraznený text"
+ bold_text: "výrazný text"
+ notification_levels:
+ never: "Nikdy"
+ settings:
+ follow: "Pridať sa"
+ followed: "Vytvorený"
+ notifications: "Upozornenia"
+ preview: "Ukážka"
+ save: "Uložiť"
+ saved: "Uložené"
+ unfollow: "Opustiť"
+ incoming_webhooks:
+ back: "Späť"
+ description: "Popis"
+ delete: "Odstrániť"
+ emoji: "Emoji"
+ name: "Meno"
+ save: "Uložiť"
+ edit: "Upraviť"
+ system: "systém"
+ url: "URL"
+ username: "Používateľské meno"
+ selection:
+ cancel: "Zrušiť"
+ copy: "Kopírovať"
+ new_topic:
+ title: "Presuň na novú tému"
+ existing_topic:
+ title: "Presuň do existujúcej témy."
+ emoji_picker:
+ objects: "Objekty"
+ flags: "Označenia"
+ draft_channel_screen:
+ header: "Nová správa"
+ cancel: "Zrušiť"
+ discourse_automation:
+ scriptables:
+ send_chat_message:
+ fields:
+ message:
+ label: Správa
diff --git a/plugins/chat/config/locales/client.sl.yml b/plugins/chat/config/locales/client.sl.yml
index 23489a48b1..1f9ced9dba 100644
--- a/plugins/chat/config/locales/client.sl.yml
+++ b/plugins/chat/config/locales/client.sl.yml
@@ -5,3 +5,111 @@
# https://translate.discourse.org/
sl:
+ js:
+ chat:
+ create: "Ustvari"
+ cancel: "Prekliči"
+ channel_settings:
+ edit: "Uredi"
+ add: "Dodaj"
+ join: "Pridruži se"
+ leave: "Zapusti"
+ close: "Zapri"
+ delete: "Izbriši"
+ edited: "urejen"
+ muted: "utišani"
+ joined: "pridružen"
+ email_frequency:
+ never: "Nikoli"
+ flag: "Prijavi"
+ join: "Pridruži se"
+ mention_warning:
+ dismiss: "opusti"
+ reply: "Odgovori"
+ edit: "Uredi"
+ rebake_message: "Obnovi HTML"
+ bookmark_message: "Zaznamek"
+ save: "Shrani"
+ sounds:
+ none: "Brez"
+ exit: "nazaj"
+ channel_status:
+ closed: "Zaprto"
+ open: "Odpri"
+ browse:
+ back: "Nazaj"
+ filter_all: Vse
+ filter_closed: Zaprto
+ chat_message_separator:
+ today: Danes
+ yesterday: Včeraj
+ about_view:
+ title: Naziv
+ description: Opis
+ channel_info:
+ back_to_channel: "Nazaj"
+ tabs:
+ about: O nas
+ members: Člani
+ settings: Nastavitve
+ direct_message_creator:
+ title: Novo zasebno sporočilo
+ prefix: "Do:"
+ create_channel:
+ type: "Tip"
+ types:
+ category: "Kategorija"
+ topic: "Tema"
+ composer:
+ italic_text: "poudarjeno"
+ bold_text: "krepko"
+ notification_levels:
+ never: "Nikoli"
+ settings:
+ follow: "Pridruži se"
+ followed: "Pridružen"
+ notifications: "Obvestila"
+ preview: "Predogled"
+ save: "Shrani"
+ saved: "Shranjeno"
+ unfollow: "Zapusti"
+ incoming_webhooks:
+ back: "Nazaj"
+ description: "Opis"
+ delete: "Izbriši"
+ emoji: "Emoji"
+ name: "Ime"
+ save: "Shrani"
+ edit: "Uredi"
+ system: "sistem"
+ url: "URL"
+ username: "Uporabniško ime"
+ selection:
+ cancel: "Prekliči"
+ copy: "Kopiraj"
+ new_topic:
+ title: "Prestavi v novo temo"
+ existing_topic:
+ title: "Prestavi v obstoječo temo"
+ new_message:
+ title: "Prestavi v novo ZS"
+ emoji_picker:
+ objects: "Stvari"
+ activities: "Dejavnosti"
+ flags: "Zastave"
+ symbols: "Simboli"
+ draft_channel_screen:
+ header: "Novo zasebno sporočilo"
+ cancel: "Prekliči"
+ notifications:
+ chat_quoted: "%{username} %{description}"
+ discourse_automation:
+ scriptables:
+ send_chat_message:
+ fields:
+ message:
+ label: Opozorilo
+ review:
+ types:
+ reviewable_chat_message:
+ flagged_by: "Prijavljen od"
diff --git a/plugins/chat/config/locales/client.sq.yml b/plugins/chat/config/locales/client.sq.yml
index 7f051b7a7c..9bd366f3be 100644
--- a/plugins/chat/config/locales/client.sq.yml
+++ b/plugins/chat/config/locales/client.sq.yml
@@ -5,3 +5,91 @@
# https://translate.discourse.org/
sq:
+ js:
+ chat:
+ cancel: "Anulo"
+ channel_settings:
+ edit: "Redakto"
+ add: "Shto"
+ close: "Mbyll"
+ delete: "Fshij"
+ muted: "të heshtur"
+ joined: "anëtarësuar"
+ email_frequency:
+ never: "Asnjëherë"
+ flag: "Sinjalizoni"
+ mention_warning:
+ dismiss: "hiqe"
+ reply: "Përgjigju"
+ edit: "Redakto"
+ rebake_message: "Rindërtoni HTML"
+ bookmark_message: "Shto tek të preferuarat"
+ save: "Ruaj"
+ sounds:
+ none: "Asnjë"
+ exit: "kthehu mbrapa"
+ channel_status:
+ open: "Fillo"
+ browse:
+ back: "Kthehu mbrapa"
+ filter_all: Të Gjithë
+ chat_message_separator:
+ today: Sot
+ yesterday: Dje
+ about_view:
+ title: Titulli
+ description: Përshkrimi
+ channel_info:
+ back_to_channel: "Kthehu mbrapa"
+ tabs:
+ about: Rreth
+ members: Anëtarë
+ settings: Rregullimet
+ direct_message_creator:
+ title: Mesazh i ri
+ prefix: "Për:"
+ create_channel:
+ type: "Lloji"
+ types:
+ category: "Kategori"
+ topic: "Topic"
+ composer:
+ italic_text: "tekst i theksuar"
+ bold_text: "tekst i trashë"
+ notification_levels:
+ never: "Asnjëherë"
+ settings:
+ followed: "Anëtarësuar"
+ notifications: "Njoftimet"
+ preview: "Parashikimi"
+ save: "Ruaj"
+ saved: "U ruajt"
+ incoming_webhooks:
+ back: "Kthehu mbrapa"
+ description: "Përshkrimi"
+ delete: "Fshije"
+ emoji: "Emoji"
+ name: "Emri juaj"
+ save: "Ruaj"
+ edit: "Redakto"
+ system: "sistem"
+ url: "URL"
+ username: "Emri i përdoruesit"
+ selection:
+ cancel: "Anulo"
+ copy: "Kopjo"
+ new_topic:
+ title: "Ktheje në një temë të re"
+ existing_topic:
+ title: "Transfero tek një Temë tjetër"
+ emoji_picker:
+ flags: "Sinjalizime"
+ draft_channel_screen:
+ header: "Mesazh i ri"
+ cancel: "Anulo"
+ discourse_automation:
+ scriptables:
+ send_chat_message:
+ fields:
+ message:
+ label: Mesazh
diff --git a/plugins/chat/config/locales/client.sr.yml b/plugins/chat/config/locales/client.sr.yml
index 51f68df4d6..6659cccd58 100644
--- a/plugins/chat/config/locales/client.sr.yml
+++ b/plugins/chat/config/locales/client.sr.yml
@@ -9,3 +9,88 @@ sr:
chat:
dates:
time_tiny: "h:mm"
+ cancel: "Odustani"
+ channel_settings:
+ edit: "Izmeni"
+ add: "Dodaj"
+ close: "Zatvori"
+ delete: "Obriši"
+ muted: "utišano"
+ joined: "pridružen"
+ email_frequency:
+ never: "Nikad"
+ flag: "Označi Zastavom"
+ mention_warning:
+ dismiss: "одбаци"
+ reply: "Odgovori"
+ edit: "Izmeni"
+ rebake_message: "Popravi HTML"
+ bookmark_message: "Markiraj"
+ bookmark_message_edit: "Uredi marker"
+ save: "Sačuvaj"
+ sounds:
+ none: "Ništa"
+ exit: "nazad"
+ channel_status:
+ open: "Otvori"
+ browse:
+ back: "Nazad"
+ filter_all: sve
+ chat_message_separator:
+ today: Danas
+ yesterday: Juče
+ about_view:
+ title: Naslov
+ description: Opis
+ channel_info:
+ back_to_channel: "Nazad"
+ tabs:
+ about: O nama
+ members: Članovi
+ settings: Podešavanja
+ direct_message_creator:
+ title: Nova privatna poruka
+ create_channel:
+ type: "Tip"
+ types:
+ category: "Kategorija"
+ topic: "Tema"
+ composer:
+ italic_text: "italic tekst"
+ bold_text: "boldovan tekst"
+ notification_levels:
+ never: "Nikad"
+ settings:
+ followed: "Pridružio"
+ notifications: "Obaveštenja"
+ save: "Sačuvaj"
+ saved: "Sačuvano"
+ incoming_webhooks:
+ back: "Nazad"
+ description: "Opis"
+ delete: "Obriši"
+ emoji: "Emoji"
+ name: "Ime foruma"
+ save: "Sačuvaj"
+ edit: "Izmeni"
+ system: "sistem"
+ url: "URL"
+ username: "Korisničko Ime"
+ selection:
+ cancel: "Odustani"
+ copy: "Kopija"
+ new_topic:
+ title: "Prebaci u Novu Temu"
+ existing_topic:
+ title: "Prebaci u Postojeću Temu"
+ emoji_picker:
+ flags: "Zastave"
+ draft_channel_screen:
+ header: "Nova privatna poruka"
+ cancel: "Odustani"
+ discourse_automation:
+ scriptables:
+ send_chat_message:
+ fields:
+ message:
+ label: Privatna poruka
diff --git a/plugins/chat/config/locales/client.sv.yml b/plugins/chat/config/locales/client.sv.yml
index c8f1475c07..f0f030f5bf 100644
--- a/plugins/chat/config/locales/client.sv.yml
+++ b/plugins/chat/config/locales/client.sv.yml
@@ -36,7 +36,7 @@ sv:
move_to_channel:
title: "Flytta meddelanden till kanal"
instructions:
- one: "Du flyttar 1 -meddelande. Välj en destinationskanal. Ett platshållarmeddelande kommer att skapas i kanalen %{channelTitle} för att indikera att detta meddelande har flyttats."
+ one: "Du flyttar %{count} meddelande. Välj en destinationskanal. Ett platshållarmeddelande kommer att skapas i kanalen %{channelTitle} för att indikera att detta meddelande har flyttats."
other: "Du flyttar %{count} meddelanden. Välj en destinationskanal. Ett platshållarmeddelande kommer att skapas i kanalen %{channelTitle} för att indikera att dessa meddelanden har flyttats."
confirm_move: "Flytta meddelanden"
channel_settings:
@@ -70,6 +70,7 @@ sv:
process_started: "Processen för att radera kanalen har påbörjats. Denna modal kommer att stängas inom kort och du kommer inte längre att se den raderade kanalen någonstans."
channels_list_popup:
browse: "Bläddra bland kanaler"
+ create: "Ny kanal"
click_to_join: "Klicka här för att se tillgängliga kanaler."
close: "Stäng"
collapse: "Komprimera chattruta"
@@ -78,8 +79,8 @@ sv:
hidden: "Ett meddelande doldes. [view]"
delete: "Radera"
edited: "redigerad"
- muted: "tystad"
- joined: "anslöt sig"
+ muted: "tystat"
+ joined: "gick med"
empty_state:
direct_message_cta: "Starta en personlig chatt"
direct_message: "Du kan också starta en personlig chatt med en eller flera användare."
@@ -91,6 +92,7 @@ sv:
when_away: "Endast när du är borta"
enable: "Aktivera chatt"
flag: "Flagga"
+ emoji: "Infoga emoji"
flagged: "Detta meddelande har flaggats för granskning"
invalid_access: "Du har inte tillgång till den här chattkanalen"
invitation_notification: "%{username} bjöd in dig att gå med i en chattkanal"
@@ -135,8 +137,12 @@ sv:
reply: "Svara"
edit: "Redigera"
copy_link: "Kopiera länk"
- rebake_message: "Omskapa HTML"
- bookmark_message: "Bokmärke"
+ rebake_message: "Generera HTML"
+ retry_staged_message:
+ title: "Nätverksfel"
+ action: "Skicka igen?"
+ unreliable_network: "Nätverket är opålitligt, det fungerar kanske inte att skicka meddelanden och spara utkast"
+ bookmark_message: "Bokmärk"
bookmark_message_edit: "Redigera bokmärke"
restore: "Återställ raderat meddelande"
save: "Spara"
@@ -169,8 +175,9 @@ sv:
closed_header: "Kanalen är stängd"
closed: "Stängd"
open_header: "Kanalen är öppen"
- open: "Öppen"
+ open: "Öppna"
browse:
+ back: "Tillbaka"
title: Kanaler
filter_all: Alla
filter_open: Öppnad
@@ -185,7 +192,7 @@ sv:
about_view:
associated_topic: Länkat ämne
associated_category: Länkad kategori
- title: Titel
+ title: Rubrik
description: Beskrivning
channel_info:
back_to_all_channels: "Alla kanaler"
@@ -214,25 +221,27 @@ sv:
no_memberships: Denna kanal har inga medlemmar
no_memberships_found: Inga medlemmar hittades
memberships_count:
- one: 1 medlem
+ one: "%{count} medlem"
other: "%{count} medlemmar"
create_channel:
auto_join_users:
+ public_category_warning: "%{category} är en offentlig kategori. Vill du automatiskt lägga till alla nyligen aktiva användare till den här kanalen?"
warning_groups:
- one: Lägg automatiskt till %{members_count} användare från %{group_1}?
- other: Lägg automatiskt till %{members_count} användare från %{group_1} och %{group_2}?
+ one: Lägg automatiskt till %{members_count} användare från %{group}?
+ other: Lägg automatiskt till %{members_count} användare från %{group} och %{group_2}?
warning_multiple_groups: Lägg automatiskt till %{members_count} användare från %{group_1} och %{count} andra?
choose_category:
label: "Välj en kategori"
none: "Välj en..."
default_hint: Hantera åtkomst genom att besöka %{category} säkerhetsinställningar
hint_groups:
- one: Användare i %{hint_1} kommer att ha åtkomst till denna kanal enligt säkerhetsinställningar
- other: Användare i %{hint_1} och %{hint_2} kommer att ha åtkomst till denna kanal enligt säkerhetsinställningarna
+ one: Användare i %{hint} kommer att ha åtkomst till denna kanal enligt säkerhetsinställningar
+ other: Användare i %{hint} och %{hint_2} kommer att ha åtkomst till denna kanal enligt säkerhetsinställningarna
hint_multiple_groups: Användare i %{hint_1} och %{count} andra grupper kommer att ha åtkomst till denna kanal enligt säkerhetsinställningarna
create: "Skapa kanal"
description: "Beskrivning (valfritt)"
name: "Kanalnamn"
+ title: "Ny kanal"
type: "Typ"
types:
category: "Kategori"
@@ -247,8 +256,8 @@ sv:
you_others_and_more: "Du, %{usernames} och %{more} andra reagerade med :%{emoji}:"
composer:
toggle_toolbar: "Växla verktygsfält"
- italic_text: "betonad text"
- bold_text: "stark text"
+ italic_text: "kursiv text"
+ bold_text: "fet text"
code_text: "kod text"
quote:
original_channel: 'Ursprungligen skickad i %{channel}'
@@ -258,10 +267,12 @@ sv:
mention: "Endast för omnämnanden"
always: "För all aktivitet"
settings:
+ enable_auto_join_users: "Lägg automatiskt till alla nyligen aktiva användare"
+ disable_auto_join_users: "Sluta lägga till användare automatiskt"
auto_join_users_warning: "Varje användare som inte är medlem i den här kanalen och har tillgång till kategorin %{category} kommer att gå med. Är du säker?"
desktop_notification_level: "Skrivbordsaviseringar"
follow: "Gå med"
- followed: "Anslöt sig"
+ followed: "Gick med"
mobile_notification_level: "Mobila push-meddelanden"
mute: "Tysta kanal"
muted_on: "På"
@@ -278,13 +289,14 @@ sv:
new: "Ny personlig chatt"
create: "Kör"
leave: "Lämna den här personliga chatten"
+ cannot_create: "Tyvärr kan du inte skicka direktmeddelanden."
incoming_webhooks:
back: "Tillbaka"
channel_placeholder: "Välj en kanal"
confirm_destroy: "Är du säker på att du vill ta bort denna inkommande webhook? Detta kan inte ångras."
current_emoji: "Nuvarande emoji"
description: "Beskrivning"
- delete: "Radera"
+ delete: "Ta bort"
emoji: "Emoji"
emoji_instructions: "Systemavatar kommer att användas om emoji lämnas tom."
name: "Namn"
@@ -300,8 +312,10 @@ sv:
system: "system"
title: "Inkommande webhooks"
url: "URL"
+ url_instructions: "Den här webbadressen innehåller ett hemligt värde - håll det säkert."
username: "Användarnamn"
username_instructions: "Användarnamn på bot som gör inlägg på kanalen. Standardinställning är 'system' när det lämnas tomt."
+ instructions: "Inkommande webhooks kan användas av externa system för att skicka meddelanden till en utsedd chattkanal som botanvändare via /hooks/:key -slutpunkten. Försändelsen består av en enda text-parameter, som är begränsad till 2000 tecken.text-parametrar, extraherar länkar och omnämnanden baserat på formatet på https://api.slack.com/reference/surfaces/formatting, men /hooks/:key/ slack-slutpunkten måste användas för detta."
selection:
cancel: "Avbryt"
quote_selection: "Citat i ämne"
@@ -336,8 +350,30 @@ sv:
public: "Kanalhistoriken behålls i %{days} dagar."
dm: "Personlig chatthistorik behålls i %{days} dagar."
topic_button_title: "Chatt"
+ flags:
+ off_topic: "Det här meddelandet är inte relevant för den aktuella diskussionen enligt kanaltiteln och bör förmodligen flyttas någon annanstans."
+ inappropriate: "Detta meddelande innehåller saker som en förnuftig person skulle anse vara stötande, kränkande eller en överträdelse av vårt forums riktlinjer."
+ spam: "Det här meddelandet är en annons eller vandalism. Det är inte lämpligt eller relevant med avseende på den aktuella kanalen."
+ notify_user: "Jag vill prata med den här personen direkt och privat om meddelandet."
+ notify_moderators: "Detta meddelande kräver personalens uppmärksamhet av en annan anledning som inte anges ovan."
+ flagging:
+ action: "Flagga meddelande"
emoji_picker:
+ favorites: "Används ofta"
+ smileys_&_emotion: "Smileys och emojis"
+ objects: "Objekt"
+ people_&_body: "Människor och kropp"
+ travel_&_places: "Resor och platser"
+ animals_&_nature: "Djur och natur"
+ food_&_drink: "Mat och dryck"
+ activities: "Aktiviteter"
+ flags: "Flaggor"
+ symbols: "Symboler"
+ search_placeholder: "Sök efter emojinamn och alias..."
no_results: "Inga resultat"
+ draft_channel_screen:
+ header: "Nytt meddelande"
+ cancel: "Avbryt"
notifications:
chat_invitation: "bjöd in dig att gå med i en chattkanal"
chat_invitation_html: "%{username} bjöd in dig att gå med i en chattkanal"
@@ -346,12 +382,12 @@ sv:
chat_mention:
direct: 'nämnde dig i "%{channel}"'
direct_html: '%{username} nämnde dig i "%{channel}"'
- other: 'nämnde %{identifier} i "%{channel}"'
+ other_plain: 'nämnde %{identifier} i "%{channel}"'
other_html: '%{username} nämnde %{identifier} i "%{channel}"'
direct_message_chat_mention:
direct: "nämnde dig i personlig chatt"
direct_html: "%{username} nämnde dig i en personlig chatt"
- other: "nämnde %{identifier} i personlig chatt"
+ other_plain: "nämnde %{identifier} i personlig chatt"
other_html: "%{username} nämnde %{identifier} i personlig chatt"
chat_message: "Nytt chattmeddelande"
chat_quoted: "%{username} citerade ditt chattmeddelande"
@@ -376,6 +412,8 @@ sv:
label: Avsändare
description: Standard är system
review:
+ transcript:
+ view: "Visa tidigare meddelandens avskrift"
types:
reviewable_chat_message:
title: "Flaggat chattmeddelande"
@@ -401,3 +439,12 @@ sv:
allowed_pm_users_instructions: "Tillåt endast personliga meddelanden eller chattmeddelanden från dessa användare."
allow_private_messages_from_specific_users: "Tillåt endast specifika användare att skicka personliga meddelanden eller chattmeddelanden"
ignored_users_instructions: "Neka alla inlägg, meddelanden, notifikationer, personliga meddelanden eller direkta chattmeddelanden från dessa användare."
+ user_menu:
+ no_chat_notifications_title: "Du har inga chattaviseringar än"
+ no_chat_notifications_body: >
+ Du kommer att aviseras i den här panelen när någon skickar direktmeddelanden till dig eller @nämner dig i chatten. Aviseringar kommer också att skickas till din e-postadress när du inte har loggat in på ett tag. 归档一个频道会使它进入只读模式,并将该频道的所有信息移到一个新的或现有的主题中。将无法发送新消息,也不能编辑或删除现有的信息。
你确定要对 %{channelTitle} 频道进行存档吗?
" - process_started: "归档过程已经开始。这个弹窗很快就会关闭,当归档过程完成后,你会收到一条私信。" + instructions: "归档频道会使它进入只读模式,并将该频道的所有消息移至一个新的或现有的话题。将无法发送新消息,也不能编辑或删除现有消息。
确定要归档%{channelTitle}频道吗?
" + process_started: "归档过程已经开始。此对话框将很快关闭,当归档过程完成后,您会收到一条个人消息。" retry: "重试" channel_open: - title: "启用频道" - instructions: "重新启用频道,所有用户将能够发送消息并编辑他们现有的消息。" + title: "打开频道" + instructions: "重新打开频道,所有用户将能够发送消息和编辑他们现有的消息。" channel_close: title: "关闭频道" - instructions: "关闭该频道可以防止非工作人员用户发送新信息或编辑现有信息。你确定要关闭这个频道吗?" + instructions: "关闭该频道会阻止非管理人员用户发送新消息或编辑现有消息。确定要关闭此频道吗?" channel_delete: title: "删除频道" - instructions: "删除 %{name} 频道和聊天历史。所有信息和相关数据,如回应和上传,将被永久删除。如果你想保留频道历史,并将其停用,你可能想将该频道归档,而不是删除。
你确定你要 ,永久删除 该频道?为了确认,请在下面的方框中输入该频道的名称。
" + instructions: "删除%{name}频道和聊天历史。所有消息和相关数据,例如回应和上传,将被永久删除。如果您想保留频道历史记录,并将其停用,您可能想要归档频道。
确定要永久删除该频道吗?要确认,请在下面的框中输入频道的名称。
" confirm: "我明白后果,删除频道" confirm_channel_name: "输入频道名称" - process_started: "删除该频道的过程已经开始。这个对话框将很快关闭,你将无法看到被删除的频道。" + process_started: "删除频道的过程已经开始。此对话框将很快关闭,您将无法再查看被删除的频道。" channels_list_popup: browse: "浏览频道" + create: "新建频道" click_to_join: "点击此处查看可用频道。" close: "关闭" collapse: "折叠聊天抽屉" confirm_flag: "确定要举报 %{username} 的消息吗?" deleted: "一条消息已被删除。[查看]" - hidden: "一条消息已被删除。[查看]" + hidden: "一条消息已被隐藏。[查看]" delete: "删除" edited: "已编辑" - muted: "已静音" + muted: "已设为免打扰" joined: "已加入" empty_state: direct_message_cta: "开始个人聊天" direct_message: "您还可以与一位或多位用户开始个人聊天。" - title: "未找到频道" + title: "找不到频道" email_frequency: - description: "我们只有在过去15分钟内没有看到你时才会给你发电子邮件。" + description: "只有在过去 15 分钟内没有见到您,我们才会给您发送电子邮件。" never: "永不" title: "电子邮件通知" when_away: "仅在离开时" enable: "启用聊天" flag: "举报" + emoji: "插入表情符号" flagged: "此消息已被举报,等待审核" invalid_access: "您无权查看此聊天频道" - invitation_notification: "%{username} 邀请你加入一个聊天频道" + invitation_notification: "%{username}邀请您加入一个聊天频道" in_reply_to: "回复" heading: "聊天" join: "加入" @@ -116,16 +118,16 @@ zh_CN: description: "阻止发送所有非聊天推送通知" ignore_channel_wide_mention: title: "忽略频道范围内的提及" - description: "不要发送频道范围内提及的通知(@here 和 @all)" + description: "不发送频道范围内提及(@here 和 @all)的通知" open: "打开聊天" open_full_page: "打开全屏聊天" close_full_page: "关闭全屏聊天" open_message: "在聊天中打开消息" placeholder_self: "做些记录" - placeholder_others: "与 %{messageRecipient} 聊天" - placeholder_new_message_disallowed: "频道 %{status},您现在无法发送新消息。" + placeholder_others: "在 %{messageRecipient} 聊天" + placeholder_new_message_disallowed: "频道的状态为%{status},您现在无法发送新消息。" placeholder_silenced: "您目前无法发送消息。" - placeholder_start_conversation: 与 %{usernames}开始对话 + placeholder_start_conversation: 开始与 %{usernames} 的对话 remove_upload: "移除文件" react: "使用表情符号回复" reply: "回复" @@ -141,7 +143,7 @@ zh_CN: restore: "恢复已删除的消息" save: "保存" select: "选择" - silence: "静音用户" + silence: "将用户禁言" return_to_list: "返回频道列表" scroll_to_bottom: "滚动到底部" scroll_to_new_messages: "查看新消息" @@ -159,20 +161,20 @@ zh_CN: you_flagged: "您已举报此消息" exit: "返回" channel_status: - read_only_header: "频道只读" + read_only_header: "频道为只读" read_only: "只读" - archived_header: "频道已归档" + archived_header: "频道已被归档" archived: "已归档" - archive_failed: "频道归档失败。 %{completed}/%{total} 信息已被归档到 目标主题。请按重试,尝试完成存档。" - archive_completed: "见 归档主题" - closed_header: "频道已关闭" + archive_failed: "频道归档失败。%{completed} 条(共 %{total} 条)消息已被归档到目标话题。按“重试”,尝试完成存档。" + archive_completed: "请参阅归档话题" + closed_header: "频道已被关闭" closed: "已关闭" - open_header: "频道已开启" - open: "开启" + open_header: "频道处于开放状态" + open: "开放" browse: title: 频道 - filter_all: 全部 - filter_open: 已开启 + filter_all: 所有 + filter_open: 已打开 filter_closed: 已关闭 filter_archived: 已归档 filter_input_placeholder: 按名称搜索频道 @@ -182,7 +184,7 @@ zh_CN: members_view: filter_placeholder: 查找成员 about_view: - associated_topic: 已关联的主题 + associated_topic: 链接的话题 associated_category: 链接的类别 title: 标题 description: 描述 @@ -200,7 +202,7 @@ zh_CN: channel_edit_description_modal: title: 编辑描述 input_placeholder: 添加描述 - description: 告诉人们这个频道是关于什么 + description: 告诉人们这个频道的主题 direct_message_creator: title: 新消息 prefix: "至:" @@ -211,24 +213,26 @@ zh_CN: no_channels: "没有频道符合您的搜索" channel: no_memberships: 此频道没有成员 - no_memberships_found: 未找到成员 + no_memberships_found: 找不到成员 memberships_count: other: "%{count} 个成员" create_channel: auto_join_users: + public_category_warning: "%{category}是公共类别。是否自动将所有最近活跃的用户添加到此频道?" warning_groups: - other: 自动添加 %{members_count} 位来自 %{group_1} 和 %{group_2} 的用户? - warning_multiple_groups: 自动添加 %{members_count} 位来自 %{group_1} 和其他 %{count} 位用户? + other: 自动从%{group}和%{group_2}添加 %{members_count} 个用户? + warning_multiple_groups: 自动从%{group_1}和其他 %{count} 个群组添加 %{members_count} 个用户? choose_category: label: "选择一个类别" none: "选择一个…" - default_hint: 访问%{category}安全设置管理访问 + default_hint: 访问%{category}安全设置管理访问权限 hint_groups: - other: 根据 安全设置 %{hint_1} 和 %{hint_2}的用户将有权访问此频道 - hint_multiple_groups: 根据 安全设置, %{hint_1} 组和其他 %{count} 个组中的用户将有权访问此频道 + other: 根据安全设置,%{hint}和%{hint_2}中的用户将可以访问此频道 + hint_multiple_groups: 根据安全设置,%{hint_1}和其他 %{count} 个群组中的用户将有权访问此频道 create: "创建频道" description: "描述(可选)" name: "频道名称" + title: "新建频道" type: "类型" types: category: "类别" @@ -247,14 +251,16 @@ zh_CN: bold_text: "粗体" code_text: "代码文本" quote: - original_channel: '最初发送于 %{channel}' - copy_success: "引用的聊天已复制到剪贴板" + original_channel: '最初在%{channel}中发送。' + copy_success: "聊天引用已复制到剪贴板" notification_levels: never: "永不" mention: "仅限提及" always: "所有活动" settings: - auto_join_users_warning: "每个不是该频道成员且有权访问 %{category} 类别的用户都将加入。你确定吗?" + enable_auto_join_users: "自动添加所有最近活跃的用户" + disable_auto_join_users: "停止自动添加用户" + auto_join_users_warning: "所有不是此频道成员且有权访问%{category}类别的用户都将加入。确定吗?" desktop_notification_level: "桌面通知" follow: "加入" followed: "已加入" @@ -274,6 +280,7 @@ zh_CN: new: "新的个人聊天" create: "开始" leave: "离开此个人聊天" + cannot_create: "抱歉,您无法发送直接消息。" incoming_webhooks: back: "返回" channel_placeholder: "选择一个频道" @@ -296,11 +303,13 @@ zh_CN: system: "系统" title: "传入网络钩子" url: "URL" + url_instructions: "此 URL 包含一个秘密值 - 请安全保管。" username: "用户名" username_instructions: "发布到频道的机器人的用户名。留空时默认为“系统”。" + instructions: "外部系统可以使用传入网络钩子以机器人用户身份通过/hooks/:key 端点将消息发布到指定的聊天频道。有效负荷由单个 text 参数组成,限制为 2000 个字符。text 参数以及基于 https://api.slack.com/reference/surfaces/formatting 中的格式提取链接和提及,但是必须为此使用 /hooks/:key/slack 端点。"
selection:
cancel: "取消"
- quote_selection: "在主题中引用"
+ quote_selection: "话题中的引用"
copy: "复制"
move_selection_to_channel: "移至频道"
error: "移动聊天消息时出错"
@@ -309,12 +318,12 @@ zh_CN:
title: "移动到新话题"
instructions:
other: "您将创建一个新话题并使用您选择的 %{count} 条聊天消息进行填充。"
- instructions_channel_archive: "您将要创建一个新主题并将频道消息归档到该主题。"
+ instructions_channel_archive: "您将要创建一个新话题并将频道消息归档到该话题。"
existing_topic:
title: "移动到现有话题"
instructions:
other: "请选择您要将这 %{count} 条聊天消息移动到的话题。"
- instructions_channel_archive: "请选择您要将频道消息归档到的主题。"
+ instructions_channel_archive: "请选择您要将频道消息归档到的话题。"
new_message:
title: "移动到新消息"
instructions:
@@ -328,25 +337,47 @@ zh_CN:
public: "频道历史记录保留 %{days} 天。"
dm: "个人聊天记录保留 %{days} 天。"
topic_button_title: "聊天"
+ flags:
+ off_topic: "此消息与频道标题定义的当前讨论无关,可能应当移至其他地方。"
+ inappropriate: "此消息包含理性的人会认为具有攻击性、辱骂性或违反我们的社区准则的内容。"
+ spam: "此消息是广告或破坏行为。它对当前频道没有用,也不相关。"
+ notify_user: "我想亲自直接与此人谈谈他们的消息。"
+ notify_moderators: "由于上面未列出的另一个原因,此消息需要管理人员加以注意。"
+ flagging:
+ action: "举报消息"
emoji_picker:
+ favorites: "常用"
+ smileys_&_emotion: "笑脸和情感"
+ objects: "物体"
+ people_&_body: "人和身体"
+ travel_&_places: "旅行和地点"
+ animals_&_nature: "动物和自然"
+ food_&_drink: "食品和饮料"
+ activities: "活动"
+ flags: "旗帜"
+ symbols: "符号"
+ search_placeholder: "按表情符号名称和别名搜索…"
no_results: "没有结果"
+ draft_channel_screen:
+ header: "新消息"
+ cancel: "取消"
notifications:
chat_invitation: "邀请您加入聊天频道"
- chat_invitation_html: "%{username} 邀请您加入聊天频道"
- chat_quoted: "%{username} %{description}"
+ chat_invitation_html: "%{username}邀请您加入一个聊天频道"
+ chat_quoted: "%{username} %{description}"
popup:
chat_mention:
- direct: '在“%{channel}”中提到了你'
- direct_html: '%{username} 在“%{channel}”中提到了你'
- other: '在 "%{channel}" 中提到 %{identifier}'
- other_html: '%{username} 在 “%{channel}”中提到 %{identifier}'
+ direct: '在“%{channel}”中提及您'
+ direct_html: '%{username} 在“%{channel}”中提及您'
+ other_plain: '在“%{channel}”中提及“%{identifier}”'
+ other_html: '%{username} 在“%{channel}”中提及“%{identifier}”'
direct_message_chat_mention:
- direct: "在个人聊天中提到了你"
- direct_html: "%{username} 在个人聊天中提到了你"
- other: "在个人聊天中提到 %{identifier}"
- other_html: "%{username} 个人聊天中提到 %{identifier}"
+ direct: "在个人聊天中提及您"
+ direct_html: "%{username} 在个人聊天中提及您"
+ other_plain: "在个人聊天中提及“%{identifier}”"
+ other_html: "%{username} 在个人聊天中提及“%{identifier}”"
chat_message: "新的聊天消息"
- chat_quoted: "%{username} 引用了你的聊天消息"
+ chat_quoted: "%{username} 引用了您的聊天消息"
titles:
chat_mention: "聊天提及"
chat_invitation: "聊天邀请"
@@ -363,11 +394,13 @@ zh_CN:
chat_channel_id:
label: 聊天频道 ID
message:
- label: 消息
+ label: 私信
sender:
label: 发送人
description: 默认为“系统”
review:
+ transcript:
+ view: "查看以前的消息副本"
types:
reviewable_chat_message:
title: "举报的聊天消息"
@@ -379,17 +412,25 @@ zh_CN:
switch_channel_arrows: "%{shortcut} 切换频道"
open_quick_channel_selector: "%{shortcut} 打开快速频道选择器"
open_insert_link_modal: "%{shortcut} 插入超链接(仅输入框)"
- composer_bold: "%{shortcut} 粗体(仅输入框中)"
- composer_italic: "%{shortcut} 斜体(仅输入框中)"
- composer_code: "%{shortcut} 代码(仅输入框中)"
- drawer_open: "%{shortcut} 打开聊天面板"
- drawer_close: "%{shortcut} 关闭聊天面板"
+ composer_bold: "%{shortcut} 粗体(仅输入框)"
+ composer_italic: "%{shortcut} 斜体(仅输入框)"
+ composer_code: "%{shortcut} 代码(仅输入框)"
+ drawer_open: "%{shortcut} 打开聊天抽屉"
+ drawer_close: "%{shortcut} 关闭聊天抽屉"
topic_statuses:
chat:
- help: "已为此主题启用聊天"
+ help: "已为此话题启用聊天"
user:
- allow_private_messages: "允许其他用户向我发送私信和发起聊天"
+ allow_private_messages: "允许其他用户向我发送个人消息和聊天直接消息"
muted_users_instructions: "禁止来自这些用户的所有通知、个人消息和聊天消息。"
- allowed_pm_users_instructions: "仅允许来自这些用户的个人消息或聊天消息。"
- allow_private_messages_from_specific_users: "只允许特定用户向我发送个人消息或聊天消息"
- ignored_users_instructions: "禁止来自这些用户的所有帖子、消息、通知、个人消息和聊天消息。"
+ allowed_pm_users_instructions: "仅允许来自这些用户的个人消息或聊天直接消息。"
+ allow_private_messages_from_specific_users: "只允许特定用户向我发送个人消息或聊天直接消息"
+ ignored_users_instructions: "禁止来自这些用户的所有帖子、消息、通知、个人消息和聊天直接消息。"
+ user_menu:
+ no_chat_notifications_title: "您还没有任何聊天通知"
+ no_chat_notifications_body: >
+ 当有人在聊天中直接向您发送消息或提及 (@) 您时,您将在此面板中收到通知。当您有一段时间没有登录时,通知也会发送到您的电子邮件。U kunt die bron niet bekijken!
+Je kunt die bron niet bekijken!
Dit zal worden vervangen door een eigen Discourse 403-pagina.
Misschien probeerde u iets te wijzigen waarvoor u geen toegang hebt.
+Misschien probeerde je iets te wijzigen waartoe je geen toegang hebt.
De software die dit discussieforum mogelijk maakt, ondervond een onverwacht probleem. Onze excuses voor het ongemak.
Er is gedetailleerde informatie over de fout vastgelegd, en een automatische melding gegenereerd. We zullen ernaar kijken.
-Er is geen verdere actie nodig. Als de foutsituatie echter blijft bestaan, kunt u extra details verstrekken, waaronder stappen om de fout te reproduceren, door een discussietopic in de feedbackcategorie van de website te openen.
+Er is geen verdere actie nodig. Als de foutsituatie echter blijft bestaan, kun je aanvullende informatie verstrekken, waaronder stappen om de fout te reproduceren, door een discussietopic te openen in de feedbackcategorie van de website.